Exemple #1
0
 public function convertSqlToJob(SQLTokenIterator $tokens)
 {
     // catch both cases when select is current AND when its next token.
     $tokens->seekTokenNum(SqlToken::T_SELECT());
     if ($tokens->getCurrentTokenNumber() !== SqlToken::T_SELECT()) {
         throw new ErrorException("Tried to convert select-sql to job when sql-token-iterator does not point to T_SELECT!");
     }
     $entitySelect = new SelectStatement();
     ### SPECIAL FLAGS
     foreach ([[SpecialFlags::FLAG_ALL(), SqlToken::T_ALL()], [SpecialFlags::FLAG_DISTINCT(), SqlToken::T_DISTINCT()], [SpecialFlags::FLAG_DISTINCTROW(), SqlToken::T_DISTINCTROW()], [SpecialFlags::FLAG_HIGH_PRIORITY(), SqlToken::T_HIGH_PRIORITY()], [SpecialFlags::FLAG_STRAIGHT_JOIN(), SqlToken::T_STRAIGHT_JOIN()], [SpecialFlags::FLAG_SQL_SMALL_RESULT(), SqlToken::T_SQL_SMALL_RESULT()], [SpecialFlags::FLAG_SQL_BIG_RESULT(), SqlToken::T_SQL_BIG_RESULT()], [SpecialFlags::FLAG_SQL_BUFFER_RESULT(), SqlToken::T_SQL_BUFFER_RESULT()], [SpecialFlags::FLAG_SQL_CACHE(), SqlToken::T_SQL_CACHE()], [SpecialFlags::FLAG_SQL_NO_CACHE(), SqlToken::T_SQL_NO_CACHE()], [SpecialFlags::FLAG_SQL_CALC_FOUND_ROWS(), SqlToken::T_SQL_CALC_FOUND_ROWS()]] as $pair) {
         list($flagValue, $tokenNum) = $pair;
         if ($tokens->seekTokenNum($tokenNum)) {
             $entitySelect->addFlag($flagValue);
         }
     }
     ### COLLECT COLUMNS
     do {
         try {
             switch (true) {
                 # parse jokers like: fooTable.*
                 case is_int($tokens->isTokenText('*', TokenIterator::NEXT, [T_STRING, '.'])):
                     if ($this->tableParser->canParseTokens($tokens)) {
                         $tableFilter = $this->tableParser->convertSqlToJob($tokens);
                     } else {
                         $tableFilter = null;
                     }
                     $tokens->seekTokenText('*', TokenIterator::NEXT, [T_STRING, '.']);
                     $entitySelect->addColumnAllTable($tableFilter);
                     break;
                 case $this->valueParser->canParseTokens($tokens):
                     $value = $this->valueParser->convertSqlToJob($tokens);
                     if ($tokens->seekTokenNum(T_STRING, TokenIterator::NEXT, [SqlToken::T_AS()])) {
                         $entitySelect->addColumnValue($value, $tokens->getCurrentTokenString());
                     } else {
                         $entitySelect->addColumnValue($value);
                     }
                     break;
                 default:
                     throw new MalformedSqlException("Non-column-sql found in column-part of select!", $tokens);
             }
         } catch (MalformedSqlException $exception) {
             throw new MalformedSqlException($exception->getMessage(), $tokens);
         }
     } while ($tokens->seekTokenText(','));
     ### COLLECT TABLES
     if ($tokens->seekTokenNum(SqlToken::T_FROM())) {
         if (!$this->joinParser->canParseTokens($tokens)) {
             throw new MalformedSqlException("Missing valid join definition after FROM in SELECT statement!", $tokens);
         }
         $entitySelect->setJoinDefinition($this->joinParser->convertSqlToJob($tokens));
     }
     ### PREPENDED CONDITION (WHERE)
     if ($tokens->seekTokenNum(SqlToken::T_WHERE())) {
         if (!$this->valueParser->canParseTokens($tokens)) {
             throw new MalformedSqlException("Missing condition for WHERE clause in SELECT statement!", $tokens);
         }
         $entitySelect->setCondition($this->valueParser->convertSqlToJob($tokens));
     }
     ### GROUP
     if ($tokens->seekTokenNum(SqlToken::T_GROUP())) {
         if (!$tokens->seekTokenNum(SqlToken::T_BY())) {
             throw new MalformedSqlException("Missing BY after GROUP in SELECT statement!", $tokens);
         }
         do {
             $groupingDefinition = new GroupingDefinition();
             if (!$this->columnParser->canParseTokens($tokens)) {
                 throw new MalformedSqlException("Invalid grouping value in SELECT statement!!", $tokens);
             }
             $groupingDefinition->setValue($this->columnParser->convertSqlToJob($tokens));
             if ($tokens->seekTokenNum(SqlToken::T_DESC())) {
                 $groupingDefinition->setDirection(SqlToken::T_DESC());
             } elseif ($tokens->seekTokenNum(SqlToken::T_ASC())) {
                 $groupingDefinition->setDirection(SqlToken::T_ASC());
             }
             $entitySelect->addGrouping($groupingDefinition);
         } while ($tokens->seekTokenText(','));
     }
     ### APPENDED CONDITION (HAVING)
     if ($tokens->seekTokenNum(SqlToken::T_HAVING())) {
         if (!$this->valueParser->canParseTokens($tokens)) {
             throw new MalformedSqlException("Missing condition for WHERE clause in SELECT statement!", $tokens);
         }
         $condition = new ConditionJob();
         $condition->setFirstParameter($this->valueParser->convertSqlToJob($tokens));
         $entitySelect->setResultFilter($condition);
     }
     ### ORDER
     if ($tokens->seekTokenNum(SqlToken::T_ORDER())) {
         if (!$tokens->seekTokenNum(SqlToken::T_BY())) {
             throw new MalformedSqlException("Missing BY after ORDER on SELECT statement!", $tokens);
         }
         do {
             if (!$this->valueParser->canParseTokens($tokens)) {
                 throw new MalformedSqlException("Missing value for ORDER BY part on SELECT statement!", $tokens);
             }
             $orderValue = $this->valueParser->convertSqlToJob($tokens);
             if ($tokens->seekTokenNum(SqlToken::T_DESC())) {
                 $entitySelect->addOrderColumn($orderValue, SqlToken::T_DESC());
             } else {
                 $tokens->seekTokenNum(SqlToken::T_ASC());
                 $entitySelect->addOrderColumn($orderValue, SqlToken::T_ASC());
             }
         } while ($tokens->seekTokenText(','));
     }
     ### LIMIT
     if ($tokens->seekTokenNum(SqlToken::T_LIMIT())) {
         if (!$tokens->seekTokenNum(T_NUM_STRING)) {
             throw new MalformedSqlException("Missing offset number for LIMIT part in SELECT statement!", $tokens);
         }
         $entitySelect->setLimitOffset((int) $tokens->getCurrentTokenString());
         if ($tokens->seekTokenText(',')) {
             if (!$tokens->seekTokenNum(T_NUM_STRING)) {
                 throw new MalformedSqlException("Missing length number for LIMIT part in SELECT statement!", $tokens);
             }
             $entitySelect->setLimitRowCount((int) $tokens->getCurrentTokenString());
         }
     }
     ### PROCEDURE
     if ($tokens->seekTokenNum(SqlToken::T_PROCEDURE())) {
         if (!$functionParser->canParseTokens($tokens)) {
             throw new MalformedSqlException("Missing valid procedure specifier after PROCEDURE!", $tokens);
         }
         $entitySelect->setProcedure($functionParser->convertSqlToJob($tokens));
     }
     ### INTO OUTFILE|DUMPFILE
     if ($tokens->seekTokenNum(SqlToken::T_INTO())) {
         if (!$tokens->seekTokenNum(SqlToken::T_OUTFILE()) && !$tokens->seekTokenNum(SqlToken::T_DUMPFILE())) {
             throw new MalformedSqlException("Missing OUTFILE or DUMPFILE after INTO!", $tokens);
         }
         if (!$tokens->seekTokenNum(T_CONSTANT_ENCAPSED_STRING)) {
             throw new MalformedSqlException("Missing escaped string after INTO OUTFILE!");
         }
         $entitySelect->setIntoOutFile($tokens->seekTokenText($searchToken));
     }
     ### FOR UPDATE
     if ($tokens->seekTokenNum(SqlToken::T_FOR())) {
         if (!$tokens->seekTokenNum(SqlToken::T_UPDATE())) {
             throw new MalformedSqlException("Missing UPDATE after FOR on FOR UPDATE parameter in SELECT statement!", $tokens);
         }
         $entitySelect->setIsForUpdate(true);
     }
     ### LOCK IN SHARE MODE
     if ($tokens->seekTokenNum(SqlToken::T_LOCK())) {
         if (!$tokens->seekTokenNum(SqlToken::T_IN())) {
             throw new MalformedSqlException("Missing UPDATE after FOR on FOR UPDATE parameter in SELECT statement!", $tokens);
         }
         if (!$tokens->seekTokenNum(SqlToken::T_SHARE())) {
             throw new MalformedSqlException("Missing UPDATE after FOR on FOR UPDATE parameter in SELECT statement!", $tokens);
         }
         if (!$tokens->seekTokenNum(SqlToken::T_MODE())) {
             throw new MalformedSqlException("Missing UPDATE after FOR on FOR UPDATE parameter in SELECT statement!", $tokens);
         }
         $entitySelect->setIsLockInShareMode(true);
     }
     ### UNION
     if ($tokens->seekTokenNum(SqlToken::T_UNION())) {
         $isUnionAll = $tokens->seekTokenNum(SqlToken::T_ALL());
         $isUnionDistinct = $tokens->seekTokenNum(SqlToken::T_DISTINCT());
         $isUnionAll = $isUnionAll || $tokens->seekTokenNum(SqlToken::T_ALL());
         if ($isUnionAll && $isUnionDistinct) {
             throw new MalformedSqlException("UNION cannot be ALL and DISTINCT at the same time!", $tokens);
         }
         $isUnionInParenthesis = $tokens->seekTokenText('(');
         if (!$this->canParseTokens($tokens)) {
             throw new MalformedSqlException("Missing following SELECT statement after UNION in SELECT statement!", $tokens);
         }
         $entitySelect->setUnionSelect($this->convertSqlToJob($tokens), $isUnionDistinct);
         if ($isUnionInParenthesis && !$tokens->seekTokenText(')')) {
             throw new MalformedSqlException("Missing ending parenthesis after UNION in SELECT statement!", $tokens);
         }
     }
     return $entitySelect;
 }
