-
Type:
Improvement
-
Resolution: Fixed
-
Priority:
Major - P3
-
Affects Version/s: None
-
Component/s: Query
-
None
Problem
AstParameterMarker is a record whose only component is the JdbcParameterBinder, so record equality is binder identity. Hibernate allocates a separate JdbcParameter per occurrence of a query parameter, which makes two cases indistinguishable: one query parameter used twice, and two different parameters whose surrounding expressions happen to be structurally identical.
Neither binder identity nor bound value can tell them apart. Measured against a local mongod, for a query using one named parameter in two clauses, the translator receives two JdbcParameter nodes and JdbcParameterBindings returns two distinct JdbcParameterBinding instances for them, both holding the same value. Values cannot serve either, since two different parameters may be bound to equal values and the translated pipeline is cached across executions.
Fix
JdbcParameter#getParameterId() is declared on the public SQL AST interface org.hibernate.sql.ast.tree.expression.JdbcParameter, so no cast to an internal class is needed. BaseSqmToSqlAstConverter.resolveSqmParameter reuses the id of an already-resolved parameter's first occurrence, so every occurrence of one query parameter shares an id, and distinct parameters get distinct ids. Measured ids: the same named parameter twice gives 0 and 0; :a and :b give 0 and 1; the same parameter in SELECT and in WHERE gives 0 and 0; ?1 twice gives 0 and 0.
Carry the id on the marker and define equality on it: when both ids are present, compare ids; otherwise require both to be absent and the binder to be identical. The id is nullable by contract and several implementors supply none, so a null id must not match another null id from a different parameter, and the fallback keeps equals reflexive.
Why it matters
Nothing in the translator compares markers today, so there is no user-visible symptom. It will come into play for matching a GROUP BY expression against its occurrences in SELECT, HAVING, and ORDER BY (HIBERNATE-241), which rests on structural equality of translated expressions and therefore reduces to marker equality whenever a grouped expression contains a parameter. Without it, a query such as
select substring(b.string, 1, :len) from Item b group by substring(b.string, 1, :len)
fails to match its own group key. The failure direction is safe, a rejection rather than a wrong result, but the query is legal and should work.
Scope
Add the component, define the equality rule, pass the id at both construction sites, and unit-test the contract, including the null cases. No other behavior changes.
- is depended on by
-
HIBERNATE-241 Support GROUP BY and HAVING expressions
-
- Closed
-