package util.sqlparse.visitor.oracle.visitor;

import bean.Column;
import bean.Synonym;
import com.chenyang.druid.sql.ast.SQLExpr;
import com.chenyang.druid.sql.ast.SQLName;
import com.chenyang.druid.sql.ast.SQLObject;
import com.chenyang.druid.sql.ast.SQLPartition;
import com.chenyang.druid.sql.ast.SQLWindow;
import com.chenyang.druid.sql.ast.expr.SQLAggregateExpr;
import com.chenyang.druid.sql.ast.expr.SQLDbLinkExpr;
import com.chenyang.druid.sql.ast.expr.SQLIdentifierExpr;
import com.chenyang.druid.sql.ast.expr.SQLMethodInvokeExpr;
import com.chenyang.druid.sql.ast.expr.SQLNumericLiteralExpr;
import com.chenyang.druid.sql.ast.expr.SQLPropertyExpr;
import com.chenyang.druid.sql.ast.expr.SQLQueryExpr;
import com.chenyang.druid.sql.ast.expr.SQLSequenceExpr;
import com.chenyang.druid.sql.ast.expr.SQLValuableExpr;
import com.chenyang.druid.sql.ast.statement.SQLDeleteStatement;
import com.chenyang.druid.sql.ast.statement.SQLExprTableSource;
import com.chenyang.druid.sql.ast.statement.SQLInsertStatement;
import com.chenyang.druid.sql.ast.statement.SQLJoinTableSource;
import com.chenyang.druid.sql.ast.statement.SQLLateralViewTableSource;
import com.chenyang.druid.sql.ast.statement.SQLMergeStatement;
import com.chenyang.druid.sql.ast.statement.SQLSelect;
import com.chenyang.druid.sql.ast.statement.SQLSelectItem;
import com.chenyang.druid.sql.ast.statement.SQLSelectOrderByItem;
import com.chenyang.druid.sql.ast.statement.SQLSelectStatement;
import com.chenyang.druid.sql.ast.statement.SQLSubqueryTableSource;
import com.chenyang.druid.sql.ast.statement.SQLTableSource;
import com.chenyang.druid.sql.ast.statement.SQLUnionQuery;
import com.chenyang.druid.sql.ast.statement.SQLUnionQueryTableSource;
import com.chenyang.druid.sql.ast.statement.SQLUpdateSetItem;
import com.chenyang.druid.sql.ast.statement.SQLUpdateStatement;
import com.chenyang.druid.sql.ast.statement.SQLWithSubqueryClause;
import com.chenyang.druid.sql.dialect.oracle.ast.clause.OracleWithSubqueryEntry;
import com.chenyang.druid.sql.dialect.oracle.ast.expr.OracleJsonArrayAggExpr;
import com.chenyang.druid.sql.dialect.oracle.ast.expr.OracleJsonObjectExpr;
import com.chenyang.druid.sql.dialect.oracle.ast.expr.OracleSysdateExpr;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleDeleteStatement;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleInsertStatement;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleSelectJoin;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleSelectPivot;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleSelectPivotBase;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleSelectQueryBlock;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleSelectSubqueryTableSource;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleSelectTableReference;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleSelectUnPivot;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleUpdateStatement;
import com.chenyang.druid.sql.visitor.SQLASTVisitor;
import com.chenyang.druid.stat.TableStat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

import util.sqlparse.visitor.common.Owner;
import util.sqlparse.visitor.common.memo.AliasField;
import util.sqlparse.visitor.common.memo.FieldMemo;
import util.sqlparse.visitor.common.memo.TableMemo;
import util.sqlparse.visitor.common.scope.ClauseScope;
import util.sqlparse.visitor.common.scope.Scope;
import util.sqlparse.visitor.common.scope.TableScope;
import util.sqlparse.visitor.common.utils.Alias;

public class ScopeVisitor extends ParseVisitor {
   private final Alias alias;

   private boolean debug = false;

   public ScopeVisitor(Scope scope) {
      super(scope);
      this.alias = new Alias();
   }

   public boolean visit(SQLSelectStatement x) {
      if (this.scope.context.sqlType == null)
         this.scope.context.sqlType = TableStat.Mode.Select;
      if (this.debug)
         System.out.println("enter SQLSelectStatement " + x.toString());
      enterClauseScope((SQLObject)x);
      return true;
   }

   public boolean visit(SQLInsertStatement x) {
      if (this.scope.context.sqlType == null)
         this.scope.context.sqlType = TableStat.Mode.Insert;
      enterClauseScope((SQLObject)x);
      if (x.getWith() != null)
         x.getWith().accept((SQLASTVisitor)this);
      if (x.getTableSource() != null)
         x.getTableSource().accept((SQLASTVisitor)this);
      if (x.getColumns().size() == 0) {
         String schema = null, table = null;
         OracleInsertStatement stmt = (OracleInsertStatement)x;
         if (stmt.getSelect() != null) {
            stmt.getSelect().accept((SQLASTVisitor)this);
         } else {
            SQLName tableName = x.getTableName();
            if (tableName instanceof SQLIdentifierExpr) {
               SQLIdentifierExpr expr = (SQLIdentifierExpr)tableName;
               table = expr.getName();
               schema = this.scope.context.getSchema();
            } else if (tableName instanceof SQLPropertyExpr) {
               SQLPropertyExpr expr = (SQLPropertyExpr)tableName;
               table = expr.getName();
               schema = expr.getOwnerName();
            }
            if (table != null && schema != null) {
               List<Column> columns = this.scope.context.getColumns(schema, table);
               if (columns != null && columns.size() > 0)
                  for (Column column : columns) {
                     String name = this.scope.dialector.wrap(column.getColumnName());
                     SQLIdentifierExpr sQLIdentifierExpr = new SQLIdentifierExpr(name);
                     x.getColumns().add(sQLIdentifierExpr);
                     sQLIdentifierExpr.setParent((SQLObject)x);
                  }
            }
         }
      }
      for (SQLExpr column : x.getColumns()) {
         column.accept((SQLASTVisitor)this);
         FieldMemo field = (FieldMemo)column.getAttribute("field");
         if (field != null)
            field.isSelectItem = true;
      }
      if (x.getValuesList() != null)
         for (SQLInsertStatement.ValuesClause clause : x.getValuesList())
            clause.accept((SQLASTVisitor)this);
      if (x.getQuery() != null)
         x.getQuery().accept((SQLASTVisitor)this);
      return false;
   }

   public boolean visit(OracleInsertStatement x) {
      return visit((SQLInsertStatement)x);
   }