Exemple #2
0
 public function convertSqlToJob(SQLTokenIterator $tokens, $from = TokenIterator::NEXT)
 {
     if (!$tokens->seekTokenText('(', $from)) {
         throw new ErrorException("Tried to parse sql-parenthesis when token-iterator does not point to paranthesis ('(' sign)!");
     }
     /* @var $parenthesis ParenthesisPart */
     $parenthesis = new ParenthesisPart();
     switch (true) {
         case $this->selectParser->canParseTokens($tokens):
             $parenthesis->setContain($this->selectParser->convertSqlToJob($tokens));
             break;
         case $this->valueParser->canParseTokens($tokens):
             $parenthesis->setContain($this->valueParser->convertSqlToJob($tokens));
             break;
     }
     if (!$tokens->seekTokenText(')')) {
         throw new MalformedSqlException("Missing ')' at the end of a parenthesis!", $tokens);
     }
     if ($tokens->seekTokenNum(T_STRING, TokenIterator::NEXT, [SqlToken::T_AS()])) {
         $parenthesis->setAlias($tokens->getCurrentTokenString());
     }
     if ($parenthesis->getContain() instanceof Select && $tokens->isTokenNum(SqlToken::T_UNION())) {
         $unionSelect = new Select();
         while ($tokens->seekTokenNum(SqlToken::T_UNION())) {
             /* @var $lastUnionedSelect Select */
             $lastUnionedSelect = $parenthesis->getContain();
             while (!is_null($lastUnionedSelect->getUnionSelect())) {
                 $lastUnionedSelect = $lastUnionedSelect->getUnionSelect();
             }
             $isUnionAll = $tokens->seekTokenNum(SqlToken::T_ALL());
             $isUnionDistinct = $tokens->seekTokenNum(SqlToken::T_DISTINCT());
             $isUnionAll = $isUnionAll || $tokens->seekTokenNum(SqlToken::T_ALL());
             if ($isUnionAll && $isUnionDistinct) {
                 throw new MalformedSqlException("UNION cannot be ALL and DISTINCT at the same time!", $tokens);
             }
             $isUnionInParenthesis = $tokens->seekTokenText('(');
             if (!$this->selectParser->canParseTokens($tokens)) {
                 throw new MalformedSqlException("Missing following SELECT statement after UNION in SELECT statement!", $tokens);
             }
             $lastUnionedSelect->setUnionSelect($this->selectParser->convertSqlToJob($tokens), $isUnionDistinct);
             if ($isUnionInParenthesis && !$tokens->seekTokenText(')')) {
                 throw new MalformedSqlException("Missing ending parenthesis after UNION in SELECT statement!", $tokens);
             }
         }
         $unionSelect->setUnionSelect($parenthesis->getContain());
         ### APPENDED CONDITION (HAVING)
         if ($tokens->seekTokenNum(SqlToken::T_HAVING())) {
             if (!$this->valueParser->canParseTokens($tokens)) {
                 throw new MalformedSqlException("Missing condition for WHERE clause in SELECT statement!", $tokens);
             }
             $condition = new Condition();
             $condition->setFirstParameter($this->valueParser->convertSqlToJob($tokens));
             $unionSelect->setResultFilter($condition);
         }
         ### ORDER
         if ($tokens->seekTokenNum(SqlToken::T_ORDER())) {
             if (!$tokens->seekTokenNum(SqlToken::T_BY())) {
                 throw new MalformedSqlException("Missing BY after ORDER on SELECT statement!", $tokens);
             }
             do {
                 if (!$this->valueParser->canParseTokens($tokens)) {
                     throw new MalformedSqlException("Missing value for ORDER BY part on SELECT statement!", $tokens);
                 }
                 $orderValue = $this->valueParser->convertSqlToJob($tokens);
                 if ($tokens->seekTokenNum(SqlToken::T_DESC())) {
                     $unionSelect->addOrderColumn($orderValue, SqlToken::T_DESC());
                 } else {
                     $tokens->seekTokenNum(SqlToken::T_ASC());
                     $unionSelect->addOrderColumn($orderValue, SqlToken::T_ASC());
                 }
             } while ($tokens->seekTokenText(','));
         }
         ### LIMIT
         if ($tokens->seekTokenNum(SqlToken::T_LIMIT())) {
             if (!$tokens->seekTokenNum(T_NUM_STRING)) {
                 throw new MalformedSqlException("Missing offset number for LIMIT part in SELECT statement!", $tokens);
             }
             $unionSelect->setLimitOffset((int) $tokens->getCurrentTokenString());
             if ($tokens->seekTokenText(',')) {
                 if (!$tokens->seekTokenNum(T_NUM_STRING)) {
                     throw new MalformedSqlException("Missing length number for LIMIT part in SELECT statement!", $tokens);
                 }
                 $unionSelect->setLimitRowCount((int) $tokens->getCurrentTokenString());
             }
         }
         $parenthesis->setContain($unionSelect);
     }
     return $parenthesis;
 }
Exemple #3
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;
 }