From 29a63b15443ea494d7ade73361060e5085a47ef5 Mon Sep 17 00:00:00 2001 From: SAY-5 Date: Thu, 30 Apr 2026 00:38:43 -0700 Subject: [PATCH] fix(builder): preserve whitespace between SELECT and UNION body in CREATE VIEW (#655) Signed-off-by: SAY-5 --- src/Statements/CreateStatement.php | 13 ++++++++++++- tests/Builder/CreateStatementTest.php | 25 +++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/Statements/CreateStatement.php b/src/Statements/CreateStatement.php index 7b2b65a5..598e4c8a 100644 --- a/src/Statements/CreateStatement.php +++ b/src/Statements/CreateStatement.php @@ -462,11 +462,22 @@ public function build(): string $builtStatement = $this->with->build(); } + $body = TokensList::buildFromArray($this->body); + // Body tokens (e.g. trailing `UNION ALL (SELECT ...)` after the + // primary SELECT) are concatenated raw, so without a separator + // the rebuilt SQL collides with the SELECT's last token — + // `WHERE 3 = 3union all` instead of `WHERE 3 = 3 union all`. + // Only insert a space when neither side already has one to + // avoid double-spacing the WITH...AS path. + $separator = ($builtStatement !== '' && $body !== '' + && substr($builtStatement, -1) !== ' ' + && $body[0] !== ' ') ? ' ' : ''; + return 'CREATE ' . $this->options->build() . ' ' . $this->name->build() . ' ' . $fields . ' AS ' . $builtStatement - . TokensList::buildFromArray($this->body) . ' ' + . $separator . $body . ' ' . ($this->entityOptions?->build() ?? ''); } diff --git a/tests/Builder/CreateStatementTest.php b/tests/Builder/CreateStatementTest.php index 3ba1c1c5..b34241cf 100644 --- a/tests/Builder/CreateStatementTest.php +++ b/tests/Builder/CreateStatementTest.php @@ -897,4 +897,29 @@ public function testBuildCreateTableComplexIndexes(): void $stmt->build(), ); } + + /** + * Regression for #655: `CREATE VIEW ... AS SELECT ... WHERE ... UNION ALL (...)` + * used to lose the whitespace between the SELECT body and the trailing + * UNION clause, producing invalid SQL like `WHERE 3 = 3union all (...)`. + */ + public function testBuilderViewWithUnionPreservesWhitespace(): void + { + $sql = "CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER " + . "VIEW `v1` AS select 1 AS `a` where 3 = 3 union all (select 2 AS `a`)"; + + $parser = new Parser($sql); + $built = $parser->statements[0]->build(); + + $this->assertStringNotContainsString( + '3union all', + $built, + 'rebuilt CREATE VIEW must keep the space between WHERE clause and UNION', + ); + $this->assertStringContainsString( + '3 union all', + // case-insensitive match: builder may upper-case the keyword + preg_replace('/UNION\s+ALL/i', 'union all', $built), + ); + } }