/* ** This function is responsible for invoking the collation factory callback ** or substituting a collation sequence of a different encoding when the ** requested collation sequence is not available in the desired encoding. ** ** If it is not NULL, then pColl must point to the database native encoding ** collation sequence with name zName, length nName. ** ** The return value is either the collation sequence to be used in database ** db for collation type name zName, length nName, or NULL, if no collation ** sequence can be found. ** ** See also: sqlite3LocateCollSeq(), sqlite3FindCollSeq() */ static CollSeq sqlite3GetCollSeq( sqlite3 db, /* The database connection */ u8 enc, /* The desired encoding for the collating sequence */ CollSeq pColl, /* Collating sequence with native encoding, or NULL */ string zName /* Collating sequence name */ ) { CollSeq p; p = pColl; if (p == null) { p = sqlite3FindCollSeq(db, enc, zName, 0); } if (p == null || p.xCmp == null) { /* No collation sequence of this type for this encoding is registered. ** Call the collation factory to see if it can supply us with one. */ callCollNeeded(db, enc, zName); p = sqlite3FindCollSeq(db, enc, zName, 0); } if (p != null && p.xCmp == null && synthCollSeq(db, p) != 0) { p = null; } Debug.Assert(p == null || p.xCmp != null); return(p); }
/* ** Parameter zName points to a UTF-8 encoded string nName bytes long. ** Return the CollSeq* pointer for the collation sequence named zName ** for the encoding 'enc' from the database 'db'. ** ** If the entry specified is not found and 'create' is true, then create a ** new entry. Otherwise return NULL. ** ** A separate function sqlite3LocateCollSeq() is a wrapper around ** this routine. sqlite3LocateCollSeq() invokes the collation factory ** if necessary and generates an error message if the collating sequence ** cannot be found. ** ** See also: sqlite3LocateCollSeq(), sqlite3GetCollSeq() */ static CollSeq sqlite3FindCollSeq( sqlite3 db, u8 enc, string zName, u8 create ) { CollSeq[] pColl; if (zName != null) { pColl = findCollSeqEntry(db, zName, create); } else { pColl = new CollSeq[enc]; pColl[enc - 1] = db.pDfltColl; } Debug.Assert(SQLITE_UTF8 == 1 && SQLITE_UTF16LE == 2 && SQLITE_UTF16BE == 3); Debug.Assert(enc >= SQLITE_UTF8 && enc <= SQLITE_UTF16BE); if (pColl != null) { enc -= 1; // if (pColl != null) pColl += enc - 1; return(pColl[enc]); } else { return(null); } }
/* ** This routine is called on a collation sequence before it is used to ** check that it is defined. An undefined collation sequence exists when ** a database is loaded that contains references to collation sequences ** that have not been defined by sqlite3_create_collation() etc. ** ** If required, this routine calls the 'collation needed' callback to ** request a definition of the collating sequence. If this doesn't work, ** an equivalent collating sequence that uses a text encoding different ** from the main database is substituted, if one is available. */ static int sqlite3CheckCollSeq(Parse pParse, CollSeq pColl) { if (pColl != null) { string zName = pColl.zName; sqlite3 db = pParse.db; CollSeq p = sqlite3GetCollSeq(db, ENC(db), pColl, zName); if (null == p) { sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName); pParse.nErr++; return(SQLITE_ERROR); } // //Debug.Assert(p == pColl); if (p != pColl) // Had to lookup appropriate sequence { pColl.enc = p.enc; pColl.pUser = p.pUser; pColl.type = p.type; pColl.xCmp = p.xCmp; pColl.xDel = p.xDel; } } return(SQLITE_OK); }
/* ** Locate and return an entry from the db.aCollSeq hash table. If the entry ** specified by zName and nName is not found and parameter 'create' is ** true, then create a new entry. Otherwise return NULL. ** ** Each pointer stored in the sqlite3.aCollSeq hash table contains an ** array of three CollSeq structures. The first is the collation sequence ** prefferred for UTF-8, the second UTF-16le, and the third UTF-16be. ** ** Stored immediately after the three collation sequences is a copy of ** the collation sequence name. A pointer to this string is stored in ** each collation sequence structure. */ static CollSeq[] findCollSeqEntry( sqlite3 db, /* Database connection */ string zName, /* Name of the collating sequence */ int create /* Create a new entry if true */ ) { CollSeq[] pColl; int nName = sqlite3Strlen30(zName); pColl = sqlite3HashFind(db.aCollSeq, zName, nName, (CollSeq[])null); if ((null == pColl) && create != 0) { pColl = new CollSeq[3]; //sqlite3DbMallocZero(db, 3*sizeof(*pColl) + nName + 1 ); if (pColl != null) { CollSeq pDel = null; pColl[0] = new CollSeq(); pColl[0].zName = zName; pColl[0].enc = SQLITE_UTF8; pColl[1] = new CollSeq(); pColl[1].zName = zName; pColl[1].enc = SQLITE_UTF16LE; pColl[2] = new CollSeq(); pColl[2].zName = zName; pColl[2].enc = SQLITE_UTF16BE; //memcpy(pColl[0].zName, zName, nName); //pColl[0].zName[nName] = 0; CollSeq[] pDelArray = sqlite3HashInsert(ref db.aCollSeq, pColl[0].zName, nName, pColl); if (pDelArray != null) { pDel = pDelArray[0]; } /* If a malloc() failure occurred in sqlite3HashInsert(), it will ** return the pColl pointer to be deleted (because it wasn't added ** to the hash table). */ Debug.Assert(pDel == null || pDel == pColl[0]); if (pDel != null) { //// db.mallocFailed = 1; pDel = null; //was sqlite3DbFree(db,ref pDel); pColl = null; } } } return(pColl); }
/* ** This routine is called if the collation factory fails to deliver a ** collation function in the best encoding but there may be other versions ** of this collation function (for other text encodings) available. Use one ** of these instead if they exist. Avoid a UTF-8 <. UTF-16 conversion if ** possible. */ static int synthCollSeq( sqlite3 db, CollSeq pColl ) { CollSeq pColl2; string z = pColl.zName; int i; byte[] aEnc = { SQLITE_UTF16BE, SQLITE_UTF16LE, SQLITE_UTF8 }; for ( i = 0; i < 3; i++ ) { pColl2 = sqlite3FindCollSeq( db, aEnc[i], z, 0 ); if ( pColl2.xCmp != null ) { pColl = pColl2.Copy(); //memcpy(pColl, pColl2, sizeof(CollSeq)); pColl.xDel = null; /* Do not copy the destructor */ return SQLITE_OK; } } return SQLITE_ERROR; }
/* ** This routine is called if the collation factory fails to deliver a ** collation function in the best encoding but there may be other versions ** of this collation function (for other text encodings) available. Use one ** of these instead if they exist. Avoid a UTF-8 <. UTF-16 conversion if ** possible. */ static int synthCollSeq(sqlite3 db, CollSeq pColl) { CollSeq pColl2; string z = pColl.zName; int i; byte[] aEnc = { SQLITE_UTF16BE, SQLITE_UTF16LE, SQLITE_UTF8 }; for (i = 0; i < 3; i++) { pColl2 = sqlite3FindCollSeq(db, aEnc[i], z, 0); if (pColl2.xCmp != null) { pColl = pColl2.Copy(); //memcpy(pColl, pColl2, sizeof(CollSeq)); pColl.xDel = null; /* Do not copy the destructor */ return(SQLITE_OK); } } return(SQLITE_ERROR); }
public ITable pZombieTab; // List of Table objects to delete after code gen #endif #endregion public void CopyFrom(Expr cf) { op = cf.op; affinity = cf.affinity; flags = cf.flags; u = cf.u; pColl = (cf.pColl == null ? null : cf.pColl.Copy()); iTable = cf.iTable; iColumn = cf.iColumn; pAggInfo = (cf.pAggInfo == null ? null : cf.pAggInfo.Copy()); iAgg = cf.iAgg; iRightJoinTable = cf.iRightJoinTable; flags2 = cf.flags2; pTab = (cf.pTab == null ? null : cf.pTab); #if SQLITE_MAX_EXPR_DEPTH nHeight = cf.nHeight; pZombieTab = cf.pZombieTab; #endif pLeft = (cf.pLeft == null ? null : cf.pLeft.Copy()); pRight = (cf.pRight == null ? null : cf.pRight.Copy()); x.pList = (cf.x.pList == null ? null : cf.x.pList.Copy()); x.pSelect = (cf.x.pSelect == null ? null : cf.x.pSelect.Copy()); }
/* ** Compare the values contained by the two memory cells, returning ** negative, zero or positive if pMem1 is less than, equal to, or greater ** than pMem2. Sorting order is NULL's first, followed by numbers (integers ** and reals) sorted numerically, followed by text ordered by the collating ** sequence pColl and finally blob's ordered by memcmp(). ** ** Two NULL values are considered equal by this function. */ private static int sqlite3MemCompare(Mem pMem1, Mem pMem2, CollSeq pColl) { int rc; int f1, f2; int combined_flags; f1 = pMem1.flags; f2 = pMem2.flags; combined_flags = f1 | f2; Debug.Assert((combined_flags & MEM_RowSet) == 0); /* If one value is NULL, it is less than the other. If both values ** are NULL, return 0. */ if ((combined_flags & MEM_Null) != 0) { return (f2 & MEM_Null) - (f1 & MEM_Null); } /* If one value is a number and the other is not, the number is less. ** If both are numbers, compare as reals if one is a real, or as integers ** if both values are integers. */ if ((combined_flags & (MEM_Int | MEM_Real)) != 0) { if ((f1 & (MEM_Int | MEM_Real)) == 0) { return 1; } if ((f2 & (MEM_Int | MEM_Real)) == 0) { return -1; } if ((f1 & f2 & MEM_Int) == 0) { double r1, r2; if ((f1 & MEM_Real) == 0) { r1 = (double)pMem1.u.i; } else { r1 = pMem1.r; } if ((f2 & MEM_Real) == 0) { r2 = (double)pMem2.u.i; } else { r2 = pMem2.r; } if (r1 < r2) return -1; if (r1 > r2) return 1; return 0; } else { Debug.Assert((f1 & MEM_Int) != 0); Debug.Assert((f2 & MEM_Int) != 0); if (pMem1.u.i < pMem2.u.i) return -1; if (pMem1.u.i > pMem2.u.i) return 1; return 0; } } /* If one value is a string and the other is a blob, the string is less. ** If both are strings, compare using the collating functions. */ if ((combined_flags & MEM_Str) != 0) { if ((f1 & MEM_Str) == 0) { return 1; } if ((f2 & MEM_Str) == 0) { return -1; } Debug.Assert(pMem1.enc == pMem2.enc); Debug.Assert(pMem1.enc == SQLITE_UTF8 || pMem1.enc == SQLITE_UTF16LE || pMem1.enc == SQLITE_UTF16BE); /* The collation sequence must be defined at this point, even if ** the user deletes the collation sequence after the vdbe program is ** compiled (this was not always the case). */ Debug.Assert(pColl == null || pColl.xCmp != null); if (pColl != null) { if (pMem1.enc == pColl.enc) { /* The strings are already in the correct encoding. Call the ** comparison function directly */ return pColl.xCmp(pColl.pUser, pMem1.n, pMem1.z, pMem2.n, pMem2.z); } else { string v1, v2; int n1, n2; Mem c1 = null; Mem c2 = null; c1 = sqlite3Malloc(c1);// memset( &c1, 0, sizeof( c1 ) ); c2 = sqlite3Malloc(c2);// memset( &c2, 0, sizeof( c2 ) ); sqlite3VdbeMemShallowCopy(c1, pMem1, MEM_Ephem); sqlite3VdbeMemShallowCopy(c2, pMem2, MEM_Ephem); v1 = sqlite3ValueText((sqlite3_value)c1, pColl.enc); n1 = v1 == null ? 0 : c1.n; v2 = sqlite3ValueText((sqlite3_value)c2, pColl.enc); n2 = v2 == null ? 0 : c2.n; rc = pColl.xCmp(pColl.pUser, n1, v1, n2, v2); sqlite3VdbeMemRelease(c1); sqlite3VdbeMemRelease(c2); return rc; } } /* If a NULL pointer was passed as the collate function, fall through ** to the blob case and use memcmp(). */ } /* Both values must be blobs. Compare using memcmp(). */ if ((pMem1.flags & MEM_Blob) != 0) if (pMem1.zBLOB != null) rc = memcmp(pMem1.zBLOB, pMem2.zBLOB, (pMem1.n > pMem2.n) ? pMem2.n : pMem1.n); else rc = memcmp(pMem1.z, pMem2.zBLOB, (pMem1.n > pMem2.n) ? pMem2.n : pMem1.n); else rc = memcmp(pMem1.z, pMem2.z, (pMem1.n > pMem2.n) ? pMem2.n : pMem1.n); if (rc == 0) { rc = pMem1.n - pMem2.n; } return rc; }
/* ** Set the explicit collating sequence for an expression to the ** collating sequence supplied in the second argument. */ static Expr sqlite3ExprSetColl( Expr pExpr, CollSeq pColl ) { if ( pExpr != null && pColl != null ) { pExpr.pColl = pColl; pExpr.flags |= EP_ExpCollate; } return pExpr; }
/* ** Parameter zName points to a UTF-8 encoded string nName bytes long. ** Return the CollSeq* pointer for the collation sequence named zName ** for the encoding 'enc' from the database 'db'. ** ** If the entry specified is not found and 'create' is true, then create a ** new entry. Otherwise return NULL. ** ** A separate function sqlite3LocateCollSeq() is a wrapper around ** this routine. sqlite3LocateCollSeq() invokes the collation factory ** if necessary and generates an error message if the collating sequence ** cannot be found. ** ** See also: sqlite3LocateCollSeq(), sqlite3GetCollSeq() */ static CollSeq sqlite3FindCollSeq( sqlite3 db, u8 enc, string zName, u8 create ) { CollSeq[] pColl; if ( zName != null ) { pColl = findCollSeqEntry( db, zName, create ); } else { pColl = new CollSeq[enc]; pColl[enc - 1] = db.pDfltColl; } Debug.Assert( SQLITE_UTF8 == 1 && SQLITE_UTF16LE == 2 && SQLITE_UTF16BE == 3 ); Debug.Assert( enc >= SQLITE_UTF8 && enc <= SQLITE_UTF16BE ); if ( pColl != null ) { enc -= 1; // if (pColl != null) pColl += enc - 1; return pColl[enc]; } else return null; }
/* ** Locate and return an entry from the db.aCollSeq hash table. If the entry ** specified by zName and nName is not found and parameter 'create' is ** true, then create a new entry. Otherwise return NULL. ** ** Each pointer stored in the sqlite3.aCollSeq hash table contains an ** array of three CollSeq structures. The first is the collation sequence ** prefferred for UTF-8, the second UTF-16le, and the third UTF-16be. ** ** Stored immediately after the three collation sequences is a copy of ** the collation sequence name. A pointer to this string is stored in ** each collation sequence structure. */ static CollSeq[] findCollSeqEntry( sqlite3 db, /* Database connection */ string zName, /* Name of the collating sequence */ int create /* Create a new entry if true */ ) { CollSeq[] pColl; int nName = sqlite3Strlen30( zName ); pColl = sqlite3HashFind( db.aCollSeq, zName, nName, (CollSeq[])null ); if ( ( null == pColl ) && create != 0 ) { pColl = new CollSeq[3]; //sqlite3DbMallocZero(db, 3*sizeof(*pColl) + nName + 1 ); if ( pColl != null ) { CollSeq pDel = null; pColl[0] = new CollSeq(); pColl[0].zName = zName; pColl[0].enc = SQLITE_UTF8; pColl[1] = new CollSeq(); pColl[1].zName = zName; pColl[1].enc = SQLITE_UTF16LE; pColl[2] = new CollSeq(); pColl[2].zName = zName; pColl[2].enc = SQLITE_UTF16BE; //memcpy(pColl[0].zName, zName, nName); //pColl[0].zName[nName] = 0; CollSeq[] pDelArray = sqlite3HashInsert( ref db.aCollSeq, pColl[0].zName, nName, pColl ); if ( pDelArray != null ) pDel = pDelArray[0]; /* If a malloc() failure occurred in sqlite3HashInsert(), it will ** return the pColl pointer to be deleted (because it wasn't added ** to the hash table). */ Debug.Assert( pDel == null || pDel == pColl[0] ); if ( pDel != null ) { //// db.mallocFailed = 1; pDel = null; //was sqlite3DbFree(db,ref pDel); pColl = null; } } } return pColl; }
/* ** This routine is called on a collation sequence before it is used to ** check that it is defined. An undefined collation sequence exists when ** a database is loaded that contains references to collation sequences ** that have not been defined by sqlite3_create_collation() etc. ** ** If required, this routine calls the 'collation needed' callback to ** request a definition of the collating sequence. If this doesn't work, ** an equivalent collating sequence that uses a text encoding different ** from the main database is substituted, if one is available. */ static int sqlite3CheckCollSeq( Parse pParse, CollSeq pColl ) { if ( pColl != null ) { string zName = pColl.zName; sqlite3 db = pParse.db; CollSeq p = sqlite3GetCollSeq( db, ENC( db ), pColl, zName ); if ( null == p ) { sqlite3ErrorMsg( pParse, "no such collation sequence: %s", zName ); pParse.nErr++; return SQLITE_ERROR; } // //Debug.Assert(p == pColl); if ( p != pColl ) // Had to lookup appropriate sequence { pColl.enc = p.enc; pColl.pUser = p.pUser; pColl.type = p.type; pColl.xCmp = p.xCmp; pColl.xDel = p.xDel; } } return SQLITE_OK; }
/* ** This function is responsible for invoking the collation factory callback ** or substituting a collation sequence of a different encoding when the ** requested collation sequence is not available in the desired encoding. ** ** If it is not NULL, then pColl must point to the database native encoding ** collation sequence with name zName, length nName. ** ** The return value is either the collation sequence to be used in database ** db for collation type name zName, length nName, or NULL, if no collation ** sequence can be found. ** ** See also: sqlite3LocateCollSeq(), sqlite3FindCollSeq() */ static CollSeq sqlite3GetCollSeq( sqlite3 db, /* The database connection */ u8 enc, /* The desired encoding for the collating sequence */ CollSeq pColl, /* Collating sequence with native encoding, or NULL */ string zName /* Collating sequence name */ ) { CollSeq p; p = pColl; if ( p == null ) { p = sqlite3FindCollSeq( db, enc, zName, 0 ); } if ( p == null || p.xCmp == null ) { /* No collation sequence of this type for this encoding is registered. ** Call the collation factory to see if it can supply us with one. */ callCollNeeded( db, enc, zName ); p = sqlite3FindCollSeq( db, enc, zName, 0 ); } if ( p != null && p.xCmp == null && synthCollSeq( db, p ) != 0 ) { p = null; } Debug.Assert( p == null || p.xCmp != null ); return p; }
//CollSeq static int sqlite3VdbeAddOp4( Vdbe p, int op, int p1, int p2, int p3, CollSeq pP4, int p4type ) { union_p4 _p4 = new union_p4(); _p4.pColl = pP4; int addr = sqlite3VdbeAddOp3( p, op, p1, p2, p3 ); sqlite3VdbeChangeP4( p, addr, _p4, p4type ); return addr; }
/* ** Change the value of the P4 operand for a specific instruction. ** This routine is useful when a large program is loaded from a ** static array using sqlite3VdbeAddOpList but we want to make a ** few minor changes to the program. ** ** If n>=0 then the P4 operand is dynamic, meaning that a copy of ** the string is made into memory obtained from sqlite3Malloc(). ** A value of n==0 means copy bytes of zP4 up to and including the ** first null byte. If n>0 then copy n+1 bytes of zP4. ** ** If n==P4_KEYINFO it means that zP4 is a pointer to a KeyInfo structure. ** A copy is made of the KeyInfo structure into memory obtained from ** sqlite3Malloc, to be freed when the Vdbe is finalized. ** n==P4_KEYINFO_HANDOFF indicates that zP4 points to a KeyInfo structure ** stored in memory that the caller has obtained from sqlite3Malloc. The ** caller should not free the allocation, it will be freed when the Vdbe is ** finalized. ** ** Other values of n (P4_STATIC, P4_COLLSEQ etc.) indicate that zP4 points ** to a string or structure that is guaranteed to exist for the lifetime of ** the Vdbe. In these cases we can just copy the pointer. ** ** If addr<0 then change P4 on the most recently inserted instruction. */ //P4_COLLSEQ static void sqlite3VdbeChangeP4( Vdbe p, int addr, CollSeq pColl, int n ) { union_p4 _p4 = new union_p4(); _p4.pColl = pColl; sqlite3VdbeChangeP4( p, addr, _p4, n ); }
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); }