package com.chenyang.druid.sql.dialect.db2.parser;

import com.chenyang.druid.DbType;
import com.chenyang.druid.sql.ast.SQLDataType;
import com.chenyang.druid.sql.ast.SQLExpr;
import com.chenyang.druid.sql.ast.SQLName;
import com.chenyang.druid.sql.ast.expr.SQLBinaryOpExpr;
import com.chenyang.druid.sql.ast.expr.SQLListExpr;
import com.chenyang.druid.sql.ast.statement.SQLConstraint;
import com.chenyang.druid.sql.ast.statement.SQLSelect;
import com.chenyang.druid.sql.ast.statement.SQLTableElement;
import com.chenyang.druid.sql.ast.statement.SQLUpdateSetItem;
import com.chenyang.druid.sql.dialect.db2.ast.DB2SQLPartition;
import com.chenyang.druid.sql.dialect.db2.ast.DB2SQLPartitionBy;
import com.chenyang.druid.sql.dialect.db2.ast.expr.DB2OptionValue;
import com.chenyang.druid.sql.dialect.db2.ast.expr.constraint.DB2Constraint;
import com.chenyang.druid.sql.dialect.db2.ast.expr.constraint.DB2StorageParameter;
import com.chenyang.druid.sql.dialect.db2.ast.expr.tablesource.DB2ExprTableSource;
import com.chenyang.druid.sql.dialect.db2.ast.stmt.DB2CreateTableStatement;
import com.chenyang.druid.sql.dialect.db2.ast.stmt.alterTable.DB2ColumnDefinition;
import com.chenyang.druid.sql.parser.Lexer;
import com.chenyang.druid.sql.parser.SQLCreateTableParser;
import com.chenyang.druid.sql.parser.SQLExprParser;
import com.chenyang.druid.sql.parser.SQLSelectParser;
import com.chenyang.druid.sql.parser.Token;
import com.chenyang.druid.util.FnvHash;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

public class DB2CreateTableParser extends SQLCreateTableParser {
   public DB2CreateTableParser(Lexer lexer) {
      super((SQLExprParser)(new DB2ExprParser(lexer)));
   }

   public DB2CreateTableParser(String sql) {
      super((SQLExprParser)(new DB2ExprParser(sql)));
   }

   public DB2CreateTableParser(SQLExprParser exprParser) {
      super(exprParser);
   }

   public DB2SQLPartitionBy parsePartitionBy2() {
      this.lexer.nextToken();
      this.accept(Token.BY);
      DB2SQLPartitionBy partitionBy1 = new DB2SQLPartitionBy();
      partitionBy1.setPartitionType(this.exprParser.name());
      this.accept(Token.LPAREN);
      partitionBy1.setColumn(this.exprParser.name());
      this.accept(Token.RPAREN);
      boolean useTemplate = false;
      boolean hasSubpartition = false;

      while(this.lexer.identifierEquals("SUBPARTITION")) {
         hasSubpartition = true;
         this.lexer.nextToken();
         this.accept(Token.BY);
         DB2SQLPartitionBy subPartitionBy = new DB2SQLPartitionBy();
         subPartitionBy.setIfSubPartitionBy(true);
         subPartitionBy.setPartitionType(this.exprParser.name());
         this.accept(Token.LPAREN);
         subPartitionBy.setColumn(this.exprParser.name());
         this.accept(Token.RPAREN);
         partitionBy1.getSubPartitionByList().add(subPartitionBy);
         if (this.lexer.identifierEquals("SUBPARTITION")) {
            Lexer.SavePoint mark = this.lexer.mark();
            this.lexer.nextToken();
            if (this.lexer.token() == Token.BY) {
               this.lexer.reset(mark);
               continue;
            }

            if (this.lexer.identifierEquals("TEMPLATE")) {
               useTemplate = true;
               subPartitionBy.setIfUseTemplate(true);
               this.lexer.nextToken();
               this.accept(Token.LPAREN);
               subPartitionBy.setPartitionList(this.parsePartitionSpec());
               this.accept(Token.RPAREN);
               continue;
            }
         }

         if (!this.lexer.identifierEquals("SUBPARTITION")) {
            break;
         }
      }

      if (!useTemplate && hasSubpartition) {
         int level = 0;

         do {
            if (this.lexer.token() == Token.LPAREN) {
               ++level;
               this.lexer.nextToken();
            }

            ArrayList<DB2SQLPartition> partition = this.parsePartitionSpec();
            if (level == 1) {
               partitionBy1.getPartitionList().addAll(partition);
            } else {
               ((DB2SQLPartitionBy)partitionBy1.getSubPartitionByList().get(level - 2)).getPartitionList().addAll(partition);
            }

            while(this.lexer.token() == Token.RPAREN) {
               --level;
               this.lexer.nextToken();
            }

            if (this.lexer.token() == Token.COMMA) {
               this.lexer.nextToken();
            }
         } while(level != 0);

         for(DB2SQLPartitionBy partitionBy : partitionBy1.getSubPartitionByList()) {
            HashSet<String> strings = new HashSet();
            ArrayList<DB2SQLPartition> partitionList = partitionBy.getPartitionList();
            Iterator<DB2SQLPartition> iterator = partitionList.iterator();

            while(iterator.hasNext()) {
               DB2SQLPartition next = (DB2SQLPartition)iterator.next();
               String s = next.getName().toString();
               if (strings.contains(s)) {
                  iterator.remove();
               } else {
                  strings.add(s);
               }
            }
         }
      } else if (this.lexer.token() == Token.LPAREN) {
         this.lexer.nextToken();
         ArrayList<DB2SQLPartition> specList = this.parsePartitionSpec();

         for(DB2SQLPartition spec : specList) {
            spec.setSubPartition(false);
         }

         this.lexer.nextToken();
         partitionBy1.setPartitionList(specList);
      }

      return partitionBy1;
   }

