Open Source Repository

Home /hibernate/hibernate-3.2.7.ga | Repository Home


org/hibernate/hql/ast/tree/FromElementFactory.java
// $Id: FromElementFactory.java 9586 2006-03-09 21:11:44Z [email protected] $
package org.hibernate.hql.ast.tree;

import org.hibernate.engine.JoinSequence;
import org.hibernate.hql.antlr.SqlTokenTypes;
import org.hibernate.hql.ast.util.ASTUtil;
import org.hibernate.hql.ast.util.AliasGenerator;
import org.hibernate.hql.ast.util.PathHelper;
import org.hibernate.hql.ast.util.SessionFactoryHelper;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.sql.JoinFragment;
import org.hibernate.type.AssociationType;
import org.hibernate.type.CollectionType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
import org.hibernate.util.StringHelper;

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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Encapsulates the creation of FromElements and JoinSequences.
 *
 @author josh Oct 12, 2004 4:54:25 AM
 */
class FromElementFactory implements SqlTokenTypes {

  private static final Log log = LogFactory.getLogFromElementFactory.class );

  private FromClause fromClause;
  private FromElement origin;
  private String path;

  private String classAlias;
  private String[] columns;
  private boolean implied;
  private boolean inElementsFunction;
  private boolean collection;
  private QueryableCollection queryableCollection;
  private CollectionType collectionType;

  /**
   * Creates entity from elements.
   */
  public FromElementFactory(FromClause fromClause, FromElement origin, String path) {
    this.fromClause = fromClause;
    this.origin = origin;
    this.path = path;
    collection = false;
  }

  /**
   * Creates collection from elements.
   */
  public FromElementFactory(
          FromClause fromClause,
          FromElement origin,
          String path,
          String classAlias,
          String[] columns,
          boolean implied) {
    thisfromClause, origin, path );
    this.classAlias = classAlias;
    this.columns = columns;
    this.implied = implied;
    collection = true;
  }

  FromElement addFromElement() throws SemanticException {
    FromClause parentFromClause = fromClause.getParentFromClause();
    if parentFromClause != null ) {
      // Look up class name using the first identifier in the path.
      String pathAlias = PathHelper.getAliaspath );
      FromElement parentFromElement = parentFromClause.getFromElementpathAlias );
      if parentFromElement != null ) {
        return createFromElementInSubselectpath, pathAlias, parentFromElement, classAlias );
      }
    }

    EntityPersister entityPersister = fromClause.getSessionFactoryHelper().requireClassPersisterpath );

    FromElement elem = createAndAddFromElementpath,
        classAlias,
        entityPersister,
        EntityType ) ( ( Queryable entityPersister ).getType(),
        null );

    // Add to the query spaces.
    fromClause.getWalker().addQuerySpacesentityPersister.getQuerySpaces() );

    return elem;
  }

  private FromElement createFromElementInSubselect(
          String path,
          String pathAlias,
          FromElement parentFromElement,
          String classAliasthrows SemanticException {
    if log.isDebugEnabled() ) {
      log.debug"createFromElementInSubselect() : path = " + path );
    }
    // Create an DotNode AST for the path and resolve it.
    FromElement fromElement = evaluateFromElementPathpath, classAlias );
    EntityPersister entityPersister = fromElement.getEntityPersister();

    // If the first identifier in the path referrs to the class alias (not the class name), then this
    // is a correlated subselect.  If it's a correlated sub-select, use the existing table alias.  Otherwise
    // generate a new one.
    String tableAlias = null;
    boolean correlatedSubselect = pathAlias.equalsparentFromElement.getClassAlias() );
    if correlatedSubselect ) {
      tableAlias = fromElement.getTableAlias();
    }
    else {
      tableAlias = null;
    }

    // If the from element isn't in the same clause, create a new from element.
    if fromElement.getFromClause() != fromClause ) {
      if log.isDebugEnabled() ) {
        log.debug"createFromElementInSubselect() : creating a new FROM element..." );
      }
      fromElement = createFromElemententityPersister );
      initializeAndAddFromElementfromElement,
          path,
          classAlias,
          entityPersister,
          EntityType ) ( ( Queryable entityPersister ).getType(),
          tableAlias
      );
    }
    if log.isDebugEnabled() ) {
      log.debug"createFromElementInSubselect() : " + path + " -> " + fromElement );
    }
    return fromElement;
  }

  private FromElement evaluateFromElementPath(String path, String classAliasthrows SemanticException {
    ASTFactory factory = fromClause.getASTFactory();
    FromReferenceNode pathNode = FromReferenceNode PathHelper.parsePathpath, factory );
    pathNode.recursiveResolveFromReferenceNode.ROOT_LEVEL, // This is the root level node.
        false, // Generate an explicit from clause at the root.
        classAlias,
            null
    );
    if pathNode.getImpliedJoin() != null ) {
      return pathNode.getImpliedJoin();
    }
    else {
      return pathNode.getFromElement();
    }
  }

  FromElement createCollectionElementsJoin(
          QueryableCollection queryableCollection,
          String collectionNamethrows SemanticException {
    JoinSequence collectionJoinSequence = fromClause.getSessionFactoryHelper()
            .createCollectionJoinSequencequeryableCollection, collectionName );
    this.queryableCollection = queryableCollection;
    return createCollectionJoincollectionJoinSequence, null );
  }

  FromElement createCollection(
          QueryableCollection queryableCollection,
          String role,
          int joinType,
          boolean fetchFlag,
          boolean indexed)
      throws SemanticException {
    if !collection ) {
      throw new IllegalStateException"FromElementFactory not initialized for collections!" );
    }
    this.inElementsFunction = indexed;
    FromElement elem;
    this.queryableCollection = queryableCollection;
    collectionType = queryableCollection.getCollectionType();
    String roleAlias = fromClause.getAliasGenerator().createNamerole );

    // Correlated subqueries create 'special' implied from nodes
    // because correlated subselects can't use an ANSI-style join
    boolean explicitSubqueryFromElement = fromClause.isSubQuery() && !implied;
    if explicitSubqueryFromElement ) {
      String pathRoot = StringHelper.rootpath );
      FromElement origin = fromClause.getFromElementpathRoot );
      if origin == null || origin.getFromClause() != fromClause ) {
        implied = true;
      }
    }

    // super-duper-classic-parser-regression-testing-mojo-magic...
    if explicitSubqueryFromElement && DotNode.useThetaStyleImplicitJoins ) {
      implied = true;
    }

    Type elementType = queryableCollection.getElementType();
    if elementType.isEntityType() ) {       // A collection of entities...
      elem = createEntityAssociationrole, roleAlias, joinType );
    }
    else if elementType.isComponentType() ) {    // A collection of components...
      JoinSequence joinSequence = createJoinSequenceroleAlias, joinType );
      elem = createCollectionJoinjoinSequence, roleAlias );
    }
    else {                      // A collection of scalar elements...
      JoinSequence joinSequence = createJoinSequenceroleAlias, joinType );
      elem = createCollectionJoinjoinSequence, roleAlias );
    }

    elem.setRolerole );
    elem.setQueryableCollectionqueryableCollection );
    // Don't include sub-classes for implied collection joins or subquery joins.
    if implied ) {
      elem.setIncludeSubclassesfalse );
    }

    if explicitSubqueryFromElement ) {
      elem.setInProjectionListtrue );  // Treat explict from elements in sub-queries properly.
    }

    if fetchFlag ) {
      elem.setFetchtrue );
    }
    return elem;
  }

  FromElement createEntityJoin(
          String entityClass,
          String tableAlias,
          JoinSequence joinSequence,
          boolean fetchFlag,
          boolean inFrom,
          EntityType typethrows SemanticException {
    FromElement elem = createJoinentityClass, tableAlias, joinSequence, type, false );
    elem.setFetchfetchFlag );
    EntityPersister entityPersister = elem.getEntityPersister();
    int numberOfTables = entityPersister.getQuerySpaces().length;
    if numberOfTables > && implied && !elem.useFromFragment() ) {
      if log.isDebugEnabled() ) {
        log.debug"createEntityJoin() : Implied multi-table entity join" );
      }
      elem.setUseFromFragmenttrue );
    }

    // If this is an implied join in a FROM clause, then use ANSI-style joining, and set the
    // flag on the FromElement that indicates that it was implied in the FROM clause itself.
    if implied && inFrom ) {
      joinSequence.setUseThetaStylefalse );
      elem.setUseFromFragmenttrue );
      elem.setImpliedInFromClausetrue );
    }
    if elem.getWalker().isSubQuery() ) {
      // two conditions where we need to transform this to a theta-join syntax:
      //      1) 'elem' is the "root from-element" in correlated subqueries
      //      2) The DotNode.useThetaStyleImplicitJoins has been set to true
      //          and 'elem' represents an implicit join
      if elem.getFromClause() != elem.getOrigin().getFromClause() ||
//              ( implied && DotNode.useThetaStyleImplicitJoins ) ) {
              DotNode.useThetaStyleImplicitJoins ) {
        // the "root from-element" in correlated subqueries do need this piece
        elem.setTypeFROM_FRAGMENT );
        joinSequence.setUseThetaStyletrue );
        elem.setUseFromFragmentfalse );
      }
    }

    return elem;
  }

  FromElement createElementJoin(QueryableCollection queryableCollectionthrows SemanticException {
    FromElement elem;

    implied = true//TODO: always true for now, but not if we later decide to support elements() in the from clause
    inElementsFunction = true;
    Type elementType = queryableCollection.getElementType();
    if !elementType.isEntityType() ) {
      throw new IllegalArgumentException"Cannot create element join for a collection of non-entities!" );
    }
    this.queryableCollection = queryableCollection;
    SessionFactoryHelper sfh = fromClause.getSessionFactoryHelper();
    FromElement destination = null;
    String tableAlias = null;
    EntityPersister entityPersister = queryableCollection.getElementPersister();
    tableAlias = fromClause.getAliasGenerator().createNameentityPersister.getEntityName() );
    String associatedEntityName = entityPersister.getEntityName();
    EntityPersister targetEntityPersister = sfh.requireClassPersisterassociatedEntityName );
    // Create the FROM element for the target (the elements of the collection).
    destination = createAndAddFromElement
        associatedEntityName,
        classAlias,
        targetEntityPersister,
        EntityType queryableCollection.getElementType(),
        tableAlias
      );
    // If the join is implied, then don't include sub-classes on the element.
    if implied ) {
      destination.setIncludeSubclassesfalse );
    }
    fromClause.addCollectionJoinFromElementByPathpath, destination );
