@Override
protected void postProcessInsert(AST insert) throws SemanticException, QueryException {
InsertStatement insertStatement = (InsertStatement) insert;
insertStatement.validate();
SelectClause selectClause = insertStatement.getSelectClause();
Queryable persister = insertStatement.getIntoClause().getQueryable();
if ( !insertStatement.getIntoClause().isExplicitIdInsertion() ) {
// the insert did not explicitly reference the id. See if
// 1) that is allowed
// 2) whether we need to alter the SQL tree to account for id
final IdentifierGenerator generator = persister.getIdentifierGenerator();
if ( !BulkInsertionCapableIdentifierGenerator.class.isInstance( generator ) ) {
throw new QueryException(
"Invalid identifier generator encountered for implicit id handling as part of bulk insertions"
);
}
final BulkInsertionCapableIdentifierGenerator capableGenerator =
BulkInsertionCapableIdentifierGenerator.class.cast( generator );
if ( !capableGenerator.supportsBulkInsertionIdentifierGeneration() ) {
throw new QueryException(
"Identifier generator reported it does not support implicit id handling as part of bulk insertions"
);
}
final String fragment = capableGenerator.determineBulkInsertionIdentifierGenerationSelectFragment(
sessionFactoryHelper.getFactory().getDialect()
);
if ( fragment != null ) {
// we got a fragment from the generator, so alter the sql tree...
//
// first, wrap the fragment as a node
AST fragmentNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, fragment );
// next, rearrange the SQL tree to add the fragment node as the first select expression
AST originalFirstSelectExprNode = selectClause.getFirstChild();
selectClause.setFirstChild( fragmentNode );
fragmentNode.setNextSibling( originalFirstSelectExprNode );
// finally, prepend the id column name(s) to the insert-spec
insertStatement.getIntoClause().prependIdColumnSpec();
}
}
if ( sessionFactoryHelper.getFactory().getDialect().supportsParametersInInsertSelect() ) {
AST child = selectClause.getFirstChild();
int i = 0;
while ( child != null ) {
if ( child instanceof ParameterNode ) {
// infer the parameter type from the type listed in the INSERT INTO clause
( (ParameterNode) child ).setExpectedType(
insertStatement.getIntoClause()
.getInsertionTypes()[selectClause.getParameterPositions().get( i )]
);
i++;
}
child = child.getNextSibling();
}
}
final boolean includeVersionProperty = persister.isVersioned() &&
!insertStatement.getIntoClause().isExplicitVersionInsertion() &&
persister.isVersionPropertyInsertable();
if ( includeVersionProperty ) {
// We need to seed the version value as part of this bulk insert
VersionType versionType = persister.getVersionType();
AST versionValueNode = null;
if ( sessionFactoryHelper.getFactory().getDialect().supportsParametersInInsertSelect() ) {
int[] sqlTypes = versionType.sqlTypes( sessionFactoryHelper.getFactory() );
if ( sqlTypes == null || sqlTypes.length == 0 ) {
throw new IllegalStateException( versionType.getClass() + ".sqlTypes() returns null or empty array" );
}
if ( sqlTypes.length > 1 ) {
throw new IllegalStateException(
versionType.getClass() +
".sqlTypes() returns > 1 element; only single-valued versions are allowed."
);
}
versionValueNode = getASTFactory().create( HqlSqlTokenTypes.PARAM, "?" );
ParameterSpecification paramSpec = new VersionTypeSeedParameterSpecification( versionType );
( (ParameterNode) versionValueNode ).setHqlParameterSpecification( paramSpec );
parameters.add( 0, paramSpec );
if ( sessionFactoryHelper.getFactory().getDialect().requiresCastingOfParametersInSelectClause() ) {
// we need to wrtap the param in a cast()
MethodNode versionMethodNode = (MethodNode) getASTFactory().create(
HqlSqlTokenTypes.METHOD_CALL,
"("
);
AST methodIdentNode = getASTFactory().create( HqlSqlTokenTypes.IDENT, "cast" );
versionMethodNode.addChild( methodIdentNode );
versionMethodNode.initializeMethodNode( methodIdentNode, true );
AST castExprListNode = getASTFactory().create( HqlSqlTokenTypes.EXPR_LIST, "exprList" );
methodIdentNode.setNextSibling( castExprListNode );
castExprListNode.addChild( versionValueNode );
versionValueNode.setNextSibling(
getASTFactory().create(
HqlSqlTokenTypes.IDENT,
sessionFactoryHelper.getFactory().getDialect().getTypeName( sqlTypes[0] )
)
);
processFunction( versionMethodNode, true );
versionValueNode = versionMethodNode;
}
}
else {
if ( isIntegral( versionType ) ) {
try {
Object seedValue = versionType.seed( null );
versionValueNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, seedValue.toString() );
}
catch (Throwable t) {
throw new QueryException( "could not determine seed value for version on bulk insert [" + versionType + "]" );
}
}
else if ( isDatabaseGeneratedTimestamp( versionType ) ) {
String functionName = sessionFactoryHelper.getFactory()
.getDialect()
.getCurrentTimestampSQLFunctionName();
versionValueNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, functionName );
}
else {
throw new QueryException( "cannot handle version type [" + versionType + "] on bulk inserts with dialects not supporting parameters in insert-select statements" );
}
}
AST currentFirstSelectExprNode = selectClause.getFirstChild();
selectClause.setFirstChild( versionValueNode );
versionValueNode.setNextSibling( currentFirstSelectExprNode );
insertStatement.getIntoClause().prependVersionColumnSpec();
}