Пример #1
0
        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);
        }