HHH-15161 Infer parameter types in insert-select and avoid rendering cast in simple insert-select
This commit is contained in:
parent
a4b6b237dd
commit
6a7bec612f
|
@ -32,6 +32,7 @@ import org.hibernate.sql.ast.tree.from.FunctionTableReference;
|
|||
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
||||
import org.hibernate.sql.ast.tree.from.UnionTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.ValuesTableReference;
|
||||
import org.hibernate.sql.ast.tree.insert.InsertStatement;
|
||||
import org.hibernate.sql.ast.tree.insert.Values;
|
||||
import org.hibernate.sql.ast.tree.select.QueryGroup;
|
||||
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||
|
@ -125,7 +126,9 @@ public class OracleSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
|
|||
// Even if Oracle supports the OFFSET/FETCH clause, there are conditions where we still want to use the ROWNUM pagination
|
||||
if ( supportsOffsetFetchClause() ) {
|
||||
// When the query has no sort specifications and offset, we want to use the ROWNUM pagination as that is a special locking case
|
||||
return !queryPart.hasSortSpecifications() && !hasOffset( queryPart );
|
||||
return !queryPart.hasSortSpecifications() && !hasOffset( queryPart )
|
||||
// Workaround an Oracle bug, segmentation fault for insert queries with a plain query group and fetch clause
|
||||
|| queryPart instanceof QueryGroup && getClauseStack().isEmpty() && getStatement() instanceof InsertStatement;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -191,7 +191,14 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
|
|||
);
|
||||
appendSql( "* from (" );
|
||||
renderQueryGroup( queryGroup, false );
|
||||
appendSql( ") grp_" );
|
||||
appendSql( ") grp_(c0" );
|
||||
// Sybase doesn't have implicit names for non-column select expressions, so we need to assign names
|
||||
final int itemCount = queryGroup.getFirstQuerySpec().getSelectClause().getSqlSelections().size();
|
||||
for (int i = 1; i < itemCount; i++) {
|
||||
appendSql( ",c" );
|
||||
appendSql( i );
|
||||
}
|
||||
appendSql( ')' );
|
||||
visitOrderBy( queryGroup.getSortSpecifications() );
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -21,7 +21,7 @@ import org.hibernate.query.hql.spi.SqmCreationProcessingState;
|
|||
import org.hibernate.query.hql.spi.SqmCreationState;
|
||||
import org.hibernate.query.hql.spi.SqmPathRegistry;
|
||||
import org.hibernate.query.sqm.internal.SqmDmlCreationProcessingState;
|
||||
import org.hibernate.query.sqm.internal.SqmQuerySpecCreationProcessingStateStandardImpl;
|
||||
import org.hibernate.query.sqm.internal.SqmQueryPartCreationProcessingStateStandardImpl;
|
||||
import org.hibernate.query.sqm.spi.BaseSemanticQueryWalker;
|
||||
import org.hibernate.query.sqm.spi.SqmCreationContext;
|
||||
import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
|
||||
|
@ -266,7 +266,7 @@ public class QuerySplitter {
|
|||
final SqmSelectStatement<R> copy = new SqmSelectStatement<>( statement.nodeBuilder() );
|
||||
|
||||
processingStateStack.push(
|
||||
new SqmQuerySpecCreationProcessingStateStandardImpl(
|
||||
new SqmQueryPartCreationProcessingStateStandardImpl(
|
||||
processingStateStack.getCurrent(),
|
||||
copy,
|
||||
this
|
||||
|
|
|
@ -94,7 +94,7 @@ import org.hibernate.query.sqm.function.SqmFunctionDescriptor;
|
|||
import org.hibernate.query.sqm.internal.ParameterCollector;
|
||||
import org.hibernate.query.sqm.internal.SqmCreationProcessingStateImpl;
|
||||
import org.hibernate.query.sqm.internal.SqmDmlCreationProcessingState;
|
||||
import org.hibernate.query.sqm.internal.SqmQuerySpecCreationProcessingStateStandardImpl;
|
||||
import org.hibernate.query.sqm.internal.SqmQueryPartCreationProcessingStateStandardImpl;
|
||||
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
|
||||
import org.hibernate.query.sqm.spi.ParameterDeclarationContext;
|
||||
import org.hibernate.query.sqm.spi.SqmCreationContext;
|
||||
|
@ -383,7 +383,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
parameterCollector = selectStatement;
|
||||
|
||||
processingStateStack.push(
|
||||
new SqmQuerySpecCreationProcessingStateStandardImpl(
|
||||
new SqmQueryPartCreationProcessingStateStandardImpl(
|
||||
processingStateStack.getCurrent(),
|
||||
selectStatement,
|
||||
this
|
||||
|
@ -470,7 +470,6 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
try {
|
||||
for ( HqlParser.SimplePathContext stateFieldCtx : targetFieldsSpecContext.simplePath() ) {
|
||||
final SqmPath<?> stateField = (SqmPath<?>) visitSimplePath( stateFieldCtx );
|
||||
// todo : validate each resolved stateField...
|
||||
insertStatement.addInsertTargetStateField( stateField );
|
||||
}
|
||||
}
|
||||
|
@ -509,7 +508,6 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
|
||||
for ( HqlParser.SimplePathContext stateFieldCtx : targetFieldsSpecContext.simplePath() ) {
|
||||
final SqmPath<?> stateField = (SqmPath<?>) visitSimplePath( stateFieldCtx );
|
||||
// todo : validate each resolved stateField...
|
||||
insertStatement.addInsertTargetStateField( stateField );
|
||||
}
|
||||
|
||||
|
@ -518,7 +516,6 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
finally {
|
||||
processingStateStack.pop();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -639,9 +636,9 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
if ( children.size() > 3 ) {
|
||||
final SqmCreationProcessingState firstProcessingState = processingStateStack.pop();
|
||||
processingStateStack.push(
|
||||
new SqmQuerySpecCreationProcessingStateStandardImpl(
|
||||
new SqmQueryPartCreationProcessingStateStandardImpl(
|
||||
processingStateStack.getCurrent(),
|
||||
(SqmSelectQuery<?>) firstProcessingState.getProcessingQuery(),
|
||||
firstProcessingState.getProcessingQuery(),
|
||||
this
|
||||
)
|
||||
);
|
||||
|
@ -676,9 +673,9 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
(HqlParser.OrderedQueryContext) children.get( i + 1 );
|
||||
final List<SqmQueryPart<Object>> queryParts;
|
||||
processingStateStack.push(
|
||||
new SqmQuerySpecCreationProcessingStateStandardImpl(
|
||||
new SqmQueryPartCreationProcessingStateStandardImpl(
|
||||
processingStateStack.getCurrent(),
|
||||
(SqmSelectQuery<?>) firstProcessingState.getProcessingQuery(),
|
||||
firstProcessingState.getProcessingQuery(),
|
||||
this
|
||||
)
|
||||
);
|
||||
|
@ -709,7 +706,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
try {
|
||||
final SqmSelectStatement<Object> selectStatement = new SqmSelectStatement<>( creationContext.getNodeBuilder() );
|
||||
processingStateStack.push(
|
||||
new SqmQuerySpecCreationProcessingStateStandardImpl(
|
||||
new SqmQueryPartCreationProcessingStateStandardImpl(
|
||||
processingStateStack.getCurrent(),
|
||||
selectStatement,
|
||||
this
|
||||
|
@ -4531,7 +4528,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
);
|
||||
|
||||
processingStateStack.push(
|
||||
new SqmQuerySpecCreationProcessingStateStandardImpl(
|
||||
new SqmQueryPartCreationProcessingStateStandardImpl(
|
||||
processingStateStack.getCurrent(),
|
||||
subQuery,
|
||||
this
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* 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.query.hql.spi;
|
||||
|
||||
import org.hibernate.Incubating;
|
||||
import org.hibernate.query.sqm.tree.select.SqmSelectQuery;
|
||||
|
||||
/**
|
||||
* SqmCreationProcessingState specialization for processing a SQM query-spec
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Incubating
|
||||
public interface SqmQuerySpecCreationProcessingState extends SqmCreationProcessingState {
|
||||
@Override
|
||||
SqmSelectQuery<?> getProcessingQuery();
|
||||
}
|
|
@ -8,8 +8,7 @@ package org.hibernate.query.sqm.internal;
|
|||
|
||||
import org.hibernate.query.hql.spi.SqmCreationProcessingState;
|
||||
import org.hibernate.query.hql.spi.SqmCreationState;
|
||||
import org.hibernate.query.hql.spi.SqmQuerySpecCreationProcessingState;
|
||||
import org.hibernate.query.sqm.tree.select.SqmSelectQuery;
|
||||
import org.hibernate.query.sqm.tree.SqmQuery;
|
||||
|
||||
/**
|
||||
* Models the state related to parsing a sqm spec. As a "linked list" to account for
|
||||
|
@ -18,15 +17,13 @@ import org.hibernate.query.sqm.tree.select.SqmSelectQuery;
|
|||
* @author Steve Ebersole
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
public class SqmQuerySpecCreationProcessingStateStandardImpl
|
||||
extends SqmCreationProcessingStateImpl
|
||||
implements SqmQuerySpecCreationProcessingState {
|
||||
public class SqmQueryPartCreationProcessingStateStandardImpl extends SqmCreationProcessingStateImpl {
|
||||
|
||||
private final SqmCreationProcessingState parentState;
|
||||
|
||||
public SqmQuerySpecCreationProcessingStateStandardImpl(
|
||||
public SqmQueryPartCreationProcessingStateStandardImpl(
|
||||
SqmCreationProcessingState parentState,
|
||||
SqmSelectQuery<?> processingQuery,
|
||||
SqmQuery<?> processingQuery,
|
||||
SqmCreationState creationState) {
|
||||
super( processingQuery, creationState );
|
||||
this.parentState = parentState;
|
||||
|
@ -37,8 +34,4 @@ public class SqmQuerySpecCreationProcessingStateStandardImpl
|
|||
return parentState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmSelectQuery<?> getProcessingQuery() {
|
||||
return (SqmSelectQuery<?>) super.getProcessingQuery();
|
||||
}
|
||||
}
|
|
@ -108,7 +108,6 @@ import org.hibernate.query.QueryLogging;
|
|||
import org.hibernate.query.ReturnableType;
|
||||
import org.hibernate.query.SemanticException;
|
||||
import org.hibernate.query.criteria.JpaPath;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.query.spi.QueryOptions;
|
||||
import org.hibernate.query.spi.QueryParameterBinding;
|
||||
import org.hibernate.query.spi.QueryParameterBindings;
|
||||
|
@ -255,6 +254,7 @@ import org.hibernate.query.sqm.tree.select.SqmSubQuery;
|
|||
import org.hibernate.query.sqm.tree.update.SqmAssignment;
|
||||
import org.hibernate.query.sqm.tree.update.SqmSetClause;
|
||||
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
import org.hibernate.sql.ast.SqlTreeCreationException;
|
||||
|
@ -1886,10 +1886,13 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
final SelectClause sqlSelectClause = currentQuerySpec().getSelectClause();
|
||||
if ( selectClause == null ) {
|
||||
final SqmFrom<?, ?> implicitSelection = determineImplicitSelection( (SqmQuerySpec<?>) currentSqmQueryPart );
|
||||
visitSelection( new SqmSelection<>( implicitSelection, implicitSelection.nodeBuilder() ) );
|
||||
visitSelection( 0, new SqmSelection<>( implicitSelection, implicitSelection.nodeBuilder() ) );
|
||||
}
|
||||
else {
|
||||
super.visitSelectClause( selectClause );
|
||||
final List<SqmSelection<?>> selections = selectClause.getSelections();
|
||||
for ( int i = 0; i < selections.size(); i++ ) {
|
||||
visitSelection( i, selections.get( i ) );
|
||||
}
|
||||
sqlSelectClause.makeDistinct( selectClause.isDistinct() );
|
||||
}
|
||||
return sqlSelectClause;
|
||||
|
@ -1906,6 +1909,20 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
|
||||
@Override
|
||||
public Void visitSelection(SqmSelection<?> sqmSelection) {
|
||||
return visitSelection(
|
||||
currentSqmQueryPart.getFirstQuerySpec().getSelectClause().getSelections().indexOf( sqmSelection ),
|
||||
sqmSelection
|
||||
);
|
||||
}
|
||||
|
||||
public Void visitSelection(int index, SqmSelection<?> sqmSelection) {
|
||||
final boolean contributesToTopLevelSelectClause = currentClauseStack.depth() == 1 && currentClauseStack.getCurrent() == Clause.SELECT;
|
||||
// Only infer the type on the "top level" select clauses
|
||||
final boolean inferTargetPath = statement instanceof SqmInsertSelectStatement<?> && contributesToTopLevelSelectClause;
|
||||
if ( inferTargetPath ) {
|
||||
final SqmPath<?> path = ( (SqmInsertSelectStatement<?>) statement ).getInsertionTargetPaths().get( index );
|
||||
inferrableTypeAccessStack.push( () -> determineValueMapping( path ) );
|
||||
}
|
||||
final List<Map.Entry<String, DomainResultProducer<?>>> resultProducers;
|
||||
final SqmSelectableNode<?> selectionNode = sqmSelection.getSelectableNode();
|
||||
if ( selectionNode instanceof SqmJpaCompoundSelection<?> ) {
|
||||
|
@ -1937,7 +1954,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
}
|
||||
|
||||
final Stack<SqlAstProcessingState> processingStateStack = getProcessingStateStack();
|
||||
final boolean needsDomainResults = domainResults != null && currentClauseContributesToTopLevelSelectClause();
|
||||
final boolean needsDomainResults = domainResults != null && contributesToTopLevelSelectClause;
|
||||
final boolean collectDomainResults;
|
||||
if ( processingStateStack.depth() == 1 ) {
|
||||
collectDomainResults = needsDomainResults;
|
||||
|
@ -2002,14 +2019,12 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
}
|
||||
);
|
||||
}
|
||||
if ( inferTargetPath ) {
|
||||
inferrableTypeAccessStack.pop();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean currentClauseContributesToTopLevelSelectClause() {
|
||||
// The current clause contributes to the top level select if the clause stack contains just SELECT
|
||||
return currentClauseStack.findCurrentFirst( clause -> clause == Clause.SELECT ? null : clause ) == null;
|
||||
}
|
||||
|
||||
protected Expression resolveGroupOrOrderByExpression(SqmExpression<?> groupByClauseExpression) {
|
||||
final int sqmPosition;
|
||||
final NavigablePath path;
|
||||
|
|
|
@ -2903,7 +2903,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
|||
appendSql( OPEN_PARENTHESIS );
|
||||
}
|
||||
appendSql( "select " );
|
||||
if ( getClauseStack().isEmpty() ) {
|
||||
if ( getClauseStack().isEmpty() && !( statement instanceof InsertStatement ) ) {
|
||||
appendSql( '*' );
|
||||
}
|
||||
else {
|
||||
|
@ -3097,6 +3097,14 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
|||
selectItemsToInline = null;
|
||||
}
|
||||
final SqlAstNodeRenderingMode original = parameterRenderingMode;
|
||||
final SqlAstNodeRenderingMode defaultRenderingMode;
|
||||
if ( statement instanceof InsertStatement && clauseStack.depth() == 1 && queryPartStack.depth() == 1 ) {
|
||||
// Databases support inferring parameter types for simple insert-select statements
|
||||
defaultRenderingMode = SqlAstNodeRenderingMode.DEFAULT;
|
||||
}
|
||||
else {
|
||||
defaultRenderingMode = SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER;
|
||||
}
|
||||
if ( needsSelectAliases || referenceStrategy == SelectItemReferenceStrategy.ALIAS && hasSelectAliasInGroupByClause() ) {
|
||||
String separator = NO_SEPARATOR;
|
||||
if ( columnAliases == null ) {
|
||||
|
@ -3107,7 +3115,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
|||
parameterRenderingMode = SqlAstNodeRenderingMode.INLINE_ALL_PARAMETERS;
|
||||
}
|
||||
else {
|
||||
parameterRenderingMode = SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER;
|
||||
parameterRenderingMode = defaultRenderingMode;
|
||||
}
|
||||
visitSqlSelection( sqlSelection );
|
||||
parameterRenderingMode = original;
|
||||
|
@ -3124,7 +3132,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
|||
parameterRenderingMode = SqlAstNodeRenderingMode.INLINE_ALL_PARAMETERS;
|
||||
}
|
||||
else {
|
||||
parameterRenderingMode = SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER;
|
||||
parameterRenderingMode = defaultRenderingMode;
|
||||
}
|
||||
visitSqlSelection( sqlSelection );
|
||||
parameterRenderingMode = original;
|
||||
|
@ -3146,7 +3154,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
|||
parameterRenderingMode = SqlAstNodeRenderingMode.INLINE_ALL_PARAMETERS;
|
||||
}
|
||||
else {
|
||||
parameterRenderingMode = SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER;
|
||||
parameterRenderingMode = defaultRenderingMode;
|
||||
}
|
||||
visitSqlSelection( sqlSelection );
|
||||
parameterRenderingMode = original;
|
||||
|
@ -3162,7 +3170,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
|||
parameterRenderingMode = SqlAstNodeRenderingMode.INLINE_ALL_PARAMETERS;
|
||||
}
|
||||
else {
|
||||
parameterRenderingMode = SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER;
|
||||
parameterRenderingMode = defaultRenderingMode;
|
||||
}
|
||||
visitSqlSelection( sqlSelection );
|
||||
appendSql( WHITESPACE );
|
||||
|
|
|
@ -273,6 +273,56 @@ public class BulkManipulationTest extends BaseCoreFunctionalTestCase {
|
|||
data.cleanup();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-15161")
|
||||
public void testInsertWithNullParamValue() {
|
||||
TestData data = new TestData();
|
||||
data.prepare();
|
||||
|
||||
Session s = openSession();
|
||||
Transaction t = s.beginTransaction();
|
||||
|
||||
Query q = s.createQuery( "insert into Pickup (id, owner, vin) select id, :owner, vin from Car" );
|
||||
q.setParameter( "owner", null );
|
||||
|
||||
q.executeUpdate();
|
||||
|
||||
t.commit();
|
||||
t = s.beginTransaction();
|
||||
|
||||
s.createQuery( "delete Vehicle" ).executeUpdate();
|
||||
|
||||
t.commit();
|
||||
s.close();
|
||||
|
||||
data.cleanup();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-15161")
|
||||
public void testInsertWithNullParamValueSetOperation() {
|
||||
TestData data = new TestData();
|
||||
data.prepare();
|
||||
|
||||
Session s = openSession();
|
||||
Transaction t = s.beginTransaction();
|
||||
|
||||
Query q = s.createQuery( "insert into Pickup (id, owner, vin) (select id, :owner, vin from Car union all select id, :owner, vin from Car) order by 1 limit 1" );
|
||||
q.setParameter( "owner", null );
|
||||
|
||||
q.executeUpdate();
|
||||
|
||||
t.commit();
|
||||
t = s.beginTransaction();
|
||||
|
||||
s.createQuery( "delete Vehicle" ).executeUpdate();
|
||||
|
||||
t.commit();
|
||||
s.close();
|
||||
|
||||
data.cleanup();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertWithMultipleNamedParams() {
|
||||
TestData data = new TestData();
|
||||
|
|
Loading…
Reference in New Issue