private IQueryPlanNode PlanQuery(IRequest context, SqlQueryExpression queryExpression,
QueryExpressionFrom queryFrom, IList<SortColumn> sortColumns, QueryLimit limit)
{
// ----- Resolve the SELECT list
// If there are 0 columns selected, then we assume the result should
// show all of the columns in the result.
bool doSubsetColumn = (queryExpression.SelectColumns.Any());
// What we are selecting
var columns = BuildSelectColumns(queryExpression, queryFrom);
// Prepare the column_set,
var preparedColumns = columns.Prepare(context);
sortColumns = ResolveOrderByRefs(preparedColumns, sortColumns);
// -----
// Set up plans for each table in the from clause of the command. For
// sub-queries, we recurse.
var tablePlanner = CreateTablePlanner(context, queryFrom);
// -----
// The WHERE and HAVING clauses
var whereClause = queryExpression.WhereExpression;
var havingClause = queryExpression.HavingExpression;
PrepareJoins(tablePlanner, queryExpression, queryFrom, ref whereClause);
// Prepare the WHERE and HAVING clause, qualifies all variables and
// prepares sub-queries.
whereClause = PrepareSearchExpression(context, queryFrom, whereClause);
havingClause = PrepareSearchExpression(context, queryFrom, havingClause);
// Any extra Aggregate functions that are part of the HAVING clause that
// we need to add. This is a list of a name followed by the expression
// that contains the aggregate function.
var extraAggregateFunctions = new List<SqlExpression>();
if (havingClause != null)
havingClause = FilterHaving(havingClause, extraAggregateFunctions, context);
// Any GROUP BY functions,
ObjectName[] groupByList;
IList<SqlExpression> groupByFunctions;
var gsz = ResolveGroupBy(queryExpression, queryFrom, context, out groupByList, out groupByFunctions);
// Resolve GROUP MAX variable to a reference in this from set
var groupmaxColumn = ResolveGroupMax(queryExpression, queryFrom);
// -----
// Now all the variables should be resolved and correlated variables set
// up as appropriate.
// If nothing in the FROM clause then simply evaluate the result of the
// select
if (queryFrom.SourceCount == 0)
return EvaluateToSingle(preparedColumns);
// Plan the where clause. The returned node is the plan to evaluate the
// WHERE clause.
var node = tablePlanner.PlanSearchExpression(whereClause);
SqlExpression[] defFunList;
string[] defFunNames;
var fsz = MakeupFunctions(preparedColumns, extraAggregateFunctions, out defFunList, out defFunNames);
var groupInfo = new GroupInfo {
Columns = preparedColumns,
FunctionCount = fsz,
FunctionNames = defFunNames,
FunctionExpressions = defFunList,
GroupByCount = gsz,
GroupByNames = groupByList,
GroupByExpressions = groupByFunctions.ToArray(),
GroupMax = groupmaxColumn
};
node = PlanGroup(node, groupInfo);
// The result column list
var selectColumns = preparedColumns.SelectedColumns.ToList();
int sz = selectColumns.Count;
// Evaluate the having clause if necessary
if (havingClause != null) {
// Before we evaluate the having expression we must substitute all the
// aliased variables.
var havingExpr = havingClause;
// TODO: this requires a visitor to modify the having expression
havingExpr = ReplaceAliasedVariables(havingExpr, selectColumns);
var source = tablePlanner.SinglePlan;
source.UpdatePlan(node);
node = tablePlanner.PlanSearchExpression(havingExpr);
}
// Do we have a composite select expression to process?
IQueryPlanNode rightComposite = null;
if (queryExpression.NextComposite != null) {
var compositeExpr = queryExpression.NextComposite;
var compositeFrom = QueryExpressionFrom.Create(context, compositeExpr);
// Form the right plan
rightComposite = PlanQuery(context, compositeExpr, compositeFrom, null, null);
}
// Do we do a final subset column?
ObjectName[] aliases = null;
if (doSubsetColumn) {
// Make up the lists
var subsetVars = new ObjectName[sz];
aliases = new ObjectName[sz];
for (int i = 0; i < sz; ++i) {
SelectColumn scol = selectColumns[i];
subsetVars[i] = scol.InternalName;
aliases[i] = scol.ResolvedName;
}
// If we are distinct then add the DistinctNode here
if (queryExpression.Distinct)
node = new DistinctNode(node, subsetVars);
// Process the ORDER BY?
// Note that the ORDER BY has to occur before the subset call, but
// after the distinct because distinct can affect the ordering of the
// result.
if (rightComposite == null && sortColumns != null)
node = PlanForOrderBy(node, sortColumns, queryFrom, selectColumns);
// Rename the columns as specified in the SELECT
node = new SubsetNode(node, subsetVars, aliases);
} else {
// Process the ORDER BY?
if (rightComposite == null && sortColumns != null)
node = PlanForOrderBy(node, sortColumns, queryFrom, selectColumns);
}
// Do we have a composite to merge in?
if (rightComposite != null) {
// For the composite
node = new CompositeNode(node, rightComposite, queryExpression.CompositeFunction, queryExpression.IsCompositeAll);
// Final order by?
if (sortColumns != null)
node = PlanForOrderBy(node, sortColumns, queryFrom, selectColumns);
// Ensure a final subset node
if (!(node is SubsetNode) && aliases != null) {
node = new SubsetNode(node, aliases, aliases);
}
}
if (limit != null)
node = new LimitNode(node, limit.Offset, limit.Count);
return node;
}