content: /*
   * Copyright 1999-2017 Alibaba Group Holding Ltd.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package com.alibaba.druid.sql.repository;
  
  import com.alibaba.druid.sql.SQLUtils;
  import com.alibaba.druid.sql.ast.*;
  import com.alibaba.druid.sql.ast.expr.*;
  import com.alibaba.druid.sql.ast.statement.*;
  import com.alibaba.druid.sql.dialect.db2.ast.DB2Object;
  import com.alibaba.druid.sql.dialect.db2.ast.stmt.DB2SelectQueryBlock;
  import com.alibaba.druid.sql.dialect.db2.visitor.DB2ASTVisitorAdapter;
  import com.alibaba.druid.sql.dialect.hive.ast.HiveInsert;
  import com.alibaba.druid.sql.dialect.hive.ast.HiveInsertStatement;
  import com.alibaba.druid.sql.dialect.hive.ast.HiveMultiInsertStatement;
  import com.alibaba.druid.sql.dialect.hive.visitor.HiveASTVisitorAdapter;
  import com.alibaba.druid.sql.dialect.mysql.ast.MysqlForeignKey;
  import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlCursorDeclareStatement;
  import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareStatement;
  import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlRepeatStatement;
  import com.alibaba.druid.sql.dialect.mysql.ast.statement.*;
  import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitorAdapter;
  import com.alibaba.druid.sql.dialect.odps.ast.OdpsCreateTableStatement;
  import com.alibaba.druid.sql.dialect.odps.ast.OdpsSelectQueryBlock;
  import com.alibaba.druid.sql.dialect.odps.visitor.OdpsASTVisitorAdapter;
  import com.alibaba.druid.sql.dialect.oracle.ast.stmt.*;
  import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitorAdapter;
  import com.alibaba.druid.sql.dialect.postgresql.ast.stmt.*;
  import com.alibaba.druid.sql.dialect.postgresql.visitor.PGASTVisitorAdapter;
  import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerSelectQueryBlock;
  import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerInsertStatement;
  import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerUpdateStatement;
  import com.alibaba.druid.sql.dialect.sqlserver.visitor.SQLServerASTVisitorAdapter;
  import com.alibaba.druid.sql.visitor.SQLASTVisitorAdapter;
  import com.alibaba.druid.util.FnvHash;
  import com.alibaba.druid.util.PGUtils;
  
  import java.util.ArrayList;
  import java.util.List;
  
  class SchemaResolveVisitorFactory {
      static class MySqlResolveVisitor extends MySqlASTVisitorAdapter implements SchemaResolveVisitor {
          private SchemaRepository repository;
          private int options;
          private 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()) {
                  visit(declareItem);
              }
              return false;
          }
  
          public boolean visit(MySqlCursorDeclareStatement x) {
              return true;
          }
  
          public boolean visit(MysqlForeignKey x) {
              resolve(this, x);
              return false;
          }
  
          public boolean visit(MySqlSelectQueryBlock x) {
              resolve(this, x);
              return false;
          }
  
          public boolean visit(SQLSelectItem x) {
              SQLExpr expr = x.getExpr();
              if (expr instanceof SQLIdentifierExpr) {
                  resolveIdent(this, (SQLIdentifierExpr) expr);
                  return false;
              }
  
              if (expr instanceof SQLPropertyExpr) {
                  resolve(this, (SQLPropertyExpr) expr);
                  return false;
              }
  
              return true;
          }
  
          public boolean visit(MySqlCreateTableStatement x) {
              resolve(this, x);
              SQLExprTableSource like = x.getLike();
              if (like != null) {
                  like.accept(this);
              }
              return false;
          }
  
          public boolean visit(MySqlUpdateStatement x) {
              resolve(this, x);
              return false;
          }
  
          public boolean visit(MySqlDeleteStatement x) {
              resolve(this, x);
              return false;
          }
  
          public boolean visit(MySqlInsertStatement x) {
              resolve(this, x);
              return false;
          }
  
          @Override
          public boolean isEnabled(Option option) {
              return (options & option.mask) != 0;
          }
  
          public int getOptions() {
              return options;
          }
  
          @Override
          public Context getContext() {
              return context;
          }
  
          public Context createContext(SQLObject object) {
              return this.context = new Context(object, context);
          }
  
          @Override
          public void popContext() {
              if (context != null) {
                  context = context.parent;
              }
          }
  
          public SchemaRepository getRepository() {
              return repository;
          }
      }
  
      static class DB2ResolveVisitor extends DB2ASTVisitorAdapter implements SchemaResolveVisitor {
          private SchemaRepository repository;
          private int options;
          private Context context;
  
          public DB2ResolveVisitor(SchemaRepository repository, int options) {
              this.repository = repository;
              this.options = options;
          }
  
          public boolean visit(DB2SelectQueryBlock x) {
              resolve(this, x);
              return false;
          }
  
          public boolean visit(SQLSelectItem x) {
              SQLExpr expr = x.getExpr();
              if (expr instanceof SQLIdentifierExpr) {
                  resolveIdent(this, (SQLIdentifierExpr) expr);
                  return false;
              }
  
              if (expr instanceof SQLPropertyExpr) {
                  resolve(this, (SQLPropertyExpr) expr);
                  return false;
              }
  
              return true;
          }
  
          public boolean visit(SQLIdentifierExpr x) {
              long hash64 = x.hashCode64();
              if (hash64 == FnvHash.Constants.CURRENT_DATE || hash64 == DB2Object.Constants.CURRENT_TIME) {
                  return false;
              }
  
              resolveIdent(this, x);
              return true;
          }
  
          @Override
          public boolean isEnabled(Option option) {
              return (options & option.mask) != 0;
          }
  
          public int getOptions() {
              return options;
          }
  
          @Override
          public Context getContext() {
              return context;
          }
  
          public Context createContext(SQLObject object) {
              return this.context = new Context(object, context);
          }
  
          @Override
          public void popContext() {
              if (context != null) {
                  context = context.parent;
              }
          }
  
          @Override
          public SchemaRepository getRepository() {
              return repository;
          }
      }
  
      static class OracleResolveVisitor extends OracleASTVisitorAdapter implements SchemaResolveVisitor {
          private SchemaRepository repository;
          private int options;
          private Context context;
  
          public OracleResolveVisitor(SchemaRepository repository, int options) {
              this.repository = repository;
              this.options = options;
          }
  
          public boolean visit(OracleCreatePackageStatement x) {
              Context ctx = createContext(x);
  
              for (SQLStatement stmt : x.getStatements()) {
                  stmt.accept(this);
              }
  
              popContext();
              return false;
          }
  
          public boolean visit(OracleForStatement x) {
              Context ctx = createContext(x);
  
              SQLName index = x.getIndex();
              SQLExpr range = x.getRange();
  
              if (index != null) {
                  SQLDeclareItem declareItem = new SQLDeclareItem(index, 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);
              }
  
              popContext();
              return false;
          }
  
          public boolean visit(OracleForeignKey x) {
              resolve(this, x);
              return false;
          }
  
          public boolean visit(OracleSelectTableReference x) {
              resolve(this, x);
              return false;
          }
  
          public boolean visit(OracleSelectQueryBlock x) {
              resolve(this, x);
              return false;
          }
  
          public boolean visit(SQLSelectItem x) {
              SQLExpr expr = x.getExpr();
              if (expr instanceof SQLIdentifierExpr) {
                  resolveIdent(this, (SQLIdentifierExpr) expr);
                  return false;
              }
  
              if (expr instanceof SQLPropertyExpr) {
                  resolve(this, (SQLPropertyExpr) expr);
                  return false;
              }
  
              return true;
          }
  
          public boolean visit(SQLIdentifierExpr x) {
              if (x.nameHashCode64() == FnvHash.Constants.ROWNUM) {
                  return false;
              }
  
              resolveIdent(this, x);
              return true;
          }
  
          public boolean visit(OracleCreateTableStatement x) {
              resolve(this, x);
              return false;
          }
  
          public boolean visit(OracleUpdateStatement x) {
              resolve(this, x);
              return false;
          }
  
          public boolean visit(OracleDeleteStatement x) {
              resolve(this, x);
              return false;
          }
  
          public boolean visit(OracleMultiInsertStatement x) {
              Context ctx = createContext(x);
  
              SQLSelect select = x.getSubQuery();
              visit(select);
  
              OracleSelectSubqueryTableSource tableSource = new OracleSelectSubqueryTableSource(select);
              tableSource.setParent(x);
              ctx.setTableSource(tableSource);
  
              for (OracleMultiInsertStatement.Entry entry : x.getEntries()) {
                  entry.accept(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) {
              resolve(this, x);
              return false;
          }
  
          @Override
          public boolean isEnabled(Option option) {
              return (options & option.mask) != 0;
          }
  
          public int getOptions() {
              return options;
          }
  
          @Override
          public Context getContext() {
              return context;
          }
  
          public Context createContext(SQLObject object) {
              return this.context = new Context(object, context);
          }
  
          @Override
          public void popContext() {
              if (context != null) {
                  context = context.parent;
              }
          }
  
          public SchemaRepository getRepository() {
              return repository;
          }
      }
  
      static class OdpsResolveVisitor extends OdpsASTVisitorAdapter implements SchemaResolveVisitor {
          private int options;
          private SchemaRepository repository;
          private Context context;
  
          public OdpsResolveVisitor(SchemaRepository repository, int options) {
              this.repository = repository;
              this.options = options;
          }
  
          public boolean visit(OdpsSelectQueryBlock x) {
              resolve(this, x);
              return false;
          }
  
          public boolean visit(SQLSelectItem x) {
              SQLExpr expr = x.getExpr();
              if (expr instanceof SQLIdentifierExpr) {
                  resolveIdent(this, (SQLIdentifierExpr) expr);
                  return false;
              }
  
              if (expr instanceof SQLPropertyExpr) {
                  resolve(this, (SQLPropertyExpr) expr);
                  return false;
              }
  
              return true;
          }
  
          public boolean visit(OdpsCreateTableStatement x) {
              resolve(this, x);
              return false;
          }
  
          public boolean visit(HiveInsert x) {
              Context ctx = createContext(x);
  
              SQLExprTableSource tableSource = x.getTableSource();
              if (tableSource != null) {
                  ctx.setTableSource(x.getTableSource());
                  visit(tableSource);
              }
  
              List<SQLAssignItem> partitions = x.getPartitions();
              if (partitions != null) {
                  for (SQLAssignItem item : partitions) {
                      item.accept(this);
                  }
              }
  
              SQLSelect select = x.getQuery();
              if (select != null) {
                  visit(select);
              }
  
              popContext();
              return false;
          }
  
          public boolean visit(HiveInsertStatement x) {
              resolve(this, x);
              return false;
          }
  
          @Override
          public boolean isEnabled(Option option) {
              return (options & option.mask) != 0;
          }
  
          public int getOptions() {
              return options;
          }
  
          @Override
          public Context getContext() {
              return context;
          }
  
          public Context createContext(SQLObject object) {
              return this.context = new Context(object, context);
          }
  
          @Override
          public void popContext() {
              if (context != null) {
                  context = context.parent;
              }
          }
  
          public SchemaRepository getRepository() {
              return repository;
          }
      }
  
      static class HiveResolveVisitor extends HiveASTVisitorAdapter implements SchemaResolveVisitor {
          private int options;
          private SchemaRepository repository;
          private Context context;
  
          public HiveResolveVisitor(SchemaRepository repository, int options) {
              this.repository = repository;
              this.options = options;
          }
  
          public boolean visit(OdpsSelectQueryBlock x) {
              resolve(this, x);
              return false;
          }
  
          public boolean visit(SQLSelectItem x) {
              SQLExpr expr = x.getExpr();
              if (expr instanceof SQLIdentifierExpr) {
                  resolveIdent(this, (SQLIdentifierExpr) expr);
                  return false;
              }
  
              if (expr instanceof SQLPropertyExpr) {
                  resolve(this, (SQLPropertyExpr) expr);
                  return false;
              }
  
              return true;
          }
  
          public boolean visit(SQLCreateTableStatement x) {
              resolve(this, x);
              return false;
          }
  
          public boolean visit(HiveInsert x) {
              Context ctx = createContext(x);
  
              SQLExprTableSource tableSource = x.getTableSource();
              if (tableSource != null) {
                  ctx.setTableSource(x.getTableSource());
                  visit(tableSource);
              }
  
              List<SQLAssignItem> partitions = x.getPartitions();
              if (partitions != null) {
                  for (SQLAssignItem item : partitions) {
                      item.accept(this);
                  }
              }
  
              SQLSelect select = x.getQuery();
              if (select != null) {
                  visit(select);
              }
  
              popContext();
              return false;
          }
  
          @Override
          public boolean isEnabled(Option option) {
              return (options & option.mask) != 0;
          }
  
          public int getOptions() {
              return options;
          }
  
          @Override
          public Context getContext() {
              return context;
          }
  
          public Context createContext(SQLObject object) {
              return this.context = new Context(object, context);
          }
  
          @Override
          public void popContext() {
              if (context != null) {
                  context = context.parent;
              }
          }
  
          public SchemaRepository getRepository() {
              return repository;
          }
      }
  
      static class PGResolveVisitor extends PGASTVisitorAdapter implements SchemaResolveVisitor {
          private int options;
          private SchemaRepository repository;
          private Context context;
  
          public PGResolveVisitor(SchemaRepository repository, int options) {
              this.repository = repository;
              this.options = options;
          }
  
          public boolean visit(PGSelectQueryBlock x) {
              resolve(this, x);
              return false;
          }
  
          public boolean visit(PGFunctionTableSource x) {
              for (SQLParameter parameter : x.getParameters()) {
                  SQLName name = parameter.getName();
                  if (name instanceof SQLIdentifierExpr) {
                      SQLIdentifierExpr identName = (SQLIdentifierExpr) name;
                      identName.setResolvedTableSource(x);
                  }
              }
  
              return false;
          }
  
          public boolean visit(SQLSelectItem x) {
              SQLExpr expr = x.getExpr();
              if (expr instanceof SQLIdentifierExpr) {
                  resolveIdent(this, (SQLIdentifierExpr) expr);
                  return false;
              }
  
              if (expr instanceof SQLPropertyExpr) {
                  resolve(this, (SQLPropertyExpr) expr);
                  return false;
              }
  
              return true;
          }
  
          public boolean visit(SQLIdentifierExpr x) {
              if (PGUtils.isPseudoColumn(x.nameHashCode64())) {
                  return false;
              }
  
              resolveIdent(this, x);
              return true;
          }
  
          public boolean visit(PGUpdateStatement x) {
              resolve(this, x);
              return false;
          }
  
          public boolean visit(PGDeleteStatement x) {
              resolve(this, x);
              return false;
          }
  
          public boolean visit(PGSelectStatement x) {
              createContext(x);
              visit(x.getSelect());
              popContext();
              return false;
          }
  
          public boolean visit(PGInsertStatement x) {
              resolve(this, x);
              return false;
          }
  
          @Override
          public boolean isEnabled(Option option) {
              return (options & option.mask) != 0;
          }
  
          public int getOptions() {
              return options;
          }
  
          @Override
          public Context getContext() {
              return context;
          }
  
          public Context createContext(SQLObject object) {
              return this.context = new Context(object, context);
          }
  
          @Override
          public void popContext() {
              if (context != null) {
                  context = context.parent;
              }
          }
  
          public SchemaRepository getRepository() {
              return repository;
          }
      }
  
      static class SQLServerResolveVisitor extends SQLServerASTVisitorAdapter implements SchemaResolveVisitor {
          private int options;
          private SchemaRepository repository;
          private Context context;
  
          public SQLServerResolveVisitor(SchemaRepository repository, int options) {
              this.repository = repository;
              this.options = options;
          }
  
          public boolean visit(SQLServerSelectQueryBlock x) {
              resolve(this, x);
              return false;
          }
  
          public boolean visit(SQLSelectItem x) {
              SQLExpr expr = x.getExpr();
              if (expr instanceof SQLIdentifierExpr) {
                  resolveIdent(this, (SQLIdentifierExpr) expr);
                  return false;
              }
  
              if (expr instanceof SQLPropertyExpr) {
                  resolve(this, (SQLPropertyExpr) expr);
                  return false;
              }
  
              return true;
          }
  
          public boolean visit(SQLServerUpdateStatement x) {
              resolve(this, x);
              return false;
          }
  
          public boolean visit(SQLServerInsertStatement x) {
              resolve(this, x);
              return false;
          }
  
          @Override
          public boolean isEnabled(Option option) {
              return (options & option.mask) != 0;
          }
  
          public int getOptions() {
              return options;
          }
  
          @Override
          public Context getContext() {
              return context;
          }
  
          public Context createContext(SQLObject object) {
              return this.context = new Context(object, context);
          }
  
          @Override
          public void popContext() {
              if (context != null) {
                  context = context.parent;
              }
          }
  
          public SchemaRepository getRepository() {
              return repository;
          }
      }
  
      static class SQLResolveVisitor extends SQLASTVisitorAdapter implements SchemaResolveVisitor {
          private int options;
          private SchemaRepository repository;
          private 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) {
                  resolveIdent(this, (SQLIdentifierExpr) expr);
                  return false;
              }
  
              if (expr instanceof SQLPropertyExpr) {
                  resolve(this, (SQLPropertyExpr) expr);
                  return false;
              }
  
              return true;
          }
  
          @Override
          public boolean isEnabled(Option option) {
              return (options & option.mask) != 0;
          }
  
          public int getOptions() {
              return options;
          }
  
          @Override
          public Context getContext() {
              return context;
          }
  
          public Context createContext(SQLObject object) {
              return this.context = new Context(object, context);
          }
  
          @Override
          public void popContext() {
              if (context != null) {
                  context = context.parent;
              }
          }
  
          public SchemaRepository getRepository() {
              return repository;
          }
      }
  
      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 = 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) {
                  List<SQLSelectOrderByItem> columns = ((SQLUniqueConstraint) e).getColumns();
                  for (SQLSelectOrderByItem orderByItem : columns) {
                      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);
          }
  
          List<SQLUpdateSetItem> items = x.getItems();
          for (SQLUpdateSetItem item : items) {
              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);
          }
  
          if (x instanceof HiveInsertStatement) {
              for (SQLAssignItem item : ((HiveInsertStatement) x).getPartitions()) {
                  item.accept(visitor);
              }
          }
  
          for (SQLInsertStatement.ValuesClause valuesClause : x.getValuesList()) {
              valuesClause.accept(visitor);
          }
  
          SQLSelect query = x.getQuery();
          if (query != null) {
              visitor.visit(query);
          }
  
          if (x instanceof OracleInsertStatement) {
              SQLObject returning = ((OracleInsertStatement) x).getReturning();
              if (returning != null) {
                  returning.accept(visitor);
              }
          }
  
          visitor.popContext();
      }
  
      static void resolveIdent(SchemaResolveVisitor visitor, SQLIdentifierExpr x) {
          SchemaResolveVisitor.Context ctx = visitor.getContext();
          if (ctx == null) {
              return;
          }
  
          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) {
                  final SQLTableSource left = join.getLeft(), 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 (tableSource instanceof SQLExprTableSource) {
                  SchemaObject table = ((SQLExprTableSource) tableSource).getSchemaObject();
                  if (table != null) {
                      if (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; // maybe parent
                          }
                      }
                  }
              }
          }
  
          if (tableSource instanceof SQLExprTableSource) {
              SQLExpr expr = ((SQLExprTableSource) tableSource).getExpr();
  
              if (expr instanceof SQLMethodInvokeExpr) {
                  SQLMethodInvokeExpr func = (SQLMethodInvokeExpr) expr;
                  if (func.methodNameHashCode64() == FnvHash.Constants.ANN) {
                      expr = 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);
  
              SQLObject column = tableSource.findColumn(hash);
              if (column == null) {
                  column = tableSource.resolveColumn(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(x, 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;
              for (SQLObject current = x, parent = x.getParent(); parent != null; current = parent, parent = parent.getParent()) {
                  if (parent instanceof SQLSelectGroupByClause && parent.getParent() == queryBlock) {
                      SQLSelectGroupByClause groupBy = (SQLSelectGroupByClause) parent;
                      if (current == groupBy.getHaving()) {
                          having = true;
                      }
                      break;
                  }
              }
              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) {
              return;
          }
  
          long owner_hash = 0;
          {
              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) {
          final SQLBinaryOperator op = x.getOperator();
          final 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 = groupList.get(i);
                  item.accept(visitor);
              }
              return;
          }
  
          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;
          }
  
          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);
          if (ctx != null && ctx.level >= 32) {
              return;
          }
  
          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);
              }
          } else if (x.getParent() != null && x.getParent().getParent() instanceof HiveInsert
                  && x.getParent().getParent().getParent() instanceof HiveMultiInsertStatement) {
              HiveMultiInsertStatement insert = (HiveMultiInsertStatement) x.getParent().getParent().getParent();
              if (insert.getFrom() instanceof SQLExprTableSource) {
                  from = insert.getFrom();
                  ctx.setTableSource(from);
              }
          }
  
          List<SQLSelectItem> selectList = x.getSelectList();
  
          List<SQLSelectItem> columns = new ArrayList<SQLSelectItem>();
          for (int i = selectList.size() - 1; i >= 0; i--) {
              SQLSelectItem selectItem = selectList.get(i);
              SQLExpr expr = selectItem.getExpr();
              if (expr instanceof SQLAllColumnExpr) {
                  SQLAllColumnExpr allColumnExpr = (SQLAllColumnExpr) expr;
                  SQLExpr owner = allColumnExpr.getOwner();
                  SQLTableSource resolvedTableSource = from;
                  if (owner instanceof SQLIdentifierExpr) {
                      String ownerName = ((SQLIdentifierExpr) owner).getName();
                      resolvedTableSource = x.findTableSource(ownerName);
                  }
                  allColumnExpr.setResolvedTableSource(resolvedTableSource);
  
                  visitor.visit(allColumnExpr);
  
                  if (visitor.isEnabled(SchemaResolveVisitor.Option.ResolveAllColumn)) {
                      extractColumns(visitor, resolvedTableSource, null, columns);
                  }
              } else if (expr instanceof SQLPropertyExpr) {
                  SQLPropertyExpr propertyExpr = (SQLPropertyExpr) expr;
                  visitor.visit(propertyExpr);
  
                  String ownerName = propertyExpr.getOwnernName();
                  if (propertyExpr.getName().equals("*")) {
                      if (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 SQLAllColumnExpr) {
                  SQLAllColumnExpr allColumnExpr = (SQLAllColumnExpr) expr;
                  SQLExpr owner = allColumnExpr.getOwner();
                  if (owner instanceof SQLIdentifierExpr) {
                      SQLIdentifierExpr ownerIdent = (SQLIdentifierExpr) owner;
                      String ownerName = ownerIdent.getName();
                      if (visitor.isEnabled(SchemaResolveVisitor.Option.ResolveAllColumn)) {
                          SQLTableSource tableSource = x.findTableSource(ownerName);
                          extractColumns(visitor, tableSource, ownerName, columns);
                      }
                  }
              } else if (expr instanceof SQLIdentifierExpr) {
                  SQLIdentifierExpr identExpr = (SQLIdentifierExpr) expr;
                  visitor.visit(identExpr);
  
                  long name_hash = identExpr.nameHashCode64();
  
                  SQLColumnDefinition column = identExpr.getResolvedColumn();
                  if (column != null) {
                      continue;
                  }
                  if (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;
              }
              final List<SQLSelectItem> subSelectList = subQuery.getSelectList();
              for (SQLSelectItem subSelectItem : subSelectList) {
                  if (subSelectItem.getAlias() != null) {
                      continue;
                  }
  
                  if (!(subSelectItem.getExpr() instanceof SQLName)) {
                      return; // skip
                  }
              }
  
              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;
              }
  
              final List<SQLSelectItem> subSelectList = firstQueryBlock.getSelectList();
              for (SQLSelectItem subSelectItem : subSelectList) {
                  if (subSelectItem.getAlias() != null) {
                      continue;
                  }
  
                  if (!(subSelectItem.getExpr() instanceof SQLName)) {
                      return; // skip
                  }
              }
  
              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) {
          SchemaResolveVisitor.Context ctx = visitor.createContext(x);
  
          SQLWithSubqueryClause with = x.getWithSubQuery();
          if (with != null) {
              visitor.visit(with);
          }
  
          SQLSelectQuery query = x.getQuery();
          if (query != null) {
              if (query instanceof SQLSelectQueryBlock) {
                  try {
                      visitor.visit((SQLSelectQueryBlock) query);
                  } catch (StackOverflowError ignored) {
                      // ignore
                  }
              } 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();
          final SchemaResolveVisitor.Context context = visitor.getContext();
          for (SQLWithSubqueryClause.Entry entry : entries) {
              SQLSelect query = entry.getSubQuery();
              if (query != null) {
                  visitor.visit(query);
  
                  final long alias_hash = entry.aliasHashCode64();
                  if (context != null && alias_hash != 0) {
                      context.addTableSource(alias_hash, entry);
                  }
              } else if (entry.getExpr() != null) {
                  entry.getExpr().accept(visitor);
              } else {
                  SQLStatement returningStatement = entry.getReturningStatement();
                  if (returningStatement != null) {
                      returningStatement.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 = func.getArguments().get(0);
  
                  annFeature = 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 || expr instanceof SQLAllColumnExpr) {
              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;
                  }
              } else if (expr instanceof SQLAllColumnExpr) {
                  SQLExpr owner = ((SQLAllColumnExpr) expr).getOwner();
                  if (owner instanceof SQLIdentifierExpr) {
                      identifierExpr = (SQLIdentifierExpr) owner;
                  }
              }
  
              if (identifierExpr != null) {
                  checkParameter(visitor, identifierExpr);
  
                  SQLTableSource tableSource = unwrapAlias(visitor.getContext(), null, identifierExpr.nameHashCode64());
                  if (tableSource == null && x.getParent() instanceof HiveMultiInsertStatement) {
                      SQLWithSubqueryClause with = ((HiveMultiInsertStatement) x.getParent()).getWith();
                      if (with != null) {
                          SQLWithSubqueryClause.Entry entry = with.findEntry(identifierExpr.nameHashCode64());
                          tableSource = entry;
                      }
                  }
  
                  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) {
                          if (annFeature instanceof SQLIdentifierExpr) {
                              SQLIdentifierExpr identExpr = (SQLIdentifierExpr) annFeature;
                              SQLColumnDefinition column = table.findColumn(identExpr.nameHashCode64());
                              if (column != null) {
                                  identExpr.setResolvedColumn(column);
                              }
                          }
                      }
                      return;
                  }
  
                  SchemaObject view = repository.findView((SQLName) expr);
                  if (view != null) {
                      x.setSchemaObject(view);
                      return;
                  }
              }
              return;
          }
  
          if (expr instanceof SQLMethodInvokeExpr) {
              visitor.visit((SQLMethodInvokeExpr) expr);
              return;
          }
  
          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);
                  }
              }
              //if (queryBlock.findColumn())
              return;
          }
  
          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);
          }
  
          List<SQLMergeStatement.When> whens = x.getWhens();
          for (SQLMergeStatement.When when : whens) {
              SQLName by = when.getBy();
              if (by != null) {
                  by.accept(visitor);
              }
  
              if (when instanceof SQLMergeStatement.WhenUpdate) {
                  SQLMergeStatement.WhenUpdate updateClause = (SQLMergeStatement.WhenUpdate) when;
                  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);
                      }
                  }
              } else if (when instanceof SQLMergeStatement.WhenInsert) {
                  SQLMergeStatement.WhenInsert insertClause = (SQLMergeStatement.WhenInsert) when;
                  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 = when.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(), 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(), 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) {
          SchemaResolveVisitor.Context ctx = 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) {
          SchemaResolveVisitor.Context ctx = 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 // skip if of is not 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;
          }
  
          SchemaResolveVisitor.Context ctx = visitor.getContext();
          if (ctx == null) {
              return false;
          }
  
          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;
                      }
                  }
  
                  SchemaRepository repo = visitor.getRepository();
                  if (repo != null) {
                      SchemaObject view = repo.findView(x);
                      if (view != null && view.getStatement() instanceof SQLCreateViewStatement) {
                          x.setResolvedOwnerObject(view.getStatement());
                      }
                  }
              }
  
              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) {
              return;
          }
  
          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) {
              return;
          }
  
          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);
      }
  
      // for performance
      static void resolveExpr(SchemaResolveVisitor visitor, SQLExpr x) {
          if (x == null) {
              return;
          }
  
          Class<?> clazz = x.getClass();
          if (clazz == SQLIdentifierExpr.class) {
              visitor.visit((SQLIdentifierExpr) x);
              return;
          } else if (clazz == SQLIntegerExpr.class || clazz == SQLCharExpr.class) {
              // skip
              return;
          }
  
          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);
              }
              return;
          }
          SQLSelectQuery left = x.getLeft();
          SQLSelectQuery right = x.getRight();
  
          boolean bracket = x.isParenthesized() && !(x.getParent() instanceof SQLUnionQueryTableSource);
  
          if ((!bracket)
                  && left instanceof SQLUnionQuery
                  && ((SQLUnionQuery) left).getOperator() == operator
                  && !right.isParenthesized()
                  && x.getOrderBy() == null) {
              SQLUnionQuery leftUnion = (SQLUnionQuery) left;
  
              List<SQLSelectQuery> rights = new ArrayList<SQLSelectQuery>();
              rights.add(right);
  
              if (leftUnion.getRelations().size() > 2) {
                  rights.addAll(leftUnion.getRelations());
              } else {
                  for (; ; ) {
                      SQLSelectQuery leftLeft = leftUnion.getLeft();
                      SQLSelectQuery leftRight = leftUnion.getRight();
  
                      if ((!leftUnion.isParenthesized())
                              && leftUnion.getOrderBy() == null
                              && (!leftLeft.isParenthesized())
                              && (!leftRight.isParenthesized())
                              && leftLeft instanceof SQLUnionQuery
                              && ((SQLUnionQuery) leftLeft).getOperator() == operator) {
                          rights.add(leftRight);
                          leftUnion = (SQLUnionQuery) leftLeft;
                          continue;
                      } else {
                          rights.add(leftRight);
                          rights.add(leftLeft);
                      }
                      break;
                  }
              }
  
              for (int i = rights.size() - 1; i >= 0; i--) {
                  SQLSelectQuery item = rights.get(i);
                  item.accept(visitor);
              }
              return;
          }
  
          if (left != null) {
              left.accept(visitor);
          }
  
          if (right != null) {
              right.accept(visitor);
          }
      }
  }
  