package com.chenyang.druid.sql.ast.statement;

import com.chenyang.druid.DbType;
import com.chenyang.druid.sql.SQLUtils;
import com.chenyang.druid.sql.ast.SQLCommentHint;
import com.chenyang.druid.sql.ast.SQLDbTypedObject;
import com.chenyang.druid.sql.ast.SQLExpr;
import com.chenyang.druid.sql.ast.SQLLimit;
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.SQLOrderBy;
import com.chenyang.druid.sql.ast.SQLOrderingSpecification;
import com.chenyang.druid.sql.ast.SQLReplaceable;
import com.chenyang.druid.sql.ast.SQLWindow;
import com.chenyang.druid.sql.ast.expr.SQLAggregateExpr;
import com.chenyang.druid.sql.ast.expr.SQLAllColumnExpr;
import com.chenyang.druid.sql.ast.expr.SQLBinaryOpExpr;
import com.chenyang.druid.sql.ast.expr.SQLBinaryOpExprGroup;
import com.chenyang.druid.sql.ast.expr.SQLBinaryOperator;
import com.chenyang.druid.sql.ast.expr.SQLBooleanExpr;
import com.chenyang.druid.sql.ast.expr.SQLIdentifierExpr;
import com.chenyang.druid.sql.ast.expr.SQLInListExpr;
import com.chenyang.druid.sql.ast.expr.SQLIntegerExpr;
import com.chenyang.druid.sql.ast.expr.SQLMethodInvokeExpr;
import com.chenyang.druid.sql.ast.expr.SQLNullExpr;
import com.chenyang.druid.sql.ast.expr.SQLPropertyExpr;
import com.chenyang.druid.sql.visitor.SQLASTVisitor;
import com.chenyang.druid.sql.visitor.SQLASTVisitorAdapter;
import com.chenyang.druid.util.FnvHash;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.TreeSet;

public class SQLSelectQueryBlock extends SQLObjectImpl implements SQLSelectQuery, SQLReplaceable, SQLDbTypedObject {
   protected int distionOption;
   protected final List<SQLSelectItem> selectList = new ArrayList();
   protected SQLTableSource from;
   protected SQLExprTableSource into;
   protected SQLExpr where;
   protected SQLExpr startWith;
   protected SQLExpr connectBy;
   protected boolean prior = false;
   protected boolean noCycle = false;
   protected SQLOrderBy orderBySiblings;
   protected SQLSelectGroupByClause groupBy;
   protected List<SQLWindow> windows;
   protected SQLOrderBy orderBy;
   protected boolean parenthesized = false;
   protected boolean forUpdate = false;
   protected boolean noWait = false;
   protected boolean skipLocked = false;
   protected boolean forShare = false;
   protected SQLExpr waitTime;
   protected SQLLimit limit;
   protected List<SQLExpr> forUpdateOf;
   protected List<SQLSelectOrderByItem> distributeBy;
   protected List<SQLSelectOrderByItem> sortBy;
   protected List<SQLSelectOrderByItem> clusterBy;
   protected String cachedSelectList;
   protected long cachedSelectListHash;
   protected DbType dbType;
   protected List<SQLCommentHint> hints;

   public SQLSelectQueryBlock() {
   }

   public SQLSelectQueryBlock(DbType dbType) {
      this.dbType = dbType;
   }

   public SQLExprTableSource getInto() {
      return this.into;
   }

   public void setInto(SQLExpr into) {
      this.setInto(new SQLExprTableSource(into));
   }

   public void setInto(SQLExprTableSource into) {
      if (into != null) {
         into.setParent(this);
      }

      this.into = into;
   }

   public SQLSelectGroupByClause getGroupBy() {
      return this.groupBy;
   }

   public void setGroupBy(SQLSelectGroupByClause x) {
      if (x != null) {
         x.setParent(this);
      }

      this.groupBy = x;
   }

   public List<SQLWindow> getWindows() {
      return this.windows;
   }

   public void addWindow(SQLWindow x) {
      if (x != null) {
         x.setParent(this);
      }

      if (this.windows == null) {
         this.windows = new ArrayList(4);
      }

      this.windows.add(x);
   }

   public SQLExpr getWhere() {
      return this.where;
   }

   public void setWhere(SQLExpr x) {
      if (x != null) {
         x.setParent(this);
      }

      this.where = x;
   }

   public void addWhere(SQLExpr condition) {
      if (condition != null) {
         if (this.where == null) {
            condition.setParent(this);
            this.where = condition;
         } else {
            for(SQLExpr item : SQLBinaryOpExpr.split(this.where, SQLBinaryOperator.BooleanAnd)) {
               if (condition.equals(item)) {
                  return;
               }

               if (condition instanceof SQLInListExpr) {
                  SQLInListExpr inListExpr = (SQLInListExpr)condition;
                  if (item instanceof SQLBinaryOpExpr) {
                     SQLBinaryOpExpr binaryOpItem = (SQLBinaryOpExpr)item;
                     SQLExpr left = binaryOpItem.getLeft();
                     SQLExpr right = binaryOpItem.getRight();
                     if (inListExpr.getExpr().equals(left) && binaryOpItem.getOperator() == SQLBinaryOperator.Equality && !(right instanceof SQLNullExpr)) {
                        if (inListExpr.getTargetList().contains(right)) {
                           return;
                        }

                        SQLUtils.replaceInParent((SQLExpr)item, (SQLExpr)(new SQLBooleanExpr(false)));
                        return;
                     }
                  } else if (item instanceof SQLInListExpr) {
                     SQLInListExpr inListItem = (SQLInListExpr)item;
                     if (inListExpr.getExpr().equals(inListItem.getExpr())) {
                        TreeSet<SQLExpr> set = new TreeSet();

                        for(SQLExpr itemItem : inListItem.getTargetList()) {
                           set.add(itemItem);
                        }

                        List<SQLExpr> andList = new ArrayList();

                        for(SQLExpr exprItem : inListExpr.getTargetList()) {
                           if (set.contains(exprItem)) {
                              andList.add(exprItem.clone());
                           }
                        }

                        if (andList.size() == 0) {
                           SQLUtils.replaceInParent((SQLExpr)item, (SQLExpr)(new SQLBooleanExpr(false)));
                           return;
                        }

                        inListItem.getTargetList().clear();

                        for(SQLExpr val : andList) {
                           inListItem.addTarget(val);
                        }

                        return;
                     }
                  }
               }
            }

            this.where = SQLBinaryOpExpr.and(this.where, condition);
            this.where.setParent(this);
         }
      }
   }

