diff --git a/src/Query/Query.php b/src/Query/Query.php index 98e69b962..4ba681e7b 100644 --- a/src/Query/Query.php +++ b/src/Query/Query.php @@ -662,7 +662,7 @@ public function where(array|string|ExpressionInterface|null $condition, array $p return $this; } - public function withQuery(QueryInterface|string $query, string $alias, bool $recursive = false): static + public function withQuery(QueryInterface|string $query, ExpressionInterface|string $alias, bool $recursive = false): static { $this->withQueries[] = ['query' => $query, 'alias' => $alias, 'recursive' => $recursive]; return $this; diff --git a/src/Query/QueryPartsInterface.php b/src/Query/QueryPartsInterface.php index f6b9ca974..8c3ddfd84 100644 --- a/src/Query/QueryPartsInterface.php +++ b/src/Query/QueryPartsInterface.php @@ -645,10 +645,11 @@ public function where(array|string|ExpressionInterface|null $condition, array $p * Prepends an SQL statement using `WITH` syntax. * * @param QueryInterface|string $query The SQL statement to append using `UNION`. - * @param string $alias The query alias in `WITH` construction. + * @param ExpressionInterface|string $alias The query alias in `WITH` construction. + * To specify the alias in plain SQL, you may pass an instance of {@see ExpressionInterface}. * @param bool $recursive Its `true` if using `WITH RECURSIVE` and `false` if using `WITH`. */ - public function withQuery(QueryInterface|string $query, string $alias, bool $recursive = false): static; + public function withQuery(QueryInterface|string $query, ExpressionInterface|string $alias, bool $recursive = false): static; /** * Specifies the `WITH` query clause for the query. diff --git a/src/QueryBuilder/AbstractDQLQueryBuilder.php b/src/QueryBuilder/AbstractDQLQueryBuilder.php index e42a55da3..014656ebf 100644 --- a/src/QueryBuilder/AbstractDQLQueryBuilder.php +++ b/src/QueryBuilder/AbstractDQLQueryBuilder.php @@ -616,12 +616,16 @@ private function quoteTableNames(array $tables, array &$params): array /** * Quotes an alias of Common Table Expressions (CTE) * - * @param string $name The alias name with or without column names to quote. + * @param ExpressionInterface|string $name The alias name with or without column names to quote. * * @return string The quoted alias. */ - private function quoteCteAlias(string $name): string + private function quoteCteAlias(ExpressionInterface|string $name): string { + if ($name instanceof ExpressionInterface) { + return $this->buildExpression($name); + } + if (!str_contains($name, '(')) { return $this->quoter->quoteTableName($name); } @@ -631,16 +635,9 @@ private function quoteCteAlias(string $name): string } /** @psalm-suppress PossiblyUndefinedArrayOffset */ - [$name, $columnNames] = explode('(', substr($name, 0, -1), 2); - - $quotedName = $this->quoter->quoteTableName(trim($name)); - $columnNames = explode(',', $columnNames); - $quotedColumnNames = []; - - foreach ($columnNames as $columnName) { - $quotedColumnNames[] = $this->quoter->quoteColumnName(trim($columnName)); - } + [$name, $columns] = explode('(', substr($name, 0, -1), 2); + $name = trim($name); - return $quotedName . '(' . implode(', ', $quotedColumnNames) . ')'; + return $this->quoter->quoteTableName($name) . '(' . $this->buildColumns($columns) . ')'; } } diff --git a/tests/AbstractQueryBuilderTest.php b/tests/AbstractQueryBuilderTest.php index c12b6ee74..5b72f2694 100644 --- a/tests/AbstractQueryBuilderTest.php +++ b/tests/AbstractQueryBuilderTest.php @@ -1172,29 +1172,21 @@ public function testBuildWithQueryRecursive(): void $this->assertSame([], $params); } - public function testBuildWithQueryRecursiveWithColumns() + /** @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::cteAliases */ + public function testBuildWithQueryAlias($alias, $expected) { $db = $this->getConnection(); - $qb = $db->getQueryBuilder(); - $withQuery = (new Query($db))->select(['id', 'name'])->from('user')->where(['status' => 1]); - $query = (new Query($db))->withQuery($withQuery, 'u(id, name)', true)->from('account'); - [$sql, $params] = $qb->build($query); + $withQuery = (new Query($db))->from('t'); + $query = (new Query($db))->withQuery($withQuery, $alias)->from('t'); - $expected = DbHelper::replaceQuotes( - <<getDriverName(), - ); + [$sql, $params] = $qb->build($query); - if (in_array($db->getDriverName(), ['oci', 'sqlsrv'], true)) { - $expected = str_replace('WITH RECURSIVE ', 'WITH ', $expected); - } + $expected = DbHelper::replaceQuotes($expected, $db->getDriverName()); $this->assertSame($expected, $sql); - $this->assertSame([':qp0' => 1], $params); + $this->assertSame([], $params); } /** diff --git a/tests/Provider/QueryBuilderProvider.php b/tests/Provider/QueryBuilderProvider.php index f86d147d9..bb09c3e1a 100644 --- a/tests/Provider/QueryBuilderProvider.php +++ b/tests/Provider/QueryBuilderProvider.php @@ -1231,4 +1231,40 @@ public static function upsert(): array ], ]; } + + public static function cteAliases(): array + { + return [ + 'simple' => [ + 'a', + << [ + 'a(b)', + << [ + 'a(b,c,d)', + << [ + 'a(b,c,d) ', + << [ + new Expression('a(b,c,d)'), + <<