Skip to content

Commit d12afde

Browse files
committed
Stop CTE views at statement delimiters
1 parent 040d960 commit d12afde

3 files changed

Lines changed: 496 additions & 740 deletions

File tree

src/Statements/WithStatement.php

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -213,10 +213,34 @@ public function parse(Parser $parser, TokensList $list): void
213213
// Length of expression tokens is null by default, in order for the $subList to start
214214
// from $list->idx to the end of the $list.
215215
$lengthOfExpressionTokens = null;
216+
$idxOfDelimiter = null;
217+
218+
// Keep a terminal delimiter in the subparser, but stop before a delimiter
219+
// that separates this CTE expression from another statement.
220+
if ($list->getNextOfType(TokenType::Delimiter)) {
221+
$delimiterIndex = $list->idx - 1;
222+
$nextToken = $list->getNext();
223+
if ($nextToken !== null && $nextToken->token !== '') {
224+
$idxOfDelimiter = $delimiterIndex;
225+
$idxOfLastParsedToken = $idxOfDelimiter + 1;
226+
$lengthOfExpressionTokens = $idxOfDelimiter - $idxBeforeSearch;
227+
}
228+
}
229+
230+
$list->idx = $idxBeforeSearch;
231+
232+
$idxOfOn = null;
233+
$searchEnd = $idxOfDelimiter ?? $list->count;
234+
for ($i = $idxBeforeSearch; $i < $searchEnd; ++$i) {
235+
$candidate = $list->tokens[$i];
236+
if ($candidate->type === TokenType::Keyword && $candidate->keyword === 'ON') {
237+
$idxOfOn = $i;
238+
$list->idx = $idxOfOn + 1;
239+
break;
240+
}
241+
}
216242

217-
if ($list->getNextOfTypeAndValue(TokenType::Keyword, 'ON')) {
218-
// (-1) because getNextOfTypeAndValue returned ON and increased the index.
219-
$idxOfOn = $list->idx - 1;
243+
if ($idxOfOn !== null) {
220244
// We want to make sure that it's `ON DUPLICATE KEY UPDATE`
221245
$dubplicateToken = $list->getNext();
222246
$keyToken = $list->getNext();
@@ -228,7 +252,7 @@ public function parse(Parser $parser, TokensList $list): void
228252
) {
229253
// Index of the last parsed token will be the token before the ON Keyword
230254
$idxOfLastParsedToken = $idxOfOn - 1;
231-
// The length of the expression tokens would be the difference
255+
// The length of expression tokens would be the difference
232256
// between the first unrelated token `ON` and the idx
233257
// before skipping the CTE tokens.
234258
$lengthOfExpressionTokens = $idxOfOn - $idxBeforeSearch;

tests/Parser/CreateStatementTest.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace PhpMyAdmin\SqlParser\Tests\Parser;
66

7+
use PhpMyAdmin\SqlParser\Parser;
78
use PhpMyAdmin\SqlParser\Tests\TestCase;
89
use PHPUnit\Framework\Attributes\DataProvider;
910

@@ -82,4 +83,29 @@ public static function createProvider(): array
8283
['parser/parseCreateOrReplaceView1'],
8384
];
8485
}
86+
87+
public function testCreateViewsWithCteRemainSeparate(): void
88+
{
89+
$sql = <<<'SQL'
90+
CREATE VIEW view1 AS
91+
WITH foo AS (SELECT 1 AS id)
92+
SELECT * FROM foo;
93+
CREATE VIEW view2 AS
94+
WITH bar AS (SELECT 2 AS id)
95+
SELECT * FROM bar;
96+
SQL;
97+
98+
$parser = new Parser($sql);
99+
100+
self::assertSame([], $this->getErrorsAsArray($parser));
101+
self::assertCount(2, $parser->statements);
102+
self::assertSame(
103+
'CREATE VIEW view1 AS WITH foo AS (SELECT 1 AS `id`) SELECT * FROM foo ',
104+
$parser->statements[0]->build(),
105+
);
106+
self::assertSame(
107+
'CREATE VIEW view2 AS WITH bar AS (SELECT 2 AS `id`) SELECT * FROM bar ',
108+
$parser->statements[1]->build(),
109+
);
110+
}
85111
}

0 commit comments

Comments
 (0)