/library/Library/WP7/SQLiteDriver/sqlite/build_c.cs
C# | 4331 lines | 3723 code | 86 blank | 522 comment | 288 complexity | e464bb4162b4f5b8e141695420744dce MD5 | raw file
- using System;
- using System.Diagnostics;
- using System.IO;
- using System.Text;
- using i16 = System.Int16;
- using u8 = System.Byte;
- using u16 = System.UInt16;
- using u32 = System.UInt32;
- using Pgno = System.UInt32;
- namespace Community.CsharpSqlite
- {
- public partial class Sqlite3
- {
- /*
- ** 2001 September 15
- **
- ** The author disclaims copyright to this source code. In place of
- ** a legal notice, here is a blessing:
- **
- ** May you do good and not evil.
- ** May you find forgiveness for yourself and forgive others.
- ** May you share freely, never taking more than you give.
- **
- *************************************************************************
- ** This file contains C code routines that are called by the SQLite parser
- ** when syntax rules are reduced. The routines in this file handle the
- ** following kinds of SQL syntax:
- **
- ** CREATE TABLE
- ** DROP TABLE
- ** CREATE INDEX
- ** DROP INDEX
- ** creating ID lists
- ** BEGIN TRANSACTION
- ** COMMIT
- ** ROLLBACK
- *************************************************************************
- ** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
- ** C#-SQLite is an independent reimplementation of the SQLite software library
- **
- ** SQLITE_SOURCE_ID: 2010-12-07 20:14:09 a586a4deeb25330037a49df295b36aaf624d0f45
- **
- *************************************************************************
- */
- //#include "sqliteInt.h"
- /*
- ** This routine is called when a new SQL statement is beginning to
- ** be parsed. Initialize the pParse structure as needed.
- */
- static void sqlite3BeginParse( Parse pParse, int explainFlag )
- {
- pParse.explain = (byte)explainFlag;
- pParse.nVar = 0;
- }
- #if !SQLITE_OMIT_SHARED_CACHE
- /*
- ** The TableLock structure is only used by the sqlite3TableLock() and
- ** codeTableLocks() functions.
- */
- //struct TableLock {
- // int iDb; /* The database containing the table to be locked */
- // int iTab; /* The root page of the table to be locked */
- // u8 isWriteLock; /* True for write lock. False for a read lock */
- // string zName; /* Name of the table */
- //};
- public class TableLock
- {
- public int iDb; /* The database containing the table to be locked */
- public int iTab; /* The root page of the table to be locked */
- public u8 isWriteLock; /* True for write lock. False for a read lock */
- public string zName; /* Name of the table */
- }
- /*
- ** Record the fact that we want to lock a table at run-time.
- **
- ** The table to be locked has root page iTab and is found in database iDb.
- ** A read or a write lock can be taken depending on isWritelock.
- **
- ** This routine just records the fact that the lock is desired. The
- ** code to make the lock occur is generated by a later call to
- ** codeTableLocks() which occurs during sqlite3FinishCoding().
- */
- void sqlite3TableLock(
- Parse *pParse, /* Parsing context */
- int iDb, /* Index of the database containing the table to lock */
- int iTab, /* Root page number of the table to be locked */
- u8 isWriteLock, /* True for a write lock */
- const char *zName /* Name of the table to be locked */
- ){
- Parse *pToplevel = sqlite3ParseToplevel(pParse);
- int i;
- int nBytes;
- TableLock *p;
- assert( iDb>=0 );
- for(i=0; i<pToplevel->nTableLock; i++){
- p = &pToplevel->aTableLock[i];
- if( p->iDb==iDb && p->iTab==iTab ){
- p->isWriteLock = (p->isWriteLock || isWriteLock);
- return;
- }
- }
- nBytes = sizeof(TableLock) * (pToplevel->nTableLock+1);
- pToplevel->aTableLock =
- sqlite3DbReallocOrFree(pToplevel->db, pToplevel->aTableLock, nBytes);
- if( pToplevel->aTableLock ){
- p = &pToplevel->aTableLock[pToplevel->nTableLock++];
- p->iDb = iDb;
- p->iTab = iTab;
- p->isWriteLock = isWriteLock;
- p->zName = zName;
- }else{
- pToplevel->nTableLock = 0;
- pToplevel->db->mallocFailed = 1;
- }
- }
- /*
- ** Code an OP_TableLock instruction for each table locked by the
- ** statement (configured by calls to sqlite3TableLock()).
- */
- static void codeTableLocks( Parse pParse )
- {
- int i;
- Vdbe pVdbe;
- pVdbe = sqlite3GetVdbe( pParse );
- Debug.Assert( pVdbe != null ); /* sqlite3GetVdbe cannot fail: VDBE already allocated */
- for ( i = 0 ; i < pParse.nTableLock ; i++ )
- {
- TableLock p = pParse.aTableLock[i];
- int p1 = p.iDb;
- sqlite3VdbeAddOp4( pVdbe, OP_TableLock, p1, p.iTab, p.isWriteLock,
- p.zName, P4_STATIC );
- }
- }
- #else
- // #define codeTableLocks(x)
- static void codeTableLocks( Parse pParse )
- {
- }
- #endif
- /*
- ** This routine is called after a single SQL statement has been
- ** parsed and a VDBE program to execute that statement has been
- ** prepared. This routine puts the finishing touches on the
- ** VDBE program and resets the pParse structure for the next
- ** parse.
- **
- ** Note that if an error occurred, it might be the case that
- ** no VDBE code was generated.
- */
- static void sqlite3FinishCoding( Parse pParse )
- {
- sqlite3 db;
- Vdbe v;
- db = pParse.db;
- // if ( db.mallocFailed != 0 ) return;
- if ( pParse.nested != 0 )
- return;
- if ( pParse.nErr != 0 )
- return;
- /* Begin by generating some termination code at the end of the
- ** vdbe program
- */
- v = sqlite3GetVdbe( pParse );
- Debug.Assert( 0 == pParse.isMultiWrite
- || sqlite3VdbeAssertMayAbort( v, pParse.mayAbort ) != 0 );
- if ( v != null )
- {
- sqlite3VdbeAddOp0( v, OP_Halt );
- /* The cookie mask contains one bit for each database file open.
- ** (Bit 0 is for main, bit 1 is for temp, and so forth.) Bits are
- ** set for each database that is used. Generate code to start a
- ** transaction on each used database and to verify the schema cookie
- ** on each used database.
- */
- if ( pParse.cookieGoto > 0 )
- {
- u32 mask;
- int iDb;
- sqlite3VdbeJumpHere( v, pParse.cookieGoto - 1 );
- for ( iDb = 0, mask = 1; iDb < db.nDb; mask <<= 1, iDb++ )
- {
- if ( ( mask & pParse.cookieMask ) == 0 )
- continue;
- sqlite3VdbeUsesBtree( v, iDb );
- sqlite3VdbeAddOp2( v, OP_Transaction, iDb, ( mask & pParse.writeMask ) != 0 );
- if ( db.init.busy == 0 )
- {
- sqlite3VdbeAddOp2( v, OP_VerifyCookie, iDb, pParse.cookieValue[iDb] );
- }
- }
- #if !SQLITE_OMIT_VIRTUALTABLE
- {
- int i;
- for(i=0; i<pParse.nVtabLock; i++){
- char *vtab = (char *)sqlite3GetVTable(db, pParse->apVtabLock[i]);
- sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB);
- }
- pParse.nVtabLock = 0;
- }
- #endif
- /* Once all the cookies have been verified and transactions opened,
- ** obtain the required table-locks. This is a no-op unless the
- ** shared-cache feature is enabled.
- */
- codeTableLocks( pParse );
- /* Initialize any AUTOINCREMENT data structures required.
- */
- sqlite3AutoincrementBegin( pParse );
- /* Finally, jump back to the beginning of the executable code. */
- sqlite3VdbeAddOp2( v, OP_Goto, 0, pParse.cookieGoto );
- }
- }
- /* Get the VDBE program ready for execution
- */
- if ( v != null && ALWAYS( pParse.nErr == 0 ) /* && 0 == db.mallocFailed */ )
- {
- #if SQLITE_DEBUG
- TextWriter trace = ( db.flags & SQLITE_VdbeTrace ) != 0 ? Console.Out : null;
- sqlite3VdbeTrace( v, trace );
- #endif
- Debug.Assert( pParse.iCacheLevel == 0 ); /* Disables and re-enables match */
- /* A minimum of one cursor is required if autoincrement is used
- * See ticket [a696379c1f08866] */
- if ( pParse.pAinc != null && pParse.nTab == 0 )
- pParse.nTab = 1;
- sqlite3VdbeMakeReady( v, pParse.nVar, pParse.nMem,
- pParse.nTab, pParse.nMaxArg, pParse.explain,
- ( pParse.isMultiWrite != 0 && pParse.mayAbort != 0 ) ? 1 : 0 );
- pParse.rc = SQLITE_DONE;
- pParse.colNamesSet = 0;
- }
- else
- {
- pParse.rc = SQLITE_ERROR;
- }
- pParse.nTab = 0;
- pParse.nMem = 0;
- pParse.nSet = 0;
- pParse.nVar = 0;
- pParse.cookieMask = 0;
- pParse.cookieGoto = 0;
- }
- /*
- ** Run the parser and code generator recursively in order to generate
- ** code for the SQL statement given onto the end of the pParse context
- ** currently under construction. When the parser is run recursively
- ** this way, the final OP_Halt is not appended and other initialization
- ** and finalization steps are omitted because those are handling by the
- ** outermost parser.
- **
- ** Not everything is nestable. This facility is designed to permit
- ** INSERT, UPDATE, and DELETE operations against SQLITE_MASTER. Use
- ** care if you decide to try to use this routine for some other purposes.
- */
- static void sqlite3NestedParse( Parse pParse, string zFormat, params object[] ap )
- {
- string zSql; // char *zSql;
- string zErrMsg = "";// char* zErrMsg = 0;
- sqlite3 db = pParse.db;
- //# define SAVE_SZ (Parse.Length - offsetof(Parse,nVar))
- // char saveBuf[SAVE_SZ];
- if ( pParse.nErr != 0 )
- return;
- Debug.Assert( pParse.nested < 10 ); /* Nesting should only be of limited depth */
- // va_list ap;
- lock ( lock_va_list )
- {
- va_start( ap, zFormat );
- zSql = sqlite3VMPrintf( db, zFormat, ap );
- va_end( ref ap );
- }
- //if( zSql=="" ){
- // return; /* A malloc must have failed */
- //}
- lock ( nestingLock )
- {
- pParse.nested++;
- pParse.SaveMembers(); // memcpy(saveBuf, pParse.nVar, SAVE_SZ);
- pParse.ResetMembers(); // memset(pParse.nVar, 0, SAVE_SZ);
- sqlite3RunParser( pParse, zSql, ref zErrMsg );
- sqlite3DbFree( db, ref zErrMsg );
- sqlite3DbFree( db, ref zSql );
- pParse.RestoreMembers(); // memcpy(pParse.nVar, saveBuf, SAVE_SZ);
- pParse.nested--;
- }
- }
- static Object nestingLock = new Object();
- /*
- ** Locate the in-memory structure that describes a particular database
- ** table given the name of that table and (optionally) the name of the
- ** database containing the table. Return NULL if not found.
- **
- ** If zDatabase is 0, all databases are searched for the table and the
- ** first matching table is returned. (No checking for duplicate table
- ** names is done.) The search order is TEMP first, then MAIN, then any
- ** auxiliary databases added using the ATTACH command.
- **
- ** See also sqlite3LocateTable().
- */
- static Table sqlite3FindTable( sqlite3 db, string zName, string zDatabase )
- {
- Table p = null;
- int i;
- int nName;
- Debug.Assert( zName != null );
- nName = sqlite3Strlen30( zName );
- for ( i = OMIT_TEMPDB; i < db.nDb; i++ )
- {
- int j = ( i < 2 ) ? i ^ 1 : i; /* Search TEMP before MAIN */
- if ( zDatabase != null && !zDatabase.Equals( db.aDb[j].zName, StringComparison.InvariantCultureIgnoreCase ) )
- continue;
- p = sqlite3HashFind( db.aDb[j].pSchema.tblHash, zName, nName, (Table)null );
- if ( p != null )
- break;
- }
- return p;
- }
- /*
- ** Locate the in-memory structure that describes a particular database
- ** table given the name of that table and (optionally) the name of the
- ** database containing the table. Return NULL if not found. Also leave an
- ** error message in pParse.zErrMsg.
- **
- ** The difference between this routine and sqlite3FindTable() is that this
- ** routine leaves an error message in pParse.zErrMsg where
- ** sqlite3FindTable() does not.
- */
- static Table sqlite3LocateTable(
- Parse pParse, /* context in which to report errors */
- int isView, /* True if looking for a VIEW rather than a TABLE */
- string zName, /* Name of the table we are looking for */
- string zDbase /* Name of the database. Might be NULL */
- )
- {
- Table p;
- /* Read the database schema. If an error occurs, leave an error message
- ** and code in pParse and return NULL. */
- if ( SQLITE_OK != sqlite3ReadSchema( pParse ) )
- {
- return null;
- }
- p = sqlite3FindTable( pParse.db, zName, zDbase );
- if ( p == null )
- {
- string zMsg = isView != 0 ? "no such view" : "no such table";
- if ( zDbase != null )
- {
- sqlite3ErrorMsg( pParse, "%s: %s.%s", zMsg, zDbase, zName );
- }
- else
- {
- sqlite3ErrorMsg( pParse, "%s: %s", zMsg, zName );
- }
- pParse.checkSchema = 1;
- }
- return p;
- }
- /*
- ** Locate the in-memory structure that describes
- ** a particular index given the name of that index
- ** and the name of the database that contains the index.
- ** Return NULL if not found.
- **
- ** If zDatabase is 0, all databases are searched for the
- ** table and the first matching index is returned. (No checking
- ** for duplicate index names is done.) The search order is
- ** TEMP first, then MAIN, then any auxiliary databases added
- ** using the ATTACH command.
- */
- static Index sqlite3FindIndex( sqlite3 db, string zName, string zDb )
- {
- Index p = null;
- int i;
- int nName = sqlite3Strlen30( zName );
- for ( i = OMIT_TEMPDB; i < db.nDb; i++ )
- {
- int j = ( i < 2 ) ? i ^ 1 : i; /* Search TEMP before MAIN */
- Schema pSchema = db.aDb[j].pSchema;
- Debug.Assert( pSchema != null );
- if ( zDb != null && !zDb.Equals( db.aDb[j].zName, StringComparison.InvariantCultureIgnoreCase ) )
- continue;
- p = sqlite3HashFind( pSchema.idxHash, zName, nName, (Index)null );
- if ( p != null )
- break;
- }
- return p;
- }
- /*
- ** Reclaim the memory used by an index
- */
- static void freeIndex( sqlite3 db, ref Index p )
- {
- #if !SQLITE_OMIT_ANALYZE
- sqlite3DeleteIndexSamples( db, p );
- #endif
- sqlite3DbFree( db, ref p.zColAff );
- sqlite3DbFree( db, ref p );
- }
- /*
- ** For the index called zIdxName which is found in the database iDb,
- ** unlike that index from its Table then remove the index from
- ** the index hash table and free all memory structures associated
- ** with the index.
- */
- static void sqlite3UnlinkAndDeleteIndex( sqlite3 db, int iDb, string zIdxName )
- {
- Index pIndex;
- int len;
- Hash pHash = db.aDb[iDb].pSchema.idxHash;
- len = sqlite3Strlen30( zIdxName );
- pIndex = sqlite3HashInsert( ref pHash, zIdxName, len, (Index)null );
- if ( pIndex != null )
- {
- if ( pIndex.pTable.pIndex == pIndex )
- {
- pIndex.pTable.pIndex = pIndex.pNext;
- }
- else
- {
- Index p;
- /* Justification of ALWAYS(); The index must be on the list of
- ** indices. */
- p = pIndex.pTable.pIndex;
- while ( ALWAYS( p != null ) && p.pNext != pIndex )
- {
- p = p.pNext;
- }
- if ( ALWAYS( p != null && p.pNext == pIndex ) )
- {
- p.pNext = pIndex.pNext;
- }
- }
- freeIndex( db, ref pIndex );
- }
- db.flags |= SQLITE_InternChanges;
- }
- /*
- ** Erase all schema information from the in-memory hash tables of
- ** a single database. This routine is called to reclaim memory
- ** before the database closes. It is also called during a rollback
- ** if there were schema changes during the transaction or if a
- ** schema-cookie mismatch occurs.
- **
- ** If iDb==0 then reset the internal schema tables for all database
- ** files. If iDb>=1 then reset the internal schema for only the
- ** single file indicated.
- */
- static void sqlite3ResetInternalSchema( sqlite3 db, int iDb )
- {
- int i, j;
- Debug.Assert( iDb >= 0 && iDb < db.nDb );
- if ( iDb == 0 )
- {
- sqlite3BtreeEnterAll( db );
- }
- for ( i = iDb; i < db.nDb; i++ )
- {
- Db pDb = db.aDb[i];
- if ( pDb.pSchema != null )
- {
- Debug.Assert( i == 1 || ( pDb.pBt != null && sqlite3BtreeHoldsMutex( pDb.pBt ) ) );
- Debug.Assert( i == 1 || ( pDb.pBt != null ) );
- sqlite3SchemaFree( pDb.pSchema );
- }
- if ( iDb > 0 )
- return;
- }
- Debug.Assert( iDb == 0 );
- db.flags &= ~SQLITE_InternChanges;
- sqlite3VtabUnlockList( db );
- sqlite3BtreeLeaveAll( db );
- /* If one or more of the auxiliary database files has been closed,
- ** then remove them from the auxiliary database list. We take the
- ** opportunity to do this here since we have just deleted all of the
- ** schema hash tables and therefore do not have to make any changes
- ** to any of those tables.
- */
- for ( i = j = 2; i < db.nDb; i++ )
- {
- Db pDb = db.aDb[i];
- if ( pDb.pBt == null )
- {
- sqlite3DbFree( db, ref pDb.zName );
- continue;
- }
- if ( j < i )
- {
- db.aDb[j] = db.aDb[i];
- }
- j++;
- }
- if ( db.nDb != j )
- db.aDb[j] = new Db();//memset(db.aDb[j], 0, (db.nDb-j)*sizeof(db.aDb[j]));
- db.nDb = j;
- if ( db.nDb <= 2 && db.aDb != db.aDbStatic )
- {
- Array.Copy( db.aDb, db.aDbStatic, 2 );// memcpy(db.aDbStatic, db.aDb, 2*sizeof(db.aDb[0]));
- sqlite3DbFree( db, ref db.aDb );
- //db.aDb = db.aDbStatic;
- }
- }
- /*
- ** This routine is called when a commit occurs.
- */
- static void sqlite3CommitInternalChanges( sqlite3 db )
- {
- db.flags &= ~SQLITE_InternChanges;
- }
- /*
- ** Delete memory allocated for the column names of a table or view (the
- ** Table.aCol[] array).
- */
- static void sqliteDeleteColumnNames( sqlite3 db, Table pTable )
- {
- int i;
- Column pCol;
- Debug.Assert( pTable != null );
- for ( i = 0; i < pTable.nCol; i++ )
- {
- pCol = pTable.aCol[i];
- if ( pCol != null )
- {
- sqlite3DbFree( db, ref pCol.zName );
- sqlite3ExprDelete( db, ref pCol.pDflt );
- sqlite3DbFree( db, ref pCol.zDflt );
- sqlite3DbFree( db, ref pCol.zType );
- sqlite3DbFree( db, ref pCol.zColl );
- }
- }
- }
- /*
- ** Remove the memory data structures associated with the given
- ** Table. No changes are made to disk by this routine.
- **
- ** This routine just deletes the data structure. It does not unlink
- ** the table data structure from the hash table. But it does destroy
- ** memory structures of the indices and foreign keys associated with
- ** the table.
- */
- static void sqlite3DeleteTable( sqlite3 db, ref Table pTable )
- {
- Index pIndex;
- Index pNext;
- Debug.Assert( null == pTable || pTable.nRef > 0 );
- /* Do not delete the table until the reference count reaches zero. */
- if ( null == pTable )
- return;
- if ( (// ( !db || db->pnBytesFreed == 0 ) &&
- ( --pTable.nRef ) > 0 ) )
- return;
- /* Delete all indices associated with this table. */
- for ( pIndex = pTable.pIndex; pIndex != null; pIndex = pNext )
- {
- pNext = pIndex.pNext;
- Debug.Assert( pIndex.pSchema == pTable.pSchema );
- //if( null==db || db.pnBytesFreed==0 ){
- string zName = pIndex.zName;
- //
- #if !NDEBUG || SQLITE_COVERAGE_TEST
- // TESTONLY ( Index pOld = ) sqlite3HashInsert(
- //ref pIndex.pSchema.idxHash, zName, sqlite3Strlen30(zName), 0
- // );
- Index pOld = sqlite3HashInsert(
- ref pIndex.pSchema.idxHash, zName, sqlite3Strlen30( zName ), (Index)null
- );
- Debug.Assert( pOld == pIndex || pOld == null );
- #else
- // TESTONLY ( Index pOld = ) sqlite3HashInsert(
- //ref pIndex.pSchema.idxHash, zName, sqlite3Strlen30(zName), 0
- // );
- sqlite3HashInsert(
- ref pIndex.pSchema.idxHash, zName, sqlite3Strlen30(zName),(Index)null
- );
- #endif
- //}
- freeIndex( db, ref pIndex );
- }
- /* Delete any foreign keys attached to this table. */
- sqlite3FkDelete( db, pTable );
- /* Delete the Table structure itself.
- */
- sqliteDeleteColumnNames( db, pTable );
- sqlite3DbFree( db, ref pTable.zName );
- sqlite3DbFree( db, ref pTable.zColAff );
- sqlite3SelectDelete( db, ref pTable.pSelect );
- #if !SQLITE_OMIT_CHECK
- sqlite3ExprDelete( db, ref pTable.pCheck );
- #endif
- #if !SQLITE_OMIT_VIRTUALTABLE
- sqlite3VtabClear(db, pTable);
- #endif
- pTable = null;// sqlite3DbFree( db, ref pTable );
- }
- /*
- ** Unlink the given table from the hash tables and the delete the
- ** table structure with all its indices and foreign keys.
- */
- static void sqlite3UnlinkAndDeleteTable( sqlite3 db, int iDb, string zTabName )
- {
- Table p;
- Db pDb;
- Debug.Assert( db != null );
- Debug.Assert( iDb >= 0 && iDb < db.nDb );
- Debug.Assert( zTabName != null );
- testcase( zTabName.Length == 0 ); /* Zero-length table names are allowed */
- pDb = db.aDb[iDb];
- p = sqlite3HashInsert( ref pDb.pSchema.tblHash, zTabName,
- sqlite3Strlen30( zTabName ), (Table)null );
- sqlite3DeleteTable( db, ref p );
- db.flags |= SQLITE_InternChanges;
- }
- /*
- ** Given a token, return a string that consists of the text of that
- ** token. Space to hold the returned string
- ** is obtained from sqliteMalloc() and must be freed by the calling
- ** function.
- **
- ** Any quotation marks (ex: "name", 'name', [name], or `name`) that
- ** surround the body of the token are removed.
- **
- ** Tokens are often just pointers into the original SQL text and so
- ** are not \000 terminated and are not persistent. The returned string
- ** is \000 terminated and is persistent.
- */
- static string sqlite3NameFromToken( sqlite3 db, Token pName )
- {
- string zName;
- if ( pName != null && pName.z != null )
- {
- zName = pName.z.Substring( 0, pName.n );//sqlite3DbStrNDup(db, (char*)pName.z, pName.n);
- sqlite3Dequote( ref zName );
- }
- else
- {
- return null;
- }
- return zName;
- }
- /*
- ** Open the sqlite_master table stored in database number iDb for
- ** writing. The table is opened using cursor 0.
- */
- static void sqlite3OpenMasterTable( Parse p, int iDb )
- {
- Vdbe v = sqlite3GetVdbe( p );
- sqlite3TableLock( p, iDb, MASTER_ROOT, 1, SCHEMA_TABLE( iDb ) );
- sqlite3VdbeAddOp3( v, OP_OpenWrite, 0, MASTER_ROOT, iDb );
- sqlite3VdbeChangeP4( v, -1, (int)5, P4_INT32 ); /* 5 column table */
- if ( p.nTab == 0 )
- {
- p.nTab = 1;
- }
- }
- /*
- ** Parameter zName points to a nul-terminated buffer containing the name
- ** of a database ("main", "temp" or the name of an attached db). This
- ** function returns the index of the named database in db->aDb[], or
- ** -1 if the named db cannot be found.
- */
- static int sqlite3FindDbName( sqlite3 db, string zName )
- {
- int i = -1; /* Database number */
- if ( zName != null )
- {
- Db pDb;
- int n = sqlite3Strlen30( zName );
- for ( i = ( db.nDb - 1 ); i >= 0; i-- )
- {
- pDb = db.aDb[i];
- if ( ( OMIT_TEMPDB == 0 || i != 1 ) && n == sqlite3Strlen30( pDb.zName ) &&
- pDb.zName.Equals( zName, StringComparison.InvariantCultureIgnoreCase ) )
- {
- break;
- }
- }
- }
- return i;
- }
- /*
- ** The token *pName contains the name of a database (either "main" or
- ** "temp" or the name of an attached db). This routine returns the
- ** index of the named database in db->aDb[], or -1 if the named db
- ** does not exist.
- */
- static int sqlite3FindDb( sqlite3 db, Token pName )
- {
- int i; /* Database number */
- string zName; /* Name we are searching for */
- zName = sqlite3NameFromToken( db, pName );
- i = sqlite3FindDbName( db, zName );
- sqlite3DbFree( db, ref zName );
- return i;
- }
- /* The table or view or trigger name is passed to this routine via tokens
- ** pName1 and pName2. If the table name was fully qualified, for example:
- **
- ** CREATE TABLE xxx.yyy (...);
- **
- ** Then pName1 is set to "xxx" and pName2 "yyy". On the other hand if
- ** the table name is not fully qualified, i.e.:
- **
- ** CREATE TABLE yyy(...);
- **
- ** Then pName1 is set to "yyy" and pName2 is "".
- **
- ** This routine sets the ppUnqual pointer to point at the token (pName1 or
- ** pName2) that stores the unqualified table name. The index of the
- ** database "xxx" is returned.
- */
- static int sqlite3TwoPartName(
- Parse pParse, /* Parsing and code generating context */
- Token pName1, /* The "xxx" in the name "xxx.yyy" or "xxx" */
- Token pName2, /* The "yyy" in the name "xxx.yyy" */
- ref Token pUnqual /* Write the unqualified object name here */
- )
- {
- int iDb; /* Database holding the object */
- sqlite3 db = pParse.db;
- if ( ALWAYS( pName2 != null ) && pName2.n > 0 )
- {
- if ( db.init.busy != 0 )
- {
- sqlite3ErrorMsg( pParse, "corrupt database" );
- pParse.nErr++;
- return -1;
- }
- pUnqual = pName2;
- iDb = sqlite3FindDb( db, pName1 );
- if ( iDb < 0 )
- {
- sqlite3ErrorMsg( pParse, "unknown database %T", pName1 );
- pParse.nErr++;
- return -1;
- }
- }
- else
- {
- Debug.Assert( db.init.iDb == 0 || db.init.busy != 0 );
- iDb = db.init.iDb;
- pUnqual = pName1;
- }
- return iDb;
- }
- /*
- ** This routine is used to check if the UTF-8 string zName is a legal
- ** unqualified name for a new schema object (table, index, view or
- ** trigger). All names are legal except those that begin with the string
- ** "sqlite_" (in upper, lower or mixed case). This portion of the namespace
- ** is reserved for internal use.
- */
- static int sqlite3CheckObjectName( Parse pParse, string zName )
- {
- if ( 0 == pParse.db.init.busy && pParse.nested == 0
- && ( pParse.db.flags & SQLITE_WriteSchema ) == 0
- && zName.StartsWith( "sqlite_", System.StringComparison.InvariantCultureIgnoreCase ) )
- {
- sqlite3ErrorMsg( pParse, "object name reserved for internal use: %s", zName );
- return SQLITE_ERROR;
- }
- return SQLITE_OK;
- }
- /*
- ** Begin constructing a new table representation in memory. This is
- ** the first of several action routines that get called in response
- ** to a CREATE TABLE statement. In particular, this routine is called
- ** after seeing tokens "CREATE" and "TABLE" and the table name. The isTemp
- ** flag is true if the table should be stored in the auxiliary database
- ** file instead of in the main database file. This is normally the case
- ** when the "TEMP" or "TEMPORARY" keyword occurs in between
- ** CREATE and TABLE.
- **
- ** The new table record is initialized and put in pParse.pNewTable.
- ** As more of the CREATE TABLE statement is parsed, additional action
- ** routines will be called to add more information to this record.
- ** At the end of the CREATE TABLE statement, the sqlite3EndTable() routine
- ** is called to complete the construction of the new table record.
- */
- static void sqlite3StartTable(
- Parse pParse, /* Parser context */
- Token pName1, /* First part of the name of the table or view */
- Token pName2, /* Second part of the name of the table or view */
- int isTemp, /* True if this is a TEMP table */
- int isView, /* True if this is a VIEW */
- int isVirtual, /* True if this is a VIRTUAL table */
- int noErr /* Do nothing if table already exists */
- )
- {
- Table pTable;
- string zName = null; /* The name of the new table */
- sqlite3 db = pParse.db;
- Vdbe v;
- int iDb; /* Database number to create the table in */
- Token pName = new Token(); /* Unqualified name of the table to create */
- /* The table or view name to create is passed to this routine via tokens
- ** pName1 and pName2. If the table name was fully qualified, for example:
- **
- ** CREATE TABLE xxx.yyy (...);
- **
- ** Then pName1 is set to "xxx" and pName2 "yyy". On the other hand if
- ** the table name is not fully qualified, i.e.:
- **
- ** CREATE TABLE yyy(...);
- **
- ** Then pName1 is set to "yyy" and pName2 is "".
- **
- ** The call below sets the pName pointer to point at the token (pName1 or
- ** pName2) that stores the unqualified table name. The variable iDb is
- ** set to the index of the database that the table or view is to be
- ** created in.
- */
- iDb = sqlite3TwoPartName( pParse, pName1, pName2, ref pName );
- if ( iDb < 0 )
- return;
- if ( 0 == OMIT_TEMPDB && isTemp != 0 && pName2.n > 0 && iDb != 1 )
- {
- /* If creating a temp table, the name may not be qualified. Unless
- ** the database name is "temp" anyway. */
- sqlite3ErrorMsg( pParse, "temporary table name must be unqualified" );
- return;
- }
- if ( OMIT_TEMPDB == 0 && isTemp != 0 )
- iDb = 1;
- pParse.sNameToken = pName;
- zName = sqlite3NameFromToken( db, pName );
- if ( zName == null )
- return;
- if ( SQLITE_OK != sqlite3CheckObjectName( pParse, zName ) )
- {
- goto begin_table_error;
- }
- if ( db.init.iDb == 1 )
- isTemp = 1;
- #if !SQLITE_OMIT_AUTHORIZATION
- Debug.Assert( (isTemp & 1)==isTemp );
- {
- int code;
- char *zDb = db.aDb[iDb].zName;
- if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0, zDb) ){
- goto begin_table_error;
- }
- if( isView ){
- if( OMIT_TEMPDB ==0&& isTemp ){
- code = SQLITE_CREATE_TEMP_VIEW;
- }else{
- code = SQLITE_CREATE_VIEW;
- }
- }else{
- if( OMIT_TEMPDB ==0&& isTemp ){
- code = SQLITE_CREATE_TEMP_TABLE;
- }else{
- code = SQLITE_CREATE_TABLE;
- }
- }
- if( !isVirtual && sqlite3AuthCheck(pParse, code, zName, 0, zDb) ){
- goto begin_table_error;
- }
- }
- #endif
- /* Make sure the new table name does not collide with an existing
- ** index or table name in the same database. Issue an error message if
- ** it does. The exception is if the statement being parsed was passed
- ** to an sqlite3_declare_vtab() call. In that case only the column names
- ** and types will be used, so there is no need to test for namespace
- ** collisions.
- */
- if ( !IN_DECLARE_VTAB )
- {
- String zDb = db.aDb[iDb].zName;
- if ( SQLITE_OK != sqlite3ReadSchema( pParse ) )
- {
- goto begin_table_error;
- }
- pTable = sqlite3FindTable( db, zName, zDb );
- if ( pTable != null )
- {
- if ( noErr == 0 )
- {
- sqlite3ErrorMsg( pParse, "table %T already exists", pName );
- }
- goto begin_table_error;
- }
- if ( sqlite3FindIndex( db, zName, zDb ) != null )
- {
- sqlite3ErrorMsg( pParse, "there is already an index named %s", zName );
- goto begin_table_error;
- }
- }
- pTable = new Table();// sqlite3DbMallocZero(db, Table).Length;
- if ( pTable == null )
- {
- // db.mallocFailed = 1;
- pParse.rc = SQLITE_NOMEM;
- pParse.nErr++;
- goto begin_table_error;
- }
- pTable.zName = zName;
- pTable.iPKey = -1;
- pTable.pSchema = db.aDb[iDb].pSchema;
- pTable.nRef = 1;
- pTable.nRowEst = 1000000;
- Debug.Assert( pParse.pNewTable == null );
- pParse.pNewTable = pTable;
- /* If this is the magic sqlite_sequence table used by autoincrement,
- ** then record a pointer to this table in the main database structure
- ** so that INSERT can find the table easily.
- */
- #if !SQLITE_OMIT_AUTOINCREMENT
- if ( pParse.nested == 0 && zName == "sqlite_sequence" )
- {
- pTable.pSchema.pSeqTab = pTable;
- }
- #endif
- /* Begin generating the code that will insert the table record into
- ** the SQLITE_MASTER table. Note in particular that we must go ahead
- ** and allocate the record number for the table entry now. Before any
- ** PRIMARY KEY or UNIQUE keywords are parsed. Those keywords will cause
- ** indices to be created and the table record must come before the
- ** indices. Hence, the record number for the table must be allocated
- ** now.
- */
- if ( 0 == db.init.busy && ( v = sqlite3GetVdbe( pParse ) ) != null )
- {
- int j1;
- int fileFormat;
- int reg1, reg2, reg3;
- sqlite3BeginWriteOperation( pParse, 0, iDb );
- if ( isVirtual != 0 )
- {
- sqlite3VdbeAddOp0( v, OP_VBegin );
- }
- /* If the file format and encoding in the database have not been set,
- ** set them now.
- */
- reg1 = pParse.regRowid = ++pParse.nMem;
- reg2 = pParse.regRoot = ++pParse.nMem;
- reg3 = ++pParse.nMem;
- sqlite3VdbeAddOp3( v, OP_ReadCookie, iDb, reg3, BTREE_FILE_FORMAT );
- sqlite3VdbeUsesBtree( v, iDb );
- j1 = sqlite3VdbeAddOp1( v, OP_If, reg3 );
- fileFormat = ( db.flags & SQLITE_LegacyFileFmt ) != 0 ?
- 1 : SQLITE_MAX_FILE_FORMAT;
- sqlite3VdbeAddOp2( v, OP_Integer, fileFormat, reg3 );
- sqlite3VdbeAddOp3( v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, reg3 );
- sqlite3VdbeAddOp2( v, OP_Integer, ENC( db ), reg3 );
- sqlite3VdbeAddOp3( v, OP_SetCookie, iDb, BTREE_TEXT_ENCODING, reg3 );
- sqlite3VdbeJumpHere( v, j1 );
- /* This just creates a place-holder record in the sqlite_master table.
- ** The record created does not contain anything yet. It will be replaced
- ** by the real entry in code generated at sqlite3EndTable().
- **
- ** The rowid for the new entry is left in register pParse->regRowid.
- ** The root page number of the new table is left in reg pParse->regRoot.
- ** The rowid and root page number values are needed by the code that
- ** sqlite3EndTable will generate.
- */
- if ( isView != 0 || isVirtual != 0 )
- {
- sqlite3VdbeAddOp2( v, OP_Integer, 0, reg2 );
- }
- else
- {
- sqlite3VdbeAddOp2( v, OP_CreateTable, iDb, reg2 );
- }
- sqlite3OpenMasterTable( pParse, iDb );
- sqlite3VdbeAddOp2( v, OP_NewRowid, 0, reg1 );
- sqlite3VdbeAddOp2( v, OP_Null, 0, reg3 );
- sqlite3VdbeAddOp3( v, OP_Insert, 0, reg3, reg1 );
- sqlite3VdbeChangeP5( v, OPFLAG_APPEND );
- sqlite3VdbeAddOp0( v, OP_Close );
- }
- /* Normal (non-error) return. */
- return;
- /* If an error occurs, we jump here */
- begin_table_error:
- sqlite3DbFree( db, ref zName );
- return;
- }
- /*
- ** This macro is used to compare two strings in a case-insensitive manner.
- ** It is slightly faster than calling sqlite3StrICmp() directly, but
- ** produces larger code.
- **
- ** WARNING: This macro is not compatible with the strcmp() family. It
- ** returns true if the two strings are equal, otherwise false.
- */
- //#define STRICMP(x, y) (\
- //sqlite3UpperToLower[*(unsigned char *)(x)]== \
- //sqlite3UpperToLower[*(unsigned char *)(y)] \
- //&& sqlite3StrICmp((x)+1,(y)+1)==0 )
- /*
- ** Add a new column to the table currently being constructed.
- **
- ** The parser calls this routine once for each column declaration
- ** in a CREATE TABLE statement. sqlite3StartTable() gets called
- ** first to get things going. Then this routine is called for each
- ** column.
- */
- static void sqlite3AddColumn( Parse pParse, Token pName )
- {
- Table p;
- int i;
- string z;
- Column pCol;
- sqlite3 db = pParse.db;
- if ( ( p = pParse.pNewTable ) == null )
- return;
- #if SQLITE_MAX_COLUMN || !SQLITE_MAX_COLUMN
- if ( p.nCol + 1 > db.aLimit[SQLITE_LIMIT_COLUMN] )
- {
- sqlite3ErrorMsg( pParse, "too many columns on %s", p.zName );
- return;
- }
- #endif
- z = sqlite3NameFromToken( db, pName );
- if ( z == null )
- return;
- for ( i = 0; i < p.nCol; i++ )
- {
- if ( z.Equals( p.aCol[i].zName, StringComparison.InvariantCultureIgnoreCase ) )
- {//STRICMP(z, p.aCol[i].zName) ){
- sqlite3ErrorMsg( pParse, "duplicate column name: %s", z );
- sqlite3DbFree( db, ref z );
- return;
- }
- }
- if ( ( p.nCol & 0x7 ) == 0 )
- {
- //aNew = sqlite3DbRealloc(db,p.aCol,(p.nCol+8)*sizeof(p.aCol[0]));
- //if( aNew==0 ){
- // sqlite3DbFree(db,ref z);
- // return;
- //}
- Array.Resize( ref p.aCol, p.nCol + 8 );
- }
- p.aCol[p.nCol] = new Column();
- pCol = p.aCol[p.nCol];
- //memset(pCol, 0, sizeof(p.aCol[0]));
- pCol.zName = z;
- /* If there is no type specified, columns have the default affinity
- ** 'NONE'. If there is a type specified, then sqlite3AddColumnType() will
- ** be called next to set pCol.affinity correctly.
- */
- pCol.affinity = SQLITE_AFF_NONE;
- p.nCol++;
- }
- /*
- ** This routine is called by the parser while in the middle of
- ** parsing a CREATE TABLE statement. A "NOT NULL" constraint has
- ** been seen on a column. This routine sets the notNull flag on
- ** the column currently under construction.
- */
- static void sqlite3AddNotNull( Parse pParse, int onError )
- {
- Table p;
- p = pParse.pNewTable;
- if ( p == null || NEVER( p.nCol < 1 ) )
- return;
- p.aCol[p.nCol - 1].notNull = (u8)onError;
- }
- /*
- ** Scan the column type name zType (length nType) and return the
- ** associated affinity type.
- **
- ** This routine does a case-independent search of zType for the
- ** substrings in the following table. If one of the substrings is
- ** found, the corresponding affinity is returned. If zType contains
- ** more than one of the substrings, entries toward the top of
- ** the table take priority. For example, if zType is 'BLOBINT',
- ** SQLITE_AFF_INTEGER is returned.
- **
- ** Substring | Affinity
- ** --------------------------------
- ** 'INT' | SQLITE_AFF_INTEGER
- ** 'CHAR' | SQLITE_AFF_TEXT
- ** 'CLOB' | SQLITE_AFF_TEXT
- ** 'TEXT' | SQLITE_AFF_TEXT
- ** 'BLOB' | SQLITE_AFF_NONE
- ** 'REAL' | SQLITE_AFF_REAL
- ** 'FLOA' | SQLITE_AFF_REAL
- ** 'DOUB' | SQLITE_AFF_REAL
- **
- ** If none of the substrings in the above table are found,
- ** SQLITE_AFF_NUMERIC is returned.
- */
- static char sqlite3AffinityType( string zIn )
- {
- //u32 h = 0;
- //char aff = SQLITE_AFF_NUMERIC;
- zIn = zIn.ToLower();
- if ( zIn.Contains( "char" ) || zIn.Contains( "clob" ) || zIn.Contains( "text" ) )
- return SQLITE_AFF_TEXT;
- if ( zIn.Contains( "blob" ) )
- return SQLITE_AFF_NONE;
- if ( zIn.Contains( "doub" ) || zIn.Contains( "floa" ) || zIn.Contains( "real" ) )
- return SQLITE_AFF_REAL;
- if ( zIn.Contains( "int" ) )
- return SQLITE_AFF_INTEGER;
- return SQLITE_AFF_NUMERIC;
- // string zEnd = pType.z.Substring(pType.n);
- // while( zIn!=zEnd ){
- // h = (h<<8) + sqlite3UpperToLower[*zIn];
- // zIn++;
- // if( h==(('c'<<24)+('h'<<16)+('a'<<8)+'r') ){ /* CHAR */
- // aff = SQLITE_AFF_TEXT;
- // }else if( h==(('c'<<24)+('l'<<16)+('o'<<8)+'b') ){ /* CLOB */
- // aff = SQLITE_AFF_TEXT;
- // }else if( h==(('t'<<24)+('e'<<16)+('x'<<8)+'t') ){ /* TEXT */
- // aff = SQLITE_AFF_TEXT;
- // }else if( h==(('b'<<24)+('l'<<16)+('o'<<8)+'b') /* BLOB */
- // && (aff==SQLITE_AFF_NUMERIC || aff==SQLITE_AFF_REAL) ){
- // aff = SQLITE_AFF_NONE;
- //#if !SQLITE_OMIT_FLOATING_POINT
- // }else if( h==(('r'<<24)+('e'<<16)+('a'<<8)+'l') /* REAL */
- // && aff==SQLITE_AFF_NUMERIC ){
- // aff = SQLITE_AFF_REAL;
- // }else if( h==(('f'<<24)+('l'<<16)+('o'<<8)+'a') /* FLOA */
- // && aff==SQLITE_AFF_NUMERIC ){
- // aff = SQLITE_AFF_REAL;
- // }else if( h==(('d'<<24)+('o'<<16)+('u'<<8)+'b') /* DOUB */
- // && aff==SQLITE_AFF_NUMERIC ){
- // aff = SQLITE_AFF_REAL;
- //#endif
- // }else if( (h&0x00FFFFFF)==(('i'<<16)+('n'<<8)+'t') ){ /* INT */
- // aff = SQLITE_AFF_INTEGER;
- // break;
- // }
- // }
- // return aff;
- }
- /*
- ** This routine is called by the parser while in the middle of
- ** parsing a CREATE TABLE statement. The pFirst token is the first
- ** token in the sequence of tokens that describe the type of the
- ** column currently under construction. pLast is the last token
- ** in the sequence. Use this information to construct a string
- ** that contains the typename of the column and store that string
- ** in zType.
- */
- static void sqlite3AddColumnType( Parse pParse, Token pType )
- {
- Table p;
- Column pCol;
- p = pParse.pNewTable;
- if ( p == null || NEVER( p.nCol < 1 ) )
- return;
- pCol = p.aCol[p.nCol - 1];
- Debug.Assert( pCol.zType == null );
- pCol.zType = sqlite3NameFromToken( pParse.db, pType );
- pCol.affinity = sqlite3AffinityType( pCol.zType );
- }
- /*
- ** The expression is the default value for the most recently added column
- ** of the table currently under construction.
- **
- ** Default value expressions must be constant. Raise an exception if this
- ** is not the case.
- **
- ** This routine is called by the parser while in the middle of
- ** parsing a CREATE TABLE statement.
- */
- static void sqlite3AddDefaultValue( Parse pParse, ExprSpan pSpan )
- {
- Table p;
- Column pCol;
- sqlite3 db = pParse.db;
- p = pParse.pNewTable;
- if ( p != null )
- {
- pCol = ( p.aCol[p.nCol - 1] );
- if ( sqlite3ExprIsConstantOrFunction( pSpan.pExpr ) == 0 )
- {
- sqlite3ErrorMsg( pParse, "default value of column [%s] is not constant",
- pCol.zName );
- }
- else
- {
- /* A copy of pExpr is used instead of the original, as pExpr contains
- ** tokens that point to volatile memory. The 'span' of the expression
- ** is required by pragma table_info.
- */
- sqlite3ExprDelete( db, ref pCol.pDflt );
- pCol.pDflt = sqlite3ExprDup( db, pSpan.pExpr, EXPRDUP_REDUCE );
- sqlite3DbFree( db, ref pCol.zDflt );
- pCol.zDflt = pSpan.zStart.Substring( 0, pSpan.zStart.Length - pSpan.zEnd.Length );
- //sqlite3DbStrNDup( db, pSpan.zStart,
- // (int)( pSpan.zEnd.Length - pSpan.zStart.Length ) );
- }
- }
- sqlite3ExprDelete( db, ref pSpan.pExpr );
- }
- /*
- ** Designate the PRIMARY KEY for the table. pList is a list of names
- ** of columns that form the primary key. If pList is NULL, then the
- ** most recently added column of the table is the primary key.
- **
- ** A table can have at most one primary key. If the table already has
- ** a primary key (and this is the second primary key) then create an
- ** error.
- **
- ** If the PRIMARY KEY is on a single column whose datatype is INTEGER,
- ** then we will try to use that column as the rowid. Set the Table.iPKey
- ** field of the table under construction to be the index of the
- ** INTEGER PRIMARY KEY column. Table.iPKey is set to -1 if there is
- ** no INTEGER PRIMARY KEY.
- **
- ** If the key is not an INTEGER PRIMARY KEY, then create a unique
- ** index for the key. No index is created for INTEGER PRIMARY KEYs.
- */
- // OVERLOADS, so I don't need to rewrite parse.c
- static void sqlite3AddPrimaryKey( Parse pParse, int null_2, int onError, int autoInc, int sortOrder )
- {
- sqlite3AddPrimaryKey( pParse, null, onError, autoInc, sortOrder );
- }
- static void sqlite3AddPrimaryKey(
- Parse pParse, /* Parsing context */
- ExprList pList, /* List of field names to be indexed */
- int onError, /* What to do with a uniqueness conflict */
- int autoInc, /* True if the AUTOINCREMENT keyword is present */
- int sortOrder /* SQLITE_SO_ASC or SQLITE_SO_DESC */
- )
- {
- Table pTab = pParse.pNewTable;
- string zType = null;
- int iCol = -1, i;
- if ( pTab == null || IN_DECLARE_VTAB )
- goto primary_key_exit;
- if ( ( pTab.tabFlags & TF_HasPrimaryKey ) != 0 )
- {
- sqlite3ErrorMsg( pParse,
- "table \"%s\" has more than one primary key", pTab.zName );
- goto primary_key_exit;
- }
- pTab.tabFlags |= TF_HasPrimaryKey;
- if ( pList == null )
- {
- iCol = pTab.nCol - 1;
- pTab.aCol[iCol].isPrimKey = 1;
- }
- else
- {
- for ( i = 0; i < pList.nExpr; i++ )
- {
- for ( iCol = 0; iCol < pTab.nCol; iCol++ )
- {
- if ( pList.a[i].zName.Equals( pTab.aCol[iCol].zName, StringComparison.InvariantCultureIgnoreCase ) )
- {
- break;
- }
- }
- if ( iCol < pTab.nCol )
- {
- pTab.aCol[iCol].isPrimKey = 1;
- }
- }
- if ( pList.nExpr > 1 )
- iCol = -1;
- }
- if ( iCol >= 0 && iCol < pTab.nCol )
- {
- zType = pTab.aCol[iCol].zType;
- }
- if ( zType != null && zType.Equals( "INTEGER", StringComparison.InvariantCultureIgnoreCase )
- && sortOrder == SQLITE_SO_ASC )
- {
- pTab.iPKey = iCol;
- pTab.keyConf = (byte)onError;
- Debug.Assert( autoInc == 0 || autoInc == 1 );
- pTab.tabFlags |= (u8)( autoInc * TF_Autoincrement );
- }
- else if ( autoInc != 0 )
- {
- #if !SQLITE_OMIT_AUTOINCREMENT
- sqlite3ErrorMsg( pParse, "AUTOINCREMENT is only allowed on an " +
- "INTEGER PRIMARY KEY" );
- #endif
- }
- else
- {
- Index p;
- p = sqlite3CreateIndex( pParse, 0, 0, 0, pList, onError, 0, 0, sortOrder, 0 );
- if ( p != null )
- {
- p.autoIndex = 2;
- }
- pList = null;
- }
- primary_key_exit:
- sqlite3ExprListDelete( pParse.db, ref pList );
- return;
- }
- /*
- ** Add a new CHECK constraint to the table currently under construction.
- */
- static void sqlite3AddCheckConstraint(
- Parse pParse, /* Parsing context */
- Expr pCheckExpr /* The check expression */
- )
- {
- sqlite3 db = pParse.db;
- #if !SQLITE_OMIT_CHECK
- Table pTab = pParse.pNewTable;
- if ( pTab != null && !IN_DECLARE_VTAB )
- {
- pTab.pCheck = sqlite3ExprAnd( db, pTab.pCheck, pCheckExpr );
- }
- else
- #endif
- {
- sqlite3ExprDelete( db, ref pCheckExpr );
- }
- }
- /*
- ** Set the collation function of the most recently parsed table column
- ** to the CollSeq given.
- */
- static void sqlite3AddCollateType( Parse pParse, Token pToken )
- {
- Table p;
- int i;
- string zColl; /* Dequoted name of collation sequence */
- sqlite3 db;
- if ( ( p = pParse.pNewTable ) == null )
- return;
- i = p.nCol - 1;
- db = pParse.db;
- zColl = sqlite3NameFromToken( db, pToken );
- if ( zColl == null )
- return;
- if ( sqlite3LocateCollSeq( pParse, zColl ) != null )
- {
- Index pIdx;
- p.aCol[i].zColl = zColl;
- /* If the column is declared as "<name> PRIMARY KEY COLLATE <type>",
- ** then an index may have been created on this column before the
- ** collation type was added. Correct this if it is the case.
- */
- for ( pIdx = p.pIndex; pIdx != null; pIdx = pIdx.pNext )
- {
- Debug.Assert( pIdx.nColumn == 1 );
- if ( pIdx.aiColumn[0] == i )
- {
- pIdx.azColl[0] = p.aCol[i].zColl;
- }
- }
- }
- else
- {
- sqlite3DbFree( db, ref zColl );
- }
- }
- /*
- ** This function returns the collation sequence for database native text
- ** encoding identified by the string zName, length nName.
- **
- ** If the requested collation sequence is not available, or not available
- ** in the database native encoding, the collation factory is invoked to
- ** request it. If the collation factory does not supply such a sequence,
- ** and the sequence is available in another text encoding, then that is
- ** returned instead.
- **
- ** If no versions of the requested collations sequence are available, or
- ** another error occurs, NULL is returned and an error message written into
- ** pParse.
- **
- ** This routine is a wrapper around sqlite3FindCollSeq(). This routine
- ** invokes the collation factory if the named collation cannot be found
- ** and generates an error message.
- **
- ** See also: sqlite3FindCollSeq(), sqlite3GetCollSeq()
- */
- static CollSeq sqlite3LocateCollSeq( Parse pParse, string zName )
- {
- sqlite3 db = pParse.db;
- u8 enc = db.aDb[0].pSchema.enc;// ENC(db);
- u8 initbusy = db.init.busy;
- CollSeq pColl;
- pColl = sqlite3FindCollSeq( db, enc, zName, initbusy );
- if ( 0 == initbusy && ( pColl == null || pColl.xCmp == null ) )
- {
- pColl = sqlite3GetCollSeq( db, enc, pColl, zName );
- if ( pColl == null )
- {
- sqlite3ErrorMsg( pParse, "no such collation sequence: %s", zName );
- }
- }
- return pColl;
- }
- /*
- ** Generate code that will increment the schema cookie.
- **
- ** The schema cookie is used to determine when the schema for the
- ** database changes. After each schema change, the cookie value
- ** changes. When a process first reads the schema it records the
- ** cookie. Thereafter, whenever it goes to access the database,
- ** it checks the cookie to make sure the schema has not changed
- ** since it was last read.
- **
- ** This plan is not completely bullet-proof. It is possible for
- ** the schema to change multiple times and for the cookie to be
- ** set back to prior value. But schema changes are infrequent
- ** and the probability of hitting the same cookie value is only
- ** 1 chance in 2^32. So we're safe enough.
- */
- static void sqlite3ChangeCookie( Parse pParse, int iDb )
- {
- int r1 = sqlite3GetTempReg( pParse );
- sqlite3 db = pParse.db;
- Vdbe v = pParse.pVdbe;
- sqlite3VdbeAddOp2( v, OP_Integer, db.aDb[iDb].pSchema.schema_cookie + 1, r1 );
- sqlite3VdbeAddOp3( v, OP_SetCookie, iDb, BTREE_SCHEMA_VERSION, r1 );
- sqlite3ReleaseTempReg( pParse, r1 );
- }
- /*
- ** Measure the number of characters needed to output the given
- ** identifier. The number returned includes any quotes used
- ** but does not include the null terminator.
- **
- ** The estimate is conservative. It might be larger that what is
- ** really needed.
- */
- static int identLength( string z )
- {
- int n;
- for ( n = 0; n < z.Length; n++ )
- {
- if ( z[n] == (byte)'"' )
- {
- n++;
- }
- }
- return n + 2;
- }
- /*
- ** The first parameter is a pointer to an output buffer. The second
- ** parameter is a pointer to an integer that contains the offset at
- ** which to write into the output buffer. This function copies the
- ** nul-terminated string pointed to by the third parameter, zSignedIdent,
- ** to the specified offset in the buffer and updates *pIdx to refer
- ** to the first byte after the last byte written before returning.
- **
- ** If the string zSignedIdent consists entirely of alpha-numeric
- ** characters, does not begin with a digit and is not an SQL keyword,
- ** then it is copied to the output buffer exactly as it is. Otherwise,
- ** it is quoted using double-quotes.
- */
- static void identPut( StringBuilder z, ref int pIdx, string zSignedIdent )
- {
- string zIdent = zSignedIdent;
- int i;
- int j;
- bool needQuote;
- i = pIdx;
- for ( j = 0; j < zIdent.Length; j++ )
- {
- if ( !sqlite3Isalnum( zIdent[j] ) && zIdent[j] != '_' )
- break;
- }
- needQuote = sqlite3Isdigit( zIdent[0] ) || sqlite3KeywordCode( zIdent, j ) != TK_ID;
- if ( !needQuote )
- {
- needQuote = ( j < zIdent.Length && zIdent[j] != 0 );
- }
- if ( needQuote )
- {
- if ( i == z.Length )
- z.Append( '\0' );
- z[i++] = '"';
- }
- for ( j = 0; j < zIdent.Length; j++ )
- {
- if ( i == z.Length )
- z.Append( '\0' );
- z[i++] = zIdent[j];
- if ( zIdent[j] == '"' )
- {
- if ( i == z.Length )
- z.Append( '\0' );
- z[i++] = '"';
- }
- }
- if ( needQuote )
- {
- if ( i == z.Length )
- z.Append( '\0' );
- z[i++] = '"';
- }
- //z[i] = 0;
- pIdx = i;
- }
- /*
- ** Generate a CREATE TABLE statement appropriate for the given
- ** table. Memory to hold the text of the statement is obtained
- ** from sqliteMalloc() and must be freed by the calling function.
- */
- static string createTableStmt( sqlite3 db, Table p )
- {
- int i, k, n;
- StringBuilder zStmt;
- string zSep;
- string zSep2;
- string zEnd;
- Column pCol;
- n = 0;
- for ( i = 0; i < p.nCol; i++ )
- {//, pCol++){
- pCol = p.aCol[i];
- n += identLength( pCol.zName ) + 5;
- }
- n += identLength( p.zName );
- if ( n < 50 )
- {
- zSep = "";
- zSep2 = ",";
- zEnd = ")";
- }
- else
- {
- zSep = "\n ";
- zSep2 = ",\n ";
- zEnd = "\n)";
- }
- n += 35 + 6 * p.nCol;
- zStmt = new StringBuilder( n );
- //zStmt = sqlite3DbMallocRaw(0, n);
- //if( zStmt==0 ){
- // db.mallocFailed = 1;
- // return 0;
- //}
- //sqlite3_snprintf(n, zStmt,"CREATE TABLE ");
- zStmt.Append( "CREATE TABLE " );
- k = sqlite3Strlen30( zStmt );
- identPut( zStmt, ref k, p.zName );
- zStmt.Append( '(' );//zStmt[k++] = '(';
- for ( i = 0; i < p.nCol; i++ )
- {//, pCol++){
- pCol = p.aCol[i];
- string[] azType = new string[] {
- /* SQLITE_AFF_TEXT */ " TEXT",
- /* SQLITE_AFF_NONE */ "",
- /* SQLITE_AFF_NUMERIC */ " NUM",
- /* SQLITE_AFF_INTEGER */ " INT",
- /* SQLITE_AFF_REAL */ " REAL"
- };
- int len;
- string zType;
- zStmt.Append( zSep );// sqlite3_snprintf(n-k, zStmt[k], zSep);
- k = sqlite3Strlen30( zStmt );// k += strlen(zStmt[k]);
- zSep = zSep2;
- identPut( zStmt, ref k, pCol.zName );
- Debug.Assert( pCol.affinity - SQLITE_AFF_TEXT >= 0 );
- Debug.Assert( pCol.affinity - SQLITE_AFF_TEXT < azType.Length );//sizeof(azType)/sizeof(azType[0]) );
- testcase( pCol.affinity == SQLITE_AFF_TEXT );
- testcase( pCol.affinity == SQLITE_AFF_NONE );
- testcase( pCol.affinity == SQLITE_AFF_NUMERIC );
- testcase( pCol.affinity == SQLITE_AFF_INTEGER );
- testcase( pCol.affinity == SQLITE_AFF_REAL );
- zType = azType[pCol.affinity - SQLITE_AFF_TEXT];
- len = sqlite3Strlen30( zType );
- Debug.Assert( pCol.affinity == SQLITE_AFF_NONE
- || pCol.affinity == sqlite3AffinityType( zType ) );
- zStmt.Append( zType );// memcpy( &zStmt[k], zType, len );
- k += len;
- Debug.Assert( k <= n );
- }
- zStmt.Append( zEnd );//sqlite3_snprintf(n-k, zStmt[k], "%s", zEnd);
- return zStmt.ToString();
- }
- /*
- ** This routine is called to report the final ")" that terminates
- ** a CREATE TABLE statement.
- **
- ** The table structure that other action routines have been building
- ** is added to the internal hash tables, assuming no errors have
- ** occurred.
- **
- ** An entry for the table is made in the master table on disk, unless
- ** this is a temporary table or db.init.busy==1. When db.init.busy==1
- ** it means we are reading the sqlite_master table because we just
- ** connected to the database or because the sqlite_master table has
- ** recently changed, so the entry for this table already exists in
- ** the sqlite_master table. We do not want to create it again.
- **
- ** If the pSelect argument is not NULL, it means that this routine
- ** was called to create a table generated from a
- ** "CREATE TABLE ... AS SELECT ..." statement. The column names of
- ** the new table will match the result set of the SELECT.
- */
- // OVERLOADS, so I don't need to rewrite parse.c
- static void sqlite3EndTable( Parse pParse, Token pCons, Token pEnd, int null_4 )
- {
- sqlite3EndTable( pParse, pCons, pEnd, null );
- }
- static void sqlite3EndTable( Parse pParse, int null_2, int null_3, Select pSelect )
- {
- sqlite3EndTable( pParse, null, null, pSelect );
- }
- static void sqlite3EndTable(
- Parse pParse, /* Parse context */
- Token pCons, /* The ',' token after the last column defn. */
- Token pEnd, /* The final ')' token in the CREATE TABLE */
- Select pSelect /* Select from a "CREATE ... AS SELECT" */
- )
- {
- Table p;
- sqlite3 db = pParse.db;
- int iDb;
- if ( ( pEnd == null && pSelect == null ) /*|| db.mallocFailed != 0 */ )
- {
- return;
- }
- p = pParse.pNewTable;
- if ( p == null )
- return;
- Debug.Assert( 0 == db.init.busy || pSelect == null );
- iDb = sqlite3SchemaToIndex( db, p.pSchema );
- #if !SQLITE_OMIT_CHECK
- /* Resolve names in all CHECK constraint expressions.
- */
- if ( p.pCheck != null )
- {
- SrcList sSrc; /* Fake SrcList for pParse.pNewTable */
- NameContext sNC; /* Name context for pParse.pNewTable */
- sNC = new NameContext();// memset(sNC, 0, sizeof(sNC));
- sSrc = new SrcList();// memset(sSrc, 0, sizeof(sSrc));
- sSrc.nSrc = 1;
- sSrc.a = new SrcList_item[1];
- sSrc.a[0] = new SrcList_item();
- sSrc.a[0].zName = p.zName;
- sSrc.a[0].pTab = p;
- sSrc.a[0].iCursor = -1;
- sNC.pParse = pParse;
- sNC.pSrcList = sSrc;
- sNC.isCheck = 1;
- if ( sqlite3ResolveExprNames( sNC, ref p.pCheck ) != 0 )
- {
- return;
- }
- }
- #endif // * !SQLITE_OMIT_CHECK) */
- /* If the db.init.busy is 1 it means we are reading the SQL off the
- ** "sqlite_master" or "sqlite_temp_master" table on the disk.
- ** So do not write to the disk again. Extract the root page number
- ** for the table from the db.init.newTnum field. (The page number
- ** should have been put there by the sqliteOpenCb routine.)
- */
- if ( db.init.busy != 0 )
- {
- p.tnum = db.init.newTnum;
- }
- /* If not initializing, then create a record for the new table
- ** in the SQLITE_MASTER table of the database.
- **
- ** If this is a TEMPORARY table, write the entry into the auxiliary
- ** file instead of into the main database file.
- */
- if ( 0 == db.init.busy )
- {
- int n;
- Vdbe v;
- String zType = ""; /* "view" or "table" */
- String zType2 = ""; /* "VIEW" or "TABLE" */
- String zStmt = ""; /* Text of the CREATE TABLE or CREATE VIEW statement */
- v = sqlite3GetVdbe( pParse );
- if ( NEVER( v == null ) )
- return;
- sqlite3VdbeAddOp1( v, OP_Close, 0 );
- /*
- ** Initialize zType for the new view or table.
- */
- if ( p.pSelect == null )
- {
- /* A regular table */
- zType = "table";
- zType2 = "TABLE";
- #if !SQLITE_OMIT_VIEW
- }
- else
- {
- /* A view */
- zType = "view";
- zType2 = "VIEW";
- #endif
- }
- /* If this is a CREATE TABLE xx AS SELECT ..., execute the SELECT
- ** statement to populate the new table. The root-page number for the
- ** new table is in register pParse->regRoot.
- **
- ** Once the SELECT has been coded by sqlite3Select(), it is in a
- ** suitable state to query for the column names and types to be used
- ** by the new table.
- **
- ** A shared-cache write-lock is not required to write to the new table,
- ** as a schema-lock must have already been obtained to create it. Since
- ** a schema-lock excludes all other database users, the write-lock would
- ** be redundant.
- */
- if ( pSelect != null )
- {
- SelectDest dest = new SelectDest();
- Table pSelTab;
- Debug.Assert( pParse.nTab == 1 );
- sqlite3VdbeAddOp3( v, OP_OpenWrite, 1, pParse.regRoot, iDb );
- sqlite3VdbeChangeP5( v, 1 );
- pParse.nTab = 2;
- sqlite3SelectDestInit( dest, SRT_Table, 1 );
- sqlite3Select( pParse, pSelect, ref dest );
- sqlite3VdbeAddOp1( v, OP_Close, 1 );
- if ( pParse.nErr == 0 )
- {
- pSelTab = sqlite3ResultSetOfSelect( pParse, pSelect );
- if ( pSelTab == null )
- return;
- Debug.Assert( p.aCol == null );
- p.nCol = pSelTab.nCol;
- p.aCol = pSelTab.aCol;
- pSelTab.nCol = 0;
- pSelTab.aCol = null;
- sqlite3DeleteTable( db, ref pSelTab );
- }
- }
- /* Compute the complete text of the CREATE statement */
- if ( pSelect != null )
- {
- zStmt = createTableStmt( db, p );
- }
- else
- {
- n = (int)( pParse.sNameToken.z.Length - pEnd.z.Length ) + 1;
- zStmt = sqlite3MPrintf( db,
- "CREATE %s %.*s", zType2, n, pParse.sNameToken.z
- );
- }
- /* A slot for the record has already been allocated in the
- ** SQLITE_MASTER table. We just need to update that slot with all
- ** the information we've collected.
- */
- sqlite3NestedParse( pParse,
- "UPDATE %Q.%s " +
- "SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q " +
- "WHERE rowid=#%d",
- db.aDb[iDb].zName, SCHEMA_TABLE( iDb ),
- zType,
- p.zName,
- p.zName,
- pParse.regRoot,
- zStmt,
- pParse.regRowid
- );
- sqlite3DbFree( db, ref zStmt );
- sqlite3ChangeCookie( pParse, iDb );
- #if !SQLITE_OMIT_AUTOINCREMENT
- /* Check to see if we need to create an sqlite_sequence table for
- ** keeping track of autoincrement keys.
- */
- if ( ( p.tabFlags & TF_Autoincrement ) != 0 )
- {
- Db pDb = db.aDb[iDb];
- if ( pDb.pSchema.pSeqTab == null )
- {
- sqlite3NestedParse( pParse,
- "CREATE TABLE %Q.sqlite_sequence(name,seq)",
- pDb.zName
- );
- }
- }
- #endif
- /* Reparse everything to update our internal data structures */
- sqlite3VdbeAddOp4( v, OP_ParseSchema, iDb, 0, 0,
- sqlite3MPrintf( db, "tbl_name='%q'", p.zName ), P4_DYNAMIC );
- }
- /* Add the table to the in-memory representation of the database.
- */
- if ( db.init.busy != 0 )
- {
- Table pOld;
- Schema pSchema = p.pSchema;
- pOld = sqlite3HashInsert( ref pSchema.tblHash, p.zName,
- sqlite3Strlen30( p.zName ), p );
- if ( pOld != null )
- {
- Debug.Assert( p == pOld ); /* Malloc must have failed inside HashInsert() */
- // db.mallocFailed = 1;
- return;
- }
- pParse.pNewTable = null;
- db.nTable++;
- db.flags |= SQLITE_InternChanges;
- #if !SQLITE_OMIT_ALTERTABLE
- if ( p.pSelect == null )
- {
- string zName = pParse.sNameToken.z;
- int nName;
- Debug.Assert( pSelect == null && pCons != null && pEnd != null );
- if ( pCons.z == null )
- {
- pCons = pEnd;
- }
- nName = zName.Length - pCons.z.Length;
- p.addColOffset = 13 + nName; // sqlite3Utf8CharLen(zName, nName);
- }
- #endif
- }
- }
- #if !SQLITE_OMIT_VIEW
- /*
- ** The parser calls this routine in order to create a new VIEW
- */
- static void sqlite3CreateView(
- Parse pParse, /* The parsing context */
- Token pBegin, /* The CREATE token that begins the statement */
- Token pName1, /* The token that holds the name of the view */
- Token pName2, /* The token that holds the name of the view */
- Select pSelect, /* A SELECT statement that will become the new view */
- int isTemp, /* TRUE for a TEMPORARY view */
- int noErr /* Suppress error messages if VIEW already exists */
- )
- {
- Table p;
- int n;
- string z;//const char *z;
- Token sEnd;
- DbFixer sFix = new DbFixer();
- Token pName = null;
- int iDb;
- sqlite3 db = pParse.db;
- if ( pParse.nVar > 0 )
- {
- sqlite3ErrorMsg( pParse, "parameters are not allowed in views" );
- sqlite3SelectDelete( db, ref pSelect );
- return;
- }
- sqlite3StartTable( pParse, pName1, pName2, isTemp, 1, 0, noErr );
- p = pParse.pNewTable;
- if ( p == null || pParse.nErr != 0 )
- {
- sqlite3SelectDelete( db, ref pSelect );
- return;
- }
- sqlite3TwoPartName( pParse, pName1, pName2, ref pName );
- iDb = sqlite3SchemaToIndex( db, p.pSchema );
- if ( sqlite3FixInit( sFix, pParse, iDb, "view", pName ) != 0
- && sqlite3FixSelect( sFix, pSelect ) != 0
- )
- {
- sqlite3SelectDelete( db, ref pSelect );
- return;
- }
- /* Make a copy of the entire SELECT statement that defines the view.
- ** This will force all the Expr.token.z values to be dynamically
- ** allocated rather than point to the input string - which means that
- ** they will persist after the current sqlite3_exec() call returns.
- */
- p.pSelect = sqlite3SelectDup( db, pSelect, EXPRDUP_REDUCE );
- sqlite3SelectDelete( db, ref pSelect );
- //if ( db.mallocFailed != 0 )
- //{
- // return;
- //}
- if ( 0 == db.init.busy )
- {
- sqlite3ViewGetColumnNames( pParse, p );
- }
- /* Locate the end of the CREATE VIEW statement. Make sEnd point to
- ** the end.
- */
- sEnd = pParse.sLastToken;
- if ( ALWAYS( sEnd.z[0] != 0 ) && sEnd.z[0] != ';' )
- {
- sEnd.z = sEnd.z.Substring( sEnd.n );
- }
- sEnd.n = 0;
- n = (int)( pBegin.z.Length - sEnd.z.Length );//sEnd.z - pBegin.z;
- z = pBegin.z;
- while ( ALWAYS( n > 0 ) && sqlite3Isspace( z[n - 1] ) )
- {
- n--;
- }
- sEnd.z = z.Substring( n - 1 );
- sEnd.n = 1;
- /* Use sqlite3EndTable() to add the view to the SQLITE_MASTER table */
- sqlite3EndTable( pParse, null, sEnd, null );
- return;
- }
- #else
- static void sqlite3CreateView(
- Parse pParse, /* The parsing context */
- Token pBegin, /* The CREATE token that begins the statement */
- Token pName1, /* The token that holds the name of the view */
- Token pName2, /* The token that holds the name of the view */
- Select pSelect, /* A SELECT statement that will become the new view */
- int isTemp, /* TRUE for a TEMPORARY view */
- int noErr /* Suppress error messages if VIEW already exists */
- )
- {
- }
- #endif // * SQLITE_OMIT_VIEW */
- #if !SQLITE_OMIT_VIEW || !SQLITE_OMIT_VIRTUALTABLE
- /*
- ** The Table structure pTable is really a VIEW. Fill in the names of
- ** the columns of the view in the pTable structure. Return the number
- ** of errors. If an error is seen leave an error message in pParse.zErrMsg.
- */
- static int sqlite3ViewGetColumnNames( Parse pParse, Table pTable )
- {
- Table pSelTab; /* A fake table from which we get the result set */
- Select pSel; /* Copy of the SELECT that implements the view */
- int nErr = 0; /* Number of errors encountered */
- int n; /* Temporarily holds the number of cursors assigned */
- sqlite3 db = pParse.db; /* Database connection for malloc errors */
- dxAuth xAuth; //)(void*,int,const char*,const char*,const char*,const char*);
- Debug.Assert( pTable != null );
- #if !SQLITE_OMIT_VIRTUALTABLE
- if ( sqlite3VtabCallConnect( pParse, pTable ) )
- {
- return SQLITE_ERROR;
- }
- #endif
- if ( IsVirtual( pTable ) )
- return 0;
- #if !SQLITE_OMIT_VIEW
- /* A positive nCol means the columns names for this view are
- ** already known.
- */
- if ( pTable.nCol > 0 )
- return 0;
- /* A negative nCol is a special marker meaning that we are currently
- ** trying to compute the column names. If we enter this routine with
- ** a negative nCol, it means two or more views form a loop, like this:
- **
- ** CREATE VIEW one AS SELECT * FROM two;
- ** CREATE VIEW two AS SELECT * FROM one;
- **
- ** Actually, the error above is now caught prior to reaching this point.
- ** But the following test is still important as it does come up
- ** in the following:
- **
- ** CREATE TABLE main.ex1(a);
- ** CREATE TEMP VIEW ex1 AS SELECT a FROM ex1;
- ** SELECT * FROM temp.ex1;
- */
- if ( pTable.nCol < 0 )
- {
- sqlite3ErrorMsg( pParse, "view %s is circularly defined", pTable.zName );
- return 1;
- }
- Debug.Assert( pTable.nCol >= 0 );
- /* If we get this far, it means we need to compute the table names.
- ** Note that the call to sqlite3ResultSetOfSelect() will expand any
- ** "*" elements in the results set of the view and will assign cursors
- ** to the elements of the FROM clause. But we do not want these changes
- ** to be permanent. So the computation is done on a copy of the SELECT
- ** statement that defines the view.
- */
- Debug.Assert( pTable.pSelect != null );
- pSel = sqlite3SelectDup( db, pTable.pSelect, 0 );
- if ( pSel != null )
- {
- u8 enableLookaside = db.lookaside.bEnabled;
- n = pParse.nTab;
- sqlite3SrcListAssignCursors( pParse, pSel.pSrc );
- pTable.nCol = -1;
- db.lookaside.bEnabled = 0;
- #if !SQLITE_OMIT_AUTHORIZATION
- xAuth = db.xAuth;
- db.xAuth = 0;
- pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
- db.xAuth = xAuth;
- #else
- pSelTab = sqlite3ResultSetOfSelect( pParse, pSel );
- #endif
- db.lookaside.bEnabled = enableLookaside;
- pParse.nTab = n;
- if ( pSelTab != null )
- {
- Debug.Assert( pTable.aCol == null );
- pTable.nCol = pSelTab.nCol;
- pTable.aCol = pSelTab.aCol;
- pSelTab.nCol = 0;
- pSelTab.aCol = null;
- sqlite3DeleteTable( db, ref pSelTab );
- pTable.pSchema.flags |= DB_UnresetViews;
- }
- else
- {
- pTable.nCol = 0;
- nErr++;
- }
- sqlite3SelectDelete( db, ref pSel );
- }
- else
- {
- nErr++;
- }
- #endif // * SQLITE_OMIT_VIEW */
- return nErr;
- }
- #endif // * !SQLITE_OMIT_VIEW) || !SQLITE_OMIT_VIRTUALTABLE) */
- #if !SQLITE_OMIT_VIEW
- /*
- ** Clear the column names from every VIEW in database idx.
- */
- static void sqliteViewResetAll( sqlite3 db, int idx )
- {
- HashElem i;
- if ( !DbHasProperty( db, idx, DB_UnresetViews ) )
- return;
- //for(i=sqliteHashFirst(&db.aDb[idx].pSchema.tblHash); i;i=sqliteHashNext(i)){
- for ( i = db.aDb[idx].pSchema.tblHash.first; i != null; i = i.next )
- {
- Table pTab = (Table)i.data;// sqliteHashData( i );
- if ( pTab.pSelect != null )
- {
- sqliteDeleteColumnNames( db, pTab );
- pTab.aCol = null;
- pTab.nCol = 0;
- }
- }
- DbClearProperty( db, idx, DB_UnresetViews );
- }
- #else
- //# define sqliteViewResetAll(A,B)
- static void sqliteViewResetAll( sqlite3 A, int B )
- {
- }
- #endif // * SQLITE_OMIT_VIEW */
- /*
- ** This function is called by the VDBE to adjust the internal schema
- ** used by SQLite when the btree layer moves a table root page. The
- ** root-page of a table or index in database iDb has changed from iFrom
- ** to iTo.
- **
- ** Ticket #1728: The symbol table might still contain information
- ** on tables and/or indices that are the process of being deleted.
- ** If you are unlucky, one of those deleted indices or tables might
- ** have the same rootpage number as the real table or index that is
- ** being moved. So we cannot stop searching after the first match
- ** because the first match might be for one of the deleted indices
- ** or tables and not the table/index that is actually being moved.
- ** We must continue looping until all tables and indices with
- ** rootpage==iFrom have been converted to have a rootpage of iTo
- ** in order to be certain that we got the right one.
- */
- #if !SQLITE_OMIT_AUTOVACUUM
- static void sqlite3RootPageMoved( Db pDb, int iFrom, int iTo )
- {
- HashElem pElem;
- Hash pHash;
- pHash = pDb.pSchema.tblHash;
- for ( pElem = pHash.first; pElem != null; pElem = pElem.next )// ( pElem = sqliteHashFirst( pHash ) ; pElem ; pElem = sqliteHashNext( pElem ) )
- {
- Table pTab = (Table)pElem.data;// sqliteHashData( pElem );
- if ( pTab.tnum == iFrom )
- {
- pTab.tnum = iTo;
- }
- }
- pHash = pDb.pSchema.idxHash;
- for ( pElem = pHash.first; pElem != null; pElem = pElem.next )// ( pElem = sqliteHashFirst( pHash ) ; pElem ; pElem = sqliteHashNext( pElem ) )
- {
- Index pIdx = (Index)pElem.data;// sqliteHashData( pElem );
- if ( pIdx.tnum == iFrom )
- {
- pIdx.tnum = iTo;
- }
- }
- }
- #endif
- /*
- ** Write code to erase the table with root-page iTable from database iDb.
- ** Also write code to modify the sqlite_master table and internal schema
- ** if a root-page of another table is moved by the btree-layer whilst
- ** erasing iTable (this can happen with an auto-vacuum database).
- */
- static void destroyRootPage( Parse pParse, int iTable, int iDb )
- {
- Vdbe v = sqlite3GetVdbe( pParse );
- int r1 = sqlite3GetTempReg( pParse );
- sqlite3VdbeAddOp3( v, OP_Destroy, iTable, r1, iDb );
- sqlite3MayAbort( pParse );
- #if !SQLITE_OMIT_AUTOVACUUM
- /* OP_Destroy stores an in integer r1. If this integer
- ** is non-zero, then it is the root page number of a table moved to
- ** location iTable. The following code modifies the sqlite_master table to
- ** reflect this.
- **
- ** The "#NNN" in the SQL is a special constant that means whatever value
- ** is in register NNN. See grammar rules associated with the TK_REGISTER
- ** token for additional information.
- */
- sqlite3NestedParse( pParse,
- "UPDATE %Q.%s SET rootpage=%d WHERE #%d AND rootpage=#%d",
- pParse.db.aDb[iDb].zName, SCHEMA_TABLE( iDb ), iTable, r1, r1 );
- #endif
- sqlite3ReleaseTempReg( pParse, r1 );
- }
- /*
- ** Write VDBE code to erase table pTab and all associated indices on disk.
- ** Code to update the sqlite_master tables and internal schema definitions
- ** in case a root-page belonging to another table is moved by the btree layer
- ** is also added (this can happen with an auto-vacuum database).
- */
- static void destroyTable( Parse pParse, Table pTab )
- {
- #if SQLITE_OMIT_AUTOVACUUM
- Index pIdx;
- int iDb = sqlite3SchemaToIndex( pParse.db, pTab.pSchema );
- destroyRootPage( pParse, pTab.tnum, iDb );
- for ( pIdx = pTab.pIndex ; pIdx != null ; pIdx = pIdx.pNext )
- {
- destroyRootPage( pParse, pIdx.tnum, iDb );
- }
- #else
- /* If the database may be auto-vacuum capable (if SQLITE_OMIT_AUTOVACUUM
- ** is not defined), then it is important to call OP_Destroy on the
- ** table and index root-pages in order, starting with the numerically
- ** largest root-page number. This guarantees that none of the root-pages
- ** to be destroyed is relocated by an earlier OP_Destroy. i.e. if the
- ** following were coded:
- **
- ** OP_Destroy 4 0
- ** ...
- ** OP_Destroy 5 0
- **
- ** and root page 5 happened to be the largest root-page number in the
- ** database, then root page 5 would be moved to page 4 by the
- ** "OP_Destroy 4 0" opcode. The subsequent "OP_Destroy 5 0" would hit
- ** a free-list page.
- */
- int iTab = pTab.tnum;
- int iDestroyed = 0;
- while ( true )
- {
- Index pIdx;
- int iLargest = 0;
- if ( iDestroyed == 0 || iTab < iDestroyed )
- {
- iLargest = iTab;
- }
- for ( pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext )
- {
- int iIdx = pIdx.tnum;
- Debug.Assert( pIdx.pSchema == pTab.pSchema );
- if ( ( iDestroyed == 0 || ( iIdx < iDestroyed ) ) && iIdx > iLargest )
- {
- iLargest = iIdx;
- }
- }
- if ( iLargest == 0 )
- {
- return;
- }
- else
- {
- int iDb = sqlite3SchemaToIndex( pParse.db, pTab.pSchema );
- destroyRootPage( pParse, iLargest, iDb );
- iDestroyed = iLargest;
- }
- }
- #endif
- }
- /*
- ** This routine is called to do the work of a DROP TABLE statement.
- ** pName is the name of the table to be dropped.
- */
- static void sqlite3DropTable( Parse pParse, SrcList pName, int isView, int noErr )
- {
- Table pTab;
- Vdbe v;
- sqlite3 db = pParse.db;
- int iDb;
- //if ( db.mallocFailed != 0 )
- //{
- // goto exit_drop_table;
- //}
- Debug.Assert( pParse.nErr == 0 );
- Debug.Assert( pName.nSrc == 1 );
- if ( noErr != 0 )
- db.suppressErr++;
- pTab = sqlite3LocateTable( pParse, isView,
- pName.a[0].zName, pName.a[0].zDatabase );
- if ( noErr != 0 )
- db.suppressErr--;
- if ( pTab == null )
- {
- goto exit_drop_table;
- }
- iDb = sqlite3SchemaToIndex( db, pTab.pSchema );
- Debug.Assert( iDb >= 0 && iDb < db.nDb );
- /* If pTab is a virtual table, call ViewGetColumnNames() to ensure
- ** it is initialized.
- */
- if ( IsVirtual( pTab ) && sqlite3ViewGetColumnNames( pParse, pTab ) != 0 )
- {
- goto exit_drop_table;
- }
- #if !SQLITE_OMIT_AUTHORIZATION
- {
- int code;
- string zTab = SCHEMA_TABLE(iDb);
- string zDb = db.aDb[iDb].zName;
- string zArg2 = 0;
- if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb)){
- goto exit_drop_table;
- }
- if( isView ){
- if( OMIT_TEMPDB ==0&& iDb==1 ){
- code = SQLITE_DROP_TEMP_VIEW;
- }else{
- code = SQLITE_DROP_VIEW;
- }
- }else if( IsVirtual(pTab) ){
- code = SQLITE_DROP_VTABLE;
- zArg2 = sqlite3GetVTable(db, pTab)->pMod->zName;
- }else{
- if( OMIT_TEMPDB ==0&& iDb==1 ){
- code = SQLITE_DROP_TEMP_TABLE;
- }else{
- code = SQLITE_DROP_TABLE;
- }
- }
- if( sqlite3AuthCheck(pParse, code, pTab.zName, zArg2, zDb) ){
- goto exit_drop_table;
- }
- if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab.zName, 0, zDb) ){
- goto exit_drop_table;
- }
- }
- #endif
- if ( pTab.zName.StartsWith( "sqlite_", System.StringComparison.InvariantCultureIgnoreCase ) )
- {
- sqlite3ErrorMsg( pParse, "table %s may not be dropped", pTab.zName );
- goto exit_drop_table;
- }
- #if !SQLITE_OMIT_VIEW
- /* Ensure DROP TABLE is not used on a view, and DROP VIEW is not used
- ** on a table.
- */
- if ( isView != 0 && pTab.pSelect == null )
- {
- sqlite3ErrorMsg( pParse, "use DROP TABLE to delete table %s", pTab.zName );
- goto exit_drop_table;
- }
- if ( 0 == isView && pTab.pSelect != null )
- {
- sqlite3ErrorMsg( pParse, "use DROP VIEW to delete view %s", pTab.zName );
- goto exit_drop_table;
- }
- #endif
- /* Generate code to remove the table from the master table
- ** on disk.
- */
- v = sqlite3GetVdbe( pParse );
- if ( v != null )
- {
- Trigger pTrigger;
- Db pDb = db.aDb[iDb];
- sqlite3BeginWriteOperation( pParse, 1, iDb );
- #if !SQLITE_OMIT_VIRTUALTABLE
- if ( IsVirtual( pTab ) )
- {
- sqlite3VdbeAddOp0( v, OP_VBegin );
- }
- #endif
- sqlite3FkDropTable( pParse, pName, pTab );
- /* Drop all triggers associated with the table being dropped. Code
- ** is generated to remove entries from sqlite_master and/or
- ** sqlite_temp_master if required.
- */
- pTrigger = sqlite3TriggerList( pParse, pTab );
- while ( pTrigger != null )
- {
- Debug.Assert( pTrigger.pSchema == pTab.pSchema ||
- pTrigger.pSchema == db.aDb[1].pSchema );
- sqlite3DropTriggerPtr( pParse, pTrigger );
- pTrigger = pTrigger.pNext;
- }
- #if !SQLITE_OMIT_AUTOINCREMENT
- /* Remove any entries of the sqlite_sequence table associated with
- ** the table being dropped. This is done before the table is dropped
- ** at the btree level, in case the sqlite_sequence table needs to
- ** move as a result of the drop (can happen in auto-vacuum mode).
- */
- if ( ( pTab.tabFlags & TF_Autoincrement ) != 0 )
- {
- sqlite3NestedParse( pParse,
- "DELETE FROM %s.sqlite_sequence WHERE name=%Q",
- pDb.zName, pTab.zName
- );
- }
- #endif
- /* Drop all SQLITE_MASTER table and index entries that refer to the
- ** table. The program name loops through the master table and deletes
- ** every row that refers to a table of the same name as the one being
- ** dropped. Triggers are handled seperately because a trigger can be
- ** created in the temp database that refers to a table in another
- ** database.
- */
- sqlite3NestedParse( pParse,
- "DELETE FROM %Q.%s WHERE tbl_name=%Q and type!='trigger'",
- pDb.zName, SCHEMA_TABLE( iDb ), pTab.zName );
- /* Drop any statistics from the sqlite_stat1 table, if it exists */
- if ( sqlite3FindTable( db, "sqlite_stat1", db.aDb[iDb].zName ) != null )
- {
- sqlite3NestedParse( pParse,
- "DELETE FROM %Q.sqlite_stat1 WHERE tbl=%Q", pDb.zName, pTab.zName
- );
- }
- if ( 0 == isView && !IsVirtual( pTab ) )
- {
- destroyTable( pParse, pTab );
- }
- /* Remove the table entry from SQLite's internal schema and modify
- ** the schema cookie.
- */
- if ( IsVirtual( pTab ) )
- {
- sqlite3VdbeAddOp4( v, OP_VDestroy, iDb, 0, 0, pTab.zName, 0 );
- }
- sqlite3VdbeAddOp4( v, OP_DropTable, iDb, 0, 0, pTab.zName, 0 );
- sqlite3ChangeCookie( pParse, iDb );
- }
- sqliteViewResetAll( db, iDb );
- exit_drop_table:
- sqlite3SrcListDelete( db, ref pName );
- }
- /*
- ** This routine is called to create a new foreign key on the table
- ** currently under construction. pFromCol determines which columns
- ** in the current table point to the foreign key. If pFromCol==0 then
- ** connect the key to the last column inserted. pTo is the name of
- ** the table referred to. pToCol is a list of tables in the other
- ** pTo table that the foreign key points to. flags contains all
- ** information about the conflict resolution algorithms specified
- ** in the ON DELETE, ON UPDATE and ON INSERT clauses.
- **
- ** An FKey structure is created and added to the table currently
- ** under construction in the pParse.pNewTable field.
- **
- ** The foreign key is set for IMMEDIATE processing. A subsequent call
- ** to sqlite3DeferForeignKey() might change this to DEFERRED.
- */
- // OVERLOADS, so I don't need to rewrite parse.c
- static void sqlite3CreateForeignKey( Parse pParse, int null_2, Token pTo, ExprList pToCol, int flags )
- {
- sqlite3CreateForeignKey( pParse, null, pTo, pToCol, flags );
- }
- static void sqlite3CreateForeignKey(
- Parse pParse, /* Parsing context */
- ExprList pFromCol, /* Columns in this table that point to other table */
- Token pTo, /* Name of the other table */
- ExprList pToCol, /* Columns in the other table */
- int flags /* Conflict resolution algorithms. */
- )
- {
- sqlite3 db = pParse.db;
- #if !SQLITE_OMIT_FOREIGN_KEY
- FKey pFKey = null;
- FKey pNextTo;
- Table p = pParse.pNewTable;
- int nByte;
- int i;
- int nCol;
- //string z;
- Debug.Assert( pTo != null );
- if ( p == null || IN_DECLARE_VTAB )
- goto fk_end;
- if ( pFromCol == null )
- {
- int iCol = p.nCol - 1;
- if ( NEVER( iCol < 0 ) )
- goto fk_end;
- if ( pToCol != null && pToCol.nExpr != 1 )
- {
- sqlite3ErrorMsg( pParse, "foreign key on %s" +
- " should reference only one column of table %T",
- p.aCol[iCol].zName, pTo );
- goto fk_end;
- }
- nCol = 1;
- }
- else if ( pToCol != null && pToCol.nExpr != pFromCol.nExpr )
- {
- sqlite3ErrorMsg( pParse,
- "number of columns in foreign key does not match the number of " +
- "columns in the referenced table" );
- goto fk_end;
- }
- else
- {
- nCol = pFromCol.nExpr;
- }
- //nByte = sizeof(*pFKey) + (nCol-1)*sizeof(pFKey.aCol[0]) + pTo.n + 1;
- //if( pToCol ){
- // for(i=0; i<pToCol.nExpr; i++){
- // nByte += sqlite3Strlen30(pToCol->a[i].zName) + 1;
- // }
- //}
- pFKey = new FKey();//sqlite3DbMallocZero(db, nByte );
- if ( pFKey == null )
- {
- goto fk_end;
- }
- pFKey.pFrom = p;
- pFKey.pNextFrom = p.pFKey;
- //z = pFKey.aCol[nCol].zCol;
- pFKey.aCol = new FKey.sColMap[nCol];// z;
- pFKey.aCol[0] = new FKey.sColMap();
- pFKey.zTo = pTo.z.Substring( 0, pTo.n ); //memcpy( z, pTo.z, pTo.n );
- //z[pTo.n] = 0;
- sqlite3Dequote( ref pFKey.zTo );
- //z += pTo.n + 1;
- pFKey.nCol = nCol;
- if ( pFromCol == null )
- {
- pFKey.aCol[0].iFrom = p.nCol - 1;
- }
- else
- {
- for ( i = 0; i < nCol; i++ )
- {
- if ( pFKey.aCol[i] == null )
- pFKey.aCol[i] = new FKey.sColMap();
- int j;
- for ( j = 0; j < p.nCol; j++ )
- {
- if ( p.aCol[j].zName.Equals( pFromCol.a[i].zName, StringComparison.InvariantCultureIgnoreCase ) )
- {
- pFKey.aCol[i].iFrom = j;
- break;
- }
- }
- if ( j >= p.nCol )
- {
- sqlite3ErrorMsg( pParse,
- "unknown column \"%s\" in foreign key definition",
- pFromCol.a[i].zName );
- goto fk_end;
- }
- }
- }
- if ( pToCol != null )
- {
- for ( i = 0; i < nCol; i++ )
- {
- int n = sqlite3Strlen30( pToCol.a[i].zName );
- if ( pFKey.aCol[i] == null )
- pFKey.aCol[i] = new FKey.sColMap();
- pFKey.aCol[i].zCol = pToCol.a[i].zName;
- //memcpy( z, pToCol.a[i].zName, n );
- //z[n] = 0;
- //z += n + 1;
- }
- }
- pFKey.isDeferred = 0;
- pFKey.aAction[0] = (u8)( flags & 0xff ); /* ON DELETE action */
- pFKey.aAction[1] = (u8)( ( flags >> 8 ) & 0xff ); /* ON UPDATE action */
- pNextTo = sqlite3HashInsert( ref p.pSchema.fkeyHash,
- pFKey.zTo, sqlite3Strlen30( pFKey.zTo ), pFKey
- );
- //if( pNextTo==pFKey ){
- // db.mallocFailed = 1;
- // goto fk_end;
- //}
- if ( pNextTo != null )
- {
- Debug.Assert( pNextTo.pPrevTo == null );
- pFKey.pNextTo = pNextTo;
- pNextTo.pPrevTo = pFKey;
- }
- /* Link the foreign key to the table as the last step.
- */
- p.pFKey = pFKey;
- pFKey = null;
- fk_end:
- sqlite3DbFree( db, ref pFKey );
- #endif // * !SQLITE_OMIT_FOREIGN_KEY) */
- sqlite3ExprListDelete( db, ref pFromCol );
- sqlite3ExprListDelete( db, ref pToCol );
- }
- /*
- ** This routine is called when an INITIALLY IMMEDIATE or INITIALLY DEFERRED
- ** clause is seen as part of a foreign key definition. The isDeferred
- ** parameter is 1 for INITIALLY DEFERRED and 0 for INITIALLY IMMEDIATE.
- ** The behavior of the most recently created foreign key is adjusted
- ** accordingly.
- */
- static void sqlite3DeferForeignKey( Parse pParse, int isDeferred )
- {
- #if !SQLITE_OMIT_FOREIGN_KEY
- Table pTab;
- FKey pFKey;
- if ( ( pTab = pParse.pNewTable ) == null || ( pFKey = pTab.pFKey ) == null )
- return;
- Debug.Assert( isDeferred == 0 || isDeferred == 1 ); /* EV: R-30323-21917 */
- pFKey.isDeferred = (u8)isDeferred;
- #endif
- }
- /*
- ** Generate code that will erase and refill index pIdx. This is
- ** used to initialize a newly created index or to recompute the
- ** content of an index in response to a REINDEX command.
- **
- ** if memRootPage is not negative, it means that the index is newly
- ** created. The register specified by memRootPage contains the
- ** root page number of the index. If memRootPage is negative, then
- ** the index already exists and must be cleared before being refilled and
- ** the root page number of the index is taken from pIndex.tnum.
- */
- static void sqlite3RefillIndex( Parse pParse, Index pIndex, int memRootPage )
- {
- Table pTab = pIndex.pTable; /* The table that is indexed */
- int iTab = pParse.nTab++; /* Btree cursor used for pTab */
- int iIdx = pParse.nTab++; /* Btree cursor used for pIndex */
- int addr1; /* Address of top of loop */
- int tnum; /* Root page of index */
- Vdbe v; /* Generate code into this virtual machine */
- KeyInfo pKey; /* KeyInfo for index */
- int regIdxKey; /* Registers containing the index key */
- int regRecord; /* Register holding assemblied index record */
- sqlite3 db = pParse.db; /* The database connection */
- int iDb = sqlite3SchemaToIndex( db, pIndex.pSchema );
- #if !SQLITE_OMIT_AUTHORIZATION
- if( sqlite3AuthCheck(pParse, SQLITE_REINDEX, pIndex.zName, 0,
- db.aDb[iDb].zName ) ){
- return;
- }
- #endif
- /* Require a write-lock on the table to perform this operation */
- sqlite3TableLock( pParse, iDb, pTab.tnum, 1, pTab.zName );
- v = sqlite3GetVdbe( pParse );
- if ( v == null )
- return;
- if ( memRootPage >= 0 )
- {
- tnum = memRootPage;
- }
- else
- {
- tnum = pIndex.tnum;
- sqlite3VdbeAddOp2( v, OP_Clear, tnum, iDb );
- }
- pKey = sqlite3IndexKeyinfo( pParse, pIndex );
- sqlite3VdbeAddOp4( v, OP_OpenWrite, iIdx, tnum, iDb,
- pKey, P4_KEYINFO_HANDOFF );
- if ( memRootPage >= 0 )
- {
- sqlite3VdbeChangeP5( v, 1 );
- }
- sqlite3OpenTable( pParse, iTab, iDb, pTab, OP_OpenRead );
- addr1 = sqlite3VdbeAddOp2( v, OP_Rewind, iTab, 0 );
- regRecord = sqlite3GetTempReg( pParse );
- regIdxKey = sqlite3GenerateIndexKey( pParse, pIndex, iTab, regRecord, true );
- if ( pIndex.onError != OE_None )
- {
- int regRowid = regIdxKey + pIndex.nColumn;
- int j2 = sqlite3VdbeCurrentAddr( v ) + 2;
- int pRegKey = regIdxKey;// SQLITE_INT_TO_PTR( regIdxKey );
- /* The registers accessed by the OP_IsUnique opcode were allocated
- ** using sqlite3GetTempRange() inside of the sqlite3GenerateIndexKey()
- ** call above. Just before that function was freed they were released
- ** (made available to the compiler for reuse) using
- ** sqlite3ReleaseTempRange(). So in some ways having the OP_IsUnique
- ** opcode use the values stored within seems dangerous. However, since
- ** we can be sure that no other temp registers have been allocated
- ** since sqlite3ReleaseTempRange() was called, it is safe to do so.
- */
- sqlite3VdbeAddOp4( v, OP_IsUnique, iIdx, j2, regRowid, pRegKey, P4_INT32 );
- sqlite3HaltConstraint(
- pParse, OE_Abort, "indexed columns are not unique", P4_STATIC );
- }
- sqlite3VdbeAddOp2( v, OP_IdxInsert, iIdx, regRecord );
- sqlite3VdbeChangeP5( v, OPFLAG_USESEEKRESULT );
- sqlite3ReleaseTempReg( pParse, regRecord );
- sqlite3VdbeAddOp2( v, OP_Next, iTab, addr1 + 1 );
- sqlite3VdbeJumpHere( v, addr1 );
- sqlite3VdbeAddOp1( v, OP_Close, iTab );
- sqlite3VdbeAddOp1( v, OP_Close, iIdx );
- }
- /*
- ** Create a new index for an SQL table. pName1.pName2 is the name of the index
- ** and pTblList is the name of the table that is to be indexed. Both will
- ** be NULL for a primary key or an index that is created to satisfy a
- ** UNIQUE constraint. If pTable and pIndex are NULL, use pParse.pNewTable
- ** as the table to be indexed. pParse.pNewTable is a table that is
- ** currently being constructed by a CREATE TABLE statement.
- **
- ** pList is a list of columns to be indexed. pList will be NULL if this
- ** is a primary key or unique-constraint on the most recent column added
- ** to the table currently under construction.
- **
- ** If the index is created successfully, return a pointer to the new Index
- ** structure. This is used by sqlite3AddPrimaryKey() to mark the index
- ** as the tables primary key (Index.autoIndex==2).
- */
- // OVERLOADS, so I don't need to rewrite parse.c
- static Index sqlite3CreateIndex( Parse pParse, int null_2, int null_3, int null_4, int null_5, int onError, int null_7, int null_8, int sortOrder, int ifNotExist )
- {
- return sqlite3CreateIndex( pParse, null, null, null, null, onError, null, null, sortOrder, ifNotExist );
- }
- static Index sqlite3CreateIndex( Parse pParse, int null_2, int null_3, int null_4, ExprList pList, int onError, int null_7, int null_8, int sortOrder, int ifNotExist )
- {
- return sqlite3CreateIndex( pParse, null, null, null, pList, onError, null, null, sortOrder, ifNotExist );
- }
- static Index sqlite3CreateIndex(
- Parse pParse, /* All information about this Parse */
- Token pName1, /* First part of index name. May be NULL */
- Token pName2, /* Second part of index name. May be NULL */
- SrcList pTblName, /* Table to index. Use pParse.pNewTable if 0 */
- ExprList pList, /* A list of columns to be indexed */
- int onError, /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
- Token pStart, /* The CREATE token that begins this statement */
- Token pEnd, /* The ")" that closes the CREATE INDEX statement */
- int sortOrder, /* Sort order of primary key when pList==NULL */
- int ifNotExist /* Omit error if index already exists */
- )
- {
- Index pRet = null; /* Pointer to return */
- Table pTab = null; /* Table to be indexed */
- Index pIndex = null; /* The index to be created */
- string zName = null; /* Name of the index */
- int nName; /* Number of characters in zName */
- int i, j;
- Token nullId = new Token(); /* Fake token for an empty ID list */
- DbFixer sFix = new DbFixer(); /* For assigning database names to pTable */
- int sortOrderMask; /* 1 to honor DESC in index. 0 to ignore. */
- sqlite3 db = pParse.db;
- Db pDb; /* The specific table containing the indexed database */
- int iDb; /* Index of the database that is being written */
- Token pName = null; /* Unqualified name of the index to create */
- ExprList_item pListItem; /* For looping over pList */
- int nCol;
- int nExtra = 0;
- StringBuilder zExtra = new StringBuilder();
- Debug.Assert( pStart == null || pEnd != null ); /* pEnd must be non-NULL if pStart is */
- Debug.Assert( pParse.nErr == 0 ); /* Never called with prior errors */
- if ( /* db.mallocFailed != 0 || */ IN_DECLARE_VTAB )
- {
- goto exit_create_index;
- }
- if ( SQLITE_OK != sqlite3ReadSchema( pParse ) )
- {
- goto exit_create_index;
- }
- /*
- ** Find the table that is to be indexed. Return early if not found.
- */
- if ( pTblName != null )
- {
- /* Use the two-part index name to determine the database
- ** to search for the table. 'Fix' the table name to this db
- ** before looking up the table.
- */
- Debug.Assert( pName1 != null && pName2 != null );
- iDb = sqlite3TwoPartName( pParse, pName1, pName2, ref pName );
- if ( iDb < 0 )
- goto exit_create_index;
- #if !SQLITE_OMIT_TEMPDB
- /* If the index name was unqualified, check if the the table
- ** is a temp table. If so, set the database to 1. Do not do this
- ** if initialising a database schema.
- */
- if ( 0 == db.init.busy )
- {
- pTab = sqlite3SrcListLookup( pParse, pTblName );
- if ( pName2.n == 0 && pTab != null && pTab.pSchema == db.aDb[1].pSchema )
- {
- iDb = 1;
- }
- }
- #endif
- if ( sqlite3FixInit( sFix, pParse, iDb, "index", pName ) != 0 &&
- sqlite3FixSrcList( sFix, pTblName ) != 0
- )
- {
- /* Because the parser constructs pTblName from a single identifier,
- ** sqlite3FixSrcList can never fail. */
- Debugger.Break();
- }
- pTab = sqlite3LocateTable( pParse, 0, pTblName.a[0].zName,
- pTblName.a[0].zDatabase );
- if ( pTab == null /*|| db.mallocFailed != 0 */ )
- goto exit_create_index;
- Debug.Assert( db.aDb[iDb].pSchema == pTab.pSchema );
- }
- else
- {
- Debug.Assert( pName == null );
- pTab = pParse.pNewTable;
- if ( pTab == null )
- goto exit_create_index;
- iDb = sqlite3SchemaToIndex( db, pTab.pSchema );
- }
- pDb = db.aDb[iDb];
- Debug.Assert( pTab != null );
- Debug.Assert( pParse.nErr == 0 );
- if ( pTab.zName.StartsWith( "sqlite_", System.StringComparison.InvariantCultureIgnoreCase )
- && !pTab.zName.StartsWith( "sqlite_altertab_", System.StringComparison.InvariantCultureIgnoreCase ) )
- {
- sqlite3ErrorMsg( pParse, "table %s may not be indexed", pTab.zName );
- goto exit_create_index;
- }
- #if !SQLITE_OMIT_VIEW
- if ( pTab.pSelect != null )
- {
- sqlite3ErrorMsg( pParse, "views may not be indexed" );
- goto exit_create_index;
- }
- #endif
- if ( IsVirtual( pTab ) )
- {
- sqlite3ErrorMsg( pParse, "virtual tables may not be indexed" );
- goto exit_create_index;
- }
- /*
- ** Find the name of the index. Make sure there is not already another
- ** index or table with the same name.
- **
- ** Exception: If we are reading the names of permanent indices from the
- ** sqlite_master table (because some other process changed the schema) and
- ** one of the index names collides with the name of a temporary table or
- ** index, then we will continue to process this index.
- **
- ** If pName==0 it means that we are
- ** dealing with a primary key or UNIQUE constraint. We have to invent our
- ** own name.
- */
- if ( pName != null )
- {
- zName = sqlite3NameFromToken( db, pName );
- if ( zName == null )
- goto exit_create_index;
- if ( SQLITE_OK != sqlite3CheckObjectName( pParse, zName ) )
- {
- goto exit_create_index;
- }
- if ( 0 == db.init.busy )
- {
- if ( sqlite3FindTable( db, zName, null ) != null )
- {
- sqlite3ErrorMsg( pParse, "there is already a table named %s", zName );
- goto exit_create_index;
- }
- }
- if ( sqlite3FindIndex( db, zName, pDb.zName ) != null )
- {
- if ( ifNotExist == 0 )
- {
- sqlite3ErrorMsg( pParse, "index %s already exists", zName );
- }
- goto exit_create_index;
- }
- }
- else
- {
- int n = 0;
- Index pLoop;
- for ( pLoop = pTab.pIndex, n = 1; pLoop != null; pLoop = pLoop.pNext, n++ )
- {
- }
- zName = sqlite3MPrintf( db, "sqlite_autoindex_%s_%d", pTab.zName, n );
- if ( zName == null )
- {
- goto exit_create_index;
- }
- }
- /* Check for authorization to create an index.
- */
- #if !SQLITE_OMIT_AUTHORIZATION
- {
- string zDb = pDb.zName;
- if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iDb), 0, zDb) ){
- goto exit_create_index;
- }
- i = SQLITE_CREATE_INDEX;
- if( OMIT_TEMPDB ==0&& iDb==1 ) i = SQLITE_CREATE_TEMP_INDEX;
- if( sqlite3AuthCheck(pParse, i, zName, pTab.zName, zDb) ){
- goto exit_create_index;
- }
- }
- #endif
- /* If pList==0, it means this routine was called to make a primary
- ** key out of the last column added to the table under construction.
- ** So create a fake list to simulate this.
- */
- if ( pList == null )
- {
- nullId.z = pTab.aCol[pTab.nCol - 1].zName;
- nullId.n = sqlite3Strlen30( nullId.z );
- pList = sqlite3ExprListAppend( pParse, null, null );
- if ( pList == null )
- goto exit_create_index;
- sqlite3ExprListSetName( pParse, pList, nullId, 0 );
- pList.a[0].sortOrder = (u8)sortOrder;
- }
- /* Figure out how many bytes of space are required to store explicitly
- ** specified collation sequence names.
- */
- for ( i = 0; i < pList.nExpr; i++ )
- {
- Expr pExpr = pList.a[i].pExpr;
- if ( pExpr != null )
- {
- CollSeq pColl = pExpr.pColl;
- /* Either pColl!=0 or there was an OOM failure. But if an OOM
- ** failure we have quit before reaching this point. */
- if ( ALWAYS( pColl != null ) )
- {
- nExtra += ( 1 + sqlite3Strlen30( pColl.zName ) );
- }
- }
- }
- /*
- ** Allocate the index structure.
- */
- nName = sqlite3Strlen30( zName );
- nCol = pList.nExpr;
- pIndex = new Index();
- // sqlite3DbMallocZero( db,
- // Index.Length + /* Index structure */
- // sizeof( int ) * nCol + /* Index.aiColumn */
- // sizeof( int ) * ( nCol + 1 ) + /* Index.aiRowEst */
- // sizeof( char* ) * nCol + /* Index.azColl */
- // u8.Length * nCol + /* Index.aSortOrder */
- // nName + 1 + /* Index.zName */
- // nExtra /* Collation sequence names */
- //);
- //if ( db.mallocFailed != 0 )
- //{
- // goto exit_create_index;
- //}
- pIndex.azColl = new string[nCol + 1];//(char**)(pIndex[1]);
- pIndex.aiColumn = new int[nCol + 1];//(int *)(pIndex->azColl[nCol]);
- pIndex.aiRowEst = new int[nCol + 1];//(unsigned *)(pIndex->aiColumn[nCol]);
- pIndex.aSortOrder = new byte[nCol + 1];//(u8 *)(pIndex->aiRowEst[nCol+1]);
- //pIndex.zName = null;// (char*)( &pIndex->aSortOrder[nCol] );
- zExtra = new StringBuilder( nName + 1 );// (char*)( &pIndex.zName[nName + 1] );
- if ( zName.Length == nName )
- pIndex.zName = zName;
- else
- {
- pIndex.zName = zName.Substring( 0, nName );
- }// memcpy( pIndex.zName, zName, nName + 1 );
- pIndex.pTable = pTab;
- pIndex.nColumn = pList.nExpr;
- pIndex.onError = (u8)onError;
- pIndex.autoIndex = (u8)( pName == null ? 1 : 0 );
- pIndex.pSchema = db.aDb[iDb].pSchema;
- /* Check to see if we should honor DESC requests on index columns
- */
- if ( pDb.pSchema.file_format >= 4 )
- {
- sortOrderMask = 1; /* Honor DESC */
- }
- else
- {
- sortOrderMask = 0; /* Ignore DESC */
- }
- /* Scan the names of the columns of the table to be indexed and
- ** load the column indices into the Index structure. Report an error
- ** if any column is not found.
- **
- ** TODO: Add a test to make sure that the same column is not named
- ** more than once within the same index. Only the first instance of
- ** the column will ever be used by the optimizer. Note that using the
- ** same column more than once cannot be an error because that would
- ** break backwards compatibility - it needs to be a warning.
- */
- for ( i = 0; i < pList.nExpr; i++ )
- {//, pListItem++){
- pListItem = pList.a[i];
- string zColName = pListItem.zName;
- Column pTabCol;
- byte requestedSortOrder;
- string zColl; /* Collation sequence name */
- for ( j = 0; j < pTab.nCol; j++ )
- {//, pTabCol++){
- pTabCol = pTab.aCol[j];
- if ( zColName.Equals( pTabCol.zName, StringComparison.InvariantCultureIgnoreCase ) )
- break;
- }
- if ( j >= pTab.nCol )
- {
- sqlite3ErrorMsg( pParse, "table %s has no column named %s",
- pTab.zName, zColName );
- pParse.checkSchema = 1;
- goto exit_create_index;
- }
- pIndex.aiColumn[i] = j;
- /* Justification of the ALWAYS(pListItem->pExpr->pColl): Because of
- ** the way the "idxlist" non-terminal is constructed by the parser,
- ** if pListItem->pExpr is not null then either pListItem->pExpr->pColl
- ** must exist or else there must have been an OOM error. But if there
- ** was an OOM error, we would never reach this point. */
- if ( pListItem.pExpr != null && ALWAYS( pListItem.pExpr.pColl ) )
- {
- int nColl;
- zColl = pListItem.pExpr.pColl.zName;
- nColl = sqlite3Strlen30( zColl );
- Debug.Assert( nExtra >= nColl );
- zExtra = new StringBuilder( zColl.Substring( 0, nColl ) );// memcpy( zExtra, zColl, nColl );
- zColl = zExtra.ToString();
- //zExtra += nColl;
- nExtra -= nColl;
- }
- else
- {
- zColl = pTab.aCol[j].zColl;
- if ( zColl == null )
- {
- zColl = db.pDfltColl.zName;
- }
- }
- if ( 0 == db.init.busy && sqlite3LocateCollSeq( pParse, zColl ) == null )
- {
- goto exit_create_index;
- }
- pIndex.azColl[i] = zColl;
- requestedSortOrder = (u8)( ( pListItem.sortOrder & sortOrderMask ) != 0 ? 1 : 0 );
- pIndex.aSortOrder[i] = (u8)requestedSortOrder;
- }
- sqlite3DefaultRowEst( pIndex );
- if ( pTab == pParse.pNewTable )
- {
- /* This routine has been called to create an automatic index as a
- ** result of a PRIMARY KEY or UNIQUE clause on a column definition, or
- ** a PRIMARY KEY or UNIQUE clause following the column definitions.
- ** i.e. one of:
- **
- ** CREATE TABLE t(x PRIMARY KEY, y);
- ** CREATE TABLE t(x, y, UNIQUE(x, y));
- **
- ** Either way, check to see if the table already has such an index. If
- ** so, don't bother creating this one. This only applies to
- ** automatically created indices. Users can do as they wish with
- ** explicit indices.
- **
- ** Two UNIQUE or PRIMARY KEY constraints are considered equivalent
- ** (and thus suppressing the second one) even if they have different
- ** sort orders.
- **
- ** If there are different collating sequences or if the columns of
- ** the constraint occur in different orders, then the constraints are
- ** considered distinct and both result in separate indices.
- */
- Index pIdx;
- for ( pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext )
- {
- int k;
- Debug.Assert( pIdx.onError != OE_None );
- Debug.Assert( pIdx.autoIndex != 0 );
- Debug.Assert( pIndex.onError != OE_None );
- if ( pIdx.nColumn != pIndex.nColumn )
- continue;
- for ( k = 0; k < pIdx.nColumn; k++ )
- {
- string z1;
- string z2;
- if ( pIdx.aiColumn[k] != pIndex.aiColumn[k] )
- break;
- z1 = pIdx.azColl[k];
- z2 = pIndex.azColl[k];
- if ( z1 != z2 && !z1.Equals( z2, StringComparison.InvariantCultureIgnoreCase ) )
- break;
- }
- if ( k == pIdx.nColumn )
- {
- if ( pIdx.onError != pIndex.onError )
- {
- /* This constraint creates the same index as a previous
- ** constraint specified somewhere in the CREATE TABLE statement.
- ** However the ON CONFLICT clauses are different. If both this
- ** constraint and the previous equivalent constraint have explicit
- ** ON CONFLICT clauses this is an error. Otherwise, use the
- ** explicitly specified behavior for the index.
- */
- if ( !( pIdx.onError == OE_Default || pIndex.onError == OE_Default ) )
- {
- sqlite3ErrorMsg( pParse,
- "conflicting ON CONFLICT clauses specified", 0 );
- }
- if ( pIdx.onError == OE_Default )
- {
- pIdx.onError = pIndex.onError;
- }
- }
- goto exit_create_index;
- }
- }
- }
- /* Link the new Index structure to its table and to the other
- ** in-memory database structures.
- */
- if ( db.init.busy != 0 )
- {
- Index p;
- p = sqlite3HashInsert( ref pIndex.pSchema.idxHash,
- pIndex.zName, sqlite3Strlen30( pIndex.zName ),
- pIndex );
- if ( p != null )
- {
- Debug.Assert( p == pIndex ); /* Malloc must have failed */
- // db.mallocFailed = 1;
- goto exit_create_index;
- }
- db.flags |= SQLITE_InternChanges;
- if ( pTblName != null )
- {
- pIndex.tnum = db.init.newTnum;
- }
- }
- /* If the db.init.busy is 0 then create the index on disk. This
- ** involves writing the index into the master table and filling in the
- ** index with the current table contents.
- **
- ** The db.init.busy is 0 when the user first enters a CREATE INDEX
- ** command. db.init.busy is 1 when a database is opened and
- ** CREATE INDEX statements are read out of the master table. In
- ** the latter case the index already exists on disk, which is why
- ** we don't want to recreate it.
- **
- ** If pTblName==0 it means this index is generated as a primary key
- ** or UNIQUE constraint of a CREATE TABLE statement. Since the table
- ** has just been created, it contains no data and the index initialization
- ** step can be skipped.
- */
- else //if ( 0 == db.init.busy )
- {
- Vdbe v;
- string zStmt;
- int iMem = ++pParse.nMem;
- v = sqlite3GetVdbe( pParse );
- if ( v == null )
- goto exit_create_index;
- /* Create the rootpage for the index
- */
- sqlite3BeginWriteOperation( pParse, 1, iDb );
- sqlite3VdbeAddOp2( v, OP_CreateIndex, iDb, iMem );
- /* Gather the complete text of the CREATE INDEX statement into
- ** the zStmt variable
- */
- if ( pStart != null )
- {
- Debug.Assert( pEnd != null );
- /* A named index with an explicit CREATE INDEX statement */
- zStmt = sqlite3MPrintf( db, "CREATE%s INDEX %.*s",
- onError == OE_None ? "" : " UNIQUE",
- pName.z.Length - pEnd.z.Length + 1,
- pName.z );
- }
- else
- {
- /* An automatic index created by a PRIMARY KEY or UNIQUE constraint */
- /* zStmt = sqlite3MPrintf(""); */
- zStmt = null;
- }
- /* Add an entry in sqlite_master for this index
- */
- sqlite3NestedParse( pParse,
- "INSERT INTO %Q.%s VALUES('index',%Q,%Q,#%d,%Q);",
- db.aDb[iDb].zName, SCHEMA_TABLE( iDb ),
- pIndex.zName,
- pTab.zName,
- iMem,
- zStmt
- );
- sqlite3DbFree( db, ref zStmt );
- /* Fill the index with data and reparse the schema. Code an OP_Expire
- ** to invalidate all pre-compiled statements.
- */
- if ( pTblName != null )
- {
- sqlite3RefillIndex( pParse, pIndex, iMem );
- sqlite3ChangeCookie( pParse, iDb );
- sqlite3VdbeAddOp4( v, OP_ParseSchema, iDb, 0, 0,
- sqlite3MPrintf( db, "name='%q' AND type='index'", pIndex.zName ), P4_DYNAMIC );
- sqlite3VdbeAddOp1( v, OP_Expire, 0 );
- }
- }
- /* When adding an index to the list of indices for a table, make
- ** sure all indices labeled OE_Replace come after all those labeled
- ** OE_Ignore. This is necessary for the correct constraint check
- ** processing (in sqlite3GenerateConstraintChecks()) as part of
- ** UPDATE and INSERT statements.
- */
- if ( db.init.busy != 0 || pTblName == null )
- {
- if ( onError != OE_Replace || pTab.pIndex == null
- || pTab.pIndex.onError == OE_Replace )
- {
- pIndex.pNext = pTab.pIndex;
- pTab.pIndex = pIndex;
- }
- else
- {
- Index pOther = pTab.pIndex;
- while ( pOther.pNext != null && pOther.pNext.onError != OE_Replace )
- {
- pOther = pOther.pNext;
- }
- pIndex.pNext = pOther.pNext;
- pOther.pNext = pIndex;
- }
- pRet = pIndex;
- pIndex = null;
- }
- /* Clean up before exiting */
- exit_create_index:
- if ( pIndex != null )
- {
- //sqlite3DbFree(db, ref pIndex.zColAff );
- sqlite3DbFree( db, ref pIndex );
- }
- sqlite3ExprListDelete( db, ref pList );
- sqlite3SrcListDelete( db, ref pTblName );
- sqlite3DbFree( db, ref zName );
- return pRet;
- }
- /*
- ** Fill the Index.aiRowEst[] array with default information - information
- ** to be used when we have not run the ANALYZE command.
- **
- ** aiRowEst[0] is suppose to contain the number of elements in the index.
- ** Since we do not know, guess 1 million. aiRowEst[1] is an estimate of the
- ** number of rows in the table that match any particular value of the
- ** first column of the index. aiRowEst[2] is an estimate of the number
- ** of rows that match any particular combiniation of the first 2 columns
- ** of the index. And so forth. It must always be the case that
- *
- ** aiRowEst[N]<=aiRowEst[N-1]
- ** aiRowEst[N]>=1
- **
- ** Apart from that, we have little to go on besides intuition as to
- ** how aiRowEst[] should be initialized. The numbers generated here
- ** are based on typical values found in actual indices.
- */
- static void sqlite3DefaultRowEst( Index pIdx )
- {
- int[] a = pIdx.aiRowEst;
- int i;
- int n;
- Debug.Assert( a != null );
- a[0] = (int)pIdx.pTable.nRowEst;
- if ( a[0] < 10 )
- a[0] = 10;
- n = 10;
- for ( i = 1; i <= pIdx.nColumn; i++ )
- {
- a[i] = n;
- if ( n > 5 )
- n--;
- }
- if ( pIdx.onError != OE_None )
- {
- a[pIdx.nColumn] = 1;
- }
- }
- /*
- ** This routine will drop an existing named index. This routine
- ** implements the DROP INDEX statement.
- */
- static void sqlite3DropIndex( Parse pParse, SrcList pName, int ifExists )
- {
- Index pIndex;
- Vdbe v;
- sqlite3 db = pParse.db;
- int iDb;
- Debug.Assert( pParse.nErr == 0 ); /* Never called with prior errors */
- //if ( db.mallocFailed != 0 )
- //{
- // goto exit_drop_index;
- //}
- Debug.Assert( pName.nSrc == 1 );
- if ( SQLITE_OK != sqlite3ReadSchema( pParse ) )
- {
- goto exit_drop_index;
- }
- pIndex = sqlite3FindIndex( db, pName.a[0].zName, pName.a[0].zDatabase );
- if ( pIndex == null )
- {
- if ( ifExists == 0 )
- {
- sqlite3ErrorMsg( pParse, "no such index: %S", pName, 0 );
- }
- pParse.checkSchema = 1;
- goto exit_drop_index;
- }
- if ( pIndex.autoIndex != 0 )
- {
- sqlite3ErrorMsg( pParse, "index associated with UNIQUE " +
- "or PRIMARY KEY constraint cannot be dropped", 0 );
- goto exit_drop_index;
- }
- iDb = sqlite3SchemaToIndex( db, pIndex.pSchema );
- #if !SQLITE_OMIT_AUTHORIZATION
- {
- int code = SQLITE_DROP_INDEX;
- Table pTab = pIndex.pTable;
- string zDb = db.aDb[iDb].zName;
- string zTab = SCHEMA_TABLE(iDb);
- if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){
- goto exit_drop_index;
- }
- if( OMIT_TEMPDB ==0&& iDb ) code = SQLITE_DROP_TEMP_INDEX;
- if( sqlite3AuthCheck(pParse, code, pIndex.zName, pTab.zName, zDb) ){
- goto exit_drop_index;
- }
- }
- #endif
- /* Generate code to remove the index and from the master table */
- v = sqlite3GetVdbe( pParse );
- if ( v != null )
- {
- sqlite3BeginWriteOperation( pParse, 1, iDb );
- sqlite3NestedParse( pParse,
- "DELETE FROM %Q.%s WHERE name=%Q AND type='index'",
- db.aDb[iDb].zName, SCHEMA_TABLE( iDb ),
- pIndex.zName
- );
- if ( sqlite3FindTable( db, "sqlite_stat1", db.aDb[iDb].zName ) != null )
- {
- sqlite3NestedParse( pParse,
- "DELETE FROM %Q.sqlite_stat1 WHERE idx=%Q",
- db.aDb[iDb].zName, pIndex.zName
- );
- }
- sqlite3ChangeCookie( pParse, iDb );
- destroyRootPage( pParse, pIndex.tnum, iDb );
- sqlite3VdbeAddOp4( v, OP_DropIndex, iDb, 0, 0, pIndex.zName, 0 );
- }
- exit_drop_index:
- sqlite3SrcListDelete( db, ref pName );
- }
- /*
- ** pArray is a pointer to an array of objects. Each object in the
- ** array is szEntry bytes in size. This routine allocates a new
- ** object on the end of the array.
- **
- ** pnEntry is the number of entries already in use. pnAlloc is
- ** the previously allocated size of the array. initSize is the
- ** suggested initial array size allocation.
- **
- ** The index of the new entry is returned in pIdx.
- **
- ** This routine returns a pointer to the array of objects. This
- ** might be the same as the pArray parameter or it might be a different
- ** pointer if the array was resized.
- */
- static T[] sqlite3ArrayAllocate<T>(
- sqlite3 db, /* Connection to notify of malloc failures */
- T[] pArray, /* Array of objects. Might be reallocated */
- int szEntry, /* Size of each object in the array */
- int initSize, /* Suggested initial allocation, in elements */
- ref int pnEntry, /* Number of objects currently in use */
- ref int pnAlloc, /* Current size of the allocation, in elements */
- ref int pIdx /* Write the index of a new slot here */
- ) where T : new()
- {
- //char* z;
- if ( pnEntry >= pnAlloc )
- {
- //void* pNew;
- int newSize;
- newSize = ( pnAlloc ) * 2 + initSize;
- //pNew = sqlite3DbRealloc(db, pArray, newSize * szEntry);
- //if (pNew == 0)
- //{
- // pIdx = -1;
- // return pArray;
- //}
- pnAlloc = newSize; //sqlite3DbMallocSize(db, pNew)/szEntry;
- //pArray = pNew;
- Array.Resize( ref pArray, newSize );
- }
- pArray[pnEntry] = new T();
- //z = (char*)pArray;
- //memset(z[*pnEntry * szEntry], 0, szEntry);
- pIdx = pnEntry;
- ++pnEntry;
- return pArray;
- }
- /*
- ** Append a new element to the given IdList. Create a new IdList if
- ** need be.
- **
- ** A new IdList is returned, or NULL if malloc() fails.
- */
- // OVERLOADS, so I don't need to rewrite parse.c
- static IdList sqlite3IdListAppend( sqlite3 db, int null_2, Token pToken )
- {
- return sqlite3IdListAppend( db, null, pToken );
- }
- static IdList sqlite3IdListAppend( sqlite3 db, IdList pList, Token pToken )
- {
- int i = 0;
- if ( pList == null )
- {
- pList = new IdList();//sqlite3DbMallocZero(db, sizeof(IdList));
- if ( pList == null )
- return null;
- pList.nAlloc = 0;
- }
- pList.a = (IdList_item[])sqlite3ArrayAllocate(
- db,
- pList.a,
- -1,//sizeof(pList.a[0]),
- 5,
- ref pList.nId,
- ref pList.nAlloc,
- ref i
- );
- if ( i < 0 )
- {
- sqlite3IdListDelete( db, ref pList );
- return null;
- }
- pList.a[i].zName = sqlite3NameFromToken( db, pToken );
- return pList;
- }
- /*
- ** Delete an IdList.
- */
- static void sqlite3IdListDelete( sqlite3 db, ref IdList pList )
- {
- int i;
- if ( pList == null )
- return;
- for ( i = 0; i < pList.nId; i++ )
- {
- sqlite3DbFree( db, ref pList.a[i].zName );
- }
- sqlite3DbFree( db, ref pList.a );
- sqlite3DbFree( db, ref pList );
- }
- /*
- ** Return the index in pList of the identifier named zId. Return -1
- ** if not found.
- */
- static int sqlite3IdListIndex( IdList pList, string zName )
- {
- int i;
- if ( pList == null )
- return -1;
- for ( i = 0; i < pList.nId; i++ )
- {
- if ( pList.a[i].zName.Equals( zName, StringComparison.InvariantCultureIgnoreCase ) )
- return i;
- }
- return -1;
- }
- /*
- ** Expand the space allocated for the given SrcList object by
- ** creating nExtra new slots beginning at iStart. iStart is zero based.
- ** New slots are zeroed.
- **
- ** For example, suppose a SrcList initially contains two entries: A,B.
- ** To append 3 new entries onto the end, do this:
- **
- ** sqlite3SrcListEnlarge(db, pSrclist, 3, 2);
- **
- ** After the call above it would contain: A, B, nil, nil, nil.
- ** If the iStart argument had been 1 instead of 2, then the result
- ** would have been: A, nil, nil, nil, B. To prepend the new slots,
- ** the iStart value would be 0. The result then would
- ** be: nil, nil, nil, A, B.
- **
- ** If a memory allocation fails the SrcList is unchanged. The
- ** db.mallocFailed flag will be set to true.
- */
- static SrcList sqlite3SrcListEnlarge(
- sqlite3 db, /* Database connection to notify of OOM errors */
- SrcList pSrc, /* The SrcList to be enlarged */
- int nExtra, /* Number of new slots to add to pSrc.a[] */
- int iStart /* Index in pSrc.a[] of first new slot */
- )
- {
- int i;
- /* Sanity checking on calling parameters */
- Debug.Assert( iStart >= 0 );
- Debug.Assert( nExtra >= 1 );
- Debug.Assert( pSrc != null );
- Debug.Assert( iStart <= pSrc.nSrc );
- /* Allocate additional space if needed */
- if ( pSrc.nSrc + nExtra > pSrc.nAlloc )
- {
- int nAlloc = pSrc.nSrc + nExtra;
- int nGot;
- // sqlite3DbRealloc(db, pSrc,
- // sizeof(*pSrc) + (nAlloc-1)*sizeof(pSrc.a[0]) );
- pSrc.nAlloc = (i16)nAlloc;
- Array.Resize( ref pSrc.a, nAlloc );
- // nGot = (sqlite3DbMallocSize(db, pNew) - sizeof(*pSrc))/sizeof(pSrc->a[0])+1;
- //pSrc->nAlloc = (u16)nGot;
- }
- /* Move existing slots that come after the newly inserted slots
- ** out of the way */
- for ( i = pSrc.nSrc - 1; i >= iStart; i-- )
- {
- pSrc.a[i + nExtra] = pSrc.a[i];
- }
- pSrc.nSrc += (i16)nExtra;
- /* Zero the newly allocated slots */
- //memset(&pSrc.a[iStart], 0, sizeof(pSrc.a[0])*nExtra);
- for ( i = iStart; i < iStart + nExtra; i++ )
- {
- pSrc.a[i] = new SrcList_item();
- pSrc.a[i].iCursor = -1;
- }
- /* Return a pointer to the enlarged SrcList */
- return pSrc;
- }
- /*
- ** Append a new table name to the given SrcList. Create a new SrcList if
- ** need be. A new entry is created in the SrcList even if pTable is NULL.
- **
- ** A SrcList is returned, or NULL if there is an OOM error. The returned
- ** SrcList might be the same as the SrcList that was input or it might be
- ** a new one. If an OOM error does occurs, then the prior value of pList
- ** that is input to this routine is automatically freed.
- **
- ** If pDatabase is not null, it means that the table has an optional
- ** database name prefix. Like this: "database.table". The pDatabase
- ** points to the table name and the pTable points to the database name.
- ** The SrcList.a[].zName field is filled with the table name which might
- ** come from pTable (if pDatabase is NULL) or from pDatabase.
- ** SrcList.a[].zDatabase is filled with the database name from pTable,
- ** or with NULL if no database is specified.
- **
- ** In other words, if call like this:
- **
- ** sqlite3SrcListAppend(D,A,B,0);
- **
- ** Then B is a table name and the database name is unspecified. If called
- ** like this:
- **
- ** sqlite3SrcListAppend(D,A,B,C);
- **
- ** Then C is the table name and B is the database name. If C is defined
- ** then so is B. In other words, we never have a case where:
- **
- ** sqlite3SrcListAppend(D,A,0,C);
- **
- ** Both pTable and pDatabase are assumed to be quoted. They are dequoted
- ** before being added to the SrcList.
- */
- // OVERLOADS, so I don't need to rewrite parse.c
- static SrcList sqlite3SrcListAppend( sqlite3 db, int null_2, Token pTable, int null_4 )
- {
- return sqlite3SrcListAppend( db, null, pTable, null );
- }
- static SrcList sqlite3SrcListAppend( sqlite3 db, int null_2, Token pTable, Token pDatabase )
- {
- return sqlite3SrcListAppend( db, null, pTable, pDatabase );
- }
- static SrcList sqlite3SrcListAppend(
- sqlite3 db, /* Connection to notify of malloc failures */
- SrcList pList, /* Append to this SrcList. NULL creates a new SrcList */
- Token pTable, /* Table to append */
- Token pDatabase /* Database of the table */
- )
- {
- SrcList_item pItem;
- Debug.Assert( pDatabase == null || pTable != null ); /* Cannot have C without B */
- if ( pList == null )
- {
- pList = new SrcList();//sqlite3DbMallocZero(db, SrcList.Length );
- //if ( pList == null ) return null;
- pList.nAlloc = 1;
- pList.a = new SrcList_item[1];
- }
- pList = sqlite3SrcListEnlarge( db, pList, 1, pList.nSrc );
- //if ( db.mallocFailed != 0 )
- //{
- // sqlite3SrcListDelete( db, ref pList );
- // return null;
- //}
- pItem = pList.a[pList.nSrc - 1];
- if ( pDatabase != null && String.IsNullOrEmpty( pDatabase.z ) )
- {
- pDatabase = null;
- }
- if ( pDatabase != null )
- {
- Token pTemp = pDatabase;
- pDatabase = pTable;
- pTable = pTemp;
- }
- pItem.zName = sqlite3NameFromToken( db, pTable );
- pItem.zDatabase = sqlite3NameFromToken( db, pDatabase );
- return pList;
- }
- /*
- ** Assign VdbeCursor index numbers to all tables in a SrcList
- */
- static void sqlite3SrcListAssignCursors( Parse pParse, SrcList pList )
- {
- int i;
- SrcList_item pItem;
- Debug.Assert( pList != null /* || pParse.db.mallocFailed != 0 */ );
- if ( pList != null )
- {
- for ( i = 0; i < pList.nSrc; i++ )
- {
- pItem = pList.a[i];
- if ( pItem.iCursor >= 0 )
- break;
- pItem.iCursor = pParse.nTab++;
- if ( pItem.pSelect != null )
- {
- sqlite3SrcListAssignCursors( pParse, pItem.pSelect.pSrc );
- }
- }
- }
- }
- /*
- ** Delete an entire SrcList including all its substructure.
- */
- static void sqlite3SrcListDelete( sqlite3 db, ref SrcList pList )
- {
- int i;
- SrcList_item pItem;
- if ( pList == null )
- return;
- for ( i = 0; i < pList.nSrc; i++ )
- {//, pItem++){
- pItem = pList.a[i];
- sqlite3DbFree( db, ref pItem.zDatabase );
- sqlite3DbFree( db, ref pItem.zName );
- sqlite3DbFree( db, ref pItem.zAlias );
- sqlite3DbFree( db, ref pItem.zIndex );
- sqlite3DeleteTable( db, ref pItem.pTab );
- sqlite3SelectDelete( db, ref pItem.pSelect );
- sqlite3ExprDelete( db, ref pItem.pOn );
- sqlite3IdListDelete( db, ref pItem.pUsing );
- }
- sqlite3DbFree( db, ref pList );
- }
- /*
- ** This routine is called by the parser to add a new term to the
- ** end of a growing FROM clause. The "p" parameter is the part of
- ** the FROM clause that has already been constructed. "p" is NULL
- ** if this is the first term of the FROM clause. pTable and pDatabase
- ** are the name of the table and database named in the FROM clause term.
- ** pDatabase is NULL if the database name qualifier is missing - the
- ** usual case. If the term has a alias, then pAlias points to the
- ** alias token. If the term is a subquery, then pSubquery is the
- ** SELECT statement that the subquery encodes. The pTable and
- ** pDatabase parameters are NULL for subqueries. The pOn and pUsing
- ** parameters are the content of the ON and USING clauses.
- **
- ** Return a new SrcList which encodes is the FROM with the new
- ** term added.
- */
- // OVERLOADS, so I don't need to rewrite parse.c
- static SrcList sqlite3SrcListAppendFromTerm( Parse pParse, SrcList p, int null_3, int null_4, Token pAlias, Select pSubquery, Expr pOn, IdList pUsing )
- {
- return sqlite3SrcListAppendFromTerm( pParse, p, null, null, pAlias, pSubquery, pOn, pUsing );
- }
- static SrcList sqlite3SrcListAppendFromTerm( Parse pParse, SrcList p, Token pTable, Token pDatabase, Token pAlias, int null_6, Expr pOn, IdList pUsing )
- {
- return sqlite3SrcListAppendFromTerm( pParse, p, pTable, pDatabase, pAlias, null, pOn, pUsing );
- }
- static SrcList sqlite3SrcListAppendFromTerm(
- Parse pParse, /* Parsing context */
- SrcList p, /* The left part of the FROM clause already seen */
- Token pTable, /* Name of the table to add to the FROM clause */
- Token pDatabase, /* Name of the database containing pTable */
- Token pAlias, /* The right-hand side of the AS subexpression */
- Select pSubquery, /* A subquery used in place of a table name */
- Expr pOn, /* The ON clause of a join */
- IdList pUsing /* The USING clause of a join */
- )
- {
- SrcList_item pItem;
- sqlite3 db = pParse.db;
- if ( null == p && ( pOn != null || pUsing != null ) )
- {
- sqlite3ErrorMsg( pParse, "a JOIN clause is required before %s",
- ( pOn != null ? "ON" : "USING" )
- );
- goto append_from_error;
- }
- p = sqlite3SrcListAppend( db, p, pTable, pDatabase );
- //if ( p == null || NEVER( p.nSrc == 0 ) )
- //{
- // goto append_from_error;
- //}
- pItem = p.a[p.nSrc - 1];
- Debug.Assert( pAlias != null );
- if ( pAlias.n != 0 )
- {
- pItem.zAlias = sqlite3NameFromToken( db, pAlias );
- }
- pItem.pSelect = pSubquery;
- pItem.pOn = pOn;
- pItem.pUsing = pUsing;
- return p;
- append_from_error:
- Debug.Assert( p == null );
- sqlite3ExprDelete( db, ref pOn );
- sqlite3IdListDelete( db, ref pUsing );
- sqlite3SelectDelete( db, ref pSubquery );
- return null;
- }
- /*
- ** Add an INDEXED BY or NOT INDEXED clause to the most recently added
- ** element of the source-list passed as the second argument.
- */
- static void sqlite3SrcListIndexedBy( Parse pParse, SrcList p, Token pIndexedBy )
- {
- Debug.Assert( pIndexedBy != null );
- if ( p != null && ALWAYS( p.nSrc > 0 ) )
- {
- SrcList_item pItem = p.a[p.nSrc - 1];
- Debug.Assert( 0 == pItem.notIndexed && pItem.zIndex == null );
- if ( pIndexedBy.n == 1 && null == pIndexedBy.z )
- {
- /* A "NOT INDEXED" clause was supplied. See parse.y
- ** construct "indexed_opt" for details. */
- pItem.notIndexed = 1;
- }
- else
- {
- pItem.zIndex = sqlite3NameFromToken( pParse.db, pIndexedBy );
- }
- }
- }
- /*
- ** When building up a FROM clause in the parser, the join operator
- ** is initially attached to the left operand. But the code generator
- ** expects the join operator to be on the right operand. This routine
- ** Shifts all join operators from left to right for an entire FROM
- ** clause.
- **
- ** Example: Suppose the join is like this:
- **
- ** A natural cross join B
- **
- ** The operator is "natural cross join". The A and B operands are stored
- ** in p.a[0] and p.a[1], respectively. The parser initially stores the
- ** operator with A. This routine shifts that operator over to B.
- */
- static void sqlite3SrcListShiftJoinType( SrcList p )
- {
- if ( p != null && p.a != null )
- {
- int i;
- for ( i = p.nSrc - 1; i > 0; i-- )
- {
- p.a[i].jointype = p.a[i - 1].jointype;
- }
- p.a[0].jointype = 0;
- }
- }
- /*
- ** Begin a transaction
- */
- static void sqlite3BeginTransaction( Parse pParse, int type )
- {
- sqlite3 db;
- Vdbe v;
- int i;
- Debug.Assert( pParse != null );
- db = pParse.db;
- Debug.Assert( db != null );
- /* if( db.aDb[0].pBt==0 ) return; */
- if ( sqlite3AuthCheck( pParse, SQLITE_TRANSACTION, "BEGIN", null, null ) != 0 )
- {
- return;
- }
- v = sqlite3GetVdbe( pParse );
- if ( v == null )
- return;
- if ( type != TK_DEFERRED )
- {
- for ( i = 0; i < db.nDb; i++ )
- {
- sqlite3VdbeAddOp2( v, OP_Transaction, i, ( type == TK_EXCLUSIVE ) ? 2 : 1 );
- sqlite3VdbeUsesBtree( v, i );
- }
- }
- sqlite3VdbeAddOp2( v, OP_AutoCommit, 0, 0 );
- }
- /*
- ** Commit a transaction
- */
- static void sqlite3CommitTransaction( Parse pParse )
- {
- sqlite3 db;
- Vdbe v;
- Debug.Assert( pParse != null );
- db = pParse.db;
- Debug.Assert( db != null );
- /* if( db.aDb[0].pBt==0 ) return; */
- if ( sqlite3AuthCheck( pParse, SQLITE_TRANSACTION, "COMMIT", null, null ) != 0 )
- {
- return;
- }
- v = sqlite3GetVdbe( pParse );
- if ( v != null )
- {
- sqlite3VdbeAddOp2( v, OP_AutoCommit, 1, 0 );
- }
- }
- /*
- ** Rollback a transaction
- */
- static void sqlite3RollbackTransaction( Parse pParse )
- {
- sqlite3 db;
- Vdbe v;
- Debug.Assert( pParse != null );
- db = pParse.db;
- Debug.Assert( db != null );
- /* if( db.aDb[0].pBt==0 ) return; */
- if ( sqlite3AuthCheck( pParse, SQLITE_TRANSACTION, "ROLLBACK", null, null ) != 0 )
- {
- return;
- }
- v = sqlite3GetVdbe( pParse );
- if ( v != null )
- {
- sqlite3VdbeAddOp2( v, OP_AutoCommit, 1, 1 );
- }
- }
- /*
- ** This function is called by the parser when it parses a command to create,
- ** release or rollback an SQL savepoint.
- */
- #if !SQLITE_OMIT_AUTHORIZATION
- const string[] az = { "BEGIN", "RELEASE", "ROLLBACK" };
- #endif
- static void sqlite3Savepoint( Parse pParse, int op, Token pName )
- {
- string zName = sqlite3NameFromToken( pParse.db, pName );
- if ( zName != null )
- {
- Vdbe v = sqlite3GetVdbe( pParse );
- #if !SQLITE_OMIT_AUTHORIZATION
- Debug.Assert( !SAVEPOINT_BEGIN && SAVEPOINT_RELEASE==1 && SAVEPOINT_ROLLBACK==2 );
- #endif
- if ( null == v
- #if !SQLITE_OMIT_AUTHORIZATION
- || sqlite3AuthCheck(pParse, SQLITE_SAVEPOINT, az[op], zName, 0)
- #endif
- )
- {
- sqlite3DbFree( pParse.db, ref zName );
- return;
- }
- sqlite3VdbeAddOp4( v, OP_Savepoint, op, 0, 0, zName, P4_DYNAMIC );
- }
- }
- /*
- ** Make sure the TEMP database is open and available for use. Return
- ** the number of errors. Leave any error messages in the pParse structure.
- */
- static int sqlite3OpenTempDatabase( Parse pParse )
- {
- sqlite3 db = pParse.db;
- if ( db.aDb[1].pBt == null && pParse.explain == 0 )
- {
- int rc;
- Btree pBt = null;
- const int flags =
- SQLITE_OPEN_READWRITE |
- SQLITE_OPEN_CREATE |
- SQLITE_OPEN_EXCLUSIVE |
- SQLITE_OPEN_DELETEONCLOSE |
- SQLITE_OPEN_TEMP_DB;
- rc = sqlite3BtreeOpen( null, db, ref pBt, 0, flags );
- if ( rc != SQLITE_OK )
- {
- sqlite3ErrorMsg( pParse, "unable to open a temporary database " +
- "file for storing temporary tables" );
- pParse.rc = rc;
- return 1;
- }
- db.aDb[1].pBt = pBt;
- Debug.Assert( db.aDb[1].pSchema != null );
- if ( SQLITE_NOMEM == sqlite3BtreeSetPageSize( pBt, db.nextPagesize, -1, 0 ) )
- {
- // db.mallocFailed = 1;
- }
- }
- return 0;
- }
- /*
- ** Generate VDBE code that will verify the schema cookie and start
- ** a read-transaction for all named database files.
- **
- ** It is important that all schema cookies be verified and all
- ** read transactions be started before anything else happens in
- ** the VDBE program. But this routine can be called after much other
- ** code has been generated. So here is what we do:
- **
- ** The first time this routine is called, we code an OP_Goto that
- ** will jump to a subroutine at the end of the program. Then we
- ** record every database that needs its schema verified in the
- ** pParse.cookieMask field. Later, after all other code has been
- ** generated, the subroutine that does the cookie verifications and
- ** starts the transactions will be coded and the OP_Goto P2 value
- ** will be made to point to that subroutine. The generation of the
- ** cookie verification subroutine code happens in sqlite3FinishCoding().
- **
- ** If iDb<0 then code the OP_Goto only - don't set flag to verify the
- ** schema on any databases. This can be used to position the OP_Goto
- ** early in the code, before we know if any database tables will be used.
- */
- static void sqlite3CodeVerifySchema( Parse pParse, int iDb )
- {
- Parse pToplevel = sqlite3ParseToplevel( pParse );
- if ( pToplevel.cookieGoto == 0 )
- {
- Vdbe v = sqlite3GetVdbe( pToplevel );
- if ( v == null )
- return; /* This only happens if there was a prior error */
- pToplevel.cookieGoto = sqlite3VdbeAddOp2( v, OP_Goto, 0, 0 ) + 1;
- }
- if ( iDb >= 0 )
- {
- sqlite3 db = pToplevel.db;
- int mask;
- Debug.Assert( iDb < db.nDb );
- Debug.Assert( db.aDb[iDb].pBt != null || iDb == 1 );
- Debug.Assert( iDb < SQLITE_MAX_ATTACHED + 2 );
- mask = (int)( 1 << iDb );
- if ( ( pToplevel.cookieMask & mask ) == 0 )
- {
- pToplevel.cookieMask |= (u32)mask;
- pToplevel.cookieValue[iDb] = db.aDb[iDb].pSchema.schema_cookie;
- if ( 0 == OMIT_TEMPDB && iDb == 1 )
- {
- sqlite3OpenTempDatabase( pToplevel );
- }
- }
- }
- }
- /*
- ** Generate VDBE code that prepares for doing an operation that
- ** might change the database.
- **
- ** This routine starts a new transaction if we are not already within
- ** a transaction. If we are already within a transaction, then a checkpoint
- ** is set if the setStatement parameter is true. A checkpoint should
- ** be set for operations that might fail (due to a constraint) part of
- ** the way through and which will need to undo some writes without having to
- ** rollback the whole transaction. For operations where all constraints
- ** can be checked before any changes are made to the database, it is never
- ** necessary to undo a write and the checkpoint should not be set.
- */
- static void sqlite3BeginWriteOperation( Parse pParse, int setStatement, int iDb )
- {
- Parse pToplevel = sqlite3ParseToplevel( pParse );
- sqlite3CodeVerifySchema( pParse, iDb );
- pToplevel.writeMask |= (u32)( 1 << iDb );
- pToplevel.isMultiWrite |= (u8)setStatement;
- }
- /*
- ** Indicate that the statement currently under construction might write
- ** more than one entry (example: deleting one row then inserting another,
- ** inserting multiple rows in a table, or inserting a row and index entries.)
- ** If an abort occurs after some of these writes have completed, then it will
- ** be necessary to undo the completed writes.
- */
- static void sqlite3MultiWrite( Parse pParse )
- {
- Parse pToplevel = sqlite3ParseToplevel( pParse );
- pToplevel.isMultiWrite = 1;
- }
- /*
- ** The code generator calls this routine if is discovers that it is
- ** possible to abort a statement prior to completion. In order to
- ** perform this abort without corrupting the database, we need to make
- ** sure that the statement is protected by a statement transaction.
- **
- ** Technically, we only need to set the mayAbort flag if the
- ** isMultiWrite flag was previously set. There is a time dependency
- ** such that the abort must occur after the multiwrite. This makes
- ** some statements involving the REPLACE conflict resolution algorithm
- ** go a little faster. But taking advantage of this time dependency
- ** makes it more difficult to prove that the code is correct (in
- ** particular, it prevents us from writing an effective
- ** implementation of sqlite3AssertMayAbort()) and so we have chosen
- ** to take the safe route and skip the optimization.
- */
- static void sqlite3MayAbort( Parse pParse )
- {
- Parse pToplevel = sqlite3ParseToplevel( pParse );
- pToplevel.mayAbort = 1;
- }
- /*
- ** Code an OP_Halt that causes the vdbe to return an SQLITE_CONSTRAINT
- ** error. The onError parameter determines which (if any) of the statement
- ** and/or current transaction is rolled back.
- */
- static void sqlite3HaltConstraint( Parse pParse, int onError, string p4, int p4type )
- {
- Vdbe v = sqlite3GetVdbe( pParse );
- if ( onError == OE_Abort )
- {
- sqlite3MayAbort( pParse );
- }
- sqlite3VdbeAddOp4( v, OP_Halt, SQLITE_CONSTRAINT, onError, 0, p4, p4type );
- }
- static void sqlite3HaltConstraint( Parse pParse, int onError, byte[] p4, int p4type )
- {
- Vdbe v = sqlite3GetVdbe( pParse );
- if ( onError == OE_Abort )
- {
- sqlite3MayAbort( pParse );
- }
- sqlite3VdbeAddOp4( v, OP_Halt, SQLITE_CONSTRAINT, onError, 0, p4, p4type );
- }
- /*
- ** Check to see if pIndex uses the collating sequence pColl. Return
- ** true if it does and false if it does not.
- */
- #if !SQLITE_OMIT_REINDEX
- static bool collationMatch( string zColl, Index pIndex )
- {
- int i;
- Debug.Assert( zColl != null );
- for ( i = 0; i < pIndex.nColumn; i++ )
- {
- string z = pIndex.azColl[i];
- Debug.Assert( z != null );
- if ( z.Equals( zColl, StringComparison.InvariantCultureIgnoreCase ) )
- {
- return true;
- }
- }
- return false;
- }
- #endif
- /*
- ** Recompute all indices of pTab that use the collating sequence pColl.
- ** If pColl == null then recompute all indices of pTab.
- */
- #if !SQLITE_OMIT_REINDEX
- static void reindexTable( Parse pParse, Table pTab, string zColl )
- {
- Index pIndex; /* An index associated with pTab */
- for ( pIndex = pTab.pIndex; pIndex != null; pIndex = pIndex.pNext )
- {
- if ( zColl == null || collationMatch( zColl, pIndex ) )
- {
- int iDb = sqlite3SchemaToIndex( pParse.db, pTab.pSchema );
- sqlite3BeginWriteOperation( pParse, 0, iDb );
- sqlite3RefillIndex( pParse, pIndex, -1 );
- }
- }
- }
- #endif
- /*
- ** Recompute all indices of all tables in all databases where the
- ** indices use the collating sequence pColl. If pColl == null then recompute
- ** all indices everywhere.
- */
- #if !SQLITE_OMIT_REINDEX
- static void reindexDatabases( Parse pParse, string zColl )
- {
- Db pDb; /* A single database */
- int iDb; /* The database index number */
- sqlite3 db = pParse.db; /* The database connection */
- HashElem k; /* For looping over tables in pDb */
- Table pTab; /* A table in the database */
- for ( iDb = 0; iDb < db.nDb; iDb++ )//, pDb++ )
- {
- pDb = db.aDb[iDb];
- Debug.Assert( pDb != null );
- for ( k = pDb.pSchema.tblHash.first; k != null; k = k.next ) //for ( k = sqliteHashFirst( pDb.pSchema.tblHash ) ; k != null ; k = sqliteHashNext( k ) )
- {
- pTab = (Table)k.data;// sqliteHashData( k );
- reindexTable( pParse, pTab, zColl );
- }
- }
- }
- #endif
- /*
- ** Generate code for the REINDEX command.
- **
- ** REINDEX -- 1
- ** REINDEX <collation> -- 2
- ** REINDEX ?<database>.?<tablename> -- 3
- ** REINDEX ?<database>.?<indexname> -- 4
- **
- ** Form 1 causes all indices in all attached databases to be rebuilt.
- ** Form 2 rebuilds all indices in all databases that use the named
- ** collating function. Forms 3 and 4 rebuild the named index or all
- ** indices associated with the named table.
- */
- #if !SQLITE_OMIT_REINDEX
- // OVERLOADS, so I don't need to rewrite parse.c
- static void sqlite3Reindex( Parse pParse, int null_2, int null_3 )
- {
- sqlite3Reindex( pParse, null, null );
- }
- static void sqlite3Reindex( Parse pParse, Token pName1, Token pName2 )
- {
- CollSeq pColl; /* Collating sequence to be reindexed, or NULL */
- string z; /* Name of a table or index */
- string zDb; /* Name of the database */
- Table pTab; /* A table in the database */
- Index pIndex; /* An index associated with pTab */
- int iDb; /* The database index number */
- sqlite3 db = pParse.db; /* The database connection */
- Token pObjName = new Token(); /* Name of the table or index to be reindexed */
- /* Read the database schema. If an error occurs, leave an error message
- ** and code in pParse and return NULL. */
- if ( SQLITE_OK != sqlite3ReadSchema( pParse ) )
- {
- return;
- }
- if ( pName1 == null )
- {
- reindexDatabases( pParse, null );
- return;
- }
- else if ( NEVER( pName2 == null ) || pName2.z == null || pName2.z.Length == 0 )
- {
- string zColl;
- Debug.Assert( pName1.z != null );
- zColl = sqlite3NameFromToken( pParse.db, pName1 );
- if ( zColl == null )
- return;
- pColl = sqlite3FindCollSeq( db, ENC( db ), zColl, 0 );
- if ( pColl != null )
- {
- reindexDatabases( pParse, zColl );
- sqlite3DbFree( db, ref zColl );
- return;
- }
- sqlite3DbFree( db, ref zColl );
- }
- iDb = sqlite3TwoPartName( pParse, pName1, pName2, ref pObjName );
- if ( iDb < 0 )
- return;
- z = sqlite3NameFromToken( db, pObjName );
- if ( z == null )
- return;
- zDb = db.aDb[iDb].zName;
- pTab = sqlite3FindTable( db, z, zDb );
- if ( pTab != null )
- {
- reindexTable( pParse, pTab, null );
- sqlite3DbFree( db, ref z );
- return;
- }
- pIndex = sqlite3FindIndex( db, z, zDb );
- sqlite3DbFree( db, ref z );
- if ( pIndex != null )
- {
- sqlite3BeginWriteOperation( pParse, 0, iDb );
- sqlite3RefillIndex( pParse, pIndex, -1 );
- return;
- }
- sqlite3ErrorMsg( pParse, "unable to identify the object to be reindexed" );
- }
- #endif
- /*
- ** Return a dynamicly allocated KeyInfo structure that can be used
- ** with OP_OpenRead or OP_OpenWrite to access database index pIdx.
- **
- ** If successful, a pointer to the new structure is returned. In this case
- ** the caller is responsible for calling sqlite3DbFree(db, ) on the returned
- ** pointer. If an error occurs (out of memory or missing collation
- ** sequence), NULL is returned and the state of pParse updated to reflect
- ** the error.
- */
- static KeyInfo sqlite3IndexKeyinfo( Parse pParse, Index pIdx )
- {
- int i;
- int nCol = pIdx.nColumn;
- //int nBytes = KeyInfo.Length + (nCol - 1) * CollSeq*.Length + nCol;
- sqlite3 db = pParse.db;
- KeyInfo pKey = new KeyInfo();// (KeyInfo*)sqlite3DbMallocZero(db, nBytes);
- if ( pKey != null )
- {
- pKey.db = pParse.db;
- pKey.aSortOrder = new byte[nCol];
- pKey.aColl = new CollSeq[nCol];// (u8*)&(pKey.aColl[nCol]);
- // Debug.Assert(pKey.aSortOrder[nCol] == &(((u8*)pKey)[nBytes]));
- for ( i = 0; i < nCol; i++ )
- {
- string zColl = pIdx.azColl[i];
- Debug.Assert( zColl != null );
- pKey.aColl[i] = sqlite3LocateCollSeq( pParse, zColl );
- pKey.aSortOrder[i] = pIdx.aSortOrder[i];
- }
- pKey.nField = (u16)nCol;
- }
- if ( pParse.nErr != 0 )
- {
- pKey = null;
- sqlite3DbFree( db, ref pKey );
- }
- return pKey;
- }
- }
- }