//    origin.addDestination(destination);
    // Add the query spaces.
    fromClause.getWalker().addQuerySpacesentityPersister.getQuerySpaces() );

    CollectionType type = queryableCollection.getCollectionType();
    String role = type.getRole();
    String roleAlias = origin.getTableAlias();

    String[] targetColumns = sfh.getCollectionElementColumnsrole, roleAlias );
    AssociationType elementAssociationType = sfh.getElementAssociationTypetype );

    // Create the join element under the from element.
    int joinType = JoinFragment.INNER_JOIN;
    JoinSequence joinSequence = sfh.createJoinSequenceimplied, elementAssociationType, tableAlias, joinType, targetColumns );
    elem = initializeJoinpath, destination, joinSequence, targetColumns, origin, false );
    elem.setUseFromFragmenttrue );  // The associated entity is implied, but it must be included in the FROM.
    elem.setCollectionTableAliasroleAlias );  // The collection alias is the role.
    return elem;
  }

  private FromElement createCollectionJoin(JoinSequence collectionJoinSequence, String tableAliasthrows SemanticException {
    String text = queryableCollection.getTableName();
    AST ast = createFromElementtext );
    FromElement destination = FromElement ast;
    Type elementType = queryableCollection.getElementType();
    if elementType.isCollectionType() ) {
      throw new SemanticException"Collections of collections are not supported!" );
    }
    destination.initializeCollectionfromClause, classAlias, tableAlias );
    destination.setTypeJOIN_FRAGMENT );    // Tag this node as a JOIN.
    destination.setIncludeSubclassesfalse );  // Don't include subclasses in the join.
    destination.setCollectionJointrue );    // This is a clollection join.
    destination.setJoinSequencecollectionJoinSequence );
    destination.setOriginorigin, false );
    destination.setCollectionTableAlias(tableAlias);
