package com.alibaba.druid.sql.dialect.dm.parser;

import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLName;
import com.alibaba.druid.sql.ast.SQLPartition;
import com.alibaba.druid.sql.ast.SQLPartitionBy;
import com.alibaba.druid.sql.ast.SQLPartitionByHash;
import com.alibaba.druid.sql.ast.SQLPartitionByList;
import com.alibaba.druid.sql.ast.SQLPartitionByRange;
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.druid.sql.ast.expr.SQLNumericLiteralExpr;
import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition;
import com.alibaba.druid.sql.ast.statement.SQLConstraint;
import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement;
import com.alibaba.druid.sql.ast.statement.SQLExprTableSource;
import com.alibaba.druid.sql.ast.statement.SQLExternalRecordFormat;
import com.alibaba.druid.sql.ast.statement.SQLSelect;
import com.alibaba.druid.sql.ast.statement.SQLTableElement;
import com.alibaba.druid.sql.dialect.dm.ast.clause.DmStorageClause;
import com.alibaba.druid.sql.dialect.dm.ast.stmt.DmCreateTableStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleLobStorageClause;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleLobParameters;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSupplementalIdKey;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSupplementalLogGrp;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleXmlColumnProperties;
import com.alibaba.druid.sql.dialect.oracle.parser.OracleExprParser;
import com.alibaba.druid.sql.dialect.oracle.parser.OracleSelectParser;
import com.alibaba.druid.sql.parser.Lexer;
import com.alibaba.druid.sql.parser.ParserException;
import com.alibaba.druid.sql.parser.SQLCreateTableParser;
import com.alibaba.druid.sql.parser.SQLExprParser;
import com.alibaba.druid.sql.parser.Token;
import com.alibaba.druid.util.FnvHash;

public class DmCreateTableParser extends SQLCreateTableParser {
   public DmCreateTableParser(Lexer lexer) {
      super((SQLExprParser)(new DmExprParser(lexer)));
   }

   public DmCreateTableParser(String sql) {
      super((SQLExprParser)(new DmExprParser(sql)));
   }

   protected DmCreateTableStatement newCreateStatement() {
      return new DmCreateTableStatement();
   }

