Ejemplo n.º 1
0
 static void IncrAggFunctionDepth(Expr expr, int n)
 {
     if (n > 0)
     {
         Walker w = new Walker();
         w.ExprCallback = IncrAggDepth;
         w.u.I = n;
         w.WalkExpr(expr);
     }
 }
Ejemplo n.º 2
0
 public static void ResolveSelectNames(Parse parse, Select p, NameContext outerNC)
 {
     Debug.Assert(p != null);
     Walker w = new Walker();
     w.ExprCallback = ResolveExprStep;
     w.SelectCallback = ResolveSelectStep;
     w.Parse = parse;
     w.u.NC = outerNC;
     w.WalkSelect(p);
 }
Ejemplo n.º 3
0
        public static bool ResolveExprNames(NameContext nc, ref Expr expr)
        {
            if (expr == null) return false;
#if MAX_EXPR_DEPTH
            {
                Parse parse = nc.Parse;
                if (Expr.CheckHeight(parse, expr.Height + nc.Parse.Height) != 0)
                    return true;
                parse.Height += expr.Height;
            }
#endif
            bool savedHasAgg = ((nc.NCFlags & NC.HasAgg) != 0);
            nc.NCFlags &= ~NC.HasAgg;
            Walker w = new Walker();
            w.ExprCallback = ResolveExprStep;
            w.SelectCallback = ResolveSelectStep;
            w.Parse = nc.Parse;
            w.u.NC = nc;
            w.WalkExpr(expr);
#if MAX_EXPR_DEPTH
            nc.Parse.Height -= expr.Height;
#endif
            if (nc.Errs > 0 || w.Parse.Errs > 0)
                E.ExprSetProperty(expr, EP.Error);
            if ((nc.NCFlags & NC.HasAgg) != 0)
                E.ExprSetProperty(expr, EP.Agg);
            else if (savedHasAgg)
                nc.NCFlags |= NC.HasAgg;
            return E.ExprHasProperty(expr, EP.Error);
        }
Ejemplo n.º 4
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);
        }
