package util.sqlparse.visitor.sqlserver.visitor;

import bean.Column;
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.SQLObjectImpl;
import com.chenyang.druid.sql.ast.SQLPartition;
import com.chenyang.druid.sql.ast.expr.SQLBinaryOpExpr;
import com.chenyang.druid.sql.ast.expr.SQLBinaryOperator;
import com.chenyang.druid.sql.ast.expr.SQLIdentifierExpr;
import com.chenyang.druid.sql.ast.expr.SQLMethodInvokeExpr;
import com.chenyang.druid.sql.ast.expr.SQLPropertyExpr;
import com.chenyang.druid.sql.ast.expr.SQLQueryExpr;
import com.chenyang.druid.sql.ast.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.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.SQLValuesTableSource;
import com.chenyang.druid.sql.ast.statement.SQLWithSubqueryClause;
import com.chenyang.druid.sql.dialect.sqlserver.ast.SQLServerSelectQueryBlock;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerDeleteStatement;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerInsertStatement;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerMergeStatement;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerPivotBase;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerSelectPivot;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerSelectSubqueryTableSource;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerSelectTableReference;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerSelectTableSource;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerSelectUnPivot;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerUnionQueryTableSource;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerUpdateStatement;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerValuesTableSource;
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 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;

public class ScopeVisitor extends ParseVisitor {
   public static final String $PARTITION = "$partition";
   public static final String[] functionNames = new String[]{"CURRENT_TIMESTAMP", "year", "quater", "dayofyear", "month", "day", "week", "hour", "minute", "second", "millisecond"};
   private final util.sqlparse.visitor.common.utils.Alias alias = new util.sqlparse.visitor.common.utils.Alias();
   private boolean debug = false;

   public ScopeVisitor(Scope scope) {
      super(scope);
   }

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