   public boolean visit(OracleMultiInsertStatement x) {
      if (this.scope.context.sqlType == null)
         this.scope.context.sqlType = TableStat.Mode.Insert;
      enterClauseScope((SQLObject)x);
      if (x.getSubQuery() != null)
         x.getSubQuery().accept((SQLASTVisitor)this);
      for (OracleMultiInsertStatement.Entry entry : x.getEntries())
         entry.accept((SQLASTVisitor)this);
      return false;
   }

   public boolean visit(OracleMultiInsertStatement.InsertIntoClause x) {
      x.getTableSource().accept((SQLASTVisitor)this);
      Scope current = this.scope.getCurrent();
      TableMemo table = current.getTable((SQLObject)x.getTableSource().getExpr());
      List<FieldMemo> fields = current.expandAllColumns(table, false);
      if (x.getColumns() == null || x.getColumns().size() == 0) {
         for (FieldMemo field : fields) {
            SQLIdentifierExpr expr = new SQLIdentifierExpr();
            expr.setName(field.name);
            field.table = table;
            field.isSelectItem = true;
            field.alias = field.name;
            current.addField(field);
            x.addColumn((SQLExpr)expr);
         }
      } else {
         for (SQLExpr column : x.getColumns()) {
            visitChild((SQLObject)column);
            FieldMemo field = (FieldMemo)column.getAttribute("field");
            if (field != null) {
               FieldMemo fieldCopy = field.copy();
               fieldCopy.isSelectItem = true;
               fieldCopy.table = table;
               fieldCopy.alias = field.name;
               current.addField(fieldCopy);
            }
         }
      }
      return false;
   }

   public boolean visit(SQLUpdateStatement x) {
      if (this.scope.context.sqlType == null)
         this.scope.context.sqlType = TableStat.Mode.Update;
      enterClauseScope((SQLObject)x);
      if (x.getTableSource() != null)
         x.getTableSource().accept((SQLASTVisitor)this);
      if (x.getFrom() != null)
         x.getFrom().accept((SQLASTVisitor)this);
      for (int i = 0; i < x.getItems().size(); i++) {
         SQLUpdateSetItem item = x.getItems().get(i);
         if (item != null)
            item.accept((SQLASTVisitor)this);
      }
      if (x.getWhere() != null)
         x.getWhere().accept((SQLASTVisitor)this);
      if (x.getOrderBy() != null)
         x.getOrderBy().accept((SQLASTVisitor)this);
      return false;
   }

   public boolean visit(OracleUpdateStatement x) {
      return visit((SQLUpdateStatement)x);
   }

   public boolean visit(SQLMergeStatement x) {
      if (this.scope.context.sqlType == null)
         this.scope.context.sqlType = TableStat.Mode.Merge;
      enterClauseScope((SQLObject)x);
      if (x.getInto() != null)
         x.getInto().accept((SQLASTVisitor)this);
      TableMemo intoTable = (TableMemo)x.getInto().getAttribute("table");
      if (x.getUsing() != null) {
         x.getUsing().accept((SQLASTVisitor)this);
         if (x.getUsing() instanceof OracleSelectSubqueryTableSource) {
            Scope usingScope = this.scope.getScope((SQLObject)x.getUsing());
            if (usingScope != null)
               usingScope.top = true;
         }
      }
      if (x.getOn() != null)
         x.getOn().accept((SQLASTVisitor)this);
      if (x.getUpdateClause() != null) {
         x.getUpdateClause().putAttribute("intoTable", intoTable);
         x.getUpdateClause().accept((SQLASTVisitor)this);
      }
      if (x.getInsertClause() != null) {
         x.getInsertClause().putAttribute("intoTable", intoTable);
         x.getInsertClause().accept((SQLASTVisitor)this);
      }
      return false;
   }

   public boolean visit(SQLMergeStatement.MergeUpdateClause x) {
      TableMemo table = (TableMemo)x.getAttribute("intoTable");
      Scope current = this.scope.getCurrent();
      if (x.getItems() != null)
         for (SQLUpdateSetItem item : x.getItems()) {
            SQLExpr column = item.getColumn();
            String name = null;
            if (column instanceof SQLName)
               name = ((SQLName)column).getSimpleName();
            if (name != null) {
               FieldMemo field = new FieldMemo();
               field.name = current.dialector.wrap(name.toUpperCase());
               field.alias = field.name;
               field.table = table;
               field.tables.add(table);
               field.exprs.add(column);
               field.isSelectItem = true;
               field.ref = (SQLObject)column;
               field.scope = current;
               current.addField(field);
            }
         }
      return false;
   }

   public boolean visit(SQLMergeStatement.MergeInsertClause x) {
      Scope current = this.scope.getCurrent();
      TableMemo table = (TableMemo)x.getAttribute("intoTable");
      if (x.getColumns().size() == 0) {
         List<FieldMemo> fields = current.expandAllColumns(table, false);
         for (FieldMemo field : fields) {
            SQLIdentifierExpr expr = new SQLIdentifierExpr();
            expr.setName(field.name);
            expr.setParent((SQLObject)x);
            x.getColumns().add(expr);
            field.tables.add(table);
            field.exprs.add(expr);
            field.ref = (SQLObject)expr;
            field.scope = current;
            current.addField(field);
         }
      } else {
         for (SQLExpr column : x.getColumns()) {
            String name = null;
            if (column instanceof SQLName)
               name = ((SQLName)column).getSimpleName();
            if (name != null) {
               name = current.dialector.wrap(name);
               FieldMemo field = new FieldMemo();
               field.name = name;
               field.alias = name;
               field.table = table;
               field.tables.add(table);
               field.exprs.add(column);
               field.isSelectItem = true;
               field.ref = (SQLObject)column;
               field.scope = current;
               current.addField(field);
            }
         }
      }
      current.openFieldState();
      if (x.getValues() != null)
         acceptChild(x.getValues());
      current.closeFieldState();
      return false;
   }

   public boolean visit(SQLDeleteStatement x) {
      if (this.scope.context.sqlType == null)
         this.scope.context.sqlType = TableStat.Mode.Delete;
      enterClauseScope((SQLObject)x);
      return true;
   }

   public boolean visit(OracleDeleteStatement x) {
      return visit((SQLDeleteStatement)x);
   }

   public boolean visit(SQLWithSubqueryClause x) {
      if (this.debug)
         System.out.println("enter SQLWithSubqueryClause  " + x.toString());
      ClauseScope clauseScope = enterClauseScope((SQLObject)x);
      ((Scope)clauseScope).top = true;
      ((Scope)clauseScope).fromField = clauseScope.isFromField((SQLObject)x);
      return true;
   }

