package com.chenyang.druid.wall;

import com.chenyang.druid.DbType;
import com.chenyang.druid.sql.SQLUtils;
import com.chenyang.druid.sql.ast.SQLName;
import com.chenyang.druid.sql.ast.SQLStatement;
import com.chenyang.druid.sql.ast.statement.SQLSelectStatement;
import com.chenyang.druid.sql.ast.statement.SQLUpdateStatement;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.MySqlHintStatement;
import com.chenyang.druid.sql.parser.Lexer;
import com.chenyang.druid.sql.parser.NotAllowCommentException;
import com.chenyang.druid.sql.parser.ParserException;
import com.chenyang.druid.sql.parser.SQLStatementParser;
import com.chenyang.druid.sql.parser.Token;
import com.chenyang.druid.sql.visitor.ExportParameterVisitor;
import com.chenyang.druid.sql.visitor.ParameterizedOutputVisitorUtils;
import com.chenyang.druid.util.JdbcSqlStatUtils;
import com.chenyang.druid.util.LRUCache;
import com.chenyang.druid.util.Utils;
import com.chenyang.druid.wall.spi.WallVisitorUtils;
import com.chenyang.druid.wall.violation.IllegalSQLObjectViolation;
import com.chenyang.druid.wall.violation.SyntaxErrorViolation;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public abstract class WallProvider {
   private String name;
   private final Map<String, Object> attributes;
   private boolean whiteListEnable;
   private LRUCache<String, WallSqlStat> whiteList;
   private int MAX_SQL_LENGTH;
   private int whiteSqlMaxSize;
   private boolean blackListEnable;
   private LRUCache<String, WallSqlStat> blackList;
   private LRUCache<String, WallSqlStat> blackMergedList;
   private int blackSqlMaxSize;
   protected final WallConfig config;
   private final ReentrantReadWriteLock lock;
   private static final ThreadLocal<Boolean> privileged = new ThreadLocal();
   private final ConcurrentMap<String, WallFunctionStat> functionStats;
   private final ConcurrentMap<String, WallTableStat> tableStats;
   public final WallDenyStat commentDeniedStat;
   protected DbType dbType;
   protected final AtomicLong checkCount;
   protected final AtomicLong hardCheckCount;
   protected final AtomicLong whiteListHitCount;
   protected final AtomicLong blackListHitCount;
   protected final AtomicLong syntaxErrorCount;
   protected final AtomicLong violationCount;
   protected final AtomicLong violationEffectRowCount;
   private static final ThreadLocal<Object> tenantValueLocal = new ThreadLocal();

   public WallProvider(WallConfig config) {
      this.attributes = new ConcurrentHashMap(1, 0.75F, 1);
      this.whiteListEnable = true;
      this.MAX_SQL_LENGTH = 8192;
      this.whiteSqlMaxSize = 1000;
      this.blackListEnable = true;
      this.blackSqlMaxSize = 200;
      this.lock = new ReentrantReadWriteLock();
      this.functionStats = new ConcurrentHashMap(16, 0.75F, 1);
      this.tableStats = new ConcurrentHashMap(16, 0.75F, 1);
      this.commentDeniedStat = new WallDenyStat();
      this.dbType = null;
      this.checkCount = new AtomicLong();
      this.hardCheckCount = new AtomicLong();
      this.whiteListHitCount = new AtomicLong();
      this.blackListHitCount = new AtomicLong();
      this.syntaxErrorCount = new AtomicLong();
      this.violationCount = new AtomicLong();
      this.violationEffectRowCount = new AtomicLong();
      this.config = config;
   }

   public WallProvider(WallConfig config, String dbType) {
      this(config, DbType.of(dbType));
   }

   public WallProvider(WallConfig config, DbType dbType) {
      this.attributes = new ConcurrentHashMap(1, 0.75F, 1);
      this.whiteListEnable = true;
      this.MAX_SQL_LENGTH = 8192;
      this.whiteSqlMaxSize = 1000;
      this.blackListEnable = true;
      this.blackSqlMaxSize = 200;
      this.lock = new ReentrantReadWriteLock();
      this.functionStats = new ConcurrentHashMap(16, 0.75F, 1);
      this.tableStats = new ConcurrentHashMap(16, 0.75F, 1);
      this.commentDeniedStat = new WallDenyStat();
      this.dbType = null;
      this.checkCount = new AtomicLong();
      this.hardCheckCount = new AtomicLong();
      this.whiteListHitCount = new AtomicLong();
      this.blackListHitCount = new AtomicLong();
      this.syntaxErrorCount = new AtomicLong();
      this.violationCount = new AtomicLong();
      this.violationEffectRowCount = new AtomicLong();
      this.config = config;
      this.dbType = dbType;
   }

   public String getName() {
      return this.name;
   }

   public void setName(String name) {
      this.name = name;
   }

   public Map<String, Object> getAttributes() {
      return this.attributes;
   }

   public void reset() {
      this.checkCount.set(0L);
      this.hardCheckCount.set(0L);
      this.violationCount.set(0L);
      this.whiteListHitCount.set(0L);
      this.blackListHitCount.set(0L);
      this.clearWhiteList();
      this.clearBlackList();
      this.functionStats.clear();
      this.tableStats.clear();
   }

   public ConcurrentMap<String, WallTableStat> getTableStats() {
      return this.tableStats;
   }

   public ConcurrentMap<String, WallFunctionStat> getFunctionStats() {
      return this.functionStats;
   }

   public WallSqlStat getSqlStat(String sql) {
      WallSqlStat sqlStat = this.getWhiteSql(sql);
      if (sqlStat == null) {
         sqlStat = this.getBlackSql(sql);
      }

      return sqlStat;
   }

   public WallTableStat getTableStat(String tableName) {
      String lowerCaseName = tableName.toLowerCase();
      if (lowerCaseName.startsWith("`") && lowerCaseName.endsWith("`")) {
         lowerCaseName = lowerCaseName.substring(1, lowerCaseName.length() - 1);
      }

      return this.getTableStatWithLowerName(lowerCaseName);
   }

   public void addUpdateCount(WallSqlStat sqlStat, long updateCount) {
      sqlStat.addUpdateCount(updateCount);
      Map<String, WallSqlTableStat> sqlTableStats = sqlStat.getTableStats();
      if (sqlTableStats != null) {
         for(Map.Entry<String, WallSqlTableStat> entry : sqlTableStats.entrySet()) {
            String tableName = (String)entry.getKey();
            WallTableStat tableStat = this.getTableStat(tableName);
            if (tableStat != null) {
               WallSqlTableStat sqlTableStat = (WallSqlTableStat)entry.getValue();
               if (sqlTableStat.getDeleteCount() > 0) {
                  tableStat.addDeleteDataCount(updateCount);
               } else if (sqlTableStat.getUpdateCount() > 0) {
                  tableStat.addUpdateDataCount(updateCount);
               } else if (sqlTableStat.getInsertCount() > 0) {
                  tableStat.addInsertDataCount(updateCount);
               }
            }
         }

      }
   }

   public void addFetchRowCount(WallSqlStat sqlStat, long fetchRowCount) {
      sqlStat.addAndFetchRowCount(fetchRowCount);
      Map<String, WallSqlTableStat> sqlTableStats = sqlStat.getTableStats();
      if (sqlTableStats != null) {
         for(Map.Entry<String, WallSqlTableStat> entry : sqlTableStats.entrySet()) {
            String tableName = (String)entry.getKey();
            WallTableStat tableStat = this.getTableStat(tableName);
            if (tableStat != null) {
               WallSqlTableStat sqlTableStat = (WallSqlTableStat)entry.getValue();
               if (sqlTableStat.getSelectCount() > 0) {
                  tableStat.addFetchRowCount(fetchRowCount);
               }
            }
         }

      }
   }

   public WallTableStat getTableStatWithLowerName(String lowerCaseName) {
      WallTableStat stat = (WallTableStat)this.tableStats.get(lowerCaseName);
      if (stat == null) {
         if (this.tableStats.size() > 10000) {
            return null;
         }

         this.tableStats.putIfAbsent(lowerCaseName, new WallTableStat());
         stat = (WallTableStat)this.tableStats.get(lowerCaseName);
      }

      return stat;
   }

   public WallFunctionStat getFunctionStat(String functionName) {
      String lowerCaseName = functionName.toLowerCase();
      return this.getFunctionStatWithLowerName(lowerCaseName);
   }

   public WallFunctionStat getFunctionStatWithLowerName(String lowerCaseName) {
      WallFunctionStat stat = (WallFunctionStat)this.functionStats.get(lowerCaseName);
      if (stat == null) {
         if (this.functionStats.size() > 10000) {
            return null;
         }

         this.functionStats.putIfAbsent(lowerCaseName, new WallFunctionStat());
         stat = (WallFunctionStat)this.functionStats.get(lowerCaseName);
      }

      return stat;
   }

   public WallConfig getConfig() {
      return this.config;
   }

   public WallSqlStat addWhiteSql(String sql, Map<String, WallSqlTableStat> tableStats, Map<String, WallSqlFunctionStat> functionStats, boolean syntaxError) {
      if (!this.whiteListEnable) {
         WallSqlStat stat = new WallSqlStat(tableStats, functionStats, syntaxError);
         return stat;
      } else {
         String mergedSql;
         try {
            mergedSql = ParameterizedOutputVisitorUtils.parameterize(sql, this.dbType);
         } catch (Exception var24) {
            WallSqlStat stat = new WallSqlStat(tableStats, functionStats, syntaxError);
            stat.incrementAndGetExecuteCount();
            return stat;
         }

         if (mergedSql != sql) {
            this.lock.readLock().lock();

            WallSqlStat mergedStat;
            try {
               if (this.whiteList == null) {
                  this.whiteList = new LRUCache<String, WallSqlStat>(this.whiteSqlMaxSize);
               }

               mergedStat = (WallSqlStat)this.whiteList.get(mergedSql);
            } finally {
               this.lock.readLock().unlock();
            }

            if (mergedStat == null) {
               WallSqlStat newStat = new WallSqlStat(tableStats, functionStats, syntaxError);
               newStat.setSample(sql);
               this.lock.writeLock().lock();

               try {
                  mergedStat = (WallSqlStat)this.whiteList.get(mergedSql);
                  if (mergedStat == null) {
                     this.whiteList.put(mergedSql, newStat);
                     mergedStat = newStat;
                  }
               } finally {
                  this.lock.writeLock().unlock();
               }
            }

            mergedStat.incrementAndGetExecuteCount();
            return mergedStat;
         } else {
            this.lock.writeLock().lock();

            WallSqlStat newStat;
            try {
               if (this.whiteList == null) {
                  this.whiteList = new LRUCache<String, WallSqlStat>(this.whiteSqlMaxSize);
               }

               WallSqlStat wallStat = (WallSqlStat)this.whiteList.get(sql);
               if (wallStat == null) {
                  wallStat = new WallSqlStat(tableStats, functionStats, syntaxError);
                  this.whiteList.put(sql, wallStat);
                  wallStat.setSample(sql);
                  wallStat.incrementAndGetExecuteCount();
               }

               newStat = wallStat;
            } finally {
               this.lock.writeLock().unlock();
            }

            return newStat;
         }
      }
   }

   public WallSqlStat addBlackSql(String sql, Map<String, WallSqlTableStat> tableStats, Map<String, WallSqlFunctionStat> functionStats, List<Violation> violations, boolean syntaxError) {
      if (!this.blackListEnable) {
         return new WallSqlStat(tableStats, functionStats, violations, syntaxError);
      } else {
         String mergedSql;
         try {
            mergedSql = ParameterizedOutputVisitorUtils.parameterize(sql, this.dbType);
         } catch (Exception var12) {
            mergedSql = sql;
         }

         this.lock.writeLock().lock();

         WallSqlStat var8;
         try {
            if (this.blackList == null) {
               this.blackList = new LRUCache<String, WallSqlStat>(this.blackSqlMaxSize);
            }

            if (this.blackMergedList == null) {
               this.blackMergedList = new LRUCache<String, WallSqlStat>(this.blackSqlMaxSize);
            }

            WallSqlStat wallStat = (WallSqlStat)this.blackList.get(sql);
            if (wallStat == null) {
               wallStat = (WallSqlStat)this.blackMergedList.get(mergedSql);
               if (wallStat == null) {
                  wallStat = new WallSqlStat(tableStats, functionStats, violations, syntaxError);
                  this.blackMergedList.put(mergedSql, wallStat);
                  wallStat.setSample(sql);
               }

               wallStat.incrementAndGetExecuteCount();
               this.blackList.put(sql, wallStat);
            }

            var8 = wallStat;
         } finally {
            this.lock.writeLock().unlock();
         }

         return var8;
      }
   }

   public Set<String> getWhiteList() {
      Set<String> hashSet = new HashSet();
      this.lock.readLock().lock();

      try {
         if (this.whiteList != null) {
            hashSet.addAll(this.whiteList.keySet());
         }
      } finally {
         this.lock.readLock().unlock();
      }

      return Collections.unmodifiableSet(hashSet);
   }

   public Set<String> getSqlList() {
      Set<String> hashSet = new HashSet();
      this.lock.readLock().lock();

      try {
         if (this.whiteList != null) {
            hashSet.addAll(this.whiteList.keySet());
         }

         if (this.blackMergedList != null) {
            hashSet.addAll(this.blackMergedList.keySet());
         }
      } finally {
         this.lock.readLock().unlock();
      }

      return Collections.unmodifiableSet(hashSet);
   }

   public Set<String> getBlackList() {
      Set<String> hashSet = new HashSet();
      this.lock.readLock().lock();

      try {
         if (this.blackList != null) {
            hashSet.addAll(this.blackList.keySet());
         }
      } finally {
         this.lock.readLock().unlock();
      }

      return Collections.unmodifiableSet(hashSet);
   }

   public void clearCache() {
      this.lock.writeLock().lock();

      try {
         if (this.whiteList != null) {
            this.whiteList = null;
         }

         if (this.blackList != null) {
            this.blackList = null;
         }

         if (this.blackMergedList != null) {
            this.blackMergedList = null;
         }
      } finally {
         this.lock.writeLock().unlock();
      }

   }

   public void clearWhiteList() {
      this.lock.writeLock().lock();

      try {
         if (this.whiteList != null) {
            this.whiteList = null;
         }
      } finally {
         this.lock.writeLock().unlock();
      }

   }

   public void clearBlackList() {
      this.lock.writeLock().lock();

      try {
         if (this.blackList != null) {
            this.blackList = null;
         }
      } finally {
         this.lock.writeLock().unlock();
      }

   }

   public WallSqlStat getWhiteSql(String sql) {
      WallSqlStat stat = null;
      this.lock.readLock().lock();

      try {
         if (this.whiteList == null) {
            Object var3 = null;
            return (WallSqlStat)var3;
         }

         stat = (WallSqlStat)this.whiteList.get(sql);
      } finally {
         this.lock.readLock().unlock();
      }

      if (stat != null) {
         return stat;
      } else {
         String mergedSql;
         try {
            mergedSql = ParameterizedOutputVisitorUtils.parameterize(sql, this.dbType, (List)null);
         } catch (Exception var13) {
            return null;
         }

         this.lock.readLock().lock();

         try {
            stat = (WallSqlStat)this.whiteList.get(mergedSql);
         } finally {
            this.lock.readLock().unlock();
         }

         return stat;
      }
   }

   public WallSqlStat getBlackSql(String sql) {
      this.lock.readLock().lock();

      WallSqlStat var2;
      try {
         if (this.blackList != null) {
            var2 = (WallSqlStat)this.blackList.get(sql);
            return var2;
         }

         var2 = null;
      } finally {
         this.lock.readLock().unlock();
      }

      return var2;
   }

   public boolean whiteContains(String sql) {
      return this.getWhiteSql(sql) != null;
   }

   public abstract SQLStatementParser createParser(String var1);

   public abstract WallVisitor createWallVisitor();

   public abstract ExportParameterVisitor createExportParameterVisitor();

   public boolean checkValid(String sql) {
      WallContext originalContext = WallContext.current();

      boolean var4;
      try {
         WallContext.create(this.dbType);
         WallCheckResult result = this.checkInternal(sql);
         var4 = result.getViolations().isEmpty();
      } finally {
         if (originalContext == null) {
            WallContext.clearContext();
         }

      }

      return var4;
   }

   public void incrementCommentDeniedCount() {
      this.commentDeniedStat.incrementAndGetDenyCount();
   }

   public boolean checkDenyFunction(String functionName) {
      if (functionName == null) {
         return true;
      } else {
         functionName = functionName.toLowerCase();
         return !this.getConfig().getDenyFunctions().contains(functionName);
      }
   }

   public boolean checkDenySchema(String schemaName) {
      if (schemaName == null) {
         return true;
      } else if (!this.config.isSchemaCheck()) {
         return true;
      } else {
         schemaName = schemaName.toLowerCase();
         return !this.getConfig().getDenySchemas().contains(schemaName);
      }
   }

   public boolean checkDenyTable(String tableName) {
      if (tableName == null) {
         return true;
      } else {
         tableName = WallVisitorUtils.form(tableName);
         return !this.getConfig().getDenyTables().contains(tableName);
      }
   }

   public boolean checkReadOnlyTable(String tableName) {
      if (tableName == null) {
         return true;
      } else {
         tableName = WallVisitorUtils.form(tableName);
         return !this.getConfig().isReadOnly(tableName);
      }
   }

   public WallDenyStat getCommentDenyStat() {
      return this.commentDeniedStat;
   }

   public WallCheckResult check(String sql) {
      WallContext originalContext = WallContext.current();

      WallCheckResult var3;
      try {
         WallContext.createIfNotExists(this.dbType);
         var3 = this.checkInternal(sql);
      } finally {
         if (originalContext == null) {
            WallContext.clearContext();
         }

      }

      return var3;
   }

   private WallCheckResult checkInternal(String sql) {
      this.checkCount.incrementAndGet();
      WallContext context = WallContext.current();
      if (this.config.isDoPrivilegedAllow() && ispPrivileged()) {
         WallCheckResult checkResult = new WallCheckResult();
         checkResult.setSql(sql);
         return checkResult;
      } else {
         boolean mulltiTenant = this.config.getTenantTablePattern() != null && this.config.getTenantTablePattern().length() > 0;
         if (!mulltiTenant) {
            WallCheckResult checkResult = this.checkWhiteAndBlackList(sql);
            if (checkResult != null) {
               checkResult.setSql(sql);
               return checkResult;
            }
         }

         this.hardCheckCount.incrementAndGet();
         List<Violation> violations = new ArrayList();
         List<SQLStatement> statementList = new ArrayList();
         boolean syntaxError = false;
         boolean endOfComment = false;

         try {
            SQLStatementParser parser = this.createParser(sql);
            parser.getLexer().setCommentHandler(WallCommentHandler.instance);
            if (!this.config.isCommentAllow()) {
               parser.getLexer().setAllowComment(false);
            }

            if (!this.config.isCompleteInsertValuesCheck()) {
               parser.setParseCompleteValues(false);
               parser.setParseValuesSize(this.config.getInsertValuesCheckSize());
            }

            parser.parseStatementList(statementList);
            Token lastToken = parser.getLexer().token();
            if (lastToken != Token.EOF && this.config.isStrictSyntaxCheck()) {
               violations.add(new IllegalSQLObjectViolation(1001, "not terminal sql, token " + lastToken, sql));
            }

            endOfComment = parser.getLexer().isEndOfComment();
         } catch (NotAllowCommentException var19) {
            violations.add(new IllegalSQLObjectViolation(1104, "comment not allow", sql));
            this.incrementCommentDeniedCount();
         } catch (ParserException e) {
            this.syntaxErrorCount.incrementAndGet();
            syntaxError = true;
            if (this.config.isStrictSyntaxCheck()) {
               violations.add(new SyntaxErrorViolation(e, sql));
            }
         } catch (Exception e) {
            if (this.config.isStrictSyntaxCheck()) {
               violations.add(new SyntaxErrorViolation(e, sql));
            }
         }

         if (statementList.size() > 1 && !this.config.isMultiStatementAllow()) {
            violations.add(new IllegalSQLObjectViolation(2201, "multi-statement not allow", sql));
         }

         WallVisitor visitor = this.createWallVisitor();
         visitor.setSqlEndOfComment(endOfComment);
         if (statementList.size() > 0) {
            boolean lastIsHint = false;

            for(int i = 0; i < statementList.size(); ++i) {
               SQLStatement stmt = (SQLStatement)statementList.get(i);
               if ((i == 0 || lastIsHint) && stmt instanceof MySqlHintStatement) {
                  lastIsHint = true;
               } else {
                  try {
                     stmt.accept(visitor);
                  } catch (ParserException e) {
                     violations.add(new SyntaxErrorViolation(e, sql));
                  }
               }
            }
         }

         if (visitor.getViolations().size() > 0) {
            violations.addAll(visitor.getViolations());
         }

         Map<String, WallSqlTableStat> tableStat = context.getTableStats();
         boolean updateCheckHandlerEnable = false;
         WallUpdateCheckHandler updateCheckHandler = this.config.getUpdateCheckHandler();
         if (updateCheckHandler != null) {
            for(SQLStatement stmt : statementList) {
               if (stmt instanceof SQLUpdateStatement) {
                  SQLUpdateStatement updateStmt = (SQLUpdateStatement)stmt;
                  SQLName table = updateStmt.getTableName();
                  if (table != null) {
                     String tableName = table.getSimpleName();
                     Set<String> updateCheckColumns = this.config.getUpdateCheckTable(tableName);
                     if (updateCheckColumns != null && updateCheckColumns.size() > 0) {
                        updateCheckHandlerEnable = true;
                        break;
                     }
                  }
               }
            }
         }

         WallSqlStat sqlStat = null;
         if (violations.size() > 0) {
            this.violationCount.incrementAndGet();
            if (!updateCheckHandlerEnable && sql.length() < this.MAX_SQL_LENGTH) {
               sqlStat = this.addBlackSql(sql, tableStat, context.getFunctionStats(), violations, syntaxError);
            }
         } else if (!updateCheckHandlerEnable && sql.length() < this.MAX_SQL_LENGTH) {
            boolean selectLimit = false;
            if (this.config.getSelectLimit() > 0) {
               for(SQLStatement stmt : statementList) {
                  if (stmt instanceof SQLSelectStatement) {
                     selectLimit = true;
                     break;
                  }
               }
            }

            if (!selectLimit) {
               sqlStat = this.addWhiteSql(sql, tableStat, context.getFunctionStats(), syntaxError);
            }
         }

         if (sqlStat == null && updateCheckHandlerEnable) {
            sqlStat = new WallSqlStat(tableStat, context.getFunctionStats(), violations, syntaxError);
         }

         Map<String, WallSqlTableStat> tableStats = null;
         Map<String, WallSqlFunctionStat> functionStats = null;
         if (context != null) {
            tableStats = context.getTableStats();
            functionStats = context.getFunctionStats();
            this.recordStats(tableStats, functionStats);
         }

         WallCheckResult result;
         if (sqlStat != null) {
            context.setSqlStat(sqlStat);
            result = new WallCheckResult(sqlStat, statementList);
         } else {
            result = new WallCheckResult((WallSqlStat)null, violations, tableStats, functionStats, statementList, syntaxError);
         }

         String resultSql;
         if (visitor.isSqlModified()) {
            resultSql = SQLUtils.toSQLString(statementList, this.dbType);
         } else {
            resultSql = sql;
         }

         result.setSql(resultSql);
         result.setUpdateCheckItems(visitor.getUpdateCheckItems());
         return result;
      }
   }

   private WallCheckResult checkWhiteAndBlackList(String sql) {
      if (this.config.getUpdateCheckHandler() != null) {
         return null;
      } else {
         if (this.blackListEnable) {
            WallSqlStat sqlStat = this.getBlackSql(sql);
            if (sqlStat != null) {
               this.blackListHitCount.incrementAndGet();
               this.violationCount.incrementAndGet();
               if (sqlStat.isSyntaxError()) {
                  this.syntaxErrorCount.incrementAndGet();
               }

               sqlStat.incrementAndGetExecuteCount();
               this.recordStats(sqlStat.getTableStats(), sqlStat.getFunctionStats());
               return new WallCheckResult(sqlStat);
            }
         }

         if (this.whiteListEnable) {
            WallSqlStat sqlStat = this.getWhiteSql(sql);
            if (sqlStat != null) {
               this.whiteListHitCount.incrementAndGet();
               sqlStat.incrementAndGetExecuteCount();
               if (sqlStat.isSyntaxError()) {
                  this.syntaxErrorCount.incrementAndGet();
               }

               this.recordStats(sqlStat.getTableStats(), sqlStat.getFunctionStats());
               WallContext context = WallContext.current();
               if (context != null) {
                  context.setSqlStat(sqlStat);
               }

               return new WallCheckResult(sqlStat);
            }
         }

         return null;
      }
   }

   void recordStats(Map<String, WallSqlTableStat> tableStats, Map<String, WallSqlFunctionStat> functionStats) {
      if (tableStats != null) {
         for(Map.Entry<String, WallSqlTableStat> entry : tableStats.entrySet()) {
            String tableName = (String)entry.getKey();
            WallSqlTableStat sqlTableStat = (WallSqlTableStat)entry.getValue();
            WallTableStat tableStat = this.getTableStat(tableName);
            if (tableStat != null) {
               tableStat.addSqlTableStat(sqlTableStat);
            }
         }
      }

      if (functionStats != null) {
         for(Map.Entry<String, WallSqlFunctionStat> entry : functionStats.entrySet()) {
            String tableName = (String)entry.getKey();
            WallSqlFunctionStat sqlTableStat = (WallSqlFunctionStat)entry.getValue();
            WallFunctionStat functionStat = this.getFunctionStatWithLowerName(tableName);
            if (functionStat != null) {
               functionStat.addSqlFunctionStat(sqlTableStat);
            }
         }
      }

   }

   public static boolean ispPrivileged() {
      Boolean value = (Boolean)privileged.get();
      return value == null ? false : value;
   }

   public static <T> T doPrivileged(PrivilegedAction<T> action) {
      Boolean original = (Boolean)privileged.get();
      privileged.set(Boolean.TRUE);

      Object var2;
      try {
         var2 = action.run();
      } finally {
         privileged.set(original);
      }

      return (T)var2;
   }

   public static void setTenantValue(Object value) {
      tenantValueLocal.set(value);
   }

   public static Object getTenantValue() {
      return tenantValueLocal.get();
   }

   public long getWhiteListHitCount() {
      return this.whiteListHitCount.get();
   }

   public long getBlackListHitCount() {
      return this.blackListHitCount.get();
   }

   public long getSyntaxErrorCount() {
      return this.syntaxErrorCount.get();
   }

   public long getCheckCount() {
      return this.checkCount.get();
   }

   public long getViolationCount() {
      return this.violationCount.get();
   }

   public long getHardCheckCount() {
      return this.hardCheckCount.get();
   }

   public long getViolationEffectRowCount() {
      return this.violationEffectRowCount.get();
   }

   public void addViolationEffectRowCount(long rowCount) {
      this.violationEffectRowCount.addAndGet(rowCount);
   }

   public WallProviderStatValue getStatValue(boolean reset) {
      WallProviderStatValue statValue = new WallProviderStatValue();
      statValue.setName(this.name);
      statValue.setCheckCount(JdbcSqlStatUtils.get(this.checkCount, reset));
      statValue.setHardCheckCount(JdbcSqlStatUtils.get(this.hardCheckCount, reset));
      statValue.setViolationCount(JdbcSqlStatUtils.get(this.violationCount, reset));
      statValue.setViolationEffectRowCount(JdbcSqlStatUtils.get(this.violationEffectRowCount, reset));
      statValue.setBlackListHitCount(JdbcSqlStatUtils.get(this.blackListHitCount, reset));
      statValue.setWhiteListHitCount(JdbcSqlStatUtils.get(this.whiteListHitCount, reset));
      statValue.setSyntaxErrorCount(JdbcSqlStatUtils.get(this.syntaxErrorCount, reset));

      for(Map.Entry<String, WallTableStat> entry : this.tableStats.entrySet()) {
         String tableName = (String)entry.getKey();
         WallTableStat tableStat = (WallTableStat)entry.getValue();
         WallTableStatValue tableStatValue = tableStat.getStatValue(reset);
         if (tableStatValue.getTotalExecuteCount() != 0L) {
            tableStatValue.setName(tableName);
            statValue.getTables().add(tableStatValue);
         }
      }

      for(Map.Entry<String, WallFunctionStat> entry : this.functionStats.entrySet()) {
         String functionName = (String)entry.getKey();
         WallFunctionStat functionStat = (WallFunctionStat)entry.getValue();
         WallFunctionStatValue functionStatValue = functionStat.getStatValue(reset);
         if (functionStatValue.getInvokeCount() != 0L) {
            functionStatValue.setName(functionName);
            statValue.getFunctions().add(functionStatValue);
         }
      }

      Lock lock = (Lock)(reset ? this.lock.writeLock() : this.lock.readLock());
      lock.lock();

      try {
         if (this.whiteList != null) {
            for(Map.Entry<String, WallSqlStat> entry : this.whiteList.entrySet()) {
               String sql = (String)entry.getKey();
               WallSqlStat sqlStat = (WallSqlStat)entry.getValue();
               WallSqlStatValue sqlStatValue = sqlStat.getStatValue(reset);
               if (sqlStatValue.getExecuteCount() != 0L) {
                  sqlStatValue.setSql(sql);
                  long sqlHash = sqlStat.getSqlHash();
                  if (sqlHash == 0L) {
                     sqlHash = Utils.fnv_64(sql);
                     sqlStat.setSqlHash(sqlHash);
                  }

                  sqlStatValue.setSqlHash(sqlHash);
                  statValue.getWhiteList().add(sqlStatValue);
               }
            }
         }

         if (this.blackMergedList != null) {
            for(Map.Entry<String, WallSqlStat> entry : this.blackMergedList.entrySet()) {
               String sql = (String)entry.getKey();
               WallSqlStat sqlStat = (WallSqlStat)entry.getValue();
               WallSqlStatValue sqlStatValue = sqlStat.getStatValue(reset);
               if (sqlStatValue.getExecuteCount() != 0L) {
                  sqlStatValue.setSql(sql);
                  statValue.getBlackList().add(sqlStatValue);
               }
            }
         }
      } finally {
         lock.unlock();
      }

      return statValue;
   }

   public Map<String, Object> getStatsMap() {
      return this.getStatValue(false).toMap();
   }

   public boolean isWhiteListEnable() {
      return this.whiteListEnable;
   }

   public void setWhiteListEnable(boolean whiteListEnable) {
      this.whiteListEnable = whiteListEnable;
   }

   public boolean isBlackListEnable() {
      return this.blackListEnable;
   }

   public void setBlackListEnable(boolean blackListEnable) {
      this.blackListEnable = blackListEnable;
   }

   public static class WallCommentHandler implements Lexer.CommentHandler {
      public static final WallCommentHandler instance = new WallCommentHandler();

      public boolean handle(Token lastToken, String comment) {
         if (lastToken == null) {
            return false;
         } else {
            switch (lastToken) {
               case SELECT:
               case INSERT:
               case DELETE:
               case UPDATE:
               case TRUNCATE:
               case SET:
               case CREATE:
               case ALTER:
               case DROP:
               case SHOW:
               case REPLACE:
                  return true;
               default:
                  WallContext context = WallContext.current();
                  if (context != null) {
                     context.incrementCommentCount();
                  }

                  return false;
            }
         }
      }
   }
}