   public DB2CreateTableStatement parseCreateTable() {
      List<String> comments = null;
      if (this.lexer.isKeepComments() && this.lexer.hasComment()) {
         comments = this.lexer.readAndResetComments();
      }

      DB2CreateTableStatement stmt = this.parseCreateTable(true);
      if (comments != null) {
         stmt.addBeforeComment(comments);
      }

      return stmt;
   }

   public DB2CreateTableStatement parseCreateTable(boolean acceptCreate) {
      DB2CreateTableStatement createTable = new DB2CreateTableStatement();
      createTable.setDbType(DbType.db2);
      if (acceptCreate) {
         if (this.lexer.hasComment() && this.lexer.isKeepComments()) {
            createTable.addBeforeComment(this.lexer.readAndResetComments());
         }

         this.accept(Token.CREATE);
      }

      boolean isGlobal = false;
      boolean isLocal = false;
      if (this.lexer.token() != Token.GLOBAL && !this.lexer.identifierEquals("GLOBAL")) {
         if (this.lexer.token() == Token.LOCAL || this.lexer.identifierEquals("LOCAL")) {
            this.lexer.nextToken();
            isLocal = true;
         }
      } else {
         this.lexer.nextToken();
         isGlobal = true;
      }

      if (this.lexer.token() != Token.TEMPORARY && !this.lexer.identifierEquals("TEMPORARY")) {
         if (this.lexer.token() == Token.TEMP || this.lexer.identifierEquals("TEMP")) {
            this.lexer.nextToken();
            if (isGlobal) {
               createTable.setTableType(DB2CreateTableStatement.TableType.GLOBAL_TEMP);
            } else if (isLocal) {
               createTable.setTableType(DB2CreateTableStatement.TableType.LOCAL_TEMP);
            } else {
               createTable.setTableType(DB2CreateTableStatement.TableType.TEMP);
            }
         }
      } else {
         this.lexer.nextToken();
         if (isGlobal) {
            createTable.setTableType(DB2CreateTableStatement.TableType.GLOBAL_TEMPORARY);
         } else if (isLocal) {
            createTable.setTableType(DB2CreateTableStatement.TableType.LOCAL_TEMPORARY);
         } else {
            createTable.setTableType(DB2CreateTableStatement.TableType.TEMPORARY);
         }
      }

      if (this.lexer.token() == Token.UNLOGGED || this.lexer.identifierEquals("UNLOGGED")) {
         this.lexer.nextToken();
         if (isGlobal) {
            createTable.setTableType(DB2CreateTableStatement.TableType.GLOBAL_UNLOGGED);
         } else if (isLocal) {
            createTable.setTableType(DB2CreateTableStatement.TableType.LOCAL_UNLOGGED);
         } else {
            createTable.setTableType(DB2CreateTableStatement.TableType.UNLOGGED);
         }
      }

      this.accept(Token.TABLE);
      if (this.lexer.token() == Token.IF || this.lexer.identifierEquals("IF")) {
         this.lexer.nextToken();
         this.accept(Token.NOT);
         this.accept(Token.EXISTS);
         createTable.setIfNotExiists(true);
      }

      DB2ExprTableSource tableSource = new DB2ExprTableSource();
      SQLName name = this.exprParser.name();
      tableSource.setExpr(name);
      createTable.setTableSource(tableSource);
      createTable.setName(name);
      if (this.lexer.token() == Token.OF) {
         this.lexer.nextToken();
         createTable.setOfType(this.exprParser.name());
      }

      if (this.lexer.token() == Token.LPAREN) {
         this.lexer.nextToken();

         do {
            if (this.lexer.token() == Token.CONSTRAINT) {
               SQLConstraint constraint = this.exprParser.parseConstaint();
               constraint.setParent(createTable);
               createTable.getTableElementList().add((SQLTableElement)constraint);
            } else {
               DB2ColumnDefinition DB2ColumnDefinition = this.parseColumnDefinition();
               DB2ColumnDefinition.setParent(createTable);
               createTable.getTableElementList().add(DB2ColumnDefinition);
            }

            if (this.lexer.token() != Token.COMMA) {
               break;
            }

            this.lexer.nextToken();
         } while(this.lexer.token() != Token.RPAREN);

         this.accept(Token.RPAREN);
         if (this.lexer.identifierEquals(FnvHash.Constants.INHERITS)) {
            this.lexer.nextToken();
            this.accept(Token.LPAREN);

            while(this.lexer.token() != Token.RPAREN) {
               SQLName inherits = this.exprParser.name();
               createTable.addParentTable(inherits);
               if (this.lexer.token() == Token.COMMA) {
                  this.lexer.nextToken();
               }
            }

            this.accept(Token.RPAREN);
            this.lexer.nextToken();
         }
      }

      if (this.lexer.token() == Token.WITH) {
         this.lexer.nextToken();
         this.accept(Token.LPAREN);
         this.parseAssignItems(createTable.getTableOptions(), createTable, false);
         this.accept(Token.RPAREN);
      }

      if (this.lexer.token() == Token.ON) {
         this.lexer.nextToken();
         if (this.lexer.token() == Token.COMMIT || this.lexer.identifierEquals("COMMIT")) {
            this.lexer.nextToken();
            if (this.lexer.token() == Token.DELETE) {
               this.lexer.nextToken();
               this.lexer.nextToken();
               createTable.setOnCommit(DB2CreateTableStatement.OnCommit.deleteRows);
            } else if (this.lexer.token() == Token.DROP) {
               this.lexer.nextToken();
               createTable.setOnCommit(DB2CreateTableStatement.OnCommit.drop);
            } else if (this.lexer.identifierEquals("PRESERVE")) {
               this.lexer.nextToken();
               this.lexer.nextToken();
               createTable.setOnCommit(DB2CreateTableStatement.OnCommit.preserveRows);
            }
         }
      }

      if (this.lexer.token() == Token.TABLESPACE) {
         this.lexer.nextToken();
         createTable.setTablespace(this.exprParser.name());
      }

      if (this.lexer.token() == Token.AS) {
         createTable.setAs(true);
         this.lexer.nextToken();
         SQLSelectParser sqlSelectParser = this.createSQLSelectParser();
         SQLSelect select = sqlSelectParser.select();
         createTable.setSelect(select);
         if (this.lexer.token() == Token.WITH) {
            this.lexer.nextToken();
            if (this.lexer.identifierEquals("NO")) {
               this.lexer.nextToken();
               createTable.setWithNoData(true);
            } else {
               createTable.setWithNoData(false);
            }

            if (this.lexer.identifierEquals("DATA")) {
               if (!createTable.getWithNoData()) {
                  createTable.setWithData(true);
               }

               this.lexer.nextToken();
            }
         }
      }

      if (this.lexer.identifierEquals("DISTRIBUTED")) {
         this.lexer.nextToken();
         createTable.setDistributedBy(this.parseDistributedBy());
      }

      if (this.lexer.token() == Token.PARTITION) {
         DB2SQLPartitionBy partitionBy = this.parsePartitionBy2();
         createTable.setPartitioning(partitionBy);
      }

      if (this.lexer.identifierEquals("DISTRIBUTE")) {
         this.lexer.nextToken();
         this.accept(Token.ON);
         createTable.setDistributeOn(true);
         if (this.lexer.identifierEquals("RANDOM")) {
            createTable.setRandom(true);
            this.lexer.nextToken();
         } else if (this.lexer.identifierEquals("HASH")) {
            createTable.setHash(true);
            this.lexer.nextToken();
            this.accept(Token.LPAREN);
            SQLListExpr listExpr = new SQLListExpr();
            this.exprParser.exprList(listExpr.getItems(), listExpr);
            createTable.setHashCols(listExpr);
            this.accept(Token.RPAREN);
         }
      }

      if (this.lexer.identifierEquals("ORGANIZE")) {
         this.lexer.nextToken();
         this.accept(Token.ON);
         createTable.setOrganizeOn(true);
         if (this.lexer.identifierEquals("NONE")) {
            createTable.setNone(true);
            this.lexer.nextToken();
         } else {
            this.accept(Token.LPAREN);
            createTable.setOrganizeOnCol(this.exprParser.name());
            this.accept(Token.RPAREN);
         }
      }

      if (this.lexer.token() == Token.ROW) {
         this.lexer.nextToken();
         if (this.lexer.identifierEquals("SECURITY")) {
            createTable.setRowSecurity(true);
            this.lexer.nextToken();
         }
      }

      if (this.lexer.identifierEquals("DATA_VERSION_RETENTION_TIME")) {
         createTable.setDataVersionRetentionTime(true);
         this.lexer.nextToken();
         createTable.setDataVersionRetentionTimeNum(this.lexer.numberExpr());
         this.lexer.nextToken();
      }

      return createTable;
   }

