diff Source/Data/Sql/QueryVisitor.cs @ 0:f990fcb411a9

Копия текущей версии из github
author cin
date Thu, 27 Mar 2014 21:46:09 +0400
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Source/Data/Sql/QueryVisitor.cs	Thu Mar 27 21:46:09 2014 +0400
@@ -0,0 +1,1465 @@
+using System;
+using System.Collections.Generic;
+
+namespace BLToolkit.Data.Sql
+{
+	using VisitFunc   = Func<IQueryElement,bool>;
+	using FindFunc    = Func<IQueryElement,bool>;
+	using ConvertFunc = Func<IQueryElement,IQueryElement>;
+
+	public class QueryVisitor
+	{
+		#region Visit
+
+		readonly Dictionary<IQueryElement,IQueryElement> _visitedElements = new Dictionary<IQueryElement, IQueryElement>();
+		public   Dictionary<IQueryElement,IQueryElement>  VisitedElements
+		{
+			get { return _visitedElements; }
+		}
+
+		bool                     _all;
+		Func<IQueryElement,bool> _action1;
+		Action<IQueryElement>    _action2;
+
+		public void VisitParentFirst(IQueryElement element, Func<IQueryElement,bool> action)
+		{
+			_visitedElements.Clear();
+			_action1 = action;
+			Visit1(element);
+		}
+
+		void Visit1(IQueryElement element)
+		{
+			if (element == null || _visitedElements.ContainsKey(element))
+				return;
+
+			_visitedElements.Add(element, element);
+
+			if (!_action1(element))
+				return;
+
+			switch (element.ElementType)
+			{
+				case QueryElementType.SqlFunction:
+					{
+						foreach (var p in ((SqlFunction)element).Parameters) Visit1(p);
+						break;
+					}
+
+				case QueryElementType.SqlExpression:
+					{
+						foreach (var v in ((SqlExpression)element).Parameters) Visit1(v);
+						break;
+					}
+
+				case QueryElementType.SqlBinaryExpression:
+					{
+						//var bexpr = (SqlBinaryExpression)element;
+						Visit1(((SqlBinaryExpression)element).Expr1);
+						Visit1(((SqlBinaryExpression)element).Expr2);
+						break;
+					}
+
+				case QueryElementType.SqlTable:
+					{
+						var table = (SqlTable)element;
+
+						Visit1(table.All);
+						foreach (var field in table.Fields.Values) Visit1(field);
+						foreach (var join  in table.Joins)         Visit1(join);
+
+						if (table.TableArguments != null)
+							foreach (var a in table.TableArguments) Visit1(a);
+
+						break;
+					}
+
+				case QueryElementType.Join:
+					{
+						foreach (var j in ((Join)element).JoinOns) Visit1(j);
+						break;
+					}
+
+				case QueryElementType.Column:
+					{
+						Visit1(((SqlQuery.Column)element).Expression);
+						break;
+					}
+
+				case QueryElementType.TableSource:
+					{
+						//var table = ((SqlQuery.TableSource)element);
+
+						Visit1(((SqlQuery.TableSource)element).Source);
+						foreach (var j in ((SqlQuery.TableSource)element).Joins) Visit1(j);
+						break;
+					}
+
+				case QueryElementType.JoinedTable:
+					{
+						//var join = (SqlQuery.JoinedTable)element;
+						Visit1(((SqlQuery.JoinedTable)element).Table);
+						Visit1(((SqlQuery.JoinedTable)element).Condition);
+						break;
+					}
+
+				case QueryElementType.SearchCondition:
+					{
+						foreach (var c in ((SqlQuery.SearchCondition)element).Conditions) Visit1(c);
+						break;
+					}
+
+				case QueryElementType.Condition:
+					{
+						Visit1(((SqlQuery.Condition)element).Predicate);
+						break;
+					}
+
+				case QueryElementType.ExprPredicate:
+					{
+						Visit1(((SqlQuery.Predicate.Expr)element).Expr1);
+						break;
+					}
+
+				case QueryElementType.NotExprPredicate:
+					{
+						Visit1(((SqlQuery.Predicate.NotExpr)element).Expr1);
+						break;
+					}
+
+				case QueryElementType.ExprExprPredicate:
+					{
+						//var p = ((SqlQuery.Predicate.ExprExpr)element);
+						Visit1(((SqlQuery.Predicate.ExprExpr)element).Expr1);
+						Visit1(((SqlQuery.Predicate.ExprExpr)element).Expr2);
+						break;
+					}
+
+				case QueryElementType.LikePredicate:
+					{
+						//var p = ((SqlQuery.Predicate.Like)element);
+						Visit1(((SqlQuery.Predicate.Like)element).Expr1);
+						Visit1(((SqlQuery.Predicate.Like)element).Expr2);
+						Visit1(((SqlQuery.Predicate.Like)element).Escape);
+						break;
+					}
+
+				case QueryElementType.BetweenPredicate:
+					{
+						//var p = (SqlQuery.Predicate.Between)element;
+						Visit1(((SqlQuery.Predicate.Between)element).Expr1);
+						Visit1(((SqlQuery.Predicate.Between)element).Expr2);
+						Visit1(((SqlQuery.Predicate.Between)element).Expr3);
+						break;
+					}
+
+				case QueryElementType.IsNullPredicate:
+					{
+						Visit1(((SqlQuery.Predicate.IsNull)element).Expr1);
+						break;
+					}
+
+				case QueryElementType.InSubQueryPredicate:
+					{
+						//var p = (SqlQuery.Predicate.InSubQuery)element;
+						Visit1(((SqlQuery.Predicate.InSubQuery)element).Expr1);
+						Visit1(((SqlQuery.Predicate.InSubQuery)element).SubQuery);
+						break;
+					}
+
+				case QueryElementType.InListPredicate:
+					{
+						//var p = (SqlQuery.Predicate.InList)element;
+						Visit1(((SqlQuery.Predicate.InList)element).Expr1);
+						foreach (var value in ((SqlQuery.Predicate.InList)element).Values) Visit1(value);
+						break;
+					}
+
+				case QueryElementType.FuncLikePredicate:
+					{
+						Visit1(((SqlQuery.Predicate.FuncLike)element).Function);
+						break;
+					}
+
+				case QueryElementType.SetExpression:
+					{
+						//var s = (SqlQuery.SetExpression)element;
+						Visit1(((SqlQuery.SetExpression)element).Column);
+						Visit1(((SqlQuery.SetExpression)element).Expression);
+						break;
+					}
+
+				case QueryElementType.InsertClause:
+					{
+						//var sc = (SqlQuery.InsertClause)element;
+
+						if (((SqlQuery.InsertClause)element).Into != null)
+							Visit1(((SqlQuery.InsertClause)element).Into);
+
+						foreach (var c in ((SqlQuery.InsertClause)element).Items.ToArray()) Visit1(c);
+						break;
+					}
+
+				case QueryElementType.UpdateClause:
+					{
+						//var sc = (SqlQuery.UpdateClause)element;
+
+						if (((SqlQuery.UpdateClause)element).Table != null)
+							Visit1(((SqlQuery.UpdateClause)element).Table);
+
+						foreach (var c in ((SqlQuery.UpdateClause)element).Items.ToArray()) Visit1(c);
+						foreach (var c in ((SqlQuery.UpdateClause)element).Keys. ToArray()) Visit1(c);
+						break;
+					}
+
+				case QueryElementType.DeleteClause:
+					{
+						if (((SqlQuery.DeleteClause)element).Table != null)
+							Visit1(((SqlQuery.DeleteClause)element).Table);
+						break;
+					}
+
+				case QueryElementType.SelectClause:
+					{
+						//var sc = (SqlQuery.SelectClause)element;
+						Visit1(((SqlQuery.SelectClause)element).TakeValue);
+						Visit1(((SqlQuery.SelectClause)element).SkipValue);
+
+						foreach (var c in ((SqlQuery.SelectClause)element).Columns.ToArray()) Visit1(c);
+						break;
+					}
+
+				case QueryElementType.FromClause:
+					{
+						foreach (var t in ((SqlQuery.FromClause)element).Tables) Visit1(t);
+						break;
+					}
+
+				case QueryElementType.WhereClause:
+					{
+						Visit1(((SqlQuery.WhereClause)element).SearchCondition);
+						break;
+					}
+
+				case QueryElementType.GroupByClause:
+					{
+						foreach (var i in ((SqlQuery.GroupByClause)element).Items) Visit1(i);
+						break;
+					}
+
+				case QueryElementType.OrderByClause:
+					{
+						foreach (var i in ((SqlQuery.OrderByClause)element).Items) Visit1(i);
+						break;
+					}
+
+				case QueryElementType.OrderByItem:
+					{
+						Visit1(((SqlQuery.OrderByItem)element).Expression);
+						break;
+					}
+
+				case QueryElementType.Union:
+					Visit1(((SqlQuery.Union)element).SqlQuery);
+					break;
+
+				case QueryElementType.SqlQuery:
+					{
+						if (_all)
+						{
+							if (_visitedElements.ContainsKey(element))
+								return;
+							_visitedElements.Add(element, element);
+						}
+
+						var q = (SqlQuery)element;
+
+						switch (q.QueryType)
+						{
+							case QueryType.InsertOrUpdate :
+								Visit1(q.Insert);
+								Visit1(q.Update);
+
+								if (q.From.Tables.Count == 0)
+									break;
+
+								goto default;
+
+							case QueryType.Update :
+								Visit1(q.Update);
+								break;
+
+							case QueryType.Delete :
+								Visit1(q.Delete);
+								Visit1(q.Select);
+								break;
+
+							case QueryType.Insert :
+								Visit1(q.Insert);
+
+								if (q.From.Tables.Count != 0)
+									Visit1(q.Select);
+
+								break;
+
+							default :
+								Visit1(q.Select);
+								break;
+						}
+
+						Visit1(q.From);
+						Visit1(q.Where);
+						Visit1(q.GroupBy);
+						Visit1(q.Having);
+						Visit1(q.OrderBy);
+
+						if (q.HasUnion)
+						{
+							foreach (var i in q.Unions)
+							{
+								if (i.SqlQuery == q)
+									throw new InvalidOperationException();
+
+								Visit1(i);
+							}
+						}
+
+						break;
+					}
+			}
+		}
+
+		public void Visit(IQueryElement element, Action<IQueryElement> action)
+		{
+			_visitedElements.Clear();
+			_all         = false;
+			_action2     = action;
+			Visit2(element);
+		}
+
+		public void VisitAll(IQueryElement element, Action<IQueryElement> action)
+		{
+			_visitedElements.Clear();
+			_all         = true;
+			_action2     = action;
+			Visit2(element);
+		}
+
+		void Visit2(IQueryElement element)
+		{
+			if (element == null || !_all && _visitedElements.ContainsKey(element))
+				return;
+
+			switch (element.ElementType)
+			{
+				case QueryElementType.SqlFunction:
+					{
+						foreach (var p in ((SqlFunction)element).Parameters) Visit2(p);
+						break;
+					}
+
+				case QueryElementType.SqlExpression:
+					{
+						foreach (var v in ((SqlExpression)element).Parameters) Visit2(v);
+						break;
+					}
+
+				case QueryElementType.SqlBinaryExpression:
+					{
+						//var bexpr = (SqlBinaryExpression)element;
+						Visit2(((SqlBinaryExpression)element).Expr1);
+						Visit2(((SqlBinaryExpression)element).Expr2);
+						break;
+					}
+
+				case QueryElementType.SqlTable:
+					{
+						var table = (SqlTable)element;
+
+						Visit2(table.All);
+						foreach (var field in table.Fields.Values) Visit2(field);
+						foreach (var join  in table.Joins)         Visit2(join);
+
+						if (table.TableArguments != null)
+							foreach (var a in table.TableArguments) Visit2(a);
+
+						break;
+					}
+
+				case QueryElementType.Join:
+					{
+						foreach (var j in ((Join)element).JoinOns) Visit2(j);
+						break;
+					}
+
+				case QueryElementType.Column:
+					{
+						Visit2(((SqlQuery.Column)element).Expression);
+						break;
+					}
+
+				case QueryElementType.TableSource:
+					{
+						//var table = ((SqlQuery.TableSource)element);
+
+						Visit2(((SqlQuery.TableSource)element).Source);
+						foreach (var j in ((SqlQuery.TableSource)element).Joins) Visit2(j);
+						break;
+					}
+
+				case QueryElementType.JoinedTable:
+					{
+						//var join = (SqlQuery.JoinedTable)element;
+						Visit2(((SqlQuery.JoinedTable)element).Table);
+						Visit2(((SqlQuery.JoinedTable)element).Condition);
+						break;
+					}
+
+				case QueryElementType.SearchCondition:
+					{
+						foreach (var c in ((SqlQuery.SearchCondition)element).Conditions) Visit2(c);
+						break;
+					}
+
+				case QueryElementType.Condition:
+					{
+						Visit2(((SqlQuery.Condition)element).Predicate);
+						break;
+					}
+
+				case QueryElementType.ExprPredicate:
+					{
+						Visit2(((SqlQuery.Predicate.Expr)element).Expr1);
+						break;
+					}
+
+				case QueryElementType.NotExprPredicate:
+					{
+						Visit2(((SqlQuery.Predicate.NotExpr)element).Expr1);
+						break;
+					}
+
+				case QueryElementType.ExprExprPredicate:
+					{
+						//var p = ((SqlQuery.Predicate.ExprExpr)element);
+						Visit2(((SqlQuery.Predicate.ExprExpr)element).Expr1);
+						Visit2(((SqlQuery.Predicate.ExprExpr)element).Expr2);
+						break;
+					}
+
+				case QueryElementType.LikePredicate:
+					{
+						//var p = ((SqlQuery.Predicate.Like)element);
+						Visit2(((SqlQuery.Predicate.Like)element).Expr1);
+						Visit2(((SqlQuery.Predicate.Like)element).Expr2);
+						Visit2(((SqlQuery.Predicate.Like)element).Escape);
+						break;
+					}
+
+				case QueryElementType.BetweenPredicate:
+					{
+						//var p = (SqlQuery.Predicate.Between)element;
+						Visit2(((SqlQuery.Predicate.Between)element).Expr1);
+						Visit2(((SqlQuery.Predicate.Between)element).Expr2);
+						Visit2(((SqlQuery.Predicate.Between)element).Expr3);
+						break;
+					}
+
+				case QueryElementType.IsNullPredicate:
+					{
+						Visit2(((SqlQuery.Predicate.IsNull)element).Expr1);
+						break;
+					}
+
+				case QueryElementType.InSubQueryPredicate:
+					{
+						//var p = (SqlQuery.Predicate.InSubQuery)element;
+						Visit2(((SqlQuery.Predicate.InSubQuery)element).Expr1);
+						Visit2(((SqlQuery.Predicate.InSubQuery)element).SubQuery);
+						break;
+					}
+
+				case QueryElementType.InListPredicate:
+					{
+						//var p = (SqlQuery.Predicate.InList)element;
+						Visit2(((SqlQuery.Predicate.InList)element).Expr1);
+						foreach (var value in ((SqlQuery.Predicate.InList)element).Values) Visit2(value);
+						break;
+					}
+
+				case QueryElementType.FuncLikePredicate:
+					{
+						Visit2(((SqlQuery.Predicate.FuncLike)element).Function);
+						break;
+					}
+
+				case QueryElementType.SetExpression:
+					{
+						//var s = (SqlQuery.SetExpression)element;
+						Visit2(((SqlQuery.SetExpression)element).Column);
+						Visit2(((SqlQuery.SetExpression)element).Expression);
+						break;
+					}
+
+				case QueryElementType.InsertClause:
+					{
+						//var sc = (SqlQuery.InsertClause)element;
+
+						if (((SqlQuery.InsertClause)element).Into != null)
+							Visit2(((SqlQuery.InsertClause)element).Into);
+
+						foreach (var c in ((SqlQuery.InsertClause)element).Items.ToArray()) Visit2(c);
+						break;
+					}
+
+				case QueryElementType.UpdateClause:
+					{
+						//var sc = (SqlQuery.UpdateClause)element;
+
+						if (((SqlQuery.UpdateClause)element).Table != null)
+							Visit2(((SqlQuery.UpdateClause)element).Table);
+
+						foreach (var c in ((SqlQuery.UpdateClause)element).Items.ToArray()) Visit2(c);
+						foreach (var c in ((SqlQuery.UpdateClause)element).Keys. ToArray()) Visit2(c);
+						break;
+					}
+
+				case QueryElementType.DeleteClause:
+					{
+						if (((SqlQuery.DeleteClause)element).Table != null)
+							Visit2(((SqlQuery.DeleteClause)element).Table);
+						break;
+					}
+
+				case QueryElementType.SelectClause:
+					{
+						//var sc = (SqlQuery.SelectClause)element;
+						Visit2(((SqlQuery.SelectClause)element).TakeValue);
+						Visit2(((SqlQuery.SelectClause)element).SkipValue);
+
+						foreach (var c in ((SqlQuery.SelectClause)element).Columns.ToArray()) Visit2(c);
+						break;
+					}
+
+				case QueryElementType.FromClause:
+					{
+						foreach (var t in ((SqlQuery.FromClause)element).Tables) Visit2(t);
+						break;
+					}
+
+				case QueryElementType.WhereClause:
+					{
+						Visit2(((SqlQuery.WhereClause)element).SearchCondition);
+						break;
+					}
+
+				case QueryElementType.GroupByClause:
+					{
+						foreach (var i in ((SqlQuery.GroupByClause)element).Items) Visit2(i);
+						break;
+					}
+
+				case QueryElementType.OrderByClause:
+					{
+						foreach (var i in ((SqlQuery.OrderByClause)element).Items) Visit2(i);
+						break;
+					}
+
+				case QueryElementType.OrderByItem:
+					{
+						Visit2(((SqlQuery.OrderByItem)element).Expression);
+						break;
+					}
+
+				case QueryElementType.Union:
+					Visit2(((SqlQuery.Union)element).SqlQuery);
+					break;
+
+				case QueryElementType.SqlQuery:
+					{
+						if (_all)
+						{
+							if (_visitedElements.ContainsKey(element))
+								return;
+							_visitedElements.Add(element, element);
+						}
+
+						var q = (SqlQuery)element;
+
+						switch (q.QueryType)
+						{
+							case QueryType.InsertOrUpdate :
+								Visit2(q.Insert);
+								Visit2(q.Update);
+
+								if (q.From.Tables.Count == 0)
+									break;
+
+								goto default;
+
+							case QueryType.Update :
+								Visit2(q.Update);
+								break;
+
+							case QueryType.Delete :
+								Visit2(q.Delete);
+								Visit2(q.Select);
+								break;
+
+							case QueryType.Insert :
+								Visit2(q.Insert);
+
+								if (q.From.Tables.Count != 0)
+									Visit2(q.Select);
+
+								break;
+
+							default :
+								Visit2(q.Select);
+								break;
+						}
+
+						// Visit2(q.From);
+						//
+						if (q.From != null && (_all || !_visitedElements.ContainsKey(q.From)))
+						{
+							foreach (var t in q.From.Tables)
+							{
+								//Visit2(t);
+								//
+								if (t != null && (_all || !_visitedElements.ContainsKey(t)))
+								{
+									Visit2(t.Source);
+
+									foreach (var j in t.Joins)
+										Visit2(j);
+
+									_action2(t);
+									if (!_all)
+										_visitedElements.Add(t, t);
+								}
+							}
+							_action2(q.From);
+							if (!_all)
+								_visitedElements.Add(q.From, q.From);
+						}
+
+						Visit2(q.Where);
+						Visit2(q.GroupBy);
+						Visit2(q.Having);
+						Visit2(q.OrderBy);
+
+						if (q.HasUnion)
+						{
+							foreach (var i in q.Unions)
+							{
+								if (i.SqlQuery == q)
+									throw new InvalidOperationException();
+
+								Visit2(i);
+							}
+						}
+
+						break;
+					}
+			}
+
+			_action2(element);
+			if (!_all)
+				_visitedElements.Add(element, element);
+		}
+
+		#endregion
+
+		#region Find
+
+		IQueryElement Find<T>(IEnumerable<T> arr, FindFunc find)
+			where T : class, IQueryElement
+		{
+			if (arr == null)
+				return null;
+
+			foreach (var item in arr)
+			{
+				var e = Find(item, find);
+				if (e != null)
+					return e;
+			}
+
+			return null;
+		}
+
+		public IQueryElement Find(IQueryElement element, FindFunc find)
+		{
+			if (element == null || find(element))
+				return element;
+
+			switch (element.ElementType)
+			{
+				case QueryElementType.SqlFunction       : return Find(((SqlFunction)                element).Parameters,      find);
+				case QueryElementType.SqlExpression     : return Find(((SqlExpression)              element).Parameters,      find);
+				case QueryElementType.Join              : return Find(((Join)                       element).JoinOns,         find);
+				case QueryElementType.Column            : return Find(((SqlQuery.Column)            element).Expression,      find);
+				case QueryElementType.SearchCondition   : return Find(((SqlQuery.SearchCondition)   element).Conditions,      find);
+				case QueryElementType.Condition         : return Find(((SqlQuery.Condition)         element).Predicate,       find);
+				case QueryElementType.ExprPredicate     : return Find(((SqlQuery.Predicate.Expr)    element).Expr1,           find);
+				case QueryElementType.NotExprPredicate  : return Find(((SqlQuery.Predicate.NotExpr) element).Expr1,           find);
+				case QueryElementType.IsNullPredicate   : return Find(((SqlQuery.Predicate.IsNull)  element).Expr1,           find);
+				case QueryElementType.FromClause        : return Find(((SqlQuery.FromClause)        element).Tables,          find);
+				case QueryElementType.WhereClause       : return Find(((SqlQuery.WhereClause)       element).SearchCondition, find);
+				case QueryElementType.GroupByClause     : return Find(((SqlQuery.GroupByClause)     element).Items,           find);
+				case QueryElementType.OrderByClause     : return Find(((SqlQuery.OrderByClause)     element).Items,           find);
+				case QueryElementType.OrderByItem       : return Find(((SqlQuery.OrderByItem)       element).Expression,      find);
+				case QueryElementType.Union             : return Find(((SqlQuery.Union)             element).SqlQuery,        find);
+				case QueryElementType.FuncLikePredicate : return Find(((SqlQuery.Predicate.FuncLike)element).Function,        find);
+
+				case QueryElementType.SqlBinaryExpression:
+					{
+						var bexpr = (SqlBinaryExpression)element;
+						return
+							Find(bexpr.Expr1, find) ??
+							Find(bexpr.Expr2, find);
+					}
+
+				case QueryElementType.SqlTable:
+					{
+						var table = (SqlTable)element;
+						return
+							Find(table.All,            find) ??
+							Find(table.Fields.Values,  find) ??
+							Find(table.Joins,          find) ??
+							Find(table.TableArguments, find);
+					}
+
+				case QueryElementType.TableSource:
+					{
+						var table = (SqlQuery.TableSource)element;
+						return
+							Find(table.Source, find) ??
+							Find(table.Joins,  find);
+					}
+
+				case QueryElementType.JoinedTable:
+					{
+						var join = (SqlQuery.JoinedTable)element;
+						return
+							Find(join.Table,     find) ??
+							Find(join.Condition, find);
+					}
+
+				case QueryElementType.ExprExprPredicate:
+					{
+						var p = (SqlQuery.Predicate.ExprExpr)element;
+						return
+							Find(p.Expr1, find) ??
+							Find(p.Expr2, find);
+					}
+
+				case QueryElementType.LikePredicate:
+					{
+						var p = (SqlQuery.Predicate.Like)element;
+						return
+							Find(p.Expr1,  find) ??
+							Find(p.Expr2,  find) ??
+							Find(p.Escape, find);
+					}
+
+				case QueryElementType.BetweenPredicate:
+					{
+						var p = (SqlQuery.Predicate.Between)element;
+						return
+							Find(p.Expr1, find) ??
+							Find(p.Expr2, find) ??
+							Find(p.Expr3, find);
+					}
+
+				case QueryElementType.InSubQueryPredicate:
+					{
+						var p = (SqlQuery.Predicate.InSubQuery)element;
+						return
+							Find(p.Expr1,    find) ??
+							Find(p.SubQuery, find);
+					}
+
+				case QueryElementType.InListPredicate:
+					{
+						var p = (SqlQuery.Predicate.InList)element;
+						return
+							Find(p.Expr1,  find) ??
+							Find(p.Values, find);
+					}
+
+				case QueryElementType.SetExpression:
+					{
+						var s = (SqlQuery.SetExpression)element;
+						return
+							Find(s.Column,     find) ??
+							Find(s.Expression, find);
+					}
+
+				case QueryElementType.InsertClause:
+					{
+						var sc = (SqlQuery.InsertClause)element;
+						return
+							Find(sc.Into,  find) ??
+							Find(sc.Items, find);
+					}
+
+				case QueryElementType.UpdateClause:
+					{
+						var sc = (SqlQuery.UpdateClause)element;
+						return
+							Find(sc.Table, find) ??
+							Find(sc.Items, find) ??
+							Find(sc.Keys,  find);
+					}
+
+				case QueryElementType.DeleteClause:
+					{
+						var sc = (SqlQuery.DeleteClause)element;
+						return Find(sc.Table, find);
+					}
+
+				case QueryElementType.SelectClause:
+					{
+						var sc = (SqlQuery.SelectClause)element;
+						return
+							Find(sc.TakeValue, find) ??
+							Find(sc.SkipValue, find) ??
+							Find(sc.Columns,   find);
+					}
+
+				case QueryElementType.SqlQuery:
+					{
+						var q = (SqlQuery)element;
+						return
+							Find(q.Select,  find) ??
+							(q.IsInsert ? Find(q.Insert, find) : null) ??
+							(q.IsUpdate ? Find(q.Update, find) : null) ??
+							Find(q.From,    find) ??
+							Find(q.Where,   find) ??
+							Find(q.GroupBy, find) ??
+							Find(q.Having,  find) ??
+							Find(q.OrderBy, find) ??
+							(q.HasUnion ? Find(q.Unions, find) : null);
+					}
+			}
+
+			return null;
+		}
+
+		#endregion
+
+		#region Convert
+
+		public T Convert<T>(T element, ConvertFunc action)
+			where T : class, IQueryElement
+		{
+			_visitedElements.Clear();
+			return (T)ConvertInternal(element, action) ?? element;
+		}
+
+		IQueryElement ConvertInternal(IQueryElement element, ConvertFunc action)
+		{
+			if (element == null)
+				return null;
+
+			IQueryElement newElement;
+
+			if (_visitedElements.TryGetValue(element, out newElement))
+				return newElement;
+
+			switch (element.ElementType)
+			{
+				case QueryElementType.SqlFunction:
+					{
+						var func  = (SqlFunction)element;
+						var parms = Convert(func.Parameters, action);
+
+						if (parms != null && !ReferenceEquals(parms, func.Parameters))
+							newElement = new SqlFunction(func.SystemType, func.Name, func.Precedence, parms);
+
+						break;
+					}
+
+				case QueryElementType.SqlExpression:
+					{
+						var expr      = (SqlExpression)element;
+						var parameter = Convert(expr.Parameters, action);
+
+						if (parameter != null && !ReferenceEquals(parameter, expr.Parameters))
+							newElement = new SqlExpression(expr.SystemType, expr.Expr, expr.Precedence, parameter);
+
+						break;
+					}
+
+				case QueryElementType.SqlBinaryExpression:
+					{
+						var bexpr = (SqlBinaryExpression)element;
+						var expr1 = (ISqlExpression)ConvertInternal(bexpr.Expr1, action);
+						var expr2 = (ISqlExpression)ConvertInternal(bexpr.Expr2, action);
+
+						if (expr1 != null && !ReferenceEquals(expr1, bexpr.Expr1) ||
+							expr2 != null && !ReferenceEquals(expr2, bexpr.Expr2))
+							newElement = new SqlBinaryExpression(bexpr.SystemType, expr1 ?? bexpr.Expr1, bexpr.Operation, expr2 ?? bexpr.Expr2, bexpr.Precedence);
+
+						break;
+					}
+
+				case QueryElementType.SqlTable:
+					{
+						var table   = (SqlTable)element;
+						var fields1 = ToArray(table.Fields);
+						var fields2 = Convert(fields1,     action, f => new SqlField(f));
+						var joins   = Convert(table.Joins, action, j => j.Clone());
+						var targs   = table.TableArguments == null ? null : Convert(table.TableArguments, action);
+
+						var fe = fields2 == null || ReferenceEquals(fields1,     fields2);
+						var je = joins   == null || ReferenceEquals(table.Joins, joins);
+						var ta = ReferenceEquals(table.TableArguments, targs);
+
+						if (!fe || !je || !ta)
+						{
+							if (fe)
+							{
+								fields2 = fields1;
+
+								for (var i = 0; i < fields2.Length; i++)
+								{
+									var field = fields2[i];
+
+									fields2[i] = new SqlField(field);
+
+									_visitedElements[field] = fields2[i];
+								}
+							}
+
+							newElement = new SqlTable(table, fields2, joins ?? table.Joins, targs ?? table.TableArguments);
+
+							_visitedElements[((SqlTable)newElement).All] = table.All;
+						}
+
+						break;
+					}
+
+				case QueryElementType.Join:
+					{
+						var join = (Join)element;
+						var ons  = Convert(join.JoinOns, action);
+
+						if (ons != null && !ReferenceEquals(join.JoinOns, ons))
+							newElement = new Join(join.TableName, join.Alias, ons);
+
+						break;
+					}
+
+				case QueryElementType.Column:
+					{
+						var col  = (SqlQuery.Column)element;
+						var expr = (ISqlExpression)ConvertInternal(col.Expression, action);
+
+						IQueryElement parent;
+						_visitedElements.TryGetValue(col.Parent, out parent);
+
+						if (parent != null || expr != null && !ReferenceEquals(expr, col.Expression))
+							newElement = new SqlQuery.Column(parent == null ? col.Parent : (SqlQuery)parent, expr ?? col.Expression, col._alias);
+
+						break;
+					}
+
+				case QueryElementType.TableSource:
+					{
+						var table  = (SqlQuery.TableSource)element;
+						var source = (ISqlTableSource)ConvertInternal(table.Source, action);
+						var joins  = Convert(table.Joins, action);
+
+						if (source != null && !ReferenceEquals(source, table.Source) ||
+							joins  != null && !ReferenceEquals(table.Joins, joins))
+							newElement = new SqlQuery.TableSource(source ?? table.Source, table._alias, joins ?? table.Joins);
+
+						break;
+					}
+
+				case QueryElementType.JoinedTable:
+					{
+						var join  = (SqlQuery.JoinedTable)element;
+						var table = (SqlQuery.TableSource)    ConvertInternal(join.Table,     action);
+						var cond  = (SqlQuery.SearchCondition)ConvertInternal(join.Condition, action);
+
+						if (table != null && !ReferenceEquals(table, join.Table) ||
+							cond  != null && !ReferenceEquals(cond,  join.Condition))
+							newElement = new SqlQuery.JoinedTable(join.JoinType, table ?? join.Table, join.IsWeak, cond ?? join.Condition);
+
+						break;
+					}
+
+				case QueryElementType.SearchCondition:
+					{
+						var sc    = (SqlQuery.SearchCondition)element;
+						var conds = Convert(sc.Conditions, action);
+
+						if (conds != null && !ReferenceEquals(sc.Conditions, conds))
+							newElement = new SqlQuery.SearchCondition(conds);
+
+						break;
+					}
+
+				case QueryElementType.Condition:
+					{
+						var c = (SqlQuery.Condition)element;
+						var p = (ISqlPredicate)ConvertInternal(c.Predicate, action);
+
+						if (p != null && !ReferenceEquals(c.Predicate, p))
+							newElement = new SqlQuery.Condition(c.IsNot, p, c.IsOr);
+
+						break;
+					}
+
+				case QueryElementType.ExprPredicate:
+					{
+						var p = (SqlQuery.Predicate.Expr)element;
+						var e = (ISqlExpression)ConvertInternal(p.Expr1, action);
+
+						if (e != null && !ReferenceEquals(p.Expr1, e))
+							newElement = new SqlQuery.Predicate.Expr(e, p.Precedence);
+
+						break;
+					}
+
+				case QueryElementType.NotExprPredicate:
+					{
+						var p = (SqlQuery.Predicate.NotExpr)element;
+						var e = (ISqlExpression)ConvertInternal(p.Expr1, action);
+
+						if (e != null && !ReferenceEquals(p.Expr1, e))
+							newElement = new SqlQuery.Predicate.NotExpr(e, p.IsNot, p.Precedence);
+
+						break;
+					}
+
+				case QueryElementType.ExprExprPredicate:
+					{
+						var p  = (SqlQuery.Predicate.ExprExpr)element;
+						var e1 = (ISqlExpression)ConvertInternal(p.Expr1, action);
+						var e2 = (ISqlExpression)ConvertInternal(p.Expr2, action);
+
+						if (e1 != null && !ReferenceEquals(p.Expr1, e1) || e2 != null && !ReferenceEquals(p.Expr2, e2))
+							newElement = new SqlQuery.Predicate.ExprExpr(e1 ?? p.Expr1, p.Operator, e2 ?? p.Expr2);
+
+						break;
+					}
+
+				case QueryElementType.LikePredicate:
+					{
+						var p  = (SqlQuery.Predicate.Like)element;
+						var e1 = (ISqlExpression)ConvertInternal(p.Expr1,  action);
+						var e2 = (ISqlExpression)ConvertInternal(p.Expr2,  action);
+						var es = (ISqlExpression)ConvertInternal(p.Escape, action);
+
+						if (e1 != null && !ReferenceEquals(p.Expr1, e1) ||
+							e2 != null && !ReferenceEquals(p.Expr2, e2) ||
+							es != null && !ReferenceEquals(p.Escape, es))
+							newElement = new SqlQuery.Predicate.Like(e1 ?? p.Expr1, p.IsNot, e2 ?? p.Expr2, es ?? p.Escape);
+
+						break;
+					}
+
+				case QueryElementType.BetweenPredicate:
+					{
+						var p = (SqlQuery.Predicate.Between)element;
+						var e1 = (ISqlExpression)ConvertInternal(p.Expr1, action);
+						var e2 = (ISqlExpression)ConvertInternal(p.Expr2, action);
+						var e3 = (ISqlExpression)ConvertInternal(p.Expr3, action);
+
+						if (e1 != null && !ReferenceEquals(p.Expr1, e1) ||
+							e2 != null && !ReferenceEquals(p.Expr2, e2) ||
+							e3 != null && !ReferenceEquals(p.Expr3, e3))
+							newElement = new SqlQuery.Predicate.Between(e1 ?? p.Expr1, p.IsNot, e2 ?? p.Expr2, e3 ?? p.Expr3);
+
+						break;
+					}
+
+				case QueryElementType.IsNullPredicate:
+					{
+						var p = (SqlQuery.Predicate.IsNull)element;
+						var e = (ISqlExpression)ConvertInternal(p.Expr1, action);
+
+						if (e != null && !ReferenceEquals(p.Expr1, e))
+							newElement = new SqlQuery.Predicate.IsNull(e, p.IsNot);
+
+						break;
+					}
+
+				case QueryElementType.InSubQueryPredicate:
+					{
+						var p = (SqlQuery.Predicate.InSubQuery)element;
+						var e = (ISqlExpression)ConvertInternal(p.Expr1,    action);
+						var q = (SqlQuery)ConvertInternal(p.SubQuery, action);
+
+						if (e != null && !ReferenceEquals(p.Expr1, e) || q != null && !ReferenceEquals(p.SubQuery, q))
+							newElement = new SqlQuery.Predicate.InSubQuery(e ?? p.Expr1, p.IsNot, q ?? p.SubQuery);
+
+						break;
+					}
+
+				case QueryElementType.InListPredicate:
+					{
+						var p = (SqlQuery.Predicate.InList)element;
+						var e = (ISqlExpression)ConvertInternal(p.Expr1,    action);
+						var v = Convert(p.Values, action);
+
+						if (e != null && !ReferenceEquals(p.Expr1, e) || v != null && !ReferenceEquals(p.Values, v))
+							newElement = new SqlQuery.Predicate.InList(e ?? p.Expr1, p.IsNot, v ?? p.Values);
+
+						break;
+					}
+
+				case QueryElementType.FuncLikePredicate:
+					{
+						var p = (SqlQuery.Predicate.FuncLike)element;
+						var f = (SqlFunction)ConvertInternal(p.Function, action);
+
+						if (f != null && !ReferenceEquals(p.Function, f))
+							newElement = new SqlQuery.Predicate.FuncLike(f);
+
+						break;
+					}
+
+				case QueryElementType.SetExpression:
+					{
+						var s = (SqlQuery.SetExpression)element;
+						var c = (ISqlExpression)ConvertInternal(s.Column,     action);
+						var e = (ISqlExpression)ConvertInternal(s.Expression, action);
+
+						if (c != null && !ReferenceEquals(s.Column, c) || e != null && !ReferenceEquals(s.Expression, e))
+							newElement = new SqlQuery.SetExpression(c ?? s.Column, e ?? s.Expression);
+
+						break;
+					}
+
+				case QueryElementType.InsertClause:
+					{
+						var s = (SqlQuery.InsertClause)element;
+						var t = s.Into != null ? (SqlTable)ConvertInternal(s.Into, action) : null;
+						var i = Convert(s.Items, action);
+
+						if (t != null && !ReferenceEquals(s.Into, t) || i != null && !ReferenceEquals(s.Items, i))
+						{
+							var sc = new SqlQuery.InsertClause { Into = t ?? s.Into };
+
+							sc.Items.AddRange(i ?? s.Items);
+							sc.WithIdentity = s.WithIdentity;
+
+							newElement = sc;
+						}
+
+						break;
+					}
+
+				case QueryElementType.UpdateClause:
+					{
+						var s = (SqlQuery.UpdateClause)element;
+						var t = s.Table != null ? (SqlTable)ConvertInternal(s.Table, action) : null;
+						var i = Convert(s.Items, action);
+						var k = Convert(s.Keys,  action);
+
+						if (t != null && !ReferenceEquals(s.Table, t) ||
+							i != null && !ReferenceEquals(s.Items, i) ||
+							k != null && !ReferenceEquals(s.Keys,  k))
+						{
+							var sc = new SqlQuery.UpdateClause { Table = t ?? s.Table };
+
+							sc.Items.AddRange(i ?? s.Items);
+							sc.Keys. AddRange(k ?? s.Keys);
+
+							newElement = sc;
+						}
+
+						break;
+					}
+
+				case QueryElementType.DeleteClause:
+					{
+						var s = (SqlQuery.DeleteClause)element;
+						var t = s.Table != null ? (SqlTable)ConvertInternal(s.Table, action) : null;
+
+						if (t != null && !ReferenceEquals(s.Table, t))
+						{
+							newElement = new SqlQuery.DeleteClause { Table = t ?? s.Table };
+						}
+
+						break;
+					}
+
+				case QueryElementType.SelectClause:
+					{
+						var sc   = (SqlQuery.SelectClause)element;
+						var cols = Convert(sc.Columns, action);
+						var take = (ISqlExpression)ConvertInternal(sc.TakeValue, action);
+						var skip = (ISqlExpression)ConvertInternal(sc.SkipValue, action);
+
+						IQueryElement parent;
+						_visitedElements.TryGetValue(sc.SqlQuery, out parent);
+
+						if (parent != null ||
+							cols != null && !ReferenceEquals(sc.Columns,   cols) ||
+							take != null && !ReferenceEquals(sc.TakeValue, take) ||
+							skip != null && !ReferenceEquals(sc.SkipValue, skip))
+						{
+							newElement = new SqlQuery.SelectClause(sc.IsDistinct, take ?? sc.TakeValue, skip ?? sc.SkipValue, cols ?? sc.Columns);
+							((SqlQuery.SelectClause)newElement).SetSqlQuery((SqlQuery)parent);
+						}
+
+						break;
+					}
+
+				case QueryElementType.FromClause:
+					{
+						var fc   = (SqlQuery.FromClause)element;
+						var ts = Convert(fc.Tables, action);
+
+						IQueryElement parent;
+						_visitedElements.TryGetValue(fc.SqlQuery, out parent);
+
+						if (parent != null || ts != null && !ReferenceEquals(fc.Tables, ts))
+						{
+							newElement = new SqlQuery.FromClause(ts ?? fc.Tables);
+							((SqlQuery.FromClause)newElement).SetSqlQuery((SqlQuery)parent);
+						}
+
+						break;
+					}
+
+				case QueryElementType.WhereClause:
+					{
+						var wc   = (SqlQuery.WhereClause)element;
+						var cond = (SqlQuery.SearchCondition)ConvertInternal(wc.SearchCondition, action);
+
+						IQueryElement parent;
+						_visitedElements.TryGetValue(wc.SqlQuery, out parent);
+
+						if (parent != null || cond != null && !ReferenceEquals(wc.SearchCondition, cond))
+						{
+							newElement = new SqlQuery.WhereClause(cond ?? wc.SearchCondition);
+							((SqlQuery.WhereClause)newElement).SetSqlQuery((SqlQuery)parent);
+						}
+
+						break;
+					}
+
+				case QueryElementType.GroupByClause:
+					{
+						var gc = (SqlQuery.GroupByClause)element;
+						var es = Convert(gc.Items, action);
+
+						IQueryElement parent;
+						_visitedElements.TryGetValue(gc.SqlQuery, out parent);
+
+						if (parent != null || es != null && !ReferenceEquals(gc.Items, es))
+						{
+							newElement = new SqlQuery.GroupByClause(es ?? gc.Items);
+							((SqlQuery.GroupByClause)newElement).SetSqlQuery((SqlQuery)parent);
+						}
+
+						break;
+					}
+
+				case QueryElementType.OrderByClause:
+					{
+						var oc = (SqlQuery.OrderByClause)element;
+						var es = Convert(oc.Items, action);
+
+						IQueryElement parent;
+						_visitedElements.TryGetValue(oc.SqlQuery, out parent);
+
+						if (parent != null || es != null && !ReferenceEquals(oc.Items, es))
+						{
+							newElement = new SqlQuery.OrderByClause(es ?? oc.Items);
+							((SqlQuery.OrderByClause)newElement).SetSqlQuery((SqlQuery)parent);
+						}
+
+						break;
+					}
+
+				case QueryElementType.OrderByItem:
+					{
+						var i = (SqlQuery.OrderByItem)element;
+						var e = (ISqlExpression)ConvertInternal(i.Expression, action);
+
+						if (e != null && !ReferenceEquals(i.Expression, e))
+							newElement = new SqlQuery.OrderByItem(e, i.IsDescending);
+
+						break;
+					}
+
+				case QueryElementType.Union:
+					{
+						var u = (SqlQuery.Union)element;
+						var q = (SqlQuery)ConvertInternal(u.SqlQuery, action);
+
+						if (q != null && !ReferenceEquals(u.SqlQuery, q))
+							newElement = new SqlQuery.Union(q, u.IsAll);
+
+						break;
+					}
+
+				case QueryElementType.SqlQuery:
+					{
+						var q = (SqlQuery)element;
+						IQueryElement parent = null;
+
+						var doConvert = q.ParentSql != null && !_visitedElements.TryGetValue(q.ParentSql, out parent);
+
+						if (!doConvert)
+						{
+							doConvert = null != Find(q, e =>
+							{
+								if (_visitedElements.ContainsKey(e) && _visitedElements[e] != e)
+									return true;
+
+								var ret = action(e);
+
+								if (ret != null && !ReferenceEquals(e, ret))
+								{
+									_visitedElements.Add(e, ret);
+									return true;
+								}
+
+								return false;
+							});
+						}
+
+						if (!doConvert)
+							break;
+
+						var nq = new SqlQuery { QueryType = q.QueryType };
+
+						_visitedElements.Add(q, nq);
+
+						var fc = (SqlQuery.FromClause)   ConvertInternal(q.From,    action) ?? q.From;
+						var sc = (SqlQuery.SelectClause) ConvertInternal(q.Select,  action) ?? q.Select;
+						var ic = q.IsInsert ? ((SqlQuery.InsertClause)ConvertInternal(q.Insert, action) ?? q.Insert) : null;
+						var uc = q.IsUpdate ? ((SqlQuery.UpdateClause)ConvertInternal(q.Update, action) ?? q.Update) : null;
+						var dc = q.IsDelete ? ((SqlQuery.DeleteClause)ConvertInternal(q.Delete, action) ?? q.Delete) : null;
+						var wc = (SqlQuery.WhereClause)  ConvertInternal(q.Where,   action) ?? q.Where;
+						var gc = (SqlQuery.GroupByClause)ConvertInternal(q.GroupBy, action) ?? q.GroupBy;
+						var hc = (SqlQuery.WhereClause)  ConvertInternal(q.Having,  action) ?? q.Having;
+						var oc = (SqlQuery.OrderByClause)ConvertInternal(q.OrderBy, action) ?? q.OrderBy;
+						var us = q.HasUnion ? Convert(q.Unions, action) : q.Unions;
+
+						var ps = new List<SqlParameter>(q.Parameters.Count);
+
+						foreach (var p in q.Parameters)
+						{
+							IQueryElement e;
+
+							if (_visitedElements.TryGetValue(p, out e))
+							{
+								if (e == null)
+									ps.Add(p);
+								else if (e is SqlParameter)
+									ps.Add((SqlParameter)e);
+							}
+						}
+
+						nq.Init(ic, uc, dc, sc, fc, wc, gc, hc, oc, us, (SqlQuery)parent, q.IsParameterDependent, ps);
+
+						_visitedElements[q] = action(nq) ?? nq;
+
+						return nq;
+					}
+			}
+
+			newElement = newElement == null ? action(element) : (action(newElement) ?? newElement);
+
+			_visitedElements.Add(element, newElement);
+
+			return newElement;
+		}
+
+		static TE[] ToArray<TK,TE>(IDictionary<TK,TE> dic)
+		{
+			var es = new TE[dic.Count];
+			var i  = 0;
+
+			foreach (var e in dic.Values)
+				es[i++] = e;
+
+			return es;
+		}
+
+		delegate T Clone<T>(T obj);
+
+		T[] Convert<T>(T[] arr, ConvertFunc action)
+			where T : class, IQueryElement
+		{
+			return Convert(arr, action, null);
+		}
+
+		T[] Convert<T>(T[] arr1, ConvertFunc action, Clone<T> clone)
+			where T : class, IQueryElement
+		{
+			T[] arr2 = null;
+
+			for (var i = 0; i < arr1.Length; i++)
+			{
+				var elem1 = arr1[i];
+				var elem2 = (T)ConvertInternal(elem1, action);
+
+				if (elem2 != null && !ReferenceEquals(elem1, elem2))
+				{
+					if (arr2 == null)
+					{
+						arr2 = new T[arr1.Length];
+
+						for (var j = 0; j < i; j++)
+							arr2[j] = clone == null ? arr1[j] : clone(arr1[j]);
+					}
+
+					arr2[i] = elem2;
+				}
+				else if (arr2 != null)
+					arr2[i] = clone == null ? elem1 : clone(elem1);
+			}
+
+			return arr2;
+		}
+
+		List<T> Convert<T>(List<T> list, ConvertFunc action)
+			where T : class, IQueryElement
+		{
+			return Convert(list, action, null);
+		}
+
+		List<T> Convert<T>(List<T> list1, ConvertFunc action, Clone<T> clone)
+			where T : class, IQueryElement
+		{
+			List<T> list2 = null;
+
+			for (var i = 0; i < list1.Count; i++)
+			{
+				var elem1 = list1[i];
+				var elem2 = (T)ConvertInternal(elem1, action);
+
+				if (elem2 != null && !ReferenceEquals(elem1, elem2))
+				{
+					if (list2 == null)
+					{
+						list2 = new List<T>(list1.Count);
+
+						for (var j = 0; j < i; j++)
+							list2.Add(clone == null ? list1[j] : clone(list1[j]));
+					}
+
+					list2.Add(elem2);
+				}
+				else if (list2 != null)
+					list2.Add(clone == null ? elem1 : clone(elem1));
+			}
+
+			return list2;
+		}
+
+		#endregion
+	}
+}