summaryrefslogtreecommitdiff
path: root/ext/sqlite/libsqlite/src/build.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/sqlite/libsqlite/src/build.c')
-rw-r--r--ext/sqlite/libsqlite/src/build.c1219
1 files changed, 429 insertions, 790 deletions
diff --git a/ext/sqlite/libsqlite/src/build.c b/ext/sqlite/libsqlite/src/build.c
index afa3f16025..76077ac943 100644
--- a/ext/sqlite/libsqlite/src/build.c
+++ b/ext/sqlite/libsqlite/src/build.c
@@ -18,8 +18,6 @@
** CREATE INDEX
** DROP INDEX
** creating ID lists
-** COPY
-** VACUUM
** BEGIN TRANSACTION
** COMMIT
** ROLLBACK
@@ -38,6 +36,7 @@
*/
void sqliteBeginParse(Parse *pParse, int explainFlag){
sqlite *db = pParse->db;
+ int i;
pParse->explain = explainFlag;
if((db->flags & SQLITE_Initialized)==0 && pParse->initFlag==0 ){
int rc = sqliteInit(db, &pParse->zErrMsg);
@@ -46,6 +45,12 @@ void sqliteBeginParse(Parse *pParse, int explainFlag){
pParse->nErr++;
}
}
+ for(i=0; i<db->nDb; i++){
+ DbClearProperty(db, i, DB_Locked);
+ if( !db->aDb[i].inTrans ){
+ DbClearProperty(db, i, DB_Cookie);
+ }
+ }
}
/*
@@ -85,7 +90,7 @@ void sqliteExec(Parse *pParse){
if( pParse->useCallback ){
if( pParse->explain ){
rc = sqliteVdbeList(v);
- db->next_cookie = db->schema_cookie;
+ db->next_cookie = db->aDb[0].schema_cookie;
}else{
sqliteVdbeExec(v);
}
@@ -98,7 +103,6 @@ void sqliteExec(Parse *pParse){
pParse->rc = pParse->nErr ? SQLITE_ERROR : SQLITE_DONE;
}
pParse->colNamesSet = 0;
- pParse->schemaVerified = 0;
}else if( pParse->useCallback==0 ){
pParse->rc = SQLITE_ERROR;
}
@@ -111,22 +115,82 @@ void sqliteExec(Parse *pParse){
/*
** Locate the in-memory structure that describes
** a particular database table given the name
-** of that table. Return NULL if not found.
+** of that table and (optionally) the name of the database
+** containing the table. Return NULL if not found.
+**
+** See also sqliteLocateTable().
*/
-Table *sqliteFindTable(sqlite *db, const char *zName){
+Table *sqliteFindTable(sqlite *db, const char *zName, const char *zDatabase){
+ Table *p = 0;
+ int i;
+ for(i=0; i<db->nDb; i++){
+ int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
+ if( zDatabase!=0 && sqliteStrICmp(zDatabase, db->aDb[j].zName) ) continue;
+ p = sqliteHashFind(&db->aDb[j].tblHash, zName, strlen(zName)+1);
+ if( p ) 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.
+**
+** If pParse->useDb is not negative, then the table must be
+** located in that database. If a different database is specified,
+** an error message is generated into pParse->zErrMsg.
+*/
+Table *sqliteLocateTable(Parse *pParse, const char *zName, const char *zDbase){
+ sqlite *db;
+ const char *zUse;
Table *p;
- p = sqliteHashFind(&db->tblHash, zName, strlen(zName)+1);
+ db = pParse->db;
+ if( pParse->useDb<0 ){
+ p = sqliteFindTable(db, zName, zDbase);
+ }else {
+ assert( pParse->useDb<db->nDb );
+ assert( db->aDb[pParse->useDb].pBt!=0 );
+ zUse = db->aDb[pParse->useDb].zName;
+ if( zDbase && pParse->useDb!=1 && sqliteStrICmp(zDbase, zUse)!=0 ){
+ sqliteErrorMsg(pParse,"cannot use database %s in this context", zDbase);
+ return 0;
+ }
+ p = sqliteFindTable(db, zName, zUse);
+ if( p==0 && pParse->useDb==1 && zDbase==0 ){
+ p = sqliteFindTable(db, zName, 0);
+ }
+ }
+ if( p==0 ){
+ if( zDbase ){
+ sqliteErrorMsg(pParse, "no such table: %s.%s", zDbase, zName);
+ }else if( (pParse->useDb==0 || pParse->useDb>=2)
+ && sqliteFindTable(db, zName, 0)!=0 ){
+ sqliteErrorMsg(pParse, "table \"%s\" is not in database \"%s\"",
+ zName, zUse);
+ }else{
+ sqliteErrorMsg(pParse, "no such table: %s", zName);
+ }
+ }
return p;
}
/*
** Locate the in-memory structure that describes
-** a particular index given the name of that index.
+** a particular index given the name of that index
+** and the name of the database that contains the index.
** Return NULL if not found.
*/
-Index *sqliteFindIndex(sqlite *db, const char *zName){
- Index *p;
- p = sqliteHashFind(&db->idxHash, zName, strlen(zName)+1);
+Index *sqliteFindIndex(sqlite *db, const char *zName, const char *zDb){
+ Index *p = 0;
+ int i;
+ for(i=0; i<db->nDb; i++){
+ int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
+ if( zDb && sqliteStrICmp(zDb, db->aDb[j].zName) ) continue;
+ p = sqliteHashFind(&db->aDb[j].idxHash, zName, strlen(zName)+1);
+ if( p ) break;
+ }
return p;
}
@@ -140,10 +204,13 @@ Index *sqliteFindIndex(sqlite *db, const char *zName){
*/
static void sqliteDeleteIndex(sqlite *db, Index *p){
Index *pOld;
+
assert( db!=0 && p->zName!=0 );
- pOld = sqliteHashInsert(&db->idxHash, p->zName, strlen(p->zName)+1, 0);
+ pOld = sqliteHashInsert(&db->aDb[p->iDb].idxHash, p->zName,
+ strlen(p->zName)+1, 0);
if( pOld!=0 && pOld!=p ){
- sqliteHashInsert(&db->idxHash, pOld->zName, strlen(pOld->zName)+1, pOld);
+ sqliteHashInsert(&db->aDb[p->iDb].idxHash, pOld->zName,
+ strlen(pOld->zName)+1, pOld);
}
sqliteFree(p);
}
@@ -171,29 +238,67 @@ void sqliteUnlinkAndDeleteIndex(sqlite *db, Index *pIndex){
** database connection. This routine is called to reclaim memory
** before the connection closes. It is also called during a rollback
** if there were schema changes during the transaction.
+**
+** If iDb<=0 then reset the internal schema tables for all database
+** files. If iDb>=2 then reset the internal schema for only the
+** single file indicates.
*/
-void sqliteResetInternalSchema(sqlite *db){
+void sqliteResetInternalSchema(sqlite *db, int iDb){
HashElem *pElem;
Hash temp1;
Hash temp2;
+ int i, j;
+
+ assert( iDb>=0 && iDb<db->nDb );
+ db->flags &= ~SQLITE_Initialized;
+ for(i=iDb; i<db->nDb; i++){
+ Db *pDb = &db->aDb[i];
+ temp1 = pDb->tblHash;
+ temp2 = pDb->trigHash;
+ sqliteHashInit(&pDb->trigHash, SQLITE_HASH_STRING, 0);
+ sqliteHashClear(&pDb->aFKey);
+ sqliteHashClear(&pDb->idxHash);
+ for(pElem=sqliteHashFirst(&temp2); pElem; pElem=sqliteHashNext(pElem)){
+ Trigger *pTrigger = sqliteHashData(pElem);
+ sqliteDeleteTrigger(pTrigger);
+ }
+ sqliteHashClear(&temp2);
+ sqliteHashInit(&pDb->tblHash, SQLITE_HASH_STRING, 0);
+ for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){
+ Table *pTab = sqliteHashData(pElem);
+ sqliteDeleteTable(db, pTab);
+ }
+ sqliteHashClear(&temp1);
+ DbClearProperty(db, i, DB_SchemaLoaded);
+ if( iDb>0 ) return;
+ }
+ assert( iDb==0 );
+ db->flags &= ~SQLITE_InternChanges;
- sqliteHashClear(&db->aFKey);
- temp1 = db->tblHash;
- temp2 = db->trigHash;
- sqliteHashInit(&db->trigHash, SQLITE_HASH_STRING, 0);
- sqliteHashClear(&db->idxHash);
- for(pElem=sqliteHashFirst(&temp2); pElem; pElem=sqliteHashNext(pElem)){
- Trigger *pTrigger = sqliteHashData(pElem);
- sqliteDeleteTrigger(pTrigger);
- }
- sqliteHashClear(&temp2);
- sqliteHashInit(&db->tblHash, SQLITE_HASH_STRING, 0);
- for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){
- Table *pTab = sqliteHashData(pElem);
- sqliteDeleteTable(db, pTab);
- }
- sqliteHashClear(&temp1);
- db->flags &= ~(SQLITE_Initialized|SQLITE_InternChanges);
+ /* If one or more of the auxiliary database files has been closed,
+ ** then remove then 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++){
+ if( db->aDb[i].pBt==0 ){
+ sqliteFree(db->aDb[i].zName);
+ db->aDb[i].zName = 0;
+ continue;
+ }
+ if( j<i ){
+ db->aDb[j] = db->aDb[i];
+ }
+ j++;
+ }
+ memset(&db->aDb[j], 0, (db->nDb-j)*sizeof(db->aDb[j]));
+ db->nDb = j;
+ if( db->nDb<=2 && db->aDb!=db->aDbStatic ){
+ memcpy(db->aDbStatic, db->aDb, 2*sizeof(db->aDb[0]));
+ sqliteFree(db->aDb);
+ db->aDb = db->aDbStatic;
+ }
}
/*
@@ -203,7 +308,7 @@ void sqliteResetInternalSchema(sqlite *db){
*/
void sqliteRollbackInternalChanges(sqlite *db){
if( db->flags & SQLITE_InternChanges ){
- sqliteResetInternalSchema(db);
+ sqliteResetInternalSchema(db, 0);
}
}
@@ -211,7 +316,7 @@ void sqliteRollbackInternalChanges(sqlite *db){
** This routine is called when a commit occurs.
*/
void sqliteCommitInternalChanges(sqlite *db){
- db->schema_cookie = db->next_cookie;
+ db->aDb[0].schema_cookie = db->next_cookie;
db->flags &= ~SQLITE_InternChanges;
}
@@ -241,6 +346,7 @@ void sqliteDeleteTable(sqlite *db, Table *pTable){
*/
for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){
pNext = pIndex->pNext;
+ assert( pIndex->iDb==pTable->iDb || (pTable->iDb==0 && pIndex->iDb==1) );
sqliteDeleteIndex(db, pIndex);
}
@@ -249,7 +355,9 @@ void sqliteDeleteTable(sqlite *db, Table *pTable){
*/
for(pFKey=pTable->pFKey; pFKey; pFKey=pNextFKey){
pNextFKey = pFKey->pNextFrom;
- assert( sqliteHashFind(&db->aFKey,pFKey->zTo,strlen(pFKey->zTo)+1)!=pFKey );
+ assert( pTable->iDb<db->nDb );
+ assert( sqliteHashFind(&db->aDb[pTable->iDb].aFKey,
+ pFKey->zTo, strlen(pFKey->zTo)+1)!=pFKey );
sqliteFree(pFKey);
}
@@ -273,14 +381,15 @@ void sqliteDeleteTable(sqlite *db, Table *pTable){
static void sqliteUnlinkAndDeleteTable(sqlite *db, Table *p){
Table *pOld;
FKey *pF1, *pF2;
+ int i = p->iDb;
assert( db!=0 );
- pOld = sqliteHashInsert(&db->tblHash, p->zName, strlen(p->zName)+1, 0);
+ pOld = sqliteHashInsert(&db->aDb[i].tblHash, p->zName, strlen(p->zName)+1, 0);
assert( pOld==0 || pOld==p );
for(pF1=p->pFKey; pF1; pF1=pF1->pNextFrom){
int nTo = strlen(pF1->zTo) + 1;
- pF2 = sqliteHashFind(&db->aFKey, pF1->zTo, nTo);
+ pF2 = sqliteHashFind(&db->aDb[i].aFKey, pF1->zTo, nTo);
if( pF2==pF1 ){
- sqliteHashInsert(&db->aFKey, pF1->zTo, nTo, pF1->pNextTo);
+ sqliteHashInsert(&db->aDb[i].aFKey, pF1->zTo, nTo, pF1->pNextTo);
}else{
while( pF2 && pF2->pNextTo!=pF1 ){ pF2=pF2->pNextTo; }
if( pF2 ){
@@ -310,13 +419,8 @@ char *sqliteTableNameFromToken(Token *pName){
** on cursor 0.
*/
void sqliteOpenMasterTable(Vdbe *v, int isTemp){
- if( isTemp ){
- sqliteVdbeAddOp(v, OP_OpenWrAux, 0, 2);
- sqliteVdbeChangeP3(v, -1, TEMP_MASTER_NAME, P3_STATIC);
- }else{
- sqliteVdbeAddOp(v, OP_OpenWrite, 0, 2);
- sqliteVdbeChangeP3(v, -1, MASTER_NAME, P3_STATIC);
- }
+ sqliteVdbeAddOp(v, OP_Integer, isTemp, 0);
+ sqliteVdbeAddOp(v, OP_OpenWrite, 0, 2);
}
/*
@@ -348,17 +452,21 @@ void sqliteStartTable(
char *zName;
sqlite *db = pParse->db;
Vdbe *v;
+ int iDb;
pParse->sFirstToken = *pStart;
zName = sqliteTableNameFromToken(pName);
if( zName==0 ) return;
+ if( pParse->iDb==1 ) isTemp = 1;
#ifndef SQLITE_OMIT_AUTHORIZATION
- if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0) ){
- sqliteFree(zName);
- return;
- }
+ assert( (isTemp & 1)==isTemp );
{
int code;
+ char *zDb = isTemp ? "temp" : "main";
+ if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0, zDb) ){
+ sqliteFree(zName);
+ return;
+ }
if( isView ){
if( isTemp ){
code = SQLITE_CREATE_TEMP_VIEW;
@@ -372,7 +480,7 @@ void sqliteStartTable(
code = SQLITE_CREATE_TABLE;
}
}
- if( sqliteAuthCheck(pParse, code, zName, 0) ){
+ if( sqliteAuthCheck(pParse, code, zName, 0, zDb) ){
sqliteFree(zName);
return;
}
@@ -383,8 +491,8 @@ void sqliteStartTable(
/* Before trying to create a temporary table, make sure the Btree for
** holding temporary tables is open.
*/
- if( isTemp && db->pBeTemp==0 ){
- int rc = sqliteBtreeOpen(0, 0, MAX_PAGES, &db->pBeTemp);
+ if( isTemp && db->aDb[1].pBt==0 && !pParse->explain ){
+ int rc = sqliteBtreeFactory(db, 0, 0, MAX_PAGES, &db->aDb[1].pBt);
if( rc!=SQLITE_OK ){
sqliteSetString(&pParse->zErrMsg, "unable to open a temporary database "
"file for storing temporary tables", 0);
@@ -392,7 +500,7 @@ void sqliteStartTable(
return;
}
if( db->flags & SQLITE_InTrans ){
- rc = sqliteBtreeBeginTrans(db->pBeTemp);
+ rc = sqliteBtreeBeginTrans(db->aDb[1].pBt);
if( rc!=SQLITE_OK ){
sqliteSetNString(&pParse->zErrMsg, "unable to get a write lock on "
"the temporary database file", 0);
@@ -407,26 +515,19 @@ void sqliteStartTable(
**
** If we are re-reading the sqlite_master table because of a schema
** change and a new permanent table is found whose name collides with
- ** an existing temporary table, then ignore the new permanent table.
- ** We will continue parsing, but the pParse->nameClash flag will be set
- ** so we will know to discard the table record once parsing has finished.
+ ** an existing temporary table, that is not an error.
*/
- pTable = sqliteFindTable(db, zName);
- if( pTable!=0 ){
- if( pTable->isTemp && pParse->initFlag ){
- pParse->nameClash = 1;
- }else{
- sqliteSetNString(&pParse->zErrMsg, "table ", 0, pName->z, pName->n,
- " already exists", 0, 0);
- sqliteFree(zName);
- pParse->nErr++;
- return;
- }
- }else{
- pParse->nameClash = 0;
+ pTable = sqliteFindTable(db, zName, 0);
+ iDb = isTemp ? 1 : pParse->iDb;
+ if( pTable!=0 && (pTable->iDb==iDb || !pParse->initFlag) ){
+ sqliteSetNString(&pParse->zErrMsg, "table ", 0, pName->z, pName->n,
+ " already exists", 0, 0);
+ sqliteFree(zName);
+ pParse->nErr++;
+ return;
}
- if( (pIdx = sqliteFindIndex(db, zName))!=0 &&
- (!pIdx->pTable->isTemp || !pParse->initFlag) ){
+ if( (pIdx = sqliteFindIndex(db, zName, 0))!=0 &&
+ (pIdx->iDb==0 || !pParse->initFlag) ){
sqliteSetString(&pParse->zErrMsg, "there is already an index named ",
zName, 0);
sqliteFree(zName);
@@ -443,7 +544,7 @@ void sqliteStartTable(
pTable->aCol = 0;
pTable->iPKey = -1;
pTable->pIndex = 0;
- pTable->isTemp = isTemp;
+ pTable->iDb = iDb;
if( pParse->pNewTable ) sqliteDeleteTable(db, pParse->pNewTable);
pParse->pNewTable = pTable;
@@ -606,12 +707,12 @@ void sqliteAddPrimaryKey(Parse *pParse, IdList *pList, int onError){
Table *pTab = pParse->pNewTable;
char *zType = 0;
int iCol = -1;
- if( pTab==0 ) return;
+ if( pTab==0 ) goto primary_key_exit;
if( pTab->hasPrimKey ){
sqliteSetString(&pParse->zErrMsg, "table \"", pTab->zName,
"\" has more than one primary key", 0);
pParse->nErr++;
- return;
+ goto primary_key_exit;
}
pTab->hasPrimKey = 1;
if( pList==0 ){
@@ -629,8 +730,13 @@ void sqliteAddPrimaryKey(Parse *pParse, IdList *pList, int onError){
pTab->iPKey = iCol;
pTab->keyConf = onError;
}else{
- sqliteCreateIndex(pParse, 0, 0, pList, onError, 0, 0);
+ sqliteCreateIndex(pParse, 0, 0, pList, onError, 0, 0, 0);
+ pList = 0;
}
+
+primary_key_exit:
+ sqliteIdListDelete(pList);
+ return;
}
/*
@@ -708,8 +814,8 @@ void sqliteAddCollateType(Parse *pParse, int collType){
** 1 chance in 2^32. So we're safe enough.
*/
void sqliteChangeCookie(sqlite *db, Vdbe *v){
- if( db->next_cookie==db->schema_cookie ){
- db->next_cookie = db->schema_cookie + sqliteRandomByte() + 1;
+ if( db->next_cookie==db->aDb[0].schema_cookie ){
+ db->next_cookie = db->aDb[0].schema_cookie + sqliteRandomByte() + 1;
db->flags |= SQLITE_InternChanges;
sqliteVdbeAddOp(v, OP_Integer, db->next_cookie, 0);
sqliteVdbeAddOp(v, OP_SetCookie, 0, 0);
@@ -778,7 +884,7 @@ static char *createTableStmt(Table *p){
n += 35 + 6*p->nCol;
zStmt = sqliteMallocRaw( n );
if( zStmt==0 ) return 0;
- strcpy(zStmt, p->isTemp ? "CREATE TEMP TABLE " : "CREATE TABLE ");
+ strcpy(zStmt, p->iDb==1 ? "CREATE TEMP TABLE " : "CREATE TABLE ");
k = strlen(zStmt);
identPut(zStmt, &k, p->zName);
zStmt[k++] = '(';
@@ -859,7 +965,7 @@ void sqliteEndTable(Parse *pParse, Token *pEnd, Select *pSelect){
if( v==0 ) return;
if( p->pSelect==0 ){
/* A regular table */
- sqliteVdbeAddOp(v, OP_CreateTable, 0, p->isTemp);
+ sqliteVdbeAddOp(v, OP_CreateTable, 0, p->iDb);
sqliteVdbeChangeP3(v, -1, (char *)&p->tnum, P3_POINTER);
}else{
/* A view */
@@ -891,13 +997,13 @@ void sqliteEndTable(Parse *pParse, Token *pEnd, Select *pSelect){
}
sqliteVdbeAddOp(v, OP_MakeRecord, 5, 0);
sqliteVdbeAddOp(v, OP_PutIntKey, 0, 0);
- if( !p->isTemp ){
+ if( !p->iDb ){
sqliteChangeCookie(db, v);
}
sqliteVdbeAddOp(v, OP_Close, 0, 0);
if( pSelect ){
- int op = p->isTemp ? OP_OpenWrAux : OP_OpenWrite;
- sqliteVdbeAddOp(v, op, 1, 0);
+ sqliteVdbeAddOp(v, OP_Integer, p->iDb, 0);
+ sqliteVdbeAddOp(v, OP_OpenWrite, 1, 0);
pParse->nTab = 2;
sqliteSelect(pParse, pSelect, SRT_Table, 1, 0, 0, 0);
}
@@ -906,19 +1012,19 @@ void sqliteEndTable(Parse *pParse, Token *pEnd, Select *pSelect){
/* Add the table to the in-memory representation of the database.
*/
- assert( pParse->nameClash==0 || pParse->initFlag==1 );
- if( pParse->explain==0 && pParse->nameClash==0 && pParse->nErr==0 ){
+ if( pParse->explain==0 && pParse->nErr==0 ){
Table *pOld;
FKey *pFKey;
- pOld = sqliteHashInsert(&db->tblHash, p->zName, strlen(p->zName)+1, p);
+ pOld = sqliteHashInsert(&db->aDb[p->iDb].tblHash,
+ p->zName, strlen(p->zName)+1, p);
if( pOld ){
assert( p==pOld ); /* Malloc must have failed inside HashInsert() */
return;
}
for(pFKey=p->pFKey; pFKey; pFKey=pFKey->pNextFrom){
int nTo = strlen(pFKey->zTo) + 1;
- pFKey->pNextTo = sqliteHashFind(&db->aFKey, pFKey->zTo, nTo);
- sqliteHashInsert(&db->aFKey, pFKey->zTo, nTo, pFKey);
+ pFKey->pNextTo = sqliteHashFind(&db->aDb[p->iDb].aFKey, pFKey->zTo, nTo);
+ sqliteHashInsert(&db->aDb[p->iDb].aFKey, pFKey->zTo, nTo, pFKey);
}
pParse->pNewTable = 0;
db->nTable++;
@@ -1038,7 +1144,7 @@ int sqliteViewGetColumnNames(Parse *pParse, Table *pTable){
pSelTab->nCol = 0;
pSelTab->aCol = 0;
sqliteDeleteTable(0, pSelTab);
- pParse->db->flags |= SQLITE_UnresetViews;
+ DbSetProperty(pParse->db, pTable->iDb, DB_UnresetViews);
}else{
pTable->nCol = 0;
nErr++;
@@ -1072,18 +1178,18 @@ static void sqliteViewResetColumnNames(Table *pTable){
}
/*
-** Clear the column names from every VIEW.
+** Clear the column names from every VIEW in database idx.
*/
-void sqliteViewResetAll(sqlite *db){
+static void sqliteViewResetAll(sqlite *db, int idx){
HashElem *i;
- if( (db->flags & SQLITE_UnresetViews)==0 ) return;
- for(i=sqliteHashFirst(&db->tblHash); i; i=sqliteHashNext(i)){
+ if( !DbHasProperty(db, idx, DB_UnresetViews) ) return;
+ for(i=sqliteHashFirst(&db->aDb[idx].tblHash); i; i=sqliteHashNext(i)){
Table *pTab = sqliteHashData(i);
if( pTab->pSelect ){
sqliteViewResetColumnNames(pTab);
}
}
- db->flags &= ~SQLITE_UnresetViews;
+ DbClearProperty(db, idx, DB_UnresetViews);
}
/*
@@ -1095,7 +1201,7 @@ Table *sqliteTableFromToken(Parse *pParse, Token *pTok){
Table *pTab;
zName = sqliteTableNameFromToken(pTok);
if( zName==0 ) return 0;
- pTab = sqliteFindTable(pParse->db, zName);
+ pTab = sqliteFindTable(pParse->db, zName, 0);
sqliteFree(zName);
if( pTab==0 ){
sqliteSetNString(&pParse->zErrMsg, "no such table: ", 0,
@@ -1114,33 +1220,38 @@ void sqliteDropTable(Parse *pParse, Token *pName, int isView){
Vdbe *v;
int base;
sqlite *db = pParse->db;
+ int iDb;
if( pParse->nErr || sqlite_malloc_failed ) return;
pTable = sqliteTableFromToken(pParse, pName);
if( pTable==0 ) return;
+ iDb = pTable->iDb;
+ assert( iDb>=0 && iDb<db->nDb );
#ifndef SQLITE_OMIT_AUTHORIZATION
- if( sqliteAuthCheck(pParse, SQLITE_DELETE, SCHEMA_TABLE(pTable->isTemp),0)){
- return;
- }
{
int code;
+ const char *zTab = SCHEMA_TABLE(pTable->iDb);
+ const char *zDb = db->aDb[pTable->iDb].zName;
+ if( sqliteAuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb)){
+ return;
+ }
if( isView ){
- if( pTable->isTemp ){
+ if( iDb==1 ){
code = SQLITE_DROP_TEMP_VIEW;
}else{
code = SQLITE_DROP_VIEW;
}
}else{
- if( pTable->isTemp ){
+ if( iDb==1 ){
code = SQLITE_DROP_TEMP_TABLE;
}else{
code = SQLITE_DROP_TABLE;
}
}
- if( sqliteAuthCheck(pParse, code, pTable->zName, 0) ){
+ if( sqliteAuthCheck(pParse, code, pTable->zName, 0, zDb) ){
return;
}
- if( sqliteAuthCheck(pParse, SQLITE_DELETE, pTable->zName, 0) ){
+ if( sqliteAuthCheck(pParse, SQLITE_DELETE, pTable->zName, 0, zDb) ){
return;
}
}
@@ -1181,31 +1292,44 @@ void sqliteDropTable(Parse *pParse, Token *pName, int isView){
};
Index *pIdx;
Trigger *pTrigger;
- sqliteBeginWriteOperation(pParse, 0, pTable->isTemp);
- sqliteOpenMasterTable(v, pTable->isTemp);
+ sqliteBeginWriteOperation(pParse, 0, pTable->iDb);
+
/* Drop all triggers associated with the table being dropped */
pTrigger = pTable->pTrigger;
while( pTrigger ){
- Token tt;
- tt.z = pTable->pTrigger->name;
- tt.n = strlen(pTable->pTrigger->name);
- sqliteDropTrigger(pParse, &tt, 1);
+ SrcList *pNm;
+ assert( pTrigger->iDb==pTable->iDb || pTrigger->iDb==1 );
+ pNm = sqliteSrcListAppend(0, 0, 0);
+ pNm->a[0].zName = sqliteStrDup(pTrigger->name);
+ pNm->a[0].zDatabase = sqliteStrDup(db->aDb[pTable->iDb].zName);
+ sqliteDropTrigger(pParse, pNm, 1);
if( pParse->explain ){
pTrigger = pTrigger->pNext;
}else{
pTrigger = pTable->pTrigger;
}
}
+
+ /* Drop all SQLITE_MASTER entries that refer to the table */
+ sqliteOpenMasterTable(v, pTable->iDb);
base = sqliteVdbeAddOpList(v, ArraySize(dropTable), dropTable);
sqliteVdbeChangeP3(v, base+1, pTable->zName, 0);
- if( !pTable->isTemp ){
+
+ /* Drop all SQLITE_TEMP_MASTER entries that refer to the table */
+ if( pTable->iDb!=1 ){
+ sqliteOpenMasterTable(v, 1);
+ base = sqliteVdbeAddOpList(v, ArraySize(dropTable), dropTable);
+ sqliteVdbeChangeP3(v, base+1, pTable->zName, 0);
+ }
+
+ if( pTable->iDb==0 ){
sqliteChangeCookie(db, v);
}
sqliteVdbeAddOp(v, OP_Close, 0, 0);
if( !isView ){
- sqliteVdbeAddOp(v, OP_Destroy, pTable->tnum, pTable->isTemp);
+ sqliteVdbeAddOp(v, OP_Destroy, pTable->tnum, pTable->iDb);
for(pIdx=pTable->pIndex; pIdx; pIdx=pIdx->pNext){
- sqliteVdbeAddOp(v, OP_Destroy, pIdx->tnum, pTable->isTemp);
+ sqliteVdbeAddOp(v, OP_Destroy, pIdx->tnum, pIdx->iDb);
}
}
sqliteEndWriteOperation(pParse);
@@ -1220,7 +1344,7 @@ void sqliteDropTable(Parse *pParse, Token *pName, int isView){
sqliteUnlinkAndDeleteTable(db, pTable);
db->flags |= SQLITE_InternChanges;
}
- sqliteViewResetAll(db);
+ sqliteViewResetAll(db, iDb);
}
/*
@@ -1403,9 +1527,10 @@ void sqliteDeferForeignKey(Parse *pParse, int isDeferred){
void sqliteCreateIndex(
Parse *pParse, /* All information about this parse */
Token *pName, /* Name of the index. May be NULL */
- Token *pTable, /* Name of the table to index. Use pParse->pNewTable if 0 */
+ SrcList *pTable, /* Name of the table to index. Use pParse->pNewTable if 0 */
IdList *pList, /* A list of columns to be indexed */
int onError, /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
+ int isTemp, /* True if this is a temporary index */
Token *pStart, /* The CREATE token that begins a CREATE TABLE statement */
Token *pEnd /* The ")" that closes the CREATE INDEX statement */
){
@@ -1415,7 +1540,6 @@ void sqliteCreateIndex(
int i, j;
Token nullId; /* Fake token for an empty ID list */
sqlite *db = pParse->db;
- int hideName = 0; /* Do not put table name in the hash table */
if( pParse->nErr || sqlite_malloc_failed ) goto exit_create_index;
@@ -1424,15 +1548,22 @@ void sqliteCreateIndex(
*/
if( pTable!=0 ){
assert( pName!=0 );
- pTab = sqliteTableFromToken(pParse, pTable);
+ assert( pTable->nSrc==1 );
+ pTab = sqliteSrcListLookup(pParse, pTable);
}else{
assert( pName==0 );
pTab = pParse->pNewTable;
}
if( pTab==0 || pParse->nErr ) goto exit_create_index;
if( pTab->readOnly ){
+ sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName,
+ " may not be indexed", 0);
+ pParse->nErr++;
+ goto exit_create_index;
+ }
+ if( !isTemp && pTab->iDb>=2 && pParse->initFlag==0 ){
sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName,
- " may not have new indices added", 0);
+ " may not have non-temporary indices added", 0);
pParse->nErr++;
goto exit_create_index;
}
@@ -1441,16 +1572,8 @@ void sqliteCreateIndex(
pParse->nErr++;
goto exit_create_index;
}
-
- /* If this index is created while re-reading the schema from sqlite_master
- ** but the table associated with this index is a temporary table, it can
- ** only mean that the table that this index is really associated with is
- ** one whose name is hidden behind a temporary table with the same name.
- ** Since its table has been suppressed, we need to also suppress the
- ** index.
- */
- if( pParse->initFlag && !pParse->isTemp && pTab->isTemp ){
- goto exit_create_index;
+ if( pTab->iDb==1 ){
+ isTemp = 1;
}
/*
@@ -1460,40 +1583,30 @@ void sqliteCreateIndex(
** 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, but we will not
- ** store its name in the hash table. Set the hideName flag to accomplish
- ** this.
+ ** 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 ){
+ if( pName && !pParse->initFlag ){
Index *pISameName; /* Another index with the same name */
Table *pTSameName; /* A table with same name as the index */
- zName = sqliteTableNameFromToken(pName);
+ zName = sqliteStrNDup(pName->z, pName->n);
if( zName==0 ) goto exit_create_index;
- if( (pISameName = sqliteFindIndex(db, zName))!=0 ){
- if( pISameName->pTable->isTemp && pParse->initFlag ){
- hideName = 1;
- }else{
- sqliteSetString(&pParse->zErrMsg, "index ", zName,
- " already exists", 0);
- pParse->nErr++;
- goto exit_create_index;
- }
+ if( (pISameName = sqliteFindIndex(db, zName, 0))!=0 ){
+ sqliteSetString(&pParse->zErrMsg, "index ", zName,
+ " already exists", 0);
+ pParse->nErr++;
+ goto exit_create_index;
}
- if( (pTSameName = sqliteFindTable(db, zName))!=0 ){
- if( pTSameName->isTemp && pParse->initFlag ){
- hideName = 1;
- }else{
- sqliteSetString(&pParse->zErrMsg, "there is already a table named ",
- zName, 0);
- pParse->nErr++;
- goto exit_create_index;
- }
+ if( (pTSameName = sqliteFindTable(db, zName, 0))!=0 ){
+ sqliteSetString(&pParse->zErrMsg, "there is already a table named ",
+ zName, 0);
+ pParse->nErr++;
+ goto exit_create_index;
}
- }else{
+ }else if( pName==0 ){
char zBuf[30];
int n;
Index *pLoop;
@@ -1502,19 +1615,26 @@ void sqliteCreateIndex(
zName = 0;
sqliteSetString(&zName, "(", pTab->zName, " autoindex ", zBuf, 0);
if( zName==0 ) goto exit_create_index;
- hideName = sqliteFindIndex(db, zName)!=0;
+ }else{
+ zName = sqliteStrNDup(pName->z, pName->n);
}
/* Check for authorization to create an index.
*/
#ifndef SQLITE_OMIT_AUTHORIZATION
- if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(pTab->isTemp), 0) ){
- goto exit_create_index;
- }
- i = SQLITE_CREATE_INDEX;
- if( pTab->isTemp ) i = SQLITE_CREATE_TEMP_INDEX;
- if( sqliteAuthCheck(pParse, i, zName, pTab->zName) ){
- goto exit_create_index;
+ {
+ const char *zDb = db->aDb[pTab->iDb].zName;
+
+ assert( isTemp==0 || isTemp==1 );
+ assert( pTab->iDb==pParse->iDb || isTemp==1 );
+ if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0, zDb) ){
+ goto exit_create_index;
+ }
+ i = SQLITE_CREATE_INDEX;
+ if( isTemp ) i = SQLITE_CREATE_TEMP_INDEX;
+ if( sqliteAuthCheck(pParse, i, zName, pTab->zName, zDb) ){
+ goto exit_create_index;
+ }
}
#endif
@@ -1540,8 +1660,9 @@ void sqliteCreateIndex(
strcpy(pIndex->zName, zName);
pIndex->pTable = pTab;
pIndex->nColumn = pList->nId;
- pIndex->onError = pIndex->isUnique = onError;
+ pIndex->onError = onError;
pIndex->autoIndex = pName==0;
+ pIndex->iDb = isTemp ? 1 : pParse->iDb;
/* Scan the names of the columns of the table to be indexed and
** load the column indices into the Index structure. Report an error
@@ -1564,9 +1685,10 @@ void sqliteCreateIndex(
/* Link the new Index structure to its table and to the other
** in-memory database structures.
*/
- if( !pParse->explain && !hideName ){
+ if( !pParse->explain ){
Index *p;
- p = sqliteHashInsert(&db->idxHash, pIndex->zName, strlen(zName)+1, pIndex);
+ p = sqliteHashInsert(&db->aDb[isTemp].idxHash,
+ pIndex->zName, strlen(zName)+1, pIndex);
if( p ){
assert( p==pIndex ); /* Malloc must have failed */
sqliteFree(pIndex);
@@ -1622,7 +1744,6 @@ void sqliteCreateIndex(
int lbl1, lbl2;
int i;
int addr;
- int isTemp = pTab->isTemp;
v = sqliteGetVdbe(pParse);
if( v==0 ) goto exit_create_index;
@@ -1642,11 +1763,8 @@ void sqliteCreateIndex(
pIndex->tnum = 0;
if( pTable ){
sqliteVdbeAddOp(v, OP_Dup, 0, 0);
- if( isTemp ){
- sqliteVdbeAddOp(v, OP_OpenWrAux, 1, 0);
- }else{
- sqliteVdbeAddOp(v, OP_OpenWrite, 1, 0);
- }
+ sqliteVdbeAddOp(v, OP_Integer, isTemp, 0);
+ sqliteVdbeAddOp(v, OP_OpenWrite, 1, 0);
}
addr = sqliteVdbeAddOp(v, OP_String, 0, 0);
if( pStart && pEnd ){
@@ -1656,13 +1774,19 @@ void sqliteCreateIndex(
sqliteVdbeAddOp(v, OP_MakeRecord, 5, 0);
sqliteVdbeAddOp(v, OP_PutIntKey, 0, 0);
if( pTable ){
- sqliteVdbeAddOp(v, isTemp ? OP_OpenAux : OP_Open, 2, pTab->tnum);
+ sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
+ sqliteVdbeAddOp(v, OP_OpenRead, 2, pTab->tnum);
sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
lbl2 = sqliteVdbeMakeLabel(v);
sqliteVdbeAddOp(v, OP_Rewind, 2, lbl2);
lbl1 = sqliteVdbeAddOp(v, OP_Recno, 2, 0);
for(i=0; i<pIndex->nColumn; i++){
- sqliteVdbeAddOp(v, OP_Column, 2, pIndex->aiColumn[i]);
+ int iCol = pIndex->aiColumn[i];
+ if( pTab->iPKey==iCol ){
+ sqliteVdbeAddOp(v, OP_Dup, i, 0);
+ }else{
+ sqliteVdbeAddOp(v, OP_Column, 2, iCol);
+ }
}
sqliteVdbeAddOp(v, OP_MakeIdxKey, pIndex->nColumn, 0);
if( db->file_format>=4 ) sqliteAddIdxKeyType(v, pIndex);
@@ -1685,6 +1809,7 @@ void sqliteCreateIndex(
/* Clean up before exiting */
exit_create_index:
sqliteIdListDelete(pList);
+ sqliteSrcListDelete(pTable);
sqliteFree(zName);
return;
}
@@ -1693,39 +1818,40 @@ exit_create_index:
** This routine will drop an existing named index. This routine
** implements the DROP INDEX statement.
*/
-void sqliteDropIndex(Parse *pParse, Token *pName){
+void sqliteDropIndex(Parse *pParse, SrcList *pName){
Index *pIndex;
- char *zName;
Vdbe *v;
sqlite *db = pParse->db;
if( pParse->nErr || sqlite_malloc_failed ) return;
- zName = sqliteTableNameFromToken(pName);
- if( zName==0 ) return;
- pIndex = sqliteFindIndex(db, zName);
- sqliteFree(zName);
+ assert( pName->nSrc==1 );
+ pIndex = sqliteFindIndex(db, pName->a[0].zName, pName->a[0].zDatabase);
if( pIndex==0 ){
- sqliteSetNString(&pParse->zErrMsg, "no such index: ", 0,
- pName->z, pName->n, 0);
- pParse->nErr++;
- return;
+ sqliteErrorMsg(pParse, "no such index: %S", pName, 0);
+ goto exit_drop_index;
}
if( pIndex->autoIndex ){
- sqliteSetString(&pParse->zErrMsg, "index associated with UNIQUE "
+ sqliteErrorMsg(pParse, "index associated with UNIQUE "
"or PRIMARY KEY constraint cannot be dropped", 0);
- pParse->nErr++;
- return;
+ goto exit_drop_index;
+ }
+ if( pIndex->iDb>1 ){
+ sqliteErrorMsg(pParse, "cannot alter schema of attached "
+ "databases", 0);
+ goto exit_drop_index;
}
#ifndef SQLITE_OMIT_AUTHORIZATION
{
int code = SQLITE_DROP_INDEX;
Table *pTab = pIndex->pTable;
- if( sqliteAuthCheck(pParse, SQLITE_DELETE, SCHEMA_TABLE(pTab->isTemp), 0) ){
- return;
+ const char *zDb = db->aDb[pIndex->iDb].zName;
+ const char *zTab = SCHEMA_TABLE(pIndex->iDb);
+ if( sqliteAuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){
+ goto exit_drop_index;
}
- if( pTab->isTemp ) code = SQLITE_DROP_TEMP_INDEX;
- if( sqliteAuthCheck(pParse, code, pIndex->zName, pTab->zName) ){
- return;
+ if( pIndex->iDb ) code = SQLITE_DROP_TEMP_INDEX;
+ if( sqliteAuthCheck(pParse, code, pIndex->zName, pTab->zName, zDb) ){
+ goto exit_drop_index;
}
}
#endif
@@ -1745,17 +1871,16 @@ void sqliteDropIndex(Parse *pParse, Token *pName){
{ OP_Delete, 0, 0, 0}, /* 8 */
};
int base;
- Table *pTab = pIndex->pTable;
- sqliteBeginWriteOperation(pParse, 0, pTab->isTemp);
- sqliteOpenMasterTable(v, pTab->isTemp);
+ sqliteBeginWriteOperation(pParse, 0, pIndex->iDb);
+ sqliteOpenMasterTable(v, pIndex->iDb);
base = sqliteVdbeAddOpList(v, ArraySize(dropIndex), dropIndex);
sqliteVdbeChangeP3(v, base+1, pIndex->zName, 0);
- if( !pTab->isTemp ){
+ if( pIndex->iDb==0 ){
sqliteChangeCookie(db, v);
}
sqliteVdbeAddOp(v, OP_Close, 0, 0);
- sqliteVdbeAddOp(v, OP_Destroy, pIndex->tnum, pTab->isTemp);
+ sqliteVdbeAddOp(v, OP_Destroy, pIndex->tnum, pIndex->iDb);
sqliteEndWriteOperation(pParse);
}
@@ -1765,6 +1890,9 @@ void sqliteDropIndex(Parse *pParse, Token *pName){
sqliteUnlinkAndDeleteIndex(db, pIndex);
db->flags |= SQLITE_InternChanges;
}
+
+exit_drop_index:
+ sqliteSrcListDelete(pName);
}
/*
@@ -1807,25 +1935,53 @@ IdList *sqliteIdListAppend(IdList *pList, Token *pToken){
** need be. A new entry is created in the SrcList even if pToken is NULL.
**
** A new SrcList is returned, or NULL if malloc() fails.
+**
+** 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:
+**
+** sqliteSrcListAppend(A,B,0);
+**
+** Then B is a table name and the database name is unspecified. If called
+** like this:
+**
+** sqliteSrcListAppend(A,B,C);
+**
+** Then C is the table name and B is the database name.
*/
-SrcList *sqliteSrcListAppend(SrcList *pList, Token *pToken){
+SrcList *sqliteSrcListAppend(SrcList *pList, Token *pTable, Token *pDatabase){
if( pList==0 ){
- pList = sqliteMalloc( sizeof(IdList) );
+ pList = sqliteMalloc( sizeof(SrcList) );
if( pList==0 ) return 0;
}
- if( (pList->nSrc & 7)==0 ){
- struct SrcList_item *a;
- a = sqliteRealloc(pList->a, (pList->nSrc+8)*sizeof(pList->a[0]) );
- if( a==0 ){
+ if( (pList->nSrc & 7)==1 ){
+ SrcList *pNew;
+ pNew = sqliteRealloc(pList,
+ sizeof(*pList) + (pList->nSrc+8)*sizeof(pList->a[0]) );
+ if( pNew==0 ){
sqliteSrcListDelete(pList);
return 0;
}
- pList->a = a;
+ pList = pNew;
}
memset(&pList->a[pList->nSrc], 0, sizeof(pList->a[0]));
- if( pToken ){
+ if( pDatabase && pDatabase->z==0 ){
+ pDatabase = 0;
+ }
+ if( pDatabase && pTable ){
+ Token *pTemp = pDatabase;
+ pDatabase = pTable;
+ pTable = pTemp;
+ }
+ if( pTable ){
char **pz = &pList->a[pList->nSrc].zName;
- sqliteSetNString(pz, pToken->z, pToken->n, 0);
+ sqliteSetNString(pz, pTable->z, pTable->n, 0);
if( *pz==0 ){
sqliteSrcListDelete(pList);
return 0;
@@ -1833,11 +1989,34 @@ SrcList *sqliteSrcListAppend(SrcList *pList, Token *pToken){
sqliteDequote(*pz);
}
}
+ if( pDatabase ){
+ char **pz = &pList->a[pList->nSrc].zDatabase;
+ sqliteSetNString(pz, pDatabase->z, pDatabase->n, 0);
+ if( *pz==0 ){
+ sqliteSrcListDelete(pList);
+ return 0;
+ }else{
+ sqliteDequote(*pz);
+ }
+ }
+ pList->a[pList->nSrc].iCursor = -1;
pList->nSrc++;
return pList;
}
/*
+** Assign cursors to all tables in a SrcList
+*/
+void sqliteSrcListAssignCursors(Parse *pParse, SrcList *pList){
+ int i;
+ for(i=0; i<pList->nSrc; i++){
+ if( pList->a[i].iCursor<0 ){
+ pList->a[i].iCursor = pParse->nTab++;
+ }
+ }
+}
+
+/*
** Add an alias to the last identifier on the given identifier list.
*/
void sqliteSrcListAddAlias(SrcList *pList, Token *pToken){
@@ -1881,6 +2060,7 @@ void sqliteSrcListDelete(SrcList *pList){
int i;
if( pList==0 ) return;
for(i=0; i<pList->nSrc; i++){
+ sqliteFree(pList->a[i].zDatabase);
sqliteFree(pList->a[i].zName);
sqliteFree(pList->a[i].zAlias);
if( pList->a[i].pTab && pList->a[i].pTab->isTransient ){
@@ -1890,137 +2070,20 @@ void sqliteSrcListDelete(SrcList *pList){
sqliteExprDelete(pList->a[i].pOn);
sqliteIdListDelete(pList->a[i].pUsing);
}
- sqliteFree(pList->a);
sqliteFree(pList);
}
/*
-** The COPY command is for compatibility with PostgreSQL and specificially
-** for the ability to read the output of pg_dump. The format is as
-** follows:
-**
-** COPY table FROM file [USING DELIMITERS string]
-**
-** "table" is an existing table name. We will read lines of code from
-** file to fill this table with data. File might be "stdin". The optional
-** delimiter string identifies the field separators. The default is a tab.
-*/
-void sqliteCopy(
- Parse *pParse, /* The parser context */
- Token *pTableName, /* The name of the table into which we will insert */
- Token *pFilename, /* The file from which to obtain information */
- Token *pDelimiter, /* Use this as the field delimiter */
- int onError /* What to do if a constraint fails */
-){
- Table *pTab;
- char *zTab;
- int i;
- Vdbe *v;
- int addr, end;
- Index *pIdx;
- char *zFile = 0;
- sqlite *db = pParse->db;
-
-
- zTab = sqliteTableNameFromToken(pTableName);
- if( sqlite_malloc_failed || zTab==0 ) goto copy_cleanup;
- pTab = sqliteTableNameToTable(pParse, zTab);
- sqliteFree(zTab);
- if( pTab==0 ) goto copy_cleanup;
- zFile = sqliteStrNDup(pFilename->z, pFilename->n);
- sqliteDequote(zFile);
- if( sqliteAuthCheck(pParse, SQLITE_INSERT, pTab->zName, zFile)
- || sqliteAuthCheck(pParse, SQLITE_COPY, pTab->zName, zFile) ){
- goto copy_cleanup;
- }
- v = sqliteGetVdbe(pParse);
- if( v ){
- int openOp;
- sqliteBeginWriteOperation(pParse, 1, pTab->isTemp);
- addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0);
- sqliteVdbeChangeP3(v, addr, pFilename->z, pFilename->n);
- sqliteVdbeDequoteP3(v, addr);
- openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite;
- sqliteVdbeAddOp(v, openOp, 0, pTab->tnum);
- sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
- for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
- sqliteVdbeAddOp(v, openOp, i, pIdx->tnum);
- sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC);
- }
- if( db->flags & SQLITE_CountRows ){
- sqliteVdbeAddOp(v, OP_Integer, 0, 0); /* Initialize the row count */
- }
- end = sqliteVdbeMakeLabel(v);
- addr = sqliteVdbeAddOp(v, OP_FileRead, pTab->nCol, end);
- if( pDelimiter ){
- sqliteVdbeChangeP3(v, addr, pDelimiter->z, pDelimiter->n);
- sqliteVdbeDequoteP3(v, addr);
- }else{
- sqliteVdbeChangeP3(v, addr, "\t", 1);
- }
- if( pTab->iPKey>=0 ){
- sqliteVdbeAddOp(v, OP_FileColumn, pTab->iPKey, 0);
- sqliteVdbeAddOp(v, OP_MustBeInt, 0, 0);
- }else{
- sqliteVdbeAddOp(v, OP_NewRecno, 0, 0);
- }
- for(i=0; i<pTab->nCol; i++){
- if( i==pTab->iPKey ){
- /* The integer primary key column is filled with NULL since its
- ** value is always pulled from the record number */
- sqliteVdbeAddOp(v, OP_String, 0, 0);
- }else{
- sqliteVdbeAddOp(v, OP_FileColumn, i, 0);
- }
- }
- sqliteGenerateConstraintChecks(pParse, pTab, 0, 0, 0, 0, onError, addr);
- sqliteCompleteInsertion(pParse, pTab, 0, 0, 0, 0);
- if( (db->flags & SQLITE_CountRows)!=0 ){
- sqliteVdbeAddOp(v, OP_AddImm, 1, 0); /* Increment row count */
- }
- sqliteVdbeAddOp(v, OP_Goto, 0, addr);
- sqliteVdbeResolveLabel(v, end);
- sqliteVdbeAddOp(v, OP_Noop, 0, 0);
- sqliteEndWriteOperation(pParse);
- if( db->flags & SQLITE_CountRows ){
- sqliteVdbeAddOp(v, OP_ColumnName, 0, 0);
- sqliteVdbeChangeP3(v, -1, "rows inserted", P3_STATIC);
- sqliteVdbeAddOp(v, OP_Callback, 1, 0);
- }
- }
-
-copy_cleanup:
- sqliteFree(zFile);
- return;
-}
-
-/*
-** The non-standard VACUUM command is used to clean up the database,
-** collapse free space, etc. It is modelled after the VACUUM command
-** in PostgreSQL.
-**
-** In version 1.0.x of SQLite, the VACUUM command would call
-** gdbm_reorganize() on all the database tables. But beginning
-** with 2.0.0, SQLite no longer uses GDBM so this command has
-** become a no-op.
-*/
-void sqliteVacuum(Parse *pParse, Token *pTableName){
- /* Do nothing */
-}
-
-/*
** Begin a transaction
*/
void sqliteBeginTransaction(Parse *pParse, int onError){
sqlite *db;
- if( pParse==0 || (db=pParse->db)==0 || db->pBe==0 ) return;
+ if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return;
if( pParse->nErr || sqlite_malloc_failed ) return;
- if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "BEGIN", 0) ) return;
+ if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "BEGIN", 0, 0) ) return;
if( db->flags & SQLITE_InTrans ){
- pParse->nErr++;
- sqliteSetString(&pParse->zErrMsg, "cannot start a transaction "
- "within a transaction", 0);
+ sqliteErrorMsg(pParse, "cannot start a transaction within a transaction");
return;
}
sqliteBeginWriteOperation(pParse, 0, 0);
@@ -2034,13 +2097,11 @@ void sqliteBeginTransaction(Parse *pParse, int onError){
void sqliteCommitTransaction(Parse *pParse){
sqlite *db;
- if( pParse==0 || (db=pParse->db)==0 || db->pBe==0 ) return;
+ if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return;
if( pParse->nErr || sqlite_malloc_failed ) return;
- if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "COMMIT", 0) ) return;
+ if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "COMMIT", 0, 0) ) return;
if( (db->flags & SQLITE_InTrans)==0 ){
- pParse->nErr++;
- sqliteSetString(&pParse->zErrMsg,
- "cannot commit - no transaction is active", 0);
+ sqliteErrorMsg(pParse, "cannot commit - no transaction is active");
return;
}
db->flags &= ~SQLITE_InTrans;
@@ -2055,13 +2116,11 @@ void sqliteRollbackTransaction(Parse *pParse){
sqlite *db;
Vdbe *v;
- if( pParse==0 || (db=pParse->db)==0 || db->pBe==0 ) return;
+ if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return;
if( pParse->nErr || sqlite_malloc_failed ) return;
- if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "ROLLBACK", 0) ) return;
+ if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "ROLLBACK", 0, 0) ) return;
if( (db->flags & SQLITE_InTrans)==0 ){
- pParse->nErr++;
- sqliteSetString(&pParse->zErrMsg,
- "cannot rollback - no transaction is active", 0);
+ sqliteErrorMsg(pParse, "cannot rollback - no transaction is active");
return;
}
v = sqliteGetVdbe(pParse);
@@ -2073,6 +2132,21 @@ void sqliteRollbackTransaction(Parse *pParse){
}
/*
+** Generate VDBE code that will verify the schema cookie for all
+** named database files.
+*/
+void sqliteCodeVerifySchema(Parse *pParse, int iDb){
+ sqlite *db = pParse->db;
+ Vdbe *v = sqliteGetVdbe(pParse);
+ assert( iDb>=0 && iDb<db->nDb );
+ assert( db->aDb[iDb].pBt!=0 );
+ if( iDb!=1 && !DbHasProperty(db, iDb, DB_Cookie) ){
+ sqliteVdbeAddOp(v, OP_VerifyCookie, iDb, db->aDb[iDb].schema_cookie);
+ DbSetProperty(db, iDb, DB_Cookie);
+ }
+}
+
+/*
** Generate VDBE code that prepares for doing an operation that
** might change the database.
**
@@ -2085,485 +2159,50 @@ void sqliteRollbackTransaction(Parse *pParse){
** 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.
**
-** The tempOnly flag indicates that only temporary tables will be changed
-** during this write operation. The primary database table is not
-** write-locked. Only the temporary database file gets a write lock.
-** Other processes can continue to read or write the primary database file.
+** Only database iDb and the temp database are made writable by this call.
+** If iDb==0, then the main and temp databases are made writable. If
+** iDb==1 then only the temp database is made writable. If iDb>1 then the
+** specified auxiliary database and the temp database are made writable.
*/
-void sqliteBeginWriteOperation(Parse *pParse, int setCheckpoint, int tempOnly){
+void sqliteBeginWriteOperation(Parse *pParse, int setCheckpoint, int iDb){
Vdbe *v;
+ sqlite *db = pParse->db;
+ if( DbHasProperty(db, iDb, DB_Locked) ) return;
v = sqliteGetVdbe(pParse);
if( v==0 ) return;
- if( pParse->trigStack ) return; /* if this is in a trigger */
- if( (pParse->db->flags & SQLITE_InTrans)==0 ){
- sqliteVdbeAddOp(v, OP_Transaction, tempOnly, 0);
- if( !tempOnly ){
- sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0);
- pParse->schemaVerified = 1;
+ if( !db->aDb[iDb].inTrans ){
+ sqliteVdbeAddOp(v, OP_Transaction, iDb, 0);
+ DbSetProperty(db, iDb, DB_Locked);
+ sqliteCodeVerifySchema(pParse, iDb);
+ if( iDb!=1 ){
+ sqliteBeginWriteOperation(pParse, setCheckpoint, 1);
}
}else if( setCheckpoint ){
- sqliteVdbeAddOp(v, OP_Checkpoint, 0, 0);
+ sqliteVdbeAddOp(v, OP_Checkpoint, iDb, 0);
+ DbSetProperty(db, iDb, DB_Locked);
}
}
/*
** Generate code that concludes an operation that may have changed
-** the database. This is a companion function to BeginWriteOperation().
-** If a transaction was started, then commit it. If a checkpoint was
-** started then commit that.
+** the database. If a statement transaction was started, then emit
+** an OP_Commit that will cause the changes to be committed to disk.
+**
+** Note that checkpoints are automatically committed at the end of
+** a statement. Note also that there can be multiple calls to
+** sqliteBeginWriteOperation() but there should only be a single
+** call to sqliteEndWriteOperation() at the conclusion of the statement.
*/
void sqliteEndWriteOperation(Parse *pParse){
Vdbe *v;
+ sqlite *db = pParse->db;
if( pParse->trigStack ) return; /* if this is in a trigger */
v = sqliteGetVdbe(pParse);
if( v==0 ) return;
- if( pParse->db->flags & SQLITE_InTrans ){
- /* Do Nothing */
+ if( db->flags & SQLITE_InTrans ){
+ /* A BEGIN has executed. Do not commit until we see an explicit
+ ** COMMIT statement. */
}else{
sqliteVdbeAddOp(v, OP_Commit, 0, 0);
}
}
-
-
-/*
-** Interpret the given string as a boolean value.
-*/
-static int getBoolean(char *z){
- static char *azTrue[] = { "yes", "on", "true" };
- int i;
- if( z[0]==0 ) return 0;
- if( isdigit(z[0]) || (z[0]=='-' && isdigit(z[1])) ){
- return atoi(z);
- }
- for(i=0; i<sizeof(azTrue)/sizeof(azTrue[0]); i++){
- if( sqliteStrICmp(z,azTrue[i])==0 ) return 1;
- }
- return 0;
-}
-
-/*
-** Interpret the given string as a safety level. Return 0 for OFF,
-** 1 for ON or NORMAL and 2 for FULL.
-**
-** Note that the values returned are one less that the values that
-** should be passed into sqliteBtreeSetSafetyLevel(). The is done
-** to support legacy SQL code. The safety level used to be boolean
-** and older scripts may have used numbers 0 for OFF and 1 for ON.
-*/
-static int getSafetyLevel(char *z){
- static const struct {
- const char *zWord;
- int val;
- } aKey[] = {
- { "no", 0 },
- { "off", 0 },
- { "false", 0 },
- { "yes", 1 },
- { "on", 1 },
- { "true", 1 },
- { "full", 2 },
- };
- int i;
- if( z[0]==0 ) return 1;
- if( isdigit(z[0]) || (z[0]=='-' && isdigit(z[1])) ){
- return atoi(z);
- }
- for(i=0; i<sizeof(aKey)/sizeof(aKey[0]); i++){
- if( sqliteStrICmp(z,aKey[i].zWord)==0 ) return aKey[i].val;
- }
- return 1;
-}
-
-/*
-** Process a pragma statement.
-**
-** Pragmas are of this form:
-**
-** PRAGMA id = value
-**
-** The identifier might also be a string. The value is a string, and
-** identifier, or a number. If minusFlag is true, then the value is
-** a number that was preceded by a minus sign.
-*/
-void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
- char *zLeft = 0;
- char *zRight = 0;
- sqlite *db = pParse->db;
-
- zLeft = sqliteStrNDup(pLeft->z, pLeft->n);
- sqliteDequote(zLeft);
- if( minusFlag ){
- zRight = 0;
- sqliteSetNString(&zRight, "-", 1, pRight->z, pRight->n, 0);
- }else{
- zRight = sqliteStrNDup(pRight->z, pRight->n);
- sqliteDequote(zRight);
- }
- if( sqliteAuthCheck(pParse, SQLITE_PRAGMA, zLeft, zRight) ){
- sqliteFree(zLeft);
- sqliteFree(zRight);
- return;
- }
-
- /*
- ** PRAGMA default_cache_size
- ** PRAGMA default_cache_size=N
- **
- ** The first form reports the current persistent setting for the
- ** page cache size. The value returned is the maximum number of
- ** pages in the page cache. The second form sets both the current
- ** page cache size value and the persistent page cache size value
- ** stored in the database file.
- **
- ** The default cache size is stored in meta-value 2 of page 1 of the
- ** database file. The cache size is actually the absolute value of
- ** this memory location. The sign of meta-value 2 determines the
- ** synchronous setting. A negative value means synchronous is off
- ** and a positive value means synchronous is on.
- */
- if( sqliteStrICmp(zLeft,"default_cache_size")==0 ){
- static VdbeOp getCacheSize[] = {
- { OP_ReadCookie, 0, 2, 0},
- { OP_AbsValue, 0, 0, 0},
- { OP_Dup, 0, 0, 0},
- { OP_Integer, 0, 0, 0},
- { OP_Ne, 0, 6, 0},
- { OP_Integer, MAX_PAGES,0, 0},
- { OP_ColumnName, 0, 0, "cache_size"},
- { OP_Callback, 1, 0, 0},
- };
- Vdbe *v = sqliteGetVdbe(pParse);
- if( v==0 ) return;
- if( pRight->z==pLeft->z ){
- sqliteVdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize);
- }else{
- int addr;
- int size = atoi(zRight);
- if( size<0 ) size = -size;
- sqliteBeginWriteOperation(pParse, 0, 0);
- sqliteVdbeAddOp(v, OP_Integer, size, 0);
- sqliteVdbeAddOp(v, OP_ReadCookie, 0, 2);
- addr = sqliteVdbeAddOp(v, OP_Integer, 0, 0);
- sqliteVdbeAddOp(v, OP_Ge, 0, addr+3);
- sqliteVdbeAddOp(v, OP_Negative, 0, 0);
- sqliteVdbeAddOp(v, OP_SetCookie, 0, 2);
- sqliteEndWriteOperation(pParse);
- db->cache_size = db->cache_size<0 ? -size : size;
- sqliteBtreeSetCacheSize(db->pBe, db->cache_size);
- }
- }else
-
- /*
- ** PRAGMA cache_size
- ** PRAGMA cache_size=N
- **
- ** The first form reports the current local setting for the
- ** page cache size. The local setting can be different from
- ** the persistent cache size value that is stored in the database
- ** file itself. The value returned is the maximum number of
- ** pages in the page cache. The second form sets the local
- ** page cache size value. It does not change the persistent
- ** cache size stored on the disk so the cache size will revert
- ** to its default value when the database is closed and reopened.
- ** N should be a positive integer.
- */
- if( sqliteStrICmp(zLeft,"cache_size")==0 ){
- static VdbeOp getCacheSize[] = {
- { OP_ColumnName, 0, 0, "cache_size"},
- { OP_Callback, 1, 0, 0},
- };
- Vdbe *v = sqliteGetVdbe(pParse);
- if( v==0 ) return;
- if( pRight->z==pLeft->z ){
- int size = db->cache_size;;
- if( size<0 ) size = -size;
- sqliteVdbeAddOp(v, OP_Integer, size, 0);
- sqliteVdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize);
- }else{
- int size = atoi(zRight);
- if( size<0 ) size = -size;
- if( db->cache_size<0 ) size = -size;
- db->cache_size = size;
- sqliteBtreeSetCacheSize(db->pBe, db->cache_size);
- }
- }else
-
- /*
- ** PRAGMA default_synchronous
- ** PRAGMA default_synchronous=ON|OFF|NORMAL|FULL
- **
- ** The first form returns the persistent value of the "synchronous" setting
- ** that is stored in the database. This is the synchronous setting that
- ** is used whenever the database is opened unless overridden by a separate
- ** "synchronous" pragma. The second form changes the persistent and the
- ** local synchronous setting to the value given.
- **
- ** If synchronous is OFF, SQLite does not attempt any fsync() systems calls
- ** to make sure data is committed to disk. Write operations are very fast,
- ** but a power failure can leave the database in an inconsistent state.
- ** If synchronous is ON or NORMAL, SQLite will do an fsync() system call to
- ** make sure data is being written to disk. The risk of corruption due to
- ** a power loss in this mode is negligible but non-zero. If synchronous
- ** is FULL, extra fsync()s occur to reduce the risk of corruption to near
- ** zero, but with a write performance penalty. The default mode is NORMAL.
- */
- if( sqliteStrICmp(zLeft,"default_synchronous")==0 ){
- static VdbeOp getSync[] = {
- { OP_ColumnName, 0, 0, "synchronous"},
- { OP_ReadCookie, 0, 3, 0},
- { OP_Dup, 0, 0, 0},
- { OP_If, 0, 0, 0}, /* 3 */
- { OP_ReadCookie, 0, 2, 0},
- { OP_Integer, 0, 0, 0},
- { OP_Lt, 0, 5, 0},
- { OP_AddImm, 1, 0, 0},
- { OP_Callback, 1, 0, 0},
- { OP_Halt, 0, 0, 0},
- { OP_AddImm, -1, 0, 0}, /* 10 */
- { OP_Callback, 1, 0, 0}
- };
- Vdbe *v = sqliteGetVdbe(pParse);
- if( v==0 ) return;
- if( pRight->z==pLeft->z ){
- int addr = sqliteVdbeAddOpList(v, ArraySize(getSync), getSync);
- sqliteVdbeChangeP2(v, addr+3, addr+10);
- }else{
- int addr;
- int size = db->cache_size;
- if( size<0 ) size = -size;
- sqliteBeginWriteOperation(pParse, 0, 0);
- sqliteVdbeAddOp(v, OP_ReadCookie, 0, 2);
- sqliteVdbeAddOp(v, OP_Dup, 0, 0);
- addr = sqliteVdbeAddOp(v, OP_Integer, 0, 0);
- sqliteVdbeAddOp(v, OP_Ne, 0, addr+3);
- sqliteVdbeAddOp(v, OP_AddImm, MAX_PAGES, 0);
- sqliteVdbeAddOp(v, OP_AbsValue, 0, 0);
- db->safety_level = getSafetyLevel(zRight)+1;
- if( db->safety_level==1 ){
- sqliteVdbeAddOp(v, OP_Negative, 0, 0);
- size = -size;
- }
- sqliteVdbeAddOp(v, OP_SetCookie, 0, 2);
- sqliteVdbeAddOp(v, OP_Integer, db->safety_level, 0);
- sqliteVdbeAddOp(v, OP_SetCookie, 0, 3);
- sqliteEndWriteOperation(pParse);
- db->cache_size = size;
- sqliteBtreeSetCacheSize(db->pBe, db->cache_size);
- sqliteBtreeSetSafetyLevel(db->pBe, db->safety_level);
- }
- }else
-
- /*
- ** PRAGMA synchronous
- ** PRAGMA synchronous=OFF|ON|NORMAL|FULL
- **
- ** Return or set the local value of the synchronous flag. Changing
- ** the local value does not make changes to the disk file and the
- ** default value will be restored the next time the database is
- ** opened.
- */
- if( sqliteStrICmp(zLeft,"synchronous")==0 ){
- static VdbeOp getSync[] = {
- { OP_ColumnName, 0, 0, "synchronous"},
- { OP_Callback, 1, 0, 0},
- };
- Vdbe *v = sqliteGetVdbe(pParse);
- if( v==0 ) return;
- if( pRight->z==pLeft->z ){
- sqliteVdbeAddOp(v, OP_Integer, db->safety_level-1, 0);
- sqliteVdbeAddOpList(v, ArraySize(getSync), getSync);
- }else{
- int size = db->cache_size;
- if( size<0 ) size = -size;
- db->safety_level = getSafetyLevel(zRight)+1;
- if( db->safety_level==1 ) size = -size;
- db->cache_size = size;
- sqliteBtreeSetCacheSize(db->pBe, db->cache_size);
- sqliteBtreeSetSafetyLevel(db->pBe, db->safety_level);
- }
- }else
-
- if( sqliteStrICmp(zLeft, "trigger_overhead_test")==0 ){
- if( getBoolean(zRight) ){
- always_code_trigger_setup = 1;
- }else{
- always_code_trigger_setup = 0;
- }
- }else
-
- if( sqliteStrICmp(zLeft, "vdbe_trace")==0 ){
- if( getBoolean(zRight) ){
- db->flags |= SQLITE_VdbeTrace;
- }else{
- db->flags &= ~SQLITE_VdbeTrace;
- }
- }else
-
- if( sqliteStrICmp(zLeft, "full_column_names")==0 ){
- if( getBoolean(zRight) ){
- db->flags |= SQLITE_FullColNames;
- }else{
- db->flags &= ~SQLITE_FullColNames;
- }
- }else
-
- if( sqliteStrICmp(zLeft, "show_datatypes")==0 ){
- if( getBoolean(zRight) ){
- db->flags |= SQLITE_ReportTypes;
- }else{
- db->flags &= ~SQLITE_ReportTypes;
- }
- }else
-
- if( sqliteStrICmp(zLeft, "result_set_details")==0 ){
- if( getBoolean(zRight) ){
- db->flags |= SQLITE_ResultDetails;
- }else{
- db->flags &= ~SQLITE_ResultDetails;
- }
- }else
-
- if( sqliteStrICmp(zLeft, "count_changes")==0 ){
- if( getBoolean(zRight) ){
- db->flags |= SQLITE_CountRows;
- }else{
- db->flags &= ~SQLITE_CountRows;
- }
- }else
-
- if( sqliteStrICmp(zLeft, "empty_result_callbacks")==0 ){
- if( getBoolean(zRight) ){
- db->flags |= SQLITE_NullCallback;
- }else{
- db->flags &= ~SQLITE_NullCallback;
- }
- }else
-
- if( sqliteStrICmp(zLeft, "table_info")==0 ){
- Table *pTab;
- Vdbe *v;
- pTab = sqliteFindTable(db, zRight);
- if( pTab ) v = sqliteGetVdbe(pParse);
- if( pTab && v ){
- static VdbeOp tableInfoPreface[] = {
- { OP_ColumnName, 0, 0, "cid"},
- { OP_ColumnName, 1, 0, "name"},
- { OP_ColumnName, 2, 0, "type"},
- { OP_ColumnName, 3, 0, "notnull"},
- { OP_ColumnName, 4, 0, "dflt_value"},
- };
- int i;
- sqliteVdbeAddOpList(v, ArraySize(tableInfoPreface), tableInfoPreface);
- sqliteViewGetColumnNames(pParse, pTab);
- for(i=0; i<pTab->nCol; i++){
- sqliteVdbeAddOp(v, OP_Integer, i, 0);
- sqliteVdbeAddOp(v, OP_String, 0, 0);
- sqliteVdbeChangeP3(v, -1, pTab->aCol[i].zName, P3_STATIC);
- sqliteVdbeAddOp(v, OP_String, 0, 0);
- sqliteVdbeChangeP3(v, -1,
- pTab->aCol[i].zType ? pTab->aCol[i].zType : "numeric", P3_STATIC);
- sqliteVdbeAddOp(v, OP_Integer, pTab->aCol[i].notNull, 0);
- sqliteVdbeAddOp(v, OP_String, 0, 0);
- sqliteVdbeChangeP3(v, -1, pTab->aCol[i].zDflt, P3_STATIC);
- sqliteVdbeAddOp(v, OP_Callback, 5, 0);
- }
- }
- }else
-
- if( sqliteStrICmp(zLeft, "index_info")==0 ){
- Index *pIdx;
- Table *pTab;
- Vdbe *v;
- pIdx = sqliteFindIndex(db, zRight);
- if( pIdx ) v = sqliteGetVdbe(pParse);
- if( pIdx && v ){
- static VdbeOp tableInfoPreface[] = {
- { OP_ColumnName, 0, 0, "seqno"},
- { OP_ColumnName, 1, 0, "cid"},
- { OP_ColumnName, 2, 0, "name"},
- };
- int i;
- pTab = pIdx->pTable;
- sqliteVdbeAddOpList(v, ArraySize(tableInfoPreface), tableInfoPreface);
- for(i=0; i<pIdx->nColumn; i++){
- int cnum = pIdx->aiColumn[i];
- sqliteVdbeAddOp(v, OP_Integer, i, 0);
- sqliteVdbeAddOp(v, OP_Integer, cnum, 0);
- sqliteVdbeAddOp(v, OP_String, 0, 0);
- assert( pTab->nCol>cnum );
- sqliteVdbeChangeP3(v, -1, pTab->aCol[cnum].zName, P3_STATIC);
- sqliteVdbeAddOp(v, OP_Callback, 3, 0);
- }
- }
- }else
-
- if( sqliteStrICmp(zLeft, "index_list")==0 ){
- Index *pIdx;
- Table *pTab;
- Vdbe *v;
- pTab = sqliteFindTable(db, zRight);
- if( pTab ){
- v = sqliteGetVdbe(pParse);
- pIdx = pTab->pIndex;
- }
- if( pTab && pIdx && v ){
- int i = 0;
- static VdbeOp indexListPreface[] = {
- { OP_ColumnName, 0, 0, "seq"},
- { OP_ColumnName, 1, 0, "name"},
- { OP_ColumnName, 2, 0, "unique"},
- };
-
- sqliteVdbeAddOpList(v, ArraySize(indexListPreface), indexListPreface);
- while(pIdx){
- sqliteVdbeAddOp(v, OP_Integer, i, 0);
- sqliteVdbeAddOp(v, OP_String, 0, 0);
- sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC);
- sqliteVdbeAddOp(v, OP_Integer, pIdx->onError!=OE_None, 0);
- sqliteVdbeAddOp(v, OP_Callback, 3, 0);
- ++i;
- pIdx = pIdx->pNext;
- }
- }
- }else
-
-#ifndef NDEBUG
- if( sqliteStrICmp(zLeft, "parser_trace")==0 ){
- extern void sqliteParserTrace(FILE*, char *);
- if( getBoolean(zRight) ){
- sqliteParserTrace(stdout, "parser: ");
- }else{
- sqliteParserTrace(0, 0);
- }
- }else
-#endif
-
- if( sqliteStrICmp(zLeft, "integrity_check")==0 ){
- static VdbeOp checkDb[] = {
- { OP_SetInsert, 0, 0, "2"},
- { OP_Open, 0, 2, 0},
- { OP_Rewind, 0, 6, 0},
- { OP_Column, 0, 3, 0}, /* 3 */
- { OP_SetInsert, 0, 0, 0},
- { OP_Next, 0, 3, 0},
- { OP_IntegrityCk, 0, 0, 0}, /* 6 */
- { OP_ColumnName, 0, 0, "integrity_check"},
- { OP_Callback, 1, 0, 0},
- { OP_SetInsert, 1, 0, "2"},
- { OP_OpenAux, 1, 2, 0},
- { OP_Rewind, 1, 15, 0},
- { OP_Column, 1, 3, 0}, /* 12 */
- { OP_SetInsert, 1, 0, 0},
- { OP_Next, 1, 12, 0},
- { OP_IntegrityCk, 1, 1, 0}, /* 15 */
- { OP_Callback, 1, 0, 0},
- };
- Vdbe *v = sqliteGetVdbe(pParse);
- if( v==0 ) return;
- sqliteVdbeAddOpList(v, ArraySize(checkDb), checkDb);
- }else
-
- {}
- sqliteFree(zLeft);
- sqliteFree(zRight);
-}