   private DB2ColumnDefinition parseColumnDefinition() {
      String TABLE_CONSTRAINT_FLAG = "table-constraint";
      DB2ColumnDefinition definition = new DB2ColumnDefinition();
      definition.setName(this.exprParser.name());
      if (this.lexer.token() != Token.WITH && !this.lexer.identifierEquals("WITH")) {
         SQLDataType sqlDataType = this.exprParser.parseDataType();
         definition.setDataType(sqlDataType);
         if (this.lexer.identifierEquals("COLLATE")) {
            this.lexer.nextToken();
            definition.setCollateExpr(this.exprParser.expr());
         }
      } else {
         this.lexer.nextToken();
         this.lexer.nextToken();
         definition.setWith(true);
      }

      DB2Constraint constraint = null;

      do {
         constraint = this.parseColConstraint();
         if (constraint != null) {
            Object attribute = constraint.getAttribute("table-constraint");
            if (attribute != null && (Boolean)attribute) {
               definition.setTableConstraint(constraint);
               break;
            }

            definition.addConstraint(constraint);
         }
      } while(constraint != null);

      if (this.lexer.identifierEquals("ENCODING")) {
         this.lexer.nextToken();
         this.accept(Token.LPAREN);
         SQLExpr encoding = null;

         do {
            SQLExpr tbConstraint = this.parseOpionValue(true);
            if (tbConstraint != null) {
               definition.addEncoding(tbConstraint);
            }

            if (this.lexer.token() == Token.COMMA) {
               this.lexer.nextToken();
            }
         } while(this.lexer.token() != Token.RPAREN);

         this.lexer.nextToken();
      }

      DB2Constraint tbConstraint = this.parseColConstraint();
      if (tbConstraint != null) {
         definition.setTableConstraint(tbConstraint);
      }

      if (this.lexer.token() == Token.LIKE || this.lexer.identifierEquals("LIKE")) {
         this.lexer.nextToken();
         DB2ExprTableSource ts = new DB2ExprTableSource();
         ts.setExpr(this.exprParser.expr());
         definition.setLike(ts);
         DB2ColumnDefinition.ColumnLikeOption option = null;

         do {
            option = this.parseLikeOption();
            if (option != null) {
               definition.addOption(option);
            }
         } while(option != null);
      }

      DB2Constraint.DB2Reference reference = null;

      do {
         reference = this.parseReference();
         if (reference != null) {
            definition.addColumnReferenceStorageDirective(reference);
         }
      } while(reference != null);

      return definition;
   }

