All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.hql.ast.HqlSqlWalker Maven / Gradle / Ivy

There is a newer version: 3.6.0.Beta2
Show newest version
// $Id: HqlSqlWalker.java 10946 2006-12-07 14:50:59Z [email protected] $
package org.hibernate.hql.ast;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.QueryException;
import org.hibernate.HibernateException;
import org.hibernate.engine.JoinSequence;
import org.hibernate.engine.ParameterBinder;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.hql.QueryTranslator;
import org.hibernate.hql.antlr.HqlSqlBaseWalker;
import org.hibernate.hql.antlr.HqlSqlTokenTypes;
import org.hibernate.hql.antlr.HqlTokenTypes;
import org.hibernate.hql.antlr.SqlTokenTypes;
import org.hibernate.hql.ast.tree.AssignmentSpecification;
import org.hibernate.hql.ast.tree.CollectionFunction;
import org.hibernate.hql.ast.tree.ConstructorNode;
import org.hibernate.hql.ast.tree.DeleteStatement;
import org.hibernate.hql.ast.tree.DotNode;
import org.hibernate.hql.ast.tree.FromClause;
import org.hibernate.hql.ast.tree.FromElement;
import org.hibernate.hql.ast.tree.FromReferenceNode;
import org.hibernate.hql.ast.tree.IdentNode;
import org.hibernate.hql.ast.tree.IndexNode;
import org.hibernate.hql.ast.tree.InsertStatement;
import org.hibernate.hql.ast.tree.IntoClause;
import org.hibernate.hql.ast.tree.MethodNode;
import org.hibernate.hql.ast.tree.ParameterNode;
import org.hibernate.hql.ast.tree.QueryNode;
import org.hibernate.hql.ast.tree.ResolvableNode;
import org.hibernate.hql.ast.tree.RestrictableStatement;
import org.hibernate.hql.ast.tree.SelectClause;
import org.hibernate.hql.ast.tree.SelectExpression;
import org.hibernate.hql.ast.tree.UpdateStatement;
import org.hibernate.hql.ast.tree.Node;
import org.hibernate.hql.ast.tree.OperatorNode;
import org.hibernate.hql.ast.util.ASTPrinter;
import org.hibernate.hql.ast.util.ASTUtil;
import org.hibernate.hql.ast.util.AliasGenerator;
import org.hibernate.hql.ast.util.JoinProcessor;
import org.hibernate.hql.ast.util.LiteralProcessor;
import org.hibernate.hql.ast.util.SessionFactoryHelper;
import org.hibernate.hql.ast.util.SyntheticAndFactory;
import org.hibernate.hql.ast.util.NodeTraverser;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.PostInsertIdentifierGenerator;
import org.hibernate.id.SequenceGenerator;
import org.hibernate.param.NamedParameterSpecification;
import org.hibernate.param.ParameterSpecification;
import org.hibernate.param.PositionalParameterSpecification;
import org.hibernate.param.VersionTypeSeedParameterSpecification;
import org.hibernate.param.CollectionFilterKeyParameterSpecification;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.sql.JoinFragment;
import org.hibernate.type.AssociationType;
import org.hibernate.type.Type;
import org.hibernate.type.VersionType;
import org.hibernate.type.DbTimestampType;
import org.hibernate.usertype.UserVersionType;
import org.hibernate.util.ArrayHelper;

import antlr.ASTFactory;
import antlr.RecognitionException;
import antlr.SemanticException;
import antlr.collections.AST;

