From 44262050c03e74f65d3a21079fef5491c89d4628 Mon Sep 17 00:00:00 2001 From: William Desportes Date: Fri, 17 Jan 2025 00:50:31 +0100 Subject: [PATCH 1/2] Fix #605 - Add "RECURSIVE" on build() for "WITH RECURSIVE" on the WithStatement class Signed-off-by: William Desportes --- phpstan-baseline.neon | 5 --- src/Statements/WithStatement.php | 10 ++++- tests/Builder/CreateStatementTest.php | 29 ++++++++++++ tests/Components/OptionsArrayTest.php | 13 ++++++ tests/Parser/WithStatementTest.php | 64 +++++++++++++++++++++++++++ 5 files changed, 114 insertions(+), 7 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index d51e3afef..453c59dec 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -325,11 +325,6 @@ parameters: count: 1 path: src/Components/OptionsArray.php - - - message: "#^Property PhpMyAdmin\\\\SqlParser\\\\Components\\\\OptionsArray\\:\\:\\$options \\(array\\\\) does not accept array\\\\.$#" - count: 8 - path: src/Components/OptionsArray.php - - message: "#^Right side of && is always true\\.$#" count: 1 diff --git a/src/Statements/WithStatement.php b/src/Statements/WithStatement.php index 58c1d1dbc..7c63dc475 100644 --- a/src/Statements/WithStatement.php +++ b/src/Statements/WithStatement.php @@ -280,11 +280,17 @@ public function parse(Parser $parser, TokensList $list) */ public function build() { - $str = 'WITH '; + $initial = true; + $str = 'WITH'; + + if ($this->options !== null && $this->options->options !== []) { + $str .= ' ' . OptionsArray::build($this->options); + } foreach ($this->withers as $wither) { - $str .= $str === 'WITH ' ? '' : ', '; + $str .= $initial ? ' ' : ', '; $str .= WithKeyword::build($wither); + $initial = false; } $str .= ' '; diff --git a/tests/Builder/CreateStatementTest.php b/tests/Builder/CreateStatementTest.php index 4300e15d5..c76497fd7 100644 --- a/tests/Builder/CreateStatementTest.php +++ b/tests/Builder/CreateStatementTest.php @@ -15,6 +15,8 @@ use PhpMyAdmin\SqlParser\Tests\TestCase; use PhpMyAdmin\SqlParser\TokensList; +use function implode; + class CreateStatementTest extends TestCase { public function testBuilder(): void @@ -399,6 +401,33 @@ public function testBuilderView(): void . ' AS (SELECT 1 UNION ALL SELECT 2) SELECT col1 FROM cte AS `d` ', $stmt->build() ); + + $parser = new Parser( + implode("\n", [ + 'CREATE VIEW number_sequence_view AS', + 'WITH RECURSIVE number_sequence AS (', + ' SELECT 1 AS `number`', + ' UNION ALL', + ' SELECT `number` + 1', + ' FROM number_sequence', + ' WHERE `number` < 5', + ')', + 'SELECT * FROM number_sequence;', + ]) + ); + $stmt = $parser->statements[0]; + $this->assertEquals( + 'CREATE VIEW number_sequence_view AS' + . ' WITH RECURSIVE number_sequence AS (' + . 'SELECT 1 AS `number`' + . ' UNION ALL' + . ' SELECT `number`+ 1' + . ' FROM number_sequence' + . ' WHERE `number` < 5' + . ')' + . ' SELECT * FROM number_sequence ', + $stmt->build() + ); } public function testBuilderViewComplex(): void diff --git a/tests/Components/OptionsArrayTest.php b/tests/Components/OptionsArrayTest.php index 4d4d0b23e..143e0931a 100644 --- a/tests/Components/OptionsArrayTest.php +++ b/tests/Components/OptionsArrayTest.php @@ -131,4 +131,17 @@ public function testBuild(): void 'ALL SQL_CALC_FOUND_ROWS MAX_STATEMENT_TIME=42' ); } + + public function testBuildWithRecursive(): void + { + $component = OptionsArray::parse( + new Parser(), + $this->getTokensList('RECURSIVE'), + ['RECURSIVE' => 1] + ); + $this->assertEquals( + OptionsArray::build($component), + 'RECURSIVE' + ); + } } diff --git a/tests/Parser/WithStatementTest.php b/tests/Parser/WithStatementTest.php index 448d374e1..414e05263 100644 --- a/tests/Parser/WithStatementTest.php +++ b/tests/Parser/WithStatementTest.php @@ -74,6 +74,70 @@ public function testWith(): void $this->assertEquals($expected, $parser->statements[0]->build()); } + public function testWithRecursive(): void + { + $sql = <<getErrorsAsArray($lexer); + $this->assertCount(0, $lexerErrors); + $parser = new Parser($lexer->list); + $parserErrors = $this->getErrorsAsArray($parser); + $this->assertCount(0, $parserErrors); + $this->assertCount(1, $parser->statements); + + // phpcs:disable Generic.Files.LineLength.TooLong + $expected = <<assertEquals($expected, $parser->statements[0]->build()); + } + + public function testWithRecursiveWithers(): void + { + $sql = <<getErrorsAsArray($lexer); + $this->assertCount(0, $lexerErrors); + $parser = new Parser($lexer->list); + $parserErrors = $this->getErrorsAsArray($parser); + $this->assertCount(0, $parserErrors); + $this->assertCount(1, $parser->statements); + + // phpcs:disable Generic.Files.LineLength.TooLong + $expected = <<assertEquals($expected, $parser->statements[0]->build()); + } + public function testWithHasErrors(): void { $sql = << Date: Fri, 17 Jan 2025 00:53:02 +0100 Subject: [PATCH 2/2] Add a ChangeLog entry for #605 Signed-off-by: William Desportes --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b433d3a82..e90099a1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +# [5.10.3] - YYYY-MM-DD + +### Fixed + +- Add "RECURSIVE" on build() for "WITH RECURSIVE" on the WithStatement class (#605) + ## [5.10.2] - 2024-12-05 ### Added