Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -2152,7 +2152,7 @@ parameters:

-
message: "#^Dynamic call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertEquals\\(\\)\\.$#"
count: 35
count: 34
path: tests/Builder/CreateStatementTest.php

-
Expand Down
12 changes: 12 additions & 0 deletions src/Components/OptionsArray.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use function count;
use function implode;
use function is_array;
use function is_string;
use function ksort;
use function sprintf;
use function strcasecmp;
Expand Down Expand Up @@ -118,6 +119,17 @@ public static function parse(Parser $parser, TokensList $list, array $options =

if ($lastOption === null) {
$upper = strtoupper($token->token);
if (
isset($options['_ALLOW_QUOTED'])
&& $token->type === Token::TYPE_SYMBOL
&& ($token->flags & Token::FLAG_SYMBOL_BACKTICK) !== 0
&& is_string($token->value)
) {
// The name of the option is quoted (e.g. MariaDB quotes
// engine-defined table options like `PAGE_COMPRESSED`).
$upper = strtoupper($token->value);
}

if (! isset($options[$upper])) {
// There is no option to be processed.
break;
Expand Down
68 changes: 66 additions & 2 deletions src/Statements/CreateStatement.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,14 @@ class CreateStatement extends Statement
/**
* All table options.
*
* @var array<string, int|array<int, int|string>>
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
* @var array<string, bool|int|array<int, int|string>>
* @psalm-var array<string, (bool|positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
*/
public static $TABLE_OPTIONS = [
// Tells the `OptionsArray` to also accept quoted option names.
// MariaDB quotes engine-defined options in `SHOW CREATE TABLE`.
'_ALLOW_QUOTED' => true,

'ENGINE' => [
1,
'var=',
Expand Down Expand Up @@ -219,6 +223,66 @@ class CreateStatement extends Statement
23,
'var',
],
'PAGE_CHECKSUM' => [
24,
'var',
],
'TRANSACTIONAL' => [
25,
'var',
],
'ENCRYPTED' => [
26,
'var',
],
'ENCRYPTION_KEY_ID' => [
27,
'var',
],
'ENCRYPTION' => [
28,
'var',
],
'COMPRESSION' => [
29,
'var',
],
'STATS_AUTO_RECALC' => [
30,
'var',
],
'STATS_PERSISTENT' => [
31,
'var',
],
'STATS_SAMPLE_PAGES' => [
32,
'var',
],
'AUTOEXTEND_SIZE' => [
33,
'var',
],
'IETF_QUOTES' => [
34,
'var',
],
'SEQUENCE' => [
35,
'var',
],
'ENGINE_ATTRIBUTE' => [
36,
'var',
],
'SECONDARY_ENGINE_ATTRIBUTE' => [
37,
'var',
],
'ADAPTIVE_HASH_INDEX' => [
38,
'var',
],
];

/**
Expand Down
92 changes: 88 additions & 4 deletions tests/Builder/CreateStatementTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,100 @@ public function testBuilderWithComments(): void
);
}

public function testBuilderCompressed(): void
public function testBuilderQuotedTableOptions(): void
{
$parser = new Parser('CREATE TABLE users ( user_id int ) PAGE_COMPRESSED=1 PAGE_COMPRESSION_LEVEL=9;');
// MariaDB quotes engine-defined table options in SHOW CREATE TABLE.
$parser = new Parser(
'CREATE TABLE `t` ( `id` int(11) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4'
. " `PAGE_COMPRESSED`='ON' `PAGE_COMPRESSION_LEVEL`=9 `ENCRYPTED`=YES `ENCRYPTION_KEY_ID`=2;"
);
self::assertEmpty($parser->errors);
$stmt = $parser->statements[0];
$this->assertEquals(
"CREATE TABLE users (\n `user_id` int\n) PAGE_COMPRESSED=1 PAGE_COMPRESSION_LEVEL=9",
self::assertInstanceOf(CreateStatement::class, $stmt);
self::assertNotNull($stmt->entityOptions);
self::assertSame("'ON'", $stmt->entityOptions->has('PAGE_COMPRESSED', true));
self::assertEquals(
"CREATE TABLE `t` (\n `id` int(11) NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
. " PAGE_COMPRESSED='ON' PAGE_COMPRESSION_LEVEL=9 ENCRYPTED=YES ENCRYPTION_KEY_ID=2",
$stmt->build()
);
}

/**
* @return string[][]
*/
public static function tableOptionsProvider(): array
{
return [
['AUTO_INCREMENT=5', 'AUTO_INCREMENT=5'],
['AVG_ROW_LENGTH=100', 'AVG_ROW_LENGTH=100'],
['AVG_ROW_LENGTH 100', 'AVG_ROW_LENGTH 100'],
['CHARACTER SET=utf8mb4', 'CHARACTER SET=utf8mb4'],
['CHARACTER SET utf8mb4', 'CHARACTER SET=utf8mb4'],
['CHARSET=utf8mb4', 'CHARSET=utf8mb4'],
['DEFAULT CHARACTER SET=utf8mb4', 'DEFAULT CHARACTER SET=utf8mb4'],
['DEFAULT CHARSET=utf8mb4', 'DEFAULT CHARSET=utf8mb4'],
['CHECKSUM=1', 'CHECKSUM=1'],
['DEFAULT COLLATE=utf8mb4_bin', 'DEFAULT COLLATE=utf8mb4_bin'],
['COLLATE=utf8mb4_bin', 'COLLATE=utf8mb4_bin'],
["COMMENT='foo'", "COMMENT='foo'"],
["CONNECTION='mysql://user@host/db/t'", "CONNECTION='mysql://user@host/db/t'"],
["DATA DIRECTORY = '/tmp/data'", "DATA DIRECTORY='/tmp/data'"],
['DELAY_KEY_WRITE=1', 'DELAY_KEY_WRITE=1'],
["INDEX DIRECTORY='/tmp/index'", "INDEX DIRECTORY='/tmp/index'"],
['INSERT_METHOD=LAST', 'INSERT_METHOD=LAST'],
['KEY_BLOCK_SIZE=8', 'KEY_BLOCK_SIZE=8'],
['MAX_ROWS=1000', 'MAX_ROWS=1000'],
['MIN_ROWS=10', 'MIN_ROWS=10'],
['PACK_KEYS=DEFAULT', 'PACK_KEYS=DEFAULT'],
["PASSWORD='secret'", "PASSWORD='secret'"],
['ROW_FORMAT=DYNAMIC', 'ROW_FORMAT=DYNAMIC'],
['TABLESPACE `ts` STORAGE DISK', 'TABLESPACE `ts` STORAGE DISK'],
['UNION = (`t1`, `t2`)', 'UNION=(`t1`, `t2`)'],
['PAGE_COMPRESSED=1', 'PAGE_COMPRESSED=1'],
["`PAGE_COMPRESSED`='ON'", "PAGE_COMPRESSED='ON'"],
['PAGE_COMPRESSION_LEVEL=9', 'PAGE_COMPRESSION_LEVEL=9'],
['PAGE_CHECKSUM=1', 'PAGE_CHECKSUM=1'],
['TRANSACTIONAL=1', 'TRANSACTIONAL=1'],
['ENCRYPTED=YES', 'ENCRYPTED=YES'],
['ENCRYPTION_KEY_ID=2', 'ENCRYPTION_KEY_ID=2'],
["ENCRYPTION='Y'", "ENCRYPTION='Y'"],
["COMPRESSION='zlib'", "COMPRESSION='zlib'"],
['STATS_AUTO_RECALC=1', 'STATS_AUTO_RECALC=1'],
['STATS_PERSISTENT=0', 'STATS_PERSISTENT=0'],
['STATS_SAMPLE_PAGES=25', 'STATS_SAMPLE_PAGES=25'],
['AUTOEXTEND_SIZE=4M', 'AUTOEXTEND_SIZE=4M'],
['IETF_QUOTES=YES', 'IETF_QUOTES=YES'],
['SEQUENCE=1', 'SEQUENCE=1'],
["ENGINE_ATTRIBUTE='{}'", "ENGINE_ATTRIBUTE='{}'"],
["SECONDARY_ENGINE_ATTRIBUTE='{}'", "SECONDARY_ENGINE_ATTRIBUTE='{}'"],
['ADAPTIVE_HASH_INDEX=YES', 'ADAPTIVE_HASH_INDEX=YES'],
];
}

/**
* @dataProvider tableOptionsProvider
*/
#[DataProvider('tableOptionsProvider')]
public function testBuilderTableOption(string $option, string $expected): void
{
// The trailing ENGINE option checks that the option does not consume the following tokens.
$parser = new Parser('CREATE TABLE `t` ( `id` int ) ' . $option . ' ENGINE=InnoDB;');
self::assertEmpty($parser->errors);
self::assertSame(
"CREATE TABLE `t` (\n `id` int\n) ENGINE=InnoDB " . $expected,
$parser->statements[0]->build()
);
}

public function testBuilderQuotedNameIsNotAnOption(): void
{
// Quoted option names are only accepted for table options.
$parser = new Parser('SELECT `DISTINCT` FROM `t`');
self::assertEmpty($parser->errors);
self::assertSame('SELECT `DISTINCT` FROM `t`', $parser->statements[0]->build());
}

public function testBuilderCollate(): void
{
$parser = new Parser(
Expand Down
Loading