Exemplo n.º 1
0
 public function convertSqlToJob(SQLTokenIterator $tokens)
 {
     $alterJob = new AlterStatement();
     $tokens->seekTokenNum(SqlToken::T_ALTER());
     if ($tokens->getCurrentTokenNumber() !== SqlToken::T_ALTER()) {
         throw new ErrorException("Tried to parse an ALTER statement when token-iterator does not point to T_ALTER!");
     }
     $alterJob->setDoIgnoreErrors($tokens->seekTokenNum(SqlToken::T_IGNORE()));
     if (!$tokens->seekTokenNum(SqlToken::T_TABLE())) {
         throw new MalformedSqlException("Missing TABLE for ALTER statement!", $tokens);
     }
     if (!$this->tableParser->canParseTokens($tokens)) {
         throw new MalformedSqlException("Missing Table-Specifier for ALTER TABLE statement!");
     }
     $alterJob->setTable($this->tableParser->convertSqlToJob($tokens));
     $dataChange = new AlterTableDataChange();
     do {
         switch (true) {
             case $tokens->seekTokenNum(SqlToken::T_ADD()):
                 $isInParenthesises = $tokens->seekTokenText('(');
                 do {
                     switch (true) {
                         case $tokens->seekTokenNum(SqlToken::T_COLUMN()):
                             if ($tokens->seekTokenText('(')) {
                                 do {
                                     if (!$this->columnDefinitionParser->canParseTokens($tokens)) {
                                         throw new MalformedSqlException("Missing column definition after ALTER TABLE ADD COLUMN!", $tokens);
                                     }
                                     $dataChange->setAttribute(AlterAttributeType::ADD());
                                     $dataChange->setSubjectColumnDefinition($this->columnDefinitionParser->convertSqlToJob($tokens));
                                     $alterJob->addDataChange(clone $dataChange);
                                 } while ($tokens->seekTokenText(','));
                                 if (!$tokens->seekTokenText(')')) {
                                     throw new MalformedSqlException("Missing ending parenthesis after column list", $tokens);
                                 }
                             } else {
                                 if (!$this->columnDefinitionParser->canParseTokens($tokens)) {
                                     throw new MalformedSqlException("Missing column definition after ALTER TABLE ADD COLUMN!", $tokens);
                                 }
                                 $dataChange->setAttribute(AlterAttributeType::ADD());
                                 $dataChange->setSubjectColumnDefinition($this->columnDefinitionParser->convertSqlToJob($tokens));
                                 $alterJob->addDataChange(clone $dataChange);
                             }
                             break;
                         case $this->columnDefinitionParser->canParseTokens($tokens):
                             $dataChange->setAttribute(AlterAttributeType::ADD());
                             $dataChange->setSubjectColumnDefinition($this->columnDefinitionParser->convertSqlToJob($tokens));
                             $alterJob->addDataChange(clone $dataChange);
                             break;
                         case $tokens->seekTokenNum(SqlToken::T_PRIMARY(), TokenIterator::NEXT, [SqlToken::T_CONSTRAINT(), T_STRING]):
                         case $tokens->seekTokenNum(SqlToken::T_UNIQUE(), TokenIterator::NEXT, [SqlToken::T_CONSTRAINT(), T_STRING]):
                         case $tokens->seekTokenNum(SqlToken::T_FOREIGN(), TokenIterator::NEXT, [SqlToken::T_CONSTRAINT(), T_STRING]):
                         case $tokens->seekTokenNum(SqlToken::T_FULLTEXT()):
                         case $tokens->seekTokenNum(SqlToken::T_SPATIAL()):
                         case $tokens->seekTokenNum(SqlToken::T_INDEX()):
                             $indexJob = new IndexJob();
                             if ($tokens->isTokenNum(SqlToken::T_CONSTRAINT(), TokenIterator::PREVIOUS)) {
                                 $beforeIndex = $tokens->getIndex();
                                 if (!$tokens->seekTokenNum(T_STRING, TokenIterator::PREVIOUS)) {
                                     throw new MalformedSqlException("Missing constraing-symbol T_STRING after T_CONSTRAINT!", $tokens);
                                 }
                                 $indexJob->setContraintSymbol($tokens->getCurrentTokenString());
                                 $tokens->seekIndex($beforeIndex);
                             }
                             $needsReferenceDefinition = false;
                             switch ($tokens->getCurrentTokenNumber()) {
                                 case SqlToken::T_PRIMARY():
                                     $indexJob->setIsPrimary(true);
                                     $indexJob->setName("PRIMARY");
                                     if (!$tokens->seekTokenNum(SqlToken::T_KEY())) {
                                         throw new MalformedSqlException("Missing T_KEY after T_FOREIGN!", $tokens);
                                     }
                                     break;
                                 case SqlToken::T_UNIQUE():
                                     $indexJob->setIsUnique(true);
                                     $tokens->seekTokenNum(SqlToken::T_INDEX());
                                     break;
                                 case SqlToken::T_FOREIGN():
                                     if (!$tokens->seekTokenNum(SqlToken::T_KEY())) {
                                         throw new MalformedSqlException("Missing T_KEY after T_FOREIGN!", $tokens);
                                     }
                                     $needsReferenceDefinition = true;
                                     break;
                                 case SqlToken::T_FULLTEXT():
                                     $indexJob->setIsFullText(true);
                                     break;
                                 case SqlToken::T_SPATIAL():
                                     $indexJob->setIsSpatial(true);
                                     break;
                             }
                             if (!$indexJob->getIsPrimary() && $tokens->seekTokenNum(T_STRING)) {
                                 $indexJob->setName($tokens->getCurrentTokenString());
                             }
                             if ($tokens->seekTokenNum(T_STRING)) {
                                 $indexJob->setType(IndexType::factory(strtoupper($tokens->getCurrentTokenString())));
                             }
                             if (!$tokens->seekTokenText('(')) {
                                 throw new MalformedSqlException("Missing beginning parenthesis for defining columns for PRIMARY KEY index!", $tokens);
                             }
                             do {
                                 if (!$this->columnParser->canParseTokens($tokens)) {
                                     throw new MalformedSqlException("Invalid column-specifier in defining columns for PRIMARY KEY index!", $tokens);
                                 }
                                 $indexJob->addColumn($this->columnParser->convertSqlToJob($tokens));
                             } while ($tokens->seekTokenText(','));
                             if (!$tokens->seekTokenText(')')) {
                                 throw new MalformedSqlException("Missing ending parenthesis for defining columns for PRIMARY KEY index!", $tokens);
                             }
                             if ($needsReferenceDefinition) {
                                 if (!$tokens->seekTokenNum(SqlToken::T_REFERENCES())) {
                                     throw new MalformedSqlException("Missing reference-definition in foreign-constraint-definition!", $tokens);
                                 }
                                 if (!$this->tableParser->canParseTokens($tokens)) {
                                     throw new MalformedSqlException("Missing table-definition in foreign-constraint-definition!", $tokens);
                                 }
                                 $fkTable = $this->tableParser->convertSqlToJob($tokens);
                                 # columns in index
                                 if ($tokens->seekTokenText('(')) {
                                     do {
                                         if (!$this->columnParser->canParseTokens($tokens)) {
                                             throw new MalformedSqlException("Invalid column in column-list for defining index!", $tokens);
                                         }
                                         $fkColumn = $this->columnParser->convertSqlToJob($tokens);
                                         $indexJob->addForeignKey(Column::factory("{$fkTable}.{$fkColumn->getColumn()}"));
                                     } while ($tokens->seekTokenText(','));
                                     if (!$tokens->seekTokenText(')')) {
                                         throw new MalformedSqlException("Missing closing parenthesis at column-list for index!", $tokens);
                                     }
                                 }
                                 if ($tokens->seekTokenNum(SqlToken::T_MATCH())) {
                                     switch (true) {
                                         case $tokens->seekTokenNum(SqlToken::T_FULL()):
                                             $indexJob->setForeignKeyMatchType(MatchType::FULL());
                                             break;
                                         case $tokens->seekTokenNum(SqlToken::T_PARTIAL()):
                                             $indexJob->setForeignKeyMatchType(MatchType::PARTIAL());
                                             break;
                                         case $tokens->seekTokenNum(SqlToken::T_SIMPLE()):
                                             $indexJob->setForeignKeyMatchType(MatchType::SIMPLE());
                                             break;
                                         default:
                                             throw new MalformedSqlException("Invalid match parameter for foreign key!", $tokens);
                                     }
                                 }
                                 while ($tokens->seekTokenNum(SqlToken::T_ON())) {
                                     switch (true) {
                                         case $tokens->seekTokenNum(SqlToken::T_DELETE()):
                                             switch (true) {
                                                 case $tokens->seekTokenNum(SqlToken::T_RESTRICT()):
                                                     $indexJob->setForeignKeyOnDeleteReferenceOption(ReferenceOption::RESTRICT());
                                                     break;
                                                 case $tokens->seekTokenNum(SqlToken::T_CASCADE()):
                                                     $indexJob->setForeignKeyOnDeleteReferenceOption(ReferenceOption::CASCADE());
                                                     break;
                                                 case $tokens->seekTokenNum(SqlToken::T_SET()) && $tokens->seekTokenNum(SqlToken::T_NULL()):
                                                     $indexJob->setForeignKeyOnDeleteReferenceOption(ReferenceOption::SET_NULL());
                                                     break;
                                                 case $tokens->seekTokenNum(SqlToken::T_NO()) && $tokens->seekTokenText('ACTION'):
                                                     $indexJob->setForeignKeyOnDeleteReferenceOption(ReferenceOption::NO_ACTION());
                                                     break;
                                                 default:
                                                     throw new MalformedSqlException("Invalid reference-option for foreign key ON DELETE option!", $tokens);
                                             }
                                             break;
                                         case $tokens->seekTokenNum(SqlToken::T_UPDATE()):
                                             switch (true) {
                                                 case $tokens->seekTokenNum(SqlToken::T_RESTRICT()):
                                                     $indexJob->setForeignKeyOnUpdateReferenceOption(ReferenceOption::RESTRICT());
                                                     break;
                                                 case $tokens->seekTokenNum(SqlToken::T_CASCADE()):
                                                     $indexJob->setForeignKeyOnUpdateReferenceOption(ReferenceOption::CASCADE());
                                                     break;
                                                 case $tokens->seekTokenNum(SqlToken::T_SET()) && $tokens->seekTokenNum(SqlToken::T_NULL()):
                                                     $indexJob->setForeignKeyOnUpdateReferenceOption(ReferenceOption::SET_NULL());
                                                     break;
                                                 case $tokens->seekTokenNum(SqlToken::T_NO()) && $tokens->seekTokenText('ACTION'):
                                                     $indexJob->setForeignKeyOnUpdateReferenceOption(ReferenceOption::NO_ACTION());
                                                     break;
                                                 default:
                                                     throw new MalformedSqlException("Invalid reference-option for foreign key ON UPDATE option!", $tokens);
                                             }
                                             break;
                                         default:
                                             throw new MalformedSqlException("Invalid ON event for foreign key (allowed are UPDATE and DELETE)!", $tokens);
                                     }
                                 }
                             }
                             $dataChange->setAttribute(AlterAttributeType::ADD());
                             $dataChange->setSubjectIndex($indexJob);
                             $alterJob->addDataChange(clone $dataChange);
                             break;
                     }
                 } while ($isInParenthesises && $tokens->seekTokenText(','));
                 if ($isInParenthesises && !$tokens->seekTokenText(')')) {
                     throw new MalformedSqlException("Missing closing parenthesis after ALTER ADD statement!", $tokens);
                 }
                 break;
             case $tokens->seekTokenNum(SqlToken::T_ALTER()):
                 $tokens->seekTokenNum(SqlToken::T_COLUMN());
                 if (!$this->columnParser->canParseTokens($tokens)) {
                     throw new MalformedSqlException("Missing column-specification for ALTER COLUMN statement!", $tokens);
                 }
                 $dataChange->setAttribute(AlterAttributeType::DEFAULT_VALUE());
                 $dataChange->setSubject($this->columnParser->convertSqlToJob($tokens));
                 switch (true) {
                     case $tokens->seekTokenNum(SqlToken::T_SET()):
                         if (!$tokens->seekTokenNum(SqlToken::T_DEFAULT())) {
                             throw new MalformedSqlException("Missing T_DEFAULT for ALTER TABLE ALTER COLUMN SET DEFAULT statement", $tokens);
                         }
                         if (!$this->valueParser->canParseTokens($tokens)) {
                             throw new MalformedSqlException("Missing new valid value for DEFAULT value!");
                         }
                         $dataChange->setValue($this->valueParser->convertSqlToJob($tokens));
                         break;
                     case $tokens->seekTokenNum(SqlToken::T_DROP()):
                         if (!$tokens->seekTokenNum(SqlToken::T_DEFAULT())) {
                             throw new MalformedSqlException("Missing T_DEFAULT for ALTER TABLE ALTER COLUMN SET DEFAULT statement", $tokens);
                         }
                         $dataChange->setValue(null);
                         break;
                     default:
                         throw new MalformedSqlException("Invalid action (SET or DROP) for ALTER TABLE ALTER COLUMN statement!", $tokens);
                 }
                 $alterJob->addDataChange(clone $dataChange);
                 break;
             case $tokens->seekTokenNum(SqlToken::T_CHANGE()):
                 $dataChange->setAttribute(AlterAttributeType::MODIFY());
                 $tokens->seekTokenNum(SqlToken::T_COLUMN());
                 if (!$this->columnParser->canParseTokens($tokens)) {
                     throw new MalformedSqlException("Missing column-specification for ALTER TABLE CHANGE COLUMN statement!", $tokens);
                 }
                 $dataChange->setSubject($this->columnParser->convertSqlToJob($tokens));
                 if (!$this->columnDefinitionParser->canParseTokens($tokens)) {
                     throw new MalformedSqlException("Missing valid column-definiton for ALTER TABLE CHANGE COLUMN statement!", $tokens);
                 }
                 $dataChange->setValue($this->columnDefinitionParser->convertSqlToJob($tokens));
                 switch (true) {
                     case $tokens->seekTokenNum(SqlToken::T_FIRST()):
                         $dataChange->setAttribute(AlterAttributeType::SET_FIRST());
                         break;
                     case $tokens->seekTokenNum(SqlToken::T_AFTER()):
                         $dataChange->setAttribute(AlterAttributeType::SET_AFTER());
                         if (!$this->columnParser->canParseTokens($tokens)) {
                             throw new MalformedSqlException("Missing column specifier for ALTER TABLE CHANGE COLUMN AFTER statement!", $tokens);
                         }
                         $dataChange->setValue($this->columnParser->convertSqlToJob($tokens));
                         break;
                 }
                 $alterJob->addDataChange(clone $dataChange);
                 break;
             case $tokens->seekTokenNum(SqlToken::T_MODIFY()):
                 $dataChange->setAttribute(AlterAttributeType::MODIFY());
                 $tokens->seekTokenNum(SqlToken::T_COLUMN());
                 if (!$this->columnDefinitionParser->canParseTokens($tokens)) {
                     throw new MalformedSqlException("Missing valid column definition for ALTER TABLE MODIFY COLUMN statement!", $tokens);
                 }
                 $dataChange->setSubjectColumnDefinition($this->columnDefinitionParser->convertSqlToJob($tokens));
                 switch (true) {
                     case $tokens->seekTokenNum(SqlToken::T_FIRST()):
                         $dataChange->setAttribute(AlterAttributeType::SET_FIRST());
                         break;
                     case $tokens->seekTokenNum(SqlToken::T_AFTER()):
                         $dataChange->setAttribute(AlterAttributeType::SET_AFTER());
                         if (!$this->columnParser->canParseTokens($tokens)) {
                             throw new MalformedSqlException("Missing column specifier for ALTER TABLE MODIFY COLUMN AFTER statement!", $tokens);
                         }
                         $dataChange->setValue($this->columnParser->convertSqlToJob($tokens));
                         break;
                 }
                 $alterJob->addDataChange(clone $dataChange);
                 break;
             case $tokens->seekTokenNum(SqlToken::T_DROP()):
                 $dataChange->setAttribute(AlterAttributeType::DROP());
                 switch (true) {
                     case $tokens->seekTokenNum(SqlToken::T_COLUMN()):
                         if (!$this->columnParser->canParseTokens($tokens)) {
                             throw new MalformedSqlException("Missing valid column specificator for ALTER TABLE DROP COLUMN statement!", $tokens);
                         }
                         $dataChange->setSubject($this->columnParser->convertSqlToJob($tokens));
                         break;
                     case $this->columnParser->canParseTokens($tokens):
                         $dataChange->setSubject($this->columnParser->convertSqlToJob($tokens));
                         break;
                     case $tokens->seekTokenNum(SqlToken::T_PRIMARY()):
                         if (!$tokens->seekTokenNum(SqlToken::T_KEY())) {
                             throw new MalformedSqlException("Missing T_KEY after T_PRIMARY for ALTER TABLE DROP PRIMARY KEY statement!");
                         }
                         $dataChange->setSubject(Index::factory("PRIMARY"));
                         break;
                     case $tokens->seekTokenNum(SqlToken::T_FOREIGN()):
                         if (!$tokens->seekTokenNum(SqlToken::T_KEY())) {
                             throw new MalformedSqlException("Missing T_KEY after T_FOREIGN for ALTER TABLE DROP FOREIGN KEY statement!", $tokens);
                         }
                     case $tokens->seekTokenNum(SqlToken::T_INDEX()):
                         if (!$tokens->seekTokenNum(T_STRING)) {
                             throw new MalformedSqlException("Missing index name for ALTER TABLE DROP INDEX statement!", $tokens);
                         }
                         $dataChange->setSubject(Index::factory($tokens->getCurrentTokenString()));
                         break;
                 }
                 $alterJob->addDataChange(clone $dataChange);
                 break;
             case $tokens->seekTokenNum(SqlToken::T_DISABLE()):
                 $alterJob->setAction(Action::DISABLE());
                 if (!$tokens->seekTokenText(SqlToken::T_KEYS())) {
                     throw new MalformedSqlException("Missing T_KEYS after T_DISABLE!", $tokens);
                 }
                 break;
             case $tokens->seekTokenNum(SqlToken::T_ENABLE()):
                 $alterJob->setAction(Action::ENABLE());
                 if (!$tokens->seekTokenText(SqlToken::T_KEYS())) {
                     throw new MalformedSqlException("Missing T_KEYS after T_DISABLE!", $tokens);
                 }
                 break;
             case $tokens->seekTokenNum(SqlToken::T_RENAME()):
                 if (!$tokens->seekTokenNum(SqlToken::T_TO())) {
                     throw new MalformedSqlException("Missing T_TO after T_RENAME for ALTER TABLE RENAME TO statement!", $tokens);
                 }
                 if (!$tokens->seekTokenNum(T_STRING)) {
                     throw new MalformedSqlException("Missing new table-name for ALTER TABLE RENAME TO statement!", $tokens);
                 }
                 $dataChange->setAttribute(AlterAttributeType::RENAME());
                 $dataChange->setValue($tokens->getCurrentTokenString());
                 $alterJob->addDataChange(clone $dataChange);
                 break;
             case $tokens->seekTokenNum(SqlToken::T_ORDER()):
                 if (!$tokens->seekTokenNum(SqlToken::T_BY())) {
                     throw new MalformedSqlException("Missing BY after ORDER in ALTER TABLE ORDER BY statement!", $tokens);
                 }
                 if (!$this->columnParser->canParseTokens($tokens)) {
                     throw new MalformedSqlException("Missing column specifier for ALTER TABLE ORDER BY statement!", $tokens);
                 }
                 $dataChange->setSubject($this->columnParser->convertSqlToJob($tokens));
                 switch (true) {
                     case $tokens->seekTokenNum(SqlToken::T_DESC()):
                         $dataChange->setAttribute(AlterAttributeType::ORDER_BY_DESC());
                         break;
                     default:
                     case $tokens->seekTokenNum(SqlToken::T_ASC()):
                         $dataChange->setAttribute(AlterAttributeType::ORDER_BY_ASC());
                         break;
                 }
                 break;
             case $tokens->seekTokenNum(SqlToken::T_CHARACTER(), TokenIterator::NEXT, [SqlToken::T_DEFAULT()]):
             case $tokens->seekTokenNum(SqlToken::T_CHARACTER(), TokenIterator::NEXT, [SqlToken::T_CONVERT(), SqlToken::T_TO()]):
                 if (!$tokens->seekTokenNum(SqlToken::T_SET())) {
                     throw new MalformedSqlException("Missing T_SET after T_CHARACTER for ALTER TABLE CONVERT TO CHARACTER SET statement!", $tokens);
                 }
                 if (!$tokens->seekTokenNum(T_STRING)) {
                     throw new MalformedSqlException("Missing character-set specifier for ALTER TABLE CONVERT TO CHARACTER SET statement!", $tokens);
                 }
                 $dataChange->setAttribute(AlterAttributeType::CHARACTER_SET());
                 $dataChange->setValue($tokens->getCurrentTokenString());
                 $alterJob->addDataChange(clone $dataChange);
                 if ($tokens->seekTokenNum(SqlToken::T_COLLATE())) {
                     if (!$tokens->seekTokenNum(T_STRING)) {
                         throw new MalformedSqlException("Missing collation-specifier for ALTER TABLE CONVERT TO CHARACTER SET COLLATE statement!", $tokens);
                     }
                     $dataChange->setAttribute(AlterAttributeType::COLLATE());
                     $dataChange->setValue($tokens->getCurrentTokenString());
                     $alterJob->addDataChange(clone $dataChange);
                 }
                 break;
             case $tokens->seekTokenNum(SqlToken::T_DISCARD()):
             case $tokens->seekTokenNum(SqlToken::T_IMPORT()):
                 if (!$tokens->seekTokenNum(SqlToken::T_TABLESPACE())) {
                     throw new MalformedSqlException("Missing T_TABLESPACE after T_DISCARD or T_IMPORT!", $tokens);
                 }
                 break;
         }
     } while ($tokens->seekTokenText(','));
     return $alterJob;
 }