   private DB2Constraint parseColConstraint() {
      String TABLE_CONSTRAINT_FLAG = "table-constraint";
      boolean flag = false;
      DB2Constraint c = new DB2Constraint();
      if (this.lexer.token() == Token.CONSTRAINT) {
         this.lexer.nextToken();
         c.setName(this.exprParser.name());
         flag = true;
      }

      if (this.lexer.token() != Token.NOT && !this.lexer.identifierEquals("NOT")) {
         if (this.lexer.token() != Token.NULL && !this.lexer.identifierEquals("NULL")) {
            if (this.lexer.token() == Token.CHECK) {
               this.lexer.nextToken();
               this.accept(Token.LPAREN);
               DB2Constraint.DB2ConstraintCheck check = new DB2Constraint.DB2ConstraintCheck();
               check.setExpr(this.exprParser.expr());
               c.setCheck(check);
               this.accept(Token.RPAREN);
               if (this.lexer.identifierEquals("NO")) {
                  check.setNoInherit(true);
                  this.lexer.nextToken();
                  this.lexer.nextToken();
               }

               flag = true;
            } else if (this.lexer.token() != Token.DEFAULT && !this.lexer.identifierEquals("DEFAULT")) {
               if (this.lexer.token() != Token.UNIQUE && !this.lexer.identifierEquals("UNIQUE")) {
                  if (this.lexer.token() != Token.PRIMARY && !this.lexer.identifierEquals("PRIMARY")) {
                     if (this.lexer.token() != Token.FOREIGN && !this.lexer.identifierEquals("FOREIGN")) {
                        if (this.lexer.token() == Token.REFERENCES || this.lexer.identifierEquals("REFERENCES")) {
                           DB2Constraint.DB2Reference ref = this.parseReference();
                           c.setReference(ref);
                           flag = true;
                        }
                     } else {
                        this.lexer.nextToken();
                        c.putAttribute("table-constraint", true);
                        this.accept(Token.KEY);
                        this.accept(Token.LPAREN);
                        DB2Constraint.DB2ForeignKey foreign = new DB2Constraint.DB2ForeignKey();

                        do {
                           foreign.addColumn(this.exprParser.name());
                           if (this.lexer.token() == Token.COMMA) {
                              this.lexer.nextToken();
                           }
                        } while(this.lexer.token() != Token.RPAREN);

                        this.lexer.nextToken();
                        foreign.setReference(this.parseReference());
                        flag = true;
                     }
                  } else {
                     this.lexer.nextToken();
                     this.accept(Token.KEY);
                     DB2Constraint.DB2PrimaryKey primaryKey = new DB2Constraint.DB2PrimaryKey();
                     if (this.lexer.token() == Token.LPAREN) {
                        c.putAttribute("table-constraint", true);
                        this.lexer.nextToken();

                        do {
                           primaryKey.addColumn(this.exprParser.name());
                           if (this.lexer.token() == Token.COMMA) {
                              this.lexer.nextToken();
                           }
                        } while(this.lexer.token() != Token.RPAREN);

                        this.lexer.nextToken();
                     }

                     primaryKey.setIndexParamaters(this.parseIndexParamaters());
                     c.setPrimaryKey(primaryKey);
                     flag = true;
                  }
               } else {
                  this.lexer.nextToken();
                  DB2Constraint.DB2Unique unique = new DB2Constraint.DB2Unique();
                  if (this.lexer.token() == Token.LPAREN) {
                     c.putAttribute("table-constraint", true);
                     this.lexer.nextToken();

                     do {
                        unique.addColumn(this.exprParser.name());
                        if (this.lexer.token() == Token.COMMA) {
                           this.lexer.nextToken();
                        }
                     } while(this.lexer.token() != Token.RPAREN);

                     this.lexer.nextToken();
                  }

                  unique.setIndexParamaters(this.parseIndexParamaters());
                  c.setUnique(unique);
                  flag = true;
               }
            } else {
               this.lexer.nextToken();
               c.setDefValue(this.exprParser.expr());
               c.putAttribute("table-constraint", true);
               flag = true;
            }
         } else {
            this.lexer.nextToken();
            c.setiNull(true);
            c.putAttribute("table-constraint", true);
            flag = true;
         }
      } else {
         Lexer.SavePoint mark = this.lexer.mark();
         this.lexer.nextToken();
         if (this.lexer.token() != Token.NULL && !this.lexer.identifierEquals("NULL")) {
            this.lexer.reset(mark);
         } else {
            c.setNotNull(true);
            this.lexer.nextToken();
            flag = true;
         }
      }

      DB2Constraint.ConstraintType constraintType = this.parseConstraintType();
      if (constraintType != null) {
         c.setType(constraintType);
         flag = true;
      }

      return !flag ? null : c;
   }