   public boolean visit(SQLWithSubqueryClause.Entry x) {
      TableScope scope = enterTableScope((SQLTableSource)x);
      scope.fromField = scope.isFromField((SQLObject)x);
      scope.top = true;
      if (x.getSubQuery() != null)
         x.getSubQuery().accept((SQLASTVisitor)this);
      TableMemo table = registerTable(x);
      table.topTable = true;
      x.putAttribute("table", table);
      x.putAttribute("scope", scope);
      scope.table = table;
      return false;
   }

   public boolean visit(OracleWithSubqueryEntry x) {
      TableScope scope = enterTableScope((SQLTableSource)x);
      scope.fromField = scope.isFromField((SQLObject)x);
      scope.top = true;
      if (x.getSubQuery() != null)
         x.getSubQuery().accept((SQLASTVisitor)this);
      if (x.getSearchClause() != null)
         x.getSearchClause().accept((SQLASTVisitor)this);
      if (x.getCycleClause() != null)
         x.getCycleClause().accept((SQLASTVisitor)this);
      TableMemo table = registerTable((SQLWithSubqueryClause.Entry)x);
      table.topTable = true;
      x.putAttribute("table", table);
      x.putAttribute("scope", scope);
      scope.table = table;
      return false;
   }

   public void endVisit(OracleWithSubqueryEntry x) {
      endVisit((SQLWithSubqueryClause.Entry)x);
   }

   public void endVisit(SQLWithSubqueryClause.Entry x) {
      if (this.debug)
         System.out.println("enter SQLWithSubqueryClause.Entry  " + x.toString());
      Scope current = this.scope.getCurrent();
      TableMemo table = (TableMemo)x.getAttribute("table");
      boolean addColumns = (x.getColumns().size() == 0);
      List<FieldMemo> fields = new ArrayList<FieldMemo>();
      current.getSubFieldsForWith(fields);
      for (Scope child : current.getChildren())
         table.getChildren().addAll(child.getBranchTables());
      int k = 0;
      for (FieldMemo field : fields) {
         FieldMemo newNameField = field.copy();
         newNameField.name = field.alias;
         newNameField.alias = field.alias;
         if (!addColumns) {
            SQLExpr expr = x.getColumns().get(k);
            SQLName exprName = (SQLName)expr;
            String fieldName = exprName.getSimpleName();
            if (isEqual(fieldName, field.alias)) {
               newNameField.name = fieldName;
               newNameField.alias = fieldName;
            }
            newNameField.ref = (SQLObject)expr;
            newNameField.exprs.add(expr);
         } else {
            SQLIdentifierExpr name = new SQLIdentifierExpr();
            String fieldName = newNameField.name;
            for (AliasField aliasField : field.aliasFields) {
               if (aliasField.isOutput) {
                  fieldName = aliasField.alias;
                  break;
               }
            }
            newNameField.name = fieldName;
            newNameField.alias = fieldName;
            name.setName(fieldName);
            x.getColumns().add(name);
            name.setParent((SQLObject)x);
            newNameField.ref = (SQLObject)name;
            newNameField.exprs.add(name);
         }
         newNameField.table = table;
         newNameField.scope = current;
         newNameField.tables.add(table);
         newNameField.children.add(field);
         current.addField(newNameField);
         k++;
      }
      this.scope.exitScope();
   }

   public boolean visit(OracleSelectQueryBlock x) {
      if (this.debug)
         System.out.println("enter OracleSelectQueryBlock  " + x.toString());
      enterClauseScope((SQLObject)x);
      this.scope.fromField = this.scope.isFromField((SQLObject)x);
      if (x.getFrom() != null) {
         parseView(x.getFrom(), (SQLObject)x);
      } else {
         SQLIdentifierExpr tableName = new SQLIdentifierExpr();
         tableName.setName("dual");
         SQLExprTableSource tableSource = new SQLExprTableSource();
         tableSource.setExpr((SQLExpr)tableName);
         tableSource.setParent((SQLObject)x);
         x.setFrom((SQLTableSource)tableSource);
      }
      SQLTableSource from = x.getFrom();
      if (from instanceof SQLExprTableSource) {
         SQLExprTableSource xf = (SQLExprTableSource)from;
         SQLExpr expr = xf.getExpr();
         if (expr instanceof SQLIdentifierExpr || expr instanceof SQLPropertyExpr) {
            Scope current = this.scope.getCurrent();
            SQLExpr ownerTable = expr;
            if (ownerTable instanceof SQLPropertyExpr) {
               SQLPropertyExpr propOwner = (SQLPropertyExpr)ownerTable;
               String name = propOwner.getName();
               String schema = propOwner.getOwnerName();
               Synonym syn = current.dialector.getSynonym(schema, name);
               if (syn != null && syn.getTableName() != null) {
                  propOwner.setOwner(syn.getTableOwner());
                  propOwner.setName(syn.getTableName());
               }
            } else if (ownerTable instanceof SQLIdentifierExpr) {
               SQLIdentifierExpr idOnwer = (SQLIdentifierExpr)ownerTable;
               String schema = current.getDefaultSchema();
               String name = idOnwer.getName();
               Synonym syn = current.dialector.getSynonym(schema, name);
               if (syn != null && syn.getTableName() != null) {
                  SQLPropertyExpr propertyExpr = new SQLPropertyExpr();
                  propertyExpr.setOwner(syn.getTableOwner());
                  propertyExpr.setName(syn.getTableName());
                  xf.setExpr((SQLExpr)propertyExpr);
               }
            }
         }
      }
      x.getFrom().accept((SQLASTVisitor)this);
      parsePivotTableSource(x.getFrom());
      parseSelectList(x);
      if (x.getWindows() != null)
         for (int i = 0; i < x.getWindows().size(); i++) {
            SQLWindow item = x.getWindows().get(i);
            item.accept((SQLASTVisitor)this);
         }
      if (x.getInto() != null)
         x.getInto().accept((SQLASTVisitor)this);
      if (x.getWhere() != null)
         x.getWhere().accept((SQLASTVisitor)this);
      if (x.getStartWith() != null)
         x.getStartWith().accept((SQLASTVisitor)this);
      if (x.getConnectBy() != null)
         x.getConnectBy().accept((SQLASTVisitor)this);
      if (x.getGroupBy() != null)
         x.getGroupBy().accept((SQLASTVisitor)this);
      if (x.getOrderBy() != null)
         x.getOrderBy().accept((SQLASTVisitor)this);
      if (x.getWaitTime() != null)
         x.getWaitTime().accept((SQLASTVisitor)this);
      if (x.getLimit() != null)
         x.getLimit().accept((SQLASTVisitor)this);
      if (x.getForUpdateOf() != null)
         acceptChild(x.getForUpdateOf());
      return false;
   }

