Skip to content

Commit d94bd98

Browse files
committed
Merge #381 - Fix #329 Support ALTER queries of PARTITIONS
Fixes: #329 Pull-request: #381 Signed-off-by: William Desportes <[email protected]>
2 parents 6ccc924 + edbf59e commit d94bd98

File tree

5 files changed

+139
-20
lines changed

5 files changed

+139
-20
lines changed

phpstan-baseline.neon

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,17 @@ parameters:
1111
path: src/Components/AlterOperation.php
1212

1313
-
14-
message: "#^Parameter \\#1 \\$key of function array_key_exists expects int\\|string, float\\|int\\|string given\\.$#"
15-
count: 2
16-
path: src/Components/AlterOperation.php
17-
18-
-
19-
message: "#^Parameter \\#1 \\$tokenValue of static method PhpMyAdmin\\\\SqlParser\\\\Components\\\\AlterOperation\\:\\:checkIfColumnDefinitionKeyword\\(\\) expects string, float\\|int\\|string given\\.$#"
14+
message: "#^Parameter \\#1 \\$component of static method PhpMyAdmin\\\\SqlParser\\\\Components\\\\PartitionDefinition\\:\\:build\\(\\) expects array\\<PhpMyAdmin\\\\SqlParser\\\\Components\\\\PartitionDefinition\\>\\|PhpMyAdmin\\\\SqlParser\\\\Components\\\\PartitionDefinition, array\\<PhpMyAdmin\\\\SqlParser\\\\Component\\>\\|PhpMyAdmin\\\\SqlParser\\\\Components\\\\ArrayObj given\\.$#"
2015
count: 1
2116
path: src/Components/AlterOperation.php
2217

2318
-
24-
message: "#^Property PhpMyAdmin\\\\SqlParser\\\\Components\\\\AlterOperation\\:\\:\\$field \\(PhpMyAdmin\\\\SqlParser\\\\Components\\\\Expression\\) does not accept PhpMyAdmin\\\\SqlParser\\\\Components\\\\Expression\\|null\\.$#"
19+
message: "#^Parameter \\#1 \\$key of function array_key_exists expects int\\|string, float\\|int\\|string given\\.$#"
2520
count: 2
2621
path: src/Components/AlterOperation.php
2722

2823
-
29-
message: "#^Property PhpMyAdmin\\\\SqlParser\\\\Components\\\\AlterOperation\\:\\:\\$field \\(PhpMyAdmin\\\\SqlParser\\\\Components\\\\Expression\\) in isset\\(\\) is not nullable\\.$#"
24+
message: "#^Parameter \\#1 \\$tokenValue of static method PhpMyAdmin\\\\SqlParser\\\\Components\\\\AlterOperation\\:\\:checkIfColumnDefinitionKeyword\\(\\) expects string, float\\|int\\|string given\\.$#"
3025
count: 1
3126
path: src/Components/AlterOperation.php
3227

@@ -35,11 +30,6 @@ parameters:
3530
count: 1
3631
path: src/Components/AlterOperation.php
3732

38-
-
39-
message: "#^Result of && is always true\\.$#"
40-
count: 1
41-
path: src/Components/AlterOperation.php
42-
4333
-
4434
message: "#^array\\<PhpMyAdmin\\\\SqlParser\\\\Token\\>\\|string does not accept PhpMyAdmin\\\\SqlParser\\\\Token\\.$#"
4535
count: 3

src/Components/AlterOperation.php

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,6 @@ class AlterOperation extends Component
106106
'MODIFY' => 1,
107107
'OPTIMIZE' => 1,
108108
'ORDER' => 1,
109-
'PARTITION' => 1,
110109
'REBUILD' => 1,
111110
'REMOVE' => 1,
112111
'RENAME' => 1,
@@ -123,6 +122,8 @@ class AlterOperation extends Component
123122
'FULLTEXT' => 2,
124123
'KEY' => 2,
125124
'KEYS' => 2,
125+
'PARTITION' => 2,
126+
'PARTITION BY' => 2,
126127
'PARTITIONING' => 2,
127128
'PRIMARY KEY' => 2,
128129
'SPATIAL' => 2,
@@ -191,10 +192,17 @@ class AlterOperation extends Component
191192
/**
192193
* The altered field.
193194
*
194-
* @var Expression
195+
* @var Expression|string|null
195196
*/
196197
public $field;
197198

