Datei: NDODLL/SqlPersistenceHandling/SqlQueryGenerator.cs

Last Commit (48a2b1a)
1 using NDO.Mapping;
2 using NDO.Query;
3 using NDOql.Expressions;
4 using System;
5 using System.Collections.Generic;
6 using System.Linq;
7 using System.Text;
8 using System.Text.RegularExpressions;
9 using NDO.Configuration;
10
11 namespace NDO.SqlPersistenceHandling
12 {
13 ····class SqlQueryGenerator : IQueryGenerator
14 ····{
15 ········private readonly INDOContainer configContainer;
16 ········private List<QueryInfo> subQueries = new List<QueryInfo>();
17 ········private Func<Dictionary<Relation, Class>, bool, Class, bool, object, string> selectPartCreator;
18 ········private object additionalSelectPartData = null;
19 ········private Mappings mappings;
20
21 ········public SqlQueryGenerator(INDOContainer configContainer)
22 ········{
23 ············this.configContainer = configContainer;
24 ············this.mappings = this.configContainer.Resolve<Mappings>();
25 ········}
26
27 ········/// <summary>
28 ········/// Creates a Sql query string for the complete query over all types.
29 ········/// </summary>
30 ········/// <param name="queryContextsList">List of all contexts which define possible mutations of concrete classes in results and relations.</param>
31 ········/// <param name="expressionTree">The syntax tree of the NDOql query expression.</param>
32 ········/// <param name="hollow">Determines, if the query results should be hollow objects.</param>
33 ········/// <param name="orderings">List of orderings for the resultset.</param>
34 ········/// <param name="skip">Determines how many records of the resultset should be skipped. The resultset must be ordered.</param>
35 ········/// <param name="take">Determines how many records of the resultset should be returned by the query. The resultset must be ordered.</param>
36 ········/// <param name="prefetch">Query for the given prefetch relation.</param>
37 ········/// <returns>A query string.</returns>
38 ········/// <remarks>The result can be used for debugging and display purposes or with handlers, which don't support distributed databases.</remarks>
39 ········public string GenerateQueryStringForAllTypes(
40 ············List<QueryContextsEntry> queryContextsList,
41 ············OqlExpression expressionTree,
42 ············bool hollow,
43 ············List<QueryOrder> orderings,
44 ············int skip,
45 ············int take, string prefetch = null )
46 ········{
47 ············this.selectPartCreator = CreateQuerySelectPart;
48
49 ············CreateSubQueriesForAllTypes(queryContextsList, expressionTree, hollow, orderings, skip, take );
50 ············StringBuilder generatedQuery = new StringBuilder();
51
52 ············foreach (QueryInfo qi in subQueries)
53 ············{
54 ················generatedQuery.Append(qi.QueryString);
55 ················generatedQuery.Append(";\r\n");
56 ············}
57
58 ············generatedQuery.Length -= 3; // the trailing ;\r\n
59
60 ············return generatedQuery.ToString();
61 ········}
62
63 ········/// <summary>
64 ········/// Creates a SQL query string, which can be passed to the IPersistenceHandler to fetch the results for a given concrete type.
65 ········/// </summary>
66 ········/// <param name="queryContextsEntry">All contexts which define possible mutations of concrete classes in results and relations.</param>
67 ········/// <param name="expressionTree">The syntax tree of the NDOql query expression.</param>
68 ········/// <param name="hollow">Determines, if the query results should be hollow objects.</param>
69 ········/// <param name="hasSubclassResultsets">Determines, if this query is part of a composed query which spans over several tables.</param>
70 ········/// <param name="orderings">List of orderings for the resultset.</param>
71 ········/// <param name="skip">Determines how many records of the resultset should be skipped. The resultset must be ordered.</param>
72 ········/// <param name="take">Determines how many records of the resultset should be returned by the query. The resultset must be ordered.</param>
73 ········/// <param name="prefetch">Query for the given prefetch relation.</param>
74 ········/// <returns>A Query string.</returns>
75 ········public string GenerateQueryString(
76 ············QueryContextsEntry queryContextsEntry,
77 ············OqlExpression expressionTree,
78 ············bool hollow,
79 ············bool hasSubclassResultsets,
80 ············List<QueryOrder> orderings,
81 ············int skip,
82 ············int take,
83 ············string prefetch )
84 ········{
85 ············this.selectPartCreator = CreateQuerySelectPart;
86
87 ············return InnerGenerateQueryString( queryContextsEntry, expressionTree, hollow, hasSubclassResultsets, orderings, skip, take, prefetch );
88 ········}
89
90 ········private string InnerGenerateQueryString( QueryContextsEntry queryContextsEntry, OqlExpression expressionTree, bool hollow, bool hasSubclassResultsets, List<QueryOrder> orderings, int skip, int take, string prefetch )
91 ········{
92 ············CreateSubQueries( queryContextsEntry, expressionTree, hollow, hasSubclassResultsets, orderings, skip, take );
93 ············StringBuilder generatedQuery = new StringBuilder();
94
95 ············foreach (QueryInfo qi in subQueries)
96 ············{
97 ················generatedQuery.Append( qi.QueryString );
98 ················generatedQuery.Append( ";\n" );
99 ············}
100
101 ············generatedQuery.Length--; // the trailing \n
102
103 ············if (subQueries.Count == 1)
104 ················generatedQuery.Length--; // the semicolon
105
106 ············return generatedQuery.ToString();
107 ········}
108
109 ········void CreateSubQueriesForAllTypes(
110 ············List<QueryContextsEntry> queryContextsList,
111 ············OqlExpression expressionTree,
112 ············bool hollow,
113 ············List<QueryOrder> orderings,
114 ············int skip,
115 ············int take,
116 ············string prefetch = null )
117 ········{
118 ············foreach (var item in queryContextsList)
119 ············{
120 ················CreateSubQueries( item, expressionTree, hollow, queryContextsList.Count > 1, orderings, skip, take );
121 ············}
122 ········}
123
124 ········void CreateSubQueries( QueryContextsEntry queryContextsEntry,
125 ············OqlExpression expressionTree,
126 ············bool hollow,
127 ············bool hasSubclassResultsets,
128 ············List<QueryOrder> orderings,
129 ············int skip,
130 ············int take )
131 ········{
132 ············Type t = queryContextsEntry.Type;
133 ············var queryContexts = queryContextsEntry.QueryContexts;
134
135 ············if (queryContexts.Count == 0)
136 ············{
137 ················this.subQueries.Add( new QueryInfo( t, ConstructQueryString( t, new Dictionary<Relation, Class>(), expressionTree, hollow, hasSubclassResultsets, orderings, skip, take ) ) );
138 ············}
139 ············else
140 ············{
141 ················string queryString = string.Empty;
142 ················int added = 0;
143 ················foreach (var queryContext in queryContexts)
144 ················{
145 ····················if (!queryContext.Any( kvp => kvp.Value.SystemType == t ))
146 ····················{
147 ························if (added > 0)
148 ····························queryString += " UNION \r\n";
149 ························queryString += ConstructQueryString( t, queryContext, expressionTree, hollow, hasSubclassResultsets, orderings, skip, take );
150 ························added++;
151 ····················}
152 ················}
153 ················this.subQueries.Add( new QueryInfo( t, queryString ) );
154 ············}
155 ········}
156
157 ········SqlColumnListGenerator CreateColumnListGenerator( Class cls )
158 ········{
159 ············var key = $"{nameof(SqlColumnListGenerator)}-{cls.FullName}";
160 ············return configContainer.ResolveOrRegisterType<SqlColumnListGenerator>( new ContainerControlledLifetimeManager(), key, new ParameterOverride( "cls", cls ) );
161 ········}
162
163 ········string ConstructQueryString(
164 ············Type resultType,
165 ············Dictionary<Relation, Class> relationContext,
166 ············OqlExpression expressionTree,
167 ············bool hollow,
168 ············bool hasSubclassResultsets,
169 ············List<QueryOrder> orderings,
170 ············int skip,
171 ············int take )
172 ········{
173 ············Class cls = this.mappings.FindClass( resultType );
174
175 ············StringBuilder sb = new StringBuilder( "SELECT " );
176
177 ············var from = new FromGenerator( cls, relationContext ).GenerateFromExpression( expressionTree );
178 ············var qualifyWithTableName = from.IndexOf( "INNER JOIN" ) > -1;
179
180 ············string columnList = this.selectPartCreator( relationContext, hollow, cls, qualifyWithTableName, this.additionalSelectPartData );
181 ············sb.Append( columnList );
182
183 ············// If we need to sort a set of hollow results for different subclasses
184 ············// we need to fetch the ordering columns together with the oid columns
185 ············if (hollow && orderings.Count > 0 && hasSubclassResultsets)
186 ············{
187 ················var orderCols = (from o in orderings select cls.FindField( o.FieldName ).Column.GetQualifiedName()).ToArray();
188 ················columnList += ", " + String.Join( ", ", orderCols );
189 ············}
190
191 ············sb.Append( ' ' );
192 ············sb.Append( from );
193 ············string where = new WhereGenerator( cls, relationContext ).GenerateWhereClause( expressionTree );
194 ············if (where != string.Empty)
195 ············{
196 ················sb.Append( ' ' );
197 ················sb.Append( where );
198 ············}
199
200 ············if (orderings.Count > 0)
201 ············{
202 ················sb.Append( ' ' );
203 ················sb.Append( new OrderGenerator( cls ).GenerateOrderClause( orderings, skip, take ) );
204 ············}
205
206 ············return sb.ToString();
207 ········}
208
209 ········private string CreateQuerySelectPart( Dictionary<Relation, Class> relationContext, bool hollow, Class cls, bool qualifyWithTableName, object additionalData )
210 ········{
211 ············var generator = CreateColumnListGenerator( cls );
212
213 ············// We have to hack around a special behavior of SQLite, generating
214 ············// new columns with fully specified column names, if the query
215 ············// is a UNION············
216 ············var generateAliasNames = relationContext.Count > 0 && cls.Provider.GetType().FullName.IndexOf( "Sqlite" ) > -1;
217
218 ············return generator.Result( hollow, generateAliasNames, qualifyWithTableName );
219 ········}
220
221 ········private string CreateAggregateSelectPart( Dictionary<Relation, Class> relationContext, bool hollow, Class cls, bool qualifyWithTableName, object additionalData )
222 ········{
223 ············var tuple = (Tuple<string, AggregateType>)additionalData;
224 ············string field = tuple.Item1;
225 ············AggregateType aggregateType = tuple.Item2;
226 ············bool isStar = field == "*";
227
228 ············Column column = null;
229 ············if (field.ToLower().StartsWith( "oid" ))
230 ············{
231 ················Regex regex = new Regex( @"\(\s*(\d+)\s*\)" );
232 ················Match match = regex.Match( field );
233 ················int index = 0;
234 ················if (match.Success)
235 ····················index = int.Parse( match.Groups[1].Value );
236 ················column = ((OidColumn)cls.Oid.OidColumns[index]);
237 ············}
238 ············else
239 ············{
240 ················if (!isStar)
241 ····················column = cls.FindField( field ).Column;
242 ············}
243
244 ············var provider = cls.Provider;
245 ············var colName = isStar ? "*" : column.GetQualifiedName();
246 ············//var tableName = qualifyWithTableName ? cls.GetQualifiedTableName() + "." : String.Empty;
247
248 return $"{ aggregateType. ToString( ) . ToUpper( ) } ( { colName} ) AS { provider. GetQuotedName( "AggrResult" ) } ";
249 ········}
250
251 ········public string GenerateAggregateQueryString( string field, QueryContextsEntry queryContextsEntry, OqlExpression expressionTree, bool hasSubclassResultsets, AggregateType aggregateType )
252 ········{
253 ············this.selectPartCreator = CreateAggregateSelectPart;
254 ············this.additionalSelectPartData = new Tuple<string,AggregateType>( field, aggregateType );
255
256 ············var query = InnerGenerateQueryString( queryContextsEntry, expressionTree, true, hasSubclassResultsets, new List<QueryOrder>(), 0, 0, null );
257 ············return query;
258 ········}
259
260 ········public string GeneratePrefetchQuery( Type parentType, IEnumerable<IPersistenceCapable> parents, string prefetch )
261 ········{
262 ············Class parentCls = this.mappings.FindClass( parentType );
263
264 ············StringBuilder sb = new StringBuilder( "SELECT " );
265 ············List<Relation> relations = new List<Relation>();
266
267 ············if (prefetch.IndexOf('.') == -1)
268 ············{
269 ················var rel = parentCls.FindRelation( prefetch );
270 ················if (rel != null)
271 ····················relations.Add( rel );
272 ············}
273
274 ············new RelationContextGenerator( this.mappings ).CreateContextForName( parentCls, prefetch, relations );
275
276 ············if (relations.Count == 0)
277 ················throw new NDOException( 76, $"Prefetch: Can't find relation mapping with name {prefetch} in class {parentCls.FullName}" );
278
279 ············Class cls = mappings.FindClass( relations[relations.Count - 1].ReferencedTypeName );
280 ············var relationContext = new Dictionary<Relation, Class>();
281 #warning Hier fehlt der INNER JOIN
282 ············string columnList = CreateQuerySelectPart( relationContext, false, cls, true, null );
283 ············sb.Append( columnList );
284
285 ············sb.Append( ' ' );
286 ············sb.Append( new FromGenerator( cls, relationContext ).GenerateFromExpression( null, relations ) );
287 ············// We can be sure, that we have only one oid column here.
288 ············var oidList = String.Join( ",", from p in parents select p.NDOObjectId.Id.Values[0].ToString() );
289 ············string where = $"WHERE {parentCls.Oid.OidColumns.First().GetQualifiedName()} IN ({oidList})";
290 ············sb.Append( ' ' );
291 ············sb.Append( where );
292
293 ············return sb.ToString();
294 ········}
295 ····}
296 }
297
New Commit (790647f)
1 using NDO.Mapping;
2 using NDO.Query;
3 using NDOql.Expressions;
4 using System;
5 using System.Collections.Generic;
6 using System.Linq;
7 using System.Text;
8 using System.Text.RegularExpressions;
9 using NDO.Configuration;
10
11 namespace NDO.SqlPersistenceHandling
12 {
13 ····class SqlQueryGenerator : IQueryGenerator
14 ····{
15 ········private readonly INDOContainer configContainer;
16 ········private List<QueryInfo> subQueries = new List<QueryInfo>();
17 ········private Func<Dictionary<Relation, Class>, bool, Class, bool, object, string> selectPartCreator;
18 ········private object additionalSelectPartData = null;
19 ········private Mappings mappings;
20
21 ········public SqlQueryGenerator(INDOContainer configContainer)
22 ········{
23 ············this.configContainer = configContainer;
24 ············this.mappings = this.configContainer.Resolve<Mappings>();
25 ········}
26
27 ········/// <summary>
28 ········/// Creates a Sql query string for the complete query over all types.
29 ········/// </summary>
30 ········/// <param name="queryContextsList">List of all contexts which define possible mutations of concrete classes in results and relations.</param>
31 ········/// <param name="expressionTree">The syntax tree of the NDOql query expression.</param>
32 ········/// <param name="hollow">Determines, if the query results should be hollow objects.</param>
33 ········/// <param name="orderings">List of orderings for the resultset.</param>
34 ········/// <param name="skip">Determines how many records of the resultset should be skipped. The resultset must be ordered.</param>
35 ········/// <param name="take">Determines how many records of the resultset should be returned by the query. The resultset must be ordered.</param>
36 ········/// <param name="prefetch">Query for the given prefetch relation.</param>
37 ········/// <returns>A query string.</returns>
38 ········/// <remarks>The result can be used for debugging and display purposes or with handlers, which don't support distributed databases.</remarks>
39 ········public string GenerateQueryStringForAllTypes(
40 ············List<QueryContextsEntry> queryContextsList,
41 ············OqlExpression expressionTree,
42 ············bool hollow,
43 ············List<QueryOrder> orderings,
44 ············int skip,
45 ············int take, string prefetch = null )
46 ········{
47 ············this.selectPartCreator = CreateQuerySelectPart;
48
49 ············CreateSubQueriesForAllTypes(queryContextsList, expressionTree, hollow, orderings, skip, take );
50 ············StringBuilder generatedQuery = new StringBuilder();
51
52 ············foreach (QueryInfo qi in subQueries)
53 ············{
54 ················generatedQuery.Append(qi.QueryString);
55 ················generatedQuery.Append(";\r\n");
56 ············}
57
58 ············generatedQuery.Length -= 3; // the trailing ;\r\n
59
60 ············return generatedQuery.ToString();
61 ········}
62
63 ········/// <summary>
64 ········/// Creates a SQL query string, which can be passed to the IPersistenceHandler to fetch the results for a given concrete type.
65 ········/// </summary>
66 ········/// <param name="queryContextsEntry">All contexts which define possible mutations of concrete classes in results and relations.</param>
67 ········/// <param name="expressionTree">The syntax tree of the NDOql query expression.</param>
68 ········/// <param name="hollow">Determines, if the query results should be hollow objects.</param>
69 ········/// <param name="hasSubclassResultsets">Determines, if this query is part of a composed query which spans over several tables.</param>
70 ········/// <param name="orderings">List of orderings for the resultset.</param>
71 ········/// <param name="skip">Determines how many records of the resultset should be skipped. The resultset must be ordered.</param>
72 ········/// <param name="take">Determines how many records of the resultset should be returned by the query. The resultset must be ordered.</param>
73 ········/// <param name="prefetch">Query for the given prefetch relation.</param>
74 ········/// <returns>A Query string.</returns>
75 ········public string GenerateQueryString(
76 ············QueryContextsEntry queryContextsEntry,
77 ············OqlExpression expressionTree,
78 ············bool hollow,
79 ············bool hasSubclassResultsets,
80 ············List<QueryOrder> orderings,
81 ············int skip,
82 ············int take,
83 ············string prefetch )
84 ········{
85 ············this.selectPartCreator = CreateQuerySelectPart;
86
87 ············return InnerGenerateQueryString( queryContextsEntry, expressionTree, hollow, hasSubclassResultsets, orderings, skip, take, prefetch );
88 ········}
89
90 ········private string InnerGenerateQueryString( QueryContextsEntry queryContextsEntry, OqlExpression expressionTree, bool hollow, bool hasSubclassResultsets, List<QueryOrder> orderings, int skip, int take, string prefetch )
91 ········{
92 ············CreateSubQueries( queryContextsEntry, expressionTree, hollow, hasSubclassResultsets, orderings, skip, take );
93 ············StringBuilder generatedQuery = new StringBuilder();
94
95 ············foreach (QueryInfo qi in subQueries)
96 ············{
97 ················generatedQuery.Append( qi.QueryString );
98 ················generatedQuery.Append( ";\n" );
99 ············}
100
101 ············generatedQuery.Length--; // the trailing \n
102
103 ············if (subQueries.Count == 1)
104 ················generatedQuery.Length--; // the semicolon
105
106 ············return generatedQuery.ToString();
107 ········}
108
109 ········void CreateSubQueriesForAllTypes(
110 ············List<QueryContextsEntry> queryContextsList,
111 ············OqlExpression expressionTree,
112 ············bool hollow,
113 ············List<QueryOrder> orderings,
114 ············int skip,
115 ············int take,
116 ············string prefetch = null )
117 ········{
118 ············foreach (var item in queryContextsList)
119 ············{
120 ················CreateSubQueries( item, expressionTree, hollow, queryContextsList.Count > 1, orderings, skip, take );
121 ············}
122 ········}
123
124 ········void CreateSubQueries( QueryContextsEntry queryContextsEntry,
125 ············OqlExpression expressionTree,
126 ············bool hollow,
127 ············bool hasSubclassResultsets,
128 ············List<QueryOrder> orderings,
129 ············int skip,
130 ············int take )
131 ········{
132 ············Type t = queryContextsEntry.Type;
133 ············var queryContexts = queryContextsEntry.QueryContexts;
134
135 ············if (queryContexts.Count == 0)
136 ············{
137 ················this.subQueries.Add( new QueryInfo( t, ConstructQueryString( t, new Dictionary<Relation, Class>(), expressionTree, hollow, hasSubclassResultsets, orderings, skip, take ) ) );
138 ············}
139 ············else
140 ············{
141 ················string queryString = string.Empty;
142 ················int added = 0;
143 ················foreach (var queryContext in queryContexts)
144 ················{
145 ····················if (!queryContext.Any( kvp => kvp.Value.SystemType == t ))
146 ····················{
147 ························if (added > 0)
148 ····························queryString += " UNION \r\n";
149 ························queryString += ConstructQueryString( t, queryContext, expressionTree, hollow, hasSubclassResultsets, orderings, skip, take );
150 ························added++;
151 ····················}
152 ················}
153 ················this.subQueries.Add( new QueryInfo( t, queryString ) );
154 ············}
155 ········}
156
157 ········SqlColumnListGenerator CreateColumnListGenerator( Class cls )
158 ········{
159 ············var key = $"{nameof(SqlColumnListGenerator)}-{cls.FullName}";
160 ············return configContainer.ResolveOrRegisterType<SqlColumnListGenerator>( new ContainerControlledLifetimeManager(), key, new ParameterOverride( "cls", cls ) );
161 ········}
162
163 ········string ConstructQueryString(
164 ············Type resultType,
165 ············Dictionary<Relation, Class> relationContext,
166 ············OqlExpression expressionTree,
167 ············bool hollow,
168 ············bool hasSubclassResultsets,
169 ············List<QueryOrder> orderings,
170 ············int skip,
171 ············int take )
172 ········{
173 ············Class cls = this.mappings.FindClass( resultType );
174
175 ············StringBuilder sb = new StringBuilder( "SELECT " );
176
177 ············var from = new FromGenerator( cls, relationContext ).GenerateFromExpression( expressionTree );
178 ············var qualifyWithTableName = from.IndexOf( "INNER JOIN" ) > -1;
179
180 ············string columnList = this.selectPartCreator( relationContext, hollow, cls, qualifyWithTableName, this.additionalSelectPartData );
181 ············sb.Append( columnList );
182
183 ············// If we need to sort a set of hollow results for different subclasses
184 ············// we need to fetch the ordering columns together with the oid columns
185 ············if (hollow && orderings.Count > 0 && hasSubclassResultsets)
186 ············{
187 ················var orderCols = (from o in orderings select cls.FindField( o.FieldName ).Column.GetQualifiedName()).ToArray();
188 ················columnList += ", " + String.Join( ", ", orderCols );
189 ············}
190
191 ············sb.Append( ' ' );
192 ············sb.Append( from );
193 ············string where = new WhereGenerator( cls, relationContext ).GenerateWhereClause( expressionTree );
194 ············if (where != string.Empty)
195 ············{
196 ················sb.Append( ' ' );
197 ················sb.Append( where );
198 ············}
199
200 ············if (orderings.Count > 0)
201 ············{
202 ················sb.Append( ' ' );
203 ················sb.Append( new OrderGenerator( cls ).GenerateOrderClause( orderings, skip, take ) );
204 ············}
205
206 ············return sb.ToString();
207 ········}
208
209 ········private string CreateQuerySelectPart( Dictionary<Relation, Class> relationContext, bool hollow, Class cls, bool qualifyWithTableName, object additionalData )
210 ········{
211 ············var generator = CreateColumnListGenerator( cls );
212
213 ············// We have to hack around a special behavior of SQLite, generating
214 ············// new columns with fully specified column names, if the query
215 ············// is a UNION············
216 ············var generateAliasNames = relationContext.Count > 0 && cls.Provider.GetType().FullName.IndexOf( "Sqlite" ) > -1;
217
218 ············return generator.Result( hollow, generateAliasNames, qualifyWithTableName );
219 ········}
220
221 ········private string CreateAggregateSelectPart( Dictionary<Relation, Class> relationContext, bool hollow, Class cls, bool qualifyWithTableName, object additionalData )
222 ········{
223 ············var tuple = (Tuple<string, AggregateType>)additionalData;
224 ············string field = tuple.Item1;
225 ············AggregateType aggregateType = tuple.Item2;
226 ············bool isStar = field == "*";
227
228 ············Column column = null;
229 ············if (field.ToLower().StartsWith( "oid" ))
230 ············{
231 ················Regex regex = new Regex( @"\(\s*(\d+)\s*\)" );
232 ················Match match = regex.Match( field );
233 ················int index = 0;
234 ················if (match.Success)
235 ····················index = int.Parse( match.Groups[1].Value );
236 ················column = ((OidColumn)cls.Oid.OidColumns[index]);
237 ············}
238 ············else
239 ············{
240 ················if (!isStar)
241 ····················column = cls.FindField( field ).Column;
242 ············}
243
244 ············var provider = cls.Provider;
245 ············var colName = isStar ? "*" : column.GetQualifiedName();
 
246
247 return $"{ aggregateType. ToString( ) . ToUpper( ) } ( { colName} ) AS { provider. GetQuotedName( "AggrResult" ) } ";
248 ········}
249
250 ········public string GenerateAggregateQueryString( string field, QueryContextsEntry queryContextsEntry, OqlExpression expressionTree, bool hasSubclassResultsets, AggregateType aggregateType )
251 ········{
252 ············this.selectPartCreator = CreateAggregateSelectPart;
253 ············this.additionalSelectPartData = new Tuple<string,AggregateType>( field, aggregateType );
254
255 ············var query = InnerGenerateQueryString( queryContextsEntry, expressionTree, true, hasSubclassResultsets, new List<QueryOrder>(), 0, 0, null );
256 ············return query;
257 ········}
258
259 ········public string GeneratePrefetchQuery( Type parentType, IEnumerable<IPersistenceCapable> parents, string prefetch )
260 ········{
261 ············Class parentCls = this.mappings.FindClass( parentType );
262
263 ············StringBuilder sb = new StringBuilder( "SELECT " );
264 ············List<Relation> relations = new List<Relation>();
265
266 ············if (prefetch.IndexOf('.') == -1)
267 ············{
268 ················var rel = parentCls.FindRelation( prefetch );
269 ················if (rel != null)
270 ····················relations.Add( rel );
271 ············}
272
273 ············new RelationContextGenerator( this.mappings ).CreateContextForName( parentCls, prefetch, relations );
274
275 ············if (relations.Count == 0)
276 ················throw new NDOException( 76, $"Prefetch: Can't find relation mapping with name {prefetch} in class {parentCls.FullName}" );
277
278 ············Class cls = mappings.FindClass( relations[relations.Count - 1].ReferencedTypeName );
279 ············var relationContext = new Dictionary<Relation, Class>();
280 #warning Hier fehlt der INNER JOIN
281 ············string columnList = CreateQuerySelectPart( relationContext, false, cls, true, null );
282 ············sb.Append( columnList );
283
284 ············sb.Append( ' ' );
285 ············sb.Append( new FromGenerator( cls, relationContext ).GenerateFromExpression( null, relations ) );
286 ············// We can be sure, that we have only one oid column here.
287 ············var oidList = String.Join( ",", from p in parents select p.NDOObjectId.Id.Values[0].ToString() );
288 ············string where = $"WHERE {parentCls.Oid.OidColumns.First().GetQualifiedName()} IN ({oidList})";
289 ············sb.Append( ' ' );
290 ············sb.Append( where );
291
292 ············return sb.ToString();
293 ········}
294 ····}
295 }
296