   private void parsePivotTableSource(SQLTableSource tableSource) {
      Object pivotOptions = tableSource.getAttribute("pivot");
      boolean isPivot = (pivotOptions == null) ? false : ((Boolean)pivotOptions).booleanValue();
      if (!isPivot)
         return;
      OracleSelectPivotBase pivot = null;
      if (tableSource instanceof OracleSelectTableReference) {
         pivot = ((OracleSelectTableReference)tableSource).getPivot();
      } else if (tableSource instanceof OracleSelectJoin) {
         pivot = ((OracleSelectJoin)tableSource).getPivot();
      } else if (tableSource instanceof OracleSelectSubqueryTableSource) {
         pivot = ((OracleSelectSubqueryTableSource)tableSource).getPivot();
      }
      if (pivot == null)
         return;
      if (pivot instanceof OracleSelectPivot) {
         tableSource.putAttribute("pivot_reverse", Boolean.valueOf(false));
         parsePivot((OracleSelectPivot)pivot);
      } else if (pivot instanceof OracleSelectUnPivot) {
         tableSource.putAttribute("pivot_reverse", Boolean.valueOf(true));
         parseUnPivot((OracleSelectUnPivot)pivot);
      }
   }

   private void parsePivot(OracleSelectPivot pivot) {
      Scope current = this.scope.getCurrent();
      List<OracleSelectPivot.Item> items = pivot.getItems();
      List<FieldMemo> aggColumns = new ArrayList<FieldMemo>();
      for (OracleSelectPivot.Item column : items) {
         String name = column.getAlias();
         if (name == null)
            name = column.getExpr().toString();
         List<SQLExpr> exprs = this.scope.getSubFieldExprs(column.getExpr());
         for (SQLExpr expr : exprs) {
            FieldMemo memo = getOrCreateFieldByName(expr.toString(), true);
            memo.ref = (SQLObject)expr;
            memo.exprs.add(column);
            aggColumns.add(memo);
         }
      }
      current.putAttribute("pivot-agg-fields", aggColumns);
      List<SQLExpr> forColumns = pivot.getPivotFor();
      List<FieldMemo> forFields = new ArrayList<FieldMemo>();
      for (SQLExpr forColumn : forColumns) {
         SQLName sqlName = (SQLName)forColumn;
         String name = sqlName.getSimpleName();
         FieldMemo field = getOrCreateFieldByName(name, true);
         field.ref = (SQLObject)forColumn;
         field.exprs.add(forColumn);
         forFields.add(field);
      }
      current.putAttribute("pivot-for-fields", forFields);
      List<OracleSelectPivot.Item> columns = pivot.getPivotIn();
      List<FieldMemo> newFields = new ArrayList<FieldMemo>();
      for (OracleSelectPivot.Item column : columns) {
         String name = column.getAlias();
         if (name == null)
            name = column.getExpr().toString();
         FieldMemo memo = getOrCreateFieldByName(name, true);
         if (memo.name == null) {
            memo.name = name;
            memo.alias = memo.name;
         }
         memo.ref = (SQLObject)column;
         memo.exprs.add(column);
         newFields.add(memo);
      }
      for (FieldMemo newField : newFields)
         newField.children.addAll(aggColumns);
      current.putAttribute("pivot-in-fields", newFields);
   }

   private void parseUnPivot(OracleSelectUnPivot pivot) {
      Scope current = this.scope.getCurrent();
      List<SQLExpr> items = pivot.getItems();
      List<FieldMemo> aggColumns = new ArrayList<FieldMemo>();
      for (SQLExpr column : items) {
         boolean isPrimitive = this.scope.isPrimitive(column);
         if (isPrimitive) {
            FieldMemo memo = getOrCreateFieldByName(column.toString());
            memo.ref = (SQLObject)column;
            memo.exprs.add(column);
            aggColumns.add(memo);
            continue;
         }
         List<SQLExpr> exprs = this.scope.getSubFieldExprs(column);
         for (SQLExpr expr : exprs) {
            FieldMemo memo = getOrCreateFieldByName(expr.toString());
            memo.ref = (SQLObject)expr;
            memo.exprs.add(column);
            aggColumns.add(memo);
         }
      }
      current.putAttribute("pivot-agg-fields", aggColumns);
      List<OracleSelectPivot.Item> columns = pivot.getPivotIn();
      List<FieldMemo> newFields = new ArrayList<FieldMemo>();
      for (OracleSelectPivot.Item column : columns) {
         String name = column.getAlias();
         if (name == null)
            name = column.getExpr().toString();
         FieldMemo memo = getOrCreateFieldByName(name);
         memo.ref = (SQLObject)column;
         memo.exprs.add(column);
         newFields.add(memo);
      }
      current.putAttribute("pivot-in-fields", newFields);
   }

   private List<FieldMemo> parsePivotColumns(SQLObject x, List<FieldMemo> source) {
      Scope current = this.scope.getCurrent();
      Object pivotOptions = x.getAttribute("pivot");
      if (pivotOptions == null)
         return source;
      if (!((Boolean)pivotOptions).booleanValue())
         return source;
      List<FieldMemo> target = new ArrayList<FieldMemo>();
      target.addAll(source);
      boolean reverse = ((Boolean)x.getAttribute("pivot_reverse")).booleanValue();
      List<FieldMemo> aggColumns = (List<FieldMemo>)current.getAttribute("pivot-agg-fields");
      List<FieldMemo> forColumns = (List<FieldMemo>)current.getAttribute("pivot-for-fields");
      List<FieldMemo> inColumns = (List<FieldMemo>)current.getAttribute("pivot-in-fields");
      if (!reverse) {
         for (FieldMemo column : aggColumns) {
            Iterator<FieldMemo> iterator = target.iterator();
            while (iterator.hasNext()) {
               FieldMemo z = iterator.next();
               if (isEqual(z.name, column.name))
                  iterator.remove();
            }
         }
         for (FieldMemo column : forColumns) {
            Iterator<FieldMemo> iterator = target.iterator();
            while (iterator.hasNext()) {
               FieldMemo z = iterator.next();
               if (isEqual(z.name, column.name))
                  iterator.remove();
            }
         }
         target.addAll(inColumns);
      } else {
         for (FieldMemo column : inColumns) {
            Iterator<FieldMemo> iterator = target.iterator();
            while (iterator.hasNext()) {
               FieldMemo z = iterator.next();
               if (isEqual(z.name, column.name))
                  iterator.remove();
            }
         }
         target.addAll(aggColumns);
      }
      return target;
   }

   private FieldMemo getOrCreateFieldByName(String name) {
      return getOrCreateFieldByName(name, false);
   }

