-
Notifications
You must be signed in to change notification settings - Fork 3.6k
HHH-19396 cannot select the same column twice with different aliases while using cte #10158
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
base: main
Are you sure you want to change the base?
Conversation
5126bf6
to
8b3f32e
Compare
Previous change was always setting |
…wice in same (sub)query
…mporary tables and subqueries Selection items representing same column in (sub)query will be represented with single instance, this will hide real alias
8b3f32e
to
38b7ede
Compare
hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java
Outdated
Show resolved
Hide resolved
hibernate-core/src/main/java/org/hibernate/query/sqm/tuple/internal/AnonymousTupleType.java
Outdated
Show resolved
Hide resolved
hibernate-core/src/main/java/org/hibernate/query/sqm/tuple/internal/AnonymousTupleType.java
Outdated
Show resolved
Hide resolved
Temporary deduplication disabling moved from org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.buildCacheableSqmInterpretation into org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitCteContainer
6a83ba0
to
9a986df
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Getting better @cigaly, there's still some things I'd like to address.
|
||
public AnonymousTupleType(SqmSelectableNode<?>[] components) { | ||
public AnonymousTupleType(SqmSelectQuery<T> selectQuery) { | ||
final SqmSelectableNode<?>[] components = extractSqmExpressibles( selectQuery ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method and extractAliases
basically do the same thing: they call selectQuery.getQueryPart().getFirstQuerySpec().getSelectClause()
and iterate over the SqmSelectableNode
s. I believe we could do it in one single look, please try to consolidate the 2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By moving some things up, some down, constructor can look like
public AnonymousTupleType(SqmSelectQuery<T> selectQuery) {
final SqmSelectClause selectClause = selectQuery.getQueryPart()
.getFirstQuerySpec()
.getSelectClause();
final SqmSelectableNode<?>[] components = extractSqmExpressibles( selectClause );
final String[] aliases = extractAliases( selectClause );
expressibles = new SqmBindableType<?>[components.length];
componentSourcePaths = new NavigablePath[components.length];
componentNames = new String[components.length];
//noinspection unchecked
javaTypeDescriptor = (JavaType<T>) new ObjectArrayJavaType( getTypeDescriptors( components ) );
componentIndexMap = linkedMapOfSize( components.length );
for ( int i = 0; i < components.length; i++ ) {
expressibles[i] = components[i].getNodeType();
if ( components[i] instanceof SqmPath<?> path ) {
componentSourcePaths[i] = path.getNavigablePath();
}
String alias = aliases[i];
if ( alias == null ) {
throw new SemanticException( "Select item at position " + (i+1) + " in select list has no alias"
+ " (aliases are required in CTEs and in subqueries occurring in from clause)" );
}
componentIndexMap.put( alias, i );
componentNames[i] = alias;
}
}
This will combine two for loops into one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, you're still calling extractSqmExpressibles
and extractAliases
, which effectively both look over the select clause selection items, correct?
I'm thinking we can do both things (collect expressible and aliases) in the same loop, e.g. something like:
final SqmSelectClause selectClause = subQuery.getQuerySpec().getSelectClause();
final List<SqmSelection<?>> selections = selectClause.getSelections();
if ( selections.isEmpty() ) {
throw new IllegalArgumentException( "subquery has no selection items" );
}
final List<SqmSelectableNode<?>> selectableNodes = new ArrayList<>( selections.size() );
final List<String> aliases = new ArrayList<>( selections.size() );
for ( SqmSelection<?> selection : selections ) {
selection.getSelectableNode().visitSubSelectableNodes( sub -> {
selectableNodes.add( sub );
if ( selection.isCompoundSelection() ) {
aliases.add( sub.getAlias() );
}
} );
if ( !selection.isCompoundSelection() ) {
aliases.add( selection.getAlias() );
}
}
Then subSelections
and aliases
contain the results.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean, something like this?
public AnonymousTupleType(SqmSelectQuery<T> selectQuery) {
final SqmSelectClause selectClause = selectQuery.getQueryPart()
.getFirstQuerySpec()
.getSelectClause();
if ( selectClause == null || selectClause.getSelectionItems().isEmpty() ) {
throw new IllegalArgumentException( "selectQuery has no selection items" );
}
// todo: right now, we "snapshot" the state of the selectQuery when creating this type, but maybe we shouldn't?
// i.e. what if the selectQuery changes later on? Or should we somehow mark the selectQuery to signal,
// that changes to the select clause are invalid after a certain point?
final List<SqmSelection<?>> selections = selectClause.getSelections();
final List<SqmSelectableNode<?>> subSelections = new ArrayList<>();
final List<String> aliases = new ArrayList<>();
if ( selections != null ) {
if ( selections.size() == 1 ) {
final SqmSelection<?> selection = selections.getFirst();
selection.getSelectableNode().visitSubSelectableNodes( subSelections::add );
selection.getSelectableNode().visitSubSelectableNodes( subSelections::add );
if ( selection.getSelectableNode().isCompoundSelection() ) {
selection.getSelectableNode().visitSubSelectableNodes( node ->
aliases.add( node.getAlias() )
);
}
else {
aliases.add( selection.getAlias() );
}
}
else {
for ( SqmSelection<?> selection : selections ) {
selection.getSelectableNode().visitSubSelectableNodes( subSelections::add );
if ( selection.getSelectableNode().isCompoundSelection() ) {
selection.getSelectableNode().visitSubSelectableNodes( node ->
aliases.add( node.getAlias() )
);
}
else {
aliases.add( selection.getAlias() );
}
}
}
}
final SqmSelectableNode<?>[] components = subSelections.toArray(new SqmSelectableNode[0]);
expressibles = new SqmBindableType<?>[components.length];
componentSourcePaths = new NavigablePath[components.length];
componentNames = new String[components.length];
//noinspection unchecked
javaTypeDescriptor = (JavaType<T>) new ObjectArrayJavaType( getTypeDescriptors( components ) );
componentIndexMap = linkedMapOfSize( components.length );
for ( int i = 0; i < components.length; i++ ) {
expressibles[i] = components[i].getNodeType();
if ( components[i] instanceof SqmPath<?> path ) {
componentSourcePaths[i] = path.getNavigablePath();
}
String alias = aliases.get( i );
if ( alias == null ) {
throw new SemanticException( "Select item at position " + (i+1) + " in select list has no alias"
+ " (aliases are required in CTEs and in subqueries occurring in from clause)" );
}
componentIndexMap.put( alias, i );
componentNames[i] = alias;
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a lot of unnecessary code. The whole if ( selections.size() == 1 )
seems unnecessary, and why would we call visitSubSelectableNodes
twice? What I meant was what I wrote in my previous comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, my mistake, I've made copy of code from org.hibernate.query.sqm.tree.select.SqmSelectClause#getSelectionItems
. I will now (properly) change code and commit changes.
hibernate-core/src/main/java/org/hibernate/query/sqm/tuple/internal/AnonymousTupleType.java
Outdated
Show resolved
Hide resolved
hibernate-core/src/main/java/org/hibernate/query/sqm/tuple/internal/AnonymousTupleType.java
Outdated
Show resolved
Hide resolved
9a986df
to
9429487
Compare
Jira issue HHH-19396
When creating
SqlAstQueryPartProcessingStateImpl
in methodorg.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter#visitQuerySpec
parameterdeduplicateSelectionItems
must be set tofalse
(see comment in moved code block)When
selectStatement
contains same column more than once in methodorg.hibernate.query.sqm.tree.cte.SqmCteTable#createStatementTable
,SqmCteTable
constructor will be unable to properly resolve aliases. To prevent this alias in select clause selection should be used if exist, one from selectable node should be used otherwise.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license
and can be relicensed under the terms of the LGPL v2.1 license in the future at the maintainers' discretion.
For more information on licensing, please check here.
https://hibernate.atlassian.net/browse/HHH-19396