Exemplo n.º 2
0
 /**
  * This parses a CREATE TABLE statement.
  *
  * @param SQLTokenIterator $tokens
  * @throws MalformedSql
  */
 protected function parseCreateTable(SQLTokenIterator $tokens)
 {
     $createTableJob = new CreateTableStatement();
     $createTableJob->setIsTemporaryTable(is_int($tokens->isTokenNum(SqlToken::T_TEMPORARY(), TokenIterator::PREVIOUS)));
     # [IF NOT EXISTS]:
     if ($tokens->seekTokenNum(SqlToken::T_IF())) {
         if (!$tokens->seekTokenNum(SqlToken::T_NOT()) || !$tokens->seekTokenNum(SqlToken::T_EXISTS())) {
             throw new MalformedSqlException("Invalid create-database statement (invalid 'IF NOT EXISTS')!", $tokens);
         }
         $createTableJob->setIfNotExists(true);
     } else {
         $createTableJob->setIfNotExists(false);
     }
     # NAME
     if ($tokens->seekTokenNum(T_STRING)) {
         $createTableJob->setName($tokens->getCurrentTokenString());
     } elseif ($this->valueParser->canParseTokens($tokens)) {
         $createTableJob->setName($this->valueParser->convertSqlToJob($tokens));
     } else {
         throw new MalformedSqlException("Missing name of table to create!", $tokens);
     }
     # COLUMN DEFINITION
     $checkEndParenthesis = $tokens->seekTokenText('(');
     # LIKE other table?
     if ($tokens->seekTokenNum(SqlToken::T_LIKE())) {
         if (!$this->tableParser->canParseTokens($tokens)) {
             throw new MalformedSqlException("Missing valid table-specifier for 'CREATE TABLE LIKE' statement!", $tokens);
         }
         $createTableJob->setLikeTable($this->tableParser->convertSqlToJob($tokens));
     } elseif ($this->selectParser->canParseTokens($tokens)) {
         $createTableJob->setFromSelectStatement($this->selectParser->convertSqlToJob($tokens));
         # normal column definition
     } else {
         do {
             switch (true) {
                 # normal column definition
                 case $this->columnDefinitonParser->canParseTokens($tokens):
                     $createTableJob->addColumnDefinition($this->columnDefinitonParser->convertSqlToJob($tokens));
                     break;
                     # [CONSTRAINT [$keyName]] PRIMARY KEY [$keyType] ($column[, $column, ...])
                 # [CONSTRAINT [$keyName]] PRIMARY KEY [$keyType] ($column[, $column, ...])
                 case $tokens->seekTokenNum(SqlToken::T_PRIMARY(), TokenIterator::NEXT, [T_STRING, SqlToken::T_CONSTRAINT()]):
                     $indexJob = new IndexPart();
                     $indexJob->setIsPrimary(true);
                     if ($tokens->isTokenNum(SqlToken::T_CONSTRAINT(), TokenIterator::PREVIOUS, [T_STRING]) && $tokens->seekTokenNum(T_STRING, TokenIterator::PREVIOUS)) {
                         $indexJob->setContraintSymbol($tokens->getPreviousTokenString());
                         $tokens->seekTokenNum(SqlToken::T_PRIMARY());
                     }
                     $indexJob->setName("PRIMARY");
                     if (!$tokens->seekTokenNum(SqlToken::T_KEY())) {
                         throw new MalformedSqlException("Missing T_KEY for PRIMARY KEY constraint in create-table statement!", $tokens);
                     }
                     # define index type (BTREE, HASH, ...)
                     if ($tokens->seekTokenNum(T_STRING)) {
                         $indexJob->setType(IndexType::factory($tokens->getCurrentTokenString()));
                     } else {
                         $indexJob->setType(IndexType::BTREE());
                     }
                     # columns in index
                     if ($tokens->seekTokenText('(')) {
                         do {
                             if (!$this->columnParser->canParseTokens($tokens)) {
                                 throw new MalformedSqlException("Invalid column in column-list for defining index!", $tokens);
                             }
                             $indexJob->addColumn($this->columnParser->convertSqlToJob($tokens));
                         } while ($tokens->seekTokenText(','));
                         if (!$tokens->seekTokenText(')')) {
                             throw new MalformedSqlException("Missing closing parenthesis at column-list for index!", $tokens);
                         }
                     }
                     $createTableJob->addIndex($indexJob);
                     break;
                     # KEY|INDEX [index_name] [index_type] (index_col_name,...)
                 # KEY|INDEX [index_name] [index_type] (index_col_name,...)
                 case $tokens->seekTokenNum(SqlToken::T_INDEX()):
                 case $tokens->seekTokenNUm(SqlToken::T_KEY()):
                     /* @var $indexJob IndexPart */
                     $indexJob = new IndexPart();
                     if ($tokens->seekTokenNum(T_STRING)) {
                         $indexJob->setName($tokens->getCurrentTokenString());
                     } else {
                         $indexJob->setName(null);
                         # first column name is used
                     }
                     # define index type (BTREE, HASH, ...)
                     if ($tokens->seekTokenNum(T_STRING)) {
                         $indexJob->setType(IndexType::factory($tokens->getCurrentTokenString()));
                     } else {
                         $indexJob->setType(IndexType::BTREE());
                     }
                     # columns in index
                     if ($tokens->seekTokenText('(')) {
                         do {
                             if (!$this->columnParser->canParseTokens($tokens)) {
                                 throw new MalformedSqlException("Invalid column in column-list for defining index!", $tokens);
                             }
                             $indexJob->addColumn($this->columnParser->convertSqlToJob($tokens));
                         } while ($tokens->seekTokenText(','));
                         if (!$tokens->seekTokenText(')')) {
                             throw new MalformedSqlException("Missing closing parenthesis at column-list for index!", $tokens);
                         }
                     }
                     $createTableJob->addIndex($indexJob);
                     break;
                     # [CONSTRAINT [symbol]] UNIQUE|FULLTEXT|SPATIAL [INDEX] [index_name] [index_type] (index_col_name,...)
                 # [CONSTRAINT [symbol]] UNIQUE|FULLTEXT|SPATIAL [INDEX] [index_name] [index_type] (index_col_name,...)
                 case $tokens->seekTokenNum(SqlToken::T_UNIQUE(), TokenIterator::NEXT, [SqlToken::T_CONSTRAINT(), T_STRING]):
                 case $tokens->seekTokenNum(SqlToken::T_FULLTEXT(), TokenIterator::NEXT, [SqlToken::T_CONSTRAINT(), T_STRING]):
                 case $tokens->seekTokenNum(SqlToken::T_SPATIAL(), TokenIterator::NEXT, [SqlToken::T_CONSTRAINT(), T_STRING]):
                     /* @var $indexJob IndexPart */
                     $indexJob = new IndexPart();
                     switch ($tokens->getCurrentTokenNumber()) {
                         case SqlToken::T_UNIQUE():
                             $indexJob->setIsUnique(true);
                             break;
                         case SqlToken::T_FULLTEXT():
                             $indexJob->setIsFullText(true);
                             break;
                         case SqlToken::T_SPATIAL():
                             $indexJob->setIsSpatial(true);
                             break;
                     }
                     if ($tokens->isTokenNum(SqlToken::T_CONSTRAINT(), TokenIterator::PREVIOUS, [T_STRING]) && $tokens->seekTokenNum(T_STRING, TokenIterator::PREVIOUS)) {
                         $indexJob->setContraintSymbol($tokens->getPreviousTokenString());
                         $tokens->seekTokenNum(SqlToken::T_PRIMARY());
                     }
                     $tokens->seekTokenNum(SqlToken::T_KEY());
                     $tokens->seekTokenNum(SqlToken::T_INDEX());
                     if ($tokens->seekTokenNum(T_STRING)) {
                         $indexJob->setName($tokens->getCurrentTokenString());
                     } else {
                         $indexJob->setName(null);
                         # first column name is used
                     }
                     # define index type (BTREE, HASH, ...)
                     if ($tokens->seekTokenNum(T_STRING)) {
                         $indexJob->setType(IndexType::factory($tokens->getCurrentTokenString()));
                     } else {
                         $indexJob->setType(IndexType::BTREE());
                     }
                     # columns in index
                     if ($tokens->seekTokenText('(')) {
                         do {
                             if (!$this->columnParser->canParseTokens($tokens)) {
                                 throw new MalformedSqlException("Invalid column in column-list for defining index!", $tokens);
                             }
                             $indexJob->addColumn($this->columnParser->convertSqlToJob($tokens));
                         } while ($tokens->seekTokenText(','));
                         if (!$tokens->seekTokenText(')')) {
                             throw new MalformedSqlException("Missing closing parenthesis at column-list for index!", $tokens);
                         }
                     }
                     $createTableJob->addIndex($indexJob);
                     break;
                     # [CONSTRAINT [$symbol]] FOREIGN KEY [$name] ($column[, $column, ...]) [$reference]
                 # [CONSTRAINT [$symbol]] FOREIGN KEY [$name] ($column[, $column, ...]) [$reference]
                 case $tokens->seekTokenNum(SqlToken::T_FOREIGN(), TokenIterator::NEXT, [T_STRING, SqlToken::T_CONSTRAINT()]):
                     /* @var $indexJob IndexPart */
                     $indexJob = new IndexPart();
                     if ($tokens->isTokenNum(SqlToken::T_CONSTRAINT(), TokenIterator::PREVIOUS, [T_STRING]) && $tokens->seekTokenNum(T_STRING, TokenIterator::PREVIOUS)) {
                         $indexJob->setContraintSymbol($tokens->getCurrentTokenString());
                         $tokens->seekTokenNum(SqlToken::T_FOREIGN());
                     }
                     if (!$tokens->seekTokenNum(SqlToken::T_KEY())) {
                         throw new MalformedSqlException("Missing T_KEY after T_FOREIGN in constraint-definition!", $tokens);
                     }
                     if ($tokens->seekTokenNum(T_STRING)) {
                         $indexJob->setName($tokens->getCurrentTokenString());
                     } else {
                         $indexJob->setName(null);
                         # first column name is used
                     }
                     # columns in index
                     if ($tokens->seekTokenText('(')) {
                         do {
                             if (!$this->columnParser->canParseTokens($tokens)) {
                                 throw new MalformedSqlException("Invalid column in column-list for defining index!", $tokens);
                             }
                             $indexJob->addColumn($this->columnParser->convertSqlToJob($tokens));
                         } while ($tokens->seekTokenText(','));
                         if (!$tokens->seekTokenText(')')) {
                             throw new MalformedSqlException("Missing closing parenthesis at column-list for index!", $tokens);
                         }
                     }
                     if (!$tokens->seekTokenNum(SqlToken::T_REFERENCES())) {
                         throw new MalformedSqlException("Missing reference-definition in foreign-constraint-definition!", $tokens);
                     }
                     if (!$this->tableParser->canParseTokens($tokens)) {
                         throw new MalformedSqlException("Missing table-definition in foreign-constraint-definition!", $tokens);
                     }
                     $fkTable = $this->tableParser->convertSqlToJob($tokens);
                     # columns in index
                     if ($tokens->seekTokenText('(')) {
                         do {
                             if (!$this->columnParser->canParseTokens($tokens)) {
                                 throw new MalformedSqlException("Invalid column in column-list for defining index!", $tokens);
                             }
                             $fkColumn = $this->columnParser->convertSqlToJob($tokens);
                             $fkColumn = ColumnSpecifier::factory($fkTable . '.' . $fkColumn->getColumn());
                             $indexJob->addForeignKey($fkColumn);
                         } while ($tokens->seekTokenText(','));
                         if (!$tokens->seekTokenText(')')) {
                             throw new MalformedSqlException("Missing closing parenthesis at column-list for index!", $tokens);
                         }
                     }
                     if ($tokens->seekTokenNum(SqlToken::T_MATCH())) {
                         switch (true) {
                             case $tokens->seekTokenNum(SqlToken::T_FULL()):
                                 $indexJob->setForeignKeyMatchType(MatchType::FULL());
                                 break;
                             case $tokens->seekTokenNum(SqlToken::T_PARTIAL()):
                                 $indexJob->setForeignKeyMatchType(MatchType::PARTIAL());
                                 break;
                             case $tokens->seekTokenNum(SqlToken::T_SIMPLE()):
                                 $indexJob->setForeignKeyMatchType(MatchType::SIMPLE());
                                 break;
                             default:
                                 throw new MalformedSqlException("Invalid match parameter for foreign key!", $tokens);
                         }
                     }
                     while ($tokens->seekTokenNum(SqlToken::T_ON())) {
                         switch (true) {
                             case $tokens->seekTokenNum(SqlToken::T_DELETE()):
                                 switch (true) {
                                     case $tokens->seekTokenNum(SqlToken::T_RESTRICT()):
                                         $option = ReferenceOption::RESTRICT();
                                         break;
                                     case $tokens->seekTokenNum(SqlToken::T_CASCADE()):
                                         $option = ReferenceOption::CASCADE();
                                         break;
                                     case $tokens->seekTokenNum(SqlToken::T_SET()) && $tokens->seekTokenNum(SqlToken::T_NULL()):
                                         $option = ReferenceOption::SET_NULL();
                                         break;
                                     case $tokens->seekTokenNum(SqlToken::T_NO()) && $tokens->seekTokenText("ACTION"):
                                         $option = ReferenceOption::NO_ACTION();
                                         break;
                                     default:
                                         throw new MalformedSqlException("Invalid reference-option for foreign key ON DELETE option!", $tokens);
                                 }
                                 $indexJob->setForeignKeyOnDeleteReferenceOption($option);
                                 break;
                             case $tokens->seekTokenNum(SqlToken::T_UPDATE()):
                                 switch (true) {
                                     case $tokens->seekTokenNum(SqlToken::T_RESTRICT()):
                                         $option = ReferenceOption::RESTRICT();
                                         break;
                                     case $tokens->seekTokenNum(SqlToken::T_CASCADE()):
                                         $option = ReferenceOption::CASCADE();
                                         break;
                                     case $tokens->seekTokenNum(SqlToken::T_SET()) && $tokens->seekTokenNum(SqlToken::T_NULL()):
                                         $option = ReferenceOption::SET_NULL();
                                         break;
                                     case $tokens->seekTokenNum(SqlToken::T_NO()) && $tokens->seekTokenText("ACTION"):
                                         $option = ReferenceOption::NO_ACTION();
                                         break;
                                     default:
                                         throw new MalformedSqlException("Invalid reference-option for foreign key ON UPDATE option!", $tokens);
                                 }
                                 $indexJob->setForeignKeyOnUpdateReferenceOption($option);
                                 break;
                             default:
                                 throw new MalformedSqlException("Invalid ON event for foreign key (allowed are UPDATE and DELETE)!", $tokens);
                         }
                     }
                     $createTableJob->addIndex($indexJob);
                     break;
                     # CHECK (expression)
                 # CHECK (expression)
                 case $tokens->seekTokenNum(SqlToken::T_CHECK()):
                     if (!$this->conditionParser->canParseTokens($tokens)) {
                         throw new MalformedSqlException("Invalid CHECK condition statement!", $tokens);
                     }
                     $createTableJob->addCheck($this->conditionParser->convertSqlToJob($tokens));
                     break;
                 default:
                     throw new MalformedSqlException("Invalid definition in CREATE TABLE statement!", $tokens);
             }
         } while ($tokens->seekTokenText(','));
     }
     if ($checkEndParenthesis && !$tokens->seekTokenText(')')) {
         throw new MalformedSqlException("Missing closing parenthesis at end of table-definition!", $tokens);
     }
     ### TABLE OPTIONS
     while (true) {
         switch (true) {
             case $tokens->seekTokenNum(SqlToken::T_ENGINE()):
             case $tokens->seekTokenNum(SqlToken::T_TYPE()):
                 $tokens->seekTokenText('=');
                 if (!$tokens->seekTokenNum(T_STRING)) {
                     throw new MalformedSqlException("Missing T_STRING after T_ENGINE!", $tokens);
                 }
                 $createTableJob->setEngine(Engine::factory($tokens->getCurrentTokenString()));
                 break;
             case $tokens->seekTokenNum(SqlToken::T_AUTO_INCREMENT()):
                 $tokens->seekTokenText('=');
                 if (!$tokens->seekTokenNum(T_NUM_STRING)) {
                     throw new MalformedSqlException("Missing number-string for T_AUTO_INCREMENT!", $tokens);
                 }
                 $createTableJob->setAutoIncrement((int) $tokens->getCurrentTokenString());
                 break;
             case $tokens->seekTokenNum(SqlToken::T_AVG_ROW_LENGTH()):
                 $tokens->seekTokenText('=');
                 if (!$tokens->seekTokenNum(T_NUM_STRING)) {
                     throw new MalformedSqlException("Missing number-string for T_AVG_ROW_LENGTH!", $tokens);
                 }
                 $createTableJob->setAverageRowLength((int) $tokens->getCurrentTokenString());
                 break;
             case $tokens->seekTokenNum(SqlToken::T_CHARACTER(), TokenIterator::NEXT, [SqlToken::T_DEFAULT()]):
                 if (!$tokens->seekTokenNum(SqlToken::T_SET())) {
                     throw new MalformedSqlException("Missing SET after CHARACTER keyword!", $tokens);
                 }
             case $tokens->seekTokenNum(SqlToken::T_CHARSET(), TokenIterator::NEXT, [SqlToken::T_DEFAULT()]):
                 $tokens->seekTokenText('=');
                 if (!$tokens->seekTokenNum(T_CONSTANT_ENCAPSED_STRING) && !$tokens->seekTokenNum(T_STRING)) {
                     throw new MalformedSqlException("Missing string for CHARACTER SET!", $tokens);
                 }
                 $createTableJob->setCharacterSet($tokens->getCurrentTokenString());
                 break;
             case $tokens->seekTokenNum(SqlToken::T_COLLATE()):
                 $tokens->seekTokenText('=');
                 if (!$tokens->seekTokenNum(T_CONSTANT_ENCAPSED_STRING) && !$tokens->seekTokenNum(T_STRING)) {
                     throw new MalformedSqlException("Missing string for COLLATE!", $tokens);
                 }
                 $createTableJob->setCollate($tokens->getCurrentTokenString());
                 break;
             case $tokens->seekTokenNum(SqlToken::T_CHECKSUM()):
                 $tokens->seekTokenText('=');
                 switch (true) {
                     case $tokens->seekTokenText('0'):
                         $createTableJob->setUseChecksum(false);
                         break;
                     case $tokens->seekTokenText('1'):
                         $createTableJob->setUseChecksum(true);
                         break;
                     default:
                         throw new MalformedSqlException("Invalid value for CHECKSUM! (only 0 or 1 allowed!)", $tokens);
                 }
                 break;
             case $tokens->seekTokenNum(SqlToken::T_COMMENT()):
                 $tokens->seekTokenText('=');
                 if (!$tokens->seekTokenNum(T_CONSTANT_ENCAPSED_STRING)) {
                     throw new MalformedSqlException("Missing encapsed string for comment!", $tokens);
                 }
                 $createTableJob->setComment($tokens->getCurrentTokenString());
                 break;
             case $tokens->seekTokenNum(SqlToken::T_CONNECTION()):
                 $tokens->seekTokenText('=');
                 if (!$tokens->seekTokenNum(T_CONSTANT_ENCAPSED_STRING)) {
                     throw new MalformedSqlException("Missing encapsed string for connection-string!", $tokens);
                 }
                 $createTableJob->setConnectString($tokens->getCurrentTokenString());
                 break;
             case $tokens->seekTokenNum(SqlToken::T_MAX_ROWS()):
                 $tokens->seekTokenText('=');
                 if (!$tokens->seekTokenNum(T_NUM_STRING)) {
                     throw new MalformedSqlException("Missing number-string for MAX_ROWS!", $tokens);
                 }
                 $createTableJob->setMaximumRows((int) $tokens->getCurrentTokenString());
                 break;
             case $tokens->seekTokenNum(SqlToken::T_MIN_ROWS()):
                 $tokens->seekTokenText('=');
                 if (!$tokens->seekTokenNum(T_NUM_STRING)) {
                     throw new MalformedSqlException("Missing number-string for MIN_ROWS!", $tokens);
                 }
                 $createTableJob->setMinimumRows((int) $tokens->getCurrentTokenString());
                 break;
             case $tokens->seekTokenNum(SqlToken::T_PACK_KEYS()):
                 $tokens->seekTokenText('=');
                 switch (true) {
                     case $tokens->seekTokenText('DEFAULT'):
                     case $tokens->seekTokenText('0'):
                         $createTableJob->setDelayKeyWrite(false);
                         break;
                     case $tokens->seekTokenText('1'):
                         $createTableJob->setDelayKeyWrite(true);
                         break;
                     default:
                         throw new MalformedSqlException("Invalid value for PACK_KEYS! (only DEFAULT, 0 or 1 allowed!)", $tokens);
                 }
                 break;
             case $tokens->seekTokenNum(SqlToken::T_PASSWORD()):
                 $tokens->seekTokenText('=');
                 if (!$tokens->seekTokenNum(T_CONSTANT_ENCAPSED_STRING)) {
                     throw new MalformedSqlException("Missing encapsed string for password!", $tokens);
                 }
                 $createTableJob->setPassword($tokens->getCurrentTokenString());
                 break;
             case $tokens->seekTokenNum(SqlToken::T_DELAY_KEY_WRITE()):
                 $tokens->seekTokenText('=');
                 switch (true) {
                     case $tokens->seekTokenText('0'):
                         $createTableJob->setDelayKeyWrite(false);
                         break;
                     case $tokens->seekTokenText('1'):
                         $createTableJob->setDelayKeyWrite(true);
                         break;
                     default:
                         throw new MalformedSqlException("Invalid value for DELAY_KEY_WRITE! (only 0 or 1 allowed!)", $tokens);
                 }
                 break;
             case $tokens->seekTokenNum(SqlToken::T_ROW_FORMAT()):
                 $tokens->seekTokenText('=');
                 $keyword = $tokens->getExclusiveTokenString();
                 $rowFormat = RowFormat::factory($keyword);
                 $tokens->seekIndex($tokens->getExclusiveTokenIndex());
                 $createTableJob->setRowFormat($rowFormat);
                 break;
             case $tokens->seekTokenNum(SqlToken::T_UNION()):
                 $tokens->seekTokenText('=');
                 if (!$tokens->seekTokenText('(')) {
                     throw new MalformedSqlException("Missing opening parenthesis for union-table-definition!", $tokens);
                 }
                 do {
                     if (!$this->tableParser->canParseTokens($tokens)) {
                         throw new MalformedSqlException("Invalid table in table-list for defining union tables!", $tokens);
                     }
                     $createTableJob->addUnionTable($this->tableParser->convertSqlToJob($tokens));
                 } while ($tokens->seekTokenText(','));
                 if (!$tokens->seekTokenText(')')) {
                     throw new MalformedSqlException("Missing closing parenthesis at union-table-list!", $tokens);
                 }
                 break;
             case $tokens->seekTokenNum(SqlToken::T_INSERT_METHOD()):
                 $tokens->seekTokenText('=');
                 switch (true) {
                     case $tokens->seekTokenNum(SqlToken::T_NO()):
                         $createTableJob->setInsertMethod(InsertMethod::NO());
                         break;
                     case $tokens->seekTokenNum(SqlToken::T_FIRST()):
                         $createTableJob->setInsertMethod(InsertMethod::FIRST());
                         break;
                     case $tokens->seekTokenNum(SqlToken::T_LAST()):
                         $createTableJob->setInsertMethod(InsertMethod::LAST());
                         break;
                     default:
                         throw new MalformedSqlException("Invalid value given for insert-method (allowed are NO, FIRST and LAST)!");
                 }
                 break;
             case $tokens->seekTokenNum(SqlToken::T_DATA()):
                 $tokens->seekTokenText('=');
                 if (!$tokens->seekTokenNum(SqlToken::T_DIRECTORY())) {
                     throw new MalformedSqlException("Missing T_DIRECTORY after T_DATA for data-directory!", $tokens);
                 }
                 if (!$tokens->seekTokenNum(T_CONSTANT_ENCAPSED_STRING)) {
                     throw new MalformedSqlException("Missing encapsed string for comment!", $tokens);
                 }
                 $createTableJob->setDataDirectory($tokens->getCurrentTokenString());
                 break;
             case $tokens->seekTokenNum(SqlToken::T_INDEX()):
                 $tokens->seekTokenText('=');
                 if (!$tokens->seekTokenNum(SqlToken::T_DIRECTORY())) {
                     throw new MalformedSqlException("Missing T_DIRECTORY after T_INDEX for index-directory!", $tokens);
                 }
                 if (!$tokens->seekTokenNum(T_CONSTANT_ENCAPSED_STRING)) {
                     throw new MalformedSqlException("Missing encapsed string for comment!", $tokens);
                 }
                 $createTableJob->setIndexDirectory($tokens->getCurrentTokenString());
                 break;
             default:
                 break 2;
         }
     }
     return $createTableJob;
 }