   private FieldMemo getOrCreateFieldByName(String name, boolean createField) {
      FieldMemo memo;
      Scope current = this.scope.getCurrent();
      FieldMemo exists = current.getFieldByName(name);
      if (exists == null) {
         memo = new FieldMemo();
         memo.name = current.dialector.wrap(name);
         memo.tables.addAll(current.getTableMap().values());
      } else {
         memo = exists.copy();
         memo.tables.addAll(exists.tables);
         memo.children.add(exists);
      }
      memo.alias = memo.name;
      if (memo.tables.size() == 1)
         memo.table = memo.tables.get(0);
      memo.scope = current;
      if (createField)
         current.addField(memo);
      memo.isSelectItem = false;
      return memo;
   }

   private void parseSelectList(OracleSelectQueryBlock x) {
      Scope current = this.scope.getCurrent();
      List<SQLSelectItem> selectList = x.getSelectList();
      for (int i = 0; i < selectList.size(); i++) {
         SQLSelectItem item = selectList.get(i);
         boolean isAll = this.scope.isAllColumn(item.getExpr());
         if (item.getAlias() == null && !isAll)
            setAlias(item);
         if (isAll && item.getAttribute("expanded") == null) {
            item.putAttribute("expanded", Boolean.valueOf(true));
            SQLObject parent = item.getParent();
            Owner owner = this.scope.getAllOwner(item.getExpr());
            TableMemo table = (owner.alias == null) ? null : current.getTable((SQLObject)item.getExpr());
            SQLTableSource tableSource = x.getFrom();
            boolean isSub = ((table != null && table.isSub) || tableSource instanceof SQLSubqueryTableSource || tableSource instanceof SQLUnionQueryTableSource);
            List<FieldMemo> fields = current.expandAllColumns(table, isSub);
            fields = parsePivotColumns((SQLObject)x.getFrom(), fields);
            if (fields.size() != 0) {
               int j = 0;
               selectList.remove(i);
               String alias = x.getFrom().getAlias();
               if (alias == null)
                  alias = owner.alias;
               for (FieldMemo field : fields) {
                  if (field.name.equalsIgnoreCase("*"))
                     continue;
                  TableMemo fieldTable = field.table;
                  SQLPropertyExpr propExpr = new SQLPropertyExpr();
                  propExpr.setOwner((alias == null) ? fieldTable.alias : alias);
                  if (Pattern.compile("\\s|\\(").matcher(field.alias).find()) {
                     propExpr.setName("\"" + field.alias + "\"");
                  } else {
                     propExpr.setName(field.alias);
                  }
                  SQLSelectItem newItem = new SQLSelectItem();
                  newItem.setParent(parent);
                  newItem.setExpr((SQLExpr)propExpr);
                  newItem.setAlias(field.alias);
                  field.scope = current;
                  field.isSelectItem = true;
                  field.ref = (SQLObject)newItem;
                  field.exprs.add(propExpr);
                  selectList.add(i + j++, newItem);
               }
            }
         }
      }
      int k = Integer.MIN_VALUE;
      for (SQLSelectItem item : selectList) {
         SQLExpr sqlExpr = item.getExpr();
         boolean isPrimitive = this.scope.isPrimitive(sqlExpr);
         item.putAttribute("isPrimitive", Boolean.valueOf(isPrimitive));
         item.accept((SQLASTVisitor)this);
         FieldMemo field = (FieldMemo)sqlExpr.getAttribute("field");
         if (sqlExpr instanceof com.chenyang.druid.sql.ast.expr.SQLValuableExpr)
            field.isConstant = true;
         field.id = k++;
      }
   }

   private SQLTableSource parseView(SQLTableSource ts, SQLObject parent) {
      if (ts instanceof SQLExprTableSource || ts instanceof OracleSelectTableReference) {
         getViewTableSource(ts);
      } else if (ts instanceof SQLJoinTableSource) {
         SQLJoinTableSource jts = (SQLJoinTableSource)ts;
         SQLTableSource left = parseView(jts.getLeft(), (SQLObject)jts);
         if (left != null) {
            jts.setLeft(left);
            left.setParent((SQLObject)jts);
         }
         SQLTableSource right = parseView(jts.getRight(), (SQLObject)jts);
         if (right != null) {
            jts.setRight(right);
            right.setParent((SQLObject)jts);
         }
      }
      return null;
   }

   private void getViewTableSource(SQLTableSource ts) {
      SQLExprTableSource e = (SQLExprTableSource)ts;
      String name = e.getTableName();
      if (name == null)
         return;
      String schema = e.getSchema();
      PassViewVisitor passViewVisitor = new PassViewVisitor(this.scope, schema);
      if (null != ts.getParent() && ts.getParent() instanceof OracleSelectQueryBlock) {
         ts.getParent().accept((SQLASTVisitor)passViewVisitor);
         return;
      }
      if (ts instanceof OracleSelectTableReference)
         ts.accept((SQLASTVisitor)passViewVisitor);
   }

   public boolean visit(SQLUnionQuery x) {
      ClauseScope clauseScope = enterClauseScope((SQLObject)x);
      ((Scope)clauseScope).union = true;
      ((Scope)clauseScope).isolated = false;
      visitChild(x.getRelations());
      for (Scope s1 : clauseScope.getChildren())
         s1.isolated = false;
      visitChild((SQLObject)x.getOrderBy());
      visitChild((SQLObject)x.getLimit());
      return false;
   }

   public boolean visit(SQLLateralViewTableSource x) {
      TableMemo table = registerTable((SQLTableSource)x);
      TableScope scope = enterTableScope((SQLTableSource)x);
      scope.table = table;
      scope.isolated = false;
      return true;
   }

   public boolean visit(SQLSubqueryTableSource x) {
      if (this.debug)
         System.out.println("enter SQLSubqueryTableSource  " + x.toString());
      if (x.getAlias() == null)
         x.setAlias(this.alias.aliasTable());
      TableMemo table = registerTable((SQLTableSource)x);
      TableScope scope = enterTableScope((SQLTableSource)x);
      scope.isolated = false;
      scope.table = table;
      if (x.getSelect() != null)
         x.getSelect().accept((SQLASTVisitor)this);
      for (Scope s : scope.getChildren())
         s.isolated = false;
      return false;
   }

   public boolean visit(OracleSelectSubqueryTableSource x) {
      if (this.debug)
         System.out.println("enter SQLSubqueryTableSource  " + x.toString());
      if (x.getAlias() == null)
         x.setAlias(this.alias.aliasTable());
      x.putAttribute("pivot", Boolean.valueOf((x.getPivot() != null)));
      TableMemo table = registerTable((SQLTableSource)x);
      TableScope scope = enterTableScope((SQLTableSource)x);
      scope.table = table;
      scope.isolated = false;
      if (x.getSelect() != null)
         x.getSelect().accept((SQLASTVisitor)this);
      for (Scope s : scope.getChildren())
         s.isolated = false;
      return false;
   }

