Mercurial > pub > bltoolkit
comparison Source/Data/Sql/SqlQuery.cs @ 0:f990fcb411a9
Копия текущей версии из github
author | cin |
---|---|
date | Thu, 27 Mar 2014 21:46:09 +0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:f990fcb411a9 |
---|---|
1 using System; | |
2 using System.Collections; | |
3 using System.Collections.Generic; | |
4 using System.Diagnostics; | |
5 using System.IO; | |
6 using System.Linq; | |
7 using System.Text; | |
8 using System.Threading; | |
9 | |
10 using JetBrains.Annotations; | |
11 | |
12 namespace BLToolkit.Data.Sql | |
13 { | |
14 using Reflection; | |
15 | |
16 using FJoin = SqlQuery.FromClause.Join; | |
17 | |
18 [DebuggerDisplay("SQL = {SqlText}")] | |
19 public class SqlQuery : ISqlTableSource | |
20 { | |
21 #region Init | |
22 | |
23 static readonly Dictionary<string,object> _reservedWords = new Dictionary<string,object>(); | |
24 | |
25 static SqlQuery() | |
26 { | |
27 using (var stream = typeof(SqlQuery).Assembly.GetManifestResourceStream(typeof(SqlQuery), "ReservedWords.txt")) | |
28 using (var reader = new StreamReader(stream)) | |
29 { | |
30 /* | |
31 var words = reader.ReadToEnd().Replace(' ', '\n').Replace('\t', '\n').Split('\n'); | |
32 var q = from w in words where w.Length > 0 orderby w select w; | |
33 | |
34 var text = string.Join("\n", q.Distinct().ToArray()); | |
35 */ | |
36 | |
37 string s; | |
38 while ((s = reader.ReadLine()) != null) | |
39 _reservedWords.Add(s, s); | |
40 } | |
41 } | |
42 | |
43 public SqlQuery() | |
44 { | |
45 SourceID = Interlocked.Increment(ref SourceIDCounter); | |
46 | |
47 _select = new SelectClause (this); | |
48 _from = new FromClause (this); | |
49 _where = new WhereClause (this); | |
50 _groupBy = new GroupByClause(this); | |
51 _having = new WhereClause (this); | |
52 _orderBy = new OrderByClause(this); | |
53 } | |
54 | |
55 internal SqlQuery(int id) | |
56 { | |
57 SourceID = id; | |
58 } | |
59 | |
60 internal void Init( | |
61 InsertClause insert, | |
62 UpdateClause update, | |
63 DeleteClause delete, | |
64 SelectClause select, | |
65 FromClause from, | |
66 WhereClause where, | |
67 GroupByClause groupBy, | |
68 WhereClause having, | |
69 OrderByClause orderBy, | |
70 List<Union> unions, | |
71 SqlQuery parentSql, | |
72 bool parameterDependent, | |
73 List<SqlParameter> parameters) | |
74 { | |
75 _insert = insert; | |
76 _update = update; | |
77 _delete = delete; | |
78 _select = select; | |
79 _from = from; | |
80 _where = where; | |
81 _groupBy = groupBy; | |
82 _having = having; | |
83 _orderBy = orderBy; | |
84 _unions = unions; | |
85 ParentSql = parentSql; | |
86 IsParameterDependent = parameterDependent; | |
87 _parameters.AddRange(parameters); | |
88 | |
89 foreach (var col in select.Columns) | |
90 col.Parent = this; | |
91 | |
92 _select. SetSqlQuery(this); | |
93 _from. SetSqlQuery(this); | |
94 _where. SetSqlQuery(this); | |
95 _groupBy.SetSqlQuery(this); | |
96 _having. SetSqlQuery(this); | |
97 _orderBy.SetSqlQuery(this); | |
98 } | |
99 | |
100 readonly List<SqlParameter> _parameters = new List<SqlParameter>(); | |
101 public List<SqlParameter> Parameters | |
102 { | |
103 get { return _parameters; } | |
104 } | |
105 | |
106 private List<object> _properties; | |
107 public List<object> Properties | |
108 { | |
109 get { return _properties ?? (_properties = new List<object>()); } | |
110 } | |
111 | |
112 public bool IsParameterDependent { get; set; } | |
113 public SqlQuery ParentSql { get; set; } | |
114 | |
115 public bool IsSimple | |
116 { | |
117 get { return !Select.HasModifier && Where.IsEmpty && GroupBy.IsEmpty && Having.IsEmpty && OrderBy.IsEmpty; } | |
118 } | |
119 | |
120 private QueryType _queryType = QueryType.Select; | |
121 public QueryType QueryType | |
122 { | |
123 get { return _queryType; } | |
124 set { _queryType = value; } | |
125 } | |
126 | |
127 public bool IsSelect { get { return _queryType == QueryType.Select; } } | |
128 public bool IsDelete { get { return _queryType == QueryType.Delete; } } | |
129 public bool IsInsertOrUpdate { get { return _queryType == QueryType.InsertOrUpdate; } } | |
130 public bool IsInsert { get { return _queryType == QueryType.Insert || _queryType == QueryType.InsertOrUpdate; } } | |
131 public bool IsUpdate { get { return _queryType == QueryType.Update || _queryType == QueryType.InsertOrUpdate; } } | |
132 | |
133 #endregion | |
134 | |
135 #region Column | |
136 | |
137 public class Column : IEquatable<Column>, ISqlExpression, IChild<SqlQuery> | |
138 { | |
139 public Column(SqlQuery parent, ISqlExpression expression, string alias) | |
140 { | |
141 if (expression == null) throw new ArgumentNullException("expression"); | |
142 | |
143 Parent = parent; | |
144 Expression = expression; | |
145 _alias = alias; | |
146 | |
147 #if DEBUG | |
148 _columnNumber = ++_columnCounter; | |
149 #endif | |
150 } | |
151 | |
152 public Column(SqlQuery builder, ISqlExpression expression) | |
153 : this(builder, expression, null) | |
154 { | |
155 } | |
156 | |
157 #if DEBUG | |
158 readonly int _columnNumber; | |
159 static int _columnCounter; | |
160 #endif | |
161 | |
162 public ISqlExpression Expression { get; set; } | |
163 | |
164 internal string _alias; | |
165 public string Alias | |
166 { | |
167 get | |
168 { | |
169 if (_alias == null) | |
170 { | |
171 if (Expression is SqlField) | |
172 { | |
173 var field = (SqlField)Expression; | |
174 return field.Alias ?? field.PhysicalName; | |
175 } | |
176 | |
177 if (Expression is Column) | |
178 { | |
179 var col = (Column)Expression; | |
180 return col.Alias; | |
181 } | |
182 } | |
183 | |
184 return _alias; | |
185 } | |
186 set { _alias = value; } | |
187 } | |
188 | |
189 public bool Equals(Column other) | |
190 { | |
191 return Expression.Equals(other.Expression); | |
192 } | |
193 | |
194 #if OVERRIDETOSTRING | |
195 | |
196 public override string ToString() | |
197 { | |
198 return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); | |
199 } | |
200 | |
201 #endif | |
202 | |
203 #region ISqlExpression Members | |
204 | |
205 public bool CanBeNull() | |
206 { | |
207 return Expression.CanBeNull(); | |
208 } | |
209 | |
210 public bool Equals(ISqlExpression other, Func<ISqlExpression,ISqlExpression,bool> comparer) | |
211 { | |
212 if (this == other) | |
213 return true; | |
214 | |
215 return | |
216 other is Column && | |
217 Expression.Equals(((Column)other).Expression, comparer) && | |
218 comparer(this, other); | |
219 } | |
220 | |
221 public int Precedence | |
222 { | |
223 get { return Sql.Precedence.Primary; } | |
224 } | |
225 | |
226 public Type SystemType | |
227 { | |
228 get { return Expression.SystemType; } | |
229 } | |
230 | |
231 public ICloneableElement Clone(Dictionary<ICloneableElement, ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) | |
232 { | |
233 if (!doClone(this)) | |
234 return this; | |
235 | |
236 ICloneableElement clone; | |
237 | |
238 var parent = (SqlQuery)Parent.Clone(objectTree, doClone); | |
239 | |
240 if (!objectTree.TryGetValue(this, out clone)) | |
241 objectTree.Add(this, clone = new Column( | |
242 parent, | |
243 (ISqlExpression)Expression.Clone(objectTree, doClone), | |
244 _alias)); | |
245 | |
246 return clone; | |
247 } | |
248 | |
249 #endregion | |
250 | |
251 #region IEquatable<ISqlExpression> Members | |
252 | |
253 bool IEquatable<ISqlExpression>.Equals(ISqlExpression other) | |
254 { | |
255 if (this == other) | |
256 return true; | |
257 | |
258 return other is Column && Equals((Column)other); | |
259 } | |
260 | |
261 #endregion | |
262 | |
263 #region ISqlExpressionWalkable Members | |
264 | |
265 [Obsolete] | |
266 public ISqlExpression Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) | |
267 { | |
268 if (!(skipColumns && Expression is Column)) | |
269 Expression = Expression.Walk(skipColumns, func); | |
270 | |
271 return func(this); | |
272 } | |
273 | |
274 #endregion | |
275 | |
276 #region IChild<ISqlTableSource> Members | |
277 | |
278 string IChild<SqlQuery>.Name | |
279 { | |
280 get { return Alias; } | |
281 } | |
282 | |
283 public SqlQuery Parent { get; set; } | |
284 | |
285 #endregion | |
286 | |
287 #region IQueryElement Members | |
288 | |
289 public QueryElementType ElementType { get { return QueryElementType.Column; } } | |
290 | |
291 StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) | |
292 { | |
293 if (dic.ContainsKey(this)) | |
294 return sb.Append("..."); | |
295 | |
296 dic.Add(this, this); | |
297 | |
298 sb | |
299 .Append('t') | |
300 .Append(Parent.SourceID) | |
301 .Append("."); | |
302 | |
303 #if DEBUG | |
304 sb.Append('[').Append(_columnNumber).Append(']'); | |
305 #endif | |
306 | |
307 if (Expression is SqlQuery) | |
308 { | |
309 sb | |
310 .Append("(\n\t\t"); | |
311 var len = sb.Length; | |
312 Expression.ToString(sb, dic).Replace("\n", "\n\t\t", len, sb.Length - len); | |
313 sb.Append("\n\t)"); | |
314 } | |
315 /*else if (Expression is Column) | |
316 { | |
317 var col = (Column)Expression; | |
318 sb | |
319 .Append("t") | |
320 .Append(col.Parent.SourceID) | |
321 .Append(".") | |
322 .Append(col.Alias ?? "c" + (col.Parent.Select.Columns.IndexOf(col) + 1)); | |
323 }*/ | |
324 else | |
325 { | |
326 Expression.ToString(sb, dic); | |
327 } | |
328 | |
329 dic.Remove(this); | |
330 | |
331 return sb; | |
332 } | |
333 | |
334 #endregion | |
335 } | |
336 | |
337 #endregion | |
338 | |
339 #region TableSource | |
340 | |
341 public class TableSource : ISqlTableSource | |
342 { | |
343 public TableSource(ISqlTableSource source, string alias) | |
344 : this(source, alias, null) | |
345 { | |
346 } | |
347 | |
348 public TableSource(ISqlTableSource source, string alias, params JoinedTable[] joins) | |
349 { | |
350 if (source == null) throw new ArgumentNullException("source"); | |
351 | |
352 Source = source; | |
353 _alias = alias; | |
354 | |
355 if (joins != null) | |
356 _joins.AddRange(joins); | |
357 } | |
358 | |
359 public TableSource(ISqlTableSource source, string alias, IEnumerable<JoinedTable> joins) | |
360 { | |
361 if (source == null) throw new ArgumentNullException("source"); | |
362 | |
363 Source = source; | |
364 _alias = alias; | |
365 | |
366 if (joins != null) | |
367 _joins.AddRange(joins); | |
368 } | |
369 | |
370 public ISqlTableSource Source { get; set; } | |
371 public SqlTableType SqlTableType { get { return Source.SqlTableType; } } | |
372 | |
373 internal string _alias; | |
374 public string Alias | |
375 { | |
376 get | |
377 { | |
378 if (string.IsNullOrEmpty(_alias)) | |
379 { | |
380 if (Source is TableSource) | |
381 return (Source as TableSource).Alias; | |
382 | |
383 if (Source is SqlTable) | |
384 return ((SqlTable)Source).Alias; | |
385 } | |
386 | |
387 return _alias; | |
388 } | |
389 set { _alias = value; } | |
390 } | |
391 | |
392 public TableSource this[ISqlTableSource table] | |
393 { | |
394 get { return this[table, null]; } | |
395 } | |
396 | |
397 public TableSource this[ISqlTableSource table, string alias] | |
398 { | |
399 get | |
400 { | |
401 foreach (var tj in Joins) | |
402 { | |
403 var t = CheckTableSource(tj.Table, table, alias); | |
404 | |
405 if (t != null) | |
406 return t; | |
407 } | |
408 | |
409 return null; | |
410 } | |
411 } | |
412 | |
413 readonly List<JoinedTable> _joins = new List<JoinedTable>(); | |
414 public List<JoinedTable> Joins | |
415 { | |
416 get { return _joins; } | |
417 } | |
418 | |
419 public void ForEach(Action<TableSource> action, HashSet<SqlQuery> visitedQueries) | |
420 { | |
421 action(this); | |
422 foreach (var join in Joins) | |
423 join.Table.ForEach(action, visitedQueries); | |
424 | |
425 if (Source is SqlQuery && visitedQueries.Contains((SqlQuery)Source)) | |
426 ((SqlQuery)Source).ForEachTable(action, visitedQueries); | |
427 } | |
428 | |
429 public IEnumerable<ISqlTableSource> GetTables() | |
430 { | |
431 yield return Source; | |
432 | |
433 foreach (var join in Joins) | |
434 foreach (var table in join.Table.GetTables()) | |
435 yield return table; | |
436 } | |
437 | |
438 public int GetJoinNumber() | |
439 { | |
440 var n = Joins.Count; | |
441 | |
442 foreach (var join in Joins) | |
443 n += join.Table.GetJoinNumber(); | |
444 | |
445 return n; | |
446 } | |
447 | |
448 #if OVERRIDETOSTRING | |
449 | |
450 public override string ToString() | |
451 { | |
452 return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); | |
453 } | |
454 | |
455 #endif | |
456 | |
457 #region IEquatable<ISqlExpression> Members | |
458 | |
459 bool IEquatable<ISqlExpression>.Equals(ISqlExpression other) | |
460 { | |
461 return this == other; | |
462 } | |
463 | |
464 #endregion | |
465 | |
466 #region ISqlExpressionWalkable Members | |
467 | |
468 [Obsolete] | |
469 public ISqlExpression Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) | |
470 { | |
471 Source = (ISqlTableSource)Source.Walk(skipColumns, func); | |
472 | |
473 foreach (var t in Joins) | |
474 ((ISqlExpressionWalkable)t).Walk(skipColumns, func); | |
475 | |
476 return this; | |
477 } | |
478 | |
479 #endregion | |
480 | |
481 #region ISqlTableSource Members | |
482 | |
483 public int SourceID { get { return Source.SourceID; } } | |
484 public SqlField All { get { return Source.All; } } | |
485 | |
486 IList<ISqlExpression> ISqlTableSource.GetKeys(bool allIfEmpty) | |
487 { | |
488 return Source.GetKeys(allIfEmpty); | |
489 } | |
490 | |
491 #endregion | |
492 | |
493 #region ICloneableElement Members | |
494 | |
495 public ICloneableElement Clone(Dictionary<ICloneableElement, ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) | |
496 { | |
497 if (!doClone(this)) | |
498 return this; | |
499 | |
500 ICloneableElement clone; | |
501 | |
502 if (!objectTree.TryGetValue(this, out clone)) | |
503 { | |
504 var ts = new TableSource((ISqlTableSource)Source.Clone(objectTree, doClone), _alias); | |
505 | |
506 objectTree.Add(this, clone = ts); | |
507 | |
508 ts._joins.AddRange(_joins.ConvertAll(jt => (JoinedTable)jt.Clone(objectTree, doClone))); | |
509 } | |
510 | |
511 return clone; | |
512 } | |
513 | |
514 #endregion | |
515 | |
516 #region IQueryElement Members | |
517 | |
518 public QueryElementType ElementType { get { return QueryElementType.TableSource; } } | |
519 | |
520 StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) | |
521 { | |
522 if (sb.Length > 500) | |
523 return sb; | |
524 | |
525 if (dic.ContainsKey(this)) | |
526 return sb.Append("..."); | |
527 | |
528 dic.Add(this, this); | |
529 | |
530 if (Source is SqlQuery) | |
531 { | |
532 sb.Append("(\n\t"); | |
533 var len = sb.Length; | |
534 Source.ToString(sb, dic).Replace("\n", "\n\t", len, sb.Length - len); | |
535 sb.Append("\n)"); | |
536 } | |
537 else | |
538 Source.ToString(sb, dic); | |
539 | |
540 sb | |
541 .Append(" as t") | |
542 .Append(SourceID); | |
543 | |
544 foreach (IQueryElement join in Joins) | |
545 { | |
546 sb.AppendLine().Append('\t'); | |
547 var len = sb.Length; | |
548 join.ToString(sb, dic).Replace("\n", "\n\t", len, sb.Length - len); | |
549 } | |
550 | |
551 dic.Remove(this); | |
552 | |
553 return sb; | |
554 } | |
555 | |
556 #endregion | |
557 | |
558 #region ISqlExpression Members | |
559 | |
560 public bool CanBeNull() | |
561 { | |
562 return Source.CanBeNull(); | |
563 } | |
564 | |
565 public bool Equals(ISqlExpression other, Func<ISqlExpression,ISqlExpression,bool> comparer) | |
566 { | |
567 return this == other; | |
568 } | |
569 | |
570 public int Precedence { get { return Source.Precedence; } } | |
571 public Type SystemType { get { return Source.SystemType; } } | |
572 | |
573 #endregion | |
574 } | |
575 | |
576 #endregion | |
577 | |
578 #region TableJoin | |
579 | |
580 public enum JoinType | |
581 { | |
582 Auto, | |
583 Inner, | |
584 Left, | |
585 CrossApply, | |
586 OuterApply | |
587 } | |
588 | |
589 public class JoinedTable : IQueryElement, ISqlExpressionWalkable, ICloneableElement | |
590 { | |
591 public JoinedTable(JoinType joinType, TableSource table, bool isWeak, SearchCondition searchCondition) | |
592 { | |
593 JoinType = joinType; | |
594 Table = table; | |
595 IsWeak = isWeak; | |
596 Condition = searchCondition; | |
597 CanConvertApply = true; | |
598 } | |
599 | |
600 public JoinedTable(JoinType joinType, TableSource table, bool isWeak) | |
601 : this(joinType, table, isWeak, new SearchCondition()) | |
602 { | |
603 } | |
604 | |
605 public JoinedTable(JoinType joinType, ISqlTableSource table, string alias, bool isWeak) | |
606 : this(joinType, new TableSource(table, alias), isWeak) | |
607 { | |
608 } | |
609 | |
610 public JoinType JoinType { get; set; } | |
611 public TableSource Table { get; set; } | |
612 public SearchCondition Condition { get; private set; } | |
613 public bool IsWeak { get; set; } | |
614 public bool CanConvertApply { get; set; } | |
615 | |
616 public ICloneableElement Clone(Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) | |
617 { | |
618 if (!doClone(this)) | |
619 return this; | |
620 | |
621 ICloneableElement clone; | |
622 | |
623 if (!objectTree.TryGetValue(this, out clone)) | |
624 objectTree.Add(this, clone = new JoinedTable( | |
625 JoinType, | |
626 (TableSource)Table.Clone(objectTree, doClone), | |
627 IsWeak, | |
628 (SearchCondition)Condition.Clone(objectTree, doClone))); | |
629 | |
630 return clone; | |
631 } | |
632 | |
633 #if OVERRIDETOSTRING | |
634 | |
635 public override string ToString() | |
636 { | |
637 return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); | |
638 } | |
639 | |
640 #endif | |
641 | |
642 #region ISqlExpressionWalkable Members | |
643 | |
644 [Obsolete] | |
645 public ISqlExpression Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> action) | |
646 { | |
647 Condition = (SearchCondition)((ISqlExpressionWalkable)Condition).Walk(skipColumns, action); | |
648 | |
649 #pragma warning disable 0618 | |
650 Table.Walk(skipColumns, action); | |
651 #pragma warning restore 0618 | |
652 | |
653 return null; | |
654 } | |
655 | |
656 #endregion | |
657 | |
658 #region IQueryElement Members | |
659 | |
660 public QueryElementType ElementType { get { return QueryElementType.JoinedTable; } } | |
661 | |
662 StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) | |
663 { | |
664 if (dic.ContainsKey(this)) | |
665 return sb.Append("..."); | |
666 | |
667 dic.Add(this, this); | |
668 | |
669 switch (JoinType) | |
670 { | |
671 case JoinType.Inner : sb.Append("INNER JOIN "); break; | |
672 case JoinType.Left : sb.Append("LEFT JOIN "); break; | |
673 case JoinType.CrossApply : sb.Append("CROSS APPLY "); break; | |
674 case JoinType.OuterApply : sb.Append("OUTER APPLY "); break; | |
675 default : sb.Append("SOME JOIN "); break; | |
676 } | |
677 | |
678 ((IQueryElement)Table).ToString(sb, dic); | |
679 sb.Append(" ON "); | |
680 ((IQueryElement)Condition).ToString(sb, dic); | |
681 | |
682 dic.Remove(this); | |
683 | |
684 return sb; | |
685 } | |
686 | |
687 #endregion | |
688 } | |
689 | |
690 #endregion | |
691 | |
692 #region Predicate | |
693 | |
694 public abstract class Predicate : ISqlPredicate | |
695 { | |
696 public enum Operator | |
697 { | |
698 Equal, // = Is the operator used to test the equality between two expressions. | |
699 NotEqual, // <> != Is the operator used to test the condition of two expressions not being equal to each other. | |
700 Greater, // > Is the operator used to test the condition of one expression being greater than the other. | |
701 GreaterOrEqual, // >= Is the operator used to test the condition of one expression being greater than or equal to the other expression. | |
702 NotGreater, // !> Is the operator used to test the condition of one expression not being greater than the other expression. | |
703 Less, // < Is the operator used to test the condition of one expression being less than the other. | |
704 LessOrEqual, // <= Is the operator used to test the condition of one expression being less than or equal to the other expression. | |
705 NotLess // !< Is the operator used to test the condition of one expression not being less than the other expression. | |
706 } | |
707 | |
708 public class Expr : Predicate | |
709 { | |
710 public Expr([NotNull] ISqlExpression exp1, int precedence) | |
711 : base(precedence) | |
712 { | |
713 if (exp1 == null) throw new ArgumentNullException("exp1"); | |
714 | |
715 Expr1 = exp1; | |
716 } | |
717 | |
718 public Expr([NotNull] ISqlExpression exp1) | |
719 : base(exp1.Precedence) | |
720 { | |
721 if (exp1 == null) throw new ArgumentNullException("exp1"); | |
722 | |
723 Expr1 = exp1; | |
724 } | |
725 | |
726 public ISqlExpression Expr1 { get; set; } | |
727 | |
728 [Obsolete] | |
729 protected override void Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) | |
730 { | |
731 Expr1 = Expr1.Walk(skipColumns, func); | |
732 | |
733 if (Expr1 == null) | |
734 throw new InvalidOperationException(); | |
735 } | |
736 | |
737 public override bool CanBeNull() | |
738 { | |
739 return Expr1.CanBeNull(); | |
740 } | |
741 | |
742 protected override ICloneableElement Clone(Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) | |
743 { | |
744 if (!doClone(this)) | |
745 return this; | |
746 | |
747 ICloneableElement clone; | |
748 | |
749 if (!objectTree.TryGetValue(this, out clone)) | |
750 objectTree.Add(this, clone = new Expr((ISqlExpression)Expr1.Clone(objectTree, doClone), Precedence)); | |
751 | |
752 return clone; | |
753 } | |
754 | |
755 public override QueryElementType ElementType | |
756 { | |
757 get { return QueryElementType.ExprPredicate; } | |
758 } | |
759 | |
760 protected override void ToString(StringBuilder sb, Dictionary<IQueryElement, IQueryElement> dic) | |
761 { | |
762 Expr1.ToString(sb, dic); | |
763 } | |
764 } | |
765 | |
766 public class NotExpr : Expr | |
767 { | |
768 public NotExpr(ISqlExpression exp1, bool isNot, int precedence) | |
769 : base(exp1, precedence) | |
770 { | |
771 IsNot = isNot; | |
772 } | |
773 | |
774 public bool IsNot { get; set; } | |
775 | |
776 protected override ICloneableElement Clone(Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) | |
777 { | |
778 if (!doClone(this)) | |
779 return this; | |
780 | |
781 ICloneableElement clone; | |
782 | |
783 if (!objectTree.TryGetValue(this, out clone)) | |
784 objectTree.Add(this, clone = new NotExpr((ISqlExpression)Expr1.Clone(objectTree, doClone), IsNot, Precedence)); | |
785 | |
786 return clone; | |
787 } | |
788 | |
789 public override QueryElementType ElementType | |
790 { | |
791 get { return QueryElementType.NotExprPredicate; } | |
792 } | |
793 | |
794 protected override void ToString(StringBuilder sb, Dictionary<IQueryElement, IQueryElement> dic) | |
795 { | |
796 if (IsNot) sb.Append("NOT ("); | |
797 base.ToString(sb, dic); | |
798 if (IsNot) sb.Append(")"); | |
799 } | |
800 } | |
801 | |
802 // { expression { = | <> | != | > | >= | ! > | < | <= | !< } expression | |
803 // | |
804 public class ExprExpr : Expr | |
805 { | |
806 public ExprExpr(ISqlExpression exp1, Operator op, ISqlExpression exp2) | |
807 : base(exp1, Sql.Precedence.Comparison) | |
808 { | |
809 this.Operator = op; | |
810 Expr2 = exp2; | |
811 } | |
812 | |
813 public new Operator Operator { get; private set; } | |
814 public ISqlExpression Expr2 { get; internal set; } | |
815 | |
816 [Obsolete] | |
817 protected override void Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) | |
818 { | |
819 #pragma warning disable 0618 | |
820 base.Walk(skipColumns, func); | |
821 #pragma warning restore 0618 | |
822 Expr2 = Expr2.Walk(skipColumns, func); | |
823 } | |
824 | |
825 public override bool CanBeNull() | |
826 { | |
827 return base.CanBeNull() || Expr2.CanBeNull(); | |
828 } | |
829 | |
830 protected override ICloneableElement Clone(Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) | |
831 { | |
832 if (!doClone(this)) | |
833 return this; | |
834 | |
835 ICloneableElement clone; | |
836 | |
837 if (!objectTree.TryGetValue(this, out clone)) | |
838 objectTree.Add(this, clone = new ExprExpr( | |
839 (ISqlExpression)Expr1.Clone(objectTree, doClone), this.Operator, (ISqlExpression)Expr2.Clone(objectTree, doClone))); | |
840 | |
841 return clone; | |
842 } | |
843 | |
844 public override QueryElementType ElementType | |
845 { | |
846 get { return QueryElementType.ExprExprPredicate; } | |
847 } | |
848 | |
849 protected override void ToString(StringBuilder sb, Dictionary<IQueryElement, IQueryElement> dic) | |
850 { | |
851 Expr1.ToString(sb, dic); | |
852 | |
853 string op; | |
854 | |
855 switch (this.Operator) | |
856 { | |
857 case Operator.Equal : op = "="; break; | |
858 case Operator.NotEqual : op = "<>"; break; | |
859 case Operator.Greater : op = ">"; break; | |
860 case Operator.GreaterOrEqual: op = ">="; break; | |
861 case Operator.NotGreater : op = "!>"; break; | |
862 case Operator.Less : op = "<"; break; | |
863 case Operator.LessOrEqual : op = "<="; break; | |
864 case Operator.NotLess : op = "!<"; break; | |
865 default: throw new InvalidOperationException(); | |
866 } | |
867 | |
868 sb.Append(" ").Append(op).Append(" "); | |
869 | |
870 Expr2.ToString(sb, dic); | |
871 } | |
872 } | |
873 | |
874 // string_expression [ NOT ] LIKE string_expression [ ESCAPE 'escape_character' ] | |
875 // | |
876 public class Like : NotExpr | |
877 { | |
878 public Like(ISqlExpression exp1, bool isNot, ISqlExpression exp2, ISqlExpression escape) | |
879 : base(exp1, isNot, Sql.Precedence.Comparison) | |
880 { | |
881 Expr2 = exp2; | |
882 Escape = escape; | |
883 } | |
884 | |
885 public ISqlExpression Expr2 { get; internal set; } | |
886 public ISqlExpression Escape { get; internal set; } | |
887 | |
888 [Obsolete] | |
889 protected override void Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) | |
890 { | |
891 #pragma warning disable 0618 | |
892 base.Walk(skipColumns, func); | |
893 #pragma warning restore 0618 | |
894 Expr2 = Expr2.Walk(skipColumns, func); | |
895 | |
896 if (Escape != null) | |
897 Escape = Escape.Walk(skipColumns, func); | |
898 } | |
899 | |
900 protected override ICloneableElement Clone(Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) | |
901 { | |
902 if (!doClone(this)) | |
903 return this; | |
904 | |
905 ICloneableElement clone; | |
906 | |
907 if (!objectTree.TryGetValue(this, out clone)) | |
908 objectTree.Add(this, clone = new Like( | |
909 (ISqlExpression)Expr1.Clone(objectTree, doClone), IsNot, (ISqlExpression)Expr2.Clone(objectTree, doClone), Escape)); | |
910 | |
911 return clone; | |
912 } | |
913 | |
914 public override QueryElementType ElementType | |
915 { | |
916 get { return QueryElementType.LikePredicate; } | |
917 } | |
918 | |
919 protected override void ToString(StringBuilder sb, Dictionary<IQueryElement, IQueryElement> dic) | |
920 { | |
921 Expr1.ToString(sb, dic); | |
922 | |
923 if (IsNot) sb.Append(" NOT"); | |
924 sb.Append(" LIKE "); | |
925 | |
926 Expr2.ToString(sb, dic); | |
927 | |
928 if (Escape != null) | |
929 { | |
930 sb.Append(" ESCAPE "); | |
931 Escape.ToString(sb, dic); | |
932 } | |
933 } | |
934 } | |
935 | |
936 // expression [ NOT ] BETWEEN expression AND expression | |
937 // | |
938 public class Between : NotExpr | |
939 { | |
940 public Between(ISqlExpression exp1, bool isNot, ISqlExpression exp2, ISqlExpression exp3) | |
941 : base(exp1, isNot, Sql.Precedence.Comparison) | |
942 { | |
943 Expr2 = exp2; | |
944 Expr3 = exp3; | |
945 } | |
946 | |
947 public ISqlExpression Expr2 { get; internal set; } | |
948 public ISqlExpression Expr3 { get; internal set; } | |
949 | |
950 [Obsolete] | |
951 protected override void Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) | |
952 { | |
953 #pragma warning disable 0618 | |
954 base.Walk(skipColumns, func); | |
955 #pragma warning restore 0618 | |
956 Expr2 = Expr2.Walk(skipColumns, func); | |
957 Expr3 = Expr3.Walk(skipColumns, func); | |
958 } | |
959 | |
960 protected override ICloneableElement Clone(Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) | |
961 { | |
962 if (!doClone(this)) | |
963 return this; | |
964 | |
965 ICloneableElement clone; | |
966 | |
967 if (!objectTree.TryGetValue(this, out clone)) | |
968 objectTree.Add(this, clone = new Between( | |
969 (ISqlExpression)Expr1.Clone(objectTree, doClone), | |
970 IsNot, | |
971 (ISqlExpression)Expr2.Clone(objectTree, doClone), | |
972 (ISqlExpression)Expr3.Clone(objectTree, doClone))); | |
973 | |
974 return clone; | |
975 } | |
976 | |
977 public override QueryElementType ElementType | |
978 { | |
979 get { return QueryElementType.BetweenPredicate; } | |
980 } | |
981 | |
982 protected override void ToString(StringBuilder sb, Dictionary<IQueryElement, IQueryElement> dic) | |
983 { | |
984 Expr1.ToString(sb, dic); | |
985 | |
986 if (IsNot) sb.Append(" NOT"); | |
987 sb.Append(" BETWEEN "); | |
988 | |
989 Expr2.ToString(sb, dic); | |
990 sb.Append(" AND "); | |
991 Expr3.ToString(sb, dic); | |
992 } | |
993 } | |
994 | |
995 // expression IS [ NOT ] NULL | |
996 // | |
997 public class IsNull : NotExpr | |
998 { | |
999 public IsNull(ISqlExpression exp1, bool isNot) | |
1000 : base(exp1, isNot, Sql.Precedence.Comparison) | |
1001 { | |
1002 } | |
1003 | |
1004 protected override ICloneableElement Clone(Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) | |
1005 { | |
1006 if (!doClone(this)) | |
1007 return this; | |
1008 | |
1009 ICloneableElement clone; | |
1010 | |
1011 if (!objectTree.TryGetValue(this, out clone)) | |
1012 objectTree.Add(this, clone = new IsNull((ISqlExpression)Expr1.Clone(objectTree, doClone), IsNot)); | |
1013 | |
1014 return clone; | |
1015 } | |
1016 | |
1017 protected override void ToString(StringBuilder sb, Dictionary<IQueryElement, IQueryElement> dic) | |
1018 { | |
1019 Expr1.ToString(sb, dic); | |
1020 sb | |
1021 .Append(" IS ") | |
1022 .Append(IsNot ? "NOT " : "") | |
1023 .Append("NULL"); | |
1024 } | |
1025 | |
1026 public override QueryElementType ElementType | |
1027 { | |
1028 get { return QueryElementType.IsNullPredicate; } | |
1029 } | |
1030 } | |
1031 | |
1032 // expression [ NOT ] IN ( subquery | expression [ ,...n ] ) | |
1033 // | |
1034 public class InSubQuery : NotExpr | |
1035 { | |
1036 public InSubQuery(ISqlExpression exp1, bool isNot, SqlQuery subQuery) | |
1037 : base(exp1, isNot, Sql.Precedence.Comparison) | |
1038 { | |
1039 SubQuery = subQuery; | |
1040 } | |
1041 | |
1042 public SqlQuery SubQuery { get; private set; } | |
1043 | |
1044 [Obsolete] | |
1045 protected override void Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) | |
1046 { | |
1047 #pragma warning disable 0618 | |
1048 base.Walk(skipColumns, func); | |
1049 #pragma warning restore 0618 | |
1050 SubQuery = (SqlQuery)((ISqlExpression)SubQuery).Walk(skipColumns, func); | |
1051 } | |
1052 | |
1053 protected override ICloneableElement Clone(Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) | |
1054 { | |
1055 if (!doClone(this)) | |
1056 return this; | |
1057 | |
1058 ICloneableElement clone; | |
1059 | |
1060 if (!objectTree.TryGetValue(this, out clone)) | |
1061 objectTree.Add(this, clone = new InSubQuery( | |
1062 (ISqlExpression)Expr1.Clone(objectTree, doClone), | |
1063 IsNot, | |
1064 (SqlQuery)SubQuery.Clone(objectTree, doClone))); | |
1065 | |
1066 return clone; | |
1067 } | |
1068 | |
1069 public override QueryElementType ElementType | |
1070 { | |
1071 get { return QueryElementType.InSubQueryPredicate; } | |
1072 } | |
1073 | |
1074 protected override void ToString(StringBuilder sb, Dictionary<IQueryElement, IQueryElement> dic) | |
1075 { | |
1076 Expr1.ToString(sb, dic); | |
1077 | |
1078 if (IsNot) sb.Append(" NOT"); | |
1079 sb.Append(" IN ("); | |
1080 | |
1081 ((IQueryElement)SubQuery).ToString(sb, dic); | |
1082 sb.Append(")"); | |
1083 } | |
1084 } | |
1085 | |
1086 public class InList : NotExpr | |
1087 { | |
1088 public InList(ISqlExpression exp1, bool isNot, params ISqlExpression[] values) | |
1089 : base(exp1, isNot, Sql.Precedence.Comparison) | |
1090 { | |
1091 if (values != null && values.Length > 0) | |
1092 _values.AddRange(values); | |
1093 } | |
1094 | |
1095 public InList(ISqlExpression exp1, bool isNot, IEnumerable<ISqlExpression> values) | |
1096 : base(exp1, isNot, Sql.Precedence.Comparison) | |
1097 { | |
1098 if (values != null) | |
1099 _values.AddRange(values); | |
1100 } | |
1101 | |
1102 readonly List<ISqlExpression> _values = new List<ISqlExpression>(); | |
1103 public List<ISqlExpression> Values { get { return _values; } } | |
1104 | |
1105 [Obsolete] | |
1106 protected override void Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> action) | |
1107 { | |
1108 #pragma warning disable 0618 | |
1109 base.Walk(skipColumns, action); | |
1110 #pragma warning restore 0618 | |
1111 for (var i = 0; i < _values.Count; i++) | |
1112 _values[i] = _values[i].Walk(skipColumns, action); | |
1113 } | |
1114 | |
1115 protected override ICloneableElement Clone(Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) | |
1116 { | |
1117 if (!doClone(this)) | |
1118 return this; | |
1119 | |
1120 ICloneableElement clone; | |
1121 | |
1122 if (!objectTree.TryGetValue(this, out clone)) | |
1123 { | |
1124 objectTree.Add(this, clone = new InList( | |
1125 (ISqlExpression)Expr1.Clone(objectTree, doClone), | |
1126 IsNot, | |
1127 _values.ConvertAll(e => (ISqlExpression)e.Clone(objectTree, doClone)).ToArray())); | |
1128 } | |
1129 | |
1130 return clone; | |
1131 } | |
1132 | |
1133 public override QueryElementType ElementType | |
1134 { | |
1135 get { return QueryElementType.InListPredicate; } | |
1136 } | |
1137 | |
1138 protected override void ToString(StringBuilder sb, Dictionary<IQueryElement, IQueryElement> dic) | |
1139 { | |
1140 Expr1.ToString(sb, dic); | |
1141 | |
1142 if (IsNot) sb.Append(" NOT"); | |
1143 sb.Append(" IN ("); | |
1144 | |
1145 foreach (var value in Values) | |
1146 { | |
1147 value.ToString(sb, dic); | |
1148 sb.Append(','); | |
1149 } | |
1150 | |
1151 if (Values.Count > 0) | |
1152 sb.Length--; | |
1153 | |
1154 sb.Append(")"); | |
1155 } | |
1156 } | |
1157 | |
1158 // CONTAINS ( { column | * } , '< contains_search_condition >' ) | |
1159 // FREETEXT ( { column | * } , 'freetext_string' ) | |
1160 // expression { = | <> | != | > | >= | !> | < | <= | !< } { ALL | SOME | ANY } ( subquery ) | |
1161 // EXISTS ( subquery ) | |
1162 | |
1163 public class FuncLike : Predicate | |
1164 { | |
1165 public FuncLike(SqlFunction func) | |
1166 : base(func.Precedence) | |
1167 { | |
1168 Function = func; | |
1169 } | |
1170 | |
1171 public SqlFunction Function { get; private set; } | |
1172 | |
1173 [Obsolete] | |
1174 protected override void Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) | |
1175 { | |
1176 Function = (SqlFunction)((ISqlExpression)Function).Walk(skipColumns, func); | |
1177 } | |
1178 | |
1179 public override bool CanBeNull() | |
1180 { | |
1181 return Function.CanBeNull(); | |
1182 } | |
1183 | |
1184 protected override ICloneableElement Clone(Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) | |
1185 { | |
1186 if (!doClone(this)) | |
1187 return this; | |
1188 | |
1189 ICloneableElement clone; | |
1190 | |
1191 if (!objectTree.TryGetValue(this, out clone)) | |
1192 objectTree.Add(this, clone = new FuncLike((SqlFunction)Function.Clone(objectTree, doClone))); | |
1193 | |
1194 return clone; | |
1195 } | |
1196 | |
1197 public override QueryElementType ElementType | |
1198 { | |
1199 get { return QueryElementType.FuncLikePredicate; } | |
1200 } | |
1201 | |
1202 protected override void ToString(StringBuilder sb, Dictionary<IQueryElement, IQueryElement> dic) | |
1203 { | |
1204 ((IQueryElement)Function).ToString(sb, dic); | |
1205 } | |
1206 } | |
1207 | |
1208 #region Overrides | |
1209 | |
1210 #if OVERRIDETOSTRING | |
1211 | |
1212 public override string ToString() | |
1213 { | |
1214 return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); | |
1215 } | |
1216 | |
1217 #endif | |
1218 | |
1219 #endregion | |
1220 | |
1221 protected Predicate(int precedence) | |
1222 { | |
1223 Precedence = precedence; | |
1224 } | |
1225 | |
1226 #region IPredicate Members | |
1227 | |
1228 public int Precedence { get; private set; } | |
1229 | |
1230 public abstract bool CanBeNull(); | |
1231 protected abstract ICloneableElement Clone (Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone); | |
1232 [Obsolete] | |
1233 protected abstract void Walk (bool skipColumns, Func<ISqlExpression,ISqlExpression> action); | |
1234 | |
1235 [Obsolete] | |
1236 ISqlExpression ISqlExpressionWalkable.Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) | |
1237 { | |
1238 Walk(skipColumns, func); | |
1239 return null; | |
1240 } | |
1241 | |
1242 ICloneableElement ICloneableElement.Clone(Dictionary<ICloneableElement, ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) | |
1243 { | |
1244 if (!doClone(this)) | |
1245 return this; | |
1246 | |
1247 return Clone(objectTree, doClone); | |
1248 } | |
1249 | |
1250 #endregion | |
1251 | |
1252 #region IQueryElement Members | |
1253 | |
1254 public abstract QueryElementType ElementType { get; } | |
1255 | |
1256 protected abstract void ToString(StringBuilder sb, Dictionary<IQueryElement, IQueryElement> dic); | |
1257 | |
1258 StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) | |
1259 { | |
1260 if (dic.ContainsKey(this)) | |
1261 return sb.Append("..."); | |
1262 | |
1263 dic.Add(this, this); | |
1264 ToString(sb, dic); | |
1265 dic.Remove(this); | |
1266 | |
1267 return sb; | |
1268 } | |
1269 | |
1270 #endregion | |
1271 } | |
1272 | |
1273 #endregion | |
1274 | |
1275 #region Condition | |
1276 | |
1277 public class Condition : IQueryElement, ICloneableElement | |
1278 { | |
1279 public Condition(bool isNot, ISqlPredicate predicate) | |
1280 { | |
1281 IsNot = isNot; | |
1282 Predicate = predicate; | |
1283 } | |
1284 | |
1285 public Condition(bool isNot, ISqlPredicate predicate, bool isOr) | |
1286 { | |
1287 IsNot = isNot; | |
1288 Predicate = predicate; | |
1289 IsOr = isOr; | |
1290 } | |
1291 | |
1292 public bool IsNot { get; set; } | |
1293 public ISqlPredicate Predicate { get; set; } | |
1294 public bool IsOr { get; set; } | |
1295 | |
1296 public int Precedence | |
1297 { | |
1298 get | |
1299 { | |
1300 return | |
1301 IsNot ? Sql.Precedence.LogicalNegation : | |
1302 IsOr ? Sql.Precedence.LogicalDisjunction : | |
1303 Sql.Precedence.LogicalConjunction; | |
1304 } | |
1305 } | |
1306 | |
1307 public ICloneableElement Clone(Dictionary<ICloneableElement, ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) | |
1308 { | |
1309 if (!doClone(this)) | |
1310 return this; | |
1311 | |
1312 ICloneableElement clone; | |
1313 | |
1314 if (!objectTree.TryGetValue(this, out clone)) | |
1315 objectTree.Add(this, clone = new Condition(IsNot, (ISqlPredicate)Predicate.Clone(objectTree, doClone), IsOr)); | |
1316 | |
1317 return clone; | |
1318 } | |
1319 | |
1320 public bool CanBeNull() | |
1321 { | |
1322 return Predicate.CanBeNull(); | |
1323 } | |
1324 | |
1325 #if OVERRIDETOSTRING | |
1326 | |
1327 public override string ToString() | |
1328 { | |
1329 return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); | |
1330 } | |
1331 | |
1332 #endif | |
1333 | |
1334 #region IQueryElement Members | |
1335 | |
1336 public QueryElementType ElementType { get { return QueryElementType.Condition; } } | |
1337 | |
1338 StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) | |
1339 { | |
1340 if (dic.ContainsKey(this)) | |
1341 return sb.Append("..."); | |
1342 | |
1343 dic.Add(this, this); | |
1344 | |
1345 sb.Append('('); | |
1346 | |
1347 if (IsNot) sb.Append("NOT "); | |
1348 | |
1349 Predicate.ToString(sb, dic); | |
1350 sb.Append(')').Append(IsOr ? " OR " : " AND "); | |
1351 | |
1352 dic.Remove(this); | |
1353 | |
1354 return sb; | |
1355 } | |
1356 | |
1357 #endregion | |
1358 } | |
1359 | |
1360 #endregion | |
1361 | |
1362 #region SearchCondition | |
1363 | |
1364 public class SearchCondition : ConditionBase<SearchCondition, SearchCondition.Next>, ISqlPredicate, ISqlExpression | |
1365 { | |
1366 public SearchCondition() | |
1367 { | |
1368 } | |
1369 | |
1370 public SearchCondition(IEnumerable<Condition> list) | |
1371 { | |
1372 _conditions.AddRange(list); | |
1373 } | |
1374 | |
1375 public SearchCondition(params Condition[] list) | |
1376 { | |
1377 _conditions.AddRange(list); | |
1378 } | |
1379 | |
1380 public class Next | |
1381 { | |
1382 internal Next(SearchCondition parent) | |
1383 { | |
1384 _parent = parent; | |
1385 } | |
1386 | |
1387 readonly SearchCondition _parent; | |
1388 | |
1389 public SearchCondition Or { get { return _parent.SetOr(true); } } | |
1390 public SearchCondition And { get { return _parent.SetOr(false); } } | |
1391 | |
1392 public ISqlExpression ToExpr() { return _parent; } | |
1393 } | |
1394 | |
1395 readonly List<Condition> _conditions = new List<Condition>(); | |
1396 public List<Condition> Conditions | |
1397 { | |
1398 get { return _conditions; } | |
1399 } | |
1400 | |
1401 protected override SearchCondition Search | |
1402 { | |
1403 get { return this; } | |
1404 } | |
1405 | |
1406 protected override Next GetNext() | |
1407 { | |
1408 return new Next(this); | |
1409 } | |
1410 | |
1411 #region Overrides | |
1412 | |
1413 #if OVERRIDETOSTRING | |
1414 | |
1415 public override string ToString() | |
1416 { | |
1417 return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); | |
1418 } | |
1419 | |
1420 #endif | |
1421 | |
1422 #endregion | |
1423 | |
1424 #region IPredicate Members | |
1425 | |
1426 public int Precedence | |
1427 { | |
1428 get | |
1429 { | |
1430 if (_conditions.Count == 0) return Sql.Precedence.Unknown; | |
1431 if (_conditions.Count == 1) return _conditions[0].Precedence; | |
1432 | |
1433 return _conditions.Select(_ => | |
1434 _.IsNot ? Sql.Precedence.LogicalNegation : | |
1435 _.IsOr ? Sql.Precedence.LogicalDisjunction : | |
1436 Sql.Precedence.LogicalConjunction).Min(); | |
1437 } | |
1438 } | |
1439 | |
1440 public Type SystemType | |
1441 { | |
1442 get { return typeof(bool); } | |
1443 } | |
1444 | |
1445 [Obsolete] | |
1446 ISqlExpression ISqlExpressionWalkable.Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) | |
1447 { | |
1448 foreach (var condition in Conditions) | |
1449 condition.Predicate.Walk(skipColumns, func); | |
1450 | |
1451 return func(this); | |
1452 } | |
1453 | |
1454 #endregion | |
1455 | |
1456 #region IEquatable<ISqlExpression> Members | |
1457 | |
1458 bool IEquatable<ISqlExpression>.Equals(ISqlExpression other) | |
1459 { | |
1460 return this == other; | |
1461 } | |
1462 | |
1463 #endregion | |
1464 | |
1465 #region ISqlExpression Members | |
1466 | |
1467 public bool CanBeNull() | |
1468 { | |
1469 foreach (var c in Conditions) | |
1470 if (c.CanBeNull()) | |
1471 return true; | |
1472 | |
1473 return false; | |
1474 } | |
1475 | |
1476 public bool Equals(ISqlExpression other, Func<ISqlExpression,ISqlExpression,bool> comparer) | |
1477 { | |
1478 return this == other; | |
1479 } | |
1480 | |
1481 #endregion | |
1482 | |
1483 #region ICloneableElement Members | |
1484 | |
1485 public ICloneableElement Clone(Dictionary<ICloneableElement, ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) | |
1486 { | |
1487 if (!doClone(this)) | |
1488 return this; | |
1489 | |
1490 ICloneableElement clone; | |
1491 | |
1492 if (!objectTree.TryGetValue(this, out clone)) | |
1493 { | |
1494 var sc = new SearchCondition(); | |
1495 | |
1496 objectTree.Add(this, clone = sc); | |
1497 | |
1498 sc._conditions.AddRange(_conditions.ConvertAll(c => (Condition)c.Clone(objectTree, doClone))); | |
1499 } | |
1500 | |
1501 return clone; | |
1502 } | |
1503 | |
1504 #endregion | |
1505 | |
1506 #region IQueryElement Members | |
1507 | |
1508 public QueryElementType ElementType { get { return QueryElementType.SearchCondition; } } | |
1509 | |
1510 StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) | |
1511 { | |
1512 if (dic.ContainsKey(this)) | |
1513 return sb.Append("..."); | |
1514 | |
1515 dic.Add(this, this); | |
1516 | |
1517 foreach (IQueryElement c in Conditions) | |
1518 c.ToString(sb, dic); | |
1519 | |
1520 if (Conditions.Count > 0) | |
1521 sb.Length -= 4; | |
1522 | |
1523 dic.Remove(this); | |
1524 | |
1525 return sb; | |
1526 } | |
1527 | |
1528 #endregion | |
1529 } | |
1530 | |
1531 #endregion | |
1532 | |
1533 #region ConditionBase | |
1534 | |
1535 interface IConditionExpr<T> | |
1536 { | |
1537 T Expr (ISqlExpression expr); | |
1538 T Field (SqlField field); | |
1539 T SubQuery(SqlQuery sqlQuery); | |
1540 T Value (object value); | |
1541 } | |
1542 | |
1543 public abstract class ConditionBase<T1,T2> : IConditionExpr<ConditionBase<T1,T2>.Expr_> | |
1544 where T1 : ConditionBase<T1,T2> | |
1545 { | |
1546 public class Expr_ | |
1547 { | |
1548 internal Expr_(ConditionBase<T1,T2> condition, bool isNot, ISqlExpression expr) | |
1549 { | |
1550 _condition = condition; | |
1551 _isNot = isNot; | |
1552 _expr = expr; | |
1553 } | |
1554 | |
1555 readonly ConditionBase<T1,T2> _condition; | |
1556 readonly bool _isNot; | |
1557 readonly ISqlExpression _expr; | |
1558 | |
1559 T2 Add(ISqlPredicate predicate) | |
1560 { | |
1561 _condition.Search.Conditions.Add(new Condition(_isNot, predicate)); | |
1562 return _condition.GetNext(); | |
1563 } | |
1564 | |
1565 #region Predicate.ExprExpr | |
1566 | |
1567 public class Op_ : IConditionExpr<T2> | |
1568 { | |
1569 internal Op_(Expr_ expr, Predicate.Operator op) | |
1570 { | |
1571 _expr = expr; | |
1572 _op = op; | |
1573 } | |
1574 | |
1575 readonly Expr_ _expr; | |
1576 readonly Predicate.Operator _op; | |
1577 | |
1578 public T2 Expr (ISqlExpression expr) { return _expr.Add(new Predicate.ExprExpr(_expr._expr, _op, expr)); } | |
1579 public T2 Field (SqlField field) { return Expr(field); } | |
1580 public T2 SubQuery(SqlQuery subQuery) { return Expr(subQuery); } | |
1581 public T2 Value (object value) { return Expr(new SqlValue(value)); } | |
1582 | |
1583 public T2 All (SqlQuery subQuery) { return Expr(SqlFunction.CreateAll (subQuery)); } | |
1584 public T2 Some (SqlQuery subQuery) { return Expr(SqlFunction.CreateSome(subQuery)); } | |
1585 public T2 Any (SqlQuery subQuery) { return Expr(SqlFunction.CreateAny (subQuery)); } | |
1586 } | |
1587 | |
1588 public Op_ Equal { get { return new Op_(this, Predicate.Operator.Equal); } } | |
1589 public Op_ NotEqual { get { return new Op_(this, Predicate.Operator.NotEqual); } } | |
1590 public Op_ Greater { get { return new Op_(this, Predicate.Operator.Greater); } } | |
1591 public Op_ GreaterOrEqual { get { return new Op_(this, Predicate.Operator.GreaterOrEqual); } } | |
1592 public Op_ NotGreater { get { return new Op_(this, Predicate.Operator.NotGreater); } } | |
1593 public Op_ Less { get { return new Op_(this, Predicate.Operator.Less); } } | |
1594 public Op_ LessOrEqual { get { return new Op_(this, Predicate.Operator.LessOrEqual); } } | |
1595 public Op_ NotLess { get { return new Op_(this, Predicate.Operator.NotLess); } } | |
1596 | |
1597 #endregion | |
1598 | |
1599 #region Predicate.Like | |
1600 | |
1601 public T2 Like(ISqlExpression expression, SqlValue escape) { return Add(new Predicate.Like(_expr, false, expression, escape)); } | |
1602 public T2 Like(ISqlExpression expression) { return Like(expression, null); } | |
1603 public T2 Like(string expression, SqlValue escape) { return Like(new SqlValue(expression), escape); } | |
1604 public T2 Like(string expression) { return Like(new SqlValue(expression), null); } | |
1605 | |
1606 #endregion | |
1607 | |
1608 #region Predicate.Between | |
1609 | |
1610 public T2 Between (ISqlExpression expr1, ISqlExpression expr2) { return Add(new Predicate.Between(_expr, false, expr1, expr2)); } | |
1611 public T2 NotBetween(ISqlExpression expr1, ISqlExpression expr2) { return Add(new Predicate.Between(_expr, true, expr1, expr2)); } | |
1612 | |
1613 #endregion | |
1614 | |
1615 #region Predicate.IsNull | |
1616 | |
1617 public T2 IsNull { get { return Add(new Predicate.IsNull(_expr, false)); } } | |
1618 public T2 IsNotNull { get { return Add(new Predicate.IsNull(_expr, true)); } } | |
1619 | |
1620 #endregion | |
1621 | |
1622 #region Predicate.In | |
1623 | |
1624 public T2 In (SqlQuery subQuery) { return Add(new Predicate.InSubQuery(_expr, false, subQuery)); } | |
1625 public T2 NotIn(SqlQuery subQuery) { return Add(new Predicate.InSubQuery(_expr, true, subQuery)); } | |
1626 | |
1627 Predicate.InList CreateInList(bool isNot, object[] exprs) | |
1628 { | |
1629 var list = new Predicate.InList(_expr, isNot, null); | |
1630 | |
1631 if (exprs != null && exprs.Length > 0) | |
1632 { | |
1633 foreach (var item in exprs) | |
1634 { | |
1635 if (item == null || item is SqlValue && ((SqlValue)item).Value == null) | |
1636 continue; | |
1637 | |
1638 if (item is ISqlExpression) | |
1639 list.Values.Add((ISqlExpression)item); | |
1640 else | |
1641 list.Values.Add(new SqlValue(item)); | |
1642 } | |
1643 } | |
1644 | |
1645 return list; | |
1646 } | |
1647 | |
1648 public T2 In (params object[] exprs) { return Add(CreateInList(false, exprs)); } | |
1649 public T2 NotIn(params object[] exprs) { return Add(CreateInList(true, exprs)); } | |
1650 | |
1651 #endregion | |
1652 } | |
1653 | |
1654 public class Not_ : IConditionExpr<Expr_> | |
1655 { | |
1656 internal Not_(ConditionBase<T1,T2> condition) | |
1657 { | |
1658 _condition = condition; | |
1659 } | |
1660 | |
1661 readonly ConditionBase<T1,T2> _condition; | |
1662 | |
1663 public Expr_ Expr (ISqlExpression expr) { return new Expr_(_condition, true, expr); } | |
1664 public Expr_ Field (SqlField field) { return Expr(field); } | |
1665 public Expr_ SubQuery(SqlQuery subQuery) { return Expr(subQuery); } | |
1666 public Expr_ Value (object value) { return Expr(new SqlValue(value)); } | |
1667 | |
1668 public T2 Exists(SqlQuery subQuery) | |
1669 { | |
1670 _condition.Search.Conditions.Add(new Condition(true, new Predicate.FuncLike(SqlFunction.CreateExists(subQuery)))); | |
1671 return _condition.GetNext(); | |
1672 } | |
1673 } | |
1674 | |
1675 protected abstract SearchCondition Search { get; } | |
1676 protected abstract T2 GetNext(); | |
1677 | |
1678 protected T1 SetOr(bool value) | |
1679 { | |
1680 Search.Conditions[Search.Conditions.Count - 1].IsOr = value; | |
1681 return (T1)this; | |
1682 } | |
1683 | |
1684 public Not_ Not { get { return new Not_(this); } } | |
1685 | |
1686 public Expr_ Expr (ISqlExpression expr) { return new Expr_(this, false, expr); } | |
1687 public Expr_ Field (SqlField field) { return Expr(field); } | |
1688 public Expr_ SubQuery(SqlQuery subQuery) { return Expr(subQuery); } | |
1689 public Expr_ Value (object value) { return Expr(new SqlValue(value)); } | |
1690 | |
1691 public T2 Exists(SqlQuery subQuery) | |
1692 { | |
1693 Search.Conditions.Add(new Condition(false, new Predicate.FuncLike(SqlFunction.CreateExists(subQuery)))); | |
1694 return GetNext(); | |
1695 } | |
1696 } | |
1697 | |
1698 #endregion | |
1699 | |
1700 #region OrderByItem | |
1701 | |
1702 public class OrderByItem : IQueryElement, ICloneableElement | |
1703 { | |
1704 public OrderByItem(ISqlExpression expression, bool isDescending) | |
1705 { | |
1706 Expression = expression; | |
1707 IsDescending = isDescending; | |
1708 } | |
1709 | |
1710 public ISqlExpression Expression { get; internal set; } | |
1711 public bool IsDescending { get; private set; } | |
1712 | |
1713 [Obsolete] | |
1714 internal void Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) | |
1715 { | |
1716 Expression = Expression.Walk(skipColumns, func); | |
1717 } | |
1718 | |
1719 public ICloneableElement Clone(Dictionary<ICloneableElement, ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) | |
1720 { | |
1721 if (!doClone(this)) | |
1722 return this; | |
1723 | |
1724 ICloneableElement clone; | |
1725 | |
1726 if (!objectTree.TryGetValue(this, out clone)) | |
1727 objectTree.Add(this, clone = new OrderByItem((ISqlExpression)Expression.Clone(objectTree, doClone), IsDescending)); | |
1728 | |
1729 return clone; | |
1730 } | |
1731 | |
1732 #region Overrides | |
1733 | |
1734 #if OVERRIDETOSTRING | |
1735 | |
1736 public override string ToString() | |
1737 { | |
1738 return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); | |
1739 } | |
1740 | |
1741 #endif | |
1742 | |
1743 #endregion | |
1744 | |
1745 #region IQueryElement Members | |
1746 | |
1747 public QueryElementType ElementType | |
1748 { | |
1749 get { return QueryElementType.OrderByItem; } | |
1750 } | |
1751 | |
1752 StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) | |
1753 { | |
1754 Expression.ToString(sb, dic); | |
1755 | |
1756 if (IsDescending) | |
1757 sb.Append(" DESC"); | |
1758 | |
1759 return sb; | |
1760 } | |
1761 | |
1762 #endregion | |
1763 } | |
1764 | |
1765 #endregion | |
1766 | |
1767 #region ClauseBase | |
1768 | |
1769 public abstract class ClauseBase | |
1770 { | |
1771 protected ClauseBase(SqlQuery sqlQuery) | |
1772 { | |
1773 SqlQuery = sqlQuery; | |
1774 } | |
1775 | |
1776 public SelectClause Select { get { return SqlQuery.Select; } } | |
1777 public FromClause From { get { return SqlQuery.From; } } | |
1778 public WhereClause Where { get { return SqlQuery.Where; } } | |
1779 public GroupByClause GroupBy { get { return SqlQuery.GroupBy; } } | |
1780 public WhereClause Having { get { return SqlQuery.Having; } } | |
1781 public OrderByClause OrderBy { get { return SqlQuery.OrderBy; } } | |
1782 public SqlQuery End() { return SqlQuery; } | |
1783 | |
1784 protected internal SqlQuery SqlQuery { get; private set; } | |
1785 | |
1786 internal void SetSqlQuery(SqlQuery sqlQuery) | |
1787 { | |
1788 SqlQuery = sqlQuery; | |
1789 } | |
1790 } | |
1791 | |
1792 public abstract class ClauseBase<T1, T2> : ConditionBase<T1, T2> | |
1793 where T1 : ClauseBase<T1, T2> | |
1794 { | |
1795 protected ClauseBase(SqlQuery sqlQuery) | |
1796 { | |
1797 SqlQuery = sqlQuery; | |
1798 } | |
1799 | |
1800 public SelectClause Select { get { return SqlQuery.Select; } } | |
1801 public FromClause From { get { return SqlQuery.From; } } | |
1802 public GroupByClause GroupBy { get { return SqlQuery.GroupBy; } } | |
1803 public WhereClause Having { get { return SqlQuery.Having; } } | |
1804 public OrderByClause OrderBy { get { return SqlQuery.OrderBy; } } | |
1805 public SqlQuery End() { return SqlQuery; } | |
1806 | |
1807 protected internal SqlQuery SqlQuery { get; private set; } | |
1808 | |
1809 internal void SetSqlQuery(SqlQuery sqlQuery) | |
1810 { | |
1811 SqlQuery = sqlQuery; | |
1812 } | |
1813 } | |
1814 | |
1815 #endregion | |
1816 | |
1817 #region SelectClause | |
1818 | |
1819 public class SelectClause : ClauseBase, IQueryElement, ISqlExpressionWalkable | |
1820 { | |
1821 #region Init | |
1822 | |
1823 internal SelectClause(SqlQuery sqlQuery) : base(sqlQuery) | |
1824 { | |
1825 } | |
1826 | |
1827 internal SelectClause( | |
1828 SqlQuery sqlQuery, | |
1829 SelectClause clone, | |
1830 Dictionary<ICloneableElement,ICloneableElement> objectTree, | |
1831 Predicate<ICloneableElement> doClone) | |
1832 : base(sqlQuery) | |
1833 { | |
1834 _columns.AddRange(clone._columns.ConvertAll(c => (Column)c.Clone(objectTree, doClone))); | |
1835 | |
1836 IsDistinct = clone.IsDistinct; | |
1837 TakeValue = clone.TakeValue == null ? null : (ISqlExpression)clone.TakeValue.Clone(objectTree, doClone); | |
1838 SkipValue = clone.SkipValue == null ? null : (ISqlExpression)clone.SkipValue.Clone(objectTree, doClone); | |
1839 } | |
1840 | |
1841 internal SelectClause(bool isDistinct, ISqlExpression takeValue, ISqlExpression skipValue, IEnumerable<Column> columns) | |
1842 : base(null) | |
1843 { | |
1844 IsDistinct = isDistinct; | |
1845 TakeValue = takeValue; | |
1846 SkipValue = skipValue; | |
1847 | |
1848 _columns.AddRange(columns); | |
1849 } | |
1850 | |
1851 #endregion | |
1852 | |
1853 #region Columns | |
1854 | |
1855 public SelectClause Field(SqlField field) | |
1856 { | |
1857 AddOrGetColumn(new Column(SqlQuery, field)); | |
1858 return this; | |
1859 } | |
1860 | |
1861 public SelectClause Field(SqlField field, string alias) | |
1862 { | |
1863 AddOrGetColumn(new Column(SqlQuery, field, alias)); | |
1864 return this; | |
1865 } | |
1866 | |
1867 public SelectClause SubQuery(SqlQuery subQuery) | |
1868 { | |
1869 if (subQuery.ParentSql != null && subQuery.ParentSql != SqlQuery) | |
1870 throw new ArgumentException("SqlQuery already used as subquery"); | |
1871 | |
1872 subQuery.ParentSql = SqlQuery; | |
1873 | |
1874 AddOrGetColumn(new Column(SqlQuery, subQuery)); | |
1875 return this; | |
1876 } | |
1877 | |
1878 public SelectClause SubQuery(SqlQuery sqlQuery, string alias) | |
1879 { | |
1880 if (sqlQuery.ParentSql != null && sqlQuery.ParentSql != SqlQuery) | |
1881 throw new ArgumentException("SqlQuery already used as subquery"); | |
1882 | |
1883 sqlQuery.ParentSql = SqlQuery; | |
1884 | |
1885 AddOrGetColumn(new Column(SqlQuery, sqlQuery, alias)); | |
1886 return this; | |
1887 } | |
1888 | |
1889 public SelectClause Expr(ISqlExpression expr) | |
1890 { | |
1891 AddOrGetColumn(new Column(SqlQuery, expr)); | |
1892 return this; | |
1893 } | |
1894 | |
1895 public SelectClause Expr(ISqlExpression expr, string alias) | |
1896 { | |
1897 AddOrGetColumn(new Column(SqlQuery, expr, alias)); | |
1898 return this; | |
1899 } | |
1900 | |
1901 public SelectClause Expr(string expr, params ISqlExpression[] values) | |
1902 { | |
1903 AddOrGetColumn(new Column(SqlQuery, new SqlExpression(null, expr, values))); | |
1904 return this; | |
1905 } | |
1906 | |
1907 public SelectClause Expr(Type systemType, string expr, params ISqlExpression[] values) | |
1908 { | |
1909 AddOrGetColumn(new Column(SqlQuery, new SqlExpression(systemType, expr, values))); | |
1910 return this; | |
1911 } | |
1912 | |
1913 public SelectClause Expr(string expr, int priority, params ISqlExpression[] values) | |
1914 { | |
1915 AddOrGetColumn(new Column(SqlQuery, new SqlExpression(null, expr, priority, values))); | |
1916 return this; | |
1917 } | |
1918 | |
1919 public SelectClause Expr(Type systemType, string expr, int priority, params ISqlExpression[] values) | |
1920 { | |
1921 AddOrGetColumn(new Column(SqlQuery, new SqlExpression(systemType, expr, priority, values))); | |
1922 return this; | |
1923 } | |
1924 | |
1925 public SelectClause Expr(string alias, string expr, int priority, params ISqlExpression[] values) | |
1926 { | |
1927 AddOrGetColumn(new Column(SqlQuery, new SqlExpression(null, expr, priority, values))); | |
1928 return this; | |
1929 } | |
1930 | |
1931 public SelectClause Expr(Type systemType, string alias, string expr, int priority, params ISqlExpression[] values) | |
1932 { | |
1933 AddOrGetColumn(new Column(SqlQuery, new SqlExpression(systemType, expr, priority, values))); | |
1934 return this; | |
1935 } | |
1936 | |
1937 public SelectClause Expr<T>(ISqlExpression expr1, string operation, ISqlExpression expr2) | |
1938 { | |
1939 AddOrGetColumn(new Column(SqlQuery, new SqlBinaryExpression(typeof(T), expr1, operation, expr2))); | |
1940 return this; | |
1941 } | |
1942 | |
1943 public SelectClause Expr<T>(ISqlExpression expr1, string operation, ISqlExpression expr2, int priority) | |
1944 { | |
1945 AddOrGetColumn(new Column(SqlQuery, new SqlBinaryExpression(typeof(T), expr1, operation, expr2, priority))); | |
1946 return this; | |
1947 } | |
1948 | |
1949 public SelectClause Expr<T>(string alias, ISqlExpression expr1, string operation, ISqlExpression expr2, int priority) | |
1950 { | |
1951 AddOrGetColumn(new Column(SqlQuery, new SqlBinaryExpression(typeof(T), expr1, operation, expr2, priority), alias)); | |
1952 return this; | |
1953 } | |
1954 | |
1955 public int Add(ISqlExpression expr) | |
1956 { | |
1957 if (expr is Column && ((Column)expr).Parent == SqlQuery) | |
1958 throw new InvalidOperationException(); | |
1959 | |
1960 return Columns.IndexOf(AddOrGetColumn(new Column(SqlQuery, expr))); | |
1961 } | |
1962 | |
1963 public int Add(ISqlExpression expr, string alias) | |
1964 { | |
1965 return Columns.IndexOf(AddOrGetColumn(new Column(SqlQuery, expr, alias))); | |
1966 } | |
1967 | |
1968 Column AddOrGetColumn(Column col) | |
1969 { | |
1970 foreach (var c in Columns) | |
1971 if (c.Equals(col)) | |
1972 return col; | |
1973 | |
1974 #if DEBUG | |
1975 | |
1976 switch (col.Expression.ElementType) | |
1977 { | |
1978 case QueryElementType.SqlField : | |
1979 { | |
1980 var table = ((SqlField)col.Expression).Table; | |
1981 | |
1982 //if (SqlQuery.From.GetFromTables().Any(_ => _ == table)) | |
1983 // throw new InvalidOperationException("Wrong field usage."); | |
1984 | |
1985 break; | |
1986 } | |
1987 | |
1988 case QueryElementType.Column : | |
1989 { | |
1990 var query = ((Column)col.Expression).Parent; | |
1991 | |
1992 //if (!SqlQuery.From.GetFromQueries().Any(_ => _ == query)) | |
1993 // throw new InvalidOperationException("Wrong column usage."); | |
1994 | |
1995 break; | |
1996 } | |
1997 | |
1998 case QueryElementType.SqlQuery : | |
1999 { | |
2000 if (col.Expression == SqlQuery) | |
2001 throw new InvalidOperationException("Wrong query usage."); | |
2002 break; | |
2003 } | |
2004 } | |
2005 | |
2006 #endif | |
2007 | |
2008 Columns.Add(col); | |
2009 | |
2010 return col; | |
2011 } | |
2012 | |
2013 readonly List<Column> _columns = new List<Column>(); | |
2014 public List<Column> Columns | |
2015 { | |
2016 get { return _columns; } | |
2017 } | |
2018 | |
2019 #endregion | |
2020 | |
2021 #region HasModifier | |
2022 | |
2023 public bool HasModifier | |
2024 { | |
2025 get { return IsDistinct || SkipValue != null || TakeValue != null; } | |
2026 } | |
2027 | |
2028 #endregion | |
2029 | |
2030 #region Distinct | |
2031 | |
2032 public SelectClause Distinct | |
2033 { | |
2034 get { IsDistinct = true; return this; } | |
2035 } | |
2036 | |
2037 public bool IsDistinct { get; set; } | |
2038 | |
2039 #endregion | |
2040 | |
2041 #region Take | |
2042 | |
2043 public SelectClause Take(int value) | |
2044 { | |
2045 TakeValue = new SqlValue(value); | |
2046 return this; | |
2047 } | |
2048 | |
2049 public SelectClause Take(ISqlExpression value) | |
2050 { | |
2051 TakeValue = value; | |
2052 return this; | |
2053 } | |
2054 | |
2055 public ISqlExpression TakeValue { get; set; } | |
2056 | |
2057 #endregion | |
2058 | |
2059 #region Skip | |
2060 | |
2061 public SelectClause Skip(int value) | |
2062 { | |
2063 SkipValue = new SqlValue(value); | |
2064 return this; | |
2065 } | |
2066 | |
2067 public SelectClause Skip(ISqlExpression value) | |
2068 { | |
2069 SkipValue = value; | |
2070 return this; | |
2071 } | |
2072 | |
2073 public ISqlExpression SkipValue { get; set; } | |
2074 | |
2075 #endregion | |
2076 | |
2077 #region Overrides | |
2078 | |
2079 #if OVERRIDETOSTRING | |
2080 | |
2081 public override string ToString() | |
2082 { | |
2083 return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); | |
2084 } | |
2085 | |
2086 #endif | |
2087 | |
2088 #endregion | |
2089 | |
2090 #region ISqlExpressionWalkable Members | |
2091 | |
2092 [Obsolete] | |
2093 ISqlExpression ISqlExpressionWalkable.Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) | |
2094 { | |
2095 for (var i = 0; i < Columns.Count; i++) | |
2096 { | |
2097 var col = Columns[i]; | |
2098 #pragma warning disable 0618 | |
2099 var expr = col.Walk(skipColumns, func); | |
2100 #pragma warning restore 0618 | |
2101 | |
2102 if (expr is Column) | |
2103 Columns[i] = (Column)expr; | |
2104 else | |
2105 Columns[i] = new Column(col.Parent, expr, col.Alias); | |
2106 } | |
2107 | |
2108 if (TakeValue != null) TakeValue = TakeValue.Walk(skipColumns, func); | |
2109 if (SkipValue != null) SkipValue = SkipValue.Walk(skipColumns, func); | |
2110 | |
2111 return null; | |
2112 } | |
2113 | |
2114 #endregion | |
2115 | |
2116 #region IQueryElement Members | |
2117 | |
2118 public QueryElementType ElementType { get { return QueryElementType.SelectClause; } } | |
2119 | |
2120 StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) | |
2121 { | |
2122 if (dic.ContainsKey(this)) | |
2123 return sb.Append("..."); | |
2124 | |
2125 dic.Add(this, this); | |
2126 | |
2127 sb.Append("SELECT "); | |
2128 | |
2129 if (IsDistinct) sb.Append("DISTINCT "); | |
2130 | |
2131 if (SkipValue != null) | |
2132 { | |
2133 sb.Append("SKIP "); | |
2134 SkipValue.ToString(sb, dic); | |
2135 sb.Append(" "); | |
2136 } | |
2137 | |
2138 if (TakeValue != null) | |
2139 { | |
2140 sb.Append("TAKE "); | |
2141 TakeValue.ToString(sb, dic); | |
2142 sb.Append(" "); | |
2143 } | |
2144 | |
2145 sb.AppendLine(); | |
2146 | |
2147 if (Columns.Count == 0) | |
2148 sb.Append("\t*, \n"); | |
2149 else | |
2150 foreach (var c in Columns) | |
2151 { | |
2152 sb.Append("\t"); | |
2153 ((IQueryElement)c).ToString(sb, dic); | |
2154 sb | |
2155 .Append(" as ") | |
2156 .Append(c.Alias ?? "c" + (Columns.IndexOf(c) + 1)) | |
2157 .Append(", \n"); | |
2158 } | |
2159 | |
2160 sb.Length -= 3; | |
2161 | |
2162 dic.Remove(this); | |
2163 | |
2164 return sb; | |
2165 } | |
2166 | |
2167 #endregion | |
2168 } | |
2169 | |
2170 private SelectClause _select; | |
2171 public SelectClause Select | |
2172 { | |
2173 get { return _select; } | |
2174 } | |
2175 | |
2176 #endregion | |
2177 | |
2178 #region InsertClause | |
2179 | |
2180 public class SetExpression : IQueryElement, ISqlExpressionWalkable, ICloneableElement | |
2181 { | |
2182 public SetExpression(ISqlExpression column, ISqlExpression expression) | |
2183 { | |
2184 Column = column; | |
2185 Expression = expression; | |
2186 | |
2187 if (expression is SqlParameter) | |
2188 { | |
2189 var p = (SqlParameter)expression; | |
2190 | |
2191 //if (type.IsEnum) | |
2192 // p.SetEnumConverter(type, mappingSchema); | |
2193 | |
2194 if (column is SqlField) | |
2195 { | |
2196 var field = (SqlField)column; | |
2197 | |
2198 if (field.MemberMapper != null) | |
2199 { | |
2200 if (field.MemberMapper.MapMemberInfo.IsDbTypeSet) | |
2201 p.DbType = field.MemberMapper.MapMemberInfo.DbType; | |
2202 | |
2203 if (field.MemberMapper.MapMemberInfo.IsDbSizeSet) | |
2204 p.DbSize = field.MemberMapper.MapMemberInfo.DbSize; | |
2205 } | |
2206 } | |
2207 } | |
2208 } | |
2209 | |
2210 public ISqlExpression Column { get; set; } | |
2211 public ISqlExpression Expression { get; set; } | |
2212 | |
2213 #region Overrides | |
2214 | |
2215 #if OVERRIDETOSTRING | |
2216 | |
2217 public override string ToString() | |
2218 { | |
2219 return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); | |
2220 } | |
2221 | |
2222 #endif | |
2223 | |
2224 #endregion | |
2225 | |
2226 #region ICloneableElement Members | |
2227 | |
2228 public ICloneableElement Clone(Dictionary<ICloneableElement, ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) | |
2229 { | |
2230 if (!doClone(this)) | |
2231 return this; | |
2232 | |
2233 ICloneableElement clone; | |
2234 | |
2235 if (!objectTree.TryGetValue(this, out clone)) | |
2236 { | |
2237 objectTree.Add(this, clone = new SetExpression( | |
2238 (ISqlExpression)Column. Clone(objectTree, doClone), | |
2239 (ISqlExpression)Expression.Clone(objectTree, doClone))); | |
2240 } | |
2241 | |
2242 return clone; | |
2243 } | |
2244 | |
2245 #endregion | |
2246 | |
2247 #region ISqlExpressionWalkable Members | |
2248 | |
2249 [Obsolete] | |
2250 ISqlExpression ISqlExpressionWalkable.Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) | |
2251 { | |
2252 Column = Column. Walk(skipColumns, func); | |
2253 Expression = Expression.Walk(skipColumns, func); | |
2254 return null; | |
2255 } | |
2256 | |
2257 #endregion | |
2258 | |
2259 #region IQueryElement Members | |
2260 | |
2261 public QueryElementType ElementType { get { return QueryElementType.SetExpression; } } | |
2262 | |
2263 StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) | |
2264 { | |
2265 Column.ToString(sb, dic); | |
2266 sb.Append(" = "); | |
2267 Expression.ToString(sb, dic); | |
2268 | |
2269 return sb; | |
2270 } | |
2271 | |
2272 #endregion | |
2273 } | |
2274 | |
2275 public class InsertClause : IQueryElement, ISqlExpressionWalkable, ICloneableElement | |
2276 { | |
2277 public InsertClause() | |
2278 { | |
2279 Items = new List<SetExpression>(); | |
2280 } | |
2281 | |
2282 public List<SetExpression> Items { get; private set; } | |
2283 public SqlTable Into { get; set; } | |
2284 public bool WithIdentity { get; set; } | |
2285 | |
2286 #region Overrides | |
2287 | |
2288 #if OVERRIDETOSTRING | |
2289 | |
2290 public override string ToString() | |
2291 { | |
2292 return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); | |
2293 } | |
2294 | |
2295 #endif | |
2296 | |
2297 #endregion | |
2298 | |
2299 #region ICloneableElement Members | |
2300 | |
2301 public ICloneableElement Clone(Dictionary<ICloneableElement, ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) | |
2302 { | |
2303 if (!doClone(this)) | |
2304 return this; | |
2305 | |
2306 var clone = new InsertClause { WithIdentity = WithIdentity }; | |
2307 | |
2308 if (Into != null) | |
2309 clone.Into = (SqlTable)Into.Clone(objectTree, doClone); | |
2310 | |
2311 foreach (var item in Items) | |
2312 clone.Items.Add((SetExpression)item.Clone(objectTree, doClone)); | |
2313 | |
2314 objectTree.Add(this, clone); | |
2315 | |
2316 return clone; | |
2317 } | |
2318 | |
2319 #endregion | |
2320 | |
2321 #region ISqlExpressionWalkable Members | |
2322 | |
2323 [Obsolete] | |
2324 ISqlExpression ISqlExpressionWalkable.Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) | |
2325 { | |
2326 if (Into != null) | |
2327 ((ISqlExpressionWalkable)Into).Walk(skipColumns, func); | |
2328 | |
2329 foreach (var t in Items) | |
2330 ((ISqlExpressionWalkable)t).Walk(skipColumns, func); | |
2331 | |
2332 return null; | |
2333 } | |
2334 | |
2335 #endregion | |
2336 | |
2337 #region IQueryElement Members | |
2338 | |
2339 public QueryElementType ElementType { get { return QueryElementType.InsertClause; } } | |
2340 | |
2341 StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) | |
2342 { | |
2343 sb.Append("VALUES "); | |
2344 | |
2345 if (Into != null) | |
2346 ((IQueryElement)Into).ToString(sb, dic); | |
2347 | |
2348 sb.AppendLine(); | |
2349 | |
2350 foreach (var e in Items) | |
2351 { | |
2352 sb.Append("\t"); | |
2353 ((IQueryElement)e).ToString(sb, dic); | |
2354 sb.AppendLine(); | |
2355 } | |
2356 | |
2357 return sb; | |
2358 } | |
2359 | |
2360 #endregion | |
2361 } | |
2362 | |
2363 private InsertClause _insert; | |
2364 public InsertClause Insert | |
2365 { | |
2366 get { return _insert ?? (_insert = new InsertClause()); } | |
2367 } | |
2368 | |
2369 public void ClearInsert() | |
2370 { | |
2371 _insert = null; | |
2372 } | |
2373 | |
2374 #endregion | |
2375 | |
2376 #region UpdateClause | |
2377 | |
2378 public class UpdateClause : IQueryElement, ISqlExpressionWalkable, ICloneableElement | |
2379 { | |
2380 public UpdateClause() | |
2381 { | |
2382 Items = new List<SetExpression>(); | |
2383 Keys = new List<SetExpression>(); | |
2384 } | |
2385 | |
2386 public List<SetExpression> Items { get; private set; } | |
2387 public List<SetExpression> Keys { get; private set; } | |
2388 public SqlTable Table { get; set; } | |
2389 | |
2390 #region Overrides | |
2391 | |
2392 #if OVERRIDETOSTRING | |
2393 | |
2394 public override string ToString() | |
2395 { | |
2396 return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); | |
2397 } | |
2398 | |
2399 #endif | |
2400 | |
2401 #endregion | |
2402 | |
2403 #region ICloneableElement Members | |
2404 | |
2405 public ICloneableElement Clone(Dictionary<ICloneableElement, ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) | |
2406 { | |
2407 if (!doClone(this)) | |
2408 return this; | |
2409 | |
2410 var clone = new UpdateClause(); | |
2411 | |
2412 if (Table != null) | |
2413 clone.Table = (SqlTable)Table.Clone(objectTree, doClone); | |
2414 | |
2415 foreach (var item in Items) | |
2416 clone.Items.Add((SetExpression)item.Clone(objectTree, doClone)); | |
2417 | |
2418 foreach (var item in Keys) | |
2419 clone.Keys.Add((SetExpression)item.Clone(objectTree, doClone)); | |
2420 | |
2421 objectTree.Add(this, clone); | |
2422 | |
2423 return clone; | |
2424 } | |
2425 | |
2426 #endregion | |
2427 | |
2428 #region ISqlExpressionWalkable Members | |
2429 | |
2430 [Obsolete] | |
2431 ISqlExpression ISqlExpressionWalkable.Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) | |
2432 { | |
2433 if (Table != null) | |
2434 ((ISqlExpressionWalkable)Table).Walk(skipColumns, func); | |
2435 | |
2436 foreach (var t in Items) | |
2437 ((ISqlExpressionWalkable)t).Walk(skipColumns, func); | |
2438 | |
2439 foreach (var t in Keys) | |
2440 ((ISqlExpressionWalkable)t).Walk(skipColumns, func); | |
2441 | |
2442 return null; | |
2443 } | |
2444 | |
2445 #endregion | |
2446 | |
2447 #region IQueryElement Members | |
2448 | |
2449 public QueryElementType ElementType { get { return QueryElementType.UpdateClause; } } | |
2450 | |
2451 StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) | |
2452 { | |
2453 sb.Append("SET "); | |
2454 | |
2455 if (Table != null) | |
2456 ((IQueryElement)Table).ToString(sb, dic); | |
2457 | |
2458 sb.AppendLine(); | |
2459 | |
2460 foreach (var e in Items) | |
2461 { | |
2462 sb.Append("\t"); | |
2463 ((IQueryElement)e).ToString(sb, dic); | |
2464 sb.AppendLine(); | |
2465 } | |
2466 | |
2467 return sb; | |
2468 } | |
2469 | |
2470 #endregion | |
2471 } | |
2472 | |
2473 private UpdateClause _update; | |
2474 public UpdateClause Update | |
2475 { | |
2476 get { return _update ?? (_update = new UpdateClause()); } | |
2477 } | |
2478 | |
2479 public void ClearUpdate() | |
2480 { | |
2481 _update = null; | |
2482 } | |
2483 | |
2484 #endregion | |
2485 | |
2486 #region DeleteClause | |
2487 | |
2488 public class DeleteClause : IQueryElement, ISqlExpressionWalkable, ICloneableElement | |
2489 { | |
2490 public SqlTable Table { get; set; } | |
2491 | |
2492 #region Overrides | |
2493 | |
2494 #if OVERRIDETOSTRING | |
2495 | |
2496 public override string ToString() | |
2497 { | |
2498 return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); | |
2499 } | |
2500 | |
2501 #endif | |
2502 | |
2503 #endregion | |
2504 | |
2505 #region ICloneableElement Members | |
2506 | |
2507 public ICloneableElement Clone(Dictionary<ICloneableElement, ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) | |
2508 { | |
2509 if (!doClone(this)) | |
2510 return this; | |
2511 | |
2512 var clone = new DeleteClause(); | |
2513 | |
2514 if (Table != null) | |
2515 clone.Table = (SqlTable)Table.Clone(objectTree, doClone); | |
2516 | |
2517 objectTree.Add(this, clone); | |
2518 | |
2519 return clone; | |
2520 } | |
2521 | |
2522 #endregion | |
2523 | |
2524 #region ISqlExpressionWalkable Members | |
2525 | |
2526 [Obsolete] | |
2527 ISqlExpression ISqlExpressionWalkable.Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) | |
2528 { | |
2529 if (Table != null) | |
2530 ((ISqlExpressionWalkable)Table).Walk(skipColumns, func); | |
2531 | |
2532 return null; | |
2533 } | |
2534 | |
2535 #endregion | |
2536 | |
2537 #region IQueryElement Members | |
2538 | |
2539 public QueryElementType ElementType { get { return QueryElementType.DeleteClause; } } | |
2540 | |
2541 StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) | |
2542 { | |
2543 sb.Append("DELETE FROM "); | |
2544 | |
2545 if (Table != null) | |
2546 ((IQueryElement)Table).ToString(sb, dic); | |
2547 | |
2548 sb.AppendLine(); | |
2549 | |
2550 return sb; | |
2551 } | |
2552 | |
2553 #endregion | |
2554 } | |
2555 | |
2556 private DeleteClause _delete; | |
2557 public DeleteClause Delete | |
2558 { | |
2559 get { return _delete ?? (_delete = new DeleteClause()); } | |
2560 } | |
2561 | |
2562 public void ClearDelete() | |
2563 { | |
2564 _delete = null; | |
2565 } | |
2566 | |
2567 #endregion | |
2568 | |
2569 #region FromClause | |
2570 | |
2571 public class FromClause : ClauseBase, IQueryElement, ISqlExpressionWalkable | |
2572 { | |
2573 #region Join | |
2574 | |
2575 public class Join : ConditionBase<Join,Join.Next> | |
2576 { | |
2577 public class Next | |
2578 { | |
2579 internal Next(Join parent) | |
2580 { | |
2581 _parent = parent; | |
2582 } | |
2583 | |
2584 readonly Join _parent; | |
2585 | |
2586 public Join Or { get { return _parent.SetOr(true); } } | |
2587 public Join And { get { return _parent.SetOr(false); } } | |
2588 | |
2589 public static implicit operator Join(Next next) | |
2590 { | |
2591 return next._parent; | |
2592 } | |
2593 } | |
2594 | |
2595 protected override SearchCondition Search | |
2596 { | |
2597 get { return JoinedTable.Condition; } | |
2598 } | |
2599 | |
2600 protected override Next GetNext() | |
2601 { | |
2602 return new Next(this); | |
2603 } | |
2604 | |
2605 internal Join(JoinType joinType, ISqlTableSource table, string alias, bool isWeak, ICollection<Join> joins) | |
2606 { | |
2607 JoinedTable = new JoinedTable(joinType, table, alias, isWeak); | |
2608 | |
2609 if (joins != null && joins.Count > 0) | |
2610 foreach (var join in joins) | |
2611 JoinedTable.Table.Joins.Add(join.JoinedTable); | |
2612 } | |
2613 | |
2614 public JoinedTable JoinedTable { get; private set; } | |
2615 } | |
2616 | |
2617 #endregion | |
2618 | |
2619 internal FromClause(SqlQuery sqlQuery) : base(sqlQuery) | |
2620 { | |
2621 } | |
2622 | |
2623 internal FromClause( | |
2624 SqlQuery sqlQuery, | |
2625 FromClause clone, | |
2626 Dictionary<ICloneableElement,ICloneableElement> objectTree, | |
2627 Predicate<ICloneableElement> doClone) | |
2628 : base(sqlQuery) | |
2629 { | |
2630 _tables.AddRange(clone._tables.ConvertAll(ts => (TableSource)ts.Clone(objectTree, doClone))); | |
2631 } | |
2632 | |
2633 internal FromClause(IEnumerable<TableSource> tables) | |
2634 : base(null) | |
2635 { | |
2636 _tables.AddRange(tables); | |
2637 } | |
2638 | |
2639 public FromClause Table(ISqlTableSource table, params FJoin[] joins) | |
2640 { | |
2641 return Table(table, null, joins); | |
2642 } | |
2643 | |
2644 public FromClause Table(ISqlTableSource table, string alias, params FJoin[] joins) | |
2645 { | |
2646 var ts = AddOrGetTable(table, alias); | |
2647 | |
2648 if (joins != null && joins.Length > 0) | |
2649 foreach (var join in joins) | |
2650 ts.Joins.Add(join.JoinedTable); | |
2651 | |
2652 return this; | |
2653 } | |
2654 | |
2655 TableSource GetTable(ISqlTableSource table, string alias) | |
2656 { | |
2657 foreach (var ts in Tables) | |
2658 if (ts.Source == table) | |
2659 if (alias == null || ts.Alias == alias) | |
2660 return ts; | |
2661 else | |
2662 throw new ArgumentException("alias"); | |
2663 | |
2664 return null; | |
2665 } | |
2666 | |
2667 TableSource AddOrGetTable(ISqlTableSource table, string alias) | |
2668 { | |
2669 var ts = GetTable(table, alias); | |
2670 | |
2671 if (ts != null) | |
2672 return ts; | |
2673 | |
2674 var t = new TableSource(table, alias); | |
2675 | |
2676 Tables.Add(t); | |
2677 | |
2678 return t; | |
2679 } | |
2680 | |
2681 public TableSource this[ISqlTableSource table] | |
2682 { | |
2683 get { return this[table, null]; } | |
2684 } | |
2685 | |
2686 public TableSource this[ISqlTableSource table, string alias] | |
2687 { | |
2688 get | |
2689 { | |
2690 foreach (var ts in Tables) | |
2691 { | |
2692 var t = CheckTableSource(ts, table, alias); | |
2693 | |
2694 if (t != null) | |
2695 return t; | |
2696 } | |
2697 | |
2698 return null; | |
2699 } | |
2700 } | |
2701 | |
2702 public bool IsChild(ISqlTableSource table) | |
2703 { | |
2704 foreach (var ts in Tables) | |
2705 if (ts.Source == table || CheckChild(ts.Joins, table)) | |
2706 return true; | |
2707 return false; | |
2708 } | |
2709 | |
2710 static bool CheckChild(IEnumerable<JoinedTable> joins, ISqlTableSource table) | |
2711 { | |
2712 foreach (var j in joins) | |
2713 if (j.Table.Source == table || CheckChild(j.Table.Joins, table)) | |
2714 return true; | |
2715 return false; | |
2716 } | |
2717 | |
2718 readonly List<TableSource> _tables = new List<TableSource>(); | |
2719 public List<TableSource> Tables | |
2720 { | |
2721 get { return _tables; } | |
2722 } | |
2723 | |
2724 static IEnumerable<ISqlTableSource> GetJoinTables(TableSource source, QueryElementType elementType) | |
2725 { | |
2726 if (source.Source.ElementType == elementType) | |
2727 yield return source.Source; | |
2728 | |
2729 foreach (var join in source.Joins) | |
2730 foreach (var table in GetJoinTables(join.Table, elementType)) | |
2731 yield return table; | |
2732 } | |
2733 | |
2734 internal IEnumerable<ISqlTableSource> GetFromTables() | |
2735 { | |
2736 return Tables.SelectMany(_ => GetJoinTables(_, QueryElementType.SqlTable)); | |
2737 } | |
2738 | |
2739 internal IEnumerable<ISqlTableSource> GetFromQueries() | |
2740 { | |
2741 return Tables.SelectMany(_ => GetJoinTables(_, QueryElementType.SqlQuery)); | |
2742 } | |
2743 | |
2744 static TableSource FindTableSource(TableSource source, SqlTable table) | |
2745 { | |
2746 if (source.Source == table) | |
2747 return source; | |
2748 | |
2749 foreach (var join in source.Joins) | |
2750 { | |
2751 var ts = FindTableSource(join.Table, table); | |
2752 if (ts != null) | |
2753 return ts; | |
2754 } | |
2755 | |
2756 return null; | |
2757 } | |
2758 | |
2759 public ISqlTableSource FindTableSource(SqlTable table) | |
2760 { | |
2761 foreach (var source in Tables) | |
2762 { | |
2763 var ts = FindTableSource(source, table); | |
2764 if (ts != null) | |
2765 return ts; | |
2766 } | |
2767 | |
2768 return null; | |
2769 } | |
2770 | |
2771 #region Overrides | |
2772 | |
2773 #if OVERRIDETOSTRING | |
2774 | |
2775 public override string ToString() | |
2776 { | |
2777 return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); | |
2778 } | |
2779 | |
2780 #endif | |
2781 | |
2782 #endregion | |
2783 | |
2784 #region ISqlExpressionWalkable Members | |
2785 | |
2786 [Obsolete] | |
2787 ISqlExpression ISqlExpressionWalkable.Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) | |
2788 { | |
2789 for (var i = 0; i < Tables.Count; i++) | |
2790 ((ISqlExpressionWalkable)Tables[i]).Walk(skipColumns, func); | |
2791 | |
2792 return null; | |
2793 } | |
2794 | |
2795 #endregion | |
2796 | |
2797 #region IQueryElement Members | |
2798 | |
2799 public QueryElementType ElementType { get { return QueryElementType.FromClause; } } | |
2800 | |
2801 StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) | |
2802 { | |
2803 sb.Append(" \nFROM \n"); | |
2804 | |
2805 if (Tables.Count > 0) | |
2806 { | |
2807 foreach (IQueryElement ts in Tables) | |
2808 { | |
2809 sb.Append('\t'); | |
2810 var len = sb.Length; | |
2811 ts.ToString(sb, dic).Replace("\n", "\n\t", len, sb.Length - len); | |
2812 sb.Append(", "); | |
2813 } | |
2814 | |
2815 sb.Length -= 2; | |
2816 } | |
2817 | |
2818 return sb; | |
2819 } | |
2820 | |
2821 #endregion | |
2822 } | |
2823 | |
2824 public static FJoin InnerJoin (ISqlTableSource table, params FJoin[] joins) { return new FJoin(JoinType.Inner, table, null, false, joins); } | |
2825 public static FJoin InnerJoin (ISqlTableSource table, string alias, params FJoin[] joins) { return new FJoin(JoinType.Inner, table, alias, false, joins); } | |
2826 public static FJoin LeftJoin (ISqlTableSource table, params FJoin[] joins) { return new FJoin(JoinType.Left, table, null, false, joins); } | |
2827 public static FJoin LeftJoin (ISqlTableSource table, string alias, params FJoin[] joins) { return new FJoin(JoinType.Left, table, alias, false, joins); } | |
2828 public static FJoin Join (ISqlTableSource table, params FJoin[] joins) { return new FJoin(JoinType.Auto, table, null, false, joins); } | |
2829 public static FJoin Join (ISqlTableSource table, string alias, params FJoin[] joins) { return new FJoin(JoinType.Auto, table, alias, false, joins); } | |
2830 public static FJoin CrossApply (ISqlTableSource table, params FJoin[] joins) { return new FJoin(JoinType.CrossApply, table, null, false, joins); } | |
2831 public static FJoin CrossApply (ISqlTableSource table, string alias, params FJoin[] joins) { return new FJoin(JoinType.CrossApply, table, alias, false, joins); } | |
2832 public static FJoin OuterApply (ISqlTableSource table, params FJoin[] joins) { return new FJoin(JoinType.OuterApply, table, null, false, joins); } | |
2833 public static FJoin OuterApply (ISqlTableSource table, string alias, params FJoin[] joins) { return new FJoin(JoinType.OuterApply, table, alias, false, joins); } | |
2834 | |
2835 public static FJoin WeakInnerJoin(ISqlTableSource table, params FJoin[] joins) { return new FJoin(JoinType.Inner, table, null, true, joins); } | |
2836 public static FJoin WeakInnerJoin(ISqlTableSource table, string alias, params FJoin[] joins) { return new FJoin(JoinType.Inner, table, alias, true, joins); } | |
2837 public static FJoin WeakLeftJoin (ISqlTableSource table, params FJoin[] joins) { return new FJoin(JoinType.Left, table, null, true, joins); } | |
2838 public static FJoin WeakLeftJoin (ISqlTableSource table, string alias, params FJoin[] joins) { return new FJoin(JoinType.Left, table, alias, true, joins); } | |
2839 public static FJoin WeakJoin (ISqlTableSource table, params FJoin[] joins) { return new FJoin(JoinType.Auto, table, null, true, joins); } | |
2840 public static FJoin WeakJoin (ISqlTableSource table, string alias, params FJoin[] joins) { return new FJoin(JoinType.Auto, table, alias, true, joins); } | |
2841 | |
2842 private FromClause _from; | |
2843 public FromClause From | |
2844 { | |
2845 get { return _from; } | |
2846 } | |
2847 | |
2848 #endregion | |
2849 | |
2850 #region WhereClause | |
2851 | |
2852 public class WhereClause : ClauseBase<WhereClause,WhereClause.Next>, IQueryElement, ISqlExpressionWalkable | |
2853 { | |
2854 public class Next : ClauseBase | |
2855 { | |
2856 internal Next(WhereClause parent) : base(parent.SqlQuery) | |
2857 { | |
2858 _parent = parent; | |
2859 } | |
2860 | |
2861 readonly WhereClause _parent; | |
2862 | |
2863 public WhereClause Or { get { return _parent.SetOr(true); } } | |
2864 public WhereClause And { get { return _parent.SetOr(false); } } | |
2865 } | |
2866 | |
2867 internal WhereClause(SqlQuery sqlQuery) : base(sqlQuery) | |
2868 { | |
2869 SearchCondition = new SearchCondition(); | |
2870 } | |
2871 | |
2872 internal WhereClause( | |
2873 SqlQuery sqlQuery, | |
2874 WhereClause clone, | |
2875 Dictionary<ICloneableElement,ICloneableElement> objectTree, | |
2876 Predicate<ICloneableElement> doClone) | |
2877 : base(sqlQuery) | |
2878 { | |
2879 SearchCondition = (SearchCondition)clone.SearchCondition.Clone(objectTree, doClone); | |
2880 } | |
2881 | |
2882 internal WhereClause(SearchCondition searchCondition) : base(null) | |
2883 { | |
2884 SearchCondition = searchCondition; | |
2885 } | |
2886 | |
2887 public SearchCondition SearchCondition { get; private set; } | |
2888 | |
2889 public bool IsEmpty | |
2890 { | |
2891 get { return SearchCondition.Conditions.Count == 0; } | |
2892 } | |
2893 | |
2894 protected override SearchCondition Search | |
2895 { | |
2896 get { return SearchCondition; } | |
2897 } | |
2898 | |
2899 protected override Next GetNext() | |
2900 { | |
2901 return new Next(this); | |
2902 } | |
2903 | |
2904 #if OVERRIDETOSTRING | |
2905 | |
2906 public override string ToString() | |
2907 { | |
2908 return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); | |
2909 } | |
2910 | |
2911 #endif | |
2912 | |
2913 #region ISqlExpressionWalkable Members | |
2914 | |
2915 [Obsolete] | |
2916 ISqlExpression ISqlExpressionWalkable.Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> action) | |
2917 { | |
2918 SearchCondition = (SearchCondition)((ISqlExpressionWalkable)SearchCondition).Walk(skipColumns, action); | |
2919 return null; | |
2920 } | |
2921 | |
2922 #endregion | |
2923 | |
2924 #region IQueryElement Members | |
2925 | |
2926 public QueryElementType ElementType | |
2927 { | |
2928 get { return QueryElementType.WhereClause; } | |
2929 } | |
2930 | |
2931 StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) | |
2932 { | |
2933 if (Search.Conditions.Count == 0) | |
2934 return sb; | |
2935 | |
2936 sb.Append("\nWHERE\n\t"); | |
2937 return ((IQueryElement)Search).ToString(sb, dic); | |
2938 } | |
2939 | |
2940 #endregion | |
2941 } | |
2942 | |
2943 private WhereClause _where; | |
2944 public WhereClause Where | |
2945 { | |
2946 get { return _where; } | |
2947 } | |
2948 | |
2949 #endregion | |
2950 | |
2951 #region GroupByClause | |
2952 | |
2953 public class GroupByClause : ClauseBase, IQueryElement, ISqlExpressionWalkable | |
2954 { | |
2955 internal GroupByClause(SqlQuery sqlQuery) : base(sqlQuery) | |
2956 { | |
2957 } | |
2958 | |
2959 internal GroupByClause( | |
2960 SqlQuery sqlQuery, | |
2961 GroupByClause clone, | |
2962 Dictionary<ICloneableElement,ICloneableElement> objectTree, | |
2963 Predicate<ICloneableElement> doClone) | |
2964 : base(sqlQuery) | |
2965 { | |
2966 _items.AddRange(clone._items.ConvertAll(e => (ISqlExpression)e.Clone(objectTree, doClone))); | |
2967 } | |
2968 | |
2969 internal GroupByClause(IEnumerable<ISqlExpression> items) : base(null) | |
2970 { | |
2971 _items.AddRange(items); | |
2972 } | |
2973 | |
2974 public GroupByClause Expr(ISqlExpression expr) | |
2975 { | |
2976 Add(expr); | |
2977 return this; | |
2978 } | |
2979 | |
2980 public GroupByClause Field(SqlField field) | |
2981 { | |
2982 return Expr(field); | |
2983 } | |
2984 | |
2985 void Add(ISqlExpression expr) | |
2986 { | |
2987 foreach (var e in Items) | |
2988 if (e.Equals(expr)) | |
2989 return; | |
2990 | |
2991 Items.Add(expr); | |
2992 } | |
2993 | |
2994 readonly List<ISqlExpression> _items = new List<ISqlExpression>(); | |
2995 public List<ISqlExpression> Items | |
2996 { | |
2997 get { return _items; } | |
2998 } | |
2999 | |
3000 public bool IsEmpty | |
3001 { | |
3002 get { return Items.Count == 0; } | |
3003 } | |
3004 | |
3005 #if OVERRIDETOSTRING | |
3006 | |
3007 public override string ToString() | |
3008 { | |
3009 return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); | |
3010 } | |
3011 | |
3012 #endif | |
3013 | |
3014 #region ISqlExpressionWalkable Members | |
3015 | |
3016 [Obsolete] | |
3017 ISqlExpression ISqlExpressionWalkable.Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) | |
3018 { | |
3019 for (var i = 0; i < Items.Count; i++) | |
3020 Items[i] = Items[i].Walk(skipColumns, func); | |
3021 | |
3022 return null; | |
3023 } | |
3024 | |
3025 #endregion | |
3026 | |
3027 #region IQueryElement Members | |
3028 | |
3029 public QueryElementType ElementType { get { return QueryElementType.GroupByClause; } } | |
3030 | |
3031 StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) | |
3032 { | |
3033 if (Items.Count == 0) | |
3034 return sb; | |
3035 | |
3036 sb.Append(" \nGROUP BY \n"); | |
3037 | |
3038 foreach (var item in Items) | |
3039 { | |
3040 sb.Append('\t'); | |
3041 item.ToString(sb, dic); | |
3042 sb.Append(","); | |
3043 } | |
3044 | |
3045 sb.Length--; | |
3046 | |
3047 return sb; | |
3048 } | |
3049 | |
3050 #endregion | |
3051 } | |
3052 | |
3053 private GroupByClause _groupBy; | |
3054 public GroupByClause GroupBy | |
3055 { | |
3056 get { return _groupBy; } | |
3057 } | |
3058 | |
3059 #endregion | |
3060 | |
3061 #region HavingClause | |
3062 | |
3063 private WhereClause _having; | |
3064 public WhereClause Having | |
3065 { | |
3066 get { return _having; } | |
3067 } | |
3068 | |
3069 #endregion | |
3070 | |
3071 #region OrderByClause | |
3072 | |
3073 public class OrderByClause : ClauseBase, IQueryElement, ISqlExpressionWalkable | |
3074 { | |
3075 internal OrderByClause(SqlQuery sqlQuery) : base(sqlQuery) | |
3076 { | |
3077 } | |
3078 | |
3079 internal OrderByClause( | |
3080 SqlQuery sqlQuery, | |
3081 OrderByClause clone, | |
3082 Dictionary<ICloneableElement,ICloneableElement> objectTree, | |
3083 Predicate<ICloneableElement> doClone) | |
3084 : base(sqlQuery) | |
3085 { | |
3086 _items.AddRange(clone._items.ConvertAll(item => (OrderByItem)item.Clone(objectTree, doClone))); | |
3087 } | |
3088 | |
3089 internal OrderByClause(IEnumerable<OrderByItem> items) : base(null) | |
3090 { | |
3091 _items.AddRange(items); | |
3092 } | |
3093 | |
3094 public OrderByClause Expr(ISqlExpression expr, bool isDescending) | |
3095 { | |
3096 Add(expr, isDescending); | |
3097 return this; | |
3098 } | |
3099 | |
3100 public OrderByClause Expr (ISqlExpression expr) { return Expr(expr, false); } | |
3101 public OrderByClause ExprAsc (ISqlExpression expr) { return Expr(expr, false); } | |
3102 public OrderByClause ExprDesc (ISqlExpression expr) { return Expr(expr, true); } | |
3103 public OrderByClause Field (SqlField field, bool isDescending) { return Expr(field, isDescending); } | |
3104 public OrderByClause Field (SqlField field) { return Expr(field, false); } | |
3105 public OrderByClause FieldAsc (SqlField field) { return Expr(field, false); } | |
3106 public OrderByClause FieldDesc(SqlField field) { return Expr(field, true); } | |
3107 | |
3108 void Add(ISqlExpression expr, bool isDescending) | |
3109 { | |
3110 foreach (var item in Items) | |
3111 if (item.Expression.Equals(expr, (x, y) => | |
3112 { | |
3113 var col = x as Column; | |
3114 return col == null || !col.Parent.HasUnion || x == y; | |
3115 })) | |
3116 return; | |
3117 | |
3118 Items.Add(new OrderByItem(expr, isDescending)); | |
3119 } | |
3120 | |
3121 readonly List<OrderByItem> _items = new List<OrderByItem>(); | |
3122 public List<OrderByItem> Items | |
3123 { | |
3124 get { return _items; } | |
3125 } | |
3126 | |
3127 public bool IsEmpty | |
3128 { | |
3129 get { return Items.Count == 0; } | |
3130 } | |
3131 | |
3132 #if OVERRIDETOSTRING | |
3133 | |
3134 public override string ToString() | |
3135 { | |
3136 return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); | |
3137 } | |
3138 | |
3139 #endif | |
3140 | |
3141 #region ISqlExpressionWalkable Members | |
3142 | |
3143 [Obsolete] | |
3144 ISqlExpression ISqlExpressionWalkable.Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) | |
3145 { | |
3146 #pragma warning disable 0618 | |
3147 foreach (var t in Items) | |
3148 t.Walk(skipColumns, func); | |
3149 #pragma warning restore 0618 | |
3150 return null; | |
3151 } | |
3152 | |
3153 #endregion | |
3154 | |
3155 #region IQueryElement Members | |
3156 | |
3157 public QueryElementType ElementType { get { return QueryElementType.OrderByClause; } } | |
3158 | |
3159 StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) | |
3160 { | |
3161 if (Items.Count == 0) | |
3162 return sb; | |
3163 | |
3164 sb.Append(" \nORDER BY \n"); | |
3165 | |
3166 foreach (IQueryElement item in Items) | |
3167 { | |
3168 sb.Append('\t'); | |
3169 item.ToString(sb, dic); | |
3170 sb.Append(", "); | |
3171 } | |
3172 | |
3173 sb.Length -= 2; | |
3174 | |
3175 return sb; | |
3176 } | |
3177 | |
3178 #endregion | |
3179 } | |
3180 | |
3181 private OrderByClause _orderBy; | |
3182 public OrderByClause OrderBy | |
3183 { | |
3184 get { return _orderBy; } | |
3185 } | |
3186 | |
3187 #endregion | |
3188 | |
3189 #region Union | |
3190 | |
3191 public class Union : IQueryElement | |
3192 { | |
3193 public Union() | |
3194 { | |
3195 } | |
3196 | |
3197 public Union(SqlQuery sqlQuery, bool isAll) | |
3198 { | |
3199 SqlQuery = sqlQuery; | |
3200 IsAll = isAll; | |
3201 } | |
3202 | |
3203 public SqlQuery SqlQuery { get; private set; } | |
3204 public bool IsAll { get; private set; } | |
3205 | |
3206 public QueryElementType ElementType | |
3207 { | |
3208 get { return QueryElementType.Union; } | |
3209 } | |
3210 | |
3211 #if OVERRIDETOSTRING | |
3212 | |
3213 public override string ToString() | |
3214 { | |
3215 return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); | |
3216 } | |
3217 | |
3218 #endif | |
3219 | |
3220 StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) | |
3221 { | |
3222 sb.Append(" \nUNION").Append(IsAll ? " ALL" : "").Append(" \n"); | |
3223 return ((IQueryElement)SqlQuery).ToString(sb, dic); | |
3224 } | |
3225 } | |
3226 | |
3227 private List<Union> _unions; | |
3228 public List<Union> Unions | |
3229 { | |
3230 get { return _unions ?? (_unions = new List<Union>()); } | |
3231 } | |
3232 | |
3233 public bool HasUnion { get { return _unions != null && _unions.Count > 0; } } | |
3234 | |
3235 public void AddUnion(SqlQuery union, bool isAll) | |
3236 { | |
3237 Unions.Add(new Union(union, isAll)); | |
3238 } | |
3239 | |
3240 #endregion | |
3241 | |
3242 #region FinalizeAndValidate | |
3243 | |
3244 public void FinalizeAndValidate(bool isApplySupported, bool optimizeColumns) | |
3245 { | |
3246 #if DEBUG | |
3247 var sqlText = SqlText; | |
3248 | |
3249 var dic = new Dictionary<SqlQuery,SqlQuery>(); | |
3250 | |
3251 new QueryVisitor().VisitAll(this, e => | |
3252 { | |
3253 var sql = e as SqlQuery; | |
3254 | |
3255 if (sql != null) | |
3256 { | |
3257 if (dic.ContainsKey(sql)) | |
3258 throw new InvalidOperationException("SqlQuery circle reference detected."); | |
3259 | |
3260 dic.Add(sql, sql); | |
3261 } | |
3262 }); | |
3263 #endif | |
3264 | |
3265 OptimizeUnions(); | |
3266 FinalizeAndValidateInternal(isApplySupported, optimizeColumns, true, new List<ISqlTableSource>()); | |
3267 ResolveFields(); | |
3268 SetAliases(); | |
3269 | |
3270 #if DEBUG | |
3271 sqlText = SqlText; | |
3272 #endif | |
3273 } | |
3274 | |
3275 class QueryData | |
3276 { | |
3277 public SqlQuery Query; | |
3278 public List<ISqlExpression> Fields = new List<ISqlExpression>(); | |
3279 public List<QueryData> Queries = new List<QueryData>(); | |
3280 } | |
3281 | |
3282 void ResolveFields() | |
3283 { | |
3284 var root = GetQueryData(); | |
3285 | |
3286 ResolveFields(root); | |
3287 } | |
3288 | |
3289 QueryData GetQueryData() | |
3290 { | |
3291 var data = new QueryData { Query = this }; | |
3292 | |
3293 new QueryVisitor().VisitParentFirst(this, e => | |
3294 { | |
3295 switch (e.ElementType) | |
3296 { | |
3297 case QueryElementType.SqlField : | |
3298 { | |
3299 var field = (SqlField)e; | |
3300 | |
3301 if (field.Name.Length != 1 || field.Name[0] != '*') | |
3302 data.Fields.Add(field); | |
3303 | |
3304 break; | |
3305 } | |
3306 | |
3307 case QueryElementType.SqlQuery : | |
3308 { | |
3309 if (e != this) | |
3310 { | |
3311 data.Queries.Add(((SqlQuery)e).GetQueryData()); | |
3312 return false; | |
3313 } | |
3314 | |
3315 break; | |
3316 } | |
3317 | |
3318 case QueryElementType.Column : | |
3319 return ((Column)e).Parent == this; | |
3320 | |
3321 case QueryElementType.SqlTable : | |
3322 return false; | |
3323 } | |
3324 | |
3325 return true; | |
3326 }); | |
3327 | |
3328 return data; | |
3329 } | |
3330 | |
3331 static TableSource FindField(SqlField field, TableSource table) | |
3332 { | |
3333 if (field.Table == table.Source) | |
3334 return table; | |
3335 | |
3336 foreach (var @join in table.Joins) | |
3337 { | |
3338 var t = FindField(field, @join.Table); | |
3339 | |
3340 if (t != null) | |
3341 return @join.Table; | |
3342 } | |
3343 | |
3344 return null; | |
3345 } | |
3346 | |
3347 static ISqlExpression GetColumn(QueryData data, SqlField field) | |
3348 { | |
3349 foreach (var query in data.Queries) | |
3350 { | |
3351 var q = query.Query; | |
3352 | |
3353 foreach (var table in q.From.Tables) | |
3354 { | |
3355 var t = FindField(field, table); | |
3356 | |
3357 if (t != null) | |
3358 { | |
3359 var n = q.Select.Columns.Count; | |
3360 var idx = q.Select.Add(field); | |
3361 | |
3362 if (n != q.Select.Columns.Count) | |
3363 if (!q.GroupBy.IsEmpty || q.Select.Columns.Exists(c => IsAggregationFunction(c.Expression))) | |
3364 q.GroupBy.Items.Add(field); | |
3365 | |
3366 return q.Select.Columns[idx]; | |
3367 } | |
3368 } | |
3369 } | |
3370 | |
3371 return null; | |
3372 } | |
3373 | |
3374 static void ResolveFields(QueryData data) | |
3375 { | |
3376 if (data.Queries.Count == 0) | |
3377 return; | |
3378 | |
3379 var dic = new Dictionary<ISqlExpression,ISqlExpression>(); | |
3380 | |
3381 foreach (SqlField field in data.Fields) | |
3382 { | |
3383 if (dic.ContainsKey(field)) | |
3384 continue; | |
3385 | |
3386 var found = false; | |
3387 | |
3388 foreach (var table in data.Query.From.Tables) | |
3389 { | |
3390 found = FindField(field, table) != null; | |
3391 | |
3392 if (found) | |
3393 break; | |
3394 } | |
3395 | |
3396 if (!found) | |
3397 { | |
3398 var expr = GetColumn(data, field); | |
3399 | |
3400 if (expr != null) | |
3401 dic.Add(field, expr); | |
3402 } | |
3403 } | |
3404 | |
3405 if (dic.Count > 0) | |
3406 new QueryVisitor().VisitParentFirst(data.Query, e => | |
3407 { | |
3408 ISqlExpression ex; | |
3409 | |
3410 switch (e.ElementType) | |
3411 { | |
3412 case QueryElementType.SqlQuery : | |
3413 return e == data.Query; | |
3414 | |
3415 case QueryElementType.SqlFunction : | |
3416 { | |
3417 var parms = ((SqlFunction)e).Parameters; | |
3418 | |
3419 for (var i = 0; i < parms.Length; i++) | |
3420 if (dic.TryGetValue(parms[i], out ex)) | |
3421 parms[i] = ex; | |
3422 | |
3423 break; | |
3424 } | |
3425 | |
3426 case QueryElementType.SqlExpression : | |
3427 { | |
3428 var parms = ((SqlExpression)e).Parameters; | |
3429 | |
3430 for (var i = 0; i < parms.Length; i++) | |
3431 if (dic.TryGetValue(parms[i], out ex)) | |
3432 parms[i] = ex; | |
3433 | |
3434 break; | |
3435 } | |
3436 | |
3437 case QueryElementType.SqlBinaryExpression : | |
3438 { | |
3439 var expr = (SqlBinaryExpression)e; | |
3440 if (dic.TryGetValue(expr.Expr1, out ex)) expr.Expr1 = ex; | |
3441 if (dic.TryGetValue(expr.Expr2, out ex)) expr.Expr2 = ex; | |
3442 break; | |
3443 } | |
3444 | |
3445 case QueryElementType.ExprPredicate : | |
3446 case QueryElementType.NotExprPredicate : | |
3447 case QueryElementType.IsNullPredicate : | |
3448 case QueryElementType.InSubQueryPredicate : | |
3449 { | |
3450 var expr = (Predicate.Expr)e; | |
3451 if (dic.TryGetValue(expr.Expr1, out ex)) expr.Expr1 = ex; | |
3452 break; | |
3453 } | |
3454 | |
3455 case QueryElementType.ExprExprPredicate : | |
3456 { | |
3457 var expr = (Predicate.ExprExpr)e; | |
3458 if (dic.TryGetValue(expr.Expr1, out ex)) expr.Expr1 = ex; | |
3459 if (dic.TryGetValue(expr.Expr2, out ex)) expr.Expr2 = ex; | |
3460 break; | |
3461 } | |
3462 | |
3463 case QueryElementType.LikePredicate : | |
3464 { | |
3465 var expr = (Predicate.Like)e; | |
3466 if (dic.TryGetValue(expr.Expr1, out ex)) expr.Expr1 = ex; | |
3467 if (dic.TryGetValue(expr.Expr2, out ex)) expr.Expr2 = ex; | |
3468 if (dic.TryGetValue(expr.Escape, out ex)) expr.Escape = ex; | |
3469 break; | |
3470 } | |
3471 | |
3472 case QueryElementType.BetweenPredicate : | |
3473 { | |
3474 var expr = (Predicate.Between)e; | |
3475 if (dic.TryGetValue(expr.Expr1, out ex)) expr.Expr1 = ex; | |
3476 if (dic.TryGetValue(expr.Expr2, out ex)) expr.Expr2 = ex; | |
3477 if (dic.TryGetValue(expr.Expr3, out ex)) expr.Expr3 = ex; | |
3478 break; | |
3479 } | |
3480 | |
3481 case QueryElementType.InListPredicate : | |
3482 { | |
3483 var expr = (Predicate.InList)e; | |
3484 | |
3485 if (dic.TryGetValue(expr.Expr1, out ex)) expr.Expr1 = ex; | |
3486 | |
3487 for (var i = 0; i < expr.Values.Count; i++) | |
3488 if (dic.TryGetValue(expr.Values[i], out ex)) | |
3489 expr.Values[i] = ex; | |
3490 | |
3491 break; | |
3492 } | |
3493 | |
3494 case QueryElementType.Column : | |
3495 { | |
3496 var expr = (Column)e; | |
3497 | |
3498 if (expr.Parent != data.Query) | |
3499 return false; | |
3500 | |
3501 if (dic.TryGetValue(expr.Expression, out ex)) expr.Expression = ex; | |
3502 | |
3503 break; | |
3504 } | |
3505 | |
3506 case QueryElementType.SetExpression : | |
3507 { | |
3508 var expr = (SetExpression)e; | |
3509 if (dic.TryGetValue(expr.Expression, out ex)) expr.Expression = ex; | |
3510 break; | |
3511 } | |
3512 | |
3513 case QueryElementType.GroupByClause : | |
3514 { | |
3515 var expr = (GroupByClause)e; | |
3516 | |
3517 for (var i = 0; i < expr.Items.Count; i++) | |
3518 if (dic.TryGetValue(expr.Items[i], out ex)) | |
3519 expr.Items[i] = ex; | |
3520 | |
3521 break; | |
3522 } | |
3523 | |
3524 case QueryElementType.OrderByItem : | |
3525 { | |
3526 var expr = (OrderByItem)e; | |
3527 if (dic.TryGetValue(expr.Expression, out ex)) expr.Expression = ex; | |
3528 break; | |
3529 } | |
3530 } | |
3531 | |
3532 return true; | |
3533 }); | |
3534 | |
3535 foreach (var query in data.Queries) | |
3536 if (query.Queries.Count > 0) | |
3537 ResolveFields(query); | |
3538 } | |
3539 | |
3540 void OptimizeUnions() | |
3541 { | |
3542 var exprs = new Dictionary<ISqlExpression,ISqlExpression>(); | |
3543 | |
3544 new QueryVisitor().Visit(this, e => | |
3545 { | |
3546 var sql = e as SqlQuery; | |
3547 | |
3548 if (sql == null || sql.From.Tables.Count != 1 || !sql.IsSimple || sql._insert != null || sql._update != null || sql._delete != null) | |
3549 return; | |
3550 | |
3551 var table = sql.From.Tables[0]; | |
3552 | |
3553 if (table.Joins.Count != 0 || !(table.Source is SqlQuery)) | |
3554 return; | |
3555 | |
3556 var union = (SqlQuery)table.Source; | |
3557 | |
3558 if (!union.HasUnion) | |
3559 return; | |
3560 | |
3561 for (var i = 0; i < sql.Select.Columns.Count; i++) | |
3562 { | |
3563 var scol = sql. Select.Columns[i]; | |
3564 var ucol = union.Select.Columns[i]; | |
3565 | |
3566 if (scol.Expression != ucol) | |
3567 return; | |
3568 } | |
3569 | |
3570 exprs.Add(union, sql); | |
3571 | |
3572 for (var i = 0; i < sql.Select.Columns.Count; i++) | |
3573 { | |
3574 var scol = sql. Select.Columns[i]; | |
3575 var ucol = union.Select.Columns[i]; | |
3576 | |
3577 scol.Expression = ucol.Expression; | |
3578 scol._alias = ucol._alias; | |
3579 | |
3580 exprs.Add(ucol, scol); | |
3581 } | |
3582 | |
3583 for (var i = sql.Select.Columns.Count; i < union.Select.Columns.Count; i++) | |
3584 sql.Select.Expr(union.Select.Columns[i].Expression); | |
3585 | |
3586 sql.From.Tables.Clear(); | |
3587 sql.From.Tables.AddRange(union.From.Tables); | |
3588 | |
3589 sql.Where. SearchCondition.Conditions.AddRange(union.Where. SearchCondition.Conditions); | |
3590 sql.Having. SearchCondition.Conditions.AddRange(union.Having.SearchCondition.Conditions); | |
3591 sql.GroupBy.Items. AddRange(union.GroupBy.Items); | |
3592 sql.OrderBy.Items. AddRange(union.OrderBy.Items); | |
3593 sql.Unions.InsertRange(0, union.Unions); | |
3594 }); | |
3595 | |
3596 ((ISqlExpressionWalkable)this).Walk(false, expr => | |
3597 { | |
3598 ISqlExpression e; | |
3599 | |
3600 if (exprs.TryGetValue(expr, out e)) | |
3601 return e; | |
3602 | |
3603 return expr; | |
3604 }); | |
3605 } | |
3606 | |
3607 void FinalizeAndValidateInternal(bool isApplySupported, bool optimizeColumns, bool optimizeSearchCondition, List<ISqlTableSource> tables) | |
3608 { | |
3609 OptimizeSearchCondition(Where. SearchCondition); | |
3610 OptimizeSearchCondition(Having.SearchCondition); | |
3611 | |
3612 if (optimizeSearchCondition) | |
3613 { | |
3614 ForEachTable(table => | |
3615 { | |
3616 foreach (var join in table.Joins) | |
3617 OptimizeSearchCondition(join.Condition); | |
3618 }, new HashSet<SqlQuery>()); | |
3619 } | |
3620 | |
3621 new QueryVisitor().Visit(this, e => | |
3622 { | |
3623 var sql = e as SqlQuery; | |
3624 | |
3625 if (sql != null && sql != this) | |
3626 { | |
3627 sql.ParentSql = this; | |
3628 sql.FinalizeAndValidateInternal(isApplySupported, optimizeColumns, false, tables); | |
3629 | |
3630 if (sql.IsParameterDependent) | |
3631 IsParameterDependent = true; | |
3632 } | |
3633 }); | |
3634 | |
3635 ResolveWeakJoins(tables); | |
3636 OptimizeColumns(); | |
3637 OptimizeApplies (isApplySupported, optimizeColumns); | |
3638 OptimizeSubQueries(isApplySupported, optimizeColumns); | |
3639 OptimizeApplies (isApplySupported, optimizeColumns); | |
3640 | |
3641 new QueryVisitor().Visit(this, e => | |
3642 { | |
3643 var sql = e as SqlQuery; | |
3644 | |
3645 if (sql != null && sql != this) | |
3646 sql.RemoveOrderBy(); | |
3647 }); | |
3648 } | |
3649 | |
3650 internal static void OptimizeSearchCondition(SearchCondition searchCondition) | |
3651 { | |
3652 // This 'if' could be replaced by one simple match: | |
3653 // | |
3654 // match (searchCondition.Conditions) | |
3655 // { | |
3656 // | [SearchCondition(true, _) sc] => | |
3657 // searchCondition.Conditions = sc.Conditions; | |
3658 // OptimizeSearchCondition(searchCodition) | |
3659 // | |
3660 // | [SearchCondition(false, [SearchCondition(true, [ExprExpr]) sc])] => ... | |
3661 // | |
3662 // | [Expr(true, SqlValue(true))] | |
3663 // | [Expr(false, SqlValue(false))] | |
3664 // searchCondition.Conditions = [] | |
3665 // } | |
3666 // | |
3667 // One day I am going to rewrite all this crap in Nemerle. | |
3668 // | |
3669 if (searchCondition.Conditions.Count == 1) | |
3670 { | |
3671 var cond = searchCondition.Conditions[0]; | |
3672 | |
3673 if (cond.Predicate is SearchCondition) | |
3674 { | |
3675 var sc = (SearchCondition)cond.Predicate; | |
3676 | |
3677 if (!cond.IsNot) | |
3678 { | |
3679 searchCondition.Conditions.Clear(); | |
3680 searchCondition.Conditions.AddRange(sc.Conditions); | |
3681 | |
3682 OptimizeSearchCondition(searchCondition); | |
3683 return; | |
3684 } | |
3685 | |
3686 if (sc.Conditions.Count == 1) | |
3687 { | |
3688 var c1 = sc.Conditions[0]; | |
3689 | |
3690 if (!c1.IsNot && c1.Predicate is Predicate.ExprExpr) | |
3691 { | |
3692 var ee = (Predicate.ExprExpr)c1.Predicate; | |
3693 Predicate.Operator op; | |
3694 | |
3695 switch (ee.Operator) | |
3696 { | |
3697 case Predicate.Operator.Equal : op = Predicate.Operator.NotEqual; break; | |
3698 case Predicate.Operator.NotEqual : op = Predicate.Operator.Equal; break; | |
3699 case Predicate.Operator.Greater : op = Predicate.Operator.LessOrEqual; break; | |
3700 case Predicate.Operator.NotLess : | |
3701 case Predicate.Operator.GreaterOrEqual : op = Predicate.Operator.Less; break; | |
3702 case Predicate.Operator.Less : op = Predicate.Operator.GreaterOrEqual; break; | |
3703 case Predicate.Operator.NotGreater : | |
3704 case Predicate.Operator.LessOrEqual : op = Predicate.Operator.Greater; break; | |
3705 default: throw new InvalidOperationException(); | |
3706 } | |
3707 | |
3708 c1.Predicate = new Predicate.ExprExpr(ee.Expr1, op, ee.Expr2); | |
3709 | |
3710 searchCondition.Conditions.Clear(); | |
3711 searchCondition.Conditions.AddRange(sc.Conditions); | |
3712 | |
3713 OptimizeSearchCondition(searchCondition); | |
3714 return; | |
3715 } | |
3716 } | |
3717 } | |
3718 | |
3719 if (cond.Predicate.ElementType == QueryElementType.ExprPredicate) | |
3720 { | |
3721 var expr = (Predicate.Expr)cond.Predicate; | |
3722 | |
3723 if (expr.Expr1 is SqlValue) | |
3724 { | |
3725 var value = (SqlValue)expr.Expr1; | |
3726 | |
3727 if (value.Value is bool) | |
3728 if (cond.IsNot ? !(bool)value.Value : (bool)value.Value) | |
3729 searchCondition.Conditions.Clear(); | |
3730 } | |
3731 } | |
3732 } | |
3733 | |
3734 for (var i = 0; i < searchCondition.Conditions.Count; i++) | |
3735 { | |
3736 var cond = searchCondition.Conditions[i]; | |
3737 | |
3738 if (cond.Predicate is Predicate.Expr) | |
3739 { | |
3740 var expr = (Predicate.Expr)cond.Predicate; | |
3741 | |
3742 if (expr.Expr1 is SqlValue) | |
3743 { | |
3744 var value = (SqlValue)expr.Expr1; | |
3745 | |
3746 if (value.Value is bool) | |
3747 { | |
3748 if (cond.IsNot ? !(bool)value.Value : (bool)value.Value) | |
3749 { | |
3750 if (i > 0) | |
3751 { | |
3752 if (searchCondition.Conditions[i-1].IsOr) | |
3753 { | |
3754 searchCondition.Conditions.RemoveRange(0, i); | |
3755 OptimizeSearchCondition(searchCondition); | |
3756 | |
3757 break; | |
3758 } | |
3759 } | |
3760 } | |
3761 } | |
3762 } | |
3763 } | |
3764 else if (cond.Predicate is SearchCondition) | |
3765 { | |
3766 var sc = (SearchCondition)cond.Predicate; | |
3767 OptimizeSearchCondition(sc); | |
3768 } | |
3769 } | |
3770 } | |
3771 | |
3772 void ForEachTable(Action<TableSource> action, HashSet<SqlQuery> visitedQueries) | |
3773 { | |
3774 if (!visitedQueries.Add(this)) | |
3775 return; | |
3776 | |
3777 foreach (var table in From.Tables) | |
3778 table.ForEach(action, visitedQueries); | |
3779 | |
3780 new QueryVisitor().Visit(this, e => | |
3781 { | |
3782 if (e is SqlQuery && e != this) | |
3783 ((SqlQuery)e).ForEachTable(action, visitedQueries); | |
3784 }); | |
3785 } | |
3786 | |
3787 void RemoveOrderBy() | |
3788 { | |
3789 if (OrderBy.Items.Count > 0 && Select.SkipValue == null && Select.TakeValue == null) | |
3790 OrderBy.Items.Clear(); | |
3791 } | |
3792 | |
3793 internal void ResolveWeakJoins(List<ISqlTableSource> tables) | |
3794 { | |
3795 Func<TableSource,bool> findTable = null; findTable = table => | |
3796 { | |
3797 if (tables.Contains(table.Source)) | |
3798 return true; | |
3799 | |
3800 foreach (var join in table.Joins) | |
3801 { | |
3802 if (findTable(join.Table)) | |
3803 { | |
3804 join.IsWeak = false; | |
3805 return true; | |
3806 } | |
3807 } | |
3808 | |
3809 if (table.Source is SqlQuery) | |
3810 foreach (var t in ((SqlQuery)table.Source).From.Tables) | |
3811 if (findTable(t)) | |
3812 return true; | |
3813 | |
3814 return false; | |
3815 }; | |
3816 | |
3817 var areTablesCollected = false; | |
3818 | |
3819 ForEachTable(table => | |
3820 { | |
3821 for (var i = 0; i < table.Joins.Count; i++) | |
3822 { | |
3823 var join = table.Joins[i]; | |
3824 | |
3825 if (join.IsWeak) | |
3826 { | |
3827 if (!areTablesCollected) | |
3828 { | |
3829 areTablesCollected = true; | |
3830 | |
3831 Action<IQueryElement> tableCollector = expr => | |
3832 { | |
3833 var field = expr as SqlField; | |
3834 | |
3835 if (field != null && !tables.Contains(field.Table)) | |
3836 tables.Add(field.Table); | |
3837 }; | |
3838 | |
3839 var visitor = new QueryVisitor(); | |
3840 | |
3841 visitor.VisitAll(Select, tableCollector); | |
3842 visitor.VisitAll(Where, tableCollector); | |
3843 visitor.VisitAll(GroupBy, tableCollector); | |
3844 visitor.VisitAll(Having, tableCollector); | |
3845 visitor.VisitAll(OrderBy, tableCollector); | |
3846 | |
3847 if (_insert != null) | |
3848 visitor.VisitAll(Insert, tableCollector); | |
3849 | |
3850 if (_update != null) | |
3851 visitor.VisitAll(Update, tableCollector); | |
3852 | |
3853 if (_delete != null) | |
3854 visitor.VisitAll(Delete, tableCollector); | |
3855 | |
3856 visitor.VisitAll(From, expr => | |
3857 { | |
3858 var tbl = expr as SqlTable; | |
3859 | |
3860 if (tbl != null && tbl.TableArguments != null) | |
3861 { | |
3862 var v = new QueryVisitor(); | |
3863 | |
3864 foreach (var arg in tbl.TableArguments) | |
3865 v.VisitAll(arg, tableCollector); | |
3866 } | |
3867 }); | |
3868 } | |
3869 | |
3870 if (findTable(join.Table)) | |
3871 { | |
3872 join.IsWeak = false; | |
3873 } | |
3874 else | |
3875 { | |
3876 table.Joins.RemoveAt(i); | |
3877 i--; | |
3878 } | |
3879 } | |
3880 } | |
3881 }, new HashSet<SqlQuery>()); | |
3882 } | |
3883 | |
3884 TableSource OptimizeSubQuery( | |
3885 TableSource source, | |
3886 bool optimizeWhere, | |
3887 bool allColumns, | |
3888 bool isApplySupported, | |
3889 bool optimizeValues, | |
3890 bool optimizeColumns) | |
3891 { | |
3892 foreach (var jt in source.Joins) | |
3893 { | |
3894 var table = OptimizeSubQuery( | |
3895 jt.Table, | |
3896 jt.JoinType == JoinType.Inner || jt.JoinType == JoinType.CrossApply, | |
3897 false, | |
3898 isApplySupported, | |
3899 jt.JoinType == JoinType.Inner || jt.JoinType == JoinType.CrossApply, | |
3900 optimizeColumns); | |
3901 | |
3902 if (table != jt.Table) | |
3903 { | |
3904 var sql = jt.Table.Source as SqlQuery; | |
3905 | |
3906 if (sql != null && sql.OrderBy.Items.Count > 0) | |
3907 foreach (var item in sql.OrderBy.Items) | |
3908 OrderBy.Expr(item.Expression, item.IsDescending); | |
3909 | |
3910 jt.Table = table; | |
3911 } | |
3912 } | |
3913 | |
3914 return source.Source is SqlQuery ? | |
3915 RemoveSubQuery(source, optimizeWhere, allColumns && !isApplySupported, optimizeValues, optimizeColumns) : | |
3916 source; | |
3917 } | |
3918 | |
3919 static bool CheckColumn(Column column, ISqlExpression expr, SqlQuery query, bool optimizeValues, bool optimizeColumns) | |
3920 { | |
3921 if (expr is SqlField || expr is Column) | |
3922 return false; | |
3923 | |
3924 if (expr is SqlValue) | |
3925 return !optimizeValues && 1.Equals(((SqlValue)expr).Value); | |
3926 | |
3927 if (expr is SqlBinaryExpression) | |
3928 { | |
3929 var e = (SqlBinaryExpression)expr; | |
3930 | |
3931 if (e.Operation == "*" && e.Expr1 is SqlValue) | |
3932 { | |
3933 var value = (SqlValue)e.Expr1; | |
3934 | |
3935 if (value.Value is int && (int)value.Value == -1) | |
3936 return CheckColumn(column, e.Expr2, query, optimizeValues, optimizeColumns); | |
3937 } | |
3938 } | |
3939 | |
3940 var visitor = new QueryVisitor(); | |
3941 | |
3942 if (optimizeColumns && | |
3943 visitor.Find(expr, e => e is SqlQuery || IsAggregationFunction(e)) == null) | |
3944 { | |
3945 var n = 0; | |
3946 var q = query.ParentSql ?? query; | |
3947 | |
3948 visitor.VisitAll(q, e => { if (e == column) n++; }); | |
3949 | |
3950 return n > 2; | |
3951 } | |
3952 | |
3953 return true; | |
3954 } | |
3955 | |
3956 TableSource RemoveSubQuery( | |
3957 TableSource childSource, | |
3958 bool concatWhere, | |
3959 bool allColumns, | |
3960 bool optimizeValues, | |
3961 bool optimizeColumns) | |
3962 { | |
3963 var query = (SqlQuery)childSource.Source; | |
3964 | |
3965 var isQueryOK = query.From.Tables.Count == 1; | |
3966 | |
3967 isQueryOK = isQueryOK && (concatWhere || query.Where.IsEmpty && query.Having.IsEmpty); | |
3968 isQueryOK = isQueryOK && !query.HasUnion && query.GroupBy.IsEmpty && !query.Select.HasModifier; | |
3969 | |
3970 if (!isQueryOK) | |
3971 return childSource; | |
3972 | |
3973 var isColumnsOK = | |
3974 (allColumns && !query.Select.Columns.Exists(c => IsAggregationFunction(c.Expression))) || | |
3975 !query.Select.Columns.Exists(c => CheckColumn(c, c.Expression, query, optimizeValues, optimizeColumns)); | |
3976 | |
3977 if (!isColumnsOK) | |
3978 return childSource; | |
3979 | |
3980 var map = new Dictionary<ISqlExpression,ISqlExpression>(query.Select.Columns.Count); | |
3981 | |
3982 foreach (var c in query.Select.Columns) | |
3983 map.Add(c, c.Expression); | |
3984 | |
3985 var top = this; | |
3986 | |
3987 while (top.ParentSql != null) | |
3988 top = top.ParentSql; | |
3989 | |
3990 ((ISqlExpressionWalkable)top).Walk(false, expr => | |
3991 { | |
3992 ISqlExpression fld; | |
3993 return map.TryGetValue(expr, out fld) ? fld : expr; | |
3994 }); | |
3995 | |
3996 new QueryVisitor().Visit(top, expr => | |
3997 { | |
3998 if (expr.ElementType == QueryElementType.InListPredicate) | |
3999 { | |
4000 var p = (Predicate.InList)expr; | |
4001 | |
4002 if (p.Expr1 == query) | |
4003 p.Expr1 = query.From.Tables[0]; | |
4004 } | |
4005 }); | |
4006 | |
4007 query.From.Tables[0].Joins.AddRange(childSource.Joins); | |
4008 | |
4009 if (query.From.Tables[0].Alias == null) | |
4010 query.From.Tables[0].Alias = childSource.Alias; | |
4011 | |
4012 if (!query.Where. IsEmpty) ConcatSearchCondition(Where, query.Where); | |
4013 if (!query.Having.IsEmpty) ConcatSearchCondition(Having, query.Having); | |
4014 | |
4015 ((ISqlExpressionWalkable)top).Walk(false, expr => | |
4016 { | |
4017 if (expr is SqlQuery) | |
4018 { | |
4019 var sql = (SqlQuery)expr; | |
4020 | |
4021 if (sql.ParentSql == query) | |
4022 sql.ParentSql = query.ParentSql ?? this; | |
4023 } | |
4024 | |
4025 return expr; | |
4026 }); | |
4027 | |
4028 return query.From.Tables[0]; | |
4029 } | |
4030 | |
4031 static bool IsAggregationFunction(IQueryElement expr) | |
4032 { | |
4033 if (expr is SqlFunction) | |
4034 switch (((SqlFunction)expr).Name) | |
4035 { | |
4036 case "Count" : | |
4037 case "Average" : | |
4038 case "Min" : | |
4039 case "Max" : | |
4040 case "Sum" : return true; | |
4041 } | |
4042 | |
4043 return false; | |
4044 } | |
4045 | |
4046 void OptimizeApply(TableSource tableSource, JoinedTable joinTable, bool isApplySupported, bool optimizeColumns) | |
4047 { | |
4048 var joinSource = joinTable.Table; | |
4049 | |
4050 foreach (var join in joinSource.Joins) | |
4051 if (join.JoinType == JoinType.CrossApply || join.JoinType == JoinType.OuterApply) | |
4052 OptimizeApply(joinSource, join, isApplySupported, optimizeColumns); | |
4053 | |
4054 if (isApplySupported && !joinTable.CanConvertApply) | |
4055 return; | |
4056 | |
4057 if (joinSource.Source.ElementType == QueryElementType.SqlQuery) | |
4058 { | |
4059 var sql = (SqlQuery)joinSource.Source; | |
4060 var isAgg = sql.Select.Columns.Exists(c => IsAggregationFunction(c.Expression)); | |
4061 | |
4062 if (isApplySupported && (isAgg || sql.Select.TakeValue != null || sql.Select.SkipValue != null)) | |
4063 return; | |
4064 | |
4065 var searchCondition = new List<Condition>(sql.Where.SearchCondition.Conditions); | |
4066 | |
4067 sql.Where.SearchCondition.Conditions.Clear(); | |
4068 | |
4069 if (!ContainsTable(tableSource.Source, sql)) | |
4070 { | |
4071 joinTable.JoinType = joinTable.JoinType == JoinType.CrossApply ? JoinType.Inner : JoinType.Left; | |
4072 joinTable.Condition.Conditions.AddRange(searchCondition); | |
4073 } | |
4074 else | |
4075 { | |
4076 sql.Where.SearchCondition.Conditions.AddRange(searchCondition); | |
4077 | |
4078 var table = OptimizeSubQuery( | |
4079 joinTable.Table, | |
4080 joinTable.JoinType == JoinType.Inner || joinTable.JoinType == JoinType.CrossApply, | |
4081 joinTable.JoinType == JoinType.CrossApply, | |
4082 isApplySupported, | |
4083 joinTable.JoinType == JoinType.Inner || joinTable.JoinType == JoinType.CrossApply, | |
4084 optimizeColumns); | |
4085 | |
4086 if (table != joinTable.Table) | |
4087 { | |
4088 var q = joinTable.Table.Source as SqlQuery; | |
4089 | |
4090 if (q != null && q.OrderBy.Items.Count > 0) | |
4091 foreach (var item in q.OrderBy.Items) | |
4092 OrderBy.Expr(item.Expression, item.IsDescending); | |
4093 | |
4094 joinTable.Table = table; | |
4095 | |
4096 OptimizeApply(tableSource, joinTable, isApplySupported, optimizeColumns); | |
4097 } | |
4098 } | |
4099 } | |
4100 else | |
4101 { | |
4102 if (!ContainsTable(tableSource.Source, joinSource.Source)) | |
4103 joinTable.JoinType = joinTable.JoinType == JoinType.CrossApply ? JoinType.Inner : JoinType.Left; | |
4104 } | |
4105 } | |
4106 | |
4107 static bool ContainsTable(ISqlTableSource table, IQueryElement sql) | |
4108 { | |
4109 return null != new QueryVisitor().Find(sql, e => | |
4110 e == table || | |
4111 e.ElementType == QueryElementType.SqlField && table == ((SqlField)e).Table || | |
4112 e.ElementType == QueryElementType.Column && table == ((Column) e).Parent); | |
4113 } | |
4114 | |
4115 static void ConcatSearchCondition(WhereClause where1, WhereClause where2) | |
4116 { | |
4117 if (where1.IsEmpty) | |
4118 { | |
4119 where1.SearchCondition.Conditions.AddRange(where2.SearchCondition.Conditions); | |
4120 } | |
4121 else | |
4122 { | |
4123 if (where1.SearchCondition.Precedence < Sql.Precedence.LogicalConjunction) | |
4124 { | |
4125 var sc1 = new SearchCondition(); | |
4126 | |
4127 sc1.Conditions.AddRange(where1.SearchCondition.Conditions); | |
4128 | |
4129 where1.SearchCondition.Conditions.Clear(); | |
4130 where1.SearchCondition.Conditions.Add(new Condition(false, sc1)); | |
4131 } | |
4132 | |
4133 if (where2.SearchCondition.Precedence < Sql.Precedence.LogicalConjunction) | |
4134 { | |
4135 var sc2 = new SearchCondition(); | |
4136 | |
4137 sc2.Conditions.AddRange(where2.SearchCondition.Conditions); | |
4138 | |
4139 where1.SearchCondition.Conditions.Add(new Condition(false, sc2)); | |
4140 } | |
4141 else | |
4142 where1.SearchCondition.Conditions.AddRange(where2.SearchCondition.Conditions); | |
4143 } | |
4144 } | |
4145 | |
4146 void OptimizeSubQueries(bool isApplySupported, bool optimizeColumns) | |
4147 { | |
4148 for (var i = 0; i < From.Tables.Count; i++) | |
4149 { | |
4150 var table = OptimizeSubQuery(From.Tables[i], true, false, isApplySupported, true, optimizeColumns); | |
4151 | |
4152 if (table != From.Tables[i]) | |
4153 { | |
4154 var sql = From.Tables[i].Source as SqlQuery; | |
4155 | |
4156 if (!Select.Columns.All(c => IsAggregationFunction(c.Expression))) | |
4157 if (sql != null && sql.OrderBy.Items.Count > 0) | |
4158 foreach (var item in sql.OrderBy.Items) | |
4159 OrderBy.Expr(item.Expression, item.IsDescending); | |
4160 | |
4161 From.Tables[i] = table; | |
4162 } | |
4163 } | |
4164 } | |
4165 | |
4166 void OptimizeApplies(bool isApplySupported, bool optimizeColumns) | |
4167 { | |
4168 foreach (var table in From.Tables) | |
4169 foreach (var join in table.Joins) | |
4170 if (join.JoinType == JoinType.CrossApply || join.JoinType == JoinType.OuterApply) | |
4171 OptimizeApply(table, join, isApplySupported, optimizeColumns); | |
4172 } | |
4173 | |
4174 void OptimizeColumns() | |
4175 { | |
4176 ((ISqlExpressionWalkable)Select).Walk(false, expr => | |
4177 { | |
4178 var query = expr as SqlQuery; | |
4179 | |
4180 if (query != null && query.From.Tables.Count == 0 && query.Select.Columns.Count == 1) | |
4181 { | |
4182 new QueryVisitor().Visit(query.Select.Columns[0].Expression, e => | |
4183 { | |
4184 if (e.ElementType == QueryElementType.SqlQuery) | |
4185 { | |
4186 var q = (SqlQuery)e; | |
4187 | |
4188 if (q.ParentSql == query) | |
4189 q.ParentSql = query.ParentSql; | |
4190 } | |
4191 }); | |
4192 | |
4193 return query.Select.Columns[0].Expression; | |
4194 } | |
4195 | |
4196 return expr; | |
4197 }); | |
4198 } | |
4199 | |
4200 IDictionary<string,object> _aliases; | |
4201 | |
4202 public void RemoveAlias(string alias) | |
4203 { | |
4204 if (_aliases != null) | |
4205 { | |
4206 alias = alias.ToUpper(); | |
4207 if (_aliases.ContainsKey(alias)) | |
4208 _aliases.Remove(alias); | |
4209 } | |
4210 } | |
4211 | |
4212 public string GetAlias(string desiredAlias, string defaultAlias) | |
4213 { | |
4214 if (_aliases == null) | |
4215 _aliases = new Dictionary<string,object>(); | |
4216 | |
4217 var alias = desiredAlias; | |
4218 | |
4219 if (string.IsNullOrEmpty(desiredAlias) || desiredAlias.Length > 30) | |
4220 { | |
4221 desiredAlias = defaultAlias; | |
4222 alias = defaultAlias + "1"; | |
4223 } | |
4224 | |
4225 for (var i = 1; ; i++) | |
4226 { | |
4227 var s = alias.ToUpper(); | |
4228 | |
4229 if (!_aliases.ContainsKey(s) && !_reservedWords.ContainsKey(s)) | |
4230 { | |
4231 _aliases.Add(s, s); | |
4232 break; | |
4233 } | |
4234 | |
4235 alias = desiredAlias + i; | |
4236 } | |
4237 | |
4238 return alias; | |
4239 } | |
4240 | |
4241 public string[] GetTempAliases(int n, string defaultAlias) | |
4242 { | |
4243 var aliases = new string[n]; | |
4244 | |
4245 for (var i = 0; i < aliases.Length; i++) | |
4246 aliases[i] = GetAlias(defaultAlias, defaultAlias); | |
4247 | |
4248 foreach (var t in aliases) | |
4249 RemoveAlias(t); | |
4250 | |
4251 return aliases; | |
4252 } | |
4253 | |
4254 void SetAliases() | |
4255 { | |
4256 _aliases = null; | |
4257 | |
4258 var objs = new Dictionary<object,object>(); | |
4259 | |
4260 Parameters.Clear(); | |
4261 | |
4262 new QueryVisitor().VisitAll(this, expr => | |
4263 { | |
4264 switch (expr.ElementType) | |
4265 { | |
4266 case QueryElementType.SqlParameter: | |
4267 { | |
4268 var p = (SqlParameter)expr; | |
4269 | |
4270 if (p.IsQueryParameter) | |
4271 { | |
4272 if (!objs.ContainsKey(expr)) | |
4273 { | |
4274 objs.Add(expr, expr); | |
4275 p.Name = GetAlias(p.Name, "p"); | |
4276 } | |
4277 | |
4278 Parameters.Add(p); | |
4279 } | |
4280 else | |
4281 IsParameterDependent = true; | |
4282 } | |
4283 | |
4284 break; | |
4285 | |
4286 case QueryElementType.Column: | |
4287 { | |
4288 if (!objs.ContainsKey(expr)) | |
4289 { | |
4290 objs.Add(expr, expr); | |
4291 | |
4292 var c = (Column)expr; | |
4293 | |
4294 if (c.Alias != "*") | |
4295 c.Alias = GetAlias(c.Alias, "c"); | |
4296 } | |
4297 } | |
4298 | |
4299 break; | |
4300 | |
4301 case QueryElementType.TableSource: | |
4302 { | |
4303 var table = (TableSource)expr; | |
4304 | |
4305 if (!objs.ContainsKey(table)) | |
4306 { | |
4307 objs.Add(table, table); | |
4308 table.Alias = GetAlias(table.Alias, "t"); | |
4309 } | |
4310 } | |
4311 | |
4312 break; | |
4313 | |
4314 case QueryElementType.SqlQuery: | |
4315 { | |
4316 var sql = (SqlQuery)expr; | |
4317 | |
4318 if (sql.HasUnion) | |
4319 { | |
4320 for (var i = 0; i < sql.Select.Columns.Count; i++) | |
4321 { | |
4322 var col = sql.Select.Columns[i]; | |
4323 | |
4324 foreach (var t in sql.Unions) | |
4325 { | |
4326 var union = t.SqlQuery.Select; | |
4327 | |
4328 objs.Remove(union.Columns[i].Alias); | |
4329 | |
4330 union.Columns[i].Alias = col.Alias; | |
4331 } | |
4332 } | |
4333 } | |
4334 } | |
4335 | |
4336 break; | |
4337 } | |
4338 }); | |
4339 } | |
4340 | |
4341 #endregion | |
4342 | |
4343 #region ProcessParameters | |
4344 | |
4345 public SqlQuery ProcessParameters() | |
4346 { | |
4347 if (IsParameterDependent) | |
4348 { | |
4349 var query = new QueryVisitor().Convert(this, e => | |
4350 { | |
4351 switch (e.ElementType) | |
4352 { | |
4353 case QueryElementType.SqlParameter : | |
4354 { | |
4355 var p = (SqlParameter)e; | |
4356 | |
4357 if (p.Value == null) | |
4358 return new SqlValue(null); | |
4359 } | |
4360 | |
4361 break; | |
4362 | |
4363 case QueryElementType.ExprExprPredicate : | |
4364 { | |
4365 var ee = (Predicate.ExprExpr)e; | |
4366 | |
4367 if (ee.Operator == Predicate.Operator.Equal || ee.Operator == Predicate.Operator.NotEqual) | |
4368 { | |
4369 object value1; | |
4370 object value2; | |
4371 | |
4372 if (ee.Expr1 is SqlValue) | |
4373 value1 = ((SqlValue)ee.Expr1).Value; | |
4374 else if (ee.Expr1 is SqlParameter) | |
4375 value1 = ((SqlParameter)ee.Expr1).Value; | |
4376 else | |
4377 break; | |
4378 | |
4379 if (ee.Expr2 is SqlValue) | |
4380 value2 = ((SqlValue)ee.Expr2).Value; | |
4381 else if (ee.Expr2 is SqlParameter) | |
4382 value2 = ((SqlParameter)ee.Expr2).Value; | |
4383 else | |
4384 break; | |
4385 | |
4386 var value = Equals(value1, value2); | |
4387 | |
4388 if (ee.Operator == Predicate.Operator.NotEqual) | |
4389 value = !value; | |
4390 | |
4391 return new Predicate.Expr(new SqlValue(value), Sql.Precedence.Comparison); | |
4392 } | |
4393 } | |
4394 | |
4395 break; | |
4396 | |
4397 case QueryElementType.InListPredicate : | |
4398 return ConvertInListPredicate((Predicate.InList)e); | |
4399 } | |
4400 | |
4401 return null; | |
4402 }); | |
4403 | |
4404 if (query != this) | |
4405 { | |
4406 query.Parameters.Clear(); | |
4407 | |
4408 new QueryVisitor().VisitAll(query, expr => | |
4409 { | |
4410 if (expr.ElementType == QueryElementType.SqlParameter) | |
4411 { | |
4412 var p = (SqlParameter)expr; | |
4413 if (p.IsQueryParameter) | |
4414 query.Parameters.Add(p); | |
4415 } | |
4416 }); | |
4417 } | |
4418 | |
4419 return query; | |
4420 } | |
4421 | |
4422 return this; | |
4423 } | |
4424 | |
4425 static Predicate ConvertInListPredicate(Predicate.InList p) | |
4426 { | |
4427 if (p.Values == null || p.Values.Count == 0) | |
4428 return new Predicate.Expr(new SqlValue(p.IsNot)); | |
4429 | |
4430 if (p.Values.Count == 1 && p.Values[0] is SqlParameter) | |
4431 { | |
4432 var pr = (SqlParameter)p.Values[0]; | |
4433 | |
4434 if (pr.Value == null) | |
4435 return new Predicate.Expr(new SqlValue(p.IsNot)); | |
4436 | |
4437 if (pr.Value is IEnumerable) | |
4438 { | |
4439 if (p.Expr1 is ISqlTableSource) | |
4440 { | |
4441 var items = (IEnumerable)pr.Value; | |
4442 var table = (ISqlTableSource)p.Expr1; | |
4443 var keys = table.GetKeys(true); | |
4444 | |
4445 if (keys == null || keys.Count == 0) | |
4446 throw new SqlException("Cant create IN expression."); | |
4447 | |
4448 if (keys.Count == 1) | |
4449 { | |
4450 var values = new List<ISqlExpression>(); | |
4451 var field = GetUnderlayingField(keys[0]); | |
4452 | |
4453 foreach (var item in items) | |
4454 { | |
4455 var value = field.MemberMapper.GetValue(item); | |
4456 values.Add(new SqlValue(value)); | |
4457 } | |
4458 | |
4459 if (values.Count == 0) | |
4460 return new Predicate.Expr(new SqlValue(p.IsNot)); | |
4461 | |
4462 return new Predicate.InList(keys[0], p.IsNot, values); | |
4463 } | |
4464 | |
4465 { | |
4466 var sc = new SearchCondition(); | |
4467 | |
4468 foreach (var item in items) | |
4469 { | |
4470 var itemCond = new SearchCondition(); | |
4471 | |
4472 foreach (var key in keys) | |
4473 { | |
4474 var field = GetUnderlayingField(key); | |
4475 var value = field.MemberMapper.GetValue(item); | |
4476 var cond = value == null ? | |
4477 new Condition(false, new Predicate.IsNull (field, false)) : | |
4478 new Condition(false, new Predicate.ExprExpr(field, Predicate.Operator.Equal, new SqlValue(value))); | |
4479 | |
4480 itemCond.Conditions.Add(cond); | |
4481 } | |
4482 | |
4483 sc.Conditions.Add(new Condition(false, new Predicate.Expr(itemCond), true)); | |
4484 } | |
4485 | |
4486 if (sc.Conditions.Count == 0) | |
4487 return new Predicate.Expr(new SqlValue(p.IsNot)); | |
4488 | |
4489 if (p.IsNot) | |
4490 return new Predicate.NotExpr(sc, true, Sql.Precedence.LogicalNegation); | |
4491 | |
4492 return new Predicate.Expr(sc, Sql.Precedence.LogicalDisjunction); | |
4493 } | |
4494 } | |
4495 | |
4496 if (p.Expr1 is SqlExpression) | |
4497 { | |
4498 var expr = (SqlExpression)p.Expr1; | |
4499 | |
4500 if (expr.Expr.Length > 1 && expr.Expr[0] == '\x1') | |
4501 { | |
4502 var type = TypeHelper.GetListItemType(pr.Value); | |
4503 var ta = TypeAccessor.GetAccessor(type); | |
4504 var items = (IEnumerable)pr.Value; | |
4505 var names = expr.Expr.Substring(1).Split(','); | |
4506 | |
4507 if (expr.Parameters.Length == 1) | |
4508 { | |
4509 var values = new List<ISqlExpression>(); | |
4510 | |
4511 foreach (var item in items) | |
4512 { | |
4513 var value = ta[names[0]].GetValue(item); | |
4514 values.Add(new SqlValue(value)); | |
4515 } | |
4516 | |
4517 if (values.Count == 0) | |
4518 return new Predicate.Expr(new SqlValue(p.IsNot)); | |
4519 | |
4520 return new Predicate.InList(expr.Parameters[0], p.IsNot, values); | |
4521 } | |
4522 | |
4523 { | |
4524 var sc = new SearchCondition(); | |
4525 | |
4526 foreach (var item in items) | |
4527 { | |
4528 var itemCond = new SearchCondition(); | |
4529 | |
4530 for (var i = 0; i < expr.Parameters.Length; i++) | |
4531 { | |
4532 var sql = expr.Parameters[i]; | |
4533 var value = ta[names[i]].GetValue(item); | |
4534 var cond = value == null ? | |
4535 new Condition(false, new Predicate.IsNull (sql, false)) : | |
4536 new Condition(false, new Predicate.ExprExpr(sql, Predicate.Operator.Equal, new SqlValue(value))); | |
4537 | |
4538 itemCond.Conditions.Add(cond); | |
4539 } | |
4540 | |
4541 sc.Conditions.Add(new Condition(false, new Predicate.Expr(itemCond), true)); | |
4542 } | |
4543 | |
4544 if (sc.Conditions.Count == 0) | |
4545 return new Predicate.Expr(new SqlValue(p.IsNot)); | |
4546 | |
4547 if (p.IsNot) | |
4548 return new Predicate.NotExpr(sc, true, Sql.Precedence.LogicalNegation); | |
4549 | |
4550 return new Predicate.Expr(sc, Sql.Precedence.LogicalDisjunction); | |
4551 } | |
4552 } | |
4553 } | |
4554 | |
4555 /* | |
4556 var itemType = items.GetType().GetItemType(); | |
4557 | |
4558 if (itemType == typeof(DateTime) || itemType == typeof(DateTimeOffset) || | |
4559 itemType == typeof(DateTime?) || itemType == typeof(DateTimeOffset?)) | |
4560 { | |
4561 var list = new List<SqlParameter>(); | |
4562 | |
4563 foreach (var item in items) | |
4564 list.Add(new SqlParameter(itemType, "p", item, (MappingSchema)null)); | |
4565 | |
4566 return new Predicate.InList(p.Expr1, p.IsNot, list); | |
4567 } | |
4568 */ | |
4569 } | |
4570 } | |
4571 | |
4572 return null; | |
4573 } | |
4574 | |
4575 static SqlField GetUnderlayingField(ISqlExpression expr) | |
4576 { | |
4577 switch (expr.ElementType) | |
4578 { | |
4579 case QueryElementType.SqlField: return (SqlField)expr; | |
4580 case QueryElementType.Column : return GetUnderlayingField(((Column)expr).Expression); | |
4581 } | |
4582 | |
4583 throw new InvalidOperationException(); | |
4584 } | |
4585 | |
4586 #endregion | |
4587 | |
4588 #region Clone | |
4589 | |
4590 SqlQuery(SqlQuery clone, Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) | |
4591 { | |
4592 objectTree.Add(clone, this); | |
4593 objectTree.Add(clone.All, All); | |
4594 | |
4595 SourceID = Interlocked.Increment(ref SourceIDCounter); | |
4596 | |
4597 _queryType = clone._queryType; | |
4598 | |
4599 if (IsInsert) _insert = (InsertClause)clone._insert.Clone(objectTree, doClone); | |
4600 if (IsUpdate) _update = (UpdateClause)clone._update.Clone(objectTree, doClone); | |
4601 if (IsDelete) _delete = (DeleteClause)clone._delete.Clone(objectTree, doClone); | |
4602 | |
4603 _select = new SelectClause (this, clone._select, objectTree, doClone); | |
4604 _from = new FromClause (this, clone._from, objectTree, doClone); | |
4605 _where = new WhereClause (this, clone._where, objectTree, doClone); | |
4606 _groupBy = new GroupByClause(this, clone._groupBy, objectTree, doClone); | |
4607 _having = new WhereClause (this, clone._having, objectTree, doClone); | |
4608 _orderBy = new OrderByClause(this, clone._orderBy, objectTree, doClone); | |
4609 | |
4610 _parameters.AddRange(clone._parameters.ConvertAll(p => (SqlParameter)p.Clone(objectTree, doClone))); | |
4611 IsParameterDependent = clone.IsParameterDependent; | |
4612 | |
4613 new QueryVisitor().Visit(this, expr => | |
4614 { | |
4615 var sb = expr as SqlQuery; | |
4616 | |
4617 if (sb != null && sb.ParentSql == clone) | |
4618 sb.ParentSql = this; | |
4619 }); | |
4620 } | |
4621 | |
4622 public SqlQuery Clone() | |
4623 { | |
4624 return (SqlQuery)Clone(new Dictionary<ICloneableElement,ICloneableElement>(), _ => true); | |
4625 } | |
4626 | |
4627 public SqlQuery Clone(Predicate<ICloneableElement> doClone) | |
4628 { | |
4629 return (SqlQuery)Clone(new Dictionary<ICloneableElement,ICloneableElement>(), doClone); | |
4630 } | |
4631 | |
4632 #endregion | |
4633 | |
4634 #region Helpers | |
4635 | |
4636 public TableSource GetTableSource(ISqlTableSource table) | |
4637 { | |
4638 var ts = From[table]; | |
4639 return ts == null && ParentSql != null? ParentSql.GetTableSource(table) : ts; | |
4640 } | |
4641 | |
4642 static TableSource CheckTableSource(TableSource ts, ISqlTableSource table, string alias) | |
4643 { | |
4644 if (ts.Source == table && (alias == null || ts.Alias == alias)) | |
4645 return ts; | |
4646 | |
4647 var jt = ts[table, alias]; | |
4648 | |
4649 if (jt != null) | |
4650 return jt; | |
4651 | |
4652 if (ts.Source is SqlQuery) | |
4653 { | |
4654 var s = ((SqlQuery)ts.Source).From[table, alias]; | |
4655 | |
4656 if (s != null) | |
4657 return s; | |
4658 } | |
4659 | |
4660 return null; | |
4661 } | |
4662 | |
4663 #endregion | |
4664 | |
4665 #region Overrides | |
4666 | |
4667 public string SqlText { get { return ToString(); } } | |
4668 | |
4669 #if OVERRIDETOSTRING | |
4670 | |
4671 public override string ToString() | |
4672 { | |
4673 return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); | |
4674 } | |
4675 | |
4676 #endif | |
4677 | |
4678 #endregion | |
4679 | |
4680 #region ISqlExpression Members | |
4681 | |
4682 public bool CanBeNull() | |
4683 { | |
4684 return true; | |
4685 } | |
4686 | |
4687 public bool Equals(ISqlExpression other, Func<ISqlExpression, ISqlExpression, bool> comparer) | |
4688 { | |
4689 return this == other; | |
4690 } | |
4691 | |
4692 public int Precedence | |
4693 { | |
4694 get { return Sql.Precedence.Unknown; } | |
4695 } | |
4696 | |
4697 public Type SystemType | |
4698 { | |
4699 get | |
4700 { | |
4701 if (Select.Columns.Count == 1) | |
4702 return Select.Columns[0].SystemType; | |
4703 | |
4704 if (From.Tables.Count == 1 && From.Tables[0].Joins.Count == 0) | |
4705 return From.Tables[0].SystemType; | |
4706 | |
4707 return null; | |
4708 } | |
4709 } | |
4710 | |
4711 #endregion | |
4712 | |
4713 #region ICloneableElement Members | |
4714 | |
4715 public ICloneableElement Clone(Dictionary<ICloneableElement, ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) | |
4716 { | |
4717 if (!doClone(this)) | |
4718 return this; | |
4719 | |
4720 ICloneableElement clone; | |
4721 | |
4722 if (!objectTree.TryGetValue(this, out clone)) | |
4723 clone = new SqlQuery(this, objectTree, doClone); | |
4724 | |
4725 return clone; | |
4726 } | |
4727 | |
4728 #endregion | |
4729 | |
4730 #region ISqlExpressionWalkable Members | |
4731 | |
4732 [Obsolete] | |
4733 ISqlExpression ISqlExpressionWalkable.Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) | |
4734 { | |
4735 if (_insert != null) ((ISqlExpressionWalkable)_insert).Walk(skipColumns, func); | |
4736 if (_update != null) ((ISqlExpressionWalkable)_update).Walk(skipColumns, func); | |
4737 if (_delete != null) ((ISqlExpressionWalkable)_delete).Walk(skipColumns, func); | |
4738 | |
4739 ((ISqlExpressionWalkable)Select) .Walk(skipColumns, func); | |
4740 ((ISqlExpressionWalkable)From) .Walk(skipColumns, func); | |
4741 ((ISqlExpressionWalkable)Where) .Walk(skipColumns, func); | |
4742 ((ISqlExpressionWalkable)GroupBy).Walk(skipColumns, func); | |
4743 ((ISqlExpressionWalkable)Having) .Walk(skipColumns, func); | |
4744 ((ISqlExpressionWalkable)OrderBy).Walk(skipColumns, func); | |
4745 | |
4746 if (HasUnion) | |
4747 foreach (var union in Unions) | |
4748 ((ISqlExpressionWalkable)union.SqlQuery).Walk(skipColumns, func); | |
4749 | |
4750 return func(this); | |
4751 } | |
4752 | |
4753 #endregion | |
4754 | |
4755 #region IEquatable<ISqlExpression> Members | |
4756 | |
4757 bool IEquatable<ISqlExpression>.Equals(ISqlExpression other) | |
4758 { | |
4759 return this == other; | |
4760 } | |
4761 | |
4762 #endregion | |
4763 | |
4764 #region ISqlTableSource Members | |
4765 | |
4766 public static int SourceIDCounter; | |
4767 | |
4768 public int SourceID { get; private set; } | |
4769 public SqlTableType SqlTableType { get { return SqlTableType.Table; } } | |
4770 | |
4771 private SqlField _all; | |
4772 public SqlField All | |
4773 { | |
4774 get | |
4775 { | |
4776 if (_all == null) | |
4777 { | |
4778 _all = new SqlField(null, "*", "*", true, -1, null, null); | |
4779 ((IChild<ISqlTableSource>)_all).Parent = this; | |
4780 } | |
4781 | |
4782 return _all; | |
4783 } | |
4784 | |
4785 internal set | |
4786 { | |
4787 _all = value; | |
4788 | |
4789 if (_all != null) | |
4790 ((IChild<ISqlTableSource>)_all).Parent = this; | |
4791 } | |
4792 } | |
4793 | |
4794 List<ISqlExpression> _keys; | |
4795 | |
4796 public IList<ISqlExpression> GetKeys(bool allIfEmpty) | |
4797 { | |
4798 if (_keys == null && From.Tables.Count == 1 && From.Tables[0].Joins.Count == 0) | |
4799 { | |
4800 _keys = new List<ISqlExpression>(); | |
4801 | |
4802 var q = | |
4803 from key in ((ISqlTableSource)From.Tables[0]).GetKeys(allIfEmpty) | |
4804 from col in Select.Columns | |
4805 where col.Expression == key | |
4806 select col as ISqlExpression; | |
4807 | |
4808 _keys = q.ToList(); | |
4809 } | |
4810 | |
4811 return _keys; | |
4812 } | |
4813 | |
4814 #endregion | |
4815 | |
4816 #region IQueryElement Members | |
4817 | |
4818 public QueryElementType ElementType { get { return QueryElementType.SqlQuery; } } | |
4819 | |
4820 StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) | |
4821 { | |
4822 if (dic.ContainsKey(this)) | |
4823 return sb.Append("..."); | |
4824 | |
4825 dic.Add(this, this); | |
4826 | |
4827 sb | |
4828 .Append("(") | |
4829 .Append(SourceID) | |
4830 .Append(") "); | |
4831 | |
4832 ((IQueryElement)Select). ToString(sb, dic); | |
4833 ((IQueryElement)From). ToString(sb, dic); | |
4834 ((IQueryElement)Where). ToString(sb, dic); | |
4835 ((IQueryElement)GroupBy).ToString(sb, dic); | |
4836 ((IQueryElement)Having). ToString(sb, dic); | |
4837 ((IQueryElement)OrderBy).ToString(sb, dic); | |
4838 | |
4839 if (HasUnion) | |
4840 foreach (IQueryElement u in Unions) | |
4841 u.ToString(sb, dic); | |
4842 | |
4843 dic.Remove(this); | |
4844 | |
4845 return sb; | |
4846 } | |
4847 | |
4848 #endregion | |
4849 } | |
4850 } |