/**
 * Implements methods used by the HQL->SQL tree transform grammar (a.k.a. the second phase).
 * 
    *
  • Isolates the Hibernate API-specific code from the ANTLR generated code.
  • *
  • Handles the SQL framgents generated by the persisters in order to create the SELECT and FROM clauses, * taking into account the joins and projections that are implied by the mappings (persister/queryable).
  • *
  • Uses SqlASTFactory to create customized AST nodes.
  • *
* * @see SqlASTFactory */ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, ParameterBinder.NamedParameterSource { private static final Log log = LogFactory.getLog( 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 ASTPrinter printer; private final String collectionFilterRole; private FromClause currentFromClause = null; private SelectClause selectClause; private Set querySpaces = new HashSet(); private int parameterCount; private Map namedParameters = new HashMap(); private ArrayList parameters = new ArrayList(); private int numberOfParametersInSetClause; private int positionalParameterCount; private ArrayList assignmentSpecifications = new ArrayList(); private int impliedJoinType; /** * Create a new tree transformer. * * @param qti Back pointer to the query translator implementation that is using this tree transform. * @param sfi The session factory implementor where the Hibernate mappings can be found. * @param parser A reference to the phase-1 parser * @param tokenReplacements Registers the token replacement map with the walker. This map will * be used to substitute function names and constants. * @param collectionRole The collection role name of the collection used as the basis for the * filter, NULL if this is not a collection filter compilation. */ public HqlSqlWalker( QueryTranslatorImpl qti, SessionFactoryImplementor sfi, HqlParser parser, Map tokenReplacements, String collectionRole) { setASTFactory( new SqlASTFactory( this ) ); this.parseErrorHandler = new ErrorCounter(); this.queryTranslatorImpl = qti; this.sessionFactoryHelper = new SessionFactoryHelper( sfi ); this.literalProcessor = new LiteralProcessor( this ); this.tokenReplacements = tokenReplacements; this.hqlParser = parser; this.printer = new ASTPrinter( SqlTokenTypes.class ); this.collectionFilterRole = collectionRole; } protected void prepareFromClauseInputTree(AST fromClauseInput) { if ( !isSubQuery() ) { // // inject param specifications to account for dynamic filter param values // if ( ! getEnabledFilters().isEmpty() ) { // Iterator filterItr = getEnabledFilters().values().iterator(); // while ( filterItr.hasNext() ) { // FilterImpl filter = ( FilterImpl ) filterItr.next(); // if ( ! filter.getFilterDefinition().getParameterNames().isEmpty() ) { // Iterator paramItr = filter.getFilterDefinition().getParameterNames().iterator(); // while ( paramItr.hasNext() ) { // String parameterName = ( String ) paramItr.next(); // // currently param filters *only* work with single-column parameter types; // // if that limitation is ever lifted, this logic will need to change to account for that // ParameterNode collectionFilterKeyParameter = ( ParameterNode ) astFactory.create( PARAM, "?" ); // DynamicFilterParameterSpecification paramSpec = new DynamicFilterParameterSpecification( // filter.getName(), // parameterName, // filter.getFilterDefinition().getParameterType( parameterName ), // positionalParameterCount++ // ); // collectionFilterKeyParameter.setHqlParameterSpecification( paramSpec ); // parameters.add( paramSpec ); // } // } // } // } if ( isFilter() ) { // Handle collection-fiter compilation. // IMPORTANT NOTE: This is modifying the INPUT (HQL) tree, not the output tree! 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 = ASTUtil.create( inputAstFactory, HqlTokenTypes.FILTER_ENTITY, collectionElementEntityName ); ASTUtil.createSibling( inputAstFactory, HqlTokenTypes.ALIAS, "this", fromElement ); fromClauseInput.addChild( fromElement ); // Show the modified AST. if ( log.isDebugEnabled() ) { log.debug( "prepareFromClauseInputTree() : Filter - Added 'this' as a from element..." ); } queryTranslatorImpl.showHqlAst( hqlParser.getAST() ); // Create a parameter specification for the collection filter... Type collectionFilterKeyType = sessionFactoryHelper.requireQueryableCollection( collectionFilterRole ).getKeyType(); ParameterNode collectionFilterKeyParameter = ( ParameterNode ) astFactory.create( PARAM, "?" ); CollectionFilterKeyParameterSpecification collectionFilterKeyParameterSpec = new CollectionFilterKeyParameterSpecification( collectionFilterRole, collectionFilterKeyType, positionalParameterCount++ ); collectionFilterKeyParameter.setHqlParameterSpecification( collectionFilterKeyParameterSpec ); parameters.add( collectionFilterKeyParameterSpec ); } } } public boolean isFilter() { return collectionFilterRole != null; } 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; } public void reportError(RecognitionException e) { parseErrorHandler.reportError( e ); // Use the delegate. } public void reportError(String s) { parseErrorHandler.reportError( s ); // Use the delegate. } public void reportWarning(String s) { parseErrorHandler.reportWarning( s ); } /** * Returns the set of unique query spaces (a.k.a. * table names) that occurred in the query. * * @return A set of table names (Strings). */ public Set getQuerySpaces() { return querySpaces; } protected AST createFromElement(String path, AST alias, AST propertyFetch) throws SemanticException { FromElement fromElement = currentFromClause.addFromElement( path, alias ); fromElement.setAllPropertyFetch(propertyFetch!=null); return fromElement; } 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 ); // Get the names of the columns used to link between the collection // owner and the collection elements. 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(), JoinFragment.INNER_JOIN, persister.getElementColumnNames( fkTableAlias ) ); } join.addCondition( fkTableAlias, keyColumnNames, " = ?" ); fromElement.setJoinSequence( join ); fromElement.setFilter( true ); if ( log.isDebugEnabled() ) { log.debug( "createFromFilterElement() : processed filter FROM element." ); } return fromElement; } 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" ); } // The path AST should be a DotNode, and it should have been evaluated already. if ( path.getType() != SqlTokenTypes.DOT ) { throw new SemanticException( "Path expected for join!" ); } DotNode dot = ( DotNode ) path; int hibernateJoinType = JoinProcessor.toHibernateJoinType( joinType ); dot.setJoinType( hibernateJoinType ); // Tell the dot node about the join type. dot.setFetch( fetch ); // Generate an explicit join for the root dot node. The implied joins will be collected and passed up // to the root dot node. dot.resolve( true, false, alias == null ? null : alias.getText() ); FromElement 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 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(); NodeTraverser traverser = new NodeTraverser( visitor ); traverser.traverseDepthFirst( hqlSqlWithNode ); FromElement referencedFromElement = visitor.getReferencedFromElement(); if ( referencedFromElement != fromElement ) { throw new InvalidWithClauseException( "with-clause expressions did not reference from-clause element to which the with-clause was associated" ); } SqlGenerator sql = new SqlGenerator( getSessionFactoryHelper().getFactory() ); sql.whereExpr( hqlSqlWithNode.getFirstChild() ); fromElement.setWithClauseFragment( visitor.getJoinAlias(), "(" + 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 FromElement referencedFromElement; private String joinAlias; public void visit(AST node) { // todo : currently expects that the individual with expressions apply to the same sql table join. // This may not be the case for joined-subclass where the property values // might be coming from different tables in the joined hierarchy. At some // point we should expand this to support that capability. However, that has // some difficulties: // 1) the biggest is how to handle ORs when the individual comparisons are // linked to different sql joins. // 2) here we would need to track each comparison individually, along with // the join alias to which it applies and then pass that information // back to the FromElement so it can pass it along to the JoinSequence if ( node instanceof DotNode ) { DotNode dotNode = ( DotNode ) node; FromElement fromElement = dotNode.getFromElement(); if ( referencedFromElement != null ) { if ( fromElement != referencedFromElement ) { throw new HibernateException( "with-clause referenced two different from-clause elements" ); } } else { referencedFromElement = fromElement; joinAlias = extractAppliedAlias( dotNode ); // todo : temporary // needed because currently persister is the one that // creates and renders the join fragments for inheritence // hierarchies... if ( !joinAlias.equals( referencedFromElement.getTableAlias() ) ) { throw new InvalidWithClauseException( "with clause can only reference columns in the driving table" ); } } } } private String extractAppliedAlias(DotNode dotNode) { return dotNode.getText().substring( 0, dotNode.getText().indexOf( '.' ) ); } public FromElement getReferencedFromElement() { return referencedFromElement; } public String getJoinAlias() { return joinAlias; } } /** * Sets the current 'FROM' context. * * @param fromNode The new 'FROM' context. * @param inputFromNode The from node from the input AST. */ protected void pushFromClause(AST fromNode, AST inputFromNode) { FromClause newFromClause = ( FromClause ) fromNode; newFromClause.setParentFromClause( currentFromClause ); currentFromClause = newFromClause; } /** * Returns to the previous 'FROM' context. */ private void popFromClause() { currentFromClause = currentFromClause.getParentFromClause(); } protected void lookupAlias(AST aliasRef) throws SemanticException { FromElement alias = currentFromClause.getFromElement( aliasRef.getText() ); FromReferenceNode aliasRefNode = ( FromReferenceNode ) aliasRef; aliasRefNode.setFromElement( alias ); } protected void setImpliedJoinType(int joinType) { impliedJoinType = JoinProcessor.toHibernateJoinType( joinType ); } public int getImpliedJoinType() { return impliedJoinType; } protected AST lookupProperty(AST dot, boolean root, boolean inSelect) throws SemanticException { DotNode dotNode = ( DotNode ) dot; FromReferenceNode lhs = dotNode.getLhs(); AST rhs = lhs.getNextSibling(); switch ( rhs.getType() ) { case SqlTokenTypes.ELEMENTS: case SqlTokenTypes.INDICES: if ( log.isDebugEnabled() ) { log.debug( "lookupProperty() " + dotNode.getPath() + " => " + rhs.getText() + "(" + lhs.getPath() + ")" ); } CollectionFunction f = ( CollectionFunction ) rhs; // Re-arrange the tree so that the collection function is the root and the lhs is the path. f.setFirstChild( lhs ); lhs.setNextSibling( null ); dotNode.setFirstChild( f ); resolve( lhs ); // Don't forget to resolve the argument! f.resolve( inSelect ); // Resolve the collection function now. return f; default: // Resolve everything up to this dot, but don't resolve the placeholders yet. dotNode.resolveFirstChild(); return dotNode; } } 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.trace( "attempting to resolve property [" + identText + "] as a non-qualified ref" ); return fromElement.getPropertyMapping( identText ).toType( identText ) != null; } catch( QueryException e ) { // Should mean that no such property was found } } return false; } 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}" ); // TODO : better way?!? ( ( 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; } protected void processQuery(AST select, AST query) throws SemanticException { if ( log.isDebugEnabled() ) { log.debug( "processQuery() : " + query.toStringTree() ); } try { QueryNode qn = ( QueryNode ) query; // Was there an explicit select expression? boolean explicitSelect = select != null && select.getNumberOfChildren() > 0; if ( !explicitSelect ) { // No explicit select expression; render the id and properties // projection lists for every persister in the from clause into // a single 'token node'. //TODO: the only reason we need this stuff now is collection filters, // we should get rid of derived select clause completely! createSelectClauseFromFromClause( qn ); } else { // Use the explicitly declared select expression; determine the // return types indicated by each select token useSelectClause( select ); } // After that, process the JOINs. // Invoke a delegate to do the work, as this is farily complex. JoinProcessor joinProcessor = new JoinProcessor( astFactory, queryTranslatorImpl ); joinProcessor.processJoins( qn, isSubQuery() ); // Attach any mapping-defined "ORDER BY" fragments Iterator itr = qn.getFromClause().getProjectionList().iterator(); while ( itr.hasNext() ) { final FromElement fromElement = ( FromElement ) itr.next(); // if ( fromElement.isFetch() && fromElement.isCollectionJoin() ) { if ( fromElement.isFetch() && fromElement.getQueryableCollection() != null ) { // Does the collection referenced by this FromElement // specify an order-by attribute? If so, attach it to // the query's order-by 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(); // Make #@%$^#^&# sure no alias is applied to the table name fromElement.setText( persister.getTableName() ); // append any filter fragments; the EMPTY_MAP is used under the assumption that // currently enabled filters should not affect this process if ( persister.getDiscriminatorType() != null ) { new SyntheticAndFactory( getASTFactory() ).addDiscriminatorWhereFragment( statement, persister, java.util.Collections.EMPTY_MAP, fromElement.getTableAlias() ); } } protected void postProcessUpdate(AST update) throws SemanticException { UpdateStatement updateStatement = ( UpdateStatement ) update; postProcessDML( updateStatement ); } protected void postProcessDelete(AST delete) throws SemanticException { postProcessDML( ( DeleteStatement ) delete ); } public static boolean supportsIdGenWithBulkInsertion(IdentifierGenerator generator) { return SequenceGenerator.class.isAssignableFrom( generator.getClass() ) || PostInsertIdentifierGenerator.class.isAssignableFrom( generator.getClass() ); } 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() ) { // We need to generate ids as part of this bulk insert. // // Note that this is only supported for sequence-style generators and // post-insert-style generators; basically, only in-db generators IdentifierGenerator generator = persister.getIdentifierGenerator(); if ( !supportsIdGenWithBulkInsertion( generator ) ) { throw new QueryException( "can only generate ids as part of bulk insert with either sequence or post-insert style generators" ); } AST idSelectExprNode = null; if ( SequenceGenerator.class.isAssignableFrom( generator.getClass() ) ) { String seqName = ( String ) ( ( SequenceGenerator ) generator ).generatorKey(); String nextval = sessionFactoryHelper.getFactory().getDialect().getSelectSequenceNextValString( seqName ); idSelectExprNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, nextval ); } else { //Don't need this, because we should never ever be selecting no columns in an insert ... select... //and because it causes a bug on DB2 /*String idInsertString = sessionFactoryHelper.getFactory().getDialect().getIdentityInsertString(); if ( idInsertString != null ) { idSelectExprNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, idInsertString ); }*/ } if ( idSelectExprNode != null ) { AST currentFirstSelectExprNode = selectClause.getFirstChild(); selectClause.setFirstChild( idSelectExprNode ); idSelectExprNode.setNextSibling( currentFirstSelectExprNode ); insertStatement.getIntoClause().prependIdColumnSpec(); } } 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() ) { versionValueNode = getASTFactory().create( HqlSqlTokenTypes.PARAM, "?" ); ParameterSpecification paramSpec = new VersionTypeSeedParameterSpecification( versionType ); ( ( ParameterNode ) versionValueNode ).setHqlParameterSpecification( paramSpec ); parameters.add( 0, paramSpec ); } 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(); } 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) { // currently only the Hibernate-supplied DbTimestampType is supported here 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 ); if ( log.isDebugEnabled() ) { log.debug( "Derived SELECT clause created." ); } } protected void resolve(AST node) throws SemanticException { if ( node != null ) { // This is called when it's time to fully resolve a path expression. ResolvableNode r = ( ResolvableNode ) node; if ( isInFunctionCall() ) { r.resolveInFunctionCall( false, true ); } else { r.resolve( false, true ); // Generate implicit joins, only if necessary. } } } protected void resolveSelectExpression(AST node) throws SemanticException { // This is called when it's time to fully resolve a path expression. int type = node.getType(); switch ( type ) { case DOT: DotNode dot = ( DotNode ) node; dot.resolveSelectExpression(); break; case ALIAS_REF: // Notify the FROM element that it is being referenced by the select. FromReferenceNode aliasRefNode = ( FromReferenceNode ) node; //aliasRefNode.resolve( false, false, aliasRefNode.getText() ); //TODO: is it kosher to do it here? aliasRefNode.resolve( false, false ); //TODO: is it kosher to do it here? FromElement fromElement = aliasRefNode.getFromElement(); if ( fromElement != null ) { fromElement.setIncludeSubclasses( true ); } default: break; } } protected void beforeSelectClause() throws SemanticException { // Turn off includeSubclasses on all FromElements. FromClause from = getCurrentFromClause(); List fromElements = from.getFromElements(); for ( Iterator iterator = fromElements.iterator(); iterator.hasNext(); ) { FromElement fromElement = ( FromElement ) iterator.next(); fromElement.setIncludeSubclasses( false ); } } protected AST generatePositionalParameter(AST inputNode) throws SemanticException { if ( namedParameters.size() > 0 ) { throw new SemanticException( "cannot define positional parameter after any named parameters have been defined" ); } ParameterNode parameter = ( ParameterNode ) astFactory.create( PARAM, "?" ); PositionalParameterSpecification paramSpec = new PositionalParameterSpecification( ( ( Node ) inputNode ).getLine(), ( ( Node ) inputNode ).getColumn(), positionalParameterCount++ ); parameter.setHqlParameterSpecification( paramSpec ); parameters.add( paramSpec ); return parameter; } protected AST generateNamedParameter(AST delimiterNode, AST nameNode) throws SemanticException { String name = nameNode.getText(); trackNamedParameterPositions( name ); // create the node initially with the param name so that it shows // appropriately in the "original text" attribute ParameterNode parameter = ( ParameterNode ) astFactory.create( NAMED_PARAM, name ); parameter.setText( "?" ); NamedParameterSpecification paramSpec = new NamedParameterSpecification( ( ( Node ) delimiterNode ).getLine(), ( ( Node ) delimiterNode ).getColumn(), name ); parameter.setHqlParameterSpecification( paramSpec ); parameters.add( paramSpec ); return parameter; } private void trackNamedParameterPositions(String name) { Integer loc = new Integer( parameterCount++ ); Object o = namedParameters.get( name ); if ( o == null ) { namedParameters.put( name, loc ); } else if ( o instanceof Integer ) { ArrayList list = new ArrayList( 4 ); list.add( o ); list.add( loc ); namedParameters.put( name, list ); } else { ( ( ArrayList ) o ).add( loc ); } } protected void processConstant(AST constant) throws SemanticException { literalProcessor.processConstant( constant, true ); // Use the delegate, resolve identifiers as FROM element aliases. } protected void processBoolean(AST constant) throws SemanticException { literalProcessor.processBoolean( constant ); // Use the delegate. } protected void processNumericLiteral(AST literal) { literalProcessor.processNumeric( literal ); } protected void processIndex(AST indexOp) throws SemanticException { IndexNode indexNode = ( IndexNode ) indexOp; indexNode.resolve( true, true ); } protected void processFunction(AST functionCall, boolean inSelect) throws SemanticException { MethodNode methodNode = ( MethodNode ) functionCall; methodNode.resolve( inSelect ); } protected void processConstructor(AST constructor) throws SemanticException { ConstructorNode constructorNode = ( ConstructorNode ) constructor; constructorNode.prepare(); } protected void setAlias(AST selectExpr, AST ident) { ((SelectExpression) selectExpr).setAlias(ident.getText()); } /** * Returns the locations of all occurrences of the named parameter. */ public int[] getNamedParameterLocations(String name) throws QueryException { Object o = namedParameters.get( name ); if ( o == null ) { QueryException qe = new QueryException( QueryTranslator.ERROR_NAMED_PARAMETER_DOES_NOT_APPEAR + name ); qe.setQueryString( queryTranslatorImpl.getQueryString() ); throw qe; } if ( o instanceof Integer ) { return new int[]{( ( Integer ) o ).intValue()}; } else { return ArrayHelper.toIntArray( ( ArrayList ) o ); } } public void addQuerySpaces(Serializable[] spaces) { for ( int i = 0; i < spaces.length; i++ ) { querySpaces.add( spaces[i] ); } } 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() { // select clauses for insert statements should alwasy be treated as shallow return getStatementType() == INSERT || queryTranslatorImpl.isShallowQuery(); } public Map getEnabledFilters() { return queryTranslatorImpl.getEnabledFilters(); } public LiteralProcessor getLiteralProcessor() { return literalProcessor; } public ASTPrinter getASTPrinter() { return printer; } public ArrayList getParameters() { return parameters; } public int getNumberOfParametersInSetClause() { return numberOfParametersInSetClause; } 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() ) { // no need to even collect this information if the persister is considered multi-table 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; } 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; } protected void prepareVersioned(AST updateNode, AST versioned) throws SemanticException { UpdateStatement updateStatement = ( UpdateStatement ) updateNode; FromClause fromClause = updateStatement.getFromClause(); if ( versioned != null ) { // Make sure that the persister is versioned 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 ( Date.class.isAssignableFrom( versionType.getReturnedClass() ) ) { versionIncrementNode = getASTFactory().create( HqlSqlTokenTypes.PARAM, "?" ); ParameterSpecification paramSpec = new VersionTypeSeedParameterSpecification( versionType ); ( ( ParameterNode ) versionIncrementNode ).setHqlParameterSpecification( paramSpec ); parameters.add( 0, paramSpec ); } else { // Not possible to simply re-use the versionPropertyNode here as it causes // OOM errors due to circularity :( 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 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; } protected void prepareLogicOperator(AST operator) throws SemanticException { ( ( OperatorNode ) operator ).initialize(); } protected void prepareArithmeticOperator(AST operator) throws SemanticException { ( ( OperatorNode ) operator ).initialize(); } public static void panic() { throw new QueryException( "TreeWalker: panic" ); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy