Datei: NDODLL/SqlPersistenceHandling/WhereGenerator.cs

Last Commit (28c8c45)
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using NDOql.Expressions;
6 using NDO.Mapping;
7 using System.Globalization;
8 using NDO.Query;
9
10 namespace NDO.SqlPersistenceHandling
11 {
12 ····/// <summary>
13 ····/// Helper class to generate Column names of the select statement
14 ····/// </summary>
15 ····internal class WhereGenerator
16 ····{
17 ········Class cls;
18 ········Dictionary<Relation, Class> queryContext;
19 ········static readonly string anKey = "where";
20
21 ········internal WhereGenerator(Class cls, Dictionary<Relation, Class> queryContext)
22 ········{
23 ············this.cls = cls;
24 ············this.queryContext = queryContext;
25 ········}
26
27 ········internal string GenerateWhereClause(OqlExpression expressionTree)
28 ········{
29 ············if (expressionTree == null)
30 ················return string.Empty;
31
32 ············AnnotateExpressionTree( expressionTree );
33
34 ············// We remove the dummy expression here.
35 ············return "WHERE " + WhereString( expressionTree );
36 ········}
37
38 ········private void MoveParameterExpression(OqlExpression expressionTree, int fromOrdinal, int additionalSpace)
39 ········{
40 ············if (additionalSpace == 0)
41 ················return;
42
43 ············// Moves the ordinal numbers of ParameterExpressions above the current expression to leave parameters for
44 ············// the additional columns.
45 ············foreach (ParameterExpression parExp in expressionTree.GetAll( e =>
46 ················{
47 ····················ParameterExpression pe = e as ParameterExpression;
48 ····················if (pe == null)
49 ························return false;
50 ····················return pe.Ordinal > fromOrdinal;
51 ················} ))
52 ············{
53 ················parExp.Ordinal += additionalSpace;
54 ············}
55 ········}
56
57 ········private void AnnotateExpressionTree( OqlExpression expressionTree )
58 ········{
59 ············foreach (ParameterExpression parExp in expressionTree.GetAll(e=>e is ParameterExpression).ToList())
60 ············{
61 ················if (Guid.Empty.Equals( parExp.ParameterValue ) || DateTime.MinValue.Equals( parExp.ParameterValue ))
62 ················{
63 ····················var parent = parExp.Parent;
64 ····················if (parent.Operator == "=")
65 ····················{
66 ························var i = parent.Children.IndexOf(parExp);
67 ························var nullExp = new NamedConstantExpression( "NULL", false, 0, 0 );
68 ························parent.Children[i] = nullExp;
69 ························( (IManageExpression) nullExp ).SetParent( parent );
70 ························parent.Operator = "IS";
71 ····················}
72 ····················if (parent.Operator == "<>")
73 ····················{
74 ························var i = parent.Children.IndexOf(parExp);
75 ························var nullExp = new NamedConstantExpression( "NULL", true, 0, 0 );
76 ························parent.Children[i] = nullExp;
77 ························( (IManageExpression) nullExp ).SetParent( parent );
78 ························parent.Operator = "IS";
79 ····················}
80 ················}
81 ············}
82
83 ············foreach (IdentifierExpression exp in expressionTree.GetAll( e => e is IdentifierExpression ).ToList())
84 ············{
85 ················string[] arr = ((string)exp.Value).Split( '.' );
86 ················string fieldName = arr[arr.Length - 1]; // In case of embedded or value types this will be overwritten
87 ················Relation relation;
88 ················Class parentClass = GetParentClass( exp, arr, out fieldName, out relation );
89
90 ················if (fieldName == "oid")
91 ················{
92 ····················string[] oidColumns = (from c in parentClass.Oid.OidColumns select QualifiedColumnName.Get( c )).ToArray();
93 ····················if (relation != null)
94 ····················{
95 ························// In these cases we don't need the join to the table of the class owning the oid.
96 ························// It's sufficient to compare against the foreign keys stored in the owner class' table
97 ························// or in the mapping table
98 ························if (relation.MappingTable != null)
99 ························{
100 ····························oidColumns = (from c in relation.MappingTable.ChildForeignKeyColumns select QualifiedColumnName.Get( relation.MappingTable, c )).ToArray();
101 ························}
102 ························else if (relation.Multiplicity == RelationMultiplicity.Element)
103 ························{
104 ····························oidColumns = (from c in relation.ForeignKeyColumns select QualifiedColumnName.Get( c )).ToArray();
105 ························}
106 ····················}
107
108 ····················ParameterExpression parExp = exp.Siblings[0] as ParameterExpression;
109 ····················var isDirectSingleExpression = exp.Parent.Operator != "=" || oidColumns.Length == 1 && exp.Siblings[0] is ConstantExpression; // like "oid = 123"
110 ····················if (parExp == null && !isDirectSingleExpression)
111 ························throw new QueryException( 10010, $"Expression '{exp.ToString()}' resolves to an oid. It's sibling expression must be a ParameterExpression. But the sibling is {exp.Siblings[0]}" );
112
113 ····················object[] oidKeys = null;
114
115 ····················if (!isDirectSingleExpression)
116 ····················{
117 ························// split the ObjectId or an array into individual parameters
118 ························oidKeys = ExtractOidKeys( parExp );
119
120 ························// Now set the parameter value of the first column
121 ························if (oidKeys != null)
122 ····························parExp.ParameterValue = oidKeys[0];
123 ····················}
124
125 ····················if (oidColumns.Length > 1 && exp.Children.Count == 0)
126 ····················{
127 ························OqlExpression equalsExpression = exp.Parent;··// Must be a = expression like 'xxx.oid = {0}'.
128 ························// We need some additional parameters for the additional columns.
129 ························MoveParameterExpression( expressionTree, parExp.Ordinal, oidColumns.Length - 1 );
130
131 ························// Replace the parent expression with a new AND expression
132 ························OqlExpression andExpression = new OqlExpression( 0, 0 );
133 ························((IManageExpression)andExpression).SetParent( equalsExpression.Parent );
134 ························equalsExpression.Parent.Children.Remove( equalsExpression );
135 ························equalsExpression.Parent.Add( andExpression );
136 ························// We need to set Parent and Child explicitly.
137 ························// See comment in IManageExpression.SetParent.
138 ························// Reuse the original equality expression as first child of the AND expression
139 ························((IManageExpression)equalsExpression).SetParent( andExpression );
140 ························andExpression.Add( equalsExpression );
141 ························exp.SetAnnotation( anKey, oidColumns[0] );
142 ························int currentOrdinal = parExp.Ordinal;
143 ························// Now add the additional children of the AND expression
144 ························for (int i = 1; i < oidColumns.Length; i++)
145 ························{
146 ····························OqlExpression newParent = equalsExpression.DeepClone; // equality expression and it's both children
147 ····························andExpression.Add( newParent, "AND" );
148 ····························((IManageExpression)newParent).SetParent( andExpression );
149 ····························// Now patch the Annotation and a new parameter to the children
150 ····························IdentifierExpression newIdentExp = (IdentifierExpression)newParent.Children.Where( e => e is IdentifierExpression ).First();
151 ····························newIdentExp.SetAnnotation( anKey, oidColumns[i]);
152 ····························ParameterExpression newParExp = (ParameterExpression)newParent.Children.Where( e => e is ParameterExpression ).First();
153 ····························if (oidKeys != null)
154 ································newParExp.ParameterValue = oidKeys[i];
155 ····························newParExp.Ordinal = ++currentOrdinal;
156 ························}
157 ····················}
158 ····················else
159 ····················{
160 ························int index = 0;
161 ························if (exp.Children.Count > 0 && exp.Children[0] is IndexExpression)
162 ····························index = (int)exp.Children[0].Value;
163 ························if (index >= oidColumns.Length)
164 ····························throw new IndexOutOfRangeException("oid index exceeds oid column count");
165 ························exp.SetAnnotation( anKey, oidColumns[index]);····················}
166 ················}
167 ················else
168 ················{
169 ····················Field field = parentClass.FindField( fieldName );
170 ····················if (field != null)
171 ····················{
172 ························exp.SetAnnotation( anKey, QualifiedColumnName.Get( field.Column ) );
173 ····················}
174 ····················else
175 ····················{
176
177 ························Relation oneTooneRelation = parentClass.Relations.FirstOrDefault( r => r.Multiplicity == RelationMultiplicity.Element && (r.FieldName == fieldName || r.AccessorName == fieldName) );
178 ························if (oneTooneRelation != null)
179 ····························exp.SetAnnotation( anKey, QualifiedColumnName.Get( oneTooneRelation.ForeignKeyColumns.First() ) );
180
181 ························else
182 ····························throw new Exception( "Can't find Field mapping for " + fieldName + " in " + exp.Value );
183 ····················}
184 ················}
185 ············}
186 ········}
187
188 ········private static object[] ExtractOidKeys( ParameterExpression parExp )
189 ········{
190 ············object[] oidKeys = null;
191 ············if (parExp.ParameterValue != null)
192 ············{
193 ················oidKeys = ((parExp.ParameterValue as ObjectId)?.Id);
194 ················if (oidKeys == null)
195 ····················oidKeys = (parExp.ParameterValue as object[]);
196 ················if (oidKeys == null)
197 ····················oidKeys = new object[] { parExp.ParameterValue };
198 ············}
199
200 ············return oidKeys;
201 ········}
202
203 ········private Class GetParentClass( OqlExpression exp, string[] arr, out string fieldName, out Relation relation )
204 ········{
205 ············Class relClass = this.cls;
206 ············NDOMapping mappings = relClass.Parent;
207 ············int i;
208 ············relation = null;
209
210 ············for (i = 0; i < arr.Length - 1; i++)
211 ············{
212 ················relation = relClass.FindRelation( arr[i] );
213 ················if (relation == null)··// must be a value type or embedded type
214 ················{
215 ····················break;
216 ················}
217
218 ················if (this.queryContext.ContainsKey(relation))
219 ····················relClass = this.queryContext[relation];
220 ················else
221 ····················relClass = mappings.FindClass( relation.ReferencedType );
222 ············}
223
224 ············fieldName = String.Join( ".", arr, i, arr.Length - i );
225 ············if (relClass.FindField(fieldName) == null &&··fieldName.IndexOf('.') > -1)
226 ············{
227 ················// We might have a value type or embedded type here.
228 ················// These don't have accessor names.
229 ················// So we try to find the field via the standard column name.
230 ················// Note, that this doesn't work, if the column name was remapped.
231 ················// But we don't see another solution for this situation.
232 ················string tempName = fieldName.Replace( '.', '_' );
233 ················var field = relClass.Fields.FirstOrDefault( f => f.Column.Name == tempName );
234 ················if (field != null)
235 ····················fieldName = field.Name;
236 ············}
237
238 ············return relClass;
239 ········}
240
241 ········string WhereString(OqlExpression thisExpression)
242 ········{
243 ············StringBuilder sb = new StringBuilder();
244
245 ············if (thisExpression.IsTerminating || thisExpression is IdentifierExpression)
246 ············{
247 ················if (thisExpression.UnaryOp != null)
248 ················{
249 ····················sb.Append(thisExpression.UnaryOp);
250 ····················sb.Append(' ');
251 ················}
252 ················if (thisExpression.Value.GetType().IsPrimitive)
253 ················{
254 ····················sb.Append( Convert.ToString( thisExpression.Value, CultureInfo.InvariantCulture ) );
255 ················}
256 ················else
257 ················{
258 ····················IdentifierExpression iexp = thisExpression as IdentifierExpression;
259 ····················if (iexp != null)
260 ····················{
261 ························sb.Append( iexp.GetAnnotation<string>( anKey ) );
262 ····················}
263 ····················else
264 ····················{
265 ························sb.Append( thisExpression.Value.ToString() );
266 ····················}
267 ················}
268 ············}
269 ············else
270 ············{
271 ················if (thisExpression.UnaryOp != null)
272 ················{
273 ····················sb.Append( thisExpression.UnaryOp );
274 ····················sb.Append( ' ' );
275 ················}
 
 
 
 
276 ················if (thisExpression.HasBrackets)
277 ····················sb.Append('(');
278 ················string op1 = thisExpression.Operator;
279
280 ················int childExpEndIndex = thisExpression.Children.Count - 1;
281 ················for (int i = 0; i <= childExpEndIndex; i++)
282 ················{
283 ····················OqlExpression child = thisExpression.Children[i];
284 ····················sb.Append(WhereString(child));
285 ····················if (i < childExpEndIndex)
286 ····················{
287 ························if (op1 != ",")
288 ····························sb.Append(' ');
289 ························sb.Append(op1);
290 ························if (op1 == "BETWEEN")
291 ························{
292 ····························op1 = "AND";
293 ························}
294 ························sb.Append(' ');
295 ····················}
296 ················}
297 ················
298 ················if (thisExpression.HasBrackets)
299 ····················sb.Append(')');
300 ············}
301 ············return sb.ToString();
302 ········}
303 ····}
304 }
305
New Commit (b47b95e)
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using NDOql.Expressions;
6 using NDO.Mapping;
7 using System.Globalization;
8 using NDO.Query;
9
10 namespace NDO.SqlPersistenceHandling
11 {
12 ····/// <summary>
13 ····/// Helper class to generate Column names of the select statement
14 ····/// </summary>
15 ····internal class WhereGenerator
16 ····{
17 ········Class cls;
18 ········Dictionary<Relation, Class> queryContext;
19 ········static readonly string anKey = "where";
20
21 ········internal WhereGenerator(Class cls, Dictionary<Relation, Class> queryContext)
22 ········{
23 ············this.cls = cls;
24 ············this.queryContext = queryContext;
25 ········}
26
27 ········internal string GenerateWhereClause(OqlExpression expressionTree)
28 ········{
29 ············if (expressionTree == null)
30 ················return string.Empty;
31
32 ············AnnotateExpressionTree( expressionTree );
33
34 ············// We remove the dummy expression here.
35 ············return "WHERE " + WhereString( expressionTree );
36 ········}
37
38 ········private void MoveParameterExpression(OqlExpression expressionTree, int fromOrdinal, int additionalSpace)
39 ········{
40 ············if (additionalSpace == 0)
41 ················return;
42
43 ············// Moves the ordinal numbers of ParameterExpressions above the current expression to leave parameters for
44 ············// the additional columns.
45 ············foreach (ParameterExpression parExp in expressionTree.GetAll( e =>
46 ················{
47 ····················ParameterExpression pe = e as ParameterExpression;
48 ····················if (pe == null)
49 ························return false;
50 ····················return pe.Ordinal > fromOrdinal;
51 ················} ))
52 ············{
53 ················parExp.Ordinal += additionalSpace;
54 ············}
55 ········}
56
57 ········private void AnnotateExpressionTree( OqlExpression expressionTree )
58 ········{
59 ············foreach (ParameterExpression parExp in expressionTree.GetAll(e=>e is ParameterExpression).ToList())
60 ············{
61 ················if (Guid.Empty.Equals( parExp.ParameterValue ) || DateTime.MinValue.Equals( parExp.ParameterValue ))
62 ················{
63 ····················var parent = parExp.Parent;
64 ····················if (parent.Operator == "=")
65 ····················{
66 ························var i = parent.Children.IndexOf(parExp);
67 ························var nullExp = new NamedConstantExpression( "NULL", false, 0, 0 );
68 ························parent.Children[i] = nullExp;
69 ························( (IManageExpression) nullExp ).SetParent( parent );
70 ························parent.Operator = "IS";
71 ····················}
72 ····················if (parent.Operator == "<>")
73 ····················{
74 ························var i = parent.Children.IndexOf(parExp);
75 ························var nullExp = new NamedConstantExpression( "NULL", true, 0, 0 );
76 ························parent.Children[i] = nullExp;
77 ························( (IManageExpression) nullExp ).SetParent( parent );
78 ························parent.Operator = "IS";
79 ····················}
80 ················}
81 ············}
82
83 ············foreach (IdentifierExpression exp in expressionTree.GetAll( e => e is IdentifierExpression ).ToList())
84 ············{
85 ················string[] arr = ((string)exp.Value).Split( '.' );
86 ················string fieldName = arr[arr.Length - 1]; // In case of embedded or value types this will be overwritten
87 ················Relation relation;
88 ················Class parentClass = GetParentClass( exp, arr, out fieldName, out relation );
89
90 ················if (fieldName == "oid")
91 ················{
92 ····················string[] oidColumns = (from c in parentClass.Oid.OidColumns select QualifiedColumnName.Get( c )).ToArray();
93 ····················if (relation != null)
94 ····················{
95 ························// In these cases we don't need the join to the table of the class owning the oid.
96 ························// It's sufficient to compare against the foreign keys stored in the owner class' table
97 ························// or in the mapping table
98 ························if (relation.MappingTable != null)
99 ························{
100 ····························oidColumns = (from c in relation.MappingTable.ChildForeignKeyColumns select QualifiedColumnName.Get( relation.MappingTable, c )).ToArray();
101 ························}
102 ························else if (relation.Multiplicity == RelationMultiplicity.Element)
103 ························{
104 ····························oidColumns = (from c in relation.ForeignKeyColumns select QualifiedColumnName.Get( c )).ToArray();
105 ························}
106 ····················}
107
108 ····················ParameterExpression parExp = exp.Siblings[0] as ParameterExpression;
109 ····················var isDirectSingleExpression = exp.Parent.Operator != "=" || oidColumns.Length == 1 && exp.Siblings[0] is ConstantExpression; // like "oid = 123"
110 ····················if (parExp == null && !isDirectSingleExpression)
111 ························throw new QueryException( 10010, $"Expression '{exp.ToString()}' resolves to an oid. It's sibling expression must be a ParameterExpression. But the sibling is {exp.Siblings[0]}" );
112
113 ····················object[] oidKeys = null;
114
115 ····················if (!isDirectSingleExpression)
116 ····················{
117 ························// split the ObjectId or an array into individual parameters
118 ························oidKeys = ExtractOidKeys( parExp );
119
120 ························// Now set the parameter value of the first column
121 ························if (oidKeys != null)
122 ····························parExp.ParameterValue = oidKeys[0];
123 ····················}
124
125 ····················if (oidColumns.Length > 1 && exp.Children.Count == 0)
126 ····················{
127 ························OqlExpression equalsExpression = exp.Parent;··// Must be a = expression like 'xxx.oid = {0}'.
128 ························// We need some additional parameters for the additional columns.
129 ························MoveParameterExpression( expressionTree, parExp.Ordinal, oidColumns.Length - 1 );
130
131 ························// Replace the parent expression with a new AND expression
132 ························OqlExpression andExpression = new OqlExpression( 0, 0 );
133 ························((IManageExpression)andExpression).SetParent( equalsExpression.Parent );
134 ························equalsExpression.Parent.Children.Remove( equalsExpression );
135 ························equalsExpression.Parent.Add( andExpression );
136 ························// We need to set Parent and Child explicitly.
137 ························// See comment in IManageExpression.SetParent.
138 ························// Reuse the original equality expression as first child of the AND expression
139 ························((IManageExpression)equalsExpression).SetParent( andExpression );
140 ························andExpression.Add( equalsExpression );
141 ························exp.SetAnnotation( anKey, oidColumns[0] );
142 ························int currentOrdinal = parExp.Ordinal;
143 ························// Now add the additional children of the AND expression
144 ························for (int i = 1; i < oidColumns.Length; i++)
145 ························{
146 ····························OqlExpression newParent = equalsExpression.DeepClone; // equality expression and it's both children
147 ····························andExpression.Add( newParent, "AND" );
148 ····························((IManageExpression)newParent).SetParent( andExpression );
149 ····························// Now patch the Annotation and a new parameter to the children
150 ····························IdentifierExpression newIdentExp = (IdentifierExpression)newParent.Children.Where( e => e is IdentifierExpression ).First();
151 ····························newIdentExp.SetAnnotation( anKey, oidColumns[i]);
152 ····························ParameterExpression newParExp = (ParameterExpression)newParent.Children.Where( e => e is ParameterExpression ).First();
153 ····························if (oidKeys != null)
154 ································newParExp.ParameterValue = oidKeys[i];
155 ····························newParExp.Ordinal = ++currentOrdinal;
156 ························}
157 ····················}
158 ····················else
159 ····················{
160 ························int index = 0;
161 ························if (exp.Children.Count > 0 && exp.Children[0] is IndexExpression)
162 ····························index = (int)exp.Children[0].Value;
163 ························if (index >= oidColumns.Length)
164 ····························throw new IndexOutOfRangeException("oid index exceeds oid column count");
165 ························exp.SetAnnotation( anKey, oidColumns[index]);····················}
166 ················}
167 ················else
168 ················{
169 ····················Field field = parentClass.FindField( fieldName );
170 ····················if (field != null)
171 ····················{
172 ························exp.SetAnnotation( anKey, QualifiedColumnName.Get( field.Column ) );
173 ····················}
174 ····················else
175 ····················{
176
177 ························Relation oneTooneRelation = parentClass.Relations.FirstOrDefault( r => r.Multiplicity == RelationMultiplicity.Element && (r.FieldName == fieldName || r.AccessorName == fieldName) );
178 ························if (oneTooneRelation != null)
179 ····························exp.SetAnnotation( anKey, QualifiedColumnName.Get( oneTooneRelation.ForeignKeyColumns.First() ) );
180
181 ························else
182 ····························throw new Exception( "Can't find Field mapping for " + fieldName + " in " + exp.Value );
183 ····················}
184 ················}
185 ············}
186 ········}
187
188 ········private static object[] ExtractOidKeys( ParameterExpression parExp )
189 ········{
190 ············object[] oidKeys = null;
191 ············if (parExp.ParameterValue != null)
192 ············{
193 ················oidKeys = ((parExp.ParameterValue as ObjectId)?.Id);
194 ················if (oidKeys == null)
195 ····················oidKeys = (parExp.ParameterValue as object[]);
196 ················if (oidKeys == null)
197 ····················oidKeys = new object[] { parExp.ParameterValue };
198 ············}
199
200 ············return oidKeys;
201 ········}
202
203 ········private Class GetParentClass( OqlExpression exp, string[] arr, out string fieldName, out Relation relation )
204 ········{
205 ············Class relClass = this.cls;
206 ············NDOMapping mappings = relClass.Parent;
207 ············int i;
208 ············relation = null;
209
210 ············for (i = 0; i < arr.Length - 1; i++)
211 ············{
212 ················relation = relClass.FindRelation( arr[i] );
213 ················if (relation == null)··// must be a value type or embedded type
214 ················{
215 ····················break;
216 ················}
217
218 ················if (this.queryContext.ContainsKey(relation))
219 ····················relClass = this.queryContext[relation];
220 ················else
221 ····················relClass = mappings.FindClass( relation.ReferencedType );
222 ············}
223
224 ············fieldName = String.Join( ".", arr, i, arr.Length - i );
225 ············if (relClass.FindField(fieldName) == null &&··fieldName.IndexOf('.') > -1)
226 ············{
227 ················// We might have a value type or embedded type here.
228 ················// These don't have accessor names.
229 ················// So we try to find the field via the standard column name.
230 ················// Note, that this doesn't work, if the column name was remapped.
231 ················// But we don't see another solution for this situation.
232 ················string tempName = fieldName.Replace( '.', '_' );
233 ················var field = relClass.Fields.FirstOrDefault( f => f.Column.Name == tempName );
234 ················if (field != null)
235 ····················fieldName = field.Name;
236 ············}
237
238 ············return relClass;
239 ········}
240
241 ········string WhereString(OqlExpression thisExpression)
242 ········{
243 ············StringBuilder sb = new StringBuilder();
244
245 ············if (thisExpression.IsTerminating || thisExpression is IdentifierExpression)
246 ············{
247 ················if (thisExpression.UnaryOp != null)
248 ················{
249 ····················sb.Append(thisExpression.UnaryOp);
250 ····················sb.Append(' ');
251 ················}
252 ················if (thisExpression.Value.GetType().IsPrimitive)
253 ················{
254 ····················sb.Append( Convert.ToString( thisExpression.Value, CultureInfo.InvariantCulture ) );
255 ················}
256 ················else
257 ················{
258 ····················IdentifierExpression iexp = thisExpression as IdentifierExpression;
259 ····················if (iexp != null)
260 ····················{
261 ························sb.Append( iexp.GetAnnotation<string>( anKey ) );
262 ····················}
263 ····················else
264 ····················{
265 ························sb.Append( thisExpression.Value.ToString() );
266 ····················}
267 ················}
268 ············}
269 ············else
270 ············{
271 ················if (thisExpression.UnaryOp != null)
272 ················{
273 ····················sb.Append( thisExpression.UnaryOp );
274 ····················sb.Append( ' ' );
275 ················}
276 ················if (thisExpression is FunctionExpression)
277 ················{
278 ····················sb.Append( thisExpression.Value );
279 ················}
280 ················if (thisExpression.HasBrackets)
281 ····················sb.Append('(');
282 ················string op1 = thisExpression.Operator;
283
284 ················int childExpEndIndex = thisExpression.Children.Count - 1;
285 ················for (int i = 0; i <= childExpEndIndex; i++)
286 ················{
287 ····················OqlExpression child = thisExpression.Children[i];
288 ····················sb.Append(WhereString(child));
289 ····················if (i < childExpEndIndex)
290 ····················{
291 ························if (op1 != ",")
292 ····························sb.Append(' ');
293 ························sb.Append(op1);
294 ························if (op1 == "BETWEEN")
295 ························{
296 ····························op1 = "AND";
297 ························}
298 ························sb.Append(' ');
299 ····················}
300 ················}
301 ················
302 ················if (thisExpression.HasBrackets)
303 ····················sb.Append(')');
304 ············}
305 ············return sb.ToString();
306 ········}
307 ····}
308 }
309