package com.chenyang.druid.sql.visitor;

import com.chenyang.druid.DbType;
import com.chenyang.druid.sql.SQLUtils;
import com.chenyang.druid.sql.ast.SQLCurrentTimeExpr;
import com.chenyang.druid.sql.ast.SQLDeclareItem;
import com.chenyang.druid.sql.ast.SQLExpr;
import com.chenyang.druid.sql.ast.SQLIndexDefinition;
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.SQLOrderingSpecification;
import com.chenyang.druid.sql.ast.SQLOver;
import com.chenyang.druid.sql.ast.SQLParameter;
import com.chenyang.druid.sql.ast.SQLPartition;
import com.chenyang.druid.sql.ast.SQLPartitionByHash;
import com.chenyang.druid.sql.ast.SQLPartitionByList;
import com.chenyang.druid.sql.ast.SQLPartitionByRange;
import com.chenyang.druid.sql.ast.SQLPartitionValue;
import com.chenyang.druid.sql.ast.SQLStatement;
import com.chenyang.druid.sql.ast.SQLSubPartition;
import com.chenyang.druid.sql.ast.SQLSubPartitionByHash;
import com.chenyang.druid.sql.ast.SQLWindow;
import com.chenyang.druid.sql.ast.expr.SQLAggregateExpr;
import com.chenyang.druid.sql.ast.expr.SQLAllColumnExpr;
import com.chenyang.druid.sql.ast.expr.SQLArrayExpr;
import com.chenyang.druid.sql.ast.expr.SQLBetweenExpr;
import com.chenyang.druid.sql.ast.expr.SQLBinaryOpExpr;
import com.chenyang.druid.sql.ast.expr.SQLBinaryOperator;
import com.chenyang.druid.sql.ast.expr.SQLCastExpr;
import com.chenyang.druid.sql.ast.expr.SQLCharExpr;
import com.chenyang.druid.sql.ast.expr.SQLCurrentOfCursorExpr;
import com.chenyang.druid.sql.ast.expr.SQLDbLinkExpr;
import com.chenyang.druid.sql.ast.expr.SQLExprUtils;
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.SQLPropertyExpr;
import com.chenyang.druid.sql.ast.expr.SQLSequenceExpr;
import com.chenyang.druid.sql.ast.statement.SQLAlterDatabaseStatement;
import com.chenyang.druid.sql.ast.statement.SQLAlterFunctionStatement;
import com.chenyang.druid.sql.ast.statement.SQLAlterIndexStatement;
import com.chenyang.druid.sql.ast.statement.SQLAlterOutlineStatement;
import com.chenyang.druid.sql.ast.statement.SQLAlterProcedureStatement;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableAddColumn;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableAddConstraint;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableAddIndex;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableAnalyzePartition;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableArchivePartition;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableCheckPartition;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableCoalescePartition;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableConvertCharSet;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableDisableConstraint;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableDiscardPartition;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableDropConstraint;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableDropForeignKey;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableDropIndex;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableDropKey;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableDropPartition;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableDropPrimaryKey;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableEnableConstraint;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableExchangePartition;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableImportPartition;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableItem;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableOptimizePartition;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableReOrganizePartition;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableRebuildPartition;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableRename;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableRepairPartition;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableSetOption;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableStatement;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableTruncatePartition;
import com.chenyang.druid.sql.ast.statement.SQLAlterTypeStatement;
import com.chenyang.druid.sql.ast.statement.SQLAlterViewStatement;
import com.chenyang.druid.sql.ast.statement.SQLAnalyzeTableStatement;
import com.chenyang.druid.sql.ast.statement.SQLAssignItem;
import com.chenyang.druid.sql.ast.statement.SQLBlockStatement;
import com.chenyang.druid.sql.ast.statement.SQLCallStatement;
import com.chenyang.druid.sql.ast.statement.SQLCheck;
import com.chenyang.druid.sql.ast.statement.SQLCloseStatement;
import com.chenyang.druid.sql.ast.statement.SQLColumnConstraint;
import com.chenyang.druid.sql.ast.statement.SQLColumnDefinition;
import com.chenyang.druid.sql.ast.statement.SQLCommentStatement;
import com.chenyang.druid.sql.ast.statement.SQLConstraint;
import com.chenyang.druid.sql.ast.statement.SQLCopyFromStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateDatabaseStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateFunctionStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateIndexStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateMaterializedViewStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateOutlineStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateProcedureStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateRoleStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateSequenceStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateTableGroupStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateTableStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateTriggerStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateViewStatement;
import com.chenyang.druid.sql.ast.statement.SQLDefault;
import com.chenyang.druid.sql.ast.statement.SQLDeleteStatement;
import com.chenyang.druid.sql.ast.statement.SQLDescribeStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropCatalogStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropDatabaseStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropFunctionStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropIndexStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropMaterializedViewStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropOutlineStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropProcedureStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropRoleStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropSequenceStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropSynonymStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropTableGroupStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropTableSpaceStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropTableStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropTriggerStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropTypeStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropUserStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropViewStatement;
import com.chenyang.druid.sql.ast.statement.SQLDumpStatement;
import com.chenyang.druid.sql.ast.statement.SQLExplainStatement;
import com.chenyang.druid.sql.ast.statement.SQLExportTableStatement;
import com.chenyang.druid.sql.ast.statement.SQLExprStatement;
import com.chenyang.druid.sql.ast.statement.SQLExprTableSource;
import com.chenyang.druid.sql.ast.statement.SQLExternalRecordFormat;
import com.chenyang.druid.sql.ast.statement.SQLFetchStatement;
import com.chenyang.druid.sql.ast.statement.SQLForeignKeyImpl;
import com.chenyang.druid.sql.ast.statement.SQLGrantStatement;
import com.chenyang.druid.sql.ast.statement.SQLIfStatement;
import com.chenyang.druid.sql.ast.statement.SQLImportTableStatement;
import com.chenyang.druid.sql.ast.statement.SQLInsertStatement;
import com.chenyang.druid.sql.ast.statement.SQLJoinTableSource;
import com.chenyang.druid.sql.ast.statement.SQLLateralViewTableSource;
import com.chenyang.druid.sql.ast.statement.SQLMergeStatement;
import com.chenyang.druid.sql.ast.statement.SQLObjectType;
import com.chenyang.druid.sql.ast.statement.SQLOpenStatement;
import com.chenyang.druid.sql.ast.statement.SQLPartitionRef;
import com.chenyang.druid.sql.ast.statement.SQLPrimaryKey;
import com.chenyang.druid.sql.ast.statement.SQLRefreshMaterializedViewStatement;
import com.chenyang.druid.sql.ast.statement.SQLReplaceStatement;
import com.chenyang.druid.sql.ast.statement.SQLRevokeStatement;
import com.chenyang.druid.sql.ast.statement.SQLRollbackStatement;
import com.chenyang.druid.sql.ast.statement.SQLSavePointStatement;
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.SQLSelectOrderByItem;
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.SQLShowColumnsStatement;
import com.chenyang.druid.sql.ast.statement.SQLShowCreateTableStatement;
import com.chenyang.druid.sql.ast.statement.SQLShowCreateViewStatement;
import com.chenyang.druid.sql.ast.statement.SQLShowDatabasesStatement;
import com.chenyang.druid.sql.ast.statement.SQLShowIndexesStatement;
import com.chenyang.druid.sql.ast.statement.SQLShowMaterializedViewStatement;
import com.chenyang.druid.sql.ast.statement.SQLShowTableGroupsStatement;
import com.chenyang.druid.sql.ast.statement.SQLShowTablesStatement;
import com.chenyang.druid.sql.ast.statement.SQLShowViewsStatement;
import com.chenyang.druid.sql.ast.statement.SQLSubqueryTableSource;
import com.chenyang.druid.sql.ast.statement.SQLSyncMetaStatement;
import com.chenyang.druid.sql.ast.statement.SQLTableElement;
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.SQLUnique;
import com.chenyang.druid.sql.ast.statement.SQLUniqueConstraint;
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.ast.statement.SQLValuesTableSource;
import com.chenyang.druid.sql.ast.statement.SQLWithSubqueryClause;
import com.chenyang.druid.sql.dialect.mysql.ast.expr.MySqlExpr;
import com.chenyang.druid.sql.dialect.mysql.visitor.MySqlASTVisitorAdapter;
import com.chenyang.druid.sql.dialect.oracle.ast.expr.OracleExpr;
import com.chenyang.druid.sql.dialect.oracle.visitor.OracleASTVisitorAdapter;
import com.chenyang.druid.sql.repository.SchemaObject;
import com.chenyang.druid.sql.repository.SchemaRepository;
import com.chenyang.druid.stat.TableStat;
import com.chenyang.druid.util.FnvHash;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class SchemaStatVisitor extends SQLASTVisitorAdapter {
   protected SchemaRepository repository;
   protected final List<SQLName> originalTables;
   protected final HashMap<TableStat.Name, TableStat> tableStats;
   protected final Map<Long, TableStat.Column> columns;
   protected final List<TableStat.Condition> conditions;
   protected final Set<TableStat.Relationship> relationships;
   protected final List<TableStat.Column> orderByColumns;
   protected final Set<TableStat.Column> groupByColumns;
   protected final List<SQLAggregateExpr> aggregateFunctions;
   protected final List<SQLMethodInvokeExpr> functions;
   private List<Object> parameters;
   private TableStat.Mode mode;
   protected DbType dbType;

   public SchemaStatVisitor() {
      this((DbType)null);
   }

   public SchemaStatVisitor(SchemaRepository repository) {
      this.originalTables = new ArrayList();
      this.tableStats = new LinkedHashMap();
      this.columns = new LinkedHashMap();
      this.conditions = new ArrayList();
      this.relationships = new LinkedHashSet();
      this.orderByColumns = new ArrayList();
      this.groupByColumns = new LinkedHashSet();
      this.aggregateFunctions = new ArrayList();
      this.functions = new ArrayList(2);
      if (repository != null) {
         this.dbType = repository.getDbType();
      }

      this.repository = repository;
   }

   public SchemaStatVisitor(DbType dbType) {
      this((SchemaRepository)(new SchemaRepository(dbType)), new ArrayList());
      this.dbType = dbType;
   }

   public SchemaStatVisitor(List<Object> parameters) {
      this((DbType)null, parameters);
   }

   public SchemaStatVisitor(DbType dbType, List<Object> parameters) {
      this(new SchemaRepository(dbType), parameters);
      this.parameters = parameters;
   }

   public SchemaStatVisitor(SchemaRepository repository, List<Object> parameters) {
      this.originalTables = new ArrayList();
      this.tableStats = new LinkedHashMap();
      this.columns = new LinkedHashMap();
      this.conditions = new ArrayList();
      this.relationships = new LinkedHashSet();
      this.orderByColumns = new ArrayList();
      this.groupByColumns = new LinkedHashSet();
      this.aggregateFunctions = new ArrayList();
      this.functions = new ArrayList(2);
      this.repository = repository;
      this.parameters = parameters;
      if (repository != null) {
         DbType dbType = repository.getDbType();
         if (dbType != null && this.dbType == null) {
            this.dbType = dbType;
         }
      }

   }

   public SchemaRepository getRepository() {
      return this.repository;
   }

   public void setRepository(SchemaRepository repository) {
      this.repository = repository;
   }

   public List<Object> getParameters() {
      return this.parameters;
   }

   public void setParameters(List<Object> parameters) {
      this.parameters = parameters;
   }

   public TableStat getTableStat(String tableName) {
      tableName = this.handleName(tableName);
      TableStat.Name tableNameObj = new TableStat.Name(tableName);
      TableStat stat = (TableStat)this.tableStats.get(tableNameObj);
      if (stat == null) {
         stat = new TableStat();
         this.tableStats.put(new TableStat.Name(tableName), stat);
      }

      return stat;
   }

   public TableStat getTableStat(SQLName tableName) {
      String strName;
      if (tableName instanceof SQLIdentifierExpr) {
         strName = ((SQLIdentifierExpr)tableName).normalizedName();
      } else if (tableName instanceof SQLPropertyExpr) {
         strName = ((SQLPropertyExpr)tableName).normalizedName();
      } else {
         strName = tableName.toString();
      }

      long hashCode64 = tableName.hashCode64();
      if (hashCode64 == FnvHash.Constants.DUAL) {
         return null;
      } else {
         this.originalTables.add(tableName);
         TableStat.Name tableNameObj = new TableStat.Name(strName, hashCode64);
         TableStat stat = (TableStat)this.tableStats.get(tableNameObj);
         if (stat == null) {
            stat = new TableStat();
            this.tableStats.put(new TableStat.Name(strName, hashCode64), stat);
         }

         return stat;
      }
   }

   protected TableStat.Column addColumn(String tableName, String columnName) {
      TableStat.Column c = new TableStat.Column(tableName, columnName, this.dbType);
      TableStat.Column column = (TableStat.Column)this.columns.get(c.hashCode64());
      if (column == null && columnName != null) {
         column = c;
         this.columns.put(c.hashCode64(), c);
      }

      return column;
   }

   protected TableStat.Column addColumn(SQLName table, String columnName) {
      String tableName;
      if (table instanceof SQLIdentifierExpr) {
         tableName = ((SQLIdentifierExpr)table).normalizedName();
      } else if (table instanceof SQLPropertyExpr) {
         tableName = ((SQLPropertyExpr)table).normalizedName();
      } else {
         tableName = table.toString();
      }

      long tableHashCode64 = table.hashCode64();
      long basic = tableHashCode64 ^ 46L;
      basic *= 1099511628211L;
      long columnHashCode64 = FnvHash.hashCode64(basic, columnName);
      TableStat.Column column = (TableStat.Column)this.columns.get(columnHashCode64);
      if (column == null && columnName != null) {
         column = new TableStat.Column(tableName, columnName, columnHashCode64);
         this.columns.put(columnHashCode64, column);
      }

      return column;
   }

   private String handleName(String ident) {
      int len = ident.length();
      if (ident.charAt(0) == '[' && ident.charAt(len - 1) == ']') {
         ident = ident.substring(1, len - 1);
      } else {
         boolean flag0 = false;
         boolean flag1 = false;
         boolean flag2 = false;
         boolean flag3 = false;

         for(int i = 0; i < len; ++i) {
            char ch = ident.charAt(i);
            if (ch == '"') {
               flag0 = true;
            } else if (ch == '`') {
               flag1 = true;
            } else if (ch == ' ') {
               flag2 = true;
            } else if (ch == '\'') {
               flag3 = true;
            }
         }

         if (flag0) {
            ident = ident.replaceAll("\"", "");
         }

         if (flag1) {
            ident = ident.replaceAll("`", "");
         }

         if (flag2) {
            ident = ident.replaceAll(" ", "");
         }

         if (flag3) {
            ident = ident.replaceAll("'", "");
         }
      }

      return ident;
   }

   protected TableStat.Mode getMode() {
      return this.mode;
   }

   protected void setModeOrigin(SQLObject x) {
      TableStat.Mode originalMode = (TableStat.Mode)x.getAttribute("_original_use_mode");
      this.mode = originalMode;
   }

   protected TableStat.Mode setMode(SQLObject x, TableStat.Mode mode) {
      TableStat.Mode oldMode = this.mode;
      x.putAttribute("_original_use_mode", oldMode);
      this.mode = mode;
      return oldMode;
   }

   private boolean visitOrderBy(SQLIdentifierExpr x) {
      SQLTableSource tableSource = x.getResolvedTableSource();
      if (tableSource == null && x.getParent() instanceof SQLSelectOrderByItem && x.getParent().getParent() instanceof SQLOrderBy && x.getParent().getParent().getParent() instanceof SQLSelectQueryBlock) {
         SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock)x.getParent().getParent().getParent();
         SQLSelectItem selectItem = queryBlock.findSelectItem(x.nameHashCode64());
         if (selectItem != null) {
            SQLExpr selectItemExpr = selectItem.getExpr();
            if (!(selectItemExpr instanceof SQLIdentifierExpr)) {
               if (selectItemExpr instanceof SQLPropertyExpr) {
                  return this.visitOrderBy((SQLPropertyExpr)selectItemExpr);
               }

               return false;
            }

            x = (SQLIdentifierExpr)selectItemExpr;
         }
      }

      String tableName = null;
      if (tableSource instanceof SQLExprTableSource) {
         SQLExpr expr = ((SQLExprTableSource)tableSource).getExpr();
         if (expr instanceof SQLIdentifierExpr) {
            SQLIdentifierExpr table = (SQLIdentifierExpr)expr;
            tableName = table.getName();
         } else if (expr instanceof SQLPropertyExpr) {
            SQLPropertyExpr table = (SQLPropertyExpr)expr;
            tableName = table.toString();
         } else if (expr instanceof SQLMethodInvokeExpr) {
            SQLMethodInvokeExpr methodInvokeExpr = (SQLMethodInvokeExpr)expr;
            if ("table".equalsIgnoreCase(methodInvokeExpr.getMethodName()) && methodInvokeExpr.getArguments().size() == 1 && methodInvokeExpr.getArguments().get(0) instanceof SQLName) {
               SQLName table = (SQLName)methodInvokeExpr.getArguments().get(0);
               if (table instanceof SQLPropertyExpr) {
                  SQLPropertyExpr propertyExpr = (SQLPropertyExpr)table;
                  SQLIdentifierExpr owner = (SQLIdentifierExpr)propertyExpr.getOwner();
                  if (propertyExpr.getResolvedTableSource() != null && propertyExpr.getResolvedTableSource() instanceof SQLExprTableSource) {
                     SQLExpr resolveExpr = ((SQLExprTableSource)propertyExpr.getResolvedTableSource()).getExpr();
                     if (resolveExpr instanceof SQLName) {
                        tableName = resolveExpr.toString() + "." + propertyExpr.getName();
                     }
                  }
               }

               if (tableName == null) {
                  tableName = table.toString();
               }
            }
         }
      } else {
         if (tableSource instanceof SQLWithSubqueryClause.Entry) {
            return false;
         }

         if (tableSource instanceof SQLSubqueryTableSource) {
            SQLSelectQueryBlock queryBlock = ((SQLSubqueryTableSource)tableSource).getSelect().getQueryBlock();
            if (queryBlock == null) {
               return false;
            }

            SQLSelectItem selectItem = queryBlock.findSelectItem(x.nameHashCode64());
            if (selectItem == null) {
               return false;
            }

            SQLExpr selectItemExpr = selectItem.getExpr();
            SQLTableSource columnTableSource = null;
            if (selectItemExpr instanceof SQLIdentifierExpr) {
               columnTableSource = ((SQLIdentifierExpr)selectItemExpr).getResolvedTableSource();
            } else if (selectItemExpr instanceof SQLPropertyExpr) {
               columnTableSource = ((SQLPropertyExpr)selectItemExpr).getResolvedTableSource();
            }

            if (columnTableSource instanceof SQLExprTableSource && ((SQLExprTableSource)columnTableSource).getExpr() instanceof SQLName) {
               SQLName tableExpr = (SQLName)((SQLExprTableSource)columnTableSource).getExpr();
               if (tableExpr instanceof SQLIdentifierExpr) {
                  tableName = ((SQLIdentifierExpr)tableExpr).normalizedName();
               } else if (tableExpr instanceof SQLPropertyExpr) {
                  tableName = ((SQLPropertyExpr)tableExpr).normalizedName();
               }
            }
         } else {
            boolean skip = false;

            for(SQLObject parent = x.getParent(); parent != null; parent = parent.getParent()) {
               if (parent instanceof SQLSelectQueryBlock) {
                  SQLTableSource from = ((SQLSelectQueryBlock)parent).getFrom();
                  if (from instanceof SQLValuesTableSource) {
                     skip = true;
                     break;
                  }
               } else if (parent instanceof SQLSelectQuery) {
                  break;
               }
            }
         }
      }

      String identName = x.getName();
      if (tableName != null) {
         this.orderByAddColumn(tableName, identName, x);
      } else {
         this.orderByAddColumn("UNKNOWN", identName, x);
      }

      return false;
   }

   private boolean visitOrderBy(SQLPropertyExpr x) {
      if (this.isSubQueryOrParamOrVariant(x)) {
         return false;
      } else {
         String owner = null;
         SQLTableSource tableSource = x.getResolvedTableSource();
         if (tableSource instanceof SQLExprTableSource) {
            SQLExpr tableSourceExpr = ((SQLExprTableSource)tableSource).getExpr();
            if (tableSourceExpr instanceof SQLName) {
               owner = tableSourceExpr.toString();
            }
         }

         if (owner == null && x.getOwner() instanceof SQLIdentifierExpr) {
            owner = ((SQLIdentifierExpr)x.getOwner()).getName();
         }

         if (owner == null) {
            return false;
         } else {
            this.orderByAddColumn(owner, x.getName(), x);
            return false;
         }
      }
   }

   private boolean visitOrderBy(SQLIntegerExpr x) {
      SQLObject parent = x.getParent();
      if (!(parent instanceof SQLSelectOrderByItem)) {
         return false;
      } else {
         if (parent.getParent() instanceof SQLSelectQueryBlock) {
            int selectItemIndex = x.getNumber().intValue() - 1;
            SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock)parent.getParent();
            List<SQLSelectItem> selectList = queryBlock.getSelectList();
            if (selectItemIndex < 0 || selectItemIndex >= selectList.size()) {
               return false;
            }

            SQLExpr selectItemExpr = ((SQLSelectItem)selectList.get(selectItemIndex)).getExpr();
            if (selectItemExpr instanceof SQLIdentifierExpr) {
               this.visitOrderBy((SQLIdentifierExpr)selectItemExpr);
            } else if (selectItemExpr instanceof SQLPropertyExpr) {
               this.visitOrderBy((SQLPropertyExpr)selectItemExpr);
            }
         }

         return false;
      }
   }

   private void orderByAddColumn(String table, String columnName, SQLObject expr) {
      TableStat.Column column = new TableStat.Column(table, columnName);
      SQLObject parent = expr.getParent();
      if (parent instanceof SQLSelectOrderByItem) {
         SQLOrderingSpecification type = ((SQLSelectOrderByItem)parent).getType();
         column.getAttributes().put("orderBy.type", type);
      }

      this.orderByColumns.add(column);
   }

   public boolean visit(SQLOrderBy x) {
      SQLASTVisitor orderByVisitor = this.createOrderByVisitor(x);
      SQLSelectQueryBlock query = null;
      if (x.getParent() instanceof SQLSelectQueryBlock) {
         query = (SQLSelectQueryBlock)x.getParent();
      }

      if (query != null) {
         for(SQLSelectOrderByItem item : x.getItems()) {
            SQLExpr expr = item.getExpr();
            if (expr instanceof SQLIntegerExpr) {
               int intValue = ((SQLIntegerExpr)expr).getNumber().intValue() - 1;
               if (intValue < query.getSelectList().size()) {
                  SQLExpr selectItemExpr = ((SQLSelectItem)query.getSelectList().get(intValue)).getExpr();
                  selectItemExpr.accept(orderByVisitor);
               }
            } else if (!(expr instanceof MySqlExpr) && expr instanceof OracleExpr) {
            }
         }
      }

      x.accept(orderByVisitor);

      for(SQLSelectOrderByItem orderByItem : x.getItems()) {
         this.statExpr(orderByItem.getExpr());
      }

      return false;
   }

   public boolean visit(SQLOver x) {
      SQLName of = x.getOf();
      SQLOrderBy orderBy = x.getOrderBy();
      List<SQLExpr> partitionBy = x.getPartitionBy();
      if (of == null && orderBy != null) {
         orderBy.accept(this);
      }

      if (partitionBy != null) {
         for(SQLExpr expr : partitionBy) {
            expr.accept(this);
         }
      }

      return false;
   }

   public boolean visit(SQLWindow x) {
      SQLOver over = x.getOver();
      if (over != null) {
         this.visit(over);
      }

      return false;
   }

   protected SQLASTVisitor createOrderByVisitor(SQLOrderBy x) {
      if (this.dbType == null) {
         this.dbType = DbType.other;
      }

      switch (this.dbType) {
         case mysql:
            return new MySqlOrderByStatVisitor(x);
         case oracle:
            return new OracleOrderByStatVisitor(x);
         default:
            return new OrderByStatVisitor(x);
      }
   }

   public Set<TableStat.Relationship> getRelationships() {
      return this.relationships;
   }

   public List<TableStat.Column> getOrderByColumns() {
      return this.orderByColumns;
   }

   public Set<TableStat.Column> getGroupByColumns() {
      return this.groupByColumns;
   }

   public List<TableStat.Condition> getConditions() {
      return this.conditions;
   }

   public List<SQLAggregateExpr> getAggregateFunctions() {
      return this.aggregateFunctions;
   }

   public boolean visit(SQLBetweenExpr x) {
      SQLObject parent = x.getParent();
      SQLExpr test = x.getTestExpr();
      SQLExpr begin = x.getBeginExpr();
      SQLExpr end = x.getEndExpr();
      this.statExpr(test);
      this.statExpr(begin);
      this.statExpr(end);
      this.handleCondition(test, "BETWEEN", begin, end);
      return false;
   }

   public boolean visit(SQLBinaryOpExpr x) {
      SQLObject parent = x.getParent();
      if (parent instanceof SQLIfStatement) {
         return true;
      } else {
         SQLBinaryOperator op = x.getOperator();
         SQLExpr left = x.getLeft();
         SQLExpr right = x.getRight();
         if ((op == SQLBinaryOperator.BooleanAnd || op == SQLBinaryOperator.BooleanOr) && left instanceof SQLBinaryOpExpr && ((SQLBinaryOpExpr)left).getOperator() == op) {
            List<SQLExpr> groupList = SQLBinaryOpExpr.split(x, op);

            for(int i = 0; i < groupList.size(); ++i) {
               SQLExpr item = (SQLExpr)groupList.get(i);
               item.accept(this);
            }

            return false;
         } else {
            switch (op) {
               case LessThan:
               case LessThanOrEqual:
               case GreaterThan:
               case GreaterThanOrEqual:
               case Equality:
               case NotEqual:
               case LessThanOrGreater:
               case LessThanOrEqualOrGreaterThan:
               case SoudsLike:
               case Like:
               case NotLike:
               case Is:
               case IsNot:
                  this.handleCondition(left, op.name, right);
                  String reverseOp = op.name;
                  switch (op) {
                     case LessThan:
                        reverseOp = SQLBinaryOperator.GreaterThan.name;
                        break;
                     case LessThanOrEqual:
                        reverseOp = SQLBinaryOperator.GreaterThanOrEqual.name;
                        break;
                     case GreaterThan:
                        reverseOp = SQLBinaryOperator.LessThan.name;
                        break;
                     case GreaterThanOrEqual:
                        reverseOp = SQLBinaryOperator.LessThanOrEqual.name;
                  }

                  this.handleCondition(right, reverseOp, left);
                  this.handleRelationship(left, op.name, right);
                  break;
               case BooleanOr:
                  for(SQLExpr item : SQLBinaryOpExpr.split(x, op)) {
                     if (item instanceof SQLBinaryOpExpr) {
                        this.visit((SQLBinaryOpExpr)item);
                     } else {
                        item.accept(this);
                     }
                  }

                  return false;
               case Modulus:
                  if (right instanceof SQLIdentifierExpr) {
                     long hashCode64 = ((SQLIdentifierExpr)right).hashCode64();
                     if (hashCode64 == FnvHash.Constants.ISOPEN) {
                        left.accept(this);
                        return false;
                     }
                  }
            }

            if (left instanceof SQLBinaryOpExpr) {
               this.visit((SQLBinaryOpExpr)left);
            } else {
               this.statExpr(left);
            }

            this.statExpr(right);
            return false;
         }
      }
   }

   protected void handleRelationship(SQLExpr left, String operator, SQLExpr right) {
      TableStat.Column leftColumn = this.getColumn(left);
      if (leftColumn != null) {
         TableStat.Column rightColumn = this.getColumn(right);
         if (rightColumn != null) {
            TableStat.Relationship relationship = new TableStat.Relationship(leftColumn, rightColumn, operator);
            this.relationships.add(relationship);
         }
      }
   }

   protected void handleCondition(SQLExpr expr, String operator, List<SQLExpr> values) {
      this.handleCondition(expr, operator, (SQLExpr[])values.toArray(new SQLExpr[values.size()]));
   }

   protected void handleCondition(SQLExpr expr, String operator, SQLExpr... valueExprs) {
      if (expr instanceof SQLCastExpr) {
         expr = ((SQLCastExpr)expr).getExpr();
      } else if (expr instanceof SQLMethodInvokeExpr) {
         SQLMethodInvokeExpr func = (SQLMethodInvokeExpr)expr;
         List<SQLExpr> arguments = func.getArguments();
         if (func.methodNameHashCode64() == FnvHash.Constants.COALESCE && arguments.size() > 0) {
            boolean allLiteral = true;

            for(int i = 1; i < arguments.size(); ++i) {
               SQLExpr arg = (SQLExpr)arguments.get(i);
               if (!(arg instanceof SQLLiteralExpr)) {
                  allLiteral = false;
               }
            }

            if (allLiteral) {
               expr = (SQLExpr)arguments.get(0);
            }
         }
      }

      TableStat.Column column = this.getColumn(expr);
      if (column == null && expr instanceof SQLBinaryOpExpr && valueExprs.length == 1 && valueExprs[0] instanceof SQLLiteralExpr) {
         SQLBinaryOpExpr left = (SQLBinaryOpExpr)expr;
         SQLLiteralExpr right = (SQLLiteralExpr)valueExprs[0];
         if (left.getRight() instanceof SQLIntegerExpr && right instanceof SQLIntegerExpr) {
            long v0 = ((SQLIntegerExpr)left.getRight()).getNumber().longValue();
            long v1 = ((SQLIntegerExpr)right).getNumber().longValue();
            SQLBinaryOperator op = left.getOperator();
            long v;
            switch (op) {
               case Add:
                  v = v1 - v0;
                  break;
               case Subtract:
                  v = v1 + v0;
                  break;
               default:
                  return;
            }

            this.handleCondition(left.getLeft(), operator, new SQLIntegerExpr(v));
            return;
         }
      }

      if (column != null) {
         TableStat.Condition condition = null;

         for(TableStat.Condition item : this.getConditions()) {
            if (item.getColumn().equals(column) && item.getOperator().equals(operator)) {
               condition = item;
               break;
            }
         }

         if (condition == null) {
            condition = new TableStat.Condition(column, operator);
            this.conditions.add(condition);
         }

         for(SQLExpr item : valueExprs) {
            TableStat.Column valueColumn = this.getColumn(item);
            if (valueColumn == null) {
               if (item instanceof SQLCastExpr) {
                  item = ((SQLCastExpr)item).getExpr();
               }

               Object value;
               if (!(item instanceof SQLMethodInvokeExpr) && !(item instanceof SQLCurrentTimeExpr)) {
                  value = SQLEvalVisitorUtils.eval(this.dbType, item, this.parameters, false);
                  if (value == SQLEvalVisitor.EVAL_VALUE_NULL) {
                     value = null;
                  }
               } else {
                  value = item.toString();
               }

               condition.addValue(value);
            }
         }

      }
   }

   public DbType getDbType() {
      return this.dbType;
   }

   protected TableStat.Column getColumn(SQLExpr expr) {
      expr = this.unwrapExpr(expr);
      if (!(expr instanceof SQLPropertyExpr)) {
         if (expr instanceof SQLIdentifierExpr) {
            SQLIdentifierExpr identifierExpr = (SQLIdentifierExpr)expr;
            if (identifierExpr.getResolvedParameter() != null) {
               return null;
            } else if (identifierExpr.getResolvedTableSource() instanceof SQLSubqueryTableSource) {
               return null;
            } else if (identifierExpr.getResolvedDeclareItem() == null && identifierExpr.getResolvedParameter() == null) {
               String column = identifierExpr.getName();
               SQLName table = null;
               SQLTableSource tableSource = identifierExpr.getResolvedTableSource();
               if (tableSource instanceof SQLExprTableSource) {
                  SQLExpr tableSourceExpr = ((SQLExprTableSource)tableSource).getExpr();
                  if (tableSourceExpr != null && !(tableSourceExpr instanceof SQLName)) {
                     tableSourceExpr = this.unwrapExpr(tableSourceExpr);
                  }

                  if (tableSourceExpr instanceof SQLName) {
                     table = (SQLName)tableSourceExpr;
                  }
               }

               if (table != null) {
                  long tableHashCode64 = table.hashCode64();
                  long basic = tableHashCode64 ^ 46L;
                  basic *= 1099511628211L;
                  long columnHashCode64 = FnvHash.hashCode64(basic, column);
                  TableStat.Column old = (TableStat.Column)this.columns.get(columnHashCode64);
                  return old != null ? old : new TableStat.Column(table.toString(), column, columnHashCode64);
               } else {
                  return new TableStat.Column("UNKNOWN", column);
               }
            } else {
               return null;
            }
         } else {
            if (expr instanceof SQLMethodInvokeExpr) {
               SQLMethodInvokeExpr methodInvokeExpr = (SQLMethodInvokeExpr)expr;
               List<SQLExpr> arguments = methodInvokeExpr.getArguments();
               long nameHash = methodInvokeExpr.methodNameHashCode64();
               if (nameHash == FnvHash.Constants.DATE_FORMAT && arguments.size() == 2 && arguments.get(0) instanceof SQLName && arguments.get(1) instanceof SQLCharExpr) {
                  return this.getColumn((SQLExpr)arguments.get(0));
               }
            }

            return null;
         }
      } else {
         SQLPropertyExpr propertyExpr = (SQLPropertyExpr)expr;
         SQLExpr owner = propertyExpr.getOwner();
         String column = SQLUtils.normalize(propertyExpr.getName());
         if (owner instanceof SQLName) {
            SQLName table = (SQLName)owner;
            SQLObject resolvedOwnerObject = propertyExpr.getResolvedOwnerObject();
            if (resolvedOwnerObject instanceof SQLSubqueryTableSource || resolvedOwnerObject instanceof SQLCreateProcedureStatement || resolvedOwnerObject instanceof SQLCreateFunctionStatement) {
               table = null;
            }

            if (resolvedOwnerObject instanceof SQLExprTableSource) {
               SQLExpr tableSourceExpr = ((SQLExprTableSource)resolvedOwnerObject).getExpr();
               if (tableSourceExpr instanceof SQLName) {
                  table = (SQLName)tableSourceExpr;
               }
            } else if (resolvedOwnerObject instanceof SQLValuesTableSource) {
               return null;
            }

            if (table != null) {
               String tableName;
               if (table instanceof SQLIdentifierExpr) {
                  tableName = ((SQLIdentifierExpr)table).normalizedName();
               } else if (table instanceof SQLPropertyExpr) {
                  tableName = ((SQLPropertyExpr)table).normalizedName();
               } else {
                  tableName = table.toString();
               }

               long tableHashCode64 = table.hashCode64();
               if (resolvedOwnerObject instanceof SQLExprTableSource) {
                  SchemaObject schemaObject = ((SQLExprTableSource)resolvedOwnerObject).getSchemaObject();
                  if (schemaObject != null && schemaObject.getStatement() instanceof SQLCreateTableStatement) {
                     SQLColumnDefinition columnDef = schemaObject.findColumn(propertyExpr.nameHashCode64());
                     if (columnDef == null) {
                        tableName = "UNKNOWN";
                        tableHashCode64 = FnvHash.Constants.UNKNOWN;
                     }
                  }
               }

               long basic = tableHashCode64 ^ 46L;
               basic *= 1099511628211L;
               long columnHashCode64 = FnvHash.hashCode64(basic, column);
               TableStat.Column columnObj = (TableStat.Column)this.columns.get(columnHashCode64);
               if (columnObj == null) {
                  columnObj = new TableStat.Column(tableName, column, columnHashCode64);
                  if (!(resolvedOwnerObject instanceof SQLSubqueryTableSource) && !(resolvedOwnerObject instanceof SQLWithSubqueryClause.Entry)) {
                     this.columns.put(columnHashCode64, columnObj);
                  }
               }

               return columnObj;
            }
         }

         return null;
      }
   }

   private SQLExpr unwrapExpr(SQLExpr expr) {
      SQLExpr original = expr;
      int i = 0;

      while(true) {
         label99: {
            if (expr instanceof SQLMethodInvokeExpr) {
               SQLMethodInvokeExpr methodInvokeExp = (SQLMethodInvokeExpr)expr;
               if (methodInvokeExp.getArguments().size() == 1) {
                  SQLExpr firstExpr = (SQLExpr)methodInvokeExp.getArguments().get(0);
                  expr = firstExpr;
                  break label99;
               }
            }

            if (expr instanceof SQLCastExpr) {
               expr = ((SQLCastExpr)expr).getExpr();
            } else {
               if (!(expr instanceof SQLPropertyExpr)) {
                  break;
               }

               SQLPropertyExpr propertyExpr = (SQLPropertyExpr)expr;
               SQLTableSource resolvedTableSource = propertyExpr.getResolvedTableSource();
               if (resolvedTableSource instanceof SQLSubqueryTableSource) {
                  SQLSelect select = ((SQLSubqueryTableSource)resolvedTableSource).getSelect();
                  SQLSelectQueryBlock queryBlock = select.getFirstQueryBlock();
                  if (queryBlock == null) {
                     break;
                  }

                  if (queryBlock.getGroupBy() != null && original.getParent() instanceof SQLBinaryOpExpr) {
                     SQLExpr other = ((SQLBinaryOpExpr)original.getParent()).other(original);
                     if (!SQLExprUtils.isLiteralExpr(other)) {
                        break;
                     }
                  }

                  SQLSelectItem selectItem = queryBlock.findSelectItem(propertyExpr.nameHashCode64());
                  if (selectItem != null) {
                     SQLExpr selectItemExpr = selectItem.getExpr();
                     if (selectItemExpr instanceof SQLMethodInvokeExpr && ((SQLMethodInvokeExpr)selectItemExpr).getArguments().size() == 1) {
                        selectItemExpr = (SQLExpr)((SQLMethodInvokeExpr)selectItemExpr).getArguments().get(0);
                     }

                     if (selectItemExpr == expr) {
                        break;
                     }

                     expr = selectItemExpr;
                  } else {
                     if (!queryBlock.selectItemHasAllColumn()) {
                        break;
                     }

                     SQLTableSource allColumnTableSource = null;
                     SQLTableSource from = queryBlock.getFrom();
                     if (from instanceof SQLJoinTableSource) {
                        SQLSelectItem allColumnSelectItem = queryBlock.findAllColumnSelectItem();
                        if (allColumnSelectItem != null && allColumnSelectItem.getExpr() instanceof SQLPropertyExpr) {
                           SQLExpr owner = ((SQLPropertyExpr)allColumnSelectItem.getExpr()).getOwner();
                           if (owner instanceof SQLName) {
                              allColumnTableSource = from.findTableSource(((SQLName)owner).nameHashCode64());
                           }
                        }
                     } else {
                        allColumnTableSource = from;
                     }

                     if (allColumnTableSource == null) {
                        break;
                     }

                     propertyExpr = propertyExpr.clone();
                     propertyExpr.setResolvedTableSource(allColumnTableSource);
                     if (allColumnTableSource instanceof SQLExprTableSource) {
                        propertyExpr.setOwner(((SQLExprTableSource)allColumnTableSource).getExpr().clone());
                     }

                     expr = propertyExpr;
                  }
               } else {
                  if (!(resolvedTableSource instanceof SQLExprTableSource)) {
                     break;
                  }

                  SQLExprTableSource exprTableSource = (SQLExprTableSource)resolvedTableSource;
                  if (exprTableSource.getSchemaObject() != null) {
                     break;
                  }

                  SQLTableSource redirectTableSource = null;
                  SQLExpr tableSourceExpr = exprTableSource.getExpr();
                  if (tableSourceExpr instanceof SQLIdentifierExpr) {
                     redirectTableSource = ((SQLIdentifierExpr)tableSourceExpr).getResolvedTableSource();
                  } else if (tableSourceExpr instanceof SQLPropertyExpr) {
                     redirectTableSource = ((SQLPropertyExpr)tableSourceExpr).getResolvedTableSource();
                  }

                  if (redirectTableSource == resolvedTableSource) {
                     redirectTableSource = null;
                  }

                  if (redirectTableSource == null) {
                     propertyExpr = propertyExpr.clone();
                     propertyExpr.setOwner(tableSourceExpr);
                     expr = propertyExpr;
                     break;
                  }

                  propertyExpr = propertyExpr.clone();
                  if (redirectTableSource instanceof SQLExprTableSource) {
                     propertyExpr.setOwner(((SQLExprTableSource)redirectTableSource).getExpr().clone());
                  }

                  propertyExpr.setResolvedTableSource(redirectTableSource);
                  expr = propertyExpr;
               }
            }
         }

         ++i;
      }

      return expr;
   }

   public boolean visit(SQLTruncateStatement x) {
      this.setMode(x, TableStat.Mode.Delete);

      for(SQLExprTableSource tableSource : x.getTableSources()) {
         SQLName name = (SQLName)tableSource.getExpr();
         TableStat stat = this.getTableStat(name);
         stat.incrementDeleteCount();
      }

      return false;
   }

   public boolean visit(SQLDropViewStatement x) {
      this.setMode(x, TableStat.Mode.Drop);
      return true;
   }

   public boolean visit(SQLDropTableStatement x) {
      this.setMode(x, TableStat.Mode.Insert);

      for(SQLExprTableSource tableSource : x.getTableSources()) {
         SQLName name = (SQLName)tableSource.getExpr();
         TableStat stat = this.getTableStat(name);
         stat.incrementDropCount();
      }

      return false;
   }

   public boolean visit(SQLInsertStatement x) {
      if (this.repository != null && x.getParent() == null) {
         this.repository.resolve((SQLStatement)x);
      }

      this.setMode(x, TableStat.Mode.Insert);
      if (x.getTableName() instanceof SQLName) {
         String ident = x.getTableName().toString();
         TableStat stat = this.getTableStat(x.getTableName());
         stat.incrementInsertCount();
      }

      this.accept(x.getColumns());
      this.accept(x.getQuery());
      return false;
   }

   protected static void putAliasMap(Map<String, String> aliasMap, String name, String value) {
      if (aliasMap != null && name != null) {
         aliasMap.put(name.toLowerCase(), value);
      }
   }

   protected void accept(SQLObject x) {
      if (x != null) {
         x.accept(this);
      }

   }

   protected void accept(List<? extends SQLObject> nodes) {
      int i = 0;

      for(int size = nodes.size(); i < size; ++i) {
         this.accept((SQLObject)nodes.get(i));
      }

   }

   public boolean visit(SQLSelectQueryBlock x) {
      SQLTableSource from = x.getFrom();
      boolean isHiveMultiInsert = false;
      if (from == null) {
         for(SQLSelectItem selectItem : x.getSelectList()) {
            this.statExpr(selectItem.getExpr());
         }

         return false;
      } else {
         this.setMode(x, TableStat.Mode.Select);
         if (from != null) {
            from.accept(this);
         }

         SQLExprTableSource into = x.getInto();
         if (into != null && into.getExpr() instanceof SQLName) {
            SQLName intoExpr = (SQLName)into.getExpr();
            boolean isParam = intoExpr instanceof SQLIdentifierExpr && isParam((SQLIdentifierExpr)intoExpr);
            if (!isParam) {
               TableStat stat = this.getTableStat(intoExpr);
               if (stat != null) {
                  stat.incrementInsertCount();
               }
            }

            into.accept(this);
         }

         for(SQLSelectItem selectItem : x.getSelectList()) {
            if (selectItem.getClass() == SQLSelectItem.class) {
               this.statExpr(selectItem.getExpr());
            } else {
               selectItem.accept(this);
            }
         }

         SQLExpr where = x.getWhere();
         if (where != null) {
            this.statExpr(where);
         }

         SQLExpr startWith = x.getStartWith();
         if (startWith != null) {
            this.statExpr(startWith);
         }

         SQLExpr connectBy = x.getConnectBy();
         if (connectBy != null) {
            this.statExpr(connectBy);
         }

         SQLSelectGroupByClause groupBy = x.getGroupBy();
         if (groupBy != null) {
            for(SQLExpr expr : groupBy.getItems()) {
               this.statExpr(expr);
            }
         }

         List<SQLWindow> windows = x.getWindows();
         if (windows != null && windows.size() > 0) {
            for(SQLWindow window : windows) {
               window.accept(this);
            }
         }

         SQLOrderBy orderBy = x.getOrderBy();
         if (orderBy != null) {
            this.visit(orderBy);
         }

         SQLExpr first = x.getFirst();
         if (first != null) {
            this.statExpr(first);
         }

         List<SQLSelectOrderByItem> distributeBy = x.getDistributeBy();
         if (distributeBy != null) {
            for(SQLSelectOrderByItem item : distributeBy) {
               this.statExpr(item.getExpr());
            }
         }

         List<SQLSelectOrderByItem> sortBy = x.getSortBy();
         if (sortBy != null) {
            for(SQLSelectOrderByItem orderByItem : sortBy) {
               this.statExpr(orderByItem.getExpr());
            }
         }

         for(SQLExpr expr : x.getForUpdateOf()) {
            this.statExpr(expr);
         }

         return false;
      }
   }

   private static boolean isParam(SQLIdentifierExpr x) {
      return x.getResolvedParameter() != null || x.getResolvedDeclareItem() != null;
   }

   public void endVisit(SQLSelectQueryBlock x) {
      this.setModeOrigin(x);
   }

   public boolean visit(SQLJoinTableSource x) {
      SQLTableSource left = x.getLeft();
      SQLTableSource right = x.getRight();
      left.accept(this);
      right.accept(this);
      SQLExpr condition = x.getCondition();
      if (condition != null) {
         condition.accept(this);
      }

      if (x.getUsing().size() > 0 && left instanceof SQLExprTableSource && right instanceof SQLExprTableSource) {
         SQLExpr leftExpr = ((SQLExprTableSource)left).getExpr();
         SQLExpr rightExpr = ((SQLExprTableSource)right).getExpr();

         for(SQLExpr expr : x.getUsing()) {
            if (expr instanceof SQLIdentifierExpr) {
               String name = ((SQLIdentifierExpr)expr).getName();
               SQLPropertyExpr leftPropExpr = new SQLPropertyExpr(leftExpr, name);
               SQLPropertyExpr rightPropExpr = new SQLPropertyExpr(rightExpr, name);
               leftPropExpr.setResolvedTableSource(left);
               rightPropExpr.setResolvedTableSource(right);
               SQLBinaryOpExpr usingCondition = new SQLBinaryOpExpr(leftPropExpr, SQLBinaryOperator.Equality, rightPropExpr);
               usingCondition.accept(this);
            }
         }
      }

      return false;
   }

   public boolean visit(SQLPropertyExpr x) {
      TableStat.Column column = null;
      String ident = SQLUtils.normalize(x.getName());
      SQLTableSource tableSource = x.getResolvedTableSource();
      if (tableSource instanceof SQLSubqueryTableSource) {
         SQLSelect subSelect = ((SQLSubqueryTableSource)tableSource).getSelect();
         SQLSelectQueryBlock subQuery = subSelect.getQueryBlock();
         if (subQuery != null) {
            SQLTableSource subTableSource = subQuery.findTableSourceWithColumn(x.nameHashCode64());
            if (subTableSource != null) {
               tableSource = subTableSource;
            }
         }
      }

      if (tableSource instanceof SQLExprTableSource) {
         SQLExpr expr = ((SQLExprTableSource)tableSource).getExpr();
         if (expr instanceof SQLIdentifierExpr) {
            SQLIdentifierExpr table = (SQLIdentifierExpr)expr;
            SQLTableSource resolvedTableSource = table.getResolvedTableSource();
            if (resolvedTableSource instanceof SQLExprTableSource) {
               expr = ((SQLExprTableSource)resolvedTableSource).getExpr();
            }
         } else if (expr instanceof SQLPropertyExpr) {
            SQLPropertyExpr table = (SQLPropertyExpr)expr;
            SQLTableSource resolvedTableSource = table.getResolvedTableSource();
            if (resolvedTableSource instanceof SQLExprTableSource) {
               expr = ((SQLExprTableSource)resolvedTableSource).getExpr();
            }
         }

         if (expr instanceof SQLIdentifierExpr) {
            SQLIdentifierExpr table = (SQLIdentifierExpr)expr;
            SQLTableSource resolvedTableSource = table.getResolvedTableSource();
            if (resolvedTableSource instanceof SQLWithSubqueryClause.Entry) {
               return false;
            }

            String tableName = table.getName();
            SchemaObject schemaObject = ((SQLExprTableSource)tableSource).getSchemaObject();
            if (schemaObject != null && schemaObject.getStatement() instanceof SQLCreateTableStatement && !"*".equals(ident)) {
               SQLColumnDefinition columnDef = schemaObject.findColumn(x.nameHashCode64());
               if (columnDef == null) {
                  column = this.addColumn("UNKNOWN", ident);
               }
            }

            if (column == null) {
               column = this.addColumn((SQLName)table, ident);
            }

            if (this.isParentGroupBy(x)) {
               this.groupByColumns.add(column);
            }
         } else if (expr instanceof SQLPropertyExpr) {
            SQLPropertyExpr table = (SQLPropertyExpr)expr;
            column = this.addColumn((SQLName)table, ident);
            if (column != null && this.isParentGroupBy(x)) {
               this.groupByColumns.add(column);
            }
         } else if (expr instanceof SQLMethodInvokeExpr) {
            SQLMethodInvokeExpr methodInvokeExpr = (SQLMethodInvokeExpr)expr;
            if ("table".equalsIgnoreCase(methodInvokeExpr.getMethodName()) && methodInvokeExpr.getArguments().size() == 1 && methodInvokeExpr.getArguments().get(0) instanceof SQLName) {
               SQLName table = (SQLName)methodInvokeExpr.getArguments().get(0);
               String tableName = null;
               if (table instanceof SQLPropertyExpr) {
                  SQLPropertyExpr propertyExpr = (SQLPropertyExpr)table;
                  SQLIdentifierExpr owner = (SQLIdentifierExpr)propertyExpr.getOwner();
                  if (propertyExpr.getResolvedTableSource() != null && propertyExpr.getResolvedTableSource() instanceof SQLExprTableSource) {
                     SQLExpr resolveExpr = ((SQLExprTableSource)propertyExpr.getResolvedTableSource()).getExpr();
                     if (resolveExpr instanceof SQLName) {
                        tableName = resolveExpr.toString() + "." + propertyExpr.getName();
                     }
                  }
               }

               if (tableName == null) {
                  tableName = table.toString();
               }

               column = this.addColumn(tableName, ident);
            }
         }
      } else {
         if (tableSource instanceof SQLWithSubqueryClause.Entry || tableSource instanceof SQLSubqueryTableSource || tableSource instanceof SQLUnionQueryTableSource || tableSource instanceof SQLValuesTableSource || tableSource instanceof SQLLateralViewTableSource) {
            return false;
         }

         if (x.getResolvedProcudure() != null) {
            return false;
         }

         if (x.getResolvedOwnerObject() instanceof SQLParameter) {
            return false;
         }

         boolean skip = false;

         for(SQLObject parent = x.getParent(); parent != null; parent = parent.getParent()) {
            if (parent instanceof SQLSelectQueryBlock) {
               SQLTableSource from = ((SQLSelectQueryBlock)parent).getFrom();
               if (from instanceof SQLValuesTableSource) {
                  skip = true;
                  break;
               }
            } else if (parent instanceof SQLSelectQuery) {
               break;
            }
         }

         if (!skip) {
            column = this.handleUnknownColumn(ident);
         }
      }

      if (column != null) {
         SQLObject parent = x.getParent();
         if (parent instanceof SQLSelectOrderByItem) {
            parent = parent.getParent();
            if (parent instanceof SQLIndexDefinition) {
               parent = parent.getParent();
            }
         }

         if (parent instanceof SQLPrimaryKey) {
            column.setPrimaryKey(true);
         } else if (parent instanceof SQLUnique) {
            column.setUnique(true);
         }

         this.setColumn(x, column);
      }

      return false;
   }

   protected boolean isPseudoColumn(long hash) {
      return false;
   }

   public boolean visit(SQLIdentifierExpr x) {
      if (isParam(x)) {
         return false;
      } else {
         SQLTableSource tableSource = x.getResolvedTableSource();
         if (x.getParent() instanceof SQLSelectOrderByItem) {
            SQLSelectOrderByItem selectOrderByItem = (SQLSelectOrderByItem)x.getParent();
            if (selectOrderByItem.getResolvedSelectItem() != null) {
               return false;
            }
         }

         if (tableSource != null || x.getResolvedParameter() == null && x.getResolvedDeclareItem() == null) {
            long hash = x.nameHashCode64();
            if (this.isPseudoColumn(hash)) {
               return false;
            } else if ((hash == FnvHash.Constants.LEVEL || hash == FnvHash.Constants.CONNECT_BY_ISCYCLE || hash == FnvHash.Constants.ROWNUM) && x.getResolvedColumn() == null && tableSource == null) {
               return false;
            } else {
               TableStat.Column column = null;
               String ident = x.normalizedName();
               if (tableSource instanceof SQLExprTableSource) {
                  SQLExpr expr = ((SQLExprTableSource)tableSource).getExpr();
                  if (expr instanceof SQLMethodInvokeExpr) {
                     SQLMethodInvokeExpr func = (SQLMethodInvokeExpr)expr;
                     if (func.methodNameHashCode64() == FnvHash.Constants.ANN) {
                        expr = (SQLExpr)func.getArguments().get(0);
                     }
                  }

                  if (expr instanceof SQLIdentifierExpr) {
                     SQLIdentifierExpr table = (SQLIdentifierExpr)expr;
                     column = this.addColumn((SQLName)table, ident);
                     if (column != null && this.isParentGroupBy(x)) {
                        this.groupByColumns.add(column);
                     }
                  } else if (!(expr instanceof SQLPropertyExpr) && !(expr instanceof SQLDbLinkExpr)) {
                     if (expr instanceof SQLMethodInvokeExpr) {
                        SQLMethodInvokeExpr methodInvokeExpr = (SQLMethodInvokeExpr)expr;
                        if ("table".equalsIgnoreCase(methodInvokeExpr.getMethodName()) && methodInvokeExpr.getArguments().size() == 1 && methodInvokeExpr.getArguments().get(0) instanceof SQLName) {
                           SQLName table = (SQLName)methodInvokeExpr.getArguments().get(0);
                           String tableName = null;
                           if (table instanceof SQLPropertyExpr) {
                              SQLPropertyExpr propertyExpr = (SQLPropertyExpr)table;
                              SQLIdentifierExpr owner = (SQLIdentifierExpr)propertyExpr.getOwner();
                              if (propertyExpr.getResolvedTableSource() != null && propertyExpr.getResolvedTableSource() instanceof SQLExprTableSource) {
                                 SQLExpr resolveExpr = ((SQLExprTableSource)propertyExpr.getResolvedTableSource()).getExpr();
                                 if (resolveExpr instanceof SQLName) {
                                    tableName = resolveExpr.toString() + "." + propertyExpr.getName();
                                 }
                              }
                           }

                           if (tableName == null) {
                              tableName = table.toString();
                           }

                           column = this.addColumn(tableName, ident);
                        }
                     }
                  } else {
                     String tableName;
                     if (expr instanceof SQLPropertyExpr) {
                        tableName = ((SQLPropertyExpr)expr).normalizedName();
                     } else {
                        tableName = expr.toString();
                     }

                     column = this.addColumn(tableName, ident);
                     if (column != null && this.isParentGroupBy(x)) {
                        this.groupByColumns.add(column);
                     }
                  }
               } else {
                  if (tableSource instanceof SQLWithSubqueryClause.Entry || tableSource instanceof SQLSubqueryTableSource || tableSource instanceof SQLLateralViewTableSource) {
                     return false;
                  }

                  boolean skip = false;

                  for(SQLObject parent = x.getParent(); parent != null; parent = parent.getParent()) {
                     if (parent instanceof SQLSelectQueryBlock) {
                        SQLTableSource from = ((SQLSelectQueryBlock)parent).getFrom();
                        if (from instanceof SQLValuesTableSource) {
                           skip = true;
                           break;
                        }
                     } else if (parent instanceof SQLSelectQuery) {
                        break;
                     }
                  }

                  if (x.getParent() instanceof SQLMethodInvokeExpr && ((SQLMethodInvokeExpr)x.getParent()).methodNameHashCode64() == FnvHash.Constants.ANN) {
                     skip = true;
                  }

                  if (!skip) {
                     column = this.handleUnknownColumn(ident);
                  }
               }

               if (column != null) {
                  SQLObject parent = x.getParent();
                  if (parent instanceof SQLSelectOrderByItem) {
                     parent = parent.getParent();
                     if (parent instanceof SQLIndexDefinition) {
                        parent = parent.getParent();
                     }
                  }

                  if (parent instanceof SQLPrimaryKey) {
                     column.setPrimaryKey(true);
                  } else if (parent instanceof SQLUnique) {
                     column.setUnique(true);
                  }

                  this.setColumn(x, column);
               }

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

   private boolean isParentSelectItem(SQLObject parent) {
      for(int i = 0; parent != null && i <= 100; ++i) {
         if (parent instanceof SQLSelectItem) {
            return true;
         }

         if (parent instanceof SQLSelectQueryBlock) {
            return false;
         }

         parent = parent.getParent();
      }

      return false;
   }

   private boolean isParentGroupBy(SQLObject parent) {
      while(parent != null) {
         if (parent instanceof SQLSelectItem) {
            return false;
         }

         if (parent instanceof SQLSelectGroupByClause) {
            return true;
         }

         parent = parent.getParent();
      }

      return false;
   }

   private void setColumn(SQLExpr x, TableStat.Column column) {
      SQLObject current = x;

      for(int i = 0; i < 100; ++i) {
         SQLObject parent = current.getParent();
         if (parent == null) {
            break;
         }

         if (parent instanceof SQLSelectQueryBlock) {
            SQLSelectQueryBlock query = (SQLSelectQueryBlock)parent;
            if (query.getWhere() == current) {
               column.setWhere(true);
            }
            break;
         }

         if (parent instanceof SQLSelectGroupByClause) {
            SQLSelectGroupByClause groupBy = (SQLSelectGroupByClause)parent;
            if (current == groupBy.getHaving()) {
               column.setHaving(true);
            } else if (groupBy.getItems().contains(current)) {
               column.setGroupBy(true);
            }
            break;
         }

         if (this.isParentSelectItem(parent)) {
            column.setSelec(true);
            break;
         }

         if (parent instanceof SQLJoinTableSource) {
            SQLJoinTableSource join = (SQLJoinTableSource)parent;
            if (join.getCondition() == current) {
               column.setJoin(true);
            }
            break;
         }

         current = parent;
      }

   }

   protected TableStat.Column handleUnknownColumn(String columnName) {
      return this.addColumn("UNKNOWN", columnName);
   }

   public boolean visit(SQLAllColumnExpr x) {
      SQLTableSource tableSource = x.getResolvedTableSource();
      if (tableSource == null) {
         return false;
      } else {
         this.statAllColumn(x, tableSource);
         return false;
      }
   }

   private void statAllColumn(SQLAllColumnExpr x, SQLTableSource tableSource) {
      if (tableSource instanceof SQLExprTableSource) {
         this.statAllColumn(x, (SQLExprTableSource)tableSource);
      } else {
         if (tableSource instanceof SQLJoinTableSource) {
            SQLJoinTableSource join = (SQLJoinTableSource)tableSource;
            this.statAllColumn(x, join.getLeft());
            this.statAllColumn(x, join.getRight());
         }

      }
   }

   private void statAllColumn(SQLAllColumnExpr x, SQLExprTableSource tableSource) {
      SQLName expr = tableSource.getName();
      SQLCreateTableStatement createStmt = null;
      SchemaObject tableObject = tableSource.getSchemaObject();
      if (tableObject != null) {
         SQLStatement stmt = tableObject.getStatement();
         if (stmt instanceof SQLCreateTableStatement) {
            createStmt = (SQLCreateTableStatement)stmt;
         }
      }

      if (createStmt != null && createStmt.getTableElementList().size() > 0) {
         SQLName tableName = createStmt.getName();

         for(SQLTableElement e : createStmt.getTableElementList()) {
            if (e instanceof SQLColumnDefinition) {
               SQLColumnDefinition columnDefinition = (SQLColumnDefinition)e;
               SQLName columnName = columnDefinition.getName();
               TableStat.Column column = this.addColumn(tableName, columnName.toString());
               if (this.isParentSelectItem(x.getParent())) {
                  column.setSelec(true);
               }
            }
         }
      } else if (expr != null) {
         TableStat.Column column = this.addColumn(expr, "*");
         if (this.isParentSelectItem(x.getParent())) {
            column.setSelec(true);
         }
      }

   }

   public Map<TableStat.Name, TableStat> getTables() {
      return this.tableStats;
   }

   public boolean containsTable(String tableName) {
      return this.tableStats.containsKey(new TableStat.Name(tableName));
   }

   public boolean containsColumn(String tableName, String columnName) {
      int p = tableName.indexOf(46);
      long hashCode;
      if (p != -1) {
         SQLExpr owner = SQLUtils.toSQLExpr(tableName, this.dbType);
         hashCode = (new SQLPropertyExpr(owner, columnName)).hashCode64();
      } else {
         hashCode = FnvHash.hashCode64(tableName, columnName);
      }

      return this.columns.containsKey(hashCode);
   }

   public Collection<TableStat.Column> getColumns() {
      return this.columns.values();
   }

   public TableStat.Column getColumn(String tableName, String columnName) {
      TableStat.Column column = new TableStat.Column(tableName, columnName);
      return (TableStat.Column)this.columns.get(column.hashCode64());
   }

   public boolean visit(SQLSelectStatement x) {
      if (this.repository != null && x.getParent() == null) {
         this.repository.resolve(x);
      }

      this.visit(x.getSelect());
      return false;
   }

   public void endVisit(SQLSelectStatement x) {
   }

   public boolean visit(SQLWithSubqueryClause.Entry x) {
      String alias = x.getAlias();
      SQLWithSubqueryClause with = (SQLWithSubqueryClause)x.getParent();
      if (Boolean.TRUE == with.getRecursive()) {
         SQLSelect select = x.getSubQuery();
         if (select != null) {
            select.accept(this);
         } else {
            x.getReturningStatement().accept(this);
         }
      } else {
         SQLSelect select = x.getSubQuery();
         if (select != null) {
            select.accept(this);
         } else {
            x.getReturningStatement().accept(this);
         }
      }

      return false;
   }

   public boolean visit(SQLSubqueryTableSource x) {
      x.getSelect().accept(this);
      return false;
   }

   protected boolean isSimpleExprTableSource(SQLExprTableSource x) {
      return x.getExpr() instanceof SQLName;
   }

   public TableStat getTableStat(SQLExprTableSource tableSource) {
      return this.getTableStatWithUnwrap(tableSource.getExpr());
   }

   protected TableStat getTableStatWithUnwrap(SQLExpr expr) {
      SQLExpr identExpr = null;
      expr = this.unwrapExpr(expr);
      if (expr instanceof SQLIdentifierExpr) {
         SQLIdentifierExpr identifierExpr = (SQLIdentifierExpr)expr;
         if (identifierExpr.nameHashCode64() == FnvHash.Constants.DUAL) {
            return null;
         }

         if (this.isSubQueryOrParamOrVariant(identifierExpr)) {
            return null;
         }
      }

      SQLTableSource tableSource = null;
      if (expr instanceof SQLIdentifierExpr) {
         tableSource = ((SQLIdentifierExpr)expr).getResolvedTableSource();
      } else if (expr instanceof SQLPropertyExpr) {
         tableSource = ((SQLPropertyExpr)expr).getResolvedTableSource();
      }

      if (tableSource instanceof SQLExprTableSource) {
         SQLExpr tableSourceExpr = ((SQLExprTableSource)tableSource).getExpr();
         if (tableSourceExpr instanceof SQLName) {
            identExpr = tableSourceExpr;
         }
      }

      if (identExpr == null) {
         identExpr = expr;
      }

      return identExpr instanceof SQLName ? this.getTableStat((SQLName)identExpr) : this.getTableStat(identExpr.toString());
   }

   public boolean visit(SQLExprTableSource x) {
      SQLExpr expr = x.getExpr();
      if (expr instanceof SQLMethodInvokeExpr) {
         SQLMethodInvokeExpr func = (SQLMethodInvokeExpr)expr;
         if (func.methodNameHashCode64() == FnvHash.Constants.ANN) {
            expr = (SQLExpr)func.getArguments().get(0);
         }
      }

      if (this.isSimpleExprTableSource(x)) {
         TableStat stat = this.getTableStatWithUnwrap(expr);
         if (stat == null) {
            return false;
         }

         TableStat.Mode mode = this.getMode();
         if (mode != null) {
            switch (mode) {
               case Delete:
                  stat.incrementDeleteCount();
                  break;
               case Insert:
                  stat.incrementInsertCount();
                  break;
               case Update:
                  stat.incrementUpdateCount();
                  break;
               case Select:
                  stat.incrementSelectCount();
                  break;
               case Merge:
                  stat.incrementMergeCount();
                  break;
               case Drop:
                  stat.incrementDropCount();
            }
         }
      } else {
         this.accept(expr);
      }

      return false;
   }

   protected boolean isSubQueryOrParamOrVariant(SQLIdentifierExpr identifierExpr) {
      SQLObject resolvedColumnObject = identifierExpr.getResolvedColumnObject();
      if (!(resolvedColumnObject instanceof SQLWithSubqueryClause.Entry) && !(resolvedColumnObject instanceof SQLParameter) && !(resolvedColumnObject instanceof SQLDeclareItem)) {
         SQLObject resolvedOwnerObject = identifierExpr.getResolvedOwnerObject();
         return resolvedOwnerObject instanceof SQLSubqueryTableSource || resolvedOwnerObject instanceof SQLWithSubqueryClause.Entry;
      } else {
         return true;
      }
   }

   protected boolean isSubQueryOrParamOrVariant(SQLPropertyExpr x) {
      SQLObject resolvedOwnerObject = x.getResolvedOwnerObject();
      if (!(resolvedOwnerObject instanceof SQLSubqueryTableSource) && !(resolvedOwnerObject instanceof SQLWithSubqueryClause.Entry)) {
         SQLExpr owner = x.getOwner();
         if (owner instanceof SQLIdentifierExpr && this.isSubQueryOrParamOrVariant((SQLIdentifierExpr)owner)) {
            return true;
         } else {
            SQLTableSource tableSource = x.getResolvedTableSource();
            if (tableSource instanceof SQLExprTableSource) {
               SQLExprTableSource exprTableSource = (SQLExprTableSource)tableSource;
               if (exprTableSource.getSchemaObject() != null) {
                  return false;
               }

               SQLExpr expr = exprTableSource.getExpr();
               if (expr instanceof SQLIdentifierExpr) {
                  return this.isSubQueryOrParamOrVariant((SQLIdentifierExpr)expr);
               }

               if (expr instanceof SQLPropertyExpr) {
                  return this.isSubQueryOrParamOrVariant((SQLPropertyExpr)expr);
               }
            }

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

   public boolean visit(SQLSelectItem x) {
      this.statExpr(x.getExpr());
      return false;
   }

   public boolean visit(SQLSelect x) {
      SQLWithSubqueryClause with = x.getWithSubQuery();
      if (with != null) {
         with.accept(this);
      }

      SQLSelectQuery query = x.getQuery();
      if (query != null) {
         query.accept(this);
      }

      SQLOrderBy orderBy = x.getOrderBy();
      if (orderBy != null) {
         this.accept(x.getOrderBy());
      }

      return false;
   }

   public boolean visit(SQLAggregateExpr x) {
      this.aggregateFunctions.add(x);
      this.accept(x.getArguments());
      this.accept(x.getOrderBy());
      this.accept(x.getOver());
      return false;
   }

   public boolean visit(SQLMethodInvokeExpr x) {
      this.functions.add(x);
      this.accept(x.getArguments());
      return false;
   }

   public boolean visit(SQLUpdateStatement x) {
      if (this.repository != null && x.getParent() == null) {
         this.repository.resolve((SQLStatement)x);
      }

      this.setMode(x, TableStat.Mode.Update);
      SQLTableSource tableSource = x.getTableSource();
      if (tableSource instanceof SQLExprTableSource) {
         SQLName identName = ((SQLExprTableSource)tableSource).getName();
         TableStat stat = this.getTableStat(identName);
         stat.incrementUpdateCount();
      } else {
         tableSource.accept(this);
      }

      SQLTableSource from = x.getFrom();
      if (from != null) {
         from.accept(this);
      }

      List<SQLUpdateSetItem> items = x.getItems();
      int i = 0;

      for(int size = items.size(); i < size; ++i) {
         SQLUpdateSetItem item = (SQLUpdateSetItem)items.get(i);
         this.visit(item);
      }

      SQLExpr where = x.getWhere();
      if (where != null) {
         where.accept(this);
      }

      return false;
   }

   public boolean visit(SQLUpdateSetItem x) {
      SQLExpr column = x.getColumn();
      if (column != null) {
         this.statExpr(column);
         TableStat.Column columnStat = this.getColumn(column);
         if (columnStat != null) {
            columnStat.setUpdate(true);
         }
      }

      SQLExpr value = x.getValue();
      if (value != null) {
         this.statExpr(value);
      }

      return false;
   }

   public boolean visit(SQLDeleteStatement x) {
      if (this.repository != null && x.getParent() == null) {
         this.repository.resolve((SQLStatement)x);
      }

      this.setMode(x, TableStat.Mode.Delete);
      if (x.getTableSource() instanceof SQLSubqueryTableSource) {
         SQLSelectQuery selectQuery = ((SQLSubqueryTableSource)x.getTableSource()).getSelect().getQuery();
         if (selectQuery instanceof SQLSelectQueryBlock) {
            SQLSelectQueryBlock subQueryBlock = (SQLSelectQueryBlock)selectQuery;
            subQueryBlock.getWhere().accept(this);
         }
      }

      TableStat stat = this.getTableStat(x.getTableName());
      stat.incrementDeleteCount();
      SQLExpr where = x.getWhere();
      if (where != null) {
         where.accept(this);
      }

      return false;
   }

   public boolean visit(SQLInListExpr x) {
      if (x.isNot()) {
         this.handleCondition(x.getExpr(), "NOT IN", x.getTargetList());
      } else {
         this.handleCondition(x.getExpr(), "IN", x.getTargetList());
      }

      return true;
   }

   public boolean visit(SQLInSubQueryExpr x) {
      if (x.isNot()) {
         this.handleCondition(x.getExpr(), "NOT IN");
      } else {
         this.handleCondition(x.getExpr(), "IN");
      }

      return true;
   }

   public void endVisit(SQLDeleteStatement x) {
   }

   public void endVisit(SQLUpdateStatement x) {
   }

   public boolean visit(SQLCreateTableStatement x) {
      if (this.repository != null && x.getParent() == null) {
         this.repository.resolve((SQLStatement)x);
      }

      for(SQLTableElement e : x.getTableElementList()) {
         e.setParent(x);
      }

      TableStat stat = this.getTableStat(x.getName());
      stat.incrementCreateCount();
      this.accept(x.getTableElementList());
      if (x.getInherits() != null) {
         x.getInherits().accept(this);
      }

      if (x.getSelect() != null) {
         x.getSelect().accept(this);
      }

      SQLExprTableSource like = x.getLike();
      if (like != null) {
         like.accept(this);
      }

      return false;
   }

   public boolean visit(SQLColumnDefinition x) {
      SQLName tableName = null;
      SQLObject parent = x.getParent();
      if (parent instanceof SQLCreateTableStatement) {
         tableName = ((SQLCreateTableStatement)parent).getName();
      }

      if (tableName == null) {
         return true;
      } else {
         String columnName = x.getName().toString();
         TableStat.Column column = this.addColumn(tableName, columnName);
         if (x.getDataType() != null) {
            column.setDataType(x.getDataType().getName());
         }

         for(SQLColumnConstraint item : x.getConstraints()) {
            if (item instanceof SQLPrimaryKey) {
               column.setPrimaryKey(true);
            } else if (item instanceof SQLUnique) {
               column.setUnique(true);
            }
         }

         return false;
      }
   }

   public boolean visit(SQLCallStatement x) {
      return false;
   }

   public void endVisit(SQLCommentStatement x) {
   }

   public boolean visit(SQLCommentStatement x) {
      return false;
   }

   public boolean visit(SQLCurrentOfCursorExpr x) {
      return false;
   }

   public boolean visit(SQLAlterTableAddColumn x) {
      SQLAlterTableStatement stmt = (SQLAlterTableStatement)x.getParent();
      String table = stmt.getName().toString();

      for(SQLColumnDefinition column : x.getColumns()) {
         String columnName = SQLUtils.normalize(column.getName().toString());
         this.addColumn(stmt.getName(), columnName);
      }

      return false;
   }

   public void endVisit(SQLAlterTableAddColumn x) {
   }

   public boolean visit(SQLRollbackStatement x) {
      return false;
   }

   public boolean visit(SQLCreateViewStatement x) {
      if (this.repository != null && x.getParent() == null) {
         this.repository.resolve((SQLStatement)x);
      }

      x.getSubQuery().accept(this);
      return false;
   }

   public boolean visit(SQLAlterViewStatement x) {
      if (this.repository != null && x.getParent() == null) {
         this.repository.resolve((SQLStatement)x);
      }

      x.getSubQuery().accept(this);
      return false;
   }

   public boolean visit(SQLAlterTableDropForeignKey x) {
      return false;
   }

   public boolean visit(SQLUseStatement x) {
      return false;
   }

   public boolean visit(SQLAlterTableDisableConstraint x) {
      return false;
   }

   public boolean visit(SQLAlterTableEnableConstraint x) {
      return false;
   }

   public boolean visit(SQLAlterTableStatement x) {
      if (this.repository != null && x.getParent() == null) {
         this.repository.resolve((SQLStatement)x);
      }

      TableStat stat = this.getTableStat(x.getName());
      stat.incrementAlterCount();

      for(SQLAlterTableItem item : x.getItems()) {
         item.setParent(x);
         item.accept(this);
      }

      return false;
   }

   public boolean visit(SQLAlterTableDropConstraint x) {
      return false;
   }

   public boolean visit(SQLDropIndexStatement x) {
      this.setMode(x, TableStat.Mode.DropIndex);
      SQLExprTableSource table = x.getTableName();
      if (table != null) {
         SQLName name = (SQLName)table.getExpr();
         TableStat stat = this.getTableStat(name);
         stat.incrementDropIndexCount();
      }

      return false;
   }

   public boolean visit(SQLCreateIndexStatement x) {
      this.setMode(x, TableStat.Mode.CreateIndex);
      SQLName table = (SQLName)((SQLExprTableSource)x.getTable()).getExpr();
      TableStat stat = this.getTableStat(table);
      stat.incrementCreateIndexCount();

      for(SQLSelectOrderByItem item : x.getItems()) {
         SQLExpr expr = item.getExpr();
         if (expr instanceof SQLIdentifierExpr) {
            SQLIdentifierExpr identExpr = (SQLIdentifierExpr)expr;
            String columnName = identExpr.getName();
            this.addColumn(table, columnName);
         } else if (expr instanceof SQLMethodInvokeExpr) {
            SQLMethodInvokeExpr methodInvokeExpr = (SQLMethodInvokeExpr)expr;
            if (methodInvokeExpr.getArguments().size() == 1) {
               SQLExpr param = (SQLExpr)methodInvokeExpr.getArguments().get(0);
               if (param instanceof SQLIdentifierExpr) {
                  SQLIdentifierExpr identExpr = (SQLIdentifierExpr)param;
                  String columnName = identExpr.getName();
                  this.addColumn(table, columnName);
               }
            }
         }
      }

      return false;
   }

   public boolean visit(SQLForeignKeyImpl x) {
      for(SQLName column : x.getReferencingColumns()) {
         column.accept(this);
      }

      SQLName table = x.getReferencedTableName();
      TableStat stat = this.getTableStat(x.getReferencedTableName());
      stat.incrementReferencedCount();

      for(SQLName column : x.getReferencedColumns()) {
         String columnName = column.getSimpleName();
         this.addColumn(table, columnName);
      }

      return false;
   }

   public boolean visit(SQLDropSequenceStatement x) {
      return false;
   }

   public boolean visit(SQLDropTriggerStatement x) {
      return false;
   }

   public boolean visit(SQLDropUserStatement x) {
      return false;
   }

   public boolean visit(SQLGrantStatement x) {
      if (x.getResource() != null && (x.getResourceType() == null || x.getResourceType() == SQLObjectType.TABLE)) {
         x.getResource().accept(this);
      }

      return false;
   }

   public boolean visit(SQLRevokeStatement x) {
      if (x.getResource() != null) {
         x.getResource().accept(this);
      }

      return false;
   }

   public boolean visit(SQLDropDatabaseStatement x) {
      return false;
   }

   public boolean visit(SQLAlterTableAddIndex x) {
      for(SQLSelectOrderByItem item : x.getColumns()) {
         item.accept(this);
      }

      SQLName table = ((SQLAlterTableStatement)x.getParent()).getName();
      TableStat tableStat = this.getTableStat(table);
      tableStat.incrementCreateIndexCount();
      return false;
   }

   public boolean visit(SQLCheck x) {
      x.getExpr().accept(this);
      return false;
   }

   public boolean visit(SQLDefault x) {
      x.getExpr().accept(this);
      x.getColumn().accept(this);
      return false;
   }

   public boolean visit(SQLCreateTriggerStatement x) {
      SQLExprTableSource on = x.getOn();
      on.accept(this);
      return false;
   }

   public boolean visit(SQLDropFunctionStatement x) {
      return false;
   }

   public boolean visit(SQLDropTableSpaceStatement x) {
      return false;
   }

   public boolean visit(SQLDropProcedureStatement x) {
      return false;
   }

   public boolean visit(SQLAlterTableRename x) {
      return false;
   }

   public boolean visit(SQLArrayExpr x) {
      this.accept(x.getValues());
      SQLExpr exp = x.getExpr();
      if (exp instanceof SQLIdentifierExpr && ((SQLIdentifierExpr)exp).getName().equals("ARRAY")) {
         return false;
      } else {
         if (exp != null) {
            exp.accept(this);
         }

         return false;
      }
   }

   public boolean visit(SQLOpenStatement x) {
      return false;
   }

   public boolean visit(SQLFetchStatement x) {
      return false;
   }

   public boolean visit(SQLDropMaterializedViewStatement x) {
      return false;
   }

   public boolean visit(SQLShowMaterializedViewStatement x) {
      return false;
   }

   public boolean visit(SQLRefreshMaterializedViewStatement x) {
      return false;
   }

   public boolean visit(SQLCloseStatement x) {
      return false;
   }

   public boolean visit(SQLCreateProcedureStatement x) {
      if (this.repository != null && x.getParent() == null) {
         this.repository.resolve((SQLStatement)x);
      }

      this.accept(x.getBlock());
      return false;
   }

   public boolean visit(SQLCreateFunctionStatement x) {
      if (this.repository != null && x.getParent() == null) {
         this.repository.resolve((SQLStatement)x);
      }

      this.accept(x.getBlock());
      return false;
   }

   public boolean visit(SQLBlockStatement x) {
      if (this.repository != null && x.getParent() == null) {
         this.repository.resolve((SQLStatement)x);
      }

      for(SQLParameter param : x.getParameters()) {
         param.setParent(x);
         param.accept(this);
      }

      for(SQLStatement stmt : x.getStatementList()) {
         stmt.accept(this);
      }

      SQLStatement exception = x.getException();
      if (exception != null) {
         exception.accept(this);
      }

      return false;
   }

   public boolean visit(SQLShowTablesStatement x) {
      return false;
   }

   public boolean visit(SQLDeclareItem x) {
      return false;
   }

   public boolean visit(SQLPartitionByHash x) {
      return false;
   }

   public boolean visit(SQLPartitionByRange x) {
      return false;
   }

   public boolean visit(SQLPartitionByList x) {
      return false;
   }

   public boolean visit(SQLPartition x) {
      return false;
   }

   public boolean visit(SQLSubPartition x) {
      return false;
   }

   public boolean visit(SQLSubPartitionByHash x) {
      return false;
   }

   public boolean visit(SQLPartitionValue x) {
      return false;
   }

   public boolean visit(SQLAlterDatabaseStatement x) {
      return false;
   }

   public boolean visit(SQLAlterTableConvertCharSet x) {
      return false;
   }

   public boolean visit(SQLAlterTableDropPartition x) {
      return false;
   }

   public boolean visit(SQLAlterTableReOrganizePartition x) {
      return false;
   }

   public boolean visit(SQLAlterTableCoalescePartition x) {
      return false;
   }

   public boolean visit(SQLAlterTableTruncatePartition x) {
      return false;
   }

   public boolean visit(SQLAlterTableDiscardPartition x) {
      return false;
   }

   public boolean visit(SQLAlterTableImportPartition x) {
      return false;
   }

   public boolean visit(SQLAlterTableAnalyzePartition x) {
      return false;
   }

   public boolean visit(SQLAlterTableCheckPartition x) {
      return false;
   }

   public boolean visit(SQLAlterTableOptimizePartition x) {
      return false;
   }

   public boolean visit(SQLAlterTableRebuildPartition x) {
      return false;
   }

   public boolean visit(SQLAlterTableRepairPartition x) {
      return false;
   }

   public boolean visit(SQLSequenceExpr x) {
      return false;
   }

   public boolean visit(SQLMergeStatement x) {
      if (this.repository != null && x.getParent() == null) {
         this.repository.resolve((SQLStatement)x);
      }

      this.setMode(x.getUsing(), TableStat.Mode.Select);
      x.getUsing().accept(this);
      this.setMode(x, TableStat.Mode.Merge);
      SQLTableSource into = x.getInto();
      if (into instanceof SQLExprTableSource) {
         String ident = ((SQLExprTableSource)into).getExpr().toString();
         TableStat stat = this.getTableStat(ident);
         stat.incrementMergeCount();
      } else {
         into.accept(this);
      }

      x.getOn().accept(this);
      if (x.getUpdateClause() != null) {
         x.getUpdateClause().accept(this);
      }

      if (x.getInsertClause() != null) {
         x.getInsertClause().accept(this);
      }

      return false;
   }

   public boolean visit(SQLSetStatement x) {
      return false;
   }

   public List<SQLMethodInvokeExpr> getFunctions() {
      return this.functions;
   }

   public boolean visit(SQLCreateSequenceStatement x) {
      return false;
   }

   public boolean visit(SQLAlterTableAddConstraint x) {
      SQLConstraint constraint = x.getConstraint();
      if (constraint instanceof SQLUniqueConstraint) {
         SQLAlterTableStatement stmt = (SQLAlterTableStatement)x.getParent();
         TableStat tableStat = this.getTableStat(stmt.getName());
         tableStat.incrementCreateIndexCount();
      }

      return true;
   }

   public boolean visit(SQLAlterTableDropIndex x) {
      SQLAlterTableStatement stmt = (SQLAlterTableStatement)x.getParent();
      TableStat tableStat = this.getTableStat(stmt.getName());
      tableStat.incrementDropIndexCount();
      return false;
   }

   public boolean visit(SQLAlterTableDropPrimaryKey x) {
      SQLAlterTableStatement stmt = (SQLAlterTableStatement)x.getParent();
      TableStat tableStat = this.getTableStat(stmt.getName());
      tableStat.incrementDropIndexCount();
      return false;
   }

   public boolean visit(SQLAlterTableDropKey x) {
      SQLAlterTableStatement stmt = (SQLAlterTableStatement)x.getParent();
      TableStat tableStat = this.getTableStat(stmt.getName());
      tableStat.incrementDropIndexCount();
      return false;
   }

   public boolean visit(SQLDescribeStatement x) {
      SQLName tableName = x.getObject();
      this.getTableStat(x.getObject());
      SQLName column = x.getColumn();
      if (column != null) {
         String columnName = column.toString();
         this.addColumn(tableName, columnName);
      }

      return false;
   }

   public boolean visit(SQLExplainStatement x) {
      if (this.repository != null && x.getParent() == null) {
         this.repository.resolve((SQLStatement)x);
      }

      if (x.getStatement() != null) {
         this.accept(x.getStatement());
      }

      return false;
   }

   public boolean visit(SQLCreateMaterializedViewStatement x) {
      if (this.repository != null && x.getParent() == null) {
         this.repository.resolve((SQLStatement)x);
      }

      SQLSelect query = x.getQuery();
      if (query != null) {
         query.accept(this);
      }

      return false;
   }

   public boolean visit(SQLReplaceStatement x) {
      if (this.repository != null && x.getParent() == null) {
         this.repository.resolve((SQLStatement)x);
      }

      this.setMode(x, TableStat.Mode.Replace);
      SQLName tableName = x.getTableName();
      TableStat stat = this.getTableStat(tableName);
      if (stat != null) {
         stat.incrementInsertCount();
      }

      this.accept(x.getColumns());
      this.accept(x.getValuesList());
      this.accept(x.getQuery());
      return false;
   }

   protected final void statExpr(SQLExpr x) {
      if (x != null) {
         Class<?> clazz = x.getClass();
         if (clazz == SQLIdentifierExpr.class) {
            this.visit((SQLIdentifierExpr)x);
         } else if (clazz == SQLPropertyExpr.class) {
            this.visit((SQLPropertyExpr)x);
         } else if (clazz == SQLBinaryOpExpr.class) {
            this.visit((SQLBinaryOpExpr)x);
         } else if (!(x instanceof SQLLiteralExpr)) {
            x.accept(this);
         }

      }
   }

   public boolean visit(SQLAlterFunctionStatement x) {
      return false;
   }

   public boolean visit(SQLDropSynonymStatement x) {
      return false;
   }

   public boolean visit(SQLAlterTypeStatement x) {
      return false;
   }

   public boolean visit(SQLAlterProcedureStatement x) {
      return false;
   }

   public boolean visit(SQLExprStatement x) {
      SQLExpr expr = x.getExpr();
      return !(expr instanceof SQLName);
   }

   public boolean visit(SQLDropTypeStatement x) {
      return false;
   }

   public boolean visit(SQLExternalRecordFormat x) {
      return false;
   }

   public boolean visit(SQLCreateDatabaseStatement x) {
      return false;
   }

   public boolean visit(SQLCreateTableGroupStatement x) {
      return false;
   }

   public boolean visit(SQLDropTableGroupStatement x) {
      return false;
   }

   public boolean visit(SQLShowDatabasesStatement x) {
      return false;
   }

   public boolean visit(SQLShowColumnsStatement x) {
      SQLName table = x.getTable();
      TableStat stat = this.getTableStat(table);
      if (stat != null) {
      }

      return false;
   }

   public boolean visit(SQLShowCreateTableStatement x) {
      SQLName table = x.getName();
      if (table != null) {
         TableStat stat = this.getTableStat(table);
         if (stat != null) {
         }
      }

      return false;
   }

   public boolean visit(SQLShowTableGroupsStatement x) {
      return false;
   }

   public boolean visit(SQLAlterTableSetOption x) {
      return false;
   }

   public boolean visit(SQLShowCreateViewStatement x) {
      return false;
   }

   public boolean visit(SQLCreateRoleStatement x) {
      return false;
   }

   public boolean visit(SQLDropRoleStatement x) {
      return false;
   }

   public boolean visit(SQLShowViewsStatement x) {
      return false;
   }

   public boolean visit(SQLAlterTableExchangePartition x) {
      SQLExprTableSource table = x.getTable();
      if (table != null) {
         table.accept(this);
      }

      return false;
   }

   public boolean visit(SQLDropCatalogStatement x) {
      return false;
   }

   public boolean visit(SQLUnionQuery x) {
      SQLUnionOperator operator = x.getOperator();
      List<SQLSelectQuery> relations = x.getRelations();
      if (relations.size() > 2) {
         for(SQLSelectQuery relation : x.getRelations()) {
            relation.accept(this);
         }

         return false;
      } else {
         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();
            rights.add(right);

            while(true) {
               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);
                  rights.add(leftLeft);

                  for(int i = rights.size() - 1; i >= 0; --i) {
                     leftRight = (SQLSelectQuery)rights.get(i);
                     leftRight.accept(this);
                  }

                  return false;
               }

               rights.add(leftRight);
               leftUnion = (SQLUnionQuery)leftLeft;
            }
         } else {
            return true;
         }
      }
   }

   public boolean visit(SQLValuesTableSource x) {
      return false;
   }

   public boolean visit(SQLAlterIndexStatement x) {
      SQLExprTableSource table = x.getTable();
      if (table != null) {
         table.accept(this);
      }

      return false;
   }

   public boolean visit(SQLShowIndexesStatement x) {
      SQLExprTableSource table = x.getTable();
      if (table != null) {
         table.accept(this);
      }

      return false;
   }

   public boolean visit(SQLAnalyzeTableStatement x) {
      for(SQLExprTableSource table : x.getTables()) {
         table.accept(this);
      }

      SQLExprTableSource table = x.getTables().size() == 1 ? (SQLExprTableSource)x.getTables().get(0) : null;
      SQLPartitionRef partition = x.getPartition();
      if (partition != null) {
         for(SQLPartitionRef.Item item : partition.getItems()) {
            SQLIdentifierExpr columnName = item.getColumnName();
            columnName.setResolvedTableSource(table);
            columnName.accept(this);
         }
      }

      return false;
   }

   public boolean visit(SQLExportTableStatement x) {
      SQLExprTableSource table = x.getTable();
      if (table != null) {
         table.accept(this);
      }

      for(SQLAssignItem item : x.getPartition()) {
         SQLExpr target = item.getTarget();
         if (target instanceof SQLIdentifierExpr) {
            ((SQLIdentifierExpr)target).setResolvedTableSource(table);
            target.accept(this);
         }
      }

      return false;
   }

   public boolean visit(SQLImportTableStatement x) {
      SQLExprTableSource table = x.getTable();
      if (table != null) {
         table.accept(this);
      }

      for(SQLAssignItem item : x.getPartition()) {
         SQLExpr target = item.getTarget();
         if (target instanceof SQLIdentifierExpr) {
            ((SQLIdentifierExpr)target).setResolvedTableSource(table);
            target.accept(this);
         }
      }

      return false;
   }

   public boolean visit(SQLCreateOutlineStatement x) {
      if (this.repository != null && x.getParent() == null) {
         this.repository.resolve((SQLStatement)x);
      }

      if (x.getOn() != null) {
         x.getOn().accept(this);
      }

      if (x.getTo() != null) {
         x.getTo().accept(this);
      }

      return false;
   }

   public boolean visit(SQLDumpStatement x) {
      if (this.repository != null && x.getParent() == null) {
         this.repository.resolve((SQLStatement)x);
      }

      SQLExprTableSource into = x.getInto();
      if (into != null) {
         into.accept(this);
      }

      SQLSelect select = x.getSelect();
      if (select != null) {
         select.accept(this);
      }

      return false;
   }

   public boolean visit(SQLDropOutlineStatement x) {
      return false;
   }

   public boolean visit(SQLAlterOutlineStatement x) {
      return false;
   }

   public boolean visit(SQLAlterTableArchivePartition x) {
      return true;
   }

   public boolean visit(SQLCopyFromStatement x) {
      SQLExprTableSource table = x.getTable();
      if (table != null) {
         table.accept(this);
      }

      for(SQLName column : x.getColumns()) {
         this.addColumn(table.getName(), column.getSimpleName());
      }

      return false;
   }

   public boolean visit(SQLSyncMetaStatement x) {
      return false;
   }

   public List<SQLName> getOriginalTables() {
      return this.originalTables;
   }

   public boolean visit(SQLUnique x) {
      for(SQLSelectOrderByItem column : x.getColumns()) {
         column.accept(this);
      }

      return false;
   }

   public boolean visit(SQLSavePointStatement x) {
      return false;
   }

   protected class OrderByStatVisitor extends SQLASTVisitorAdapter {
      private final SQLOrderBy orderBy;

      public OrderByStatVisitor(SQLOrderBy orderBy) {
         this.orderBy = orderBy;

         for(SQLSelectOrderByItem item : orderBy.getItems()) {
            item.getExpr().setParent(item);
         }

      }

      public SQLOrderBy getOrderBy() {
         return this.orderBy;
      }

      public boolean visit(SQLIdentifierExpr x) {
         return SchemaStatVisitor.this.visitOrderBy(x);
      }

      public boolean visit(SQLPropertyExpr x) {
         return SchemaStatVisitor.this.visitOrderBy(x);
      }

      public boolean visit(SQLIntegerExpr x) {
         return SchemaStatVisitor.this.visitOrderBy(x);
      }
   }

   protected class MySqlOrderByStatVisitor extends MySqlASTVisitorAdapter {
      private final SQLOrderBy orderBy;

      public MySqlOrderByStatVisitor(SQLOrderBy orderBy) {
         this.orderBy = orderBy;

         for(SQLSelectOrderByItem item : orderBy.getItems()) {
            item.getExpr().setParent(item);
         }

      }

      public SQLOrderBy getOrderBy() {
         return this.orderBy;
      }

      public boolean visit(SQLIdentifierExpr x) {
         return SchemaStatVisitor.this.visitOrderBy(x);
      }

      public boolean visit(SQLPropertyExpr x) {
         return SchemaStatVisitor.this.visitOrderBy(x);
      }

      public boolean visit(SQLIntegerExpr x) {
         return SchemaStatVisitor.this.visitOrderBy(x);
      }
   }

   protected class OracleOrderByStatVisitor extends OracleASTVisitorAdapter {
      private final SQLOrderBy orderBy;

      public OracleOrderByStatVisitor(SQLOrderBy orderBy) {
         this.orderBy = orderBy;

         for(SQLSelectOrderByItem item : orderBy.getItems()) {
            item.getExpr().setParent(item);
         }

      }

      public SQLOrderBy getOrderBy() {
         return this.orderBy;
      }

      public boolean visit(SQLIdentifierExpr x) {
         return SchemaStatVisitor.this.visitOrderBy(x);
      }

      public boolean visit(SQLPropertyExpr x) {
         SQLExpr unwrapped = SchemaStatVisitor.this.unwrapExpr(x);
         if (unwrapped instanceof SQLPropertyExpr) {
            SchemaStatVisitor.this.visitOrderBy((SQLPropertyExpr)unwrapped);
         } else if (unwrapped instanceof SQLIdentifierExpr) {
            SchemaStatVisitor.this.visitOrderBy((SQLIdentifierExpr)unwrapped);
         }

         return false;
      }

      public boolean visit(SQLIntegerExpr x) {
         return SchemaStatVisitor.this.visitOrderBy(x);
      }
   }
}