   private DB2Constraint.DB2IndexParamaters parseIndexParamaters() {
      DB2Constraint.DB2IndexParamaters ps = new DB2Constraint.DB2IndexParamaters();
      if (this.lexer.token() == Token.WITH || this.lexer.identifierEquals("WITH")) {
         this.lexer.nextToken();
         this.accept(Token.LPAREN);

         do {
            DB2StorageParameter parameter = new DB2StorageParameter();
            parameter.setOption(this.exprParser.expr());
            if (this.lexer.token() != Token.COMMA && this.lexer.token() != Token.RPAREN) {
               parameter.setValue(this.exprParser.expr());
            }

            ps.addParameter(parameter);
            if (this.lexer.token() == Token.COMMA) {
               this.lexer.nextToken();
            }
         } while(this.lexer.token() != Token.RPAREN);

         this.lexer.nextToken();
      }

      if (this.lexer.token() == Token.USING || this.lexer.identifierEquals("USING")) {
         this.lexer.nextToken();
         this.accept(Token.INDEX);
         this.accept(Token.TABLESPACE);
         ps.setTablespace(this.exprParser.expr());
      }

      return ps;
   }

   private DB2Constraint.DB2Reference parseReference() {
      if (this.lexer.token() != Token.REFERENCES && !this.lexer.identifierEquals("REFERENCES")) {
         return null;
      } else {
         this.lexer.nextToken();
         DB2Constraint.DB2Reference ref = new DB2Constraint.DB2Reference();
         ref.setRefTable(this.exprParser.expr());
         if (this.lexer.token() == Token.LPAREN) {
            this.lexer.nextToken();

            do {
               ref.addColumn(this.exprParser.name());
               if (this.lexer.token() == Token.COMMA) {
                  this.lexer.nextToken();
               }
            } while(this.lexer.token() != Token.RPAREN);

            this.lexer.nextToken();
         }

         if (this.lexer.identifierEquals("MATCH")) {
            this.lexer.nextToken();
            if (this.lexer.token() != Token.FULL && !this.lexer.identifierEquals("FULL")) {
               if (this.lexer.identifierEquals("PARTIAL")) {
                  this.lexer.nextToken();
                  ref.setMatchType(DB2Constraint.DB2Reference.MatchType.partial);
               } else if (this.lexer.identifierEquals("SIMPLE")) {
                  this.lexer.nextToken();
                  ref.setMatchType(DB2Constraint.DB2Reference.MatchType.simple);
               }
            } else {
               this.lexer.nextToken();
               ref.setMatchType(DB2Constraint.DB2Reference.MatchType.full);
            }
         }

         if (this.lexer.token() == Token.ON || this.lexer.identifierEquals("ON")) {
            this.lexer.nextToken();
            if (this.lexer.token() == Token.DELETE) {
               this.lexer.nextToken();
               ref.setDeleteAction(this.parseKeyAction());
            } else if (this.lexer.token() == Token.UPDATE) {
               this.lexer.nextToken();
               ref.setUpdateAction(this.parseKeyAction());
            }
         }

         return ref;
      }
   }

