package com.chenyang.druid.sql.repository;

import com.chenyang.druid.sql.SQLUtils;
import com.chenyang.druid.sql.ast.SQLDataType;
import com.chenyang.druid.sql.ast.SQLDeclareItem;
import com.chenyang.druid.sql.ast.SQLExpr;
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.SQLOver;
import com.chenyang.druid.sql.ast.SQLParameter;
import com.chenyang.druid.sql.ast.SQLStatement;
import com.chenyang.druid.sql.ast.SQLWindow;
import com.chenyang.druid.sql.ast.expr.SQLAllColumnExpr;
import com.chenyang.druid.sql.ast.expr.SQLBinaryOpExpr;
import com.chenyang.druid.sql.ast.expr.SQLBinaryOpExprGroup;
import com.chenyang.druid.sql.ast.expr.SQLBinaryOperator;
import com.chenyang.druid.sql.ast.expr.SQLCastExpr;
import com.chenyang.druid.sql.ast.expr.SQLCharExpr;
import com.chenyang.druid.sql.ast.expr.SQLIdentifierExpr;
import com.chenyang.druid.sql.ast.expr.SQLIntegerExpr;
import com.chenyang.druid.sql.ast.expr.SQLListExpr;
import com.chenyang.druid.sql.ast.expr.SQLMethodInvokeExpr;
import com.chenyang.druid.sql.ast.expr.SQLPropertyExpr;
import com.chenyang.druid.sql.ast.expr.SQLQueryExpr;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableAddConstraint;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableItem;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableStatement;
import com.chenyang.druid.sql.ast.statement.SQLBlockStatement;
import com.chenyang.druid.sql.ast.statement.SQLColumnDefinition;
import com.chenyang.druid.sql.ast.statement.SQLCreateFunctionStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateProcedureStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateTableStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateViewStatement;
import com.chenyang.druid.sql.ast.statement.SQLDeleteStatement;
import com.chenyang.druid.sql.ast.statement.SQLExprTableSource;
import com.chenyang.druid.sql.ast.statement.SQLFetchStatement;
import com.chenyang.druid.sql.ast.statement.SQLForeignKeyConstraint;
import com.chenyang.druid.sql.ast.statement.SQLForeignKeyImpl;
import com.chenyang.druid.sql.ast.statement.SQLIfStatement;
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.SQLReplaceStatement;
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.SQLSubqueryTableSource;
import com.chenyang.druid.sql.ast.statement.SQLTableElement;
import com.chenyang.druid.sql.ast.statement.SQLTableSource;
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.SQLUniqueConstraint;
import com.chenyang.druid.sql.ast.statement.SQLUpdateSetItem;
import com.chenyang.druid.sql.ast.statement.SQLUpdateStatement;
import com.chenyang.druid.sql.ast.statement.SQLWithSubqueryClause;
import com.chenyang.druid.sql.dialect.greenplum.ast.stmt.GPInsertStatement;
import com.chenyang.druid.sql.dialect.greenplum.ast.stmt.GPSelectQueryBlock;
import com.chenyang.druid.sql.dialect.greenplum.ast.stmt.GPSelectStatement;
import com.chenyang.druid.sql.dialect.greenplum.ast.stmt.GPUpdateStatement;
import com.chenyang.druid.sql.dialect.greenplum.visitor.GPASTVisitorAdapter;
import com.chenyang.druid.sql.dialect.kingbase.ast.KingbaseValuesTableSource;
import com.chenyang.druid.sql.dialect.kingbase.ast.ValueClause;
import com.chenyang.druid.sql.dialect.kingbase.ast.clause.KingbaseSQLOver;
import com.chenyang.druid.sql.dialect.kingbase.ast.clause.KingbaseWithSubqueryEntry;
import com.chenyang.druid.sql.dialect.kingbase.ast.expression.KingbaseArgumentExpr;
import com.chenyang.druid.sql.dialect.kingbase.ast.expression.KingbaseCaseStatement;
import com.chenyang.druid.sql.dialect.kingbase.ast.expression.KingbaseCharExpr;
import com.chenyang.druid.sql.dialect.kingbase.ast.expression.KingbaseConstantExpr;
import com.chenyang.druid.sql.dialect.kingbase.ast.expression.KingbaseDateTimeExpr;
import com.chenyang.druid.sql.dialect.kingbase.ast.expression.KingbaseExprTableSource;
import com.chenyang.druid.sql.dialect.kingbase.ast.expression.KingbaseForUpdateExpr;
import com.chenyang.druid.sql.dialect.kingbase.ast.expression.KingbaseTypeCastExpr;
import com.chenyang.druid.sql.dialect.kingbase.ast.expression.KingbaseUpdateTableSource;
import com.chenyang.druid.sql.dialect.kingbase.ast.statement.KingbaseDeleteStatement;
import com.chenyang.druid.sql.dialect.kingbase.ast.statement.KingbaseInsertStatement;
import com.chenyang.druid.sql.dialect.kingbase.ast.statement.KingbaseMultiInsertStatement;
import com.chenyang.druid.sql.dialect.kingbase.ast.statement.KingbaseSelectQueryBlock;
import com.chenyang.druid.sql.dialect.kingbase.ast.statement.KingbaseUpdateStatement;
import com.chenyang.druid.sql.dialect.kingbase.visitor.KingbaseASTVisitor;
import com.chenyang.druid.sql.dialect.mysql.ast.MysqlForeignKey;
import com.chenyang.druid.sql.dialect.mysql.ast.clause.MySqlCursorDeclareStatement;
import com.chenyang.druid.sql.dialect.mysql.ast.clause.MySqlDeclareStatement;
import com.chenyang.druid.sql.dialect.mysql.ast.clause.MySqlRepeatStatement;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.MySqlDeleteStatement;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement;
import com.chenyang.druid.sql.dialect.mysql.visitor.MySqlASTVisitorAdapter;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleCreatePackageStatement;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleCreateTableStatement;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleDeleteStatement;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleForStatement;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleForeignKey;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleInsertStatement;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleSelectQueryBlock;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleSelectSubqueryTableSource;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleSelectTableReference;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleUpdateStatement;
import com.chenyang.druid.sql.dialect.oracle.visitor.OracleASTVisitorAdapter;
import com.chenyang.druid.sql.dialect.sqlserver.ast.SQLServerSelectQueryBlock;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerInsertStatement;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerUpdateStatement;
import com.chenyang.druid.sql.dialect.sqlserver.visitor.SQLServerASTVisitorAdapter;
import com.chenyang.druid.sql.visitor.SQLASTVisitor;
import com.chenyang.druid.sql.visitor.SQLASTVisitorAdapter;
import com.chenyang.druid.util.FnvHash;
import java.util.ArrayList;
import java.util.List;

class SchemaResolveVisitorFactory {
   static void resolve(SchemaResolveVisitor visitor, SQLCreateTableStatement x) {
      SchemaResolveVisitor.Context ctx = visitor.createContext(x);
      SQLExprTableSource table = x.getTableSource();
      ctx.setTableSource(table);
      table.accept(visitor);
      List<SQLTableElement> elements = x.getTableElementList();

      for(int i = 0; i < elements.size(); ++i) {
         SQLTableElement e = (SQLTableElement)elements.get(i);
         if (e instanceof SQLColumnDefinition) {
            SQLColumnDefinition columnn = (SQLColumnDefinition)e;
            SQLName columnnName = columnn.getName();
            if (columnnName instanceof SQLIdentifierExpr) {
               SQLIdentifierExpr identifierExpr = (SQLIdentifierExpr)columnnName;
               identifierExpr.setResolvedTableSource(table);
               identifierExpr.setResolvedColumn(columnn);
            }
         } else if (e instanceof SQLUniqueConstraint) {
            for(SQLSelectOrderByItem orderByItem : ((SQLUniqueConstraint)e).getColumns()) {
               SQLExpr orderByItemExpr = orderByItem.getExpr();
               if (orderByItemExpr instanceof SQLIdentifierExpr) {
                  SQLIdentifierExpr identifierExpr = (SQLIdentifierExpr)orderByItemExpr;
                  identifierExpr.setResolvedTableSource(table);
                  SQLColumnDefinition column = x.findColumn(identifierExpr.nameHashCode64());
                  if (column != null) {
                     identifierExpr.setResolvedColumn(column);
                  }
               }
            }
         } else {
            e.accept(visitor);
         }
      }

      SQLSelect select = x.getSelect();
      if (select != null) {
         visitor.visit(select);
      }

      SchemaRepository repository = visitor.getRepository();
      if (repository != null) {
         repository.acceptCreateTable(x);
      }

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

   }

   static void resolve(SchemaResolveVisitor visitor, SQLUpdateStatement x) {
      SchemaResolveVisitor.Context ctx = visitor.createContext(x);
      SQLWithSubqueryClause with = x.getWith();
      if (with != null) {
         with.accept(visitor);
      }

      SQLTableSource table = x.getTableSource();
      SQLTableSource from = x.getFrom();
      ctx.setTableSource(table);
      ctx.setFrom(from);
      table.accept(visitor);
      if (from != null) {
         from.accept(visitor);
      }

      for(SQLUpdateSetItem item : x.getItems()) {
         SQLExpr column = item.getColumn();
         if (column instanceof SQLIdentifierExpr) {
            SQLIdentifierExpr identifierExpr = (SQLIdentifierExpr)column;
            identifierExpr.setResolvedTableSource(table);
            visitor.visit(identifierExpr);
         } else if (column instanceof SQLListExpr) {
            SQLListExpr columnGroup = (SQLListExpr)column;

            for(SQLExpr columnGroupItem : columnGroup.getItems()) {
               if (columnGroupItem instanceof SQLIdentifierExpr) {
                  SQLIdentifierExpr identifierExpr = (SQLIdentifierExpr)columnGroupItem;
                  identifierExpr.setResolvedTableSource(table);
                  visitor.visit(identifierExpr);
               } else {
                  columnGroupItem.accept(visitor);
               }
            }
         } else {
            column.accept(visitor);
         }

         SQLExpr value = item.getValue();
         if (value != null) {
            value.accept(visitor);
         }
      }

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

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

      for(SQLExpr sqlExpr : x.getReturning()) {
         sqlExpr.accept(visitor);
      }

      visitor.popContext();
   }

