aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/build.c1435
-rw-r--r--src/dbbe.c510
-rw-r--r--src/dbbe.h119
-rw-r--r--src/main.c219
-rw-r--r--src/shell.c384
-rw-r--r--src/sqlite.h118
-rw-r--r--src/sqliteInt.h235
-rw-r--r--src/tclsqlite.c241
-rw-r--r--src/tokenize.c375
-rw-r--r--src/util.c445
-rw-r--r--src/vdbe.c1973
-rw-r--r--src/vdbe.h166
-rw-r--r--src/where.c566
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;
+ }
+ }
+}