package org.hibernate.hql.internal.ast;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.hibernate.QueryException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.internal.JoinSequence;
import org.hibernate.engine.internal.ParameterBinder;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.hql.internal.CollectionProperties;
import org.hibernate.hql.internal.antlr.HqlSqlBaseWalker;
import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes;
import org.hibernate.hql.internal.antlr.HqlTokenTypes;
import org.hibernate.hql.internal.antlr.SqlTokenTypes;
import org.hibernate.hql.internal.ast.tree.AggregateNode;
import org.hibernate.hql.internal.ast.tree.AssignmentSpecification;
import org.hibernate.hql.internal.ast.tree.CastFunctionNode;
import org.hibernate.hql.internal.ast.tree.CollectionFunction;
import org.hibernate.hql.internal.ast.tree.ConstructorNode;
import org.hibernate.hql.internal.ast.tree.DeleteStatement;
import org.hibernate.hql.internal.ast.tree.DotNode;
import org.hibernate.hql.internal.ast.tree.EntityJoinFromElement;
import org.hibernate.hql.internal.ast.tree.FromClause;
import org.hibernate.hql.internal.ast.tree.FromElement;
import org.hibernate.hql.internal.ast.tree.FromElementFactory;
import org.hibernate.hql.internal.ast.tree.FromReferenceNode;
import org.hibernate.hql.internal.ast.tree.IdentNode;
import org.hibernate.hql.internal.ast.tree.IndexNode;
import org.hibernate.hql.internal.ast.tree.InsertStatement;
import org.hibernate.hql.internal.ast.tree.IntoClause;
import org.hibernate.hql.internal.ast.tree.MethodNode;
import org.hibernate.hql.internal.ast.tree.OperatorNode;
import org.hibernate.hql.internal.ast.tree.ParameterContainer;
import org.hibernate.hql.internal.ast.tree.ParameterNode;
import org.hibernate.hql.internal.ast.tree.QueryNode;
import org.hibernate.hql.internal.ast.tree.ResolvableNode;
import org.hibernate.hql.internal.ast.tree.RestrictableStatement;
import org.hibernate.hql.internal.ast.tree.ResultVariableRefNode;
import org.hibernate.hql.internal.ast.tree.SelectClause;
import org.hibernate.hql.internal.ast.tree.SelectExpression;
import org.hibernate.hql.internal.ast.tree.UpdateStatement;
import org.hibernate.hql.internal.ast.util.ASTPrinter;
import org.hibernate.hql.internal.ast.util.ASTUtil;
import org.hibernate.hql.internal.ast.util.AliasGenerator;
import org.hibernate.hql.internal.ast.util.JoinProcessor;
import org.hibernate.hql.internal.ast.util.LiteralProcessor;
import org.hibernate.hql.internal.ast.util.NodeTraverser;
import org.hibernate.hql.internal.ast.util.SessionFactoryHelper;
import org.hibernate.hql.internal.ast.util.SyntheticAndFactory;
import org.hibernate.hql.internal.ast.util.TokenPrinters;
import org.hibernate.hql.spi.QueryTranslator;
import org.hibernate.id.BulkInsertionCapableIdentifierGenerator;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.log.DeprecationLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.param.CollectionFilterKeyParameterSpecification;
import org.hibernate.param.NamedParameterSpecification;
import org.hibernate.param.ParameterSpecification;
import org.hibernate.param.PositionalParameterSpecification;
import org.hibernate.param.VersionTypeSeedParameterSpecification;
import org.hibernate.persister.collection.CollectionPropertyNames;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.sql.JoinType;
import org.hibernate.type.AssociationType;
import org.hibernate.type.CompositeType;
import org.hibernate.type.DbTimestampType;
import org.hibernate.type.Type;
import org.hibernate.type.VersionType;
import org.hibernate.usertype.UserVersionType;
import antlr.ASTFactory;
import antlr.RecognitionException;
import antlr.SemanticException;
import antlr.collections.AST;
import static org.hibernate.hql.spi.QueryTranslator.ERROR_LEGACY_ORDINAL_PARAMS_NO_LONGER_SUPPORTED;
public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, ParameterBinder.NamedParameterSource {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( HqlSqlWalker.class );
private final QueryTranslatorImpl queryTranslatorImpl;
private final HqlParser hqlParser;
private final SessionFactoryHelper sessionFactoryHelper;
private final Map tokenReplacements;
private final AliasGenerator aliasGenerator = new AliasGenerator();
private final LiteralProcessor literalProcessor;
private final ParseErrorHandler parseErrorHandler;
private final String collectionFilterRole;
private FromClause currentFromClause;
private SelectClause selectClause;
private Map<String, SelectExpression> selectExpressionsByResultVariable = new HashMap<>();
private Set<Serializable> querySpaces = new HashSet<>();
private int parameterCount;
private Map namedParameters;
private Map positionalParameters;
private ArrayList<ParameterSpecification> parameterSpecs = new ArrayList<>();
private int numberOfParametersInSetClause;
private ArrayList assignmentSpecifications = new ArrayList();
private JoinType impliedJoinType = JoinType.INNER_JOIN;
private boolean inEntityGraph;
public HqlSqlWalker(
QueryTranslatorImpl qti,
SessionFactoryImplementor sfi,
HqlParser parser,
Map tokenReplacements,
String collectionRole) {
setASTFactory( new SqlASTFactory( this ) );
this.parseErrorHandler = new ErrorTracker( qti.getQueryString() );
this.queryTranslatorImpl = qti;
this.sessionFactoryHelper = new SessionFactoryHelper( sfi );
this.literalProcessor = new LiteralProcessor( this );
this.tokenReplacements = tokenReplacements;
this.collectionFilterRole = collectionRole;
this.hqlParser = parser;
}
private int traceDepth;
@Override
public void traceIn(String ruleName, AST tree) {
if ( !LOG.isTraceEnabled() ) {
return;
}
if ( inputState.guessing > 0 ) {
return;
}
String prefix = StringHelper.repeat( '-', ( traceDepth++ * 2 ) ) + "-> ";
String traceText = ruleName + " (" + buildTraceNodeName( tree ) + ")";
LOG.trace( prefix + traceText );
}
private String buildTraceNodeName(AST tree) {
return tree == null
? "???"
: tree.getText() + " [" + TokenPrinters.SQL_TOKEN_PRINTER.getTokenTypeName( tree.getType() ) + "]";
}
@Override
public void traceOut(String ruleName, AST tree) {
if ( !LOG.isTraceEnabled() ) {
return;
}
if ( inputState.guessing > 0 ) {
return;
}
String prefix = "<-" + StringHelper.repeat( '-', ( --traceDepth * 2 ) ) + " ";
LOG.trace( prefix + ruleName );
}
@Override
protected void prepareFromClauseInputTree(AST fromClauseInput) {
if ( !isSubQuery() ) {
if ( isFilter() ) {
QueryableCollection persister = sessionFactoryHelper.getCollectionPersister( collectionFilterRole );
Type collectionElementType = persister.getElementType();
if ( !collectionElementType.isEntityType() ) {
throw new QueryException( "collection of values in filter: this" );
}
String collectionElementEntityName = persister.getElementPersister().getEntityName();
ASTFactory inputAstFactory = hqlParser.getASTFactory();
AST fromElement = inputAstFactory.create( HqlTokenTypes.FILTER_ENTITY, collectionElementEntityName );
ASTUtil.createSibling( inputAstFactory, HqlTokenTypes.ALIAS, "this", fromElement );
fromClauseInput.addChild( fromElement );
LOG.debug( "prepareFromClauseInputTree() : Filter - Added 'this' as a from element..." );
queryTranslatorImpl.showHqlAst( hqlParser.getAST() );
final Type collectionFilterKeyType = sessionFactoryHelper.requireQueryableCollection( collectionFilterRole )
.getKeyType();
final ParameterNode collectionFilterKeyParameter = (ParameterNode) astFactory.create( PARAM, "?" );
final CollectionFilterKeyParameterSpecification collectionFilterKeyParameterSpec = new CollectionFilterKeyParameterSpecification(
collectionFilterRole,
collectionFilterKeyType
);
parameterCount++;
collectionFilterKeyParameter.setHqlParameterSpecification( collectionFilterKeyParameterSpec );
parameterSpecs.add( collectionFilterKeyParameterSpec );
}
}
}
public boolean isFilter() {
return collectionFilterRole != null;
}
public String getCollectionFilterRole() {
return collectionFilterRole;
}
public boolean isInEntityGraph() {
return inEntityGraph;
}
public SessionFactoryHelper getSessionFactoryHelper() {
return sessionFactoryHelper;
}
public Map getTokenReplacements() {
return tokenReplacements;
}
public AliasGenerator getAliasGenerator() {
return aliasGenerator;
}
public FromClause getCurrentFromClause() {
return currentFromClause;
}
public ParseErrorHandler getParseErrorHandler() {
return parseErrorHandler;
}
@Override
public void reportError(RecognitionException e) {
parseErrorHandler.reportError( e );
}
@Override
public void reportError(String s) {
parseErrorHandler.reportError( s );
}
@Override
public void reportWarning(String s) {
parseErrorHandler.reportWarning( s );
}
public Set<Serializable> getQuerySpaces() {
return querySpaces;
}
@Override
protected AST createFromElement(String path, AST alias, AST propertyFetch) throws SemanticException {
FromElement fromElement = currentFromClause.addFromElement( path, alias );
fromElement.setAllPropertyFetch( propertyFetch != null );
return fromElement;
}
@Override
protected AST createFromFilterElement(AST filterEntity, AST alias) throws SemanticException {
FromElement fromElement = currentFromClause.addFromElement( filterEntity.getText(), alias );
FromClause fromClause = fromElement.getFromClause();
QueryableCollection persister = sessionFactoryHelper.getCollectionPersister( collectionFilterRole );
String[] keyColumnNames = persister.getKeyColumnNames();
String fkTableAlias = persister.isOneToMany()
? fromElement.getTableAlias()
: fromClause.getAliasGenerator().createName( collectionFilterRole );
JoinSequence join = sessionFactoryHelper.createJoinSequence();
join.setRoot( persister, fkTableAlias );
if ( !persister.isOneToMany() ) {
join.addJoin(
(AssociationType) persister.getElementType(),
fromElement.getTableAlias(),
JoinType.INNER_JOIN,
persister.getElementColumnNames( fkTableAlias )
);
}
join.addCondition( fkTableAlias, keyColumnNames, " = ?" );
fromElement.setJoinSequence( join );
fromElement.setFilter( true );
LOG.debug( "createFromFilterElement() : processed filter FROM element." );
return fromElement;
}
@Override
protected void createFromJoinElement(
AST path,
AST alias,
int joinType,
AST fetchNode,
AST propertyFetch,
AST with) throws SemanticException {
boolean fetch = fetchNode != null;
if ( fetch && isSubQuery() ) {
throw new QueryException( "fetch not allowed in subquery from-elements" );
}
final EntityPersister entityJoinReferencedPersister = resolveEntityJoinReferencedPersister( path );
if ( entityJoinReferencedPersister != null ) {
final EntityJoinFromElement join = createEntityJoin(
entityJoinReferencedPersister,
alias,
joinType,
propertyFetch,
with
);
( (FromReferenceNode) path ).setFromElement( join );
}
else {
if ( path.getType() != SqlTokenTypes.DOT ) {
throw new SemanticException( "Path expected for join!" );
}
DotNode dot = (DotNode) path;
JoinType hibernateJoinType = JoinProcessor.toHibernateJoinType( joinType );
dot.setJoinType( hibernateJoinType );
dot.setFetch( fetch );
dot.resolve( true, false, alias == null ? null : alias.getText() );
final FromElement fromElement;
if ( dot.getDataType() != null && dot.getDataType().isComponentType() ) {
if ( dot.getDataType().isAnyType() ) {
throw new SemanticException( "An AnyType attribute cannot be join fetched" );
}
FromElementFactory factory = new FromElementFactory(
getCurrentFromClause(),
dot.getLhs().getFromElement(),
dot.getPropertyPath(),
alias == null ? null : alias.getText(),
null,
false
);
fromElement = factory.createComponentJoin( (CompositeType) dot.getDataType() );
}
else {
fromElement = dot.getImpliedJoin();
fromElement.setAllPropertyFetch( propertyFetch != null );
if ( with != null ) {
if ( fetch ) {
throw new SemanticException( "with-clause not allowed on fetched associations; use filters" );
}
handleWithFragment( fromElement, with );
}
}
if ( LOG.isDebugEnabled() ) {
LOG.debug(
"createFromJoinElement() : "
+ getASTPrinter().showAsString( fromElement, "-- join tree --" )
);
}
}
}
private EntityPersister resolveEntityJoinReferencedPersister(AST path) {
if ( path.getType() == IDENT ) {
final IdentNode pathIdentNode = (IdentNode) path;
String name = path.getText();
if ( name == null ) {
name = pathIdentNode.getOriginalText();
}
return sessionFactoryHelper.findEntityPersisterByName( name );
}
else if ( path.getType() == DOT ) {
final String pathText = ASTUtil.getPathText( path );
return sessionFactoryHelper.findEntityPersisterByName( pathText );
}
return null;
}
@Override
protected void finishFromClause(AST fromClause) throws SemanticException {
( (FromClause) fromClause ).finishInit();
}
private EntityJoinFromElement createEntityJoin(
EntityPersister entityPersister,
AST aliasNode,
int joinType,
AST propertyFetch,
AST with) throws SemanticException {
final String alias = aliasNode == null ? null : aliasNode.getText();
LOG.debugf( "Creating entity-join FromElement [%s -> %s]", alias, entityPersister.getEntityName() );
EntityJoinFromElement join = new EntityJoinFromElement(
this,
getCurrentFromClause(),
entityPersister,
JoinProcessor.toHibernateJoinType( joinType ),
propertyFetch != null,
alias
);
if ( with != null ) {
handleWithFragment( join, with );
}
return join;
}
private void handleWithFragment(FromElement fromElement, AST hqlWithNode) throws SemanticException {
try {
withClause( hqlWithNode );
AST hqlSqlWithNode = returnAST;
if ( LOG.isDebugEnabled() ) {
LOG.debug(
"handleWithFragment() : " + getASTPrinter().showAsString(
hqlSqlWithNode,
"-- with clause --"
)
);
}
WithClauseVisitor visitor = new WithClauseVisitor( fromElement, queryTranslatorImpl );
NodeTraverser traverser = new NodeTraverser( visitor );
traverser.traverseDepthFirst( hqlSqlWithNode );
SqlGenerator sql = new SqlGenerator( getSessionFactoryHelper().getFactory() );
sql.whereExpr( hqlSqlWithNode.getFirstChild() );
fromElement.setWithClauseFragment( "(" + sql.getSQL() + ")" );
}
catch (SemanticException e) {
throw e;
}
catch (InvalidWithClauseException e) {
throw e;
}
catch (Exception e) {
throw new SemanticException( e.getMessage() );
}
}
private static class WithClauseVisitor implements NodeTraverser.VisitationStrategy {
private final FromElement joinFragment;
private final QueryTranslatorImpl queryTranslatorImpl;
private FromElement referencedFromElement;
private String joinAlias;
public WithClauseVisitor(FromElement fromElement, QueryTranslatorImpl queryTranslatorImpl) {
this.joinFragment = fromElement;
this.queryTranslatorImpl = queryTranslatorImpl;
}
public void visit(AST node) {
if ( node instanceof DotNode ) {
DotNode dotNode = (DotNode) node;
FromElement fromElement = dotNode.getFromElement();
if ( referencedFromElement != null ) {
}
else {
referencedFromElement = fromElement;
joinAlias = extractAppliedAlias( dotNode );
}
}
else if ( node instanceof ParameterNode ) {
applyParameterSpecification( ( (ParameterNode) node ).getHqlParameterSpecification() );
}
else if ( node instanceof ParameterContainer ) {
applyParameterSpecifications( (ParameterContainer) node );
}
}
private void applyParameterSpecifications(ParameterContainer parameterContainer) {
if ( parameterContainer.hasEmbeddedParameters() ) {
ParameterSpecification[] specs = parameterContainer.getEmbeddedParameters();
for ( ParameterSpecification spec : specs ) {
applyParameterSpecification( spec );
}
}
}
private void applyParameterSpecification(ParameterSpecification paramSpec) {
joinFragment.addEmbeddedParameter( paramSpec );
}
private String (DotNode dotNode) {
return dotNode.getText().substring( 0, dotNode.getText().indexOf( '.' ) );
}
public FromElement getReferencedFromElement() {
return referencedFromElement;
}
public String getJoinAlias() {
return joinAlias;
}
}
@Override
protected void pushFromClause(AST fromNode, AST inputFromNode) {
FromClause newFromClause = (FromClause) fromNode;
newFromClause.setParentFromClause( currentFromClause );
currentFromClause = newFromClause;
}
private void popFromClause() {
currentFromClause = currentFromClause.getParentFromClause();
}
@Override
protected void lookupAlias(AST aliasRef)
throws SemanticException {
FromElement alias = currentFromClause.getFromElement( aliasRef.getText() );
FromReferenceNode aliasRefNode = (FromReferenceNode) aliasRef;
aliasRefNode.setFromElement( alias );
}
@Override
protected void setImpliedJoinType(int joinType) {
impliedJoinType = JoinProcessor.toHibernateJoinType( joinType );
}
public JoinType getImpliedJoinType() {
return impliedJoinType;
}
@Override
protected AST lookupProperty(AST dot, boolean root, boolean inSelect) throws SemanticException {
DotNode dotNode = (DotNode) dot;
FromReferenceNode lhs = dotNode.getLhs();
AST rhs = lhs.getNextSibling();
if ( lhs.getDataType() != null
&& lhs.getDataType().isCollectionType() ) {
if ( CollectionProperties.isCollectionProperty( rhs.getText() ) ) {
DeprecationLogger.DEPRECATION_LOGGER.logDeprecationOfCollectionPropertiesInHql(
rhs.getText(),
lhs.getPath()
);
}
if ( CollectionPropertyNames.COLLECTION_INDICES.equalsIgnoreCase( rhs.getText() )
|| CollectionPropertyNames.COLLECTION_ELEMENTS.equalsIgnoreCase( rhs.getText() ) ) {
if ( LOG.isDebugEnabled() ) {
LOG.debugf(
"lookupProperty() %s => %s(%s)",
dotNode.getPath(),
rhs.getText(),
lhs.getPath()
);
}
final CollectionFunction f;
if ( rhs instanceof CollectionFunction ) {
f = (CollectionFunction) rhs;
}
else {
f = new CollectionFunction();
f.initialize( SqlTokenTypes.METHOD_CALL, rhs.getText() );
f.initialize( this );
}
f.setFirstChild( lhs );
lhs.setNextSibling( null );
dotNode.setFirstChild( f );
resolve( lhs );
f.resolve( inSelect );
return f;
}
}
dotNode.resolveFirstChild();
return dotNode;
}
@Override
protected boolean isNonQualifiedPropertyRef(AST ident) {
final String identText = ident.getText();
if ( currentFromClause.isFromElementAlias( identText ) ) {
return false;
}
List fromElements = currentFromClause.getExplicitFromElements();
if ( fromElements.size() == 1 ) {
final FromElement fromElement = (FromElement) fromElements.get( 0 );
try {
LOG.tracev( "Attempting to resolve property [{0}] as a non-qualified ref", identText );
return fromElement.getPropertyMapping( identText ).toType( identText ) != null;
}
catch (QueryException e) {
}
}
return false;
}
@Override
protected AST lookupNonQualifiedProperty(AST property) throws SemanticException {
final FromElement fromElement = (FromElement) currentFromClause.getExplicitFromElements().get( 0 );
AST syntheticDotNode = generateSyntheticDotNodeForNonQualifiedPropertyRef( property, fromElement );
return lookupProperty( syntheticDotNode, false, getCurrentClauseType() == HqlSqlTokenTypes.SELECT );
}
private AST generateSyntheticDotNodeForNonQualifiedPropertyRef(AST property, FromElement fromElement) {
AST dot = getASTFactory().create( DOT, "{non-qualified-property-ref}" );
( (DotNode) dot ).setPropertyPath( ( (FromReferenceNode) property ).getPath() );
IdentNode syntheticAlias = (IdentNode) getASTFactory().create( IDENT, "{synthetic-alias}" );
syntheticAlias.setFromElement( fromElement );
syntheticAlias.setResolved();
dot.setFirstChild( syntheticAlias );
dot.addChild( property );
return dot;
}
@Override
protected void processQuery(AST select, AST query) throws SemanticException {
if ( LOG.isDebugEnabled() ) {
LOG.debugf( "processQuery() : %s", query.toStringTree() );
}
try {
QueryNode qn = (QueryNode) query;
boolean explicitSelect = select != null && select.getNumberOfChildren() > 0;
if ( queryTranslatorImpl.getEntityGraphQueryHint() != null ) {
final boolean oldInEntityGraph = inEntityGraph;
try {
inEntityGraph = true;
qn.getFromClause().getFromElements().addAll(
queryTranslatorImpl.getEntityGraphQueryHint().toFromElements( qn.getFromClause(), this )
);
}
finally {
inEntityGraph = oldInEntityGraph;
}
}
if ( !explicitSelect ) {
createSelectClauseFromFromClause( qn );
}
else {
useSelectClause( select );
}
JoinProcessor joinProcessor = new JoinProcessor( this );
joinProcessor.processJoins( qn );
Iterator itr = qn.getFromClause().getProjectionList().iterator();
while ( itr.hasNext() ) {
final FromElement fromElement = (FromElement) itr.next();
if ( fromElement.isFetch() && fromElement.getQueryableCollection() != null ) {
if ( fromElement.getQueryableCollection().hasOrdering() ) {
String orderByFragment = fromElement
.getQueryableCollection()
.getSQLOrderByString( fromElement.getCollectionTableAlias() );
qn.getOrderByClause().addOrderFragment( orderByFragment );
}
if ( fromElement.getQueryableCollection().hasManyToManyOrdering() ) {
String orderByFragment = fromElement.getQueryableCollection()
.getManyToManyOrderByString( fromElement.getTableAlias() );
qn.getOrderByClause().addOrderFragment( orderByFragment );
}
}
}
}
finally {
popFromClause();
}
}
protected void postProcessDML(RestrictableStatement statement) throws SemanticException {
statement.getFromClause().resolve();
FromElement fromElement = (FromElement) statement.getFromClause().getFromElements().get( 0 );
Queryable persister = fromElement.getQueryable();
fromElement.setText( persister.getTableName() );
if ( persister.getDiscriminatorType() != null || !queryTranslatorImpl.getEnabledFilters().isEmpty() ) {
new SyntheticAndFactory( this ).addDiscriminatorWhereFragment(
statement,
persister,
queryTranslatorImpl.getEnabledFilters(),
fromElement.getTableAlias()
);
}
}
@Override
protected void postProcessUpdate(AST update) throws SemanticException {
UpdateStatement updateStatement = (UpdateStatement) update;
postProcessDML( updateStatement );
}
@Override
protected void postProcessDelete(AST delete) throws SemanticException {
postProcessDML( (DeleteStatement) delete );
}
@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() ) {
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 ) {
AST fragmentNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, fragment );
AST originalFirstSelectExprNode = selectClause.getFirstChild();
selectClause.setFirstChild( fragmentNode );
fragmentNode.setNextSibling( originalFirstSelectExprNode );
insertStatement.getIntoClause().prependIdColumnSpec();
}
}
if ( sessionFactoryHelper.getFactory().getDialect().supportsParametersInInsertSelect() ) {
AST child = selectClause.getFirstChild();
int i = 0;
while ( child != null ) {
if ( child instanceof ParameterNode ) {
( (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 ) {
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 );
parameterSpecs.add( 0, paramSpec );
if ( sessionFactoryHelper.getFactory().getDialect().requiresCastingOfParametersInSelectClause() ) {
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 parameterSpecs in insert-select statements" );
}
}
AST currentFirstSelectExprNode = selectClause.getFirstChild();
selectClause.setFirstChild( versionValueNode );
versionValueNode.setNextSibling( currentFirstSelectExprNode );
insertStatement.getIntoClause().prependVersionColumnSpec();
}
if ( insertStatement.getIntoClause().isDiscriminated() ) {
String sqlValue = insertStatement.getIntoClause().getQueryable().getDiscriminatorSQLValue();
AST discrimValue = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, sqlValue );
insertStatement.getSelectClause().addChild( discrimValue );
}
}
private boolean isDatabaseGeneratedTimestamp(Type type) {
return DbTimestampType.class.isAssignableFrom( type.getClass() );
}
private boolean isIntegral(Type type) {
return Long.class.isAssignableFrom( type.getReturnedClass() )
|| Integer.class.isAssignableFrom( type.getReturnedClass() )
|| long.class.isAssignableFrom( type.getReturnedClass() )
|| int.class.isAssignableFrom( type.getReturnedClass() );
}
private void useSelectClause(AST select) throws SemanticException {
selectClause = (SelectClause) select;
selectClause.initializeExplicitSelectClause( currentFromClause );
}
private void createSelectClauseFromFromClause(QueryNode qn) throws SemanticException {
AST select = astFactory.create( SELECT_CLAUSE, "{derived select clause}" );
AST sibling = qn.getFromClause();
qn.setFirstChild( select );
select.setNextSibling( sibling );
selectClause = (SelectClause) select;
selectClause.initializeDerivedSelectClause( currentFromClause );
LOG.debug( "Derived SELECT clause created." );
}
@Override
protected void resolve(AST node) throws SemanticException {
resolve(node, null);
}
@Override
protected void resolve(AST node, AST predicateNode) throws SemanticException {
if ( node != null ) {
ResolvableNode r = (ResolvableNode) node;
if ( isInFunctionCall() ) {
r.resolveInFunctionCall( false, true );
}
else {
r.resolve( false, true, null, null, predicateNode );
}
}
}
@Override
protected void resolveSelectExpression(AST node) throws SemanticException {
int type = node.getType();
switch ( type ) {
case DOT: {
DotNode dot = (DotNode) node;
dot.resolveSelectExpression();
break;
}
case ALIAS_REF: {
FromReferenceNode aliasRefNode = (FromReferenceNode) node;
aliasRefNode.resolve( false, false );
FromElement fromElement = aliasRefNode.getFromElement();
if ( fromElement != null ) {
fromElement.setIncludeSubclasses( true );
}
break;
}
default: {
break;
}
}
}
@Override
protected void beforeSelectClause() throws SemanticException {
FromClause from = getCurrentFromClause();
List fromElements = from.getFromElements();
for ( Iterator iterator = fromElements.iterator(); iterator.hasNext(); ) {
FromElement fromElement = (FromElement) iterator.next();
fromElement.setIncludeSubclasses( false );
}
}
@Override
protected AST generatePositionalParameter(AST delimiterNode, AST numberNode) throws SemanticException {
if ( getSessionFactoryHelper().isStrictJPAQLComplianceEnabled() && namedParameters != null ) {
throw new SemanticException(
"Cannot mix positional and named parameters: " + queryTranslatorImpl.getQueryString()
);
}
if ( numberNode == null ) {
throw new QueryException(
String.format(
Locale.ROOT,
ERROR_LEGACY_ORDINAL_PARAMS_NO_LONGER_SUPPORTED,
queryTranslatorImpl.getQueryString()
)
);
}
final String positionString = numberNode.getText();
final int label = Integer.parseInt( positionString );
trackPositionalParameterPositions( label );
final ParameterNode parameter = (ParameterNode) astFactory.create( PARAM, positionString );
parameter.setText( "?" );
final int queryParamtersPosition = isFilter()
? label
: label - 1;
final PositionalParameterSpecification paramSpec = new PositionalParameterSpecification(
delimiterNode.getLine(),
delimiterNode.getColumn(),
label,
queryParamtersPosition
);
parameter.setHqlParameterSpecification( paramSpec );
parameterSpecs.add( paramSpec );
return parameter;
}
@SuppressWarnings("unchecked")
private void trackPositionalParameterPositions(int label) {
if ( positionalParameters == null ) {
positionalParameters = new HashMap();
}
final Integer loc = parameterCount++;
final Object existingValue = positionalParameters.get( label );
if ( existingValue == null ) {
positionalParameters.put( label, loc );
}
else if ( existingValue instanceof Integer ) {
final ArrayList list = new ArrayList();
positionalParameters.put( label, list );
list.add( existingValue );
list.add( loc );
}
else {
( (List) existingValue ).add( loc );
}
}
@Override
protected AST generateNamedParameter(AST delimiterNode, AST nameNode) throws SemanticException {
if ( getSessionFactoryHelper().isStrictJPAQLComplianceEnabled() && positionalParameters != null ) {
throw new SemanticException(
"Cannot mix positional and named parameters: " + queryTranslatorImpl.getQueryString()
);
}
final String name = nameNode.getText();
trackNamedParameterPositions( name );
final ParameterNode parameter = (ParameterNode) astFactory.create( NAMED_PARAM, name );
parameter.setText( "?" );
final NamedParameterSpecification paramSpec = new NamedParameterSpecification(
delimiterNode.getLine(),
delimiterNode.getColumn(),
name
);
parameter.setHqlParameterSpecification( paramSpec );
parameterSpecs.add( paramSpec );
return parameter;
}
@SuppressWarnings("unchecked")
private void trackNamedParameterPositions(String name) {
if ( namedParameters == null ) {
namedParameters = new HashMap();
}
final Integer loc = parameterCount++;
final Object existingValue = namedParameters.get( name );
if ( existingValue == null ) {
namedParameters.put( name, loc );
}
else if ( existingValue instanceof Integer ) {
ArrayList<Integer> list = new ArrayList<>( 4 );
list.add( (Integer) existingValue );
list.add( loc );
namedParameters.put( name, list );
}
else {
( (List) existingValue ).add( loc );
}
}
@Override
protected void processConstant(AST constant) throws SemanticException {
literalProcessor.processConstant(
constant,
true
);
}
@Override
protected void processBoolean(AST constant) throws SemanticException {
literalProcessor.processBoolean( constant );
}
@Override
protected void processNumericLiteral(AST literal) {
literalProcessor.processNumeric( literal );
}
@Override
protected void processIndex(AST indexOp) throws SemanticException {
IndexNode indexNode = (IndexNode) indexOp;
indexNode.resolve( true, true );
}
@Override
protected void processFunction(AST functionCall, boolean inSelect) throws SemanticException {
MethodNode methodNode = (MethodNode) functionCall;
methodNode.resolve( inSelect );
}
@Override
protected void processCastFunction(AST castFunctionCall, boolean inSelect) throws SemanticException {
CastFunctionNode castFunctionNode = (CastFunctionNode) castFunctionCall;
castFunctionNode.resolve( inSelect );
}
@Override
protected void processAggregation(AST node, boolean inSelect) throws SemanticException {
AggregateNode aggregateNode = (AggregateNode) node;
aggregateNode.resolve();
}
@Override
protected void processConstructor(AST constructor) throws SemanticException {
ConstructorNode constructorNode = (ConstructorNode) constructor;
constructorNode.prepare();
}
@Override
protected void setAlias(AST selectExpr, AST ident) {
( (SelectExpression) selectExpr ).setAlias( ident.getText() );
if ( !isSubQuery() ) {
selectExpressionsByResultVariable.put( ident.getText(), (SelectExpression) selectExpr );
}
}
@Override
protected boolean isOrderExpressionResultVariableRef(AST orderExpressionNode) throws SemanticException {
if ( !isSubQuery() &&
orderExpressionNode.getType() == IDENT &&
selectExpressionsByResultVariable.containsKey( orderExpressionNode.getText() ) ) {
return true;
}
return false;
}
@Override
protected void handleResultVariableRef(AST resultVariableRef) throws SemanticException {
if ( isSubQuery() ) {
throw new SemanticException(
"References to result variables in subqueries are not supported."
);
}
( (ResultVariableRefNode) resultVariableRef ).setSelectExpression(
selectExpressionsByResultVariable.get( resultVariableRef.getText() )
);
}
public int[] getNamedParameterLocations(String name) throws QueryException {
Object o = namedParameters.get( name );
if ( o == null ) {
throw new QueryException(
QueryTranslator.ERROR_NAMED_PARAMETER_DOES_NOT_APPEAR + name,
queryTranslatorImpl.getQueryString()
);
}
if ( o instanceof Integer ) {
return new int[] {(Integer) o};
}
else {
return ArrayHelper.toIntArray( (ArrayList) o );
}
}
public void addQuerySpaces(Serializable[] spaces) {
querySpaces.addAll( Arrays.asList( spaces ) );
}
public Type[] getReturnTypes() {
return selectClause.getQueryReturnTypes();
}
public String[] getReturnAliases() {
return selectClause.getQueryReturnAliases();
}
public SelectClause getSelectClause() {
return selectClause;
}
public FromClause getFinalFromClause() {
FromClause top = currentFromClause;
while ( top.getParentFromClause() != null ) {
top = top.getParentFromClause();
}
return top;
}
public boolean isShallowQuery() {
return getStatementType() == INSERT || queryTranslatorImpl.isShallowQuery();
}
public Map getEnabledFilters() {
return queryTranslatorImpl.getEnabledFilters();
}
public LiteralProcessor getLiteralProcessor() {
return literalProcessor;
}
public ASTPrinter getASTPrinter() {
return TokenPrinters.SQL_TOKEN_PRINTER;
}
public ArrayList<ParameterSpecification> getParameterSpecs() {
return parameterSpecs;
}
public int getNumberOfParametersInSetClause() {
return numberOfParametersInSetClause;
}
@Override
protected void evaluateAssignment(AST eq) throws SemanticException {
prepareLogicOperator( eq );
Queryable persister = getCurrentFromClause().getFromElement().getQueryable();
evaluateAssignment( eq, persister, -1 );
}
private void evaluateAssignment(AST eq, Queryable persister, int targetIndex) {
if ( persister.isMultiTable() ) {
AssignmentSpecification specification = new AssignmentSpecification( eq, persister );
if ( targetIndex >= 0 ) {
assignmentSpecifications.add( targetIndex, specification );
}
else {
assignmentSpecifications.add( specification );
}
numberOfParametersInSetClause += specification.getParameters().length;
}
}
public ArrayList getAssignmentSpecifications() {
return assignmentSpecifications;
}
@Override
protected AST createIntoClause(String path, AST propertySpec) throws SemanticException {
Queryable persister = (Queryable) getSessionFactoryHelper().requireClassPersister( path );
IntoClause intoClause = (IntoClause) getASTFactory().create( INTO, persister.getEntityName() );
intoClause.setFirstChild( propertySpec );
intoClause.initialize( persister );
addQuerySpaces( persister.getQuerySpaces() );
return intoClause;
}
@Override
protected void prepareVersioned(AST updateNode, AST versioned) throws SemanticException {
UpdateStatement updateStatement = (UpdateStatement) updateNode;
FromClause fromClause = updateStatement.getFromClause();
if ( versioned != null ) {
Queryable persister = fromClause.getFromElement().getQueryable();
if ( !persister.isVersioned() ) {
throw new SemanticException( "increment option specified for update of non-versioned entity" );
}
VersionType versionType = persister.getVersionType();
if ( versionType instanceof UserVersionType ) {
throw new SemanticException( "user-defined version types not supported for increment option" );
}
AST eq = getASTFactory().create( HqlSqlTokenTypes.EQ, "=" );
AST versionPropertyNode = generateVersionPropertyNode( persister );
eq.setFirstChild( versionPropertyNode );
AST versionIncrementNode = null;
if ( isTimestampBasedVersion( versionType ) ) {
versionIncrementNode = getASTFactory().create( HqlSqlTokenTypes.PARAM, "?" );
ParameterSpecification paramSpec = new VersionTypeSeedParameterSpecification( versionType );
( (ParameterNode) versionIncrementNode ).setHqlParameterSpecification( paramSpec );
parameterSpecs.add( 0, paramSpec );
}
else {
versionIncrementNode = getASTFactory().create( HqlSqlTokenTypes.PLUS, "+" );
versionIncrementNode.setFirstChild( generateVersionPropertyNode( persister ) );
versionIncrementNode.addChild( getASTFactory().create( HqlSqlTokenTypes.IDENT, "1" ) );
}
eq.addChild( versionIncrementNode );
evaluateAssignment( eq, persister, 0 );
AST setClause = updateStatement.getSetClause();
AST currentFirstSetElement = setClause.getFirstChild();
setClause.setFirstChild( eq );
eq.setNextSibling( currentFirstSetElement );
}
}
private boolean isTimestampBasedVersion(VersionType versionType) {
final Class javaType = versionType.getReturnedClass();
return Date.class.isAssignableFrom( javaType )
|| Calendar.class.isAssignableFrom( javaType );
}
private AST generateVersionPropertyNode(Queryable persister) throws SemanticException {
String versionPropertyName = persister.getPropertyNames()[persister.getVersionProperty()];
AST versionPropertyRef = getASTFactory().create( HqlSqlTokenTypes.IDENT, versionPropertyName );
AST versionPropertyNode = lookupNonQualifiedProperty( versionPropertyRef );
resolve( versionPropertyNode );
return versionPropertyNode;
}
@Override
protected void prepareLogicOperator(AST operator) throws SemanticException {
( (OperatorNode) operator ).initialize();
}
@Override
protected void prepareArithmeticOperator(AST operator) throws SemanticException {
( (OperatorNode) operator ).initialize();
}
@Override
protected void validateMapPropertyExpression(AST node) throws SemanticException {
try {
FromReferenceNode fromReferenceNode = (FromReferenceNode) node;
QueryableCollection collectionPersister = fromReferenceNode.getFromElement().getQueryableCollection();
if ( !Map.class.isAssignableFrom( collectionPersister.getCollectionType().getReturnedClass() ) ) {
throw new SemanticException( "node did not reference a map" );
}
}
catch (SemanticException se) {
throw se;
}
catch (Throwable t) {
throw new SemanticException( "node did not reference a map" );
}
}
public Set<String> getTreatAsDeclarationsByPath(String path) {
return hqlParser.getTreatMap().get( path );
}
public Dialect getDialect() {
return sessionFactoryHelper.getFactory().getServiceRegistry().getService( JdbcServices.class ).getDialect();
}
public static void panic() {
throw new QueryException( "TreeWalker: panic" );
}
}