   private DB2Constraint.DB2Reference.KeyAction parseKeyAction() {
      if (this.lexer.token() == Token.ON) {
         this.lexer.nextToken();
         if (this.lexer.token() == Token.DELETE) {
            this.lexer.nextToken();
            return DB2Constraint.DB2Reference.KeyAction.onDelete;
         }

         if (this.lexer.token() == Token.UPDATE) {
            this.lexer.nextToken();
            return DB2Constraint.DB2Reference.KeyAction.onUpdate;
         }
      } else {
         if (this.lexer.identifierEquals("NO")) {
            this.lexer.nextToken();
            this.lexer.nextToken();
            return DB2Constraint.DB2Reference.KeyAction.noAction;
         }

         if (this.lexer.token() == Token.RESTRICT || this.lexer.identifierEquals("RESTRICT")) {
            this.lexer.nextToken();
            return DB2Constraint.DB2Reference.KeyAction.restrict;
         }

         if (this.lexer.token() == Token.CASCADE || this.lexer.identifierEquals("CASCADE")) {
            this.lexer.nextToken();
            return DB2Constraint.DB2Reference.KeyAction.cascade;
         }

         if (this.lexer.token() == Token.SET || this.lexer.identifierEquals("SET")) {
            this.lexer.nextToken();
            if (this.lexer.token() == Token.DEFAULT || this.lexer.identifierEquals("DEFAULT")) {
               this.lexer.nextToken();
               return DB2Constraint.DB2Reference.KeyAction.setDefault;
            }

            if (this.lexer.token() == Token.NULL || this.lexer.identifierEquals("NULL")) {
               this.lexer.nextToken();
               return DB2Constraint.DB2Reference.KeyAction.setNull;
            }
         }
      }

      return null;
   }

