Skip to content

Commit db5a2e9

Browse files
committed
Support multi line expressions and multiple expressions on KEYs
Signed-off-by: William Desportes <williamdes@wdes.fr>
1 parent ef2c3f7 commit db5a2e9

File tree

3 files changed

+184
-12
lines changed

3 files changed

+184
-12
lines changed

src/Components/Key.php

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,15 @@ public static function parse(Parser $parser, TokensList $list, array $options =
164164
$state = 1;
165165
} elseif ($state === 1) {
166166
if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) {
167-
// Switch to expression mode
167+
$positionBeforeSearch = $list->idx;
168+
$list->idx++;// Ignore the current token "(" or the search condition will always be true
169+
$nextToken = $list->getNext();
170+
$list->idx = $positionBeforeSearch;// Restore the position
171+
168172
if (
169-
isset($list->tokens[$list->idx + 1])
170-
&& $list->tokens[$list->idx + 1]->value === '('
173+
$nextToken !== null && $nextToken->value === '('
171174
) {
175+
// Switch to expression mode
172176
$state = 5;
173177
} else {
174178
$state = 2;
@@ -211,15 +215,37 @@ public static function parse(Parser $parser, TokensList $list, array $options =
211215
++$list->idx;
212216
break;
213217
} elseif ($state === 5) {
214-
$ret->expr = Expression::parse(
215-
$parser,
216-
$list,
217-
array(
218-
'parenthesesDelimited' => true
219-
)
220-
);
221-
++$list->idx;// skip end parenthese
222-
$state = 4;// go back to state 4 to fetch options
218+
if ($token->type === Token::TYPE_OPERATOR) {
219+
// This got back to here and we reached the end of the expression
220+
if ($token->value === ')') {
221+
$state = 4;// go back to state 4 to fetch options
222+
continue;
223+
}
224+
// The expression is not finished, adding a separator for the next expression
225+
if ($token->value === ',') {
226+
$ret->expr .= ', ';
227+
continue;
228+
}
229+
// Start of the expression
230+
if ($token->value === '(') {
231+
// This is the first expression, set to empty
232+
if ($ret->expr === null) {
233+
$ret->expr = '';
234+
}
235+
236+
$ret->expr .= Expression::parse(
237+
$parser,
238+
$list,
239+
array(
240+
'parenthesesDelimited' => true
241+
)
242+
);
243+
continue;
244+
}
245+
// Another unexpected operator was found
246+
}
247+
// Something else than an operator was found
248+
$parser->error('Unexpected token.', $token);
223249
}
224250
}
225251

tests/Builder/CreateStatementTest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,8 +684,26 @@ public function testBuildCreateTableComplexIndexes()
684684
KEY `status_idx` (`status`),
685685
KEY `waterway_idx` (`waterway_id`),
686686
KEY `time_taken_idx` (`time_taken`),
687+
KEY `updated_tz_ind3` (
688+
-- my expression
689+
(convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB'))
690+
) COMMENT 'foo\'s',
691+
KEY `updated_tz_ind_two_indexes_commented` (
692+
-- first expression
693+
(
694+
convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB')
695+
)
696+
,
697+
-- second expression
698+
(
699+
convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'FR')
700+
)
701+
)
702+
-- and now some options
703+
COMMENT 'haha, this is a complex and indented case',
687704
KEY `alias_type_idx` (`alias_type`),
688705
KEY `updated_tz_ind2` ((convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB'))) COMMENT 'foo\'s',
706+
KEY `updated_tz_ind_two_indexes` ((convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB')), (convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'FR'))) COMMENT 'bar\'s',
689707
KEY `updated_tz_ind` ((convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB')))
690708
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
691709
SQL
@@ -713,8 +731,15 @@ public function testBuildCreateTableComplexIndexes()
713731
KEY `status_idx` (`status`),
714732
KEY `waterway_idx` (`waterway_id`),
715733
KEY `time_taken_idx` (`time_taken`),
734+
KEY `updated_tz_ind3` ((convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB'))) COMMENT 'foo\'s',
735+
KEY `updated_tz_ind_two_indexes_commented` ((
736+
convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB')
737+
), (
738+
convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'FR')
739+
)) COMMENT 'haha, this is a complex and indented case',
716740
KEY `alias_type_idx` (`alias_type`),
717741
KEY `updated_tz_ind2` ((convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB'))) COMMENT 'foo\'s',
742+
KEY `updated_tz_ind_two_indexes` ((convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB')), (convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'FR'))) COMMENT 'bar\'s',
718743
KEY `updated_tz_ind` ((convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB')))
719744
)
720745
SQL;

tests/Components/KeyTest.php

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
use PhpMyAdmin\SqlParser\Components\Expression;
66
use PhpMyAdmin\SqlParser\Components\Key;
77
use PhpMyAdmin\SqlParser\Components\OptionsArray;
8+
use PhpMyAdmin\SqlParser\Exceptions\ParserException;
89
use PhpMyAdmin\SqlParser\Parser;
910
use PhpMyAdmin\SqlParser\Tests\TestCase;
11+
use PhpMyAdmin\SqlParser\Token;
1012

1113
class KeyTest extends TestCase
1214
{
@@ -192,4 +194,123 @@ public function testParseKeyExpressionWithOptions()
192194
);
193195
$this->assertSame(array(), $component->columns);
194196
}
197+
198+
public function testParseKeyExpressionWithOptionsError()
199+
{
200+
$parser = new Parser();
201+
$component = Key::parse(
202+
$parser,
203+
$this->getTokensList(
204+
'KEY `updated_tz_ind2` (()convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'GB\'))) COMMENT \'my comment\','
205+
)
206+
);
207+
$this->assertEquals('KEY', $component->type);
208+
$this->assertEquals('updated_tz_ind2', $component->name);
209+
$this->assertEquals(new OptionsArray(
210+
array(
211+
)
212+
), $component->options);
213+
$t = new Token(
214+
'convert_tz',
215+
Token::TYPE_KEYWORD,
216+
33
217+
);
218+
$t->position = 25;
219+
220+
$this->assertEquals(array(
221+
new ParserException(
222+
'Unexpected token.',
223+
$t
224+
)
225+
), $parser->errors);
226+
$expr = new Expression(
227+
'(convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'GB\'))'
228+
);
229+
$expr->function = 'convert_tz';
230+
$this->assertEquals(
231+
'()(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'GB\')',
232+
$component->expr
233+
);
234+
$this->assertSame(array(), $component->columns);
235+
}
236+
237+
public function testParseKeyOneExpressionWithOptions()
238+
{
239+
$parser = new Parser();
240+
$component = Key::parse(
241+
$parser,
242+
$this->getTokensList(
243+
'KEY `updated_tz_ind2`'
244+
. ' ('
245+
. '(convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'GB\')), '
246+
. '(convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'FR\'))'
247+
. ')'
248+
. ' COMMENT \'my comment\','
249+
)
250+
);
251+
$this->assertEquals('KEY', $component->type);
252+
$this->assertEquals('updated_tz_ind2', $component->name);
253+
$this->assertEquals(new OptionsArray(
254+
array(
255+
4 => array(
256+
'name' => 'COMMENT',
257+
'equals' => false,
258+
'expr' => '\'my comment\'',
259+
'value' => 'my comment',
260+
)
261+
)
262+
), $component->options);
263+
$this->assertSame(array(), $parser->errors);
264+
$expr = new Expression(
265+
'(convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'GB\')),'
266+
. ' (convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'FR\'))'
267+
);
268+
$expr->function = 'convert_tz';
269+
$this->assertEquals(
270+
$expr,
271+
$component->expr
272+
);
273+
$this->assertSame(array(), $component->columns);
274+
}
275+
276+
public function testParseKeyMultipleExpressionsWithOptions()
277+
{
278+
$parser = new Parser();
279+
$component = Key::parse(
280+
$parser,
281+
$this->getTokensList(
282+
'KEY `updated_tz_ind2`'
283+
. ' ('
284+
. '(convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'GB\')), '
285+
. '(convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'FR\')), '
286+
. '(convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'RU\'))'
287+
. ')'
288+
. ' COMMENT \'my comment\','
289+
)
290+
);
291+
$this->assertEquals('KEY', $component->type);
292+
$this->assertEquals('updated_tz_ind2', $component->name);
293+
$this->assertEquals(new OptionsArray(
294+
array(
295+
4 => array(
296+
'name' => 'COMMENT',
297+
'equals' => false,
298+
'expr' => '\'my comment\'',
299+
'value' => 'my comment',
300+
)
301+
)
302+
), $component->options);
303+
$expr = new Expression(
304+
'(convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'GB\')),'
305+
. ' (convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'FR\')),'
306+
. ' (convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'RU\'))'
307+
);
308+
$expr->function = 'convert_tz';
309+
$this->assertEquals(
310+
$expr,
311+
$component->expr
312+
);
313+
$this->assertSame(array(), $component->columns);
314+
$this->assertSame(array(), $parser->errors);
315+
}
195316
}

0 commit comments

Comments
 (0)