   public void addWhereForDynamicFilter(SQLExpr condition) {
      if (condition != null) {
         if (this.where == null) {
            condition.setParent(this);
            this.where = condition;
         } else {
            if (this.where instanceof SQLBinaryOpExpr || this.where instanceof SQLBinaryOpExprGroup) {
               for(SQLExpr item : SQLBinaryOpExpr.split(this.where, SQLBinaryOperator.BooleanAnd)) {
                  if (condition.equals(item)) {
                     return;
                  }

                  if (condition instanceof SQLInListExpr) {
                     SQLInListExpr inListExpr = (SQLInListExpr)condition;
                     if (item instanceof SQLBinaryOpExpr) {
                        SQLBinaryOpExpr binaryOpItem = (SQLBinaryOpExpr)item;
                        SQLExpr left = binaryOpItem.getLeft();
                        SQLExpr right = binaryOpItem.getRight();
                        if (inListExpr.getExpr().equals(left)) {
                           if (inListExpr.getTargetList().contains(right)) {
                              this.replace(item, inListExpr);
                           } else {
                              SQLInListExpr inListExpr2 = inListExpr.clone();
                              inListExpr2.addTarget(right.clone());
                              this.replace(item, inListExpr2);
                           }

                           return;
                        }
                     } else if (item instanceof SQLInListExpr) {
                        SQLInListExpr inListItem = (SQLInListExpr)item;
                        if (inListExpr.getExpr().equals(inListItem.getExpr())) {
                           TreeSet<SQLExpr> set = new TreeSet();

                           for(SQLExpr itemItem : inListItem.getTargetList()) {
                              set.add(itemItem);
                           }

                           for(SQLExpr exprItem : inListExpr.getTargetList()) {
                              if (!set.contains(exprItem)) {
                                 inListItem.addTarget(exprItem.clone());
                              }
                           }

                           return;
                        }
                     }
                  }
               }
            }

            this.where = SQLBinaryOpExpr.and(this.where, condition);
            this.where.setParent(this);
         }
      }
   }

   public void whereOr(SQLExpr condition) {
      if (condition != null) {
         if (this.where == null) {
            condition.setParent(this);
            this.where = condition;
         } else if (!SQLBinaryOpExpr.isOr(this.where) && !SQLBinaryOpExpr.isOr(condition)) {
            this.where = SQLBinaryOpExpr.or(this.where, condition);
            this.where.setParent(this);
         } else {
            SQLBinaryOpExprGroup group = new SQLBinaryOpExprGroup(SQLBinaryOperator.BooleanOr, this.dbType);
            group.add(this.where);
            group.add(condition);
            group.setParent(this);
            this.where = group;
         }

      }
   }

   public void addHaving(SQLExpr condition) {
      if (condition != null) {
         if (this.groupBy == null) {
            this.groupBy = new SQLSelectGroupByClause();
         }

         this.groupBy.addHaving(condition);
      }
   }

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

   public void setOrderBy(SQLOrderBy orderBy) {
      if (orderBy != null) {
         orderBy.setParent(this);
      }

      this.orderBy = orderBy;
   }

   public void addOrderBy(SQLOrderBy orderBy) {
      if (orderBy != null) {
         if (this.orderBy == null) {
            this.setOrderBy(orderBy);
         } else {
            for(SQLSelectOrderByItem item : orderBy.getItems()) {
               this.orderBy.addItem(item.clone());
            }

         }
      }
   }

   public void addOrderBy(SQLSelectOrderByItem orderByItem) {
      if (orderByItem != null) {
         if (this.orderBy == null) {
            this.orderBy = new SQLOrderBy();
            this.orderBy.setParent(this);
         }

         this.orderBy.addItem(orderByItem);
      }
   }