Ejemplo n.º 5
0
        static WRC ResolveExprStep(Walker walker, Expr expr)
        {
            NameContext nc = walker.u.NC;
            Debug.Assert(nc != null);
            Parse parse = nc.Parse;
            Debug.Assert(parse == walker.Parse);

            if (E.ExprHasAnyProperty(expr, EP.Resolved)) return WRC.Prune;
            E.ExprSetProperty(expr, EP.Resolved);
#if !NDEBUG
            if (nc.SrcList != null && nc.SrcList.Allocs > 0)
            {
                SrcList srcList = nc.SrcList;
                for (int i = 0; i < nc.SrcList.Srcs; i++)
                    Debug.Assert(srcList.Ids[i].Cursor >= 0 && srcList.Ids[i].Cursor < parse.Tabs);
            }
#endif
            switch (expr.OP)
            {

#if ENABLE_UPDATE_DELETE_LIMIT && !OMIT_SUBQUERY
                // The special operator TK_ROW means use the rowid for the first column in the FROM clause.  This is used by the LIMIT and ORDER BY
                // clause processing on UPDATE and DELETE statements.
                case TK.ROW:
                    {
                        SrcList srcList = nc.SrcList;
                        Debug.Assert(srcList != null && srcList.Srcs == 1);
                        SrcList.SrcListItem item = srcList.Ids[0];
                        expr.OP = TK.COLUMN;
                        expr.Table = item.Table;
                        expr.TableId = item.Cursor;
                        expr.ColumnIdx = -1;
                        expr.Aff = AFF.INTEGER;
                        break;
                    }
#endif

                case TK.ID:  // A lone identifier is the name of a column.
                    {
                        return LookupName(parse, null, null, expr.u.Token, nc, expr);
                    }

                case TK.DOT: // A table name and column name: ID.ID Or a database, table and column: ID.ID.ID
                    {
                        string columnName;
                        string tableName;
                        string dbName;
                        // if (srcList == nullptr) break;
                        Expr right = expr.Right;
                        if (right.OP == TK.ID)
                        {
                            dbName = null;
                            tableName = expr.Left.u.Token;
                            columnName = right.u.Token;
                        }
                        else
                        {
                            Debug.Assert(right.OP == TK.DOT);
                            dbName = expr.Left.u.Token;
                            tableName = right.Left.u.Token;
                            columnName = right.Right.u.Token;
                        }
                        return LookupName(parse, dbName, tableName, columnName, nc, expr);
                    }

                case TK.CONST_FUNC:
                case TK.FUNCTION: // Resolve function names
                    {
                        ExprList list = expr.x.List; // The argument list
                        int n = (list != null ? list.Exprs : 0); // Number of arguments
                        bool noSuchFunc = false; // True if no such function exists
                        bool wrongNumArgs = false; // True if wrong number of arguments
                        bool isAgg = false; // True if is an aggregate function
                        TEXTENCODE encode = E.CTXENCODE(parse.Ctx); // The database encoding

                        C.ASSERTCOVERAGE(expr.OP == TK.CONST_FUNC);
                        Debug.Assert(!E.ExprHasProperty(expr, EP.xIsSelect));
                        string id = expr.u.Token; // The function name.
                        int idLength = id.Length; // Number of characters in function name
                        FuncDef def = Callback.FindFunction(parse.Ctx, id, idLength, n, encode, false); // Information about the function
                        if (def == null)
                        {
                            def = Callback.FindFunction(parse.Ctx, id, idLength, -2, encode, false);
                            if (def == null)
                                noSuchFunc = true;
                            else
                                wrongNumArgs = true;
                        }
                        else
                            isAgg = (def.Func == null);
#if !OMIT_AUTHORIZATION
                        if (def != null)
                        {
                            ARC auth = Auth.Check(parse, AUTH.FUNCTION, null, def.Name, null); // Authorization to use the function
                            if (auth != ARC.OK)
                            {
                                if (auth == ARC.DENY)
                                {
                                    parse.ErrorMsg("not authorized to use function: %s", def.Name);
                                    nc.Errs++;
                                }
                                expr.OP = TK.NULL;
                                return WRC.Prune;
                            }
                        }
#endif
                        if (isAgg && (nc.NCFlags & NC.AllowAgg) == 0)
                        {
                            parse.ErrorMsg("misuse of aggregate function %.*s()", idLength, id);
                            nc.Errs++;
                            isAgg = false;
                        }
                        else if (noSuchFunc && !ctx.Init.Busy)
                        {
                            parse.ErrorMsg("no such function: %.*s", idLength, id);
                            nc.Errs++;
                        }
                        else if (wrongNumArgs)
                        {
                            parse.ErrorMsg("wrong number of arguments to function %.*s()", idLength, id);
                            nc.Errs++;
                        }
                        if (isAgg) nc.NCFlags &= ~NC.AllowAgg;
                        walker.WalkExprList(list);
                        if (isAgg)
                        {
                            NameContext nc2 = nc;
                            expr.OP = TK.AGG_FUNCTION;
                            expr.OP2 = 0;
                            while (nc2 != null && !expr.FunctionUsesThisSrc(nc2.SrcList))
                            {
                                expr.OP2++;
                                nc2 = nc2.Next;
                            }
                            if (nc2 != null) nc2.NCFlags |= NC.HasAgg;
                            nc.NCFlags |= NC.AllowAgg;
                        }
                        // FIX ME:  Compute pExpr->affinity based on the expected return type of the function 
                        return WRC.Prune;
                    }
#if !OMIT_SUBQUERY
                case TK.SELECT:
                case TK.EXISTS:
                    {
                        C.ASSERTCOVERAGE(expr.OP == TK.EXISTS);
                        goto case TK.IN;
                    }
#endif
                case TK.IN:
                    {
                        C.ASSERTCOVERAGE(expr.OP == TK.IN);
                        if (E.ExprHasProperty(expr, EP.xIsSelect))
                        {
                            int refs = nc.Refs;
#if !OMIT_CHECK
                            if ((nc.NCFlags & NC.IsCheck) != 0)
                                parse.ErrorMsg("subqueries prohibited in CHECK constraints");
#endif
                            walker.WalkSelect(expr.x.Select);
                            Debug.Assert(nc.Refs >= refs);
                            if (refs != nc.Refs)
                                E.ExprSetProperty(expr, EP.VarSelect);
                        }
                        break;
                    }
#if !OMIT_CHECK
                case TK.VARIABLE:
                    {
                        if ((nc.NCFlags & NC.IsCheck) != 0)
                            parse.ErrorMsg("parameters prohibited in CHECK constraints");

                        break;
                    }
#endif
            }
            return (parse.Errs != 0 || parse.Ctx.MallocFailed ? WRC.Abort : WRC.Continue);
        }