   public boolean visit(OracleSelectTableReference x) {
      if (!(x.getExpr() instanceof SQLIdentifierExpr) && !(x.getExpr() instanceof SQLPropertyExpr) && !(x.getExpr() instanceof SQLDbLinkExpr)) {
         x.putAttribute("pivot", Boolean.valueOf((x.getPivot() != null)));
         if (this.debug)
            System.out.println("enter SQLSubqueryTableSource  " + x.toString());
         if (x.getAlias() == null)
            x.setAlias(this.alias.aliasTable());
         x.putAttribute("pivot", Boolean.valueOf((x.getPivot() != null)));
         TableMemo table = registerTable((SQLTableSource)x);
         TableScope scope = enterTableScope((SQLTableSource)x);
         scope.table = table;
         scope.isolated = false;
         return true;
      }
      registerTable((SQLTableSource)x);
      return true;
   }

   public void endVisit(OracleSelectTableReference x) {
      if (!(x.getExpr() instanceof SQLIdentifierExpr) && !(x.getExpr() instanceof SQLPropertyExpr) && !(x.getExpr() instanceof SQLDbLinkExpr)) {
         if (this.debug)
            System.out.println("exit OracleSelectTableReference  " + x);
         this.scope.exitScope();
      } else if (this.debug) {
         System.out.println("end OracleSelectTableReference" + x.toString());
      }
   }

   public boolean visit(SQLJoinTableSource x) {
      (this.scope.getCurrent()).join = true;
      return true;
   }

   public boolean visit(OracleSelectJoin x) {
      (this.scope.getCurrent()).join = true;
      return true;
   }

   public boolean visit(SQLUnionQueryTableSource x) {
      TableMemo table = registerTable((SQLTableSource)x);
      TableScope scope = enterTableScope((SQLTableSource)x);
      scope.union = true;
      scope.fromField = scope.isFromField((SQLObject)x);
      x.putAttribute("table", table);
      x.putAttribute("scope", scope);
      scope.table = table;
      scope.isolated = false;
      visitChild((SQLObject)x.getUnion());
      for (Scope s1 : scope.getChildren())
         s1.isolated = false;
      return false;
   }

   public boolean visit(SQLExprTableSource x) {
      registerTable((SQLTableSource)x);
      return true;
   }

   public boolean visit(SQLSelectItem x) {
      FieldMemo field = createField(x.getExpr(), x.getAlias(), true);
      field.ref = (SQLObject)x;
      return false;
   }

   public boolean visit(SQLAggregateExpr x) {
      if (x.getMethodName().equalsIgnoreCase("LISTAGG"))
         setAttr((SQLExpr)x, "AGG-PASS", Boolean.valueOf(true));
      return true;
   }

   public boolean visit(SQLIdentifierExpr x) {
      if (this.scope.getCurrent().isFieldState()) {
         boolean needCreate = true;
         if (x.getParent() instanceof SQLAggregateExpr) {
            needCreate = false;
            SQLAggregateExpr agg = (SQLAggregateExpr)x.getParent();
            if (agg.getMethodName().equalsIgnoreCase("LISTAGG")) {
               List<SQLExpr> arguments = agg.getArguments();
               if (arguments.size() > 0 &&
                       arguments.get(0) == x) {
                  needCreate = true;
                  x.removeAttribute("AGG-PASS");
               }
            } else {
               needCreate = true;
            }
         }
         if (needCreate && !x.containsAttribute("AGG-PASS")) {
            FieldMemo field = createField((SQLExpr)x, x.getName(), false);
            x.putAttribute("field", field);
         }
      } else if (!(x.getParent() instanceof SQLTableSource) && !(x.getParent() instanceof com.chenyang.druid.sql.ast.SQLPartition) && !(x.getParent() instanceof SQLDbLinkExpr)) {
         x.putAttribute("orderBy", Boolean.valueOf(x.getParent() instanceof com.chenyang.druid.sql.ast.statement.SQLSelectOrderByItem));
         FieldMemo field = createField((SQLExpr)x, x.getName(), false);
         x.putAttribute("field", field);
         if (x.getParent() instanceof SQLUpdateSetItem)
            field.isSelectItem = true;
      }
      return false;
   }

   private void setAttr(SQLExpr e, String name, Object value) {
      if (e == null)
         return;
      List<SQLObject> children = e.getChildren();
      if (children != null && children.size() > 0)
         for (SQLObject child : children) {
            child.putAttribute(name, value);
            if (child instanceof SQLExpr)
               setAttr((SQLExpr)child, name, value);
         }
   }

   public boolean visit(SQLPropertyExpr x) {
      if (this.scope.getCurrent().isFieldState()) {
         createField((SQLExpr)x, x.getName(), false);
      } else if (!(x.getParent() instanceof SQLTableSource) && !(x.getParent() instanceof com.chenyang.druid.sql.ast.SQLPartition)) {
         FieldMemo field = createField((SQLExpr)x, x.getName(), false);
         if (x.getParent() instanceof SQLUpdateSetItem)
            field.isSelectItem = true;
      }
      return false;
   }

   public boolean visit(OracleJsonArrayAggExpr x) {
      visitChild((SQLObject)x.getExpr());
      visitChild((SQLObject)x.getOrderBy());
      return false;
   }

   public boolean visit(OracleJsonObjectExpr x) {
      for (OracleJsonObjectExpr.OracleJsonObjectExprItem item : x.getItems()) {
         SQLExpr value = item.getValue();
         value.accept((SQLASTVisitor)this);
      }
      return false;
   }

   private void createSequenceTable(String name) {
      Scope current = this.scope.getCurrent();
      TableMemo table = new TableMemo();
      table.name = name;
      table.alias = name;
      table.nowName = table.alias;
      table.schema = this.scope.getDefaultSchema();
      table.sequence = true;
      table.scope = current;
      current.addTable(table);
      this.scope.context.append(table);
   }