   public SQLCreateTableStatement parseBaseCreateTable(boolean acceptCreate) {
      DmCreateTableStatement createTable = this.newCreateStatement();
      createTable.setDbType(this.getDbType());
      if (acceptCreate) {
         if (this.lexer.hasComment() && this.lexer.isKeepComments()) {
            createTable.addBeforeComment(this.lexer.readAndResetComments());
         }

         this.accept(Token.CREATE);
      }

      if (this.lexer.identifierEquals("GLOBAL")) {
         this.lexer.nextToken();
         if (!this.lexer.identifierEquals("TEMPORARY")) {
            throw new ParserException("syntax error " + this.lexer.info());
         }

         this.lexer.nextToken();
         createTable.setType(SQLCreateTableStatement.Type.GLOBAL_TEMPORARY);
      } else if (this.lexer.token() == Token.IDENTIFIER && this.lexer.stringVal().equalsIgnoreCase("LOCAL")) {
         this.lexer.nextToken();
         if (this.lexer.token() != Token.IDENTIFIER || !this.lexer.stringVal().equalsIgnoreCase("TEMPORAY")) {
            throw new ParserException("syntax error. " + this.lexer.info());
         }

         this.lexer.nextToken();
         createTable.setType(SQLCreateTableStatement.Type.LOCAL_TEMPORARY);
      } else if (this.lexer.identifierEquals("TEMPORARY")) {
         this.lexer.nextToken();
         createTable.setType(SQLCreateTableStatement.Type.TEMPORARY);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.DIMENSION)) {
         this.lexer.nextToken();
         createTable.setDimension(true);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.EXTERNAL)) {
         this.lexer.nextToken();
         createTable.setExternal(true);
      }

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

      createTable.setName(this.exprParser.name());
      if (this.lexer.token() == Token.LPAREN) {
         this.lexer.nextToken();

         do {
            Token token = this.lexer.token();
            if (token != Token.IDENTIFIER && token != Token.LITERAL_ALIAS) {
               if (token != Token.PRIMARY && token != Token.UNIQUE && token != Token.CHECK && token != Token.CONSTRAINT && token != Token.FOREIGN && token != Token.NOT) {
                  if (token == Token.TABLESPACE) {
                     throw new ParserException("TODO " + this.lexer.info());
                  }

                  SQLColumnDefinition column = this.exprParser.parseColumn();
                  createTable.getTableElementList().add(column);
               } else {
                  SQLConstraint constraint = this.exprParser.parseConstaint();
                  constraint.setParent(createTable);
                  createTable.getTableElementList().add((SQLTableElement)constraint);
               }
            } else if (this.lexer.identifierEquals("CLUSTER")) {
               SQLConstraint constraint = this.exprParser.parseConstaint();
               constraint.setParent(createTable);
               createTable.getTableElementList().add((SQLTableElement)constraint);
            } else {
               SQLColumnDefinition column = this.exprParser.parseColumn(createTable);
               column.setParent(createTable);
               createTable.getTableElementList().add(column);
            }

            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);
            SQLName inherits = this.exprParser.name();
            createTable.setInherits(new SQLExprTableSource(inherits));
            this.accept(Token.RPAREN);
         }
      }

      if (this.lexer.token() == Token.AS) {
         this.lexer.nextToken();
         SQLSelect select = null;
         if (DbType.oracle == this.dbType) {
            select = (new OracleSelectParser(this.exprParser)).select();
         } else {
            select = this.createSQLSelectParser().select();
         }

         createTable.setSelect(select);
      }

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

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

      if (this.lexer.token() == Token.PARTITION) {
         SQLPartitionBy partitionClause = this.parsePartitionBy();
         createTable.setPartitioning(partitionClause);
      }

      if (this.lexer.token() == Token.PARTITION) {
         SQLPartitionBy partitionClause = this.parsePartitionBy();
         createTable.setPartitioning(partitionClause);
      }

      this.parserExternaleTableFrom(createTable);
      this.parseCreateTableRest(createTable);
      return createTable;
   }

   private void parserExternaleTableFrom(DmCreateTableStatement createTable) {
      if (this.lexer.token() == Token.FROM) {
         this.lexer.nextToken();
         if (this.lexer.identifierEquals("datafile")) {
            this.lexer.nextToken();
            createTable.setDatafile(true);
            if (this.lexer.token() == Token.DEFAULT) {
               this.lexer.nextToken();
               this.acceptIdentifier("DIRECTORY");
               createTable.setDirectory(this.exprParser.name());
               this.acceptIdentifier("LOCATION");
               this.accept(Token.LPAREN);
               createTable.setLocation(this.exprParser.name());
               this.accept(Token.RPAREN);
               this.parseParms(createTable);
            } else {
               createTable.setFilepath(this.exprParser.name());
               this.parseParms(createTable);
            }
         } else if (this.lexer.token() == Token.DEFAULT) {
            this.lexer.nextToken();
            this.acceptIdentifier("DIRECTORY");
            createTable.setDirectory(this.exprParser.name());
            this.acceptIdentifier("LOCATION");
            this.accept(Token.LPAREN);
            createTable.setLocation(this.exprParser.name());
            this.accept(Token.RPAREN);
         } else {
            createTable.setFilepath(this.exprParser.name());
         }
      }

   }

   private void parseParms(DmCreateTableStatement createTable) {
      if (this.lexer.identifierEquals("parms")) {
         this.lexer.nextToken();
         this.accept(Token.LPAREN);

         while(true) {
            if (this.lexer.identifierEquals("fields")) {
               this.lexer.nextToken();
               this.acceptIdentifier("delimited");
               this.accept(Token.BY);
               DmCreateTableStatement.PARMS item = new DmCreateTableStatement.PARMS();
               item.setType(DmCreateTableStatement.Type.FIELDS_DELIMITED_BY);
               item.setExpr(this.exprParser.expr());
               createTable.getParmsList().add(item);
            } else if (this.lexer.identifierEquals("records")) {
               this.lexer.nextToken();
               this.acceptIdentifier("delimited");
               this.accept(Token.BY);
               DmCreateTableStatement.PARMS item = new DmCreateTableStatement.PARMS();
               item.setType(DmCreateTableStatement.Type.RECORDS_DELIMITED_BY);
               item.setExpr(this.exprParser.expr());
               createTable.getParmsList().add(item);
            } else if (this.lexer.token() == Token.ERRORS) {
               this.lexer.nextToken();
               DmCreateTableStatement.PARMS item = new DmCreateTableStatement.PARMS();
               item.setType(DmCreateTableStatement.Type.ERRORS);
               item.setExpr(this.exprParser.expr());
               createTable.getParmsList().add(item);
            } else if (this.lexer.identifierEquals("BADFILE")) {
               this.lexer.nextToken();
               DmCreateTableStatement.PARMS item = new DmCreateTableStatement.PARMS();
               item.setType(DmCreateTableStatement.Type.BADFILE);
               item.setExpr(this.exprParser.expr());
               createTable.getParmsList().add(item);
            } else if (this.lexer.identifierEquals("LOG")) {
               this.lexer.nextToken();
               DmCreateTableStatement.PARMS item = new DmCreateTableStatement.PARMS();
               item.setType(DmCreateTableStatement.Type.LOG);
               item.setExpr(this.exprParser.expr());
               createTable.getParmsList().add(item);
            } else if (this.lexer.identifierEquals("NULL_STR")) {
               this.lexer.nextToken();
               DmCreateTableStatement.PARMS item = new DmCreateTableStatement.PARMS();
               item.setType(DmCreateTableStatement.Type.NULL_STR);
               item.setExpr(this.exprParser.expr());
               createTable.getParmsList().add(item);
            } else if (this.lexer.identifierEquals("SKIP")) {
               this.lexer.nextToken();
               DmCreateTableStatement.PARMS item = new DmCreateTableStatement.PARMS();
               item.setType(DmCreateTableStatement.Type.SKIP);
               item.setExpr(this.exprParser.expr());
               createTable.getParmsList().add(item);
            } else if (this.lexer.identifierEquals("CHARACTER_CODE")) {
               this.lexer.nextToken();
               DmCreateTableStatement.PARMS item = new DmCreateTableStatement.PARMS();
               item.setType(DmCreateTableStatement.Type.CHARACTER_CODE);
               item.setExpr(this.exprParser.expr());
               createTable.getParmsList().add(item);
            }

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

            this.lexer.nextToken();
         }
      }

   }

   public DmCreateTableStatement parseCreateTable(boolean acceptCreate) {
      DmCreateTableStatement stmt = (DmCreateTableStatement)this.parseBaseCreateTable(acceptCreate);
      if (this.lexer.token() == Token.OF) {
         this.lexer.nextToken();
         stmt.setOf(this.exprParser.name());
         if (this.lexer.identifierEquals("OIDINDEX")) {
            this.lexer.nextToken();
            DmCreateTableStatement.OIDIndex oidIndex = new DmCreateTableStatement.OIDIndex();
            if (this.lexer.token() != Token.LPAREN) {
               oidIndex.setName(this.exprParser.name());
            }

            this.accept(Token.LPAREN);
            this.getExprParser().parseSegmentAttributes(oidIndex);
            this.accept(Token.RPAREN);
            stmt.setOidIndex(oidIndex);
         }
      }

      label245:
      while(true) {
         this.getExprParser().parseSegmentAttributes(stmt);
         if (this.lexer.identifierEquals(FnvHash.Constants.IN_MEMORY_METADATA)) {
            this.lexer.nextToken();
            stmt.setInMemoryMetadata(true);
         } else if (this.lexer.identifierEquals("DISKSPACE")) {
            this.acceptIdentifier("DISKSPACE");
            if (this.lexer.token() == Token.LIMIT) {
               this.accept(Token.LIMIT);
               stmt.setSpacelimit(this.lexer.integerValue().intValue());
               this.lexer.nextToken();
            } else {
               this.accept(Token.UNLIMITED);
               stmt.setUnlimited(true);
            }
         } else if (this.lexer.identifierEquals(FnvHash.Constants.CURSOR_SPECIFIC_SEGMENT)) {
            this.lexer.nextToken();
            stmt.setCursorSpecificSegment(true);
         } else if (this.lexer.identifierEquals(FnvHash.Constants.NOPARALLEL)) {
            this.lexer.nextToken();
            stmt.setParallel(false);
         } else if (this.lexer.identifierEquals(FnvHash.Constants.PARALLEL)) {
            this.lexer.nextToken();
            stmt.setParallel(true);
            if (this.lexer.token() == Token.LITERAL_INT) {
               stmt.setParallelValue(this.exprParser.primary());
            }
         } else if (this.lexer.token() == Token.CACHE) {
            this.lexer.nextToken();
            stmt.setCache(Boolean.TRUE);
         } else if (this.lexer.token() == Token.NOCACHE) {
            this.lexer.nextToken();
            stmt.setCache(Boolean.FALSE);
         } else if (this.lexer.token() == Token.ENABLE) {
            this.lexer.nextToken();
            if (this.lexer.token() != Token.ROW) {
               throw new ParserException("TODO : " + this.lexer.info());
            }

            this.lexer.nextToken();
            this.acceptIdentifier("MOVEMENT");
            stmt.setEnableRowMovement(Boolean.TRUE);
         } else if (this.lexer.token() == Token.DISABLE) {
            this.lexer.nextToken();
            if (this.lexer.token() != Token.ROW) {
               throw new ParserException("TODO : " + this.lexer.info());
            }

            this.lexer.nextToken();
            this.acceptIdentifier("MOVEMENT");
            stmt.setEnableRowMovement(Boolean.FALSE);
         } else if (this.lexer.token() == Token.ON) {
            this.lexer.nextToken();
            this.accept(Token.COMMIT);
            if (this.lexer.identifierEquals("PRESERVE")) {
               this.lexer.nextToken();
               this.accept(Token.ROWS);
               stmt.setOnCommitPreserveRows(true);
            } else {
               this.accept(Token.DELETE);
               this.accept(Token.ROWS);
               stmt.setOnCommitDeleteRows(true);
            }
         } else if (this.lexer.token() == Token.STORAGE) {
            DmStorageClause storage = ((DmExprParser)this.exprParser).parseStorage();
            stmt.setStorage(storage);
         } else if (this.lexer.identifierEquals("ORGANIZATION")) {
            this.parseOrganization(stmt);
         } else if (this.lexer.identifierEquals(FnvHash.Constants.CLUSTER)) {
            this.lexer.nextToken();
            SQLName cluster = this.exprParser.name();
            stmt.setCluster(cluster);
            this.accept(Token.LPAREN);
            this.exprParser.names(stmt.getClusterColumns(), cluster);
            this.accept(Token.RPAREN);
         } else if (this.lexer.identifierEquals("MONITORING")) {
            this.lexer.nextToken();
            stmt.setMonitoring(true);
         } else if (this.lexer.identifierEquals(FnvHash.Constants.INCLUDING)) {
            this.lexer.nextToken();
            this.exprParser.names(stmt.getIncluding(), stmt);
            this.acceptIdentifier("OVERFLOW");
         } else if (this.lexer.token() == Token.LOB) {
            OracleLobStorageClause lobStorage = ((OracleExprParser)this.exprParser).parseLobStorage();
            stmt.setLobStorage(lobStorage);
         } else if (this.lexer.token() == Token.SEGMENT) {
            this.lexer.nextToken();
            this.accept(Token.CREATION);
            if (this.lexer.token() == Token.IMMEDIATE) {
               this.lexer.nextToken();
               stmt.setDeferredSegmentCreation(DmCreateTableStatement.DeferredSegmentCreation.IMMEDIATE);
            } else {
               this.accept(Token.DEFERRED);
               stmt.setDeferredSegmentCreation(DmCreateTableStatement.DeferredSegmentCreation.DEFERRED);
            }
         } else if (this.lexer.token() == Token.COLUMN) {
            this.lexer.nextToken();
            SQLName name = this.exprParser.name();
            if (this.lexer.token() == Token.NOT) {
               this.lexer.nextToken();
            }

            if (this.lexer.identifierEquals(FnvHash.Constants.SUBSTITUTABLE)) {
               this.lexer.nextToken();
               this.acceptIdentifier("AT");
               this.accept(Token.ALL);
               this.acceptIdentifier("LEVELS");
            }
         } else {
            if (this.lexer.identifierEquals(FnvHash.Constants.VARRAY)) {
               this.lexer.nextToken();
               SQLName name = this.exprParser.name();
               this.accept(Token.STORE);
               this.accept(Token.AS);
               if (this.lexer.identifierEquals(FnvHash.Constants.BASICFILE)) {
                  this.lexer.nextToken();
               }

               throw new ParserException("TODO : " + this.lexer.info());
            }

            if (this.lexer.token() == Token.PARTITION) {
               this.lexer.nextToken();
               this.accept(Token.BY);
               if (this.lexer.identifierEquals("RANGE")) {
                  SQLPartitionByRange partitionByRange = this.getExprParser().partitionByRange();
                  this.getExprParser().partitionClauseRest((SQLPartitionBy)partitionByRange);
                  stmt.setPartitioning(partitionByRange);
               } else if (!this.lexer.identifierEquals("HASH")) {
                  if (!this.lexer.identifierEquals("LIST")) {
                     throw new ParserException("TODO : " + this.lexer.info());
                  }

                  SQLPartitionByList partitionByList = this.partitionByList();
                  this.getExprParser().partitionClauseRest((SQLPartitionBy)partitionByList);
                  stmt.setPartitioning(partitionByList);
               } else {
                  SQLPartitionByHash partitionByHash = this.getExprParser().partitionByHash();
                  this.getExprParser().partitionClauseRest((SQLPartitionBy)partitionByHash);
                  if (this.lexer.token() == Token.LPAREN) {
                     this.lexer.nextToken();

                     while(true) {
                        SQLPartition partition = this.getExprParser().parsePartition();
                        partitionByHash.addPartition(partition);
                        if (this.lexer.token() != Token.COMMA) {
                           if (this.lexer.token() != Token.RPAREN) {
                              throw new ParserException("TODO : " + this.lexer.info());
                           }

                           this.lexer.nextToken();
                           break;
                        }

                        this.lexer.nextToken();
                     }
                  }

                  stmt.setPartitioning(partitionByHash);
               }
            } else {
               if (!this.lexer.identifierEquals(FnvHash.Constants.XMLTYPE)) {
                  if (this.lexer.token() == Token.AS) {
                     this.lexer.nextToken();
                     SQLSelect select = (new OracleSelectParser(this.exprParser)).select();
                     stmt.setSelect(select);
                  }

                  return stmt;
               }

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

               OracleXmlColumnProperties xmlColumnProperties = new OracleXmlColumnProperties();
               xmlColumnProperties.setColumn(this.exprParser.name());
               if (this.lexer.token() == Token.STORE) {
                  this.lexer.nextToken();
                  this.accept(Token.AS);
                  OracleXmlColumnProperties.OracleXMLTypeStorage storage = new OracleXmlColumnProperties.OracleXMLTypeStorage();
                  if (this.lexer.identifierEquals("SECUREFILE")) {
                     storage.setSecureFile(true);
                     this.lexer.nextToken();
                  } else if (this.lexer.identifierEquals("BASICFILE")) {
                     storage.setBasicFile(true);
                     this.lexer.nextToken();
                  }

                  if (this.lexer.identifierEquals("BINARY")) {
                     this.lexer.nextToken();
                     this.acceptIdentifier("XML");
                     storage.setBinaryXml(true);
                  } else if (this.lexer.identifierEquals("CLOB")) {
                     this.lexer.nextToken();
                     storage.setClob(true);
                  }

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

                     label205:
                     while(true) {
                        switch (this.lexer.token()) {
                           case TABLESPACE:
                              this.lexer.nextToken();
                              SQLName tableSpace = this.exprParser.name();
                              lobParameters.setTableSpace(tableSpace);
                              break;
                           case ENABLE:
                           case DISABLE:
                              Boolean enable = this.lexer.token() == Token.ENABLE;
                              this.lexer.nextToken();
                              this.accept(Token.STORAGE);
                              this.accept(Token.IN);
                              this.accept(Token.ROW);
                              lobParameters.setEnableStorageInRow(enable);
                              break;
                           case CHUNK:
                              this.lexer.nextToken();
                              SQLExpr chunk = this.exprParser.expr();
                              lobParameters.setChunk(chunk);
                              break;
                           case NOCACHE:
                              this.lexer.nextToken();
                              lobParameters.setCache(false);
                              break;
                           case LOGGING:
                              this.lexer.nextToken();
                              lobParameters.setLogging(true);
                              break;
                           case NOCOMPRESS:
                              this.lexer.nextToken();
                              lobParameters.setCompress(false);
                              break;
                           case KEEP_DUPLICATES:
                              this.lexer.nextToken();
                              lobParameters.setKeepDuplicates(true);
                              break;
                           case STORAGE:
                              DmStorageClause storageClause = this.getExprParser().parseStorage();
                              break;
                           case IDENTIFIER:
                              long hash = this.lexer.hash_lower();
                              if (hash == FnvHash.Constants.PCTVERSION) {
                                 lobParameters.setPctVersion(this.exprParser.primary());
                                 this.lexer.nextToken();
                                 break;
                              }
                           default:
                              break label205;
                        }
                     }

                     this.accept(Token.RPAREN);
                     storage.setLobParameters(lobParameters);
                  }
               }

               while(true) {
                  while(!this.lexer.identifierEquals(FnvHash.Constants.ALLOW)) {
                     if (!this.lexer.identifierEquals(FnvHash.Constants.DISALLOW)) {
                        stmt.setXmlTypeColumnProperties(xmlColumnProperties);
                        continue label245;
                     }

                     this.lexer.nextToken();
                     if (this.lexer.identifierEquals("NONSCHEMA")) {
                        this.lexer.nextToken();
                        xmlColumnProperties.setAllowNonSchema(false);
                     } else {
                        if (!this.lexer.identifierEquals("ANYSCHEMA")) {
                           throw new ParserException("TODO : " + this.lexer.info());
                        }

                        this.lexer.nextToken();
                        xmlColumnProperties.setAllowAnySchema(false);
                     }
                  }

                  this.lexer.nextToken();
                  if (this.lexer.identifierEquals("NONSCHEMA")) {
                     this.lexer.nextToken();
                     xmlColumnProperties.setAllowNonSchema(true);
                  } else {
                     if (!this.lexer.identifierEquals("ANYSCHEMA")) {
                        throw new ParserException("TODO : " + this.lexer.info());
                     }

                     this.lexer.nextToken();
                     xmlColumnProperties.setAllowAnySchema(true);
                  }
               }
            }
         }
      }
   }

   private void parseOrganization(DmCreateTableStatement stmt) {
      DmCreateTableStatement.Organization organization = new DmCreateTableStatement.Organization();
      this.acceptIdentifier("ORGANIZATION");
      if (this.lexer.token() == Token.INDEX) {
         this.lexer.nextToken();
         organization.setType("INDEX");
         this.getExprParser().parseSegmentAttributes(organization);
         if (this.lexer.identifierEquals(FnvHash.Constants.PCTTHRESHOLD)) {
            this.lexer.nextToken();
            if (this.lexer.token() == Token.LITERAL_INT) {
               int pctthreshold = ((SQLNumericLiteralExpr)this.exprParser.primary()).getNumber().intValue();
               organization.setPctthreshold(pctthreshold);
            }
         }
      } else if (this.lexer.identifierEquals("HEAP")) {
         this.lexer.nextToken();
         organization.setType("HEAP");
         this.getExprParser().parseSegmentAttributes(organization);
      } else {
         if (!this.lexer.identifierEquals("EXTERNAL")) {
            throw new ParserException("TODO " + this.lexer.info());
         }

         this.lexer.nextToken();
         organization.setType("EXTERNAL");
         this.accept(Token.LPAREN);
         if (this.lexer.identifierEquals("TYPE")) {
            this.lexer.nextToken();
            organization.setExternalType(this.exprParser.name());
         }

         this.accept(Token.DEFAULT);
         this.acceptIdentifier("DIRECTORY");
         organization.setExternalDirectory(this.exprParser.expr());
         if (this.lexer.identifierEquals("ACCESS")) {
            this.lexer.nextToken();
            this.acceptIdentifier("PARAMETERS");
            if (this.lexer.token() == Token.LPAREN) {
               this.lexer.nextToken();
               SQLExternalRecordFormat recordFormat = new SQLExternalRecordFormat();
               if (this.lexer.identifierEquals("RECORDS")) {
                  this.lexer.nextToken();
                  if (!this.lexer.identifierEquals("DELIMITED")) {
                     throw new ParserException("TODO " + this.lexer.info());
                  }

                  this.lexer.nextToken();
                  this.accept(Token.BY);
                  if (!this.lexer.identifierEquals("NEWLINE")) {
                     throw new ParserException("TODO " + this.lexer.info());
                  }

                  this.lexer.nextToken();
                  recordFormat.setDelimitedBy(new SQLIdentifierExpr("NEWLINE"));
                  if (this.lexer.identifierEquals(FnvHash.Constants.NOLOGFILE)) {
                     this.lexer.nextToken();
                     recordFormat.setLogfile(false);
                  }

                  if (this.lexer.identifierEquals(FnvHash.Constants.NOBADFILE)) {
                     this.lexer.nextToken();
                     recordFormat.setBadfile(false);
                  }
               }

               if (this.lexer.identifierEquals(FnvHash.Constants.FIELDS)) {
                  this.lexer.nextToken();
                  if (!this.lexer.identifierEquals(FnvHash.Constants.TERMINATED)) {
                     throw new ParserException("TODO " + this.lexer.info());
                  }

                  this.lexer.nextToken();
                  this.accept(Token.BY);
                  recordFormat.setTerminatedBy(this.exprParser.primary());
                  if (this.lexer.identifierEquals(FnvHash.Constants.LTRIM)) {
                     this.lexer.nextToken();
                     recordFormat.setLtrim(true);
                  }
               }

               if (this.lexer.identifierEquals(FnvHash.Constants.MISSING)) {
                  this.lexer.nextToken();
                  this.acceptIdentifier("FIELD");
                  this.accept(Token.VALUES);
                  this.acceptIdentifier("ARE");
                  this.accept(Token.NULL);
                  recordFormat.setMissingFieldValuesAreNull(true);
               }

               if (this.lexer.token() == Token.REJECT) {
                  this.lexer.nextToken();
                  this.acceptIdentifier("ROWS");
                  this.accept(Token.WITH);
                  this.accept(Token.ALL);
                  this.accept(Token.NULL);
                  this.acceptIdentifier("FIELDS");
                  recordFormat.setRejectRowsWithAllNullFields(true);
               }

               organization.setExternalDirectoryRecordFormat(recordFormat);
               this.accept(Token.RPAREN);
            } else if (this.lexer.token() == Token.USING) {
               this.lexer.nextToken();
               this.acceptIdentifier("CLOB");
               throw new ParserException("TODO " + this.lexer.info());
            }
         }

         this.acceptIdentifier("LOCATION");
         this.accept(Token.LPAREN);
         this.exprParser.exprList(organization.getExternalDirectoryLocation(), organization);
         this.accept(Token.RPAREN);
         this.accept(Token.RPAREN);
         if (this.lexer.token() == Token.REJECT) {
            this.lexer.nextToken();
            this.accept(Token.LIMIT);
            organization.setExternalRejectLimit(this.exprParser.primary());
         }
      }

      stmt.setOrganization(organization);
   }

   protected SQLPartitionByList partitionByList() {
      this.acceptIdentifier("LIST");
      SQLPartitionByList partitionByList = new SQLPartitionByList();
      this.accept(Token.LPAREN);
      partitionByList.addColumn(this.exprParser.expr());
      this.accept(Token.RPAREN);
      this.getExprParser().parsePartitionByRest(partitionByList);
      return partitionByList;
   }

   public SQLTableElement parseCreateTableSupplementalLogingProps() {
      this.acceptIdentifier("SUPPLEMENTAL");
      this.acceptIdentifier("LOG");
      if (this.lexer.token() != Token.GROUP) {
         if (this.lexer.identifierEquals(FnvHash.Constants.DATA)) {
            this.lexer.nextToken();
            OracleSupplementalIdKey idKey = new OracleSupplementalIdKey();
            this.accept(Token.LPAREN);

            while(true) {
               if (this.lexer.token() == Token.ALL) {
                  this.lexer.nextToken();
                  idKey.setAll(true);
               } else if (this.lexer.token() == Token.PRIMARY) {
                  this.lexer.nextToken();
                  this.accept(Token.KEY);
                  idKey.setPrimaryKey(true);
               } else if (this.lexer.token() == Token.UNIQUE) {
                  this.lexer.nextToken();
                  if (this.lexer.token() == Token.INDEX) {
                     this.lexer.nextToken();
                     idKey.setUniqueIndex(true);
                  } else {
                     idKey.setUnique(true);
                  }
               } else if (this.lexer.token() == Token.FOREIGN) {
                  this.lexer.nextToken();
                  this.accept(Token.KEY);
                  idKey.setForeignKey(true);
               }

               if (this.lexer.token() != Token.COMMA) {
                  if (this.lexer.token() == Token.RPAREN) {
                     this.accept(Token.RPAREN);
                     this.acceptIdentifier("COLUMNS");
                     return idKey;
                  }

                  throw new ParserException("TODO " + this.lexer.info());
               }

               this.lexer.nextToken();
            }
         } else {
            throw new ParserException("TODO " + this.lexer.info());
         }
      } else {
         this.lexer.nextToken();
         OracleSupplementalLogGrp logGrp = new OracleSupplementalLogGrp();
         logGrp.setGroup(this.exprParser.name());
         this.accept(Token.LPAREN);

         while(true) {
            SQLName column = this.exprParser.name();
            if (this.lexer.identifierEquals("NO")) {
               this.lexer.nextToken();
               this.acceptIdentifier("LOG");
               column.putAttribute("NO LOG", Boolean.TRUE);
            }

            logGrp.addColumn(column);
            if (this.lexer.token() != Token.COMMA) {
               if (this.lexer.token() == Token.RPAREN) {
                  this.accept(Token.RPAREN);
                  if (this.lexer.identifierEquals("ALWAYS")) {
                     this.lexer.nextToken();
                     logGrp.setAlways(true);
                  }

                  return logGrp;
               }

               throw new ParserException("TODO " + this.lexer.info());
            }

            this.lexer.nextToken();
         }
      }
   }

   public DmExprParser getExprParser() {
      return (DmExprParser)this.exprParser;
   }
}