Ejemplo n.º 6
0
 static WRC IncrAggDepth(Walker walker, Expr expr)
 {
     if (expr.OP == TK.AGG_FUNCTION) expr.OP2 += (byte)walker.u.I;
     return WRC.Continue;
 }
Ejemplo n.º 7
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);
        }
Ejemplo n.º 8
0
        static WRC ResolveExprStep(Walker walker, Expr expr)
        {
            NameContext nc = walker.u.NC;

            Debug.Assert(nc != null);
            Parse parse = nc.Parse;

            Debug.Assert(parse == walker.Parse);

            if (E.ExprHasAnyProperty(expr, EP.Resolved))
            {
                return(WRC.Prune);
            }
            E.ExprSetProperty(expr, EP.Resolved);
#if !NDEBUG
            if (nc.SrcList != null && nc.SrcList.Allocs > 0)
            {
                SrcList srcList = nc.SrcList;
                for (int i = 0; i < nc.SrcList.Srcs; i++)
                {
                    Debug.Assert(srcList.Ids[i].Cursor >= 0 && srcList.Ids[i].Cursor < parse.Tabs);
                }
            }
#endif
            switch (expr.OP)
            {
#if ENABLE_UPDATE_DELETE_LIMIT && !OMIT_SUBQUERY
            // The special operator TK_ROW means use the rowid for the first column in the FROM clause.  This is used by the LIMIT and ORDER BY
            // clause processing on UPDATE and DELETE statements.
            case TK.ROW:
            {
                SrcList srcList = nc.SrcList;
                Debug.Assert(srcList != null && srcList.Srcs == 1);
                SrcList.SrcListItem item = srcList.Ids[0];
                expr.OP       = TK.COLUMN;
                expr.Table    = item.Table;
                expr.TableId  = item.Cursor;
                expr.ColumnId = -1;
                expr.Aff      = AFF.INTEGER;
                break;
            }
#endif

            case TK.ID:      // A lone identifier is the name of a column.
            {
                return(LookupName(parse, null, null, expr.u.Token, nc, expr));
            }

            case TK.DOT:     // A table name and column name: ID.ID Or a database, table and column: ID.ID.ID
            {
                string columnName;
                string tableName;
                string dbName;
                // if (srcList == nullptr) break;
                Expr right = expr.Right;
                if (right.OP == TK.ID)
                {
                    dbName     = null;
                    tableName  = expr.Left.u.Token;
                    columnName = right.u.Token;
                }
                else
                {
                    Debug.Assert(right.OP == TK.DOT);
                    dbName     = expr.Left.u.Token;
                    tableName  = right.Left.u.Token;
                    columnName = right.Right.u.Token;
                }
                return(LookupName(parse, dbName, tableName, columnName, nc, expr));
            }

            case TK.CONST_FUNC:
            case TK.FUNCTION:                                              // Resolve function names
            {
                ExprList   list         = expr.x.List;                     // The argument list
                int        n            = (list != null ? list.Exprs : 0); // Number of arguments
                bool       noSuchFunc   = false;                           // True if no such function exists
                bool       wrongNumArgs = false;                           // True if wrong number of arguments
                bool       isAgg        = false;                           // True if is an aggregate function
                TEXTENCODE encode       = E.CTXENCODE(parse.Ctx);          // The database encoding

                C.ASSERTCOVERAGE(expr.OP == TK.CONST_FUNC);
                Debug.Assert(!E.ExprHasProperty(expr, EP.xIsSelect));
                string  id       = expr.u.Token;                                                     // The function name.
                int     idLength = id.Length;                                                        // Number of characters in function name
                FuncDef def      = Callback.FindFunction(parse.Ctx, id, idLength, n, encode, false); // Information about the function
                if (def == null)
                {
                    def = Callback.FindFunction(parse.Ctx, id, idLength, -2, encode, false);
                    if (def == null)
                    {
                        noSuchFunc = true;
                    }
                    else
                    {
                        wrongNumArgs = true;
                    }
                }
                else
                {
                    isAgg = (def.Func == null);
                }
#if !OMIT_AUTHORIZATION
                if (def != null)
                {
                    ARC auth = Auth.Check(parse, AUTH.FUNCTION, null, def.Name, null);         // Authorization to use the function
                    if (auth != ARC.OK)
                    {
                        if (auth == ARC.DENY)
                        {
                            parse.ErrorMsg("not authorized to use function: %s", def.Name);
                            nc.Errs++;
                        }
                        expr.OP = TK.NULL;
                        return(WRC.Prune);
                    }
                }
#endif
                if (isAgg && (nc.NCFlags & NC.AllowAgg) == 0)
                {
                    parse.ErrorMsg("misuse of aggregate function %.*s()", idLength, id);
                    nc.Errs++;
                    isAgg = false;
                }
                else if (noSuchFunc && !ctx.Init.Busy)
                {
                    parse.ErrorMsg("no such function: %.*s", idLength, id);
                    nc.Errs++;
                }
                else if (wrongNumArgs)
                {
                    parse.ErrorMsg("wrong number of arguments to function %.*s()", idLength, id);
                    nc.Errs++;
                }
                if (isAgg)
                {
                    nc.NCFlags &= ~NC.AllowAgg;
                }
                walker.WalkExprList(list);
                if (isAgg)
                {
                    NameContext nc2 = nc;
                    expr.OP  = TK.AGG_FUNCTION;
                    expr.OP2 = 0;
                    while (nc2 != null && !expr.FunctionUsesThisSrc(nc2.SrcList))
                    {
                        expr.OP2++;
                        nc2 = nc2.Next;
                    }
                    if (nc2 != null)
                    {
                        nc2.NCFlags |= NC.HasAgg;
                    }
                    nc.NCFlags |= NC.AllowAgg;
                }
                // FIX ME:  Compute pExpr->affinity based on the expected return type of the function
                return(WRC.Prune);
            }

#if !OMIT_SUBQUERY
            case TK.SELECT:
            case TK.EXISTS:
            {
                C.ASSERTCOVERAGE(expr.OP == TK.EXISTS);
                goto case TK.IN;
            }
#endif
            case TK.IN:
            {
                C.ASSERTCOVERAGE(expr.OP == TK.IN);
                if (E.ExprHasProperty(expr, EP.xIsSelect))
                {
                    int refs = nc.Refs;
#if !OMIT_CHECK
                    if ((nc.NCFlags & NC.IsCheck) != 0)
                    {
                        parse.ErrorMsg("subqueries prohibited in CHECK constraints");
                    }
#endif
                    walker.WalkSelect(expr.x.Select);
                    Debug.Assert(nc.Refs >= refs);
                    if (refs != nc.Refs)
                    {
                        E.ExprSetProperty(expr, EP.VarSelect);
                    }
                }
                break;
            }

#if !OMIT_CHECK
            case TK.VARIABLE:
            {
                if ((nc.NCFlags & NC.IsCheck) != 0)
                {
                    parse.ErrorMsg("parameters prohibited in CHECK constraints");
                }

                break;
            }
#endif
            }
            return(parse.Errs != 0 || parse.Ctx.MallocFailed ? WRC.Abort : WRC.Continue);
        }