   static void resolve(SchemaResolveVisitor visitor, SQLDeleteStatement x) {
      SchemaResolveVisitor.Context ctx = visitor.createContext(x);
      SQLWithSubqueryClause with = x.getWith();
      if (with != null) {
         visitor.visit(with);
      }

      SQLTableSource table = x.getTableSource();
      SQLTableSource from = x.getFrom();
      if (from == null) {
         from = x.getUsing();
      }

      if (table == null && from != null) {
         table = from;
         from = null;
      }

      if (from != null) {
         ctx.setFrom(from);
         from.accept(visitor);
      }

      if (table != null) {
         if (from != null && table instanceof SQLExprTableSource) {
            SQLExpr tableExpr = ((SQLExprTableSource)table).getExpr();
            if (tableExpr instanceof SQLPropertyExpr && ((SQLPropertyExpr)tableExpr).getName().equals("*")) {
               String alias = ((SQLPropertyExpr)tableExpr).getOwnernName();
               SQLTableSource refTableSource = from.findTableSource(alias);
               if (refTableSource != null) {
                  ((SQLPropertyExpr)tableExpr).setResolvedTableSource(refTableSource);
               }
            }
         }

         table.accept(visitor);
         ctx.setTableSource(table);
      }

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

      visitor.popContext();
   }

   static void resolve(SchemaResolveVisitor visitor, SQLInsertStatement x) {
      SchemaResolveVisitor.Context ctx = visitor.createContext(x);
      SQLWithSubqueryClause with = x.getWith();
      if (with != null) {
         visitor.visit(with);
      }

      SQLTableSource table = x.getTableSource();
      ctx.setTableSource(table);
      if (table != null) {
         table.accept(visitor);
      }

      for(SQLExpr column : x.getColumns()) {
         column.accept(visitor);
      }

      for(SQLInsertStatement.ValuesClause valuesClause : x.getValuesList()) {
         valuesClause.accept(visitor);
      }

      SQLSelect query = x.getQuery();
      if (query != null) {
         visitor.visit(query);
      }

      visitor.popContext();
   }