   private DB2Constraint.ConstraintType parseConstraintType() {
      if (this.lexer.identifierEquals("DEFERRABLE")) {
         this.lexer.nextToken();
         return DB2Constraint.ConstraintType.deferrable;
      } else if (this.lexer.token() != Token.NOT && !this.lexer.identifierEquals("NOT")) {
         if (this.lexer.token() == Token.INITIALLY || this.lexer.identifierEquals("INITIALLY")) {
            this.lexer.nextToken();
            if (this.lexer.token() == Token.DEFERRED || this.lexer.identifierEquals("DEFERRED")) {
               this.lexer.nextToken();
               return DB2Constraint.ConstraintType.initiallyDeferred;
            }

            if (this.lexer.token() == Token.IMMEDIATE || this.lexer.identifierEquals("IMMEDIATE")) {
               this.lexer.nextToken();
               return DB2Constraint.ConstraintType.initiallyImmediate;
            }
         }

         return null;
      } else {
         this.lexer.nextToken();
         this.lexer.nextToken();
         return DB2Constraint.ConstraintType.notDeferrable;
      }
   }

   private DB2OptionValue parseOpionValue(boolean eq) {
      if (this.lexer.token() == Token.RPAREN) {
         return null;
      } else {
         Lexer.SavePoint mark = this.lexer.mark();
         DB2OptionValue e = new DB2OptionValue();
         SQLExpr expr = this.exprParser.expr();
         if (eq) {
            if (expr instanceof SQLBinaryOpExpr) {
               SQLBinaryOpExpr bin = (SQLBinaryOpExpr)expr;
               e.setOption(bin.getLeft());
               e.setValue(bin.getRight());
               return e;
            } else if (this.lexer.token() == Token.EQ) {
               e.setOption(expr);
               this.lexer.nextToken();
               e.setValue(this.exprParser.expr());
               return e;
            } else {
               this.lexer.reset(mark);
               return null;
            }
         } else {
            e.setOption(expr);
            if (this.lexer.token() != Token.COMMA && this.lexer.token() != Token.RPAREN) {
               e.setValue(this.exprParser.expr());
            }

            return e;
         }
      }
   }

   private DB2ColumnDefinition.ColumnLikeOption parseLikeOption() {
      DB2ColumnDefinition.ColumnLikeOption o = new DB2ColumnDefinition.ColumnLikeOption();
      boolean flag = false;
      if (this.lexer.identifierEquals("INCLUDING")) {
         this.lexer.nextToken();
         flag = true;
         o.setIncludeType(DB2ColumnDefinition.ColumnLikeOption.IncludeType.including);
      } else if (this.lexer.identifierEquals("EXCLUDING")) {
         this.lexer.nextToken();
         flag = true;
         o.setIncludeType(DB2ColumnDefinition.ColumnLikeOption.IncludeType.excluding);
      }

      if (!flag) {
         return null;
      } else {
         if (this.lexer.identifierEquals("DEFAULTS")) {
            o.setOption(DB2ColumnDefinition.ColumnLikeOption.OptionType.defaults);
            this.lexer.nextToken();
         } else if (this.lexer.identifierEquals("CONSTRAINTS")) {
            o.setOption(DB2ColumnDefinition.ColumnLikeOption.OptionType.constraints);
            this.lexer.nextToken();
         } else if (this.lexer.identifierEquals("INDEXES")) {
            o.setOption(DB2ColumnDefinition.ColumnLikeOption.OptionType.indexes);
            this.lexer.nextToken();
         } else if (this.lexer.token() != Token.STORAGE && !this.lexer.identifierEquals("STORAGE")) {
            if (this.lexer.identifierEquals("COMMENTS")) {
               o.setOption(DB2ColumnDefinition.ColumnLikeOption.OptionType.comments);
               this.lexer.nextToken();
            } else if (this.lexer.token() == Token.ALL || this.lexer.identifierEquals("ALL")) {
               o.setOption(DB2ColumnDefinition.ColumnLikeOption.OptionType.all);
               this.lexer.nextToken();
            }
         } else {
            o.setOption(DB2ColumnDefinition.ColumnLikeOption.OptionType.storage);
            this.lexer.nextToken();
         }

         return o;
      }
   }

   private DB2CreateTableStatement.DB2TableDistributedBy parseDistributedBy() {
      DB2CreateTableStatement.DB2TableDistributedBy by = new DB2CreateTableStatement.DB2TableDistributedBy();
      if (this.lexer.token() == Token.BY) {
         this.lexer.nextToken();
         this.accept(Token.LPAREN);
         DB2OptionValue value = null;

         do {
            value = this.parseOpionValue(false);
            if (value != null) {
               by.addColum(value);
            }

            if (this.lexer.token() == Token.COMMA) {
               this.lexer.nextToken();
            }
         } while(value != null);

         this.lexer.nextToken();
         by.setOption(DB2CreateTableStatement.DB2TableDistributedBy.Option.by);
      } else if (this.lexer.identifierEquals("RANDOMLY")) {
         this.lexer.nextToken();
         by.setOption(DB2CreateTableStatement.DB2TableDistributedBy.Option.randomly);
      } else if (this.lexer.identifierEquals("REPLICATED")) {
         this.lexer.nextToken();
         by.setOption(DB2CreateTableStatement.DB2TableDistributedBy.Option.replicated);
      }

      return by;
   }

