Repository navigation
Conversation
…E queries
A bulk UPDATE/DELETE without an identification variable (alias) such as
UPDATE Todo SET status = com.example.domain.Status.PENDING WHERE id = :id
generated invalid SQL ("UPDATE TODO SET STATUS = WHERE ...") because the
fully-qualified enum literal in the new value was silently dropped. This also
blocks Jakarta Data @query repository methods, whose JDQL omits aliases by
convention.
FullyQualifyPathExpressionVisitor qualifies unqualified path expressions of an
UPDATE/DELETE query with a virtual "this" identification variable. It did so
for every path, including the fully-qualified enum constant used as the new
value, turning "com.example.domain.Status.PENDING" into a path rooted at the
entity. The leading segment "com" then failed to resolve to a mapping, so the
enum constant was never built and the assigned value became empty.
Only single-segment, unqualified attributes (e.g. "status") are now qualified
with the virtual identification variable. Multi-segment paths are left as-is so
a fully-qualified enum constant is resolved as an enum literal, consistent with
how unqualified SELECT queries already handle them. The update target is still
always qualified (visit(UpdateItem)) so a relationship/embeddable navigation on
the left side keeps being resolved and validated against the entity.
Fixes eclipse-ee4j#2758
Signed-off-by: Renat R. Safiullin <renatsaf@gmail.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
@renatsaf I compiled the parser source in an isolated directory and compared the PR with the parent’s original visitor. These are executed grammar/semantic-validation results, without a database, and found the following issues:
Can you please have a look at the following recommendations?
|
…TE/DELETE queries" This reverts commit 7fbd632. Leaving multi-segment paths unqualified in FullyQualifyPathExpressionVisitor broke nested navigations in UPDATE/DELETE queries without an identification variable: "DELETE FROM Employee WHERE manager.phoneNumbers IS EMPTY" fails with COLLECTION_VALUED_PATH_EXPRESSION_NOT_RESOLVABLE, and "address.city = 'x'" is rejected by the tools validator. Without the mapping metadata the parser cannot tell such a navigation from a fully qualified enum constant, so the enum constant has to be resolved where that metadata is available. Signed-off-by: Renat R. Safiullin <renatsaf@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…tion variable
An UPDATE or DELETE query that does not declare an identification variable
lost a fully qualified enum constant:
UPDATE Todo SET status = com.example.domain.Status.PENDING WHERE id = :id
generated "UPDATE TODO SET STATUS = WHERE (ID = ?)". The constant was lost in
any position: "status = <enum>" failed with an object comparison
QueryException, and "status IN (<enum>)" silently matched no rows.
FullyQualifyPathExpressionVisitor qualifies every path of such a query with a
virtual "this" identification variable, the enum constant included.
ExpressionBuilderVisitor then navigated the constant from the entity, stopped
at its first segment, which is not a mapping, and used the entity expression
as the value. The enum constant fallback only ran when the identification
variable could not be resolved.
A path qualified with the implicit "this" whose first segment is neither a
mapping nor a query key of the entity is now resolved as an enum constant.
Navigations such as "room.doors" are resolved against the entity as before.
Fixes eclipse-ee4j#2758
Signed-off-by: Renat R. Safiullin <renatsaf@gmail.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…iable Nested navigations (managerEmployee.phoneNumbers IS EMPTY, address.city, embeddedAddress.city, shelfLife.soldDate) and fully qualified enum constants in UPDATE and DELETE queries that do not declare an identification variable must validate without problems. The previous attempt to fix eclipse-ee4j#2758 broke these navigations without any test failing. Signed-off-by: Renat R. Safiullin <renatsaf@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
@robertpatrick Confirmed. All three reproduce with the tools validators, and 1 and 2 also fail at runtime ( Keeping the qualification for collection-valued paths alone would still leave case 3, and the underlying problem remains: without mapping metadata the parser cannot tell Regression tests for each case:
Running the queries against H2 also showed the original defect is not limited to |
Problem
A bulk
UPDATE/DELETEthat does not declare an identification variable loses a fully qualified enum constant:generates
UPDATE TODO SET STATUS = WHERE (ID = ?). The constant is lost wherever it appears in such a query (H2):SET status = <enum>SET STATUS =, invalid SQLWHERE status = <enum>QueryException: Object comparisons can only be used with OneToOneMappingsWHERE status IN (<enum>)STATUS IN (), no row matched, no errorSET status = NULLIF(<enum>, status)NULLIF(, STATUS), invalid SQLThis blocks Jakarta Data
@Queryrepository methods, whose JDQL omits aliases.Root cause
FullyQualifyPathExpressionVisitorqualifies every path of such a query with a virtualthisidentification variable, the enum constant included.ExpressionBuilderVisitorthen navigatescom.example.domain.Status.PENDINGfrom the entity: the first segment is not a mapping, path resolution stops, and the entity expression itself is used as the value. The existing enum-constant fallback (resolveEnumConstant) only runs when the identification variable does not resolve, which never happens for the virtualthis.Fix
In
ExpressionBuilderVisitor.PathResolver, a path qualified with the implicitthiswhose first segment is neither a mapping nor a query key of the entity is resolved as an enum constant. The parse tree and validation are unchanged, and navigations such asaddress.cityorroom.doorsare resolved against the entity as before.The first commit of this PR changed
FullyQualifyPathExpressionVisitorto leave multi-segment paths unqualified. As reported in review, that broke nested navigations (manager.phoneNumbers IS EMPTYat runtime,address.cityin the tools validator): without mapping metadata the parser cannot tell a navigation from an enum constant. That commit is reverted.Type resolution needs no change:
TypeResolverand the toolsResolverBuilderalready fall back to the enum type; each query was checked against its aliased form.Tests
JUnitJPQLJakartaDataNoAliasTest(runtime): enum constant inSET,SET+WHERE,DELETE ... WHERE =,DELETE ... IN;room.status = <enum>;room.doors IS NOT EMPTYinUPDATEandDELETE.AbstractSemanticValidatorTest: unqualifiedUPDATE/DELETEwithmanagerEmployee.phoneNumbers IS EMPTY,address.city,embeddedAddress.city,shelfLife.soldDate, and enum constants in=andIN.org.eclipse.persistence.jpa.jpql: 114263 tests, 0 failures.JUnitJPQLJakartaDataNoAliasTeston H2: 40/40; without the fix the 5 new enum tests fail. Fulljpa.test.jpqlon H2 with and without the fix: no other test changed status.Fixes #2758
🤖 Generated with Claude Code