-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
UNION queries ignore parameters set on QueryBuilder objects, causing SQL errors #6525
Comments
This is expected behavior. You have to set the parameters on the outermost query builder. cc @sbuerk |
Thank you for clarifying that this is the expected behavior. I appreciate the quick response. However, I'd like to discuss some concerns about this design decision and its implications for code quality and maintainability. While I understand that setting parameters on the outermost query builder might simplify the internal implementation, it introduces several practical issues:
Consider this example: $qb1 = $connection->createQueryBuilder()
->select('id', 'name')
->from('users')
->where('age > :age');
$qb2 = $connection->createQueryBuilder()
->select('id', 'name')
->from('admins')
->where('level > :level');
// We can't reuse $qb1 and $qb2 as-is in a UNION query
$unionQb = $connection->createQueryBuilder()
->union($qb1)
->addUnion($qb2)
->setParameter('age', 30)
->setParameter('level', 5); In this scenario, $qb1 and $qb2 lose their self-contained nature when used in a UNION, requiring knowledge of their internal parameters to be managed externally. Given these concerns, I wonder if it would be possible to consider an alternative design that allows for better encapsulation and separation of concerns, while still meeting the performance and implementation needs of the library. Thank you for your time and for considering these points. |
Thank you for your thorough answer. We've considered these points. However, they haven't been design goals for us. The goal of the query builder is to help composing a single query programmatically, nothing more, nothing less. There are no self-contained subqueries. There never were and we did not intend to introduce that concept for unions. Moreover, merging parameters of multiple query builders is not a trivial task. Think about ordered parameters or naming collisions of named parameters. This feature would introduce a lot if unnecessary complexity with really not much gain. |
Thank you for your detailed explanation of the QueryBuilder's design philosophy. I understand the focus on simplicity and the challenges involved in parameter handling for UNION queries. After considering your points, I'd like to highlight a specific aspect of the current implementation that could potentially lead to confusion:
This creates a situation where the method signature suggests functionality (by accepting QueryBuilder objects with their full interface) that isn't actually supported in this context. It's counterintuitive that you can pass a QueryBuilder to To address this inconsistency, I'd like to propose two alternative solutions:
public function union(string $part): self
{
// ... implementation
}
public function addUnion(string $part, UnionType $type = UnionType::DISTINCT): self
{
// ... implementation
} This change would make it explicit that only SQL strings are accepted, eliminating any confusion about parameter handling on QueryBuilder objects.
/**
* Specifies union parts to be used to build a UNION query.
* Replaces any previously specified parts.
*
* <code>
* $qb = $conn->createQueryBuilder()
* ->union('SELECT 1 AS field1', 'SELECT 2 AS field1');
* </code>
*
* @param string|QueryBuilder $part The union part to add
*
* @return $this
*
* @notice When a QueryBuilder is provided as $part, only its SQL string is used.
* Any parameters set on this QueryBuilder are ignored. Use setParameter()
* on the main QueryBuilder for parameters in UNION queries.
*/
public function union(string|QueryBuilder $part): self
{
// ... (existing implementation)
} Either of these solutions would:
What are your thoughts on these proposals? |
We don't use Looking at the doc block, I also realize that the code examples still show variadic calls of the |
I'm not a fan of changing the signature to string:
Adding something to the method docblocks should be done, as I expect that it should be expectable that devlopers reads method docblocks when using a method. Don't get it why I missed that when providing the union builder API. So thanks for the report on that. Will see if I can find some time to add these things if noone is quicker, but the next days are quite full packed due to upcoming release (major) of another project. |
Bug Report
Summary
Parameters in UNION queries using QueryBuilder objects are ignored, causing SQL syntax errors due to unprocessed parameter placeholders.
Current behaviour
When using
QueryBuilder
objects with theunion()
andaddUnion()
methods, parameters set usingsetParameter()
are ignored in the final query execution. This results in SQL syntax errors as parameter placeholders (e.g., ":age") are sent to the database unprocessed.How to reproduce
QueryBuilder
objectQueryBuilder
objects for UNION partssetParameter()
to set parameters on the UNION part QueryBuilder objectsunion()
andaddUnion()
methods to combine the queries, passing the QueryBuilder objectsCode snippet to reproduce:
This code results in a SQL syntax error because the ":age" and ":level" placeholders are sent to the database unprocessed.
The issue appears to be in the
DefaultUnionSQLBuilder
class:The
$union->query
is cast to a string, which only retrieves the SQL string without parameters. There's no mechanism to collect or apply parameters from the UNION parts.Expected behaviour
Parameters set on all QueryBuilder objects (main and UNION parts) using
setParameter()
should be collected and applied to the final query execution, resulting in a valid SQL query with all parameters properly substituted.The text was updated successfully, but these errors were encountered: