diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/build.c | 1435 | ||||
-rw-r--r-- | src/dbbe.c | 510 | ||||
-rw-r--r-- | src/dbbe.h | 119 | ||||
-rw-r--r-- | src/main.c | 219 | ||||
-rw-r--r-- | src/shell.c | 384 | ||||
-rw-r--r-- | src/sqlite.h | 118 | ||||
-rw-r--r-- | src/sqliteInt.h | 235 | ||||
-rw-r--r-- | src/tclsqlite.c | 241 | ||||
-rw-r--r-- | src/tokenize.c | 375 | ||||
-rw-r--r-- | src/util.c | 445 | ||||
-rw-r--r-- | src/vdbe.c | 1973 | ||||
-rw-r--r-- | src/vdbe.h | 166 | ||||
-rw-r--r-- | src/where.c | 566 |
13 files changed, 6786 insertions, 0 deletions
diff --git a/src/build.c b/src/build.c new file mode 100644 index 000000000..ba4eb170d --- /dev/null +++ b/src/build.c @@ -0,0 +1,1435 @@ +/* +** Copyright (c) 1999, 2000 D. Richard Hipp +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public +** License as published by the Free Software Foundation; either +** version 2 of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public +** License along with this library; if not, write to the +** Free Software Foundation, Inc., 59 Temple Place - Suite 330, +** Boston, MA 02111-1307, USA. +** +** Author contact information: +** drh@hwaci.com +** http://www.hwaci.com/drh/ +** +************************************************************************* +** This file contains C code routines that are called by the parser +** when syntax rules are reduced. +** +** $Id: build.c,v 1.1 2000/05/29 14:26:01 drh Exp $ +*/ +#include "sqliteInt.h" + +/* +** This routine is called after a single SQL statement has been +** parsed and we want to execute the code to implement +** the statement. Prior action routines should have already +** constructed VDBE code to do the work of the SQL statement. +** This routine just has to execute the VDBE code. +** +** Note that if an error occurred, it might be the case that +** no VDBE code was generated. +*/ +void sqliteExec(Parse *pParse){ + if( pParse->pVdbe ){ + if( pParse->explain ){ + sqliteVdbeList(pParse->pVdbe, pParse->xCallback, pParse->pArg, + &pParse->zErrMsg); + }else{ + FILE *trace = (pParse->db->flags & SQLITE_VdbeTrace)!=0 ? stderr : 0; + sqliteVdbeTrace(pParse->pVdbe, trace); + sqliteVdbeExec(pParse->pVdbe, pParse->xCallback, pParse->pArg, + &pParse->zErrMsg); + } + sqliteVdbeDelete(pParse->pVdbe); + pParse->pVdbe = 0; + } +} + +/* +** Construct a new expression node and return a pointer to it. +*/ +Expr *sqliteExpr(int op, Expr *pLeft, Expr *pRight, Token *pToken){ + Expr *pNew; + pNew = sqliteMalloc( sizeof(Expr) ); + if( pNew==0 ) return 0; + pNew->op = op; + pNew->pLeft = pLeft; + pNew->pRight = pRight; + if( pToken ){ + pNew->token = *pToken; + }else{ + pNew->token.z = ""; + pNew->token.n = 0; + } + return pNew; +} + +/* +** Construct a new expression node for a function with multiple +** arguments. +*/ +Expr *sqliteExprFunction(ExprList *pList, Token *pToken){ + Expr *pNew; + pNew = sqliteMalloc( sizeof(Expr) ); + if( pNew==0 ) return 0; + pNew->op = TK_FUNCTION; + pNew->pList = pList; + if( pToken ){ + pNew->token = *pToken; + }else{ + pNew->token.z = ""; + pNew->token.n = 0; + } + return pNew; +} + +/* +** Recursively delete an expression tree. +*/ +void sqliteExprDelete(Expr *p){ + if( p==0 ) return; + if( p->pLeft ) sqliteExprDelete(p->pLeft); + if( p->pRight ) sqliteExprDelete(p->pRight); + sqliteFree(p); +} + +/* +** Locate the in-memory structure that describes the +** format of a particular database table given the name +** of that table. Return NULL if not found. +*/ +Table *sqliteFindTable(sqlite *db, char *zName){ + Table *pTable; + int h; + + h = sqliteHashNoCase(zName, 0) % N_HASH; + for(pTable=db->apTblHash[h]; pTable; pTable=pTable->pHash){ + if( sqliteStrICmp(pTable->zName, zName)==0 ) return pTable; + } + return 0; +} + +/* +** Locate the in-memory structure that describes the +** format of a particular index table given the name +** of that table. Return NULL if not found. +*/ +Index *sqliteFindIndex(sqlite *db, char *zName){ + Index *p; + int h; + + h = sqliteHashNoCase(zName, 0) % N_HASH; + for(p=db->apIdxHash[h]; p; p=p->pHash){ + if( sqliteStrICmp(p->zName, zName)==0 ) return p; + } + return 0; +} + +/* +** Remove the given index from the index hash table, and free +** its memory structures. +** +** The index is removed from the database hash table, but it is +** not unlinked from the table that is being indexed. Unlinking +** from the table must be done by the calling function. +*/ +static void sqliteDeleteIndex(sqlite *db, Index *pIndex){ + int h; + if( pIndex->zName ){ + h = sqliteHashNoCase(pIndex->zName, 0) % N_HASH; + if( db->apIdxHash[h]==pIndex ){ + db->apIdxHash[h] = pIndex->pHash; + }else{ + Index *p; + for(p=db->apIdxHash[h]; p && p->pHash!=pIndex; p=p->pHash){} + if( p && p->pHash==pIndex ){ + p->pHash = pIndex->pHash; + } + } + } + sqliteFree(pIndex); +} + +/* +** 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 does it destroy +** memory structures of the indices associated with the table. +*/ +void sqliteDeleteTable(sqlite *db, Table *pTable){ + int i; + Index *pIndex, *pNext; + if( pTable==0 ) return; + for(i=0; i<pTable->nCol; i++){ + if( pTable->azCol[i] ) sqliteFree(pTable->azCol[i]); + } + for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){ + pNext = pIndex->pNext; + sqliteDeleteIndex(db, pIndex); + } + sqliteFree(pTable->azCol); + sqliteFree(pTable); +} + +/* +** Construct the name of a user table from a token. +** +** Space to hold the name is obtained from sqliteMalloc() and must +** be freed by the calling function. +*/ +static char *sqliteTableNameFromToken(Token *pName){ + char *zName = 0; + sqliteSetNString(&zName, pName->z, pName->n, 0); + return zName; +} + +/* +** 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. +*/ +void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName){ + Table *pTable; + char *zName; + + pParse->sFirstToken = *pStart; + zName = sqliteTableNameFromToken(pName); + pTable = sqliteFindTable(pParse->db, zName); + if( pTable!=0 ){ + sqliteSetNString(&pParse->zErrMsg, "table \"", 0, pName->z, pName->n, + "\" already exists", 0, 0); + sqliteFree(zName); + pParse->nErr++; + return; + } + if( sqliteFindIndex(pParse->db, zName) ){ + sqliteSetString(&pParse->zErrMsg, "there is already an index named \"", + zName, "\"", 0); + sqliteFree(zName); + pParse->nErr++; + return; + } + pTable = sqliteMalloc( sizeof(Table) ); + if( pTable==0 ){ + sqliteSetString(&pParse->zErrMsg, "out of memory", 0); + pParse->nErr++; + return; + } + pTable->zName = zName; + pTable->pHash = 0; + pTable->nCol = 0; + pTable->azCol = 0; + pTable->pIndex = 0; + if( pParse->pNewTable ) sqliteDeleteTable(pParse->db, pParse->pNewTable); + pParse->pNewTable = pTable; +} + +/* +** Add a new column to the table currently being constructed. +*/ +void sqliteAddColumn(Parse *pParse, Token *pName){ + Table *p; + char **pz; + if( (p = pParse->pNewTable)==0 ) return; + if( (p->nCol & 0x7)==0 ){ + p->azCol = sqliteRealloc( p->azCol, p->nCol+8); + } + if( p->azCol==0 ){ + p->nCol = 0; + return; + } + pz = &p->azCol[p->nCol++]; + *pz = 0; + sqliteSetNString(pz, pName->z, pName->n, 0); +} + +/* +** This routine is called to report the final ")" that terminates +** a CREATE TABLE statement. +** +** The table structure is added to the internal hash tables. +** +** An entry for the table is made in the master table, unless +** initFlag==1. When initFlag==1, it means we are reading the +** master table because we just connected to the database, so +** the entry for this table already exists in the master table. +** We do not want to create it again. +*/ +void sqliteEndTable(Parse *pParse, Token *pEnd){ + Table *p; + int h; + + if( pParse->nErr ) return; + + /* Add the table to the in-memory representation of the database + */ + if( (p = pParse->pNewTable)!=0 && pParse->explain==0 ){ + h = sqliteHashNoCase(p->zName, 0) % N_HASH; + p->pHash = pParse->db->apTblHash[h]; + pParse->db->apTblHash[h] = p; + pParse->pNewTable = 0; + } + + /* If not initializing, then create the table on disk. + */ + if( !pParse->initFlag ){ + static VdbeOp addTable[] = { + { OP_Open, 0, 0, MASTER_NAME }, + { OP_New, 0, 0, 0}, + { OP_String, 0, 0, "table" }, + { OP_String, 0, 0, 0}, /* 2 */ + { OP_String, 0, 0, 0}, /* 3 */ + { OP_String, 0, 0, 0}, /* 4 */ + { OP_MakeRecord, 4, 0, 0}, + { OP_Put, 0, 0, 0}, + { OP_Close, 0, 0, 0}, + }; + int n, base; + Vdbe *v = pParse->pVdbe; + + if( v==0 ){ + v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); + } + if( v==0 ) return; + n = (int)pEnd->z - (int)pParse->sFirstToken.z + 1; + base = sqliteVdbeAddOpList(v, ArraySize(addTable), addTable); + sqliteVdbeChangeP3(v, base+2, p->zName, 0); + sqliteVdbeChangeP3(v, base+3, p->zName, 0); + sqliteVdbeChangeP3(v, base+4, pParse->sFirstToken.z, n); + } +} + +/* +** Given a token, look up a table with that name. If not found, leave +** an error for the parser to find and return NULL. +*/ +static Table *sqliteTableFromToken(Parse *pParse, Token *pTok){ + char *zName = sqliteTableNameFromToken(pTok); + Table *pTab = sqliteFindTable(pParse->db, zName); + sqliteFree(zName); + if( pTab==0 ){ + sqliteSetNString(&pParse->zErrMsg, "no such table: \"", 0, + pTok->z, pTok->n, "\"", 1, 0); + pParse->nErr++; + } + return pTab; +} + +/* +** This routine is called to do the work of a DROP TABLE statement. +*/ +void sqliteDropTable(Parse *pParse, Token *pName){ + Table *pTable; + int h; + Vdbe *v; + int base; + + pTable = sqliteTableFromToken(pParse, pName); + if( pTable==0 ) return; + if( pTable->readOnly ){ + sqliteSetString(&pParse->zErrMsg, "table \"", pTable->zName, + "\" may not be dropped", 0); + pParse->nErr++; + return; + } + + /* Generate code to remove the table and its reference in sys_master */ + v = pParse->pVdbe; + if( v==0 ){ + v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); + } + if( v ){ + static VdbeOp dropTable[] = { + { OP_Open, 0, 0, MASTER_NAME }, + { OP_ListOpen, 0, 0, 0}, + { OP_String, 0, 0, 0}, /* 2 */ + { OP_Next, 0, ADDR(10), 0}, /* 3 */ + { OP_Dup, 0, 0, 0}, + { OP_Field, 0, 2, 0}, + { OP_Ne, 0, ADDR(3), 0}, + { OP_Key, 0, 0, 0}, + { OP_ListWrite, 0, 0, 0}, + { OP_Goto, 0, ADDR(3), 0}, + { OP_ListRewind, 0, 0, 0}, /* 10 */ + { OP_ListRead, 0, ADDR(14), 0}, /* 11 */ + { OP_Delete, 0, 0, 0}, + { OP_Goto, 0, ADDR(11), 0}, + { OP_Destroy, 0, 0, 0}, /* 14 */ + { OP_Close, 0, 0, 0}, + }; + Index *pIdx; + base = sqliteVdbeAddOpList(v, ArraySize(dropTable), dropTable); + sqliteVdbeChangeP3(v, base+2, pTable->zName, 0); + sqliteVdbeChangeP3(v, base+14, pTable->zName, 0); + for(pIdx=pTable->pIndex; pIdx; pIdx=pIdx->pNext){ + sqliteVdbeAddOp(v, OP_Destroy, 0, 0, pIdx->zName, 0); + } + } + + /* Remove the table structure and free its memory. + ** + ** Exception: if the SQL statement began with the EXPLAIN keyword, + ** then no changes are made. + */ + if( !pParse->explain ){ + h = sqliteHashNoCase(pTable->zName, 0) % N_HASH; + if( pParse->db->apTblHash[h]==pTable ){ + pParse->db->apTblHash[h] = pTable->pHash; + }else{ + Table *p; + for(p=pParse->db->apTblHash[h]; p && p->pHash!=pTable; p=p->pHash){} + if( p && p->pHash==pTable ){ + p->pHash = pTable->pHash; + } + } + sqliteDeleteTable(pParse->db, pTable); + } +} + +/* +** Create a new index for an SQL table. pIndex is the name of the index +** and pTable is the name of the table that is to be indexed. Both will +** be NULL for a primary key. In that case, use pParse->pNewTable as the +** table to be indexed. +** +** pList is a list of fields to be indexed. pList will be NULL if the +** most recently added field of the table is labeled as the primary key. +*/ +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 */ + IdList *pList, /* A list of fields to be indexed */ + Token *pStart, /* The CREATE token that begins a CREATE TABLE statement */ + Token *pEnd /* The ")" that closes the CREATE INDEX statement */ +){ + Table *pTab; /* Table to be indexed */ + Index *pIndex; /* The index to be created */ + char *zName = 0; + int i, j, h; + Token nullId; /* Fake token for an empty ID list */ + + /* + ** Find the table that is to be indexed. Return early if not found. + */ + if( pTable!=0 ){ + pTab = sqliteTableFromToken(pParse, pTable); + }else{ + pTab = pParse->pNewTable; + } + if( pTab==0 || pParse->nErr ) goto exit_create_index; + if( pTab->readOnly ){ + sqliteSetString(&pParse->zErrMsg, "table \"", pTab->zName, + "\" may not have new indices added", 0); + pParse->nErr++; + goto exit_create_index; + } + + /* + ** Find the name of the index. Make sure there is not already another + ** index or table with the same name. + */ + if( pName ){ + zName = sqliteTableNameFromToken(pName); + }else{ + zName = 0; + sqliteSetString(&zName, pTab->zName, "__primary_key", 0); + } + if( sqliteFindIndex(pParse->db, zName) ){ + sqliteSetString(&pParse->zErrMsg, "index \"", zName, + "\" already exists", 0); + pParse->nErr++; + goto exit_create_index; + } + if( sqliteFindTable(pParse->db, zName) ){ + sqliteSetString(&pParse->zErrMsg, "there is already a table named \"", + zName, "\"", 0); + pParse->nErr++; + goto exit_create_index; + } + + /* If pList==0, it means this routine was called to make a primary + ** key out of the last field added to the table under construction. + ** So create a fake list to simulate this. + */ + if( pList==0 ){ + nullId.z = pTab->azCol[pTab->nCol-1]; + nullId.n = strlen(nullId.z); + pList = sqliteIdListAppend(0, &nullId); + if( pList==0 ) goto exit_create_index; + } + + /* + ** Allocate the index structure. + */ + pIndex = sqliteMalloc( sizeof(Index) + strlen(zName) + + sizeof(int)*pList->nId ); + if( pIndex==0 ){ + sqliteSetString(&pParse->zErrMsg, "out of memory", 0); + pParse->nErr++; + goto exit_create_index; + } + pIndex->aiField = (int*)&pIndex[1]; + pIndex->zName = (char*)&pIndex->aiField[pList->nId]; + strcpy(pIndex->zName, zName); + pIndex->pTable = pTab; + pIndex->nField = pList->nId; + + /* Scan the names of the fields of the table to be indexed and + ** load the field indices into the Index structure. Report an error + ** if any field is not found. + */ + for(i=0; i<pList->nId; i++){ + for(j=0; j<pTab->nCol; j++){ + if( sqliteStrICmp(pList->a[i].zName, pTab->azCol[j])==0 ) break; + } + if( j>=pTab->nCol ){ + sqliteSetString(&pParse->zErrMsg, "table being indexed has no field " + "named \"", pList->a[i].zName, "\"", 0); + pParse->nErr++; + sqliteFree(pIndex); + goto exit_create_index; + } + pIndex->aiField[i] = j; + } + + /* Link the new Index structure to its table and to the other + ** in-memory database structures. + */ + if( pParse->explain==0 ){ + h = sqliteHashNoCase(pIndex->zName, 0) % N_HASH; + pIndex->pHash = pParse->db->apIdxHash[h]; + pParse->db->apIdxHash[h] = pIndex; + pIndex->pNext = pTab->pIndex; + pTab->pIndex = pIndex; + } + + /* If the initFlag 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 initFlag is 0 when the user first enters a CREATE INDEX + ** command. The initFlag 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( pParse->initFlag==0 ){ + static VdbeOp addTable[] = { + { OP_Open, 0, 0, MASTER_NAME}, + { OP_New, 0, 0, 0}, + { OP_String, 0, 0, "index"}, + { OP_String, 0, 0, 0}, /* 2 */ + { OP_String, 0, 0, 0}, /* 3 */ + { OP_String, 0, 0, 0}, /* 4 */ + { OP_MakeRecord, 4, 0, 0}, + { OP_Put, 0, 0, 0}, + { OP_Close, 0, 0, 0}, + }; + int n; + Vdbe *v = pParse->pVdbe; + int lbl1, lbl2; + int i; + + if( v==0 ){ + v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); + } + if( v==0 ) goto exit_create_index; + if( pStart && pEnd ){ + int base; + n = (int)pEnd->z - (int)pStart->z + 1; + base = sqliteVdbeAddOpList(v, ArraySize(addTable), addTable); + sqliteVdbeChangeP3(v, base+2, pIndex->zName, 0); + sqliteVdbeChangeP3(v, base+3, pTab->zName, 0); + sqliteVdbeChangeP3(v, base+4, pStart->z, n); + } + sqliteVdbeAddOp(v, OP_Open, 0, 0, pTab->zName, 0); + sqliteVdbeAddOp(v, OP_Open, 1, 0, pIndex->zName, 0); + lbl1 = sqliteVdbeMakeLabel(v); + lbl2 = sqliteVdbeMakeLabel(v); + sqliteVdbeAddOp(v, OP_Next, 0, lbl2, 0, lbl1); + sqliteVdbeAddOp(v, OP_Key, 0, 0, 0, 0); + for(i=0; i<pIndex->nField; i++){ + sqliteVdbeAddOp(v, OP_Field, 0, pIndex->aiField[i], 0, 0); + } + sqliteVdbeAddOp(v, OP_MakeKey, pIndex->nField, 0, 0, 0); + sqliteVdbeAddOp(v, OP_PutIdx, 1, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Goto, 0, lbl1, 0, 0); + sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, lbl2); + sqliteVdbeAddOp(v, OP_Close, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Close, 1, 0, 0, 0); + } + + /* Reclaim memory on an EXPLAIN call. + */ + if( pParse->explain ){ + sqliteFree(pIndex); + } + + /* Clean up before exiting */ +exit_create_index: + sqliteIdListDelete(pList); + sqliteFree(zName); + return; +} + +/* +** This routine will drop an existing named index. +*/ +void sqliteDropIndex(Parse *pParse, Token *pName){ + Index *pIndex; + char *zName; + Vdbe *v; + + zName = sqliteTableNameFromToken(pName); + pIndex = sqliteFindIndex(pParse->db, zName); + sqliteFree(zName); + if( pIndex==0 ){ + sqliteSetNString(&pParse->zErrMsg, "no such index: \"", 0, + pName->z, pName->n, "\"", 1, 0); + pParse->nErr++; + return; + } + + /* Generate code to remove the index and from the master table */ + v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); + if( v ){ + static VdbeOp dropIndex[] = { + { OP_Open, 0, 0, MASTER_NAME}, + { OP_ListOpen, 0, 0, 0}, + { OP_String, 0, 0, 0}, /* 2 */ + { OP_Next, 0, ADDR(9), 0}, /* 3 */ + { OP_Dup, 0, 0, 0}, + { OP_Field, 0, 1, 0}, + { OP_Ne, 0, ADDR(3), 0}, + { OP_Key, 0, 0, 0}, + { OP_Delete, 0, 0, 0}, + { OP_Destroy, 0, 0, 0}, /* 9 */ + { OP_Close, 0, 0, 0}, + }; + int base; + + base = sqliteVdbeAddOpList(v, ArraySize(dropIndex), dropIndex); + sqliteVdbeChangeP3(v, base+2, pIndex->zName, 0); + sqliteVdbeChangeP3(v, base+9, pIndex->zName, 0); + } + + /* Remove the index structure and free its memory. Except if the + ** EXPLAIN keyword is present, no changes are made. + */ + if( !pParse->explain ){ + if( pIndex->pTable->pIndex==pIndex ){ + pIndex->pTable->pIndex = pIndex->pNext; + }else{ + Index *p; + for(p=pIndex->pTable->pIndex; p && p->pNext!=pIndex; p=p->pNext){} + if( p && p->pNext==pIndex ){ + p->pNext = pIndex->pNext; + } + } + sqliteDeleteIndex(pParse->db, pIndex); + } +} + +/* +** Add a new element to the end of an expression list. If pList is +** initially NULL, then create a new expression list. +*/ +ExprList *sqliteExprListAppend(ExprList *pList, Expr *pExpr, Token *pName){ + int i; + if( pList==0 ){ + pList = sqliteMalloc( sizeof(ExprList) ); + } + if( pList==0 ) return 0; + if( (pList->nExpr & 7)==0 ){ + int n = pList->nExpr + 8; + pList->a = sqliteRealloc(pList->a, n*sizeof(pList->a[0])); + if( pList->a==0 ){ + pList->nExpr = 0; + return pList; + } + } + i = pList->nExpr++; + pList->a[i].pExpr = pExpr; + pList->a[i].zName = 0; + if( pName ){ + sqliteSetNString(&pList->a[i].zName, pName->z, pName->n, 0); + } + return pList; +} + +/* +** Delete an entire expression list. +*/ +void sqliteExprListDelete(ExprList *pList){ + int i; + if( pList==0 ) return; + for(i=0; i<pList->nExpr; i++){ + sqliteExprDelete(pList->a[i].pExpr); + sqliteFree(pList->a[i].zName); + } + sqliteFree(pList->a); + sqliteFree(pList); +} + +/* +** Append a new element to the given IdList. Create a new IdList if +** need be. +*/ +IdList *sqliteIdListAppend(IdList *pList, Token *pToken){ + if( pList==0 ){ + pList = sqliteMalloc( sizeof(IdList) ); + if( pList==0 ) return 0; + } + if( (pList->nId & 7)==0 ){ + pList->a = sqliteRealloc(pList->a, (pList->nId+8)*sizeof(pList->a[0]) ); + if( pList->a==0 ){ + pList->nId = 0; + return pList; + } + } + memset(&pList->a[pList->nId], 0, sizeof(pList->a[0])); + if( pToken ){ + sqliteSetNString(&pList->a[pList->nId].zName, pToken->z, pToken->n, 0); + } + pList->nId++; + return pList; +} + +/* +** Add an alias to the last identifier on the given identifier list. +*/ +void sqliteIdListAddAlias(IdList *pList, Token *pToken){ + if( pList && pList->nId>0 ){ + int i = pList->nId - 1; + sqliteSetNString(&pList->a[i].zAlias, pToken->z, pToken->n, 0); + } +} + +/* +** Delete an entire IdList +*/ +void sqliteIdListDelete(IdList *pList){ + int i; + if( pList==0 ) return; + for(i=0; i<pList->nId; i++){ + sqliteFree(pList->a[i].zName); + sqliteFree(pList->a[i].zAlias); + } + sqliteFree(pList->a); + sqliteFree(pList); +} + +/* +** This routine is call to handle SQL of the following form: +** +** insert into TABLE (IDLIST) values(EXPRLIST) +** +** The parameters are the table name and the expression list. +*/ +void sqliteInsert( + Parse *pParse, /* Parser context */ + Token *pTableName, /* Name of table into which we are inserting */ + ExprList *pList, /* List of values to be inserted */ + IdList *pField /* Field name corresponding to pList. Might be NULL */ +){ + Table *pTab; + char *zTab; + int i, j; + Vdbe *v; + + zTab = sqliteTableNameFromToken(pTableName); + pTab = sqliteFindTable(pParse->db, zTab); + sqliteFree(zTab); + if( pTab==0 ){ + sqliteSetNString(&pParse->zErrMsg, "no such table: \"", 0, + pTableName->z, pTableName->n, "\"", 1, 0); + pParse->nErr++; + goto insert_cleanup; + } + if( pTab->readOnly ){ + sqliteSetString(&pParse->zErrMsg, "table \"", pTab->zName, + "\" may not be modified", 0); + pParse->nErr++; + goto insert_cleanup; + } + if( pField==0 && pList->nExpr!=pTab->nCol ){ + char zNum1[30]; + char zNum2[30]; + sprintf(zNum1,"%d", pList->nExpr); + sprintf(zNum2,"%d", pTab->nCol); + sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName, + " has ", zNum2, " columns but only ", + zNum1, " values were supplied", 0); + pParse->nErr++; + goto insert_cleanup; + } + if( pField!=0 && pList->nExpr!=pField->nId ){ + char zNum1[30]; + char zNum2[30]; + sprintf(zNum1,"%d", pList->nExpr); + sprintf(zNum2,"%d", pTab->nCol); + sqliteSetString(&pParse->zErrMsg, zNum1, " values for ", + zNum2, " columns", 0); + pParse->nErr++; + goto insert_cleanup; + } + if( pField ){ + for(i=0; i<pField->nId; i++){ + pField->a[i].idx = -1; + } + for(i=0; i<pField->nId; i++){ + for(j=0; j<pTab->nCol; j++){ + if( sqliteStrICmp(pField->a[i].zName, pTab->azCol[j])==0 ){ + pField->a[i].idx = j; + break; + } + } + if( j>=pTab->nCol ){ + sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName, + " has no column named ", pField->a[i].zName, 0); + pParse->nErr++; + goto insert_cleanup; + } + } + } + v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); + if( v ){ + Index *pIdx; + sqliteVdbeAddOp(v, OP_Open, 0, 0, pTab->zName, 0); + sqliteVdbeAddOp(v, OP_New, 0, 0, 0, 0); + if( pTab->pIndex ){ + sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0); + } + for(i=0; i<pTab->nCol; i++){ + if( pField==0 ){ + j = i; + }else{ + for(j=0; j<pField->nId; j++){ + if( pField->a[j].idx==i ) break; + } + } + if( pField && j>=pField->nId ){ + sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0); + }else{ + sqliteExprCode(pParse, pList->a[j].pExpr); + } + } + sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Put, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Close, 0, 0, 0, 0); + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + if( pIdx->pNext ){ + sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0); + } + sqliteVdbeAddOp(v, OP_Open, 0, 0, pIdx->zName, 0); + for(i=0; i<pIdx->nField; i++){ + int idx = pIdx->aiField[i]; + if( pField==0 ){ + j = idx; + }else{ + for(j=0; j<pField->nId; j++){ + if( pField->a[j].idx==idx ) break; + } + } + if( pField && j>=pField->nId ){ + sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0); + }else{ + sqliteExprCode(pParse, pList->a[j].pExpr); + } + } + sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0); + sqliteVdbeAddOp(v, OP_PutIdx, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Close, 0, 0, 0, 0); + } + } + +insert_cleanup: + sqliteExprListDelete(pList); + sqliteIdListDelete(pField); +} + +/* +** This routine walks an expression tree and resolves references to +** table fields. Nodes of the form ID.ID or ID resolve into an +** index to the table in the table list and a field offset. The opcode +** for such nodes is changed to TK_FIELD. The iTable value is changed +** to the index of the referenced table in pTabList, and the iField value +** is changed to the index of the field of the referenced table. +** +** Unknown fields or tables provoke an error. The function returns +** the number of errors seen and leaves an error message on pParse->zErrMsg. +*/ +int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){ + if( pExpr==0 ) return 0; + switch( pExpr->op ){ + /* A lone identifier */ + case TK_ID: { + int cnt = 0; /* Number of matches */ + int i; /* Loop counter */ + char *z = pExpr->token.z; + int n = pExpr->token.n; + for(i=0; i<pTabList->nId; i++){ + int j; + Table *pTab = pTabList->a[i].pTab; + if( pTab==0 ) continue; + for(j=0; j<pTab->nCol; j++){ + if( sqliteStrNICmp(pTab->azCol[j], z, n)==0 ){ + cnt++; + pExpr->iTable = i; + pExpr->iField = j; + } + } + } + if( cnt==0 ){ + sqliteSetNString(&pParse->zErrMsg, "unknown field name: \"", -1, + pExpr->token.z, pExpr->token.n, "\"", -1, 0); + pParse->nErr++; + return 1; + }else if( cnt>1 ){ + sqliteSetNString(&pParse->zErrMsg, "ambiguous field name: \"", -1, + pExpr->token.z, pExpr->token.n, "\"", -1, 0); + pParse->nErr++; + return 1; + } + pExpr->op = TK_FIELD; + break; + } + + /* A table name and field name: ID.ID */ + case TK_DOT: { + int cnt = 0; /* Number of matches */ + int i; /* Loop counter */ + Expr *pLeft, *pRight; /* Left and right subbranches of the expr */ + int n; /* Length of an identifier */ + char *z; /* Text of an identifier */ + + pLeft = pExpr->pLeft; + pRight = pExpr->pRight; + assert( pLeft && pLeft->op==TK_ID ); + assert( pRight && pRight->op==TK_ID ); + n = pRight->token.n; + z = pRight->token.z; + for(i=0; i<pTabList->nId; i++){ + int j; + char *zTab; + Table *pTab = pTabList->a[i].pTab; + if( pTab==0 ) continue; + if( pTabList->a[i].zAlias ){ + zTab = pTabList->a[i].zAlias; + }else{ + zTab = pTab->zName; + } + if( sqliteStrNICmp(zTab, pLeft->token.z, pLeft->token.n)!=0 ) continue; + for(j=0; j<pTab->nCol; j++){ + if( sqliteStrNICmp(pTab->azCol[j], z, n)==0 ){ + cnt++; + pExpr->iTable = i; + pExpr->iField = j; + } + } + } + if( cnt==0 ){ + sqliteSetNString(&pParse->zErrMsg, "unknown field name: \"", -1, + pLeft->token.z, pLeft->token.n, ".", 1, z, n, "\"", 1, 0); + pParse->nErr++; + return 1; + }else if( cnt>1 ){ + sqliteSetNString(&pParse->zErrMsg, "ambiguous field name: \"", -1, + pExpr->token.z, pExpr->token.n, ".", 1, z, n, "\"", 1, 0); + pParse->nErr++; + return 1; + } + sqliteExprDelete(pLeft); + pExpr->pLeft = 0; + sqliteExprDelete(pRight); + pExpr->pRight = 0; + pExpr->op = TK_FIELD; + break; + } + + /* For all else, just recursively walk the tree */ + default: { + if( pExpr->pLeft + && sqliteExprResolveIds(pParse, pTabList, pExpr->pLeft) ){ + return 1; + } + if( pExpr->pRight + && sqliteExprResolveIds(pParse, pTabList, pExpr->pRight) ){ + return 1; + } + if( pExpr->pList ){ + int i; + ExprList *pList = pExpr->pList; + for(i=0; i<pList->nExpr; i++){ + if( sqliteExprResolveIds(pParse, pTabList, pList->a[i].pExpr) ){ + return 1; + } + } + } + } + } + return 0; +} + +/* +** Process a SELECT statement. +*/ +void sqliteSelect( + Parse *pParse, /* The parser context */ + ExprList *pEList, /* List of fields to extract. NULL means "*" */ + IdList *pTabList, /* List of tables to select from */ + Expr *pWhere, /* The WHERE clause. May be NULL */ + ExprList *pOrderBy /* The ORDER BY clause. May be NULL */ +){ + int i, j; + WhereInfo *pWInfo; + Vdbe *v; + + if( pParse->nErr>0 ) goto select_cleanup; + + /* Look up every table in the table list. + */ + for(i=0; i<pTabList->nId; i++){ + pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName); + if( pTabList->a[i].pTab==0 ){ + sqliteSetString(&pParse->zErrMsg, "unknown table \"", + pTabList->a[i].zName, "\"", 0); + pParse->nErr++; + goto select_cleanup; + } + } + + /* If the list of fields to retrieve is "*" then replace it with + ** a list of all fields from all tables. + */ + if( pEList==0 ){ + for(i=0; i<pTabList->nId; i++){ + Table *pTab = pTabList->a[i].pTab; + for(j=0; j<pTab->nCol; j++){ + Expr *pExpr = sqliteExpr(TK_FIELD, 0, 0, 0); + pExpr->iTable = i; + pExpr->iField = j; + pEList = sqliteExprListAppend(pEList, pExpr, 0); + } + } + } + + /* Resolve the field names in all the expressions. + */ + for(i=0; i<pEList->nExpr; i++){ + if( sqliteExprResolveIds(pParse, pTabList, pEList->a[i].pExpr) ){ + goto select_cleanup; + } + } + if( pWhere && sqliteExprResolveIds(pParse, pTabList, pWhere) ){ + goto select_cleanup; + } + if( pOrderBy ){ + for(i=0; i<pOrderBy->nExpr; i++){ + if( sqliteExprResolveIds(pParse, pTabList, pOrderBy->a[i].pExpr) ){ + goto select_cleanup; + } + } + } + + /* Begin generating code. + */ + v = pParse->pVdbe; + if( v==0 ){ + v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); + } + if( v==0 ) goto select_cleanup; + if( pOrderBy ){ + sqliteVdbeAddOp(v, OP_SortOpen, 0, 0, 0, 0); + } + + + /* Identify column names + */ + sqliteVdbeAddOp(v, OP_ColumnCount, pEList->nExpr, 0, 0, 0); + for(i=0; i<pEList->nExpr; i++){ + Expr *p; + if( pEList->a[i].zName ){ + char *zName = pEList->a[i].zName; + int addr = sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0); + if( zName[0]=='\'' || zName[0]=='"' ){ + sqliteVdbeDequoteP3(v, addr); + } + continue; + } + p = pEList->a[i].pExpr; + if( p->op!=TK_FIELD ){ + char zName[30]; + sprintf(zName, "field%d", i+1); + sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0); + }else{ + if( pTabList->nId>1 ){ + char *zName = 0; + Table *pTab = pTabList->a[p->iTable].pTab; + sqliteSetString(&zName, pTab->zName, ".", + pTab->azCol[p->iField], 0); + sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0); + sqliteFree(zName); + }else{ + Table *pTab = pTabList->a[0].pTab; + sqliteVdbeAddOp(v, OP_ColumnName, i, 0, pTab->azCol[p->iField], 0); + } + } + } + + /* Begin the database scan + */ + pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 0); + if( pWInfo==0 ) goto select_cleanup; + + /* Pull the requested fields. + */ + for(i=0; i<pEList->nExpr; i++){ + sqliteExprCode(pParse, pEList->a[i].pExpr); + } + + /* If there is no ORDER BY clause, then we can invoke the callback + ** right away. If there is an ORDER BY, then we need to put the + ** data into an appropriate sorter record. + */ + if( pOrderBy==0 ){ + sqliteVdbeAddOp(v, OP_Callback, pEList->nExpr, 0, 0, 0); + }else{ + char *zSortOrder; + sqliteVdbeAddOp(v, OP_SortMakeRec, pEList->nExpr, 0, 0, 0); + zSortOrder = sqliteMalloc( pOrderBy->nExpr + 1 ); + if( zSortOrder==0 ) goto select_cleanup; + for(i=0; i<pOrderBy->nExpr; i++){ + zSortOrder[i] = pOrderBy->a[i].idx ? '-' : '+'; + sqliteExprCode(pParse, pOrderBy->a[i].pExpr); + } + zSortOrder[pOrderBy->nExpr] = 0; + sqliteVdbeAddOp(v, OP_SortMakeKey, pOrderBy->nExpr, 0, zSortOrder, 0); + sqliteVdbeAddOp(v, OP_SortPut, 0, 0, 0, 0); + } + + /* End the database scan loop. + */ + sqliteWhereEnd(pWInfo); + + /* If there is an ORDER BY clause, then we need to sort the results + ** and send them to the callback one by one. + */ + if( pOrderBy ){ + int end = sqliteVdbeMakeLabel(v); + int addr; + sqliteVdbeAddOp(v, OP_Sort, 0, 0, 0, 0); + addr = sqliteVdbeAddOp(v, OP_SortNext, 0, end, 0, 0); + sqliteVdbeAddOp(v, OP_SortCallback, pEList->nExpr, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0); + sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, end); + } + + /* Always execute the following code before exiting, in order to + ** release resources. + */ +select_cleanup: + sqliteExprListDelete(pEList); + sqliteIdListDelete(pTabList); + sqliteExprDelete(pWhere); + sqliteExprListDelete(pOrderBy); + return; +} + +/* +** Process a DELETE FROM statement. +*/ +void sqliteDeleteFrom( + Parse *pParse, /* The parser context */ + Token *pTableName, /* The table from which we should delete things */ + Expr *pWhere /* The WHERE clause. May be null */ +){ + Vdbe *v; /* The virtual database engine */ + Table *pTab; /* The table from which records will be deleted */ + IdList *pTabList; /* An ID list holding pTab and nothing else */ + int end, addr; /* A couple addresses of generated code */ + int i; /* Loop counter */ + WhereInfo *pWInfo; /* Information about the WHERE clause */ + Index *pIdx; /* For looping over indices of the table */ + + /* Locate the table which we want to update. This table has to be + ** put in an IdList structure because some of the subroutines will + ** will be calling are designed to work with multiple tables and expect + ** an IdList* parameter instead of just a Table* parameger. + */ + pTabList = sqliteIdListAppend(0, pTableName); + for(i=0; i<pTabList->nId; i++){ + pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName); + if( pTabList->a[i].pTab==0 ){ + sqliteSetString(&pParse->zErrMsg, "unknown table \"", + pTabList->a[i].zName, "\"", 0); + pParse->nErr++; + goto delete_from_cleanup; + } + if( pTabList->a[i].pTab->readOnly ){ + sqliteSetString(&pParse->zErrMsg, "table \"", pTabList->a[i].zName, + "\" may not be modified", 0); + pParse->nErr++; + goto delete_from_cleanup; + } + } + pTab = pTabList->a[0].pTab; + + /* Resolve the field names in all the expressions. + */ + if( pWhere && sqliteExprResolveIds(pParse, pTabList, pWhere) ){ + goto delete_from_cleanup; + } + + /* Begin generating code. + */ + v = pParse->pVdbe; + if( v==0 ){ + v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); + } + if( v==0 ) goto delete_from_cleanup; + + /* Begin the database scan + */ + sqliteVdbeAddOp(v, OP_ListOpen, 0, 0, 0, 0); + pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 1); + if( pWInfo==0 ) goto delete_from_cleanup; + + /* Remember the index of every item to be deleted. + */ + sqliteVdbeAddOp(v, OP_ListWrite, 0, 0, 0, 0); + + /* End the database scan loop. + */ + sqliteWhereEnd(pWInfo); + + /* Delete every item identified in the list. + */ + sqliteVdbeAddOp(v, OP_ListRewind, 0, 0, 0, 0); + for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ + sqliteVdbeAddOp(v, OP_Open, i, 0, pIdx->zName, 0); + } + end = sqliteVdbeMakeLabel(v); + addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end, 0, 0); + if( pTab->pIndex ){ + sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Fetch, 0, 0, 0, 0); + for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ + int j; + sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0); + for(j=0; j<pIdx->nField; j++){ + sqliteVdbeAddOp(v, OP_Field, 0, pIdx->aiField[j], 0, 0); + } + sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0); + sqliteVdbeAddOp(v, OP_DeleteIdx, i, 0, 0, 0); + } + } + sqliteVdbeAddOp(v, OP_Delete, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0); + sqliteVdbeAddOp(v, OP_ListClose, 0, 0, 0, end); + +delete_from_cleanup: + sqliteIdListDelete(pTabList); + sqliteExprDelete(pWhere); + return; +} + +/* +** Process an UPDATE statement. +*/ +void sqliteUpdate( + Parse *pParse, /* The parser context */ + Token *pTableName, /* The table in which we should change things */ + ExprList *pChanges, /* Things to be changed */ + Expr *pWhere /* The WHERE clause. May be null */ +){ + int i, j; /* Loop counters */ + Table *pTab; /* The table to be updated */ + IdList *pTabList = 0; /* List containing only pTab */ + int end, addr; /* A couple of addresses in the generated code */ + WhereInfo *pWInfo; /* Information about the WHERE clause */ + Vdbe *v; /* The virtual database engine */ + Index *pIdx; /* For looping over indices */ + int nIdx; /* Number of indices that need updating */ + Index **apIdx = 0; /* An array of indices that need updating too */ + int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the + ** an expression for the i-th field of the table. + ** aXRef[i]==-1 if the i-th field is not changed. */ + + /* Locate the table which we want to update. This table has to be + ** put in an IdList structure because some of the subroutines will + ** will be calling are designed to work with multiple tables and expect + ** an IdList* parameter instead of just a Table* parameger. + */ + pTabList = sqliteIdListAppend(0, pTableName); + for(i=0; i<pTabList->nId; i++){ + pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName); + if( pTabList->a[i].pTab==0 ){ + sqliteSetString(&pParse->zErrMsg, "unknown table \"", + pTabList->a[i].zName, "\"", 0); + pParse->nErr++; + goto update_cleanup; + } + if( pTabList->a[i].pTab->readOnly ){ + sqliteSetString(&pParse->zErrMsg, "table \"", pTabList->a[i].zName, + "\" may not be modified", 0); + pParse->nErr++; + goto update_cleanup; + } + } + pTab = pTabList->a[0].pTab; + aXRef = sqliteMalloc( sizeof(int) * pTab->nCol ); + if( aXRef==0 ) goto update_cleanup; + for(i=0; i<pTab->nCol; i++) aXRef[i] = -1; + + /* Resolve the field names in all the expressions in both the + ** WHERE clause and in the new values. Also find the field index + ** for each field to be updated in the pChanges array. + */ + if( pWhere && sqliteExprResolveIds(pParse, pTabList, pWhere) ){ + goto update_cleanup; + } + for(i=0; i<pChanges->nExpr; i++){ + if( sqliteExprResolveIds(pParse, pTabList, pChanges->a[i].pExpr) ){ + goto update_cleanup; + } + for(j=0; j<pTab->nCol; j++){ + if( strcmp(pTab->azCol[j], pChanges->a[i].zName)==0 ){ + pChanges->a[i].idx = j; + aXRef[j] = i; + break; + } + } + if( j>=pTab->nCol ){ + sqliteSetString(&pParse->zErrMsg, "no such field: \"", + pChanges->a[i].zName, "\"", 0); + pParse->nErr++; + goto update_cleanup; + } + } + + /* Allocate memory for the array apIdx[] and fill it pointers to every + ** index that needs to be updated. Indices only need updating if their + ** key includes one of the fields named in pChanges. + */ + for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + for(i=0; i<pIdx->nField; i++){ + if( aXRef[pIdx->aiField[i]]>=0 ) break; + } + if( i<pIdx->nField ) nIdx++; + } + apIdx = sqliteMalloc( sizeof(Index*) * nIdx ); + if( apIdx==0 ) goto update_cleanup; + for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + for(i=0; i<pIdx->nField; i++){ + if( aXRef[pIdx->aiField[i]]>=0 ) break; + } + if( i<pIdx->nField ) apIdx[nIdx++] = pIdx; + } + + /* Begin generating code. + */ + v = pParse->pVdbe; + if( v==0 ){ + v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); + } + if( v==0 ) goto update_cleanup; + + /* Begin the database scan + */ + sqliteVdbeAddOp(v, OP_ListOpen, 0, 0, 0, 0); + pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 1); + if( pWInfo==0 ) goto update_cleanup; + + /* Remember the index of every item to be updated. + */ + sqliteVdbeAddOp(v, OP_ListWrite, 0, 0, 0, 0); + + /* End the database scan loop. + */ + sqliteWhereEnd(pWInfo); + + /* Rewind the list of records that need to be updated and + ** open every index that needs updating. + */ + sqliteVdbeAddOp(v, OP_ListRewind, 0, 0, 0, 0); + for(i=0; i<nIdx; i++){ + sqliteVdbeAddOp(v, OP_Open, i+1, 0, apIdx[i]->zName, 0); + } + + /* Loop over every record that needs updating. We have to load + ** the old data for each record to be updated because some fields + ** might not change and we will need to copy the old value, therefore. + ** Also, the old data is needed to delete the old index entires. + */ + end = sqliteVdbeMakeLabel(v); + addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end, 0, 0); + sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Fetch, 0, 0, 0, 0); + + /* Delete the old indices for the current record. + */ + for(i=0; i<nIdx; i++){ + sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0); + pIdx = apIdx[i]; + for(j=0; j<pIdx->nField; j++){ + sqliteVdbeAddOp(v, OP_Field, 0, pIdx->aiField[j], 0, 0); + } + sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0); + sqliteVdbeAddOp(v, OP_DeleteIdx, i+1, 0, 0, 0); + } + + /* Compute a completely new data for this record. + */ + for(i=0; i<pTab->nCol; i++){ + j = aXRef[i]; + if( j<0 ){ + sqliteVdbeAddOp(v, OP_Field, 0, i, 0, 0); + }else{ + sqliteExprCode(pParse, pChanges->a[j].pExpr); + } + } + + /* Insert new index entries that correspond to the new data + */ + for(i=0; i<nIdx; i++){ + sqliteVdbeAddOp(v, OP_Dup, pTab->nCol, 0, 0, 0); /* The KEY */ + pIdx = apIdx[i]; + for(j=0; j<pIdx->nField; j++){ + sqliteVdbeAddOp(v, OP_Dup, j+pTab->nCol-pIdx->aiField[j], 0, 0, 0); + } + sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0); + sqliteVdbeAddOp(v, OP_PutIdx, i+1, 0, 0, 0); + } + + /* Write the new data back into the database. + */ + sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Put, 0, 0, 0, 0); + + /* Repeat the above with the next record to be updated, until + ** all record selected by the WHERE clause have been updated. + */ + sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0); + sqliteVdbeAddOp(v, OP_ListClose, 0, 0, 0, end); + +update_cleanup: + sqliteFree(apIdx); + sqliteFree(aXRef); + sqliteIdListDelete(pTabList); + sqliteExprListDelete(pChanges); + sqliteExprDelete(pWhere); + return; +} diff --git a/src/dbbe.c b/src/dbbe.c new file mode 100644 index 000000000..808d3429f --- /dev/null +++ b/src/dbbe.c @@ -0,0 +1,510 @@ +/* +** Copyright (c) 1999, 2000 D. Richard Hipp +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public +** License as published by the Free Software Foundation; either +** version 2 of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public +** License along with this library; if not, write to the +** Free Software Foundation, Inc., 59 Temple Place - Suite 330, +** Boston, MA 02111-1307, USA. +** +** Author contact information: +** drh@hwaci.com +** http://www.hwaci.com/drh/ +** +************************************************************************* +** This file contains code to implement the database baseend (DBBE) +** for sqlite. The database backend is the interface between +** sqlite and the code that does the actually reading and writing +** of information to the disk. +** +** This file uses GDBM as the database backend. It should be +** relatively simple to convert to a different database such +** as NDBM, SDBM, or BerkeleyDB. +** +** $Id: dbbe.c,v 1.1 2000/05/29 14:26:01 drh Exp $ +*/ +#include "sqliteInt.h" +#include <gdbm.h> +#include <sys/stat.h> +#include <unistd.h> +#include <ctype.h> +#include <time.h> + +/* +** Each open database file is an instance of this structure. +*/ +typedef struct BeFile BeFile; +struct BeFile { + char *zName; /* Name of the file */ + GDBM_FILE dbf; /* The file itself */ + int nRef; /* Number of references */ + BeFile *pNext, *pPrev; /* Next and previous on list of open files */ +}; + +/* +** The complete database is an instance of the following structure. +*/ +struct Dbbe { + char *zDir; /* The directory containing the database */ + int write; /* True for write permission */ + BeFile *pOpen; /* List of open files */ + int nTemp; /* Number of temporary files created */ + FILE **apTemp; /* Space to hold temporary file pointers */ +}; + +/* +** Each file within the database is an instance of this +** structure. +*/ +struct DbbeTable { + Dbbe *pBe; /* The database of which this record is a part */ + BeFile *pFile; /* The database file for this table */ + datum key; /* Most recently used key */ + datum data; /* Most recent data */ + int needRewind; /* Next key should be the first */ + int readPending; /* The fetch hasn't actually been done yet */ +}; + +/* +** This routine opens a new database. For the current driver scheme, +** the database name is the name of the directory +** containing all the files of the database. +*/ +Dbbe *sqliteDbbeOpen( + const char *zName, /* The name of the database */ + int write, /* True if we will be writing to the database */ + int create, /* True to create database if it doesn't exist */ + char **pzErrMsg /* Write error messages (if any) here */ +){ + Dbbe *pNew; + struct stat statbuf; + + if( stat(zName, &statbuf)!=0 ){ + sqliteSetString(pzErrMsg, "can't find file \"", zName, "\"", 0); + return 0; + } + if( !S_ISDIR(statbuf.st_mode) ){ + sqliteSetString(pzErrMsg, "not a directory: \"", zName, "\"", 0); + return 0; + } + pNew = sqliteMalloc(sizeof(Dbbe) + strlen(zName) + 1); + if( pNew==0 ){ + sqliteSetString(pzErrMsg, "out of memory", 0); + return 0; + } + pNew->zDir = (char*)&pNew[1]; + strcpy(pNew->zDir, zName); + pNew->write = write; + pNew->pOpen = 0; + return pNew; +} + +/* +** Completely shutdown the given database. Close all files. Free all memory. +*/ +void sqliteDbbeClose(Dbbe *pBe){ + BeFile *pFile, *pNext; + for(pFile=pBe->pOpen; pFile; pFile=pNext){ + pNext = pFile->pNext; + gdbm_close(pFile->dbf); + memset(pFile, 0, sizeof(*pFile)); + sqliteFree(pFile); + } + memset(pBe, 0, sizeof(*pBe)); + sqliteFree(pBe); +} + +/* +** Translate the name of a table into the name of a file that holds +** that table. Space to hold the filename is obtained from +** sqliteMalloc() and must be freed by the calling function. +*/ +static char *sqliteFileOfTable(Dbbe *pBe, const char *zTable){ + char *zFile = 0; + int i; + sqliteSetString(&zFile, pBe->zDir, "/", zTable, ".tbl", 0); + if( zFile==0 ) return 0; + for(i=strlen(pBe->zDir)+1; zFile[i]; i++){ + int c = zFile[i]; + if( isupper(c) ){ + zFile[i] = tolower(c); + }else if( !isalnum(c) && c!='-' && c!='_' && c!='.' ){ + zFile[i] = '+'; + } + } + return zFile; +} + +/* +** Open a new table cursor +*/ +DbbeTable *sqliteDbbeOpenTable( + Dbbe *pBe, /* The database the table belongs to */ + const char *zTable, /* The name of the table */ + int writeable /* True to open for writing */ +){ + char *zFile; /* Name of the table file */ + DbbeTable *pTable; /* The new table cursor */ + BeFile *pFile; /* The underlying data file for this table */ + + pTable = sqliteMalloc( sizeof(*pTable) ); + if( pTable==0 ) return 0; + zFile = sqliteFileOfTable(pBe, zTable); + for(pFile=pBe->pOpen; pFile; pFile=pFile->pNext){ + if( strcmp(pFile->zName,zFile)==0 ) break; + } + if( pFile==0 ){ + pFile = sqliteMalloc( sizeof(*pFile) ); + if( pFile==0 ){ + sqliteFree(zFile); + return 0; + } + pFile->zName = zFile; + pFile->nRef = 1; + pFile->pPrev = 0; + if( pBe->pOpen ){ + pBe->pOpen->pPrev = pFile; + } + pFile->pNext = pBe->pOpen; + pBe->pOpen = pFile; + pFile->dbf = gdbm_open(pFile->zName, 0, GDBM_WRCREAT, 0640, 0); + }else{ + sqliteFree(zFile); + pFile->nRef++; + } + pTable->pBe = pBe; + pTable->pFile = pFile; + pTable->readPending = 0; + pTable->needRewind = 1; + return pTable; +} + +/* +** Drop a table from the database. +*/ +void sqliteDbbeDropTable(Dbbe *pBe, const char *zTable){ + char *zFile; /* Name of the table file */ + + zFile = sqliteFileOfTable(pBe, zTable); + unlink(zFile); + sqliteFree(zFile); +} + +/* +** Close a table previously opened by sqliteDbbeOpenTable(). +*/ +void sqliteDbbeCloseTable(DbbeTable *pTable){ + BeFile *pFile; + Dbbe *pBe; + if( pTable==0 ) return; + pFile = pTable->pFile; + pBe = pTable->pBe; + pFile->nRef--; + if( pFile->nRef<=0 ){ + if( pFile->dbf!=NULL ){ + gdbm_close(pFile->dbf); + } + if( pFile->pPrev ){ + pFile->pPrev->pNext = pFile->pNext; + }else{ + pBe->pOpen = pFile->pNext; + } + if( pFile->pNext ){ + pFile->pNext->pPrev = pFile->pPrev; + } + sqliteFree(pFile->zName); + memset(pFile, 0, sizeof(*pFile)); + sqliteFree(pFile); + } + if( pTable->key.dptr ) free(pTable->key.dptr); + if( pTable->data.dptr ) free(pTable->data.dptr); + memset(pTable, 0, sizeof(*pTable)); + sqliteFree(pTable); +} + +/* +** Clear the given datum +*/ +static void datumClear(datum *p){ + if( p->dptr ) free(p->dptr); + p->dptr = 0; + p->dsize = 0; +} + +/* +** Fetch a single record from an open table. Return 1 on success +** and 0 on failure. +*/ +int sqliteDbbeFetch(DbbeTable *pTable, int nKey, char *pKey){ + datum key; + key.dsize = nKey; + key.dptr = pKey; + datumClear(&pTable->key); + datumClear(&pTable->data); + if( pTable->pFile && pTable->pFile->dbf ){ + pTable->data = gdbm_fetch(pTable->pFile->dbf, key); + } + return pTable->data.dptr!=0; +} + +/* +** Copy bytes from the current key or data into a buffer supplied by +** the calling function. Return the number of bytes copied. +*/ +int sqliteDbbeCopyKey(DbbeTable *pTable, int offset, int size, char *zBuf){ + int n; + if( offset>=pTable->key.dsize ) return 0; + if( offset+size>pTable->key.dsize ){ + n = pTable->key.dsize - offset; + }else{ + n = size; + } + memcpy(zBuf, &pTable->key.dptr[offset], n); + return n; +} +int sqliteDbbeCopyData(DbbeTable *pTable, int offset, int size, char *zBuf){ + int n; + if( pTable->readPending && pTable->pFile && pTable->pFile->dbf ){ + pTable->data = gdbm_fetch(pTable->pFile->dbf, pTable->key); + pTable->readPending = 0; + } + if( offset>=pTable->data.dsize ) return 0; + if( offset+size>pTable->data.dsize ){ + n = pTable->data.dsize - offset; + }else{ + n = size; + } + memcpy(zBuf, &pTable->data.dptr[offset], n); + return n; +} + +/* +** Return a pointer to bytes from the key or data. The data returned +** is ephemeral. +*/ +char *sqliteDbbeReadKey(DbbeTable *pTable, int offset){ + if( offset<0 || offset>=pTable->key.dsize ) return ""; + return &pTable->key.dptr[offset]; +} +char *sqliteDbbeReadData(DbbeTable *pTable, int offset){ + if( pTable->readPending && pTable->pFile && pTable->pFile->dbf ){ + pTable->data = gdbm_fetch(pTable->pFile->dbf, pTable->key); + pTable->readPending = 0; + } + if( offset<0 || offset>=pTable->data.dsize ) return ""; + return &pTable->data.dptr[offset]; +} + +/* +** Return the total number of bytes in either data or key. +*/ +int sqliteDbbeKeyLength(DbbeTable *pTable){ + return pTable->key.dsize; +} +int sqliteDbbeDataLength(DbbeTable *pTable){ + if( pTable->readPending && pTable->pFile && pTable->pFile->dbf ){ + pTable->data = gdbm_fetch(pTable->pFile->dbf, pTable->key); + pTable->readPending = 0; + } + return pTable->data.dsize; +} + +/* +** Make is so that the next call to sqliteNextKey() finds the first +** key of the table. +*/ +int sqliteDbbeRewind(DbbeTable *pTable){ + pTable->needRewind = 1; + return 0; +} + +/* +** Read the next key from the table. Return 1 on success. Return +** 0 if there are no more keys. +*/ +int sqliteDbbeNextKey(DbbeTable *pTable){ + datum nextkey; + int rc; + if( pTable==0 || pTable->pFile==0 || pTable->pFile->dbf==0 ){ + pTable->readPending = 0; + return 0; + } + if( pTable->needRewind ){ + nextkey = gdbm_firstkey(pTable->pFile->dbf); + pTable->needRewind = 0; + }else{ + nextkey = gdbm_nextkey(pTable->pFile->dbf, pTable->key); + } + datumClear(&pTable->key); + datumClear(&pTable->data); + pTable->key = nextkey; + if( pTable->key.dptr ){ + pTable->readPending = 1; + rc = 1; + }else{ + pTable->needRewind = 1; + pTable->readPending = 0; + rc = 0; + } + return rc; +} + +/* +** The following are state variables for the RC4 algorithm. We +** use RC4 as a random number generator. Each call to RC4 gives +** a random 8-bit number. +*/ +static struct { + int i, j; + int s[256]; +} rc4; + +/* +** Initialize the RC4 algorithm. +*/ +static void rc4init(char *key, int keylen){ + int i; + char k[256]; + rc4.j = 0; + rc4.i = 0; + for(i=0; i<256; i++){ + rc4.s[i] = i; + k[i] = key[i%keylen]; + } + for(i=0; i<256; i++){ + int t; + rc4.j = (rc4.j + rc4.s[i] + k[i]) & 0xff; + t = rc4.s[rc4.j]; + rc4.s[rc4.j] = rc4.s[i]; + rc4.s[i] = t; + } +} + +/* +** Get a single 8-bit random value from the RC4 algorithm. +*/ +static int rc4byte(void){ + int t; + rc4.i = (rc4.i + 1) & 0xff; + rc4.j = (rc4.j + rc4.s[rc4.i]) & 0xff; + t = rc4.s[rc4.i]; + rc4.s[rc4.i] = rc4.s[rc4.j]; + rc4.s[rc4.j] = t; + t = rc4.s[rc4.i] + rc4.s[rc4.j]; + return t & 0xff; +} + +/* +** Get a new integer key. +*/ +int sqliteDbbeNew(DbbeTable *pTable){ + static int isInit = 0; + int iKey; + datum key; + int go = 1; + int i; + + if( !isInit ){ + struct stat statbuf; + stat(pTable->pFile->zName, &statbuf); + time(&statbuf.st_ctime); + rc4init((char*)&statbuf, sizeof(statbuf)); + isInit = 1; + } + if( pTable->pFile==0 || pTable->pFile->dbf==0 ) return 1; + while( go ){ + iKey = 0; + for(i=0; i<4; i++){ + iKey = (iKey<<8) + rc4byte(); + } + key.dptr = (char*)&iKey; + key.dsize = 4; + go = gdbm_exists(pTable->pFile->dbf, key); + } + return iKey; +} + +/* +** Write an entry into the table. Overwrite any prior entry with the +** same key. +*/ +int sqliteDbbePut(DbbeTable *pTable, int nKey,char *pKey,int nData,char *pData){ + datum data, key; + if( pTable->pFile==0 || pTable->pFile->dbf==0 ) return 0; + data.dsize = nData; + data.dptr = pData; + key.dsize = nKey; + key.dptr = pKey; + gdbm_store(pTable->pFile->dbf, key, data, GDBM_REPLACE); + datumClear(&pTable->key); + datumClear(&pTable->data); + return 1; +} + +/* +** Remove an entry from a table, if the entry exists. +*/ +int sqliteDbbeDelete(DbbeTable *pTable, int nKey, char *pKey){ + datum key; + datumClear(&pTable->key); + datumClear(&pTable->data); + if( pTable->pFile==0 || pTable->pFile->dbf==0 ) return 0; + key.dsize = nKey; + key.dptr = pKey; + gdbm_delete(pTable->pFile->dbf, key); + return 1; +} + +/* +** Open a temporary file. +*/ +FILE *sqliteDbbeOpenTempFile(Dbbe *pBe){ + char *zFile; + char zBuf[30]; + int i; + + for(i=0; i<pBe->nTemp; i++){ + if( pBe->apTemp[i]==0 ) break; + } + if( i>=pBe->nTemp ){ + pBe->nTemp++; + pBe->apTemp = sqliteRealloc(pBe->apTemp, pBe->nTemp*sizeof(FILE*) ); + } + if( pBe->apTemp==0 ) return 0; + sprintf(zBuf, "/_temp_%d~", i); + zFile = 0; + sqliteSetString(&zFile, pBe->zDir, zBuf, 0); + pBe->apTemp[i] = fopen(zFile, "w+"); + sqliteFree(zFile); + return pBe->apTemp[i]; +} + +/* +** Close a temporary file opened using sqliteDbbeOpenTempFile() +*/ +void sqliteDbbeCloseTempFile(Dbbe *pBe, FILE *f){ + int i; + for(i=0; i<pBe->nTemp; i++){ + if( pBe->apTemp[i]==f ){ + char *zFile; + char zBuf[30]; + sprintf(zBuf, "/_temp_%d~", i); + zFile = 0; + sqliteSetString(&zFile, pBe->zDir, zBuf, 0); + unlink(zFile); + sqliteFree(zFile); + pBe->apTemp[i] = 0; + break; + } + } + fclose(f); +} diff --git a/src/dbbe.h b/src/dbbe.h new file mode 100644 index 000000000..26e83a648 --- /dev/null +++ b/src/dbbe.h @@ -0,0 +1,119 @@ +/* +** Copyright (c) 1999, 2000 D. Richard Hipp +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public +** License as published by the Free Software Foundation; either +** version 2 of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public +** License along with this library; if not, write to the +** Free Software Foundation, Inc., 59 Temple Place - Suite 330, +** Boston, MA 02111-1307, USA. +** +** Author contact information: +** drh@hwaci.com +** http://www.hwaci.com/drh/ +** +************************************************************************* +** This file defines the interface to the database backend (Dbbe). +** +** The database backend is designed to be as general as possible +** so that it can easily be replaced by a different backend. +** This library was originally designed to support the following +** backends: GDBM, NDBM, SDBM, Berkeley DB. +** +** $Id: dbbe.h,v 1.1 2000/05/29 14:26:01 drh Exp $ +*/ +#ifndef _SQLITE_DBBE_H_ +#define _SQLITE_DBBE_H_ +#include <stdio.h> + +/* +** The database backend supports two opaque structures. A Dbbe is +** a context for the entire set of tables forming a complete +** database. A DbbeTable is a single table. +** +** The DbbeTable structure holds some state information, such as +** the key and data from the last retrieval. For this reason, +** the backend must allow the creation of multiple independent +** DbbeTable structures for each table in the database. +*/ +typedef struct Dbbe Dbbe; +typedef struct DbbeTable DbbeTable; + +/* +** The 18 interface routines. +*/ + +/* Open a complete database */ +Dbbe *sqliteDbbeOpen(const char *zName, int write, int create, char **pzErr); + +/* Close the whole database. */ +void sqliteDbbeClose(Dbbe*); + +/* Open a particular table of a previously opened database. +** Create the table if it doesn't already exist and writeable!=0. +*/ +DbbeTable *sqliteDbbeOpenTable(Dbbe*, const char *zTableName, int writeable); + +/* Delete a table from the database */ +void sqliteDbbeDropTable(Dbbe*, const char *zTableName); + +/* Close a table */ +void sqliteDbbeCloseTable(DbbeTable*); + +/* Fetch an entry from a table with the given key. Return 1 if +** successful and 0 if no such entry exists. +*/ +int sqliteDbbeFetch(DbbeTable*, int nKey, char *pKey); + +/* Retrieve the key or data used for the last fetch. Only size +** bytes are read beginning with the offset-th byte. The return +** value is the actual number of bytes read. +*/ +int sqliteDbbeCopyKey(DbbeTable*, int offset, int size, char *zBuf); +int sqliteDbbeCopyData(DbbeTable*, int offset, int size, char *zBuf); + +/* Retrieve the key or data. The result is ephemeral. +*/ +char *sqliteDbbeReadKey(DbbeTable*, int offset); +char *sqliteDbbeReadData(DbbeTable*, int offset); + +/* Return the length of the most recently fetched key or data. */ +int sqliteDbbeKeyLength(DbbeTable*); +int sqliteDbbeDataLength(DbbeTable*); + +/* Retrieve the next entry in the table. The first key is retrieved +** the first time this routine is called, or after a call to +** sqliteDbbeRewind(). The return value is 1 if there is another +** entry, or 0 if there are no more entries. */ +int sqliteDbbeNextKey(DbbeTable*); + +/* Make it so that the next call to sqliteDbbeNextKey() returns +** the first entry of the table. */ +int sqliteDbbeRewind(DbbeTable*); + +/* Get a new integer key for this table. */ +int sqliteDbbeNew(DbbeTable*); + +/* Write an entry into a table. If another entry already exists with +** the same key, the old entry is discarded first. +*/ +int sqliteDbbePut(DbbeTable*, int nKey, char *pKey, int nData, char *pData); + +/* Remove an entry from the table */ +int sqliteDbbeDelete(DbbeTable*, int nKey, char *pKey); + +/* Open a file suitable for temporary storage */ +FILE *sqliteDbbeOpenTempFile(Dbbe*); + +/* Close a temporary file */ +void sqliteDbbeCloseTempFile(Dbbe *, FILE *); + +#endif /* defined(_SQLITE_DBBE_H_) */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 000000000..4bbc75ced --- /dev/null +++ b/src/main.c @@ -0,0 +1,219 @@ +/* +** Copyright (c) 1999, 2000 D. Richard Hipp +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public +** License as published by the Free Software Foundation; either +** version 2 of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public +** License along with this library; if not, write to the +** Free Software Foundation, Inc., 59 Temple Place - Suite 330, +** Boston, MA 02111-1307, USA. +** +** Author contact information: +** drh@hwaci.com +** http://www.hwaci.com/drh/ +** +************************************************************************* +** Main file for the SQLite library. The routines in this file +** implement the programmer interface to the library. Routines in +** other files are for internal use by SQLite and should not be +** accessed by users of the library. +** +** $Id: main.c,v 1.1 2000/05/29 14:26:01 drh Exp $ +*/ +#include "sqliteInt.h" + +/* +** This is the callback routine for the code that initializes the +** database. Each callback contains text of a CREATE TABLE or +** CREATE INDEX statement that must be parsed to yield the internal +** structures that describe the tables. +*/ +static int sqliteOpenCb(void *pDb, int argc, char **argv, char **azColName){ + sqlite *db = (sqlite*)pDb; + Parse sParse; + int nErr; + char *zErrMsg = 0; + + if( argc!=1 ) return 0; + memset(&sParse, 0, sizeof(sParse)); + sParse.db = db; + sParse.initFlag = 1; + nErr = sqliteRunParser(&sParse, argv[0], &zErrMsg); + return nErr; +} + +/* +** Open a new SQLite database. Construct an "sqlite" structure to define +** the state of this database and return a pointer to that structure. +*/ +sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){ + sqlite *db; + Vdbe *vdbe; + Table *pTab; + char *azArg[2]; + static char master_schema[] = + "CREATE TABLE " MASTER_NAME " (\n" + " type text,\n" + " name text,\n" + " tbl_name text,\n" + " sql text\n" + ")" + ; + + /* The following program is used to initialize the internal + ** structure holding the tables and indexes of the database. + ** The database contains a special table named "sqlite_master" + ** defined as follows: + ** + ** CREATE TABLE sqlite_master ( + ** type text, -- Either "table" or "index" + ** name text, -- Name of table or index + ** tbl_name text, -- Associated table + ** sql text -- The CREATE statement for this object + ** ); + ** + ** The sqlite_master table contains a single entry for each table + ** and each index. The "type" field tells whether the entry is + ** a table or index. The "name" field is the name of the object. + ** The "tbl_name" is the name of the associated table. For tables, + ** the tbl_name field is always the same as name. For indices, the + ** tbl_name field contains the name of the table that the index + ** indexes. Finally, the sql field contains the complete text of + ** the CREATE TABLE or CREATE INDEX statement that originally created + ** the table or index. + ** + ** The following program invokes its callback on the SQL for each + ** table then goes back and invokes the callback on the + ** SQL for each index. The callback will invoke the + ** parser to build the internal representation of the + ** database scheme. + */ + static VdbeOp initProg[] = { + { OP_Open, 0, 0, MASTER_NAME}, + { OP_Next, 0, 8, 0}, /* 1 */ + { OP_Field, 0, 0, 0}, + { OP_String, 0, 0, "table"}, + { OP_Ne, 0, 1, 0}, + { OP_Field, 0, 3, 0}, + { OP_Callback, 1, 0, 0}, + { OP_Goto, 0, 1, 0}, + { OP_Rewind, 0, 0, 0}, /* 8 */ + { OP_Next, 0, 16, 0}, /* 9 */ + { OP_Field, 0, 0, 0}, + { OP_String, 0, 0, "index"}, + { OP_Ne, 0, 9, 0}, + { OP_Field, 0, 3, 0}, + { OP_Callback, 1, 0, 0}, + { OP_Goto, 0, 9, 0}, + { OP_Halt, 0, 0, 0}, /* 16 */ + }; + + /* Allocate space to hold the main database structure */ + db = sqliteMalloc( sizeof(sqlite) ); + if( pzErrMsg ) *pzErrMsg = 0; + if( db==0 ){ + sqliteSetString(pzErrMsg, "out of memory", 0); + return 0; + } + + /* Open the backend database driver */ + db->pBe = sqliteDbbeOpen(zFilename, (mode&0222)!=0, mode!=0, pzErrMsg); + if( db->pBe==0 ){ + sqliteFree(db); + return 0; + } + + /* Create a virtual machine to run the initialization program. Run + ** the program. The delete the virtual machine. + */ + azArg[0] = master_schema; + azArg[1] = 0; + sqliteOpenCb(db, 1, azArg, 0); + pTab = sqliteFindTable(db, MASTER_NAME); + if( pTab ){ + pTab->readOnly = 1; + } + vdbe = sqliteVdbeCreate(db->pBe); + sqliteVdbeAddOpList(vdbe, sizeof(initProg)/sizeof(initProg[0]), initProg); + sqliteVdbeExec(vdbe, sqliteOpenCb, db, pzErrMsg); + sqliteVdbeDelete(vdbe); + return db; +} + +/* +** Close an existing SQLite database +*/ +void sqlite_close(sqlite *db){ + int i; + sqliteDbbeClose(db->pBe); + for(i=0; i<N_HASH; i++){ + Table *pNext, *pList = db->apTblHash[i]; + db->apTblHash[i] = 0; + while( pList ){ + pNext = pList->pHash; + pList->pHash = 0; + sqliteDeleteTable(db, pList); + pList = pNext; + } + } + sqliteFree(db); +} + +/* +** Return TRUE if the given SQL string ends in a semicolon. +*/ +int sqlite_complete(const char *zSql){ + int i; + int lastWasSemi = 0; + + i = 0; + while( i>=0 && zSql[i]!=0 ){ + int tokenType; + int n; + + n = sqliteGetToken(&zSql[i], &tokenType); + switch( tokenType ){ + case TK_SPACE: + case TK_COMMENT: + break; + case TK_SEMI: + lastWasSemi = 1; + break; + default: + lastWasSemi = 0; + break; + } + i += n; + } + return lastWasSemi; +} + +/* +** Execute SQL code +*/ +int sqlite_exec( + sqlite *db, /* The database on which the SQL executes */ + char *zSql, /* The SQL to be executed */ + sqlite_callback xCallback, /* Invoke this callback routine */ + void *pArg, /* First argument to xCallback() */ + char **pzErrMsg /* Write error messages here */ +){ + Parse sParse; + int nErr; + + if( pzErrMsg ) *pzErrMsg = 0; + memset(&sParse, 0, sizeof(sParse)); + sParse.db = db; + sParse.xCallback = xCallback; + sParse.pArg = pArg; + nErr = sqliteRunParser(&sParse, zSql, pzErrMsg); + return nErr; +} diff --git a/src/shell.c b/src/shell.c new file mode 100644 index 000000000..081274745 --- /dev/null +++ b/src/shell.c @@ -0,0 +1,384 @@ +/* +** Copyright (c) 1999, 2000 D. Richard Hipp +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public +** License as published by the Free Software Foundation; either +** version 2 of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public +** License along with this library; if not, write to the +** Free Software Foundation, Inc., 59 Temple Place - Suite 330, +** Boston, MA 02111-1307, USA. +** +** Author contact information: +** drh@hwaci.com +** http://www.hwaci.com/drh/ +** +************************************************************************* +** This file contains code to implement the "sqlite" command line +** utility for accessing SQLite databases. +** +** $Id: shell.c,v 1.1 2000/05/29 14:26:01 drh Exp $ +*/ +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include "sqlite.h" +#include <unistd.h> +#include <ctype.h> + +#if !defined(NO_READLINE) +#include <readline/readline.h> +#include <readline/history.h> +#endif + +/* +** An pointer to an instance of this structure is passed from +** the main program to the callback. This is used to communicate +** state and mode information. +*/ +struct callback_data { + int cnt; /* Number of records displayed so far */ + FILE *out; /* Write results here */ + int mode; /* An output mode setting */ + int showHeader; /* True to show column names in List or Column mode */ + char separator[20];/* Separator character for MODE_List */ + int colWidth[30]; /* Width of each column when in column mode */ +}; + +/* +** These are the allowed modes. +*/ +#define MODE_Line 0 /* One field per line. Blank line between records */ +#define MODE_Column 1 /* One record per line in neat columns */ +#define MODE_List 2 /* One record per line with a separator */ + +/* +** Number of elements in an array +*/ +#define ArraySize(X) (sizeof(X)/sizeof(X[0])) + +/* +** This is the callback routine that the SQLite library +** invokes for each row of a query result. +*/ +static int callback(void *pArg, int nArg, char **azArg, char **azCol){ + int i; + struct callback_data *p = (struct callback_data*)pArg; + switch( p->mode ){ + case MODE_Line: { + if( p->cnt++>0 ) fprintf(p->out,"\n"); + for(i=0; i<nArg; i++){ + fprintf(p->out,"%s = %s\n", azCol[i], azArg[i]); + } + break; + } + case MODE_Column: { + if( p->cnt++==0 && p->showHeader ){ + for(i=0; i<nArg; i++){ + int w; + if( i<ArraySize(p->colWidth) && p->colWidth[i]>0 ){ + w = p->colWidth[i]; + }else{ + w = 10; + } + fprintf(p->out,"%-*.*s%s",w,w,azCol[i], i==nArg-1 ? "\n": " "); + } + for(i=0; i<nArg; i++){ + int w; + if( i<ArraySize(p->colWidth) && p->colWidth[i]>0 ){ + w = p->colWidth[i]; + }else{ + w = 10; + } + fprintf(p->out,"%-*.*s%s",w,w,"-------------------------------------", + i==nArg-1 ? "\n": " "); + } + } + for(i=0; i<nArg; i++){ + int w; + if( i<ArraySize(p->colWidth) && p->colWidth[i]>0 ){ + w = p->colWidth[i]; + }else{ + w = 10; + } + fprintf(p->out,"%-*.*s%s",w,w,azArg[i], i==nArg-1 ? "\n": " "); + } + break; + } + case MODE_List: { + if( p->cnt++==0 && p->showHeader ){ + for(i=0; i<nArg; i++){ + fprintf(p->out,"%s%s",azCol[i], i==nArg-1 ? "\n" : p->separator); + } + } + for(i=0; i<nArg; i++){ + fprintf(p->out,"%s%s",azArg[i], i==nArg-1 ? "\n" : p->separator); + } + break; + } + } + return 0; +} + +/* +** Text of a help message +*/ +static char zHelp[] = + ".exit Exit this program\n" + ".explain Set output mode suitable for EXPLAIN\n" + ".header ON|OFF Turn display of headers on or off\n" + ".help Show this message\n" + ".indices TABLE Show names of all indices on TABLE\n" + ".mode MODE Set mode to one of \"line\", \"column\", or" + " \"list\"\n" + ".output FILENAME Send output to FILENAME\n" + ".output stdout Send output to the screen\n" + ".schema ?TABLE? Show the CREATE statements\n" + ".separator STRING Change separator string for \"list\" mode\n" + ".tables List names all tables in the database\n" + ".width NUM NUM ... Set column widths for \"column\" mode\n" +; + +/* +** If an input line begins with "." then invoke this routine to +** process that line. +*/ +static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){ + int i = 1; + int nArg = 0; + int n, c; + char *azArg[50]; + + /* Parse the input line into tokens. + */ + while( zLine[i] && nArg<ArraySize(azArg) ){ + while( isspace(zLine[i]) ){ i++; } + if( zLine[i]=='\'' || zLine[i]=='"' ){ + int delim = zLine[i++]; + azArg[nArg++] = &zLine[i]; + while( zLine[i] && zLine[i]!=delim ){ i++; } + if( zLine[i]==delim ){ + zLine[i++] = 0; + } + }else{ + azArg[nArg++] = &zLine[i]; + while( zLine[i] && !isspace(zLine[i]) ){ i++; } + if( zLine[i] ) zLine[i++] = 0; + } + } + + /* Process the input line. + */ + if( nArg==0 ) return; + n = strlen(azArg[0]); + c = azArg[0][0]; + + if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){ + exit(0); + }else + + if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){ + p->mode = MODE_Column; + p->showHeader = 1; + p->colWidth[0] = 4; + p->colWidth[1] = 12; + p->colWidth[2] = 5; + p->colWidth[3] = 5; + p->colWidth[4] = 40; + }else + + if( c=='h' && strncmp(azArg[0], "header", n)==0 && nArg>1 ){ + int j; + char *z = azArg[1]; + int val = atoi(azArg[1]); + for(j=0; z[j]; j++){ + if( isupper(z[j]) ) z[j] = tolower(z[j]); + } + if( strcmp(z,"on")==0 ){ + val = 1; + }else if( strcmp(z,"yes")==0 ){ + val = 1; + } + p->showHeader = val; + }else + + if( c=='h' && strncmp(azArg[0], "help", n)==0 ){ + fprintf(stderr,zHelp); + }else + + if( c=='i' && strncmp(azArg[0], "indices", n)==0 && nArg>1 ){ + struct callback_data data; + char *zErrMsg = 0; + char zSql[1000]; + memcpy(&data, p, sizeof(data)); + data.showHeader = 0; + data.mode = MODE_List; + sprintf(zSql, "SELECT name FROM sqlite_master " + "WHERE type='index' AND tbl_name='%.900s'", azArg[1]); + sqlite_exec(db, zSql, callback, &data, &zErrMsg); + if( zErrMsg ){ + fprintf(stderr,"Error: %s\n", zErrMsg); + free(zErrMsg); + } + }else + + if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg==2 ){ + int n2 = strlen(azArg[1]); + if( strncmp(azArg[1],"line",n2)==0 ){ + p->mode = MODE_Line; + }else if( strncmp(azArg[1],"column",n2)==0 ){ + p->mode = MODE_Column; + }else if( strncmp(azArg[1],"list",n2)==0 ){ + p->mode = MODE_List; + } + }else + + if( c=='o' && strncmp(azArg[0], "output", n)==0 && nArg==2 ){ + if( p->out!=stdout ){ + fclose(p->out); + } + if( strcmp(azArg[1],"stdout")==0 ){ + p->out = stdout; + }else{ + p->out = fopen(azArg[1], "w"); + if( p->out==0 ){ + fprintf(stderr,"can't write to \"%s\"\n", azArg[1]); + p->out = stdout; + } + } + }else + + if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){ + struct callback_data data; + char *zErrMsg = 0; + char zSql[1000]; + memcpy(&data, p, sizeof(data)); + data.showHeader = 0; + data.mode = MODE_List; + if( nArg>1 ){ + sprintf(zSql, "SELECT sql FROM sqlite_master WHERE name='%.900s'", + azArg[1]); + }else{ + sprintf(zSql, "SELECT sql FROM sqlite_master " + "ORDER BY tbl_name, type DESC, name"); + } + sqlite_exec(db, zSql, callback, &data, &zErrMsg); + if( zErrMsg ){ + fprintf(stderr,"Error: %s\n", zErrMsg); + free(zErrMsg); + } + }else + + if( c=='s' && strncmp(azArg[0], "separator", n)==0 && nArg==2 ){ + sprintf(p->separator, "%.*s", (int)ArraySize(p->separator)-1, azArg[1]); + }else + + if( c=='t' && strncmp(azArg[0], "tables", n)==0 ){ + struct callback_data data; + char *zErrMsg = 0; + static char zSql[] = "SELECT name FROM sqlite_master WHERE type='table'"; + memcpy(&data, p, sizeof(data)); + data.showHeader = 0; + data.mode = MODE_List; + sqlite_exec(db, zSql, callback, &data, &zErrMsg); + if( zErrMsg ){ + fprintf(stderr,"Error: %s\n", zErrMsg); + free(zErrMsg); + } + }else + + if( c=='w' && strncmp(azArg[0], "width", n)==0 ){ + int j; + for(j=1; j<nArg && j<ArraySize(p->colWidth); j++){ + p->colWidth[j-1] = atoi(azArg[j]); + } + }else + + { + fprintf(stderr, "unknown command: \"%s\". Enter \".help\" for help\n", + azArg[0]); + } +} + +int main(int argc, char **argv){ + sqlite *db; + char *zErrMsg = 0; + struct callback_data data; + + if( argc!=2 && argc!=3 ){ + fprintf(stderr,"Usage: %s FILENAME ?SQL?\n", *argv); + exit(1); + } + db = sqlite_open(argv[1], 0666, &zErrMsg); + if( db==0 ){ + fprintf(stderr,"Unable to open database \"%s\": %s\n", argv[1], zErrMsg); + exit(1); + } + memset(&data, 0, sizeof(data)); + data.out = stdout; + if( argc==3 ){ + data.mode = MODE_List; + strcpy(data.separator,"|"); + if( sqlite_exec(db, argv[2], callback, &data, &zErrMsg)!=0 && zErrMsg!=0 ){ + fprintf(stderr,"SQL error: %s\n", zErrMsg); + exit(1); + } + }else{ + char *zLine; + char *zSql = 0; + int nSql = 0; + int istty = isatty(0); + data.mode = MODE_Line; + strcpy(data.separator,"|"); + data.showHeader = 0; + if( istty ){ + printf( + "Enter \".help\" for instructions\n" + ); + } + while( (zLine = readline(istty ? (zSql==0 ? "sql> " : ".... ") : 0))!=0 ){ + if( zLine && zLine[0]=='.' ){ + do_meta_command(zLine, db, &data); + free(zLine); + continue; + } + if( zSql==0 ){ + nSql = strlen(zLine); + zSql = malloc( nSql+1 ); + strcpy(zSql, zLine); + }else{ + int len = strlen(zLine); + zSql = realloc( zSql, nSql + len + 2 ); + if( zSql==0 ){ + fprintf(stderr,"%s: out of memory!\n", *argv); + exit(1); + } + strcpy(&zSql[nSql++], "\n"); + strcpy(&zSql[nSql], zLine); + nSql += len; + } + free(zLine); + if( sqlite_complete(zSql) ){ + data.cnt = 0; + if( sqlite_exec(db, zSql, callback, &data, &zErrMsg)!=0 + && zErrMsg!=0 ){ + printf("SQL error: %s\n", zErrMsg); + free(zErrMsg); + zErrMsg = 0; + } + free(zSql); + zSql = 0; + nSql = 0; + } + } + } + sqlite_close(db); + return 0; +} diff --git a/src/sqlite.h b/src/sqlite.h new file mode 100644 index 000000000..26b48746a --- /dev/null +++ b/src/sqlite.h @@ -0,0 +1,118 @@ +/* +** Copyright (c) 1999, 2000 D. Richard Hipp +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public +** License as published by the Free Software Foundation; either +** version 2 of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public +** License along with this library; if not, write to the +** Free Software Foundation, Inc., 59 Temple Place - Suite 330, +** Boston, MA 02111-1307, USA. +** +** Author contact information: +** drh@hwaci.com +** http://www.hwaci.com/drh/ +** +************************************************************************* +** This header file defines the interface that the sqlite library +** presents to client programs. +** +** @(#) $Id: sqlite.h,v 1.1 2000/05/29 14:26:01 drh Exp $ +*/ +#ifndef _SQLITE_H_ +#define _SQLITE_H_ + +/* +** Each open sqlite database is represented by an instance of the +** following opaque structure. +*/ +typedef struct sqlite sqlite; + +/* +** A function to open a new sqlite database. +** +** If the database does not exist and mode indicates write +** permission, then a new database is created. If the database +** does not exist and mode does not indicate write permission, +** then the open fails, an error message generated (if errmsg!=0) +** and the function returns 0. +** +** If mode does not indicates user write permission, then the +** database is opened read-only. +** +** The Truth: As currently implemented, all databases are opened +** for writing all the time. Maybe someday we will provide the +** ability to open a database readonly. The mode parameters is +** provide in anticipation of that enhancement. +*/ +sqlite *sqlite_open(const char *filename, int mode, char **errmsg); + +/* +** A function to close the database. +** +** Call this function with a pointer to a structure that was previously +** returned from sqlite_open() and the corresponding database will by closed. +*/ +void sqlite_close(sqlite *); + +/* +** The type for a callback function. +*/ +typedef int (*sqlite_callback)(void*,int,char**, char**); + +/* +** A function to executes one or more statements of SQL. +** +** If one or more of the SQL statements are queries, then +** the callback function specified by the 3rd parameter is +** invoked once for each row of the query result. This callback +** should normally return 0. If the callback returns a non-zero +** value then the query is aborted, all subsequent SQL statements +** are skipped and the sqlite_exec() function returns the same +** value that the callback returned. +** +** The 4th parameter is an arbitrary pointer that is passed +** to the callback function as its first parameter. +** +** The 2nd parameter to the callback function is the number of +** columns in the query result. The 3rd parameter is an array +** of string holding the values for each column. The 4th parameter +** is an array of strings holding the names of each column. +** +** The callback function may be NULL, even for queries. A NULL +** callback is not an error. It just means that no callback +** will be invoked. +** +** If an error occurs while parsing or evaluating the SQL (but +** not while executing the callback) then an appropriate error +** message is written into memory obtained from malloc() and +** *errmsg is made to point to that message. If errmsg==NULL, +** then no error message is ever written. The return value is +** non-zero if an error occurs. +*/ +int sqlite_exec( + sqlite*, /* An open database */ + char *sql, /* SQL to be executed */ + sqlite_callback, /* Callback function */ + void *, /* 1st argument to callback function */ + char **errmsg /* Error msg written here */ +); + + +/* This function returns true if the given input string comprises +** one or more complete SQL statements. +** +** The algorithm is simple. If the last token other than spaces +** and comments is a semicolon, then return true. otherwise return +** false. +*/ +int sqlite_complete(const char *sql); + +#endif /* _SQLITE_H_ */ diff --git a/src/sqliteInt.h b/src/sqliteInt.h new file mode 100644 index 000000000..b2bf129de --- /dev/null +++ b/src/sqliteInt.h @@ -0,0 +1,235 @@ +/* +** Copyright (c) 1999, 2000 D. Richard Hipp +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public +** License as published by the Free Software Foundation; either +** version 2 of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public +** License along with this library; if not, write to the +** Free Software Foundation, Inc., 59 Temple Place - Suite 330, +** Boston, MA 02111-1307, USA. +** +** Author contact information: +** drh@hwaci.com +** http://www.hwaci.com/drh/ +** +************************************************************************* +** Internal interface definitions for SQLite. +** +** @(#) $Id: sqliteInt.h,v 1.1 2000/05/29 14:26:01 drh Exp $ +*/ +#include "sqlite.h" +#include "dbbe.h" +#include "vdbe.h" +#include "parse.h" +#include <gdbm.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +/* +** The number of entries in the in-memory hash table holding the +** schema. +*/ +#define N_HASH 51 + +/* +** Name of the master database table. The master database table +** is a special table that holds the names and attributes of all +** user tables and indices. +*/ +#define MASTER_NAME "sqlite_master" + +/* +** A convenience macro that returns the number of elements in +** an array. +*/ +#define ArraySize(X) (sizeof(X)/sizeof(X[0])) + +/* +** Forward references to structures +*/ +typedef struct Table Table; +typedef struct Index Index; +typedef struct Instruction Instruction; +typedef struct Expr Expr; +typedef struct ExprList ExprList; +typedef struct Parse Parse; +typedef struct Token Token; +typedef struct IdList IdList; +typedef struct WhereInfo WhereInfo; + +/* +** Each database is an instance of the following structure +*/ +struct sqlite { + Dbbe *pBe; /* The backend driver */ + int flags; /* Miscellanous flags */ + Table *apTblHash[N_HASH]; /* All tables of the database */ + Index *apIdxHash[N_HASH]; /* All indices of the database */ +}; + +/* +** Possible values for the flags field of sqlite +*/ +#define SQLITE_VdbeTrace 0x00000001 + +/* +** Each table is represented in memory by +** an instance of the following structure +*/ +struct Table { + char *zName; /* Name of the table */ + Table *pHash; /* Next table with same hash on zName */ + int nCol; /* Number of columns in this table */ + int readOnly; /* True if this table should not be written by the user */ + char **azCol; /* Name of each column */ + Index *pIndex; /* List of indices on this table. */ +}; + +/* +** Each index is represented in memory by and +** instance of the following structure. +*/ +struct Index { + char *zName; /* Name of this index */ + Index *pHash; /* Next index with the same hash on zName */ + int nField; /* Number of fields in the table indexed by this index */ + int *aiField; /* Indices of fields used by this index. 1st is 0 */ + Table *pTable; /* The table being indexed */ + Index *pNext; /* The next index associated with the same table */ +}; + +/* +** Each token coming out of the lexer is an instance of +** this structure. +*/ +struct Token { + char *z; /* Text of the token */ + int n; /* Number of characters in this token */ +}; + +/* +** Each node of an expression in the parse tree is an instance +** of this structure +*/ +struct Expr { + int op; /* Operation performed by this node */ + Expr *pLeft, *pRight; /* Left and right subnodes */ + ExprList *pList; /* A list of expressions used as a function argument */ + Token token; /* An operand token */ + int iTable, iField; /* When op==TK_FIELD, then this node means the + ** iField-th field of the iTable-th table */ +}; + +/* +** A list of expressions. Each expression may optionally have a +** name. An expr/name combination can be used in several ways, such +** as the list of "expr AS ID" fields following a "SELECT" or in the +** list of "ID = expr" items in an UPDATE. A list of expressions can +** also be used as the argument to a function, in which case the azName +** field is not used. +*/ +struct ExprList { + int nExpr; /* Number of expressions on the list */ + struct { + Expr *pExpr; /* The list of expressions */ + char *zName; /* Token associated with this expression */ + int idx; /* ... */ + } *a; /* One entry for each expression */ +}; + +/* +** A list of identifiers. +*/ +struct IdList { + int nId; /* Number of identifiers on the list */ + struct { + char *zName; /* Text of the identifier. */ + char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */ + Table *pTab; /* Table corresponding to zName */ + int idx; /* Index of a field name in the table */ + } *a; /* One entry for each identifier on the list */ +}; + +/* +** The WHERE clause processing routine has two halves. The +** first part does the start of the WHERE loop and the second +** half does the tail of the WHERE loop. An instance of +** this structure is returned by the first half and passed +** into the second half to give some continuity. +*/ +struct WhereInfo { + Parse *pParse; + IdList *pTabList; + int iContinue; + int iBreak; +}; + +/* +** An SQL parser context +*/ +struct Parse { + sqlite *db; /* The main database structure */ + sqlite_callback xCallback; /* The callback function */ + void *pArg; /* First argument to the callback function */ + char *zErrMsg; /* An error message */ + Token sErrToken; /* The token at which the error occurred */ + Token sFirstToken; /* The first token parsed */ + Token sLastToken; /* The last token parsed */ + Table *pNewTable; /* A table being constructed by CREATE TABLE */ + Vdbe *pVdbe; /* An engine for executing database bytecode */ + int explain; /* True if the EXPLAIN flag is found on the query */ + int initFlag; /* True if reparsing CREATE TABLEs */ + int nErr; /* Number of errors seen */ +}; + +/* +** Internal function prototypes +*/ +int sqliteStrICmp(const char *, const char *); +int sqliteStrNICmp(const char *, const char *, int); +int sqliteHashNoCase(const char *, int); +int sqliteCompare(const char *, const char *); +int sqliteSortCompare(const char *, const char *); +void *sqliteMalloc(int); +void sqliteFree(void*); +void *sqliteRealloc(void*,int); +int sqliteGetToken(const char*, int *); +void sqliteSetString(char **, const char *, ...); +void sqliteSetNString(char **, ...); +int sqliteRunParser(Parse*, char*, char **); +void sqliteExec(Parse*); +Expr *sqliteExpr(int, Expr*, Expr*, Token*); +Expr *sqliteExprFunction(ExprList*, Token*); +void sqliteExprDelete(Expr*); +ExprList *sqliteExprListAppend(ExprList*,Expr*,Token*); +void sqliteExprListDelete(ExprList*); +void sqliteStartTable(Parse*,Token*,Token*); +void sqliteAddColumn(Parse*,Token*); +void sqliteEndTable(Parse*,Token*); +void sqliteDropTable(Parse*, Token*); +void sqliteDeleteTable(sqlite*, Table*); +void sqliteInsert(Parse*, Token*, ExprList*, IdList*); +IdList *sqliteIdListAppend(IdList*, Token*); +void sqliteIdListAddAlias(IdList*, Token*); +void sqliteIdListDelete(IdList*); +void sqliteCreateIndex(Parse*, Token*, Token*, IdList*, Token*, Token*); +void sqliteDropIndex(Parse*, Token*); +void sqliteSelect(Parse*, ExprList*, IdList*, Expr*, ExprList*); +void sqliteDeleteFrom(Parse*, Token*, Expr*); +void sqliteUpdate(Parse*, Token*, ExprList*, Expr*); +WhereInfo *sqliteWhereBegin(Parse*, IdList*, Expr*, int); +void sqliteWhereEnd(WhereInfo*); +void sqliteExprCode(Parse*, Expr*); +void sqliteExprIfTrue(Parse*, Expr*, int); +void sqliteExprIfFalse(Parse*, Expr*, int); +Table *sqliteFindTable(sqlite*,char*); diff --git a/src/tclsqlite.c b/src/tclsqlite.c new file mode 100644 index 000000000..55e9a8557 --- /dev/null +++ b/src/tclsqlite.c @@ -0,0 +1,241 @@ +/* +** Copyright (c) 1999, 2000 D. Richard Hipp +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public +** License as published by the Free Software Foundation; either +** version 2 of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public +** License along with this library; if not, write to the +** Free Software Foundation, Inc., 59 Temple Place - Suite 330, +** Boston, MA 02111-1307, USA. +** +** Author contact information: +** drh@hwaci.com +** http://www.hwaci.com/drh/ +** +************************************************************************* +** A TCL Interface to SQLite +** +** $Id: tclsqlite.c,v 1.1 2000/05/29 14:26:01 drh Exp $ +*/ +#include "sqlite.h" +#include <tcl.h> +#include <stdlib.h> +#include <string.h> + +/* +** An instance of this structure passes information thru the sqlite +** logic from the original TCL command into the callback routine. +*/ +typedef struct CallbackData CallbackData; +struct CallbackData { + Tcl_Interp *interp; /* The TCL interpreter */ + char *zArray; /* The array into which data is written */ + char *zCode; /* The code to execute for each row */ + int once; /* Set only for the first invocation of callback */ +}; + +/* +** Called for each row of the result. +*/ +static int DbEvalCallback( + void *clientData, /* An instance of CallbackData */ + int nCol, /* Number of columns in the result */ + char ** azCol, /* Data for each column */ + char ** azN /* Name for each column */ +){ + CallbackData *cbData = (CallbackData*)clientData; + int i, rc; + if( cbData->zArray[0] ){ + if( cbData->once ){ + for(i=0; i<nCol; i++){ + Tcl_SetVar2(cbData->interp, cbData->zArray, "*", azN[i], + TCL_LIST_ELEMENT|TCL_APPEND_VALUE); + } + } + for(i=0; i<nCol; i++){ + Tcl_SetVar2(cbData->interp, cbData->zArray, azN[i], azCol[i], 0); + } + }else{ + for(i=0; i<nCol; i++){ + Tcl_SetVar(cbData->interp, azN[i], azCol[i], 0); + } + } + cbData->once = 0; + rc = Tcl_Eval(cbData->interp, cbData->zCode); + return rc; +} + +/* +** Called when the command is deleted. +*/ +static void DbDeleteCmd(void *db){ + sqlite_close((sqlite*)db); +} + +/* +** The "sqlite" command below creates a new Tcl command for each +** connection it opens to an SQLite database. This routine is invoked +** whenever one of those connection-specific commands is executed +** in Tcl. For example, if you run Tcl code like this: +** +** sqlite db1 "my_database" +** db1 close +** +** The first command opens a connection to the "my_database" database +** and calls that connection "db1". The second command causes this +** subroutine to be invoked. +*/ +static int DbCmd(void *cd, Tcl_Interp *interp, int argc, char **argv){ + char *z; + int n, c; + sqlite *db = cd; + if( argc<2 ){ + Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], + " SUBCOMMAND ...\"", 0); + return TCL_ERROR; + } + z = argv[1]; + n = strlen(z); + c = z[0]; + + /* $db close + ** + ** Shutdown the database + */ + if( c=='c' && n>=2 && strncmp(z,"close",n)==0 ){ + Tcl_DeleteCommand(interp, argv[0]); + }else + + /* $db complete SQL + ** + ** Return TRUE if SQL is a complete SQL statement. Return FALSE if + ** additional lines of input are needed. This is similar to the + ** built-in "info complete" command of Tcl. + */ + if( c=='c' && n>=2 && strncmp(z,"complete",n)==0 ){ + char *zRes; + if( argc!=3 ){ + Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], + " complete SQL\"", 0); + return TCL_ERROR; + } + zRes = sqlite_complete(argv[2]) ? "1" : "0"; + Tcl_SetResult(interp, zRes, TCL_VOLATILE); + }else + + /* + ** $db eval $sql ?array { ...code... }? + ** + ** The SQL statement in $sql is evaluated. For each row, the values are + ** placed in elements of the array named "array" and ...code.. is executed. + ** If "array" and "code" are omitted, then no callback is every invoked. + ** If "array" is an empty string, then the values are placed in variables + ** that have the same name as the fields extracted by the query. + */ + if( c=='e' && strncmp(z,"eval",n)==0 ){ + CallbackData cbData; + char *zErrMsg; + int rc; + + if( argc!=5 && argc!=3 ){ + Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], + " eval SQL ?ARRAY-NAME CODE?", 0); + return TCL_ERROR; + } + if( argc==5 ){ + cbData.interp = interp; + cbData.zArray = argv[3]; + cbData.zCode = argv[4]; + zErrMsg = 0; + rc = sqlite_exec(db, argv[2], DbEvalCallback, &cbData, &zErrMsg); + }else{ + rc = sqlite_exec(db, argv[2], 0, 0, &zErrMsg); + } + if( zErrMsg ){ + Tcl_SetResult(interp, zErrMsg, TCL_VOLATILE); + free(zErrMsg); + } + return rc; + } + + /* The default + */ + else{ + Tcl_AppendResult(interp,"unknown subcommand \"", z, + "\" - should be one of: close complete eval", 0); + return TCL_ERROR; + } + return TCL_OK; +} + +/* +** sqlite DBNAME FILENAME ?MODE? +** +** This is the main Tcl command. When the "sqlite" Tcl command is +** invoked, this routine runs to process that command. +** +** The first argument, DBNAME, is an arbitrary name for a new +** database connection. This command creates a new command named +** DBNAME that is used to control that connection. The database +** connection is deleted when the DBNAME command is deleted. +** +** The second argument is the name of the directory that contains +** the sqlite database that is to be accessed. +*/ +static int DbMain(void *cd, Tcl_Interp *interp, int argc, char **argv){ + int mode; + sqlite *p; + char *zErrMsg; + if( argc!=3 && argc!=4 ){ + Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], + " HANDLE FILENAME ?MODE?\"", 0); + return TCL_ERROR; + } + if( argc==3 ){ + mode = 0; + }else if( Tcl_GetInt(interp, argv[3], &mode)!=TCL_OK ){ + return TCL_ERROR; + } + zErrMsg = 0; + p = sqlite_open(argv[2], mode, &zErrMsg); + if( p==0 ){ + Tcl_SetResult(interp, zErrMsg, TCL_VOLATILE); + free(zErrMsg); + return TCL_ERROR; + } + Tcl_CreateCommand(interp, argv[1], DbCmd, p, DbDeleteCmd); + return TCL_OK; +} + +/* +** Initialize this module. +** +** This Tcl module contains only a single new Tcl command named "sqlite". +** (Hence there is no namespace. There is no point in using a namespace +** if the extension only supplies one new name!) The "sqlite" command is +** used to open a new SQLite database. See the DbMain() routine above +** for additional information. +*/ +int Sqlite_Init(Tcl_Interp *interp){ + Tcl_CreateCommand(interp, "sqlite", DbMain, 0, 0); + return TCL_OK; +} +int Sqlite_SafeInit(Tcl_Interp *interp){ + return TCL_OK; +} + +/* +** If compiled using mktclapp, this routine runs to initialize +** everything. +*/ +int Et_AppInit(Tcl_Interp *interp){ + return Sqlite_Init(interp); +} diff --git a/src/tokenize.c b/src/tokenize.c new file mode 100644 index 000000000..f762b679d --- /dev/null +++ b/src/tokenize.c @@ -0,0 +1,375 @@ +/* +** Copyright (c) 1999, 2000 D. Richard Hipp +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public +** License as published by the Free Software Foundation; either +** version 2 of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public +** License along with this library; if not, write to the +** Free Software Foundation, Inc., 59 Temple Place - Suite 330, +** Boston, MA 02111-1307, USA. +** +** Author contact information: +** drh@hwaci.com +** http://www.hwaci.com/drh/ +** +************************************************************************* +** An tokenizer for SQL +** +** This file contains C code that splits an SQL input string up into +** individual tokens and sends those tokens one-by-one over to the +** parser for analysis. +** +** $Id: tokenize.c,v 1.1 2000/05/29 14:26:02 drh Exp $ +*/ +#include "sqliteInt.h" +#include <ctype.h> + +/* +** All the keywords of the SQL language are stored as in a hash +** table composed of instances of the following structure. +*/ +typedef struct Keyword Keyword; +struct Keyword { + char *zName; /* The keyword name */ + int len; /* Number of characters in the keyword */ + int tokenType; /* The token value for this keyword */ + Keyword *pNext; /* Next keyword with the same hash */ +}; + +/* +** These are the keywords +*/ +static Keyword aKeywordTable[] = { + { "AND", 0, TK_AND, 0 }, + { "AS", 0, TK_AS, 0 }, + { "ASC", 0, TK_ASC, 0 }, + { "BY", 0, TK_BY, 0 }, + { "CHECK", 0, TK_CHECK, 0 }, + { "CONSTRAINT", 0, TK_CONSTRAINT, 0 }, + { "CREATE", 0, TK_CREATE, 0 }, + { "DEFAULT", 0, TK_DEFAULT, 0 }, + { "DELETE", 0, TK_DELETE, 0 }, + { "DESC", 0, TK_DESC, 0 }, + { "DROP", 0, TK_DROP, 0 }, + { "EXPLAIN", 0, TK_EXPLAIN, 0 }, + { "FROM", 0, TK_FROM, 0 }, + { "INDEX", 0, TK_INDEX, 0 }, + { "INSERT", 0, TK_INSERT, 0 }, + { "INTO", 0, TK_INTO, 0 }, + { "IS", 0, TK_IS, 0 }, + { "ISNULL", 0, TK_ISNULL, 0 }, + { "KEY", 0, TK_KEY, 0 }, + { "NOT", 0, TK_NOT, 0 }, + { "NOTNULL", 0, TK_NOTNULL, 0 }, + { "NULL", 0, TK_NULL, 0 }, + { "ON", 0, TK_ON, 0 }, + { "OR", 0, TK_OR, 0 }, + { "ORDER", 0, TK_ORDER, 0 }, + { "PRIMARY", 0, TK_PRIMARY, 0 }, + { "SELECT", 0, TK_SELECT, 0 }, + { "SET", 0, TK_SET, 0 }, + { "TABLE", 0, TK_TABLE, 0 }, + { "UNIQUE", 0, TK_UNIQUE, 0 }, + { "UPDATE", 0, TK_UPDATE, 0 }, + { "VALUES", 0, TK_VALUES, 0 }, + { "WHERE", 0, TK_WHERE, 0 }, +}; + +/* +** This is the hash table +*/ +#define KEY_HASH_SIZE 37 +static Keyword *apHashTable[KEY_HASH_SIZE]; + + +/* +** This function looks up an identifier to determine if it is a +** keyword. If it is a keyword, the token code of that keyword is +** returned. If the input is not a keyword, TK_ID is returned. +*/ +static int sqliteKeywordCode(const char *z, int n){ + int h; + Keyword *p; + if( aKeywordTable[0].len==0 ){ + /* Initialize the keyword hash table */ + int i; + int n; + n = sizeof(aKeywordTable)/sizeof(aKeywordTable[0]); + for(i=0; i<n; i++){ + aKeywordTable[i].len = strlen(aKeywordTable[i].zName); + h = sqliteHashNoCase(aKeywordTable[i].zName, aKeywordTable[i].len); + h %= KEY_HASH_SIZE; + aKeywordTable[i].pNext = apHashTable[h]; + apHashTable[h] = &aKeywordTable[i]; + } + } + h = sqliteHashNoCase(z, n) % KEY_HASH_SIZE; + for(p=apHashTable[h]; p; p=p->pNext){ + if( p->len==n && sqliteStrNICmp(p->zName, z, n)==0 ){ + return p->tokenType; + } + } + return TK_ID; +} + +/* +** Return the length of the token that begins at z[0]. Return +** -1 if the token is (or might be) incomplete. Store the token +** type in *tokenType before returning. +*/ +int sqliteGetToken(const char *z, int *tokenType){ + int i; + switch( *z ){ + case ' ': case '\t': case '\n': case '\f': { + for(i=1; z[i] && isspace(z[i]); i++){} + *tokenType = TK_SPACE; + return i; + } + case '-': { + if( z[1]==0 ) return -1; + if( z[1]=='-' ){ + for(i=2; z[i] && z[i]!='\n'; i++){} + *tokenType = TK_COMMENT; + return i; + } + *tokenType = TK_MINUS; + return 1; + } + case '(': { + *tokenType = TK_LP; + return 1; + } + case ')': { + *tokenType = TK_RP; + return 1; + } + case ';': { + *tokenType = TK_SEMI; + return 1; + } + case '+': { + *tokenType = TK_PLUS; + return 1; + } + case '*': { + *tokenType = TK_STAR; + return 1; + } + case '/': { + *tokenType = TK_SLASH; + return 1; + } + case '=': { + *tokenType = TK_EQ; + return 1 + (z[1]=='='); + } + case '<': { + if( z[1]=='=' ){ + *tokenType = TK_LE; + return 2; + }else if( z[1]=='>' ){ + *tokenType = TK_NE; + return 2; + }else{ + *tokenType = TK_LT; + return 1; + } + } + case '>': { + if( z[1]=='=' ){ + *tokenType = TK_GE; + return 2; + }else{ + *tokenType = TK_GT; + return 1; + } + } + case '!': { + if( z[1]!='=' ){ + *tokenType = TK_ILLEGAL; + return 1; + }else{ + *tokenType = TK_NE; + return 2; + } + } + case ',': { + *tokenType = TK_COMMA; + return 1; + } + case '\'': case '"': { + int delim = z[0]; + for(i=1; z[i]; i++){ + if( z[i]==delim ){ + if( z[i+1]==delim ){ + i++; + }else{ + break; + } + } + } + if( z[i] ) i++; + *tokenType = TK_STRING; + return i; + } + case '.': { + if( !isdigit(z[1]) ){ + *tokenType = TK_DOT; + return 1; + } + /* Fall thru into the next case */ + } + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': { + for(i=1; z[i] && isdigit(z[i]); i++){} + if( z[i]=='.' ){ + i++; + while( z[i] && isdigit(z[i]) ){ i++; } + if( (z[i]=='e' || z[i]=='E') && + ( isdigit(z[i+1]) + || ((z[i+1]=='+' || z[i+1]=='-') && isdigit(z[i+2])) + ) + ){ + i += 2; + while( z[i] && isdigit(z[i]) ){ i++; } + } + *tokenType = TK_FLOAT; + }else if( z[0]=='.' ){ + *tokenType = TK_FLOAT; + }else{ + *tokenType = TK_INTEGER; + } + return i; + } + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': + case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': + case 's': case 't': case 'u': case 'v': case 'w': case 'x': + case 'y': case 'z': case '_': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': + case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': + case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': { + for(i=1; z[i] && (isalnum(z[i]) || z[i]=='_'); i++){} + *tokenType = sqliteKeywordCode(z, i); + return i; + } + default: { + break; + } + } + *tokenType = TK_ILLEGAL; + return 1; +} + +/* +** Run the parser on the given SQL string. The parser structure is +** passed in. Return the number of errors. +*/ +int sqliteRunParser(Parse *pParse, char *zSql, char **pzErrMsg){ + int nErr = 0; + int i; + void *pEngine; + int once = 1; + static FILE *trace = 0; + extern void *sqliteParserAlloc(void*(*)(int)); + extern void sqliteParserFree(void*, void(*)(void*)); + extern int sqliteParser(void*, int, ...); + extern void sqliteParserTrace(FILE*, char *); + + i = 0; + pEngine = sqliteParserAlloc(sqliteMalloc); + if( pEngine==0 ){ + sqliteSetString(pzErrMsg, "out of memory", 0); + return 1; + } + sqliteParserTrace(trace, "parser: "); + while( nErr==0 && i>=0 && zSql[i]!=0 ){ + int tokenType; + + pParse->sLastToken.z = &zSql[i]; + pParse->sLastToken.n = sqliteGetToken(&zSql[i], &tokenType); + i += pParse->sLastToken.n; + if( once ){ + pParse->sFirstToken = pParse->sLastToken; + once = 0; + } + switch( tokenType ){ + case TK_SPACE: + break; + case TK_COMMENT: { + /* Various debugging modes can be turned on and off using + ** special SQL comments. Check for the special comments + ** here and take approriate action if found. + */ + char *z = pParse->sLastToken.z; + if( sqliteStrNICmp(z,"--parser-trace-on--",19)==0 ){ + trace = stderr; + sqliteParserTrace(trace, "parser: "); + }else if( sqliteStrNICmp(z,"--parser-trace-off--", 20)==0 ){ + trace = 0; + sqliteParserTrace(trace, "parser: "); + }else if( sqliteStrNICmp(z,"--vdbe-trace-on--",17)==0 ){ + pParse->db->flags |= SQLITE_VdbeTrace; + }else if( sqliteStrNICmp(z,"--vdbe-trace-off--", 19)==0 ){ + pParse->db->flags &= ~SQLITE_VdbeTrace; + } + break; + } + case TK_ILLEGAL: + sqliteSetNString(pzErrMsg, "illegal token: \"", -1, + pParse->sLastToken.z, pParse->sLastToken.n, 0); + nErr++; + break; + default: + sqliteParser(pEngine, tokenType, pParse->sLastToken, pParse); + if( pParse->zErrMsg ){ + sqliteSetNString(pzErrMsg, "near \"", -1, + pParse->sErrToken.z, pParse->sErrToken.n, + "\": ", -1, + pParse->zErrMsg, -1, + 0); + nErr++; + } + break; + } + } + if( nErr==0 ){ + sqliteParser(pEngine, 0, pParse->sLastToken, pParse); + if( pParse->zErrMsg ){ + sqliteSetNString(pzErrMsg, "near \"", -1, + pParse->sErrToken.z, pParse->sErrToken.n, + "\": ", -1, + pParse->zErrMsg, -1, + 0); + nErr++; + } + } + sqliteParserFree(pEngine, sqliteFree); + if( pParse->zErrMsg ){ + if( pzErrMsg ){ + *pzErrMsg = pParse->zErrMsg; + }else{ + sqliteFree(pParse->zErrMsg); + } + if( !nErr ) nErr++; + } + if( pParse->pVdbe ){ + sqliteVdbeDelete(pParse->pVdbe); + pParse->pVdbe = 0; + } + if( pParse->pNewTable ){ + sqliteDeleteTable(pParse->db, pParse->pNewTable); + pParse->pNewTable = 0; + } + return nErr; +} diff --git a/src/util.c b/src/util.c new file mode 100644 index 000000000..130634547 --- /dev/null +++ b/src/util.c @@ -0,0 +1,445 @@ +/* +** Copyright (c) 1999, 2000 D. Richard Hipp +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public +** License as published by the Free Software Foundation; either +** version 2 of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public +** License along with this library; if not, write to the +** Free Software Foundation, Inc., 59 Temple Place - Suite 330, +** Boston, MA 02111-1307, USA. +** +** Author contact information: +** drh@hwaci.com +** http://www.hwaci.com/drh/ +** +************************************************************************* +** Utility functions used throughout sqlite. +** +** This file contains functions for allocating memory, comparing +** strings, and stuff like that. +** +** $Id: util.c,v 1.1 2000/05/29 14:26:02 drh Exp $ +*/ +#include "sqliteInt.h" +#include <stdarg.h> +#include <ctype.h> + +/* +** Allocate new memory and set it to zero. Return NULL if +** no memory is available. +*/ +void *sqliteMalloc(int n){ + void *p = malloc(n); + if( p==0 ) return 0; + memset(p, 0, n); + return p; +} + +/* +** Free memory previously obtained from sqliteMalloc() +*/ +void sqliteFree(void *p){ + if( p ) free(p); +} + +/* +** Resize a prior allocation. If p==0, then this routine +** works just like sqliteMalloc(). If n==0, then this routine +** works just like sqliteFree(). +*/ +void *sqliteRealloc(void *p, int n){ + if( p==0 ){ + return sqliteMalloc(n); + } + if( n==0 ){ + sqliteFree(p); + return 0; + } + return realloc(p, n); +} + +/* +** Create a string from the 2nd and subsequent arguments (up to the +** first NULL argument), store the string in memory obtained from +** sqliteMalloc() and make the pointer indicated by the 1st argument +** point to that string. +*/ +void sqliteSetString(char **pz, const char *zFirst, ...){ + va_list ap; + int nByte; + const char *z; + char *zResult; + + if( pz==0 ) return; + nByte = strlen(zFirst) + 1; + va_start(ap, zFirst); + while( (z = va_arg(ap, const char*))!=0 ){ + nByte += strlen(z); + } + va_end(ap); + sqliteFree(*pz); + *pz = zResult = sqliteMalloc( nByte ); + if( zResult==0 ) return; + strcpy(zResult, zFirst); + zResult += strlen(zResult); + va_start(ap, zFirst); + while( (z = va_arg(ap, const char*))!=0 ){ + strcpy(zResult, z); + zResult += strlen(zResult); + } + va_end(ap); +} + +/* +** Works like sqliteSetString, but each string is now followed by +** a length integer. -1 means use the whole string. +*/ +void sqliteSetNString(char **pz, ...){ + va_list ap; + int nByte; + const char *z; + char *zResult; + int n; + + if( pz==0 ) return; + nByte = 0; + va_start(ap, pz); + while( (z = va_arg(ap, const char*))!=0 ){ + n = va_arg(ap, int); + if( n<=0 ) n = strlen(z); + nByte += n; + } + va_end(ap); + sqliteFree(*pz); + *pz = zResult = sqliteMalloc( nByte + 1 ); + if( zResult==0 ) return; + va_start(ap, pz); + while( (z = va_arg(ap, const char*))!=0 ){ + n = va_arg(ap, int); + if( n<=0 ) n = strlen(z); + strncpy(zResult, z, n); + zResult += n; + } + *zResult = 0; + va_end(ap); +} + +/* An array to map all upper-case characters into their corresponding +** lower-case character. +*/ +static unsigned char UpperToLower[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121, + 122, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105,106,107, + 108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125, + 126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161, + 162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179, + 180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197, + 198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215, + 216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233, + 234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251, + 252,253,254,255 +}; + +/* +** This function computes a hash on the name of a keyword. +** Case is not significant. +*/ +int sqliteHashNoCase(const char *z, int n){ + int h = 0; + int c; + if( n<=0 ) n = strlen(z); + while( n-- > 0 && (c = *z++)!=0 ){ + h = h<<3 ^ h ^ UpperToLower[c]; + } + if( h<0 ) h = -h; + return h; +} + +/* +** Some system shave stricmp(). Others have strcasecmp(). Because +** there is no consistency, we will define our own. +*/ +int sqliteStrICmp(const char *zLeft, const char *zRight){ + register unsigned char *a, *b; + a = (unsigned char *)zLeft; + b = (unsigned char *)zRight; + while( *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } + return *a - *b; +} +int sqliteStrNICmp(const char *zLeft, const char *zRight, int N){ + register unsigned char *a, *b; + a = (unsigned char *)zLeft; + b = (unsigned char *)zRight; + while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } + return N<=0 ? 0 : *a - *b; +} + +/* Notes on string comparisions. +** +** We want the main string comparision function used for sorting to +** sort both numbers and alphanumeric words into the correct sequence. +** The same routine should do both without prior knowledge of which +** type of text the input represents. It should even work for strings +** which are a mixture of text and numbers. +** +** To accomplish this, we keep track of a state number while scanning +** the two strings. The states are as follows: +** +** 1 Beginning of word +** 2 Arbitrary text +** 3 Integer +** 4 Negative integer +** 5 Real number +** 6 Negative real +** +** The scan begins in state 1, beginning of word. Transitions to other +** states are determined by characters seen, as shown in the following +** chart: +** +** Current State Character Seen New State +** -------------------- -------------- ------------------- +** 0 Beginning of word "-" 3 Negative integer +** digit 2 Integer +** space 0 Beginning of word +** otherwise 1 Arbitrary text +** +** 1 Arbitrary text space 0 Beginning of word +** digit 2 Integer +** otherwise 1 Arbitrary text +** +** 2 Integer space 0 Beginning of word +** "." 4 Real number +** digit 2 Integer +** otherwise 1 Arbitrary text +** +** 3 Negative integer space 0 Beginning of word +** "." 5 Negative Real num +** digit 3 Negative integer +** otherwise 1 Arbitrary text +** +** 4 Real number space 0 Beginning of word +** digit 4 Real number +** otherwise 1 Arbitrary text +** +** 5 Negative real num space 0 Beginning of word +** digit 5 Negative real num +** otherwise 1 Arbitrary text +** +** To implement this state machine, we first classify each character +** into on of the following categories: +** +** 0 Text +** 1 Space +** 2 Digit +** 3 "-" +** 4 "." +** +** Given an arbitrary character, the array charClass[] maps that character +** into one of the atove categories. +*/ +static const unsigned char charClass[] = { + /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */ +/* 0x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, +/* 1x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* 2x */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 0, +/* 3x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, +/* 4x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* 5x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* 6x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* 7x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* 8x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* 9x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* Ax */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* Bx */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* Cx */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* Dx */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* Ex */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* Fx */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; +#define N_CHAR_CLASS 5 + +/* +** Given the current state number (0 thru 5), this array figures +** the new state number given the character class. +*/ +static const unsigned char stateMachine[] = { + /* Text, Space, Digit, "-", "." */ + 1, 0, 2, 3, 1, /* State 0: Beginning of word */ + 1, 0, 2, 1, 1, /* State 1: Arbitrary text */ + 1, 0, 2, 1, 4, /* State 2: Integer */ + 1, 0, 3, 1, 5, /* State 3: Negative integer */ + 1, 0, 4, 1, 1, /* State 4: Real number */ + 1, 0, 5, 1, 1, /* State 5: Negative real num */ +}; + +/* This routine does a comparison of two strings. Case is used only +** if useCase!=0. Numbers compare in numerical order. +*/ +static int privateStrCmp(const char *atext, const char *btext, int useCase){ + register unsigned char *a, *b, *map, ca, cb; + int result; + register int cclass = 0; + + a = (unsigned char *)atext; + b = (unsigned char *)btext; + if( useCase ){ + do{ + if( (ca= *a++)!=(cb= *b++) ) break; + cclass = stateMachine[cclass*N_CHAR_CLASS + charClass[ca]]; + }while( ca!=0 ); + }else{ + map = UpperToLower; + do{ + if( (ca=map[*a++])!=(cb=map[*b++]) ) break; + cclass = stateMachine[cclass*N_CHAR_CLASS + charClass[ca]]; + }while( ca!=0 ); + } + switch( cclass ){ + case 0: + case 1: { + if( isdigit(ca) && isdigit(cb) ){ + cclass = 2; + } + break; + } + default: { + break; + } + } + switch( cclass ){ + case 2: + case 3: { + if( isdigit(ca) ){ + if( isdigit(cb) ){ + int acnt, bcnt; + acnt = bcnt = 0; + while( isdigit(*a++) ) acnt++; + while( isdigit(*b++) ) bcnt++; + result = acnt - bcnt; + if( result==0 ) result = ca-cb; + }else{ + result = 1; + } + }else if( isdigit(cb) ){ + result = -1; + }else if( ca=='.' ){ + result = 1; + }else if( cb=='.' ){ + result = -1; + }else{ + result = ca - cb; + cclass = 2; + } + if( cclass==3 ) result = -result; + break; + } + case 0: + case 1: + case 4: { + result = ca - cb; + break; + } + case 5: { + result = cb - ca; + }; + } + return result; +} + +/* This comparison routine is what we use for comparison operations +** in an SQL expression. (Ex: name<'Hello' or value<5). Compare two +** strings. Use case only as a tie-breaker. Numbers compare in +** numerical order. +*/ +int sqliteCompare(const char *atext, const char *btext){ + int result; + result = privateStrCmp(atext, btext, 0); + if( result==0 ) result = privateStrCmp(atext, btext, 1); + return result; +} + +/* +** If you compile just this one file with the -DTEST_COMPARE=1 option, +** it generates a program to test the comparisons routines. +*/ +#ifdef TEST_COMPARE +#include <stdlib.h> +#include <stdio.h> +int sortCmp(const char **a, const char **b){ + return sqliteCompare(*a, *b); +} +int main(int argc, char **argv){ + int i, j, k, n; + static char *azStr[] = { + "abc", "aBc", "abcd", "aBcd", + "123", "124", "1234", "-123", "-124", "-1234", + "123.45", "123.456", "123.46", "-123.45", "-123.46", "-123.456", + "x9", "x10", "x-9", "x-10", "X9", "X10", + }; + n = sizeof(azStr)/sizeof(azStr[0]); + qsort(azStr, n, sizeof(azStr[0]), sortCmp); + for(i=0; i<n; i++){ + printf("%s\n", azStr[i]); + } + printf("Sanity1..."); + fflush(stdout); + for(i=0; i<n-1; i++){ + char *a = azStr[i]; + for(j=i+1; j<n; j++){ + char *b = azStr[j]; + if( sqliteCompare(a,b) != -sqliteCompare(b,a) ){ + printf("Failed! \"%s\" vs \"%s\"\n", a, b); + i = j = n; + } + } + } + if( i<n ){ + printf(" OK\n"); + } + return 0; +} +#endif + +/* +** This routine is used for sorting. Each key is a list one or more +** null-terminated strings. The list is terminated by two null in +** a row. For example, the following text is strings: +** +** +one\000-two\000+three\000\000 +** +** Both arguments will have the same number of strings. This routine +** returns negative, zero, or positive if the first argument is less +** than, equal to, or greater than the first. (Result is a-b). +** +** Every string begins with either a "+" or "-" character. If the +** character is "-" then the return value is negated. This is done +** to implement a sort in descending order. +*/ +int sqliteSortCompare(const char *a, const char *b){ + int len; + int res = 0; + + while( res==0 && *a && *b ){ + res = sqliteCompare(&a[1], &b[1]); + if( res==0 ){ + len = strlen(a) + 1; + a += len; + b += len; + } + } + if( *a=='-' ) res = -res; + return res; +} diff --git a/src/vdbe.c b/src/vdbe.c new file mode 100644 index 000000000..a93c237c2 --- /dev/null +++ b/src/vdbe.c @@ -0,0 +1,1973 @@ +/* +** Copyright (c) 1999, 2000 D. Richard Hipp +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public +** License as published by the Free Software Foundation; either +** version 2 of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public +** License along with this library; if not, write to the +** Free Software Foundation, Inc., 59 Temple Place - Suite 330, +** Boston, MA 02111-1307, USA. +** +** Author contact information: +** drh@hwaci.com +** http://www.hwaci.com/drh/ +** +************************************************************************* +** The code in this file implements the Virtual Database Engine (VDBE) +** +** The SQL parser generates a program which is then executed by +** the VDBE to do the work of the SQL statement. VDBE programs are +** similar in form to assembly language. The program consists of +** a linear sequence of operations. Each operation has an opcode +** and 3 operands. Operands P1 and P2 are integers. Operand P3 +** is a null-terminated string. The P2 operand must be non-negative. +** Opcodes will typically ignore one or more operands. Many opcodes +** ignore all three operands. +** +** Computation results are stored on a stack. Each entry on the +** stack is either an integer or a null-terminated string. An +** inplicit conversion from one type to the other occurs as necessary. +** +** Most of the code in this file is taken up by the sqliteVdbeExec() +** function which does the work of interpreting a VDBE program. +** But other routines are also provided to help in building up +** a program instruction by instruction. +** +** $Id: vdbe.c,v 1.1 2000/05/29 14:26:02 drh Exp $ +*/ +#include "sqliteInt.h" + +/* +** SQL is translated into a sequence of instructions to be +** executed by a virtual machine. Each instruction is an instance +** of the following structure. +*/ +typedef struct VdbeOp Op; + +/* +** Every table that the virtual machine has open is represented by an +** instance of the following structure. +*/ +struct VdbeTable { + DbbeTable *pTable; /* The table structure of the backend */ + int index; /* The next index to extract */ +}; +typedef struct VdbeTable VdbeTable; + +/* +** A sorter builds a list of elements to be sorted. Each element of +** the list is an instance of the following structure. +*/ +typedef struct Sorter Sorter; +struct Sorter { + int nKey; /* Number of bytes in the key */ + char *zKey; /* The key by which we will sort */ + int nData; /* Number of bytes in the data */ + char *pData; /* The data associated with this key */ + Sorter *pNext; /* Next in the list */ +}; + +/* +** Number of buckets used for merge-sort. +*/ +#define NSORT 30 + +/* +** An instance of the virtual machine +*/ +struct Vdbe { + Dbbe *pBe; /* Opaque context structure used by DB backend */ + FILE *trace; /* Write an execution trace here, if not NULL */ + int nOp; /* Number of instructions in the program */ + int nOpAlloc; /* Number of slots allocated for aOp[] */ + Op *aOp; /* Space to hold the virtual machine's program */ + int nLabel; /* Number of labels used */ + int nLabelAlloc; /* Number of slots allocated in aLabel[] */ + int *aLabel; /* Space to hold the labels */ + int tos; /* Index of top of stack */ + int nStackAlloc; /* Size of the stack */ + int *iStack; /* Integer values of the stack */ + char **zStack; /* Text or binary values of the stack */ + char **azColName; /* Becomes the 4th parameter to callbacks */ + int nTable; /* Number of slots in aTab[] */ + VdbeTable *aTab; /* On element of this array for each open table */ + int nList; /* Number of slots in apList[] */ + FILE **apList; /* An open file for each list */ + int nSort; /* Number of slots in apSort[] */ + Sorter **apSort; /* An open sorter list */ +}; + +/* +** Create a new virtual database engine. +*/ +Vdbe *sqliteVdbeCreate(Dbbe *pBe){ + Vdbe *p; + + p = sqliteMalloc( sizeof(Vdbe) ); + p->pBe = pBe; + return p; +} + +/* +** Turn tracing on or off +*/ +void sqliteVdbeTrace(Vdbe *p, FILE *trace){ + p->trace = trace; +} + +/* +** Add a new instruction to the list of instructions current in the +** VDBE. Return the address of the new instruction. +** +** Parameters: +** +** p Pointer to the VDBE +** +** op The opcode for this instruction +** +** p1, p2, p3 Three operands. +** +** lbl A symbolic label for this instruction. +** +** Symbolic labels are negative numbers that stand for the address +** of instructions that have yet to be coded. When the instruction +** is coded, its real address is substituted in the p2 field of +** prior and subsequent instructions that have the lbl value in +** their p2 fields. +*/ +int sqliteVdbeAddOp(Vdbe *p, int op, int p1, int p2, const char *p3, int lbl){ + int i, j; + + i = p->nOp; + p->nOp++; + if( i>=p->nOpAlloc ){ + int oldSize = p->nOpAlloc; + p->nOpAlloc = p->nOpAlloc*2 + 10; + p->aOp = sqliteRealloc(p->aOp, p->nOpAlloc*sizeof(Op)); + if( p->aOp==0 ){ + p->nOp = 0; + p->nOpAlloc = 0; + return 0; + } + memset(&p->aOp[oldSize], 0, (p->nOpAlloc-oldSize)*sizeof(Op)); + } + p->aOp[i].opcode = op; + p->aOp[i].p1 = p1; + if( p2<0 && (-1-p2)<p->nLabel && p->aLabel[-1-p2]>=0 ){ + p2 = p->aLabel[-1-p2]; + } + p->aOp[i].p2 = p2; + if( p3 && p3[0] ){ + sqliteSetString(&p->aOp[i].p3, p3, 0); + }else{ + p->aOp[i].p3 = 0; + } + if( lbl<0 && (-lbl)<=p->nLabel ){ + p->aLabel[-1-lbl] = i; + for(j=0; j<i; j++){ + if( p->aOp[j].p2==lbl ) p->aOp[j].p2 = i; + } + } + return i; +} + +/* +** Resolve label "x" to be the address of the next instruction to +** be inserted. +*/ +void sqliteVdbeResolveLabel(Vdbe *p, int x){ + int j; + if( x<0 && (-x)<=p->nLabel ){ + p->aLabel[-1-x] = p->nOp; + for(j=0; j<p->nOp; j++){ + if( p->aOp[j].p2==x ) p->aOp[j].p2 = p->nOp; + } + } +} + +/* +** Return the address of the next instruction to be inserted. +*/ +int sqliteVdbeCurrentAddr(Vdbe *p){ + return p->nOp; +} + +/* +** Add a whole list of operations to the operation stack. Return the +** address of the first operation added. +*/ +int sqliteVdbeAddOpList(Vdbe *p, int nOp, VdbeOp const *aOp){ + int addr; + if( p->nOp + nOp >= p->nOpAlloc ){ + int oldSize = p->nOpAlloc; + p->nOpAlloc = p->nOpAlloc*2 + nOp + 10; + p->aOp = sqliteRealloc(p->aOp, p->nOpAlloc*sizeof(Op)); + if( p->aOp==0 ){ + p->nOp = 0; + p->nOpAlloc = 0; + return 0; + } + memset(&p->aOp[oldSize], 0, (p->nOpAlloc-oldSize)*sizeof(Op)); + } + addr = p->nOp; + if( nOp>0 ){ + int i; + for(i=0; i<nOp; i++){ + int p2 = aOp[i].p2; + if( p2<0 ) p2 = addr + ADDR(p2); + sqliteVdbeAddOp(p, aOp[i].opcode, aOp[i].p1, p2, aOp[i].p3, 0); + } + } + return addr; +} + +/* +** Change the value of the P3 operand for a specific instruction. +** This routine is useful when a large program is loaded from a +** static array using sqliteVdbeAddOpList but we want to make a +** few minor changes to the program. +*/ +void sqliteVdbeChangeP3(Vdbe *p, int addr, const char *zP3, int n){ + if( p && addr>=0 && p->nOp>addr && zP3 ){ + sqliteSetNString(&p->aOp[addr].p3, zP3, n, 0); + } +} + +/* +** If the P3 operand to the specified instruction appears +** to be a quoted string token, then this procedure removes +** the quotes. +** +** The quoting operator can be either a grave ascent (ASCII 0x27) +** or a double quote character (ASCII 0x22). Two quotes in a row +** resolve to be a single actual quote character within the string. +*/ +void sqliteVdbeDequoteP3(Vdbe *p, int addr){ + int quote; + int i, j; + char *z; + if( addr<0 || addr>=p->nOp ) return; + z = p->aOp[addr].p3; + quote = z[0]; + if( quote!='\'' && quote!='"' ) return; + for(i=1, j=0; z[i]; i++){ + if( z[i]==quote ){ + if( z[i+1]==quote ){ + z[j++] = quote; + i++; + }else{ + z[j++] = 0; + break; + } + }else{ + z[j++] = z[i]; + } + } +} + +/* +** Create a new symbolic label for an instruction that has yet to be +** coded. The symbolic label is really just a negative number. The +** label can be used as the P2 value of an operation. Later, when +** the label is resolved to a specific address, the VDBE will scan +** through its operation list and change all values of P2 which match +** the label into the resolved address. +** +** The VDBE knows that a P2 value is a label because labels are +** always negative and P2 values are suppose to be non-negative. +** Hence, a negative P2 value is a label that has yet to be resolved. +*/ +int sqliteVdbeMakeLabel(Vdbe *p){ + int i; + i = p->nLabel++; + if( i>=p->nLabelAlloc ){ + p->nLabelAlloc = p->nLabelAlloc*2 + 10; + p->aLabel = sqliteRealloc( p->aLabel, p->nLabelAlloc*sizeof(int)); + } + if( p->aLabel==0 ){ + p->nLabel = 0; + p->nLabelAlloc = 0; + return 0; + } + p->aLabel[i] = -1; + return -1-i; +} + +/* +** Pop the stack N times. Free any memory associated with the +** popped stack elements. +*/ +static void PopStack(Vdbe *p, int N){ + if( p->zStack==0 ) return; + while( p->tos>=0 && N-->0 ){ + int i = p->tos--; + sqliteFree(p->zStack[i]); + p->zStack[i] = 0; + } +} + +/* +** Clean up the VM after execution. +** +** This routine will automatically close any tables, list, and/or +** sorters that were left open. +*/ +static void Cleanup(Vdbe *p){ + int i; + PopStack(p, p->tos+1); + sqliteFree(p->azColName); + p->azColName = 0; + for(i=0; i<p->nTable; i++){ + if( p->aTab[i].pTable ){ + sqliteDbbeCloseTable(p->aTab[i].pTable); + p->aTab[i].pTable = 0; + } + } + sqliteFree(p->aTab); + p->aTab = 0; + p->nTable = 0; + for(i=0; i<p->nList; i++){ + if( p->apList[i] ){ + sqliteDbbeCloseTempFile(p->pBe, p->apList[i]); + p->apList[i] = 0; + } + } + sqliteFree(p->apList); + p->apList = 0; + p->nList = 0; + for(i=0; i<p->nSort; i++){ + Sorter *pSorter; + while( (pSorter = p->apSort[i])!=0 ){ + p->apSort[i] = pSorter->pNext; + sqliteFree(pSorter->zKey); + sqliteFree(pSorter->pData); + sqliteFree(pSorter); + } + } + sqliteFree(p->apSort); + p->apSort = 0; + p->nSort = 0; +} + +/* +** Delete an entire VDBE. +*/ +void sqliteVdbeDelete(Vdbe *p){ + int i; + if( p==0 ) return; + Cleanup(p); + if( p->nOpAlloc==0 ){ + p->aOp = 0; + p->nOp = 0; + } + for(i=0; i<p->nOp; i++){ + sqliteFree(p->aOp[i].p3); + } + sqliteFree(p->aOp); + sqliteFree(p->aLabel); + sqliteFree(p->iStack); + sqliteFree(p->zStack); + sqliteFree(p); +} + +/* +** A translation from opcode numbers to opcode names. Used for testing +** and debugging only. +** +** If any of the numeric OP_ values for opcodes defined in sqliteVdbe.h +** change, be sure to change this array to match. You can use the +** "opNames.awk" awk script which is part of the source tree to regenerate +** this array, then copy and paste it into this file, if you want. +*/ +static char *zOpName[] = { 0, + "Open", "Close", "Destroy", "Fetch", + "New", "Put", "Delete", "Field", + "Key", "Rewind", "Next", "ResetIdx", + "NextIdx", "PutIdx", "DeleteIdx", "ListOpen", + "ListWrite", "ListRewind", "ListRead", "ListClose", + "SortOpen", "SortPut", "SortMakeRec", "SortMakeKey", + "Sort", "SortNext", "SortKey", "SortCallback", + "SortClose", "MakeRecord", "MakeKey", "Goto", + "If", "Halt", "ColumnCount", "ColumnName", + "Callback", "Integer", "String", "Pop", + "Dup", "Pull", "Add", "AddImm", + "Subtract", "Multiply", "Divide", "Min", + "Max", "Eq", "Ne", "Lt", + "Le", "Gt", "Ge", "IsNull", + "NotNull", "Negative", "And", "Or", + "Not", "Concat", "Noop", +}; + +/* +** Given the name of an opcode, return its number. Return 0 if +** there is no match. +** +** This routine is used for testing and debugging. +*/ +int sqliteVdbeOpcode(const char *zName){ + int i; + for(i=1; i<=OP_MAX; i++){ + if( sqliteStrICmp(zName, zOpName[i])==0 ) return i; + } + return 0; +} + +/* +** Give a listing of the program in the virtual machine. +** +** The interface is the same as sqliteVdbeExec(). But instead of +** running the code, it invokes the callback once for each instruction. +** This feature is used to implement "EXPLAIN". +*/ +int sqliteVdbeList( + Vdbe *p, /* The VDBE */ + sqlite_callback xCallback, /* The callback */ + void *pArg, /* 1st argument to callback */ + char **pzErrMsg /* Error msg written here */ +){ + int i, rc; + char *azField[6]; + char zAddr[20]; + char zP1[20]; + char zP2[20]; + static char *azColumnNames[] = { + "addr", "opcode", "p1", "p2", "p3", 0 + }; + + if( xCallback==0 ) return 0; + azField[0] = zAddr; + azField[2] = zP1; + azField[3] = zP2; + azField[5] = 0; + rc = 0; + if( pzErrMsg ){ *pzErrMsg = 0; } + for(i=0; rc==0 && i<p->nOp; i++){ + sprintf(zAddr,"%d",i); + sprintf(zP1,"%d", p->aOp[i].p1); + sprintf(zP2,"%d", p->aOp[i].p2); + azField[4] = p->aOp[i].p3; + if( azField[4]==0 ) azField[4] = ""; + azField[1] = zOpName[p->aOp[i].opcode]; + rc = xCallback(pArg, 5, azField, azColumnNames); + } + return rc; +} + +/* +** Make sure space has been allocated to hold at least N +** stack elements. Allocate additional stack space if +** necessary. +** +** Return 0 on success and non-zero if there are memory +** allocation errors. +*/ +static int NeedStack(Vdbe *p, int N){ + int oldAlloc; + int i; + if( N>=p->nStackAlloc ){ + oldAlloc = p->nStackAlloc; + p->nStackAlloc = N + 20; + p->iStack = sqliteRealloc(p->iStack, p->nStackAlloc*sizeof(int)); + p->zStack = sqliteRealloc(p->zStack, p->nStackAlloc*sizeof(char*)); + if( p->iStack==0 || p->zStack==0 ){ + sqliteFree(p->iStack); + sqliteFree(p->zStack); + p->iStack = 0; + p->zStack = 0; + p->nStackAlloc = 0; + return 1; + } + for(i=oldAlloc; i<p->nStackAlloc; i++){ + p->zStack[i] = 0; + } + } + return 0; +} + +/* +** Convert the given stack entity into a string if it isn't one +** already. Return non-zero if we run out of memory. +*/ +static int Stringify(Vdbe *p, int i){ + if( p->zStack[i]==0 ){ + char zBuf[30]; + sprintf(zBuf,"%d",p->iStack[i]); + sqliteSetString(&p->zStack[i], zBuf, 0); + if( p->zStack[i]==0 ) return 1; + p->iStack[i] = strlen(p->zStack[i])+1; + } + return 0; +} + +/* +** Convert the given stack entity into a integer if it isn't one +** already. +*/ +static int Integerify(Vdbe *p, int i){ + if( p->zStack[i]!=0 ){ + p->iStack[i] = atoi(p->zStack[i]); + sqliteFree(p->zStack[i]); + p->zStack[i] = 0; + } + return p->iStack[i]; +} + +/* +** The parameters are pointers to the head of two sorted lists +** of Sorter structures. Merge these two lists together and return +** a single sorted list. This routine forms the core of the merge-sort +** algorithm. +** +** In the case of a tie, left sorts in front of right. +*/ +static Sorter *Merge(Sorter *pLeft, Sorter *pRight){ + Sorter sHead; + Sorter *pTail; + pTail = &sHead; + pTail->pNext = 0; + while( pLeft && pRight ){ + int c = sqliteSortCompare(pLeft->zKey, pRight->zKey); + if( c<=0 ){ + pTail->pNext = pLeft; + pLeft = pLeft->pNext; + }else{ + pTail->pNext = pRight; + pRight = pRight->pNext; + } + pTail = pTail->pNext; + } + if( pLeft ){ + pTail->pNext = pLeft; + }else if( pRight ){ + pTail->pNext = pRight; + } + return sHead.pNext; +} + + +/* +** Execute the program in the VDBE. +** +** If an error occurs, an error message is written to memory obtained +** from sqliteMalloc() and *pzErrMsg is made to point to that memory. +** The return parameter is the number of errors. +** +** If the callback every returns non-zero, then the program exits +** immediately. No error message is written but the return value +** from the callback because the return value of this routine. +*/ +int sqliteVdbeExec( + Vdbe *p, /* The VDBE */ + sqlite_callback xCallback, /* The callback */ + void *pArg, /* 1st argument to callback */ + char **pzErrMsg /* Error msg written here */ +){ + int pc; /* The program counter */ + Op *pOp; /* Current operation */ + int rc; /* Value to return */ + char zBuf[100]; /* Space to sprintf() and integer */ + + p->tos = -1; + rc = 0; + if( pzErrMsg ){ *pzErrMsg = 0; } + for(pc=0; rc==0 && pc<p->nOp && pc>=0; pc++){ + pOp = &p->aOp[pc]; + if( p->trace ){ + fprintf(p->trace,"%4d %-12s %4d %4d %s\n", + pc, zOpName[pOp->opcode], pOp->p1, pOp->p2, + pOp->p3 ? pOp->p3 : ""); + } + switch( pOp->opcode ){ + /* Opcode: Goto P2 * * + ** + ** An unconditional jump to address P2. + ** The next instruction executed will be + ** the one at index P2 from the beginning of + ** the program. + */ + case OP_Goto: { + pc = pOp->p2; + if( pc<0 || pc>p->nOp ){ + sqliteSetString(pzErrMsg, "jump destination out of range", 0); + rc = 1; + } + pc--; + break; + } + + /* Opcode: Halt * * * + ** + ** Exit immediately. All open DBs, Lists, Sorts, etc are closed + ** automatically. + */ + case OP_Halt: { + pc = p->nOp-1; + break; + } + + /* Opcode: Integer P1 * * + ** + ** The integer value P1 is pushed onto the stack. + */ + case OP_Integer: { + int i = ++p->tos; + if( NeedStack(p, p->tos) ) goto no_mem; + p->iStack[i] = pOp->p1; + p->zStack[i] = 0; + break; + } + + /* Opcode: String * * P3 + ** + ** The string value P3 is pushed onto the stack. + */ + case OP_String: { + int i = ++p->tos; + char *z; + if( NeedStack(p, p->tos) ) goto no_mem; + z = pOp->p3; + if( z==0 ) z = ""; + p->iStack[i] = strlen(z) + 1; + sqliteSetString(&p->zStack[i], z, 0); + break; + } + + /* Opcode: Pop P1 * * + ** + ** P1 elements are popped off of the top of stack and discarded. + */ + case OP_Pop: { + PopStack(p, pOp->p1); + break; + } + + /* Opcode: Dup P1 * * + ** + ** A copy of the P1-th element of the stack + ** is made and pushed onto the top of the stack. + ** The top of the stack is element 0. So the + ** instruction "Dup 0 0 0" will make a copy of the + ** top of the stack. + */ + case OP_Dup: { + int i = p->tos - pOp->p1; + int j = ++p->tos; + if( i<0 ) goto not_enough_stack; + if( NeedStack(p, p->tos) ) goto no_mem; + p->iStack[j] = p->iStack[i]; + if( p->zStack[i] ){ + p->zStack[j] = sqliteMalloc( p->iStack[j] ); + if( p->zStack[j] ) memcpy(p->zStack[j], p->zStack[i], p->iStack[j]); + }else{ + p->zStack[j] = 0; + } + break; + } + + /* Opcode: Pull P1 * * + ** + ** The P1-th element is removed its current location on + ** the stack and pushed back on top of the stack. The + ** top of the stack is element 0, so "Pull 0 0 0" is + ** a no-op. + */ + case OP_Pull: { + int from = p->tos - pOp->p1; + int to = p->tos; + int i; + int ti; + char *tz; + if( from<0 ) goto not_enough_stack; + ti = p->iStack[from]; + tz = p->zStack[from]; + for(i=from; i<to; i++){ + p->iStack[i] = p->iStack[i+1]; + p->zStack[i] = p->zStack[i+1]; + } + p->iStack[to] = ti; + p->zStack[to] = tz; + break; + } + + /* Opcode: ColumnCount P1 * * + ** + ** Specify the number of column values that will appear in the + ** array passed as the 4th parameter to the callback. No checking + ** is done. If this value is wrong, a coredump can result. + */ + case OP_ColumnCount: { + p->azColName = sqliteRealloc(p->azColName, (pOp->p1+1)*sizeof(char*)); + if( p->azColName==0 ) goto no_mem; + p->azColName[pOp->p1] = 0; + break; + } + + /* Opcode: ColumnName P1 * P3 + ** + ** P3 becomes the P1-th column name (first is 0). An array of pointers + ** to all column names is passed as the 4th parameter to the callback. + ** The ColumnCount opcode must be executed first to allocate space to + ** hold the column names. Failure to do this will likely result in + ** a coredump. + */ + case OP_ColumnName: { + p->azColName[pOp->p1] = pOp->p3 ? pOp->p3 : ""; + break; + } + + /* Opcode: Callback P1 * * + ** + ** Pop P1 values off the stack and form them into an array. Then + ** invoke the callback function using the newly formed array as the + ** 3rd parameter. + */ + case OP_Callback: { + int i = p->tos - pOp->p1 + 1; + int j; + if( i<0 ) goto not_enough_stack; + if( NeedStack(p, p->tos+2) ) goto no_mem; + for(j=i; j<=p->tos; j++){ + if( Stringify(p, j) ) goto no_mem; + } + p->zStack[p->tos+1] = 0; + rc = xCallback(pArg, pOp->p1, &p->zStack[i], p->azColName); + PopStack(p, pOp->p1); + break; + } + + /* Opcode: Concat * * * + ** + ** Pop two elements from the stack. Append the first (what used + ** to be the top of stack) to the second (the next on stack) to + ** form a new string. Push the new string back onto the stack. + */ + case OP_Concat: { + int tos = p->tos; + int nos = tos - 1; + char *z; + if( nos<0 ) goto not_enough_stack; + Stringify(p, tos); + Stringify(p, nos); + z = 0; + sqliteSetString(&z, p->zStack[nos], p->zStack[tos], 0); + PopStack(p, 1); + sqliteFree(p->zStack[nos]); + p->zStack[nos] = z; + p->iStack[nos] = strlen(p->zStack[nos])+1; + break; + } + + /* Opcode: Add * * * + ** + ** Pop the top two elements from the stack, add them together, + ** and push the result back onto the stack. If either element + ** is a string then it is converted to a double using the atof() + ** function before the addition. + */ + /* Opcode: Multiply * * * + ** + ** Pop the top two elements from the stack, multiply them together, + ** and push the result back onto the stack. If either element + ** is a string then it is converted to a double using the atof() + ** function before the multiplication. + */ + /* Opcode: Subtract * * * + ** + ** Pop the top two elements from the stack, subtract the + ** first (what was on top of the stack) from the second (the + ** next on stack) + ** and push the result back onto the stack. If either element + ** is a string then it is converted to a double using the atof() + ** function before the subtraction. + */ + /* Opcode: Divide * * * + ** + ** Pop the top two elements from the stack, divide the + ** first (what was on top of the stack) from the second (the + ** next on stack) + ** and push the result back onto the stack. If either element + ** is a string then it is converted to a double using the atof() + ** function before the division. Division by zero causes the + ** program to abort with an error. + */ + case OP_Add: + case OP_Subtract: + case OP_Multiply: + case OP_Divide: { + int tos = p->tos; + int nos = tos - 1; + if( nos<0 ) goto not_enough_stack; + if( p->zStack[tos]==0 && p->zStack[nos]==0 ){ + int a, b; + a = p->iStack[tos]; + b = p->iStack[nos]; + switch( pOp->opcode ){ + case OP_Add: b += a; break; + case OP_Subtract: b -= a; break; + case OP_Multiply: b *= a; break; + default: { + if( a==0 ){ + sqliteSetString(pzErrMsg, "division by zero", 0); + rc = 1; + goto cleanup; + } + b /= a; + break; + } + } + PopStack(p, 1); + p->iStack[nos] = b; + }else{ + double a, b; + Stringify(p, tos); + Stringify(p, nos); + a = atof(p->zStack[tos]); + b = atof(p->zStack[nos]); + switch( pOp->opcode ){ + case OP_Add: b += a; break; + case OP_Subtract: b -= a; break; + case OP_Multiply: b *= a; break; + default: { + if( a==0.0 ){ + sqliteSetString(pzErrMsg, "division by zero", 0); + rc = 1; + goto cleanup; + } + b /= a; + break; + } + } + sprintf(zBuf,"%g",b); + PopStack(p, 1); + sqliteSetString(&p->zStack[nos], zBuf, 0); + if( p->zStack[nos]==0 ) goto no_mem; + p->iStack[nos] = strlen(p->zStack[nos]) + 1; + } + break; + } + + /* Opcode: Max * * * + ** + ** Pop the top two elements from the stack then push back the + ** largest of the two. + */ + case OP_Max: { + int tos = p->tos; + int nos = tos - 1; + if( nos<0 ) goto not_enough_stack; + if( p->zStack[tos]==0 && p->zStack[nos]==0 ){ + if( p->iStack[nos]<p->iStack[tos] ){ + p->iStack[nos] = p->iStack[tos]; + } + }else{ + Stringify(p, tos); + Stringify(p, nos); + if( sqliteCompare(p->zStack[nos], p->zStack[tos])<0 ){ + sqliteFree(p->zStack[nos]); + p->zStack[nos] = p->zStack[tos]; + p->iStack[nos] = p->iStack[tos]; + } + } + p->tos--; + break; + } + + /* Opcode: Min * * * + ** + ** Pop the top two elements from the stack then push back the + ** smaller of the two. + */ + case OP_Min: { + int tos = p->tos; + int nos = tos - 1; + if( nos<0 ) goto not_enough_stack; + if( p->zStack[tos]==0 && p->zStack[nos]==0 ){ + if( p->iStack[nos]>p->iStack[tos] ){ + p->iStack[nos] = p->iStack[tos]; + } + }else{ + Stringify(p, tos); + Stringify(p, nos); + if( sqliteCompare(p->zStack[nos], p->zStack[tos])>0 ){ + sqliteFree(p->zStack[nos]); + p->zStack[nos] = p->zStack[tos]; + p->iStack[nos] = p->iStack[tos]; + } + } + p->tos--; + break; + } + + /* Opcode: AddImm P1 * * + ** + ** Add the value P1 to whatever is on top of the stack. + */ + case OP_AddImm: { + int tos = p->tos; + if( tos<0 ) goto not_enough_stack; + Integerify(p, tos); + p->iStack[tos] += pOp->p1; + break; + } + + /* Opcode: Eq * P2 * + ** + ** Pop the top two elements from the stack. If they are equal, then + ** jump to instruction P2. Otherwise, continue to the next instruction. + */ + /* Opcode: Ne * P2 * + ** + ** Pop the top two elements from the stack. If they are not equal, then + ** jump to instruction P2. Otherwise, continue to the next instruction. + */ + /* Opcode: Lt * P2 * + ** + ** Pop the top two elements from the stack. If second element (the + ** next on stack) is less than the first (the top of stack), then + ** jump to instruction P2. Otherwise, continue to the next instruction. + ** In other words, jump if NOS<TOS. + */ + /* Opcode: Le * P2 * + ** + ** Pop the top two elements from the stack. If second element (the + ** next on stack) is less than or equal to the first (the top of stack), + ** then jump to instruction P2. In other words, jump if NOS<=TOS. + */ + /* Opcode: Gt * P2 * + ** + ** Pop the top two elements from the stack. If second element (the + ** next on stack) is greater than the first (the top of stack), + ** then jump to instruction P2. In other words, jump if NOS>TOS. + */ + /* Opcode: Ge * P2 * + ** + ** Pop the top two elements from the stack. If second element (the next + ** on stack) is greater than or equal to the first (the top of stack), + ** then jump to instruction P2. In other words, jump if NOS>=TOS. + */ + case OP_Eq: + case OP_Ne: + case OP_Lt: + case OP_Le: + case OP_Gt: + case OP_Ge: { + int tos = p->tos; + int nos = tos - 1; + int c; + if( nos<0 ) goto not_enough_stack; + if( p->zStack[tos]==0 && p->zStack[nos]==0 ){ + int a, b; + a = p->iStack[tos]; + b = p->iStack[nos]; + switch( pOp->opcode ){ + case OP_Eq: c = b==a; break; + case OP_Ne: c = b!=a; break; + case OP_Lt: c = b<a; break; + case OP_Le: c = b<=a; break; + case OP_Gt: c = b>a; break; + default: c = b>=a; break; + } + }else{ + Stringify(p, tos); + Stringify(p, nos); + c = sqliteCompare(p->zStack[nos], p->zStack[tos]); + switch( pOp->opcode ){ + case OP_Eq: c = c==0; break; + case OP_Ne: c = c!=0; break; + case OP_Lt: c = c<0; break; + case OP_Le: c = c<=0; break; + case OP_Gt: c = c>0; break; + default: c = c>=0; break; + } + } + PopStack(p, 2); + if( c ) pc = pOp->p2-1; + break; + } + + /* Opcode: And * * * + ** + ** Pop two values off the stack. Take the logical AND of the + ** two values and push the resulting boolean value back onto the + ** stack. Integers are considered false if zero and true otherwise. + ** Strings are considered false if their length is zero and true + ** otherwise. + */ + /* Opcode: Or * * * + ** + ** Pop two values off the stack. Take the logical OR of the + ** two values and push the resulting boolean value back onto the + ** stack. Integers are considered false if zero and true otherwise. + ** Strings are considered false if their length is zero and true + ** otherwise. + */ + case OP_And: + case OP_Or: { + int tos = p->tos; + int nos = tos - 1; + int x, y, c; + if( nos<0 ) goto not_enough_stack; + x = p->zStack[nos] ? p->zStack[nos][0] : p->iStack[nos]; + y = p->zStack[tos] ? p->zStack[tos][0] : p->iStack[tos]; + if( pOp->opcode==OP_And ){ + c = x && y; + }else{ + c = x || y; + } + PopStack(p, 2); + p->tos++; + p->iStack[nos] = c; + break; + } + + /* Opcode: Negative * * * + ** + ** Treat the top of the stack as a numeric quantity. Replace it + ** with its additive inverse. If the top of stack is a string, + ** then it is converted into a number using atof(). + */ + case OP_Negative: { + int tos; + if( (tos = p->tos)<0 ) goto not_enough_stack; + if( p->zStack[tos] ){ + double r = atof(p->zStack[tos]); + sprintf(zBuf, "%g", -r); + sqliteSetString(&p->zStack[tos], zBuf, 0); + p->iStack[tos] = strlen(zBuf) + 1; + }else{ + p->iStack[tos] = -p->iStack[tos]; + } + break; + } + + /* Opcode: Not * * * + ** + ** Treat the top of the stack as a boolean value. Replace it + ** with its complement. Integers are false if zero and true + ** otherwise. Strings are false if zero-length and true otherwise. + */ + case OP_Not: { + int c; + if( p->tos<0 ) goto not_enough_stack; + c = p->zStack[p->tos] ? p->zStack[p->tos][0] : p->iStack[p->tos]; + PopStack(p, 1); + p->tos++; + p->iStack[p->tos] = !c; + break; + } + + /* Opcode: Noop * * * + ** + ** Do nothing. This instruction is often useful as a jump + ** destination. + */ + case OP_Noop: { + break; + } + + /* Opcode: If * P2 * + ** + ** Pop a single boolean from the stack. If the boolean popped is + ** true, then jump to p2. Otherwise continue to the next instruction. + ** An integer is false if zero and true otherwise. A string is + ** false if it has zero length and true otherwise. + */ + case OP_If: { + int c; + if( p->tos<0 ) goto not_enough_stack; + c = p->zStack[p->tos] ? p->zStack[p->tos][0] : p->iStack[p->tos]; + PopStack(p, 1); + if( c ) pc = pOp->p2-1; + break; + } + + /* Opcode: IsNull * P2 * + ** + ** Pop a single value from the stack. If the value popped is the + ** empty string, then jump to p2. Otherwise continue to the next + ** instruction. + */ + case OP_IsNull: { + int c; + if( p->tos<0 ) goto not_enough_stack; + c = p->zStack[p->tos]!=0 && p->zStack[p->tos][0]==0; + PopStack(p, 1); + if( c ) pc = pOp->p2-1; + break; + } + + /* Opcode: NotNull * P2 * + ** + ** Pop a single value from the stack. If the value popped is not an + ** empty string, then jump to p2. Otherwise continue to the next + ** instruction. + */ + case OP_NotNull: { + int c; + if( p->tos<0 ) goto not_enough_stack; + c = p->zStack[p->tos]==0 || p->zStack[p->tos][0]!=0; + PopStack(p, 1); + if( c ) pc = pOp->p2-1; + break; + } + + /* Opcode: MakeRecord P1 * * + ** + ** Convert the top P1 entries of the stack into a single entry + ** suitable for use as a data record in the database. To do this + ** each entry is converted to a string and all the strings are + ** concatenated. The null-terminators are preserved by the concatation + ** and serve as a boundry marker between fields. The lowest entry + ** on the stack is the first in the concatenation and the top of + ** the stack is the last. After all fields are concatenated, an + ** index header is added. The index header consists of P1 integers + ** which hold the offset of the beginning of each field from the + ** beginning of the completed record including the header. + */ + case OP_MakeRecord: { + char *zNewRecord; + int nByte; + int nField; + int i, j; + int addr; + + nField = pOp->p1; + if( p->tos+1<nField ) goto not_enough_stack; + nByte = 0; + for(i=p->tos-nField+1; i<=p->tos; i++){ + if( Stringify(p, i) ) goto no_mem; + nByte += p->iStack[i]; + } + nByte += sizeof(int)*nField; + zNewRecord = sqliteMalloc( nByte ); + if( zNewRecord==0 ) goto no_mem; + j = 0; + addr = sizeof(int)*nField; + for(i=p->tos-nField+1; i<p->tos; i++){ + memcpy(&zNewRecord[j], (char*)&addr, sizeof(int)); + addr += p->iStack[i]; + j += sizeof(int); + } + memcpy(&zNewRecord[j], (char*)&addr, sizeof(int)); + j += sizeof(int); + for(i=p->tos-nField+1; i<=p->tos; i++){ + memcpy(&zNewRecord[j], p->zStack[i], p->iStack[i]); + j += p->iStack[i]; + } + PopStack(p, nField); + NeedStack(p, p->tos+1); + p->tos++; + p->iStack[p->tos] = nByte; + p->zStack[p->tos] = zNewRecord; + break; + } + + /* Opcode: MakeKey P1 * * + ** + ** Convert the top P1 entries of the stack into a single entry suitable + ** for use as the key in an index or a sort. The top P1 records are + ** concatenated with a tab character (ASCII 0x09) used as a record + ** separator. The entire concatenation is null-terminated. The + ** lowest entry in the stack is the first field and the top of the + ** stack becomes the last. + ** + ** See also the SortMakeKey opcode. + */ + case OP_MakeKey: { + char *zNewKey; + int nByte; + int nField; + int i, j; + + nField = pOp->p1; + if( p->tos+1<nField ) goto not_enough_stack; + nByte = 0; + for(i=p->tos-nField+1; i<=p->tos; i++){ + if( Stringify(p, i) ) goto no_mem; + nByte += p->iStack[i]+1; + } + zNewKey = sqliteMalloc( nByte ); + if( zNewKey==0 ) goto no_mem; + j = 0; + for(i=p->tos-nField+1; i<=p->tos; i++){ + memcpy(&zNewKey[j], p->zStack[i], p->iStack[i]-1); + j += p->iStack[i]-1; + if( i<p->tos ) zNewKey[j++] = '\t'; + } + zNewKey[j] = 0; + PopStack(p, nField); + NeedStack(p, p->tos+1); + p->tos++; + p->iStack[p->tos] = nByte; + p->zStack[p->tos] = zNewKey; + break; + } + + /* Open P1 P3 P2 + ** + ** Open a new database table named P3. Give it an identifier P1. + ** Open readonly if P2==0 and for reading and writing if P2!=0. + ** The table is created if it does not already exist and P2!=0. + ** If there is already another table opened on P1, then the old + ** table is closed first. All tables are automatically closed when + ** the VDBE finishes execution. The P1 values need not be + ** contiguous but all P1 values should be small integers. It is + ** an error for P1 to be negative. + */ + case OP_Open: { + int i = pOp->p1; + if( i<0 ) goto bad_instruction; + if( i>=p->nTable ){ + int j; + p->aTab = sqliteRealloc( p->aTab, (i+1)*sizeof(VdbeTable) ); + if( p->aTab==0 ){ p->nTable = 0; goto no_mem; } + for(j=p->nTable; j<=i; j++) p->aTab[j].pTable = 0; + p->nTable = i+1; + }else if( p->aTab[i].pTable ){ + sqliteDbbeCloseTable(p->aTab[i].pTable); + } + p->aTab[i].pTable = sqliteDbbeOpenTable(p->pBe, pOp->p3, pOp->p2); + p->aTab[i].index = 0; + break; + } + + /* Opcode: Close P1 * * + ** + ** Close a database table previously opened as P1. If P1 is not + ** currently open, this instruction is a no-op. + */ + case OP_Close: { + int i = pOp->p1; + if( i>=0 && i<p->nTable && p->aTab[i].pTable ){ + sqliteDbbeCloseTable(p->aTab[i].pTable); + p->aTab[i].pTable = 0; + } + break; + } + + /* Opcode: Fetch P1 * * + ** + ** Pop the top of the stack and use its value as a key to fetch + ** a record from database table or index P1. The data is held + ** in the P1 cursor until needed. The data is not pushed onto the + ** stack or anything like that. + */ + case OP_Fetch: { + int i = pOp->p1; + int tos = p->tos; + if( tos<0 ) goto not_enough_stack; + if( i>=0 && i<p->nTable && p->aTab[i].pTable ){ + if( p->zStack[tos]==0 ){ + sqliteDbbeFetch(p->aTab[i].pTable, sizeof(int), + (char*)&p->iStack[tos]); + }else{ + sqliteDbbeFetch(p->aTab[i].pTable, p->iStack[tos], p->zStack[tos]); + } + } + PopStack(p, 1); + break; + } + + /* Opcode: New P1 * * + ** + ** Get a new integer key not previous used by table P1 and + ** push it onto the stack. + */ + case OP_New: { + int i = pOp->p1; + int v; + if( i<0 || i>=p->nTable || p->aTab[i].pTable==0 ){ + v = 0; + }else{ + v = sqliteDbbeNew(p->aTab[i].pTable); + } + NeedStack(p, p->tos+1); + p->tos++; + p->iStack[p->tos] = v; + break; + } + + /* Opcode: Put P1 * * + ** + ** Write an entry into the database table P1. A new entry is + ** created if it doesn't already exist, or the data for an existing + ** entry is overwritten. The data is the value on the top of the + ** stack. The key is the next value down on the stack. The stack + ** is popped twice by this instruction. + */ + case OP_Put: { + int tos = p->tos; + int nos = p->tos-1; + int i = pOp->p1; + if( nos<0 ) goto not_enough_stack; + if( i>=0 && i<p->nTable && p->aTab[i].pTable!=0 ){ + char *zKey; + int nKey; + Stringify(p, tos); + if( p->zStack[nos]!=0 ){ + nKey = p->iStack[nos]; + zKey = p->zStack[nos]; + }else{ + nKey = sizeof(int); + zKey = (char*)&p->iStack[nos]; + } + sqliteDbbePut(p->aTab[i].pTable, nKey, zKey, + p->iStack[tos], p->zStack[tos]); + } + PopStack(p, 2); + break; + } + + /* Opcode: Delete P1 * * + ** + ** The top of the stack is a key. Remove this key and its data + ** from database table P1. Then pop the stack to discard the key. + */ + case OP_Delete: { + int tos = p->tos; + int i = pOp->p1; + if( tos<0 ) goto not_enough_stack; + if( i>=0 && i<p->nTable && p->aTab[i].pTable!=0 ){ + char *zKey; + int nKey; + if( p->zStack[tos]!=0 ){ + nKey = p->iStack[tos]; + zKey = p->zStack[tos]; + }else{ + nKey = sizeof(int); + zKey = (char*)&p->iStack[tos]; + } + sqliteDbbeDelete(p->aTab[i].pTable, nKey, zKey); + } + PopStack(p, 1); + break; + } + + /* Opcode: Field P1 P2 * + ** + ** Push onto the stack the value of the P2-th field from the + ** most recent Fetch from table P1. + */ + case OP_Field: { + int *pAddr; + int amt; + int i = pOp->p1; + int p2 = pOp->p2; + int tos = ++p->tos; + DbbeTable *pTab; + char *z; + + if( NeedStack(p, p->tos) ) goto no_mem; + if( i>=0 && i<p->nTable && (pTab = p->aTab[i].pTable)!=0 ){ + amt = sqliteDbbeDataLength(pTab); + if( amt<=sizeof(int)*(p2+1) ){ + sqliteSetString(&p->zStack[tos], "", 0); + break; + } + pAddr = (int*)sqliteDbbeReadData(pTab, sizeof(int)*p2); + z = sqliteDbbeReadData(pTab, *pAddr); + sqliteSetString(&p->zStack[tos], z, 0); + p->iStack[tos] = strlen(z)+1; + } + break; + } + + /* Opcode: Key P1 * * + ** + ** Push onto the stack an integer which is the first 4 bytes of the + ** the key to the current entry in a sequential scan of the table P1. + ** A sequential scan is started using the Next opcode. + */ + case OP_Key: { + int i = pOp->p1; + int tos = ++p->tos; + DbbeTable *pTab; + + if( NeedStack(p, p->tos) ) goto no_mem; + if( i>=0 && i<p->nTable && (pTab = p->aTab[i].pTable)!=0 ){ + char *z = sqliteDbbeReadKey(pTab, 0); + memcpy(&p->iStack[tos], z, sizeof(int)); + p->zStack[tos] = 0; + } + break; + } + + /* Opcode: Rewind P1 * * + ** + ** The next use of the Key or Field or Next instruction for P1 + ** will refer to the first entry in the table. + */ + case OP_Rewind: { + int i = pOp->p1; + if( i>=0 && i<p->nTable && p->aTab[i].pTable!=0 ){ + sqliteDbbeRewind(p->aTab[i].pTable); + } + break; + } + + /* Opcode: Next P1 P2 * + ** + ** Advance P1 to the next entry in the table. Or, if there are no + ** more entries, rewind P1 and jump to location P2. + */ + case OP_Next: { + int i = pOp->p1; + if( i>=0 && i<p->nTable && p->aTab[i].pTable!=0 ){ + if( sqliteDbbeNextKey(p->aTab[i].pTable)==0 ){ + pc = pOp->p2; + if( pc<0 || pc>p->nOp ){ + sqliteSetString(pzErrMsg, "jump destination out of range", 0); + rc = 1; + } + pc--; + } + } + break; + } + + /* Opcode: ResetIdx P1 * * + ** + ** Begin treating the current row of table P1 as an index. The next + ** NextIdx instruction will refer to the first index in the table. + */ + case OP_ResetIdx: { + int i = pOp->p1; + if( i>=0 && i<p->nTable ){ + p->aTab[i].index = 0; + } + break; + } + + /* Opcode: NextIdx P1 P2 * + ** + ** Push the next index from the current entry of table P1 onto the + ** stack and advance the pointer. If there are no more indices, then + ** reset the table entry and jump to P2 + */ + case OP_NextIdx: { + int i = pOp->p1; + int tos = ++p->tos; + DbbeTable *pTab; + + if( NeedStack(p, p->tos) ) goto no_mem; + p->zStack[tos] = 0; + if( i>=0 && i<p->nTable && (pTab = p->aTab[i].pTable)!=0 ){ + int *aIdx; + int nIdx; + int j; + nIdx = sqliteDbbeDataLength(pTab)/sizeof(int); + aIdx = (int*)sqliteDbbeReadData(pTab, 0); + for(j=p->aTab[i].index; j<nIdx; j++){ + if( aIdx[j]!=0 ){ + p->iStack[tos] = aIdx[j]; + break; + } + } + if( j>=nIdx ){ + j = -1; + pc = pOp->p2; + if( pc<0 || pc>p->nOp ){ + sqliteSetString(pzErrMsg, "jump destination out of range", 0); + rc = 1; + } + pc--; + } + p->aTab[i].index = j+1; + } + break; + } + + /* Opcode: PutIdx P1 * * + ** + ** The top of the stack hold an index key (proably made using the + ** MakeKey instruction) and next on stack holds an index value for + ** a table. Locate the record in the index P1 that has the key + ** and insert the index value into its + ** data. Write the results back to the index. + ** If the key doesn't exist it is created. + */ + case OP_PutIdx: { + int i = pOp->p1; + int tos = p->tos; + int nos = tos - 1; + DbbeTable *pTab; + if( nos<0 ) goto not_enough_stack; + if( i>=0 && i<p->nTable && (pTab = p->aTab[i].pTable)!=0 ){ + int r; + int newVal = Integerify(p, nos); + Stringify(p, tos); + r = sqliteDbbeFetch(pTab, p->iStack[tos], p->zStack[tos]); + if( r==0 ){ + /* Create a new record for this index */ + sqliteDbbePut(pTab, p->iStack[tos], p->zStack[tos], + sizeof(int), (char*)&newVal); + }else{ + /* Extend the existing record */ + int nIdx; + int *aIdx; + nIdx = sqliteDbbeDataLength(pTab)/sizeof(int); + aIdx = sqliteMalloc( sizeof(int)*(nIdx+1) ); + if( aIdx==0 ) goto no_mem; + sqliteDbbeCopyData(pTab, 0, nIdx*sizeof(int), (char*)aIdx); + aIdx[nIdx] = newVal; + sqliteDbbePut(pTab, p->iStack[tos], p->zStack[tos], + sizeof(int)*(nIdx+1), (char*)aIdx); + sqliteFree(aIdx); + } + } + PopStack(p, 2); + break; + } + + /* Opcode: DeleteIdx P1 * * + ** + ** The top of the stack is a key and next on stack is an index value. + ** Locate the record + ** in index P1 that has the key and remove the index value from its + ** data. Write the results back to the table. If after removing + ** the index value no more indices remain in the record, then the + ** record is removed from the table. + */ + case OP_DeleteIdx: { + int i = pOp->p1; + int tos = p->tos; + int nos = tos - 1; + DbbeTable *pTab; + if( nos<0 ) goto not_enough_stack; + if( i>=0 && i<p->nTable && (pTab = p->aTab[i].pTable)!=0 ){ + int *aIdx; + int nIdx; + int j; + int r; + int oldVal = Integerify(p, nos); + Stringify(p, tos); + r = sqliteDbbeFetch(pTab, p->iStack[tos], p->zStack[tos]); + if( r==0 ) break; + nIdx = sqliteDbbeDataLength(pTab)/sizeof(int); + aIdx = (int*)sqliteDbbeReadData(pTab, 0); + for(j=0; j<nIdx && aIdx[j]!=oldVal; j++){} + if( j>=nIdx ) break; + aIdx[j] = aIdx[nIdx-1]; + if( nIdx==1 ){ + sqliteDbbeDelete(pTab, p->iStack[tos], p->zStack[tos]); + }else{ + sqliteDbbePut(pTab, p->iStack[tos], p->zStack[tos], + sizeof(int)*(nIdx-1), (char*)aIdx); + } + } + PopStack(p, 2); + break; + } + + /* Opcode: Destroy * * P3 + ** + ** Drop the table whose name is P3. The file that holds this table + ** is removed from the disk drive. + */ + case OP_Destroy: { + sqliteDbbeDropTable(p->pBe, pOp->p3); + break; + } + + /* Opcode: ListOpen P1 * * + ** + ** Open a file used for temporary storage of index numbers. P1 + ** will server as a handle to this temporary file for future + ** interactions. If another temporary file with the P1 handle is + ** already opened, the prior file is closed and a new one opened + ** in its place. + */ + case OP_ListOpen: { + int i = pOp->p1; + if( i<0 ) goto bad_instruction; + if( i>=p->nList ){ + int j; + p->apList = sqliteRealloc( p->apList, (i+1)*sizeof(FILE*) ); + if( p->apList==0 ){ p->nList = 0; goto no_mem; } + for(j=p->nList; j<=i; j++) p->apList[j] = 0; + p->nList = i+1; + }else if( p->apList[i] ){ + sqliteDbbeCloseTempFile(p->pBe, p->apList[i]); + } + p->apList[i] = sqliteDbbeOpenTempFile(p->pBe); + break; + } + + /* Opcode: ListWrite P1 * * + ** + ** Write the integer on the top of the stack + ** into the temporary storage file P1. + */ + case OP_ListWrite: { + int i = pOp->p1; + if( i<0 ) goto bad_instruction; + if( p->tos<0 ) goto not_enough_stack; + if( i<p->nList && p->apList[i]!=0 ){ + int val = Integerify(p, p->tos); + PopStack(p, 1); + fwrite(&val, sizeof(int), 1, p->apList[i]); + } + break; + } + + /* Opcode: ListRewind P1 * * + ** + ** Rewind the temporary buffer P1 back to the beginning. + */ + case OP_ListRewind: { + int i = pOp->p1; + if( i<0 ) goto bad_instruction; + if( i<p->nList && p->apList[i]!=0 ){ + rewind(p->apList[i]); + } + break; + } + + /* Opcode: ListRead P1 P2 * + ** + ** Attempt to read an integer from temporary storage buffer P1 + ** and push it onto the stack. If the storage buffer is empty + ** push nothing but instead jump to P2. + */ + case OP_ListRead: { + int i = pOp->p1; + int val, amt; + if( i<0 || i>=p->nList || p->apList[i]==0 ) goto bad_instruction; + amt = fread(&val, sizeof(int), 1, p->apList[i]); + if( amt==1 ){ + p->tos++; + if( NeedStack(p, p->tos) ) goto no_mem; + p->iStack[p->tos] = val; + p->zStack[p->tos] = 0; + }else{ + pc = pOp->p2; + if( pc<0 || pc>p->nOp ){ + sqliteSetString(pzErrMsg, "jump destination out of range", 0); + rc = 1; + } + pc--; + } + break; + } + + /* Opcode: ListClose P1 * * + ** + ** Close the temporary storage buffer and discard its contents. + */ + case OP_ListClose: { + int i = pOp->p1; + if( i<0 ) goto bad_instruction; + if( i<p->nList && p->apList[i]!=0 ){ + sqliteDbbeCloseTempFile(p->pBe, p->apList[i]); + p->apList[i] = 0; + } + break; + } + + /* Opcode: SortOpen P1 * * + ** + ** Create a new sorter with index P1 + */ + case OP_SortOpen: { + int i = pOp->p1; + if( i<0 ) goto bad_instruction; + if( i>=p->nSort ){ + int j; + p->apSort = sqliteRealloc( p->apSort, (i+1)*sizeof(Sorter*) ); + if( p->apSort==0 ){ p->nSort = 0; goto no_mem; } + for(j=p->nSort; j<=i; j++) p->apSort[j] = 0; + p->nSort = i+1; + } + break; + } + + /* Opcode: SortPut P1 * * + ** + ** The TOS is the key and the NOS is the data. Pop both from the stack + ** and put them on the sorter. + */ + case OP_SortPut: { + int i = pOp->p1; + Sorter *pSorter; + if( i<0 || i>=p->nSort ) goto bad_instruction; + if( p->tos<1 ) goto not_enough_stack; + Stringify(p, p->tos); + Stringify(p, p->tos-1); + pSorter = sqliteMalloc( sizeof(Sorter) ); + if( pSorter==0 ) goto no_mem; + pSorter->pNext = p->apSort[i]; + p->apSort[i] = pSorter; + pSorter->nKey = p->iStack[p->tos]; + pSorter->zKey = p->zStack[p->tos]; + pSorter->nData = p->iStack[p->tos-1]; + pSorter->pData = p->zStack[p->tos-1]; + p->zStack[p->tos] = p->zStack[p->tos-1] = 0; + PopStack(p, 2); + break; + } + + /* Opcode: SortMakeRec P1 * * + ** + ** The top P1 elements are the arguments to a callback. Form these + ** elements into a single data entry that can be stored on a sorter + ** using SortPut and later fed to a callback using SortCallback. + */ + case OP_SortMakeRec: { + char *z; + char **azArg; + int nByte; + int nField; + int i, j; + + nField = pOp->p1; + if( p->tos+1<nField ) goto not_enough_stack; + nByte = 0; + for(i=p->tos-nField+1; i<=p->tos; i++){ + if( Stringify(p, i) ) goto no_mem; + nByte += p->iStack[i]; + } + nByte += sizeof(char*)*(nField+1); + azArg = sqliteMalloc( nByte ); + if( azArg==0 ) goto no_mem; + z = (char*)&azArg[nField+1]; + for(j=0, i=p->tos-nField+1; i<=p->tos; i++, j++){ + azArg[j] = z; + strcpy(z, p->zStack[i]); + z += p->iStack[i]; + } + PopStack(p, nField); + NeedStack(p, p->tos+1); + p->tos++; + p->iStack[p->tos] = nByte; + p->zStack[p->tos] = (char*)azArg; + break; + } + + /* Opcode: SortMakeKey P1 * P3 + ** + ** Convert the top few entries of the stack into a sort key. The + ** number of stack entries consumed is the number of characters in + ** the string P3. One character from P3 is prepended to each entry. + ** The first character of P3 is prepended to the element lowest in + ** the stack and the last character of P3 is appended to the top of + ** the stack. All stack entries are separated by a \000 character + ** in the result. The whole key is terminated by two \000 characters + ** in a row. + ** + ** See also the MakeKey opcode. + */ + case OP_SortMakeKey: { + char *zNewKey; + int nByte; + int nField; + int i, j, k; + + nField = strlen(pOp->p3); + if( p->tos+1<nField ) goto not_enough_stack; + nByte = 1; + for(i=p->tos-nField+1; i<=p->tos; i++){ + if( Stringify(p, i) ) goto no_mem; + nByte += p->iStack[i]+2; + } + zNewKey = sqliteMalloc( nByte ); + if( zNewKey==0 ) goto no_mem; + j = 0; + k = nField-1; + for(i=p->tos-nField+1; i<=p->tos; i++){ + zNewKey[j++] = pOp->p3[k--]; + memcpy(&zNewKey[j], p->zStack[i], p->iStack[i]-1); + j += p->iStack[i]-1; + zNewKey[j++] = 0; + } + zNewKey[j] = 0; + PopStack(p, nField); + NeedStack(p, p->tos+1); + p->tos++; + p->iStack[p->tos] = nByte; + p->zStack[p->tos] = zNewKey; + break; + } + + /* Opcode: Sort P1 * * + ** + ** Sort all elements on the given sorter. The algorithm is a + ** mergesort. + */ + case OP_Sort: { + int j; + j = pOp->p1; + if( j<0 ) goto bad_instruction; + if( j<p->nSort ){ + int i; + Sorter *pElem; + Sorter *apSorter[NSORT]; + for(i=0; i<NSORT; i++){ + apSorter[i] = 0; + } + while( p->apSort[j] ){ + pElem = p->apSort[j]; + p->apSort[j] = pElem->pNext; + pElem->pNext = 0; + for(i=0; i<NSORT-1; i++){ + if( apSorter[i]==0 ){ + apSorter[i] = pElem; + break; + }else{ + pElem = Merge(apSorter[i], pElem); + apSorter[i] = 0; + } + } + if( i>=NSORT-1 ){ + apSorter[NSORT-1] = Merge(apSorter[NSORT-1],pElem); + } + } + pElem = 0; + for(i=0; i<NSORT; i++){ + pElem = Merge(apSorter[i], pElem); + } + p->apSort[j] = pElem; + } + break; + } + + /* Opcode: SortNext P1 P2 * + ** + ** Push the data for the topmost element in the given sorter onto the + ** stack, then remove the element from the sorter. + */ + case OP_SortNext: { + int i = pOp->p1; + if( i<0 ) goto bad_instruction; + if( i<p->nSort && p->apSort[i]!=0 ){ + Sorter *pSorter = p->apSort[i]; + p->apSort[i] = pSorter->pNext; + p->tos++; + NeedStack(p, p->tos); + p->zStack[p->tos] = pSorter->pData; + p->iStack[p->tos] = pSorter->nData; + sqliteFree(pSorter->zKey); + sqliteFree(pSorter); + }else{ + pc = pOp->p2; + if( pc<0 || pc>p->nOp ){ + sqliteSetString(pzErrMsg, "jump destination out of range", 0); + rc = 1; + } + pc--; + } + break; + } + + /* Opcode: SortKey P1 * * + ** + ** Push the key for the topmost element of the sorter onto the stack. + ** But don't change the sorter an any other way. + */ + case OP_SortKey: { + int i = pOp->p1; + if( i<0 ) goto bad_instruction; + if( i<p->nSort && p->apSort[i]!=0 ){ + Sorter *pSorter = p->apSort[i]; + p->tos++; + NeedStack(p, p->tos); + sqliteSetString(&p->zStack[p->tos], pSorter->zKey, 0); + p->iStack[p->tos] = pSorter->nKey; + } + break; + } + + /* Opcode: SortCallback P1 P2 * + ** + ** The top of the stack contains a callback record built using + ** the SortMakeRec operation with the same P1 value as this + ** instruction. Pop this record from the stack and invoke the + ** callback on it. + */ + case OP_SortCallback: { + int i = p->tos; + if( i<0 ) goto not_enough_stack; + rc = xCallback(pArg, pOp->p1, (char**)p->zStack[i], p->azColName); + PopStack(p, 1); + break; + } + + /* Opcode: SortClose P1 * * + ** + ** Close the given sorter and remove all its elements. + */ + case OP_SortClose: { + Sorter *pSorter; + int i = pOp->p1; + if( i<0 ) goto bad_instruction; + if( i<p->nSort ){ + while( (pSorter = p->apSort[i])!=0 ){ + p->apSort[i] = pSorter->pNext; + sqliteFree(pSorter->zKey); + sqliteFree(pSorter->pData); + sqliteFree(pSorter); + } + } + break; + } + + /* An other opcode is illegal... + */ + default: { + sprintf(zBuf,"%d",pOp->opcode); + sqliteSetString(pzErrMsg, "unknown opcode ", zBuf, 0); + rc = 1; + break; + } + } + if( p->trace && p->tos>=0 ){ + int i; + fprintf(p->trace, "Stack:"); + for(i=p->tos; i>=0 && i>p->tos-5; i--){ + if( p->zStack[i] ){ + fprintf(p->trace, " [%.11s]", p->zStack[i]); + }else{ + fprintf(p->trace, " [%d]", p->iStack[i]); + } + } + fprintf(p->trace,"\n"); + } + } + +cleanup: + Cleanup(p); + return rc; + + /* Jump to here if a malloc() fails. It's hard to get a malloc() + ** to fail on a modern VM computer, so this code is untested. + */ +no_mem: + Cleanup(p); + sqliteSetString(pzErrMsg, "out or memory", 0); + return 1; + + /* Jump to here if a operator is encountered that requires more stack + ** operands than are currently available on the stack. + */ +not_enough_stack: + sprintf(zBuf,"%d",pc); + sqliteSetString(pzErrMsg, "too few operands on stack at ", zBuf, 0); + rc = 1; + goto cleanup; + + /* Jump here if an illegal or illformed instruction is executed. + */ +bad_instruction: + sprintf(zBuf,"%d",pc); + sqliteSetString(pzErrMsg, "illegal operation at ", zBuf, 0); + rc = 1; + goto cleanup; + +} diff --git a/src/vdbe.h b/src/vdbe.h new file mode 100644 index 000000000..5bafa9943 --- /dev/null +++ b/src/vdbe.h @@ -0,0 +1,166 @@ +/* +** Copyright (c) 1999, 2000 D. Richard Hipp +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public +** License as published by the Free Software Foundation; either +** version 2 of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public +** License along with this library; if not, write to the +** Free Software Foundation, Inc., 59 Temple Place - Suite 330, +** Boston, MA 02111-1307, USA. +** +** Author contact information: +** drh@hwaci.com +** http://www.hwaci.com/drh/ +** +************************************************************************* +** Header file for the Virtual DataBase Engine (VDBE) +** +** This header defines the interface to the virtual database engine +** or VDBE. The VDBE implements an abstract machine that runs a +** simple program to access and modify the underlying database. +** +** $Id: vdbe.h,v 1.1 2000/05/29 14:26:02 drh Exp $ +*/ +#ifndef _SQLITE_VDBE_H_ +#define _SQLITE_VDBE_H_ +#include <stdio.h> + +/* +** A single VDBE is an opaque structure named "Vdbe". Only routines +** in the source file sqliteVdbe.c are allowed to see the insides +** of this structure. +*/ +typedef struct Vdbe Vdbe; + +/* +** A single instruction of the virtual machine has an opcode +** and as many as three operands. The instruction is recorded +** as an instance of the following structure: +*/ +struct VdbeOp { + int opcode; /* What operation to perform */ + int p1; /* First operand */ + int p2; /* Second parameter (often the jump destination) */ + char *p3; /* Third parameter */ +}; +typedef struct VdbeOp VdbeOp; + +/* +** The following macro converts a relative address in the p2 field +** of a VdbeOp structure into a negative number so that +** sqliteVdbeAddOpList() knows that the address is relative. Calling +** the macro again restores the address. +*/ +#define ADDR(X) (-1-(X)) + +/* +** These are the available opcodes. +** +** If any of the values changes or if opcodes are added or removed, +** be sure to also update the zOpName[] array in sqliteVdbe.c to +** mirror the change. +** +** The source tree contains an AWK script named renumberOps.awk that +** can be used to renumber these opcodes when new opcodes are inserted. +*/ +#define OP_Open 1 +#define OP_Close 2 +#define OP_Destroy 3 +#define OP_Fetch 4 +#define OP_New 5 +#define OP_Put 6 +#define OP_Delete 7 +#define OP_Field 8 +#define OP_Key 9 +#define OP_Rewind 10 +#define OP_Next 11 +#define OP_ResetIdx 12 +#define OP_NextIdx 13 +#define OP_PutIdx 14 +#define OP_DeleteIdx 15 + +#define OP_ListOpen 16 +#define OP_ListWrite 17 +#define OP_ListRewind 18 +#define OP_ListRead 19 +#define OP_ListClose 20 + +#define OP_SortOpen 21 +#define OP_SortPut 22 +#define OP_SortMakeRec 23 +#define OP_SortMakeKey 24 +#define OP_Sort 25 +#define OP_SortNext 26 +#define OP_SortKey 27 +#define OP_SortCallback 28 +#define OP_SortClose 29 + +#define OP_MakeRecord 30 +#define OP_MakeKey 31 + +#define OP_Goto 32 +#define OP_If 33 +#define OP_Halt 34 + +#define OP_ColumnCount 35 +#define OP_ColumnName 36 +#define OP_Callback 37 + +#define OP_Integer 38 +#define OP_String 39 +#define OP_Pop 40 +#define OP_Dup 41 +#define OP_Pull 42 + +#define OP_Add 43 +#define OP_AddImm 44 +#define OP_Subtract 45 +#define OP_Multiply 46 +#define OP_Divide 47 +#define OP_Min 48 +#define OP_Max 49 +#define OP_Eq 50 +#define OP_Ne 51 +#define OP_Lt 52 +#define OP_Le 53 +#define OP_Gt 54 +#define OP_Ge 55 +#define OP_IsNull 56 +#define OP_NotNull 57 +#define OP_Negative 58 +#define OP_And 59 +#define OP_Or 60 +#define OP_Not 61 +#define OP_Concat 62 +#define OP_Noop 63 + +#define OP_MAX 63 + +/* +** Prototypes for the VDBE interface. See comments on the implementation +** for a description of what each of these routines does. +*/ +Vdbe *sqliteVdbeCreate(Dbbe*); +int sqliteVdbeAddOp(Vdbe*,int,int,int,const char*,int); +int sqliteVdbeAddOpList(Vdbe*, int nOp, VdbeOp const *aOp); +void sqliteVdbeChangeP3(Vdbe*, int addr, const char *zP1, int N); +void sqliteVdbeDequoteP3(Vdbe*, int addr); +int sqliteVdbeMakeLabel(Vdbe*); +void sqliteVdbeDelete(Vdbe*); +int sqliteVdbeOpcode(const char *zName); +int sqliteVdbeExec(Vdbe*,sqlite_callback,void*,char**); +int sqliteVdbeList(Vdbe*,sqlite_callback,void*,char**); +void sqliteVdbeResolveLabel(Vdbe*, int); +int sqliteVdbeCurrentAddr(Vdbe*); +void sqliteVdbeTrace(Vdbe*,FILE*); + + +#endif diff --git a/src/where.c b/src/where.c new file mode 100644 index 000000000..ea15a5eeb --- /dev/null +++ b/src/where.c @@ -0,0 +1,566 @@ +/* +** Copyright (c) 1999, 2000 D. Richard Hipp +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public +** License as published by the Free Software Foundation; either +** version 2 of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public +** License along with this library; if not, write to the +** Free Software Foundation, Inc., 59 Temple Place - Suite 330, +** Boston, MA 02111-1307, USA. +** +** Author contact information: +** drh@hwaci.com +** http://www.hwaci.com/drh/ +** +************************************************************************* +** This module contains C code that generates VDBE code used to process +** the WHERE clause of SQL statements. Also found here are subroutines +** to generate VDBE code to evaluate expressions. +** +** $Id: where.c,v 1.1 2000/05/29 14:26:02 drh Exp $ +*/ +#include "sqliteInt.h" + +/* +** The query generator uses an array of instances of this structure to +** help it analyze the subexpressions of the WHERE clause. Each WHERE +** clause subexpression is separated from the others by an AND operator. +*/ +typedef struct ExprInfo ExprInfo; +struct ExprInfo { + Expr *p; /* Pointer to the subexpression */ + int indexable; /* True if this subexprssion is usable by an index */ + int idxLeft; /* p->pLeft is a field in this table number. -1 if + ** p->pLeft is not the field of any table */ + int idxRight; /* p->pRight is a field in this table number. -1 if + ** p->pRight is not the field of any table */ + unsigned prereqLeft; /* Tables referenced by p->pLeft */ + unsigned prereqRight; /* Tables referenced by p->pRight */ +}; + +/* +** Determine the number of elements in an array. +*/ +#define ARRAYSIZE(X) (sizeof(X)/sizeof(X[0])) + +/* +** This routine is used to divide the WHERE expression into subexpressions +** separated by the AND operator. +** +** aSlot[] is an array of subexpressions structures. +** There are nSlot spaces left in this array. This routine attempts to +** split pExpr into subexpressions and fills aSlot[] with those subexpressions. +** The return value is the number of slots filled. +*/ +static int exprSplit(int nSlot, ExprInfo *aSlot, Expr *pExpr){ + int cnt = 0; + if( pExpr==0 || nSlot<1 ) return 0; + if( nSlot==1 || pExpr->op!=TK_AND ){ + aSlot[0].p = pExpr; + return 1; + } + if( pExpr->pLeft->op!=TK_AND ){ + aSlot[0].p = pExpr->pLeft; + cnt = 1 + exprSplit(nSlot-1, &aSlot[1], pExpr->pRight); + }else{ + cnt = exprSplit(nSlot, aSlot, pExpr->pRight); + cnt += exprSplit(nSlot-cnt, &aSlot[cnt], pExpr->pLeft); + } + return cnt; +} + +/* +** This routine walks (recursively) an expression tree and generates +** a bitmask indicating which tables are used in that expression +** tree. Bit 0 of the mask is set if table 0 is used. But 1 is set +** if table 1 is used. And so forth. +** +** In order for this routine to work, the calling function must have +** previously invoked sqliteExprResolveIds() on the expression. See +** the header comment on that routine for additional information. +*/ +static int exprTableUsage(Expr *p){ + unsigned int mask = 0; + if( p==0 ) return 0; + if( p->op==TK_FIELD ){ + return 1<<p->iTable; + } + if( p->pRight ){ + mask = exprTableUsage(p->pRight); + } + if( p->pLeft ){ + mask |= exprTableUsage(p->pLeft); + } + return mask; +} + +/* +** The input to this routine is an ExprInfo structure with only the +** "p" field filled in. The job of this routine is to analyze the +** subexpression and populate all the other fields of the ExprInfo +** structure. +*/ +static void exprAnalyze(ExprInfo *pInfo){ + Expr *pExpr = pInfo->p; + pInfo->prereqLeft = exprTableUsage(pExpr->pLeft); + pInfo->prereqRight = exprTableUsage(pExpr->pRight); + pInfo->indexable = 0; + pInfo->idxLeft = -1; + pInfo->idxRight = -1; + if( pExpr->op==TK_EQ && (pInfo->prereqRight & pInfo->prereqLeft)==0 ){ + if( pExpr->pRight->op==TK_FIELD ){ + pInfo->idxRight = pExpr->pRight->iTable; + pInfo->indexable = 1; + } + if( pExpr->pLeft->op==TK_FIELD ){ + pInfo->idxLeft = pExpr->pLeft->iTable; + pInfo->indexable = 1; + } + } +} + +/* +** Generating the beginning of the loop used for WHERE clause processing. +** The return value is a pointer to an (opaque) structure that contains +** information needed to terminate the loop. Later, the calling routine +** should invoke sqliteWhereEnd() with the return value of this function +** in order to complete the WHERE clause processing. +** +** If an error occurs, this routine returns NULL. +*/ +WhereInfo *sqliteWhereBegin( + Parse *pParse, /* The parser context */ + IdList *pTabList, /* A list of all tables */ + Expr *pWhere, /* The WHERE clause */ + int pushKey /* If TRUE, leave the table key on the stack */ +){ + int i; /* Loop counter */ + WhereInfo *pWInfo; /* Will become the return value of this function */ + Vdbe *v = pParse->pVdbe; /* The virtual database engine */ + int brk, cont; /* Addresses used during code generation */ + int *aOrder; /* Order in which pTabList entries are searched */ + int nExpr; /* Number of subexpressions in the WHERE clause */ + int loopMask; /* One bit set for each outer loop */ + int haveKey; /* True if KEY is on the stack */ + Index *aIdx[32]; /* Index to use on each nested loop. */ + ExprInfo aExpr[50]; /* The WHERE clause is divided into these expressions */ + + /* Allocate space for aOrder[]. */ + aOrder = sqliteMalloc( sizeof(int) * pTabList->nId ); + + /* Allocate and initialize the WhereInfo structure that will become the + ** return value. + */ + pWInfo = sqliteMalloc( sizeof(WhereInfo) ); + if( pWInfo==0 ){ + sqliteFree(aOrder); + return 0; + } + pWInfo->pParse = pParse; + pWInfo->pTabList = pTabList; + + /* Split the WHERE clause into as many as 32 separate subexpressions + ** where each subexpression is separated by an AND operator. Any additional + ** subexpressions are attached in the aExpr[32] and will not enter + ** into the query optimizer computations. 32 is chosen as the cutoff + ** since that is the number of bits in an integer that we use for an + ** expression-used mask. + */ + memset(aExpr, 0, sizeof(aExpr)); + nExpr = exprSplit(ARRAYSIZE(aExpr), aExpr, pWhere); + + /* Analyze all of the subexpressions. + */ + for(i=0; i<nExpr; i++){ + exprAnalyze(&aExpr[i]); + } + + /* Figure out a good nesting order for the tables. aOrder[0] will + ** be the index in pTabList of the outermost table. aOrder[1] will + ** be the first nested loop and so on. aOrder[pTabList->nId-1] will + ** be the innermost loop. + ** + ** Someday will put in a good algorithm here to reorder to the loops + ** for an effiecient query. But for now, just use whatever order the + ** tables appear in in the pTabList. + */ + for(i=0; i<pTabList->nId; i++){ + aOrder[i] = i; + } + + /* Figure out what index to use (if any) for each nested loop. + ** Make aIdx[i] point to the index to use for the i-th nested loop + ** where i==0 is the outer loop and i==pTabList->nId-1 is the inner + ** loop. + ** + ** Actually, if there are more than 32 tables in the join, only the + ** first 32 tables are candidates for indices. + */ + loopMask = 0; + for(i=0; i<pTabList->nId && i<ARRAYSIZE(aIdx); i++){ + int idx = aOrder[i]; + Table *pTab = pTabList->a[idx].pTab; + Index *pIdx; + Index *pBestIdx = 0; + + /* Do a search for usable indices. Leave pBestIdx pointing to + ** most specific usable index. + ** + ** "Most specific" means that pBestIdx is the usable index that + ** has the largest value for nField. A usable index is one for + ** which there are subexpressions to compute every field of the + ** index. + */ + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + int j; + int fieldMask = 0; + + if( pIdx->nField>32 ) continue; + for(j=0; j<nExpr; j++){ + if( aExpr[j].idxLeft==idx + && (aExpr[j].prereqRight & loopMask)==aExpr[j].prereqRight ){ + int iField = aExpr[j].p->pLeft->iField; + int k; + for(k=0; k<pIdx->nField; k++){ + if( pIdx->aiField[k]==iField ){ + fieldMask |= 1<<k; + break; + } + } + } + if( aExpr[j].idxRight==idx + && (aExpr[j].prereqLeft & loopMask)==aExpr[j].prereqLeft ){ + int iField = aExpr[j].p->pRight->iField; + int k; + for(k=0; k<pIdx->nField; k++){ + if( pIdx->aiField[k]==iField ){ + fieldMask |= 1<<k; + break; + } + } + } + } + if( fieldMask + 1 == (1<<pIdx->nField) ){ + if( pBestIdx==0 || pBestIdx->nField<pIdx->nField ){ + pBestIdx = pIdx; + } + } + } + aIdx[i] = pBestIdx; + } + + /* Open all tables in the pTabList and all indices in aIdx[]. + */ + for(i=0; i<pTabList->nId; i++){ + sqliteVdbeAddOp(v, OP_Open, i, 0, pTabList->a[i].pTab->zName, 0); + if( i<ARRAYSIZE(aIdx) && aIdx[i]!=0 ){ + sqliteVdbeAddOp(v, OP_Open, pTabList->nId+i, 0, aIdx[i]->zName, 0); + } + } + + /* Generate the code to do the search + */ + pWInfo->iBreak = brk = sqliteVdbeMakeLabel(v); + loopMask = 0; + for(i=0; i<pTabList->nId; i++){ + int j, k; + int idx = aOrder[i]; + Index *pIdx = i<ARRAYSIZE(aIdx) ? aIdx[i] : 0; + + cont = sqliteVdbeMakeLabel(v); + if( pIdx==0 ){ + /* Case 1: There was no usable index. We must do a complete + ** scan of the table. + */ + sqliteVdbeAddOp(v, OP_Next, idx, brk, 0, cont); + haveKey = 0; + }else{ + /* Case 2: We do have a usable index in pIdx. + */ + for(j=0; j<pIdx->nField; j++){ + for(k=0; k<nExpr; k++){ + if( aExpr[k].p==0 ) continue; + if( aExpr[k].idxLeft==idx + && (aExpr[k].prereqRight & loopMask)==aExpr[k].prereqRight + && aExpr[k].p->pLeft->iField==pIdx->aiField[j] + ){ + sqliteExprCode(pParse, aExpr[k].p->pRight); + aExpr[k].p = 0; + break; + } + if( aExpr[k].idxRight==idx + && (aExpr[k].prereqLeft & loopMask)==aExpr[k].prereqLeft + && aExpr[k].p->pRight->iField==pIdx->aiField[j] + ){ + sqliteExprCode(pParse, aExpr[k].p->pLeft); + aExpr[k].p = 0; + break; + } + } + } + sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Fetch, pTabList->nId+i, 0, 0, 0); + sqliteVdbeAddOp(v, OP_NextIdx, pTabList->nId+i, brk, 0, cont); + if( i==pTabList->nId-1 && pushKey ){ + haveKey = 1; + }else{ + sqliteVdbeAddOp(v, OP_Fetch, idx, 0, 0, 0); + haveKey = 0; + } + } + loopMask |= 1<<idx; + + /* Insert code to test every subexpression that can be completely + ** computed using the current set of tables. + */ + for(j=0; j<nExpr; j++){ + if( aExpr[j].p==0 ) continue; + if( (aExpr[j].prereqRight & loopMask)!=aExpr[j].prereqRight ) continue; + if( (aExpr[j].prereqLeft & loopMask)!=aExpr[j].prereqLeft ) continue; + if( haveKey ){ + sqliteVdbeAddOp(v, OP_Fetch, idx, 0, 0, 0); + haveKey = 0; + } + sqliteExprIfFalse(pParse, aExpr[j].p, cont); + aExpr[j].p = 0; + } + brk = cont; + } + pWInfo->iContinue = cont; + if( pushKey && !haveKey ){ + sqliteVdbeAddOp(v, OP_Key, 0, 0, 0, 0); + } + sqliteFree(aOrder); + return pWInfo; +} + +/* +** Generate the end of the WHERE loop. +*/ +void sqliteWhereEnd(WhereInfo *pWInfo){ + Vdbe *v = pWInfo->pParse->pVdbe; + sqliteVdbeAddOp(v, OP_Goto, 0, pWInfo->iContinue, 0, 0); + sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, pWInfo->iBreak); + sqliteFree(pWInfo); + return; +} + +/* +** Generate code into the current Vdbe to evaluate the given +** expression and leave the result on the stack. +*/ +void sqliteExprCode(Parse *pParse, Expr *pExpr){ + Vdbe *v = pParse->pVdbe; + int op; + switch( pExpr->op ){ + case TK_PLUS: op = OP_Add; break; + case TK_MINUS: op = OP_Subtract; break; + case TK_STAR: op = OP_Multiply; break; + case TK_SLASH: op = OP_Divide; break; + case TK_AND: op = OP_And; break; + case TK_OR: op = OP_Or; break; + case TK_LT: op = OP_Lt; break; + case TK_LE: op = OP_Le; break; + case TK_GT: op = OP_Gt; break; + case TK_GE: op = OP_Ge; break; + case TK_NE: op = OP_Ne; break; + case TK_EQ: op = OP_Eq; break; + case TK_ISNULL: op = OP_IsNull; break; + case TK_NOTNULL: op = OP_NotNull; break; + case TK_NOT: op = OP_Not; break; + case TK_UMINUS: op = OP_Negative; break; + default: break; + } + switch( pExpr->op ){ + case TK_FIELD: { + sqliteVdbeAddOp(v, OP_Field, pExpr->iTable, pExpr->iField, 0, 0); + break; + } + case TK_INTEGER: { + int i = atoi(pExpr->token.z); + sqliteVdbeAddOp(v, OP_Integer, i, 0, 0, 0); + break; + } + case TK_FLOAT: { + int addr = sqliteVdbeAddOp(v, OP_String, 0, 0, 0, 0); + sqliteVdbeChangeP3(v, addr, pExpr->token.z, pExpr->token.n); + break; + } + case TK_STRING: { + int addr = sqliteVdbeAddOp(v, OP_String, 0, 0, 0, 0); + sqliteVdbeChangeP3(v, addr, pExpr->token.z, pExpr->token.n); + sqliteVdbeDequoteP3(v, addr); + break; + } + case TK_NULL: { + sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0); + break; + } + case TK_AND: + case TK_OR: + case TK_PLUS: + case TK_STAR: + case TK_MINUS: + case TK_SLASH: { + sqliteExprCode(pParse, pExpr->pLeft); + sqliteExprCode(pParse, pExpr->pRight); + sqliteVdbeAddOp(v, op, 0, 0, 0, 0); + break; + } + case TK_LT: + case TK_LE: + case TK_GT: + case TK_GE: + case TK_NE: + case TK_EQ: { + int dest; + sqliteVdbeAddOp(v, OP_Integer, 0, 0, 0, 0); + sqliteExprCode(pParse, pExpr->pLeft); + sqliteExprCode(pParse, pExpr->pRight); + dest = sqliteVdbeCurrentAddr(v) + 2; + sqliteVdbeAddOp(v, op, 0, dest, 0, 0); + sqliteVdbeAddOp(v, OP_AddImm, 1, 0, 0, 0); + break; + } + case TK_NOT: + case TK_UMINUS: { + sqliteExprCode(pParse, pExpr->pLeft); + sqliteVdbeAddOp(v, op, 0, 0, 0, 0); + break; + } + case TK_ISNULL: + case TK_NOTNULL: { + int dest; + sqliteVdbeAddOp(v, OP_Integer, 0, 0, 0, 0); + sqliteExprCode(pParse, pExpr->pLeft); + dest = sqliteVdbeCurrentAddr(v) + 2; + sqliteVdbeAddOp(v, op, 0, dest, 0, 0); + sqliteVdbeAddOp(v, OP_AddImm, 1, 0, 0, 0); + break; + } + } + return; +} + +/* +** Generate code for a boolean expression such that a jump is made +** to the label "dest" if the expression is true but execution +** continues straight thru if the expression is false. +*/ +void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest){ + Vdbe *v = pParse->pVdbe; + int op = 0; + switch( pExpr->op ){ + case TK_LT: op = OP_Lt; break; + case TK_LE: op = OP_Le; break; + case TK_GT: op = OP_Gt; break; + case TK_GE: op = OP_Ge; break; + case TK_NE: op = OP_Ne; break; + case TK_EQ: op = OP_Eq; break; + case TK_ISNULL: op = OP_IsNull; break; + case TK_NOTNULL: op = OP_NotNull; break; + default: break; + } + switch( pExpr->op ){ + case TK_AND: { + int d2 = sqliteVdbeMakeLabel(v); + sqliteExprIfFalse(pParse, pExpr->pLeft, d2); + sqliteExprIfTrue(pParse, pExpr->pRight, dest); + sqliteVdbeResolveLabel(v, d2); + break; + } + case TK_OR: { + sqliteExprIfTrue(pParse, pExpr->pLeft, dest); + sqliteExprIfTrue(pParse, pExpr->pRight, dest); + break; + } + case TK_LT: + case TK_LE: + case TK_GT: + case TK_GE: + case TK_NE: + case TK_EQ: { + sqliteExprCode(pParse, pExpr->pLeft); + sqliteExprCode(pParse, pExpr->pRight); + sqliteVdbeAddOp(v, op, 0, dest, 0, 0); + break; + } + case TK_ISNULL: + case TK_NOTNULL: { + sqliteExprCode(pParse, pExpr->pLeft); + sqliteVdbeAddOp(v, op, 0, dest, 0, 0); + break; + } + default: { + sqliteExprCode(pParse, pExpr); + sqliteVdbeAddOp(v, OP_If, 0, dest, 0, 0); + break; + } + } +} + +/* +** Generate code for boolean expression such that a jump is made +** to the label "dest" if the expression is false but execution +** continues straight thru if the expression is true. +*/ +void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest){ + Vdbe *v = pParse->pVdbe; + int op = 0; + switch( pExpr->op ){ + case TK_LT: op = OP_Ge; break; + case TK_LE: op = OP_Gt; break; + case TK_GT: op = OP_Le; break; + case TK_GE: op = OP_Lt; break; + case TK_NE: op = OP_Eq; break; + case TK_EQ: op = OP_Ne; break; + case TK_ISNULL: op = OP_NotNull; break; + case TK_NOTNULL: op = OP_IsNull; break; + default: break; + } + switch( pExpr->op ){ + case TK_AND: { + sqliteExprIfFalse(pParse, pExpr->pLeft, dest); + sqliteExprIfFalse(pParse, pExpr->pRight, dest); + break; + } + case TK_OR: { + int d2 = sqliteVdbeMakeLabel(v); + sqliteExprIfTrue(pParse, pExpr->pLeft, d2); + sqliteExprIfFalse(pParse, pExpr->pRight, dest); + sqliteVdbeResolveLabel(v, d2); + break; + } + case TK_LT: + case TK_LE: + case TK_GT: + case TK_GE: + case TK_NE: + case TK_EQ: { + sqliteExprCode(pParse, pExpr->pLeft); + sqliteExprCode(pParse, pExpr->pRight); + sqliteVdbeAddOp(v, op, 0, dest, 0, 0); + break; + } + case TK_ISNULL: + case TK_NOTNULL: { + sqliteExprCode(pParse, pExpr->pLeft); + sqliteVdbeAddOp(v, op, 0, dest, 0, 0); + break; + } + default: { + sqliteExprCode(pParse, pExpr); + sqliteVdbeAddOp(v, OP_Not, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_If, 0, dest, 0, 0); + break; + } + } +} |