public function getTablePage($tableId)
 {
     if (is_numeric($tableId)) {
         $tableId = array_keys($this->columnsMap)[$tableId];
     }
     $entity = new DatabaseSchemaPage();
     $entity->setName($tableId);
     $entity->setCollation("latin1_bin");
     $entity->setEngine(Engine::INFORMATION_SCHEMA());
     $entity->setType(Type::TABLE());
     return $entity;
 }
Example #2
0
 public function getTableFactory(Engine $engine)
 {
     if (!isset($this->tableFactories[(string) $engine])) {
         throw new ErrorException("Missing table-factory for table-engine '{$engine->getName()}'!");
     }
     return $this->tableFactories[(string) $engine];
 }
Example #3
0
 public function executeJob(StatementJob $statement, array $parameters = array())
 {
     /* @var $statement CreateTableStatement */
     $context = new ExecutionContext($this->schemaManager, $statement, $parameters);
     /* @var $databaseSchema Schema */
     $databaseSchema = $this->schemaManager->getSchema();
     $schemaId = $this->schemaManager->getCurrentlyUsedDatabaseId();
     $tableName = $this->valueResolver->resolveValue($statement->getName(), $context);
     $schemaPage = new DatabaseSchemaPage();
     $schemaPage->setName($tableName);
     $schemaPage->setType(Type::TABLE());
     $schemaPage->setEngine(Engine::factory($statement->getEngine()->getName()));
     $schemaPage->setCollation(strtoupper($statement->getCollate()));
     $schemaPage->setUseChecksum($statement->getUseChecksum());
     $schemaPage->setMaxRows($statement->getMaximumRows());
     $schemaPage->setMinRows($statement->getMinimumRows());
     $schemaPage->setPackKeys($statement->getPackKeys());
     $schemaPage->setDelayKeyWrite($statement->getDelayKeyWrite());
     $schemaPage->setRowFormat(RowFormat::factory($statement->getRowFormat()));
     $schemaPage->setInsertMethod(InsertMethod::factory($statement->getInsertMethod()));
     $databaseSchema->registerTableSchema($schemaPage);
     /* @var $tableSchema TableSchema */
     $tableSchema = $this->schemaManager->getTableSchema($tableName);
     assert($tableSchema instanceof TableSchema);
     ### WRITE COLUMNS
     switch (true) {
         case is_array($statement->getColumnDefinition()):
             foreach ($statement->getColumnDefinition() as $name => $column) {
                 /* @var $column ColumnDefinition */
                 $columnPage = new ColumnSchema();
                 $columnPage->setName($name);
                 $columnPage->setDataType(DataType::factory($column->getDataType()->getName()));
                 /* @var $dataType DataType */
                 $dataType = $columnPage->getDataType();
                 $flags = 0;
                 if ($column->getIsPrimaryKey()) {
                     $flags = $flags ^ ColumnSchema::EXTRA_PRIMARY_KEY;
                 }
                 if ($column->getIsUnique()) {
                     $flags = $flags ^ ColumnSchema::EXTRA_UNIQUE_KEY;
                 }
                 if (!$column->getIsNullable()) {
                     $flags = $flags ^ ColumnSchema::EXTRA_NOT_NULL;
                 }
                 if ($column->getIsAutoIncrement()) {
                     $flags = $flags ^ ColumnSchema::EXTRA_AUTO_INCREMENT;
                 }
                 $columnPage->setExtraFlags($flags);
                 /* @var $dataType DataType */
                 $dataType = $column->getDataType();
                 $columnPage->setLength($dataType->getByteLength());
                 $columnPage->setSecondLength($dataType->getSecondByteLength());
                 if (!is_null($column->getDataTypeLength())) {
                     $columnPage->setLength($column->getDataTypeLength());
                 }
                 if (!is_null($column->getDataTypeSecondLength())) {
                     $columnPage->setSecondLength($column->getDataTypeSecondLength());
                 }
                 if (!is_null($column->getDefaultValue()) && !$columnPage->isDefaultValueInFile()) {
                     $columnPage->setDefaultValue($column->getDefaultValue());
                 }
                 $columnIndex = $tableSchema->addColumnSchema($columnPage);
                 if (!is_null($column->getDefaultValue()) && $columnPage->isDefaultValueInFile()) {
                     $defaultValueFilepath = sprintf(FilePathes::FILEPATH_DEFAULT_VALUE, $schemaId, $tableName, $columnIndex);
                     $this->tableManager->getFilesystem()->putFileContents($defaultValueFilepath, $column->getDefaultValue());
                 }
             }
             break;
         case $statement->getColumnDefinition() instanceof Select:
             break;
         case $statement->getColumnDefinition() instanceof TableInterface:
             break;
     }
     foreach ($statement->getIndexes() as $indexName => $index) {
         /* @var $index Index */
         $indexSchemaPage = new IndexSchema();
         $indexSchemaPage->setName($indexName);
         $indexSchemaPage->setEngine(IndexEngine::BTREE());
         switch (true) {
             case $index->getIsPrimary():
                 $indexSchemaPage->setType(IndexType::PRIMARY());
                 break;
             case $index->getIsUnique():
                 $indexSchemaPage->setType(IndexType::UNIQUE());
                 break;
             default:
                 $indexSchemaPage->setType(IndexType::INDEX());
                 break;
         }
         $method = ForeignKeyMethod::factory($index->getForeignKeyOnDeleteReferenceOption()->getName());
         $indexSchemaPage->setForeignKeyOnDeleteMethod($method);
         $method = ForeignKeyMethod::factory($index->getForeignKeyOnUpdateReferenceOption()->getName());
         $indexSchemaPage->setForeignKeyOnUpdateMethod($method);
         $keyLength = 0;
         $columns = array();
         foreach ($index->getColumns() as $indexColumnName) {
             $indexColumnId = $tableSchema->getColumnIndex((string) $indexColumnName);
             if (strlen($indexColumnId) <= 0) {
                 throw new ErrorException("Could not find index column '{$indexColumnName}' in table-schema!");
             }
             $columns[] = $indexColumnId;
             $keyLength += $statement->getColumnDefinition()[(string) $indexColumnName]->getDataSize();
         }
         $indexSchemaPage->setColumns($columns);
         $indexSchemaPage->setKeyLength($keyLength);
         $tableSchema->addIndexSchema($indexSchemaPage);
     }
     foreach ($this->tableManager->getTableFactories() as $tableFactory) {
         /* @var $tableFactory TableFactoryInterface */
         if ($tableFactory instanceof InformationSchemaTableFactory) {
             $tableFactory->clearCache();
         }
     }
     ### RESULT
     $result = new TemporaryResult();
     $result->setIsSuccess(true);
     return $result;
 }
