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; }
static RC btreeCursor(Btree p, Pid tableID, bool wrFlag, KeyInfo keyInfo, BtCursor cur) { var bt = p.Bt; // Shared b-tree handle Debug.Assert(p.HoldsMutex()); // The following assert statements verify that if this is a sharable b-tree database, the connection is holding the required table locks, // and that no other connection has any open cursor that conflicts with this lock. Debug.Assert(hasSharedCacheTableLock(p, (uint)tableID, keyInfo != null, (LOCK)(wrFlag ? 1 : 0) + 1)); Debug.Assert(!wrFlag || !hasReadConflicts(p, (uint)tableID)); // Assert that the caller has opened the required transaction. Debug.Assert(p.InTrans > TRANS.NONE); Debug.Assert(!wrFlag || p.InTrans == TRANS.WRITE); Debug.Assert(bt.Page1 != null && bt.Page1.Data != null); if (C._NEVER(wrFlag && (bt.BtsFlags & BTS.READ_ONLY) != 0)) return RC.READONLY; if (tableID == 1 && btreePagecount(bt) == 0) { Debug.Assert(!wrFlag); tableID = 0; } // Now that no other errors can occur, finish filling in the BtCursor variables and link the cursor into the BtShared list. cur.RootID = tableID; cur.ID = -1; cur.KeyInfo = keyInfo; cur.Btree = p; cur.Bt = bt; cur.WrFlag = wrFlag; cur.Next = bt.Cursor; if (cur.Next != null) cur.Next.Prev = cur; bt.Cursor = cur; cur.State = CURSOR.INVALID; cur.CachedRowID = 0; return RC.OK; }
public RC Cursor(Pid tableID, bool wrFlag, KeyInfo keyInfo, BtCursor cur) { Enter(); var rc = btreeCursor(this, tableID, wrFlag, keyInfo, cur); Leave(); return rc; }
static void FKLookupParent(Parse parse, int db, Table table, Index index, FKey fkey, int[] cols, int regDataId, int incr, bool isIgnore) { Vdbe v = parse.GetVdbe(); // Vdbe to add code to int curId = parse.Tabs - 1; // Cursor number to use int okId = v.MakeLabel(); // jump here if parent key found // If nIncr is less than zero, then check at runtime if there are any outstanding constraints to resolve. If there are not, there is no need // to check if deleting this row resolves any outstanding violations. // // Check if any of the key columns in the child table row are NULL. If any are, then the constraint is considered satisfied. No need to // search for a matching row in the parent table. int i; if (incr < 0) { v.AddOp2(OP.FkIfZero, fkey.IsDeferred, okId); } for (i = 0; i < fkey.Cols.length; i++) { int regId = cols[i] + regDataId + 1; v.AddOp2(OP.IsNull, regId, okId); } if (!isIgnore) { if (index == null) { // If pIdx is NULL, then the parent key is the INTEGER PRIMARY KEY column of the parent table (table pTab). int mustBeIntId; // Address of MustBeInt instruction int regTempId = parse.GetTempReg(); // Invoke MustBeInt to coerce the child key value to an integer (i.e. apply the affinity of the parent key). If this fails, then there // is no matching parent key. Before using MustBeInt, make a copy of the value. Otherwise, the value inserted into the child key column // will have INTEGER affinity applied to it, which may not be correct. v.AddOp2(OP.SCopy, cols[0] + 1 + regDataId, regTempId); mustBeIntId = v.AddOp2(OP.MustBeInt, regTempId, 0); // If the parent table is the same as the child table, and we are about to increment the constraint-counter (i.e. this is an INSERT operation), // then check if the row being inserted matches itself. If so, do not increment the constraint-counter. if (table == fkey.From && incr == 1) { v.AddOp3(OP.Eq, regDataId, okId, regTempId); } parse.OpenTable(parse, curId, db, table, OP.OpenRead); v.AddOp3(OP.NotExists, curId, 0, regTempId); v.AddOp2(OP.Goto, 0, okId); v.JumpHere(v.CurrentAddr() - 2); v.JumpHere(mustBeIntId); parse.ReleaseTempReg(regTempId); } else { int colsLength = fkey.Cols.length; int regTempId = parse.GetTempRange(colsLength); int regRecId = parse.GetTempReg(); KeyInfo key = IndexKeyinfo(parse, index); v.AddOp3(OP.OpenRead, curId, index.Id, db); v.ChangeP4(v, -1, key, Vdbe.P4T.KEYINFO_HANDOFF); for (i = 0; i < colsLength; i++) { v.AddOp2(OP.Copy, cols[i] + 1 + regDataId, regTempId + i); } // If the parent table is the same as the child table, and we are about to increment the constraint-counter (i.e. this is an INSERT operation), // then check if the row being inserted matches itself. If so, do not increment the constraint-counter. // If any of the parent-key values are NULL, then the row cannot match itself. So set JUMPIFNULL to make sure we do the OP_Found if any // of the parent-key values are NULL (at this point it is known that none of the child key values are). if (table == fkey.From && incr == 1) { int jumpId = v.CurrentAddr() + colsLength + 1; for (i = 0; i < colsLength; i++) { int childId = cols[i] + 1 + regDataId; int parentId = index.Columns[i] + 1 + regDataId; Debug.Assert(cols[i] != table.PKey); if (index.Columns[i] == table.PKey) { parentId = regDataId; // The parent key is a composite key that includes the IPK column } v.AddOp3(OP.Ne, childId, jumpId, parentId); v.ChangeP5(SQLITE_JUMPIFNULL); } v.AddOp2(OP.Goto, 0, okId); } v.AddOp3(OP.MakeRecord, regTempId, colsLength, regRecId); v.ChangeP4(-1, IndexAffinityStr(v, index), Vdbe.P4T.TRANSIENT); v.AddOp4Int(OP.Found, curId, okId, regRecId, 0); parse.ReleaseTempReg(regRecId); parse.ReleaseTempRange(regTempId, colsLength); } } if (!fkey.IsDeferred && parse.Toplevel == null && parse.IsMultiWrite == 0) { // Special case: If this is an INSERT statement that will insert exactly one row into the table, raise a constraint immediately instead of // incrementing a counter. This is necessary as the VM code is being generated for will not open a statement transaction. Debug.Assert(incr == 1); HaltConstraint(parse, OE.Abort, "foreign key constraint failed", Vdbe.P4T.STATIC); } else { if (incr > 0 && !fkey.IsDeferred) { E.Parse_Toplevel(parse).MayAbort = true; } v.AddOp2(OP.FkCounter, fkey.IsDeferred, incr); } v.ResolveLabel(v, okId); v.AddOp1(OP.Close, curId); }
internal MemPage[] Pages = new MemPage[BTCURSOR_MAX_DEPTH]; // Pages from root to current page internal void memset() { Next = Prev = null; KeyInfo = null; RootID = 0; CachedRowID = 0; Info = new CellInfo(); WrFlag = false; AtLast = 0; ValidNKey = false; State = 0; KeyLength = 0; Key = null; SkipNext = 0; #if !OMIT_INCRBLOB IsIncrblobHandle = false; Overflows = null; #endif ID = 0; }
UnpackedRecord Vdbe_AllocUnpackedRecord(KeyInfo keyInfo, byte[] space, int spaceLength, out object free);
void Vdbe_RecordUnpack(KeyInfo keyInfo, int keyLength, byte[] key, UnpackedRecord p);
public void ChangeP4(int addr, P4_t p4, int n) { Debug.Assert(Magic == VDBE_MAGIC_INIT); Context ctx = Ctx; if (Ops.data == null || ctx.MallocFailed) { if (n != (int)P4T.KEYINFO && n != (int)P4T.VTAB) FreeP4(ctx, (P4T)n, p4); return; } Debug.Assert(Ops.length > 0); Debug.Assert(addr < Ops.length); if (addr < 0) addr = Ops.length - 1; VdbeOp op = Ops[addr]; FreeP4(ctx, op.P4Type, op.P4.P); op.P4.P = null; if (n == (int)P4T.INT32) { // Note: this cast is safe, because the origin data point was an int that was cast to a (string ). op.P4.I = p4.I; //: PTR_TO_INT(p4); op.P4Type = P4T.INT32; } else if (n == (int)P4T.INT64) { op.P4.I64 = p4.I64; op.P4Type = (P4T)n; } else if (n == (int)P4T.REAL) { op.P4.Real = p4.Real; op.P4Type = (P4T)n; } else if (p4 == null) { op.P4.P = null; op.P4Type = P4T.NOTUSED; } else if (n == (int)P4T.KEYINFO) { int fields = p4.KeyInfo.Fields; KeyInfo keyInfo = new KeyInfo(); op.P4.KeyInfo = keyInfo; if (keyInfo != null) { keyInfo = p4.KeyInfo._memcpy(); op.P4Type = P4T.KEYINFO; } else { ctx.MallocFailed = true; op.P4Type = P4T.NOTUSED; } } else if (n == (int)P4T.KEYINFO_HANDOFF) { op.P4.KeyInfo = p4.KeyInfo; op.P4Type = P4T.KEYINFO; } else if (n == (int)P4T.FUNCDEF) { op.P4.Func = p4.Func; op.P4Type = P4T.FUNCDEF; } else if (n == (int)P4T.COLLSEQ) { op.P4.Coll = p4.Coll; op.P4Type = P4T.COLLSEQ; } else if (n == (int)P4T.DYNAMIC || n == (int)P4T.STATIC || n == (int)P4T.MPRINTF) { op.P4.Z = p4.Z; op.P4Type = P4T.DYNAMIC; } else if (n == (int)P4T.MEM) { op.P4.Mem = p4.Mem; op.P4Type = P4T.MEM; } else if (n == (int)P4T.INTARRAY) { op.P4.Is = p4.Is; op.P4Type = P4T.INTARRAY; } else if (n == (int)P4T.SUBPROGRAM) { op.P4.Program = p4.Program; op.P4Type = P4T.SUBPROGRAM; } else if (n == (int)P4T.VTAB) { op.P4.VTable = p4.VTable; op.P4Type = P4T.VTAB; p4.VTable.Lock(); Debug.Assert(p4.VTable.Ctx == ctx); } else if (n < 0) { op.P4.P = p4.P; op.P4Type = (P4T)n; } else { //: if (n == 0) n = _strlen30(p4); op.P4.Z = p4.Z; op.P4Type = P4T.DYNAMIC; } }
public void ChangeP4(int addr, int i, int n) { ChangeP4(addr, new P4_t { I = i }, n); } // P4_INT32 public void ChangeP4(int addr, KeyInfo keyInfo, int n) { ChangeP4(addr, new P4_t { KeyInfo = keyInfo }, n); } // P4T_KEYINFO
public static void RecordUnpack(KeyInfo keyInfo, int keyLength, byte[] key, UnpackedRecord p) { byte[] keys = key; Mem mem; p.Flags = 0; //: Debug.Assert(C._HASALIGNMENT8(mem)); int szHdr; uint idx = (uint)ConvertEx.GetVarint32(keys, 0, out szHdr); int d = szHdr; ushort u = 0; // Unsigned loop counter while (idx < szHdr && u < p.Fields && d <= keyLength) { p.Mems[u] = mem = C._alloc(p.Mems[u]); uint serialType; idx += (uint)ConvertEx.GetVarint32(keys, idx, out serialType); mem.Encode = keyInfo.Encode; mem.Ctx = (Context)keyInfo.Ctx; //mem->Flags = 0; // sqlite3VdbeSerialGet() will set this for us //: mem.Malloc = null; d += (int)SerialGet(keys, d, serialType, mem); u++; } Debug.Assert(u <= keyInfo.Fields + 1); p.Fields = (ushort)u; }
public static UnpackedRecord AllocUnpackedRecord(KeyInfo keyInfo) { var p = new UnpackedRecord(); p.Mems = new Mem[p.Fields + 1]; p.KeyInfo = keyInfo; p.Fields = (ushort)(keyInfo.Fields + 1); return p; }
public int AddOp4(OP op, int p1, int p2, int p3, KeyInfo p4, Vdbe.P4T p4t) // KeyInfo { int addr = AddOp3(op, p1, p2, p3); ChangeP4(addr, new P4_t { KeyInfo = p4 }, p4t); return addr; }