Submit
Path:
~
/
/
opt
/
psa
/
phpMyAdmin
/
vendor
/
phpmyadmin
/
sql-parser
/
src
/
Statements
/
File Content:
CreateStatement.php
<?php declare(strict_types=1); namespace PhpMyAdmin\SqlParser\Statements; use PhpMyAdmin\SqlParser\Components\ArrayObj; use PhpMyAdmin\SqlParser\Components\CreateDefinition; use PhpMyAdmin\SqlParser\Components\DataType; use PhpMyAdmin\SqlParser\Components\Expression; use PhpMyAdmin\SqlParser\Components\OptionsArray; use PhpMyAdmin\SqlParser\Components\ParameterDefinition; use PhpMyAdmin\SqlParser\Components\PartitionDefinition; use PhpMyAdmin\SqlParser\Parser; use PhpMyAdmin\SqlParser\Statement; use PhpMyAdmin\SqlParser\Token; use PhpMyAdmin\SqlParser\TokensList; use function is_array; use function trim; /** * `CREATE` statement. */ class CreateStatement extends Statement { /** * Options for `CREATE` statements. * * @var array<string, int|array<int, int|string>> * @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})> */ public static $OPTIONS = [ // CREATE TABLE 'TEMPORARY' => 1, // CREATE VIEW 'OR REPLACE' => 2, 'ALGORITHM' => [ 3, 'var=', ], // `DEFINER` is also used for `CREATE FUNCTION / PROCEDURE` 'DEFINER' => [ 4, 'expr=', ], // Used in `CREATE VIEW` 'SQL SECURITY' => [ 5, 'var', ], 'DATABASE' => 6, 'EVENT' => 6, 'FUNCTION' => 6, 'INDEX' => 6, 'UNIQUE INDEX' => 6, 'FULLTEXT INDEX' => 6, 'SPATIAL INDEX' => 6, 'PROCEDURE' => 6, 'SERVER' => 6, 'TABLE' => 6, 'TABLESPACE' => 6, 'TRIGGER' => 6, 'USER' => 6, 'VIEW' => 6, 'SCHEMA' => 6, // CREATE TABLE 'IF NOT EXISTS' => 7, ]; /** * All database options. * * @var array<string, int|array<int, int|string>> * @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})> */ public static $DB_OPTIONS = [ 'CHARACTER SET' => [ 1, 'var=', ], 'CHARSET' => [ 1, 'var=', ], 'DEFAULT CHARACTER SET' => [ 1, 'var=', ], 'DEFAULT CHARSET' => [ 1, 'var=', ], 'DEFAULT COLLATE' => [ 2, 'var=', ], 'COLLATE' => [ 2, 'var=', ], ]; /** * All table options. * * @var array<string, int|array<int, int|string>> * @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})> */ public static $TABLE_OPTIONS = [ 'ENGINE' => [ 1, 'var=', ], 'AUTO_INCREMENT' => [ 2, 'var=', ], 'AVG_ROW_LENGTH' => [ 3, 'var', ], 'CHARACTER SET' => [ 4, 'var=', ], 'CHARSET' => [ 4, 'var=', ], 'DEFAULT CHARACTER SET' => [ 4, 'var=', ], 'DEFAULT CHARSET' => [ 4, 'var=', ], 'CHECKSUM' => [ 5, 'var', ], 'DEFAULT COLLATE' => [ 6, 'var=', ], 'COLLATE' => [ 6, 'var=', ], 'COMMENT' => [ 7, 'var=', ], 'CONNECTION' => [ 8, 'var', ], 'DATA DIRECTORY' => [ 9, 'var', ], 'DELAY_KEY_WRITE' => [ 10, 'var', ], 'INDEX DIRECTORY' => [ 11, 'var', ], 'INSERT_METHOD' => [ 12, 'var', ], 'KEY_BLOCK_SIZE' => [ 13, 'var', ], 'MAX_ROWS' => [ 14, 'var', ], 'MIN_ROWS' => [ 15, 'var', ], 'PACK_KEYS' => [ 16, 'var', ], 'PASSWORD' => [ 17, 'var', ], 'ROW_FORMAT' => [ 18, 'var', ], 'TABLESPACE' => [ 19, 'var', ], 'STORAGE' => [ 20, 'var', ], 'UNION' => [ 21, 'var', ], 'PAGE_COMPRESSED' => [ 22, 'var', ], 'PAGE_COMPRESSION_LEVEL' => [ 23, 'var', ], ]; /** * All function options. * * @var array<string, int|array<int, int|string>> * @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})> */ public static $FUNC_OPTIONS = [ 'NOT' => [ 2, 'var', ], 'FUNCTION' => [ 3, 'var=', ], 'PROCEDURE' => [ 3, 'var=', ], 'CONTAINS SQL' => 4, 'NO SQL' => 4, 'READS SQL DATA' => 4, 'MODIFIES SQL DATA' => 4, 'SQL SECURITY' => [ 6, 'var', ], 'LANGUAGE' => [ 7, 'var', ], 'COMMENT' => [ 8, 'var', ], 'CREATE' => 1, 'DETERMINISTIC' => 2, ]; /** * All trigger options. * * @var array<string, int|array<int, int|string>> * @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})> */ public static $TRIGGER_OPTIONS = [ 'BEFORE' => 1, 'AFTER' => 1, 'INSERT' => 2, 'UPDATE' => 2, 'DELETE' => 2, ]; /** * The name of the entity that is created. * * Used by all `CREATE` statements. * * @var Expression|null */ public $name; /** * The options of the entity (table, procedure, function, etc.). * * Used by `CREATE TABLE`, `CREATE FUNCTION` and `CREATE PROCEDURE`. * * @see static::$TABLE_OPTIONS * @see static::$FUNC_OPTIONS * @see static::$TRIGGER_OPTIONS * * @var OptionsArray|null */ public $entityOptions; /** * If `CREATE TABLE`, a list of columns and keys. * If `CREATE VIEW`, a list of columns. * * Used by `CREATE TABLE` and `CREATE VIEW`. * * @var CreateDefinition[]|ArrayObj|null */ public $fields; /** * If `CREATE TABLE WITH`. * If `CREATE TABLE AS WITH`. * If `CREATE VIEW AS WITH`. * * Used by `CREATE TABLE`, `CREATE VIEW` * * @var WithStatement|null */ public $with; /** * If `CREATE TABLE ... SELECT`. * If `CREATE VIEW AS ` ... SELECT`. * * Used by `CREATE TABLE`, `CREATE VIEW` * * @var SelectStatement|null */ public $select; /** * If `CREATE TABLE ... LIKE`. * * Used by `CREATE TABLE` * * @var Expression|null */ public $like; /** * Expression used for partitioning. * * @var string|null */ public $partitionBy; /** * The number of partitions. * * @var int|null */ public $partitionsNum; /** * Expression used for subpartitioning. * * @var string|null */ public $subpartitionBy; /** * The number of subpartitions. * * @var int|null */ public $subpartitionsNum; /** * The partition of the new table. * * @var PartitionDefinition[]|null */ public $partitions; /** * If `CREATE TRIGGER` the name of the table. * * Used by `CREATE TRIGGER`. * * @var Expression|null */ public $table; /** * The return data type of this routine. * * Used by `CREATE FUNCTION`. * * @var DataType|null */ public $return; /** * The parameters of this routine. * * Used by `CREATE FUNCTION` and `CREATE PROCEDURE`. * * @var ParameterDefinition[]|null */ public $parameters; /** * The body of this function or procedure. * For views, it is the select statement that creates the view. * Used by `CREATE FUNCTION`, `CREATE PROCEDURE` and `CREATE VIEW`. * * @var Token[]|string */ public $body = []; /** * @return string */ public function build() { $fields = ''; if (! empty($this->fields)) { if (is_array($this->fields)) { $fields = CreateDefinition::build($this->fields) . ' '; } elseif ($this->fields instanceof ArrayObj) { $fields = ArrayObj::build($this->fields); } } if ($this->options->has('DATABASE') || $this->options->has('SCHEMA')) { return 'CREATE ' . OptionsArray::build($this->options) . ' ' . Expression::build($this->name) . ' ' . OptionsArray::build($this->entityOptions); } if ($this->options->has('TABLE')) { if ($this->select !== null) { return 'CREATE ' . OptionsArray::build($this->options) . ' ' . Expression::build($this->name) . ' ' . $this->select->build(); } if ($this->like !== null) { return 'CREATE ' . OptionsArray::build($this->options) . ' ' . Expression::build($this->name) . ' LIKE ' . Expression::build($this->like); } if ($this->with !== null) { return 'CREATE ' . OptionsArray::build($this->options) . ' ' . Expression::build($this->name) . ' ' . $this->with->build(); } $partition = ''; if (! empty($this->partitionBy)) { $partition .= "\nPARTITION BY " . $this->partitionBy; } if (! empty($this->partitionsNum)) { $partition .= "\nPARTITIONS " . $this->partitionsNum; } if (! empty($this->subpartitionBy)) { $partition .= "\nSUBPARTITION BY " . $this->subpartitionBy; } if (! empty($this->subpartitionsNum)) { $partition .= "\nSUBPARTITIONS " . $this->subpartitionsNum; } if (! empty($this->partitions)) { $partition .= "\n" . PartitionDefinition::build($this->partitions); } return 'CREATE ' . OptionsArray::build($this->options) . ' ' . Expression::build($this->name) . ' ' . $fields . OptionsArray::build($this->entityOptions) . $partition; } elseif ($this->options->has('VIEW')) { $builtStatement = ''; if ($this->select !== null) { $builtStatement = $this->select->build(); } elseif ($this->with !== null) { $builtStatement = $this->with->build(); } return 'CREATE ' . OptionsArray::build($this->options) . ' ' . Expression::build($this->name) . ' ' . $fields . ' AS ' . $builtStatement . (! empty($this->body) ? TokensList::build($this->body) : '') . ' ' . OptionsArray::build($this->entityOptions); } elseif ($this->options->has('TRIGGER')) { return 'CREATE ' . OptionsArray::build($this->options) . ' ' . Expression::build($this->name) . ' ' . OptionsArray::build($this->entityOptions) . ' ' . 'ON ' . Expression::build($this->table) . ' ' . 'FOR EACH ROW ' . TokensList::build($this->body); } elseif ($this->options->has('PROCEDURE') || $this->options->has('FUNCTION')) { $tmp = ''; if ($this->options->has('FUNCTION')) { $tmp = 'RETURNS ' . DataType::build($this->return); } return 'CREATE ' . OptionsArray::build($this->options) . ' ' . Expression::build($this->name) . ' ' . ParameterDefinition::build($this->parameters) . ' ' . $tmp . ' ' . OptionsArray::build($this->entityOptions) . ' ' . TokensList::build($this->body); } return 'CREATE ' . OptionsArray::build($this->options) . ' ' . Expression::build($this->name) . ' ' . TokensList::build($this->body); } /** * @param Parser $parser the instance that requests parsing * @param TokensList $list the list of tokens to be parsed */ public function parse(Parser $parser, TokensList $list) { ++$list->idx; // Skipping `CREATE`. // Parsing options. $this->options = OptionsArray::parse($parser, $list, static::$OPTIONS); ++$list->idx; // Skipping last option. $isDatabase = $this->options->has('DATABASE') || $this->options->has('SCHEMA'); $fieldName = $isDatabase ? 'database' : 'table'; // Parsing the field name. $this->name = Expression::parse( $parser, $list, [ 'parseField' => $fieldName, 'breakOnAlias' => true, ] ); if (! isset($this->name) || ($this->name === '')) { $parser->error('The name of the entity was expected.', $list->tokens[$list->idx]); } else { ++$list->idx; // Skipping field. } /** * Token parsed at this moment. */ $token = $list->tokens[$list->idx]; $nextidx = $list->idx + 1; while ($nextidx < $list->count && $list->tokens[$nextidx]->type === Token::TYPE_WHITESPACE) { ++$nextidx; } if ($isDatabase) { $this->entityOptions = OptionsArray::parse($parser, $list, static::$DB_OPTIONS); } elseif ($this->options->has('TABLE')) { if (($token->type === Token::TYPE_KEYWORD) && ($token->keyword === 'SELECT')) { /* CREATE TABLE ... SELECT */ $this->select = new SelectStatement($parser, $list); } elseif ($token->type === Token::TYPE_KEYWORD && ($token->keyword === 'WITH')) { /* CREATE TABLE WITH */ $this->with = new WithStatement($parser, $list); } elseif ( ($token->type === Token::TYPE_KEYWORD) && ($token->keyword === 'AS') && ($list->tokens[$nextidx]->type === Token::TYPE_KEYWORD) ) { if ($list->tokens[$nextidx]->value === 'SELECT') { /* CREATE TABLE ... AS SELECT */ $list->idx = $nextidx; $this->select = new SelectStatement($parser, $list); } elseif ($list->tokens[$nextidx]->value === 'WITH') { /* CREATE TABLE WITH */ $list->idx = $nextidx; $this->with = new WithStatement($parser, $list); } } elseif ($token->type === Token::TYPE_KEYWORD && $token->keyword === 'LIKE') { /* CREATE TABLE `new_tbl` LIKE 'orig_tbl' */ $list->idx = $nextidx; $this->like = Expression::parse( $parser, $list, [ 'parseField' => 'table', 'breakOnAlias' => true, ] ); // The 'LIKE' keyword was found, but no table_name was found next to it if ($this->like === null) { $parser->error('A table name was expected.', $list->tokens[$list->idx]); } } else { $this->fields = CreateDefinition::parse($parser, $list); if (empty($this->fields)) { $parser->error('At least one column definition was expected.', $list->tokens[$list->idx]); } ++$list->idx; $this->entityOptions = OptionsArray::parse($parser, $list, static::$TABLE_OPTIONS); /** * The field that is being filled (`partitionBy` or * `subpartitionBy`). * * @var string */ $field = null; /** * The number of brackets. `false` means no bracket was found * previously. At least one bracket is required to validate the * expression. * * @var int|bool */ $brackets = false; /* * Handles partitions. */ for (; $list->idx < $list->count; ++$list->idx) { /** * Token parsed at this moment. */ $token = $list->tokens[$list->idx]; // End of statement. if ($token->type === Token::TYPE_DELIMITER) { break; } // Skipping comments. if ($token->type === Token::TYPE_COMMENT) { continue; } if (($token->type === Token::TYPE_KEYWORD) && ($token->keyword === 'PARTITION BY')) { $field = 'partitionBy'; $brackets = false; } elseif (($token->type === Token::TYPE_KEYWORD) && ($token->keyword === 'SUBPARTITION BY')) { $field = 'subpartitionBy'; $brackets = false; } elseif (($token->type === Token::TYPE_KEYWORD) && ($token->keyword === 'PARTITIONS')) { $token = $list->getNextOfType(Token::TYPE_NUMBER); --$list->idx; // `getNextOfType` also advances one position. $this->partitionsNum = $token->value; } elseif (($token->type === Token::TYPE_KEYWORD) && ($token->keyword === 'SUBPARTITIONS')) { $token = $list->getNextOfType(Token::TYPE_NUMBER); --$list->idx; // `getNextOfType` also advances one position. $this->subpartitionsNum = $token->value; } elseif (! empty($field)) { /* * Handling the content of `PARTITION BY` and `SUBPARTITION BY`. */ // Counting brackets. if ($token->type === Token::TYPE_OPERATOR) { if ($token->value === '(') { // This is used instead of `++$brackets` because, // initially, `$brackets` is `false` cannot be // incremented. $brackets += 1; } elseif ($token->value === ')') { --$brackets; } } // Building the expression used for partitioning. $this->$field .= $token->type === Token::TYPE_WHITESPACE ? ' ' : $token->token; // Last bracket was read, the expression ended. // Comparing with `0` and not `false`, because `false` means // that no bracket was found and at least one must is // required. if ($brackets === 0) { $this->$field = trim($this->$field); $field = null; } } elseif (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) { if (! empty($this->partitionBy)) { $this->partitions = ArrayObj::parse( $parser, $list, ['type' => 'PhpMyAdmin\\SqlParser\\Components\\PartitionDefinition'] ); } break; } } } } elseif ($this->options->has('PROCEDURE') || $this->options->has('FUNCTION')) { $this->parameters = ParameterDefinition::parse($parser, $list); if ($this->options->has('FUNCTION')) { $prevToken = $token; $token = $list->getNextOfType(Token::TYPE_KEYWORD); if ($token === null || $token->keyword !== 'RETURNS') { $parser->error('A "RETURNS" keyword was expected.', $token ?? $prevToken); } else { ++$list->idx; $this->return = DataType::parse($parser, $list); } } ++$list->idx; $this->entityOptions = OptionsArray::parse($parser, $list, static::$FUNC_OPTIONS); ++$list->idx; for (; $list->idx < $list->count; ++$list->idx) { $token = $list->tokens[$list->idx]; if ($token->type === Token::TYPE_DELIMITER) { break; } $this->body[] = $token; } } elseif ($this->options->has('VIEW')) { /** @var Token $token */ $token = $list->getNext(); // Skipping whitespaces and comments. // Parsing columns list. if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) { --$list->idx; // getNext() also goes forward one field. $this->fields = ArrayObj::parse($parser, $list); ++$list->idx; // Skipping last token from the array. $list->getNext(); } // Parsing the SELECT expression if the view started with it. if ( $token->type === Token::TYPE_KEYWORD && $token->keyword === 'AS' && $list->tokens[$nextidx]->type === Token::TYPE_KEYWORD ) { if ($list->tokens[$nextidx]->value === 'SELECT') { $list->idx = $nextidx; $this->select = new SelectStatement($parser, $list); ++$list->idx; // Skipping last token from the select. } elseif ($list->tokens[$nextidx]->value === 'WITH') { ++$list->idx; $this->with = new WithStatement($parser, $list); } } // Parsing all other tokens for (; $list->idx < $list->count; ++$list->idx) { $token = $list->tokens[$list->idx]; if ($token->type === Token::TYPE_DELIMITER) { break; } $this->body[] = $token; } } elseif ($this->options->has('TRIGGER')) { // Parsing the time and the event. $this->entityOptions = OptionsArray::parse($parser, $list, static::$TRIGGER_OPTIONS); ++$list->idx; $list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'ON'); ++$list->idx; // Skipping `ON`. // Parsing the name of the table. $this->table = Expression::parse( $parser, $list, [ 'parseField' => 'table', 'breakOnAlias' => true, ] ); ++$list->idx; $list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'FOR EACH ROW'); ++$list->idx; // Skipping `FOR EACH ROW`. for (; $list->idx < $list->count; ++$list->idx) { $token = $list->tokens[$list->idx]; if ($token->type === Token::TYPE_DELIMITER) { break; } $this->body[] = $token; } } else { for (; $list->idx < $list->count; ++$list->idx) { $token = $list->tokens[$list->idx]; if ($token->type === Token::TYPE_DELIMITER) { break; } $this->body[] = $token; } } } }
Submit
FILE
FOLDER
INFO
Name
Size
Permission
Action
AlterStatement.php
4590 bytes
0644
AnalyzeStatement.php
744 bytes
0644
BackupStatement.php
631 bytes
0644
CallStatement.php
716 bytes
0644
CheckStatement.php
632 bytes
0644
ChecksumStatement.php
553 bytes
0644
CreateStatement.php
24805 bytes
0644
DeleteStatement.php
11457 bytes
0644
DropStatement.php
1647 bytes
0644
ExplainStatement.php
9290 bytes
0644
InsertStatement.php
7373 bytes
0644
KillStatement.php
5582 bytes
0644
LoadStatement.php
11341 bytes
0644
LockStatement.php
3443 bytes
0644
MaintenanceStatement.php
1501 bytes
0644
NotImplementedStatement.php
1329 bytes
0644
OptimizeStatement.php
748 bytes
0644
PurgeStatement.php
3825 bytes
0644
RenameStatement.php
1391 bytes
0644
RepairStatement.php
674 bytes
0644
ReplaceStatement.php
5091 bytes
0644
RestoreStatement.php
580 bytes
0644
SelectStatement.php
8536 bytes
0644
SetStatement.php
2417 bytes
0644
ShowStatement.php
1386 bytes
0644
TransactionStatement.php
2525 bytes
0644
TruncateStatement.php
854 bytes
0644
UpdateStatement.php
3989 bytes
0644
WithStatement.php
11754 bytes
0644
N4ST4R_ID | Naxtarrr