private static ObjectName ResolveGroupMax(TableSelectExpression expression, TableExpressionFromSet fromSet) { // Resolve GROUP MAX variable to a reference in this from set ObjectName groupmaxColumn = expression.GroupMax; if (groupmaxColumn != null) { ObjectName v = fromSet.ResolveReference(groupmaxColumn); if (v == null) { throw new ApplicationException("Could find GROUP MAX reference '" + groupmaxColumn + "'"); } groupmaxColumn = v; } return(groupmaxColumn); }
internal static TableExpressionFromSet GenerateFromSet(TableSelectExpression selectExpression, ITableSpaceContext db) { // Get the 'from_clause' from the table expression FromClause fromClause = selectExpression.From; // Create a TableExpressionFromSet for this table expression var fromSet = new TableExpressionFromSet(db.IsInCaseInsensitive); // Add all tables from the 'fromClause' foreach (FromTable fromTable in fromClause.AllTables) { string uniqueKey = fromTable.UniqueKey; ObjectName alias = fromTable.Alias; // If this is a sub-command table, if (fromTable.IsSubQueryTable) { // eg. FROM ( SELECT id FROM Part ) TableSelectExpression subQuery = fromTable.SubSelect; TableExpressionFromSet subQueryFromSet = GenerateFromSet(subQuery, db); // The aliased name of the table ObjectName aliasTableName = null; if (alias != null) { aliasTableName = alias.Clone(); } var source = new FromTableSubQuerySource(db.IsInCaseInsensitive, uniqueKey, subQuery, subQueryFromSet, aliasTableName); // Add to list of subquery tables to add to command, fromSet.AddTable(source); } else { // Else must be a standard command table, ObjectName name = fromTable.Name; // Resolve to full table name ObjectName tableName = db.ResolveTableName(name); if (!db.TableExists(tableName)) { throw new ApplicationException("Table '" + tableName + "' was not found."); } ObjectName givenName = null; if (alias != null) { givenName = alias.Clone(); } // Get the ITableQueryInfo object for this table name (aliased). ITableQueryInfo tableQueryInfo = db.GetTableQueryInfo(tableName, givenName); var source = new FromTableDirectSource(db.IsInCaseInsensitive, tableQueryInfo, uniqueKey, givenName, tableName); fromSet.AddTable(source); } } // foreach // Set up functions, aliases and exposed variables for this from set, // For each column being selected foreach (SelectColumn col in selectExpression.Columns) { // Is this a glob? (eg. Part.* ) if (col.IsGlob) { // Find the columns globbed and add to the 'selectedColumns' result. if (col.IsAll) { fromSet.ExposeAllColumns(); } else { // Otherwise the glob must be of the form '[table name].*' string tname = col.GlobPrefix; ObjectName tn = ObjectName.Parse(tname); fromSet.ExposeAllColumnsFromSource(tn); } } else { // Otherwise must be a standard column reference. Note that at this // time we aren't sure if a column expression is correlated and is // referencing an outer source. This means we can't verify if the // column expression is valid or not at this point. // If this column is aliased, add it as a function reference to the // TableExpressionFromSet. var varExpression = (VariableExpression)col.Expression; ObjectName alias = col.Alias; ObjectName v = varExpression.VariableName; bool aliasMatchV = (v != null && alias != null && fromSet.StringCompare(v.Name, alias.Name)); if (alias != null && !aliasMatchV) { fromSet.AddFunctionRef(alias.Name, col.Expression); fromSet.ExposeVariable(alias.Clone()); } else if (v != null) { ObjectName resolved = fromSet.ResolveReference(v); fromSet.ExposeVariable(resolved ?? v); } else { string funName = col.Expression.ToString(); fromSet.AddFunctionRef(funName, col.Expression); fromSet.ExposeVariable(ObjectName.Parse(funName)); } } } // for each column selected return(fromSet); }
/// <summary> /// Plans an ORDER BY set. /// </summary> /// <param name="plan"></param> /// <param name="orderBy"></param> /// <param name="fromSet"></param> /// <param name="selectedColumns"></param> /// <remarks> /// This is given its own function because we may want to plan /// this at the end of a number of composite functions. /// </remarks> /// <returns></returns> private static IQueryPlanNode PlanForOrderBy(IQueryPlanNode plan, IList <ByColumn> orderBy, TableExpressionFromSet fromSet, IList <SelectColumn> selectedColumns) { var functionTable = new ObjectName("FUNCTIONTABLE"); // Sort on the ORDER BY clause if (orderBy.Count > 0) { int sz = orderBy.Count; var orderList = new ObjectName[sz]; var ascendingList = new bool[sz]; var functionOrders = new List <Expression>(); for (int i = 0; i < sz; ++i) { ByColumn column = orderBy[i]; Expression exp = column.Expression; ascendingList[i] = column.Ascending; ObjectName v = exp.AsVariable(); if (v != null) { ObjectName newV = fromSet.ResolveReference(v); if (newV == null) { throw new ApplicationException("Can not resolve ORDER BY variable: " + v); } newV = SubstituteAliasedVariable(newV, selectedColumns); orderList[i] = newV; } else { // Otherwise we must be ordering by an expression such as // '0 - a'. // Resolve the expression, exp = exp.Prepare(fromSet.ExpressionQualifier); // Make sure we substitute any aliased columns in the order by // columns. exp = SubstituteAliasedVariables(exp, selectedColumns); // The new ordering functions are called 'FUNCTIONTABLE.#ORDER-n' // where n is the number of the ordering expression. orderList[i] = new ObjectName(functionTable, "#ORDER-" + functionOrders.Count); functionOrders.Add(exp); } } // If there are functional orderings, // For this we must define a new FunctionTable with the expressions, // then order by those columns, and then use another SubsetNode // command node. int fsz = functionOrders.Count; if (fsz > 0) { var funs = new Expression[fsz]; var fnames = new String[fsz]; for (int n = 0; n < fsz; ++n) { funs[n] = functionOrders[n]; fnames[n] = "#ORDER-" + n; } if (plan is SubsetNode) { // If the top plan is a QueryPlan.SubsetNode then we use the // information from it to create a new SubsetNode that // doesn't include the functional orders we have attached here. var topSubsetNode = (SubsetNode)plan; ObjectName[] mappedNames = topSubsetNode.NewColumnNames; // Defines the sort functions plan = new CreateFunctionsNode(plan, funs, fnames); // Then plan the sort plan = new SortNode(plan, orderList, ascendingList); // Then plan the subset plan = new SubsetNode(plan, mappedNames, mappedNames); } else { // Defines the sort functions plan = new CreateFunctionsNode(plan, funs, fnames); // Plan the sort plan = new SortNode(plan, orderList, ascendingList); } } else { // No functional orders so we only need to sort by the columns // defined. plan = new SortNode(plan, orderList, ascendingList); } } return(plan); }
internal static TableExpressionFromSet GenerateFromSet(TableSelectExpression selectExpression, ITableSpaceContext db) { // Get the 'from_clause' from the table expression FromClause fromClause = selectExpression.From; // Create a TableExpressionFromSet for this table expression var fromSet = new TableExpressionFromSet(db.IsInCaseInsensitive); // Add all tables from the 'fromClause' foreach (FromTable fromTable in fromClause.AllTables) { string uniqueKey = fromTable.UniqueKey; ObjectName alias = fromTable.Alias; // If this is a sub-command table, if (fromTable.IsSubQueryTable) { // eg. FROM ( SELECT id FROM Part ) TableSelectExpression subQuery = fromTable.SubSelect; TableExpressionFromSet subQueryFromSet = GenerateFromSet(subQuery, db); // The aliased name of the table ObjectName aliasTableName = null; if (alias != null) aliasTableName = alias.Clone(); var source = new FromTableSubQuerySource(db.IsInCaseInsensitive, uniqueKey, subQuery, subQueryFromSet, aliasTableName); // Add to list of subquery tables to add to command, fromSet.AddTable(source); } else { // Else must be a standard command table, ObjectName name = fromTable.Name; // Resolve to full table name ObjectName tableName = db.ResolveTableName(name); if (!db.TableExists(tableName)) throw new ApplicationException("Table '" + tableName + "' was not found."); ObjectName givenName = null; if (alias != null) givenName = alias.Clone(); // Get the ITableQueryInfo object for this table name (aliased). ITableQueryInfo tableQueryInfo = db.GetTableQueryInfo(tableName, givenName); var source = new FromTableDirectSource(db.IsInCaseInsensitive, tableQueryInfo, uniqueKey, givenName, tableName); fromSet.AddTable(source); } } // foreach // Set up functions, aliases and exposed variables for this from set, // For each column being selected foreach (SelectColumn col in selectExpression.Columns) { // Is this a glob? (eg. Part.* ) if (col.IsGlob) { // Find the columns globbed and add to the 'selectedColumns' result. if (col.IsAll) { fromSet.ExposeAllColumns(); } else { // Otherwise the glob must be of the form '[table name].*' string tname = col.GlobPrefix; ObjectName tn = ObjectName.Parse(tname); fromSet.ExposeAllColumnsFromSource(tn); } } else { // Otherwise must be a standard column reference. Note that at this // time we aren't sure if a column expression is correlated and is // referencing an outer source. This means we can't verify if the // column expression is valid or not at this point. // If this column is aliased, add it as a function reference to the // TableExpressionFromSet. var varExpression = (VariableExpression)col.Expression; ObjectName alias = col.Alias; ObjectName v = varExpression.VariableName; bool aliasMatchV = (v != null && alias != null && fromSet.StringCompare(v.Name, alias.Name)); if (alias != null && !aliasMatchV) { fromSet.AddFunctionRef(alias.Name, col.Expression); fromSet.ExposeVariable(alias.Clone()); } else if (v != null) { ObjectName resolved = fromSet.ResolveReference(v); fromSet.ExposeVariable(resolved ?? v); } else { string funName = col.Expression.ToString(); fromSet.AddFunctionRef(funName, col.Expression); fromSet.ExposeVariable(ObjectName.Parse(funName)); } } } // for each column selected return fromSet; }
private static ObjectName ResolveGroupMax(TableSelectExpression expression, TableExpressionFromSet fromSet) { // Resolve GROUP MAX variable to a reference in this from set ObjectName groupmaxColumn = expression.GroupMax; if (groupmaxColumn != null) { ObjectName v = fromSet.ResolveReference(groupmaxColumn); if (v == null) throw new ApplicationException("Could find GROUP MAX reference '" + groupmaxColumn + "'"); groupmaxColumn = v; } return groupmaxColumn; }
/// <summary> /// Plans an ORDER BY set. /// </summary> /// <param name="plan"></param> /// <param name="orderBy"></param> /// <param name="fromSet"></param> /// <param name="selectedColumns"></param> /// <remarks> /// This is given its own function because we may want to plan /// this at the end of a number of composite functions. /// </remarks> /// <returns></returns> private static IQueryPlanNode PlanForOrderBy(IQueryPlanNode plan, IList<ByColumn> orderBy, TableExpressionFromSet fromSet, IList<SelectColumn> selectedColumns) { var functionTable = new ObjectName("FUNCTIONTABLE"); // Sort on the ORDER BY clause if (orderBy.Count > 0) { int sz = orderBy.Count; var orderList = new ObjectName[sz]; var ascendingList = new bool[sz]; var functionOrders = new List<Expression>(); for (int i = 0; i < sz; ++i) { ByColumn column = orderBy[i]; Expression exp = column.Expression; ascendingList[i] = column.Ascending; ObjectName v = exp.AsVariable(); if (v != null) { ObjectName newV = fromSet.ResolveReference(v); if (newV == null) throw new ApplicationException("Can not resolve ORDER BY variable: " + v); newV = SubstituteAliasedVariable(newV, selectedColumns); orderList[i] = newV; } else { // Otherwise we must be ordering by an expression such as // '0 - a'. // Resolve the expression, exp = exp.Prepare(fromSet.ExpressionQualifier); // Make sure we substitute any aliased columns in the order by // columns. exp = SubstituteAliasedVariables(exp, selectedColumns); // The new ordering functions are called 'FUNCTIONTABLE.#ORDER-n' // where n is the number of the ordering expression. orderList[i] = new ObjectName(functionTable, "#ORDER-" + functionOrders.Count); functionOrders.Add(exp); } } // If there are functional orderings, // For this we must define a new FunctionTable with the expressions, // then order by those columns, and then use another SubsetNode // command node. int fsz = functionOrders.Count; if (fsz > 0) { var funs = new Expression[fsz]; var fnames = new String[fsz]; for (int n = 0; n < fsz; ++n) { funs[n] = functionOrders[n]; fnames[n] = "#ORDER-" + n; } if (plan is SubsetNode) { // If the top plan is a QueryPlan.SubsetNode then we use the // information from it to create a new SubsetNode that // doesn't include the functional orders we have attached here. var topSubsetNode = (SubsetNode)plan; ObjectName[] mappedNames = topSubsetNode.NewColumnNames; // Defines the sort functions plan = new CreateFunctionsNode(plan, funs, fnames); // Then plan the sort plan = new SortNode(plan, orderList, ascendingList); // Then plan the subset plan = new SubsetNode(plan, mappedNames, mappedNames); } else { // Defines the sort functions plan = new CreateFunctionsNode(plan, funs, fnames); // Plan the sort plan = new SortNode(plan, orderList, ascendingList); } } else { // No functional orders so we only need to sort by the columns // defined. plan = new SortNode(plan, orderList, ascendingList); } } return plan; }