   public boolean containsOrderBy(SQLSelectOrderByItem orderByItem) {
      if (orderByItem != null && this.orderBy != null) {
         if (this.orderBy.getItems().contains(orderByItem)) {
            return true;
         } else {
            SQLExpr expr = orderByItem.getExpr();
            if (expr == null && expr instanceof SQLIntegerExpr) {
               return false;
            } else {
               int index = 0;

               for(int i = 0; i < this.selectList.size(); ++i) {
                  SQLSelectItem selectItem = (SQLSelectItem)this.selectList.get(i);
                  if (selectItem.getExpr().equals(expr)) {
                     index = i + 1;
                     break;
                  }
               }

               if (index > 0) {
                  for(SQLSelectOrderByItem selectOrderByItem : this.orderBy.getItems()) {
                     SQLExpr orderByItemExpr = selectOrderByItem.getExpr();
                     if (orderByItemExpr instanceof SQLIntegerExpr && ((SQLIntegerExpr)orderByItemExpr).getNumber().intValue() == index) {
                        return true;
                     }
                  }
               }

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

   public void addOrderBy(SQLExpr orderBy, SQLOrderingSpecification type) {
      if (orderBy != null) {
         if (this.orderBy == null) {
            this.setOrderBy(new SQLOrderBy(orderBy, type));
         } else {
            this.orderBy.addItem(orderBy, type);
         }
      }
   }

   public void addOrderBy(SQLExpr orderBy) {
      if (orderBy != null) {
         if (this.orderBy == null) {
            this.setOrderBy(new SQLOrderBy(orderBy));
         } else {
            this.orderBy.addItem(orderBy);
         }
      }
   }

   public SQLOrderBy getOrderBySiblings() {
      return this.orderBySiblings;
   }

   public void setOrderBySiblings(SQLOrderBy orderBySiblings) {
      if (orderBySiblings != null) {
         orderBySiblings.setParent(this);
      }

      this.orderBySiblings = orderBySiblings;
   }

   public int getDistionOption() {
      return this.distionOption;
   }

   public void setDistionOption(int distionOption) {
      this.distionOption = distionOption;
   }

   public void setDistinct() {
      this.distionOption = 2;
   }

   public boolean isDistinct() {
      return this.distionOption == 2;
   }

   public List<SQLSelectItem> getSelectList() {
      return this.selectList;
   }

   public SQLSelectItem getSelectItem(int i) {
      return (SQLSelectItem)this.selectList.get(i);
   }

   public void addSelectItem(SQLSelectItem item) {
      this.selectList.add(item);
      item.setParent(this);
   }

   public SQLSelectItem addSelectItem(SQLExpr expr) {
      SQLSelectItem item = new SQLSelectItem(expr);
      this.addSelectItem(item);
      return item;
   }

   public void addSelectItem(String selectItemExpr, String alias) {
      SQLExpr expr = SQLUtils.toSQLExpr(selectItemExpr, this.dbType);
      this.addSelectItem(new SQLSelectItem(expr, alias));
   }

   public void addSelectItem(SQLExpr expr, String alias) {
      this.addSelectItem(new SQLSelectItem(expr, alias));
   }

   public boolean hasSelectAggregation() {
      AggregationStatVisitor v = new AggregationStatVisitor();

      for(SQLSelectItem item : this.selectList) {
         SQLExpr expr = item.getExpr();
         expr.accept(v);
      }

      return v.aggregation;
   }

   public SQLTableSource getFrom() {
      return this.from;
   }

   public void setFrom(SQLExpr from) {
      this.setFrom((SQLTableSource)(new SQLExprTableSource(from)));
   }

   public void setFrom(SQLTableSource from) {
      if (from != null) {
         from.setParent(this);
      }

      this.from = from;
   }

   public void setFrom(SQLSelectQueryBlock queryBlock, String alias) {
      if (queryBlock == null) {
         this.from = null;
      } else {
         this.setFrom(new SQLSelect(queryBlock), alias);
      }
   }

   public void setFrom(SQLSelect select, String alias) {
      if (select == null) {
         this.from = null;
      } else {
         SQLSubqueryTableSource from = new SQLSubqueryTableSource(select);
         from.setAlias(alias);
         this.setFrom((SQLTableSource)from);
      }
   }

   public void setFrom(String tableName, String alias) {
      SQLExprTableSource from;
      if (tableName != null && tableName.length() != 0) {
         SQLExpr expr = SQLUtils.toSQLExpr(tableName);
         from = new SQLExprTableSource(expr, alias);
      } else {
         from = null;
      }

      this.setFrom((SQLTableSource)from);
   }

   public boolean isParenthesized() {
      return this.parenthesized;
   }

   public void setParenthesized(boolean parenthesized) {
      this.parenthesized = parenthesized;
   }

   public boolean isForUpdate() {
      return this.forUpdate;
   }

   public void setForUpdate(boolean forUpdate) {
      this.forUpdate = forUpdate;
   }

   public boolean isNoWait() {
      return this.noWait;
   }

   public void setNoWait(boolean noWait) {
      this.noWait = noWait;
   }

   public boolean isSkipLocked() {
      return this.skipLocked;
   }

   public void setSkipLocked(boolean skipLocked) {
      this.skipLocked = skipLocked;
   }

   public boolean isForShare() {
      return this.forShare;
   }

   public void setForShare(boolean forShare) {
      this.forShare = forShare;
   }

   public SQLExpr getWaitTime() {
      return this.waitTime;
   }

   public void setWaitTime(SQLExpr waitTime) {
      if (waitTime != null) {
         waitTime.setParent(this);
      }

      this.waitTime = waitTime;
   }

   public SQLLimit getLimit() {
      return this.limit;
   }

   public void setLimit(SQLLimit limit) {
      if (limit != null) {
         limit.setParent(this);
      }

      this.limit = limit;
   }

   public void mergeLimit(SQLLimit limit) {
      if (this.limit == null) {
         this.limit = limit.clone();
      } else {
         this.limit.merge(limit);
      }
   }

   public SQLExpr getFirst() {
      return this.limit == null ? null : this.limit.getRowCount();
   }

   public void setFirst(SQLExpr first) {
      if (this.limit == null) {
         this.limit = new SQLLimit();
      }

      this.limit.setRowCount(first);
   }

   public SQLExpr getOffset() {
      return this.limit == null ? null : this.limit.getOffset();
   }

   public void setOffset(SQLExpr offset) {
      if (this.limit == null) {
         this.limit = new SQLLimit();
      }

      this.limit.setOffset(offset);
   }

   public boolean isPrior() {
      return this.prior;
   }

   public void setPrior(boolean prior) {
      this.prior = prior;
   }

   public SQLExpr getStartWith() {
      return this.startWith;
   }

   public void setStartWith(SQLExpr startWith) {
      if (startWith != null) {
         startWith.setParent(this);
      }

      this.startWith = startWith;
   }

   public SQLExpr getConnectBy() {
      return this.connectBy;
   }

   public void setConnectBy(SQLExpr connectBy) {
      if (connectBy != null) {
         connectBy.setParent(this);
      }

      this.connectBy = connectBy;
   }

   public boolean isNoCycle() {
      return this.noCycle;
   }

   public void setNoCycle(boolean noCycle) {
      this.noCycle = noCycle;
   }

   public List<SQLSelectOrderByItem> getDistributeBy() {
      if (this.distributeBy == null) {
         this.distributeBy = new ArrayList();
      }

      return this.distributeBy;
   }

   public List<SQLSelectOrderByItem> getDistributeByDirect() {
      return this.distributeBy;
   }

   public void addDistributeBy(SQLExpr x) {
      if (x != null) {
         x.setParent(this);
         if (this.distributeBy == null) {
            this.distributeBy = new ArrayList();
         }

         this.distributeBy.add(new SQLSelectOrderByItem(x));
      }
   }

   public void addDistributeBy(SQLSelectOrderByItem item) {
      if (this.distributeBy == null) {
         this.distributeBy = new ArrayList();
      }

      if (item != null) {
         item.setParent(this);
      }

      this.distributeBy.add(item);
   }

   public List<SQLSelectOrderByItem> getSortBy() {
      if (this.sortBy == null) {
         this.sortBy = new ArrayList();
      }

      return this.sortBy;
   }

   public List<SQLSelectOrderByItem> getSortByDirect() {
      return this.sortBy;
   }

   public void addSortBy(SQLSelectOrderByItem item) {
      if (this.sortBy == null) {
         this.sortBy = new ArrayList();
      }

      if (item != null) {
         item.setParent(this);
      }

      this.sortBy.add(item);
   }

   protected void accept0(SQLASTVisitor visitor) {
      if (visitor.visit(this)) {
         for(int i = 0; i < this.selectList.size(); ++i) {
            SQLSelectItem item = (SQLSelectItem)this.selectList.get(i);
            if (item != null) {
               item.accept(visitor);
            }
         }

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

         if (this.windows != null) {
            for(int i = 0; i < this.windows.size(); ++i) {
               SQLWindow item = (SQLWindow)this.windows.get(i);
               item.accept(visitor);
            }
         }

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

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

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

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

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

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

         if (this.distributeBy != null) {
            for(int i = 0; i < this.distributeBy.size(); ++i) {
               SQLSelectOrderByItem item = (SQLSelectOrderByItem)this.distributeBy.get(i);
               item.accept(visitor);
            }
         }

         if (this.sortBy != null) {
            for(int i = 0; i < this.sortBy.size(); ++i) {
               SQLSelectOrderByItem item = (SQLSelectOrderByItem)this.sortBy.get(i);
               item.accept(visitor);
            }
         }

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

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

      visitor.endVisit(this);
   }

   public boolean equals(Object o) {
      if (this == o) {
         return true;
      } else if (o != null && this.getClass() == o.getClass()) {
         SQLSelectQueryBlock that = (SQLSelectQueryBlock)o;
         if (this.distionOption != that.distionOption) {
            return false;
         } else if (this.prior != that.prior) {
            return false;
         } else if (this.noCycle != that.noCycle) {
            return false;
         } else if (this.parenthesized != that.parenthesized) {
            return false;
         } else if (this.forUpdate != that.forUpdate) {
            return false;
         } else if (this.noWait != that.noWait) {
            return false;
         } else if (this.cachedSelectListHash != that.cachedSelectListHash) {
            return false;
         } else {
            if (this.selectList != null) {
               if (!this.selectList.equals(that.selectList)) {
                  return false;
               }
            } else if (that.selectList != null) {
               return false;
            }

            if (this.from != null) {
               if (!this.from.equals(that.from)) {
                  return false;
               }
            } else if (that.from != null) {
               return false;
            }

            if (this.into != null) {
               if (!this.into.equals(that.into)) {
                  return false;
               }
            } else if (that.into != null) {
               return false;
            }

            if (this.where != null) {
               if (!this.where.equals(that.where)) {
                  return false;
               }
            } else if (that.where != null) {
               return false;
            }

            if (this.startWith != null) {
               if (!this.startWith.equals(that.startWith)) {
                  return false;
               }
            } else if (that.startWith != null) {
               return false;
            }

            if (this.connectBy != null) {
               if (!this.connectBy.equals(that.connectBy)) {
                  return false;
               }
            } else if (that.connectBy != null) {
               return false;
            }

            if (this.orderBySiblings != null) {
               if (!this.orderBySiblings.equals(that.orderBySiblings)) {
                  return false;
               }
            } else if (that.orderBySiblings != null) {
               return false;
            }

            if (this.groupBy != null) {
               if (!this.groupBy.equals(that.groupBy)) {
                  return false;
               }
            } else if (that.groupBy != null) {
               return false;
            }

            if (this.orderBy != null) {
               if (!this.orderBy.equals(that.orderBy)) {
                  return false;
               }
            } else if (that.orderBy != null) {
               return false;
            }

            if (this.waitTime != null) {
               if (!this.waitTime.equals(that.waitTime)) {
                  return false;
               }
            } else if (that.waitTime != null) {
               return false;
            }

            if (this.limit != null) {
               if (!this.limit.equals(that.limit)) {
                  return false;
               }
            } else if (that.limit != null) {
               return false;
            }

            if (this.forUpdateOf != null) {
               if (!this.forUpdateOf.equals(that.forUpdateOf)) {
                  return false;
               }
            } else if (that.forUpdateOf != null) {
               return false;
            }

            if (this.distributeBy != null) {
               if (!this.distributeBy.equals(that.distributeBy)) {
                  return false;
               }
            } else if (that.distributeBy != null) {
               return false;
            }

            if (this.sortBy != null) {
               if (!this.sortBy.equals(that.sortBy)) {
                  return false;
               }
            } else if (that.sortBy != null) {
               return false;
            }

            if (this.cachedSelectList != null) {
               if (!this.cachedSelectList.equals(that.cachedSelectList)) {
                  return false;
               }
            } else if (that.cachedSelectList != null) {
               return false;
            }

            if (this.dbType != that.dbType) {
               return false;
            } else {
               return this.hints != null ? this.hints.equals(that.hints) : that.hints == null;
            }
         }
      } else {
         return false;
      }
   }

   public int hashCode() {
      int result = this.distionOption;
      result = 31 * result + (this.selectList != null ? this.selectList.hashCode() : 0);
      result = 31 * result + (this.from != null ? this.from.hashCode() : 0);
      result = 31 * result + (this.into != null ? this.into.hashCode() : 0);
      result = 31 * result + (this.where != null ? this.where.hashCode() : 0);
      result = 31 * result + (this.startWith != null ? this.startWith.hashCode() : 0);
      result = 31 * result + (this.connectBy != null ? this.connectBy.hashCode() : 0);
      result = 31 * result + (this.prior ? 1 : 0);
      result = 31 * result + (this.noCycle ? 1 : 0);
      result = 31 * result + (this.orderBySiblings != null ? this.orderBySiblings.hashCode() : 0);
      result = 31 * result + (this.groupBy != null ? this.groupBy.hashCode() : 0);
      result = 31 * result + (this.orderBy != null ? this.orderBy.hashCode() : 0);
      result = 31 * result + (this.parenthesized ? 1 : 0);
      result = 31 * result + (this.forUpdate ? 1 : 0);
      result = 31 * result + (this.noWait ? 1 : 0);
      result = 31 * result + (this.waitTime != null ? this.waitTime.hashCode() : 0);
      result = 31 * result + (this.limit != null ? this.limit.hashCode() : 0);
      result = 31 * result + (this.forUpdateOf != null ? this.forUpdateOf.hashCode() : 0);
      result = 31 * result + (this.distributeBy != null ? this.distributeBy.hashCode() : 0);
      result = 31 * result + (this.sortBy != null ? this.sortBy.hashCode() : 0);
      result = 31 * result + (this.cachedSelectList != null ? this.cachedSelectList.hashCode() : 0);
      result = 31 * result + (int)(this.cachedSelectListHash ^ this.cachedSelectListHash >>> 32);
      result = 31 * result + (this.dbType != null ? this.dbType.hashCode() : 0);
      result = 31 * result + (this.hints != null ? this.hints.hashCode() : 0);
      return result;
   }

   public boolean equalsForMergeJoin(SQLSelectQueryBlock that) {
      if (this == that) {
         return true;
      } else if (that == null) {
         return false;
      } else if (this.into == null && this.limit == null && this.groupBy == null) {
         if (this.distionOption != that.distionOption) {
            return false;
         } else if (this.prior != that.prior) {
            return false;
         } else if (this.noCycle != that.noCycle) {
            return false;
         } else if (this.parenthesized != that.parenthesized) {
            return false;
         } else if (this.forUpdate != that.forUpdate) {
            return false;
         } else if (this.noWait != that.noWait) {
            return false;
         } else if (this.cachedSelectListHash != that.cachedSelectListHash) {
            return false;
         } else {
            if (this.selectList != null) {
               if (!this.selectList.equals(that.selectList)) {
                  return false;
               }
            } else if (that.selectList != null) {
               return false;
            }

            if (this.from != null) {
               if (!this.from.equals(that.from)) {
                  return false;
               }
            } else if (that.from != null) {
               return false;
            }

            if (this.into != null) {
               if (!this.into.equals(that.into)) {
                  return false;
               }
            } else if (that.into != null) {
               return false;
            }

            if (this.startWith != null) {
               if (!this.startWith.equals(that.startWith)) {
                  return false;
               }
            } else if (that.startWith != null) {
               return false;
            }

            if (this.connectBy != null) {
               if (!this.connectBy.equals(that.connectBy)) {
                  return false;
               }
            } else if (that.connectBy != null) {
               return false;
            }

            if (this.orderBySiblings != null) {
               if (!this.orderBySiblings.equals(that.orderBySiblings)) {
                  return false;
               }
            } else if (that.orderBySiblings != null) {
               return false;
            }

            if (this.groupBy != null) {
               if (!this.groupBy.equals(that.groupBy)) {
                  return false;
               }
            } else if (that.groupBy != null) {
               return false;
            }

            if (this.orderBy != null) {
               if (!this.orderBy.equals(that.orderBy)) {
                  return false;
               }
            } else if (that.orderBy != null) {
               return false;
            }

            if (this.waitTime != null) {
               if (!this.waitTime.equals(that.waitTime)) {
                  return false;
               }
            } else if (that.waitTime != null) {
               return false;
            }

            if (this.limit != null) {
               if (!this.limit.equals(that.limit)) {
                  return false;
               }
            } else if (that.limit != null) {
               return false;
            }

            if (this.forUpdateOf != null) {
               if (!this.forUpdateOf.equals(that.forUpdateOf)) {
                  return false;
               }
            } else if (that.forUpdateOf != null) {
               return false;
            }

            if (this.distributeBy != null) {
               if (!this.distributeBy.equals(that.distributeBy)) {
                  return false;
               }
            } else if (that.distributeBy != null) {
               return false;
            }

            if (this.sortBy != null) {
               if (!this.sortBy.equals(that.sortBy)) {
                  return false;
               }
            } else if (that.sortBy != null) {
               return false;
            }

            if (this.cachedSelectList != null) {
               if (!this.cachedSelectList.equals(that.cachedSelectList)) {
                  return false;
               }
            } else if (that.cachedSelectList != null) {
               return false;
            }

            return this.dbType == that.dbType;
         }
      } else {
         return false;
      }
   }

   public SQLSelectQueryBlock clone() {
      SQLSelectQueryBlock x = new SQLSelectQueryBlock(this.dbType);
      this.cloneTo(x);
      return x;
   }

   public List<SQLExpr> getForUpdateOf() {
      if (this.forUpdateOf == null) {
         this.forUpdateOf = new ArrayList(1);
      }

      return this.forUpdateOf;
   }

   public int getForUpdateOfSize() {
      return this.forUpdateOf == null ? 0 : this.forUpdateOf.size();
   }

   public void cloneSelectListTo(SQLSelectQueryBlock x) {
      x.distionOption = this.distionOption;

      for(SQLSelectItem item : this.selectList) {
         SQLSelectItem item2 = item.clone();
         item2.setParent(x);
         x.selectList.add(item2);
      }

   }

   public void cloneTo(SQLSelectQueryBlock x) {
      x.parenthesized = this.parenthesized;
      x.distionOption = this.distionOption;
      if (x.selectList.size() > 0) {
         x.selectList.clear();
      }

      if (this.hints != null) {
         for(SQLCommentHint hint : this.hints) {
            SQLCommentHint hint1 = hint.clone();
            hint1.setParent(x);
            x.getHints().add(hint1);
         }
      }

      for(SQLSelectItem item : this.selectList) {
         x.addSelectItem(item.clone());
      }

      if (this.from != null) {
         x.setFrom(this.from.clone());
      }

      if (this.into != null) {
         x.setInto(this.into.clone());
      }

      if (this.where != null) {
         x.setWhere(this.where.clone());
      }

      if (this.startWith != null) {
         x.setStartWith(this.startWith.clone());
      }

      if (this.connectBy != null) {
         x.setConnectBy(this.connectBy.clone());
      }

      x.prior = this.prior;
      x.noCycle = this.noCycle;
      if (this.orderBySiblings != null) {
         x.setOrderBySiblings(this.orderBySiblings.clone());
      }

      if (this.groupBy != null) {
         x.setGroupBy(this.groupBy.clone());
      }

      if (this.orderBy != null) {
         x.setOrderBy(this.orderBy.clone());
      }

      if (this.distributeBy != null) {
         if (x.distributeBy == null) {
            x.distributeBy = new ArrayList();
         }

         for(int i = 0; i < this.distributeBy.size(); ++i) {
            SQLSelectOrderByItem item = ((SQLSelectOrderByItem)this.distributeBy.get(i)).clone();
            item.setParent(x);
            x.distributeBy.add(item);
         }
      }

      if (this.sortBy != null) {
         if (x.sortBy == null) {
            x.sortBy = new ArrayList();
         }

         for(int i = 0; i < this.sortBy.size(); ++i) {
            SQLSelectOrderByItem item = ((SQLSelectOrderByItem)this.sortBy.get(i)).clone();
            item.setParent(x);
            x.sortBy.add(item);
         }
      }

      if (this.clusterBy != null) {
         if (x.clusterBy == null) {
            x.clusterBy = new ArrayList();
         }

         for(int i = 0; i < this.clusterBy.size(); ++i) {
            SQLSelectOrderByItem item = ((SQLSelectOrderByItem)this.clusterBy.get(i)).clone();
            item.setParent(x);
            x.clusterBy.add(item);
         }
      }

      x.parenthesized = this.parenthesized;
      x.forUpdate = this.forUpdate;
      x.noWait = this.noWait;
      x.skipLocked = this.skipLocked;
      x.forShare = this.forShare;
      if (this.waitTime != null) {
         x.setWaitTime(this.waitTime.clone());
      }

      if (this.limit != null) {
         x.setLimit(this.limit.clone());
      }

   }

   public boolean isBracket() {
      return this.parenthesized;
   }

   public void setBracket(boolean bracket) {
      this.parenthesized = bracket;
   }

   public SQLTableSource findTableSource(String alias) {
      return this.from == null ? null : this.from.findTableSource(alias);
   }

   public SQLTableSource findTableSourceWithColumn(String column) {
      return this.from == null ? null : this.from.findTableSourceWithColumn(column);
   }

   public SQLTableSource findTableSourceWithColumn(long columnHash) {
      if (this.from == null) {
         return null;
      } else {
         SQLTableSource tableSource = this.from.findTableSourceWithColumn(columnHash);
         if (tableSource == null && this.from instanceof SQLExprTableSource) {
            SQLSelectItem selectItem = this.findSelectItem(columnHash);
            if (selectItem != null && selectItem.getExpr() instanceof SQLName && ((SQLName)selectItem.getExpr()).nameHashCode64() == columnHash) {
               tableSource = this.from;
            }
         }

         return tableSource;
      }
   }

   public boolean replace(SQLExpr expr, SQLExpr target) {
      if (this.where == expr) {
         this.setWhere(target);
         return true;
      } else if (this.startWith == expr) {
         this.setStartWith(target);
         return true;
      } else if (this.connectBy == expr) {
         this.setConnectBy(target);
         return true;
      } else {
         return false;
      }
   }

   public SQLSelectItem findSelectItem(String ident) {
      if (ident == null) {
         return null;
      } else {
         long hash = FnvHash.hashCode64(ident);
         return this.findSelectItem(hash);
      }
   }

   public SQLSelectItem findSelectItem(long identHash) {
      for(SQLSelectItem item : this.selectList) {
         if (item.match(identHash)) {
            return item;
         }
      }

      if (this.from == null) {
         return null;
      } else {
         if (this.selectList.size() == 1 && ((SQLSelectItem)this.selectList.get(0)).getExpr() instanceof SQLAllColumnExpr) {
            SQLTableSource matchedTableSource = this.from.findTableSourceWithColumn(identHash);
            if (matchedTableSource != null) {
               return (SQLSelectItem)this.selectList.get(0);
            }
         }

         SQLSelectItem ownerAllItem = null;

         for(SQLSelectItem item : this.selectList) {
            SQLExpr itemExpr = item.getExpr();
            if (itemExpr instanceof SQLPropertyExpr && ((SQLPropertyExpr)itemExpr).getName().equals("*")) {
               if (ownerAllItem != null) {
                  return null;
               }

               ownerAllItem = item;
            }
         }

         if (ownerAllItem != null) {
            return ownerAllItem;
         } else {
            return null;
         }
      }
   }

   public boolean selectItemHasAllColumn() {
      return this.selectItemHasAllColumn(true);
   }

   public boolean selectItemHasAllColumn(boolean recursive) {
      for(SQLSelectItem item : this.selectList) {
         SQLExpr expr = item.getExpr();
         boolean allColumn = expr instanceof SQLAllColumnExpr || expr instanceof SQLPropertyExpr && ((SQLPropertyExpr)expr).getName().equals("*");
         if (allColumn) {
            if (recursive && this.from instanceof SQLSubqueryTableSource) {
               SQLSelect subSelect = ((SQLSubqueryTableSource)this.from).select;
               SQLSelectQueryBlock queryBlock = subSelect.getQueryBlock();
               if (queryBlock != null) {
                  return queryBlock.selectItemHasAllColumn();
               }
            }

            return true;
         }
      }

      return false;
   }

   public SQLSelectItem findAllColumnSelectItem() {
      SQLSelectItem allColumnItem = null;

      for(SQLSelectItem item : this.selectList) {
         SQLExpr expr = item.getExpr();
         if (!(expr instanceof SQLAllColumnExpr) && (!(expr instanceof SQLPropertyExpr) || !((SQLPropertyExpr)expr).getName().equals("*"))) {
            boolean var6 = false;
         } else {
            boolean var10000 = true;
         }

         if (allColumnItem != null) {
            return null;
         }

         allColumnItem = item;
      }

      return allColumnItem;
   }

   public SQLColumnDefinition findColumn(String columnName) {
      if (this.from == null) {
         return null;
      } else {
         long hash = FnvHash.hashCode64(columnName);
         return this.from.findColumn(hash);
      }
   }

   public SQLColumnDefinition findColumn(long columnNameHash) {
      SQLObject object = this.resolveColum(columnNameHash);
      if (object instanceof SQLColumnDefinition) {
         return (SQLColumnDefinition)object;
      } else {
         if (object instanceof SQLSelectItem) {
            SQLExpr expr = ((SQLSelectItem)object).getExpr();
            if (expr instanceof SQLName) {
               return ((SQLName)expr).getResolvedColumn();
            }
         }

         return null;
      }
   }

   public SQLObject resolveColum(long columnNameHash) {
      SQLSelectItem selectItem = this.findSelectItem(columnNameHash);
      if (selectItem != null) {
         SQLExpr selectItemExpr = selectItem.getExpr();
         if (selectItemExpr instanceof SQLAllColumnExpr) {
            SQLObject resolveColumn = this.from.resolveColum(columnNameHash);
            if (resolveColumn != null) {
               return resolveColumn;
            }
         } else if (selectItemExpr instanceof SQLPropertyExpr && ((SQLPropertyExpr)selectItemExpr).getName().equals("*")) {
            SQLTableSource resolvedTableSource = ((SQLPropertyExpr)selectItemExpr).getResolvedTableSource();
            if (resolvedTableSource instanceof SQLSubqueryTableSource) {
               SQLObject resolveColumn = resolvedTableSource.resolveColum(columnNameHash);
               if (resolveColumn != null) {
                  return resolveColumn;
               }
            }
         }

         return selectItem;
      } else {
         return this.from != null ? this.from.resolveColum(columnNameHash) : null;
      }
   }

   public void addCondition(String conditionSql) {
      if (conditionSql != null && conditionSql.length() != 0) {
         SQLExpr condition = SQLUtils.toSQLExpr(conditionSql, this.dbType);
         this.addCondition(condition);
      }
   }

   public void addCondition(SQLExpr expr) {
      if (expr != null) {
         this.setWhere(SQLBinaryOpExpr.and(this.where, expr));
      }
   }

   public boolean removeCondition(String conditionSql) {
      if (conditionSql != null && conditionSql.length() != 0) {
         SQLExpr condition = SQLUtils.toSQLExpr(conditionSql, this.dbType);
         return this.removeCondition(condition);
      } else {
         return false;
      }
   }

   public boolean removeCondition(SQLExpr condition) {
      if (condition == null) {
         return false;
      } else if (this.where instanceof SQLBinaryOpExprGroup) {
         SQLBinaryOpExprGroup group = (SQLBinaryOpExprGroup)this.where;
         int removedCount = 0;
         List<SQLExpr> items = group.getItems();

         for(int i = items.size() - 1; i >= 0; --i) {
            SQLExpr item = (SQLExpr)items.get(i);
            if (item.equals(condition)) {
               items.remove(i);
               ++removedCount;
            }
         }

         if (items.size() == 0) {
            this.where = null;
         }

         return removedCount > 0;
      } else {
         if (this.where instanceof SQLBinaryOpExpr) {
            SQLBinaryOpExpr binaryOpWhere = (SQLBinaryOpExpr)this.where;
            SQLBinaryOperator operator = binaryOpWhere.getOperator();
            if (operator == SQLBinaryOperator.BooleanAnd || operator == SQLBinaryOperator.BooleanOr) {
               List<SQLExpr> items = SQLBinaryOpExpr.split(binaryOpWhere);
               int removedCount = 0;

               for(int i = items.size() - 1; i >= 0; --i) {
                  SQLExpr item = (SQLExpr)items.get(i);
                  if (item.equals(condition) && SQLUtils.replaceInParent((SQLExpr)item, (SQLExpr)null)) {
                     ++removedCount;
                  }
               }

               return removedCount > 0;
            }
         }

         if (condition.equals(this.where)) {
            this.where = null;
            return true;
         } else {
            return false;
         }
      }
   }

   public void limit(int rowCount, int offset) {
      SQLLimit limit = new SQLLimit();
      limit.setRowCount(new SQLIntegerExpr(rowCount));
      if (offset > 0) {
         limit.setOffset(new SQLIntegerExpr(offset));
      }

      this.setLimit(limit);
   }

   public String getCachedSelectList() {
      return this.cachedSelectList;
   }

   public void setCachedSelectList(String cachedSelectList, long cachedSelectListHash) {
      this.cachedSelectList = cachedSelectList;
      this.cachedSelectListHash = cachedSelectListHash;
   }

   public long getCachedSelectListHash() {
      return this.cachedSelectListHash;
   }

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

   public void setDbType(DbType dbType) {
      this.dbType = dbType;
   }

   public List<SQLCommentHint> getHintsDirect() {
      return this.hints;
   }

   public List<SQLCommentHint> getHints() {
      if (this.hints == null) {
         this.hints = new ArrayList(2);
      }

      return this.hints;
   }

   public void setHints(List<SQLCommentHint> hints) {
      this.hints = hints;
   }

   public int getHintsSize() {
      return this.hints == null ? 0 : this.hints.size();
   }

   public boolean replaceInParent(SQLSelectQuery x) {
      if (this.parent instanceof SQLSelect) {
         ((SQLSelect)this.parent).setQuery(x);
         return true;
      } else if (this.parent instanceof SQLUnionQuery) {
         SQLUnionQuery union = (SQLUnionQuery)this.parent;
         return union.replace(this, x);
      } else {
         return false;
      }
   }

   public List<SQLSelectOrderByItem> getClusterBy() {
      if (this.clusterBy == null) {
         this.clusterBy = new ArrayList();
      }

      return this.clusterBy;
   }

   public List<SQLSelectOrderByItem> getClusterByDirect() {
      return this.clusterBy;
   }

   public void addClusterBy(SQLSelectOrderByItem item) {
      if (this.clusterBy == null) {
         this.clusterBy = new ArrayList();
      }

      if (item != null) {
         item.setParent(this);
      }

      this.clusterBy.add(item);
   }

   public List<String> computeSelecteListAlias() {
      List<String> aliasList = new ArrayList();

      for(SQLSelectItem item : this.selectList) {
         SQLExpr expr = item.getExpr();
         if (!(expr instanceof SQLAllColumnExpr) && (!(expr instanceof SQLPropertyExpr) || !((SQLPropertyExpr)expr).getName().equals("*"))) {
            aliasList.add(item.computeAlias());
         }
      }

      return aliasList;
   }

   public List<SQLTableSource> getMappJoinTableSources() {
      if (this.hints == null) {
         return Collections.emptyList();
      } else {
         List<SQLTableSource> tableSources = null;

         for(SQLCommentHint hint : this.hints) {
            String text = hint.getText();
            if (text.startsWith("+")) {
               SQLExpr hintExpr = SQLUtils.toSQLExpr(text.substring(1), this.dbType);
               if (hintExpr instanceof SQLMethodInvokeExpr) {
                  SQLMethodInvokeExpr func = (SQLMethodInvokeExpr)hintExpr;
                  if (func.methodNameHashCode64() == FnvHash.Constants.MAPJOIN) {
                     for(SQLExpr arg : func.getArguments()) {
                        SQLIdentifierExpr tablename = (SQLIdentifierExpr)arg;
                        SQLTableSource tableSource = this.findTableSource(tablename.getName());
                        if (tableSources == null) {
                           tableSources = new ArrayList(2);
                        }

                        tableSources.add(tableSource);
                     }
                  }
               }
            }
         }

         if (tableSources == null) {
            return Collections.emptyList();
         } else {
            return tableSources;
         }
      }
   }

   public boolean clearMapJoinHint() {
      if (this.hints == null) {
         return false;
      } else {
         int removeCount = 0;

         for(int i = this.hints.size() - 1; i >= 0; --i) {
            SQLCommentHint hint = (SQLCommentHint)this.hints.get(i);
            String text = hint.getText();
            if (text.startsWith("+")) {
               SQLExpr hintExpr = SQLUtils.toSQLExpr(text.substring(1), this.dbType);
               if (hintExpr instanceof SQLMethodInvokeExpr) {
                  SQLMethodInvokeExpr func = (SQLMethodInvokeExpr)hintExpr;
                  if (func.methodNameHashCode64() == FnvHash.Constants.MAPJOIN) {
                     this.hints.remove(i);
                     ++removeCount;
                  }
               }
            }
         }

         return removeCount > 0;
      }
   }

   private static class AggregationStatVisitor extends SQLASTVisitorAdapter {
      private boolean aggregation;

      private AggregationStatVisitor() {
         this.aggregation = false;
      }

      public boolean visit(SQLAggregateExpr x) {
         this.aggregation = true;
         return false;
      }
   }
}