   private FieldMemo createField(SQLExpr expr, String alias, boolean isSelectItem) {
      Scope current = this.scope.getCurrent();
      FieldMemo field = new FieldMemo();
      if (expr instanceof SQLIdentifierExpr) {
         SQLIdentifierExpr idExpr = (SQLIdentifierExpr)expr;
         boolean isOrderBy = (expr.getAttribute("orderBy") != null && ((Boolean)expr.getAttribute("orderBy")).booleanValue());
         if (isOrderBy) {
            String name = current.dialector.wrap(idExpr.getSimpleName());
            FieldMemo exist = current.getFieldByAlias(name);
            if (exist != null)
               return exist;
         }
         field.name = idExpr.getName();
         field.atom = true;
         setField(field, alias, isSelectItem, expr);
         field.children = this.scope.getCurrent().getSubRelationFields(field);
         setAtom(field);
         field = current.addField(field);
      } else if (expr instanceof OracleSysdateExpr) {
         OracleSysdateExpr prop = (OracleSysdateExpr)expr;
         field.name = current.dialector.wrap(prop.toString());
         field.isConstant = true;
         setField(field, alias, isSelectItem, expr);
         field = current.addField(field);
      } else if (expr instanceof SQLPropertyExpr) {
         SQLPropertyExpr prop = (SQLPropertyExpr)expr;
         field.name = prop.getName();
         field.atom = true;
         setField(field, alias, isSelectItem, expr);
         field.children = this.scope.getCurrent().getSubRelationFields(field);
         setAtom(field);
         field = current.addField(field);
      } else if (expr instanceof SQLSequenceExpr) {
         SQLSequenceExpr prop = (SQLSequenceExpr)expr;
         field.name = prop.getFunction().toString();
         createSequenceTable(prop.getSequence().toString());
         setField(field, alias, isSelectItem, expr);
         field.children = this.scope.getCurrent().getSubRelationFields(field);
         field = current.addField(field);
      } else if (expr instanceof com.chenyang.druid.sql.ast.expr.SQLValuableExpr) {
         field.name = expr.toString();
         field.alias = alias;
         field.isConstant = true;
         setField(field, alias, isSelectItem, expr);
         field = current.addField(field);
      } else {
         alias = current.dialector.wrap(alias);
         field.name = alias;
         field.alias = alias;
         field.complex = true;
         field.isSelectItem = isSelectItem;
         field.ref = (SQLObject)expr;
         if (expr instanceof SQLSelectItem) {
            field.exprs.add(((SQLSelectItem)expr).getExpr());
            AliasField aliasField = new AliasField();
            aliasField.alias = alias;
            aliasField.field = field;
            aliasField.expr = expr;
            aliasField.isOutput = true;
            aliasField.scope = this.scope.getCurrent();
         } else {
            field.exprs.add(expr);
            AliasField aliasField = new AliasField();
            aliasField.alias = alias;
            aliasField.field = field;
            aliasField.expr = expr;
            aliasField.isOutput = true;
            aliasField.scope = this.scope.getCurrent();
         }
         current.openFieldState();
         expr.accept((SQLASTVisitor)this);
         current.closeFieldState();
         List<SQLExpr> exprs = current.getSubFieldExprs(expr);
         List<FieldMemo> subFields = new ArrayList<FieldMemo>();
         current.getFieldsByExprs(exprs, subFields);
         List<TableMemo> tables = new ArrayList<TableMemo>();
         List<FieldMemo> children = new ArrayList<FieldMemo>();
         Set<String> names = new HashSet<String>();
         boolean isPrimitive = this.scope.isPrimitive(expr);
         if (!isPrimitive)
            current.getSubBranchFields(subFields);
         if (subFields.size() > 0) {
            for (FieldMemo subField : subFields) {
               if (!names.contains(subField.getUniqueName())) {
                  children.add(subField);
                  names.add(subField.getUniqueName());
               }
               if (subField.table != null && !names.contains(subField.table.getQualifiedName())) {
                  tables.add(subField.table);
                  names.add(subField.table.getQualifiedName());
                  continue;
               }
               if (subField.tables.size() > 0)
                  for (TableMemo table : subField.tables) {
                     if (!names.contains(table.getQualifiedName())) {
                        tables.add(table);
                        names.add(table.getQualifiedName());
                     }
                  }
            }
         } else {
            tables.addAll(current.getBranchTables());
         }
         field.tables = tables;
         if (tables.size() == 1)
            field.table = tables.get(0);
         field.children = children;
         setAtom(field);
         current.addField(field);
      }
      expr.putAttribute("field", field);
      return field;
   }

   private void setAtom(FieldMemo field) {
      if (field.children.size() == 0)
         return;
      Set<String> fdNames = field.tableNames();
      boolean found = false;
      for (FieldMemo child : field.children) {
         if (isEqual(field.name, child.alias)) {
            Set<String> cdNames = child.tableNames();
            cdNames.retainAll(fdNames);
            if (cdNames.size() > 0)
               found = true;
         }
      }
      if (!found)
         field.atom = false;
   }

   private void setField(FieldMemo field, String alias, boolean isSelectItem, SQLExpr expr) {
      Scope current = this.scope.getCurrent();
      field.alias = (alias == null) ? field.name : alias;
      field.alias = current.dialector.wrap(field.alias);
      field.name = current.dialector.wrap(field.name);
      field.isSelectItem = isSelectItem;
      field.ref = (SQLObject)expr;
      if (expr instanceof SQLSequenceExpr) {
         field.table = this.scope.getCurrent().getTable((SQLObject)((SQLSequenceExpr)expr).getSequence());
      } else {
         field.table = this.scope.getCurrent().getTable((SQLObject)expr);
      }
      field.tables.add(field.table);
      field.exprs.add(expr);
      AliasField aliasField = new AliasField();
      aliasField.alias = alias;
      aliasField.field = field;
      aliasField.expr = expr;
      aliasField.isOutput = isSelectItem;
      aliasField.scope = this.scope.getCurrent();
   }

