Example #1
0
        /*
        ** 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);
        }
Example #2
0
 /*
 ** 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);
     }
 }
Example #3
0
 /*
 ** 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);
 }
Example #4
0
        /*
        ** 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);
        }
Example #5
0
 /*
 ** 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;
 }
Example #6
0
        /*
        ** 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);
        }
Example #7
0
        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());
        }
Example #8
0
		/*
		** 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;
		}
Example #9
0
 /*
 ** 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;
 }
Example #10
0
 /*
 ** 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;
 }
Example #11
0
    /*
    ** 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;
    }
Example #12
0
    /*
    ** 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;
    }
Example #13
0
    /*
    ** 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;
    }
Example #14
0
 //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;
 }
Example #15
0
    /*
    ** 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 );
    }
Example #16
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);
        }