Ejemplo n.º 9
0
        static WRC LookupName(Parse parse, string dbName, string tableName, string colName, NameContext nc, Expr expr)
        {
            int     cnt       = 0;            // Number of matching column names
            int     cntTab    = 0;            // Number of matching table names
            int     subquerys = 0;            // How many levels of subquery
            Context ctx       = parse.Ctx;    // The database connection

            SrcList.SrcListItem item;         // Use for looping over pSrcList items
            SrcList.SrcListItem match = null; // The matching pSrcList item
            NameContext         topNC = nc;   // First namecontext in the list
            Schema schema = null;             // Schema of the expression
            bool   isTrigger = false;
            int    i, j;

            Debug.Assert(nc != null);      // the name context cannot be NULL.
            Debug.Assert(colName != null); // The Z in X.Y.Z cannot be NULL
            Debug.Assert(!E.ExprHasAnyProperty(expr, EP.TokenOnly | EP.Reduced));

            // Initialize the node to no-match
            expr.TableId = -1;
            expr.Table   = null;
            E.ExprSetIrreducible(expr);

            // Translate the schema name in zDb into a pointer to the corresponding schema.  If not found, pSchema will remain NULL and nothing will match
            // resulting in an appropriate error message toward the end of this routine
            if (dbName != null)
            {
                for (i = 0; i < ctx.DBs.length; i++)
                {
                    Debug.Assert(ctx.DBs[i].Name != null);
                    if (string.Compare(ctx.DBs[i].Name, dbName) == 0)
                    {
                        schema = ctx.DBs[i].Schema;
                        break;
                    }
                }
            }

            // Start at the inner-most context and move outward until a match is found
            while (nc != null && cnt == 0)
            {
                ExprList list;
                SrcList  srcList = nc.SrcList;
                if (srcList != null)
                {
                    for (i = 0; i < srcList.Srcs; i++)
                    {
                        item = srcList.Ids[i];
                        Table table = item.Table;
                        Debug.Assert(table != null && table.Name != null);
                        Debug.Assert(table.Cols.length > 0);
                        if (item.Select != null && (item.Select.SelFlags & SF.NestedFrom) != 0)
                        {
                            bool hit = false;
                            list = item.Select.EList;
                            for (j = 0; j < list.Exprs; j++)
                            {
                                if (Walker.MatchSpanName(list.Ids[j].Span, colName, tableName, dbName))
                                {
                                    cnt++;
                                    cntTab        = 2;
                                    match         = item;
                                    expr.ColumnId = j;
                                    hit           = true;
                                }
                            }
                            if (hit || table == null)
                            {
                                continue;
                            }
                        }
                        if (dbName != null && table.Schema != schema)
                        {
                            continue;
                        }
                        if (tableName != null)
                        {
                            string tableName2 = (item.Alias != null ? item.Alias : table.Name);
                            Debug.Assert(tableName2 != null);
                            if (!string.Equals(tableName2, tableName, StringComparison.OrdinalIgnoreCase))
                            {
                                continue;
                            }
                        }
                        if (cntTab++ == 0)
                        {
                            match = item;
                        }
                        Column col;
                        for (j = 0; j < table.Cols.length; j++)
                        {
                            col = table.Cols[j];
                            if (string.Equals(col.Name, colName, StringComparison.InvariantCultureIgnoreCase))
                            {
                                // If there has been exactly one prior match and this match is for the right-hand table of a NATURAL JOIN or is in a
                                // USING clause, then skip this match.
                                if (cnt == 1)
                                {
                                    if ((item.Jointype & JT.NATURAL) != 0)
                                    {
                                        continue;
                                    }
                                    if (NameInUsingClause(item.Using, colName))
                                    {
                                        continue;
                                    }
                                }
                                cnt++;
                                match = item;
                                // Substitute the rowid (column -1) for the INTEGER PRIMARY KEY
                                expr.ColumnId = (j == table.PKey ? -1 : (short)j);
                                break;
                            }
                        }
                    }
                    if (match != null)
                    {
                        expr.TableId = match.Cursor;
                        expr.Table   = match.Table;
                        schema       = expr.Table.Schema;
                    }
                }

#if !OMIT_TRIGGER
                // If we have not already resolved the name, then maybe it is a new.* or old.* trigger argument reference
                if (dbName == null && tableName != null && cnt == 0 && parse.TriggerTab != null)
                {
                    TK    op    = parse.TriggerOp;
                    Table table = null;
                    Debug.Assert(op == TK.DELETE || op == TK.UPDATE || op == TK.INSERT);
                    if (op != TK.DELETE && string.Equals("new", tableName, StringComparison.InvariantCultureIgnoreCase))
                    {
                        expr.TableId = 1;
                        table        = parse.TriggerTab;
                    }
                    else if (op != TK.INSERT && string.Equals("old", tableName, StringComparison.InvariantCultureIgnoreCase))
                    {
                        expr.TableId = 0;
                        table        = parse.TriggerTab;
                    }
                    if (table != null)
                    {
                        int colId;
                        schema = table.Schema;
                        cntTab++;
                        for (colId = 0; colId < table.Cols.length; colId++)
                        {
                            Column col = table.Cols[colId];
                            if (string.Equals(col.Name, colName, StringComparison.InvariantCultureIgnoreCase))
                            {
                                if (colId == table.PKey)
                                {
                                    colId = -1;
                                }
                                break;
                            }
                        }
                        if (colId >= table.Cols.length && Expr.IsRowid(colName))
                        {
                            colId = -1; // IMP: R-44911-55124
                        }
                        if (colId < table.Cols.length)
                        {
                            cnt++;
                            if (colId < 0)
                            {
                                expr.Aff = AFF.INTEGER;
                            }
                            else if (expr.TableId == 0)
                            {
                                C.ASSERTCOVERAGE(colId == 31);
                                C.ASSERTCOVERAGE(colId == 32);
                                parse.Oldmask |= (colId >= 32 ? 0xffffffff : (((uint)1) << colId));
                            }
                            else
                            {
                                C.ASSERTCOVERAGE(colId == 31);
                                C.ASSERTCOVERAGE(colId == 32);
                                parse.Newmask |= (colId >= 32 ? 0xffffffff : (((uint)1) << colId));
                            }
                            expr.ColumnId = (short)colId;
                            expr.Table    = table;
                            isTrigger     = true;
                        }
                    }
                }
#endif

                // Perhaps the name is a reference to the ROWID
                if (cnt == 0 && cntTab == 1 && Expr.IsRowid(colName))
                {
                    cnt           = 1;
                    expr.ColumnId = -1; // IMP: R-44911-55124
                    expr.Aff      = AFF.INTEGER;
                }

                // If the input is of the form Z (not Y.Z or X.Y.Z) then the name Z might refer to an result-set alias.  This happens, for example, when
                // we are resolving names in the WHERE clause of the following command:
                //
                //     SELECT a+b AS x FROM table WHERE x<10;
                //
                // In cases like this, replace pExpr with a copy of the expression that forms the result set entry ("a+b" in the example) and return immediately.
                // Note that the expression in the result set should have already been resolved by the time the WHERE clause is resolved.
                if (cnt == 0 && (list = nc.EList) != null && tableName == null)
                {
                    for (j = 0; j < list.Exprs; j++)
                    {
                        string asName = list.Ids[j].Name;
                        if (asName != null && string.Equals(asName, colName, StringComparison.InvariantCultureIgnoreCase))
                        {
                            Debug.Assert(expr.Left == null && expr.Right == null);
                            Debug.Assert(expr.x.List == null);
                            Debug.Assert(expr.x.Select == null);
                            Expr orig = list.Ids[j].Expr;
                            if ((nc.NCFlags & NC.AllowAgg) == 0 && E.ExprHasProperty(orig, EP.Agg))
                            {
                                parse.ErrorMsg("misuse of aliased aggregate %s", asName);
                                return(WRC.Abort);
                            }
                            ResolveAlias(parse, list, j, expr, "", subquerys);
                            cnt   = 1;
                            match = null;
                            Debug.Assert(tableName == null && dbName == null);
                            goto lookupname_end;
                        }
                    }
                }

                // Advance to the next name context.  The loop will exit when either we have a match (cnt>0) or when we run out of name contexts.
                if (cnt == 0)
                {
                    nc = nc.Next;
                    subquerys++;
                }
            }

            // If X and Y are NULL (in other words if only the column name Z is supplied) and the value of Z is enclosed in double-quotes, then
            // Z is a string literal if it doesn't match any column names.  In that case, we need to return right away and not make any changes to
            // pExpr.
            //
            // Because no reference was made to outer contexts, the pNC->nRef fields are not changed in any context.
            if (cnt == 0 && tableName == null && E.ExprHasProperty(expr, EP.DblQuoted))
            {
                expr.OP    = TK.STRING;
                expr.Table = null;
                return(WRC.Prune);
            }

            // cnt==0 means there was not match.  cnt>1 means there were two or more matches.  Either way, we have an error.
            if (cnt != 1)
            {
                string err = (cnt == 0 ? "no such column" : "ambiguous column name");
                if (dbName != null)
                {
                    parse.ErrorMsg("%s: %s.%s.%s", err, dbName, tableName, colName);
                }
                else if (tableName != null)
                {
                    parse.ErrorMsg("%s: %s.%s", err, tableName, colName);
                }
                else
                {
                    parse.ErrorMsg("%s: %s", err, colName);
                }
                parse.CheckSchema = 1;
                topNC.Errs++;
            }

            // If a column from a table in pSrcList is referenced, then record this fact in the pSrcList.a[].colUsed bitmask.  Column 0 causes
            // bit 0 to be set.  Column 1 sets bit 1.  And so forth.  If the column number is greater than the number of bits in the bitmask
            // then set the high-order bit of the bitmask.
            if (expr.ColumnId >= 0 && match != null)
            {
                int n = expr.ColumnId;
                C.ASSERTCOVERAGE(n == BMS - 1);
                if (n >= BMS)
                {
                    n = BMS - 1;
                }
                Debug.Assert(match.Cursor == expr.TableId);
                match.ColUsed |= ((Bitmask)1) << n;
            }

            // Clean up and return
            Expr.Delete(ctx, ref expr.Left);
            expr.Left = null;
            Expr.Delete(ctx, ref expr.Right);
            expr.Right = null;
            expr.OP    = (isTrigger ? TK.TRIGGER : TK.COLUMN);
lookupname_end:
            if (cnt == 1)
            {
                Debug.Assert(nc != null);
                Auth.Read(parse, expr, schema, nc.SrcList);
                // Increment the nRef value on all name contexts from TopNC up to the point where the name matched.
                for (; ;)
                {
                    Debug.Assert(topNC != null);
                    topNC.Refs++;
                    if (topNC == nc)
                    {
                        break;
                    }
                    topNC = topNC.Next;
                }
                return(WRC.Prune);
            }
            return(WRC.Abort);
        }