Mercurial > pub > bltoolkit
view Source/Data/Sql/SqlQuery.cs @ 4:f757da6161a1
!bug 100 + 2h fixed gregression
author | cin |
---|---|
date | Sun, 24 Aug 2014 17:57:42 +0400 |
parents | f990fcb411a9 |
children |
line wrap: on
line source
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Threading; using JetBrains.Annotations; namespace BLToolkit.Data.Sql { using Reflection; using FJoin = SqlQuery.FromClause.Join; [DebuggerDisplay("SQL = {SqlText}")] public class SqlQuery : ISqlTableSource { #region Init static readonly Dictionary<string,object> _reservedWords = new Dictionary<string,object>(); static SqlQuery() { using (var stream = typeof(SqlQuery).Assembly.GetManifestResourceStream(typeof(SqlQuery), "ReservedWords.txt")) using (var reader = new StreamReader(stream)) { /* var words = reader.ReadToEnd().Replace(' ', '\n').Replace('\t', '\n').Split('\n'); var q = from w in words where w.Length > 0 orderby w select w; var text = string.Join("\n", q.Distinct().ToArray()); */ string s; while ((s = reader.ReadLine()) != null) _reservedWords.Add(s, s); } } public SqlQuery() { SourceID = Interlocked.Increment(ref SourceIDCounter); _select = new SelectClause (this); _from = new FromClause (this); _where = new WhereClause (this); _groupBy = new GroupByClause(this); _having = new WhereClause (this); _orderBy = new OrderByClause(this); } internal SqlQuery(int id) { SourceID = id; } internal void Init( InsertClause insert, UpdateClause update, DeleteClause delete, SelectClause select, FromClause from, WhereClause where, GroupByClause groupBy, WhereClause having, OrderByClause orderBy, List<Union> unions, SqlQuery parentSql, bool parameterDependent, List<SqlParameter> parameters) { _insert = insert; _update = update; _delete = delete; _select = select; _from = from; _where = where; _groupBy = groupBy; _having = having; _orderBy = orderBy; _unions = unions; ParentSql = parentSql; IsParameterDependent = parameterDependent; _parameters.AddRange(parameters); foreach (var col in select.Columns) col.Parent = this; _select. SetSqlQuery(this); _from. SetSqlQuery(this); _where. SetSqlQuery(this); _groupBy.SetSqlQuery(this); _having. SetSqlQuery(this); _orderBy.SetSqlQuery(this); } readonly List<SqlParameter> _parameters = new List<SqlParameter>(); public List<SqlParameter> Parameters { get { return _parameters; } } private List<object> _properties; public List<object> Properties { get { return _properties ?? (_properties = new List<object>()); } } public bool IsParameterDependent { get; set; } public SqlQuery ParentSql { get; set; } public bool IsSimple { get { return !Select.HasModifier && Where.IsEmpty && GroupBy.IsEmpty && Having.IsEmpty && OrderBy.IsEmpty; } } private QueryType _queryType = QueryType.Select; public QueryType QueryType { get { return _queryType; } set { _queryType = value; } } public bool IsSelect { get { return _queryType == QueryType.Select; } } public bool IsDelete { get { return _queryType == QueryType.Delete; } } public bool IsInsertOrUpdate { get { return _queryType == QueryType.InsertOrUpdate; } } public bool IsInsert { get { return _queryType == QueryType.Insert || _queryType == QueryType.InsertOrUpdate; } } public bool IsUpdate { get { return _queryType == QueryType.Update || _queryType == QueryType.InsertOrUpdate; } } #endregion #region Column public class Column : IEquatable<Column>, ISqlExpression, IChild<SqlQuery> { public Column(SqlQuery parent, ISqlExpression expression, string alias) { if (expression == null) throw new ArgumentNullException("expression"); Parent = parent; Expression = expression; _alias = alias; #if DEBUG _columnNumber = ++_columnCounter; #endif } public Column(SqlQuery builder, ISqlExpression expression) : this(builder, expression, null) { } #if DEBUG readonly int _columnNumber; static int _columnCounter; #endif public ISqlExpression Expression { get; set; } internal string _alias; public string Alias { get { if (_alias == null) { if (Expression is SqlField) { var field = (SqlField)Expression; return field.Alias ?? field.PhysicalName; } if (Expression is Column) { var col = (Column)Expression; return col.Alias; } } return _alias; } set { _alias = value; } } public bool Equals(Column other) { return Expression.Equals(other.Expression); } #if OVERRIDETOSTRING public override string ToString() { return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); } #endif #region ISqlExpression Members public bool CanBeNull() { return Expression.CanBeNull(); } public bool Equals(ISqlExpression other, Func<ISqlExpression,ISqlExpression,bool> comparer) { if (this == other) return true; return other is Column && Expression.Equals(((Column)other).Expression, comparer) && comparer(this, other); } public int Precedence { get { return Sql.Precedence.Primary; } } public Type SystemType { get { return Expression.SystemType; } } public ICloneableElement Clone(Dictionary<ICloneableElement, ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) { if (!doClone(this)) return this; ICloneableElement clone; var parent = (SqlQuery)Parent.Clone(objectTree, doClone); if (!objectTree.TryGetValue(this, out clone)) objectTree.Add(this, clone = new Column( parent, (ISqlExpression)Expression.Clone(objectTree, doClone), _alias)); return clone; } #endregion #region IEquatable<ISqlExpression> Members bool IEquatable<ISqlExpression>.Equals(ISqlExpression other) { if (this == other) return true; return other is Column && Equals((Column)other); } #endregion #region ISqlExpressionWalkable Members [Obsolete] public ISqlExpression Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) { if (!(skipColumns && Expression is Column)) Expression = Expression.Walk(skipColumns, func); return func(this); } #endregion #region IChild<ISqlTableSource> Members string IChild<SqlQuery>.Name { get { return Alias; } } public SqlQuery Parent { get; set; } #endregion #region IQueryElement Members public QueryElementType ElementType { get { return QueryElementType.Column; } } StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) { if (dic.ContainsKey(this)) return sb.Append("..."); dic.Add(this, this); sb .Append('t') .Append(Parent.SourceID) .Append("."); #if DEBUG sb.Append('[').Append(_columnNumber).Append(']'); #endif if (Expression is SqlQuery) { sb .Append("(\n\t\t"); var len = sb.Length; Expression.ToString(sb, dic).Replace("\n", "\n\t\t", len, sb.Length - len); sb.Append("\n\t)"); } /*else if (Expression is Column) { var col = (Column)Expression; sb .Append("t") .Append(col.Parent.SourceID) .Append(".") .Append(col.Alias ?? "c" + (col.Parent.Select.Columns.IndexOf(col) + 1)); }*/ else { Expression.ToString(sb, dic); } dic.Remove(this); return sb; } #endregion } #endregion #region TableSource public class TableSource : ISqlTableSource { public TableSource(ISqlTableSource source, string alias) : this(source, alias, null) { } public TableSource(ISqlTableSource source, string alias, params JoinedTable[] joins) { if (source == null) throw new ArgumentNullException("source"); Source = source; _alias = alias; if (joins != null) _joins.AddRange(joins); } public TableSource(ISqlTableSource source, string alias, IEnumerable<JoinedTable> joins) { if (source == null) throw new ArgumentNullException("source"); Source = source; _alias = alias; if (joins != null) _joins.AddRange(joins); } public ISqlTableSource Source { get; set; } public SqlTableType SqlTableType { get { return Source.SqlTableType; } } internal string _alias; public string Alias { get { if (string.IsNullOrEmpty(_alias)) { if (Source is TableSource) return (Source as TableSource).Alias; if (Source is SqlTable) return ((SqlTable)Source).Alias; } return _alias; } set { _alias = value; } } public TableSource this[ISqlTableSource table] { get { return this[table, null]; } } public TableSource this[ISqlTableSource table, string alias] { get { foreach (var tj in Joins) { var t = CheckTableSource(tj.Table, table, alias); if (t != null) return t; } return null; } } readonly List<JoinedTable> _joins = new List<JoinedTable>(); public List<JoinedTable> Joins { get { return _joins; } } public void ForEach(Action<TableSource> action, HashSet<SqlQuery> visitedQueries) { action(this); foreach (var join in Joins) join.Table.ForEach(action, visitedQueries); if (Source is SqlQuery && visitedQueries.Contains((SqlQuery)Source)) ((SqlQuery)Source).ForEachTable(action, visitedQueries); } public IEnumerable<ISqlTableSource> GetTables() { yield return Source; foreach (var join in Joins) foreach (var table in join.Table.GetTables()) yield return table; } public int GetJoinNumber() { var n = Joins.Count; foreach (var join in Joins) n += join.Table.GetJoinNumber(); return n; } #if OVERRIDETOSTRING public override string ToString() { return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); } #endif #region IEquatable<ISqlExpression> Members bool IEquatable<ISqlExpression>.Equals(ISqlExpression other) { return this == other; } #endregion #region ISqlExpressionWalkable Members [Obsolete] public ISqlExpression Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) { Source = (ISqlTableSource)Source.Walk(skipColumns, func); foreach (var t in Joins) ((ISqlExpressionWalkable)t).Walk(skipColumns, func); return this; } #endregion #region ISqlTableSource Members public int SourceID { get { return Source.SourceID; } } public SqlField All { get { return Source.All; } } IList<ISqlExpression> ISqlTableSource.GetKeys(bool allIfEmpty) { return Source.GetKeys(allIfEmpty); } #endregion #region ICloneableElement Members public ICloneableElement Clone(Dictionary<ICloneableElement, ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) { if (!doClone(this)) return this; ICloneableElement clone; if (!objectTree.TryGetValue(this, out clone)) { var ts = new TableSource((ISqlTableSource)Source.Clone(objectTree, doClone), _alias); objectTree.Add(this, clone = ts); ts._joins.AddRange(_joins.ConvertAll(jt => (JoinedTable)jt.Clone(objectTree, doClone))); } return clone; } #endregion #region IQueryElement Members public QueryElementType ElementType { get { return QueryElementType.TableSource; } } StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) { if (sb.Length > 500) return sb; if (dic.ContainsKey(this)) return sb.Append("..."); dic.Add(this, this); if (Source is SqlQuery) { sb.Append("(\n\t"); var len = sb.Length; Source.ToString(sb, dic).Replace("\n", "\n\t", len, sb.Length - len); sb.Append("\n)"); } else Source.ToString(sb, dic); sb .Append(" as t") .Append(SourceID); foreach (IQueryElement join in Joins) { sb.AppendLine().Append('\t'); var len = sb.Length; join.ToString(sb, dic).Replace("\n", "\n\t", len, sb.Length - len); } dic.Remove(this); return sb; } #endregion #region ISqlExpression Members public bool CanBeNull() { return Source.CanBeNull(); } public bool Equals(ISqlExpression other, Func<ISqlExpression,ISqlExpression,bool> comparer) { return this == other; } public int Precedence { get { return Source.Precedence; } } public Type SystemType { get { return Source.SystemType; } } #endregion } #endregion #region TableJoin public enum JoinType { Auto, Inner, Left, CrossApply, OuterApply } public class JoinedTable : IQueryElement, ISqlExpressionWalkable, ICloneableElement { public JoinedTable(JoinType joinType, TableSource table, bool isWeak, SearchCondition searchCondition) { JoinType = joinType; Table = table; IsWeak = isWeak; Condition = searchCondition; CanConvertApply = true; } public JoinedTable(JoinType joinType, TableSource table, bool isWeak) : this(joinType, table, isWeak, new SearchCondition()) { } public JoinedTable(JoinType joinType, ISqlTableSource table, string alias, bool isWeak) : this(joinType, new TableSource(table, alias), isWeak) { } public JoinType JoinType { get; set; } public TableSource Table { get; set; } public SearchCondition Condition { get; private set; } public bool IsWeak { get; set; } public bool CanConvertApply { get; set; } public ICloneableElement Clone(Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) { if (!doClone(this)) return this; ICloneableElement clone; if (!objectTree.TryGetValue(this, out clone)) objectTree.Add(this, clone = new JoinedTable( JoinType, (TableSource)Table.Clone(objectTree, doClone), IsWeak, (SearchCondition)Condition.Clone(objectTree, doClone))); return clone; } #if OVERRIDETOSTRING public override string ToString() { return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); } #endif #region ISqlExpressionWalkable Members [Obsolete] public ISqlExpression Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> action) { Condition = (SearchCondition)((ISqlExpressionWalkable)Condition).Walk(skipColumns, action); #pragma warning disable 0618 Table.Walk(skipColumns, action); #pragma warning restore 0618 return null; } #endregion #region IQueryElement Members public QueryElementType ElementType { get { return QueryElementType.JoinedTable; } } StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) { if (dic.ContainsKey(this)) return sb.Append("..."); dic.Add(this, this); switch (JoinType) { case JoinType.Inner : sb.Append("INNER JOIN "); break; case JoinType.Left : sb.Append("LEFT JOIN "); break; case JoinType.CrossApply : sb.Append("CROSS APPLY "); break; case JoinType.OuterApply : sb.Append("OUTER APPLY "); break; default : sb.Append("SOME JOIN "); break; } ((IQueryElement)Table).ToString(sb, dic); sb.Append(" ON "); ((IQueryElement)Condition).ToString(sb, dic); dic.Remove(this); return sb; } #endregion } #endregion #region Predicate public abstract class Predicate : ISqlPredicate { public enum Operator { Equal, // = Is the operator used to test the equality between two expressions. NotEqual, // <> != Is the operator used to test the condition of two expressions not being equal to each other. Greater, // > Is the operator used to test the condition of one expression being greater than the other. GreaterOrEqual, // >= Is the operator used to test the condition of one expression being greater than or equal to the other expression. NotGreater, // !> Is the operator used to test the condition of one expression not being greater than the other expression. Less, // < Is the operator used to test the condition of one expression being less than the other. LessOrEqual, // <= Is the operator used to test the condition of one expression being less than or equal to the other expression. NotLess // !< Is the operator used to test the condition of one expression not being less than the other expression. } public class Expr : Predicate { public Expr([NotNull] ISqlExpression exp1, int precedence) : base(precedence) { if (exp1 == null) throw new ArgumentNullException("exp1"); Expr1 = exp1; } public Expr([NotNull] ISqlExpression exp1) : base(exp1.Precedence) { if (exp1 == null) throw new ArgumentNullException("exp1"); Expr1 = exp1; } public ISqlExpression Expr1 { get; set; } [Obsolete] protected override void Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) { Expr1 = Expr1.Walk(skipColumns, func); if (Expr1 == null) throw new InvalidOperationException(); } public override bool CanBeNull() { return Expr1.CanBeNull(); } protected override ICloneableElement Clone(Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) { if (!doClone(this)) return this; ICloneableElement clone; if (!objectTree.TryGetValue(this, out clone)) objectTree.Add(this, clone = new Expr((ISqlExpression)Expr1.Clone(objectTree, doClone), Precedence)); return clone; } public override QueryElementType ElementType { get { return QueryElementType.ExprPredicate; } } protected override void ToString(StringBuilder sb, Dictionary<IQueryElement, IQueryElement> dic) { Expr1.ToString(sb, dic); } } public class NotExpr : Expr { public NotExpr(ISqlExpression exp1, bool isNot, int precedence) : base(exp1, precedence) { IsNot = isNot; } public bool IsNot { get; set; } protected override ICloneableElement Clone(Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) { if (!doClone(this)) return this; ICloneableElement clone; if (!objectTree.TryGetValue(this, out clone)) objectTree.Add(this, clone = new NotExpr((ISqlExpression)Expr1.Clone(objectTree, doClone), IsNot, Precedence)); return clone; } public override QueryElementType ElementType { get { return QueryElementType.NotExprPredicate; } } protected override void ToString(StringBuilder sb, Dictionary<IQueryElement, IQueryElement> dic) { if (IsNot) sb.Append("NOT ("); base.ToString(sb, dic); if (IsNot) sb.Append(")"); } } // { expression { = | <> | != | > | >= | ! > | < | <= | !< } expression // public class ExprExpr : Expr { public ExprExpr(ISqlExpression exp1, Operator op, ISqlExpression exp2) : base(exp1, Sql.Precedence.Comparison) { this.Operator = op; Expr2 = exp2; } public new Operator Operator { get; private set; } public ISqlExpression Expr2 { get; internal set; } [Obsolete] protected override void Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) { #pragma warning disable 0618 base.Walk(skipColumns, func); #pragma warning restore 0618 Expr2 = Expr2.Walk(skipColumns, func); } public override bool CanBeNull() { return base.CanBeNull() || Expr2.CanBeNull(); } protected override ICloneableElement Clone(Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) { if (!doClone(this)) return this; ICloneableElement clone; if (!objectTree.TryGetValue(this, out clone)) objectTree.Add(this, clone = new ExprExpr( (ISqlExpression)Expr1.Clone(objectTree, doClone), this.Operator, (ISqlExpression)Expr2.Clone(objectTree, doClone))); return clone; } public override QueryElementType ElementType { get { return QueryElementType.ExprExprPredicate; } } protected override void ToString(StringBuilder sb, Dictionary<IQueryElement, IQueryElement> dic) { Expr1.ToString(sb, dic); string op; switch (this.Operator) { case Operator.Equal : op = "="; break; case Operator.NotEqual : op = "<>"; break; case Operator.Greater : op = ">"; break; case Operator.GreaterOrEqual: op = ">="; break; case Operator.NotGreater : op = "!>"; break; case Operator.Less : op = "<"; break; case Operator.LessOrEqual : op = "<="; break; case Operator.NotLess : op = "!<"; break; default: throw new InvalidOperationException(); } sb.Append(" ").Append(op).Append(" "); Expr2.ToString(sb, dic); } } // string_expression [ NOT ] LIKE string_expression [ ESCAPE 'escape_character' ] // public class Like : NotExpr { public Like(ISqlExpression exp1, bool isNot, ISqlExpression exp2, ISqlExpression escape) : base(exp1, isNot, Sql.Precedence.Comparison) { Expr2 = exp2; Escape = escape; } public ISqlExpression Expr2 { get; internal set; } public ISqlExpression Escape { get; internal set; } [Obsolete] protected override void Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) { #pragma warning disable 0618 base.Walk(skipColumns, func); #pragma warning restore 0618 Expr2 = Expr2.Walk(skipColumns, func); if (Escape != null) Escape = Escape.Walk(skipColumns, func); } protected override ICloneableElement Clone(Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) { if (!doClone(this)) return this; ICloneableElement clone; if (!objectTree.TryGetValue(this, out clone)) objectTree.Add(this, clone = new Like( (ISqlExpression)Expr1.Clone(objectTree, doClone), IsNot, (ISqlExpression)Expr2.Clone(objectTree, doClone), Escape)); return clone; } public override QueryElementType ElementType { get { return QueryElementType.LikePredicate; } } protected override void ToString(StringBuilder sb, Dictionary<IQueryElement, IQueryElement> dic) { Expr1.ToString(sb, dic); if (IsNot) sb.Append(" NOT"); sb.Append(" LIKE "); Expr2.ToString(sb, dic); if (Escape != null) { sb.Append(" ESCAPE "); Escape.ToString(sb, dic); } } } // expression [ NOT ] BETWEEN expression AND expression // public class Between : NotExpr { public Between(ISqlExpression exp1, bool isNot, ISqlExpression exp2, ISqlExpression exp3) : base(exp1, isNot, Sql.Precedence.Comparison) { Expr2 = exp2; Expr3 = exp3; } public ISqlExpression Expr2 { get; internal set; } public ISqlExpression Expr3 { get; internal set; } [Obsolete] protected override void Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) { #pragma warning disable 0618 base.Walk(skipColumns, func); #pragma warning restore 0618 Expr2 = Expr2.Walk(skipColumns, func); Expr3 = Expr3.Walk(skipColumns, func); } protected override ICloneableElement Clone(Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) { if (!doClone(this)) return this; ICloneableElement clone; if (!objectTree.TryGetValue(this, out clone)) objectTree.Add(this, clone = new Between( (ISqlExpression)Expr1.Clone(objectTree, doClone), IsNot, (ISqlExpression)Expr2.Clone(objectTree, doClone), (ISqlExpression)Expr3.Clone(objectTree, doClone))); return clone; } public override QueryElementType ElementType { get { return QueryElementType.BetweenPredicate; } } protected override void ToString(StringBuilder sb, Dictionary<IQueryElement, IQueryElement> dic) { Expr1.ToString(sb, dic); if (IsNot) sb.Append(" NOT"); sb.Append(" BETWEEN "); Expr2.ToString(sb, dic); sb.Append(" AND "); Expr3.ToString(sb, dic); } } // expression IS [ NOT ] NULL // public class IsNull : NotExpr { public IsNull(ISqlExpression exp1, bool isNot) : base(exp1, isNot, Sql.Precedence.Comparison) { } protected override ICloneableElement Clone(Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) { if (!doClone(this)) return this; ICloneableElement clone; if (!objectTree.TryGetValue(this, out clone)) objectTree.Add(this, clone = new IsNull((ISqlExpression)Expr1.Clone(objectTree, doClone), IsNot)); return clone; } protected override void ToString(StringBuilder sb, Dictionary<IQueryElement, IQueryElement> dic) { Expr1.ToString(sb, dic); sb .Append(" IS ") .Append(IsNot ? "NOT " : "") .Append("NULL"); } public override QueryElementType ElementType { get { return QueryElementType.IsNullPredicate; } } } // expression [ NOT ] IN ( subquery | expression [ ,...n ] ) // public class InSubQuery : NotExpr { public InSubQuery(ISqlExpression exp1, bool isNot, SqlQuery subQuery) : base(exp1, isNot, Sql.Precedence.Comparison) { SubQuery = subQuery; } public SqlQuery SubQuery { get; private set; } [Obsolete] protected override void Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) { #pragma warning disable 0618 base.Walk(skipColumns, func); #pragma warning restore 0618 SubQuery = (SqlQuery)((ISqlExpression)SubQuery).Walk(skipColumns, func); } protected override ICloneableElement Clone(Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) { if (!doClone(this)) return this; ICloneableElement clone; if (!objectTree.TryGetValue(this, out clone)) objectTree.Add(this, clone = new InSubQuery( (ISqlExpression)Expr1.Clone(objectTree, doClone), IsNot, (SqlQuery)SubQuery.Clone(objectTree, doClone))); return clone; } public override QueryElementType ElementType { get { return QueryElementType.InSubQueryPredicate; } } protected override void ToString(StringBuilder sb, Dictionary<IQueryElement, IQueryElement> dic) { Expr1.ToString(sb, dic); if (IsNot) sb.Append(" NOT"); sb.Append(" IN ("); ((IQueryElement)SubQuery).ToString(sb, dic); sb.Append(")"); } } public class InList : NotExpr { public InList(ISqlExpression exp1, bool isNot, params ISqlExpression[] values) : base(exp1, isNot, Sql.Precedence.Comparison) { if (values != null && values.Length > 0) _values.AddRange(values); } public InList(ISqlExpression exp1, bool isNot, IEnumerable<ISqlExpression> values) : base(exp1, isNot, Sql.Precedence.Comparison) { if (values != null) _values.AddRange(values); } readonly List<ISqlExpression> _values = new List<ISqlExpression>(); public List<ISqlExpression> Values { get { return _values; } } [Obsolete] protected override void Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> action) { #pragma warning disable 0618 base.Walk(skipColumns, action); #pragma warning restore 0618 for (var i = 0; i < _values.Count; i++) _values[i] = _values[i].Walk(skipColumns, action); } protected override ICloneableElement Clone(Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) { if (!doClone(this)) return this; ICloneableElement clone; if (!objectTree.TryGetValue(this, out clone)) { objectTree.Add(this, clone = new InList( (ISqlExpression)Expr1.Clone(objectTree, doClone), IsNot, _values.ConvertAll(e => (ISqlExpression)e.Clone(objectTree, doClone)).ToArray())); } return clone; } public override QueryElementType ElementType { get { return QueryElementType.InListPredicate; } } protected override void ToString(StringBuilder sb, Dictionary<IQueryElement, IQueryElement> dic) { Expr1.ToString(sb, dic); if (IsNot) sb.Append(" NOT"); sb.Append(" IN ("); foreach (var value in Values) { value.ToString(sb, dic); sb.Append(','); } if (Values.Count > 0) sb.Length--; sb.Append(")"); } } // CONTAINS ( { column | * } , '< contains_search_condition >' ) // FREETEXT ( { column | * } , 'freetext_string' ) // expression { = | <> | != | > | >= | !> | < | <= | !< } { ALL | SOME | ANY } ( subquery ) // EXISTS ( subquery ) public class FuncLike : Predicate { public FuncLike(SqlFunction func) : base(func.Precedence) { Function = func; } public SqlFunction Function { get; private set; } [Obsolete] protected override void Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) { Function = (SqlFunction)((ISqlExpression)Function).Walk(skipColumns, func); } public override bool CanBeNull() { return Function.CanBeNull(); } protected override ICloneableElement Clone(Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) { if (!doClone(this)) return this; ICloneableElement clone; if (!objectTree.TryGetValue(this, out clone)) objectTree.Add(this, clone = new FuncLike((SqlFunction)Function.Clone(objectTree, doClone))); return clone; } public override QueryElementType ElementType { get { return QueryElementType.FuncLikePredicate; } } protected override void ToString(StringBuilder sb, Dictionary<IQueryElement, IQueryElement> dic) { ((IQueryElement)Function).ToString(sb, dic); } } #region Overrides #if OVERRIDETOSTRING public override string ToString() { return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); } #endif #endregion protected Predicate(int precedence) { Precedence = precedence; } #region IPredicate Members public int Precedence { get; private set; } public abstract bool CanBeNull(); protected abstract ICloneableElement Clone (Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone); [Obsolete] protected abstract void Walk (bool skipColumns, Func<ISqlExpression,ISqlExpression> action); [Obsolete] ISqlExpression ISqlExpressionWalkable.Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) { Walk(skipColumns, func); return null; } ICloneableElement ICloneableElement.Clone(Dictionary<ICloneableElement, ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) { if (!doClone(this)) return this; return Clone(objectTree, doClone); } #endregion #region IQueryElement Members public abstract QueryElementType ElementType { get; } protected abstract void ToString(StringBuilder sb, Dictionary<IQueryElement, IQueryElement> dic); StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) { if (dic.ContainsKey(this)) return sb.Append("..."); dic.Add(this, this); ToString(sb, dic); dic.Remove(this); return sb; } #endregion } #endregion #region Condition public class Condition : IQueryElement, ICloneableElement { public Condition(bool isNot, ISqlPredicate predicate) { IsNot = isNot; Predicate = predicate; } public Condition(bool isNot, ISqlPredicate predicate, bool isOr) { IsNot = isNot; Predicate = predicate; IsOr = isOr; } public bool IsNot { get; set; } public ISqlPredicate Predicate { get; set; } public bool IsOr { get; set; } public int Precedence { get { return IsNot ? Sql.Precedence.LogicalNegation : IsOr ? Sql.Precedence.LogicalDisjunction : Sql.Precedence.LogicalConjunction; } } public ICloneableElement Clone(Dictionary<ICloneableElement, ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) { if (!doClone(this)) return this; ICloneableElement clone; if (!objectTree.TryGetValue(this, out clone)) objectTree.Add(this, clone = new Condition(IsNot, (ISqlPredicate)Predicate.Clone(objectTree, doClone), IsOr)); return clone; } public bool CanBeNull() { return Predicate.CanBeNull(); } #if OVERRIDETOSTRING public override string ToString() { return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); } #endif #region IQueryElement Members public QueryElementType ElementType { get { return QueryElementType.Condition; } } StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) { if (dic.ContainsKey(this)) return sb.Append("..."); dic.Add(this, this); sb.Append('('); if (IsNot) sb.Append("NOT "); Predicate.ToString(sb, dic); sb.Append(')').Append(IsOr ? " OR " : " AND "); dic.Remove(this); return sb; } #endregion } #endregion #region SearchCondition public class SearchCondition : ConditionBase<SearchCondition, SearchCondition.Next>, ISqlPredicate, ISqlExpression { public SearchCondition() { } public SearchCondition(IEnumerable<Condition> list) { _conditions.AddRange(list); } public SearchCondition(params Condition[] list) { _conditions.AddRange(list); } public class Next { internal Next(SearchCondition parent) { _parent = parent; } readonly SearchCondition _parent; public SearchCondition Or { get { return _parent.SetOr(true); } } public SearchCondition And { get { return _parent.SetOr(false); } } public ISqlExpression ToExpr() { return _parent; } } readonly List<Condition> _conditions = new List<Condition>(); public List<Condition> Conditions { get { return _conditions; } } protected override SearchCondition Search { get { return this; } } protected override Next GetNext() { return new Next(this); } #region Overrides #if OVERRIDETOSTRING public override string ToString() { return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); } #endif #endregion #region IPredicate Members public int Precedence { get { if (_conditions.Count == 0) return Sql.Precedence.Unknown; if (_conditions.Count == 1) return _conditions[0].Precedence; return _conditions.Select(_ => _.IsNot ? Sql.Precedence.LogicalNegation : _.IsOr ? Sql.Precedence.LogicalDisjunction : Sql.Precedence.LogicalConjunction).Min(); } } public Type SystemType { get { return typeof(bool); } } [Obsolete] ISqlExpression ISqlExpressionWalkable.Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) { foreach (var condition in Conditions) condition.Predicate.Walk(skipColumns, func); return func(this); } #endregion #region IEquatable<ISqlExpression> Members bool IEquatable<ISqlExpression>.Equals(ISqlExpression other) { return this == other; } #endregion #region ISqlExpression Members public bool CanBeNull() { foreach (var c in Conditions) if (c.CanBeNull()) return true; return false; } public bool Equals(ISqlExpression other, Func<ISqlExpression,ISqlExpression,bool> comparer) { return this == other; } #endregion #region ICloneableElement Members public ICloneableElement Clone(Dictionary<ICloneableElement, ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) { if (!doClone(this)) return this; ICloneableElement clone; if (!objectTree.TryGetValue(this, out clone)) { var sc = new SearchCondition(); objectTree.Add(this, clone = sc); sc._conditions.AddRange(_conditions.ConvertAll(c => (Condition)c.Clone(objectTree, doClone))); } return clone; } #endregion #region IQueryElement Members public QueryElementType ElementType { get { return QueryElementType.SearchCondition; } } StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) { if (dic.ContainsKey(this)) return sb.Append("..."); dic.Add(this, this); foreach (IQueryElement c in Conditions) c.ToString(sb, dic); if (Conditions.Count > 0) sb.Length -= 4; dic.Remove(this); return sb; } #endregion } #endregion #region ConditionBase interface IConditionExpr<T> { T Expr (ISqlExpression expr); T Field (SqlField field); T SubQuery(SqlQuery sqlQuery); T Value (object value); } public abstract class ConditionBase<T1,T2> : IConditionExpr<ConditionBase<T1,T2>.Expr_> where T1 : ConditionBase<T1,T2> { public class Expr_ { internal Expr_(ConditionBase<T1,T2> condition, bool isNot, ISqlExpression expr) { _condition = condition; _isNot = isNot; _expr = expr; } readonly ConditionBase<T1,T2> _condition; readonly bool _isNot; readonly ISqlExpression _expr; T2 Add(ISqlPredicate predicate) { _condition.Search.Conditions.Add(new Condition(_isNot, predicate)); return _condition.GetNext(); } #region Predicate.ExprExpr public class Op_ : IConditionExpr<T2> { internal Op_(Expr_ expr, Predicate.Operator op) { _expr = expr; _op = op; } readonly Expr_ _expr; readonly Predicate.Operator _op; public T2 Expr (ISqlExpression expr) { return _expr.Add(new Predicate.ExprExpr(_expr._expr, _op, expr)); } public T2 Field (SqlField field) { return Expr(field); } public T2 SubQuery(SqlQuery subQuery) { return Expr(subQuery); } public T2 Value (object value) { return Expr(new SqlValue(value)); } public T2 All (SqlQuery subQuery) { return Expr(SqlFunction.CreateAll (subQuery)); } public T2 Some (SqlQuery subQuery) { return Expr(SqlFunction.CreateSome(subQuery)); } public T2 Any (SqlQuery subQuery) { return Expr(SqlFunction.CreateAny (subQuery)); } } public Op_ Equal { get { return new Op_(this, Predicate.Operator.Equal); } } public Op_ NotEqual { get { return new Op_(this, Predicate.Operator.NotEqual); } } public Op_ Greater { get { return new Op_(this, Predicate.Operator.Greater); } } public Op_ GreaterOrEqual { get { return new Op_(this, Predicate.Operator.GreaterOrEqual); } } public Op_ NotGreater { get { return new Op_(this, Predicate.Operator.NotGreater); } } public Op_ Less { get { return new Op_(this, Predicate.Operator.Less); } } public Op_ LessOrEqual { get { return new Op_(this, Predicate.Operator.LessOrEqual); } } public Op_ NotLess { get { return new Op_(this, Predicate.Operator.NotLess); } } #endregion #region Predicate.Like public T2 Like(ISqlExpression expression, SqlValue escape) { return Add(new Predicate.Like(_expr, false, expression, escape)); } public T2 Like(ISqlExpression expression) { return Like(expression, null); } public T2 Like(string expression, SqlValue escape) { return Like(new SqlValue(expression), escape); } public T2 Like(string expression) { return Like(new SqlValue(expression), null); } #endregion #region Predicate.Between public T2 Between (ISqlExpression expr1, ISqlExpression expr2) { return Add(new Predicate.Between(_expr, false, expr1, expr2)); } public T2 NotBetween(ISqlExpression expr1, ISqlExpression expr2) { return Add(new Predicate.Between(_expr, true, expr1, expr2)); } #endregion #region Predicate.IsNull public T2 IsNull { get { return Add(new Predicate.IsNull(_expr, false)); } } public T2 IsNotNull { get { return Add(new Predicate.IsNull(_expr, true)); } } #endregion #region Predicate.In public T2 In (SqlQuery subQuery) { return Add(new Predicate.InSubQuery(_expr, false, subQuery)); } public T2 NotIn(SqlQuery subQuery) { return Add(new Predicate.InSubQuery(_expr, true, subQuery)); } Predicate.InList CreateInList(bool isNot, object[] exprs) { var list = new Predicate.InList(_expr, isNot, null); if (exprs != null && exprs.Length > 0) { foreach (var item in exprs) { if (item == null || item is SqlValue && ((SqlValue)item).Value == null) continue; if (item is ISqlExpression) list.Values.Add((ISqlExpression)item); else list.Values.Add(new SqlValue(item)); } } return list; } public T2 In (params object[] exprs) { return Add(CreateInList(false, exprs)); } public T2 NotIn(params object[] exprs) { return Add(CreateInList(true, exprs)); } #endregion } public class Not_ : IConditionExpr<Expr_> { internal Not_(ConditionBase<T1,T2> condition) { _condition = condition; } readonly ConditionBase<T1,T2> _condition; public Expr_ Expr (ISqlExpression expr) { return new Expr_(_condition, true, expr); } public Expr_ Field (SqlField field) { return Expr(field); } public Expr_ SubQuery(SqlQuery subQuery) { return Expr(subQuery); } public Expr_ Value (object value) { return Expr(new SqlValue(value)); } public T2 Exists(SqlQuery subQuery) { _condition.Search.Conditions.Add(new Condition(true, new Predicate.FuncLike(SqlFunction.CreateExists(subQuery)))); return _condition.GetNext(); } } protected abstract SearchCondition Search { get; } protected abstract T2 GetNext(); protected T1 SetOr(bool value) { Search.Conditions[Search.Conditions.Count - 1].IsOr = value; return (T1)this; } public Not_ Not { get { return new Not_(this); } } public Expr_ Expr (ISqlExpression expr) { return new Expr_(this, false, expr); } public Expr_ Field (SqlField field) { return Expr(field); } public Expr_ SubQuery(SqlQuery subQuery) { return Expr(subQuery); } public Expr_ Value (object value) { return Expr(new SqlValue(value)); } public T2 Exists(SqlQuery subQuery) { Search.Conditions.Add(new Condition(false, new Predicate.FuncLike(SqlFunction.CreateExists(subQuery)))); return GetNext(); } } #endregion #region OrderByItem public class OrderByItem : IQueryElement, ICloneableElement { public OrderByItem(ISqlExpression expression, bool isDescending) { Expression = expression; IsDescending = isDescending; } public ISqlExpression Expression { get; internal set; } public bool IsDescending { get; private set; } [Obsolete] internal void Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) { Expression = Expression.Walk(skipColumns, func); } public ICloneableElement Clone(Dictionary<ICloneableElement, ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) { if (!doClone(this)) return this; ICloneableElement clone; if (!objectTree.TryGetValue(this, out clone)) objectTree.Add(this, clone = new OrderByItem((ISqlExpression)Expression.Clone(objectTree, doClone), IsDescending)); return clone; } #region Overrides #if OVERRIDETOSTRING public override string ToString() { return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); } #endif #endregion #region IQueryElement Members public QueryElementType ElementType { get { return QueryElementType.OrderByItem; } } StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) { Expression.ToString(sb, dic); if (IsDescending) sb.Append(" DESC"); return sb; } #endregion } #endregion #region ClauseBase public abstract class ClauseBase { protected ClauseBase(SqlQuery sqlQuery) { SqlQuery = sqlQuery; } public SelectClause Select { get { return SqlQuery.Select; } } public FromClause From { get { return SqlQuery.From; } } public WhereClause Where { get { return SqlQuery.Where; } } public GroupByClause GroupBy { get { return SqlQuery.GroupBy; } } public WhereClause Having { get { return SqlQuery.Having; } } public OrderByClause OrderBy { get { return SqlQuery.OrderBy; } } public SqlQuery End() { return SqlQuery; } protected internal SqlQuery SqlQuery { get; private set; } internal void SetSqlQuery(SqlQuery sqlQuery) { SqlQuery = sqlQuery; } } public abstract class ClauseBase<T1, T2> : ConditionBase<T1, T2> where T1 : ClauseBase<T1, T2> { protected ClauseBase(SqlQuery sqlQuery) { SqlQuery = sqlQuery; } public SelectClause Select { get { return SqlQuery.Select; } } public FromClause From { get { return SqlQuery.From; } } public GroupByClause GroupBy { get { return SqlQuery.GroupBy; } } public WhereClause Having { get { return SqlQuery.Having; } } public OrderByClause OrderBy { get { return SqlQuery.OrderBy; } } public SqlQuery End() { return SqlQuery; } protected internal SqlQuery SqlQuery { get; private set; } internal void SetSqlQuery(SqlQuery sqlQuery) { SqlQuery = sqlQuery; } } #endregion #region SelectClause public class SelectClause : ClauseBase, IQueryElement, ISqlExpressionWalkable { #region Init internal SelectClause(SqlQuery sqlQuery) : base(sqlQuery) { } internal SelectClause( SqlQuery sqlQuery, SelectClause clone, Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) : base(sqlQuery) { _columns.AddRange(clone._columns.ConvertAll(c => (Column)c.Clone(objectTree, doClone))); IsDistinct = clone.IsDistinct; TakeValue = clone.TakeValue == null ? null : (ISqlExpression)clone.TakeValue.Clone(objectTree, doClone); SkipValue = clone.SkipValue == null ? null : (ISqlExpression)clone.SkipValue.Clone(objectTree, doClone); } internal SelectClause(bool isDistinct, ISqlExpression takeValue, ISqlExpression skipValue, IEnumerable<Column> columns) : base(null) { IsDistinct = isDistinct; TakeValue = takeValue; SkipValue = skipValue; _columns.AddRange(columns); } #endregion #region Columns public SelectClause Field(SqlField field) { AddOrGetColumn(new Column(SqlQuery, field)); return this; } public SelectClause Field(SqlField field, string alias) { AddOrGetColumn(new Column(SqlQuery, field, alias)); return this; } public SelectClause SubQuery(SqlQuery subQuery) { if (subQuery.ParentSql != null && subQuery.ParentSql != SqlQuery) throw new ArgumentException("SqlQuery already used as subquery"); subQuery.ParentSql = SqlQuery; AddOrGetColumn(new Column(SqlQuery, subQuery)); return this; } public SelectClause SubQuery(SqlQuery sqlQuery, string alias) { if (sqlQuery.ParentSql != null && sqlQuery.ParentSql != SqlQuery) throw new ArgumentException("SqlQuery already used as subquery"); sqlQuery.ParentSql = SqlQuery; AddOrGetColumn(new Column(SqlQuery, sqlQuery, alias)); return this; } public SelectClause Expr(ISqlExpression expr) { AddOrGetColumn(new Column(SqlQuery, expr)); return this; } public SelectClause Expr(ISqlExpression expr, string alias) { AddOrGetColumn(new Column(SqlQuery, expr, alias)); return this; } public SelectClause Expr(string expr, params ISqlExpression[] values) { AddOrGetColumn(new Column(SqlQuery, new SqlExpression(null, expr, values))); return this; } public SelectClause Expr(Type systemType, string expr, params ISqlExpression[] values) { AddOrGetColumn(new Column(SqlQuery, new SqlExpression(systemType, expr, values))); return this; } public SelectClause Expr(string expr, int priority, params ISqlExpression[] values) { AddOrGetColumn(new Column(SqlQuery, new SqlExpression(null, expr, priority, values))); return this; } public SelectClause Expr(Type systemType, string expr, int priority, params ISqlExpression[] values) { AddOrGetColumn(new Column(SqlQuery, new SqlExpression(systemType, expr, priority, values))); return this; } public SelectClause Expr(string alias, string expr, int priority, params ISqlExpression[] values) { AddOrGetColumn(new Column(SqlQuery, new SqlExpression(null, expr, priority, values))); return this; } public SelectClause Expr(Type systemType, string alias, string expr, int priority, params ISqlExpression[] values) { AddOrGetColumn(new Column(SqlQuery, new SqlExpression(systemType, expr, priority, values))); return this; } public SelectClause Expr<T>(ISqlExpression expr1, string operation, ISqlExpression expr2) { AddOrGetColumn(new Column(SqlQuery, new SqlBinaryExpression(typeof(T), expr1, operation, expr2))); return this; } public SelectClause Expr<T>(ISqlExpression expr1, string operation, ISqlExpression expr2, int priority) { AddOrGetColumn(new Column(SqlQuery, new SqlBinaryExpression(typeof(T), expr1, operation, expr2, priority))); return this; } public SelectClause Expr<T>(string alias, ISqlExpression expr1, string operation, ISqlExpression expr2, int priority) { AddOrGetColumn(new Column(SqlQuery, new SqlBinaryExpression(typeof(T), expr1, operation, expr2, priority), alias)); return this; } public int Add(ISqlExpression expr) { if (expr is Column && ((Column)expr).Parent == SqlQuery) throw new InvalidOperationException(); return Columns.IndexOf(AddOrGetColumn(new Column(SqlQuery, expr))); } public int Add(ISqlExpression expr, string alias) { return Columns.IndexOf(AddOrGetColumn(new Column(SqlQuery, expr, alias))); } Column AddOrGetColumn(Column col) { foreach (var c in Columns) if (c.Equals(col)) return col; #if DEBUG switch (col.Expression.ElementType) { case QueryElementType.SqlField : { var table = ((SqlField)col.Expression).Table; //if (SqlQuery.From.GetFromTables().Any(_ => _ == table)) // throw new InvalidOperationException("Wrong field usage."); break; } case QueryElementType.Column : { var query = ((Column)col.Expression).Parent; //if (!SqlQuery.From.GetFromQueries().Any(_ => _ == query)) // throw new InvalidOperationException("Wrong column usage."); break; } case QueryElementType.SqlQuery : { if (col.Expression == SqlQuery) throw new InvalidOperationException("Wrong query usage."); break; } } #endif Columns.Add(col); return col; } readonly List<Column> _columns = new List<Column>(); public List<Column> Columns { get { return _columns; } } #endregion #region HasModifier public bool HasModifier { get { return IsDistinct || SkipValue != null || TakeValue != null; } } #endregion #region Distinct public SelectClause Distinct { get { IsDistinct = true; return this; } } public bool IsDistinct { get; set; } #endregion #region Take public SelectClause Take(int value) { TakeValue = new SqlValue(value); return this; } public SelectClause Take(ISqlExpression value) { TakeValue = value; return this; } public ISqlExpression TakeValue { get; set; } #endregion #region Skip public SelectClause Skip(int value) { SkipValue = new SqlValue(value); return this; } public SelectClause Skip(ISqlExpression value) { SkipValue = value; return this; } public ISqlExpression SkipValue { get; set; } #endregion #region Overrides #if OVERRIDETOSTRING public override string ToString() { return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); } #endif #endregion #region ISqlExpressionWalkable Members [Obsolete] ISqlExpression ISqlExpressionWalkable.Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) { for (var i = 0; i < Columns.Count; i++) { var col = Columns[i]; #pragma warning disable 0618 var expr = col.Walk(skipColumns, func); #pragma warning restore 0618 if (expr is Column) Columns[i] = (Column)expr; else Columns[i] = new Column(col.Parent, expr, col.Alias); } if (TakeValue != null) TakeValue = TakeValue.Walk(skipColumns, func); if (SkipValue != null) SkipValue = SkipValue.Walk(skipColumns, func); return null; } #endregion #region IQueryElement Members public QueryElementType ElementType { get { return QueryElementType.SelectClause; } } StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) { if (dic.ContainsKey(this)) return sb.Append("..."); dic.Add(this, this); sb.Append("SELECT "); if (IsDistinct) sb.Append("DISTINCT "); if (SkipValue != null) { sb.Append("SKIP "); SkipValue.ToString(sb, dic); sb.Append(" "); } if (TakeValue != null) { sb.Append("TAKE "); TakeValue.ToString(sb, dic); sb.Append(" "); } sb.AppendLine(); if (Columns.Count == 0) sb.Append("\t*, \n"); else foreach (var c in Columns) { sb.Append("\t"); ((IQueryElement)c).ToString(sb, dic); sb .Append(" as ") .Append(c.Alias ?? "c" + (Columns.IndexOf(c) + 1)) .Append(", \n"); } sb.Length -= 3; dic.Remove(this); return sb; } #endregion } private SelectClause _select; public SelectClause Select { get { return _select; } } #endregion #region InsertClause public class SetExpression : IQueryElement, ISqlExpressionWalkable, ICloneableElement { public SetExpression(ISqlExpression column, ISqlExpression expression) { Column = column; Expression = expression; if (expression is SqlParameter) { var p = (SqlParameter)expression; //if (type.IsEnum) // p.SetEnumConverter(type, mappingSchema); if (column is SqlField) { var field = (SqlField)column; if (field.MemberMapper != null) { if (field.MemberMapper.MapMemberInfo.IsDbTypeSet) p.DbType = field.MemberMapper.MapMemberInfo.DbType; if (field.MemberMapper.MapMemberInfo.IsDbSizeSet) p.DbSize = field.MemberMapper.MapMemberInfo.DbSize; } } } } public ISqlExpression Column { get; set; } public ISqlExpression Expression { get; set; } #region Overrides #if OVERRIDETOSTRING public override string ToString() { return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); } #endif #endregion #region ICloneableElement Members public ICloneableElement Clone(Dictionary<ICloneableElement, ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) { if (!doClone(this)) return this; ICloneableElement clone; if (!objectTree.TryGetValue(this, out clone)) { objectTree.Add(this, clone = new SetExpression( (ISqlExpression)Column. Clone(objectTree, doClone), (ISqlExpression)Expression.Clone(objectTree, doClone))); } return clone; } #endregion #region ISqlExpressionWalkable Members [Obsolete] ISqlExpression ISqlExpressionWalkable.Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) { Column = Column. Walk(skipColumns, func); Expression = Expression.Walk(skipColumns, func); return null; } #endregion #region IQueryElement Members public QueryElementType ElementType { get { return QueryElementType.SetExpression; } } StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) { Column.ToString(sb, dic); sb.Append(" = "); Expression.ToString(sb, dic); return sb; } #endregion } public class InsertClause : IQueryElement, ISqlExpressionWalkable, ICloneableElement { public InsertClause() { Items = new List<SetExpression>(); } public List<SetExpression> Items { get; private set; } public SqlTable Into { get; set; } public bool WithIdentity { get; set; } #region Overrides #if OVERRIDETOSTRING public override string ToString() { return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); } #endif #endregion #region ICloneableElement Members public ICloneableElement Clone(Dictionary<ICloneableElement, ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) { if (!doClone(this)) return this; var clone = new InsertClause { WithIdentity = WithIdentity }; if (Into != null) clone.Into = (SqlTable)Into.Clone(objectTree, doClone); foreach (var item in Items) clone.Items.Add((SetExpression)item.Clone(objectTree, doClone)); objectTree.Add(this, clone); return clone; } #endregion #region ISqlExpressionWalkable Members [Obsolete] ISqlExpression ISqlExpressionWalkable.Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) { if (Into != null) ((ISqlExpressionWalkable)Into).Walk(skipColumns, func); foreach (var t in Items) ((ISqlExpressionWalkable)t).Walk(skipColumns, func); return null; } #endregion #region IQueryElement Members public QueryElementType ElementType { get { return QueryElementType.InsertClause; } } StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) { sb.Append("VALUES "); if (Into != null) ((IQueryElement)Into).ToString(sb, dic); sb.AppendLine(); foreach (var e in Items) { sb.Append("\t"); ((IQueryElement)e).ToString(sb, dic); sb.AppendLine(); } return sb; } #endregion } private InsertClause _insert; public InsertClause Insert { get { return _insert ?? (_insert = new InsertClause()); } } public void ClearInsert() { _insert = null; } #endregion #region UpdateClause public class UpdateClause : IQueryElement, ISqlExpressionWalkable, ICloneableElement { public UpdateClause() { Items = new List<SetExpression>(); Keys = new List<SetExpression>(); } public List<SetExpression> Items { get; private set; } public List<SetExpression> Keys { get; private set; } public SqlTable Table { get; set; } #region Overrides #if OVERRIDETOSTRING public override string ToString() { return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); } #endif #endregion #region ICloneableElement Members public ICloneableElement Clone(Dictionary<ICloneableElement, ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) { if (!doClone(this)) return this; var clone = new UpdateClause(); if (Table != null) clone.Table = (SqlTable)Table.Clone(objectTree, doClone); foreach (var item in Items) clone.Items.Add((SetExpression)item.Clone(objectTree, doClone)); foreach (var item in Keys) clone.Keys.Add((SetExpression)item.Clone(objectTree, doClone)); objectTree.Add(this, clone); return clone; } #endregion #region ISqlExpressionWalkable Members [Obsolete] ISqlExpression ISqlExpressionWalkable.Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) { if (Table != null) ((ISqlExpressionWalkable)Table).Walk(skipColumns, func); foreach (var t in Items) ((ISqlExpressionWalkable)t).Walk(skipColumns, func); foreach (var t in Keys) ((ISqlExpressionWalkable)t).Walk(skipColumns, func); return null; } #endregion #region IQueryElement Members public QueryElementType ElementType { get { return QueryElementType.UpdateClause; } } StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) { sb.Append("SET "); if (Table != null) ((IQueryElement)Table).ToString(sb, dic); sb.AppendLine(); foreach (var e in Items) { sb.Append("\t"); ((IQueryElement)e).ToString(sb, dic); sb.AppendLine(); } return sb; } #endregion } private UpdateClause _update; public UpdateClause Update { get { return _update ?? (_update = new UpdateClause()); } } public void ClearUpdate() { _update = null; } #endregion #region DeleteClause public class DeleteClause : IQueryElement, ISqlExpressionWalkable, ICloneableElement { public SqlTable Table { get; set; } #region Overrides #if OVERRIDETOSTRING public override string ToString() { return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); } #endif #endregion #region ICloneableElement Members public ICloneableElement Clone(Dictionary<ICloneableElement, ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) { if (!doClone(this)) return this; var clone = new DeleteClause(); if (Table != null) clone.Table = (SqlTable)Table.Clone(objectTree, doClone); objectTree.Add(this, clone); return clone; } #endregion #region ISqlExpressionWalkable Members [Obsolete] ISqlExpression ISqlExpressionWalkable.Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) { if (Table != null) ((ISqlExpressionWalkable)Table).Walk(skipColumns, func); return null; } #endregion #region IQueryElement Members public QueryElementType ElementType { get { return QueryElementType.DeleteClause; } } StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) { sb.Append("DELETE FROM "); if (Table != null) ((IQueryElement)Table).ToString(sb, dic); sb.AppendLine(); return sb; } #endregion } private DeleteClause _delete; public DeleteClause Delete { get { return _delete ?? (_delete = new DeleteClause()); } } public void ClearDelete() { _delete = null; } #endregion #region FromClause public class FromClause : ClauseBase, IQueryElement, ISqlExpressionWalkable { #region Join public class Join : ConditionBase<Join,Join.Next> { public class Next { internal Next(Join parent) { _parent = parent; } readonly Join _parent; public Join Or { get { return _parent.SetOr(true); } } public Join And { get { return _parent.SetOr(false); } } public static implicit operator Join(Next next) { return next._parent; } } protected override SearchCondition Search { get { return JoinedTable.Condition; } } protected override Next GetNext() { return new Next(this); } internal Join(JoinType joinType, ISqlTableSource table, string alias, bool isWeak, ICollection<Join> joins) { JoinedTable = new JoinedTable(joinType, table, alias, isWeak); if (joins != null && joins.Count > 0) foreach (var join in joins) JoinedTable.Table.Joins.Add(join.JoinedTable); } public JoinedTable JoinedTable { get; private set; } } #endregion internal FromClause(SqlQuery sqlQuery) : base(sqlQuery) { } internal FromClause( SqlQuery sqlQuery, FromClause clone, Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) : base(sqlQuery) { _tables.AddRange(clone._tables.ConvertAll(ts => (TableSource)ts.Clone(objectTree, doClone))); } internal FromClause(IEnumerable<TableSource> tables) : base(null) { _tables.AddRange(tables); } public FromClause Table(ISqlTableSource table, params FJoin[] joins) { return Table(table, null, joins); } public FromClause Table(ISqlTableSource table, string alias, params FJoin[] joins) { var ts = AddOrGetTable(table, alias); if (joins != null && joins.Length > 0) foreach (var join in joins) ts.Joins.Add(join.JoinedTable); return this; } TableSource GetTable(ISqlTableSource table, string alias) { foreach (var ts in Tables) if (ts.Source == table) if (alias == null || ts.Alias == alias) return ts; else throw new ArgumentException("alias"); return null; } TableSource AddOrGetTable(ISqlTableSource table, string alias) { var ts = GetTable(table, alias); if (ts != null) return ts; var t = new TableSource(table, alias); Tables.Add(t); return t; } public TableSource this[ISqlTableSource table] { get { return this[table, null]; } } public TableSource this[ISqlTableSource table, string alias] { get { foreach (var ts in Tables) { var t = CheckTableSource(ts, table, alias); if (t != null) return t; } return null; } } public bool IsChild(ISqlTableSource table) { foreach (var ts in Tables) if (ts.Source == table || CheckChild(ts.Joins, table)) return true; return false; } static bool CheckChild(IEnumerable<JoinedTable> joins, ISqlTableSource table) { foreach (var j in joins) if (j.Table.Source == table || CheckChild(j.Table.Joins, table)) return true; return false; } readonly List<TableSource> _tables = new List<TableSource>(); public List<TableSource> Tables { get { return _tables; } } static IEnumerable<ISqlTableSource> GetJoinTables(TableSource source, QueryElementType elementType) { if (source.Source.ElementType == elementType) yield return source.Source; foreach (var join in source.Joins) foreach (var table in GetJoinTables(join.Table, elementType)) yield return table; } internal IEnumerable<ISqlTableSource> GetFromTables() { return Tables.SelectMany(_ => GetJoinTables(_, QueryElementType.SqlTable)); } internal IEnumerable<ISqlTableSource> GetFromQueries() { return Tables.SelectMany(_ => GetJoinTables(_, QueryElementType.SqlQuery)); } static TableSource FindTableSource(TableSource source, SqlTable table) { if (source.Source == table) return source; foreach (var join in source.Joins) { var ts = FindTableSource(join.Table, table); if (ts != null) return ts; } return null; } public ISqlTableSource FindTableSource(SqlTable table) { foreach (var source in Tables) { var ts = FindTableSource(source, table); if (ts != null) return ts; } return null; } #region Overrides #if OVERRIDETOSTRING public override string ToString() { return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); } #endif #endregion #region ISqlExpressionWalkable Members [Obsolete] ISqlExpression ISqlExpressionWalkable.Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) { for (var i = 0; i < Tables.Count; i++) ((ISqlExpressionWalkable)Tables[i]).Walk(skipColumns, func); return null; } #endregion #region IQueryElement Members public QueryElementType ElementType { get { return QueryElementType.FromClause; } } StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) { sb.Append(" \nFROM \n"); if (Tables.Count > 0) { foreach (IQueryElement ts in Tables) { sb.Append('\t'); var len = sb.Length; ts.ToString(sb, dic).Replace("\n", "\n\t", len, sb.Length - len); sb.Append(", "); } sb.Length -= 2; } return sb; } #endregion } public static FJoin InnerJoin (ISqlTableSource table, params FJoin[] joins) { return new FJoin(JoinType.Inner, table, null, false, joins); } public static FJoin InnerJoin (ISqlTableSource table, string alias, params FJoin[] joins) { return new FJoin(JoinType.Inner, table, alias, false, joins); } public static FJoin LeftJoin (ISqlTableSource table, params FJoin[] joins) { return new FJoin(JoinType.Left, table, null, false, joins); } public static FJoin LeftJoin (ISqlTableSource table, string alias, params FJoin[] joins) { return new FJoin(JoinType.Left, table, alias, false, joins); } public static FJoin Join (ISqlTableSource table, params FJoin[] joins) { return new FJoin(JoinType.Auto, table, null, false, joins); } public static FJoin Join (ISqlTableSource table, string alias, params FJoin[] joins) { return new FJoin(JoinType.Auto, table, alias, false, joins); } public static FJoin CrossApply (ISqlTableSource table, params FJoin[] joins) { return new FJoin(JoinType.CrossApply, table, null, false, joins); } public static FJoin CrossApply (ISqlTableSource table, string alias, params FJoin[] joins) { return new FJoin(JoinType.CrossApply, table, alias, false, joins); } public static FJoin OuterApply (ISqlTableSource table, params FJoin[] joins) { return new FJoin(JoinType.OuterApply, table, null, false, joins); } public static FJoin OuterApply (ISqlTableSource table, string alias, params FJoin[] joins) { return new FJoin(JoinType.OuterApply, table, alias, false, joins); } public static FJoin WeakInnerJoin(ISqlTableSource table, params FJoin[] joins) { return new FJoin(JoinType.Inner, table, null, true, joins); } public static FJoin WeakInnerJoin(ISqlTableSource table, string alias, params FJoin[] joins) { return new FJoin(JoinType.Inner, table, alias, true, joins); } public static FJoin WeakLeftJoin (ISqlTableSource table, params FJoin[] joins) { return new FJoin(JoinType.Left, table, null, true, joins); } public static FJoin WeakLeftJoin (ISqlTableSource table, string alias, params FJoin[] joins) { return new FJoin(JoinType.Left, table, alias, true, joins); } public static FJoin WeakJoin (ISqlTableSource table, params FJoin[] joins) { return new FJoin(JoinType.Auto, table, null, true, joins); } public static FJoin WeakJoin (ISqlTableSource table, string alias, params FJoin[] joins) { return new FJoin(JoinType.Auto, table, alias, true, joins); } private FromClause _from; public FromClause From { get { return _from; } } #endregion #region WhereClause public class WhereClause : ClauseBase<WhereClause,WhereClause.Next>, IQueryElement, ISqlExpressionWalkable { public class Next : ClauseBase { internal Next(WhereClause parent) : base(parent.SqlQuery) { _parent = parent; } readonly WhereClause _parent; public WhereClause Or { get { return _parent.SetOr(true); } } public WhereClause And { get { return _parent.SetOr(false); } } } internal WhereClause(SqlQuery sqlQuery) : base(sqlQuery) { SearchCondition = new SearchCondition(); } internal WhereClause( SqlQuery sqlQuery, WhereClause clone, Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) : base(sqlQuery) { SearchCondition = (SearchCondition)clone.SearchCondition.Clone(objectTree, doClone); } internal WhereClause(SearchCondition searchCondition) : base(null) { SearchCondition = searchCondition; } public SearchCondition SearchCondition { get; private set; } public bool IsEmpty { get { return SearchCondition.Conditions.Count == 0; } } protected override SearchCondition Search { get { return SearchCondition; } } protected override Next GetNext() { return new Next(this); } #if OVERRIDETOSTRING public override string ToString() { return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); } #endif #region ISqlExpressionWalkable Members [Obsolete] ISqlExpression ISqlExpressionWalkable.Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> action) { SearchCondition = (SearchCondition)((ISqlExpressionWalkable)SearchCondition).Walk(skipColumns, action); return null; } #endregion #region IQueryElement Members public QueryElementType ElementType { get { return QueryElementType.WhereClause; } } StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) { if (Search.Conditions.Count == 0) return sb; sb.Append("\nWHERE\n\t"); return ((IQueryElement)Search).ToString(sb, dic); } #endregion } private WhereClause _where; public WhereClause Where { get { return _where; } } #endregion #region GroupByClause public class GroupByClause : ClauseBase, IQueryElement, ISqlExpressionWalkable { internal GroupByClause(SqlQuery sqlQuery) : base(sqlQuery) { } internal GroupByClause( SqlQuery sqlQuery, GroupByClause clone, Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) : base(sqlQuery) { _items.AddRange(clone._items.ConvertAll(e => (ISqlExpression)e.Clone(objectTree, doClone))); } internal GroupByClause(IEnumerable<ISqlExpression> items) : base(null) { _items.AddRange(items); } public GroupByClause Expr(ISqlExpression expr) { Add(expr); return this; } public GroupByClause Field(SqlField field) { return Expr(field); } void Add(ISqlExpression expr) { foreach (var e in Items) if (e.Equals(expr)) return; Items.Add(expr); } readonly List<ISqlExpression> _items = new List<ISqlExpression>(); public List<ISqlExpression> Items { get { return _items; } } public bool IsEmpty { get { return Items.Count == 0; } } #if OVERRIDETOSTRING public override string ToString() { return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); } #endif #region ISqlExpressionWalkable Members [Obsolete] ISqlExpression ISqlExpressionWalkable.Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) { for (var i = 0; i < Items.Count; i++) Items[i] = Items[i].Walk(skipColumns, func); return null; } #endregion #region IQueryElement Members public QueryElementType ElementType { get { return QueryElementType.GroupByClause; } } StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) { if (Items.Count == 0) return sb; sb.Append(" \nGROUP BY \n"); foreach (var item in Items) { sb.Append('\t'); item.ToString(sb, dic); sb.Append(","); } sb.Length--; return sb; } #endregion } private GroupByClause _groupBy; public GroupByClause GroupBy { get { return _groupBy; } } #endregion #region HavingClause private WhereClause _having; public WhereClause Having { get { return _having; } } #endregion #region OrderByClause public class OrderByClause : ClauseBase, IQueryElement, ISqlExpressionWalkable { internal OrderByClause(SqlQuery sqlQuery) : base(sqlQuery) { } internal OrderByClause( SqlQuery sqlQuery, OrderByClause clone, Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) : base(sqlQuery) { _items.AddRange(clone._items.ConvertAll(item => (OrderByItem)item.Clone(objectTree, doClone))); } internal OrderByClause(IEnumerable<OrderByItem> items) : base(null) { _items.AddRange(items); } public OrderByClause Expr(ISqlExpression expr, bool isDescending) { Add(expr, isDescending); return this; } public OrderByClause Expr (ISqlExpression expr) { return Expr(expr, false); } public OrderByClause ExprAsc (ISqlExpression expr) { return Expr(expr, false); } public OrderByClause ExprDesc (ISqlExpression expr) { return Expr(expr, true); } public OrderByClause Field (SqlField field, bool isDescending) { return Expr(field, isDescending); } public OrderByClause Field (SqlField field) { return Expr(field, false); } public OrderByClause FieldAsc (SqlField field) { return Expr(field, false); } public OrderByClause FieldDesc(SqlField field) { return Expr(field, true); } void Add(ISqlExpression expr, bool isDescending) { foreach (var item in Items) if (item.Expression.Equals(expr, (x, y) => { var col = x as Column; return col == null || !col.Parent.HasUnion || x == y; })) return; Items.Add(new OrderByItem(expr, isDescending)); } readonly List<OrderByItem> _items = new List<OrderByItem>(); public List<OrderByItem> Items { get { return _items; } } public bool IsEmpty { get { return Items.Count == 0; } } #if OVERRIDETOSTRING public override string ToString() { return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); } #endif #region ISqlExpressionWalkable Members [Obsolete] ISqlExpression ISqlExpressionWalkable.Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) { #pragma warning disable 0618 foreach (var t in Items) t.Walk(skipColumns, func); #pragma warning restore 0618 return null; } #endregion #region IQueryElement Members public QueryElementType ElementType { get { return QueryElementType.OrderByClause; } } StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) { if (Items.Count == 0) return sb; sb.Append(" \nORDER BY \n"); foreach (IQueryElement item in Items) { sb.Append('\t'); item.ToString(sb, dic); sb.Append(", "); } sb.Length -= 2; return sb; } #endregion } private OrderByClause _orderBy; public OrderByClause OrderBy { get { return _orderBy; } } #endregion #region Union public class Union : IQueryElement { public Union() { } public Union(SqlQuery sqlQuery, bool isAll) { SqlQuery = sqlQuery; IsAll = isAll; } public SqlQuery SqlQuery { get; private set; } public bool IsAll { get; private set; } public QueryElementType ElementType { get { return QueryElementType.Union; } } #if OVERRIDETOSTRING public override string ToString() { return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); } #endif StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) { sb.Append(" \nUNION").Append(IsAll ? " ALL" : "").Append(" \n"); return ((IQueryElement)SqlQuery).ToString(sb, dic); } } private List<Union> _unions; public List<Union> Unions { get { return _unions ?? (_unions = new List<Union>()); } } public bool HasUnion { get { return _unions != null && _unions.Count > 0; } } public void AddUnion(SqlQuery union, bool isAll) { Unions.Add(new Union(union, isAll)); } #endregion #region FinalizeAndValidate public void FinalizeAndValidate(bool isApplySupported, bool optimizeColumns) { #if DEBUG var sqlText = SqlText; var dic = new Dictionary<SqlQuery,SqlQuery>(); new QueryVisitor().VisitAll(this, e => { var sql = e as SqlQuery; if (sql != null) { if (dic.ContainsKey(sql)) throw new InvalidOperationException("SqlQuery circle reference detected."); dic.Add(sql, sql); } }); #endif OptimizeUnions(); FinalizeAndValidateInternal(isApplySupported, optimizeColumns, true, new List<ISqlTableSource>()); ResolveFields(); SetAliases(); #if DEBUG sqlText = SqlText; #endif } class QueryData { public SqlQuery Query; public List<ISqlExpression> Fields = new List<ISqlExpression>(); public List<QueryData> Queries = new List<QueryData>(); } void ResolveFields() { var root = GetQueryData(); ResolveFields(root); } QueryData GetQueryData() { var data = new QueryData { Query = this }; new QueryVisitor().VisitParentFirst(this, e => { switch (e.ElementType) { case QueryElementType.SqlField : { var field = (SqlField)e; if (field.Name.Length != 1 || field.Name[0] != '*') data.Fields.Add(field); break; } case QueryElementType.SqlQuery : { if (e != this) { data.Queries.Add(((SqlQuery)e).GetQueryData()); return false; } break; } case QueryElementType.Column : return ((Column)e).Parent == this; case QueryElementType.SqlTable : return false; } return true; }); return data; } static TableSource FindField(SqlField field, TableSource table) { if (field.Table == table.Source) return table; foreach (var @join in table.Joins) { var t = FindField(field, @join.Table); if (t != null) return @join.Table; } return null; } static ISqlExpression GetColumn(QueryData data, SqlField field) { foreach (var query in data.Queries) { var q = query.Query; foreach (var table in q.From.Tables) { var t = FindField(field, table); if (t != null) { var n = q.Select.Columns.Count; var idx = q.Select.Add(field); if (n != q.Select.Columns.Count) if (!q.GroupBy.IsEmpty || q.Select.Columns.Exists(c => IsAggregationFunction(c.Expression))) q.GroupBy.Items.Add(field); return q.Select.Columns[idx]; } } } return null; } static void ResolveFields(QueryData data) { if (data.Queries.Count == 0) return; var dic = new Dictionary<ISqlExpression,ISqlExpression>(); foreach (SqlField field in data.Fields) { if (dic.ContainsKey(field)) continue; var found = false; foreach (var table in data.Query.From.Tables) { found = FindField(field, table) != null; if (found) break; } if (!found) { var expr = GetColumn(data, field); if (expr != null) dic.Add(field, expr); } } if (dic.Count > 0) new QueryVisitor().VisitParentFirst(data.Query, e => { ISqlExpression ex; switch (e.ElementType) { case QueryElementType.SqlQuery : return e == data.Query; case QueryElementType.SqlFunction : { var parms = ((SqlFunction)e).Parameters; for (var i = 0; i < parms.Length; i++) if (dic.TryGetValue(parms[i], out ex)) parms[i] = ex; break; } case QueryElementType.SqlExpression : { var parms = ((SqlExpression)e).Parameters; for (var i = 0; i < parms.Length; i++) if (dic.TryGetValue(parms[i], out ex)) parms[i] = ex; break; } case QueryElementType.SqlBinaryExpression : { var expr = (SqlBinaryExpression)e; if (dic.TryGetValue(expr.Expr1, out ex)) expr.Expr1 = ex; if (dic.TryGetValue(expr.Expr2, out ex)) expr.Expr2 = ex; break; } case QueryElementType.ExprPredicate : case QueryElementType.NotExprPredicate : case QueryElementType.IsNullPredicate : case QueryElementType.InSubQueryPredicate : { var expr = (Predicate.Expr)e; if (dic.TryGetValue(expr.Expr1, out ex)) expr.Expr1 = ex; break; } case QueryElementType.ExprExprPredicate : { var expr = (Predicate.ExprExpr)e; if (dic.TryGetValue(expr.Expr1, out ex)) expr.Expr1 = ex; if (dic.TryGetValue(expr.Expr2, out ex)) expr.Expr2 = ex; break; } case QueryElementType.LikePredicate : { var expr = (Predicate.Like)e; if (dic.TryGetValue(expr.Expr1, out ex)) expr.Expr1 = ex; if (dic.TryGetValue(expr.Expr2, out ex)) expr.Expr2 = ex; if (dic.TryGetValue(expr.Escape, out ex)) expr.Escape = ex; break; } case QueryElementType.BetweenPredicate : { var expr = (Predicate.Between)e; if (dic.TryGetValue(expr.Expr1, out ex)) expr.Expr1 = ex; if (dic.TryGetValue(expr.Expr2, out ex)) expr.Expr2 = ex; if (dic.TryGetValue(expr.Expr3, out ex)) expr.Expr3 = ex; break; } case QueryElementType.InListPredicate : { var expr = (Predicate.InList)e; if (dic.TryGetValue(expr.Expr1, out ex)) expr.Expr1 = ex; for (var i = 0; i < expr.Values.Count; i++) if (dic.TryGetValue(expr.Values[i], out ex)) expr.Values[i] = ex; break; } case QueryElementType.Column : { var expr = (Column)e; if (expr.Parent != data.Query) return false; if (dic.TryGetValue(expr.Expression, out ex)) expr.Expression = ex; break; } case QueryElementType.SetExpression : { var expr = (SetExpression)e; if (dic.TryGetValue(expr.Expression, out ex)) expr.Expression = ex; break; } case QueryElementType.GroupByClause : { var expr = (GroupByClause)e; for (var i = 0; i < expr.Items.Count; i++) if (dic.TryGetValue(expr.Items[i], out ex)) expr.Items[i] = ex; break; } case QueryElementType.OrderByItem : { var expr = (OrderByItem)e; if (dic.TryGetValue(expr.Expression, out ex)) expr.Expression = ex; break; } } return true; }); foreach (var query in data.Queries) if (query.Queries.Count > 0) ResolveFields(query); } void OptimizeUnions() { var exprs = new Dictionary<ISqlExpression,ISqlExpression>(); new QueryVisitor().Visit(this, e => { var sql = e as SqlQuery; if (sql == null || sql.From.Tables.Count != 1 || !sql.IsSimple || sql._insert != null || sql._update != null || sql._delete != null) return; var table = sql.From.Tables[0]; if (table.Joins.Count != 0 || !(table.Source is SqlQuery)) return; var union = (SqlQuery)table.Source; if (!union.HasUnion) return; for (var i = 0; i < sql.Select.Columns.Count; i++) { var scol = sql. Select.Columns[i]; var ucol = union.Select.Columns[i]; if (scol.Expression != ucol) return; } exprs.Add(union, sql); for (var i = 0; i < sql.Select.Columns.Count; i++) { var scol = sql. Select.Columns[i]; var ucol = union.Select.Columns[i]; scol.Expression = ucol.Expression; scol._alias = ucol._alias; exprs.Add(ucol, scol); } for (var i = sql.Select.Columns.Count; i < union.Select.Columns.Count; i++) sql.Select.Expr(union.Select.Columns[i].Expression); sql.From.Tables.Clear(); sql.From.Tables.AddRange(union.From.Tables); sql.Where. SearchCondition.Conditions.AddRange(union.Where. SearchCondition.Conditions); sql.Having. SearchCondition.Conditions.AddRange(union.Having.SearchCondition.Conditions); sql.GroupBy.Items. AddRange(union.GroupBy.Items); sql.OrderBy.Items. AddRange(union.OrderBy.Items); sql.Unions.InsertRange(0, union.Unions); }); ((ISqlExpressionWalkable)this).Walk(false, expr => { ISqlExpression e; if (exprs.TryGetValue(expr, out e)) return e; return expr; }); } void FinalizeAndValidateInternal(bool isApplySupported, bool optimizeColumns, bool optimizeSearchCondition, List<ISqlTableSource> tables) { OptimizeSearchCondition(Where. SearchCondition); OptimizeSearchCondition(Having.SearchCondition); if (optimizeSearchCondition) { ForEachTable(table => { foreach (var join in table.Joins) OptimizeSearchCondition(join.Condition); }, new HashSet<SqlQuery>()); } new QueryVisitor().Visit(this, e => { var sql = e as SqlQuery; if (sql != null && sql != this) { sql.ParentSql = this; sql.FinalizeAndValidateInternal(isApplySupported, optimizeColumns, false, tables); if (sql.IsParameterDependent) IsParameterDependent = true; } }); ResolveWeakJoins(tables); OptimizeColumns(); OptimizeApplies (isApplySupported, optimizeColumns); OptimizeSubQueries(isApplySupported, optimizeColumns); OptimizeApplies (isApplySupported, optimizeColumns); new QueryVisitor().Visit(this, e => { var sql = e as SqlQuery; if (sql != null && sql != this) sql.RemoveOrderBy(); }); } internal static void OptimizeSearchCondition(SearchCondition searchCondition) { // This 'if' could be replaced by one simple match: // // match (searchCondition.Conditions) // { // | [SearchCondition(true, _) sc] => // searchCondition.Conditions = sc.Conditions; // OptimizeSearchCondition(searchCodition) // // | [SearchCondition(false, [SearchCondition(true, [ExprExpr]) sc])] => ... // // | [Expr(true, SqlValue(true))] // | [Expr(false, SqlValue(false))] // searchCondition.Conditions = [] // } // // One day I am going to rewrite all this crap in Nemerle. // if (searchCondition.Conditions.Count == 1) { var cond = searchCondition.Conditions[0]; if (cond.Predicate is SearchCondition) { var sc = (SearchCondition)cond.Predicate; if (!cond.IsNot) { searchCondition.Conditions.Clear(); searchCondition.Conditions.AddRange(sc.Conditions); OptimizeSearchCondition(searchCondition); return; } if (sc.Conditions.Count == 1) { var c1 = sc.Conditions[0]; if (!c1.IsNot && c1.Predicate is Predicate.ExprExpr) { var ee = (Predicate.ExprExpr)c1.Predicate; Predicate.Operator op; switch (ee.Operator) { case Predicate.Operator.Equal : op = Predicate.Operator.NotEqual; break; case Predicate.Operator.NotEqual : op = Predicate.Operator.Equal; break; case Predicate.Operator.Greater : op = Predicate.Operator.LessOrEqual; break; case Predicate.Operator.NotLess : case Predicate.Operator.GreaterOrEqual : op = Predicate.Operator.Less; break; case Predicate.Operator.Less : op = Predicate.Operator.GreaterOrEqual; break; case Predicate.Operator.NotGreater : case Predicate.Operator.LessOrEqual : op = Predicate.Operator.Greater; break; default: throw new InvalidOperationException(); } c1.Predicate = new Predicate.ExprExpr(ee.Expr1, op, ee.Expr2); searchCondition.Conditions.Clear(); searchCondition.Conditions.AddRange(sc.Conditions); OptimizeSearchCondition(searchCondition); return; } } } if (cond.Predicate.ElementType == QueryElementType.ExprPredicate) { var expr = (Predicate.Expr)cond.Predicate; if (expr.Expr1 is SqlValue) { var value = (SqlValue)expr.Expr1; if (value.Value is bool) if (cond.IsNot ? !(bool)value.Value : (bool)value.Value) searchCondition.Conditions.Clear(); } } } for (var i = 0; i < searchCondition.Conditions.Count; i++) { var cond = searchCondition.Conditions[i]; if (cond.Predicate is Predicate.Expr) { var expr = (Predicate.Expr)cond.Predicate; if (expr.Expr1 is SqlValue) { var value = (SqlValue)expr.Expr1; if (value.Value is bool) { if (cond.IsNot ? !(bool)value.Value : (bool)value.Value) { if (i > 0) { if (searchCondition.Conditions[i-1].IsOr) { searchCondition.Conditions.RemoveRange(0, i); OptimizeSearchCondition(searchCondition); break; } } } } } } else if (cond.Predicate is SearchCondition) { var sc = (SearchCondition)cond.Predicate; OptimizeSearchCondition(sc); } } } void ForEachTable(Action<TableSource> action, HashSet<SqlQuery> visitedQueries) { if (!visitedQueries.Add(this)) return; foreach (var table in From.Tables) table.ForEach(action, visitedQueries); new QueryVisitor().Visit(this, e => { if (e is SqlQuery && e != this) ((SqlQuery)e).ForEachTable(action, visitedQueries); }); } void RemoveOrderBy() { if (OrderBy.Items.Count > 0 && Select.SkipValue == null && Select.TakeValue == null) OrderBy.Items.Clear(); } internal void ResolveWeakJoins(List<ISqlTableSource> tables) { Func<TableSource,bool> findTable = null; findTable = table => { if (tables.Contains(table.Source)) return true; foreach (var join in table.Joins) { if (findTable(join.Table)) { join.IsWeak = false; return true; } } if (table.Source is SqlQuery) foreach (var t in ((SqlQuery)table.Source).From.Tables) if (findTable(t)) return true; return false; }; var areTablesCollected = false; ForEachTable(table => { for (var i = 0; i < table.Joins.Count; i++) { var join = table.Joins[i]; if (join.IsWeak) { if (!areTablesCollected) { areTablesCollected = true; Action<IQueryElement> tableCollector = expr => { var field = expr as SqlField; if (field != null && !tables.Contains(field.Table)) tables.Add(field.Table); }; var visitor = new QueryVisitor(); visitor.VisitAll(Select, tableCollector); visitor.VisitAll(Where, tableCollector); visitor.VisitAll(GroupBy, tableCollector); visitor.VisitAll(Having, tableCollector); visitor.VisitAll(OrderBy, tableCollector); if (_insert != null) visitor.VisitAll(Insert, tableCollector); if (_update != null) visitor.VisitAll(Update, tableCollector); if (_delete != null) visitor.VisitAll(Delete, tableCollector); visitor.VisitAll(From, expr => { var tbl = expr as SqlTable; if (tbl != null && tbl.TableArguments != null) { var v = new QueryVisitor(); foreach (var arg in tbl.TableArguments) v.VisitAll(arg, tableCollector); } }); } if (findTable(join.Table)) { join.IsWeak = false; } else { table.Joins.RemoveAt(i); i--; } } } }, new HashSet<SqlQuery>()); } TableSource OptimizeSubQuery( TableSource source, bool optimizeWhere, bool allColumns, bool isApplySupported, bool optimizeValues, bool optimizeColumns) { foreach (var jt in source.Joins) { var table = OptimizeSubQuery( jt.Table, jt.JoinType == JoinType.Inner || jt.JoinType == JoinType.CrossApply, false, isApplySupported, jt.JoinType == JoinType.Inner || jt.JoinType == JoinType.CrossApply, optimizeColumns); if (table != jt.Table) { var sql = jt.Table.Source as SqlQuery; if (sql != null && sql.OrderBy.Items.Count > 0) foreach (var item in sql.OrderBy.Items) OrderBy.Expr(item.Expression, item.IsDescending); jt.Table = table; } } return source.Source is SqlQuery ? RemoveSubQuery(source, optimizeWhere, allColumns && !isApplySupported, optimizeValues, optimizeColumns) : source; } static bool CheckColumn(Column column, ISqlExpression expr, SqlQuery query, bool optimizeValues, bool optimizeColumns) { if (expr is SqlField || expr is Column) return false; if (expr is SqlValue) return !optimizeValues && 1.Equals(((SqlValue)expr).Value); if (expr is SqlBinaryExpression) { var e = (SqlBinaryExpression)expr; if (e.Operation == "*" && e.Expr1 is SqlValue) { var value = (SqlValue)e.Expr1; if (value.Value is int && (int)value.Value == -1) return CheckColumn(column, e.Expr2, query, optimizeValues, optimizeColumns); } } var visitor = new QueryVisitor(); if (optimizeColumns && visitor.Find(expr, e => e is SqlQuery || IsAggregationFunction(e)) == null) { var n = 0; var q = query.ParentSql ?? query; visitor.VisitAll(q, e => { if (e == column) n++; }); return n > 2; } return true; } TableSource RemoveSubQuery( TableSource childSource, bool concatWhere, bool allColumns, bool optimizeValues, bool optimizeColumns) { var query = (SqlQuery)childSource.Source; var isQueryOK = query.From.Tables.Count == 1; isQueryOK = isQueryOK && (concatWhere || query.Where.IsEmpty && query.Having.IsEmpty); isQueryOK = isQueryOK && !query.HasUnion && query.GroupBy.IsEmpty && !query.Select.HasModifier; if (!isQueryOK) return childSource; var isColumnsOK = (allColumns && !query.Select.Columns.Exists(c => IsAggregationFunction(c.Expression))) || !query.Select.Columns.Exists(c => CheckColumn(c, c.Expression, query, optimizeValues, optimizeColumns)); if (!isColumnsOK) return childSource; var map = new Dictionary<ISqlExpression,ISqlExpression>(query.Select.Columns.Count); foreach (var c in query.Select.Columns) map.Add(c, c.Expression); var top = this; while (top.ParentSql != null) top = top.ParentSql; ((ISqlExpressionWalkable)top).Walk(false, expr => { ISqlExpression fld; return map.TryGetValue(expr, out fld) ? fld : expr; }); new QueryVisitor().Visit(top, expr => { if (expr.ElementType == QueryElementType.InListPredicate) { var p = (Predicate.InList)expr; if (p.Expr1 == query) p.Expr1 = query.From.Tables[0]; } }); query.From.Tables[0].Joins.AddRange(childSource.Joins); if (query.From.Tables[0].Alias == null) query.From.Tables[0].Alias = childSource.Alias; if (!query.Where. IsEmpty) ConcatSearchCondition(Where, query.Where); if (!query.Having.IsEmpty) ConcatSearchCondition(Having, query.Having); ((ISqlExpressionWalkable)top).Walk(false, expr => { if (expr is SqlQuery) { var sql = (SqlQuery)expr; if (sql.ParentSql == query) sql.ParentSql = query.ParentSql ?? this; } return expr; }); return query.From.Tables[0]; } static bool IsAggregationFunction(IQueryElement expr) { if (expr is SqlFunction) switch (((SqlFunction)expr).Name) { case "Count" : case "Average" : case "Min" : case "Max" : case "Sum" : return true; } return false; } void OptimizeApply(TableSource tableSource, JoinedTable joinTable, bool isApplySupported, bool optimizeColumns) { var joinSource = joinTable.Table; foreach (var join in joinSource.Joins) if (join.JoinType == JoinType.CrossApply || join.JoinType == JoinType.OuterApply) OptimizeApply(joinSource, join, isApplySupported, optimizeColumns); if (isApplySupported && !joinTable.CanConvertApply) return; if (joinSource.Source.ElementType == QueryElementType.SqlQuery) { var sql = (SqlQuery)joinSource.Source; var isAgg = sql.Select.Columns.Exists(c => IsAggregationFunction(c.Expression)); if (isApplySupported && (isAgg || sql.Select.TakeValue != null || sql.Select.SkipValue != null)) return; var searchCondition = new List<Condition>(sql.Where.SearchCondition.Conditions); sql.Where.SearchCondition.Conditions.Clear(); if (!ContainsTable(tableSource.Source, sql)) { joinTable.JoinType = joinTable.JoinType == JoinType.CrossApply ? JoinType.Inner : JoinType.Left; joinTable.Condition.Conditions.AddRange(searchCondition); } else { sql.Where.SearchCondition.Conditions.AddRange(searchCondition); var table = OptimizeSubQuery( joinTable.Table, joinTable.JoinType == JoinType.Inner || joinTable.JoinType == JoinType.CrossApply, joinTable.JoinType == JoinType.CrossApply, isApplySupported, joinTable.JoinType == JoinType.Inner || joinTable.JoinType == JoinType.CrossApply, optimizeColumns); if (table != joinTable.Table) { var q = joinTable.Table.Source as SqlQuery; if (q != null && q.OrderBy.Items.Count > 0) foreach (var item in q.OrderBy.Items) OrderBy.Expr(item.Expression, item.IsDescending); joinTable.Table = table; OptimizeApply(tableSource, joinTable, isApplySupported, optimizeColumns); } } } else { if (!ContainsTable(tableSource.Source, joinSource.Source)) joinTable.JoinType = joinTable.JoinType == JoinType.CrossApply ? JoinType.Inner : JoinType.Left; } } static bool ContainsTable(ISqlTableSource table, IQueryElement sql) { return null != new QueryVisitor().Find(sql, e => e == table || e.ElementType == QueryElementType.SqlField && table == ((SqlField)e).Table || e.ElementType == QueryElementType.Column && table == ((Column) e).Parent); } static void ConcatSearchCondition(WhereClause where1, WhereClause where2) { if (where1.IsEmpty) { where1.SearchCondition.Conditions.AddRange(where2.SearchCondition.Conditions); } else { if (where1.SearchCondition.Precedence < Sql.Precedence.LogicalConjunction) { var sc1 = new SearchCondition(); sc1.Conditions.AddRange(where1.SearchCondition.Conditions); where1.SearchCondition.Conditions.Clear(); where1.SearchCondition.Conditions.Add(new Condition(false, sc1)); } if (where2.SearchCondition.Precedence < Sql.Precedence.LogicalConjunction) { var sc2 = new SearchCondition(); sc2.Conditions.AddRange(where2.SearchCondition.Conditions); where1.SearchCondition.Conditions.Add(new Condition(false, sc2)); } else where1.SearchCondition.Conditions.AddRange(where2.SearchCondition.Conditions); } } void OptimizeSubQueries(bool isApplySupported, bool optimizeColumns) { for (var i = 0; i < From.Tables.Count; i++) { var table = OptimizeSubQuery(From.Tables[i], true, false, isApplySupported, true, optimizeColumns); if (table != From.Tables[i]) { var sql = From.Tables[i].Source as SqlQuery; if (!Select.Columns.All(c => IsAggregationFunction(c.Expression))) if (sql != null && sql.OrderBy.Items.Count > 0) foreach (var item in sql.OrderBy.Items) OrderBy.Expr(item.Expression, item.IsDescending); From.Tables[i] = table; } } } void OptimizeApplies(bool isApplySupported, bool optimizeColumns) { foreach (var table in From.Tables) foreach (var join in table.Joins) if (join.JoinType == JoinType.CrossApply || join.JoinType == JoinType.OuterApply) OptimizeApply(table, join, isApplySupported, optimizeColumns); } void OptimizeColumns() { ((ISqlExpressionWalkable)Select).Walk(false, expr => { var query = expr as SqlQuery; if (query != null && query.From.Tables.Count == 0 && query.Select.Columns.Count == 1) { new QueryVisitor().Visit(query.Select.Columns[0].Expression, e => { if (e.ElementType == QueryElementType.SqlQuery) { var q = (SqlQuery)e; if (q.ParentSql == query) q.ParentSql = query.ParentSql; } }); return query.Select.Columns[0].Expression; } return expr; }); } IDictionary<string,object> _aliases; public void RemoveAlias(string alias) { if (_aliases != null) { alias = alias.ToUpper(); if (_aliases.ContainsKey(alias)) _aliases.Remove(alias); } } public string GetAlias(string desiredAlias, string defaultAlias) { if (_aliases == null) _aliases = new Dictionary<string,object>(); var alias = desiredAlias; if (string.IsNullOrEmpty(desiredAlias) || desiredAlias.Length > 30) { desiredAlias = defaultAlias; alias = defaultAlias + "1"; } for (var i = 1; ; i++) { var s = alias.ToUpper(); if (!_aliases.ContainsKey(s) && !_reservedWords.ContainsKey(s)) { _aliases.Add(s, s); break; } alias = desiredAlias + i; } return alias; } public string[] GetTempAliases(int n, string defaultAlias) { var aliases = new string[n]; for (var i = 0; i < aliases.Length; i++) aliases[i] = GetAlias(defaultAlias, defaultAlias); foreach (var t in aliases) RemoveAlias(t); return aliases; } void SetAliases() { _aliases = null; var objs = new Dictionary<object,object>(); Parameters.Clear(); new QueryVisitor().VisitAll(this, expr => { switch (expr.ElementType) { case QueryElementType.SqlParameter: { var p = (SqlParameter)expr; if (p.IsQueryParameter) { if (!objs.ContainsKey(expr)) { objs.Add(expr, expr); p.Name = GetAlias(p.Name, "p"); } Parameters.Add(p); } else IsParameterDependent = true; } break; case QueryElementType.Column: { if (!objs.ContainsKey(expr)) { objs.Add(expr, expr); var c = (Column)expr; if (c.Alias != "*") c.Alias = GetAlias(c.Alias, "c"); } } break; case QueryElementType.TableSource: { var table = (TableSource)expr; if (!objs.ContainsKey(table)) { objs.Add(table, table); table.Alias = GetAlias(table.Alias, "t"); } } break; case QueryElementType.SqlQuery: { var sql = (SqlQuery)expr; if (sql.HasUnion) { for (var i = 0; i < sql.Select.Columns.Count; i++) { var col = sql.Select.Columns[i]; foreach (var t in sql.Unions) { var union = t.SqlQuery.Select; objs.Remove(union.Columns[i].Alias); union.Columns[i].Alias = col.Alias; } } } } break; } }); } #endregion #region ProcessParameters public SqlQuery ProcessParameters() { if (IsParameterDependent) { var query = new QueryVisitor().Convert(this, e => { switch (e.ElementType) { case QueryElementType.SqlParameter : { var p = (SqlParameter)e; if (p.Value == null) return new SqlValue(null); } break; case QueryElementType.ExprExprPredicate : { var ee = (Predicate.ExprExpr)e; if (ee.Operator == Predicate.Operator.Equal || ee.Operator == Predicate.Operator.NotEqual) { object value1; object value2; if (ee.Expr1 is SqlValue) value1 = ((SqlValue)ee.Expr1).Value; else if (ee.Expr1 is SqlParameter) value1 = ((SqlParameter)ee.Expr1).Value; else break; if (ee.Expr2 is SqlValue) value2 = ((SqlValue)ee.Expr2).Value; else if (ee.Expr2 is SqlParameter) value2 = ((SqlParameter)ee.Expr2).Value; else break; var value = Equals(value1, value2); if (ee.Operator == Predicate.Operator.NotEqual) value = !value; return new Predicate.Expr(new SqlValue(value), Sql.Precedence.Comparison); } } break; case QueryElementType.InListPredicate : return ConvertInListPredicate((Predicate.InList)e); } return null; }); if (query != this) { query.Parameters.Clear(); new QueryVisitor().VisitAll(query, expr => { if (expr.ElementType == QueryElementType.SqlParameter) { var p = (SqlParameter)expr; if (p.IsQueryParameter) query.Parameters.Add(p); } }); } return query; } return this; } static Predicate ConvertInListPredicate(Predicate.InList p) { if (p.Values == null || p.Values.Count == 0) return new Predicate.Expr(new SqlValue(p.IsNot)); if (p.Values.Count == 1 && p.Values[0] is SqlParameter) { var pr = (SqlParameter)p.Values[0]; if (pr.Value == null) return new Predicate.Expr(new SqlValue(p.IsNot)); if (pr.Value is IEnumerable) { if (p.Expr1 is ISqlTableSource) { var items = (IEnumerable)pr.Value; var table = (ISqlTableSource)p.Expr1; var keys = table.GetKeys(true); if (keys == null || keys.Count == 0) throw new SqlException("Cant create IN expression."); if (keys.Count == 1) { var values = new List<ISqlExpression>(); var field = GetUnderlayingField(keys[0]); foreach (var item in items) { var value = field.MemberMapper.GetValue(item); values.Add(new SqlValue(value)); } if (values.Count == 0) return new Predicate.Expr(new SqlValue(p.IsNot)); return new Predicate.InList(keys[0], p.IsNot, values); } { var sc = new SearchCondition(); foreach (var item in items) { var itemCond = new SearchCondition(); foreach (var key in keys) { var field = GetUnderlayingField(key); var value = field.MemberMapper.GetValue(item); var cond = value == null ? new Condition(false, new Predicate.IsNull (field, false)) : new Condition(false, new Predicate.ExprExpr(field, Predicate.Operator.Equal, new SqlValue(value))); itemCond.Conditions.Add(cond); } sc.Conditions.Add(new Condition(false, new Predicate.Expr(itemCond), true)); } if (sc.Conditions.Count == 0) return new Predicate.Expr(new SqlValue(p.IsNot)); if (p.IsNot) return new Predicate.NotExpr(sc, true, Sql.Precedence.LogicalNegation); return new Predicate.Expr(sc, Sql.Precedence.LogicalDisjunction); } } if (p.Expr1 is SqlExpression) { var expr = (SqlExpression)p.Expr1; if (expr.Expr.Length > 1 && expr.Expr[0] == '\x1') { var type = TypeHelper.GetListItemType(pr.Value); var ta = TypeAccessor.GetAccessor(type); var items = (IEnumerable)pr.Value; var names = expr.Expr.Substring(1).Split(','); if (expr.Parameters.Length == 1) { var values = new List<ISqlExpression>(); foreach (var item in items) { var value = ta[names[0]].GetValue(item); values.Add(new SqlValue(value)); } if (values.Count == 0) return new Predicate.Expr(new SqlValue(p.IsNot)); return new Predicate.InList(expr.Parameters[0], p.IsNot, values); } { var sc = new SearchCondition(); foreach (var item in items) { var itemCond = new SearchCondition(); for (var i = 0; i < expr.Parameters.Length; i++) { var sql = expr.Parameters[i]; var value = ta[names[i]].GetValue(item); var cond = value == null ? new Condition(false, new Predicate.IsNull (sql, false)) : new Condition(false, new Predicate.ExprExpr(sql, Predicate.Operator.Equal, new SqlValue(value))); itemCond.Conditions.Add(cond); } sc.Conditions.Add(new Condition(false, new Predicate.Expr(itemCond), true)); } if (sc.Conditions.Count == 0) return new Predicate.Expr(new SqlValue(p.IsNot)); if (p.IsNot) return new Predicate.NotExpr(sc, true, Sql.Precedence.LogicalNegation); return new Predicate.Expr(sc, Sql.Precedence.LogicalDisjunction); } } } /* var itemType = items.GetType().GetItemType(); if (itemType == typeof(DateTime) || itemType == typeof(DateTimeOffset) || itemType == typeof(DateTime?) || itemType == typeof(DateTimeOffset?)) { var list = new List<SqlParameter>(); foreach (var item in items) list.Add(new SqlParameter(itemType, "p", item, (MappingSchema)null)); return new Predicate.InList(p.Expr1, p.IsNot, list); } */ } } return null; } static SqlField GetUnderlayingField(ISqlExpression expr) { switch (expr.ElementType) { case QueryElementType.SqlField: return (SqlField)expr; case QueryElementType.Column : return GetUnderlayingField(((Column)expr).Expression); } throw new InvalidOperationException(); } #endregion #region Clone SqlQuery(SqlQuery clone, Dictionary<ICloneableElement,ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) { objectTree.Add(clone, this); objectTree.Add(clone.All, All); SourceID = Interlocked.Increment(ref SourceIDCounter); _queryType = clone._queryType; if (IsInsert) _insert = (InsertClause)clone._insert.Clone(objectTree, doClone); if (IsUpdate) _update = (UpdateClause)clone._update.Clone(objectTree, doClone); if (IsDelete) _delete = (DeleteClause)clone._delete.Clone(objectTree, doClone); _select = new SelectClause (this, clone._select, objectTree, doClone); _from = new FromClause (this, clone._from, objectTree, doClone); _where = new WhereClause (this, clone._where, objectTree, doClone); _groupBy = new GroupByClause(this, clone._groupBy, objectTree, doClone); _having = new WhereClause (this, clone._having, objectTree, doClone); _orderBy = new OrderByClause(this, clone._orderBy, objectTree, doClone); _parameters.AddRange(clone._parameters.ConvertAll(p => (SqlParameter)p.Clone(objectTree, doClone))); IsParameterDependent = clone.IsParameterDependent; new QueryVisitor().Visit(this, expr => { var sb = expr as SqlQuery; if (sb != null && sb.ParentSql == clone) sb.ParentSql = this; }); } public SqlQuery Clone() { return (SqlQuery)Clone(new Dictionary<ICloneableElement,ICloneableElement>(), _ => true); } public SqlQuery Clone(Predicate<ICloneableElement> doClone) { return (SqlQuery)Clone(new Dictionary<ICloneableElement,ICloneableElement>(), doClone); } #endregion #region Helpers public TableSource GetTableSource(ISqlTableSource table) { var ts = From[table]; return ts == null && ParentSql != null? ParentSql.GetTableSource(table) : ts; } static TableSource CheckTableSource(TableSource ts, ISqlTableSource table, string alias) { if (ts.Source == table && (alias == null || ts.Alias == alias)) return ts; var jt = ts[table, alias]; if (jt != null) return jt; if (ts.Source is SqlQuery) { var s = ((SqlQuery)ts.Source).From[table, alias]; if (s != null) return s; } return null; } #endregion #region Overrides public string SqlText { get { return ToString(); } } #if OVERRIDETOSTRING public override string ToString() { return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); } #endif #endregion #region ISqlExpression Members public bool CanBeNull() { return true; } public bool Equals(ISqlExpression other, Func<ISqlExpression, ISqlExpression, bool> comparer) { return this == other; } public int Precedence { get { return Sql.Precedence.Unknown; } } public Type SystemType { get { if (Select.Columns.Count == 1) return Select.Columns[0].SystemType; if (From.Tables.Count == 1 && From.Tables[0].Joins.Count == 0) return From.Tables[0].SystemType; return null; } } #endregion #region ICloneableElement Members public ICloneableElement Clone(Dictionary<ICloneableElement, ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) { if (!doClone(this)) return this; ICloneableElement clone; if (!objectTree.TryGetValue(this, out clone)) clone = new SqlQuery(this, objectTree, doClone); return clone; } #endregion #region ISqlExpressionWalkable Members [Obsolete] ISqlExpression ISqlExpressionWalkable.Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) { if (_insert != null) ((ISqlExpressionWalkable)_insert).Walk(skipColumns, func); if (_update != null) ((ISqlExpressionWalkable)_update).Walk(skipColumns, func); if (_delete != null) ((ISqlExpressionWalkable)_delete).Walk(skipColumns, func); ((ISqlExpressionWalkable)Select) .Walk(skipColumns, func); ((ISqlExpressionWalkable)From) .Walk(skipColumns, func); ((ISqlExpressionWalkable)Where) .Walk(skipColumns, func); ((ISqlExpressionWalkable)GroupBy).Walk(skipColumns, func); ((ISqlExpressionWalkable)Having) .Walk(skipColumns, func); ((ISqlExpressionWalkable)OrderBy).Walk(skipColumns, func); if (HasUnion) foreach (var union in Unions) ((ISqlExpressionWalkable)union.SqlQuery).Walk(skipColumns, func); return func(this); } #endregion #region IEquatable<ISqlExpression> Members bool IEquatable<ISqlExpression>.Equals(ISqlExpression other) { return this == other; } #endregion #region ISqlTableSource Members public static int SourceIDCounter; public int SourceID { get; private set; } public SqlTableType SqlTableType { get { return SqlTableType.Table; } } private SqlField _all; public SqlField All { get { if (_all == null) { _all = new SqlField(null, "*", "*", true, -1, null, null); ((IChild<ISqlTableSource>)_all).Parent = this; } return _all; } internal set { _all = value; if (_all != null) ((IChild<ISqlTableSource>)_all).Parent = this; } } List<ISqlExpression> _keys; public IList<ISqlExpression> GetKeys(bool allIfEmpty) { if (_keys == null && From.Tables.Count == 1 && From.Tables[0].Joins.Count == 0) { _keys = new List<ISqlExpression>(); var q = from key in ((ISqlTableSource)From.Tables[0]).GetKeys(allIfEmpty) from col in Select.Columns where col.Expression == key select col as ISqlExpression; _keys = q.ToList(); } return _keys; } #endregion #region IQueryElement Members public QueryElementType ElementType { get { return QueryElementType.SqlQuery; } } StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) { if (dic.ContainsKey(this)) return sb.Append("..."); dic.Add(this, this); sb .Append("(") .Append(SourceID) .Append(") "); ((IQueryElement)Select). ToString(sb, dic); ((IQueryElement)From). ToString(sb, dic); ((IQueryElement)Where). ToString(sb, dic); ((IQueryElement)GroupBy).ToString(sb, dic); ((IQueryElement)Having). ToString(sb, dic); ((IQueryElement)OrderBy).ToString(sb, dic); if (HasUnion) foreach (IQueryElement u in Unions) u.ToString(sb, dic); dic.Remove(this); return sb; } #endregion } }