package com.chenyang.druid.wall.spi;

import com.chenyang.druid.DbType;
import com.chenyang.druid.sql.ast.SQLCommentHint;
import com.chenyang.druid.sql.ast.SQLExpr;
import com.chenyang.druid.sql.ast.SQLLimit;
import com.chenyang.druid.sql.ast.SQLName;
import com.chenyang.druid.sql.ast.SQLObject;
import com.chenyang.druid.sql.ast.SQLOrderBy;
import com.chenyang.druid.sql.ast.SQLStatement;
import com.chenyang.druid.sql.ast.expr.SQLAggregateExpr;
import com.chenyang.druid.sql.ast.expr.SQLAllColumnExpr;
import com.chenyang.druid.sql.ast.expr.SQLBetweenExpr;
import com.chenyang.druid.sql.ast.expr.SQLBinaryOpExpr;
import com.chenyang.druid.sql.ast.expr.SQLBinaryOpExprGroup;
import com.chenyang.druid.sql.ast.expr.SQLBinaryOperator;
import com.chenyang.druid.sql.ast.expr.SQLBooleanExpr;
import com.chenyang.druid.sql.ast.expr.SQLCaseExpr;
import com.chenyang.druid.sql.ast.expr.SQLCharExpr;
import com.chenyang.druid.sql.ast.expr.SQLExistsExpr;
import com.chenyang.druid.sql.ast.expr.SQLIdentifierExpr;
import com.chenyang.druid.sql.ast.expr.SQLInListExpr;
import com.chenyang.druid.sql.ast.expr.SQLInSubQueryExpr;
import com.chenyang.druid.sql.ast.expr.SQLIntegerExpr;
import com.chenyang.druid.sql.ast.expr.SQLLiteralExpr;
import com.chenyang.druid.sql.ast.expr.SQLMethodInvokeExpr;
import com.chenyang.druid.sql.ast.expr.SQLNCharExpr;
import com.chenyang.druid.sql.ast.expr.SQLNotExpr;
import com.chenyang.druid.sql.ast.expr.SQLNumberExpr;
import com.chenyang.druid.sql.ast.expr.SQLNumericLiteralExpr;
import com.chenyang.druid.sql.ast.expr.SQLPropertyExpr;
import com.chenyang.druid.sql.ast.expr.SQLQueryExpr;
import com.chenyang.druid.sql.ast.expr.SQLUnaryExpr;
import com.chenyang.druid.sql.ast.expr.SQLValuableExpr;
import com.chenyang.druid.sql.ast.expr.SQLVariantRefExpr;
import com.chenyang.druid.sql.ast.statement.SQLAlterStatement;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableStatement;
import com.chenyang.druid.sql.ast.statement.SQLBlockStatement;
import com.chenyang.druid.sql.ast.statement.SQLCallStatement;
import com.chenyang.druid.sql.ast.statement.SQLCommentStatement;
import com.chenyang.druid.sql.ast.statement.SQLCommitStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateTableStatement;
import com.chenyang.druid.sql.ast.statement.SQLDeleteStatement;
import com.chenyang.druid.sql.ast.statement.SQLDescribeStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropTableStatement;
import com.chenyang.druid.sql.ast.statement.SQLExplainStatement;
import com.chenyang.druid.sql.ast.statement.SQLExprTableSource;
import com.chenyang.druid.sql.ast.statement.SQLInsertInto;
import com.chenyang.druid.sql.ast.statement.SQLInsertStatement;
import com.chenyang.druid.sql.ast.statement.SQLJoinTableSource;
import com.chenyang.druid.sql.ast.statement.SQLMergeStatement;
import com.chenyang.druid.sql.ast.statement.SQLReplaceStatement;
import com.chenyang.druid.sql.ast.statement.SQLRollbackStatement;
import com.chenyang.druid.sql.ast.statement.SQLSelect;
import com.chenyang.druid.sql.ast.statement.SQLSelectGroupByClause;
import com.chenyang.druid.sql.ast.statement.SQLSelectItem;
import com.chenyang.druid.sql.ast.statement.SQLSelectQuery;
import com.chenyang.druid.sql.ast.statement.SQLSelectQueryBlock;
import com.chenyang.druid.sql.ast.statement.SQLSelectStatement;
import com.chenyang.druid.sql.ast.statement.SQLSetStatement;
import com.chenyang.druid.sql.ast.statement.SQLShowStatement;
import com.chenyang.druid.sql.ast.statement.SQLStartTransactionStatement;
import com.chenyang.druid.sql.ast.statement.SQLSubqueryTableSource;
import com.chenyang.druid.sql.ast.statement.SQLTableSource;
import com.chenyang.druid.sql.ast.statement.SQLTruncateStatement;
import com.chenyang.druid.sql.ast.statement.SQLUnionOperator;
import com.chenyang.druid.sql.ast.statement.SQLUnionQuery;
import com.chenyang.druid.sql.ast.statement.SQLUnionQueryTableSource;
import com.chenyang.druid.sql.ast.statement.SQLUpdateSetItem;
import com.chenyang.druid.sql.ast.statement.SQLUpdateStatement;
import com.chenyang.druid.sql.ast.statement.SQLUseStatement;
import com.chenyang.druid.sql.dialect.mysql.ast.expr.MySqlOrderingExpr;
import com.chenyang.druid.sql.dialect.mysql.ast.expr.MySqlOutFileExpr;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.MySqlDeleteStatement;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.MySqlExplainStatement;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.MySqlHintStatement;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.MySqlLockTableStatement;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.MySqlOptimizeStatement;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.MySqlRenameTableStatement;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.MySqlShowGrantsStatement;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement;
import com.chenyang.druid.sql.dialect.mysql.parser.MySqlStatementParser;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleExecuteImmediateStatement;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement;
import com.chenyang.druid.sql.parser.SQLStatementParser;
import com.chenyang.druid.sql.visitor.ExportParameterVisitor;
import com.chenyang.druid.sql.visitor.SQLEvalVisitor;
import com.chenyang.druid.sql.visitor.SQLEvalVisitorUtils;
import com.chenyang.druid.sql.visitor.functions.Nil;
import com.chenyang.druid.support.logging.Log;
import com.chenyang.druid.support.logging.LogFactory;
import com.chenyang.druid.util.FnvHash;
import com.chenyang.druid.util.JdbcUtils;
import com.chenyang.druid.util.ServletPathMatcher;
import com.chenyang.druid.util.StringUtils;
import com.chenyang.druid.wall.WallConfig;
import com.chenyang.druid.wall.WallContext;
import com.chenyang.druid.wall.WallProvider;
import com.chenyang.druid.wall.WallSqlTableStat;
import com.chenyang.druid.wall.WallUpdateCheckHandler;
import com.chenyang.druid.wall.WallUpdateCheckItem;
import com.chenyang.druid.wall.WallVisitor;
import com.chenyang.druid.wall.violation.ErrorCode;
import com.chenyang.druid.wall.violation.IllegalSQLObjectViolation;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;

public class WallVisitorUtils {
   private static final Log LOG = LogFactory.getLog(WallVisitorUtils.class);
   public static final String HAS_TRUE_LIKE = "hasTrueLike";
   public static final String[] whiteHints = new String[]{"LOCAL", "TEMPORARY", "SQL_NO_CACHE", "SQL_CACHE", "HIGH_PRIORITY", "LOW_PRIORITY", "STRAIGHT_JOIN", "SQL_BUFFER_RESULT", "SQL_BIG_RESULT", "SQL_SMALL_RESULT", "DELAYED"};
   private static ThreadLocal<WallConditionContext> wallConditionContextLocal = new ThreadLocal();
   private static ThreadLocal<WallTopStatementContext> wallTopStatementContextLocal = new ThreadLocal();

   public static void check(WallVisitor visitor, SQLInListExpr x) {
   }

   public static boolean check(WallVisitor visitor, SQLBinaryOpExpr x) {
      if (x.getOperator() != SQLBinaryOperator.BooleanOr && x.getOperator() != SQLBinaryOperator.BooleanAnd) {
         if (x.getOperator() == SQLBinaryOperator.Add || x.getOperator() == SQLBinaryOperator.Concat) {
            List<SQLExpr> groupList = SQLBinaryOpExpr.split(x);
            if (groupList.size() >= 4) {
               int chrCount = 0;

               for(int i = 0; i < groupList.size(); ++i) {
                  SQLExpr item = (SQLExpr)groupList.get(i);
                  if (item instanceof SQLMethodInvokeExpr) {
                     SQLMethodInvokeExpr methodExpr = (SQLMethodInvokeExpr)item;
                     String methodName = methodExpr.getMethodName().toLowerCase();
                     if (("chr".equals(methodName) || "char".equals(methodName)) && methodExpr.getArguments().get(0) instanceof SQLLiteralExpr) {
                        ++chrCount;
                     }
                  } else if (item instanceof SQLCharExpr && ((SQLCharExpr)item).getText().length() > 5) {
                     chrCount = 0;
                     continue;
                  }

                  if (chrCount >= 4) {
                     addViolation(visitor, 2112, "evil concat", x);
                     break;
                  }
               }
            }
         }

         return true;
      } else {
         for(SQLExpr item : SQLBinaryOpExpr.split(x)) {
            item.accept(visitor);
         }

         return false;
      }
   }

   public static boolean check(WallVisitor visitor, SQLBinaryOpExprGroup x) {
      return true;
   }

   public static void check(WallVisitor visitor, SQLCreateTableStatement x) {
      String tableName = x.getName().getSimpleName();
      WallContext context = WallContext.current();
      if (context != null) {
         WallSqlTableStat tableStat = context.getTableStat(tableName);
         if (tableStat != null) {
            tableStat.incrementCreateCount();
         }
      }

   }

   public static void check(WallVisitor visitor, SQLAlterTableStatement x) {
      String tableName = x.getName().getSimpleName();
      WallContext context = WallContext.current();
      if (context != null) {
         WallSqlTableStat tableStat = context.getTableStat(tableName);
         if (tableStat != null) {
            tableStat.incrementAlterCount();
         }
      }

   }

   public static void check(WallVisitor visitor, SQLDropTableStatement x) {
      for(SQLTableSource item : x.getTableSources()) {
         if (item instanceof SQLExprTableSource) {
            SQLExpr expr = ((SQLExprTableSource)item).getExpr();
            String tableName = ((SQLName)expr).getSimpleName();
            WallContext context = WallContext.current();
            if (context != null) {
               WallSqlTableStat tableStat = context.getTableStat(tableName);
               if (tableStat != null) {
                  tableStat.incrementDropCount();
               }
            }
         }
      }

   }

