18 using System.Collections.Generic;
28 tablePlans =
new List<TablePlan>();
32 public bool HasJoin {
get;
private set; }
36 if (tablePlans.Count != 1)
37 throw new InvalidOperationException(
"The planner has more than one table.");
44 int sz = tablePlans.Count;
45 for (
int i = 0; i < sz; ++i) {
46 if (tablePlans[i] == plan)
65 return new TablePlan(plan, newVarList, newUniqueList);
70 tablePlans.Remove(left);
71 tablePlans.Remove(right);
74 var newPlan = ConcatPlans(left, right, mergePlan);
75 newPlan.MergeJoin(left, right);
85 if (tablePlans.Count == 1)
88 foreach (var source
in tablePlans) {
89 if (source.ContainsColumn(reference))
93 throw new ArgumentException(
"Unable to find table with variable reference: " + reference);
97 if (columnNames.Count == 0)
101 foreach (var columnName
in columnNames) {
102 var plan = FindPlan(columnName);
103 if (pivotPlan == null) {
105 }
else if (plan != pivotPlan) {
114 foreach (var plan
in tablePlans) {
115 plan.SetCachePoint();
121 var touchedPlans =
new List<TablePlan>();
123 foreach (var name
in columnNames) {
124 var plan = FindPlan(name);
126 if (!touchedPlans.Contains(plan))
127 touchedPlans.Add(plan);
130 return JoinToSingle(touchedPlans);
134 int sz = tablePlans.Count;
136 return tablePlans[0];
139 return JoinToSingle(tablePlans);
171 }
else if (plan1.
LeftPlan == plan2) {
180 throw new InvalidOperationException(
"Plans can not be naturally join because " +
181 "the left/right join plans clash.");
187 return MergePlans(plan1, plan2, node1);
197 }
else if (joinType ==
JoinType.Right) {
201 }
else if (joinType ==
JoinType.Inner) {
205 throw new InvalidOperationException(String.Format(
"Join type ({0}) is not supported.", joinType));
210 planner.AddPlan(leftPlan.
Clone());
211 planner.AddPlan(rightPlan.
Clone());
214 var node = planner.LogicalEvaluate(onExpr);
221 return MergePlans(plan1, plan2, node);
226 if (allPlans.Count == 0)
229 if (allPlans.Count == 1)
234 var workingPlanList =
new List<TablePlan>(allPlans);
237 while (workingPlanList.Count > 1) {
238 var leftPlan = workingPlanList[0];
239 var rightPlan = workingPlanList[1];
243 int status = AssertBeNaturalJoin(leftPlan, rightPlan);
246 var newPlan = NaturallyJoinPlans(leftPlan, rightPlan);
249 workingPlanList.Remove(leftPlan);
250 workingPlanList.Remove(rightPlan);
251 workingPlanList.Insert(0, newPlan);
252 }
else if (status == 1) {
255 var newPlan = NaturallyJoinPlans(leftPlan, leftPlan.RightPlan);
256 workingPlanList.Remove(leftPlan);
257 workingPlanList.Remove(leftPlan.RightPlan);
258 workingPlanList.Insert(0, newPlan);
259 }
else if (status == 2) {
262 var newPlan = NaturallyJoinPlans(leftPlan, leftPlan.LeftPlan);
263 workingPlanList.Remove(leftPlan);
264 workingPlanList.Remove(leftPlan.LeftPlan);
265 workingPlanList.Insert(0, newPlan);
267 throw new InvalidOperationException(String.Format(
"Natural join assessed status {0} is unknown.", status));
272 return workingPlanList[0];
276 if (expression == null) {
279 return SinglePlan.Plan;
283 PlanExpression(expression);
289 return SinglePlan.Plan;
296 foreach (var existingPlan
in list) {
297 if (existingPlan.TablePlan == table &&
298 (columnName == null || existingPlan.ColumnName.Equals(columnName))) {
300 existingPlan.SetSource(columnName,
SqlExpression.
And(existingPlan.Expression, exp));
312 int sz = tablePlans.Count;
313 for (
int i = 0; i < sz; ++i) {
314 copy.tablePlans.Add(tablePlans[i].Clone());
318 for (
int i = 0; i < sz; ++i) {
319 var src = tablePlans[i];
320 var mod = copy.tablePlans[i];
323 if (src.LeftPlan != null) {
324 int n = IndexOfPlan(src.LeftPlan);
325 mod.LeftJoin(copy.tablePlans[n], src.LeftJoinType, src.LeftOnExpression);
329 if (src.RightPlan != null) {
330 int n = IndexOfPlan(src.RightPlan);
331 mod.RightJoin(copy.tablePlans[n], src.RightJoinType, src.RightOnExpression);
339 int sz = tablePlans.Count;
344 var workingPlanList =
new List<TablePlan>(tablePlans);
346 var plan1 = workingPlanList[0];
347 for (
int i = 1; i < sz; ++i) {
348 var plan2 = workingPlanList[i];
350 if (plan1.RightPlan == plan2) {
351 plan1 = NaturallyJoinPlans(plan1, plan2);
359 var subLogicExpressions =
new List<SqlBinaryExpression>();
361 var subQueryExpressions =
new List<SqlBinaryExpression>();
363 var constants =
new List<SqlExpression>();
365 var patternExpressions =
new List<SqlBinaryExpression>();
368 var singleVars =
new List<SqlBinaryExpression>();
370 var multiVars =
new List<SqlBinaryExpression>();
372 foreach (var expression
in expressions) {
380 exp = (SqlBinaryExpression) expression;
384 subLogicExpressions.
Add(exp);
385 }
else if (exp.HasSubQuery()) {
386 subQueryExpressions.
Add(exp);
388 patternExpressions.
Add(exp);
391 var columnNames = exp.DiscoverReferences().ToList();
392 if (columnNames.Count == 0) {
395 }
else if (columnNames.Count == 1) {
398 }
else if (columnNames.Count > 1) {
403 throw new InvalidOperationException(
"Invalid number of column names");
410 var evaluateOrder =
new List<ExpressionPlan>();
414 EvaluateConstants(constants, evaluateOrder);
419 EvaluateSingles(singleVars, evaluateOrder);
424 EvaluatePatterns(patternExpressions, evaluateOrder);
428 EvaluateSubQueries(subQueryExpressions, evaluateOrder);
432 EvaluateMultiples(multiVars, evaluateOrder);
436 EvaluateSubLogic(subLogicExpressions, evaluateOrder);
438 evaluateOrder.Sort();
446 private void EvaluateSubLogic(List<SqlBinaryExpression> list, List<ExpressionPlan> plans) {
447 foreach (var expression
in list) {
448 var orExprs =
new[] {expression.Left, expression.Right};
462 foreach (var orExpr
in orExprs) {
463 var vars = orExpr.DiscoverReferences().ToArray();
465 bool breakRule =
false;
470 var ts = FindCommonPlan(vars);
471 bool orAfterJoins =
false;
475 }
else if (common == null) {
477 }
else if (common != ts) {
507 foreach (var expression
in list) {
509 var lhsVar = expression.Left.AsReferenceName();
510 var rhsVar = expression.Right.AsReferenceName();
520 if (lhsVar == null && rhsVar == null) {
523 }
else if (lhsVar != null && rhsVar != null) {
535 foreach (var expression
in list) {
538 var op = expression.ExpressionType;
539 if (op.IsSubQuery()) {
544 var leftColumn = expression.Left.AsReferenceName();
545 if (leftColumn == null) {
550 if (rightPlan == null)
554 var cv = rightPlan.DiscoverQueryReferences(1);
555 exhaustive = cv.Count != 0;
563 var columnNames = expression.DiscoverReferences().ToList();
567 var allCorrelated = expression.DiscoverQueryReferences(ref level);
568 int sz = allCorrelated.Count;
572 if (!columnNames.Any() && sz == 0) {
575 columnNames.AddRange(allCorrelated.Select(cv => cv.Name));
588 private void EvaluatePatterns(List<SqlBinaryExpression> list, List<ExpressionPlan> plans) {
589 foreach (var expression
in list) {
592 var leftColumnName = expression.Left.AsReferenceName();
594 if (expression.IsConstant()) {
596 }
else if (leftColumnName != null &&
597 expression.Right.IsConstant()) {
610 private void EvaluateSingles(List<SqlBinaryExpression> list, List<ExpressionPlan> plans) {
612 var simplePlanList =
new List<SingleColumnPlan>();
614 var complexPlanList =
new List<SingleColumnPlan>();
616 foreach (var expression
in list) {
620 SqlExpression left = expression.Left, right = expression.Right;
622 if (op.IsSubQuery()) {
623 singleVar = expression.Left.AsReferenceName();
625 if (singleVar != null) {
628 singleVar = expression.Left.DiscoverReferences().First();
632 singleVar = expression.Left.DiscoverReferences().FirstOrDefault();
633 if (singleVar == null) {
639 singleVar = left.DiscoverReferences().First();
642 var tableSource = FindPlan(singleVar);
645 var v = left.AsReferenceName();
647 AddSingleColumnPlan(simplePlanList, tableSource, v, singleVar,
new []{left, right}, op);
650 AddSingleColumnPlan(complexPlanList, tableSource, null, singleVar,
new []{left, right}, op);
672 var exps =
new[]{binary.Left, binary.Right};
681 var leftPlanner = Clone();
682 var rightPlanner = Clone();
685 leftPlanner.PlanExpression(exps[0]);
686 rightPlanner.PlanExpression(exps[1]);
692 int leftSz = leftPlanner.tablePlans.Count;
693 int rightSz = rightPlanner.tablePlans.Count;
694 if (leftSz != rightSz || leftPlanner.HasJoin || rightPlanner.HasJoin) {
696 leftPlanner.NaturalJoinAll();
697 rightPlanner.NaturalJoinAll();
701 var leftTableList = leftPlanner.tablePlans;
702 var rightTableList = rightPlanner.tablePlans;
703 int sz = leftTableList.Count;
707 var leftJoinList =
new List<TablePlan>();
708 var rightJoinList =
new List<TablePlan>();
709 for (
int i = 0; i < sz; ++i) {
710 var leftPlan = leftTableList[i];
711 var rightPlan = rightTableList[i];
712 if (leftPlan.IsUpdated || rightPlan.IsUpdated) {
713 leftJoinList.Add(leftPlan);
714 rightJoinList.Add(rightPlan);
719 leftPlanner.JoinToSingle(leftJoinList);
720 rightPlanner.JoinToSingle(rightJoinList);
723 leftTableList = leftPlanner.tablePlans;
724 rightTableList = rightPlanner.tablePlans;
725 sz = leftTableList.Count;
727 var newTableList =
new List<TablePlan>(sz);
729 for (
int i = 0; i < sz; ++i) {
730 var leftPlan = leftTableList[i];
731 var rightPlan = rightTableList[i];
736 if (leftPlan.IsUpdated || rightPlan.IsUpdated) {
745 leftPlan.UpdatePlan(node);
756 newTableList.Add(newPlan);
761 tablePlans = newTableList;
763 PlanExpressionList(
new[]{binary.Left, binary.Right});
765 throw new InvalidOperationException();
768 PlanExpressionList(
new []{expression});
773 tablePlans.Add(tablePlan);
779 var uniqueNames =
new[] {tableSource.
UniqueName};
780 AddPlan(
new TablePlan(plan, columns, uniqueNames));
784 var planLeft = tablePlans[betweenIndex];
785 var planRight = tablePlans[betweenIndex + 1];
786 planLeft.RightJoin(planRight, joinType, onExpression);
787 planRight.LeftJoin(planLeft, joinType, onExpression);
794 return LogicalEvaluate(searchExpression);
797 #region SingleColumnPlan
802 ColumnName = columnName;
803 UniqueName = uniqueName;
804 Expression = expression;
816 ColumnName = columnName;
817 Expression = expression;
823 #region ExpressionPlan
827 OptimizeFactor = optimizeFactor;
830 public float OptimizeFactor {
get;
private set; }
832 public abstract void AddToPlanTree();
838 int IComparable.CompareTo(
object obj) {
840 return CompareTo(other);
854 this.planner = planner;
855 this.expression = expression;
859 foreach (var tablePlan
in planner.tablePlans) {
867 #region SimpleSelectPlan
877 this.planner = planner;
878 this.columnName = columnName;
880 this.expression = expression;
884 var tablePlan = planner.FindPlan(columnName);
885 tablePlan.UpdatePlan(
new SimpleSelectNode(tablePlan.Plan, columnName, op, expression));
891 #region SimpleSinglePlan
900 this.planner = planner;
901 this.columnName = columnName;
902 this.expression = expression;
906 var tablePlan = planner.FindPlan(columnName);
913 #region ComplexSinglePlan
922 this.planner = planner;
923 this.columnName = columnName;
924 this.expression = expression;
928 var tablePlan = planner.FindPlan(columnName);
935 #region SimplePatternPlan
944 this.planner = planner;
945 this.columnName = columnName;
946 this.expression = expression;
950 var tablePlan = planner.FindPlan(columnName);
957 #region ExhaustiveSelectPlan
965 this.planner = planner;
966 this.expression = expression;
970 var columnNames = expression.DiscoverReferences();
971 var tablePlan = planner.JoinPlansForColumns(columnNames);
978 #region ExhaustiveSubQueryPlan
987 this.planner = planner;
988 this.columnNames = columnNames;
989 this.expression = expression;
993 var tablePlan = planner.JoinPlansForColumns(columnNames);
1000 #region SimpleSubQueryPlan
1008 this.planner = planner;
1009 this.expression = expression;
1013 var op = expression.ExpressionType;
1014 var columnName = expression.Left.AsReferenceName();
1015 var queryPlan = expression.Right.AsQueryPlan();
1017 var tablePlan = planner.FindPlan(columnName);
1018 var leftPlan = tablePlan.Plan;
1026 #region ExhaustiveJoinPlan
1034 this.planner = planner;
1035 this.expression = expression;
1039 var columnNames = expression.DiscoverReferences();
1040 var tablePlan = planner.JoinPlansForColumns(columnNames);
1047 #region StandardJoinPlan
1054 : base(optimizeFactor) {
1055 this.planner = planner;
1056 this.expression = expression;
1060 var op = expression.ExpressionType;
1061 var lhsVar = expression.Left.AsReferenceName();
1062 var rhsVar = expression.Right.AsReferenceName();
1063 var lhsVars = expression.Left.DiscoverReferences();
1064 var rhsVars = expression.Right.DiscoverReferences();
1066 var lhsPlan = planner.JoinPlansForColumns(lhsVars);
1067 var rhsPlan = planner.JoinPlansForColumns(rhsVars);
1069 if (lhsPlan != rhsPlan) {
1073 if (lhsVar != null || rhsVar != null) {
1077 if (lhsVar == null) {
1079 joinNode =
new JoinNode(rhsPlan.Plan, lhsPlan.Plan, rhsVar, op.Reverse(), expression.Left);
1080 planner.MergePlans(rhsPlan, lhsPlan, joinNode);
1083 joinNode =
new JoinNode(lhsPlan.Plan, rhsPlan.Plan, lhsVar, op, expression.Right);
1084 planner.MergePlans(lhsPlan, rhsPlan, joinNode);
1098 var columnNames = expression.DiscoverReferences();
1099 var tablePlan = planner.JoinPlansForColumns(columnNames);
1106 #region SubLogicPlan
1113 : base(optimizeFactor) {
1114 this.planner = planner;
1115 this.expression = expression;
1119 planner.PlanExpression(expression);
SqlExpression LeftOnExpression
void AddPlan(TablePlan tablePlan)
override SqlExpressionType ExpressionType
ExhaustiveJoinPlan(QueryTablePlanner planner, SqlExpression expression)
TablePlan FindPlan(ObjectName reference)
TablePlan NaturallyJoinPlans(TablePlan plan1, TablePlan plan2)
ComplexSinglePlan(QueryTablePlanner planner, ObjectName columnName, SqlExpression expression)
readonly QueryTablePlanner planner
ObjectName[] ColumnNames
Returns an array of ObjectName objects that references each column available in the table set item in...
readonly SqlExpression expression
int CompareTo(IExpressionPlan other)
override void AddToPlanTree()
SingleColumnPlan(TablePlan tablePlan, ObjectName columnName, ObjectName uniqueName, SqlExpression expression)
static SqlBinaryExpression And(SqlExpression left, SqlExpression right)
IQueryPlanNode LogicalEvaluate(SqlExpression expression)
string[] UniqueNames
The list of unique key names of the tables in this plan.
readonly SqlExpression expression
JoinType
Enumerates the kind of group join in a selection query.
The node for evaluating an expression that contains entirely constant values (no variables).
A query to the database to select data from a set of tables and columns.
ExhaustiveSubQueryPlan(QueryTablePlanner planner, ObjectName[] columnNames, SqlExpression expression)
A single table resource item in a query which handles the behaviour of resolving references to column...
readonly QueryTablePlanner planner
void EvaluateConstants(List< SqlExpression > list, List< ExpressionPlan > plans)
void EvaluatePatterns(List< SqlBinaryExpression > list, List< ExpressionPlan > plans)
override void AddToPlanTree()
TablePlan NaturalJoinAll()
readonly SqlExpression expression
static int AssertBeNaturalJoin(TablePlan plan1, TablePlan plan2)
SimplePatternPlan(QueryTablePlanner planner, ObjectName columnName, SqlExpression expression)
readonly SqlExpression expression
static SqlBinaryExpression Equal(SqlExpression left, SqlExpression right)
Describes the name of an object within a database.
ExhaustiveSelectPlan(QueryTablePlanner planner, SqlExpression expression)
readonly QueryTablePlanner planner
TablePlan FindCommonPlan(IList< ObjectName > columnNames)
static void AddSingleColumnPlan(IList< SingleColumnPlan > list, TablePlan table, ObjectName columnName, ObjectName uniqueName, SqlExpression[] expParts, SqlExpressionType op)
ObjectName[] ColumnNames
The list of fully qualified column objects that are accessible within this plan.
readonly QueryTablePlanner planner
string UniqueName
Gets a unique name given to this table source.
override void AddToPlanTree()
SqlExpressionType
All the possible type of SqlExpression supported
The node for performing a simple indexed query on a single column of the child node.
readonly SqlExpressionType op
ConstantPlan(QueryTablePlanner planner, SqlExpression expression)
A node element of a query plan tree. /summary>
readonly QueryTablePlanner planner
The node for performing a simple select operation on a table.
A marker node that takes the result of a child and marks it as a name that can later be retrieved...
override void AddToPlanTree()
void EvaluateSubQueries(List< SqlBinaryExpression > list, List< ExpressionPlan > plans)
readonly QueryTablePlanner planner
void UpdatePlan(IQueryPlanNode queryPlan)
readonly SqlBinaryExpression expression
static SqlBinaryExpression Add(SqlExpression left, SqlExpression right)
void EvaluateSubLogic(List< SqlBinaryExpression > list, List< ExpressionPlan > plans)
readonly QueryTablePlanner planner
SimpleSelectPlan(QueryTablePlanner planner, ObjectName columnName, SqlExpressionType op, SqlExpression expression)
static TablePlan ConcatPlans(TablePlan left, TablePlan right, IQueryPlanNode plan)
SimpleSubQueryPlan(QueryTablePlanner planner, SqlBinaryExpression expression)
IQueryPlanNode Plan
Returns the plan for this table source.
The node for performing a exhaustive select operation on the child node.
readonly QueryTablePlanner planner
A branch node for a left outer join.
void SetSource(ObjectName columnName, SqlExpression expression)
override void AddToPlanTree()
override void AddToPlanTree()
SqlExpression RightOnExpression
TablePlan MergePlans(TablePlan left, TablePlan right, IQueryPlanNode mergePlan)
readonly SqlExpression expression
TablePlan JoinToSingle(IList< TablePlan > allPlans)
SimpleSinglePlan(QueryTablePlanner planner, ObjectName columnName, SqlExpression expression)
override void AddToPlanTree()
override void AddToPlanTree()
readonly SqlExpression expression
ExpressionPlan(float optimizeFactor)
IQueryPlanNode PlanSearchExpression(SqlExpression searchExpression)
List< TablePlan > tablePlans
override void AddToPlanTree()
A branch node for naturally joining two tables together.
StandardJoinPlan(QueryTablePlanner planner, SqlBinaryExpression expression, float optimizeFactor)
void PlanExpression(SqlExpression expression)
readonly QueryTablePlanner planner
readonly SqlExpression expression
readonly SqlExpression expression
readonly ObjectName columnName
void PlanExpressionList(IEnumerable< SqlExpression > expressions)
TablePlan JoinPlansForColumns(IEnumerable< ObjectName > columnNames)
static SqlBinaryExpression Binary(SqlExpression left, SqlExpressionType expressionType, SqlExpression right)
override void AddToPlanTree()
Defines the base class for instances that represent SQL expression tree nodes.
static SqlConstantExpression Constant(object value)
abstract void AddToPlanTree()
abstract SqlExpressionType ExpressionType
Gets the type code of this SQL expression.
SubLogicPlan(QueryTablePlanner planner, SqlExpression expression, float optimizeFactor)
readonly ObjectName columnName
void EvaluateSingles(List< SqlBinaryExpression > list, List< ExpressionPlan > plans)
QueryTablePlanner Clone()
void EvaluateMultiples(List< SqlBinaryExpression > list, List< ExpressionPlan > plans)
readonly SqlExpression expression
void AddPlan(IQueryPlanNode plan, IFromTableSource tableSource)
readonly ObjectName columnName
readonly QueryTablePlanner planner
override void AddToPlanTree()
readonly ObjectName[] columnNames
readonly QueryTablePlanner planner
readonly SqlBinaryExpression expression
void JoinAt(int betweenIndex, JoinType joinType, SqlExpression onExpression)
int IndexOfPlan(TablePlan plan)
readonly ObjectName columnName