   static void resolveIdent(SchemaResolveVisitor visitor, SQLIdentifierExpr x) {
      SchemaResolveVisitor.Context ctx = visitor.getContext();
      if (ctx != null) {
         String ident = x.getName();
         long hash = x.nameHashCode64();
         SQLTableSource tableSource = null;
         if ((hash == FnvHash.Constants.LEVEL || hash == FnvHash.Constants.CONNECT_BY_ISCYCLE) && ctx.object instanceof SQLSelectQueryBlock) {
            SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock)ctx.object;
            if (queryBlock.getStartWith() != null || queryBlock.getConnectBy() != null) {
               return;
            }
         }

         SQLTableSource ctxTable = ctx.getTableSource();
         if (ctxTable instanceof SQLJoinTableSource) {
            SQLJoinTableSource join = (SQLJoinTableSource)ctxTable;
            tableSource = join.findTableSourceWithColumn(hash, ident, visitor.getOptions());
            if (tableSource == null) {
               SQLTableSource left = join.getLeft();
               SQLTableSource right = join.getRight();
               if (left instanceof SQLSubqueryTableSource && right instanceof SQLExprTableSource) {
                  SQLSelect leftSelect = ((SQLSubqueryTableSource)left).getSelect();
                  if (leftSelect.getQuery() instanceof SQLSelectQueryBlock) {
                     boolean hasAllColumn = ((SQLSelectQueryBlock)leftSelect.getQuery()).selectItemHasAllColumn();
                     if (!hasAllColumn) {
                        tableSource = right;
                     }
                  }
               } else if (right instanceof SQLSubqueryTableSource && left instanceof SQLExprTableSource) {
                  SQLSelect rightSelect = ((SQLSubqueryTableSource)right).getSelect();
                  if (rightSelect.getQuery() instanceof SQLSelectQueryBlock) {
                     boolean hasAllColumn = ((SQLSelectQueryBlock)rightSelect.getQuery()).selectItemHasAllColumn();
                     if (!hasAllColumn) {
                        tableSource = left;
                     }
                  }
               } else if (left instanceof SQLExprTableSource && right instanceof SQLExprTableSource) {
                  SQLExprTableSource leftExprTableSource = (SQLExprTableSource)left;
                  SQLExprTableSource rightExprTableSource = (SQLExprTableSource)right;
                  if (leftExprTableSource.getSchemaObject() != null && rightExprTableSource.getSchemaObject() == null) {
                     tableSource = rightExprTableSource;
                  } else if (rightExprTableSource.getSchemaObject() != null && leftExprTableSource.getSchemaObject() == null) {
                     tableSource = leftExprTableSource;
                  }
               }
            }
         } else if (ctxTable instanceof SQLSubqueryTableSource) {
            tableSource = ctxTable.findTableSourceWithColumn(hash, ident, visitor.getOptions());
         } else if (ctxTable instanceof SQLLateralViewTableSource) {
            tableSource = ctxTable.findTableSourceWithColumn(hash, ident, visitor.getOptions());
            if (tableSource == null) {
               tableSource = ((SQLLateralViewTableSource)ctxTable).getTableSource();
            }
         } else {
            for(SchemaResolveVisitor.Context parentCtx = ctx; parentCtx != null; parentCtx = parentCtx.parent) {
               SQLDeclareItem declareItem = parentCtx.findDeclare(hash);
               if (declareItem != null) {
                  x.setResolvedDeclareItem(declareItem);
                  return;
               }

               if (parentCtx.object instanceof SQLBlockStatement) {
                  SQLBlockStatement block = (SQLBlockStatement)parentCtx.object;
                  SQLParameter parameter = block.findParameter(hash);
                  if (parameter != null) {
                     x.setResolvedParameter(parameter);
                     return;
                  }
               } else if (parentCtx.object instanceof SQLCreateProcedureStatement) {
                  SQLCreateProcedureStatement createProc = (SQLCreateProcedureStatement)parentCtx.object;
                  SQLParameter parameter = createProc.findParameter(hash);
                  if (parameter != null) {
                     x.setResolvedParameter(parameter);
                     return;
                  }
               }
            }

            tableSource = ctxTable;
            if (ctxTable instanceof SQLExprTableSource) {
               SchemaObject table = ((SQLExprTableSource)ctxTable).getSchemaObject();
               if (table != null && table.findColumn(hash) == null) {
                  SQLCreateTableStatement createStmt = null;
                  SQLStatement smt = table.getStatement();
                  if (smt instanceof SQLCreateTableStatement) {
                     createStmt = (SQLCreateTableStatement)smt;
                  }

                  if (createStmt != null && createStmt.getTableElementList().size() > 0) {
                     tableSource = null;
                  }
               }
            }
         }

         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 identExpr = (SQLIdentifierExpr)expr;
               long identHash = identExpr.nameHashCode64();
               tableSource = unwrapAlias(ctx, tableSource, identHash);
            }
         }

         if (tableSource != null) {
            x.setResolvedTableSource(tableSource);
            SQLColumnDefinition column = tableSource.findColumn(hash);
            if (column != null) {
               x.setResolvedColumn(column);
            }

            if (ctxTable instanceof SQLJoinTableSource) {
               String alias = tableSource.computeAlias();
               if (alias == null || tableSource instanceof SQLWithSubqueryClause.Entry) {
                  return;
               }

               if (visitor.isEnabled(SchemaResolveVisitor.Option.ResolveIdentifierAlias)) {
                  SQLPropertyExpr propertyExpr = new SQLPropertyExpr(new SQLIdentifierExpr(alias), ident, hash);
                  propertyExpr.setResolvedColumn(x.getResolvedColumn());
                  propertyExpr.setResolvedTableSource(x.getResolvedTableSource());
                  SQLUtils.replaceInParent((SQLExpr)x, (SQLExpr)propertyExpr);
               }
            }
         }

         if (x.getResolvedColumn() == null && x.getResolvedTableSource() == null) {
            for(SchemaResolveVisitor.Context parentCtx = ctx; parentCtx != null; parentCtx = parentCtx.parent) {
               SQLDeclareItem declareItem = parentCtx.findDeclare(hash);
               if (declareItem != null) {
                  x.setResolvedDeclareItem(declareItem);
                  return;
               }

               if (parentCtx.object instanceof SQLBlockStatement) {
                  SQLBlockStatement block = (SQLBlockStatement)parentCtx.object;
                  SQLParameter parameter = block.findParameter(hash);
                  if (parameter != null) {
                     x.setResolvedParameter(parameter);
                     return;
                  }
               } else if (parentCtx.object instanceof SQLCreateProcedureStatement) {
                  SQLCreateProcedureStatement createProc = (SQLCreateProcedureStatement)parentCtx.object;
                  SQLParameter parameter = createProc.findParameter(hash);
                  if (parameter != null) {
                     x.setResolvedParameter(parameter);
                     return;
                  }
               }
            }
         }

         if (x.getResolvedColumnObject() == null && ctx.object instanceof SQLSelectQueryBlock) {
            SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock)ctx.object;
            boolean having = false;
            SQLObject current = x;

            for(SQLObject parent = x.getParent(); parent != null; parent = parent.getParent()) {
               if (parent instanceof SQLSelectGroupByClause && parent.getParent() == queryBlock) {
                  SQLSelectGroupByClause groupBy = (SQLSelectGroupByClause)parent;
                  if (current == groupBy.getHaving()) {
                     having = true;
                  }
                  break;
               }

               current = parent;
            }

            if (having) {
               SQLSelectItem selectItem = queryBlock.findSelectItem(x.hashCode64());
               if (selectItem != null) {
                  x.setResolvedColumn(selectItem);
               }
            }
         }

      }
   }

   static void resolve(SchemaResolveVisitor visitor, SQLPropertyExpr x) {
      SchemaResolveVisitor.Context ctx = visitor.getContext();
      if (ctx != null) {
         long owner_hash = 0L;
         SQLExpr ownerObj = x.getOwner();
         if (ownerObj instanceof SQLIdentifierExpr) {
            SQLIdentifierExpr owner = (SQLIdentifierExpr)ownerObj;
            owner_hash = owner.nameHashCode64();
         } else if (ownerObj instanceof SQLPropertyExpr) {
            owner_hash = ((SQLPropertyExpr)ownerObj).hashCode64();
         }

         SQLTableSource tableSource = null;
         SQLTableSource ctxTable = ctx.getTableSource();
         if (ctxTable != null) {
            tableSource = ctxTable.findTableSource(owner_hash);
         }

         if (tableSource == null) {
            SQLTableSource ctxFrom = ctx.getFrom();
            if (ctxFrom != null) {
               tableSource = ctxFrom.findTableSource(owner_hash);
            }
         }

         if (tableSource == null) {
            for(SchemaResolveVisitor.Context parentCtx = ctx; parentCtx != null; parentCtx = parentCtx.parent) {
               SQLTableSource parentCtxTable = parentCtx.getTableSource();
               if (parentCtxTable != null) {
                  tableSource = parentCtxTable.findTableSource(owner_hash);
                  if (tableSource == null) {
                     SQLTableSource ctxFrom = parentCtx.getFrom();
                     if (ctxFrom != null) {
                        tableSource = ctxFrom.findTableSource(owner_hash);
                     }
                  }

                  if (tableSource != null) {
                     break;
                  }
               } else {
                  if (parentCtx.object instanceof SQLBlockStatement) {
                     SQLBlockStatement block = (SQLBlockStatement)parentCtx.object;
                     SQLParameter parameter = block.findParameter(owner_hash);
                     if (parameter != null) {
                        x.setResolvedOwnerObject(parameter);
                        return;
                     }
                  } else if (parentCtx.object instanceof SQLMergeStatement) {
                     SQLMergeStatement mergeStatement = (SQLMergeStatement)parentCtx.object;
                     SQLTableSource into = mergeStatement.getInto();
                     if (into instanceof SQLSubqueryTableSource && into.aliasHashCode64() == owner_hash) {
                        x.setResolvedOwnerObject(into);
                     }
                  }

                  SQLDeclareItem declareItem = parentCtx.findDeclare(owner_hash);
                  if (declareItem != null) {
                     SQLObject resolvedObject = declareItem.getResolvedObject();
                     if (resolvedObject instanceof SQLCreateProcedureStatement || resolvedObject instanceof SQLCreateFunctionStatement || resolvedObject instanceof SQLTableSource) {
                        x.setResolvedOwnerObject(resolvedObject);
                     }
                     break;
                  }
               }
            }
         }

         if (tableSource != null) {
            x.setResolvedTableSource(tableSource);
            SQLObject column = tableSource.resolveColum(x.nameHashCode64());
            if (column instanceof SQLColumnDefinition) {
               x.setResolvedColumn((SQLColumnDefinition)column);
            } else if (column instanceof SQLSelectItem) {
               x.setResolvedColumn((SQLSelectItem)column);
            }
         }

      }
   }

   static void resolve(SchemaResolveVisitor visitor, SQLBinaryOpExpr x) {
      SQLBinaryOperator op = x.getOperator();
      SQLExpr left = x.getLeft();
      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(visitor);
         }

      } else {
         if (left != null) {
            if (left instanceof SQLBinaryOpExpr) {
               resolve(visitor, (SQLBinaryOpExpr)left);
            } else {
               left.accept(visitor);
            }
         }

         SQLExpr right = x.getRight();
         if (right != null) {
            right.accept(visitor);
         }

      }
   }

   static SQLTableSource unwrapAlias(SchemaResolveVisitor.Context ctx, SQLTableSource tableSource, long identHash) {
      if (ctx == null) {
         return tableSource;
      } else {
         if (ctx.object instanceof SQLDeleteStatement && (ctx.getTableSource() == null || tableSource == ctx.getTableSource()) && ctx.getFrom() != null) {
            SQLTableSource found = ctx.getFrom().findTableSource(identHash);
            if (found != null) {
               return found;
            }
         }

         for(SchemaResolveVisitor.Context parentCtx = ctx; parentCtx != null; parentCtx = parentCtx.parent) {
            SQLWithSubqueryClause with = null;
            if (parentCtx.object instanceof SQLSelect) {
               SQLSelect select = (SQLSelect)parentCtx.object;
               with = select.getWithSubQuery();
            } else if (parentCtx.object instanceof SQLDeleteStatement) {
               SQLDeleteStatement delete = (SQLDeleteStatement)parentCtx.object;
               with = delete.getWith();
            } else if (parentCtx.object instanceof SQLInsertStatement) {
               SQLInsertStatement insertStmt = (SQLInsertStatement)parentCtx.object;
               with = insertStmt.getWith();
            } else if (parentCtx.object instanceof SQLUpdateStatement) {
               SQLUpdateStatement updateStmt = (SQLUpdateStatement)parentCtx.object;
               with = updateStmt.getWith();
            }

            if (with != null) {
               SQLWithSubqueryClause.Entry entry = with.findEntry(identHash);
               if (entry != null) {
                  return entry;
               }
            }
         }

         return tableSource;
      }
   }

   static void resolve(SchemaResolveVisitor visitor, SQLSelectQueryBlock x) {
      SchemaResolveVisitor.Context ctx = visitor.createContext(x);
      SQLTableSource from = x.getFrom();
      if (from != null) {
         ctx.setTableSource(from);
         Class fromClass = from.getClass();
         if (fromClass == SQLExprTableSource.class) {
            visitor.visit((SQLExprTableSource)from);
         } else {
            from.accept(visitor);
         }
      }

      List<SQLSelectItem> selectList = x.getSelectList();
      List<SQLSelectItem> columns = new ArrayList();

      for(int i = selectList.size() - 1; i >= 0; --i) {
         SQLSelectItem selectItem = (SQLSelectItem)selectList.get(i);
         SQLExpr expr = selectItem.getExpr();
         if (expr instanceof SQLAllColumnExpr) {
            SQLAllColumnExpr allColumnExpr = (SQLAllColumnExpr)expr;
            allColumnExpr.setResolvedTableSource(from);
            visitor.visit(allColumnExpr);
            if (visitor.isEnabled(SchemaResolveVisitor.Option.ResolveAllColumn)) {
               extractColumns(visitor, from, null, columns);
            }
         } else if (expr instanceof SQLPropertyExpr) {
            SQLPropertyExpr propertyExpr = (SQLPropertyExpr)expr;
            visitor.visit(propertyExpr);
            String ownerName = propertyExpr.getOwnernName();
            if (propertyExpr.getName().equals("*") && visitor.isEnabled(SchemaResolveVisitor.Option.ResolveAllColumn)) {
               SQLTableSource tableSource = x.findTableSource(ownerName);
               extractColumns(visitor, tableSource, ownerName, columns);
            }

            SQLColumnDefinition column = propertyExpr.getResolvedColumn();
            if (column != null) {
               continue;
            }

            SQLTableSource tableSource = x.findTableSource(propertyExpr.getOwnernName());
            if (tableSource != null) {
               column = tableSource.findColumn(propertyExpr.nameHashCode64());
               if (column != null) {
                  propertyExpr.setResolvedColumn(column);
               }
            }
         } else if (expr instanceof SQLIdentifierExpr) {
            SQLIdentifierExpr identExpr = (SQLIdentifierExpr)expr;
            visitor.visit(identExpr);
            long name_hash = identExpr.nameHashCode64();
            SQLColumnDefinition column = identExpr.getResolvedColumn();
            if (column != null || from == null) {
               continue;
            }

            column = from.findColumn(name_hash);
            if (column != null) {
               identExpr.setResolvedColumn(column);
            }
         } else {
            expr.accept(visitor);
         }

         if (columns.size() > 0) {
            for(SQLSelectItem column : columns) {
               column.setParent(x);
               column.getExpr().accept(visitor);
            }

            selectList.remove(i);
            selectList.addAll(i, columns);
            columns.clear();
         }
      }

      SQLExprTableSource into = x.getInto();
      if (into != null) {
         visitor.visit(into);
      }

      SQLExpr where = x.getWhere();
      if (where != null) {
         if (where instanceof SQLBinaryOpExpr) {
            SQLBinaryOpExpr binaryOpExpr = (SQLBinaryOpExpr)where;
            resolveExpr(visitor, binaryOpExpr.getLeft());
            resolveExpr(visitor, binaryOpExpr.getRight());
         } else if (where instanceof SQLBinaryOpExprGroup) {
            SQLBinaryOpExprGroup binaryOpExprGroup = (SQLBinaryOpExprGroup)where;

            for(SQLExpr item : binaryOpExprGroup.getItems()) {
               if (item instanceof SQLBinaryOpExpr) {
                  SQLBinaryOpExpr binaryOpExpr = (SQLBinaryOpExpr)item;
                  resolveExpr(visitor, binaryOpExpr.getLeft());
                  resolveExpr(visitor, binaryOpExpr.getRight());
               } else {
                  item.accept(visitor);
               }
            }
         } else {
            where.accept(visitor);
         }
      }

      SQLExpr startWith = x.getStartWith();
      if (startWith != null) {
         startWith.accept(visitor);
      }

      SQLExpr connectBy = x.getConnectBy();
      if (connectBy != null) {
         connectBy.accept(visitor);
      }

      SQLSelectGroupByClause groupBy = x.getGroupBy();
      if (groupBy != null) {
         groupBy.accept(visitor);
      }

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

      SQLOrderBy orderBy = x.getOrderBy();
      if (orderBy != null) {
         for(SQLSelectOrderByItem orderByItem : orderBy.getItems()) {
            SQLExpr orderByItemExpr = orderByItem.getExpr();
            if (orderByItemExpr instanceof SQLIdentifierExpr) {
               SQLIdentifierExpr orderByItemIdentExpr = (SQLIdentifierExpr)orderByItemExpr;
               long hash = orderByItemIdentExpr.nameHashCode64();
               SQLSelectItem selectItem = x.findSelectItem(hash);
               if (selectItem != null) {
                  orderByItem.setResolvedSelectItem(selectItem);
                  SQLExpr selectItemExpr = selectItem.getExpr();
                  if (selectItemExpr instanceof SQLIdentifierExpr) {
                     orderByItemIdentExpr.setResolvedTableSource(((SQLIdentifierExpr)selectItemExpr).getResolvedTableSource());
                     orderByItemIdentExpr.setResolvedColumn(((SQLIdentifierExpr)selectItemExpr).getResolvedColumn());
                  } else if (selectItemExpr instanceof SQLPropertyExpr) {
                     orderByItemIdentExpr.setResolvedTableSource(((SQLPropertyExpr)selectItemExpr).getResolvedTableSource());
                     orderByItemIdentExpr.setResolvedColumn(((SQLPropertyExpr)selectItemExpr).getResolvedColumn());
                  }
                  continue;
               }
            }

            orderByItemExpr.accept(visitor);
         }
      }

      int forUpdateOfSize = x.getForUpdateOfSize();
      if (forUpdateOfSize > 0) {
         for(SQLExpr sqlExpr : x.getForUpdateOf()) {
            sqlExpr.accept(visitor);
         }
      }

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

      List<SQLSelectOrderByItem> sortBy = x.getSortBy();
      if (sortBy != null) {
         for(SQLSelectOrderByItem item : sortBy) {
            item.accept(visitor);
         }
      }

      visitor.popContext();
   }

   static void extractColumns(SchemaResolveVisitor visitor, SQLTableSource from, String ownerName, List<SQLSelectItem> columns) {
      if (from instanceof SQLExprTableSource) {
         SQLExpr expr = ((SQLExprTableSource)from).getExpr();
         SchemaRepository repository = visitor.getRepository();
         if (repository == null) {
            return;
         }

         String alias = from.getAlias();
         SchemaObject table = repository.findTable((SQLExprTableSource)from);
         if (table != null) {
            SQLCreateTableStatement createTableStmt = (SQLCreateTableStatement)table.getStatement();

            for(SQLTableElement e : createTableStmt.getTableElementList()) {
               if (e instanceof SQLColumnDefinition) {
                  SQLColumnDefinition column = (SQLColumnDefinition)e;
                  if (alias != null) {
                     SQLPropertyExpr name = new SQLPropertyExpr(alias, column.getName().getSimpleName());
                     name.setResolvedColumn(column);
                     columns.add(new SQLSelectItem(name));
                  } else if (ownerName != null) {
                     SQLPropertyExpr name = new SQLPropertyExpr(ownerName, column.getName().getSimpleName());
                     name.setResolvedColumn(column);
                     columns.add(new SQLSelectItem(name));
                  } else if (from.getParent() instanceof SQLJoinTableSource && from instanceof SQLExprTableSource && expr instanceof SQLName) {
                     String tableName = expr.toString();
                     SQLPropertyExpr name = new SQLPropertyExpr(tableName, column.getName().getSimpleName());
                     name.setResolvedColumn(column);
                     columns.add(new SQLSelectItem(name));
                  } else {
                     SQLIdentifierExpr name = (SQLIdentifierExpr)column.getName().clone();
                     name.setResolvedColumn(column);
                     columns.add(new SQLSelectItem(name));
                  }
               }
            }

            return;
         }

         if (expr instanceof SQLIdentifierExpr) {
            SQLTableSource resolvedTableSource = ((SQLIdentifierExpr)expr).getResolvedTableSource();
            if (resolvedTableSource instanceof SQLWithSubqueryClause.Entry) {
               SQLWithSubqueryClause.Entry entry = (SQLWithSubqueryClause.Entry)resolvedTableSource;
               SQLSelect select = ((SQLWithSubqueryClause.Entry)resolvedTableSource).getSubQuery();
               SQLSelectQueryBlock firstQueryBlock = select.getFirstQueryBlock();
               if (firstQueryBlock != null) {
                  for(SQLSelectItem item : firstQueryBlock.getSelectList()) {
                     String itemAlias = item.computeAlias();
                     if (itemAlias != null) {
                        SQLIdentifierExpr columnExpr = new SQLIdentifierExpr(itemAlias);
                        columnExpr.setResolvedColumn(item);
                        columns.add(new SQLSelectItem(columnExpr));
                     }
                  }
               }
            }
         }
      } else if (from instanceof SQLJoinTableSource) {
         SQLJoinTableSource join = (SQLJoinTableSource)from;
         extractColumns(visitor, join.getLeft(), ownerName, columns);
         extractColumns(visitor, join.getRight(), ownerName, columns);
      } else if (from instanceof SQLSubqueryTableSource) {
         SQLSelectQueryBlock subQuery = ((SQLSubqueryTableSource)from).getSelect().getQueryBlock();
         if (subQuery == null) {
            return;
         }

         List<SQLSelectItem> subSelectList = subQuery.getSelectList();

         for(SQLSelectItem subSelectItem : subSelectList) {
            if (subSelectItem.getAlias() == null && !(subSelectItem.getExpr() instanceof SQLName)) {
               return;
            }
         }

         for(SQLSelectItem subSelectItem : subSelectList) {
            String alias = subSelectItem.computeAlias();
            columns.add(new SQLSelectItem(new SQLIdentifierExpr(alias)));
         }
      } else if (from instanceof SQLUnionQueryTableSource) {
         SQLSelectQueryBlock firstQueryBlock = ((SQLUnionQueryTableSource)from).getUnion().getFirstQueryBlock();
         if (firstQueryBlock == null) {
            return;
         }

         List<SQLSelectItem> subSelectList = firstQueryBlock.getSelectList();

         for(SQLSelectItem subSelectItem : subSelectList) {
            if (subSelectItem.getAlias() == null && !(subSelectItem.getExpr() instanceof SQLName)) {
               return;
            }
         }

         for(SQLSelectItem subSelectItem : subSelectList) {
            String alias = subSelectItem.computeAlias();
            columns.add(new SQLSelectItem(new SQLIdentifierExpr(alias)));
         }
      }

   }

   static void resolve(SchemaResolveVisitor visitor, SQLAllColumnExpr x) {
       SQLTableSource tableSource = x.getResolvedTableSource();

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

           if (queryBlock == null) {
               return;
           }

           SQLTableSource from = queryBlock.getFrom();
           if (from == null || from instanceof SQLJoinTableSource) {
               return;
           }

           x.setResolvedTableSource(from);
           tableSource = from;
       }

       if (tableSource instanceof SQLExprTableSource) {
           SQLExpr expr = ((SQLExprTableSource) tableSource).getExpr();
           if (expr instanceof SQLIdentifierExpr) {
               SQLTableSource resolvedTableSource = ((SQLIdentifierExpr) expr).getResolvedTableSource();
               if (resolvedTableSource != null) {
                   x.setResolvedTableSource(resolvedTableSource);
               }
           }
       }
   }

   static void resolve(SchemaResolveVisitor v, SQLMethodInvokeExpr x) {
      SQLExpr owner = x.getOwner();
      if (owner != null) {
         resolveExpr(v, owner);
      }

      for(SQLExpr arg : x.getArguments()) {
         resolveExpr(v, arg);
      }

      SQLExpr from = x.getFrom();
      if (from != null) {
         resolveExpr(v, from);
      }

      SQLExpr using = x.getUsing();
      if (using != null) {
         resolveExpr(v, using);
      }

      SQLExpr _for = x.getFor();
      if (_for != null) {
         resolveExpr(v, _for);
      }

      long nameHash = x.methodNameHashCode64();
      SchemaRepository repository = v.getRepository();
      if (repository != null) {
         SQLDataType dataType = repository.findFuntionReturnType(nameHash);
         if (dataType != null) {
            x.setResolvedReturnDataType(dataType);
         }
      }

   }

   static void resolve(SchemaResolveVisitor visitor, SQLSelect x) {
      visitor.createContext(x);
      SQLWithSubqueryClause with = x.getWithSubQuery();
      if (with != null) {
         visitor.visit(with);
      }

      SQLSelectQuery query = x.getQuery();
      if (query != null) {
         if (query instanceof SQLSelectQueryBlock) {
            visitor.visit((SQLSelectQueryBlock)query);
         } else {
            query.accept(visitor);
         }
      }

      SQLSelectQueryBlock queryBlock = x.getFirstQueryBlock();
      SQLOrderBy orderBy = x.getOrderBy();
      if (orderBy != null) {
         for(SQLSelectOrderByItem orderByItem : orderBy.getItems()) {
            SQLExpr orderByItemExpr = orderByItem.getExpr();
            if (orderByItemExpr instanceof SQLIdentifierExpr) {
               SQLIdentifierExpr orderByItemIdentExpr = (SQLIdentifierExpr)orderByItemExpr;
               long hash = orderByItemIdentExpr.nameHashCode64();
               SQLSelectItem selectItem = null;
               if (queryBlock != null) {
                  selectItem = queryBlock.findSelectItem(hash);
               }

               if (selectItem != null) {
                  orderByItem.setResolvedSelectItem(selectItem);
                  SQLExpr selectItemExpr = selectItem.getExpr();
                  if (selectItemExpr instanceof SQLIdentifierExpr) {
                     orderByItemIdentExpr.setResolvedTableSource(((SQLIdentifierExpr)selectItemExpr).getResolvedTableSource());
                     orderByItemIdentExpr.setResolvedColumn(((SQLIdentifierExpr)selectItemExpr).getResolvedColumn());
                  } else if (selectItemExpr instanceof SQLPropertyExpr) {
                     orderByItemIdentExpr.setResolvedTableSource(((SQLPropertyExpr)selectItemExpr).getResolvedTableSource());
                     orderByItemIdentExpr.setResolvedColumn(((SQLPropertyExpr)selectItemExpr).getResolvedColumn());
                  }
                  continue;
               }
            }

            orderByItemExpr.accept(visitor);
         }
      }

      visitor.popContext();
   }

   static void resolve(SchemaResolveVisitor visitor, SQLWithSubqueryClause x) {
      List<SQLWithSubqueryClause.Entry> entries = x.getEntries();
      SchemaResolveVisitor.Context context = visitor.getContext();

      for(SQLWithSubqueryClause.Entry entry : entries) {
         SQLSelect query = entry.getSubQuery();
         if (query != null) {
            visitor.visit(query);
            long alias_hash = entry.aliasHashCode64();
            if (context != null && alias_hash != 0L) {
               context.addTableSource(alias_hash, entry);
            }
         } else {
            entry.getReturningStatement().accept(visitor);
         }
      }

   }

   static void resolve(SchemaResolveVisitor visitor, SQLExprTableSource x) {
      SQLExpr expr = x.getExpr();
      SQLExpr annFeature = null;
      if (expr instanceof SQLMethodInvokeExpr) {
         SQLMethodInvokeExpr func = (SQLMethodInvokeExpr)expr;
         if (func.methodNameHashCode64() == FnvHash.Constants.ANN) {
            expr = (SQLExpr)func.getArguments().get(0);
            annFeature = (SQLExpr)func.getArguments().get(1);
            if (annFeature instanceof SQLIdentifierExpr) {
               ((SQLIdentifierExpr)annFeature).setResolvedTableSource(x);
            } else if (annFeature instanceof SQLPropertyExpr) {
               ((SQLPropertyExpr)annFeature).setResolvedTableSource(x);
            }
         }
      }

      if (expr instanceof SQLName) {
         if (x.getSchemaObject() != null) {
            return;
         }

         SQLIdentifierExpr identifierExpr = null;
         if (expr instanceof SQLIdentifierExpr) {
            identifierExpr = (SQLIdentifierExpr)expr;
         } else if (expr instanceof SQLPropertyExpr) {
            SQLExpr owner = ((SQLPropertyExpr)expr).getOwner();
            if (owner instanceof SQLIdentifierExpr) {
               identifierExpr = (SQLIdentifierExpr)owner;
            }
         }

         if (identifierExpr != null) {
            checkParameter(visitor, identifierExpr);
            SQLTableSource tableSource = unwrapAlias(visitor.getContext(), (SQLTableSource)null, identifierExpr.nameHashCode64());
            if (tableSource != null) {
               identifierExpr.setResolvedTableSource(tableSource);
               return;
            }
         }

         SchemaRepository repository = visitor.getRepository();
         if (repository != null) {
            SchemaObject table = repository.findTable((SQLName)expr);
            if (table != null) {
               x.setSchemaObject(table);
               if (annFeature != null && annFeature instanceof SQLIdentifierExpr) {
                  SQLIdentifierExpr identExpr = (SQLIdentifierExpr)annFeature;
                  SQLColumnDefinition column = table.findColumn(identExpr.nameHashCode64());
                  if (column != null) {
                     identExpr.setResolvedColumn(column);
                  }
               }
            }
         }
      } else if (expr instanceof SQLMethodInvokeExpr) {
         visitor.visit((SQLMethodInvokeExpr)expr);
      } else if (expr instanceof SQLQueryExpr) {
         SQLSelect select = ((SQLQueryExpr)expr).getSubQuery();
         visitor.visit(select);
         SQLSelectQueryBlock queryBlock = select.getQueryBlock();
         if (queryBlock != null && annFeature instanceof SQLIdentifierExpr) {
            SQLIdentifierExpr identExpr = (SQLIdentifierExpr)annFeature;
            SQLObject columnDef = queryBlock.resolveColum(identExpr.nameHashCode64());
            if (columnDef instanceof SQLColumnDefinition) {
               identExpr.setResolvedColumn((SQLColumnDefinition)columnDef);
            } else if (columnDef instanceof SQLSelectItem) {
               identExpr.setResolvedColumn((SQLSelectItem)columnDef);
            }
         }
      } else {
         expr.accept(visitor);
      }

   }

   static void resolve(SchemaResolveVisitor visitor, SQLAlterTableStatement x) {
      SchemaResolveVisitor.Context ctx = visitor.createContext(x);
      SQLTableSource tableSource = x.getTableSource();
      ctx.setTableSource(tableSource);

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

      visitor.popContext();
   }

   static void resolve(SchemaResolveVisitor visitor, SQLMergeStatement x) {
      SchemaResolveVisitor.Context ctx = visitor.createContext(x);
      SQLTableSource into = x.getInto();
      if (into instanceof SQLExprTableSource) {
         ctx.setTableSource(into);
      } else {
         into.accept(visitor);
      }

      SQLTableSource using = x.getUsing();
      if (using != null) {
         using.accept(visitor);
         ctx.setFrom(using);
      }

      SQLExpr on = x.getOn();
      if (on != null) {
         on.accept(visitor);
      }

      SQLMergeStatement.MergeUpdateClause updateClause = x.getUpdateClause();
      if (updateClause != null) {
         for(SQLUpdateSetItem item : updateClause.getItems()) {
            SQLExpr column = item.getColumn();
            if (column instanceof SQLIdentifierExpr) {
               ((SQLIdentifierExpr)column).setResolvedTableSource(into);
            } else if (column instanceof SQLPropertyExpr) {
               ((SQLPropertyExpr)column).setResolvedTableSource(into);
            } else {
               column.accept(visitor);
            }

            SQLExpr value = item.getValue();
            if (value != null) {
               value.accept(visitor);
            }
         }

         SQLExpr where = updateClause.getWhere();
         if (where != null) {
            where.accept(visitor);
         }

         SQLExpr deleteWhere = updateClause.getDeleteWhere();
         if (deleteWhere != null) {
            deleteWhere.accept(visitor);
         }
      }

      SQLMergeStatement.MergeInsertClause insertClause = x.getInsertClause();
      if (insertClause != null) {
         for(SQLExpr column : insertClause.getColumns()) {
            if (column instanceof SQLIdentifierExpr) {
               ((SQLIdentifierExpr)column).setResolvedTableSource(into);
            } else if (column instanceof SQLPropertyExpr) {
               ((SQLPropertyExpr)column).setResolvedTableSource(into);
            }

            column.accept(visitor);
         }

         for(SQLExpr value : insertClause.getValues()) {
            value.accept(visitor);
         }

         SQLExpr where = insertClause.getWhere();
         if (where != null) {
            where.accept(visitor);
         }
      }

      visitor.popContext();
   }

   static void resolve(SchemaResolveVisitor visitor, SQLCreateFunctionStatement x) {
      SchemaResolveVisitor.Context ctx = visitor.createContext(x);
      SQLDeclareItem declareItem = new SQLDeclareItem(x.getName().clone(), (SQLDataType)null);
      declareItem.setResolvedObject(x);
      SchemaResolveVisitor.Context parentCtx = visitor.getContext();
      if (parentCtx != null) {
         parentCtx.declare(declareItem);
      } else {
         ctx.declare(declareItem);
      }

      for(SQLParameter parameter : x.getParameters()) {
         parameter.accept(visitor);
      }

      SQLStatement block = x.getBlock();
      if (block != null) {
         block.accept(visitor);
      }

      visitor.popContext();
   }

   static void resolve(SchemaResolveVisitor visitor, SQLCreateProcedureStatement x) {
      SchemaResolveVisitor.Context ctx = visitor.createContext(x);
      SQLDeclareItem declareItem = new SQLDeclareItem(x.getName().clone(), (SQLDataType)null);
      declareItem.setResolvedObject(x);
      SchemaResolveVisitor.Context parentCtx = visitor.getContext();
      if (parentCtx != null) {
         parentCtx.declare(declareItem);
      } else {
         ctx.declare(declareItem);
      }

      for(SQLParameter parameter : x.getParameters()) {
         parameter.accept(visitor);
      }

      SQLStatement block = x.getBlock();
      if (block != null) {
         block.accept(visitor);
      }

      visitor.popContext();
   }

   static boolean resolve(SchemaResolveVisitor visitor, SQLIfStatement x) {
      visitor.createContext(x);
      SQLExpr condition = x.getCondition();
      if (condition != null) {
         condition.accept(visitor);
      }

      for(SQLStatement stmt : x.getStatements()) {
         stmt.accept(visitor);
      }

      for(SQLIfStatement.ElseIf elseIf : x.getElseIfList()) {
         elseIf.accept(visitor);
      }

      SQLIfStatement.Else e = x.getElseItem();
      if (e != null) {
         e.accept(visitor);
      }

      visitor.popContext();
      return false;
   }

   static void resolve(SchemaResolveVisitor visitor, SQLBlockStatement x) {
      visitor.createContext(x);

      for(SQLParameter parameter : x.getParameters()) {
         visitor.visit(parameter);
      }

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

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

      visitor.popContext();
   }

   static void resolve(SchemaResolveVisitor visitor, SQLParameter x) {
      SQLName name = x.getName();
      if (name instanceof SQLIdentifierExpr) {
         ((SQLIdentifierExpr)name).setResolvedParameter(x);
      }

      SQLExpr expr = x.getDefaultValue();
      SchemaResolveVisitor.Context ctx = null;
      if (expr != null) {
         if (expr instanceof SQLQueryExpr) {
            ctx = visitor.createContext(x);
            SQLSubqueryTableSource tableSource = new SQLSubqueryTableSource(((SQLQueryExpr)expr).getSubQuery());
            tableSource.setParent(x);
            tableSource.setAlias(x.getName().getSimpleName());
            ctx.setTableSource(tableSource);
         }

         expr.accept(visitor);
      }

      if (ctx != null) {
         visitor.popContext();
      }

   }

   static void resolve(SchemaResolveVisitor visitor, SQLDeclareItem x) {
      SchemaResolveVisitor.Context ctx = visitor.getContext();
      if (ctx != null) {
         ctx.declare(x);
      }

      SQLName name = x.getName();
      if (name instanceof SQLIdentifierExpr) {
         ((SQLIdentifierExpr)name).setResolvedDeclareItem(x);
      }

   }

   static void resolve(SchemaResolveVisitor visitor, SQLOver x) {
      SQLName of = x.getOf();
      SQLOrderBy orderBy = x.getOrderBy();
      List<SQLExpr> partitionBy = x.getPartitionBy();
      if (of == null && orderBy != null) {
         orderBy.accept(visitor);
      }

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

   }

   private static boolean checkParameter(SchemaResolveVisitor visitor, SQLIdentifierExpr x) {
      if (x.getResolvedParameter() != null) {
         return true;
      } else {
         SchemaResolveVisitor.Context ctx = visitor.getContext();
         if (ctx == null) {
            return false;
         } else {
            long hash = x.hashCode64();

            for(SchemaResolveVisitor.Context parentCtx = ctx; parentCtx != null; parentCtx = parentCtx.parent) {
               if (parentCtx.object instanceof SQLBlockStatement) {
                  SQLBlockStatement block = (SQLBlockStatement)parentCtx.object;
                  SQLParameter parameter = block.findParameter(hash);
                  if (parameter != null) {
                     x.setResolvedParameter(parameter);
                     return true;
                  }
               }

               if (parentCtx.object instanceof SQLCreateProcedureStatement) {
                  SQLCreateProcedureStatement createProc = (SQLCreateProcedureStatement)parentCtx.object;
                  SQLParameter parameter = createProc.findParameter(hash);
                  if (parameter != null) {
                     x.setResolvedParameter(parameter);
                     return true;
                  }
               }

               if (parentCtx.object instanceof SQLSelect) {
                  SQLSelect select = (SQLSelect)parentCtx.object;
                  SQLWithSubqueryClause with = select.getWithSubQuery();
                  if (with != null) {
                     SQLWithSubqueryClause.Entry entry = with.findEntry(hash);
                     if (entry != null) {
                        x.setResolvedTableSource(entry);
                        return true;
                     }
                  }
               }

               SQLDeclareItem declareItem = parentCtx.findDeclare(hash);
               if (declareItem != null) {
                  x.setResolvedDeclareItem(declareItem);
                  break;
               }
            }

            return false;
         }
      }
   }

   static void resolve(SchemaResolveVisitor visitor, SQLReplaceStatement x) {
      SchemaResolveVisitor.Context ctx = visitor.createContext(x);
      SQLExprTableSource tableSource = x.getTableSource();
      ctx.setTableSource(tableSource);
      visitor.visit(tableSource);

      for(SQLExpr column : x.getColumns()) {
         column.accept(visitor);
      }

      SQLQueryExpr queryExpr = x.getQuery();
      if (queryExpr != null) {
         visitor.visit(queryExpr.getSubQuery());
      }

      visitor.popContext();
   }

   static void resolve(SchemaResolveVisitor visitor, SQLFetchStatement x) {
      resolveExpr(visitor, x.getCursorName());

      for(SQLExpr expr : x.getInto()) {
         resolveExpr(visitor, expr);
      }

   }

   static void resolve(SchemaResolveVisitor visitor, SQLForeignKeyConstraint x) {
      SchemaRepository repository = visitor.getRepository();
      SQLObject parent = x.getParent();
      if (parent instanceof SQLCreateTableStatement) {
         SQLCreateTableStatement createTableStmt = (SQLCreateTableStatement)parent;
         SQLTableSource table = createTableStmt.getTableSource();

         for(SQLName item : x.getReferencingColumns()) {
            SQLIdentifierExpr columnName = (SQLIdentifierExpr)item;
            columnName.setResolvedTableSource(table);
            SQLColumnDefinition column = createTableStmt.findColumn(columnName.nameHashCode64());
            if (column != null) {
               columnName.setResolvedColumn(column);
            }
         }
      } else if (parent instanceof SQLAlterTableAddConstraint) {
         SQLAlterTableStatement stmt = (SQLAlterTableStatement)parent.getParent();
         SQLTableSource table = stmt.getTableSource();

         for(SQLName item : x.getReferencingColumns()) {
            SQLIdentifierExpr columnName = (SQLIdentifierExpr)item;
            columnName.setResolvedTableSource(table);
         }
      }

      if (repository != null) {
         SQLExprTableSource table = x.getReferencedTable();

         for(SQLName item : x.getReferencedColumns()) {
            SQLIdentifierExpr columnName = (SQLIdentifierExpr)item;
            columnName.setResolvedTableSource(table);
         }

         SQLName tableName = table.getName();
         SchemaObject tableObject = repository.findTable(tableName);
         if (tableObject != null) {
            SQLStatement tableStmt = tableObject.getStatement();
            if (tableStmt instanceof SQLCreateTableStatement) {
               SQLCreateTableStatement refCreateTableStmt = (SQLCreateTableStatement)tableStmt;

               for(SQLName item : x.getReferencedColumns()) {
                  SQLIdentifierExpr columnName = (SQLIdentifierExpr)item;
                  SQLColumnDefinition column = refCreateTableStmt.findColumn(columnName.nameHashCode64());
                  if (column != null) {
                     columnName.setResolvedColumn(column);
                  }
               }
            }

         }
      }
   }

   static void resolve(SchemaResolveVisitor visitor, SQLCreateViewStatement x) {
      x.getSubQuery().accept(visitor);
   }

   static void resolveExpr(SchemaResolveVisitor visitor, SQLExpr x) {
      if (x != null) {
         Class<?> clazz = x.getClass();
         if (clazz == SQLIdentifierExpr.class) {
            visitor.visit((SQLIdentifierExpr)x);
         } else if (clazz != SQLIntegerExpr.class && clazz != SQLCharExpr.class) {
            x.accept(visitor);
         }
      }
   }

   static void resolveUnion(SchemaResolveVisitor visitor, SQLUnionQuery x) {
      SQLUnionOperator operator = x.getOperator();
      List<SQLSelectQuery> relations = x.getRelations();
      if (relations.size() > 2) {
         for(SQLSelectQuery relation : relations) {
            relation.accept(visitor);
         }

      } 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(visitor);
                  }

                  return;
               }

               rights.add(leftRight);
               leftUnion = (SQLUnionQuery)leftLeft;
            }
         } else {
            if (left != null) {
               left.accept(visitor);
            }

            if (right != null) {
               right.accept(visitor);
            }

         }
      }
   }

   public boolean visit(SQLSelectStatement x) {
      resolve((SchemaResolveVisitor)this, x.getSelect());
      return false;
   }

   public boolean visit(SQLSelect x) {
      resolve((SchemaResolveVisitor)this, x);
      return false;
   }

   public boolean visit(SQLWithSubqueryClause x) {
      resolve((SchemaResolveVisitor)this, x);
      return false;
   }

   public boolean visit(SQLIfStatement x) {
      resolve((SchemaResolveVisitor)this, x);
      return false;
   }

   public boolean visit(SQLCreateFunctionStatement x) {
      resolve((SchemaResolveVisitor)this, x);
      return false;
   }

   public boolean visit(SQLExprTableSource x) {
      resolve((SchemaResolveVisitor)this, x);
      return false;
   }

   public boolean visit(SQLSelectQueryBlock x) {
      resolve((SchemaResolveVisitor)this, x);
      return false;
   }

   public boolean visit(SQLForeignKeyImpl x) {
      resolve((SchemaResolveVisitor)this, (SQLForeignKeyConstraint)x);
      return false;
   }

   public boolean visit(SQLIdentifierExpr x) {
      resolveIdent((SchemaResolveVisitor)this, x);
      return true;
   }

   public boolean visit(SQLPropertyExpr x) {
      resolve((SchemaResolveVisitor)this, x);
      return false;
   }

   public boolean visit(SQLBinaryOpExpr x) {
      resolve((SchemaResolveVisitor)this, x);
      return false;
   }

   public boolean visit(SQLAllColumnExpr x) {
      resolve((SchemaResolveVisitor)this, x);
      return false;
   }

   public boolean visit(SQLCreateTableStatement x) {
      resolve((SchemaResolveVisitor)this, x);
      return false;
   }

   public boolean visit(SQLUpdateStatement x) {
      resolve((SchemaResolveVisitor)this, x);
      return false;
   }

   public boolean visit(SQLDeleteStatement x) {
      resolve((SchemaResolveVisitor)this, x);
      return false;
   }

   public boolean visit(SQLAlterTableStatement x) {
      resolve((SchemaResolveVisitor)this, x);
      return false;
   }

   public boolean visit(SQLInsertStatement x) {
      resolve((SchemaResolveVisitor)this, x);
      return false;
   }

   public boolean visit(SQLParameter x) {
      resolve((SchemaResolveVisitor)this, x);
      return false;
   }

   public boolean visit(SQLDeclareItem x) {
      resolve((SchemaResolveVisitor)this, x);
      return false;
   }

   public boolean visit(SQLOver x) {
      resolve((SchemaResolveVisitor)this, x);
      return false;
   }

   public boolean visit(SQLMethodInvokeExpr x) {
      resolve((SchemaResolveVisitor)this, x);
      return false;
   }

   public boolean visit(SQLUnionQuery x) {
      resolveUnion((SchemaResolveVisitor)this, x);
      return false;
   }

   public boolean visit(SQLMergeStatement x) {
      resolve((SchemaResolveVisitor)this, x);
      return false;
   }

   public boolean visit(SQLCreateProcedureStatement x) {
      resolve((SchemaResolveVisitor)this, x);
      return false;
   }

   public boolean visit(SQLBlockStatement x) {
      resolve((SchemaResolveVisitor)this, x);
      return false;
   }

   public boolean visit(SQLReplaceStatement x) {
      resolve((SchemaResolveVisitor)this, x);
      return false;
   }

   public boolean visit(SQLCastExpr x) {
      x.getExpr().accept((SQLASTVisitor)this);
      return true;
   }

   public boolean visit(SQLFetchStatement x) {
      resolve((SchemaResolveVisitor)this, x);
      return false;
   }

   static class MySqlResolveVisitor extends MySqlASTVisitorAdapter implements SchemaResolveVisitor {
      private SchemaRepository repository;
      private int options;
      private SchemaResolveVisitor.Context context;

      public MySqlResolveVisitor(SchemaRepository repository, int options) {
         this.repository = repository;
         this.options = options;
      }

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

      public boolean visit(MySqlDeclareStatement x) {
         for(SQLDeclareItem declareItem : x.getVarList()) {
            this.visit((SQLDeclareItem)declareItem);
         }

         return false;
      }

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

      public boolean visit(MysqlForeignKey x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLForeignKeyConstraint)x);
         return false;
      }

      public boolean visit(MySqlSelectQueryBlock x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLSelectQueryBlock)x);
         return false;
      }

      public boolean visit(SQLSelectItem x) {
         SQLExpr expr = x.getExpr();
         if (expr instanceof SQLIdentifierExpr) {
            SchemaResolveVisitorFactory.resolveIdent(this, (SQLIdentifierExpr)expr);
            return false;
         } else if (expr instanceof SQLPropertyExpr) {
            SchemaResolveVisitorFactory.resolve(this, (SQLPropertyExpr)((SQLPropertyExpr)expr));
            return false;
         } else {
            return true;
         }
      }

      public boolean visit(MySqlCreateTableStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLCreateTableStatement)x);
         SQLExprTableSource like = x.getLike();
         if (like != null) {
            like.accept(this);
         }

         return false;
      }

      public boolean visit(MySqlUpdateStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLUpdateStatement)x);
         return false;
      }

      public boolean visit(MySqlDeleteStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLDeleteStatement)x);
         return false;
      }

      public boolean visit(MySqlInsertStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLInsertStatement)x);
         return false;
      }

      public boolean isEnabled(SchemaResolveVisitor.Option option) {
         return (this.options & option.mask) != 0;
      }

      public int getOptions() {
         return this.options;
      }

      public SchemaResolveVisitor.Context getContext() {
         return this.context;
      }

      public SchemaResolveVisitor.Context createContext(SQLObject object) {
         return this.context = new SchemaResolveVisitor.Context(object, this.context);
      }

      public void popContext() {
         if (this.context != null) {
            this.context = this.context.parent;
         }

      }

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

   static class KingbaseResolveVisitor extends SQLASTVisitorAdapter implements KingbaseASTVisitor, SchemaResolveVisitor {
      private SchemaRepository repository;
      private int options;
      private SchemaResolveVisitor.Context context;

      public KingbaseResolveVisitor(SchemaRepository repository, int options) {
         this.repository = repository;
         this.options = options;
      }

      public boolean visit(SQLSelectItem x) {
         SQLExpr expr = x.getExpr();
         if (expr instanceof SQLIdentifierExpr) {
            SchemaResolveVisitorFactory.resolveIdent(this, (SQLIdentifierExpr)expr);
            return false;
         } else if (expr instanceof SQLPropertyExpr) {
            SchemaResolveVisitorFactory.resolve(this, (SQLPropertyExpr)((SQLPropertyExpr)expr));
            return false;
         } else {
            return true;
         }
      }

      public boolean visit(SQLIdentifierExpr x) {
         if (x.nameHashCode64() == FnvHash.Constants.ROWNUM) {
            return false;
         } else {
            SchemaResolveVisitorFactory.resolveIdent(this, x);
            return true;
         }
      }

      public boolean visit(KingbaseInsertStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLInsertStatement)x);
         return false;
      }

      public void endVisit(KingbaseInsertStatement x) {
      }

      public boolean visit(KingbaseDeleteStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLDeleteStatement)x);
         return false;
      }

      public void endVisit(KingbaseDeleteStatement x) {
      }

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

      public void endVisit(KingbaseTypeCastExpr x) {
      }

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

      public void endVisit(KingbaseSQLOver x) {
      }

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

      public void endVisit(KingbaseDateTimeExpr x) {
      }

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

      public void endVisit(KingbaseForUpdateExpr x) {
      }

      public boolean visit(KingbaseUpdateStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLUpdateStatement)x);
         return false;
      }

      public void endVisit(KingbaseUpdateStatement x) {
      }

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

      public void endVisit(KingbaseUpdateTableSource x) {
      }

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

      public void endVisit(ValueClause x) {
      }

      public boolean visit(ValueClause.Item x) {
         return true;
      }

      public void endVisit(ValueClause.Item x) {
      }

      public boolean visit(KingbaseValuesTableSource x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLExprTableSource)x);
         return false;
      }

      public void endVisit(KingbaseValuesTableSource x) {
      }

      public boolean visit(KingbaseSelectQueryBlock x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLSelectQueryBlock)x);
         return false;
      }

      public void endVisit(KingbaseSelectQueryBlock x) {
      }

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

      public void endVisit(KingbaseArgumentExpr x) {
      }

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

      public void endVisit(KingbaseCharExpr x) {
      }

      public boolean visit(KingbaseCaseStatement.KingbaseWhenStatement x) {
         return true;
      }

      public void endVisit(KingbaseCaseStatement.KingbaseWhenStatement x) {
      }

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

      public void endVisit(KingbaseCaseStatement x) {
      }

      public boolean visit(KingbaseExprTableSource x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLExprTableSource)x);
         return false;
      }

      public void endVisit(KingbaseExprTableSource x) {
      }

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

      public void endVisit(KingbaseWithSubqueryEntry x) {
      }

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

      public void endVisit(KingbaseConstantExpr x) {
      }

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

      public void endVisit(KingbaseMultiInsertStatement x) {
      }

      public boolean visit(KingbaseMultiInsertStatement.ConditionalInsertClause x) {
         return true;
      }

      public void endVisit(KingbaseMultiInsertStatement.ConditionalInsertClause x) {
      }

      public boolean visit(KingbaseMultiInsertStatement.ConditionalInsertClauseItem x) {
         return true;
      }

      public void endVisit(KingbaseMultiInsertStatement.ConditionalInsertClauseItem x) {
      }

      public boolean visit(KingbaseMultiInsertStatement.InsertIntoClause x) {
         return true;
      }

      public void endVisit(KingbaseMultiInsertStatement.InsertIntoClause x) {
      }

      public boolean isEnabled(SchemaResolveVisitor.Option option) {
         return (this.options & option.mask) != 0;
      }

      public int getOptions() {
         return this.options;
      }

      public SchemaResolveVisitor.Context getContext() {
         return this.context;
      }

      public SchemaResolveVisitor.Context createContext(SQLObject object) {
         return this.context = new SchemaResolveVisitor.Context(object, this.context);
      }

      public void popContext() {
         if (this.context != null) {
            this.context = this.context.parent;
         }

      }

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

   static class OracleResolveVisitor extends OracleASTVisitorAdapter implements SchemaResolveVisitor {
      private SchemaRepository repository;
      private int options;
      private SchemaResolveVisitor.Context context;

      public OracleResolveVisitor(SchemaRepository repository, int options) {
         this.repository = repository;
         this.options = options;
      }

      public boolean visit(SQLSelectStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLSelect)x.getSelect());
         return false;
      }

      public boolean visit(SQLSelect x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLSelect)x);
         return false;
      }

      public boolean visit(SQLWithSubqueryClause x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLWithSubqueryClause)x);
         return false;
      }

      public boolean visit(SQLIfStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLIfStatement)x);
         return false;
      }

      public boolean visit(SQLCreateFunctionStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLCreateFunctionStatement)x);
         return false;
      }

      public boolean visit(SQLExprTableSource x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLExprTableSource)x);
         return false;
      }

      public boolean visit(SQLSelectQueryBlock x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLSelectQueryBlock)x);
         return false;
      }

      public boolean visit(SQLForeignKeyImpl x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLForeignKeyConstraint)x);
         return false;
      }

      public boolean visit(SQLIdentifierExpr x) {
         SchemaResolveVisitorFactory.resolveIdent(this, x);
         return true;
      }

      public boolean visit(SQLPropertyExpr x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLPropertyExpr)x);
         return false;
      }

      public boolean visit(SQLBinaryOpExpr x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLBinaryOpExpr)x);
         return false;
      }

      public boolean visit(SQLAllColumnExpr x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLAllColumnExpr)x);
         return false;
      }

      public boolean visit(SQLCreateTableStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLCreateTableStatement)x);
         return false;
      }

      public boolean visit(SQLUpdateStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLUpdateStatement)x);
         return false;
      }

      public boolean visit(SQLDeleteStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLDeleteStatement)x);
         return false;
      }

      public boolean visit(SQLAlterTableStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLAlterTableStatement)x);
         return false;
      }

      public boolean visit(SQLInsertStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLInsertStatement)x);
         return false;
      }

      public boolean visit(SQLParameter x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLParameter)x);
         return false;
      }

      public boolean visit(SQLDeclareItem x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLDeclareItem)x);
         return false;
      }

      public boolean visit(SQLOver x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLOver)x);
         return false;
      }

      public boolean visit(SQLMethodInvokeExpr x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLMethodInvokeExpr)x);
         return false;
      }

      public boolean visit(SQLUnionQuery x) {
         SchemaResolveVisitorFactory.resolveUnion(this, x);
         return false;
      }

      public boolean visit(SQLMergeStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLMergeStatement)x);
         return false;
      }

      public boolean visit(SQLCreateProcedureStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLCreateProcedureStatement)x);
         return false;
      }

      public boolean visit(SQLBlockStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLBlockStatement)x);
         return false;
      }

      public boolean visit(SQLReplaceStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLReplaceStatement)x);
         return false;
      }

      public boolean visit(SQLCastExpr x) {
         x.getExpr().accept(this);
         return true;
      }

      public boolean visit(SQLFetchStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLFetchStatement)x);
         return false;
      }

      public boolean visit(OracleCreatePackageStatement x) {
         this.createContext(x);

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

         this.popContext();
         return false;
      }

      public boolean visit(OracleForStatement x) {
         SchemaResolveVisitor.Context ctx = this.createContext(x);
         SQLName index = x.getIndex();
         SQLExpr range = x.getRange();
         if (index != null) {
            SQLDeclareItem declareItem = new SQLDeclareItem(index, (SQLDataType)null);
            declareItem.setParent(x);
            if (index instanceof SQLIdentifierExpr) {
               ((SQLIdentifierExpr)index).setResolvedDeclareItem(declareItem);
            }

            declareItem.setResolvedObject(range);
            ctx.declare(declareItem);
            if (range instanceof SQLQueryExpr) {
               SQLSelect select = ((SQLQueryExpr)range).getSubQuery();
               SQLSubqueryTableSource tableSource = new SQLSubqueryTableSource(select);
               declareItem.setResolvedObject(tableSource);
            }

            index.accept(this);
         }

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

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

         this.popContext();
         return false;
      }

      public boolean visit(OracleForeignKey x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLForeignKeyConstraint)x);
         return false;
      }

      public boolean visit(OracleSelectTableReference x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLExprTableSource)x);
         return false;
      }

      public boolean visit(OracleSelectQueryBlock x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLSelectQueryBlock)x);
         return false;
      }

      public boolean visit(SQLSelectItem x) {
         SQLExpr expr = x.getExpr();
         if (expr instanceof SQLIdentifierExpr) {
            SchemaResolveVisitorFactory.resolveIdent(this, (SQLIdentifierExpr)expr);
            return false;
         } else if (expr instanceof SQLPropertyExpr) {
            SchemaResolveVisitorFactory.resolve(this, (SQLPropertyExpr)((SQLPropertyExpr)expr));
            return false;
         } else {
            return true;
         }
      }

      public boolean visit(OracleCreateTableStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLCreateTableStatement)x);
         return false;
      }

      public boolean visit(OracleUpdateStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLUpdateStatement)x);
         return false;
      }

      public boolean visit(OracleDeleteStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLDeleteStatement)x);
         return false;
      }

      public boolean visit(OracleMultiInsertStatement x) {
         SchemaResolveVisitor.Context ctx = this.createContext(x);
         SQLSelect select = x.getSubQuery();
         this.visit(select);
         OracleSelectSubqueryTableSource tableSource = new OracleSelectSubqueryTableSource(select);
         tableSource.setParent(x);
         ctx.setTableSource(tableSource);

         for(OracleMultiInsertStatement.Entry entry : x.getEntries()) {
            entry.accept(this);
         }

         this.popContext();
         return false;
      }

      public boolean visit(OracleMultiInsertStatement.InsertIntoClause x) {
         for(SQLExpr column : x.getColumns()) {
            if (column instanceof SQLIdentifierExpr) {
               SQLIdentifierExpr identColumn = (SQLIdentifierExpr)column;
               identColumn.setResolvedTableSource(x.getTableSource());
            }
         }

         return true;
      }

      public boolean visit(OracleInsertStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLInsertStatement)x);
         return false;
      }

      public boolean isEnabled(SchemaResolveVisitor.Option option) {
         return (this.options & option.mask) != 0;
      }

      public int getOptions() {
         return this.options;
      }

      public SchemaResolveVisitor.Context getContext() {
         return this.context;
      }

      public SchemaResolveVisitor.Context createContext(SQLObject object) {
         return this.context = new SchemaResolveVisitor.Context(object, this.context);
      }

      public void popContext() {
         if (this.context != null) {
            this.context = this.context.parent;
         }

      }

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

   static class SQLServerResolveVisitor extends SQLServerASTVisitorAdapter implements SchemaResolveVisitor {
      private int options;
      private SchemaRepository repository;
      private SchemaResolveVisitor.Context context;

      public SQLServerResolveVisitor(SchemaRepository repository, int options) {
         this.repository = repository;
         this.options = options;
      }

      public boolean visit(SQLServerSelectQueryBlock x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLSelectQueryBlock)x);
         return false;
      }

      public boolean visit(SQLSelectItem x) {
         SQLExpr expr = x.getExpr();
         if (expr instanceof SQLIdentifierExpr) {
            SchemaResolveVisitorFactory.resolveIdent(this, (SQLIdentifierExpr)expr);
            return false;
         } else if (expr instanceof SQLPropertyExpr) {
            SchemaResolveVisitorFactory.resolve(this, (SQLPropertyExpr)((SQLPropertyExpr)expr));
            return false;
         } else {
            return true;
         }
      }

      public boolean visit(SQLServerUpdateStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLUpdateStatement)x);
         return false;
      }

      public boolean visit(SQLServerInsertStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLInsertStatement)x);
         return false;
      }

      public boolean isEnabled(SchemaResolveVisitor.Option option) {
         return (this.options & option.mask) != 0;
      }

      public int getOptions() {
         return this.options;
      }

      public SchemaResolveVisitor.Context getContext() {
         return this.context;
      }

      public SchemaResolveVisitor.Context createContext(SQLObject object) {
         return this.context = new SchemaResolveVisitor.Context(object, this.context);
      }

      public void popContext() {
         if (this.context != null) {
            this.context = this.context.parent;
         }

      }

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

      public boolean visit(SQLSelectStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLSelect)x.getSelect());
         return false;
      }

      public boolean visit(SQLSelect x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLSelect)x);
         return false;
      }

      public boolean visit(SQLWithSubqueryClause x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLWithSubqueryClause)x);
         return false;
      }

      public boolean visit(SQLIfStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLIfStatement)x);
         return false;
      }

      public boolean visit(SQLCreateFunctionStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLCreateFunctionStatement)x);
         return false;
      }

      public boolean visit(SQLExprTableSource x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLExprTableSource)x);
         return false;
      }

      public boolean visit(SQLSelectQueryBlock x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLSelectQueryBlock)x);
         return false;
      }

      public boolean visit(SQLForeignKeyImpl x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLForeignKeyConstraint)x);
         return false;
      }

      public boolean visit(SQLIdentifierExpr x) {
         SchemaResolveVisitorFactory.resolveIdent(this, x);
         return true;
      }

      public boolean visit(SQLPropertyExpr x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLPropertyExpr)x);
         return false;
      }

      public boolean visit(SQLBinaryOpExpr x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLBinaryOpExpr)x);
         return false;
      }

      public boolean visit(SQLAllColumnExpr x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLAllColumnExpr)x);
         return false;
      }

      public boolean visit(SQLCreateTableStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLCreateTableStatement)x);
         return false;
      }

      public boolean visit(SQLUpdateStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLUpdateStatement)x);
         return false;
      }

      public boolean visit(SQLDeleteStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLDeleteStatement)x);
         return false;
      }

      public boolean visit(SQLAlterTableStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLAlterTableStatement)x);
         return false;
      }

      public boolean visit(SQLInsertStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLInsertStatement)x);
         return false;
      }

      public boolean visit(SQLParameter x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLParameter)x);
         return false;
      }

      public boolean visit(SQLDeclareItem x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLDeclareItem)x);
         return false;
      }

      public boolean visit(SQLOver x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLOver)x);
         return false;
      }

      public boolean visit(SQLMethodInvokeExpr x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLMethodInvokeExpr)x);
         return false;
      }

      public boolean visit(SQLUnionQuery x) {
         SchemaResolveVisitorFactory.resolveUnion(this, x);
         return false;
      }

      public boolean visit(SQLMergeStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLMergeStatement)x);
         return false;
      }

      public boolean visit(SQLCreateProcedureStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLCreateProcedureStatement)x);
         return false;
      }

      public boolean visit(SQLBlockStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLBlockStatement)x);
         return false;
      }

      public boolean visit(SQLReplaceStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLReplaceStatement)x);
         return false;
      }

      public boolean visit(SQLCastExpr x) {
         x.getExpr().accept(this);
         return true;
      }

      public boolean visit(SQLFetchStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLFetchStatement)x);
         return false;
      }
   }

   static class GPResolveVisitor extends GPASTVisitorAdapter implements SchemaResolveVisitor {
      private int options;
      private SchemaRepository repository;
      private SchemaResolveVisitor.Context context;

      public GPResolveVisitor(SchemaRepository repository, int options) {
         this.repository = repository;
         this.options = options;
      }

      public boolean visit(GPSelectQueryBlock x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLSelectQueryBlock)x);
         return false;
      }

      public boolean visit(SQLSelectItem x) {
         SQLExpr expr = x.getExpr();
         if (expr instanceof SQLIdentifierExpr) {
            SchemaResolveVisitorFactory.resolveIdent(this, (SQLIdentifierExpr)expr);
            return false;
         } else if (expr instanceof SQLPropertyExpr) {
            SchemaResolveVisitorFactory.resolve(this, (SQLPropertyExpr)((SQLPropertyExpr)expr));
            return false;
         } else {
            return true;
         }
      }

      public boolean visit(GPUpdateStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLUpdateStatement)x);
         return false;
      }

      public boolean visit(GPInsertStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLInsertStatement)x);
         return false;
      }

      public boolean isEnabled(SchemaResolveVisitor.Option option) {
         return (this.options & option.mask) != 0;
      }

      public int getOptions() {
         return this.options;
      }

      public SchemaResolveVisitor.Context getContext() {
         return this.context;
      }

      public SchemaResolveVisitor.Context createContext(SQLObject object) {
         return this.context = new SchemaResolveVisitor.Context(object, this.context);
      }

      public void popContext() {
         if (this.context != null) {
            this.context = this.context.parent;
         }

      }

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

      public boolean visit(GPSelectStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLSelect)x.getSelect());
         return false;
      }

      public boolean visit(SQLSelect x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLSelect)x);
         return false;
      }

      public boolean visit(SQLWithSubqueryClause x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLWithSubqueryClause)x);
         return false;
      }

      public boolean visit(SQLIfStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLIfStatement)x);
         return false;
      }

      public boolean visit(SQLCreateFunctionStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLCreateFunctionStatement)x);
         return false;
      }

      public boolean visit(SQLExprTableSource x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLExprTableSource)x);
         return false;
      }

      public boolean visit(SQLSelectQueryBlock x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLSelectQueryBlock)x);
         return false;
      }

      public boolean visit(SQLForeignKeyImpl x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLForeignKeyConstraint)x);
         return false;
      }

      public boolean visit(SQLIdentifierExpr x) {
         SchemaResolveVisitorFactory.resolveIdent(this, x);
         return true;
      }

      public boolean visit(SQLPropertyExpr x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLPropertyExpr)x);
         return false;
      }

      public boolean visit(SQLBinaryOpExpr x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLBinaryOpExpr)x);
         return false;
      }

      public boolean visit(SQLAllColumnExpr x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLAllColumnExpr)x);
         return false;
      }

      public boolean visit(SQLCreateTableStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLCreateTableStatement)x);
         return false;
      }

      public boolean visit(SQLUpdateStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLUpdateStatement)x);
         return false;
      }

      public boolean visit(SQLDeleteStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLDeleteStatement)x);
         return false;
      }

      public boolean visit(SQLAlterTableStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLAlterTableStatement)x);
         return false;
      }

      public boolean visit(SQLInsertStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLInsertStatement)x);
         return false;
      }

      public boolean visit(SQLParameter x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLParameter)x);
         return false;
      }

      public boolean visit(SQLDeclareItem x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLDeclareItem)x);
         return false;
      }

      public boolean visit(SQLOver x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLOver)x);
         return false;
      }

      public boolean visit(SQLMethodInvokeExpr x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLMethodInvokeExpr)x);
         return false;
      }

      public boolean visit(SQLUnionQuery x) {
         SchemaResolveVisitorFactory.resolveUnion(this, x);
         return false;
      }

      public boolean visit(SQLMergeStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLMergeStatement)x);
         return false;
      }

      public boolean visit(SQLCreateProcedureStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLCreateProcedureStatement)x);
         return false;
      }

      public boolean visit(SQLBlockStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLBlockStatement)x);
         return false;
      }

      public boolean visit(SQLReplaceStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLReplaceStatement)x);
         return false;
      }

      public boolean visit(SQLCastExpr x) {
         x.getExpr().accept(this);
         return true;
      }

      public boolean visit(SQLFetchStatement x) {
         SchemaResolveVisitorFactory.resolve(this, (SQLFetchStatement)x);
         return false;
      }
   }

   static class SQLResolveVisitor extends SQLASTVisitorAdapter implements SchemaResolveVisitor {
      private int options;
      private SchemaRepository repository;
      private SchemaResolveVisitor.Context context;

      public SQLResolveVisitor(SchemaRepository repository, int options) {
         this.repository = repository;
         this.options = options;
      }

      public boolean visit(SQLSelectItem x) {
         SQLExpr expr = x.getExpr();
         if (expr instanceof SQLIdentifierExpr) {
            SchemaResolveVisitorFactory.resolveIdent(this, (SQLIdentifierExpr)expr);
            return false;
         } else if (expr instanceof SQLPropertyExpr) {
            SchemaResolveVisitorFactory.resolve(this, (SQLPropertyExpr)((SQLPropertyExpr)expr));
            return false;
         } else {
            return true;
         }
      }

      public boolean isEnabled(SchemaResolveVisitor.Option option) {
         return (this.options & option.mask) != 0;
      }

      public int getOptions() {
         return this.options;
      }

      public SchemaResolveVisitor.Context getContext() {
         return this.context;
      }

      public SchemaResolveVisitor.Context createContext(SQLObject object) {
         return this.context = new SchemaResolveVisitor.Context(object, this.context);
      }

      public void popContext() {
         if (this.context != null) {
            this.context = this.context.parent;
         }

      }

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