DeveelDB  20151217
complete SQL database system, primarly developed for .NET/Mono frameworks
QueryPlanner.cs
Go to the documentation of this file.
1 //
2 // Copyright 2010-2015 Deveel
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 using System;
18 using System.Collections.Generic;
19 using System.Linq;
20 
21 using Deveel.Data;
23 using Deveel.Data.Sql.Objects;
24 using Deveel.Data.Types;
25 
26 namespace Deveel.Data.Sql.Query {
27  public sealed class QueryPlanner : IQueryPlanner {
28  private static readonly ObjectName FunctionTableName = new ObjectName("FUNCTIONTABLE");
29 
31  // first check the expression is not null
32  if (expression == null)
33  return null;
34 
35  // This is used to prepare sub-queries and qualify variables in a
36  // search expression such as WHERE or HAVING.
37 
38  // Prepare the sub-queries first
39  expression = expression.Prepare(new QueryExpressionPreparer(this, queryFrom, context));
40 
41  // Then qualify all the variables. Note that this will not qualify
42  // variables in the sub-queries.
43  expression = expression.Prepare(queryFrom.ExpressionPreparer);
44 
45  return expression;
46  }
47 
49  var selectColumns = new QuerySelectColumns(queryFrom);
50 
51  foreach (var column in expression.SelectColumns) {
52  // Is this a glob? (eg. Part.* )
53  if (column.IsGlob) {
54  // Find the columns globbed and add to the 'selectedColumns' result.
55  if (column.IsAll) {
56  selectColumns.SelectAllColumnsFromAllSources();
57  } else {
58  // Otherwise the glob must be of the form '[table name].*'
59  selectColumns.SelectAllColumnsFromSource(column.TableName);
60  }
61  } else {
62  // Otherwise must be a standard column reference.
63  selectColumns.SelectSingleColumn(column);
64  }
65  }
66 
67  return selectColumns;
68  }
69 
70 
71  private static int MakeupFunctions(PreparedQuerySelectColumns columnSet, IList<SqlExpression> aggregateFunctions, out SqlExpression[] defFunList, out string[] defFunNames) {
72  // Make up the functions list,
73  var functionsList = columnSet.FunctionColumns.ToList();
74  int fsz = functionsList.Count;
75  var completeFunList = new List<object>();
76  for (int i = 0; i < fsz; ++i) {
77  var scol = functionsList[i];
78  completeFunList.Add(scol.Expression);
79  completeFunList.Add(scol.InternalName.Name);
80  }
81 
82  for (int i = 0; i < aggregateFunctions.Count; ++i) {
83  completeFunList.Add(aggregateFunctions[i]);
84  completeFunList.Add("HAVINGAGG_" + (i + 1));
85  }
86 
87  int fsz2 = completeFunList.Count / 2;
88  defFunList = new SqlExpression[fsz2];
89  defFunNames = new string[fsz2];
90  for (int i = 0; i < fsz2; ++i) {
91  defFunList[i] = (SqlExpression)completeFunList[i * 2];
92  defFunNames[i] = (string)completeFunList[(i * 2) + 1];
93  }
94 
95  return fsz;
96  }
97 
98  private IQueryPlanNode PlanGroup(IQueryPlanNode node, GroupInfo groupInfo) {
99  // If there is more than 1 aggregate function or there is a group by
100  // clause, then we must add a grouping plan.
101  if (groupInfo.Columns.AggregateCount > 0 ||
102  groupInfo.GroupByCount > 0) {
103  // If there is no GROUP BY clause then assume the entire result is the
104  // group.
105  if (groupInfo.GroupByCount == 0) {
106  node = new GroupNode(node, groupInfo.GroupMax, groupInfo.FunctionExpressions, groupInfo.FunctionNames);
107  } else {
108  // Do we have any group by functions that need to be planned first?
109  int gfsz = groupInfo.GroupByExpressions.Length;
110  if (gfsz > 0) {
111  var groupFunList = new SqlExpression[gfsz];
112  var groupFunName = new string[gfsz];
113  for (int i = 0; i < gfsz; ++i) {
114  groupFunList[i] = groupInfo.GroupByExpressions[i];
115  groupFunName[i] = "#GROUPBY-" + i;
116  }
117 
118  node = new CreateFunctionsNode(node, groupFunList, groupFunName);
119  }
120 
121  // Otherwise we provide the 'group_by_list' argument
122  node = new GroupNode(node, groupInfo.GroupByNames, groupInfo.GroupMax, groupInfo.FunctionExpressions, groupInfo.FunctionNames);
123  }
124  } else {
125  // Otherwise no grouping is occurring. We simply need create a function
126  // node with any functions defined in the SELECT.
127  // Plan a FunctionsNode with the functions defined in the SELECT.
128  if (groupInfo.FunctionCount > 0)
129  node = new CreateFunctionsNode(node, groupInfo.FunctionExpressions, groupInfo.FunctionNames);
130  }
131 
132  return node;
133  }
134 
135  #region GroupInfo
136 
137  class GroupInfo {
138  public PreparedQuerySelectColumns Columns { get; set; }
139 
140  public ObjectName GroupMax { get; set; }
141 
142  public int GroupByCount { get; set; }
143 
144  public ObjectName[] GroupByNames { get; set; }
145 
146  public SqlExpression[] GroupByExpressions { get; set; }
147 
148  public int FunctionCount { get; set; }
149 
150  public string[] FunctionNames { get; set; }
151 
152  public SqlExpression[] FunctionExpressions { get; set; }
153  }
154 
155  #endregion
156 
158  // Set up plans for each table in the from clause of the command. For
159  // sub-queries, we recurse.
160 
161  var tablePlanner = new QueryTablePlanner();
162 
163  for (int i = 0; i < queryFrom.SourceCount; i++) {
164  var tableSource = queryFrom.GetTableSource(i);
165  IQueryPlanNode plan;
166 
167  if (tableSource is FromTableSubQuerySource) {
168  var subQuerySource = (FromTableSubQuerySource) tableSource;
169 
170  var subQueryExpr = subQuerySource.QueryExpression;
171  var subQueryFrom = subQuerySource.QueryFrom;
172 
173  plan = PlanQuery(context, subQueryExpr, subQueryFrom, null, null);
174 
175  if (!(plan is SubsetNode))
176  throw new InvalidOperationException("The root node of a sub-query plan must be a subset.");
177 
178  var subsetNode = (SubsetNode) plan;
179  subsetNode.SetAliasParentName(subQuerySource.AliasName);
180  } else if (tableSource is FromTableDirectSource) {
181  var directSource = (FromTableDirectSource) tableSource;
182  plan = directSource.QueryPlan;
183  } else {
184  throw new InvalidOperationException(String.Format("The type of FROM source '{0}' is not supported.", tableSource.GetType()));
185  }
186 
187  tablePlanner.AddPlan(plan, tableSource);
188  }
189 
190  return tablePlanner;
191  }
192 
193  private void PrepareJoins(QueryTablePlanner tablePlanner, SqlQueryExpression queryExpression, QueryExpressionFrom queryFrom, ref SqlExpression searchExpression) {
194  var fromClause = queryExpression.FromClause;
195 
196  bool allInner = true;
197  for (int i = 0; i < fromClause.JoinPartCount; i++) {
198  var joinPart = fromClause.GetJoinPart(i);
199  if (joinPart.JoinType != JoinType.Inner)
200  allInner = false;
201  }
202 
203  for (int i = 0; i < fromClause.JoinPartCount; i++) {
204  var joinPart = fromClause.GetJoinPart(i);
205 
206  var joinType = joinPart.JoinType;
207  var onExpression = joinPart.OnExpression;
208 
209  if (allInner) {
210  // If the whole join set is inner joins then simply move the on
211  // expression (if there is one) to the WHERE clause.
212  if (searchExpression != null && onExpression != null)
213  searchExpression = SqlExpression.And(searchExpression, onExpression);
214  } else {
215  // Not all inner joins,
216  if (joinType == JoinType.Inner && onExpression == null) {
217  // Regular join with no ON expression, so no preparation necessary
218  } else {
219  // Either an inner join with an ON expression, or an outer join with
220  // ON expression
221  if (onExpression == null)
222  throw new InvalidOperationException(String.Format("Join of type {0} requires ON expression.", joinType));
223 
224  // Resolve the on_expression
225  onExpression = onExpression.Prepare(queryFrom.ExpressionPreparer);
226  // And set it in the planner
227  tablePlanner.JoinAt(i, joinType, onExpression);
228  }
229  }
230  }
231  }
232 
233  private SqlExpression FilterHaving(SqlExpression havingExpression, IList<SqlExpression> aggregates, IRequest context) {
234  if (havingExpression is SqlBinaryExpression) {
235  var binary = (SqlBinaryExpression) havingExpression;
236  var expType = binary.ExpressionType;
237  var newLeft = FilterHaving(binary.Left, aggregates, context);
238  var newRight = FilterHaving(binary.Right, aggregates, context);
239  return SqlExpression.Binary(newLeft, expType, newRight);
240  }
241 
242  // Not logical so determine if the expression is an aggregate or not
243  if (havingExpression.HasAggregate(context)) {
244  // Has aggregate functions so we must WriteByte this expression on the
245  // aggregate list.
246 
247  aggregates.Add(havingExpression);
248 
249  var name = new ObjectName(FunctionTableName, String.Format("HAVINGAGG_{0}", aggregates.Count));
250  return SqlExpression.Reference(name);
251  }
252 
253  return havingExpression;
254  }
255 
256  private int ResolveGroupBy(SqlQueryExpression queryExpression, QueryExpressionFrom queryFrom, IRequest context, out ObjectName[] columnNames, out IList<SqlExpression> expressions) {
257  var groupBy = queryExpression.GroupBy == null
258  ? new List<SqlExpression>(0)
259  : queryExpression.GroupBy.ToList();
260  var groupBySize = groupBy.Count;
261 
262  expressions = new List<SqlExpression>();
263  columnNames = new ObjectName[groupBySize];
264 
265  for (int i = 0; i < groupBySize; i++) {
266  var expression = groupBy[i];
267 
268  // Prepare the group by expression
269  expression = expression.Prepare(queryFrom.ExpressionPreparer);
270 
271  var columnName = expression.AsReferenceName();
272  if (columnName != null)
273  expression = queryFrom.FindExpression(columnName);
274 
275  if (expression != null) {
276  if (expression.HasAggregate(context))
277  throw new InvalidOperationException(String.Format("Aggregate expression '{0}' is not allowed in a GROUP BY clause", expression));
278 
279  expressions.Add(expression);
280  columnName = new ObjectName(FunctionTableName, String.Format("#GROUPBY-{0}", expressions.Count -1));
281  }
282 
283  columnNames[i] = columnName;
284  }
285 
286  return groupBySize;
287  }
288 
289  private ObjectName ResolveGroupMax(SqlQueryExpression queryExpression, QueryExpressionFrom queryFrom) {
290  var groupMax = queryExpression.GroupMax;
291  if (groupMax != null) {
292  var variable = queryFrom.ResolveReference(groupMax);
293  if (variable == null)
294  throw new InvalidOperationException(String.Format("The GROUP MAX column '{0}' was not found.", groupMax));
295 
296  groupMax = variable;
297  }
298 
299  return groupMax;
300  }
301 
303  if (columns.AggregateCount > 0)
304  throw new InvalidOperationException("Invalid use of aggregate function in select with no FROM clause");
305 
306  // Make up the lists
307  var selectedColumns = columns.SelectedColumns.ToList();
308  int colCount = selectedColumns.Count;
309  var colNames = new string[colCount];
310  var expList = new SqlExpression[colCount];
311  var subsetVars = new ObjectName[colCount];
312  var aliases1 = new ObjectName[colCount];
313  for (int i = 0; i < colCount; ++i) {
314  SelectColumn scol = selectedColumns[i];
315  expList[i] = scol.Expression;
316  colNames[i] = scol.InternalName.Name;
317  subsetVars[i] = scol.InternalName;
318  aliases1[i] = scol.ResolvedName;
319  }
320 
321  return new SubsetNode(new CreateFunctionsNode(new SingleRowTableNode(), expList, colNames), subsetVars, aliases1);
322  }
323 
324  private static IList<SortColumn> ResolveOrderByRefs(PreparedQuerySelectColumns columnSet,
325  IEnumerable<SortColumn> orderBy) {
326  // Resolve any numerical references in the ORDER BY list (eg.
327  // '1' will be a reference to column 1.
328  if (orderBy == null)
329  return null;
330 
331  var columnCount = columnSet.SelectedColumns.Count();
332 
333  var resolvedColumns = new List<SortColumn>();
334  foreach (var column in orderBy) {
335  var resolved = column;
336 
337  var expression = column.Expression;
338  if (expression.ExpressionType == SqlExpressionType.Constant) {
339  var value = ((SqlConstantExpression) expression).Value;
340  if (value.Type is NumericType &&
341  !value.IsNull) {
342  var colRef = ((SqlNumber) value.Value).ToInt32() - 1;
343  if (colRef >= 0 && colRef < columnCount) {
344  var funArray = columnSet.FunctionColumns.ToArray();
345  var refExp = funArray[colRef];
346 
347  resolved = new SortColumn(refExp.Expression, column.Ascending);
348  }
349  }
350  }
351 
352  resolvedColumns.Add(resolved);
353  }
354 
355  return resolvedColumns.ToArray();
356  }
357 
358  public IQueryPlanNode PlanQuery(QueryInfo queryInfo) {
359  if (queryInfo == null)
360  throw new ArgumentNullException("queryInfo");
361 
362  var context = queryInfo.Request;
363  var queryExpression = queryInfo.Expression;
364  var sortColumns = queryInfo.SortColumns;
365  var limit = queryInfo.Limit;
366 
367  var queryFrom = QueryExpressionFrom.Create(context, queryExpression);
368  var orderBy = new List<SortColumn>();
369  if (sortColumns != null)
370  orderBy.AddRange(sortColumns);
371 
372  return PlanQuery(context, queryExpression, queryFrom, orderBy, limit);
373  }
374 
375  private IQueryPlanNode PlanQuery(IRequest context, SqlQueryExpression queryExpression,
376  QueryExpressionFrom queryFrom, IList<SortColumn> sortColumns, QueryLimit limit) {
377 
378  // ----- Resolve the SELECT list
379  // If there are 0 columns selected, then we assume the result should
380  // show all of the columns in the result.
381  bool doSubsetColumn = (queryExpression.SelectColumns.Any());
382 
383  // What we are selecting
384  var columns = BuildSelectColumns(queryExpression, queryFrom);
385 
386  // Prepare the column_set,
387  var preparedColumns = columns.Prepare(context);
388 
389  sortColumns = ResolveOrderByRefs(preparedColumns, sortColumns);
390 
391  // -----
392 
393  // Set up plans for each table in the from clause of the command. For
394  // sub-queries, we recurse.
395 
396  var tablePlanner = CreateTablePlanner(context, queryFrom);
397 
398  // -----
399 
400  // The WHERE and HAVING clauses
401  var whereClause = queryExpression.WhereExpression;
402  var havingClause = queryExpression.HavingExpression;
403 
404  PrepareJoins(tablePlanner, queryExpression, queryFrom, ref whereClause);
405 
406  // Prepare the WHERE and HAVING clause, qualifies all variables and
407  // prepares sub-queries.
408  whereClause = PrepareSearchExpression(context, queryFrom, whereClause);
409  havingClause = PrepareSearchExpression(context, queryFrom, havingClause);
410 
411  // Any extra Aggregate functions that are part of the HAVING clause that
412  // we need to add. This is a list of a name followed by the expression
413  // that contains the aggregate function.
414  var extraAggregateFunctions = new List<SqlExpression>();
415  if (havingClause != null)
416  havingClause = FilterHaving(havingClause, extraAggregateFunctions, context);
417 
418  // Any GROUP BY functions,
419  ObjectName[] groupByList;
420  IList<SqlExpression> groupByFunctions;
421  var gsz = ResolveGroupBy(queryExpression, queryFrom, context, out groupByList, out groupByFunctions);
422 
423  // Resolve GROUP MAX variable to a reference in this from set
424  var groupmaxColumn = ResolveGroupMax(queryExpression, queryFrom);
425 
426  // -----
427 
428  // Now all the variables should be resolved and correlated variables set
429  // up as appropriate.
430 
431  // If nothing in the FROM clause then simply evaluate the result of the
432  // select
433  if (queryFrom.SourceCount == 0)
434  return EvaluateToSingle(preparedColumns);
435 
436  // Plan the where clause. The returned node is the plan to evaluate the
437  // WHERE clause.
438  var node = tablePlanner.PlanSearchExpression(whereClause);
439 
440  SqlExpression[] defFunList;
441  string[] defFunNames;
442  var fsz = MakeupFunctions(preparedColumns, extraAggregateFunctions, out defFunList, out defFunNames);
443 
444  var groupInfo = new GroupInfo {
445  Columns = preparedColumns,
446  FunctionCount = fsz,
447  FunctionNames = defFunNames,
448  FunctionExpressions = defFunList,
449  GroupByCount = gsz,
450  GroupByNames = groupByList,
451  GroupByExpressions = groupByFunctions.ToArray(),
452  GroupMax = groupmaxColumn
453  };
454 
455  node = PlanGroup(node, groupInfo);
456 
457  // The result column list
458  var selectColumns = preparedColumns.SelectedColumns.ToList();
459  int sz = selectColumns.Count;
460 
461  // Evaluate the having clause if necessary
462  if (havingClause != null) {
463  // Before we evaluate the having expression we must substitute all the
464  // aliased variables.
465  var havingExpr = havingClause;
466 
467  // TODO: this requires a visitor to modify the having expression
468  havingExpr = ReplaceAliasedVariables(havingExpr, selectColumns);
469 
470  var source = tablePlanner.SinglePlan;
471  source.UpdatePlan(node);
472  node = tablePlanner.PlanSearchExpression(havingExpr);
473  }
474 
475  // Do we have a composite select expression to process?
476  IQueryPlanNode rightComposite = null;
477  if (queryExpression.NextComposite != null) {
478  var compositeExpr = queryExpression.NextComposite;
479  var compositeFrom = QueryExpressionFrom.Create(context, compositeExpr);
480 
481  // Form the right plan
482  rightComposite = PlanQuery(context, compositeExpr, compositeFrom, null, null);
483  }
484 
485  // Do we do a final subset column?
486  ObjectName[] aliases = null;
487  if (doSubsetColumn) {
488  // Make up the lists
489  var subsetVars = new ObjectName[sz];
490  aliases = new ObjectName[sz];
491  for (int i = 0; i < sz; ++i) {
492  SelectColumn scol = selectColumns[i];
493  subsetVars[i] = scol.InternalName;
494  aliases[i] = scol.ResolvedName;
495  }
496 
497  // If we are distinct then add the DistinctNode here
498  if (queryExpression.Distinct)
499  node = new DistinctNode(node, subsetVars);
500 
501  // Process the ORDER BY?
502  // Note that the ORDER BY has to occur before the subset call, but
503  // after the distinct because distinct can affect the ordering of the
504  // result.
505  if (rightComposite == null && sortColumns != null)
506  node = PlanForOrderBy(node, sortColumns, queryFrom, selectColumns);
507 
508  // Rename the columns as specified in the SELECT
509  node = new SubsetNode(node, subsetVars, aliases);
510  } else {
511  // Process the ORDER BY?
512  if (rightComposite == null && sortColumns != null)
513  node = PlanForOrderBy(node, sortColumns, queryFrom, selectColumns);
514  }
515 
516  // Do we have a composite to merge in?
517  if (rightComposite != null) {
518  // For the composite
519  node = new CompositeNode(node, rightComposite, queryExpression.CompositeFunction, queryExpression.IsCompositeAll);
520 
521  // Final order by?
522  if (sortColumns != null)
523  node = PlanForOrderBy(node, sortColumns, queryFrom, selectColumns);
524 
525  // Ensure a final subset node
526  if (!(node is SubsetNode) && aliases != null) {
527  node = new SubsetNode(node, aliases, aliases);
528  }
529  }
530 
531  if (limit != null)
532  node = new LimitNode(node, limit.Offset, limit.Count);
533 
534  return node;
535  }
536 
537  private static IQueryPlanNode PlanForOrderBy(IQueryPlanNode plan, IList<SortColumn> orderBy, QueryExpressionFrom queryFrom, IList<SelectColumn> selectedColumns) {
538  // Sort on the ORDER BY clause
539  if (orderBy.Count > 0) {
540  int sz = orderBy.Count;
541  var orderList = new ObjectName[sz];
542  var ascendingList = new bool[sz];
543 
544  var functionOrders = new List<SqlExpression>();
545 
546  for (int i = 0; i < sz; ++i) {
547  var column = orderBy[i];
548  SqlExpression exp = column.Expression;
549  ascendingList[i] = column.Ascending;
550  var v = exp.AsReferenceName();
551 
552  if (v != null) {
553  var newV = queryFrom.ResolveReference(v);
554  if (newV == null)
555  throw new InvalidOperationException(String.Format("Could not resolve ORDER BY column '{0}' in expression", v));
556 
557  newV = ReplaceAliasedVariable(newV, selectedColumns);
558  orderList[i] = newV;
559  } else {
560  // Otherwise we must be ordering by an expression such as
561  // '0 - a'.
562 
563  // Resolve the expression,
564  exp = exp.Prepare(queryFrom.ExpressionPreparer);
565 
566  // Make sure we substitute any aliased columns in the order by
567  // columns.
568  exp = ReplaceAliasedVariables(exp, selectedColumns);
569 
570  // The new ordering functions are called 'FUNCTIONTABLE.#ORDER-n'
571  // where n is the number of the ordering expression.
572  orderList[i] = new ObjectName(FunctionTableName, "#ORDER-" + functionOrders.Count);
573  functionOrders.Add(exp);
574  }
575  }
576 
577  // If there are functional orderings,
578  // For this we must define a new FunctionTable with the expressions,
579  // then order by those columns, and then use another SubsetNode
580  // command node.
581  int fsz = functionOrders.Count;
582  if (fsz > 0) {
583  var funs = new SqlExpression[fsz];
584  var fnames = new String[fsz];
585  for (int n = 0; n < fsz; ++n) {
586  funs[n] = functionOrders[n];
587  fnames[n] = "#ORDER-" + n;
588  }
589 
590  if (plan is SubsetNode) {
591  // If the top plan is a SubsetNode then we use the
592  // information from it to create a new SubsetNode that
593  // doesn't include the functional orders we have attached here.
594  var topSubsetNode = (SubsetNode)plan;
595  var mappedNames = topSubsetNode.AliasColumnNames;
596 
597  // Defines the sort functions
598  plan = new CreateFunctionsNode(plan, funs, fnames);
599  // Then plan the sort
600  plan = new SortNode(plan, orderList, ascendingList);
601  // Then plan the subset
602  plan = new SubsetNode(plan, mappedNames, mappedNames);
603  } else {
604  // Defines the sort functions
605  plan = new CreateFunctionsNode(plan, funs, fnames);
606  // Plan the sort
607  plan = new SortNode(plan, orderList, ascendingList);
608  }
609 
610  } else {
611  // No functional orders so we only need to sort by the columns
612  // defined.
613  plan = new SortNode(plan, orderList, ascendingList);
614  }
615  }
616 
617  return plan;
618  }
619 
620  private static SqlExpression ReplaceAliasedVariables(SqlExpression expression, IList<SelectColumn> selectedColumns) {
621  var replacer = new VariableReplacer(selectedColumns);
622  return replacer.Visit(expression);
623  }
624 
625  private static ObjectName ReplaceAliasedVariable(ObjectName variableName, IEnumerable<SelectColumn> selectColumns) {
626  foreach (var column in selectColumns) {
627  if (column.ResolvedName.Equals(variableName))
628  return column.InternalName;
629  }
630 
631  return variableName;
632  }
633 
634  #region QueryExpressionPreparer
635 
637  private readonly QueryPlanner planner;
638  private readonly QueryExpressionFrom parent;
639  private readonly IRequest context;
640 
642  this.planner = planner;
643  this.parent = parent;
644  this.context = context;
645  }
646 
647  public bool CanPrepare(SqlExpression expression) {
648  return expression is SqlQueryExpression;
649  }
650 
651  public SqlExpression Prepare(SqlExpression expression) {
652  var queryExpression = (SqlQueryExpression) expression;
653  var queryFrom = QueryExpressionFrom.Create(context, queryExpression);
654  queryFrom.Parent = parent;
655  var plan = planner.PlanQuery(context, queryExpression, queryFrom, null, null);
656  return SqlExpression.Constant(new DataObject(new QueryType(), new SqlQueryObject(new CachePointNode(plan))));
657  }
658  }
659 
660  #endregion
661 
662  #region VariableReplacer
663 
665  private readonly IEnumerable<SelectColumn> selectColumns;
666 
667  public VariableReplacer(IEnumerable<SelectColumn> selectColumns) {
668  this.selectColumns = selectColumns;
669  }
670 
672  // TODO: should we also resolve variables?
673  return base.VisitVariableReference(reference);
674  }
675 
677  var refName = reference.ReferenceName;
678  foreach (var column in selectColumns) {
679  if (refName.Equals(column.ResolvedName))
680  return SqlExpression.Reference(column.InternalName);
681  }
682 
683  return base.VisitReference(reference);
684  }
685  }
686 
687  #endregion
688  }
689 }
QueryTablePlanner CreateTablePlanner(IRequest context, QueryExpressionFrom queryFrom)
void PrepareJoins(QueryTablePlanner tablePlanner, SqlQueryExpression queryExpression, QueryExpressionFrom queryFrom, ref SqlExpression searchExpression)
SqlExpression PrepareSearchExpression(IRequest context, QueryExpressionFrom queryFrom, SqlExpression expression)
Definition: QueryPlanner.cs:30
ObjectName ReferenceName
Gets the name of the object referenced by the expression.
readonly IEnumerable< SelectColumn > selectColumns
A branch node for performing a composite function on two child nodes.
An implementation of IFromTableSource that wraps around a SqlQueryExpression as a sub-query source...
SqlExpression FindExpression(ObjectName alias)
An expression that references an object within a context.
A long string in the system.
static SqlBinaryExpression And(SqlExpression left, SqlExpression right)
JoinType
Enumerates the kind of group join in a selection query.
Definition: JoinType.cs:23
IFromTableSource GetTableSource(int offset)
ObjectName ResolveReference(ObjectName refName)
QuerySelectColumns BuildSelectColumns(SqlQueryExpression expression, QueryExpressionFrom queryFrom)
Definition: QueryPlanner.cs:48
override SqlExpression VisitVariableReference(SqlVariableReferenceExpression reference)
bool CanPrepare(SqlExpression expression)
Verifies whether the instance of the interface can prepare the given expression.
SqlExpression Prepare(SqlExpression expression)
Returns the new translated object to be mutated from the given expression.
Describes the name of an object within a database.
Definition: ObjectName.cs:44
JoinPart GetJoinPart(int offset)
Gets the descriptor of the join at the given offset.
Definition: FromClause.cs:174
SqlExpression FilterHaving(SqlExpression havingExpression, IList< SqlExpression > aggregates, IRequest context)
SqlExpressionType
All the possible type of SqlExpression supported
A node element of a query plan tree. /summary>
int ResolveGroupBy(SqlQueryExpression queryExpression, QueryExpressionFrom queryFrom, IRequest context, out ObjectName[] columnNames, out IList< SqlExpression > expressions)
static int MakeupFunctions(PreparedQuerySelectColumns columnSet, IList< SqlExpression > aggregateFunctions, out SqlExpression[] defFunList, out string[] defFunNames)
Definition: QueryPlanner.cs:71
IQueryPlanNode PlanGroup(IQueryPlanNode node, GroupInfo groupInfo)
Definition: QueryPlanner.cs:98
The node for merging the child node with a set of new function columns over the entire result...
static QueryExpressionFrom Create(IRequest context, SqlQueryExpression expression)
static SqlBinaryExpression Add(SqlExpression left, SqlExpression right)
IQueryPlanNode EvaluateToSingle(PreparedQuerySelectColumns columns)
static IList< SortColumn > ResolveOrderByRefs(PreparedQuerySelectColumns columnSet, IEnumerable< SortColumn > orderBy)
static SqlExpression ReplaceAliasedVariables(SqlExpression expression, IList< SelectColumn > selectedColumns)
Represents a dynamic object that encapsulates a defined SqlType and a compatible constant ISqlObject ...
Definition: DataObject.cs:35
An interface used to prepare a SqlExpression object.
SqlQueryExpression Expression
Definition: QueryInfo.cs:47
virtual SqlExpression Prepare(IExpressionPreparer preparer)
Represents a column selected to be in the output of a select statement.
Definition: SelectColumn.cs:31
SqlExpression Expression
Gets the expression used to select the column.
Definition: SelectColumn.cs:65
An expression that holds a constant value.
QueryExpressionPreparer(QueryPlanner planner, QueryExpressionFrom parent, IRequest context)
IEnumerable< SortColumn > SortColumns
Definition: QueryInfo.cs:53
string Name
Gets the name of the object being referenced.
Definition: ObjectName.cs:108
ObjectName InternalName
The name of this column used internally to reference it.
static SqlReferenceExpression Reference(ObjectName objectName)
static ObjectName ReplaceAliasedVariable(ObjectName variableName, IEnumerable< SelectColumn > selectColumns)
IQueryPlanNode PlanQuery(IRequest context, SqlQueryExpression queryExpression, QueryExpressionFrom queryFrom, IList< SortColumn > sortColumns, QueryLimit limit)
static IQueryPlanNode PlanForOrderBy(IQueryPlanNode plan, IList< SortColumn > orderBy, QueryExpressionFrom queryFrom, IList< SelectColumn > selectedColumns)
static SqlBinaryExpression Binary(SqlExpression left, SqlExpressionType expressionType, SqlExpression right)
ObjectName ResolveGroupMax(SqlQueryExpression queryExpression, QueryExpressionFrom queryFrom)
Defines the base class for instances that represent SQL expression tree nodes.
static SqlConstantExpression Constant(object value)
IQueryPlanNode PlanQuery(QueryInfo queryInfo)
Object used to represent a column in the ORDER BY clauses of a select statement.
Definition: SortColumn.cs:26
override SqlExpression VisitReference(SqlReferenceExpression reference)
abstract SqlExpressionType ExpressionType
Gets the type code of this SQL expression.
ObjectName ResolvedName
The fully resolved name that this column is given in the resulting table.
VariableReplacer(IEnumerable< SelectColumn > selectColumns)
An implementation of IFromTableSource that wraps around a ObjectName/ITable object.
void JoinAt(int betweenIndex, JoinType joinType, SqlExpression onExpression)