Example #4
0
 public function getEngine()
 {
     if (is_null($this->engine)) {
         $this->engine = Engine::MYISAM();
     }
     return $this->engine;
 }
Example #5
0
 public function setData($data)
 {
     if (!is_string($data) || strlen($data) !== self::PAGE_SIZE) {
         throw new InvalidArgumentException("Invalid page-data '{$data}' given!");
     }
     $rawName = substr($data, 0, 64);
     $rawType = substr($data, 64, 2);
     $rawCollate = substr($data, 66, 8);
     $rawUseChecksum = $data[74];
     $rawMaxRows = substr($data, 75, 8);
     $rawMinRows = substr($data, 83, 8);
     $rawPackKeys = $data[91];
     $rawDelayKeyWrite = $data[92];
     $rawRowFormat = substr($data, 93, 2);
     $rawInsertMethod = substr($data, 95, 2);
     $name = rtrim($rawName, "");
     $type = unpack("n", $rawType)[1];
     $collate = rtrim($rawCollate, "");
     $useChecksum = ord($rawUseChecksum) === 0x0 ? false : true;
     $rawMaxRows = ltrim($rawMaxRows, "");
     $rawMinRows = ltrim($rawMinRows, "");
     $packKeys = ord($rawPackKeys) === 0x0 ? false : true;
     $delayKeyWrite = ord($rawDelayKeyWrite) === 0x0 ? false : true;
     $rowFormat = unpack("n", $rawRowFormat)[1];
     $insertMethod = unpack("n", $rawInsertMethod)[1];
     // convert any binary to integer
     $maxRows = hexdec(implode("", array_map(function ($chr) {
         return dechex(ord($chr));
     }, str_split($rawMaxRows))));
     // convert any binary to integer
     $minRows = hexdec(implode("", array_map(function ($chr) {
         return dechex(ord($chr));
     }, str_split($rawMinRows))));
     $this->setName($name);
     $this->setEngine(Engine::getByValue($type));
     $this->setCollation($collate);
     $this->setUseChecksum($useChecksum);
     $this->setMaxRows($maxRows);
     $this->setMinRows($minRows);
     $this->setPackKeys($packKeys);
     $this->setDelayKeyWrite($delayKeyWrite);
     $this->setRowFormat(RowFormat::getByValue($rowFormat));
     $this->setInsertMethod(InsertMethod::getByValue($insertMethod));
 }
Example #6
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;
 }
 public function getTableManager()
 {
     if (is_null($this->tableManager)) {
         $this->tableManager = new TableManager($this->getFilesystem(), $this->getSchemaManager());
         $columnDataFactory = new ColumnDataFactory($this->getFilesystem());
         $tableFactory = new TableFactory($this->getFilesystem(), $columnDataFactory);
         foreach ([Engine::MARIADB(), Engine::MYISAM(), Engine::INNODB()] as $engine) {
             $this->tableManager->registerFactory($engine, $tableFactory);
         }
         $this->tableManager->registerFactory(Engine::INFORMATION_SCHEMA(), new InformationSchemaTableFactory($this->schemaManager));
         # TODO: add factories for all the other table-engines out there
     }
     return $this->tableManager;
 }