0001: // $Id: HqlSqlWalker.java 10946 2006-12-07 14:50:59Z steve.ebersole@jboss.com $
0002: package org.hibernate.hql.ast;
0003:
0004: import java.io.Serializable;
0005: import java.util.ArrayList;
0006: import java.util.Date;
0007: import java.util.HashMap;
0008: import java.util.HashSet;
0009: import java.util.Iterator;
0010: import java.util.List;
0011: import java.util.Map;
0012: import java.util.Set;
0013:
0014: import org.apache.commons.logging.Log;
0015: import org.apache.commons.logging.LogFactory;
0016: import org.hibernate.QueryException;
0017: import org.hibernate.HibernateException;
0018: import org.hibernate.engine.JoinSequence;
0019: import org.hibernate.engine.ParameterBinder;
0020: import org.hibernate.engine.SessionFactoryImplementor;
0021: import org.hibernate.hql.QueryTranslator;
0022: import org.hibernate.hql.antlr.HqlSqlBaseWalker;
0023: import org.hibernate.hql.antlr.HqlSqlTokenTypes;
0024: import org.hibernate.hql.antlr.HqlTokenTypes;
0025: import org.hibernate.hql.antlr.SqlTokenTypes;
0026: import org.hibernate.hql.ast.tree.AssignmentSpecification;
0027: import org.hibernate.hql.ast.tree.CollectionFunction;
0028: import org.hibernate.hql.ast.tree.ConstructorNode;
0029: import org.hibernate.hql.ast.tree.DeleteStatement;
0030: import org.hibernate.hql.ast.tree.DotNode;
0031: import org.hibernate.hql.ast.tree.FromClause;
0032: import org.hibernate.hql.ast.tree.FromElement;
0033: import org.hibernate.hql.ast.tree.FromReferenceNode;
0034: import org.hibernate.hql.ast.tree.IdentNode;
0035: import org.hibernate.hql.ast.tree.IndexNode;
0036: import org.hibernate.hql.ast.tree.InsertStatement;
0037: import org.hibernate.hql.ast.tree.IntoClause;
0038: import org.hibernate.hql.ast.tree.MethodNode;
0039: import org.hibernate.hql.ast.tree.ParameterNode;
0040: import org.hibernate.hql.ast.tree.QueryNode;
0041: import org.hibernate.hql.ast.tree.ResolvableNode;
0042: import org.hibernate.hql.ast.tree.RestrictableStatement;
0043: import org.hibernate.hql.ast.tree.SelectClause;
0044: import org.hibernate.hql.ast.tree.SelectExpression;
0045: import org.hibernate.hql.ast.tree.UpdateStatement;
0046: import org.hibernate.hql.ast.tree.Node;
0047: import org.hibernate.hql.ast.tree.OperatorNode;
0048: import org.hibernate.hql.ast.util.ASTPrinter;
0049: import org.hibernate.hql.ast.util.ASTUtil;
0050: import org.hibernate.hql.ast.util.AliasGenerator;
0051: import org.hibernate.hql.ast.util.JoinProcessor;
0052: import org.hibernate.hql.ast.util.LiteralProcessor;
0053: import org.hibernate.hql.ast.util.SessionFactoryHelper;
0054: import org.hibernate.hql.ast.util.SyntheticAndFactory;
0055: import org.hibernate.hql.ast.util.NodeTraverser;
0056: import org.hibernate.id.IdentifierGenerator;
0057: import org.hibernate.id.PostInsertIdentifierGenerator;
0058: import org.hibernate.id.SequenceGenerator;
0059: import org.hibernate.param.NamedParameterSpecification;
0060: import org.hibernate.param.ParameterSpecification;
0061: import org.hibernate.param.PositionalParameterSpecification;
0062: import org.hibernate.param.VersionTypeSeedParameterSpecification;
0063: import org.hibernate.param.CollectionFilterKeyParameterSpecification;
0064: import org.hibernate.persister.collection.QueryableCollection;
0065: import org.hibernate.persister.entity.Queryable;
0066: import org.hibernate.sql.JoinFragment;
0067: import org.hibernate.type.AssociationType;
0068: import org.hibernate.type.Type;
0069: import org.hibernate.type.VersionType;
0070: import org.hibernate.type.DbTimestampType;
0071: import org.hibernate.usertype.UserVersionType;
0072: import org.hibernate.util.ArrayHelper;
0073:
0074: import antlr.ASTFactory;
0075: import antlr.RecognitionException;
0076: import antlr.SemanticException;
0077: import antlr.collections.AST;
0078:
0079: /**
0080: * Implements methods used by the HQL->SQL tree transform grammar (a.k.a. the second phase).
0081: * <ul>
0082: * <li>Isolates the Hibernate API-specific code from the ANTLR generated code.</li>
0083: * <li>Handles the SQL framgents generated by the persisters in order to create the SELECT and FROM clauses,
0084: * taking into account the joins and projections that are implied by the mappings (persister/queryable).</li>
0085: * <li>Uses SqlASTFactory to create customized AST nodes.</li>
0086: * </ul>
0087: *
0088: * @see SqlASTFactory
0089: */
0090: public class HqlSqlWalker extends HqlSqlBaseWalker implements
0091: ErrorReporter, ParameterBinder.NamedParameterSource {
0092: private static final Log log = LogFactory
0093: .getLog(HqlSqlWalker.class);
0094:
0095: private final QueryTranslatorImpl queryTranslatorImpl;
0096: private final HqlParser hqlParser;
0097: private final SessionFactoryHelper sessionFactoryHelper;
0098: private final Map tokenReplacements;
0099: private final AliasGenerator aliasGenerator = new AliasGenerator();
0100: private final LiteralProcessor literalProcessor;
0101: private final ParseErrorHandler parseErrorHandler;
0102: private final ASTPrinter printer;
0103: private final String collectionFilterRole;
0104:
0105: private FromClause currentFromClause = null;
0106: private SelectClause selectClause;
0107:
0108: private Set querySpaces = new HashSet();
0109:
0110: private int parameterCount;
0111: private Map namedParameters = new HashMap();
0112: private ArrayList parameters = new ArrayList();
0113: private int numberOfParametersInSetClause;
0114: private int positionalParameterCount;
0115:
0116: private ArrayList assignmentSpecifications = new ArrayList();
0117:
0118: private int impliedJoinType;
0119:
0120: /**
0121: * Create a new tree transformer.
0122: *
0123: * @param qti Back pointer to the query translator implementation that is using this tree transform.
0124: * @param sfi The session factory implementor where the Hibernate mappings can be found.
0125: * @param parser A reference to the phase-1 parser
0126: * @param tokenReplacements Registers the token replacement map with the walker. This map will
0127: * be used to substitute function names and constants.
0128: * @param collectionRole The collection role name of the collection used as the basis for the
0129: * filter, NULL if this is not a collection filter compilation.
0130: */
0131: public HqlSqlWalker(QueryTranslatorImpl qti,
0132: SessionFactoryImplementor sfi, HqlParser parser,
0133: Map tokenReplacements, String collectionRole) {
0134: setASTFactory(new SqlASTFactory(this ));
0135: this .parseErrorHandler = new ErrorCounter();
0136: this .queryTranslatorImpl = qti;
0137: this .sessionFactoryHelper = new SessionFactoryHelper(sfi);
0138: this .literalProcessor = new LiteralProcessor(this );
0139: this .tokenReplacements = tokenReplacements;
0140: this .hqlParser = parser;
0141: this .printer = new ASTPrinter(SqlTokenTypes.class);
0142: this .collectionFilterRole = collectionRole;
0143: }
0144:
0145: protected void prepareFromClauseInputTree(AST fromClauseInput) {
0146: if (!isSubQuery()) {
0147: // // inject param specifications to account for dynamic filter param values
0148: // if ( ! getEnabledFilters().isEmpty() ) {
0149: // Iterator filterItr = getEnabledFilters().values().iterator();
0150: // while ( filterItr.hasNext() ) {
0151: // FilterImpl filter = ( FilterImpl ) filterItr.next();
0152: // if ( ! filter.getFilterDefinition().getParameterNames().isEmpty() ) {
0153: // Iterator paramItr = filter.getFilterDefinition().getParameterNames().iterator();
0154: // while ( paramItr.hasNext() ) {
0155: // String parameterName = ( String ) paramItr.next();
0156: // // currently param filters *only* work with single-column parameter types;
0157: // // if that limitation is ever lifted, this logic will need to change to account for that
0158: // ParameterNode collectionFilterKeyParameter = ( ParameterNode ) astFactory.create( PARAM, "?" );
0159: // DynamicFilterParameterSpecification paramSpec = new DynamicFilterParameterSpecification(
0160: // filter.getName(),
0161: // parameterName,
0162: // filter.getFilterDefinition().getParameterType( parameterName ),
0163: // positionalParameterCount++
0164: // );
0165: // collectionFilterKeyParameter.setHqlParameterSpecification( paramSpec );
0166: // parameters.add( paramSpec );
0167: // }
0168: // }
0169: // }
0170: // }
0171:
0172: if (isFilter()) {
0173: // Handle collection-fiter compilation.
0174: // IMPORTANT NOTE: This is modifying the INPUT (HQL) tree, not the output tree!
0175: QueryableCollection persister = sessionFactoryHelper
0176: .getCollectionPersister(collectionFilterRole);
0177: Type collectionElementType = persister.getElementType();
0178: if (!collectionElementType.isEntityType()) {
0179: throw new QueryException(
0180: "collection of values in filter: this");
0181: }
0182:
0183: String collectionElementEntityName = persister
0184: .getElementPersister().getEntityName();
0185: ASTFactory inputAstFactory = hqlParser.getASTFactory();
0186: AST fromElement = ASTUtil.create(inputAstFactory,
0187: HqlTokenTypes.FILTER_ENTITY,
0188: collectionElementEntityName);
0189: ASTUtil.createSibling(inputAstFactory,
0190: HqlTokenTypes.ALIAS, "this", fromElement);
0191: fromClauseInput.addChild(fromElement);
0192: // Show the modified AST.
0193: if (log.isDebugEnabled()) {
0194: log
0195: .debug("prepareFromClauseInputTree() : Filter - Added 'this' as a from element...");
0196: }
0197: queryTranslatorImpl.showHqlAst(hqlParser.getAST());
0198:
0199: // Create a parameter specification for the collection filter...
0200: Type collectionFilterKeyType = sessionFactoryHelper
0201: .requireQueryableCollection(
0202: collectionFilterRole).getKeyType();
0203: ParameterNode collectionFilterKeyParameter = (ParameterNode) astFactory
0204: .create(PARAM, "?");
0205: CollectionFilterKeyParameterSpecification collectionFilterKeyParameterSpec = new CollectionFilterKeyParameterSpecification(
0206: collectionFilterRole, collectionFilterKeyType,
0207: positionalParameterCount++);
0208: collectionFilterKeyParameter
0209: .setHqlParameterSpecification(collectionFilterKeyParameterSpec);
0210: parameters.add(collectionFilterKeyParameterSpec);
0211: }
0212: }
0213: }
0214:
0215: public boolean isFilter() {
0216: return collectionFilterRole != null;
0217: }
0218:
0219: public SessionFactoryHelper getSessionFactoryHelper() {
0220: return sessionFactoryHelper;
0221: }
0222:
0223: public Map getTokenReplacements() {
0224: return tokenReplacements;
0225: }
0226:
0227: public AliasGenerator getAliasGenerator() {
0228: return aliasGenerator;
0229: }
0230:
0231: public FromClause getCurrentFromClause() {
0232: return currentFromClause;
0233: }
0234:
0235: public ParseErrorHandler getParseErrorHandler() {
0236: return parseErrorHandler;
0237: }
0238:
0239: public void reportError(RecognitionException e) {
0240: parseErrorHandler.reportError(e); // Use the delegate.
0241: }
0242:
0243: public void reportError(String s) {
0244: parseErrorHandler.reportError(s); // Use the delegate.
0245: }
0246:
0247: public void reportWarning(String s) {
0248: parseErrorHandler.reportWarning(s);
0249: }
0250:
0251: /**
0252: * Returns the set of unique query spaces (a.k.a.
0253: * table names) that occurred in the query.
0254: *
0255: * @return A set of table names (Strings).
0256: */
0257: public Set getQuerySpaces() {
0258: return querySpaces;
0259: }
0260:
0261: protected AST createFromElement(String path, AST alias,
0262: AST propertyFetch) throws SemanticException {
0263: FromElement fromElement = currentFromClause.addFromElement(
0264: path, alias);
0265: fromElement.setAllPropertyFetch(propertyFetch != null);
0266: return fromElement;
0267: }
0268:
0269: protected AST createFromFilterElement(AST filterEntity, AST alias)
0270: throws SemanticException {
0271: FromElement fromElement = currentFromClause.addFromElement(
0272: filterEntity.getText(), alias);
0273: FromClause fromClause = fromElement.getFromClause();
0274: QueryableCollection persister = sessionFactoryHelper
0275: .getCollectionPersister(collectionFilterRole);
0276: // Get the names of the columns used to link between the collection
0277: // owner and the collection elements.
0278: String[] keyColumnNames = persister.getKeyColumnNames();
0279: String fkTableAlias = persister.isOneToMany() ? fromElement
0280: .getTableAlias() : fromClause.getAliasGenerator()
0281: .createName(collectionFilterRole);
0282: JoinSequence join = sessionFactoryHelper.createJoinSequence();
0283: join.setRoot(persister, fkTableAlias);
0284: if (!persister.isOneToMany()) {
0285: join.addJoin((AssociationType) persister.getElementType(),
0286: fromElement.getTableAlias(),
0287: JoinFragment.INNER_JOIN, persister
0288: .getElementColumnNames(fkTableAlias));
0289: }
0290: join.addCondition(fkTableAlias, keyColumnNames, " = ?");
0291: fromElement.setJoinSequence(join);
0292: fromElement.setFilter(true);
0293: if (log.isDebugEnabled()) {
0294: log
0295: .debug("createFromFilterElement() : processed filter FROM element.");
0296: }
0297: return fromElement;
0298: }
0299:
0300: protected void createFromJoinElement(AST path, AST alias,
0301: int joinType, AST fetchNode, AST propertyFetch, AST with)
0302: throws SemanticException {
0303: boolean fetch = fetchNode != null;
0304: if (fetch && isSubQuery()) {
0305: throw new QueryException(
0306: "fetch not allowed in subquery from-elements");
0307: }
0308: // The path AST should be a DotNode, and it should have been evaluated already.
0309: if (path.getType() != SqlTokenTypes.DOT) {
0310: throw new SemanticException("Path expected for join!");
0311: }
0312: DotNode dot = (DotNode) path;
0313: int hibernateJoinType = JoinProcessor
0314: .toHibernateJoinType(joinType);
0315: dot.setJoinType(hibernateJoinType); // Tell the dot node about the join type.
0316: dot.setFetch(fetch);
0317: // Generate an explicit join for the root dot node. The implied joins will be collected and passed up
0318: // to the root dot node.
0319: dot
0320: .resolve(true, false, alias == null ? null : alias
0321: .getText());
0322: FromElement fromElement = dot.getImpliedJoin();
0323: fromElement.setAllPropertyFetch(propertyFetch != null);
0324:
0325: if (with != null) {
0326: if (fetch) {
0327: throw new SemanticException(
0328: "with-clause not allowed on fetched associations; use filters");
0329: }
0330: handleWithFragment(fromElement, with);
0331: }
0332:
0333: if (log.isDebugEnabled()) {
0334: log.debug("createFromJoinElement() : "
0335: + getASTPrinter().showAsString(fromElement,
0336: "-- join tree --"));
0337: }
0338: }
0339:
0340: private void handleWithFragment(FromElement fromElement,
0341: AST hqlWithNode) throws SemanticException {
0342: try {
0343: withClause(hqlWithNode);
0344: AST hqlSqlWithNode = returnAST;
0345: if (log.isDebugEnabled()) {
0346: log.debug("handleWithFragment() : "
0347: + getASTPrinter().showAsString(hqlSqlWithNode,
0348: "-- with clause --"));
0349: }
0350: WithClauseVisitor visitor = new WithClauseVisitor();
0351: NodeTraverser traverser = new NodeTraverser(visitor);
0352: traverser.traverseDepthFirst(hqlSqlWithNode);
0353: FromElement referencedFromElement = visitor
0354: .getReferencedFromElement();
0355: if (referencedFromElement != fromElement) {
0356: throw new InvalidWithClauseException(
0357: "with-clause expressions did not reference from-clause element to which the with-clause was associated");
0358: }
0359: SqlGenerator sql = new SqlGenerator(
0360: getSessionFactoryHelper().getFactory());
0361: sql.whereExpr(hqlSqlWithNode.getFirstChild());
0362: fromElement.setWithClauseFragment(visitor.getJoinAlias(),
0363: "(" + sql.getSQL() + ")");
0364:
0365: } catch (SemanticException e) {
0366: throw e;
0367: } catch (InvalidWithClauseException e) {
0368: throw e;
0369: } catch (Exception e) {
0370: throw new SemanticException(e.getMessage());
0371: }
0372: }
0373:
0374: private static class WithClauseVisitor implements
0375: NodeTraverser.VisitationStrategy {
0376: private FromElement referencedFromElement;
0377: private String joinAlias;
0378:
0379: public void visit(AST node) {
0380: // todo : currently expects that the individual with expressions apply to the same sql table join.
0381: // This may not be the case for joined-subclass where the property values
0382: // might be coming from different tables in the joined hierarchy. At some
0383: // point we should expand this to support that capability. However, that has
0384: // some difficulties:
0385: // 1) the biggest is how to handle ORs when the individual comparisons are
0386: // linked to different sql joins.
0387: // 2) here we would need to track each comparison individually, along with
0388: // the join alias to which it applies and then pass that information
0389: // back to the FromElement so it can pass it along to the JoinSequence
0390:
0391: if (node instanceof DotNode) {
0392: DotNode dotNode = (DotNode) node;
0393: FromElement fromElement = dotNode.getFromElement();
0394: if (referencedFromElement != null) {
0395: if (fromElement != referencedFromElement) {
0396: throw new HibernateException(
0397: "with-clause referenced two different from-clause elements");
0398: }
0399: } else {
0400: referencedFromElement = fromElement;
0401: joinAlias = extractAppliedAlias(dotNode);
0402: // todo : temporary
0403: // needed because currently persister is the one that
0404: // creates and renders the join fragments for inheritence
0405: // hierarchies...
0406: if (!joinAlias.equals(referencedFromElement
0407: .getTableAlias())) {
0408: throw new InvalidWithClauseException(
0409: "with clause can only reference columns in the driving table");
0410: }
0411: }
0412: }
0413: }
0414:
0415: private String extractAppliedAlias(DotNode dotNode) {
0416: return dotNode.getText().substring(0,
0417: dotNode.getText().indexOf('.'));
0418: }
0419:
0420: public FromElement getReferencedFromElement() {
0421: return referencedFromElement;
0422: }
0423:
0424: public String getJoinAlias() {
0425: return joinAlias;
0426: }
0427: }
0428:
0429: /**
0430: * Sets the current 'FROM' context.
0431: *
0432: * @param fromNode The new 'FROM' context.
0433: * @param inputFromNode The from node from the input AST.
0434: */
0435: protected void pushFromClause(AST fromNode, AST inputFromNode) {
0436: FromClause newFromClause = (FromClause) fromNode;
0437: newFromClause.setParentFromClause(currentFromClause);
0438: currentFromClause = newFromClause;
0439: }
0440:
0441: /**
0442: * Returns to the previous 'FROM' context.
0443: */
0444: private void popFromClause() {
0445: currentFromClause = currentFromClause.getParentFromClause();
0446: }
0447:
0448: protected void lookupAlias(AST aliasRef) throws SemanticException {
0449: FromElement alias = currentFromClause.getFromElement(aliasRef
0450: .getText());
0451: FromReferenceNode aliasRefNode = (FromReferenceNode) aliasRef;
0452: aliasRefNode.setFromElement(alias);
0453: }
0454:
0455: protected void setImpliedJoinType(int joinType) {
0456: impliedJoinType = JoinProcessor.toHibernateJoinType(joinType);
0457: }
0458:
0459: public int getImpliedJoinType() {
0460: return impliedJoinType;
0461: }
0462:
0463: protected AST lookupProperty(AST dot, boolean root, boolean inSelect)
0464: throws SemanticException {
0465: DotNode dotNode = (DotNode) dot;
0466: FromReferenceNode lhs = dotNode.getLhs();
0467: AST rhs = lhs.getNextSibling();
0468: switch (rhs.getType()) {
0469: case SqlTokenTypes.ELEMENTS:
0470: case SqlTokenTypes.INDICES:
0471: if (log.isDebugEnabled()) {
0472: log.debug("lookupProperty() " + dotNode.getPath()
0473: + " => " + rhs.getText() + "(" + lhs.getPath()
0474: + ")");
0475: }
0476: CollectionFunction f = (CollectionFunction) rhs;
0477: // Re-arrange the tree so that the collection function is the root and the lhs is the path.
0478: f.setFirstChild(lhs);
0479: lhs.setNextSibling(null);
0480: dotNode.setFirstChild(f);
0481: resolve(lhs); // Don't forget to resolve the argument!
0482: f.resolve(inSelect); // Resolve the collection function now.
0483: return f;
0484: default:
0485: // Resolve everything up to this dot, but don't resolve the placeholders yet.
0486: dotNode.resolveFirstChild();
0487: return dotNode;
0488: }
0489: }
0490:
0491: protected boolean isNonQualifiedPropertyRef(AST ident) {
0492: final String identText = ident.getText();
0493: if (currentFromClause.isFromElementAlias(identText)) {
0494: return false;
0495: }
0496:
0497: List fromElements = currentFromClause.getExplicitFromElements();
0498: if (fromElements.size() == 1) {
0499: final FromElement fromElement = (FromElement) fromElements
0500: .get(0);
0501: try {
0502: log.trace("attempting to resolve property ["
0503: + identText + "] as a non-qualified ref");
0504: return fromElement.getPropertyMapping(identText)
0505: .toType(identText) != null;
0506: } catch (QueryException e) {
0507: // Should mean that no such property was found
0508: }
0509: }
0510:
0511: return false;
0512: }
0513:
0514: protected AST lookupNonQualifiedProperty(AST property)
0515: throws SemanticException {
0516: final FromElement fromElement = (FromElement) currentFromClause
0517: .getExplicitFromElements().get(0);
0518: AST syntheticDotNode = generateSyntheticDotNodeForNonQualifiedPropertyRef(
0519: property, fromElement);
0520: return lookupProperty(syntheticDotNode, false,
0521: getCurrentClauseType() == HqlSqlTokenTypes.SELECT);
0522: }
0523:
0524: private AST generateSyntheticDotNodeForNonQualifiedPropertyRef(
0525: AST property, FromElement fromElement) {
0526: AST dot = getASTFactory().create(DOT,
0527: "{non-qualified-property-ref}");
0528: // TODO : better way?!?
0529: ((DotNode) dot).setPropertyPath(((FromReferenceNode) property)
0530: .getPath());
0531:
0532: IdentNode syntheticAlias = (IdentNode) getASTFactory().create(
0533: IDENT, "{synthetic-alias}");
0534: syntheticAlias.setFromElement(fromElement);
0535: syntheticAlias.setResolved();
0536:
0537: dot.setFirstChild(syntheticAlias);
0538: dot.addChild(property);
0539:
0540: return dot;
0541: }
0542:
0543: protected void processQuery(AST select, AST query)
0544: throws SemanticException {
0545: if (log.isDebugEnabled()) {
0546: log.debug("processQuery() : " + query.toStringTree());
0547: }
0548:
0549: try {
0550: QueryNode qn = (QueryNode) query;
0551:
0552: // Was there an explicit select expression?
0553: boolean explicitSelect = select != null
0554: && select.getNumberOfChildren() > 0;
0555:
0556: if (!explicitSelect) {
0557: // No explicit select expression; render the id and properties
0558: // projection lists for every persister in the from clause into
0559: // a single 'token node'.
0560: //TODO: the only reason we need this stuff now is collection filters,
0561: // we should get rid of derived select clause completely!
0562: createSelectClauseFromFromClause(qn);
0563: } else {
0564: // Use the explicitly declared select expression; determine the
0565: // return types indicated by each select token
0566: useSelectClause(select);
0567: }
0568:
0569: // After that, process the JOINs.
0570: // Invoke a delegate to do the work, as this is farily complex.
0571: JoinProcessor joinProcessor = new JoinProcessor(astFactory,
0572: queryTranslatorImpl);
0573: joinProcessor.processJoins(qn, isSubQuery());
0574:
0575: // Attach any mapping-defined "ORDER BY" fragments
0576: Iterator itr = qn.getFromClause().getProjectionList()
0577: .iterator();
0578: while (itr.hasNext()) {
0579: final FromElement fromElement = (FromElement) itr
0580: .next();
0581: // if ( fromElement.isFetch() && fromElement.isCollectionJoin() ) {
0582: if (fromElement.isFetch()
0583: && fromElement.getQueryableCollection() != null) {
0584: // Does the collection referenced by this FromElement
0585: // specify an order-by attribute? If so, attach it to
0586: // the query's order-by
0587: if (fromElement.getQueryableCollection()
0588: .hasOrdering()) {
0589: String orderByFragment = fromElement
0590: .getQueryableCollection()
0591: .getSQLOrderByString(
0592: fromElement
0593: .getCollectionTableAlias());
0594: qn.getOrderByClause().addOrderFragment(
0595: orderByFragment);
0596: }
0597: if (fromElement.getQueryableCollection()
0598: .hasManyToManyOrdering()) {
0599: String orderByFragment = fromElement
0600: .getQueryableCollection()
0601: .getManyToManyOrderByString(
0602: fromElement.getTableAlias());
0603: qn.getOrderByClause().addOrderFragment(
0604: orderByFragment);
0605: }
0606: }
0607: }
0608: } finally {
0609: popFromClause();
0610: }
0611: }
0612:
0613: protected void postProcessDML(RestrictableStatement statement)
0614: throws SemanticException {
0615: statement.getFromClause().resolve();
0616:
0617: FromElement fromElement = (FromElement) statement
0618: .getFromClause().getFromElements().get(0);
0619: Queryable persister = fromElement.getQueryable();
0620: // Make #@%$^#^&# sure no alias is applied to the table name
0621: fromElement.setText(persister.getTableName());
0622:
0623: // append any filter fragments; the EMPTY_MAP is used under the assumption that
0624: // currently enabled filters should not affect this process
0625: if (persister.getDiscriminatorType() != null) {
0626: new SyntheticAndFactory(getASTFactory())
0627: .addDiscriminatorWhereFragment(statement,
0628: persister, java.util.Collections.EMPTY_MAP,
0629: fromElement.getTableAlias());
0630: }
0631:
0632: }
0633:
0634: protected void postProcessUpdate(AST update)
0635: throws SemanticException {
0636: UpdateStatement updateStatement = (UpdateStatement) update;
0637:
0638: postProcessDML(updateStatement);
0639: }
0640:
0641: protected void postProcessDelete(AST delete)
0642: throws SemanticException {
0643: postProcessDML((DeleteStatement) delete);
0644: }
0645:
0646: public static boolean supportsIdGenWithBulkInsertion(
0647: IdentifierGenerator generator) {
0648: return SequenceGenerator.class.isAssignableFrom(generator
0649: .getClass())
0650: || PostInsertIdentifierGenerator.class
0651: .isAssignableFrom(generator.getClass());
0652: }
0653:
0654: protected void postProcessInsert(AST insert)
0655: throws SemanticException, QueryException {
0656: InsertStatement insertStatement = (InsertStatement) insert;
0657: insertStatement.validate();
0658:
0659: SelectClause selectClause = insertStatement.getSelectClause();
0660: Queryable persister = insertStatement.getIntoClause()
0661: .getQueryable();
0662:
0663: if (!insertStatement.getIntoClause().isExplicitIdInsertion()) {
0664: // We need to generate ids as part of this bulk insert.
0665: //
0666: // Note that this is only supported for sequence-style generators and
0667: // post-insert-style generators; basically, only in-db generators
0668: IdentifierGenerator generator = persister
0669: .getIdentifierGenerator();
0670: if (!supportsIdGenWithBulkInsertion(generator)) {
0671: throw new QueryException(
0672: "can only generate ids as part of bulk insert with either sequence or post-insert style generators");
0673: }
0674:
0675: AST idSelectExprNode = null;
0676:
0677: if (SequenceGenerator.class.isAssignableFrom(generator
0678: .getClass())) {
0679: String seqName = (String) ((SequenceGenerator) generator)
0680: .generatorKey();
0681: String nextval = sessionFactoryHelper.getFactory()
0682: .getDialect().getSelectSequenceNextValString(
0683: seqName);
0684: idSelectExprNode = getASTFactory().create(
0685: HqlSqlTokenTypes.SQL_TOKEN, nextval);
0686: } else {
0687: //Don't need this, because we should never ever be selecting no columns in an insert ... select...
0688: //and because it causes a bug on DB2
0689: /*String idInsertString = sessionFactoryHelper.getFactory().getDialect().getIdentityInsertString();
0690: if ( idInsertString != null ) {
0691: idSelectExprNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, idInsertString );
0692: }*/
0693: }
0694:
0695: if (idSelectExprNode != null) {
0696: AST currentFirstSelectExprNode = selectClause
0697: .getFirstChild();
0698: selectClause.setFirstChild(idSelectExprNode);
0699: idSelectExprNode
0700: .setNextSibling(currentFirstSelectExprNode);
0701:
0702: insertStatement.getIntoClause().prependIdColumnSpec();
0703: }
0704: }
0705:
0706: final boolean includeVersionProperty = persister.isVersioned()
0707: && !insertStatement.getIntoClause()
0708: .isExplicitVersionInsertion()
0709: && persister.isVersionPropertyInsertable();
0710: if (includeVersionProperty) {
0711: // We need to seed the version value as part of this bulk insert
0712: VersionType versionType = persister.getVersionType();
0713: AST versionValueNode = null;
0714:
0715: if (sessionFactoryHelper.getFactory().getDialect()
0716: .supportsParametersInInsertSelect()) {
0717: versionValueNode = getASTFactory().create(
0718: HqlSqlTokenTypes.PARAM, "?");
0719: ParameterSpecification paramSpec = new VersionTypeSeedParameterSpecification(
0720: versionType);
0721: ((ParameterNode) versionValueNode)
0722: .setHqlParameterSpecification(paramSpec);
0723: parameters.add(0, paramSpec);
0724: } else {
0725: if (isIntegral(versionType)) {
0726: try {
0727: Object seedValue = versionType.seed(null);
0728: versionValueNode = getASTFactory().create(
0729: HqlSqlTokenTypes.SQL_TOKEN,
0730: seedValue.toString());
0731: } catch (Throwable t) {
0732: throw new QueryException(
0733: "could not determine seed value for version on bulk insert ["
0734: + versionType + "]");
0735: }
0736: } else if (isDatabaseGeneratedTimestamp(versionType)) {
0737: String functionName = sessionFactoryHelper
0738: .getFactory().getDialect()
0739: .getCurrentTimestampSQLFunctionName();
0740: versionValueNode = getASTFactory().create(
0741: HqlSqlTokenTypes.SQL_TOKEN, functionName);
0742: } else {
0743: throw new QueryException(
0744: "cannot handle version type ["
0745: + versionType
0746: + "] on bulk inserts with dialects not supporting parameters in insert-select statements");
0747: }
0748: }
0749:
0750: AST currentFirstSelectExprNode = selectClause
0751: .getFirstChild();
0752: selectClause.setFirstChild(versionValueNode);
0753: versionValueNode.setNextSibling(currentFirstSelectExprNode);
0754:
0755: insertStatement.getIntoClause().prependVersionColumnSpec();
0756: }
0757:
0758: if (insertStatement.getIntoClause().isDiscriminated()) {
0759: String sqlValue = insertStatement.getIntoClause()
0760: .getQueryable().getDiscriminatorSQLValue();
0761: AST discrimValue = getASTFactory().create(
0762: HqlSqlTokenTypes.SQL_TOKEN, sqlValue);
0763: insertStatement.getSelectClause().addChild(discrimValue);
0764: }
0765:
0766: }
0767:
0768: private boolean isDatabaseGeneratedTimestamp(Type type) {
0769: // currently only the Hibernate-supplied DbTimestampType is supported here
0770: return DbTimestampType.class.isAssignableFrom(type.getClass());
0771: }
0772:
0773: private boolean isIntegral(Type type) {
0774: return Long.class.isAssignableFrom(type.getReturnedClass())
0775: || Integer.class.isAssignableFrom(type
0776: .getReturnedClass())
0777: || long.class.isAssignableFrom(type.getReturnedClass())
0778: || int.class.isAssignableFrom(type.getReturnedClass());
0779: }
0780:
0781: private void useSelectClause(AST select) throws SemanticException {
0782: selectClause = (SelectClause) select;
0783: selectClause.initializeExplicitSelectClause(currentFromClause);
0784: }
0785:
0786: private void createSelectClauseFromFromClause(QueryNode qn)
0787: throws SemanticException {
0788: AST select = astFactory.create(SELECT_CLAUSE,
0789: "{derived select clause}");
0790: AST sibling = qn.getFromClause();
0791: qn.setFirstChild(select);
0792: select.setNextSibling(sibling);
0793: selectClause = (SelectClause) select;
0794: selectClause.initializeDerivedSelectClause(currentFromClause);
0795: if (log.isDebugEnabled()) {
0796: log.debug("Derived SELECT clause created.");
0797: }
0798: }
0799:
0800: protected void resolve(AST node) throws SemanticException {
0801: if (node != null) {
0802: // This is called when it's time to fully resolve a path expression.
0803: ResolvableNode r = (ResolvableNode) node;
0804: if (isInFunctionCall()) {
0805: r.resolveInFunctionCall(false, true);
0806: } else {
0807: r.resolve(false, true); // Generate implicit joins, only if necessary.
0808: }
0809: }
0810: }
0811:
0812: protected void resolveSelectExpression(AST node)
0813: throws SemanticException {
0814: // This is called when it's time to fully resolve a path expression.
0815: int type = node.getType();
0816: switch (type) {
0817: case DOT:
0818: DotNode dot = (DotNode) node;
0819: dot.resolveSelectExpression();
0820: break;
0821: case ALIAS_REF:
0822: // Notify the FROM element that it is being referenced by the select.
0823: FromReferenceNode aliasRefNode = (FromReferenceNode) node;
0824: //aliasRefNode.resolve( false, false, aliasRefNode.getText() ); //TODO: is it kosher to do it here?
0825: aliasRefNode.resolve(false, false); //TODO: is it kosher to do it here?
0826: FromElement fromElement = aliasRefNode.getFromElement();
0827: if (fromElement != null) {
0828: fromElement.setIncludeSubclasses(true);
0829: }
0830: default:
0831: break;
0832: }
0833: }
0834:
0835: protected void beforeSelectClause() throws SemanticException {
0836: // Turn off includeSubclasses on all FromElements.
0837: FromClause from = getCurrentFromClause();
0838: List fromElements = from.getFromElements();
0839: for (Iterator iterator = fromElements.iterator(); iterator
0840: .hasNext();) {
0841: FromElement fromElement = (FromElement) iterator.next();
0842: fromElement.setIncludeSubclasses(false);
0843: }
0844: }
0845:
0846: protected AST generatePositionalParameter(AST inputNode)
0847: throws SemanticException {
0848: if (namedParameters.size() > 0) {
0849: throw new SemanticException(
0850: "cannot define positional parameter after any named parameters have been defined");
0851: }
0852: ParameterNode parameter = (ParameterNode) astFactory.create(
0853: PARAM, "?");
0854: PositionalParameterSpecification paramSpec = new PositionalParameterSpecification(
0855: ((Node) inputNode).getLine(), ((Node) inputNode)
0856: .getColumn(), positionalParameterCount++);
0857: parameter.setHqlParameterSpecification(paramSpec);
0858: parameters.add(paramSpec);
0859: return parameter;
0860: }
0861:
0862: protected AST generateNamedParameter(AST delimiterNode, AST nameNode)
0863: throws SemanticException {
0864: String name = nameNode.getText();
0865: trackNamedParameterPositions(name);
0866:
0867: // create the node initially with the param name so that it shows
0868: // appropriately in the "original text" attribute
0869: ParameterNode parameter = (ParameterNode) astFactory.create(
0870: NAMED_PARAM, name);
0871: parameter.setText("?");
0872:
0873: NamedParameterSpecification paramSpec = new NamedParameterSpecification(
0874: ((Node) delimiterNode).getLine(),
0875: ((Node) delimiterNode).getColumn(), name);
0876: parameter.setHqlParameterSpecification(paramSpec);
0877: parameters.add(paramSpec);
0878: return parameter;
0879: }
0880:
0881: private void trackNamedParameterPositions(String name) {
0882: Integer loc = new Integer(parameterCount++);
0883: Object o = namedParameters.get(name);
0884: if (o == null) {
0885: namedParameters.put(name, loc);
0886: } else if (o instanceof Integer) {
0887: ArrayList list = new ArrayList(4);
0888: list.add(o);
0889: list.add(loc);
0890: namedParameters.put(name, list);
0891: } else {
0892: ((ArrayList) o).add(loc);
0893: }
0894: }
0895:
0896: protected void processConstant(AST constant)
0897: throws SemanticException {
0898: literalProcessor.processConstant(constant, true); // Use the delegate, resolve identifiers as FROM element aliases.
0899: }
0900:
0901: protected void processBoolean(AST constant)
0902: throws SemanticException {
0903: literalProcessor.processBoolean(constant); // Use the delegate.
0904: }
0905:
0906: protected void processNumericLiteral(AST literal) {
0907: literalProcessor.processNumeric(literal);
0908: }
0909:
0910: protected void processIndex(AST indexOp) throws SemanticException {
0911: IndexNode indexNode = (IndexNode) indexOp;
0912: indexNode.resolve(true, true);
0913: }
0914:
0915: protected void processFunction(AST functionCall, boolean inSelect)
0916: throws SemanticException {
0917: MethodNode methodNode = (MethodNode) functionCall;
0918: methodNode.resolve(inSelect);
0919: }
0920:
0921: protected void processConstructor(AST constructor)
0922: throws SemanticException {
0923: ConstructorNode constructorNode = (ConstructorNode) constructor;
0924: constructorNode.prepare();
0925: }
0926:
0927: protected void setAlias(AST selectExpr, AST ident) {
0928: ((SelectExpression) selectExpr).setAlias(ident.getText());
0929: }
0930:
0931: /**
0932: * Returns the locations of all occurrences of the named parameter.
0933: */
0934: public int[] getNamedParameterLocations(String name)
0935: throws QueryException {
0936: Object o = namedParameters.get(name);
0937: if (o == null) {
0938: QueryException qe = new QueryException(
0939: QueryTranslator.ERROR_NAMED_PARAMETER_DOES_NOT_APPEAR
0940: + name);
0941: qe.setQueryString(queryTranslatorImpl.getQueryString());
0942: throw qe;
0943: }
0944: if (o instanceof Integer) {
0945: return new int[] { ((Integer) o).intValue() };
0946: } else {
0947: return ArrayHelper.toIntArray((ArrayList) o);
0948: }
0949: }
0950:
0951: public void addQuerySpaces(Serializable[] spaces) {
0952: for (int i = 0; i < spaces.length; i++) {
0953: querySpaces.add(spaces[i]);
0954: }
0955: }
0956:
0957: public Type[] getReturnTypes() {
0958: return selectClause.getQueryReturnTypes();
0959: }
0960:
0961: public String[] getReturnAliases() {
0962: return selectClause.getQueryReturnAliases();
0963: }
0964:
0965: public SelectClause getSelectClause() {
0966: return selectClause;
0967: }
0968:
0969: public FromClause getFinalFromClause() {
0970: FromClause top = currentFromClause;
0971: while (top.getParentFromClause() != null) {
0972: top = top.getParentFromClause();
0973: }
0974: return top;
0975: }
0976:
0977: public boolean isShallowQuery() {
0978: // select clauses for insert statements should alwasy be treated as shallow
0979: return getStatementType() == INSERT
0980: || queryTranslatorImpl.isShallowQuery();
0981: }
0982:
0983: public Map getEnabledFilters() {
0984: return queryTranslatorImpl.getEnabledFilters();
0985: }
0986:
0987: public LiteralProcessor getLiteralProcessor() {
0988: return literalProcessor;
0989: }
0990:
0991: public ASTPrinter getASTPrinter() {
0992: return printer;
0993: }
0994:
0995: public ArrayList getParameters() {
0996: return parameters;
0997: }
0998:
0999: public int getNumberOfParametersInSetClause() {
1000: return numberOfParametersInSetClause;
1001: }
1002:
1003: protected void evaluateAssignment(AST eq) throws SemanticException {
1004: prepareLogicOperator(eq);
1005: Queryable persister = getCurrentFromClause().getFromElement()
1006: .getQueryable();
1007: evaluateAssignment(eq, persister, -1);
1008: }
1009:
1010: private void evaluateAssignment(AST eq, Queryable persister,
1011: int targetIndex) {
1012: if (persister.isMultiTable()) {
1013: // no need to even collect this information if the persister is considered multi-table
1014: AssignmentSpecification specification = new AssignmentSpecification(
1015: eq, persister);
1016: if (targetIndex >= 0) {
1017: assignmentSpecifications
1018: .add(targetIndex, specification);
1019: } else {
1020: assignmentSpecifications.add(specification);
1021: }
1022: numberOfParametersInSetClause += specification
1023: .getParameters().length;
1024: }
1025: }
1026:
1027: public ArrayList getAssignmentSpecifications() {
1028: return assignmentSpecifications;
1029: }
1030:
1031: protected AST createIntoClause(String path, AST propertySpec)
1032: throws SemanticException {
1033: Queryable persister = (Queryable) getSessionFactoryHelper()
1034: .requireClassPersister(path);
1035:
1036: IntoClause intoClause = (IntoClause) getASTFactory().create(
1037: INTO, persister.getEntityName());
1038: intoClause.setFirstChild(propertySpec);
1039: intoClause.initialize(persister);
1040:
1041: addQuerySpaces(persister.getQuerySpaces());
1042:
1043: return intoClause;
1044: }
1045:
1046: protected void prepareVersioned(AST updateNode, AST versioned)
1047: throws SemanticException {
1048: UpdateStatement updateStatement = (UpdateStatement) updateNode;
1049: FromClause fromClause = updateStatement.getFromClause();
1050: if (versioned != null) {
1051: // Make sure that the persister is versioned
1052: Queryable persister = fromClause.getFromElement()
1053: .getQueryable();
1054: if (!persister.isVersioned()) {
1055: throw new SemanticException(
1056: "increment option specified for update of non-versioned entity");
1057: }
1058:
1059: VersionType versionType = persister.getVersionType();
1060: if (versionType instanceof UserVersionType) {
1061: throw new SemanticException(
1062: "user-defined version types not supported for increment option");
1063: }
1064:
1065: AST eq = getASTFactory().create(HqlSqlTokenTypes.EQ, "=");
1066: AST versionPropertyNode = generateVersionPropertyNode(persister);
1067:
1068: eq.setFirstChild(versionPropertyNode);
1069:
1070: AST versionIncrementNode = null;
1071: if (Date.class.isAssignableFrom(versionType
1072: .getReturnedClass())) {
1073: versionIncrementNode = getASTFactory().create(
1074: HqlSqlTokenTypes.PARAM, "?");
1075: ParameterSpecification paramSpec = new VersionTypeSeedParameterSpecification(
1076: versionType);
1077: ((ParameterNode) versionIncrementNode)
1078: .setHqlParameterSpecification(paramSpec);
1079: parameters.add(0, paramSpec);
1080: } else {
1081: // Not possible to simply re-use the versionPropertyNode here as it causes
1082: // OOM errors due to circularity :(
1083: versionIncrementNode = getASTFactory().create(
1084: HqlSqlTokenTypes.PLUS, "+");
1085: versionIncrementNode
1086: .setFirstChild(generateVersionPropertyNode(persister));
1087: versionIncrementNode.addChild(getASTFactory().create(
1088: HqlSqlTokenTypes.IDENT, "1"));
1089: }
1090:
1091: eq.addChild(versionIncrementNode);
1092:
1093: evaluateAssignment(eq, persister, 0);
1094:
1095: AST setClause = updateStatement.getSetClause();
1096: AST currentFirstSetElement = setClause.getFirstChild();
1097: setClause.setFirstChild(eq);
1098: eq.setNextSibling(currentFirstSetElement);
1099: }
1100: }
1101:
1102: private AST generateVersionPropertyNode(Queryable persister)
1103: throws SemanticException {
1104: String versionPropertyName = persister.getPropertyNames()[persister
1105: .getVersionProperty()];
1106: AST versionPropertyRef = getASTFactory().create(
1107: HqlSqlTokenTypes.IDENT, versionPropertyName);
1108: AST versionPropertyNode = lookupNonQualifiedProperty(versionPropertyRef);
1109: resolve(versionPropertyNode);
1110: return versionPropertyNode;
1111: }
1112:
1113: protected void prepareLogicOperator(AST operator)
1114: throws SemanticException {
1115: ((OperatorNode) operator).initialize();
1116: }
1117:
1118: protected void prepareArithmeticOperator(AST operator)
1119: throws SemanticException {
1120: ((OperatorNode) operator).initialize();
1121: }
1122:
1123: public static void panic() {
1124: throw new QueryException("TreeWalker: panic");
1125: }
1126: }
|