mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-28 06:49:09 +00:00
HHH-13796 - Missing from clause in query from BinaryLogicOperatorNode row value constructor translation
https://hibernate.atlassian.net/browse/HHH-13796
This commit is contained in:
parent
38f0131160
commit
85bfcc6e21
@ -24,7 +24,7 @@
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public abstract class AbstractMapComponentNode extends FromReferenceNode implements HqlSqlTokenTypes {
|
||||
public abstract class AbstractMapComponentNode extends FromReferenceNode implements HqlSqlTokenTypes, TableReferenceNode {
|
||||
private FromElement mapFromElement;
|
||||
private String[] columns;
|
||||
|
||||
|
@ -159,35 +159,43 @@ protected void translate(
|
||||
ParameterSpecification lhsEmbeddedCompositeParameterSpecification,
|
||||
ParameterSpecification rhsEmbeddedCompositeParameterSpecification,
|
||||
AST container) {
|
||||
Node leftHandOperand = this.getLeftHandOperand();
|
||||
Node rightHandOperand = this.getRightHandOperand();
|
||||
|
||||
for ( int i = valueElements - 1; i > 0; i-- ) {
|
||||
if ( i == 1 ) {
|
||||
AST op1 = getASTFactory().create( comparisonType, comparisonText );
|
||||
AST lhs1 = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, lhsElementTexts[0] );
|
||||
AST rhs1 = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, rhsElementTexts[0] );
|
||||
SqlFragment lhs1 = (SqlFragment) getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, lhsElementTexts[0] );
|
||||
SqlFragment rhs1 = (SqlFragment) getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, rhsElementTexts[0] );
|
||||
copyReferencedTables( leftHandOperand, lhs1 );
|
||||
copyReferencedTables( rightHandOperand, rhs1 );
|
||||
op1.setFirstChild( lhs1 );
|
||||
lhs1.setNextSibling( rhs1 );
|
||||
container.setFirstChild( op1 );
|
||||
AST op2 = getASTFactory().create( comparisonType, comparisonText );
|
||||
AST lhs2 = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, lhsElementTexts[1] );
|
||||
AST rhs2 = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, rhsElementTexts[1] );
|
||||
SqlFragment lhs2 = (SqlFragment) getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, lhsElementTexts[1] );
|
||||
SqlFragment rhs2 = (SqlFragment) getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, rhsElementTexts[1] );
|
||||
copyReferencedTables( leftHandOperand, lhs2 );
|
||||
copyReferencedTables( rightHandOperand, rhs2 );
|
||||
op2.setFirstChild( lhs2 );
|
||||
lhs2.setNextSibling( rhs2 );
|
||||
op1.setNextSibling( op2 );
|
||||
|
||||
// "pass along" our initial embedded parameter node(s) to the first generated
|
||||
// sql fragment so that it can be handled later for parameter binding...
|
||||
SqlFragment fragment = (SqlFragment) lhs1;
|
||||
if ( lhsEmbeddedCompositeParameterSpecification != null ) {
|
||||
fragment.addEmbeddedParameter( lhsEmbeddedCompositeParameterSpecification );
|
||||
lhs1.addEmbeddedParameter( lhsEmbeddedCompositeParameterSpecification );
|
||||
}
|
||||
if ( rhsEmbeddedCompositeParameterSpecification != null ) {
|
||||
fragment.addEmbeddedParameter( rhsEmbeddedCompositeParameterSpecification );
|
||||
lhs1.addEmbeddedParameter( rhsEmbeddedCompositeParameterSpecification );
|
||||
}
|
||||
}
|
||||
else {
|
||||
AST op = getASTFactory().create( comparisonType, comparisonText );
|
||||
AST lhs = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, lhsElementTexts[i] );
|
||||
AST rhs = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, rhsElementTexts[i] );
|
||||
SqlFragment lhs = (SqlFragment) getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, lhsElementTexts[i] );
|
||||
SqlFragment rhs = (SqlFragment) getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, rhsElementTexts[i] );
|
||||
copyReferencedTables( leftHandOperand, lhs );
|
||||
copyReferencedTables( rightHandOperand, rhs );
|
||||
op.setFirstChild( lhs );
|
||||
lhs.setNextSibling( rhs );
|
||||
AST newContainer = getASTFactory().create( container.getType(), container.getText() );
|
||||
@ -198,6 +206,13 @@ protected void translate(
|
||||
}
|
||||
}
|
||||
|
||||
private static void copyReferencedTables(Node from, SqlFragment to) {
|
||||
if (from instanceof TableReferenceNode) {
|
||||
TableReferenceNode tableReferenceNode = (TableReferenceNode) from;
|
||||
to.setReferencedTables( tableReferenceNode.getReferencedTables() );
|
||||
}
|
||||
}
|
||||
|
||||
protected static String[] extractMutationTexts(Node operand, int count) {
|
||||
if ( operand instanceof ParameterNode ) {
|
||||
String[] rtn = new String[count];
|
||||
|
@ -36,7 +36,7 @@
|
||||
*
|
||||
* @author Joshua Davis
|
||||
*/
|
||||
public class DotNode extends FromReferenceNode implements DisplayableNode, SelectExpression {
|
||||
public class DotNode extends FromReferenceNode implements DisplayableNode, SelectExpression, TableReferenceNode {
|
||||
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( DotNode.class );
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
@ -77,12 +77,6 @@ public static enum DereferenceType {
|
||||
*/
|
||||
private String propertyName;
|
||||
|
||||
/**
|
||||
* The identifier that is the name of the property. In comparison with {@link #propertyName}
|
||||
* it is always identical with identifier in the query, it is not changed during processing.
|
||||
*/
|
||||
private String originalPropertyName;
|
||||
|
||||
/**
|
||||
* The full path, to the root alias of this dot node.
|
||||
*/
|
||||
@ -168,7 +162,6 @@ public void resolveFirstChild() throws SemanticException {
|
||||
// Set the attributes of the property reference expression.
|
||||
String propName = property.getText();
|
||||
propertyName = propName;
|
||||
originalPropertyName = propName;
|
||||
// If the uresolved property path isn't set yet, just use the property name.
|
||||
if ( propertyPath == null ) {
|
||||
propertyPath = propName;
|
||||
@ -703,21 +696,15 @@ public Type getDataType() {
|
||||
|
||||
@Override
|
||||
public String[] getReferencedTables() {
|
||||
String[] referencedTables = null;
|
||||
AST firstChild = getFirstChild();
|
||||
if ( firstChild != null ) {
|
||||
if ( firstChild instanceof FromReferenceNode ) {
|
||||
FromReferenceNode fromReferenceNode = (FromReferenceNode) firstChild;
|
||||
FromElement fromElement = fromReferenceNode.getFromElement();
|
||||
if ( fromElement != null ) {
|
||||
String table = fromElement.getPropertyTableName( getOriginalPropertyName() );
|
||||
if ( table != null ) {
|
||||
referencedTables = new String[] { table };
|
||||
}
|
||||
}
|
||||
FromReferenceNode lhs = ( (FromReferenceNode) getFirstChild() );
|
||||
if ( lhs != null) {
|
||||
FromElement fromElement = lhs.getFromElement();
|
||||
if ( fromElement != null ) {
|
||||
String propertyTableName = fromElement.getPropertyTableName( propertyPath );
|
||||
return new String[] { propertyTableName };
|
||||
}
|
||||
}
|
||||
return referencedTables;
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setPropertyPath(String propertyPath) {
|
||||
@ -728,14 +715,6 @@ public String getPropertyPath() {
|
||||
return propertyPath;
|
||||
}
|
||||
|
||||
public String getPropertyName() {
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
public String getOriginalPropertyName() {
|
||||
return originalPropertyName;
|
||||
}
|
||||
|
||||
public FromReferenceNode getLhs() {
|
||||
FromReferenceNode lhs = ( (FromReferenceNode) getFirstChild() );
|
||||
if ( lhs == null ) {
|
||||
|
@ -377,7 +377,12 @@ public String getPropertyTableName(String propertyName) {
|
||||
checkInitialized();
|
||||
if ( this.persister != null ) {
|
||||
AbstractEntityPersister aep = (AbstractEntityPersister) this.persister;
|
||||
return aep.getPropertyTableName( propertyName );
|
||||
try {
|
||||
return aep.getSubclassTableName( aep.getSubclassPropertyTableNumber( propertyName ) );
|
||||
}
|
||||
catch (QueryException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -137,14 +137,4 @@ protected boolean isFromElementUpdateOrDeleteRoot(FromElement element) {
|
||||
|| getWalker().getStatementType() == HqlSqlTokenTypes.UPDATE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns table names which are referenced by this node. If the tables
|
||||
* can not be determined it returns null.
|
||||
*
|
||||
* @return table names or null.
|
||||
*/
|
||||
public String[] getReferencedTables() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,9 +17,10 @@
|
||||
*
|
||||
* @author josh
|
||||
*/
|
||||
public class SqlFragment extends Node implements ParameterContainer {
|
||||
public class SqlFragment extends Node implements ParameterContainer, TableReferenceNode {
|
||||
private JoinFragment joinFragment;
|
||||
private FromElement fromElement;
|
||||
private String[] referencedTables;
|
||||
|
||||
public void setJoinFragment(JoinFragment joinFragment) {
|
||||
this.joinFragment = joinFragment;
|
||||
@ -56,4 +57,13 @@ public boolean hasEmbeddedParameters() {
|
||||
public ParameterSpecification[] getEmbeddedParameters() {
|
||||
return embeddedParameters.toArray( new ParameterSpecification[ embeddedParameters.size() ] );
|
||||
}
|
||||
|
||||
public String[] getReferencedTables() {
|
||||
return referencedTables;
|
||||
}
|
||||
|
||||
public void setReferencedTables(String[] referencedTables) {
|
||||
this.referencedTables = referencedTables;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
|
||||
package org.hibernate.hql.internal.ast.tree;
|
||||
|
||||
/**
|
||||
* @author Jan-Willem Gmelig Meyling
|
||||
*/
|
||||
public interface TableReferenceNode {
|
||||
|
||||
/**
|
||||
* Returns table names which are referenced by this node. If the tables
|
||||
* can not be determined it returns null.
|
||||
*
|
||||
* @return table names or null.
|
||||
*/
|
||||
public String[] getReferencedTables();
|
||||
|
||||
}
|
@ -42,13 +42,10 @@ public LinkedHashMap<String, Object> createNodeProperties(AST node) {
|
||||
EntityPersister entityPersister = fromElement != null ? fromElement.getEntityPersister() : null;
|
||||
String entityPersisterStr = entityPersister != null ? entityPersister.toString() : null;
|
||||
props.put( "persister", entityPersisterStr );
|
||||
String referencedTablesStr = Arrays.toString( frn.getReferencedTables() );
|
||||
props.put( "referencedTables", referencedTablesStr );
|
||||
}
|
||||
if ( node instanceof DotNode ) {
|
||||
DotNode dn = (DotNode) node;
|
||||
props.put( "path", dn.getPath() );
|
||||
props.put( "originalPropertyName", dn.getOriginalPropertyName() );
|
||||
}
|
||||
if ( node instanceof IdentNode ) {
|
||||
IdentNode in = (IdentNode) node;
|
||||
|
@ -33,6 +33,7 @@
|
||||
import org.hibernate.hql.internal.ast.tree.ParameterContainer;
|
||||
import org.hibernate.hql.internal.ast.tree.QueryNode;
|
||||
import org.hibernate.hql.internal.ast.tree.SqlFragment;
|
||||
import org.hibernate.hql.internal.ast.tree.TableReferenceNode;
|
||||
import org.hibernate.hql.internal.classic.ParserHelper;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
@ -167,14 +168,14 @@ private Set<String> findQueryReferencedTables(QueryNode query) {
|
||||
private void collectReferencedTables(ASTIterator iterator, Set<String> result) {
|
||||
while ( iterator.hasNext() ) {
|
||||
AST node = iterator.nextNode();
|
||||
if ( node instanceof FromReferenceNode ) {
|
||||
FromReferenceNode fromReferenceNode = (FromReferenceNode) node;
|
||||
if ( node instanceof TableReferenceNode) {
|
||||
TableReferenceNode fromReferenceNode = (TableReferenceNode) node;
|
||||
String[] tables = fromReferenceNode.getReferencedTables();
|
||||
if ( tables != null ) {
|
||||
Collections.addAll(result, tables);
|
||||
}
|
||||
}
|
||||
else if (node instanceof SqlFragment) {
|
||||
if (node instanceof SqlFragment) {
|
||||
SqlFragment sqlFragment = (SqlFragment) node;
|
||||
FromElement fromElement = sqlFragment.getFromElement();
|
||||
|
||||
|
@ -6,13 +6,34 @@
|
||||
*/
|
||||
package org.hibernate.jpa.test.jointable;
|
||||
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.engine.query.spi.HQLQueryPlan;
|
||||
import org.hibernate.hql.spi.QueryTranslator;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.persistence.DiscriminatorColumn;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Embedded;
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.IdClass;
|
||||
import javax.persistence.Inheritance;
|
||||
import javax.persistence.InheritanceType;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinColumns;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.NamedQuery;
|
||||
import javax.persistence.SecondaryTable;
|
||||
import javax.persistence.Table;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
@ -24,11 +45,19 @@ public class ManyToOneJoinTableTest extends BaseCoreFunctionalTestCase {
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] {
|
||||
Person.class,
|
||||
Address.class
|
||||
Address.class,
|
||||
ResourceImpl.class,
|
||||
IssuerImpl.class
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
protected void configure(Configuration configuration) {
|
||||
super.configure(configuration);
|
||||
// configuration.setProperty(AvailableSettings.OMIT_JOIN_OF_SUPERCLASS_TABLES, Boolean.FALSE.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAvoidJoin() {
|
||||
final HQLQueryPlan plan = sessionFactory().getQueryPlanCache().getHQLQueryPlan(
|
||||
"SELECT e.id FROM Person e",
|
||||
@ -42,4 +71,119 @@ public void testAvoidJoin() {
|
||||
// Since *ToOne join tables are treated like secondary or subclass/superclass tables, the proper fix will allow many more optimizations
|
||||
assertFalse( generatedSql.contains( "join" ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegression() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
session.createNamedQuery(IssuerImpl.SELECT_RESOURCES_BY_ISSUER)
|
||||
.setParameter("issuer", session.getReference(IssuerImpl.class, new Identifier(1l, "ABC")))
|
||||
.getResultList();
|
||||
} );
|
||||
}
|
||||
|
||||
public interface Issuer extends Resource {}
|
||||
|
||||
@Entity(name = IssuerImpl.ENTITY_NAME)
|
||||
@SecondaryTable(name = IssuerImpl.TABLE_NAME)
|
||||
@NamedQuery(name = IssuerImpl.SELECT_RESOURCES_BY_ISSUER, query = "SELECT resource.identifier FROM " + ResourceImpl.ENTITY_NAME + " resource WHERE resource.identifier.issuer IN (SELECT issuer.identifier.issuer FROM " + IssuerImpl.ENTITY_NAME + " issuer WHERE issuer.parentIssuer = :issuer OR issuer = :issuer)")
|
||||
public static class IssuerImpl extends ResourceImpl implements Issuer {
|
||||
|
||||
private static final String SELECT_RESOURCES_BY_ISSUER = "SELECT_RESOURCES_BY_ISSUER";
|
||||
private static final String ENTITY_NAME = "Issuer";
|
||||
public static final String PARENT_ISSUER_COLUMN = "parent_issuer";
|
||||
public static final String PARENT_IDENTIFIER_COLUMN = "parent_identifier";
|
||||
public static final String TABLE_NAME = "issuer_impl";
|
||||
|
||||
@ManyToOne(targetEntity = IssuerImpl.class)
|
||||
@JoinColumns({
|
||||
@JoinColumn(name = PARENT_ISSUER_COLUMN, table = TABLE_NAME, referencedColumnName = "issuer"),
|
||||
@JoinColumn(name = PARENT_IDENTIFIER_COLUMN, table = TABLE_NAME, referencedColumnName = "identifier")
|
||||
})
|
||||
private Issuer parentIssuer;
|
||||
|
||||
public Identifier getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
public void setIdentifier(Identifier identifier) {
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
public Issuer getParentIssuer() {
|
||||
return parentIssuer;
|
||||
}
|
||||
|
||||
public void setParentIssuer(Issuer parentIssuer) {
|
||||
this.parentIssuer = parentIssuer;
|
||||
}
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
public static class Identifier implements Serializable {
|
||||
Long issuer;
|
||||
String identifier;
|
||||
|
||||
public Long getIssuer() {
|
||||
return issuer;
|
||||
}
|
||||
|
||||
public void setIssuer(Long issuer) {
|
||||
this.issuer = issuer;
|
||||
}
|
||||
|
||||
public String getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
public void setIdentifier(String identifier) {
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
public Identifier() {
|
||||
|
||||
}
|
||||
|
||||
public Identifier(Long issuer, String identifier) {
|
||||
this.issuer = issuer;
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Identifier that = (Identifier) o;
|
||||
return Objects.equals(issuer, that.issuer) &&
|
||||
Objects.equals(identifier, that.identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(issuer, identifier);
|
||||
}
|
||||
}
|
||||
|
||||
public interface Resource {
|
||||
|
||||
}
|
||||
|
||||
@Entity(name = ResourceImpl.ENTITY_NAME)
|
||||
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
|
||||
public static class ResourceImpl implements Resource {
|
||||
|
||||
private static final String ENTITY_NAME = "Resource";
|
||||
|
||||
@EmbeddedId
|
||||
Identifier identifier;
|
||||
|
||||
public Identifier getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
public void setIdentifier(Identifier identifier) {
|
||||
this.identifier = identifier;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user