   public static void check(WallVisitor visitor, SQLSelectItem x) {
      SQLExpr expr = x.getExpr();
      if (expr instanceof SQLVariantRefExpr && !isTopSelectItem(expr) && "@".equals(((SQLVariantRefExpr)expr).getName())) {
         addViolation(visitor, 2111, "@ not allow", x);
      }

      if (!visitor.getConfig().isSelectAllColumnAllow()) {
         if (expr instanceof SQLAllColumnExpr && x.getParent() instanceof SQLSelectQueryBlock) {
            SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock)x.getParent();
            SQLTableSource from = queryBlock.getFrom();
            if (from instanceof SQLExprTableSource) {
               addViolation(visitor, 1002, "'SELECT *' not allow", x);
            }
         }

      }
   }

   public static void check(WallVisitor visitor, SQLPropertyExpr x) {
      checkSchema(visitor, x.getOwner());
   }

   public static void checkInsert(WallVisitor visitor, SQLInsertInto x) {
      checkReadOnly(visitor, x.getTableSource());
      if (!visitor.getConfig().isInsertAllow()) {
         addViolation(visitor, 1004, "insert not allow", x);
      }

      checkInsertForMultiTenant(visitor, x);
   }

   public static void checkSelelct(WallVisitor visitor, SQLSelectQueryBlock x) {
      if (x.getInto() != null) {
         checkReadOnly(visitor, x.getInto());
      }

      if (!visitor.getConfig().isSelectIntoAllow() && x.getInto() != null) {
         addViolation(visitor, 1003, "select into not allow", x);
      } else {
         List<SQLCommentHint> hints = x.getHintsDirect();
         if (hints != null && x.getParent() instanceof SQLUnionQuery && x == ((SQLUnionQuery)x.getParent()).getRight()) {
            for(SQLCommentHint hint : hints) {
               String text = hint.getText();
               if (text.startsWith("!")) {
                  addViolation(visitor, 5000, "union select hint not allow", x);
                  return;
               }
            }
         }

         SQLExpr where = x.getWhere();
         if (where != null) {
            checkCondition(visitor, x.getWhere());
            Object whereValue = getConditionValue(visitor, where, visitor.getConfig().isSelectWhereAlwayTrueCheck());
            if (Boolean.TRUE == whereValue && visitor.getConfig().isSelectWhereAlwayTrueCheck() && visitor.isSqlEndOfComment() && !isSimpleConstExpr(where)) {
               addViolation(visitor, 2100, "select alway true condition not allow", x);
            }
         }

         checkSelectForMultiTenant(visitor, x);
      }
   }

   public static void checkHaving(WallVisitor visitor, SQLExpr x) {
      if (x != null) {
         if (Boolean.TRUE == getConditionValue(visitor, x, visitor.getConfig().isSelectHavingAlwayTrueCheck()) && visitor.getConfig().isSelectHavingAlwayTrueCheck() && visitor.isSqlEndOfComment() && !isSimpleConstExpr(x)) {
            addViolation(visitor, 2100, "having alway true condition not allow", x);
         }

      }
   }

   public static void checkDelete(WallVisitor visitor, SQLDeleteStatement x) {
      checkReadOnly(visitor, x.getTableSource());
      WallConfig config = visitor.getConfig();
      if (!config.isDeleteAllow()) {
         addViolation(visitor, 1004, "delete not allow", x);
      } else {
         boolean hasUsing = false;
         if (x instanceof MySqlDeleteStatement) {
            hasUsing = ((MySqlDeleteStatement)x).getUsing() != null;
         }

         boolean isJoinTableSource = x.getTableSource() instanceof SQLJoinTableSource;
         if (x.getWhere() == null && !hasUsing && !isJoinTableSource) {
            WallContext context = WallContext.current();
            if (context != null) {
               context.incrementDeleteNoneConditionWarnings();
            }

            if (config.isDeleteWhereNoneCheck()) {
               addViolation(visitor, 2104, "delete none condition not allow", x);
               return;
            }
         }

         SQLExpr where = x.getWhere();
         if (where != null) {
            checkCondition(visitor, where);
            if (Boolean.TRUE == getConditionValue(visitor, where, config.isDeleteWhereAlwayTrueCheck()) && config.isDeleteWhereAlwayTrueCheck() && visitor.isSqlEndOfComment() && !isSimpleConstExpr(where)) {
               addViolation(visitor, 2100, "delete alway true condition not allow", x);
            }
         }

      }
   }

   private static boolean isSimpleConstExpr(SQLExpr sqlExpr) {
      List<SQLExpr> parts = getParts(sqlExpr);
      if (parts.isEmpty()) {
         return false;
      } else {
         for(SQLExpr part : parts) {
            if (isFirst(part)) {
               Object evalValue = part.getAttribute("eval.value");
               if (evalValue == null) {
                  if (part instanceof SQLBooleanExpr) {
                     evalValue = ((SQLBooleanExpr)part).getBooleanValue();
                  } else if (part instanceof SQLNumericLiteralExpr) {
                     evalValue = ((SQLNumericLiteralExpr)part).getNumber();
                  } else if (part instanceof SQLCharExpr) {
                     evalValue = ((SQLCharExpr)part).getText();
                  } else if (part instanceof SQLNCharExpr) {
                     evalValue = ((SQLNCharExpr)part).getText();
                  }
               }

               Boolean result = SQLEvalVisitorUtils.castToBoolean(evalValue);
               if (result != null && result) {
                  return true;
               }
            }

            boolean isSimpleConstExpr = false;
            if (part != sqlExpr && !(part instanceof SQLLiteralExpr)) {
               if (part instanceof SQLBinaryOpExpr) {
                  SQLBinaryOpExpr binaryOpExpr = (SQLBinaryOpExpr)part;
                  if ((binaryOpExpr.getOperator() == SQLBinaryOperator.Equality || binaryOpExpr.getOperator() == SQLBinaryOperator.NotEqual || binaryOpExpr.getOperator() == SQLBinaryOperator.GreaterThan) && binaryOpExpr.getLeft() instanceof SQLIntegerExpr && binaryOpExpr.getRight() instanceof SQLIntegerExpr) {
                     isSimpleConstExpr = true;
                  }
               }
            } else {
               isSimpleConstExpr = true;
            }

            if (!isSimpleConstExpr) {
               return false;
            }
         }

         return true;
      }
   }

   private static void checkCondition(WallVisitor visitor, SQLExpr x) {
      if (x != null) {
         if (visitor.getConfig().isMustParameterized()) {
            ExportParameterVisitor exportParameterVisitor = visitor.getProvider().createExportParameterVisitor();
            x.accept(exportParameterVisitor);
            if (exportParameterVisitor.getParameters().size() > 0) {
               addViolation(visitor, 2200, "sql must parameterized", x);
            }
         }

      }
   }

   private static void checkJoinSelectForMultiTenant(WallVisitor visitor, SQLJoinTableSource join, SQLSelectQueryBlock x) {
      WallConfig.TenantCallBack tenantCallBack = visitor.getConfig().getTenantCallBack();
      String tenantTablePattern = visitor.getConfig().getTenantTablePattern();
      if (tenantCallBack != null || tenantTablePattern != null && tenantTablePattern.length() != 0) {
         SQLTableSource right = join.getRight();
         if (right instanceof SQLExprTableSource) {
            SQLExpr tableExpr = ((SQLExprTableSource)right).getExpr();
            if (tableExpr instanceof SQLIdentifierExpr) {
               String tableName = ((SQLIdentifierExpr)tableExpr).getName();
               String alias = null;
               String tenantColumn = null;
               if (tenantCallBack != null) {
                  tenantColumn = tenantCallBack.getTenantColumn(WallConfig.TenantCallBack.StatementType.SELECT, tableName);
               }

               if (StringUtils.isEmpty(tenantColumn) && ServletPathMatcher.getInstance().matches(tenantTablePattern, tableName)) {
                  tenantColumn = visitor.getConfig().getTenantColumn();
               }

               if (!StringUtils.isEmpty(tenantColumn)) {
                  alias = right.getAlias();
                  if (alias == null) {
                     alias = tableName;
                  }

                  SQLExpr item = null;
                  if (alias != null) {
                     item = new SQLPropertyExpr(new SQLIdentifierExpr(alias), tenantColumn);
                  } else {
                     item = new SQLIdentifierExpr(tenantColumn);
                  }

                  SQLSelectItem selectItem = new SQLSelectItem(item);
                  x.getSelectList().add(selectItem);
                  visitor.setSqlModified(true);
               }
            }
         }

      }
   }

   private static boolean isSelectStatmentForMultiTenant(SQLSelectQueryBlock queryBlock) {
      SQLObject parent;
      for(parent = queryBlock.getParent(); parent != null && parent instanceof SQLUnionQuery; parent = parent.getParent()) {
      }

      if (!(parent instanceof SQLSelect)) {
         return false;
      } else {
         parent = ((SQLSelect)parent).getParent();
         return parent instanceof SQLSelectStatement;
      }
   }

   private static void checkSelectForMultiTenant(WallVisitor visitor, SQLSelectQueryBlock x) {
      WallConfig.TenantCallBack tenantCallBack = visitor.getConfig().getTenantCallBack();
      String tenantTablePattern = visitor.getConfig().getTenantTablePattern();
      if (tenantCallBack != null || tenantTablePattern != null && tenantTablePattern.length() != 0) {
         if (x == null) {
            throw new IllegalStateException("x is null");
         } else if (isSelectStatmentForMultiTenant(x)) {
            SQLTableSource tableSource = x.getFrom();
            String alias = null;
            String matchTableName = null;
            String tenantColumn = null;
            if (tableSource instanceof SQLExprTableSource) {
               SQLExpr tableExpr = ((SQLExprTableSource)tableSource).getExpr();
               if (tableExpr instanceof SQLIdentifierExpr) {
                  String tableName = ((SQLIdentifierExpr)tableExpr).getName();
                  if (tenantCallBack != null) {
                     tenantColumn = tenantCallBack.getTenantColumn(WallConfig.TenantCallBack.StatementType.SELECT, tableName);
                  }

                  if (StringUtils.isEmpty(tenantColumn) && ServletPathMatcher.getInstance().matches(tenantTablePattern, tableName)) {
                     tenantColumn = visitor.getConfig().getTenantColumn();
                  }

                  if (!StringUtils.isEmpty(tenantColumn)) {
                     matchTableName = tableName;
                     alias = tableSource.getAlias();
                  }
               }
            } else if (tableSource instanceof SQLJoinTableSource) {
               SQLJoinTableSource join = (SQLJoinTableSource)tableSource;
               if (join.getLeft() instanceof SQLExprTableSource) {
                  SQLExpr tableExpr = ((SQLExprTableSource)join.getLeft()).getExpr();
                  if (tableExpr instanceof SQLIdentifierExpr) {
                     String tableName = ((SQLIdentifierExpr)tableExpr).getName();
                     if (tenantCallBack != null) {
                        tenantColumn = tenantCallBack.getTenantColumn(WallConfig.TenantCallBack.StatementType.SELECT, tableName);
                     }

                     if (StringUtils.isEmpty(tenantColumn) && ServletPathMatcher.getInstance().matches(tenantTablePattern, tableName)) {
                        tenantColumn = visitor.getConfig().getTenantColumn();
                     }

                     if (!StringUtils.isEmpty(tenantColumn)) {
                        matchTableName = tableName;
                        alias = join.getLeft().getAlias();
                        if (alias == null) {
                           alias = tableName;
                        }
                     }
                  }

                  checkJoinSelectForMultiTenant(visitor, join, x);
               } else {
                  checkJoinSelectForMultiTenant(visitor, join, x);
               }
            }

            if (matchTableName != null) {
               SQLExpr item = null;
               if (alias != null) {
                  item = new SQLPropertyExpr(new SQLIdentifierExpr(alias), tenantColumn);
               } else {
                  item = new SQLIdentifierExpr(tenantColumn);
               }

               SQLSelectItem selectItem = new SQLSelectItem(item);
               x.getSelectList().add(selectItem);
               visitor.setSqlModified(true);
            }
         }
      }
   }

   private static void checkUpdateForMultiTenant(WallVisitor visitor, SQLUpdateStatement x) {
      WallConfig.TenantCallBack tenantCallBack = visitor.getConfig().getTenantCallBack();
      String tenantTablePattern = visitor.getConfig().getTenantTablePattern();
      if (tenantCallBack != null || tenantTablePattern != null && tenantTablePattern.length() != 0) {
         if (x == null) {
            throw new IllegalStateException("x is null");
         } else {
            SQLTableSource tableSource = x.getTableSource();
            String alias = null;
            String matchTableName = null;
            String tenantColumn = null;
            if (tableSource instanceof SQLExprTableSource) {
               SQLExpr tableExpr = ((SQLExprTableSource)tableSource).getExpr();
               if (tableExpr instanceof SQLIdentifierExpr) {
                  String tableName = ((SQLIdentifierExpr)tableExpr).getName();
                  if (tenantCallBack != null) {
                     tenantColumn = tenantCallBack.getTenantColumn(WallConfig.TenantCallBack.StatementType.UPDATE, tableName);
                  }

                  if (StringUtils.isEmpty(tenantColumn) && ServletPathMatcher.getInstance().matches(tenantTablePattern, tableName)) {
                     tenantColumn = visitor.getConfig().getTenantColumn();
                  }

                  if (!StringUtils.isEmpty(tenantColumn)) {
                     matchTableName = tableName;
                     alias = tableSource.getAlias();
                  }
               }
            }

            if (matchTableName != null) {
               SQLExpr item = null;
               if (alias != null) {
                  item = new SQLPropertyExpr(new SQLIdentifierExpr(alias), tenantColumn);
               } else {
                  item = new SQLIdentifierExpr(tenantColumn);
               }

               SQLExpr value = generateTenantValue(visitor, alias, WallConfig.TenantCallBack.StatementType.UPDATE, matchTableName);
               SQLUpdateSetItem updateSetItem = new SQLUpdateSetItem();
               updateSetItem.setColumn(item);
               updateSetItem.setValue(value);
               x.addItem(updateSetItem);
               visitor.setSqlModified(true);
            }
         }
      }
   }

   private static void checkInsertForMultiTenant(WallVisitor visitor, SQLInsertInto x) {
      WallConfig.TenantCallBack tenantCallBack = visitor.getConfig().getTenantCallBack();
      String tenantTablePattern = visitor.getConfig().getTenantTablePattern();
      if (tenantCallBack != null || tenantTablePattern != null && tenantTablePattern.length() != 0) {
         if (x == null) {
            throw new IllegalStateException("x is null");
         } else {
            SQLExprTableSource tableSource = x.getTableSource();
            String alias = null;
            String matchTableName = null;
            String tenantColumn = null;
            SQLExpr tableExpr = tableSource.getExpr();
            if (tableExpr instanceof SQLIdentifierExpr) {
               String tableName = ((SQLIdentifierExpr)tableExpr).getName();
               if (tenantCallBack != null) {
                  tenantColumn = tenantCallBack.getTenantColumn(WallConfig.TenantCallBack.StatementType.INSERT, tableName);
               }

               if (StringUtils.isEmpty(tenantColumn) && ServletPathMatcher.getInstance().matches(tenantTablePattern, tableName)) {
                  tenantColumn = visitor.getConfig().getTenantColumn();
               }

               if (!StringUtils.isEmpty(tenantColumn)) {
                  matchTableName = tableName;
                  alias = tableSource.getAlias();
               }
            }

            if (matchTableName != null) {
               SQLExpr item = null;
               if (alias != null) {
                  item = new SQLPropertyExpr(new SQLIdentifierExpr(alias), tenantColumn);
               } else {
                  item = new SQLIdentifierExpr(tenantColumn);
               }

               SQLExpr value = generateTenantValue(visitor, alias, WallConfig.TenantCallBack.StatementType.INSERT, matchTableName);
               x.getColumns().add(item);
               List<SQLInsertStatement.ValuesClause> valuesClauses = null;
               SQLInsertStatement.ValuesClause valuesClause = null;
               if (x instanceof MySqlInsertStatement) {
                  valuesClauses = ((MySqlInsertStatement)x).getValuesList();
               } else {
                  valuesClause = x.getValues();
               }

               if (valuesClauses != null && valuesClauses.size() > 0) {
                  for(SQLInsertStatement.ValuesClause clause : valuesClauses) {
                     clause.addValue(value);
                  }
               }

               if (valuesClause != null) {
                  valuesClause.addValue(value);
               }

               SQLSelect select = x.getQuery();
               if (select != null) {
                  for(SQLSelectQueryBlock queryBlock : splitSQLSelectQuery(select.getQuery())) {
                     queryBlock.getSelectList().add(new SQLSelectItem(value));
                  }
               }

               visitor.setSqlModified(true);
            }
         }
      }
   }

   private static List<SQLSelectQueryBlock> splitSQLSelectQuery(SQLSelectQuery x) {
      List<SQLSelectQueryBlock> groupList = new ArrayList();
      Stack<SQLSelectQuery> stack = new Stack();
      stack.push(x);

      do {
         SQLSelectQuery query = (SQLSelectQuery)stack.pop();
         if (query instanceof SQLSelectQueryBlock) {
            groupList.add((SQLSelectQueryBlock)query);
         } else if (query instanceof SQLUnionQuery) {
            SQLUnionQuery unionQuery = (SQLUnionQuery)query;
            stack.push(unionQuery.getLeft());
            stack.push(unionQuery.getRight());
         }
      } while(!stack.empty());

      return groupList;
   }

   /** @deprecated */
   @Deprecated
   public static void checkConditionForMultiTenant(WallVisitor visitor, SQLExpr x, SQLObject parent) {
      String tenantTablePattern = visitor.getConfig().getTenantTablePattern();
      if (tenantTablePattern != null && tenantTablePattern.length() != 0) {
         if (parent == null) {
            throw new IllegalStateException("parent is null");
         } else {
            String alias = null;
            WallConfig.TenantCallBack.StatementType statementType = null;
            SQLTableSource tableSource;
            if (parent instanceof SQLDeleteStatement) {
               tableSource = ((SQLDeleteStatement)parent).getTableSource();
               statementType = WallConfig.TenantCallBack.StatementType.DELETE;
            } else if (parent instanceof SQLUpdateStatement) {
               tableSource = ((SQLUpdateStatement)parent).getTableSource();
               statementType = WallConfig.TenantCallBack.StatementType.UPDATE;
            } else {
               if (!(parent instanceof SQLSelectQueryBlock)) {
                  throw new IllegalStateException("not support parent : " + parent.getClass());
               }

               tableSource = ((SQLSelectQueryBlock)parent).getFrom();
               statementType = WallConfig.TenantCallBack.StatementType.SELECT;
            }

            String matchTableName = null;
            if (tableSource instanceof SQLExprTableSource) {
               SQLExpr tableExpr = ((SQLExprTableSource)tableSource).getExpr();
               if (tableExpr instanceof SQLIdentifierExpr) {
                  String tableName = ((SQLIdentifierExpr)tableExpr).getName();
                  if (ServletPathMatcher.getInstance().matches(tenantTablePattern, tableName)) {
                     matchTableName = tableName;
                     alias = tableSource.getAlias();
                  }
               }
            } else if (tableSource instanceof SQLJoinTableSource) {
               SQLJoinTableSource join = (SQLJoinTableSource)tableSource;
               if (join.getLeft() instanceof SQLExprTableSource) {
                  SQLExpr tableExpr = ((SQLExprTableSource)join.getLeft()).getExpr();
                  if (tableExpr instanceof SQLIdentifierExpr) {
                     String tableName = ((SQLIdentifierExpr)tableExpr).getName();
                     if (ServletPathMatcher.getInstance().matches(tenantTablePattern, tableName)) {
                        matchTableName = tableName;
                        alias = join.getLeft().getAlias();
                     }
                  }

                  checkJoinConditionForMultiTenant(visitor, join, false, statementType);
               } else {
                  checkJoinConditionForMultiTenant(visitor, join, true, statementType);
               }
            }

            if (matchTableName != null) {
               SQLBinaryOpExpr tenantCondition = createTenantCondition(visitor, alias, statementType, matchTableName);
               SQLExpr condition;
               if (x == null) {
                  condition = tenantCondition;
               } else {
                  condition = new SQLBinaryOpExpr(tenantCondition, SQLBinaryOperator.BooleanAnd, x);
               }

               if (parent instanceof SQLDeleteStatement) {
                  SQLDeleteStatement deleteStmt = (SQLDeleteStatement)parent;
                  deleteStmt.setWhere(condition);
                  visitor.setSqlModified(true);
               } else if (parent instanceof SQLUpdateStatement) {
                  SQLUpdateStatement updateStmt = (SQLUpdateStatement)parent;
                  updateStmt.setWhere(condition);
                  visitor.setSqlModified(true);
               } else if (parent instanceof SQLSelectQueryBlock) {
                  SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock)parent;
                  queryBlock.setWhere(condition);
                  visitor.setSqlModified(true);
               }

            }
         }
      }
   }

   /** @deprecated */
   @Deprecated
   public static void checkJoinConditionForMultiTenant(WallVisitor visitor, SQLJoinTableSource join, boolean checkLeft, WallConfig.TenantCallBack.StatementType statementType) {
      String tenantTablePattern = visitor.getConfig().getTenantTablePattern();
      if (tenantTablePattern != null && tenantTablePattern.length() != 0) {
         SQLExpr condition = join.getCondition();
         SQLTableSource right = join.getRight();
         if (right instanceof SQLExprTableSource) {
            SQLExpr tableExpr = ((SQLExprTableSource)right).getExpr();
            if (tableExpr instanceof SQLIdentifierExpr) {
               String tableName = ((SQLIdentifierExpr)tableExpr).getName();
               if (ServletPathMatcher.getInstance().matches(tenantTablePattern, tableName)) {
                  String alias = right.getAlias();
                  if (alias == null) {
                     alias = tableName;
                  }

                  SQLBinaryOpExpr tenantCondition = createTenantCondition(visitor, alias, statementType, tableName);
                  if (condition == null) {
                     condition = tenantCondition;
                  } else {
                     condition = new SQLBinaryOpExpr(tenantCondition, SQLBinaryOperator.BooleanAnd, condition);
                  }
               }
            }
         }

         if (condition != join.getCondition()) {
            join.setCondition(condition);
            visitor.setSqlModified(true);
         }

      }
   }

   /** @deprecated */
   @Deprecated
   private static SQLBinaryOpExpr createTenantCondition(WallVisitor visitor, String alias, WallConfig.TenantCallBack.StatementType statementType, String tableName) {
      SQLExpr left;
      if (alias != null) {
         left = new SQLPropertyExpr(new SQLIdentifierExpr(alias), visitor.getConfig().getTenantColumn());
      } else {
         left = new SQLIdentifierExpr(visitor.getConfig().getTenantColumn());
      }

      SQLExpr right = generateTenantValue(visitor, alias, statementType, tableName);
      SQLBinaryOpExpr tenantCondition = new SQLBinaryOpExpr(left, SQLBinaryOperator.Equality, right);
      return tenantCondition;
   }

   private static SQLExpr generateTenantValue(WallVisitor visitor, String alias, WallConfig.TenantCallBack.StatementType statementType, String tableName) {
      WallConfig.TenantCallBack callBack = visitor.getConfig().getTenantCallBack();
      if (callBack != null) {
         WallProvider.setTenantValue(callBack.getTenantValue(statementType, tableName));
      }

      Object tenantValue = WallProvider.getTenantValue();
      SQLExpr value;
      if (tenantValue instanceof Number) {
         value = new SQLNumberExpr((Number)tenantValue);
      } else {
         if (!(tenantValue instanceof String)) {
            throw new IllegalStateException("tenant value not support type " + tenantValue);
         }

         value = new SQLCharExpr((String)tenantValue);
      }

      return value;
   }

   public static void checkReadOnly(WallVisitor visitor, SQLTableSource tableSource) {
      if (tableSource instanceof SQLExprTableSource) {
         String tableName = null;
         SQLExpr tableNameExpr = ((SQLExprTableSource)tableSource).getExpr();
         if (tableNameExpr instanceof SQLName) {
            tableName = ((SQLName)tableNameExpr).getSimpleName();
         }

         boolean readOnlyValid = visitor.getProvider().checkReadOnlyTable(tableName);
         if (!readOnlyValid) {
            addViolation(visitor, 4000, "table readonly : " + tableName, tableSource);
         }
      } else if (tableSource instanceof SQLJoinTableSource) {
         SQLJoinTableSource join = (SQLJoinTableSource)tableSource;
         checkReadOnly(visitor, join.getLeft());
         checkReadOnly(visitor, join.getRight());
      }

   }

   public static void checkUpdate(WallVisitor visitor, SQLUpdateStatement x) {
      checkReadOnly(visitor, x.getTableSource());
      WallConfig config = visitor.getConfig();
      if (!config.isUpdateAllow()) {
         addViolation(visitor, 1006, "update not allow", x);
      } else {
         SQLExpr where = x.getWhere();
         if (where == null) {
            WallContext context = WallContext.current();
            if (context != null) {
               context.incrementUpdateNoneConditionWarnings();
            }

            if (config.isUpdateWhereNoneCheck()) {
               if (!(x instanceof MySqlUpdateStatement)) {
                  addViolation(visitor, 2104, "update none condition not allow", x);
                  return;
               }

               MySqlUpdateStatement mysqlUpdate = (MySqlUpdateStatement)x;
               if (mysqlUpdate.getLimit() == null) {
                  addViolation(visitor, 2104, "update none condition not allow", x);
                  return;
               }
            }
         } else {
            checkCondition(visitor, where);
            if (Boolean.TRUE == getConditionValue(visitor, where, config.isUpdateWhereAlayTrueCheck()) && config.isUpdateWhereAlayTrueCheck() && visitor.isSqlEndOfComment() && !isSimpleConstExpr(where)) {
               addViolation(visitor, 2100, "update alway true condition not allow", x);
            }

            SQLName table = x.getTableName();
            if (table == null) {
               return;
            }

            String tableName = table.getSimpleName();
            Set<String> updateCheckColumns = config.getUpdateCheckTable(tableName);
            boolean isUpdateCheckTable = updateCheckColumns != null && !updateCheckColumns.isEmpty();
            WallUpdateCheckHandler updateCheckHandler = config.getUpdateCheckHandler();
            if (isUpdateCheckTable && updateCheckHandler != null) {
               String checkColumn = (String)updateCheckColumns.iterator().next();
               SQLExpr valueExpr = null;

               for(SQLUpdateSetItem item : x.getItems()) {
                  if (item.columnMatch(checkColumn)) {
                     valueExpr = item.getValue();
                     break;
                  }
               }

               if (valueExpr != null) {
                  List<SQLExpr> conditions;
                  if (where instanceof SQLBinaryOpExpr) {
                     conditions = SQLBinaryOpExpr.split((SQLBinaryOpExpr)where, SQLBinaryOperator.BooleanAnd);
                  } else if (where instanceof SQLBinaryOpExprGroup) {
                     conditions = new ArrayList();

                     for(SQLExpr each : ((SQLBinaryOpExprGroup)where).getItems()) {
                        if (each instanceof SQLBinaryOpExpr) {
                           conditions.addAll(SQLBinaryOpExpr.split((SQLBinaryOpExpr)each, SQLBinaryOperator.BooleanAnd));
                        } else if (each instanceof SQLInListExpr) {
                           conditions.add(each);
                        }
                     }
                  } else {
                     conditions = new ArrayList();
                     conditions.add(where);
                  }

                  List<SQLExpr> filterValueExprList = new ArrayList();

                  for(SQLExpr condition : conditions) {
                     if (condition instanceof SQLBinaryOpExpr) {
                        SQLBinaryOpExpr binaryCondition = (SQLBinaryOpExpr)condition;
                        if (binaryCondition.getOperator() == SQLBinaryOperator.Equality && binaryCondition.conditionContainsColumn(checkColumn)) {
                           SQLExpr left = binaryCondition.getLeft();
                           SQLExpr right = binaryCondition.getRight();
                           if (!(left instanceof SQLValuableExpr) && !(left instanceof SQLVariantRefExpr)) {
                              if (right instanceof SQLValuableExpr || right instanceof SQLVariantRefExpr) {
                                 filterValueExprList.add(right);
                              }
                           } else {
                              filterValueExprList.add(left);
                           }
                        }
                     } else if (condition instanceof SQLInListExpr) {
                        SQLInListExpr listExpr = (SQLInListExpr)condition;
                        if (listExpr.getExpr() instanceof SQLIdentifierExpr) {
                           SQLIdentifierExpr nameExpr = (SQLIdentifierExpr)listExpr.getExpr();
                           if (nameExpr.getName().equals(checkColumn)) {
                              filterValueExprList.addAll(((SQLInListExpr)condition).getTargetList());
                           }
                        }
                     }
                  }

                  boolean allValue = valueExpr instanceof SQLValuableExpr;
                  if (allValue) {
                     for(SQLExpr filterValue : filterValueExprList) {
                        if (!(filterValue instanceof SQLValuableExpr)) {
                           allValue = false;
                           break;
                        }
                     }
                  }

                  if (allValue) {
                     Object setValue = ((SQLValuableExpr)valueExpr).getValue();
                     List<Object> filterValues = new ArrayList(filterValueExprList.size());

                     for(SQLExpr expr : filterValueExprList) {
                        filterValues.add(((SQLValuableExpr)expr).getValue());
                     }

                     List<Object> var30 = new ArrayList(new HashSet(filterValues));
                     boolean validate = updateCheckHandler.check(tableName, checkColumn, setValue, var30);
                     if (!validate) {
                        visitor.addViolation(new IllegalSQLObjectViolation(9000, "update check failed.", visitor.toSQL(x)));
                     }
                  } else {
                     visitor.addWallUpdateCheckItem(new WallUpdateCheckItem(tableName, checkColumn, valueExpr, filterValueExprList));
                  }
               }
            }
         }

         checkUpdateForMultiTenant(visitor, x);
      }
   }

   public static Object getValue(WallVisitor visitor, SQLBinaryOpExprGroup x) {
      List<SQLExpr> groupList = x.getItems();
      if (x.getOperator() == SQLBinaryOperator.BooleanOr) {
         return getValue_or(visitor, groupList);
      } else {
         return x.getOperator() == SQLBinaryOperator.BooleanAnd ? getValue_and(visitor, groupList) : null;
      }
   }

   public static Object getValue(WallVisitor visitor, SQLBinaryOpExpr x) {
      if (x.getOperator() == SQLBinaryOperator.BooleanOr) {
         List<SQLExpr> groupList = SQLBinaryOpExpr.split(x);
         return getValue_or(visitor, groupList);
      } else if (x.getOperator() == SQLBinaryOperator.BooleanAnd) {
         List<SQLExpr> groupList = SQLBinaryOpExpr.split(x);
         return getValue_and(visitor, groupList);
      } else {
         boolean checkCondition = visitor != null && (!visitor.getConfig().isConstArithmeticAllow() || !visitor.getConfig().isConditionOpBitwseAllow() || !visitor.getConfig().isConditionOpXorAllow());
         if (x.getLeft() instanceof SQLName) {
            if (x.getRight() instanceof SQLName) {
               if (x.getLeft().toString().equalsIgnoreCase(x.getRight().toString())) {
                  switch (x.getOperator()) {
                     case Equality:
                     case Like:
                        return Boolean.TRUE;
                     case NotEqual:
                     case GreaterThan:
                     case GreaterThanOrEqual:
                     case LessThan:
                     case LessThanOrEqual:
                     case LessThanOrGreater:
                     case NotLike:
                        return Boolean.FALSE;
                  }
               }
            } else if (!checkCondition) {
               switch (x.getOperator()) {
                  case Equality:
                  case NotEqual:
                  case GreaterThan:
                  case GreaterThanOrEqual:
                  case LessThan:
                  case LessThanOrEqual:
                  case LessThanOrGreater:
                     return null;
                  case Like:
               }
            }
         }

         if (x.getLeft() instanceof SQLValuableExpr && x.getRight() instanceof SQLValuableExpr) {
            Object leftValue = ((SQLValuableExpr)x.getLeft()).getValue();
            Object rightValue = ((SQLValuableExpr)x.getRight()).getValue();
            if (x.getOperator() == SQLBinaryOperator.Equality) {
               boolean evalValue = SQLEvalVisitorUtils.eq(leftValue, rightValue);
               x.putAttribute("eval.value", evalValue);
               return evalValue;
            }

            if (x.getOperator() == SQLBinaryOperator.NotEqual) {
               boolean evalValue = SQLEvalVisitorUtils.eq(leftValue, rightValue);
               x.putAttribute("eval.value", !evalValue);
               return !evalValue;
            }
         }

         Object leftResult = getValue(visitor, x.getLeft());
         Object rightResult = getValue(visitor, x.getRight());
         if (x.getOperator() == SQLBinaryOperator.Like && leftResult instanceof String && leftResult.equals(rightResult)) {
            addViolation(visitor, 2108, "same const like", x);
         }

         if (x.getOperator() == SQLBinaryOperator.Like || x.getOperator() == SQLBinaryOperator.NotLike) {
            WallContext context = WallContext.current();
            if (context != null && (rightResult instanceof Number || leftResult instanceof Number)) {
               context.incrementLikeNumberWarnings();
            }
         }

         DbType dbType = null;
         WallContext wallContext = WallContext.current();
         if (wallContext != null) {
            dbType = wallContext.getDbType();
         }

         return eval(visitor, dbType, x, Collections.emptyList());
      }
   }

   private static Object getValue_or(WallVisitor visitor, List<SQLExpr> groupList) {
      boolean allFalse = true;

      for(int i = groupList.size() - 1; i >= 0; --i) {
         SQLExpr item = (SQLExpr)groupList.get(i);
         Object result = getValue(visitor, item);
         Boolean booleanVal = SQLEvalVisitorUtils.castToBoolean(result);
         if (booleanVal != null && booleanVal) {
            WallConditionContext wallContext = getWallConditionContext();
            if (wallContext != null && !isFirst(item)) {
               wallContext.setPartAlwayTrue(true);
            }

            return true;
         }

         if (booleanVal == null) {
            allFalse = false;
            break;
         }
      }

      return allFalse ? false : null;
   }

   private static Object getValue_and(WallVisitor visitor, List<SQLExpr> groupList) {
      int dalConst = 0;
      Boolean allTrue = Boolean.TRUE;

      for(int i = groupList.size() - 1; i >= 0; --i) {
         SQLExpr item = (SQLExpr)groupList.get(i);
         Object result = getValue(visitor, item);
         Boolean booleanVal = SQLEvalVisitorUtils.castToBoolean(result);
         if (Boolean.TRUE == booleanVal) {
            WallConditionContext wallContext = getWallConditionContext();
            if (wallContext != null && !isFirst(item)) {
               wallContext.setPartAlwayTrue(true);
            }

            ++dalConst;
         } else if (Boolean.FALSE == booleanVal) {
            WallConditionContext wallContext = getWallConditionContext();
            if (wallContext != null && !isFirst(item)) {
               wallContext.setPartAlwayFalse(true);
            }

            allTrue = Boolean.FALSE;
            ++dalConst;
         } else {
            if (allTrue != Boolean.FALSE) {
               allTrue = null;
            }

            dalConst = 0;
         }

         if (dalConst == 2 && visitor != null && !visitor.getConfig().isConditionDoubleConstAllow()) {
            addViolation(visitor, 2107, "double const condition", item);
         }
      }

      if (Boolean.TRUE == allTrue) {
         return true;
      } else if (Boolean.FALSE == allTrue) {
         return false;
      } else {
         return null;
      }
   }

   public static SQLExpr getFirst(SQLExpr x) {
      if (x instanceof SQLBinaryOpExpr) {
         SQLBinaryOpExpr binary = (SQLBinaryOpExpr)x;
         if (binary.getOperator() == SQLBinaryOperator.BooleanAnd || binary.getOperator() == SQLBinaryOperator.BooleanOr) {
            return getFirst(((SQLBinaryOpExpr)x).getLeft());
         }
      }

      return x;
   }

   public static List<SQLExpr> getParts(SQLExpr x) {
      List<SQLExpr> exprs = new ArrayList();
      exprs.add(x);

      while(true) {
         List<SQLExpr> tmp = partExpr(exprs);
         if (tmp.size() == exprs.size()) {
            return exprs;
         }

         exprs = tmp;
      }
   }

   public static List<SQLExpr> partExpr(List<SQLExpr> exprs) {
      List<SQLExpr> partList = new ArrayList();

      for(SQLExpr x : exprs) {
         if (x instanceof SQLBinaryOpExpr) {
            SQLBinaryOpExpr binary = (SQLBinaryOpExpr)x;
            if (binary.getOperator() == SQLBinaryOperator.BooleanAnd || binary.getOperator() == SQLBinaryOperator.BooleanOr) {
               partList.add(((SQLBinaryOpExpr)x).getLeft());
               partList.add(((SQLBinaryOpExpr)x).getRight());
               continue;
            }
         }

         partList.add(x);
      }

      return partList;
   }

   public static boolean isFirst(SQLObject x) {
      if (x == null) {
         return true;
      } else {
         while(true) {
            SQLObject parent = x.getParent();
            if (!(parent instanceof SQLExpr)) {
               return true;
            }

            if (parent instanceof SQLBinaryOpExprGroup && x != ((SQLBinaryOpExprGroup)parent).getItems().get(0)) {
               return false;
            }

            if (parent instanceof SQLBinaryOpExpr) {
               SQLBinaryOpExpr binaryExpr = (SQLBinaryOpExpr)parent;
               if (x == binaryExpr.getRight()) {
                  return false;
               }
            }

            x = parent;
         }
      }
   }

   private static boolean hasWhere(SQLSelectQuery selectQuery) {
      if (selectQuery instanceof SQLSelectQueryBlock) {
         return ((SQLSelectQueryBlock)selectQuery).getWhere() != null;
      } else if (!(selectQuery instanceof SQLUnionQuery)) {
         return false;
      } else {
         SQLUnionQuery union = (SQLUnionQuery)selectQuery;
         return hasWhere(union.getLeft()) || hasWhere(union.getRight());
      }
   }

   public static boolean checkSqlExpr(SQLExpr x) {
      if (x == null) {
         return false;
      } else {
         SQLObject obj = x;

         while(true) {
            SQLObject parent = obj.getParent();
            if (parent == null) {
               return false;
            }

            if (parent instanceof SQLSelectGroupByClause) {
               return true;
            }

            if (parent instanceof SQLOrderBy) {
               return true;
            }

            if (parent instanceof SQLLimit) {
               return true;
            }

            if (parent instanceof MySqlOrderingExpr) {
               return true;
            }

            obj = parent;
         }
      }
   }

   public static boolean isWhereOrHaving(SQLObject x) {
      if (x == null) {
         return false;
      } else {
         while(true) {
            SQLObject parent = x.getParent();
            if (parent == null) {
               return false;
            }

            if (parent instanceof SQLJoinTableSource) {
               SQLJoinTableSource joinTableSource = (SQLJoinTableSource)parent;
               if (joinTableSource.getCondition() == x) {
                  return true;
               }
            }

            if (parent instanceof SQLUnionQuery) {
               SQLUnionQuery union = (SQLUnionQuery)parent;
               if (union.getRight() == x && hasWhere(union.getLeft())) {
                  return true;
               }
            }

            if (parent instanceof SQLSelectQueryBlock) {
               SQLSelectQueryBlock query = (SQLSelectQueryBlock)parent;
               if (query.getWhere() == x) {
                  return true;
               }
            }

            if (parent instanceof SQLDeleteStatement) {
               SQLDeleteStatement delete = (SQLDeleteStatement)parent;
               if (delete.getWhere() == x) {
                  return true;
               }

               return false;
            }

            if (parent instanceof SQLUpdateStatement) {
               SQLUpdateStatement update = (SQLUpdateStatement)parent;
               if (update.getWhere() == x) {
                  return true;
               }

               return false;
            }

            if (parent instanceof SQLSelectGroupByClause) {
               SQLSelectGroupByClause groupBy = (SQLSelectGroupByClause)parent;
               if (x == groupBy.getHaving()) {
                  return true;
               }

               return false;
            }

            x = parent;
         }
      }
   }

   public static WallConditionContext getWallConditionContext() {
      return (WallConditionContext)wallConditionContextLocal.get();
   }

   public static WallTopStatementContext getWallTopStatementContext() {
      return (WallTopStatementContext)wallTopStatementContextLocal.get();
   }

   public static void clearWallTopStatementContext() {
      wallTopStatementContextLocal.set(null);
   }

   public static void initWallTopStatementContext() {
      wallTopStatementContextLocal.set(new WallTopStatementContext());
   }

   public static Object getConditionValue(WallVisitor visitor, SQLExpr x, boolean alwayTrueCheck) {
      WallConditionContext old = (WallConditionContext)wallConditionContextLocal.get();

      Object var7;
      try {
         wallConditionContextLocal.set(new WallConditionContext());
         Object value = getValue(visitor, x);
         WallConditionContext current = (WallConditionContext)wallConditionContextLocal.get();
         WallContext context = WallContext.current();
         if (context != null && (current.hasPartAlwayTrue() || Boolean.TRUE == value) && !isFirst(x)) {
            context.incrementWarnings();
         }

         if (current.hasPartAlwayTrue() && !visitor.getConfig().isConditionAndAlwayTrueAllow()) {
            addViolation(visitor, 2100, "part alway true condition not allow", x);
         }

         if (current.hasPartAlwayFalse() && !visitor.getConfig().isConditionAndAlwayFalseAllow()) {
            addViolation(visitor, 2113, "part alway false condition not allow", x);
         }

         if (current.hasConstArithmetic() && !visitor.getConfig().isConstArithmeticAllow()) {
            addViolation(visitor, 2101, "const arithmetic not allow", x);
         }

         if (current.hasXor() && !visitor.getConfig().isConditionOpXorAllow()) {
            addViolation(visitor, 2102, "xor not allow", x);
         }

         if (current.hasBitwise() && !visitor.getConfig().isConditionOpBitwseAllow()) {
            addViolation(visitor, 2103, "bitwise operator not allow", x);
         }

         var7 = value;
      } finally {
         wallConditionContextLocal.set(old);
      }

      return var7;
   }

   public static Object getValueFromAttributes(WallVisitor visitor, SQLObject sqlObject) {
      if (sqlObject == null) {
         return null;
      } else {
         return visitor != null && visitor.getConfig().isConditionLikeTrueAllow() && sqlObject.getAttributes().containsKey("hasTrueLike") ? null : sqlObject.getAttribute("eval.value");
      }
   }

   public static Object getValue(SQLExpr x) {
      return getValue((WallVisitor)null, (SQLExpr)x);
   }

   public static Object getValue(WallVisitor visitor, SQLExpr x) {
      if (x != null && x.containsAttribute("eval.value")) {
         return getValueFromAttributes(visitor, x);
      } else if (x instanceof SQLBinaryOpExpr) {
         return getValue(visitor, (SQLBinaryOpExpr)x);
      } else if (x instanceof SQLBinaryOpExprGroup) {
         return getValue(visitor, (SQLBinaryOpExprGroup)x);
      } else if (x instanceof SQLBooleanExpr) {
         return ((SQLBooleanExpr)x).getBooleanValue();
      } else if (x instanceof SQLNumericLiteralExpr) {
         return ((SQLNumericLiteralExpr)x).getNumber();
      } else if (x instanceof SQLCharExpr) {
         return ((SQLCharExpr)x).getText();
      } else if (x instanceof SQLNCharExpr) {
         return ((SQLNCharExpr)x).getText();
      } else {
         if (x instanceof SQLNotExpr) {
            Object result = getValue(visitor, ((SQLNotExpr)x).getExpr());
            if (result instanceof Boolean) {
               return !(Boolean)result;
            }
         }

         if (x instanceof SQLQueryExpr) {
            if (isSimpleCountTableSource(visitor, ((SQLQueryExpr)x).getSubQuery())) {
               return 1;
            }

            if (isSimpleCaseTableSource(visitor, ((SQLQueryExpr)x).getSubQuery())) {
               SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock)((SQLQueryExpr)x).getSubQuery().getQuery();
               SQLCaseExpr caseExpr = (SQLCaseExpr)((SQLSelectItem)queryBlock.getSelectList().get(0)).getExpr();
               Object result = getValue(caseExpr);
               if (visitor != null && !visitor.getConfig().isCaseConditionConstAllow()) {
                  boolean leftIsName = false;
                  if (x.getParent() instanceof SQLBinaryOpExpr) {
                     SQLExpr left = ((SQLBinaryOpExpr)x.getParent()).getLeft();
                     if (left instanceof SQLName) {
                        leftIsName = true;
                     }
                  }

                  if (!leftIsName && result != null) {
                     addViolation(visitor, 2109, "const case condition", caseExpr);
                  }
               }

               return result;
            }
         }

         DbType dbType = null;
         if (visitor != null) {
            dbType = visitor.getDbType();
         }

         if (!(x instanceof SQLMethodInvokeExpr) && !(x instanceof SQLBetweenExpr) && !(x instanceof SQLInListExpr) && !(x instanceof SQLUnaryExpr)) {
            if (x instanceof SQLCaseExpr) {
               if (visitor != null && !visitor.getConfig().isCaseConditionConstAllow()) {
                  SQLCaseExpr caseExpr = (SQLCaseExpr)x;
                  boolean leftIsName = false;
                  if (caseExpr.getParent() instanceof SQLBinaryOpExpr) {
                     SQLExpr left = ((SQLBinaryOpExpr)caseExpr.getParent()).getLeft();
                     if (left instanceof SQLName) {
                        leftIsName = true;
                     }
                  }

                  if (!leftIsName && caseExpr.getValueExpr() == null && caseExpr.getItems().size() > 0) {
                     SQLCaseExpr.Item item = (SQLCaseExpr.Item)caseExpr.getItems().get(0);
                     Object conditionVal = getValue(visitor, item.getConditionExpr());
                     Object itemVal = getValue(visitor, item.getValueExpr());
                     if (conditionVal instanceof Boolean && itemVal != null) {
                        addViolation(visitor, 2109, "const case condition", caseExpr);
                     }
                  }
               }

               return eval(visitor, dbType, x, Collections.emptyList());
            } else {
               return null;
            }
         } else {
            return eval(visitor, dbType, x, Collections.emptyList());
         }
      }
   }

   public static Object eval(WallVisitor wallVisitor, DbType dbType, SQLObject sqlObject, List<Object> parameters) {
      SQLEvalVisitor visitor = SQLEvalVisitorUtils.createEvalVisitor(dbType);
      visitor.setParameters(parameters);
      visitor.registerFunction("rand", Nil.instance);
      visitor.registerFunction("sin", Nil.instance);
      visitor.registerFunction("cos", Nil.instance);
      visitor.registerFunction("asin", Nil.instance);
      visitor.registerFunction("acos", Nil.instance);
      sqlObject.accept(visitor);
      return sqlObject instanceof SQLNumericLiteralExpr ? ((SQLNumericLiteralExpr)sqlObject).getNumber() : getValueFromAttributes(wallVisitor, sqlObject);
   }

   public static boolean isSimpleCountTableSource(WallVisitor visitor, SQLTableSource tableSource) {
      if (!(tableSource instanceof SQLSubqueryTableSource)) {
         return false;
      } else {
         SQLSubqueryTableSource subQuery = (SQLSubqueryTableSource)tableSource;
         return isSimpleCountTableSource(visitor, subQuery.getSelect());
      }
   }

   public static boolean isSimpleCountTableSource(WallVisitor visitor, SQLSelect select) {
      SQLSelectQuery query = select.getQuery();
      if (query instanceof SQLSelectQueryBlock) {
         SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock)query;
         boolean allawTrueWhere = false;
         if (queryBlock.getWhere() == null) {
            allawTrueWhere = true;
         } else {
            Object whereValue = getValue(visitor, queryBlock.getWhere());
            if (whereValue instanceof Boolean) {
               if (!(Boolean)whereValue) {
                  return false;
               }

               allawTrueWhere = true;
            }
         }

         boolean simpleCount = false;
         if (queryBlock.getSelectList().size() == 1) {
            SQLExpr selectItemExpr = ((SQLSelectItem)queryBlock.getSelectList().get(0)).getExpr();
            if (selectItemExpr instanceof SQLAggregateExpr && ((SQLAggregateExpr)selectItemExpr).methodNameHashCode64() == FnvHash.Constants.COUNT) {
               simpleCount = true;
            }
         }

         if (allawTrueWhere && simpleCount) {
            return true;
         }
      }

      return false;
   }

   public static boolean isSimpleCaseTableSource(WallVisitor visitor, SQLSelect select) {
      SQLSelectQuery query = select.getQuery();
      if (query instanceof SQLSelectQueryBlock) {
         SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock)query;
         if (queryBlock.getWhere() != null) {
            Object whereValue = getValue(visitor, queryBlock.getWhere());
            if (whereValue instanceof Boolean && !(Boolean)whereValue) {
               return false;
            }
         }

         boolean simpleCase = false;
         if (queryBlock.getSelectList().size() == 1) {
            SQLExpr selectItemExpr = ((SQLSelectItem)queryBlock.getSelectList().get(0)).getExpr();
            if (selectItemExpr instanceof SQLCaseExpr) {
               simpleCase = true;
            }
         }

         if (simpleCase) {
            return true;
         }
      }

      return false;
   }

   public static void checkFunctionInTableSource(WallVisitor visitor, SQLMethodInvokeExpr x) {
      WallTopStatementContext topStatementContext = (WallTopStatementContext)wallTopStatementContextLocal.get();
      if (topStatementContext == null || !topStatementContext.fromSysSchema && !topStatementContext.fromSysTable) {
         checkSchema(visitor, x.getOwner());
         String methodName = x.getMethodName().toLowerCase();
         if (!visitor.getProvider().checkDenyTable(methodName) && (isTopStatementWithTableSource(x) || isFirstSelectTableSource(x)) && topStatementContext != null) {
            topStatementContext.setFromSysSchema(Boolean.TRUE);
            clearViolation(visitor);
         }

      }
   }

   public static void checkFunction(WallVisitor visitor, SQLMethodInvokeExpr x) {
      WallTopStatementContext topStatementContext = (WallTopStatementContext)wallTopStatementContextLocal.get();
      if (topStatementContext == null || !topStatementContext.fromSysSchema && !topStatementContext.fromSysTable) {
         checkSchema(visitor, x.getOwner());
         if (visitor.getConfig().isFunctionCheck()) {
            String methodName = x.getMethodName().toLowerCase();
            WallContext context = WallContext.current();
            if (context != null) {
               context.incrementFunctionInvoke(methodName);
            }

            if (!visitor.getProvider().checkDenyFunction(methodName)) {
               boolean isTopNoneFrom = isTopNoneFromSelect(visitor, x);
               if (isTopNoneFrom) {
                  return;
               }

               if (isTopFromDenySchema(visitor, x)) {
                  return;
               }

               boolean isShow = x.getParent() instanceof MySqlShowGrantsStatement;
               if (isShow) {
                  return;
               }

               if (isWhereOrHaving(x) || checkSqlExpr(x)) {
                  addViolation(visitor, 2001, "deny function : " + methodName, x);
               }
            }

         }
      }
   }

   public static SQLSelectQueryBlock getQueryBlock(SQLObject x) {
      if (x == null) {
         return null;
      } else if (x instanceof SQLSelectQueryBlock) {
         return (SQLSelectQueryBlock)x;
      } else {
         SQLObject parent = x.getParent();
         if (parent instanceof SQLExpr) {
            return getQueryBlock(parent);
         } else if (parent instanceof SQLSelectItem) {
            return getQueryBlock(parent);
         } else {
            return parent instanceof SQLSelectQueryBlock ? (SQLSelectQueryBlock)parent : null;
         }
      }
   }

   public static boolean isTopNoneFromSelect(WallVisitor visitor, SQLObject x) {
      while(x.getParent() instanceof SQLExpr || x.getParent() instanceof SQLCaseExpr.Item) {
         x = x.getParent();
      }

      if (!(x.getParent() instanceof SQLSelectItem)) {
         return false;
      } else {
         SQLSelectItem item = (SQLSelectItem)x.getParent();
         if (!(item.getParent() instanceof SQLSelectQueryBlock)) {
            return false;
         } else {
            SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock)item.getParent();
            if (!queryBlockFromIsNull(visitor, queryBlock)) {
               return false;
            } else if (!(queryBlock.getParent() instanceof SQLSelect)) {
               return false;
            } else {
               SQLSelect select = (SQLSelect)queryBlock.getParent();
               if (!(select.getParent() instanceof SQLSelectStatement)) {
                  return false;
               } else {
                  SQLSelectStatement stmt = (SQLSelectStatement)select.getParent();
                  return stmt.getParent() == null;
               }
            }
         }
      }
   }

   private static boolean isTopFromDenySchema(WallVisitor visitor, SQLMethodInvokeExpr x) {
      SQLObject parent;
      for(parent = x.getParent(); parent instanceof SQLExpr || parent instanceof SQLCaseExpr.Item || parent instanceof SQLSelectItem; parent = parent.getParent()) {
      }

      if (parent instanceof SQLSelectQueryBlock) {
         SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock)parent;
         if (!(queryBlock.getParent() instanceof SQLSelect)) {
            return false;
         } else {
            SQLSelect select = (SQLSelect)queryBlock.getParent();
            if (!(select.getParent() instanceof SQLSelectStatement)) {
               return false;
            } else {
               SQLSelectStatement stmt = (SQLSelectStatement)select.getParent();
               if (stmt.getParent() != null) {
                  return false;
               } else {
                  SQLTableSource from = queryBlock.getFrom();
                  if (from instanceof SQLExprTableSource) {
                     SQLExpr fromExpr = ((SQLExprTableSource)from).getExpr();
                     if (fromExpr instanceof SQLName) {
                        String fromTableName = fromExpr.toString();
                        return visitor.isDenyTable(fromTableName);
                     }
                  }

                  return false;
               }
            }
         }
      } else {
         return false;
      }
   }

   private static boolean checkSchema(WallVisitor visitor, SQLExpr x) {
      WallTopStatementContext topStatementContext = (WallTopStatementContext)wallTopStatementContextLocal.get();
      if (topStatementContext == null || !topStatementContext.fromSysSchema && !topStatementContext.fromSysTable) {
         if (x instanceof SQLName) {
            String owner = ((SQLName)x).getSimpleName();
            owner = form(owner);
            if (isInTableSource(x) && !visitor.getProvider().checkDenySchema(owner)) {
               if (!isTopStatementWithTableSource(x) && !isFirstSelectTableSource(x) && !isFirstInSubQuery(x)) {
                  SQLObject parent;
                  for(parent = x.getParent(); parent != null && !(parent instanceof SQLStatement); parent = parent.getParent()) {
                  }

                  boolean sameToTopSelectSchema = false;
                  if (parent instanceof SQLSelectStatement) {
                     SQLSelectStatement selectStmt = (SQLSelectStatement)parent;
                     SQLSelectQuery query = selectStmt.getSelect().getQuery();
                     if (query instanceof SQLSelectQueryBlock) {
                        SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock)query;

                        SQLTableSource from;
                        for(from = queryBlock.getFrom(); from instanceof SQLJoinTableSource; from = ((SQLJoinTableSource)from).getLeft()) {
                        }

                        if (from instanceof SQLExprTableSource) {
                           SQLExpr expr = ((SQLExprTableSource)from).getExpr();
                           if (expr instanceof SQLPropertyExpr) {
                              SQLExpr schemaExpr = ((SQLPropertyExpr)expr).getOwner();
                              if (schemaExpr instanceof SQLIdentifierExpr) {
                                 String schema = ((SQLIdentifierExpr)schemaExpr).getName();
                                 schema = form(schema);
                                 if (schema.equalsIgnoreCase(owner)) {
                                    sameToTopSelectSchema = true;
                                 }
                              }
                           }
                        }
                     }
                  }

                  if (!sameToTopSelectSchema) {
                     addViolation(visitor, 2002, "deny schema : " + owner, x);
                  }
               } else if (topStatementContext != null) {
                  topStatementContext.setFromSysSchema(Boolean.TRUE);
                  clearViolation(visitor);
               }

               return true;
            }

            if (visitor.getConfig().isDenyObjects(owner)) {
               addViolation(visitor, 2005, "deny object : " + owner, x);
               return true;
            }
         }

         return x instanceof SQLPropertyExpr ? checkSchema(visitor, ((SQLPropertyExpr)x).getOwner()) : true;
      } else {
         return true;
      }
   }

   private static boolean isInTableSource(SQLObject x) {
      while(x instanceof SQLExpr) {
         x = x.getParent();
      }

      if (x instanceof SQLExprTableSource) {
         return true;
      } else {
         return false;
      }
   }

   private static boolean isFirstInSubQuery(SQLObject x) {
      while(x instanceof SQLExpr) {
         x = x.getParent();
      }

      if (!(x instanceof SQLExprTableSource)) {
         return false;
      } else {
         SQLSelect sqlSelect = null;

         for(SQLObject parent = x.getParent(); parent != null; parent = parent.getParent()) {
            if (parent instanceof SQLSelect) {
               sqlSelect = (SQLSelect)parent;
               break;
            }
         }

         if (sqlSelect == null) {
            return false;
         } else {
            SQLObject var7 = sqlSelect.getParent();
            if (var7 instanceof SQLInSubQueryExpr && isFirst(var7)) {
               SQLInSubQueryExpr sqlInSubQueryExpr = (SQLInSubQueryExpr)var7;
               if (!(sqlInSubQueryExpr.getParent() instanceof SQLSelectQueryBlock)) {
                  return false;
               } else {
                  SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock)sqlInSubQueryExpr.getParent();
                  if (!(queryBlock.getParent() instanceof SQLSelect)) {
                     return false;
                  } else {
                     SQLSelect select = (SQLSelect)queryBlock.getParent();
                     if (!(select.getParent() instanceof SQLSelectStatement)) {
                        return false;
                     } else {
                        SQLSelectStatement stmt = (SQLSelectStatement)select.getParent();
                        return stmt.getParent() == null;
                     }
                  }
               }
            } else {
               return false;
            }
         }
      }
   }

   private static boolean isFirstSelectTableSource(SQLObject x) {
      while(x instanceof SQLExpr) {
         x = x.getParent();
      }

      if (!(x instanceof SQLExprTableSource)) {
         return false;
      } else {
         SQLSelectQueryBlock queryBlock = null;

         SQLObject parent;
         for(parent = x.getParent(); parent != null; parent = parent.getParent()) {
            if (parent instanceof SQLSelectQueryBlock) {
               queryBlock = (SQLSelectQueryBlock)parent;
               break;
            }
         }

         if (queryBlock == null) {
            return false;
         } else {
            boolean isWhereQueryExpr = false;
            boolean isSelectItem = false;

            do {
               x = parent;
               parent = parent.getParent();
               if (parent instanceof SQLUnionQuery) {
                  SQLUnionQuery union = (SQLUnionQuery)parent;
                  if (union.getRight() == x && hasTableSource(union.getLeft())) {
                     return false;
                  }
               } else if (!(parent instanceof SQLQueryExpr) && !(parent instanceof SQLInSubQueryExpr) && !(parent instanceof SQLExistsExpr)) {
                  if (parent instanceof SQLSelectItem) {
                     isSelectItem = true;
                  } else if ((isWhereQueryExpr || isSelectItem) && parent instanceof SQLSelectQueryBlock && hasTableSource((SQLSelectQuery)((SQLSelectQueryBlock)parent))) {
                     return false;
                  }
               } else {
                  isWhereQueryExpr = isWhereOrHaving(parent);
               }
            } while(parent != null);

            return true;
         }
      }
   }

   private static boolean hasTableSource(SQLSelectQuery x) {
      if (!(x instanceof SQLUnionQuery)) {
         return x instanceof SQLSelectQueryBlock ? hasTableSource(((SQLSelectQueryBlock)x).getFrom()) : false;
      } else {
         SQLUnionQuery union = (SQLUnionQuery)x;
         return hasTableSource(union.getLeft()) || hasTableSource(union.getRight());
      }
   }

   private static boolean hasTableSource(SQLTableSource x) {
      if (x == null) {
         return false;
      } else if (x instanceof SQLExprTableSource) {
         SQLExpr fromExpr = ((SQLExprTableSource)x).getExpr();
         if (fromExpr instanceof SQLName) {
            String name = fromExpr.toString();
            name = form(name);
            if (name.equalsIgnoreCase("DUAL")) {
               return false;
            }
         }

         return true;
      } else if (!(x instanceof SQLJoinTableSource)) {
         return x instanceof SQLSubqueryTableSource ? hasTableSource(((SQLSubqueryTableSource)x).getSelect().getQuery()) : false;
      } else {
         SQLJoinTableSource join = (SQLJoinTableSource)x;
         return hasTableSource(join.getLeft()) || hasTableSource(join.getRight());
      }
   }

   private static boolean isTopStatementWithTableSource(SQLObject x) {
      while(x instanceof SQLExpr) {
         x = x.getParent();
      }

      if (x instanceof SQLExprTableSource) {
         x = x.getParent();
         if (x instanceof SQLStatement) {
            x = x.getParent();
            if (x == null) {
               return true;
            }
         }
      }

      return false;
   }

   private static boolean isTopSelectItem(SQLObject x) {
      while(x.getParent() instanceof SQLExpr || x.getParent() instanceof SQLCaseExpr.Item) {
         x = x.getParent();
      }

      if (!(x.getParent() instanceof SQLSelectItem)) {
         return false;
      } else {
         SQLSelectItem item = (SQLSelectItem)x.getParent();
         return isTopSelectStatement(item.getParent());
      }
   }

   private static boolean isTopSelectStatement(SQLObject x) {
      if (!(x instanceof SQLSelectQueryBlock)) {
         return false;
      } else {
         SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock)x;
         if (!(queryBlock.getParent() instanceof SQLSelect)) {
            return false;
         } else {
            SQLSelect select = (SQLSelect)queryBlock.getParent();
            if (!(select.getParent() instanceof SQLSelectStatement)) {
               return false;
            } else {
               SQLSelectStatement stmt = (SQLSelectStatement)select.getParent();
               return stmt.getParent() == null;
            }
         }
      }
   }

   public static boolean isTopSelectOutFile(MySqlOutFileExpr x) {
      if (!(x.getParent() instanceof SQLExprTableSource)) {
         return false;
      } else {
         SQLExprTableSource tableSource = (SQLExprTableSource)x.getParent();
         return isTopSelectStatement(tableSource.getParent());
      }
   }

   public static boolean check(WallVisitor visitor, SQLExprTableSource x) {
      WallTopStatementContext topStatementContext = (WallTopStatementContext)wallTopStatementContextLocal.get();
      SQLExpr expr = x.getExpr();
      if (expr instanceof SQLPropertyExpr) {
         boolean checkResult = checkSchema(visitor, ((SQLPropertyExpr)expr).getOwner());
         if (!checkResult) {
            return false;
         }
      }

      if (expr instanceof SQLName) {
         String tableName = ((SQLName)expr).getSimpleName();
         WallContext context = WallContext.current();
         if (context != null) {
            WallSqlTableStat tableStat = context.getTableStat(tableName);
            if (tableStat != null) {
               SQLObject parent;
               for(parent = x.getParent(); parent instanceof SQLTableSource; parent = parent.getParent()) {
               }

               if (parent instanceof SQLSelectQueryBlock) {
                  SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock)parent;
                  if (x == queryBlock.getInto()) {
                     tableStat.incrementSelectIntoCount();
                  } else {
                     tableStat.incrementSelectCount();
                  }
               } else if (parent instanceof SQLTruncateStatement) {
                  tableStat.incrementTruncateCount();
               } else if (parent instanceof SQLInsertStatement) {
                  tableStat.incrementInsertCount();
               } else if (parent instanceof SQLDeleteStatement) {
                  tableStat.incrementDeleteCount();
               } else if (parent instanceof SQLUpdateStatement) {
                  tableStat.incrementUpdateCount();
               } else if (parent instanceof SQLReplaceStatement) {
                  tableStat.incrementReplaceCount();
               }
            }
         }

         if (topStatementContext != null && (topStatementContext.fromSysSchema || topStatementContext.fromSysTable)) {
            return true;
         }

         if (visitor.isDenyTable(tableName) && (topStatementContext == null || !topStatementContext.fromPermitTable())) {
            if (!isTopStatementWithTableSource(x) && !isFirstSelectTableSource(x)) {
               boolean isTopNoneFrom = isTopNoneFromSelect(visitor, x);
               if (isTopNoneFrom) {
                  return false;
               }

               addViolation(visitor, 2004, "deny table : " + tableName, x);
               return false;
            }

            if (topStatementContext != null) {
               topStatementContext.setFromSysTable(Boolean.TRUE);
               clearViolation(visitor);
            }

            return false;
         }

         if (visitor.getConfig().getPermitTables().contains(tableName) && isFirstSelectTableSource(x)) {
            if (topStatementContext != null) {
               topStatementContext.setFromPermitTable(Boolean.TRUE);
            }

            return false;
         }
      }

      return true;
   }

   private static void addViolation(WallVisitor visitor, int errorCode, String message, SQLObject x) {
      visitor.addViolation(new IllegalSQLObjectViolation(errorCode, message, visitor.toSQL(x)));
   }

   private static void clearViolation(WallVisitor visitor) {
      visitor.getViolations().clear();
   }

   public static boolean checkUnion(WallVisitor visitor, SQLUnionQuery x) {
       if (x.getRelations().size() > 2) {
           for (int i = 0; i < x.getRelations().size(); i++) {
               SQLSelectQuery item = x.getRelations().get(i);
               if (item instanceof SQLSelectQueryBlock && queryBlockFromIsNull(visitor, item)) {
                   continue;
               }

               item.accept(visitor);
           }
           return false;
       }

       {
           SQLUnionOperator operator = x.getOperator();
           SQLSelectQuery left = x.getLeft();
           SQLSelectQuery right = x.getRight();

           boolean bracket = x.isBracket() && !(x.getParent() instanceof SQLUnionQueryTableSource);

           if ((!bracket)
                   && left instanceof SQLUnionQuery
                   && ((SQLUnionQuery) left).getOperator() == operator
                   && !right.isBracket()
                   && x.getOrderBy() == null) {

               SQLUnionQuery leftUnion = (SQLUnionQuery) left;

               List<SQLSelectQuery> rights = new ArrayList<SQLSelectQuery>();
               rights.add(right);

               for (; ; ) {
                   SQLSelectQuery leftLeft = leftUnion.getLeft();
                   SQLSelectQuery leftRight = leftUnion.getRight();

                   if ((!leftUnion.isBracket())
                           && leftUnion.getOrderBy() == null
                           && (!leftLeft.isBracket())
                           && (!leftRight.isBracket())
                           && leftLeft instanceof SQLUnionQuery
                           && ((SQLUnionQuery) leftLeft).getOperator() == operator) {
                       rights.add(leftRight);
                       leftUnion = (SQLUnionQuery) leftLeft;
                       continue;
                   } else {
                       rights.add(leftRight);
                       rights.add(leftLeft);
                   }
                   break;
               }

               for (int i = rights.size() - 1; i >= 0; i--) {
                   SQLSelectQuery item = rights.get(i);

                   if (item instanceof SQLSelectQueryBlock && queryBlockFromIsNull(visitor, item)) {
                       continue;
                   }

                   item.accept(visitor);
               }
               return false;
           }
       }

       if (x.getOperator() == SQLUnionOperator.MINUS && !visitor.getConfig().isMinusAllow()) {
           addViolation(visitor, ErrorCode.INTERSET_NOT_ALLOW, "minus not allow", x);
           return true;
       }

       if (x.getOperator() == SQLUnionOperator.INTERSECT && !visitor.getConfig().isIntersectAllow()) {
           addViolation(visitor, ErrorCode.INTERSET_NOT_ALLOW, "intersect not allow", x);
           return true;
       }

       if (!WallVisitorUtils.queryBlockFromIsNull(visitor, x.getLeft())
               && WallVisitorUtils.queryBlockFromIsNull(visitor, x.getRight())) {
           boolean isTopUpdateStatement = false;
           boolean isTopInsertStatement = false;
           SQLObject selectParent = x.getParent();
           while (selectParent instanceof SQLSelectQuery //
                   || selectParent instanceof SQLJoinTableSource //
                   || selectParent instanceof SQLSubqueryTableSource //
                   || selectParent instanceof SQLSelect) {
               selectParent = selectParent.getParent();
           }

           if (selectParent instanceof SQLUpdateStatement) {
               isTopUpdateStatement = true;
           }

           if (selectParent instanceof SQLInsertStatement) {
               isTopInsertStatement = true;
           }

           if (isTopUpdateStatement || isTopInsertStatement) {
               return true;
           }

           if (x.getLeft() instanceof SQLSelectQueryBlock) {
               SQLSelectQueryBlock left = (SQLSelectQueryBlock) x.getLeft();
               SQLTableSource tableSource = left.getFrom();
               if (left.getWhere() == null && tableSource != null && tableSource instanceof SQLExprTableSource) {
                   return true;
               }
           }

           WallContext context = WallContext.current();
           if (context != null) {
               context.incrementUnionWarnings();
           }

           if (((x.getOperator() == SQLUnionOperator.UNION || x.getOperator() == SQLUnionOperator.UNION_ALL || x.getOperator() == SQLUnionOperator.DISTINCT)
                   && visitor.getConfig().isSelectUnionCheck() && visitor.isSqlEndOfComment())
                   || (x.getOperator() == SQLUnionOperator.MINUS && visitor.getConfig().isSelectMinusCheck())
                   || (x.getOperator() == SQLUnionOperator.INTERSECT && visitor.getConfig().isSelectIntersectCheck())
                   || (x.getOperator() == SQLUnionOperator.EXCEPT && visitor.getConfig().isSelectExceptCheck())) {
               addViolation(visitor, ErrorCode.UNION,
                       x.getOperator().toString() + " query not contains 'from clause'", x);
           }
       }

       return true;
   }

   public static boolean queryBlockFromIsNull(WallVisitor visitor, SQLSelectQuery query) {
      return queryBlockFromIsNull(visitor, query, true);
   }

   public static boolean queryBlockFromIsNull(WallVisitor visitor, SQLSelectQuery query, boolean checkSelectConst) {
      if (query instanceof SQLSelectQueryBlock) {
         SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock)query;
         SQLTableSource from = queryBlock.getFrom();
         if (queryBlock.getSelectList().size() < 1) {
            return false;
         }

         if (from == null) {
            boolean itemIsConst = true;
            boolean itemHasAlias = false;

            for(SQLSelectItem item : queryBlock.getSelectList()) {
               if (!(item.getExpr() instanceof SQLIdentifierExpr) && !(item.getExpr() instanceof SQLPropertyExpr)) {
                  if (item.getAlias() == null) {
                     continue;
                  }

                  itemHasAlias = true;
                  break;
               }

               itemIsConst = false;
               break;
            }

            if (itemIsConst && !itemHasAlias) {
               return true;
            }

            return false;
         }

         if (from instanceof SQLExprTableSource) {
            SQLExpr fromExpr = ((SQLExprTableSource)from).getExpr();
            if (fromExpr instanceof SQLName) {
               String name = fromExpr.toString();
               name = form(name);
               if (name.equalsIgnoreCase("DUAL")) {
                  return true;
               }
            }
         }

         if (queryBlock.getSelectList().size() == 1 && ((SQLSelectItem)queryBlock.getSelectList().get(0)).getExpr() instanceof SQLAllColumnExpr && from instanceof SQLSubqueryTableSource) {
            SQLSelectQuery subQuery = ((SQLSubqueryTableSource)from).getSelect().getQuery();
            if (queryBlockFromIsNull(visitor, subQuery)) {
               return true;
            }
         }

         if (checkSelectConst) {
            SQLExpr where = queryBlock.getWhere();
            if (where != null) {
               Object whereValue = getValue(visitor, where);
               if (Boolean.TRUE == whereValue) {
                  boolean allIsConst = true;

                  for(SQLSelectItem item : queryBlock.getSelectList()) {
                     if (getValue(visitor, item.getExpr()) == null) {
                        allIsConst = false;
                        break;
                     }
                  }

                  if (allIsConst) {
                     return true;
                  }
               }
            }
         }
      }

      return false;
   }

   public static String form(String name) {
      if (name.startsWith("\"") && name.endsWith("\"")) {
         name = name.substring(1, name.length() - 1);
      }

      if (name.startsWith("'") && name.endsWith("'")) {
         name = name.substring(1, name.length() - 1);
      }

      if (name.startsWith("`") && name.endsWith("`")) {
         name = name.substring(1, name.length() - 1);
      }

      name = name.toLowerCase();
      return name;
   }

   public static void loadResource(Set<String> names, String resource) {
      try {
         boolean hasResource = false;
         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
         if (classLoader != null) {
            Enumeration<URL> e = Thread.currentThread().getContextClassLoader().getResources(resource);

            while(e.hasMoreElements()) {
               URL url = (URL)e.nextElement();
               InputStream in = null;

               try {
                  in = url.openStream();
                  readFromInputStream(names, in);
                  hasResource = true;
               } finally {
                  JdbcUtils.close((Closeable)in);
               }
            }
         }

         if (!hasResource) {
            if (!resource.startsWith("/")) {
               resource = "/" + resource;
            }

            InputStream in = null;

            try {
               in = WallVisitorUtils.class.getResourceAsStream(resource);
               if (in != null) {
                  readFromInputStream(names, in);
               }
            } finally {
               JdbcUtils.close((Closeable)in);
            }
         }
      } catch (IOException e) {
         LOG.error("load oracle deny tables errror", e);
      }

   }

   private static void readFromInputStream(Set<String> names, InputStream in) throws IOException {
      BufferedReader reader = null;

      try {
         reader = new BufferedReader(new InputStreamReader(in));

         while(true) {
            String line = reader.readLine();
            if (line == null) {
               return;
            }

            line = line.trim();
            if (line.length() > 0) {
               line = line.toLowerCase();
               names.add(line);
            }
         }
      } finally {
         JdbcUtils.close((Closeable)reader);
      }
   }

   public static void preVisitCheck(WallVisitor visitor, SQLObject x) {
      WallConfig config = visitor.getProvider().getConfig();
      if (x instanceof SQLStatement) {
         boolean allow = false;
         if (!(x instanceof SQLCommentStatement)) {
            int errorCode;
            String denyMessage;
            if (x instanceof SQLInsertStatement) {
               allow = config.isInsertAllow();
               denyMessage = "insert not allow";
               errorCode = 1004;
            } else if (x instanceof SQLSelectStatement) {
               allow = true;
               denyMessage = "select not allow";
               errorCode = 1002;
            } else if (x instanceof SQLDeleteStatement) {
               allow = config.isDeleteAllow();
               denyMessage = "delete not allow";
               errorCode = 1005;
            } else if (x instanceof SQLUpdateStatement) {
               allow = config.isUpdateAllow();
               denyMessage = "update not allow";
               errorCode = 1006;
            } else if (!(x instanceof OracleMultiInsertStatement) && !(x instanceof OracleMultiInsertStatement.InsertIntoClause)) {
               if (x instanceof SQLMergeStatement) {
                  allow = config.isMergeAllow();
                  denyMessage = "merge not allow";
                  errorCode = 1009;
               } else if (!(x instanceof SQLCallStatement) && !(x instanceof OracleExecuteImmediateStatement)) {
                  if (x instanceof SQLTruncateStatement) {
                     allow = config.isTruncateAllow();
                     denyMessage = "truncate not allow";
                     errorCode = 1100;
                  } else if (x instanceof SQLCreateStatement) {
                     allow = config.isCreateTableAllow();
                     denyMessage = "create table not allow";
                     errorCode = 1101;
                  } else if (x instanceof MySqlRenameTableStatement) {
                     allow = config.isRenameTableAllow();
                     denyMessage = "rename table not allow";
                     errorCode = 1105;
                  } else if (x instanceof SQLAlterStatement) {
                     allow = config.isAlterTableAllow();
                     denyMessage = "alter table not allow";
                     errorCode = 1102;
                  } else if (x instanceof SQLDropStatement) {
                     allow = config.isDropTableAllow();
                     denyMessage = "drop table not allow";
                     errorCode = 1103;
                  } else if (x instanceof SQLSetStatement) {
                     allow = config.isSetAllow();
                     denyMessage = "set not allow";
                     errorCode = 1200;
                  } else if (x instanceof SQLReplaceStatement) {
                     allow = config.isReplaceAllow();
                     denyMessage = "replace not allow";
                     errorCode = 1010;
                  } else if (!(x instanceof SQLDescribeStatement) && (!(x instanceof MySqlExplainStatement) || !((MySqlExplainStatement)x).isDescribe())) {
                     if (x instanceof SQLShowStatement) {
                        allow = config.isShowAllow();
                        denyMessage = "show not allow";
                        errorCode = 1202;
                     } else if (x instanceof SQLCommitStatement) {
                        allow = config.isCommitAllow();
                        denyMessage = "commit not allow";
                        errorCode = 1301;
                     } else if (x instanceof SQLRollbackStatement) {
                        allow = config.isRollbackAllow();
                        denyMessage = "rollback not allow";
                        errorCode = 1302;
                     } else if (x instanceof SQLUseStatement) {
                        allow = config.isUseAllow();
                        denyMessage = "use not allow";
                        errorCode = 1203;
                     } else if (x instanceof MySqlHintStatement) {
                        allow = config.isHintAllow();
                        denyMessage = "hint not allow";
                        errorCode = 1400;
                     } else if (x instanceof MySqlLockTableStatement) {
                        allow = config.isLockTableAllow();
                        denyMessage = "lock table not allow";
                        errorCode = 1106;
                     } else if (x instanceof SQLStartTransactionStatement) {
                        allow = config.isStartTransactionAllow();
                        denyMessage = "start transaction not allow";
                        errorCode = 1303;
                     } else if (x instanceof SQLBlockStatement) {
                        allow = config.isBlockAllow();
                        denyMessage = "block statement not allow";
                        errorCode = 1304;
                     } else if (!(x instanceof SQLExplainStatement) && !(x instanceof MySqlOptimizeStatement)) {
                        allow = config.isNoneBaseStatementAllow();
                        errorCode = 1999;
                        denyMessage = x.getClass() + " not allow";
                     } else {
                        allow = true;
                        errorCode = 0;
                        denyMessage = null;
                     }
                  } else {
                     allow = config.isDescribeAllow();
                     denyMessage = "describe not allow";
                     errorCode = 1201;
                  }
               } else {
                  allow = config.isCallAllow();
                  denyMessage = "call not allow";
                  errorCode = 1300;
               }
            } else {
               allow = true;
               denyMessage = "multi-insert not allow";
               errorCode = 1004;
            }

            if (!allow) {
               addViolation(visitor, errorCode, denyMessage, x);
            }

         }
      }
   }

   public static void check(WallVisitor visitor, SQLCommentHint x) {
      if (!visitor.getConfig().isHintAllow()) {
         addViolation(visitor, 2110, "hint not allow", x);
      } else {
         String text = x.getText();
         text = text.trim();
         if (text.startsWith("!")) {
            text = text.substring(1);
         }

         if (text.length() != 0) {
            int pos;
            for(pos = 0; pos < text.length(); ++pos) {
               char ch = text.charAt(pos);
               if (ch < '0' || ch > '9') {
                  break;
               }
            }

            if (pos == 5) {
               text = text.substring(5);
               text = text.trim();
            }

            text = text.toUpperCase();
            boolean isWhite = false;

            for(String hint : whiteHints) {
               if (text.equals(hint)) {
                  isWhite = true;
                  break;
               }
            }

            if (!isWhite && (text.startsWith("FORCE INDEX") || text.startsWith("IGNORE INDEX"))) {
               isWhite = true;
            }

            if (!isWhite && text.startsWith("SET")) {
               SQLStatementParser parser = new MySqlStatementParser(text);
               List<SQLStatement> statementList = parser.parseStatementList();
               if (statementList != null && statementList.size() > 0) {
                  SQLStatement statement = (SQLStatement)statementList.get(0);
                  if (statement instanceof SQLSetStatement) {
                     isWhite = true;
                  }
               }
            }

            if (!isWhite && visitor.getDbType() == DbType.oracle && text.startsWith("+")) {
               isWhite = true;
            }

            if (!isWhite) {
               addViolation(visitor, 2110, "hint not allow", x);
            }

         }
      }
   }

   public static void check(WallVisitor visitor, SQLJoinTableSource x) {
      SQLExpr condition = x.getCondition();
      if (condition instanceof SQLName) {
         addViolation(visitor, 6000, "invalid join condition", x);
      }

   }

   public static class WallTopStatementContext {
      private boolean fromSysTable = false;
      private boolean fromSysSchema = false;
      private boolean fromPermitTable = false;

      public boolean fromSysTable() {
         return this.fromSysTable;
      }

      public void setFromSysTable(boolean fromSysTable) {
         this.fromSysTable = fromSysTable;
      }

      public boolean fromSysSchema() {
         return this.fromSysSchema;
      }

      public void setFromSysSchema(boolean fromSysSchema) {
         this.fromSysSchema = fromSysSchema;
      }

      public boolean fromPermitTable() {
         return this.fromPermitTable;
      }

      public void setFromPermitTable(boolean fromPermitTable) {
         this.fromPermitTable = fromPermitTable;
      }
   }

   public static class WallConditionContext {
      private boolean partAlwayTrue = false;
      private boolean partAlwayFalse = false;
      private boolean constArithmetic = false;
      private boolean xor = false;
      private boolean bitwise = false;

      public boolean hasPartAlwayTrue() {
         return this.partAlwayTrue;
      }

      public void setPartAlwayTrue(boolean partAllowTrue) {
         this.partAlwayTrue = partAllowTrue;
      }

      public boolean hasPartAlwayFalse() {
         return this.partAlwayFalse;
      }

      public void setPartAlwayFalse(boolean partAlwayFalse) {
         this.partAlwayFalse = partAlwayFalse;
      }

      public boolean hasConstArithmetic() {
         return this.constArithmetic;
      }

      public void setConstArithmetic(boolean constArithmetic) {
         this.constArithmetic = constArithmetic;
      }

      public boolean hasXor() {
         return this.xor;
      }

      public void setXor(boolean xor) {
         this.xor = xor;
      }

      public boolean hasBitwise() {
         return this.bitwise;
      }

      public void setBitwise(boolean bitwise) {
         this.bitwise = bitwise;
      }
   }
}