      this.enterClauseScope(x);
      return true;
   }

   public boolean visit(SQLServerInsertStatement x) {
      if (this.scope.context.sqlType == null) {
         this.scope.context.sqlType = TableStat.Mode.Insert;
      }

      this.enterClauseScope(x);
      this.visitChild(x.getWith());
      this.visitChild(x.getTableSource());
      if (x.getColumns().size() == 0) {
         String schema = null;
         String table = null;
         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) {
                  SQLExpr expr = new SQLIdentifierExpr(column.getColumnName());
                  x.getColumns().add(expr);
                  expr.setParent(x);
               }
            }
         }
      }

      for(SQLExpr column : x.getColumns()) {
         this.visitChild(column);
         FieldMemo field = (FieldMemo)column.getAttribute("field");
         if (field != null) {
            field.isSelectItem = true;
         }
      }

      this.visitChild(x.getValuesList());
      this.visitChild(x.getQuery());
      return false;
   }

   public boolean visit(SQLInsertStatement x) {
      if (this.scope.context.sqlType == null) {
         this.scope.context.sqlType = TableStat.Mode.Insert;
      }

      this.enterClauseScope(x);
      this.visitChild(x.getWith());
      this.visitChild(x.getTableSource());
      if (x.getColumns().size() == 0) {
         String schema = null;
         String table = null;
         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) {
                  SQLExpr expr = new SQLIdentifierExpr(column.getColumnName());
                  x.getColumns().add(expr);
                  expr.setParent(x);
               }
            }
         }
      }

      for(SQLExpr column : x.getColumns()) {
         this.visitChild(column);
         FieldMemo field = (FieldMemo)column.getAttribute("field");
         if (field != null) {
            field.isSelectItem = true;
         }
      }

      this.visitChild(x.getValuesList());
      this.visitChild(x.getQuery());
      return false;
   }

   public boolean visit(SQLUpdateStatement x) {
      if (this.scope.context.sqlType == null) {
         this.scope.context.sqlType = TableStat.Mode.Update;
      }

      this.enterClauseScope(x);
      this.visitChild(x.getWith());
      this.visitChild(x.getTableSource());
      this.visitChild(x.getFrom());
      this.visitChild(x.getItems());
      this.visitChild(x.getWhere());
      this.visitChild(x.getOrderBy());
      this.visitChild(x.getReturning());
      return false;
   }

   public boolean visit(SQLServerUpdateStatement x) {
      if (this.scope.context.sqlType == null) {
         this.scope.context.sqlType = TableStat.Mode.Update;
      }

      this.enterClauseScope(x);
      this.visitChild(x.getWith());
      this.visitChild(x.getTableSource());
      this.visitChild(x.getFrom());
      this.visitChild(x.getItems());
      this.visitChild(x.getWhere());
      this.visitChild(x.getOrderBy());
      return false;
   }

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

      this.enterClauseScope(x);
      this.visitChild(x.getWith());
      this.visitChild(x.getTableSource());
      this.visitChild(x.getFrom());
      this.visitChild(x.getWhere());
      return false;
   }

   public boolean visit(SQLServerDeleteStatement x) {
      this.visit((SQLDeleteStatement)x);
      return false;
   }

   public boolean visit(SQLWithSubqueryClause x) {
      if (this.debug) {
         System.out.println("enter SQLWithSubqueryClause  " + x.toString());
      }

      Scope scope = this.enterClauseScope(x);
      scope.top = true;
      scope.fromField = scope.isFromField(x);
      return true;
   }

   public boolean visit(SQLWithSubqueryClause.Entry x) {
      TableScope scope = this.enterTableScope(x);
      scope.fromField = scope.isFromField(x);
      scope.top = true;
      TableMemo table = this.registerTable(x);
      this.visitChild(x.getSubQuery());
      table.topTable = true;
      x.putAttribute("table", table);
      x.putAttribute("scope", scope);
      scope.table = table;
      return false;
   }

   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();
      current.getSubFieldsForWith(fields);

      for(Scope child : current.getChildren()) {
         table.getChildren().addAll(child.getBranchTables());
      }

      List<List<FieldMemo>> unionFieldlist = new ArrayList();
      current.getUnionFieldsForWith(unionFieldlist);
      current = this.scope.getCurrent();
      int k = 0;
      Iterator var8 = fields.iterator();

      while(true) {
         FieldMemo field;
         FieldMemo newNameField;
         while(true) {
            if (!var8.hasNext()) {
               this.scope.exitScope();
               return;
            }

            field = (FieldMemo)var8.next();
            newNameField = field.copy();
            newNameField.name = field.alias;
            newNameField.alias = field.alias;
            if (!addColumns) {
               SQLExpr expr = (SQLExpr)x.getColumns().get(k);
               SQLName exprName = (SQLName)expr;
               String fieldName = exprName.getSimpleName();
               if (!this.isEqual(fieldName, field.alias)) {
                  newNameField.name = fieldName;
                  newNameField.alias = fieldName;
               }

               newNameField.ref = expr;
               newNameField.exprs.add(expr);
               break;
            }

            SQLIdentifierExpr name = new SQLIdentifierExpr();
            String fieldName = newNameField.name;

            for(AliasField aliasField : field.aliasFields) {
               if (aliasField.isOutput) {
                  fieldName = aliasField.alias;
                  break;
               }
            }

            if (fieldName != null) {
               newNameField.name = fieldName;
               newNameField.alias = fieldName;
               name.setName(fieldName);
               x.getColumns().add(name);
               name.setParent(x);
               newNameField.ref = name;
               newNameField.exprs.add(name);
               break;
            }
         }

         newNameField.table = table;
         newNameField.scope = current;
         newNameField.tables.add(table);
         if (unionFieldlist.size() <= 0) {
            newNameField.children.add(field);
         } else {
            for(List<FieldMemo> list : unionFieldlist) {
               if (list.size() > k) {
                  FieldMemo temp = (FieldMemo)list.get(k);
                  if (temp.table == null || !temp.table.topTable) {
                     newNameField.children.add(list.get(k));
                  }
               }
            }
         }

         current.addField(newNameField);
         ++k;
      }
   }

   public boolean visit(SQLServerSelectQueryBlock x) {
      if (this.debug) {
         System.out.println("enter SQLServerSelectQueryBlock  " + x.toString());
      }

      this.enterClauseScope(x);
      this.scope.fromField = this.scope.isFromField(x);
      if (x.getFrom() != null) {
         this.parseView(x.getFrom(), x);
         this.replaceUsing(x.getFrom());
      }

      this.visitChild(x.getFrom());
      this.parsePivotTableSource(x.getFrom());
      this.parseSelectList(x);
      this.visitChild(x.getWindows());
      this.visitChild(x.getInto());
      this.visitChild(x.getWhere());
      this.visitChild(x.getStartWith());
      this.visitChild(x.getConnectBy());
      this.visitChild(x.getGroupBy());
      this.visitChild(x.getOrderBy());
      this.visitChild(x.getDistributeBy());
      this.visitChild(x.getSortBy());
      this.visitChild(x.getWaitTime());
      return false;
   }

   private void parsePivotTableSource(SQLTableSource tableSource) {
      if (tableSource != null) {
         Object pivotOptions = tableSource.getAttribute("pivot");
         boolean isPivot = pivotOptions == null ? false : (Boolean)pivotOptions;
         if (isPivot) {
            SQLServerPivotBase pivot = null;
            if (tableSource instanceof SQLServerSelectTableSource) {
               pivot = ((SQLServerSelectTableSource)tableSource).getPivot();
            }

            if (pivot != null) {
               if (pivot instanceof SQLServerSelectPivot) {
                  tableSource.putAttribute("pivot_reverse", false);
                  this.parsePivot((SQLServerSelectPivot)pivot);
               } else if (pivot instanceof SQLServerSelectUnPivot) {
                  tableSource.putAttribute("pivot_reverse", true);
                  this.parseUnPivot((SQLServerSelectUnPivot)pivot);
               }

            }
         }
      }
   }

   private void parsePivot(SQLServerSelectPivot pivot) {
      Scope current = this.scope.getCurrent();
      List<SQLServerSelectPivot.Item> items = pivot.getItems();
      List<FieldMemo> aggColumns = new ArrayList();

      for(SQLServerSelectPivot.Item column : items) {
         for(SQLExpr expr : this.scope.getSubFieldExprs(column.getExpr())) {
            FieldMemo memo = this.getOrCreateFieldByName(expr.toString());
            memo.ref = expr;
            memo.exprs.add(column);
            aggColumns.add(memo);
         }
      }

      current.putAttribute("pivot-agg-fields", aggColumns);
      List<SQLExpr> forColumns = pivot.getPivotFor();
      List<FieldMemo> forFields = new ArrayList();

      for(SQLExpr forColumn : forColumns) {
         SQLName sqlName = (SQLName)forColumn;
         String name = sqlName.getSimpleName();
         FieldMemo field = this.getOrCreateFieldByName(name);
         field.ref = forColumn;
         field.exprs.add(forColumn);
         forFields.add(field);
      }

      current.putAttribute("pivot-for-fields", forFields);
      List<SQLServerSelectPivot.Item> columns = pivot.getPivotIn();
      List<FieldMemo> newFields = new ArrayList();

      for(SQLServerSelectPivot.Item column : columns) {
         String name = column.getAlias();
         if (name == null) {
            name = column.getExpr().toString();
         }

         FieldMemo memo = this.getOrCreateFieldByName(name);
         if (memo.name == null) {
            memo.name = name;
            memo.alias = memo.name;
         }

         memo.ref = column;
         memo.exprs.add(column);
         memo.children.addAll(aggColumns);
         newFields.add(memo);
      }

      current.putAttribute("pivot-in-fields", newFields);
   }

   private void parseUnPivot(SQLServerSelectUnPivot pivot) {
      Scope current = this.scope.getCurrent();
      List<SQLExpr> items = pivot.getItems();
      List<FieldMemo> aggColumns = new ArrayList();
      List<SQLServerSelectPivot.Item> columns = pivot.getPivotIn();
      List<FieldMemo> newFields = new ArrayList();

      for(SQLServerSelectPivot.Item column : columns) {
         String name = column.getAlias();
         if (name == null) {
            name = column.getExpr().toString();
         }

         FieldMemo memo = this.getOrCreateFieldByName(name);
         memo.ref = column;
         memo.exprs.add(column);
         newFields.add(memo);
      }

      for(SQLExpr column : items) {
         boolean isPrimitive = this.scope.isPrimitive(column);
         if (isPrimitive) {
            FieldMemo memo = this.getOrCreateFieldByName(column.toString());
            memo.ref = column;
            memo.exprs.add(column);
            memo.children.addAll(newFields);
            aggColumns.add(memo);
         } else {
            for(SQLExpr expr : this.scope.getSubFieldExprs(column)) {
               FieldMemo memo = this.getOrCreateFieldByName(expr.toString());
               memo.ref = expr;
               memo.exprs.add(column);
               memo.children.addAll(newFields);
               aggColumns.add(memo);
            }
         }
      }

      current.putAttribute("pivot-agg-fields", aggColumns);
      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;
      } else if (!(Boolean)pivotOptions) {
         return source;
      } else {
         List<FieldMemo> target = new ArrayList();
         target.addAll(source);
         boolean reverse = (Boolean)x.getAttribute("pivot_reverse");
         List<FieldMemo> aggColumns = (List)current.getAttribute("pivot-agg-fields");
         List<FieldMemo> forColumns = (List)current.getAttribute("pivot-for-fields");
         List<FieldMemo> inColumns = (List)current.getAttribute("pivot-in-fields");
         if (!reverse) {
            for(FieldMemo column : aggColumns) {
               Iterator<FieldMemo> iterator = target.iterator();

               while(iterator.hasNext()) {
                  FieldMemo z = (FieldMemo)iterator.next();
                  if (this.isEqual(z.name, column.name)) {
                     iterator.remove();
                  }
               }
            }

            for(FieldMemo column : forColumns) {
               Iterator<FieldMemo> iterator = target.iterator();

               while(iterator.hasNext()) {
                  FieldMemo z = (FieldMemo)iterator.next();
                  if (this.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 = (FieldMemo)iterator.next();
                  if (this.isEqual(z.name, column.name)) {
                     iterator.remove();
                  }
               }
            }

            target.addAll(aggColumns);
         }

         return target;
      }
   }

   private FieldMemo getOrCreateFieldByName(String name) {
      Scope current = this.scope.getCurrent();
      name = current.dialector.wrap(name);
      FieldMemo exists = current.getFieldByName(name);
      FieldMemo memo;
      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 = (TableMemo)memo.tables.get(0);
      }

      memo.scope = current;
      return memo;
   }

   private void parseSelectList(SQLServerSelectQueryBlock x) {
      List<SQLSelectItem> selectList = x.getSelectList();
      this.parseSelectList(x.getFrom(), selectList);
   }

   private void parseSelectList(SQLTableSource tableSource, List<SQLSelectItem> selectList) {
      Scope current = this.scope.getCurrent();

      for(int i = 0; i < selectList.size(); ++i) {
         SQLSelectItem item = (SQLSelectItem)selectList.get(i);
         boolean isAll = this.scope.isAllColumn(item.getExpr());
         if (item.getAlias() == null && !isAll) {
            this.setAlias(item);
         }

         if (isAll && item.getAttribute("expanded") == null) {
            item.putAttribute("expanded", true);
            SQLObject parent = item.getParent();
            Owner owner = this.scope.getAllOwner(item.getExpr());
            TableMemo table = owner.alias == null ? null : current.getTable(item.getExpr());
            boolean isSub = table != null && table.isSub || tableSource instanceof SQLSubqueryTableSource || tableSource instanceof SQLUnionQueryTableSource;
            List<FieldMemo> fields = current.expandAllColumns(table, isSub);
            fields = this.parsePivotColumns(tableSource, fields);
            if (fields.size() != 0) {
               int k = 0;
               selectList.remove(i);
               String alias;
               if (tableSource instanceof SQLServerSelectTableSource && ((SQLServerSelectTableSource)tableSource).getPivot() != null) {
                  alias = ((SQLServerSelectTableSource)tableSource).getPivotAlias();
               } else {
                  alias = tableSource.getAlias();
                  if (alias == null) {
                     alias = owner.alias;
                  }
               }

               for(FieldMemo field : fields) {
                  TableMemo fieldTable = field.table;
                  SQLPropertyExpr propExpr = new SQLPropertyExpr();
                  propExpr.setOwner(alias == null ? fieldTable.alias : alias);
                  propExpr.setName(field.alias);
                  SQLSelectItem newItem = new SQLSelectItem();
                  newItem.setParent(parent);
                  newItem.setExpr(propExpr);
                  newItem.setAlias(field.alias);
                  field.scope = current;
                  field.isSelectItem = true;
                  field.ref = newItem;
                  field.exprs.add(propExpr);
                  selectList.add(i + k++, newItem);
               }
            }
         }
      }

      int k = Integer.MIN_VALUE;

      for(SQLSelectItem item : selectList) {
         SQLExpr sqlExpr = item.getExpr();
         boolean isPrimitive = this.scope.isPrimitive(sqlExpr);
         item.putAttribute("isPrimitive", isPrimitive);
         this.visitChild(item);
         FieldMemo field = (FieldMemo)sqlExpr.getAttribute("field");
         if (sqlExpr instanceof SQLValuableExpr) {
            field.isConstant = true;
         }

         field.id = k++;
      }

   }

   private SQLTableSource parseView(SQLTableSource ts, SQLObject parent) {
      if (ts instanceof SQLExprTableSource) {
         SQLTableSource viewTableSource = this.getViewTableSource(ts);
         if (viewTableSource != null) {
            if (parent instanceof SQLServerSelectQueryBlock) {
               SQLServerSelectQueryBlock block = (SQLServerSelectQueryBlock)parent;
               block.setFrom(viewTableSource);
               viewTableSource.setParent(parent);
            }

            return viewTableSource;
         }
      } else if (ts instanceof SQLJoinTableSource) {
         SQLJoinTableSource jts = (SQLJoinTableSource)ts;
         SQLTableSource left = this.parseView(jts.getLeft(), jts);
         if (left != null) {
            jts.setLeft(left);
            left.setParent(jts);
         }

         SQLTableSource right = this.parseView(jts.getRight(), jts);
         if (right != null) {
            jts.setRight(right);
            right.setParent(jts);
         }
      }

      return null;
   }

   private void replaceUsing(SQLTableSource ts) {
      if (ts instanceof SQLJoinTableSource) {
         this.parseUsing((SQLJoinTableSource)ts);
      }
   }

   private void parseUsing(SQLJoinTableSource jts) {
      SQLTableSource leftTable = jts.getLeft();
      if (leftTable instanceof SQLJoinTableSource) {
         this.parseUsing((SQLJoinTableSource)leftTable);
      }

      SQLTableSource rightTable = jts.getRight();
      String rightAlias = rightTable.getAlias();
      if (rightAlias == null) {
         rightAlias = rightTable.toString();
      }

      String leftAlias = (String)leftTable.getAttribute("join-alias");
      if (leftAlias == null) {
         leftAlias = leftTable.getAlias();
      }

      if (leftAlias == null) {
         leftAlias = leftTable.toString();
      }

      jts.putAttribute("join-alias", rightAlias);
      if (jts.getUsing() != null && jts.getUsing().size() != 0) {
         SQLBinaryOpExpr link = jts.getUsing().size() > 1 ? new SQLBinaryOpExpr() : null;

         for(int i = 0; i < jts.getUsing().size(); ++i) {
            SQLExpr sqlExpr = (SQLExpr)jts.getUsing().get(i);
            sqlExpr.setParent((SQLObject)null);
            SQLBinaryOpExpr eqExpr = new SQLBinaryOpExpr();
            eqExpr.setOperator(SQLBinaryOperator.Equality);
            SQLPropertyExpr left = new SQLPropertyExpr();
            left.setOwner(leftAlias);
            left.setName(sqlExpr.toString());
            eqExpr.setLeft(left);
            SQLPropertyExpr right = new SQLPropertyExpr();
            right.setOwner(rightAlias);
            right.setName(sqlExpr.toString());
            eqExpr.setRight(right);
            if (link == null) {
               link = eqExpr;
               break;
            }

            if (i == 0) {
               link.setLeft(eqExpr);
               link.setOperator(SQLBinaryOperator.BooleanAnd);
            } else {
               link.setRight(eqExpr);
               if (i < jts.getUsing().size() - 1) {
                  SQLBinaryOpExpr chain = new SQLBinaryOpExpr();
                  chain.setOperator(SQLBinaryOperator.BooleanAnd);
                  chain.setLeft(link);
                  link = chain;
               }
            }
         }

         SQLExpr conditon = jts.getCondition();
         if (conditon == null) {
            jts.setCondition(link);
         } else {
            SQLBinaryOpExpr operator = new SQLBinaryOpExpr();
            operator.setOperator(SQLBinaryOperator.BooleanAnd);
            operator.setLeft(link);
            operator.setRight(conditon);
         }

         jts.getUsing().clear();
      }
   }

   private SQLTableSource getViewTableSource(SQLTableSource ts) {
      SQLExprTableSource e = (SQLExprTableSource)ts;
      String name = e.getTableName();
      String schema = e.getSchema() == null ? this.scope.getDefaultSchema() : e.getSchema();
      if (name != null) {
         SQLSelect view = this.scope.context.getView(schema.toLowerCase(), name.toLowerCase());
         if (view != null && this.scope.context.sqlType == TableStat.Mode.Select) {
            SQLSubqueryTableSource subQuery = new SQLSubqueryTableSource();
            String alias = e.getAlias() == null ? e.getTableName() : e.getAlias();
            subQuery.setAlias(alias);
            subQuery.setSelect(view.clone());
            return subQuery;
         }
      }

      return null;
   }

   public boolean visit(SQLUnionQuery x) {
      Scope scope = this.enterClauseScope(x);
      scope.union = true;
      scope.isolated = false;
      this.visitChild(x.getRelations());

      for(Scope s1 : scope.getChildren()) {
         s1.isolated = false;
      }

      this.visitChild(x.getOrderBy());
      this.visitChild(x.getLimit());
      return false;
   }

   public boolean visit(SQLLateralViewTableSource x) {
      TableMemo table = this.registerTable((SQLTableSource)x);
      TableScope scope = this.enterTableScope(x);
      scope.table = table;
      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 = this.registerTable((SQLTableSource)x);
      TableScope scope = this.enterTableScope(x);
      scope.table = table;
      scope.isolated = false;
      this.visitChild(x.getSelect());

      for(Scope s : scope.getChildren()) {
         s.isolated = false;
      }

      return false;
   }

   public boolean visit(SQLServerSelectSubqueryTableSource x) {
      if (this.debug) {
         System.out.println("enter SQLServerSelectSubqueryTableSource  " + x.toString());
      }

      if (x.getAlias() == null) {
         x.setAlias(this.alias.aliasTable());
      }

      x.putAttribute("pivot", x.getPivot() != null);
      TableMemo table = this.registerTable((SQLTableSource)x);
      TableScope scope = this.enterTableScope(x);
      scope.table = table;
      scope.isolated = false;
      if (x.getSelect() != null) {
         x.getSelect().accept(this);
      }

      for(Scope s : scope.getChildren()) {
         s.isolated = false;
      }

      return false;
   }

   public boolean visit(SQLValuesTableSource x) {
      if (this.debug) {
         System.out.println("begin SQLValuesTableSource  " + x.toString());
      }

      this.registerTable((SQLTableSource)x);
      return true;
   }

   public boolean visit(SQLServerValuesTableSource x) {
      if (this.debug) {
         System.out.println("begin SQLValuesTableSource  " + x.toString());
      }

      this.registerTable((SQLTableSource)x);
      x.putAttribute("pivot", x.getPivot() != null);
      return true;
   }

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

   public boolean visit(SQLUnionQueryTableSource x) {
      if (x.getAlias() == null) {
         x.setAlias(this.alias.aliasTable());
      }

      TableMemo table = this.registerTable((SQLTableSource)x);
      TableScope scope = this.enterTableScope(x);
      scope.union = true;
      scope.fromField = scope.isFromField(x);
      x.putAttribute("table", table);
      x.putAttribute("scope", scope);
      scope.table = table;
      scope.isolated = false;
      this.visitChild(x.getUnion());

      for(Scope s1 : scope.getChildren()) {
         s1.isolated = false;
      }

      return false;
   }

   public boolean visit(SQLServerUnionQueryTableSource x) {
      this.visit((SQLUnionQueryTableSource)x);
      x.putAttribute("pivot", x.getPivot() != null);
      return false;
   }

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

   public boolean visit(SQLServerSelectTableReference x) {
      this.registerTable((SQLTableSource)x);
      x.putAttribute("pivot", x.getPivot() != null);
      return true;
   }

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

   public boolean visit(SQLIdentifierExpr x) {
      if (this.isPartition(x)) {
         return false;
      } else {
         if (this.scope.getCurrent().isFieldState()) {
            FieldMemo field = this.createField(x, x.getName(), false);
            x.putAttribute("field", field);
         } else if (!(x.getParent() instanceof SQLTableSource) && !(x.getParent() instanceof SQLPartition) || x.getParent() instanceof SQLValuesTableSource) {
            x.putAttribute("orderBy", x.getParent() instanceof SQLSelectOrderByItem);
            FieldMemo field = this.createField(x, x.getName(), false);
            x.putAttribute("field", field);
            if (x.getParent() instanceof SQLUpdateSetItem) {
               field.isSelectItem = true;
            }
         }

         return false;
      }
   }

   private boolean isPartition(SQLIdentifierExpr x) {
      return "$partition".equalsIgnoreCase(x.getName());
   }

   public boolean visit(SQLPropertyExpr x) {
      if (this.scope.getCurrent().isFieldState()) {
         FieldMemo field = this.createField(x, x.getName(), false);
         x.putAttribute("field", field);
      } else if (!(x.getParent() instanceof SQLTableSource) && !(x.getParent() instanceof SQLPartition)) {
         x.putAttribute("orderBy", x.getParent() instanceof SQLSelectOrderByItem);
         FieldMemo field = this.createField(x, x.getName(), false);
         x.putAttribute("field", field);
         if (x.getParent() instanceof SQLUpdateSetItem) {
            field.isSelectItem = true;
         }
      }

      return false;
   }

   private FieldMemo createField(SQLExpr expr, String alias, boolean isSelectItem) {
      Scope current = this.scope.getCurrent();
      FieldMemo field = new FieldMemo();
      alias = current.dialector.wrap(alias);
      if (expr instanceof SQLIdentifierExpr) {
         SQLIdentifierExpr idExpr = (SQLIdentifierExpr)expr;
         boolean isOrderBy = expr.getAttribute("orderBy") != null && (Boolean)expr.getAttribute("orderBy");
         if (isOrderBy) {
            String name = current.dialector.wrap(idExpr.getSimpleName());
            FieldMemo exist = current.getFieldByAlias(name);
            if (exist != null) {
               return exist;
            }
         }

         field.name = current.dialector.wrap(idExpr.getName());
         field.atom = true;
         this.setField(field, alias, isSelectItem, expr);
         field.children = this.scope.getCurrent().getSubRelationFields(field);
         this.setAtom(field);
         this.setPivotFieldChildren(current, field);

         for(String functionName : functionNames) {
            if (functionName.equalsIgnoreCase(idExpr.getName())) {
               field.complex = true;
               field.atom = false;
               break;
            }
         }

         field = current.addField(field);
      } else if (expr instanceof SQLPropertyExpr) {
         SQLPropertyExpr prop = (SQLPropertyExpr)expr;
         field.name = current.dialector.wrap(prop.getName());
         field.atom = true;
         this.setField(field, alias, isSelectItem, expr);
         field.children = this.scope.getCurrent().getSubRelationFields(field);
         this.setAtom(field);
         this.setPivotFieldChildren(current, field);
         field = current.addField(field);
      } else if (expr instanceof SQLValuableExpr) {
         field.name = alias;
         field.isConstant = true;
         this.setField(field, alias, isSelectItem, expr);
         field = current.addField(field);
      } else {
         alias = current.dialector.wrap(alias);
         field.name = alias;
         field.alias = alias;
         field.isSelectItem = isSelectItem;
         field.complex = true;
         field.ref = 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.scope = this.scope.getCurrent();
         }

         current.openFieldState();
         this.visitChild(expr);
         current.closeFieldState();
         List<SQLExpr> exprs = current.getSubFieldExprs(expr);
         List<FieldMemo> subFields = new ArrayList();
         current.getFieldsByExprs(exprs, subFields);
         List<TableMemo> tables = new ArrayList();
         List<FieldMemo> children = new ArrayList();
         Set<String> names = new HashSet();
         boolean isPrimitive = this.scope.isPrimitive(expr);
         if (!isPrimitive) {
            current.getSubBranchFields(subFields);
         }

         if (subFields.size() <= 0) {
            tables.addAll(current.getBranchTables());
         } else {
            for(FieldMemo subField : subFields) {
               if (!(expr instanceof SQLQueryExpr) || subField.isSelectItem) {
                  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());
                  } else if (subField.tables.size() > 0) {
                     for(TableMemo table : subField.tables) {
                        if (!names.contains(table.getQualifiedName())) {
                           tables.add(table);
                           names.add(table.getQualifiedName());
                        }
                     }
                  }
               }
            }
         }

         field.tables = tables;
         if (tables.size() == 1) {
            field.table = (TableMemo)tables.get(0);
         }

         field.children = children;
         this.setAtom(field);
         field = current.addField(field);
      }

      expr.putAttribute("field", field);
      return field;
   }

   private void setPivotFieldChildren(Scope current, FieldMemo field) {
      if (current.getAttribute("pivot-in-fields") != null) {
         List<FieldMemo> pivotField = (List)current.getAttribute("pivot-in-fields");
         FieldMemo finalField = field;
         FieldMemo temp = null;

         for(FieldMemo fieldMemo : pivotField) {
            if (this.isEqual(fieldMemo.name, finalField.name)) {
               temp = fieldMemo;
               break;
            }
         }

         if (temp != null && temp.children.size() > 0) {
            field.children.addAll(temp.children);
            field.atom = false;
         }
      }

      if (current.getAttribute("pivot-agg-fields") != null) {
         List<FieldMemo> unpivotField = (List)current.getAttribute("pivot-agg-fields");
         FieldMemo finalField = field;
         FieldMemo temp = null;

         for(FieldMemo fieldMemo : unpivotField) {
            if (this.isEqual(fieldMemo.name, finalField.name)) {
               temp = fieldMemo;
               break;
            }
         }

         if (temp != null && temp.children.size() > 0) {
            field.children.addAll(temp.children);
            field.atom = false;
         }
      }

   }

   private void setAtom(FieldMemo field) {
      if (field.children.size() != 0) {
         Set<String> fdNames = field.tableNames();
         boolean found = false;

         for(FieldMemo child : field.children) {
            if (this.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 = expr;
      TableMemo table = this.getTableFromStatement(expr, current);
      if (table != null) {
         field.table = table;
      } else {
         field.table = this.scope.getCurrent().getTable(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 getTableFromStatement(SQLExpr expr, Scope current) {
      if (current.ref instanceof SQLUpdateStatement && expr instanceof SQLIdentifierExpr) {
         SQLUpdateStatement parent = (SQLUpdateStatement)this.findStatementRecursive(expr);
         SQLTableSource tableSource = parent.getTableSource();
         if (tableSource != null && tableSource instanceof SQLExprTableSource) {
            SQLExprTableSource exprTableSource = (SQLExprTableSource)tableSource;
            String tableName = current.dialector.wrap(exprTableSource.getTableName());
            String schemaName = current.dialector.wrap(exprTableSource.getSchema() == null ? this.scope.getDefaultSchema() : exprTableSource.getSchema());
            TableMemo table = null;

            for(TableMemo z : current.getTableMap().values()) {
               if (this.isEqual(z.alias, tableName)) {
                  table = z;
                  break;
               }
            }

            if (table != null) {
               for(Column column : current.context.getColumns(schemaName, tableName)) {
                  if (this.isEqual(column.getColumnName(), ((SQLIdentifierExpr)expr).getName())) {
                     return table;
                  }
               }
            }
         }
      }

      return null;
   }

   private Object findStatementRecursive(SQLObject expr) {
      SQLObject parent = expr.getParent();
      if (parent == null) {
         return null;
      } else {
         return parent instanceof SQLUpdateStatement ? parent : this.findStatementRecursive(parent);
      }
   }

   private TableMemo registerTable(SQLTableSource x) {
      Scope current = this.scope.getCurrent();
      if (x instanceof SQLExprTableSource) {
         SQLExprTableSource exprTableSource = (SQLExprTableSource)x;
         TableMemo table = new TableMemo(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;
         if (table.name == null) {
            table.name = table.alias;
         }

         if (table.name.equals("*") && exprTableSource.getExpr() != null && exprTableSource.getExpr() instanceof SQLPropertyExpr) {
            return null;
         } else {
            if (table.name == null && table.alias != null) {
               table.name = table.alias;
            }

            if (exprTableSource.getExpr() != null && exprTableSource.getExpr() instanceof SQLMethodInvokeExpr) {
               table.isSub = true;
            }

            table.schema = schemaName;
            TableMemo refTable = current.getParentTable(schemaName, tableName);
            if (refTable != null && refTable.topTable) {
               table.schema = refTable.schema;
               table.topTable = true;
               table.scope = current;
               table.refTable = refTable;
               refTable.getChildren().add(table);
               current.addTableCheckConflict(table);
               table.topTable = refTable.topTable;
               return refTable;
            } else {
               boolean isFunction = false;
               SQLExpr expr = ((SQLExprTableSource)x).getExpr();
               isFunction = expr instanceof SQLMethodInvokeExpr;
               this.setTableRelation(table);
               if (!table.isSub && !isFunction) {
                  table.system = this.scope.dialector.isSystemTable(table.name);
                  if (!table.system) {
                     this.scope.context.append(table);
                  }
               }

               x.putAttribute("table", table);
               return table;
            }
         }
      } else if (x instanceof SQLSubqueryTableSource) {
         SQLSubqueryTableSource tableSource = (SQLSubqueryTableSource)x;
         TableMemo table = new TableMemo(x);
         if (tableSource instanceof SQLServerSelectTableSource && ((SQLServerSelectTableSource)tableSource).getPivot() != null) {
            table.name = current.dialector.wrap(((SQLServerSelectTableSource)tableSource).getPivotAlias());
         } else {
            table.name = current.dialector.wrap(tableSource.getAlias());
         }

         table.alias = table.name;
         table.nowName = table.name;
         table.schema = current.dialector.wrap(this.scope.getDefaultSchema());
         table.isSub = true;
         this.setTableRelation(table);
         x.putAttribute("table", table);
         return table;
      } else if (x instanceof SQLLateralViewTableSource) {
         SQLLateralViewTableSource tableSource = (SQLLateralViewTableSource)x;
         TableMemo table = new TableMemo(x);
         table.name = current.dialector.wrap(tableSource.getAlias());
         table.alias = table.name;
         table.schema = current.dialector.wrap(this.scope.getDefaultSchema());
         table.isSub = true;
         this.setTableRelation(table);
         x.putAttribute("table", table);
         return table;
      } else if (x instanceof SQLUnionQueryTableSource) {
         SQLUnionQueryTableSource tableSource = (SQLUnionQueryTableSource)x;
         TableMemo table = new TableMemo(x);
         if (tableSource instanceof SQLServerSelectTableSource && ((SQLServerSelectTableSource)tableSource).getPivot() != null) {
            table.name = current.dialector.wrap(((SQLServerSelectTableSource)tableSource).getPivotAlias());
         } else {
            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;
         this.setTableRelation(table);
         x.putAttribute("table", table);
         return table;
      } else {
         if (x instanceof SQLValuesTableSource) {
            SQLValuesTableSource tableSource = (SQLValuesTableSource)x;
            TableMemo table = new TableMemo(x);
            if (tableSource instanceof SQLServerSelectTableSource && ((SQLServerSelectTableSource)tableSource).getPivot() != null) {
               table.name = current.dialector.wrap(((SQLServerSelectTableSource)tableSource).getPivotAlias());
            } else {
               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;
            this.setTableRelation(table);
            x.putAttribute("table", table);
         }

         return null;
      }
   }

   private TableMemo registerTable(SQLWithSubqueryClause.Entry x) {
      Scope current = this.scope.getCurrent();
      TableMemo table = new TableMemo(x);
      table.name = current.dialector.wrap(x.getAlias());
      table.nowName = table.name;
      table.alias = table.name;
      table.schema = current.dialector.wrap(this.scope.getDefaultSchema());
      table.isSub = true;
      table.topTable = true;
      this.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.addTableCheckConflict(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 = 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) {
         SQLExpr e = x.getExpr();
         if (e instanceof SQLIdentifierExpr) {
            x.setAlias(((SQLIdentifierExpr)e).getName());
         } else if (e instanceof SQLPropertyExpr) {
            x.setAlias(((SQLPropertyExpr)e).getName());
         } else {
            x.setAlias(e.toString());
         }

      }
   }

   public boolean visit(SQLServerMergeStatement x) {
      if (this.scope.context.sqlType == null) {
         this.scope.context.sqlType = TableStat.Mode.Merge;
      }

      this.enterClauseScope(x);
      this.visitChild(x.getWith());
      if (x.getInto() != null) {
         x.getInto().accept(this);
      }

      TableMemo intoTable = (TableMemo)x.getInto().getAttribute("table");
      if (x.getUsing() != null) {
         x.getUsing().accept(this);
         if (x.getUsing() instanceof SQLServerSelectSubqueryTableSource) {
            Scope usingScope = this.scope.getScope(x.getUsing());
            if (usingScope != null) {
               usingScope.top = true;
            }
         }
      }

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

      for(SQLObjectImpl object : x.getClauses()) {
         object.putAttribute("intoTable", intoTable);
         object.accept(this);
      }

      return false;
   }

   public boolean visit(SQLServerMergeStatement.SQLServerMergeUpdateClause 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 = column;
               field.scope = current;
               current.addField(field);
            }
         }
      }

      return false;
   }

   public boolean visit(SQLServerMergeStatement.SQLServerMergeInsertClause x) {
      Scope current = this.scope.getCurrent();
      TableMemo table = (TableMemo)x.getAttribute("intoTable");
      if (x.getColumns().size() == 0) {
         for(FieldMemo field : current.expandAllColumns(table, false)) {
            SQLIdentifierExpr expr = new SQLIdentifierExpr();
            expr.setName(field.name);
            expr.setParent(x);
            x.getColumns().add(expr);
            field.tables.add(table);
            field.exprs.add(expr);
            field.ref = 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 = column;
               field.scope = current;
               current.addField(field);
            }
         }
      }

      current.openFieldState();
      if (x.getValues() != null) {
         this.visitChild(x.getValues());
      }

      current.closeFieldState();
      return false;
   }

   public boolean visit(SQLServerSelectPivot x) {
      this.visitChild(x.getItems());
      this.visitChild(x.getPivotFor());
      return false;
   }

   public boolean visit(SQLMethodInvokeExpr x) {
      this.visitChild(x.getOwner());
      String name = x.getMethodName();
      if (name != null) {
         name = name.toLowerCase();
      } else {
         name = "";
      }

      List<SQLExpr> arguments = x.getArguments();

      for(int i = 0; i < arguments.size(); ++i) {
         if (!"datediff".equals(name) && !"datepart".equals(name) && !"datename".equals(name) && !"dateadd".equals(name) && !"convert".equals(name) || i != 0) {
            SQLExpr arg = (SQLExpr)arguments.get(i);
            this.visitChild(arg);
         }
      }

      this.visitChild(x.getFrom());
      this.visitChild(x.getUsing());
      this.visitChild(x.getFor());
      return false;
   }
}