//    origin.addDestination( destination );
// This was the cause of HHH-242
//    origin.setType( FROM_FRAGMENT );      // Set the parent node type so that the AST is properly formed.
    origin.setText"" );            // The destination node will have all the FROM text.
    origin.setCollectionJointrue );      // The parent node is a collection join too (voodoo - see JoinProcessor)
    fromClause.addCollectionJoinFromElementByPathpath, destination );
    fromClause.getWalker().addQuerySpacesqueryableCollection.getCollectionSpaces() );
    return destination;
  }

  private FromElement createEntityAssociation(
          String role,
          String roleAlias,
          int joinTypethrows SemanticException {
    FromElement elem;
    Queryable entityPersister = Queryable queryableCollection.getElementPersister();
    String associatedEntityName = entityPersister.getEntityName();
    // Get the class name of the associated entity.
    if queryableCollection.isOneToMany() ) {
      if log.isDebugEnabled() ) {
        log.debug"createEntityAssociation() : One to many - path = " + path + " role = " + role + " associatedEntityName = " + associatedEntityName );
      }
      JoinSequence joinSequence = createJoinSequenceroleAlias, joinType );

      elem = createJoinassociatedEntityName, roleAlias, joinSequence, EntityType queryableCollection.getElementType()false );
    }
    else {
      if log.isDebugEnabled() ) {
        log.debug"createManyToMany() : path = " + path + " role = " + role + " associatedEntityName = " + associatedEntityName );
      }
      elem = createManyToManyrole, associatedEntityName,
          roleAlias, entityPersister, EntityType queryableCollection.getElementType(), joinType );
      fromClause.getWalker().addQuerySpacesqueryableCollection.getCollectionSpaces() );
    }
    elem.setCollectionTableAliasroleAlias );
    return elem;
  }

  private FromElement createJoin(
          String entityClass,
          String tableAlias,
          JoinSequence joinSequence,
          EntityType type,
          boolean manyToManythrows SemanticException {
    //  origin, path, implied, columns, classAlias,
    EntityPersister entityPersister = fromClause.getSessionFactoryHelper().requireClassPersisterentityClass );
    FromElement destination = createAndAddFromElemententityClass,
        classAlias,
        entityPersister,
        type,
        tableAlias );
    return initializeJoinpath, destination, joinSequence, getColumns(), origin, manyToMany );
  }

  private FromElement createManyToMany(
          String role,
          String associatedEntityName,
          String roleAlias,
          Queryable entityPersister,
          EntityType type,
          int joinTypethrows SemanticException {
    FromElement elem;
    SessionFactoryHelper sfh = fromClause.getSessionFactoryHelper();
    if inElementsFunction /*implied*/ ) {
      // For implied many-to-many, just add the end join.
      JoinSequence joinSequence = createJoinSequenceroleAlias, joinType );
      elem = createJoinassociatedEntityName, roleAlias, joinSequence, type, true );
    }
    else {
      // For an explicit many-to-many relationship, add a second join from the intermediate 
      // (many-to-many) table to the destination table.  Also, make sure that the from element's 
      // idea of the destination is the destination table.
      String tableAlias = fromClause.getAliasGenerator().createNameentityPersister.getEntityName() );
      String[] secondJoinColumns = sfh.getCollectionElementColumnsrole, roleAlias );
      // Add the second join, the one that ends in the destination table.
      JoinSequence joinSequence = createJoinSequenceroleAlias, joinType );
      joinSequence.addJoinsfh.getElementAssociationTypecollectionType ), tableAlias, joinType, secondJoinColumns );
      elem = createJoinassociatedEntityName, tableAlias, joinSequence, type, false );
      elem.setUseFromFragmenttrue );
    }
    return elem;
  }

  private JoinSequence createJoinSequence(String roleAlias, int joinType) {
    SessionFactoryHelper sessionFactoryHelper = fromClause.getSessionFactoryHelper();
    String[] joinColumns = getColumns();
    if collectionType == null ) {
      throw new IllegalStateException"collectionType is null!" );
    }
    return sessionFactoryHelper.createJoinSequenceimplied, collectionType, roleAlias, joinType, joinColumns );
  }

  private FromElement createAndAddFromElement(
          String className,
          String classAlias,
          EntityPersister entityPersister,
          EntityType type,
          String tableAlias) {
    if !entityPersister instanceof Joinable ) ) {
      throw new IllegalArgumentException"EntityPersister " + entityPersister + " does not implement Joinable!" );
    }
    FromElement element = createFromElemententityPersister );
    initializeAndAddFromElementelement, className, classAlias, entityPersister, type, tableAlias );
    return element;
  }

  private void initializeAndAddFromElement(
          FromElement element,
          String className,
          String classAlias,
          EntityPersister entityPersister,
          EntityType type,
          String tableAlias) {
    if tableAlias == null ) {
      AliasGenerator aliasGenerator = fromClause.getAliasGenerator();
      tableAlias = aliasGenerator.createNameentityPersister.getEntityName() );
    }
    element.initializeEntityfromClause, className, entityPersister, type, classAlias, tableAlias );
  }

  private FromElement createFromElement(EntityPersister entityPersister) {
    Joinable joinable = Joinable entityPersister;
    String text = joinable.getTableName();
    AST ast = createFromElementtext );
    FromElement element = FromElement ast;
    return element;
  }

  private AST createFromElement(String text) {
    AST ast = ASTUtil.createfromClause.getASTFactory(),
        implied ? IMPLIED_FROM : FROM_FRAGMENT, // This causes the factory to instantiate the desired class.
        text );
    // Reset the node type, because the rest of the system is expecting FROM_FRAGMENT, all we wanted was
    // for the factory to create the right sub-class.  This might get reset again later on anyway to make the
    // SQL generation simpler.
    ast.setTypeFROM_FRAGMENT );
    return ast;
  }

  private FromElement initializeJoin(
          String path,
          FromElement destination,
          JoinSequence joinSequence,
          String[] columns,
          FromElement origin,
          boolean manyToMany) {
    destination.setTypeJOIN_FRAGMENT );
    destination.setJoinSequencejoinSequence );
    destination.setColumnscolumns );
    destination.setOriginorigin, manyToMany );
    fromClause.addJoinByPathMappath, destination );
    return destination;
  }

  private String[] getColumns() {
    if columns == null ) {
      throw new IllegalStateException"No foriegn key columns were supplied!" );
    }
    return columns;
  }
}