public static void sqlite3ColumnDefault(Vdbe v, Table table, int i, int regId) { Debug.Assert(table != null); if (table.Select == null) { TEXTENCODE encode = Context.CTXENCODE(v.Ctx); Column col = table.Cols[i]; v.VdbeComment("%s.%s", table.Name, col.Name); Debug.Assert(i < table.Cols.length); Mem value = new Mem(); sqlite3ValueFromExpr(v.Ctx, col.Dflt, encode, col.Affinity, ref value); if (value != null) { v.ChangeP4(-1, value, Vdbe.P4T.MEM); } #if !OMIT_FLOATING_POINT if (regId >= 0 && table.Cols[i].Affinity == AFF.REAL) { v.AddOp1(OP.RealAffinity, regId); } #endif } }
static void AnalyzeOneTable(Parse parse, Table table, Index onlyIdx, int statCurId, int memId) { Context ctx = parse.Ctx; // Database handle int i; // Loop counter int regTabname = memId++; // Register containing table name int regIdxname = memId++; // Register containing index name int regSampleno = memId++; // Register containing next sample number int regCol = memId++; // Content of a column analyzed table int regRec = memId++; // Register holding completed record int regTemp = memId++; // Temporary use register int regRowid = memId++; // Rowid for the inserted record Vdbe v = parse.GetVdbe(); // The virtual machine being built up if (v == null || C._NEVER(table == null)) { return; } // Do not gather statistics on views or virtual tables or system tables if (table.Id == 0 || table.Name.StartsWith("sqlite_", StringComparison.OrdinalIgnoreCase)) { return; } Debug.Assert(Btree.HoldsAllMutexes(ctx)); int db = sqlite3SchemaToIndex(ctx, table.Schema); // Index of database containing pTab Debug.Assert(db >= 0); Debug.Assert(Btree.SchemaMutexHeld(ctx, db, null)); #if !OMIT_AUTHORIZATION if (Auth.Check(parse, AUTH.ANALYZE, table.Name, 0, ctx.DBs[db].Name)) { return; } #endif // Establish a read-lock on the table at the shared-cache level. sqlite3TableLock(parse, db, table.Id, 0, table.Name); int zeroRows = -1; // Jump from here if number of rows is zero int idxCurId = parse.Tabs++; // Cursor open on index being analyzed v.AddOp4(OP.String8, 0, regTabname, 0, table.Name, 0); for (Index idx = table.Index; idx != null; idx = idx.Next) // An index to being analyzed { if (onlyIdx != null && onlyIdx != idx) { continue; } v.NoopComment("Begin analysis of %s", idx.Name); int cols = idx.Columns.length; int[] chngAddrs = C._tagalloc <int>(ctx, cols); // Array of jump instruction addresses if (chngAddrs == null) { continue; } KeyInfo key = sqlite3IndexKeyinfo(parse, idx); if (memId + 1 + (cols * 2) > parse.Mems) { parse.Mems = memId + 1 + (cols * 2); } // Open a cursor to the index to be analyzed. Debug.Assert(db == sqlite3SchemaToIndex(ctx, idx.Schema)); v.AddOp4(OP.OpenRead, idxCurId, idx.Id, db, key, Vdbe.P4T.KEYINFO_HANDOFF); v.VdbeComment("%s", idx.Name); // Populate the registers containing the index names. v.AddOp4(OP.String8, 0, regIdxname, 0, idx.Name, 0); #if ENABLE_STAT3 bool once = false; // One-time initialization if (once) { once = false; sqlite3OpenTable(parse, tabCurId, db, table, OP.OpenRead); } v.AddOp2(OP.Count, idxCurId, regCount); v.AddOp2(OP.Integer, STAT3_SAMPLES, regTemp1); v.AddOp2(OP.Integer, 0, regNumEq); v.AddOp2(OP.Integer, 0, regNumLt); v.AddOp2(OP.Integer, -1, regNumDLt); v.AddOp3(OP.Null, 0, regSample, regAccum); v.AddOp4(OP.Function, 1, regCount, regAccum, (object)_stat3InitFuncdef, Vdbe.P4T.FUNCDEF); v.ChangeP5(2); #endif // The block of memory cells initialized here is used as follows. // // iMem: // The total number of rows in the table. // // iMem+1 .. iMem+nCol: // Number of distinct entries in index considering the left-most N columns only, where N is between 1 and nCol, // inclusive. // // iMem+nCol+1 .. Mem+2*nCol: // Previous value of indexed columns, from left to right. // // Cells iMem through iMem+nCol are initialized to 0. The others are initialized to contain an SQL NULL. for (i = 0; i <= cols; i++) { v.AddOp2(OP.Integer, 0, memId + i); } for (i = 0; i < cols; i++) { v.AddOp2(OP.Null, 0, memId + cols + i + 1); } // Start the analysis loop. This loop runs through all the entries in the index b-tree. int endOfLoop = v.MakeLabel(); // The end of the loop v.AddOp2(OP.Rewind, idxCurId, endOfLoop); int topOfLoop = v.CurrentAddr(); // The top of the loop v.AddOp2(OP.AddImm, memId, 1); int addrIfNot = 0; // address of OP_IfNot for (i = 0; i < cols; i++) { v.AddOp3(OP.Column, idxCurId, i, regCol); if (i == 0) // Always record the very first row { addrIfNot = v.AddOp1(OP.IfNot, memId + 1); } Debug.Assert(idx.CollNames != null && idx.CollNames[i] != null); CollSeq coll = sqlite3LocateCollSeq(parse, idx.CollNames[i]); chngAddrs[i] = v.AddOp4(OP.Ne, regCol, 0, memId + cols + i + 1, coll, Vdbe.P4T.COLLSEQ); v.ChangeP5(SQLITE_NULLEQ); v.VdbeComment("jump if column %d changed", i); #if ENABLE_STAT3 if (i == 0) { v.AddOp2(OP.AddImm, regNumEq, 1); v.VdbeComment("incr repeat count"); } #endif } v.AddOp2(OP.Goto, 0, endOfLoop); for (i = 0; i < cols; i++) { v.JumpHere(chngAddrs[i]); // Set jump dest for the OP_Ne if (i == 0) { v.JumpHere(addrIfNot); // Jump dest for OP_IfNot #if ENABLE_STAT3 v.AddOp4(OP.Function, 1, regNumEq, regTemp2, (object)Stat3PushFuncdef, Vdbe.P4T.FUNCDEF); v.ChangeP5(5); v.AddOp3(OP.Column, idxCurId, idx.Columns.length, regRowid); v.AddOp3(OP.Add, regNumEq, regNumLt, regNumLt); v.AddOp2(OP.AddImm, regNumDLt, 1); v.AddOp2(OP.Integer, 1, regNumEq); #endif } v.AddOp2(OP.AddImm, memId + i + 1, 1); v.AddOp3(OP.Column, idxCurId, i, memId + cols + i + 1); } C._tagfree(ctx, chngAddrs); // Always jump here after updating the iMem+1...iMem+1+nCol counters v.ResolveLabel(endOfLoop); v.AddOp2(OP.Next, idxCurId, topOfLoop); v.AddOp1(OP.Close, idxCurId); #if ENABLE_STAT3 v.AddOp4(OP.Function, 1, regNumEq, regTemp2, (object)Stat3PushFuncdef, Vdbe.P4T.FUNCDEF); v.ChangeP5(5); v.AddOp2(OP.Integer, -1, regLoop); int shortJump = v.AddOp2(OP_AddImm, regLoop, 1); // Instruction address v.AddOp4(OP.Function, 1, regAccum, regTemp1, (object)Stat3GetFuncdef, Vdbe.P4T.FUNCDEF); v.ChangeP5(2); v.AddOp1(OP.IsNull, regTemp1); v.AddOp3(OP.NotExists, tabCurId, shortJump, regTemp1); v.AddOp3(OP.Column, tabCurId, idx->Columns[0], regSample); sqlite3ColumnDefault(v, table, idx->Columns[0], regSample); v.AddOp4(OP.Function, 1, regAccum, regNumEq, (object)Stat3GetFuncdef, Vdbe.P4T.FUNCDEF); v.ChangeP5(3); v.AddOp4(OP.Function, 1, regAccum, regNumLt, (object)Stat3GetFuncdef, Vdbe.P4T.FUNCDEF); v.ChangeP5(4); v.AddOp4(OP.Function, 1, regAccum, regNumDLt, (object)Stat3GetFuncdef, Vdbe.P4T.FUNCDEF); v.ChangeP5(5); v.AddOp4(OP.MakeRecord, regTabname, 6, regRec, "bbbbbb", 0); v.AddOp2(OP.NewRowid, statCurId + 1, regNewRowid); v.AddOp3(OP.Insert, statCurId + 1, regRec, regNewRowid); v.AddOp2(OP.Goto, 0, shortJump); v.JumpHere(shortJump + 2); #endif // Store the results in sqlite_stat1. // // The result is a single row of the sqlite_stat1 table. The first two columns are the names of the table and index. The third column // is a string composed of a list of integer statistics about the index. The first integer in the list is the total number of entries // in the index. There is one additional integer in the list for each column of the table. This additional integer is a guess of how many // rows of the table the index will select. If D is the count of distinct values and K is the total number of rows, then the integer is computed // as: // I = (K+D-1)/D // If K==0 then no entry is made into the sqlite_stat1 table. // If K>0 then it is always the case the D>0 so division by zero is never possible. v.AddOp2(OP_SCopy, memId, regStat1); if (zeroRows < 0) { zeroRows = v.AddOp1(OP.IfNot, memId); } for (i = 0; i < cols; i++) { v.AddOp4(OP.String8, 0, regTemp, 0, " ", 0); v.AddOp3(OP.Concat, regTemp, regStat1, regStat1); v.AddOp3(OP.Add, memId, memId + i + 1, regTemp); v.AddOp2(OP.AddImm, regTemp, -1); v.AddOp3(OP.Divide, memId + i + 1, regTemp, regTemp); v.AddOp1(OP.ToInt, regTemp); v.AddOp3(OP.Concat, regTemp, regStat1, regStat1); } v.AddOp4(OP.MakeRecord, regTabname, 3, regRec, "aaa", 0); v.AddOp2(OP.NewRowid, statCurId, regNewRowid); v.AddOp3(OP.Insert, statCurId, regRec, regNewRowid); v.ChangeP5(OPFLAG_APPEND); } // If the table has no indices, create a single sqlite_stat1 entry containing NULL as the index name and the row count as the content. if (table.Index == null) { v.AddOp3(OP.OpenRead, idxCurId, table->Id, db); v.VdbeComment("%s", table->Name); v.AddOp2(OP.Count, idxCurId, regStat1); v.AddOp1(OP.Close, idxCurId); zeroRows = v.AddOp1(OP.IfNot, regStat1); } else { v.JumpHere(zeroRows); zeroRows = v.AddOp0(OP_Goto); } v.AddOp2(OP.Null, 0, regIdxname); v.AddOp4(OP.MakeRecord, regTabname, 3, regRec, "aaa", 0); v.AddOp2(OP.NewRowid, statCurId, regNewRowid); v.AddOp3(OP.Insert, statCurId, regRec, regNewRowid); v.ChangeP5(OPFLAG_APPEND); if (parse.Mems < regRec) { parse.Mems = regRec; } v.JumpHere(zeroRows); }