static bool ResolveOrderGroupBy(NameContext nc, Select select, ExprList orderBy, string type) { if (orderBy == null) { return(false); } int result = select.EList.Exprs; // Number of terms in the result set Parse parse = nc.Parse; // Parsing context int i; ExprList.ExprListItem item; // A term of the ORDER BY clause for (i = 0; i < orderBy.Exprs; i++) { item = orderBy.Ids[i]; Expr expr = item.Expr; int colId = ResolveAsName(parse, select.EList, expr); // Column number if (colId > 0) { // If an AS-name match is found, mark this ORDER BY column as being a copy of the iCol-th result-set column. The subsequent call to // sqlite3ResolveOrderGroupBy() will convert the expression to a copy of the iCol-th result-set expression. item.OrderByCol = (ushort)colId; continue; } if (expr.SkipCollate().IsInteger(ref colId)) { // The ORDER BY term is an integer constant. Again, set the column number so that sqlite3ResolveOrderGroupBy() will convert the // order-by term to a copy of the result-set expression if (colId < 1 || colId > 0xffff) { ResolveOutOfRangeError(parse, type, i + 1, result); return(true); } item.OrderByCol = (ushort)colId; continue; } // Otherwise, treat the ORDER BY term as an ordinary expression item.OrderByCol = 0; if (Walker.ResolveExprNames(nc, ref expr)) { return(true); } for (int j = 0; j < select.EList.Exprs; j++) { if (Expr.Compare(expr, select.EList.Ids[j].Expr) == 0) { item.OrderByCol = (ushort)(j + 1); } } } return(Walker.ResolveOrderGroupBy(parse, select, orderBy, type)); }
static int ResolveOrderByTermToExprList(Parse parse, Select select, Expr expr) { int i = 0; Debug.Assert(!expr.IsInteger(ref i)); ExprList list = select.EList; // The columns of the result set // Resolve all names in the ORDER BY term expression NameContext nc = new NameContext(); // Name context for resolving pE nc.Parse = parse; nc.SrcList = select.Src; nc.EList = list; nc.NCFlags = NC.AllowAgg; nc.Errs = 0; Context ctx = parse.Ctx; // Database connection byte savedSuppErr = ctx.SuppressErr; // Saved value of db->suppressErr ctx.SuppressErr = 1; bool r = Walker.ResolveExprNames(nc, ref expr); ctx.SuppressErr = savedSuppErr; if (r) { return(0); } // Try to match the ORDER BY expression against an expression in the result set. Return an 1-based index of the matching result-set entry. for (i = 0; i < list.Exprs; i++) { if (Expr.Compare(list.Ids[i].Expr, expr) < 2) { return(i + 1); } } // If no match, return 0. return(0); }
static WRC ResolveSelectStep(Walker walker, Select p) { Debug.Assert(p != null); if ((p.SelFlags & SF.Resolved) != 0) { return(WRC.Prune); } NameContext outerNC = walker.u.NC; // Context that contains this SELECT Parse parse = walker.Parse; // Parsing context Context ctx = parse.Ctx; // Database connection // Normally sqlite3SelectExpand() will be called first and will have already expanded this SELECT. However, if this is a subquery within // an expression, sqlite3ResolveExprNames() will be called without a prior call to sqlite3SelectExpand(). When that happens, let // sqlite3SelectPrep() do all of the processing for this SELECT. sqlite3SelectPrep() will invoke both sqlite3SelectExpand() and // this routine in the correct order. if ((p.SelFlags & SF.Expanded) == 0) { p.Prep(parse, outerNC); return(parse.Errs != 0 || ctx.MallocFailed ? WRC.Abort : WRC.Prune); } bool isCompound = (p.Prior != null); // True if p is a compound select int compounds = 0; // Number of compound terms processed so far Select leftmost = p; // Left-most of SELECT of a compound int i; NameContext nc; // Name context of this SELECT while (p != null) { Debug.Assert((p.SelFlags & SF.Expanded) != 0); Debug.Assert((p.SelFlags & SF.Resolved) == 0); p.SelFlags |= SF.Resolved; // Resolve the expressions in the LIMIT and OFFSET clauses. These are not allowed to refer to any names, so pass an empty NameContext. nc = new NameContext(); //: _memset(&nc, 0, sizeof(nc)); nc.Parse = parse; if (Walker.ResolveExprNames(nc, ref p.Limit) || Walker.ResolveExprNames(nc, ref p.Offset)) { return(WRC.Abort); } // Recursively resolve names in all subqueries SrcList.SrcListItem item; for (i = 0; i < p.Src.Srcs; i++) { item = p.Src.Ids[i]; if (item.Select != null) { NameContext nc2; // Used to iterate name contexts int refs = 0; // Refcount for pOuterNC and outer contexts string savedContext = parse.AuthContext; // Count the total number of references to pOuterNC and all of its parent contexts. After resolving references to expressions in // pItem->pSelect, check if this value has changed. If so, then SELECT statement pItem->pSelect must be correlated. Set the // pItem->isCorrelated flag if this is the case. for (nc2 = outerNC; nc2 != null; nc2 = nc2.Next) { refs += nc2.Refs; } if (item.Name != null) { parse.AuthContext = item.Name; } Walker.ResolveSelectNames(parse, item.Select, outerNC); parse.AuthContext = savedContext; if (parse.Errs != 0 || ctx.MallocFailed) { return(WRC.Abort); } for (nc2 = outerNC; nc2 != null; nc2 = nc2.Next) { refs -= nc2.Refs; } Debug.Assert(!item.IsCorrelated && refs <= 0); item.IsCorrelated = (refs != 0); } } // Set up the local name-context to pass to sqlite3ResolveExprNames() to resolve the result-set expression list. nc.NCFlags = NC.AllowAgg; nc.SrcList = p.Src; nc.Next = outerNC; // Resolve names in the result set. ExprList list = p.EList; // Result set expression list Debug.Assert(list != null); for (i = 0; i < list.Exprs; i++) { Expr expr = list.Ids[i].Expr; if (Walker.ResolveExprNames(nc, ref expr)) { return(WRC.Abort); } } // If there are no aggregate functions in the result-set, and no GROUP BY expression, do not allow aggregates in any of the other expressions. Debug.Assert((p.SelFlags & SF.Aggregate) == 0); ExprList groupBy = p.GroupBy; // The GROUP BY clause if (groupBy != null || (nc.NCFlags & NC.HasAgg) != 0) { p.SelFlags |= SF.Aggregate; } else { nc.NCFlags &= ~NC.AllowAgg; } // If a HAVING clause is present, then there must be a GROUP BY clause. if (p.Having != null && groupBy == null) { parse.ErrorMsg("a GROUP BY clause is required before HAVING"); return(WRC.Abort); } // Add the expression list to the name-context before parsing the other expressions in the SELECT statement. This is so that // expressions in the WHERE clause (etc.) can refer to expressions by aliases in the result set. // // Minor point: If this is the case, then the expression will be re-evaluated for each reference to it. nc.EList = p.EList; if (Walker.ResolveExprNames(nc, ref p.Where) || Walker.ResolveExprNames(nc, ref p.Having)) { return(WRC.Abort); } // The ORDER BY and GROUP BY clauses may not refer to terms in outer queries nc.Next = null; nc.NCFlags |= NC.AllowAgg; // Process the ORDER BY clause for singleton SELECT statements. The ORDER BY clause for compounds SELECT statements is handled // below, after all of the result-sets for all of the elements of the compound have been resolved. if (!isCompound && Walker.ResolveOrderGroupBy(nc, p, p.OrderBy, "ORDER")) { return(WRC.Abort); } if (ctx.MallocFailed) { return(WRC.Abort); } // Resolve the GROUP BY clause. At the same time, make sure the GROUP BY clause does not contain aggregate functions. if (groupBy != null) { if (Walker.ResolveOrderGroupBy(nc, p, groupBy, "GROUP") || ctx.MallocFailed) { return(WRC.Abort); } ExprList.ExprListItem item2; for (i = 0; i < groupBy.Exprs; i++) { item2 = groupBy.Ids[i]; if (E.ExprHasProperty(item2.Expr, EP.Agg)) { parse.ErrorMsg("aggregate functions are not allowed in the GROUP BY clause"); return(WRC.Abort); } } } // Advance to the next term of the compound p = p.Prior; compounds++; } // Resolve the ORDER BY on a compound SELECT after all terms of the compound have been resolved. return(isCompound && ResolveCompoundOrderBy(parse, leftmost) != 0 ? WRC.Abort : WRC.Prune); }