public string ContinueFromManyToMany( System.Type clazz, string[ ] joinColumns, QueryTranslator q ) { Start( q ); continuation = true; currentName = q.CreateNameFor( clazz ); q.AddType( currentName, clazz ); IQueryable classPersister = q.GetPersister( clazz ); join.AddJoin( classPersister.TableName, currentName, joinColumns, classPersister.IdentifierColumnNames, joinType ); currentPropertyMapping = classPersister; return currentName; }
/// <summary> /// /// </summary> /// <param name="propertyName"></param> /// <param name="propertyType"></param> /// <param name="q"></param> /// <remarks>NOTE: we avoid joining to the next table if the named property is just the foreign key value</remarks> private void DereferenceEntity( string propertyName, EntityType propertyType, QueryTranslator q ) { //if its "id" bool isIdShortcut = EntityID.Equals(propertyName) && !propertyType.IsUniqueKeyReference; //or its the id property name string idPropertyName; try { idPropertyName = propertyType.GetIdentifierOrUniqueKeyPropertyName( q.Factory ); } catch (MappingException me) { throw new QueryException(me); } bool isNamedIdPropertyShortcut = idPropertyName != null && idPropertyName.Equals( propertyName ); if ( isIdShortcut || isNamedIdPropertyShortcut ) { // special shortcut for id properties, skip the join! // this must only occur at the _end_ of a path expression DereferenceProperty( propertyName ); } else { System.Type entityClass = propertyType.AssociatedClass; String name = q.CreateNameFor( entityClass ); q.AddType( name, entityClass ); IQueryable memberPersister = q.GetPersister( entityClass ); //String[] keyColNames = memberPersister.getIdentifierColumnNames(); string[] keyColNames; try { keyColNames = propertyType.GetReferencedColumns( q.Factory ); } catch (MappingException me) { throw new QueryException(me); } AddJoin( memberPersister.TableName, name, keyColNames ); if ( propertyType.IsOneToOne ) { oneToOneOwnerName = currentName; } currentName = name; currentProperty = propertyName; q.AddPathAliasAndJoin( path.Substring( 0, path.LastIndexOf( StringHelper.Dot ) ), name, join ); componentPath = null; //componentPath = new StringBuilder( ); currentPropertyMapping = memberPersister; } }
/// <summary> /// /// </summary> /// <param name="q"></param> /// <returns></returns> public string AddFromCollection( QueryTranslator q ) { IType collectionElementType = PropertyType; if( collectionElementType == null ) { throw new QueryException( string.Format( "must specify 'elements' for collection valued property in from clause: {0}", path ) ); } if( collectionElementType.IsEntityType ) { // an association IQueryableCollection collectionPersister = q.GetCollectionPersister( collectionRole ); IQueryable entityPersister = (IQueryable) collectionPersister.ElementPersister; System.Type clazz = entityPersister.MappedClass; string[] collectionElementColumns = CurrentColumns(); string elementName; if ( collectionPersister.IsOneToMany ) { elementName = collectionName; // allow index() function q.DecoratePropertyMapping( elementName, collectionPersister ); } else { // many to many q.AddCollection( collectionName, collectionRole ); elementName = q.CreateNameFor( clazz ); string[] keyColumnNames = entityPersister.IdentifierColumnNames; join.AddJoin( entityPersister.TableName, elementName, collectionElementColumns, keyColumnNames, joinType ); } q.AddFrom( elementName, clazz, join ); currentPropertyMapping = new CollectionPropertyMapping( collectionPersister ); return elementName; } else { // collection of values q.AddFromCollection( collectionName, collectionRole, join ); return collectionName; } }
public void Token( string token, QueryTranslator q ) { // start by looking for HQL keywords.... string lcToken = token.ToLower( System.Globalization.CultureInfo.InvariantCulture ); if( lcToken.Equals( StringHelper.Comma ) ) { if( !( expectingJoin | expectingAs ) ) { throw new QueryException( "unexpected token: ," ); } expectingJoin = false; expectingAs = false; } else if( lcToken.Equals( "join" ) ) { if( !afterJoinType ) { if( !( expectingJoin | expectingAs ) ) { throw new QueryException( "unexpected token: join" ); } // inner joins can be abbreviated to 'join' joinType = JoinType.InnerJoin; expectingJoin = false; expectingAs = false; } else { afterJoinType = false; } } else if( lcToken.Equals( "fetch" ) ) { if( q.IsShallowQuery ) { throw new QueryException( "fetch may not be used with scroll() or iterate()" ); } if( joinType == JoinType.None ) { throw new QueryException( "unexpected token: fetch" ); } if( joinType == JoinType.FullJoin || joinType == JoinType.RightOuterJoin ) { throw new QueryException( "fetch may only be used with inner join or left outer join" ); } afterFetch = true; } else if( lcToken.Equals( "outer" ) ) { // 'outer' is optional and is ignored) if( !afterJoinType || ( joinType != JoinType.LeftOuterJoin && joinType != JoinType.RightOuterJoin ) ) { throw new QueryException( "unexpected token: outer" ); } } else if( joinTypes.Contains( lcToken ) ) { if( !( expectingJoin | expectingAs ) ) { throw new QueryException( "unexpected token: " + token ); } joinType = ( JoinType ) joinTypes[ lcToken ]; afterJoinType = true; expectingJoin = false; expectingAs = false; } else if( lcToken.Equals( "class" ) ) { if( !afterIn ) { throw new QueryException( "unexpected token: class" ); } if( joinType != JoinType.None ) { throw new QueryException( "outer or full join must be followed by path expression" ); } afterClass = true; } else if( lcToken.Equals( "in" ) ) { if( !expectingIn ) { throw new QueryException( "unexpected token: in" ); } afterIn = true; expectingIn = false; } else if( lcToken.Equals( "as" ) ) { if( !expectingAs ) { throw new QueryException( "unexpected token: as" ); } afterAs = true; expectingAs = false; } else { if( afterJoinType ) { throw new QueryException( "join expected: " + token ); } if( expectingJoin ) { throw new QueryException( "unexpected token: " + token ); } if( expectingIn ) { throw new QueryException( "in expected: " + token ); } // now anything that is not a HQL keyword if( afterAs || expectingAs ) { // (AS is always optional, for consistentcy with SQL/OQL // process the "new" HQL stype where aliases are assigned // _after_ the class name or path expression ie using the // AS construction if( entityName != null ) { q.SetAliasName( token, entityName ); } else { throw new QueryException( "unexpected: as " + token ); } afterAs = false; expectingJoin = true; expectingAs = false; entityName = null; } else if( afterIn ) { // process the "old" HQL style where aliases appear _first // ie using the IN or IN CLASS constructions if( alias == null ) { throw new QueryException( "alias not specified for: " + token ); } if( joinType != JoinType.None ) { throw new QueryException( "outer or full join must be followed by path expressions" ); } if( afterClass ) { // treat it as a classname IQueryable p = q.GetPersisterUsingImports( token ); if( p == null ) { throw new QueryException( "persister not found: " + token ); } q.AddFromClass( alias, p ); } else { // treat it as a path expression peParser.JoinType = JoinType.InnerJoin; peParser.UseThetaStyleJoin = true; ParserHelper.Parse( peParser, q.Unalias( token ), ParserHelper.PathSeparators, q ); if( !peParser.IsCollectionValued ) { throw new QueryException( "pathe expression did not resolve to collection: " + token ); } string nm = peParser.AddFromCollection( q ); q.SetAliasName( alias, nm ); } alias = null; afterIn = false; afterClass = false; expectingJoin = true; } else { // handle a path expression or class name that appears // at the start, in the "new" HQL style or an alias that // appears at the start in the "old HQL stype IQueryable p = q.GetPersisterUsingImports( token ); if( p != null ) { // starts with the name of a mapped class (new style) if( joinType != JoinType.None ) { throw new QueryException( "outer or full join must be followed by path expression" ); } entityName = q.CreateNameFor( p.MappedClass ); q.AddFromClass( entityName, p ); expectingAs = true; } else if( token.IndexOf( '.' ) < 0 ) { // starts with an alias (old style) // semi-bad thing about this: can't re-alias another alias... alias = token; expectingIn = true; } else { // starts with a path expression (new style) // force HQL style: from Person p inner join p.cars c //if (joinType==JoinType.None) throw new QueryException("path expression must be preceded by full, left, right or inner join"); //allow ODMG OQL style: from Person p, p.cars c if( joinType != JoinType.None ) { peParser.JoinType = joinType; } else { peParser.JoinType = JoinType.InnerJoin; } peParser.UseThetaStyleJoin = q.IsSubquery; ParserHelper.Parse( peParser, q.Unalias( token ), ParserHelper.PathSeparators, q ); entityName = peParser.AddFromAssociation( q ); joinType = JoinType.None; peParser.JoinType = JoinType.InnerJoin; if( afterFetch ) { peParser.Fetch( q, entityName ); afterFetch = false; } expectingAs = true; } } } }