public override void PrepareForDot(string propertyName) { FromElement fromElement = FromElement; if (fromElement == null) { throw new InvalidOperationException("No FROM element for index operator!"); } IQueryableCollection queryableCollection = fromElement.QueryableCollection; if (queryableCollection != null && !queryableCollection.IsOneToMany) { FromReferenceNode collectionNode = ( FromReferenceNode )GetChild(0); String path = collectionNode.Path + "[]." + propertyName; if (Log.IsDebugEnabled) { Log.Debug("Creating join for many-to-many elements for " + path); } FromElementFactory factory = new FromElementFactory(fromElement.FromClause, fromElement, path); // This will add the new from element to the origin. FromElement elementJoin = factory.CreateElementJoin(queryableCollection); FromElement = elementJoin; } }
/// <summary> /// Adds a new from element to the from node. /// </summary> /// <param name="path">The reference to the class.</param> /// <param name="alias">The alias AST.</param> /// <returns>The new FROM element.</returns> public FromElement AddFromElement(string path, IASTNode alias) { // The path may be a reference to an alias defined in the parent query. string classAlias = (alias == null) ? null : alias.Text; CheckForDuplicateClassAlias(classAlias); var factory = new FromElementFactory(this, null, path, classAlias, null, false); return(factory.AddFromElement()); }
public override void ResolveIndex(IASTNode parent) { // An ident node can represent an index expression if the ident // represents a naked property ref // *Note: this makes the assumption (which is currently the case // in the hql-sql grammar) that the ident is first resolved // itself (addrExpr -> resolve()). The other option, if that // changes, is to call resolve from here; but it is // currently un-needed overhead. if (!(IsResolved && _nakedPropertyRef)) { throw new InvalidOperationException(); } string propertyName = OriginalText; if (!DataType.IsCollectionType) { throw new SemanticException("Collection expected; [" + propertyName + "] does not refer to a collection property"); } // TODO : most of below was taken verbatim from DotNode; should either delegate this logic or super-type it CollectionType type = (CollectionType)DataType; string role = type.Role; IQueryableCollection queryableCollection = SessionFactoryHelper.RequireQueryableCollection(role); string columnTableAlias = FromElement.TableAlias; FromElementFactory factory = new FromElementFactory( Walker.CurrentFromClause, FromElement, propertyName, null, FromElement.ToColumns(columnTableAlias, propertyName, false), true ); FromElement elem = factory.CreateCollection(queryableCollection, role, JoinType.InnerJoin, false, true); FromElement = elem; Walker.AddQuerySpaces(queryableCollection.CollectionSpaces); // Always add the collection's query spaces. }
/// <summary> /// Adds a new from element to the from node. /// </summary> /// <param name="path">The reference to the class.</param> /// <param name="alias">The alias AST.</param> /// <returns>The new FROM element.</returns> public FromElement AddFromElement(string path, IASTNode alias) { // The path may be a reference to an alias defined in the parent query. string classAlias = ( alias == null ) ? null : alias.Text; CheckForDuplicateClassAlias( classAlias ); var factory = new FromElementFactory(this, null, path, classAlias, null, false); return factory.AddFromElement(); }
private void DereferenceEntityJoin(string classAlias, EntityType propertyType, bool impliedJoin, IASTNode parent) { _dereferenceType = DerefEntity; if (Log.IsDebugEnabled) { Log.Debug("dereferenceEntityJoin() : generating join for " + _propertyName + " in " + FromElement.ClassName + " " + ((classAlias == null) ? "{no alias}" : "(" + classAlias + ")") + " parent = " + ASTUtil.GetDebugstring(parent) ); } // Create a new FROM node for the referenced class. string associatedEntityName = propertyType.GetAssociatedEntityName(); string tableAlias = AliasGenerator.CreateName(associatedEntityName); string[] joinColumns = GetColumns(); string joinPath = Path; if (impliedJoin && Walker.IsInFrom) { _joinType = Walker.ImpliedJoinType; } FromClause currentFromClause = Walker.CurrentFromClause; FromElement elem = currentFromClause.FindJoinByPath(joinPath); /////////////////////////////////////////////////////////////////////////////// // // This is the piece which recognizes the condition where an implicit join path // resolved earlier in a correlated subquery is now being referenced in the // outer query. For 3.0final, we just let this generate a second join (which // is exactly how the old parser handles this). Eventually we need to add this // logic back in and complete the logic in FromClause.promoteJoin; however, // FromClause.promoteJoin has its own difficulties (see the comments in // FromClause.promoteJoin). // // if ( elem == null ) { // // see if this joinPath has been used in a "child" FromClause, and if so // // promote that element to the outer query // FromClause currentNodeOwner = getFromElement().getFromClause(); // FromClause currentJoinOwner = currentNodeOwner.locateChildFromClauseWithJoinByPath( joinPath ); // if ( currentJoinOwner != null && currentNodeOwner != currentJoinOwner ) { // elem = currentJoinOwner.findJoinByPathLocal( joinPath ); // if ( elem != null ) { // currentFromClause.promoteJoin( elem ); // // EARLY EXIT!!! // return; // } // } // } // /////////////////////////////////////////////////////////////////////////////// bool found = elem != null; // even though we might find a pre-existing element by join path, for FromElements originating in a from-clause // we should only ever use the found element if the aliases match (null != null here). Implied joins are // always (?) ok to reuse. bool useFoundFromElement = found && (elem.IsImplied || (AreSame(classAlias, elem.ClassAlias))); if (!useFoundFromElement) { // If this is an implied join in a from element, then use the impled join type which is part of the // tree parser's state (set by the gramamar actions). JoinSequence joinSequence = SessionFactoryHelper .CreateJoinSequence(impliedJoin, propertyType, tableAlias, _joinType, joinColumns); FromElementFactory factory = new FromElementFactory( currentFromClause, GetLhs().FromElement, joinPath, classAlias, joinColumns, impliedJoin ); elem = factory.CreateEntityJoin( associatedEntityName, tableAlias, joinSequence, _fetch, Walker.IsInFrom, propertyType ); } else { currentFromClause.AddDuplicateAlias(classAlias, elem); } SetImpliedJoin(elem); Walker.AddQuerySpaces(elem.EntityPersister.QuerySpaces); FromElement = elem; // This 'dot' expression now refers to the resulting from element. }
private void DereferenceCollection(CollectionType collectionType, bool implicitJoin, bool indexed, string classAlias) { _dereferenceType = DerefCollection; string role = collectionType.Role; //foo.bars.size (also handles deprecated stuff like foo.bars.maxelement for backwardness) IASTNode sibling = NextSibling; bool isSizeProperty = sibling != null && CollectionProperties.IsAnyCollectionProperty(sibling.Text); if (isSizeProperty) { indexed = true; //yuck!} } IQueryableCollection queryableCollection = SessionFactoryHelper.RequireQueryableCollection(role); string propName = Path; FromClause currentFromClause = Walker.CurrentFromClause; if (Walker.StatementType != HqlSqlWalker.SELECT && indexed && classAlias == null) { // should indicate that we are processing an INSERT/UPDATE/DELETE // query with a subquery implied via a collection property // function. Here, we need to use the table name itself as the // qualification alias. // TODO : verify this works for all databases... // TODO : is this also the case in non-"indexed" scenarios? string alias = GetLhs().FromElement.Queryable.TableName; _columns = FromElement.ToColumns(alias, _propertyPath, false, true); } //We do not look for an existing join on the same path, because //it makes sense to join twice on the same collection role FromElementFactory factory = new FromElementFactory( currentFromClause, GetLhs().FromElement, propName, classAlias, GetColumns(), implicitJoin ); FromElement elem = factory.CreateCollection(queryableCollection, role, _joinType, _fetch, indexed); if (Log.IsDebugEnabled) { Log.Debug("dereferenceCollection() : Created new FROM element for " + propName + " : " + elem); } SetImpliedJoin(elem); FromElement = elem; // This 'dot' expression now refers to the resulting from element. if (isSizeProperty) { elem.Text = ""; elem.UseWhereFragment = false; } if (!implicitJoin) { IEntityPersister entityPersister = elem.EntityPersister; if (entityPersister != null) { Walker.AddQuerySpaces(entityPersister.QuerySpaces); } } Walker.AddQuerySpaces(queryableCollection.CollectionSpaces); // Always add the collection's query spaces. }
void CreateFromJoinElement( IASTNode path, IASTNode alias, int joinType, IASTNode fetchNode, IASTNode propertyFetch, IASTNode with) { bool 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.Type != DOT ) { throw new SemanticException( "Path expected for join!" ); } DotNode dot = ( DotNode ) path; //JoinType hibernateJoinType = JoinProcessor.ToHibernateJoinType( joinType ); JoinType hibernateJoinType = _impliedJoinType; dot.JoinType = hibernateJoinType; // Tell the dot node about the join type. dot.Fetch = 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.Text ); FromElement fromElement; if (dot.DataType != null && dot.DataType.IsComponentType) { var factory = new FromElementFactory(CurrentFromClause, dot.GetLhs().FromElement, dot.PropertyPath, alias == null ? null : alias.Text, null, false); fromElement = factory.CreateComponentJoin((ComponentType) dot.DataType); } else { fromElement = dot.GetImpliedJoin(); if (fromElement == null) { throw new InvalidPathException("Invalid join: " + dot.Path); } 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() : " + _printer.ShowAsString( fromElement, "-- join tree --" ) ); } }
public override void Resolve(bool generateJoin, bool implicitJoin, string classAlias, IASTNode parent) { if (IsResolved) { return; } FromReferenceNode collectionNode = ( FromReferenceNode )GetChild(0); SessionFactoryHelperExtensions sessionFactoryHelper = SessionFactoryHelper; collectionNode.ResolveIndex(this); // Fully resolve the map reference, create implicit joins. IType type = collectionNode.DataType; if (!type.IsCollectionType) { throw new SemanticException("The [] operator cannot be applied to type " + type); } string collectionRole = (( CollectionType )type).Role; IQueryableCollection queryableCollection = sessionFactoryHelper.RequireQueryableCollection(collectionRole); if (!queryableCollection.HasIndex) { throw new QueryException("unindexed fromElement before []: " + collectionNode.Path); } // Generate the inner join -- The elements need to be joined to the collection they are in. FromElement fromElement = collectionNode.FromElement; String elementTable = fromElement.TableAlias; FromClause fromClause = fromElement.FromClause; String path = collectionNode.Path; FromElement elem = fromClause.FindCollectionJoin(path); if (elem == null) { FromElementFactory factory = new FromElementFactory(fromClause, fromElement, path); elem = factory.CreateCollectionElementsJoin(queryableCollection, elementTable); if (Log.IsDebugEnabled) { Log.Debug("No FROM element found for the elements of collection join path " + path + ", created " + elem); } } else { if (Log.IsDebugEnabled) { Log.Debug("FROM element found for collection join path " + path); } } // The 'from element' that represents the elements of the collection. FromElement = fromElement; // Add the condition to the join sequence that qualifies the indexed element. IASTNode selector = GetChild(1); if (selector == null) { throw new QueryException("No index value!"); } // Sometimes use the element table alias, sometimes use the... umm... collection table alias (many to many) String collectionTableAlias = elementTable; if (elem.CollectionTableAlias != null) { collectionTableAlias = elem.CollectionTableAlias; } // TODO: get SQL rendering out of here, create an AST for the join expressions. // Use the SQL generator grammar to generate the SQL text for the index expression. JoinSequence joinSequence = fromElement.JoinSequence; string[] indexCols = queryableCollection.IndexColumnNames; if (indexCols.Length != 1) { throw new QueryException("composite-index appears in []: " + collectionNode.Path); } SqlGenerator gen = new SqlGenerator(SessionFactoryHelper.Factory, new CommonTreeNodeStream(selector)); try { gen.simpleExpr(); //TODO: used to be exprNoParens! was this needed? } catch (RecognitionException e) { throw new QueryException(e.Message, e); } string selectorExpression = gen.GetSQL().ToString(); joinSequence.AddCondition(new SqlString(collectionTableAlias + '.' + indexCols[0] + " = " + selectorExpression)); //joinSequence.AddCondition(collectionTableAlias, new string[] { indexCols[0] }, selectorExpression, false); IList <IParameterSpecification> paramSpecs = gen.GetCollectedParameters(); if (paramSpecs != null) { switch (paramSpecs.Count) { case 0: // nothing to do break; case 1: IParameterSpecification paramSpec = paramSpecs[0]; paramSpec.ExpectedType = queryableCollection.IndexType; fromElement.SetIndexCollectionSelectorParamSpec(paramSpec); break; default: fromElement.SetIndexCollectionSelectorParamSpec( new AggregatedIndexCollectionSelectorParameterSpecifications(paramSpecs) ); break; } } // Now, set the text for this node. It should be the element columns. String[] elementColumns = queryableCollection.GetElementColumnNames(elementTable); Text = elementColumns[0]; IsResolved = true; }
private void DereferenceEntityJoin(string classAlias, EntityType propertyType, bool impliedJoin, IASTNode parent) { _dereferenceType = DerefEntity; if ( Log.IsDebugEnabled ) { Log.Debug( "dereferenceEntityJoin() : generating join for " + _propertyName + " in " + FromElement.ClassName + " " + ( ( classAlias == null ) ? "{no alias}" : "(" + classAlias + ")" ) + " parent = " + ASTUtil.GetDebugstring( parent ) ); } // Create a new FROM node for the referenced class. string associatedEntityName = propertyType.GetAssociatedEntityName(); string tableAlias = AliasGenerator.CreateName( associatedEntityName ); string[] joinColumns = GetColumns(); string joinPath = Path; if ( impliedJoin && Walker.IsInFrom ) { _joinType = Walker.ImpliedJoinType; } FromClause currentFromClause = Walker.CurrentFromClause; FromElement elem = currentFromClause.FindJoinByPath( joinPath ); /////////////////////////////////////////////////////////////////////////////// // // This is the piece which recognizes the condition where an implicit join path // resolved earlier in a correlated subquery is now being referenced in the // outer query. For 3.0final, we just let this generate a second join (which // is exactly how the old parser handles this). Eventually we need to add this // logic back in and complete the logic in FromClause.promoteJoin; however, // FromClause.promoteJoin has its own difficulties (see the comments in // FromClause.promoteJoin). // // if ( elem == null ) { // // see if this joinPath has been used in a "child" FromClause, and if so // // promote that element to the outer query // FromClause currentNodeOwner = getFromElement().getFromClause(); // FromClause currentJoinOwner = currentNodeOwner.locateChildFromClauseWithJoinByPath( joinPath ); // if ( currentJoinOwner != null && currentNodeOwner != currentJoinOwner ) { // elem = currentJoinOwner.findJoinByPathLocal( joinPath ); // if ( elem != null ) { // currentFromClause.promoteJoin( elem ); // // EARLY EXIT!!! // return; // } // } // } // /////////////////////////////////////////////////////////////////////////////// bool found = elem != null; // even though we might find a pre-existing element by join path, for FromElements originating in a from-clause // we should only ever use the found element if the aliases match (null != null here). Implied joins are // always (?) ok to reuse. bool useFoundFromElement = found && ( elem.IsImplied || ( AreSame(classAlias, elem.ClassAlias ) ) ); if ( ! useFoundFromElement ) { // If this is an implied join in a from element, then use the impled join type which is part of the // tree parser's state (set by the gramamar actions). JoinSequence joinSequence = SessionFactoryHelper .CreateJoinSequence( impliedJoin, propertyType, tableAlias, _joinType, joinColumns ); FromElementFactory factory = new FromElementFactory( currentFromClause, GetLhs().FromElement, joinPath, classAlias, joinColumns, impliedJoin ); elem = factory.CreateEntityJoin( associatedEntityName, tableAlias, joinSequence, _fetch, Walker.IsInFrom, propertyType ); } else { currentFromClause.AddDuplicateAlias(classAlias, elem); } SetImpliedJoin( elem ); Walker.AddQuerySpaces( elem.EntityPersister.QuerySpaces ); FromElement = elem; // This 'dot' expression now refers to the resulting from element. }
private void DereferenceCollection(CollectionType collectionType, bool implicitJoin, bool indexed, string classAlias) { _dereferenceType = DerefCollection; string role = collectionType.Role; //foo.bars.size (also handles deprecated stuff like foo.bars.maxelement for backwardness) IASTNode sibling = NextSibling; bool isSizeProperty = sibling != null && CollectionProperties.IsAnyCollectionProperty( sibling.Text ); if (isSizeProperty) { indexed = true; //yuck!} } IQueryableCollection queryableCollection = SessionFactoryHelper.RequireQueryableCollection( role ); string propName = Path; FromClause currentFromClause = Walker.CurrentFromClause; if ( Walker.StatementType != HqlSqlWalker.SELECT && indexed && classAlias == null ) { // should indicate that we are processing an INSERT/UPDATE/DELETE // query with a subquery implied via a collection property // function. Here, we need to use the table name itself as the // qualification alias. // TODO : verify this works for all databases... // TODO : is this also the case in non-"indexed" scenarios? string alias = GetLhs().FromElement.Queryable.TableName; _columns = FromElement.ToColumns(alias, _propertyPath, false, true); } //We do not look for an existing join on the same path, because //it makes sense to join twice on the same collection role FromElementFactory factory = new FromElementFactory( currentFromClause, GetLhs().FromElement, propName, classAlias, GetColumns(), implicitJoin ); FromElement elem = factory.CreateCollection( queryableCollection, role, _joinType, _fetch, indexed ); if ( Log.IsDebugEnabled ) { Log.Debug( "dereferenceCollection() : Created new FROM element for " + propName + " : " + elem ); } SetImpliedJoin( elem ); FromElement = elem; // This 'dot' expression now refers to the resulting from element. if ( isSizeProperty ) { elem.Text = ""; elem.UseWhereFragment = false; } if ( !implicitJoin ) { IEntityPersister entityPersister = elem.EntityPersister; if ( entityPersister != null ) { Walker.AddQuerySpaces( entityPersister.QuerySpaces ); } } Walker.AddQuerySpaces( queryableCollection.CollectionSpaces ); // Always add the collection's query spaces. }
public override void Resolve(bool generateJoin, bool implicitJoin, string classAlias, IASTNode parent) { if (IsResolved) { return; } FromReferenceNode collectionNode = ( FromReferenceNode ) GetChild(0); SessionFactoryHelperExtensions sessionFactoryHelper = SessionFactoryHelper; collectionNode.ResolveIndex( this ); // Fully resolve the map reference, create implicit joins. IType type = collectionNode.DataType; if ( !type.IsCollectionType ) { throw new SemanticException( "The [] operator cannot be applied to type " + type); } string collectionRole = ( ( CollectionType ) type ).Role; IQueryableCollection queryableCollection = sessionFactoryHelper.RequireQueryableCollection( collectionRole ); if ( !queryableCollection.HasIndex ) { throw new QueryException( "unindexed fromElement before []: " + collectionNode.Path ); } // Generate the inner join -- The elements need to be joined to the collection they are in. FromElement fromElement = collectionNode.FromElement; String elementTable = fromElement.TableAlias; FromClause fromClause = fromElement.FromClause; String path = collectionNode.Path; FromElement elem = fromClause.FindCollectionJoin( path ); if ( elem == null ) { FromElementFactory factory = new FromElementFactory( fromClause, fromElement, path ); elem = factory.CreateCollectionElementsJoin( queryableCollection, elementTable ); if ( Log.IsDebugEnabled ) { Log.Debug( "No FROM element found for the elements of collection join path " + path + ", created " + elem ); } } else { if ( Log.IsDebugEnabled ) { Log.Debug( "FROM element found for collection join path " + path ); } } // The 'from element' that represents the elements of the collection. FromElement = fromElement; // Add the condition to the join sequence that qualifies the indexed element. IASTNode selector = GetChild(1); if ( selector == null ) { throw new QueryException( "No index value!" ); } // Sometimes use the element table alias, sometimes use the... umm... collection table alias (many to many) String collectionTableAlias = elementTable; if ( elem.CollectionTableAlias != null ) { collectionTableAlias = elem.CollectionTableAlias; } // TODO: get SQL rendering out of here, create an AST for the join expressions. // Use the SQL generator grammar to generate the SQL text for the index expression. JoinSequence joinSequence = fromElement.JoinSequence; string[] indexCols = queryableCollection.IndexColumnNames; if ( indexCols.Length != 1 ) { throw new QueryException( "composite-index appears in []: " + collectionNode.Path ); } SqlGenerator gen = new SqlGenerator(SessionFactoryHelper.Factory, new CommonTreeNodeStream(selector)); try { gen.simpleExpr(); //TODO: used to be exprNoParens! was this needed? } catch ( RecognitionException e ) { throw new QueryException( e.Message, e ); } string selectorExpression = gen.GetSQL().ToString(); joinSequence.AddCondition(new SqlString(collectionTableAlias + '.' + indexCols[0] + " = " + selectorExpression )); //joinSequence.AddCondition(collectionTableAlias, new string[] { indexCols[0] }, selectorExpression, false); IList<IParameterSpecification> paramSpecs = gen.GetCollectedParameters(); if ( paramSpecs != null ) { switch ( paramSpecs.Count ) { case 0 : // nothing to do break; case 1 : IParameterSpecification paramSpec = paramSpecs[0]; paramSpec.ExpectedType = queryableCollection.IndexType; fromElement.SetIndexCollectionSelectorParamSpec( paramSpec ); break; default: fromElement.SetIndexCollectionSelectorParamSpec( new AggregatedIndexCollectionSelectorParameterSpecifications( paramSpecs ) ); break; } } // Now, set the text for this node. It should be the element columns. String[] elementColumns = queryableCollection.GetElementColumnNames( elementTable ); Text = elementColumns[0]; IsResolved = true; }
public override void PrepareForDot(string propertyName) { FromElement fromElement = FromElement; if ( fromElement == null ) { throw new InvalidOperationException( "No FROM element for index operator!" ); } IQueryableCollection queryableCollection = fromElement.QueryableCollection; if ( queryableCollection != null && !queryableCollection.IsOneToMany) { FromReferenceNode collectionNode = ( FromReferenceNode ) GetChild(0); String path = collectionNode.Path + "[]." + propertyName; if (Log.IsDebugEnabled) { Log.Debug( "Creating join for many-to-many elements for " + path ); } FromElementFactory factory = new FromElementFactory( fromElement.FromClause, fromElement, path ); // This will add the new from element to the origin. FromElement elementJoin = factory.CreateElementJoin( queryableCollection ); FromElement = elementJoin; } }
public override void ResolveIndex(IASTNode parent) { // An ident node can represent an index expression if the ident // represents a naked property ref // *Note: this makes the assumption (which is currently the case // in the hql-sql grammar) that the ident is first resolved // itself (addrExpr -> resolve()). The other option, if that // changes, is to call resolve from here; but it is // currently un-needed overhead. if (!(IsResolved && _nakedPropertyRef)) { throw new InvalidOperationException(); } string propertyName = OriginalText; if (!DataType.IsCollectionType) { throw new SemanticException("Collection expected; [" + propertyName + "] does not refer to a collection property"); } // TODO : most of below was taken verbatim from DotNode; should either delegate this logic or super-type it CollectionType type = (CollectionType) DataType; string role = type.Role; IQueryableCollection queryableCollection = SessionFactoryHelper.RequireQueryableCollection(role); string columnTableAlias = FromElement.TableAlias; FromElementFactory factory = new FromElementFactory( Walker.CurrentFromClause, FromElement, propertyName, null, FromElement.ToColumns(columnTableAlias, propertyName, false), true ); FromElement elem = factory.CreateCollection(queryableCollection, role, JoinType.InnerJoin, false, true); FromElement = elem; Walker.AddQuerySpaces(queryableCollection.CollectionSpaces); // Always add the collection's query spaces. }