private FromElement CreateManyToMany( string role, string associatedEntityName, string roleAlias, IEntityPersister entityPersister, EntityType type, JoinType joinType) { FromElement elem; SessionFactoryHelperExtensions sfh = _fromClause.SessionFactoryHelper; if (_inElementsFunction /*implied*/) { // For implied many-to-many, just add the end join. JoinSequence joinSequence = CreateJoinSequence(roleAlias, joinType); elem = CreateJoin(associatedEntityName, 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.AliasGenerator.CreateName(entityPersister.EntityName); string[] secondJoinColumns = sfh.GetCollectionElementColumns(role, roleAlias); // Add the second join, the one that ends in the destination table. JoinSequence joinSequence = CreateJoinSequence(roleAlias, joinType); joinSequence.AddJoin(sfh.GetElementAssociationType(_collectionType), tableAlias, joinType, secondJoinColumns); elem = CreateJoin(associatedEntityName, tableAlias, joinSequence, type, false); elem.UseFromFragment = true; } return(elem); }
public FromElement CreateElementJoin(IQueryableCollection queryableCollection) { _implied = true; //TODO: always true for now, but not if we later decide to support elements() in the from clause _inElementsFunction = true; IType elementType = queryableCollection.ElementType; if (!elementType.IsEntityType) { throw new InvalidOperationException("Cannot create element join for a collection of non-entities!"); } _queryableCollection = queryableCollection; SessionFactoryHelperExtensions sfh = _fromClause.SessionFactoryHelper; IEntityPersister entityPersister = queryableCollection.ElementPersister; string tableAlias = _fromClause.AliasGenerator.CreateName(entityPersister.EntityName); string associatedEntityName = entityPersister.EntityName; IEntityPersister targetEntityPersister = sfh.RequireClassPersister(associatedEntityName); // Create the FROM element for the target (the elements of the collection). FromElement destination = CreateAndAddFromElement( associatedEntityName, _classAlias, targetEntityPersister, (EntityType)queryableCollection.ElementType, tableAlias ); // If the join is implied, then don't include sub-classes on the element. if (_implied) { destination.IncludeSubclasses = false; } _fromClause.AddCollectionJoinFromElementByPath(_path, destination); // origin.addDestination(destination); // Add the query spaces. _fromClause.Walker.AddQuerySpaces(entityPersister.QuerySpaces); CollectionType type = queryableCollection.CollectionType; string role = type.Role; string roleAlias = _origin.TableAlias; string[] targetColumns = sfh.GetCollectionElementColumns(role, roleAlias); IAssociationType elementAssociationType = sfh.GetElementAssociationType(type); // Create the join element under the from element. JoinSequence joinSequence = sfh.CreateJoinSequence(_implied, elementAssociationType, tableAlias, JoinType.InnerJoin, targetColumns); FromElement elem = InitializeJoin(_path, destination, joinSequence, targetColumns, _origin, false); elem.UseFromFragment = true; // The associated entity is implied, but it must be included in the FROM. elem.CollectionTableAlias = roleAlias; // The collection alias is the role. return(elem); }
private JoinSequence CreateJoinSequence(string roleAlias, JoinType joinType) { SessionFactoryHelperExtensions sessionFactoryHelper = _fromClause.SessionFactoryHelper; string[] joinColumns = Columns; if (_collectionType == null) { throw new InvalidOperationException("collectionType is null!"); } return(sessionFactoryHelper.CreateJoinSequence(_implied, _collectionType, roleAlias, joinType, joinColumns)); }
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; }