Datei: NDODLL/SqlPersistenceHandling/SqlQueryGenerator.cs
Last Commit (b95968d)
1 | using Microsoft.Extensions.DependencyInjection; |
2 | using NDO.Mapping; |
3 | using NDO.Query; |
4 | using NDOql.Expressions; |
5 | using System; |
6 | using System.Collections.Generic; |
7 | using System.Linq; |
8 | using System.Text; |
9 | using System.Text.RegularExpressions; |
10 | |
11 | namespace NDO.SqlPersistenceHandling |
12 | { |
13 | ····class SqlQueryGenerator : IQueryGenerator |
14 | ····{ |
15 | ········private readonly IMappingsAccessor mappingsAccessor; |
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( IMappingsAccessor mappingsAccessor ) |
22 | ········{ |
23 | ············this.mappingsAccessor = mappingsAccessor; |
24 | ············this.mappings = mappingsAccessor.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 | ········string ConstructQueryString( |
158 | ············Type resultType, |
159 | ············Dictionary<Relation, Class> relationContext, |
160 | ············OqlExpression expressionTree, |
161 | ············bool hollow, |
162 | ············bool hasSubclassResultsets, |
163 | ············List<QueryOrder> orderings, |
164 | ············int skip, |
165 | ············int take ) |
166 | ········{ |
167 | ············Class cls = this.mappings.FindClass( resultType ); |
168 | |
169 | ············StringBuilder sb = new StringBuilder( "SELECT " ); |
170 | |
171 | ············var from = new FromGenerator( cls, relationContext ).GenerateFromExpression( expressionTree ); |
172 | ············var qualifyWithTableName = from.IndexOf( "INNER JOIN" ) > -1; |
173 | |
174 | ············string columnList = this.selectPartCreator( relationContext, hollow, cls, qualifyWithTableName, this.additionalSelectPartData ); |
175 | ············sb.Append( columnList ); |
176 | |
177 | ············// If we need to sort a set of hollow results for different subclasses |
178 | ············// we need to fetch the ordering columns together with the oid columns |
179 | ············if (hollow && orderings.Count > 0 && hasSubclassResultsets) |
180 | ············{ |
181 | ················var orderCols = (from o in orderings select cls.FindField( o.FieldName ).Column.GetQualifiedName()).ToArray(); |
182 | ················columnList += ", " + String.Join( ", ", orderCols ); |
183 | ············} |
184 | |
185 | ············sb.Append( ' ' ); |
186 | ············sb.Append( from ); |
187 | ············string where = new WhereGenerator( cls, relationContext ).GenerateWhereClause( expressionTree ); |
188 | ············if (where != string.Empty) |
189 | ············{ |
190 | ················sb.Append( ' ' ); |
191 | ················sb.Append( where ); |
192 | ············} |
193 | |
194 | ············if (orderings.Count > 0) |
195 | ············{ |
196 | ················sb.Append( ' ' ); |
197 | ················sb.Append( new OrderGenerator( cls ).GenerateOrderClause( orderings, skip, take ) ); |
198 | ············} |
199 | |
200 | ············return sb.ToString(); |
201 | ········} |
202 | |
203 | ········private string CreateQuerySelectPart( Dictionary<Relation, Class> relationContext, bool hollow, Class cls, bool qualifyWithTableName, object additionalData ) |
204 | ········{ |
205 | ············var generator = SqlColumnListGenerator.Get( cls ); |
206 | |
207 | ············// We have to hack around a special behavior of SQLite, generating |
208 | ············// new columns with fully specified column names, if the query |
209 | ············// is a UNION············ |
210 | ············var generateAliasNames = relationContext.Count > 0 && cls.Provider.GetType().FullName.IndexOf( "Sqlite" ) > -1; |
211 | |
212 | ············return generator.Result( hollow, generateAliasNames, qualifyWithTableName ); |
213 | ········} |
214 | |
215 | ········private string CreateAggregateSelectPart( Dictionary<Relation, Class> relationContext, bool hollow, Class cls, bool qualifyWithTableName, object additionalData ) |
216 | ········{ |
217 | ············var tuple = (Tuple<string, AggregateType>)additionalData; |
218 | ············string field = tuple.Item1; |
219 | ············AggregateType aggregateType = tuple.Item2; |
220 | ············bool isStar = field == "*"; |
221 | |
222 | ············Column column = null; |
223 | ············if (field.ToLower().StartsWith( "oid" )) |
224 | ············{ |
225 | ················Regex regex = new Regex( @"\(\s*(\d+)\s*\)" ); |
226 | ················Match match = regex.Match( field ); |
227 | ················int index = 0; |
228 | ················if (match.Success) |
229 | ····················index = int.Parse( match.Groups[1].Value ); |
230 | ················column = ((OidColumn)cls.Oid.OidColumns[index]); |
231 | ············} |
232 | ············else |
233 | ············{ |
234 | ················if (!isStar) |
235 | ····················column = cls.FindField( field ).Column; |
236 | ············} |
237 | |
238 | ············var provider = cls.Provider; |
239 | ············var colName = isStar ? "*" : column.GetQualifiedName(); |
240 | |
241 | ············return $"{aggregateType.ToString().ToUpper()}({colName}) AS {provider.GetQuotedName( "AggrResult" )}"; |
242 | ········} |
243 | |
244 | ········public string GenerateAggregateQueryString( string field, QueryContextsEntry queryContextsEntry, OqlExpression expressionTree, bool hasSubclassResultsets, AggregateType aggregateType ) |
245 | ········{ |
246 | ············this.selectPartCreator = CreateAggregateSelectPart; |
247 | ············this.additionalSelectPartData = new Tuple<string,AggregateType>( field, aggregateType ); |
248 | |
249 | ············var query = InnerGenerateQueryString( queryContextsEntry, expressionTree, true, hasSubclassResultsets, new List<QueryOrder>(), 0, 0, null ); |
250 | ············return query; |
251 | ········} |
252 | |
253 | ········public string GeneratePrefetchQuery( Type parentType, IEnumerable<IPersistenceCapable> parents, string prefetch ) |
254 | ········{ |
255 | ············Class parentCls = this.mappings.FindClass( parentType ); |
256 | |
257 | ············StringBuilder sb = new StringBuilder( "SELECT " ); |
258 | ············List<Relation> relations = new List<Relation>(); |
259 | |
260 | ············if (prefetch.IndexOf('.') == -1) |
261 | ············{ |
262 | ················var rel = parentCls.FindRelation( prefetch ); |
263 | ················if (rel != null) |
264 | ····················relations.Add( rel ); |
265 | ············} |
266 | |
267 | ············new RelationContextGenerator( this.mappingsAccessor ).CreateContextForName( parentCls, prefetch, relations ); |
268 | |
269 | ············if (relations.Count == 0) |
270 | ················throw new NDOException( 76, $"Prefetch: Can't find relation mapping with name {prefetch} in class {parentCls.FullName}" ); |
271 | |
272 | ············Class cls = mappings.FindClass( relations[relations.Count - 1].ReferencedTypeName ); |
273 | ············var relationContext = new Dictionary<Relation, Class>(); |
274 | #warning Note: This code is not complete. The INNER JOIN is missing here. |
275 | ············string columnList = CreateQuerySelectPart( relationContext, false, cls, true, null ); |
276 | ············sb.Append( columnList ); |
277 | |
278 | ············sb.Append( ' ' ); |
279 | ············sb.Append( new FromGenerator( cls, relationContext ).GenerateFromExpression( null, relations ) ); |
280 | ············// We can be sure, that we have only one oid column here. |
281 | ············var oidList = String.Join( ",", from p in parents select p.NDOObjectId.Id.Values[0].ToString() ); |
282 | ············string where = $"WHERE {parentCls.Oid.OidColumns.First().GetQualifiedName()} IN ({oidList})"; |
283 | ············sb.Append( ' ' ); |
284 | ············sb.Append( where ); |
285 | |
286 | ············return sb.ToString(); |
287 | ········} |
288 | ····} |
289 | } |
290 |
New Commit (42d0ae7)
1 | using Microsoft.Extensions.DependencyInjection; |
2 | using NDO.Mapping; |
3 | using NDO.Query; |
4 | using NDOql.Expressions; |
5 | using System; |
6 | using System.Collections.Generic; |
7 | using System.Linq; |
8 | using System.Text; |
9 | using System.Text.RegularExpressions; |
10 | |
11 | namespace NDO.SqlPersistenceHandling |
12 | { |
13 | ····class SqlQueryGenerator : IQueryGenerator |
14 | ····{ |
15 | ········private readonly IMappingsAccessor mappingsAccessor; |
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( IMappingsAccessor mappingsAccessor ) |
22 | ········{ |
23 | ············this.mappingsAccessor = mappingsAccessor; |
24 | ············this.mappings = mappingsAccessor.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 | ········string ConstructQueryString( |
158 | ············Type resultType, |
159 | ············Dictionary<Relation, Class> relationContext, |
160 | ············OqlExpression expressionTree, |
161 | ············bool hollow, |
162 | ············bool hasSubclassResultsets, |
163 | ············List<QueryOrder> orderings, |
164 | ············int skip, |
165 | ············int take ) |
166 | ········{ |
167 | ············Class cls = this.mappings.FindClass( resultType ); |
168 | |
169 | ············StringBuilder sb = new StringBuilder( "SELECT " ); |
170 | |
171 | ············var from = new FromGenerator( cls, relationContext ).GenerateFromExpression( expressionTree ); |
172 | ············var qualifyWithTableName = from.IndexOf( "INNER JOIN" ) > -1; |
173 | |
174 | ············string columnList = this.selectPartCreator( relationContext, hollow, cls, qualifyWithTableName, this.additionalSelectPartData ); |
175 | ············sb.Append( columnList ); |
176 | |
177 | ············// If we need to sort a set of hollow results for different subclasses |
178 | ············// we need to fetch the ordering columns together with the oid columns |
179 | ············if (hollow && orderings.Count > 0 && hasSubclassResultsets) |
180 | ············{ |
181 | ················var orderCols = (from o in orderings select cls.FindField( o.FieldName ).Column.GetQualifiedName()).ToArray(); |
182 | ················columnList += ", " + String.Join( ", ", orderCols ); |
183 | ············} |
184 | |
185 | ············sb.Append( ' ' ); |
186 | ············sb.Append( from ); |
187 | ············string where = new WhereGenerator( cls, relationContext ).GenerateWhereClause( expressionTree ); |
188 | ············if (where != string.Empty) |
189 | ············{ |
190 | ················sb.Append( ' ' ); |
191 | ················sb.Append( where ); |
192 | ············} |
193 | |
194 | ············if (orderings.Count > 0) |
195 | ············{ |
196 | ················sb.Append( ' ' ); |
197 | ················sb.Append( new OrderGenerator( cls ).GenerateOrderClause( orderings, skip, take ) ); |
198 | ············} |
199 | |
200 | ············return sb.ToString(); |
201 | ········} |
202 | |
203 | ········private string CreateQuerySelectPart( Dictionary<Relation, Class> relationContext, bool hollow, Class cls, bool qualifyWithTableName, object additionalData ) |
204 | ········{ |
205 | ············var generator = SqlColumnListGenerator.Get( cls ); |
206 | |
207 | ············// We have to hack around a special behavior of SQLite, generating |
208 | ············// new columns with fully specified column names, if the query |
209 | ············// is a UNION············ |
210 | ············var generateAliasNames = relationContext.Count > 0 && cls.Provider.GetType().FullName.IndexOf( "Sqlite" ) > -1; |
211 | |
212 | ············return generator.Result( hollow, generateAliasNames, qualifyWithTableName ); |
213 | ········} |
214 | |
215 | ········private string CreateAggregateSelectPart( Dictionary<Relation, Class> relationContext, bool hollow, Class cls, bool qualifyWithTableName, object additionalData ) |
216 | ········{ |
217 | ············var tuple = (Tuple<string, AggregateType>)additionalData; |
218 | ············string field = tuple.Item1; |
219 | ············AggregateType aggregateType = tuple.Item2; |
220 | ············bool isStar = field == "*"; |
221 | |
222 | ············Column column = null; |
223 | ············if (field.ToLower().StartsWith( "oid" )) |
224 | ············{ |
225 | ················Regex regex = new Regex( @"\(\s*(\d+)\s*\)" ); |
226 | ················Match match = regex.Match( field ); |
227 | ················int index = 0; |
228 | ················if (match.Success) |
229 | ····················index = int.Parse( match.Groups[1].Value ); |
230 | ················column = ((OidColumn)cls.Oid.OidColumns[index]); |
231 | ············} |
232 | ············else |
233 | ············{ |
234 | ················if (!isStar) |
235 | ····················column = cls.FindField( field ).Column; |
236 | ············} |
237 | |
238 | ············var provider = cls.Provider; |
239 | ············var colName = isStar ? "*" : column.GetQualifiedName(); |
240 | |
241 | ············return $"{aggregateType.ToString().ToUpper()}({colName}) AS {provider.GetQuotedName( "AggrResult" )}"; |
242 | ········} |
243 | |
244 | ········public string GenerateAggregateQueryString( string field, QueryContextsEntry queryContextsEntry, OqlExpression expressionTree, bool hasSubclassResultsets, AggregateType aggregateType ) |
245 | ········{ |
246 | ············this.selectPartCreator = CreateAggregateSelectPart; |
247 | ············this.additionalSelectPartData = new Tuple<string,AggregateType>( field, aggregateType ); |
248 | |
249 | ············var query = InnerGenerateQueryString( queryContextsEntry, expressionTree, true, hasSubclassResultsets, new List<QueryOrder>(), 0, 0, null ); |
250 | ············return query; |
251 | ········} |
252 | |
253 | ········public string GeneratePrefetchQuery( Type parentType, IEnumerable<IPersistenceCapable> parents, string prefetch ) |
254 | ········{ |
255 | ············Class parentCls = this.mappings.FindClass( parentType ); |
256 | |
257 | ············StringBuilder sb = new StringBuilder( "SELECT " ); |
258 | ············List<Relation> relations = new List<Relation>(); |
259 | |
260 | ············if (prefetch.IndexOf('.') == -1) |
261 | ············{ |
262 | ················var rel = parentCls.FindRelation( prefetch ); |
263 | ················if (rel != null) |
264 | ····················relations.Add( rel ); |
265 | ············} |
266 | |
267 | ············new RelationContextGenerator( this.mappingsAccessor ).CreateContextForName( parentCls, prefetch, relations ); |
268 | |
269 | ············if (relations.Count == 0) |
270 | ················throw new NDOException( 76, $"Prefetch: Can't find relation mapping with name {prefetch} in class {parentCls.FullName}" ); |
271 | |
272 | ············Class cls = mappings.FindClass( relations[relations.Count - 1].ReferencedTypeName ); |
273 | ············var relationContext = new Dictionary<Relation, Class>(); |
274 | //TODO: Note: This code is not complete. The INNER JOIN is missing here. |
275 | ············string columnList = CreateQuerySelectPart( relationContext, false, cls, true, null ); |
276 | ············sb.Append( columnList ); |
277 | |
278 | ············sb.Append( ' ' ); |
279 | ············sb.Append( new FromGenerator( cls, relationContext ).GenerateFromExpression( null, relations ) ); |
280 | ············// We can be sure, that we have only one oid column here. |
281 | ············var oidList = String.Join( ",", from p in parents select p.NDOObjectId.Id.Values[0].ToString() ); |
282 | ············string where = $"WHERE {parentCls.Oid.OidColumns.First().GetQualifiedName()} IN ({oidList})"; |
283 | ············sb.Append( ' ' ); |
284 | ············sb.Append( where ); |
285 | |
286 | ············return sb.ToString(); |
287 | ········} |
288 | ····} |
289 | } |
290 |