199+
/**
200+
* The partitions.
201+
*
202+
* @var Component[]|ArrayObj|null
203+
*/
204+
public $partitions;
205+
198206
/**
199207
* Unparsed tokens.
200208
*
@@ -203,15 +211,18 @@ class AlterOperation extends Component
203211
public $unknown = [];
204212

205213
/**
206-
* @param OptionsArray $options options of alter operation
207-
* @param Expression $field altered field
208-
* @param Token[] $unknown unparsed tokens found at the end of operation
214+
* @param OptionsArray $options options of alter operation
215+
* @param Expression|string|null $field altered field
216+
* @param Component[]|ArrayObj|null $partitions partitions definition found in the operation
217+
* @param Token[] $unknown unparsed tokens found at the end of operation
209218
*/
210219
public function __construct(
211220
$options = null,
212221
$field = null,
222+
$partitions = null,
213223
$unknown = []
214224
) {
225+
$this->partitions = $partitions;
215226
$this->options = $options;
216227
$this->field = $field;
217228
$this->unknown = $unknown;
@@ -244,12 +255,21 @@ public static function parse(Parser $parser, TokensList $list, array $options =
244255
*
245256
* 1 ----------------------[ field ]----------------------> 2
246257
*
258+
* 1 -------------[ PARTITION / PARTITION BY ]------------> 3
259+
*
247260
* 2 -------------------------[ , ]-----------------------> 0
248261
*
249262
* @var int
250263
*/
251264
$state = 0;
252265

266+
/**
267+
* partition state.
268+
*
269+
* @var int
270+
*/
271+
$partitionState = 0;
272+
253273
for (; $list->idx < $list->count; ++$list->idx) {
254274
/**
255275
* Token parsed at this moment.
@@ -272,9 +292,8 @@ public static function parse(Parser $parser, TokensList $list, array $options =
272292
// When parsing the unknown part, the whitespaces are
273293
// included to not break anything.
274294
$ret->unknown[] = $token;
295+
continue;
275296
}
276-
277-
continue;
278297
}
279298

280299
if ($state === 0) {
@@ -293,6 +312,10 @@ public static function parse(Parser $parser, TokensList $list, array $options =
293312
}
294313

295314
$state = 1;
315+
if ($ret->options->has('PARTITION') || $token->value === 'PARTITION BY') {
316+
$state = 3;
317+
$list->getPrevious(); // in order to check whether it's partition or partition by.
318+
}
296319
} elseif ($state === 1) {
297320
$ret->field = Expression::parse(
298321
$parser,
@@ -362,6 +385,46 @@ public static function parse(Parser $parser, TokensList $list, array $options =
362385
}
363386

364387
$ret->unknown[] = $token;
388+
} elseif ($state === 3) {
389+
if ($partitionState === 0) {
390+
// We want to get the next non-comment and non-space token after $token
391+
// therefore, the first getNext call will start with the current $idx which's $token,
392+
// will return it and increase $idx by 1, which's not guaranteed to be non-comment
393+
// and non-space, that's why we're calling getNext again.
394+
395+
$list->getNext();
396+
$nextToken = $list->getNext();
397+
if (
398+
($token->type === Token::TYPE_KEYWORD)
399+
&& (($token->keyword === 'PARTITION BY')
400+
|| ($token->keyword === 'PARTITION' && $nextToken && $nextToken->value !== '('))
401+
) {
402+
$partitionState = 1;
403+
} elseif (($token->type === Token::TYPE_KEYWORD) && ($token->keyword === 'PARTITION')) {
404+
$partitionState = 2;
405+
}
406+
407+
--$list->idx; // to decrease the idx by one, because the last getNext returned and increased it.
408+
409+
// reverting the effect of the getNext
410+
$list->getPrevious();
411+
$list->getPrevious();
412+
413+
++$list->idx; // to index the idx by one, because the last getPrevious returned and decreased it.
414+
} elseif ($partitionState === 1) {
415+
// Building the expression used for partitioning.
416+
if (empty($ret->field)) {
417+
$ret->field = '';
418+
}
419+
420+
$ret->field .= $token->type === Token::TYPE_WHITESPACE ? ' ' : $token->token;
421+
} elseif ($partitionState === 2) {
422+
$ret->partitions = ArrayObj::parse(
423+
$parser,
424+
$list,
425+
['type' => PartitionDefinition::class]
426+
);
427+
}
365428
}
366429
}
367430

@@ -389,6 +452,10 @@ public static function build($component, array $options = [])
389452

390453
$ret .= TokensList::build($component->unknown);
391454

455+
if (isset($component->partitions)) {
456+
$ret .= PartitionDefinition::build($component->partitions);
457+
}
458+
392459
return $ret;
393460
}
394461

src/TokensList.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,24 @@ public function getNext()
113113
return null;
114114
}
115115

116+
/**
117+
* Gets the previous token. Skips any irrelevant token (whitespaces and
118+
* comments).
119+
*/
120+
public function getPrevious(): ?Token
121+
{
122+
for (; $this->idx > 0; --$this->idx) {
123+
if (
124+
($this->tokens[$this->idx]->type !== Token::TYPE_WHITESPACE)
125+
&& ($this->tokens[$this->idx]->type !== Token::TYPE_COMMENT)
126+
) {
127+
return $this->tokens[$this->idx--];
128+
}
129+
}
130+
131+
return null;
132+
}
133+
116134
/**
117135
* Gets the next token.
118136
*

tests/Builder/AlterStatementTest.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,38 @@ public function testBuilderCompressed(): void
2828
$stmt = $parser->statements[0];
2929
$this->assertEquals($query, $stmt->build());
3030
}
31+
32+
public function testBuilderPartitions(): void
33+
{
34+
$parser = new Parser('ALTER TABLE t1 PARTITION BY HASH(id) PARTITIONS 8');
35+
$stmt = $parser->statements[0];
36+
37+
$this->assertEquals('ALTER TABLE t1 PARTITION BY HASH(id) PARTITIONS 8 ', $stmt->build());
38+
39+
$parser = new Parser('ALTER TABLE t1 ADD PARTITION (PARTITION p3 VALUES LESS THAN (2002))');
40+
$stmt = $parser->statements[0];
41+
42+
$this->assertEquals(
43+
"ALTER TABLE t1 ADD PARTITION (\n" .
44+
"PARTITION p3 VALUES LESS THAN (2002)\n" .
45+
')',
46+
$stmt->build()
47+
);
48+
49+
$parser = new Parser('ALTER TABLE p PARTITION BY LINEAR KEY ALGORITHM=2 (id) PARTITIONS 32;');
50+
$stmt = $parser->statements[0];
51+
52+
$this->assertEquals(
53+
'ALTER TABLE p PARTITION BY LINEAR KEY ALGORITHM=2 (id) PARTITIONS 32 ',
54+
$stmt->build()
55+
);
56+
57+
$parser = new Parser('ALTER TABLE t1 DROP PARTITION p0, p1;');
58+
$stmt = $parser->statements[0];
59+
60+
$this->assertEquals(
61+
'ALTER TABLE t1 DROP PARTITION p0, p1 ',
62+
$stmt->build()
63+
);
64+
}
3165
}

tests/Lexer/TokensListTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,16 @@ public function testGetNext(): void
6363
$this->assertNull($list->getNext());
6464
}
6565

66+
public function testGetPrevious(): void
67+
{
68+
$list = new TokensList($this->tokens);
69+
$list->idx = 7;
70+
$this->assertEquals($this->tokens[6], $list->getPrevious());
71+
$this->assertEquals($this->tokens[4], $list->getPrevious());
72+
$this->assertEquals($this->tokens[2], $list->getPrevious());
73+
$this->assertNull($list->getPrevious());
74+
}
75+
6676
public function testGetNextOfType(): void
6777
{
6878
$list = new TokensList($this->tokens);

0 commit comments

Comments
 (0)