   private TableMemo registerTable(SQLTableSource x) {
      Scope current = this.scope.getCurrent();
      if (x instanceof SQLExprTableSource) {
         SQLExprTableSource exprTableSource = (SQLExprTableSource)x;
         TableMemo table = new TableMemo((SQLObject)x);
         SQLExprTableSource tableSource = (SQLExprTableSource)x;
         String tableName = current.dialector.wrap(tableSource.getTableName());
         String schemaName = current.dialector.wrap((tableSource.getSchema() == null) ? this.scope.getDefaultSchema() : tableSource.getSchema());
         table.name = tableName;
         table.alias = current.dialector.wrap((tableSource.getAlias() == null) ? table.name : tableSource.getAlias());
         table.nowName = table.alias;
         table.schema = schemaName;
         if (table.name == null)
            table.name = table.alias;
         if (table.name.equals("*") && exprTableSource.getExpr() != null && exprTableSource.getExpr() instanceof SQLPropertyExpr)
            return null;
         if (table.name == null && table.alias != null)
            table.name = table.alias;
         if (exprTableSource.getExpr() != null && exprTableSource.getExpr() instanceof SQLMethodInvokeExpr)
            table.isSub = true;
         if (exprTableSource.getExpr() != null && exprTableSource.getExpr() instanceof SQLDbLinkExpr) {
            SQLDbLinkExpr link = (SQLDbLinkExpr)exprTableSource.getExpr();
            Owner owner = this.scope.getOwner((SQLObject)link.getExpr());
            table.name = current.dialector.wrap(owner.table);
            if (owner.schema != null)
               table.schema = current.dialector.wrap(owner.schema);
            if (link.getExpr().getParent() == null)
               link.getExpr().setParent((SQLObject)link);
            table.dbLink = exprTableSource.getExpr().toString();
         }
         if (x instanceof OracleSelectTableReference) {
            OracleSelectTableReference ref = (OracleSelectTableReference)x;
            if (ref.getExpr() != null && ref.getExpr() instanceof SQLMethodInvokeExpr) {
               SQLMethodInvokeExpr method = (SQLMethodInvokeExpr)ref.getExpr();
               if (method.getArguments().size() == 1 && method.getArguments().get(0) instanceof com.chenyang.druid.sql.ast.expr.SQLQueryExpr) {
                  if (table.name == null) {
                     String aliasTableName = current.dialector.wrap(this.alias.aliasTable());
                     ref.setAlias(aliasTableName);
                     table.name = aliasTableName;
                     if (table.alias == null)
                        table.alias = table.name;
                     table.isSub = true;
                  }
                  setTableRelation(table);
                  x.putAttribute("table", table);
                  return table;
               }
            }
         }
         TableMemo refTable = current.getParentTable(schemaName, tableName);
         if (refTable != null && refTable.topTable) {
            table.schema = refTable.schema;
            table.topTable = true;
            table.scope = current;
            table.refTable = refTable;
            table.getChildren().add(refTable);
            current.addTable(table);
            table.topTable = refTable.topTable;
            return refTable;
         }
         boolean isFunction = false;
         SQLExpr expr = ((SQLExprTableSource)x).getExpr();
         isFunction = expr instanceof SQLMethodInvokeExpr;
         setTableRelation(table);
         table.system = this.scope.dialector.isSystemTable(table.name);
         if (!table.isSub && !isFunction && !table.system)
            this.scope.context.append(table);
         x.putAttribute("table", table);
         return table;
      }
      if (x instanceof SQLSubqueryTableSource) {
         SQLSubqueryTableSource tableSource = (SQLSubqueryTableSource)x;
         TableMemo table = new TableMemo((SQLObject)x);
         table.name = current.dialector.wrap(tableSource.getAlias());
         table.alias = current.dialector.wrap(tableSource.getAlias());
         table.nowName = table.name;
         table.schema = current.dialector.wrap(this.scope.getDefaultSchema());
         table.isSub = true;
         setTableRelation(table);
         x.putAttribute("table", table);
         return table;
      }
      if (x instanceof SQLLateralViewTableSource) {
         SQLLateralViewTableSource tableSource = (SQLLateralViewTableSource)x;
         TableMemo table = new TableMemo((SQLObject)x);
         table.name = current.dialector.wrap(tableSource.getAlias());
         table.alias = current.dialector.wrap(tableSource.getAlias());
         table.schema = current.dialector.wrap(this.scope.getDefaultSchema());
         table.isSub = true;
         setTableRelation(table);
         x.putAttribute("table", table);
         return table;
      }
      if (x instanceof SQLUnionQueryTableSource) {
         SQLUnionQueryTableSource tableSource = (SQLUnionQueryTableSource)x;
         TableMemo table = new TableMemo((SQLObject)x);
         table.name = current.dialector.wrap(tableSource.getAlias());
         table.alias = table.name;
         table.schema = current.dialector.wrap(this.scope.getDefaultSchema());
         table.nowName = table.name;
         table.isSub = true;
         setTableRelation(table);
         x.putAttribute("table", table);
         return table;
      }
      return null;
   }

   private TableMemo registerTable(SQLWithSubqueryClause.Entry x) {
      Scope current = this.scope.getCurrent();
      TableMemo table = new TableMemo((SQLObject)x);
      table.name = current.dialector.wrap(x.getAlias());
      table.nowName = current.dialector.wrap(table.name);
      table.alias = table.name;
      table.schema = current.dialector.wrap(this.scope.getDefaultSchema());
      table.isSub = true;
      table.topTable = true;
      setTableRelation(table);
      return table;
   }

   private void setTableRelation(TableMemo table) {
      Scope current = this.scope.getCurrent();
      table.name = current.dialector.wrap(table.name);
      table.alias = current.dialector.wrap(table.alias);
      table.schema = current.dialector.wrap(table.schema);
      table.scope = current;
      current.addTable(table);
      TableScope tableScope = current.getNearestParentTableScope();
      if (tableScope != null) {
         TableMemo parentTable = tableScope.table;
         if (parentTable != null) {
            parentTable.getChildren().add(table);
            if (table.isSub)
               parentTable.isSub = true;
         }
      }
   }

   private TableScope enterTableScope(SQLTableSource x) {
      TableScope ts = new TableScope();
      ts.ref = (SQLObject)x;
      ts.alias = ts.name = x.getAlias();
      this.scope.enterScope((Scope)ts);
      return ts;
   }

   private ClauseScope enterClauseScope(SQLObject x) {
      ClauseScope clauseScope = new ClauseScope();
      clauseScope.ref = x;
      this.scope.enterScope((Scope)clauseScope);
      return clauseScope;
   }

   private void setAlias(SQLSelectItem x) {
      if (x.getAlias() != null)
         return;
      SQLExpr e = x.getExpr();
      if (e instanceof SQLIdentifierExpr) {
         String name = ((SQLIdentifierExpr)e).getName();
         if (!name.toUpperCase().equals("ROWID"))
            x.setAlias(name);
      } else if (e instanceof SQLPropertyExpr) {
         String name = ((SQLPropertyExpr)e).getName();
         if (!name.toUpperCase().equals("ROWID"))
            x.setAlias(name);
      } else if (e instanceof com.chenyang.druid.sql.ast.expr.SQLNumericLiteralExpr) {
         x.setAlias("\"" + e + "\"");
      } else if (e instanceof com.chenyang.druid.sql.ast.expr.SQLNullExpr) {
         x.setAlias("\"" + e + "\"");
      } else {
         x.setAlias(this.scope.dialector.wrap(e.toString()));
      }
   }

   public void endVisit(SQLMethodInvokeExpr x) {
      if ("LATERAL".equalsIgnoreCase(x.getMethodName())) {
         (this.scope.getCurrent()).isolated = false;
         for (Scope s : this.scope.getCurrent().getChildren())
            s.isolated = false;
      }
   }
}