static void ResolveAlias(Parse parse, ExprList list, int colId, Expr expr, string type, int subqueries) { Debug.Assert(colId >= 0 && colId < list.Exprs); Expr orig = list.Ids[colId].Expr; // The iCol-th column of the result set Debug.Assert(orig != null); Debug.Assert((orig.Flags & EP.Resolved) != 0); Context ctx = parse.Ctx; // The database connection Expr dup = Expr.Dup(ctx, orig, 0); // Copy of pOrig if (orig.OP != TK.COLUMN && (type.Length == 0 || type[0] != 'G')) { IncrAggFunctionDepth(dup, subqueries); dup = Expr.PExpr_(parse, TK.AS, dup, null, null); if (dup == null) return; if (list.Ids[colId].Alias == 0) list.Ids[colId].Alias = (ushort)(++parse.Alias.length); dup.TableId = list.Ids[colId].Alias; } if (expr.OP == TK.COLLATE) dup = Expr.AddCollateString(parse, dup, expr.u.Token); // Before calling sqlite3ExprDelete(), set the EP_Static flag. This prevents ExprDelete() from deleting the Expr structure itself, // allowing it to be repopulated by the memcpy() on the following line. E.ExprSetProperty(expr, EP.Static); Expr.Delete(ctx, ref expr); expr.memcpy(dup); if (!E.ExprHasProperty(expr, EP.IntValue) && expr.u.Token != null) { Debug.Assert((dup.Flags & (EP.Reduced | EP.TokenOnly)) == 0); dup.u.Token = expr.u.Token; dup.Flags2 |= EP2.MallocedToken; } C._tagfree(ctx, ref dup); }
public Backup Next; // Next backup associated with source pager static Btree FindBtree(Context errorCtx, Context ctx, string dbName) { int i = Parse.FindDbName(ctx, dbName); if (i == 1) { RC rc = 0; Parse parse = new Parse(); if (parse == null) { Main.Error(errorCtx, RC.NOMEM, "out of memory"); rc = RC.NOMEM; } else { parse.Ctx = ctx; if (parse.OpenTempDatabase() != 0) { Main.Error(errorCtx, parse.RC, "%s", parse.ErrMsg); rc = RC.ERROR; } C._tagfree(errorCtx, ref parse.ErrMsg); } if (rc != 0) return null; } if (i < 0) { Main.Error(errorCtx, RC.ERROR, "unknown database %s", dbName); return null; } return ctx.DBs[i].Bt; }
public static Trigger List(Parse parse, Table table) { Schema tmpSchema = parse.Ctx.DBs[1].Schema; Trigger list = null; // List of triggers to return if (parse.DisableTriggers) return null; if (tmpSchema != table.Schema) { Debug.Assert(Btree.SchemaMutexHeld(parse.Ctx, 0, tmpSchema)); for (HashElem p = tmpSchema.TriggerHash.First; p != null; p = p.Next) { Trigger trig = (Trigger)p.Data; if (trig.TabSchema == table.Schema && string.Equals(trig.Table, table.Name, StringComparison.InvariantCultureIgnoreCase)) { trig.Next = (list != null ? list : table.Triggers); list = trig; } } } return (list != null ? list : table.Triggers); }
public static Select New(Parse parse, ExprList list, SrcList src, Expr where_, ExprList groupBy, Expr having, ExprList orderBy, SF selFlags, Expr limit, Expr offset) { Context ctx = parse.Ctx; Select newSelect = new Select(); Debug.Assert(ctx.MallocFailed || offset == null || limit != null); // OFFSET implies LIMIT // Select standin; if (newSelect == null) { Debug.Assert(ctx.MallocFailed); //newSelect = standin; //_memset(newSelect, 0, sizeof(newSelect)); } if (list == null) list = Expr.ListAppend(parse, null, Expr.Expr_(ctx, TK.ALL, null)); newSelect.EList = list; if (src == null) src = new SrcList(); newSelect.Src = src; newSelect.Where = where_; newSelect.GroupBy = groupBy; newSelect.Having = having; newSelect.OrderBy = orderBy; newSelect.SelFlags = selFlags; newSelect.OP = TK.SELECT; newSelect.Limit = limit; newSelect.Offset = offset; Debug.Assert(offset == null || limit != null); newSelect.AddrOpenEphms[0] = (OP) - 1; newSelect.AddrOpenEphms[1] = (OP) - 1; newSelect.AddrOpenEphms[2] = (OP) - 1; if (ctx.MallocFailed) { ClearSelect(ctx, newSelect); //if (newSelect != standin) C._tagfree(ctx, ref newSelect); newSelect = null; } else Debug.Assert(newSelect.Src != null || parse.Errs > 0); //Debug.Assert(newSelect != standin); return newSelect; }
static void GenerateSortTail(Parse parse, Select p, Vdbe v, int columns, SelectDest dest) { int addrBreak = v.MakeLabel(); // Jump here to exit loop int addrContinue = v.MakeLabel(); // Jump here for next cycle ExprList orderBy = p.OrderBy; SRT dest2 = dest.Dest; int parmId = dest.SDParmId; int tabId = orderBy.ECursor; int regRow = Expr.GetTempReg(parse); int pseudoTab = 0; int regRowid; if (dest2 == SRT.Output || dest2 == SRT.Coroutine) { pseudoTab = parse.Tabs++; v.AddOp3(OP.OpenPseudo, pseudoTab, regRow, columns); regRowid = 0; } else regRowid = Expr.GetTempReg(parse); int addr; if ((p.SelFlags & SF.UseSorter) != 0) { int regSortOut = ++parse.Mems; int ptab2 = parse.Tabs++; v.AddOp3(OP.OpenPseudo, ptab2, regSortOut, orderBy.Exprs + 2); addr = 1 + v.AddOp2(OP.SorterSort, tabId, addrBreak); CodeOffset(v, p, addrContinue); v.AddOp2(OP.SorterData, tabId, regSortOut); v.AddOp3(OP.Column, ptab2, orderBy.Exprs + 1, regRow); v.ChangeP5(Vdbe.OPFLAG.CLEARCACHE); } else { addr = 1 + v.AddOp2(OP.Sort, tabId, addrBreak); CodeOffset(v, p, addrContinue); v.AddOp3(OP.Column, tabId, orderBy.Exprs + 1, regRow); } switch (dest2) { case SRT.Table: case SRT.EphemTab: { C.ASSERTCOVERAGE(dest2 == SRT.Table); C.ASSERTCOVERAGE(dest2 == SRT.EphemTab); v.AddOp2(OP.NewRowid, parmId, regRowid); v.AddOp3(OP.Insert, parmId, regRow, regRowid); v.ChangeP5(Vdbe.OPFLAG.APPEND); break; } #if !OMIT_SUBQUERY case SRT.Set: { Debug.Assert(columns == 1); v.AddOp4(OP.MakeRecord, regRow, 1, regRowid, dest->AffSdst, 1); Expr.CacheAffinityChange(parse, regRow, 1); v.AddOp2(OP.IdxInsert, parmId, regRowid); break; } case SRT.Mem: { Debug.Assert(columns == 1); Expr.CodeMove(parse, regRow, parmId, 1); // The LIMIT clause will terminate the loop for us break; } #endif default: { Debug.Assert(dest2 == SRT.Output || dest2 == SRT.Coroutine); C.ASSERTCOVERAGE(dest2 == SRT.Output); C.ASSERTCOVERAGE(dest2 == SRT.Coroutine); for (int i = 0; i < columns; i++) { Debug.Assert(regRow != dest.SdstId + i); v.AddOp3(OP.Column, pseudoTab, i, dest.SdstId + i); if (i == 0) v.ChangeP5(Vdbe.OPFLAG.CLEARCACHE); } if (dest2 == SRT.Output) { v.AddOp2(OP.ResultRow, dest.SdstId, columns); Expr.CacheAffinityChange(parse, dest.SdstId, columns); } else v.AddOp1(OP.Yield, dest.SDParmId); break; } } Expr.ReleaseTempReg(parse, regRow); Expr.ReleaseTempReg(parse, regRowid); // The bottom of the loop v.ResolveLabel(addrContinue); v.AddOp2(OP.Next, tabId, addr); v.ResolveLabel(addrBreak); if (dest2 == SRT.Output || dest2 == SRT.Coroutine) v.AddOp2(OP.Close, pseudoTab, 0); }
static void GenerateColumnNames(Parse parse, SrcList tabList, ExprList list) { #if !OMIT_EXPLAIN // If this is an EXPLAIN, skip this step if (parse.Explain != 0) return; #endif Vdbe v = parse.V; Context ctx = parse.Ctx; if (parse.ColNamesSet != 0 || C._NEVER(v == null) || ctx.MallocFailed) return; parse.ColNamesSet = 1; bool fullNames = ((ctx.Flags & Context.FLAG.FullColNames) != 0); bool shortNames = ((ctx.Flags & Context.FLAG.ShortColNames) != 0); v.SetNumCols(list.Exprs); for (int i = 0; i < list.Exprs; i++) { Expr p = list.Ids[i].Expr; if (C._NEVER(p == null)) continue; if (list.Ids[i].Name != null) { string name = list.Ids[i].Name; v.SetColName(i, COLNAME_NAME, name, C.DESTRUCTOR_TRANSIENT); } else if ((p.OP == TK.COLUMN || p.OP == TK.AGG_COLUMN) && tabList != null) { int colId = p.ColumnIdx; int j; for (j = 0; C._ALWAYS(j < tabList.Srcs); j++) if (tabList.Ids[j].Cursor == p.TableId) break; Debug.Assert(j < tabList.Srcs); Table table = tabList.Ids[j].Table; if (colId < 0) colId = table.PKey; Debug.Assert(colId == -1 || (colId >= 0 && colId < table.Cols.length)); string colName = (colId < 0 ? "rowid" : table.Cols[colId].Name); if (!shortNames && !fullNames) v.SetColName(i, COLNAME_NAME, list.Ids[i].Span, C.DESTRUCTOR_DYNAMIC); else if (fullNames) { string name = C._mtagprintf(ctx, "%s.%s", table.Name, colName); v.SetColName(i, COLNAME_NAME, name, C.DESTRUCTOR_DYNAMIC); } else v.SetColName(i, COLNAME_NAME, colName, C.DESTRUCTOR_TRANSIENT); } else v.SetColName(i, COLNAME_NAME, list.Ids[i].Span, C.DESTRUCTOR_TRANSIENT); } GenerateColumnTypes(parse, tabList, list); }
static void CodeDistinct(Parse parse, int table, int addrRepeatId, int n, int memId) { Vdbe v = parse.V; int r1 = Expr.GetTempReg(parse); v.AddOp4Int(OP.Found, table, addrRepeatId, memId, n); v.AddOp3(OP.MakeRecord, memId, n, r1); v.AddOp2(OP.IdxInsert, table, r1); Expr.ReleaseTempReg(parse, r1); }
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); }
static void AddWhereTerm(Parse parse, SrcList src, int leftId, int colLeftId, int rightId, int colRightId, bool isOuterJoin, ref Expr where_) { Context ctx = parse.Ctx; Debug.Assert(leftId < rightId); Debug.Assert(src.Srcs > rightId); Debug.Assert(src.Ids[leftId].Table != null); Debug.Assert(src.Ids[rightId].Table != null); Expr e1 = Walker.CreateColumnExpr(ctx, src, leftId, colLeftId); Expr e2 = Walker.CreateColumnExpr(ctx, src, rightId, colRightId); Expr eq = Expr.PExpr_(parse, TK.EQ, e1, e2, null); if (eq != null && isOuterJoin) { E.ExprSetProperty(eq, EP.FromJoin); Debug.Assert(!E.ExprHasAnyProperty(eq, EP.TokenOnly | EP.Reduced)); E.ExprSetIrreducible(eq); eq.RightJoinTable = (short)e2.TableId; } where_ = Expr.And(ctx, where_, eq); }
static void PushOntoSorter(Parse parse, ExprList orderBy, Select select, int regData) { Vdbe v = parse.V; int exprs = orderBy.Exprs; int regBase = Expr.GetTempRange(parse, exprs + 2); int regRecord = Expr.GetTempReg(parse); Expr.CacheClear(parse); Expr.CodeExprList(parse, orderBy, regBase, false); v.AddOp2(OP.Sequence, orderBy.ECursor, regBase + exprs); Expr.CodeMove(parse, regData, regBase + exprs + 1, 1); v.AddOp3(OP.MakeRecord, regBase, exprs + 2, regRecord); v.AddOp2(OP.IdxInsert, orderBy.ECursor, regRecord); Expr.ReleaseTempReg(parse, regRecord); Expr.ReleaseTempRange(parse, regBase, exprs + 2); if (select.LimitId != 0) { int limitId = (select.OffsetId != 0 ? select.OffsetId + 1 : select.LimitId); int addr1 = v.AddOp1(OP.IfZero, limitId); v.AddOp2(OP.AddImm, limitId, -1); int addr2 = v.AddOp0(OP.Goto); v.JumpHere(addr1); v.AddOp1(OP.Last, orderBy.ECursor); v.AddOp1(OP.Delete, orderBy.ECursor); v.JumpHere(addr2); } }
static int CodeTriggerProgram(Parse parse, TriggerStep stepList, OE orconf) { Vdbe v = parse.V; Context ctx = parse.Ctx; Debug.Assert(parse.TriggerTab != null && parse.Toplevel != null); Debug.Assert(stepList != null); Debug.Assert(v != null); for (TriggerStep step = stepList; step != null; step = step.Next) { // Figure out the ON CONFLICT policy that will be used for this step of the trigger program. If the statement that caused this trigger // to fire had an explicit ON CONFLICT, then use it. Otherwise, use the ON CONFLICT policy that was specified as part of the trigger // step statement. Example: // // CREATE TRIGGER AFTER INSERT ON t1 BEGIN; // INSERT OR REPLACE INTO t2 VALUES(new.a, new.b); // END; // // INSERT INTO t1 ... ; -- insert into t2 uses REPLACE policy // INSERT OR IGNORE INTO t1 ... ; -- insert into t2 uses IGNORE policy parse.Orconf = (orconf == OE.Default ? step.Orconf : orconf); switch (step.OP) { case TK.UPDATE: Update(parse, TargetSrcList(parse, step), Expr.ListDup(ctx, step.ExprList, 0), Expr.Dup(ctx, step.Where, 0), parse.Orconf); break; case TK.INSERT: Insert(parse, TargetSrcList(parse, step), Expr.ListDup(ctx, step.ExprList, 0), Select.Dup(ctx, step.Select, 0), Expr.IdListDup(ctx, step.IdList), parse.Orconf); break; case TK.DELETE: DeleteFrom(parse, TargetSrcList(parse, step), Expr.Dup(ctx, step.Where, 0)); break; default: Debug.Assert(step.OP == TK.SELECT); SelectDest sDest = new SelectDest(); Select select = Expr.SelectDup(ctx, step.Select, 0); Select.DestInit(sDest, SRT.Discard, 0); Select.Select_(parse, select, ref sDest); Select.Delete(ctx, ref select); break; } if (step.OP != TK.SELECT) v.AddOp0(OP.ResetCount); } return 0; }
public static JT JoinType(Parse parse, Token a, Token b, int null_4) { return JoinType(parse, a, b, null); }
static TriggerPrg CodeRowTrigger(Parse parse, Trigger trigger, Table table, OE orconf) { Parse top = E.Parse_Toplevel(parse); Context ctx = parse.Ctx; // Database handle Debug.Assert(trigger.Name == null || table == TableOfTrigger(trigger)); Debug.Assert(top.V != null); // Allocate the TriggerPrg and SubProgram objects. To ensure that they are freed if an error occurs, link them into the Parse.pTriggerPrg // list of the top-level Parse object sooner rather than later. TriggerPrg prg = new TriggerPrg(); // Value to return //: _tagalloc(ctx, sizeof(TriggerPrg), true); if (prg == null) { return(null); } prg.Next = top.TriggerPrg; top.TriggerPrg = prg; Vdbe.SubProgram program; // Sub-vdbe for trigger program prg.Program = program = new Vdbe.SubProgram(); // sqlite3DbMallocZero( db, sizeof( SubProgram ) ); if (program == null) { return(null); } top.V.LinkSubProgram(program); prg.Trigger = trigger; prg.Orconf = orconf; prg.Colmasks[0] = 0xffffffff; prg.Colmasks[1] = 0xffffffff; // Allocate and populate a new Parse context to use for coding the trigger sub-program. Parse subParse = new Parse(); // Parse context for sub-vdbe //: _scratchalloc(ctx, sizeof(Parse), true); if (subParse == null) { return(null); } NameContext sNC = new NameContext(); // Name context for sub-vdbe sNC.Parse = subParse; subParse.Ctx = ctx; subParse.TriggerTab = table; subParse.Toplevel = top; subParse.AuthContext = trigger.Name; subParse.TriggerOp = trigger.OP; subParse.QueryLoops = parse.QueryLoops; int endTrigger = 0; // Label to jump to if WHEN is false Vdbe v = subParse.GetVdbe(); // Temporary VM if (v != null) { #if DEBUG v.Comment("Start: %s.%s (%s %s%s%s ON %s)", trigger.Name, OnErrorText(orconf), (trigger.TRtm == TRIGGER.BEFORE ? "BEFORE" : "AFTER"), (trigger.OP == TK.UPDATE ? "UPDATE" : string.Empty), (trigger.OP == TK.INSERT ? "INSERT" : string.Empty), (trigger.OP == TK.DELETE ? "DELETE" : string.Empty), table.Name); #endif #if !OMIT_TRACE v.ChangeP4(-1, C._mtagprintf(ctx, "-- TRIGGER %s", trigger.Name), Vdbe.P4T.DYNAMIC); #endif // If one was specified, code the WHEN clause. If it evaluates to false (or NULL) the sub-vdbe is immediately halted by jumping to the // OP_Halt inserted at the end of the program. if (trigger.When != null) { Expr when = Expr.Dup(ctx, trigger.When, 0); // Duplicate of trigger WHEN expression if (ResolveExprNames(sNC, ref when) == RC.OK && !ctx.MallocFailed) { endTrigger = v.MakeLabel(); subParse.IfFalse(when, endTrigger, RC_JUMPIFNULL); } Expr.Delete(ctx, ref when); } // Code the trigger program into the sub-vdbe. CodeTriggerProgram(subParse, trigger.StepList, orconf); // Insert an OP_Halt at the end of the sub-program. if (endTrigger != 0) { v.ResolveLabel(endTrigger); } v.AddOp0(Core.OP.Halt); #if DEBUG v.Comment("End: %s.%s", trigger.Name, OnErrorText(orconf)); #endif TransferParseError(parse, subParse); if (!ctx.MallocFailed) { program.Ops.data = v.TakeOpArray(ref program.Ops.length, ref top.MaxArgs); } program.Mems = subParse.Mems; program.Csrs = subParse.Tabs; program.Token = trigger.GetHashCode(); prg.Colmasks[0] = subParse.Oldmask; prg.Colmasks[1] = subParse.Newmask; Vdbe.Delete(v); } Debug.Assert(subParse.Ainc == null && subParse.ZombieTab == null); Debug.Assert(subParse.TriggerPrg == null && subParse.MaxArgs == 0); C._scratchfree(ctx, ref subParse); return(prg); }
static int CodeTriggerProgram(Parse parse, TriggerStep stepList, OE orconf) { Vdbe v = parse.V; Context ctx = parse.Ctx; Debug.Assert(parse.TriggerTab != null && parse.Toplevel != null); Debug.Assert(stepList != null); Debug.Assert(v != null); for (TriggerStep step = stepList; step != null; step = step.Next) { // Figure out the ON CONFLICT policy that will be used for this step of the trigger program. If the statement that caused this trigger // to fire had an explicit ON CONFLICT, then use it. Otherwise, use the ON CONFLICT policy that was specified as part of the trigger // step statement. Example: // // CREATE TRIGGER AFTER INSERT ON t1 BEGIN; // INSERT OR REPLACE INTO t2 VALUES(new.a, new.b); // END; // // INSERT INTO t1 ... ; -- insert into t2 uses REPLACE policy // INSERT OR IGNORE INTO t1 ... ; -- insert into t2 uses IGNORE policy parse.Orconf = (orconf == OE.Default ? step.Orconf : orconf); switch (step.OP) { case TK.UPDATE: Update(parse, TargetSrcList(parse, step), Expr.ListDup(ctx, step.ExprList, 0), Expr.Dup(ctx, step.Where, 0), parse.Orconf); break; case TK.INSERT: Insert(parse, TargetSrcList(parse, step), Expr.ListDup(ctx, step.ExprList, 0), Select.Dup(ctx, step.Select, 0), Expr.IdListDup(ctx, step.IdList), parse.Orconf); break; case TK.DELETE: DeleteFrom(parse, TargetSrcList(parse, step), Expr.Dup(ctx, step.Where, 0)); break; default: Debug.Assert(step.OP == TK.SELECT); SelectDest sDest = new SelectDest(); Select select = Expr.SelectDup(ctx, step.Select, 0); Select.DestInit(sDest, SRT.Discard, 0); Select.Select_(parse, select, ref sDest); Select.Delete(ctx, ref select); break; } if (step.OP != TK.SELECT) { v.AddOp0(OP.ResetCount); } } 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); }
static int ResolveCompoundOrderBy(Parse parse, Select select) { ExprList orderBy = select.OrderBy; if (orderBy == null) { return(0); } Context ctx = parse.Ctx; #if true || MAX_COLUMN if (orderBy.Exprs > ctx.Limits[(int)LIMIT.COLUMN]) { parse.ErrorMsg("too many terms in ORDER BY clause"); return(1); } #endif int i; for (i = 0; i < orderBy.Exprs; i++) { orderBy.Ids[i].Done = false; } select.Next = null; while (select.Prior != null) { select.Prior.Next = select; select = select.Prior; } bool moreToDo = true; while (select != null && moreToDo) { moreToDo = false; ExprList list = select.EList; Debug.Assert(list != null); ExprList.ExprListItem item; for (i = 0; i < orderBy.Exprs; i++) { item = orderBy.Ids[i]; Expr pDup; if (item.Done) { continue; } Expr expr = item.Expr; int colId = -1; if (expr.IsInteger(ref colId)) { if (colId <= 0 || colId > list.Exprs) { ResolveOutOfRangeError(parse, "ORDER", i + 1, list.Exprs); return(1); } } else { colId = ResolveAsName(parse, list, expr); if (colId == 0) { Expr dupExpr = Expr.Dup(ctx, expr, 0); if (!ctx.MallocFailed) { Debug.Assert(dupExpr != null); colId = ResolveOrderByTermToExprList(parse, select, dupExpr); } Expr.Delete(ctx, ref dupExpr); } } if (colId > 0) { // Convert the ORDER BY term into an integer column number iCol, taking care to preserve the COLLATE clause if it exists Expr newExpr = Expr.Expr_(ctx, TK.INTEGER, null); if (newExpr == null) { return(1); } newExpr.Flags |= EP.IntValue; newExpr.u.I = colId; if (item.Expr == expr) { item.Expr = newExpr; } else { Debug.Assert(item.Expr.OP == TK.COLLATE); Debug.Assert(item.Expr.Left == expr); item.Expr.Left = newExpr; } Expr.Delete(ctx, ref expr); item.OrderByCol = (ushort)colId; item.Done = true; } else { moreToDo = true; } } select = select.Next; } for (i = 0; i < orderBy.Exprs; i++) { if (!orderBy.Ids[i].Done) { parse.ErrorMsg("%r ORDER BY term does not match any column in the result set", i + 1); return(1); } } return(0); }
static void ResolveOutOfRangeError(Parse parse, string typeName, int i, int max) { parse.ErrorMsg("%r %s BY term out of range - should be between 1 and %d", i, typeName, max); }
public static void BeginTrigger(Parse parse, Token name1, Token name2, TK trTm, TK op, IdList columns, SrcList tableName, Expr when, bool isTemp, int noErr) { Context ctx = parse.Ctx; // The database connection Debug.Assert(name1 != null); // pName1.z might be NULL, but not pName1 itself Debug.Assert(name2 != null); Debug.Assert(op == TK.INSERT || op == TK.UPDATE || op == TK.DELETE); Debug.Assert(op > 0 && op < (TK)0xff); Trigger trigger = null; // The new trigger int db; // The database to store the trigger in Token name = null; // The unqualified db name if (isTemp) { // If TEMP was specified, then the trigger name may not be qualified. if (name2.length > 0) { parse.ErrorMsg("temporary trigger may not have qualified name"); goto trigger_cleanup; } db = 1; name = name1; } else { // Figure out the db that the the trigger will be created in db = parse.TwoPartName(name1, name2, ref name); if (db < 0) { goto trigger_cleanup; } } if (tableName == null || ctx.MallocFailed) { goto trigger_cleanup; } // A long-standing parser bug is that this syntax was allowed: // CREATE TRIGGER attached.demo AFTER INSERT ON attached.tab .... // ^^^^^^^^ // To maintain backwards compatibility, ignore the database name on pTableName if we are reparsing our of SQLITE_MASTER. if (ctx.Init.Busy && db != 1) { C._tagfree(ctx, ref tableName.Ids[0].Database); tableName.Ids[0].Database = null; } // If the trigger name was unqualified, and the table is a temp table, then set iDb to 1 to create the trigger in the temporary database. // If sqlite3SrcListLookup() returns 0, indicating the table does not exist, the error is caught by the block below. //? if (tableName == null) goto trigger_cleanup; Table table = Delete.SrcListLookup(parse, tableName); // Table that the trigger fires off of if (ctx.Init.Busy == null && name2.length == 0 && table != null && table.Schema == ctx.DBs[1].Schema) { db = 1; } // Ensure the table name matches database name and that the table exists if (ctx.MallocFailed) { goto trigger_cleanup; } Debug.Assert(tableName.Srcs == 1); DbFixer sFix = new DbFixer(); // State vector for the DB fixer if (sFix.FixInit(parse, db, "trigger", name) && sFix.FixSrcList(tableName)) { goto trigger_cleanup; } table = Delete.SrcListLookup(parse, tableName); if (table == null) { // The table does not exist. if (ctx.Init.DB == 1) { // Ticket #3810. // Normally, whenever a table is dropped, all associated triggers are dropped too. But if a TEMP trigger is created on a non-TEMP table // and the table is dropped by a different database connection, the trigger is not visible to the database connection that does the // drop so the trigger cannot be dropped. This results in an "orphaned trigger" - a trigger whose associated table is missing. ctx.Init.OrphanTrigger = true; } goto trigger_cleanup; } if (E.IsVirtual(table)) { parse.ErrorMsg("cannot create triggers on virtual tables"); goto trigger_cleanup; } // Check that the trigger name is not reserved and that no trigger of the specified name exists string nameAsString = Parse.NameFromToken(ctx, name); if (nameAsString == null || parse.CheckObjectName(nameAsString) != RC.OK) { goto trigger_cleanup; } Debug.Assert(Btree.SchemaMutexHeld(ctx, db, null)); if (ctx.DBs[db].Schema.TriggerHash.Find(nameAsString, nameAsString.Length, (Trigger)null) != null) { if (noErr == 0) { parse.ErrorMsg("trigger %T already exists", name); } else { Debug.Assert(!ctx.Init.Busy); parse.CodeVerifySchema(db); } goto trigger_cleanup; } // Do not create a trigger on a system table if (table.Name.StartsWith("sqlite_", StringComparison.InvariantCultureIgnoreCase)) { parse.ErrorMsg("cannot create trigger on system table"); parse.Errs++; goto trigger_cleanup; } // INSTEAD of triggers are only for views and views only support INSTEAD of triggers. if (table.Select != null && trTm != TK.INSTEAD) { parse.ErrorMsg("cannot create %s trigger on view: %S", (trTm == TK.BEFORE ? "BEFORE" : "AFTER"), tableName, 0); goto trigger_cleanup; } if (table.Select == null && trTm == TK.INSTEAD) { parse.ErrorMsg("cannot create INSTEAD OF trigger on table: %S", tableName, 0); goto trigger_cleanup; } #if !OMIT_AUTHORIZATION { int tabDb = Prepare.SchemaToIndex(ctx, table.Schema); // Index of the database holding pTab AUTH code = AUTH.CREATE_TRIGGER; string dbName = ctx.DBs[tabDb].Name; string dbTrigName = (isTemp ? ctx.DBs[1].Name : dbName); if (tabDb == 1 || isTemp) { code = AUTH.CREATE_TEMP_TRIGGER; } if (Auth.Check(parse, code, nameAsString, table.Name, dbTrigName) != 0 || Auth.Check(parse, AUTH.INSERT, E.SCHEMA_TABLE(tabDb), 0, dbName)) { goto trigger_cleanup; } } #endif // INSTEAD OF triggers can only appear on views and BEFORE triggers cannot appear on views. So we might as well translate every // INSTEAD OF trigger into a BEFORE trigger. It simplifies code elsewhere. if (trTm == TK.INSTEAD) { trTm = TK.BEFORE; } // Build the Trigger object trigger = new Trigger(); //: (Trigger *)_tagalloc(db, sizeof(Trigger), true); if (trigger == null) { goto trigger_cleanup; } trigger.Name = name; trigger.Table = tableName.Ids[0].Name; //: _tagstrdup(ctx, tableName->Ids[0].Name); trigger.Schema = ctx.DBs[db].Schema; trigger.TabSchema = table.Schema; trigger.OP = op; trigger.TRtm = (trTm == TK.BEFORE ? TRIGGER.BEFORE : TRIGGER.AFTER); trigger.When = Expr.Dup(db, when, E.EXPRDUP_REDUCE); trigger.Columns = Expr.IdListDup(ctx, columns); Debug.Assert(parse.NewTrigger == null); parse.NewTrigger = trigger; trigger_cleanup: C._tagfree(ctx, ref name); Expr.SrcListDelete(ctx, ref tableName); Expr.IdListDelete(ctx, ref columns); Expr.Delete(ctx, ref when); if (parse.NewTrigger == null) { DeleteTrigger(ctx, ref trigger); } else { Debug.Assert(parse.NewTrigger == trigger); } }
static void TransferParseError(Parse to, Parse from) { Debug.Assert(from.ErrMsg == null || from.Errs != 0); Debug.Assert(to.ErrMsg== null || to.Errs != 0); if (to.Errs == 0) { to.ErrMsg = from.ErrMsg; to.Errs = from.Errs; } else C._tagfree(from.Ctx, ref from.ErrMsg); }
public Table ResultSetOfSelect(Parse parse) { Context ctx = parse.Ctx; Context.FLAG savedFlags = ctx.Flags; ctx.Flags &= ~Context.FLAG.FullColNames; ctx.Flags |= Context.FLAG.ShortColNames; Select select = this; select.Prep(parse, null); if (parse.Errs != 0) return null; while (select.Prior != null) select = select.Prior; ctx.Flags = savedFlags; Table table = new Table(); if (table == null) return null; // The sqlite3ResultSetOfSelect() is only used n contexts where lookaside is disabled Debug.Assert(!ctx.Lookaside.Enabled); table.Refs = 1; table.Name = null; table.RowEst = 1000000; SelectColumnsFromExprList(parse, select.EList, ref table.Cols.length, ref table.Cols.data); SelectAddColumnTypeAndCollation(parse, table.Cols.length, table.Cols.data, select); table.PKey = -1; if (ctx.MallocFailed) { Parse.DeleteTable(ctx, ref table); return null; } return table; }
static TriggerPrg CodeRowTrigger(Parse parse, Trigger trigger, Table table, OE orconf) { Parse top = E.Parse_Toplevel(parse); Context ctx = parse.Ctx; // Database handle Debug.Assert(trigger.Name == null || table == TableOfTrigger(trigger)); Debug.Assert(top.V != null); // Allocate the TriggerPrg and SubProgram objects. To ensure that they are freed if an error occurs, link them into the Parse.pTriggerPrg // list of the top-level Parse object sooner rather than later. TriggerPrg prg = new TriggerPrg(); // Value to return //: _tagalloc(ctx, sizeof(TriggerPrg), true); if (prg == null) return null; prg.Next = top.TriggerPrg; top.TriggerPrg = prg; Vdbe.SubProgram program; // Sub-vdbe for trigger program prg.Program = program = new Vdbe.SubProgram();// sqlite3DbMallocZero( db, sizeof( SubProgram ) ); if (program == null) return null; top.V.LinkSubProgram(program); prg.Trigger = trigger; prg.Orconf = orconf; prg.Colmasks[0] = 0xffffffff; prg.Colmasks[1] = 0xffffffff; // Allocate and populate a new Parse context to use for coding the trigger sub-program. Parse subParse = new Parse(); // Parse context for sub-vdbe //: _stackalloc(ctx, sizeof(Parse), true); if (subParse == null) return null; NameContext sNC = new NameContext(); // Name context for sub-vdbe sNC.Parse = subParse; subParse.Ctx = ctx; subParse.TriggerTab = table; subParse.Toplevel = top; subParse.AuthContext = trigger.Name; subParse.TriggerOp = trigger.OP; subParse.QueryLoops = parse.QueryLoops; int endTrigger = 0; // Label to jump to if WHEN is false Vdbe v = subParse.GetVdbe(); // Temporary VM if (v != null) { #if DEBUG v.Comment("Start: %s.%s (%s %s%s%s ON %s)", trigger.Name, OnErrorText(orconf), (trigger.TRtm == TRIGGER.BEFORE ? "BEFORE" : "AFTER"), (trigger.OP == TK.UPDATE ? "UPDATE" : string.Empty), (trigger.OP == TK.INSERT ? "INSERT" : string.Empty), (trigger.OP == TK.DELETE ? "DELETE" : string.Empty), table.Name); #endif #if !OMIT_TRACE v.ChangeP4(-1, C._mtagprintf(ctx, "-- TRIGGER %s", trigger.Name), Vdbe.P4T.DYNAMIC); #endif // If one was specified, code the WHEN clause. If it evaluates to false (or NULL) the sub-vdbe is immediately halted by jumping to the // OP_Halt inserted at the end of the program. if (trigger.When != null) { Expr when = Expr.Dup(ctx, trigger.When, 0); // Duplicate of trigger WHEN expression if (ResolveExprNames(sNC, ref when) == RC.OK && !ctx.MallocFailed) { endTrigger = v.MakeLabel(); subParse.IfFalse(when, endTrigger, RC_JUMPIFNULL); } Expr.Delete(ctx, ref when); } // Code the trigger program into the sub-vdbe. CodeTriggerProgram(subParse, trigger.StepList, orconf); // Insert an OP_Halt at the end of the sub-program. if (endTrigger != 0) v.ResolveLabel(endTrigger); v.AddOp0(Core.OP.Halt); #if DEBUG v.Comment("End: %s.%s", trigger.Name, OnErrorText(orconf)); #endif TransferParseError(parse, subParse); if (!ctx.MallocFailed) program.Ops.data = v.TakeOpArray(ref program.Ops.length, ref top.MaxArgs); program.Mems = subParse.Mems; program.Csrs = subParse.Tabs; program.Token = trigger.GetHashCode(); prg.Colmasks[0] = subParse.Oldmask; prg.Colmasks[1] = subParse.Newmask; Vdbe.Delete(v); } Debug.Assert(subParse.Ainc == null && subParse.ZombieTab == null); Debug.Assert(subParse.TriggerPrg == null && subParse.MaxArgs == 0); C._stackfree(ctx, ref subParse); return prg; }
public static JT JoinType(Parse parse, Token a, Token b, Token c) { Token[] alls = new Token[3]; alls[0] = a; alls[1] = b; alls[2] = c; JT jointype = 0; for (int i = 0; i < 3 && alls[i] != null; i++) { Token p = alls[i]; int j; for (j = 0; j < _keywords.Length; j++) { if (p.length == _keywords[j].Chars && p.data.StartsWith(_keyTexts.Substring(_keywords[j].I, _keywords[j].Chars), StringComparison.OrdinalIgnoreCase)) { jointype |= _keywords[j].Code; break; } } C.ASSERTCOVERAGE(j == 0 || j == 1 || j == 2 || j == 3 || j == 4 || j == 5 || j == 6); if (j >= _keywords.Length) { jointype |= JT.ERROR; break; } } if ((jointype & (JT.INNER | JT.OUTER)) == (JT.INNER | JT.OUTER) || (jointype & JT.ERROR) != 0) { string sp = " "; Debug.Assert(b != null); if (c == null) sp = ""; parse.ErrorMsg("unknown or unsupported join type: %T %T%s%T", a, b, sp, c); jointype = JT.INNER; } else if ((jointype & JT.OUTER) != 0 && (jointype & (JT.LEFT | JT.RIGHT)) != JT.LEFT) { parse.ErrorMsg("RIGHT and FULL OUTER JOINs are not currently supported"); jointype = JT.INNER; } return jointype; }
static bool CheckForMultiColumnSelectError(Parse parse, SelectDest dest, int exprs) { SRT dest2 = dest.Dest; if (exprs > 1 && (dest2 == SRT.Mem || dest2 == SRT.Set)) { parse.ErrorMsg("only a single result allowed for a SELECT that is part of an expression"); return true; } return false; }
public bool ProcessJoin(Parse parse) { SrcList src = Src; // All tables in the FROM clause SrcList.SrcListItem left; // Left table being joined SrcList.SrcListItem right; // Right table being joined int j; for (int i = 0; i < src.Srcs - 1; i++) { left = src.Ids[i]; right = src.Ids[i + 1]; Table leftTable = left.Table; Table rightTable = right.Table; if (C._NEVER(leftTable == null || rightTable == null)) continue; bool isOuter = ((right.Jointype & JT.OUTER) != 0); // When the NATURAL keyword is present, add WHERE clause terms for every column that the two tables have in common. if ((right.Jointype & JT.NATURAL) != 0) { if (right.On != null || right.Using != null) { parse.ErrorMsg("a NATURAL join may not have an ON or USING clause"); return true; } for (j = 0; j < rightTable.Cols.length; j++) { string name = rightTable.Cols[j].Name; // Name of column in the right table int leftId = 0; // Matching left table int leftColId = 0; // Matching column in the left table if (TableAndColumnIndex(src, i + 1, name, ref leftId, ref leftColId)) AddWhereTerm(parse, src, leftId, leftColId, i + 1, j, isOuter, ref Where); } } // Disallow both ON and USING clauses in the same join if (right.On != null && right.Using != null) { parse.ErrorMsg("cannot have both ON and USING clauses in the same join"); return true; } // Add the ON clause to the end of the WHERE clause, connected by if (right.On != null) { if (isOuter) SetJoinExpr(right.On, right.Cursor); Where = Expr.And(parse.Ctx, Where, right.On); right.On = null; } // Create extra terms on the WHERE clause for each column named in the USING clause. Example: If the two tables to be joined are // A and B and the USING clause names X, Y, and Z, then add this to the WHERE clause: A.X=B.X AND A.Y=B.Y AND A.Z=B.Z // Report an error if any column mentioned in the USING clause is not contained in both tables to be joined. if (right.Using != null) { IdList list = right.Using; for (j = 0; j < list.Ids.length; j++) { string name = list.Ids[j].Name; // Name of the term in the USING clause int leftId = 0; // Table on the left with matching column name int leftColId = 0; // Column number of matching column on the left int rightColId = ColumnIndex(rightTable, name); // Column number of matching column on the right if (rightColId < 0 || TableAndColumnIndex(src, i + 1, name, ref leftId, ref leftColId)) { parse.ErrorMsg("cannot join using column %s - column not present in both tables", name); return true; } AddWhereTerm(parse, src, leftId, leftColId, i + 1, rightColId, isOuter, ref Where); } } } return true; }
static KeyInfo KeyInfoFromExprList(Parse parse, ExprList list) { Context ctx = parse.Ctx; int exprs = list.Exprs; KeyInfo info = new KeyInfo(); if (info != null) { info.SortOrders = new SO[exprs]; info.Colls = new CollSeq[exprs]; info.Fields = (ushort)exprs; info.Encode = E.CTXENCODE(ctx); info.Ctx = ctx; int i; ExprList.ExprListItem item; for (i = 0; i < exprs; i++) { item = list.Ids[i]; CollSeq coll = item.Expr.CollSeq(parse); if (coll == null) coll = ctx.DefaultColl; info.Colls[i] = coll; info.SortOrders[i] = item.SortOrder; } } return info; }
public static Select New(Parse parse, int dummy1, SrcList src, int dummy2, int dummy3, int dummy4, int dummy5, SF selFlags, int dummy6, int dummy7) { return New(parse, null, src, null, null, null, null, selFlags, null, null); }
static void ExplainComposite(Parse parse, TK op, int sub1Id, int sub2Id, bool useTmp) { Debug.Assert(op == TK.UNION || op == TK.EXCEPT || op == TK.INTERSECT || op == TK.ALL); if (parse.Explain == 2) { Vdbe v = parse.V; string msg = C._mtagprintf(parse.Ctx, "COMPOUND SUBQUERIES %d AND %d %s(%s)", sub1Id, sub2Id, (useTmp ? "USING TEMP B-TREE " : ""), SelectOpName(op)); v.AddOp4(OP.Explain, parse.SelectId, 0, 0, msg, Vdbe.P4T.DYNAMIC); } }
static TriggerPrg GetRowTrigger(Parse parse, Trigger trigger, Table table, OE orconf) { Parse root = E.Parse_Toplevel(parse); Debug.Assert(trigger.Name == null || table == TableOfTrigger(trigger)); // It may be that this trigger has already been coded (or is in the process of being coded). If this is the case, then an entry with // a matching TriggerPrg.pTrigger field will be present somewhere in the Parse.pTriggerPrg list. Search for such an entry. TriggerPrg prg; for (prg = root.TriggerPrg; prg != null && (prg.Trigger != trigger || prg.Orconf != orconf); prg = prg.Next) ; // If an existing TriggerPrg could not be located, create a new one. if (prg == null) prg = CodeRowTrigger(parse, trigger, table, orconf); return prg; }
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); }
static void SelectInnerLoop(Parse parse, Select p, ExprList list, int srcTable, int columns, ExprList orderBy, DistinctCtx distinct, SelectDest dest, int continueId, int breakId) { Vdbe v = parse.V; Debug.Assert(v != null); if (C._NEVER(v == null)) return; Debug.Assert(list != null); WHERE_DISTINCT hasDistinct = (distinct != null ? distinct.TnctType : WHERE_DISTINCT.NOOP); // True if the DISTINCT keyword is present if (orderBy == null && hasDistinct == (WHERE_DISTINCT)0) CodeOffset(v, p, continueId); // Pull the requested columns. int resultCols = (columns > 0 ? columns : list.Exprs); // Number of result columns if (dest.Sdsts == 0) { dest.SdstId = parse.Mems + 1; dest.Sdsts = resultCols; parse.Mems += resultCols; } else Debug.Assert(dest.Sdsts == resultCols); int regResult = dest.SdstId; // Start of memory holding result set SRT dest2 = dest.Dest; // How to dispose of results int i; if (columns > 0) for (i = 0; i < columns; i++) v.AddOp3(OP.Column, srcTable, i, regResult + i); else if (dest2 != SRT.Exists) { // If the destination is an EXISTS(...) expression, the actual values returned by the SELECT are not required. Expr.CacheClear(parse); Expr.CodeExprList(parse, list, regResult, dest2 == SRT.Output); } columns = resultCols; // If the DISTINCT keyword was present on the SELECT statement and this row has been seen before, then do not make this row part of the result. if (hasDistinct != 0) { Debug.Assert(list != null); Debug.Assert(list.Exprs == columns); switch (distinct.TnctType) { case WHERE_DISTINCT.ORDERED: { // Allocate space for the previous row int regPrev = parse.Mems + 1; // Previous row content parse.Mems += columns; // Change the OP_OpenEphemeral coded earlier to an OP_Null sets the MEM_Cleared bit on the first register of the // previous value. This will cause the OP_Ne below to always fail on the first iteration of the loop even if the first // row is all NULLs. v.ChangeToNoop(distinct.AddrTnct); Vdbe.VdbeOp op = v.GetOp(distinct.AddrTnct); // No longer required OpenEphemeral instr. op.Opcode = OP.Null; op.P1 = 1; op.P2 = regPrev; int jumpId = v.CurrentAddr() + columns; // Jump destination for (i = 0; i < columns; i++) { CollSeq coll = list.Ids[i].Expr.CollSeq(parse); if (i < columns - 1) v.AddOp3(OP.Ne, regResult + i, jumpId, regPrev + i); else v.AddOp3(OP.Eq, regResult + i, continueId, regPrev + i); v.ChangeP4(-1, coll, Vdbe.P4T.COLLSEQ); v.ChangeP5(AFF.BIT_NULLEQ); } Debug.Assert(v.CurrentAddr() == jumpId); v.AddOp3(OP.Copy, regResult, regPrev, columns - 1); break; } case WHERE_DISTINCT.UNIQUE: { v.ChangeToNoop(distinct.AddrTnct); break; } default: { Debug.Assert(distinct.TnctType == WHERE_DISTINCT.UNORDERED); CodeDistinct(parse, distinct.TableTnct, continueId, columns, regResult); break; } } if (orderBy != null) CodeOffset(v, p, continueId); } int paramId = dest.SDParmId; // First argument to disposal method switch (dest2) { #if !OMIT_COMPOUND_SELECT case SRT.Union: { // In this mode, write each query result to the key of the temporary table iParm. int r1 = Expr.GetTempReg(parse); v.AddOp3(OP.MakeRecord, regResult, columns, r1); v.AddOp2(OP.IdxInsert, paramId, r1); Expr.ReleaseTempReg(parse, r1); break; } case SRT.Except: { // Construct a record from the query result, but instead of saving that record, use it as a key to delete elements from // the temporary table iParm. v.AddOp3(OP.IdxDelete, paramId, regResult, columns); break; } #endif case SRT.Table: case SRT.EphemTab: { // Store the result as data using a unique key. int r1 = Expr.GetTempReg(parse); C.ASSERTCOVERAGE(dest2 == SRT.Table); C.ASSERTCOVERAGE(dest2 == SRT.EphemTab); v.AddOp3(OP.MakeRecord, regResult, columns, r1); if (orderBy != null) PushOntoSorter(parse, orderBy, p, r1); else { int r2 = Expr.GetTempReg(parse); v.AddOp2(OP.NewRowid, paramId, r2); v.AddOp3(OP.Insert, paramId, r1, r2); v.ChangeP5(Vdbe.OPFLAG.APPEND); Expr.ReleaseTempReg(parse, r2); } Expr.ReleaseTempReg(parse, r1); break; } #if !OMIT_SUBQUERY case SRT.Set: { // If we are creating a set for an "expr IN (SELECT ...)" construct, then there should be a single item on the stack. Write this // item into the set table with bogus data. Debug.Assert(columns == 1); dest.AffSdst = list.Ids[0].Expr.CompareAffinity(dest.AffSdst); // At first glance you would think we could optimize out the ORDER BY in this case since the order of entries in the set // does not matter. But there might be a LIMIT clause, in which case the order does matter if (orderBy != null) PushOntoSorter(parse, orderBy, p, regResult); else { int r1 = Expr.GetTempReg(parse); v.AddOp4(OP.MakeRecord, regResult, 1, r1, dest.AffSdst, 1); Expr.CacheAffinityChange(parse, regResult, 1); v.AddOp2(OP.IdxInsert, paramId, r1); Expr.ReleaseTempReg(parse, r1); } break; } case SRT.Exists: { // If any row exist in the result set, record that fact and abort. v.AddOp2(OP.Integer, 1, paramId); // The LIMIT clause will terminate the loop for us break; } case SRT.Mem: { // If this is a scalar select that is part of an expression, then store the results in the appropriate memory cell and break out // of the scan loop. Debug.Assert(columns == 1); if (orderBy != null) PushOntoSorter(parse, orderBy, p, regResult); else Expr.CodeMove(parse, regResult, paramId, 1); // The LIMIT clause will jump out of the loop for us break; } #endif case SRT.Coroutine: case SRT.Output: { // Send the data to the callback function or to a subroutine. In the case of a subroutine, the subroutine itself is responsible for // popping the data from the stack. C.ASSERTCOVERAGE(dest2 == SRT.Coroutine); C.ASSERTCOVERAGE(dest2 == SRT.Output); if (orderBy != null) { int r1 = Expr.GetTempReg(parse); v.AddOp3(OP.MakeRecord, regResult, columns, r1); PushOntoSorter(parse, orderBy, p, r1); Expr.ReleaseTempReg(parse, r1); } else if (dest2 == SRT.Coroutine) v.AddOp1(OP.Yield, dest.SDParmId); else { v.AddOp2(OP.ResultRow, regResult, columns); Expr.CacheAffinityChange(parse, regResult, columns); } break; } #if !OMIT_TRIGGER default: { // Discard the results. This is used for SELECT statements inside the body of a TRIGGER. The purpose of such selects is to call // user-defined functions that have side effects. We do not care about the actual results of the select. Debug.Assert(dest2 == SRT.Discard); break; } #endif } // Jump to the end of the loop if the LIMIT is reached. Except, if there is a sorter, in which case the sorter has already limited the output for us. if (orderBy == null && p.LimitId != 0) v.AddOp3(OP.IfZero, p.LimitId, breakId, -1); }
public void CodeRowTriggerDirect(Parse parse, Table table, int reg, OE orconf, int ignoreJump) { Vdbe v = parse.GetVdbe(); // Main VM TriggerPrg prg = GetRowTrigger(parse, this, table, orconf); Debug.Assert(prg != null || parse.Errs != 0 || parse.Ctx.MallocFailed); // Code the OP_Program opcode in the parent VDBE. P4 of the OP_Program is a pointer to the sub-vdbe containing the trigger program. if (prg != null) { bool recursive = (Name != null && (parse.Ctx.Flags & Context.FLAG.RecTriggers) == 0); v.AddOp3(Core.OP.Program, reg, ignoreJump, ++parse.Mems); v.ChangeP4(-1, prg.Program, Vdbe.P4T.SUBPROGRAM); #if DEBUG v.Comment("Call: %s.%s", (!string.IsNullOrEmpty(p.Name) ? p.Name : "fkey"), OnErrorText(orconf)); #endif // Set the P5 operand of the OP_Program instruction to non-zero if recursive invocation of this trigger program is disallowed. Recursive // invocation is disallowed if (a) the sub-program is really a trigger, not a foreign key action, and (b) the flag to enable recursive triggers is clear. v.ChangeP5((int)(recursive ? 1 : 0)); } }
static void ExplainTempTable(Parse parse, string usage_) { if (parse.Explain == 2) { Vdbe v = parse.V; string msg = C._mtagprintf(parse.Ctx, "USE TEMP B-TREE FOR %s", usage_); v.AddOp4(OP.Explain, parse.SelectId, 0, 0, msg, Vdbe.P4T.DYNAMIC); } }
public void CodeRowTrigger(Parse parse, TK op, ExprList changes, TRIGGER trtm, Table table, int reg, OE orconf, int ignoreJump) { Debug.Assert(op == TK.UPDATE || op == TK.INSERT || op == TK.DELETE); Debug.Assert(trtm == TRIGGER.BEFORE || trtm == TRIGGER.AFTER); Debug.Assert((op == TK.UPDATE) == (changes != null)); for (Trigger p = this; p != null; p = p.Next) { // Sanity checking: The schema for the trigger and for the table are always defined. The trigger must be in the same schema as the table or else it must be a TEMP trigger. Debug.Assert(p.Schema != null); Debug.Assert(p.TabSchema != null); Debug.Assert(p.Schema == p.TabSchema || p.Schema == parse.Ctx.DBs[1].Schema); // Determine whether we should code this trigger if (p.OP == op && p.TRtm == trtm && CheckColumnOverlap(p.Columns, changes)) p.CodeRowTriggerDirect(parse, table, reg, orconf, ignoreJump); } }
static void ExplainComposite(Parse v, int w, int x, int y, bool z) { }
public uint TriggerColmask(Parse parse, ExprList changes, bool isNew, TRIGGER trtm, Table table, OE orconf) { TK op = (changes != null ? TK.UPDATE : TK.DELETE); int isNewId = (isNew ? 1 : 0); uint mask = 0; for (Trigger p = this; p != null; p = p.Next) if (p.OP == op && (trtm & p.TRtm) != 0 && CheckColumnOverlap(p.Columns, changes)) { TriggerPrg prg = GetRowTrigger(parse, p, table, orconf); if (prg != null) mask |= prg.Colmasks[isNewId]; } return mask; }
static void GenerateColumnTypes(Parse parse, SrcList tabList, ExprList list) { #if !OMIT_DECLTYPE Vdbe v = parse.V; NameContext sNC = new NameContext(); sNC.SrcList = tabList; sNC.Parse = parse; for (int i = 0; i < list.Exprs; i++) { Expr p = list.Ids[i].Expr; string typeName; #if ENABLE_COLUMN_METADATA string origDbName = null; string origTableName = null; string origColumnName = null; typeName = ColumnType(sNC, p, ref origDbName, ref origTableName, ref origColumnName); // The vdbe must make its own copy of the column-type and other column specific strings, in case the schema is reset before this // virtual machine is deleted. v.SetColName(i, COLNAME_DATABASE, origDbName, C.DESTRUCTOR_TRANSIENT); v.SetColName(i, COLNAME_TABLE, origTableName, C.DESTRUCTOR_TRANSIENT); v.SetColName(i, COLNAME_COLUMN, origColumnName, C.DESTRUCTOR_TRANSIENT); #else string dummy1 = null; typeName = ColumnType(sNC, p, ref dummy1, ref dummy1, ref dummy1); #endif v.SetColName(i, COLNAME_DECLTYPE, typeName, C.DESTRUCTOR_TRANSIENT); } #endif }
static void SelectAddColumnTypeAndCollation(Parse parse, int colsLength, Column[] cols, Select select) { Context ctx = parse.Ctx; Debug.Assert(select != null); Debug.Assert((select.SelFlags & SF.Resolved) != 0); Debug.Assert(colsLength == select.EList.Exprs || ctx.MallocFailed); if (ctx.MallocFailed) return; NameContext sNC = new NameContext(); sNC.SrcList = select.Src; ExprList.ExprListItem[] ids = select.EList.Ids; int i; Column col; for (i = 0; i < colsLength; i++) { col = cols[i]; Expr p = ids[i].Expr; string dummy1 = null; col.Type = ColumnType(sNC, p, ref dummy1, ref dummy1, ref dummy1); col.Affinity = p.Affinity(); if (col.Affinity == 0) col.Affinity = AFF.NONE; CollSeq coll = p.CollSeq(parse); if (coll != null) col.Coll = coll.Name; } }
static RC SelectColumnsFromExprList(Parse parse, ExprList list, ref short colsLengthOut, ref Column[] colsOut) { Context ctx = parse.Ctx; // Database connection int j; int colsLength; // Number of columns in the result set Column[] cols; // For looping over result columns if (list != null) { colsLength = list.Exprs; cols = new Column[colsLength]; C.ASSERTCOVERAGE(cols == null); } else { colsLength = 0; cols = null; } colsLengthOut = (short)colsLength; colsOut = cols; int i; Column col; // For looping over result columns for (i = 0; i < colsLength; i++)//, pCol++) { if (cols[i] == null) cols[i] = new Column(); col = cols[i]; // Get an appropriate name for the column Expr p = list.Ids[i].Expr; // Expression for a single result column string name; // Column name if (list.Ids[i].Name != null && (name = list.Ids[i].Name) != null) { } //: name = _tagstrdup(ctx, name); // If the column contains an "AS <name>" phrase, use <name> as the name else { Expr colExpr = p; // The expression that is the result column name while (colExpr.OP == TK.DOT) { colExpr = colExpr.Right; Debug.Assert(colExpr != null); } if (colExpr.OP == TK.COLUMN && C._ALWAYS(colExpr.Table != null)) { // For columns use the column name name int colId = colExpr.ColumnIdx; Table table = colExpr.Table; // Table associated with this expression if (colId < 0) colId = table.PKey; name = C._mtagprintf(ctx, "%s", (colId >= 0 ? table.Cols[colId].Name : "rowid")); } else if (colExpr.OP == TK.ID) { Debug.Assert(!E.ExprHasProperty(colExpr, EP.IntValue)); name = C._mtagprintf(ctx, "%s", colExpr.u.Token); } else name = C._mtagprintf(ctx, "%s", list.Ids[i].Span); // Use the original text of the column expression as its name } if (ctx.MallocFailed) { C._tagfree(ctx, ref name); break; } // Make sure the column name is unique. If the name is not unique, append a integer to the name so that it becomes unique. int nameLength = name.Length; // Size of name in zName[] int cnt; // Index added to make the name unique for (j = cnt = 0; j < i; j++) { if (string.Equals(cols[j].Name, name, StringComparison.OrdinalIgnoreCase)) { name = name.Substring(0, nameLength); string newName = C._mtagprintf(ctx, "%s:%d", name, ++cnt); C._tagfree(ctx, ref name); name = newName; j = -1; if (name == null) break; } } col.Name = name; } if (ctx.MallocFailed) { for (j = 0; j < i; j++) C._tagfree(ctx, ref cols[j].Name); C._tagfree(ctx, ref cols); colsOut = null; colsLengthOut = 0; return RC.NOMEM; } return RC.OK; }
public static void FinishTrigger(Parse parse, TriggerStep stepList, Token all) { Trigger trig = parse.NewTrigger; // Trigger being finished Context ctx = parse.Ctx; // The database Token nameToken = new Token(); // Trigger name for error reporting parse.NewTrigger = null; if (C._NEVER(parse.Errs != 0) || trig == null) { goto triggerfinish_cleanup; } string name = trig.Name; // Name of trigger int db = Prepare.SchemaToIndex(parse.Ctx, trig.Schema); // Database containing the trigger trig.StepList = stepList; while (stepList != null) { stepList.Trig = trig; stepList = stepList.Next; } nameToken.data = trig.Name; nameToken.length = (uint)nameToken.data.Length; DbFixer sFix = new DbFixer(); // Fixer object if (sFix.FixInit(parse, db, "trigger", nameToken) && sFix.FixTriggerStep(trig.StepList)) { goto triggerfinish_cleanup; } // if we are not initializing, build the sqlite_master entry if (ctx.Init.Busy) { // Make an entry in the sqlite_master table Vdbe v = parse.GetVdbe(); if (v == null) { goto triggerfinish_cleanup; } parse.BeginWriteOperation(0, db); string z = all.data.Substring(0, (int)all.length); //: _tagstrndup(ctx, (char *)all->data, all->length); parse.NestedParse("INSERT INTO %Q.%s VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')", ctx.DBs[db].Name, E.SCHEMA_TABLE(db), name, trig.Table, z); C._tagfree(ctx, ref z); parse.ChangeCookie(db); v.AddParseSchemaOp(db, C._mtagprintf(ctx, "type='trigger' AND name='%q'", name)); } if (!ctx.Init.Busy) { Trigger link = trig; Debug.Assert(Btree.SchemaMutexHeld(ctx, db, null)); trig = ctx.DBs[db].Schema.TriggerHash.Insert(name, name.Length, trig); if (trig != null) { ctx.MallocFailed = true; } else if (link.Schema == link.TabSchema) { int tableLength = link.Table.Length; Table table = (Table)link.TabSchema.TableHash.Find(link.Table, tableLength, (Table)null); Debug.Assert(table != null); link.Next = table.Triggers; table.Triggers = link; } } triggerfinish_cleanup: DeleteTrigger(ctx, ref trig); Debug.Assert(parse.NewTrigger == null); DeleteTriggerStep(ctx, ref stepList); }