   private ArrayList<DB2SQLPartition> parsePartitionSpec() {
      ArrayList<DB2SQLPartition> specList = new ArrayList();
      SQLName defaultName = null;

      while(true) {
         if (this.lexer.token() == Token.DEFAULT) {
            this.accept(Token.DEFAULT);
            this.lexer.nextToken();
            defaultName = this.exprParser.name();
         } else {
            DB2SQLPartition partition = new DB2SQLPartition();
            specList.add(partition);
            if (this.lexer.token() == Token.PARTITION) {
               partition.setSubPartition(false);
               this.lexer.nextToken();
               partition.setName(this.exprParser.name());
            } else if (this.lexer.identifierEquals("SUBPARTITION")) {
               partition.setSubPartition(true);
               this.lexer.nextToken();
               partition.setName(this.exprParser.name());
            }

            if (this.lexer.token() == Token.VALUES) {
               this.lexer.nextToken();
               this.accept(Token.LPAREN);

               while(true) {
                  partition.getValueList().add(this.exprParser.expr());
                  if (this.lexer.token() != Token.COMMA) {
                     this.accept(Token.RPAREN);
                     break;
                  }

                  this.lexer.nextToken();
               }
            } else {
               if (this.lexer.token() == Token.START) {
                  partition.setStart(true);
                  this.lexer.nextToken();
                  this.accept(Token.LPAREN);
                  if (this.lexer.token() == Token.IDENTIFIER) {
                     partition.setStartDataType(this.exprParser.name());
                  }

                  partition.setStartValue(this.exprParser.expr());
                  this.accept(Token.RPAREN);
                  if (this.lexer.identifierEquals("INCLUSIVE")) {
                     partition.setStartInclusive(true);
                     this.lexer.nextToken();
                  } else if (this.lexer.identifierEquals("EXCLUSIVE")) {
                     partition.setStartExclusive(true);
                     this.lexer.nextToken();
                  }
               }

               if (this.lexer.token() == Token.END) {
                  partition.setEnd(true);
                  this.lexer.nextToken();
                  this.accept(Token.LPAREN);
                  if (this.lexer.token() == Token.IDENTIFIER) {
                     partition.setStartDataType(this.exprParser.name());
                  }

                  partition.setEndValue(this.exprParser.expr());
                  this.accept(Token.RPAREN);
                  if (this.lexer.identifierEquals("INCLUSIVE")) {
                     partition.setEndInclusive(true);
                     this.lexer.nextToken();
                  } else if (this.lexer.identifierEquals("EXCLUSIVE")) {
                     partition.setEndExclusive(true);
                     this.lexer.nextToken();
                  }
               }

               if (this.lexer.identifierEquals("EVERY")) {
                  partition.setEvery(true);
                  this.lexer.nextToken();
                  this.accept(Token.LPAREN);
                  if (this.lexer.token() == Token.IDENTIFIER) {
                     partition.setEveryDataType(this.exprParser.name());
                  }

                  partition.setEveryValue(this.exprParser.expr());
                  this.accept(Token.RPAREN);
               }
            }

            if (this.lexer.token() == Token.WITH) {
               partition.setWith(true);
               this.lexer.nextToken();
               this.accept(Token.LPAREN);

               while(true) {
                  SQLUpdateSetItem item = this.exprParser.parseUpdateSetItem();
                  partition.getWithList().add(item);
                  if (this.lexer.token() != Token.COMMA) {
                     this.accept(Token.RPAREN);
                     break;
                  }

                  this.lexer.nextToken();
               }
            }

            if (this.lexer.token() == Token.TABLESPACE) {
               this.lexer.nextToken();
               partition.setTableSpace(this.exprParser.name());
            }
         }

         if (this.lexer.token() != Token.COMMA) {
            boolean hasDefault = false;

            for(DB2SQLPartition spec : specList) {
               if (spec.getName() != null && spec.getName().equals(defaultName)) {
                  spec.setIfDefault(true);
                  hasDefault = true;
               } else {
                  spec.setIfDefault(false);
               }
            }

            if (defaultName != null && !hasDefault) {
               DB2SQLPartition spec = new DB2SQLPartition();
               spec.setName(defaultName);
               spec.setIfDefault(true);
               specList.add(spec);
            }

            return specList;
         }

         this.lexer.nextToken();
      }
   }
}
