diff --git a/manifest b/manifest index aeb09b7ec4..f1d58fb447 100644 --- a/manifest +++ b/manifest @@ -1,26 +1,26 @@ -C :-)\s(CVS\s73) -D 2000-06-07T15:39:04 +C :-)\s(CVS\s74) +D 2000-06-07T23:51:50 F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4 F Makefile.in 17ba1ccf8d2d40c627796bba8f72952365d6d644 F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958 F configure 00a5b5c82147a576fa6e82d7c1b0d55c321d6d2c x F configure.in 6ccfd5fc80517f7cfe605a7fc7e0f62d962a233c F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47 -F src/build.c 6c9454b2e2b866979527fb41b19ad8bc49c27a20 +F src/build.c 5e3b6bab5604cd99019ea6d58f9166d879476991 F src/dbbe.c 9b191b16ff01ec5bc0af436558501d07938ba4f0 F src/dbbe.h a8a46f71238e0f09f3ec08fd9d1c8c7f4cdc49bf -F src/delete.c 8c733bb82a1b84126116d03dcdccf433c0856f5d -F src/expr.c d350fe393e1753aaa733a5d21f0830a23e547400 -F src/insert.c 93f9e36bf96b57326d8d5709ed44b568b8627aa5 -F src/main.c b4275952180b30e1ca3db266157e2d233c0a24d9 -F src/parse.y 4a2e6b14177c97f61fc3e21d4570f5a9c541c1e1 -F src/select.c 4834ab68a3308871f17fe8e22d903fcdf1b42420 +F src/delete.c c267b93f7ccb5493b677fa18201880267c699aa8 +F src/expr.c baa8a4229b3acf69d908efcd697ab63922009c9f +F src/insert.c ac4edfff474589c00b2490f206317dc5822122e5 +F src/main.c e3297835b8e38ca726ac73f2c2bdb7cf08103197 +F src/parse.y bb2126c8313c111184b89af8675911dcb57f1dca +F src/select.c 21d1097e32e0a8430c286309817be33b734eab9d F src/shell.c 3f4afc39a36e4824e8aa262623fd03568874799e F src/sqlite.h 58da0a8590133777b741f9836beaef3d58f40268 -F src/sqliteInt.h 1ac4ba75fef8249b9bd64a7736d0f584092f24a5 +F src/sqliteInt.h 816c491f9896090dde03804fd3f60346456b99df F src/tclsqlite.c 9f358618ae803bedf4fb96da5154fd45023bc1f7 F src/tokenize.c 900af9479d0feaa76b0225680196aa81afec930a -F src/update.c 18746f920f989b3d19d96c08263c92584823cd35 +F src/update.c d8d90df714bac99c68446a0c49f3d957ca6fc3c8 F src/util.c 33f9baa01e45394ef0cf85361a0e872987884315 F src/vdbe.c 0ce44df13c97472686b376cc93cca5f40079878d F src/vdbe.h 8f79f57c66ce1030f6371ff067b326d627a52c6d @@ -30,7 +30,7 @@ F test/copy.test b77a1214bd7756f2849d5c4fa6e715c0ff0c34eb F test/dbbe.test 3978ab21ff2a0531a85618c538d27047d560fc5d F test/delete.test 30451333f89479d2deb5410edd3f3cce67339944 F test/expr.test 7d017f1aa64c981b161408a015424cd90592bc16 -F test/in.test 17cd46a9ca0e5d4a804483e6fb496458494858e6 +F test/in.test 962a605b6a3a619214f84d1950dfc44fcf0d8b8f F test/index.test 9f99dca2d904b8de330863a978587f136e2df65a F test/insert.test 66f4c3bd600fec8eb1e733b928cbe6fa885eff0c F test/insert2.test 732405e30331635af8d159fccabe835eea5cd0c6 @@ -54,7 +54,7 @@ F www/c_interface.tcl 9ac800854272db5fe439e07b7435b243a5422293 F www/changes.tcl 04e66b4257589ff78a7e1de93e9dda4725fb03d6 F www/index.tcl 52e29a4eeda8d59e91af43c61fef177c5f2ffd53 F www/sqlite.tcl 2f933ce18cffd34a0a020a82435ab937137970fd -P 6f8de336d0c0c5b74fa2ff541ab974ba13638a58 -R e8ba4da21fa9f208a62c1c1e8095f5b6 +P a00b81b0e1878487a6edaaca1b0dff002ad506d8 +R edeb782218e986f032924498f63df3c4 U drh -Z 9c51fcb8cc6b0390f306d27b43fea58b +Z e7171fdd9bf45a1d225b747155ebb197 diff --git a/manifest.uuid b/manifest.uuid index 4f4f06c0c4..adde17c19d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a00b81b0e1878487a6edaaca1b0dff002ad506d8 \ No newline at end of file +2ffeb8509c469f5a499d56bb109da079fcdff570 \ No newline at end of file diff --git a/src/build.c b/src/build.c index d88e57cd45..ad6113e75b 100644 --- a/src/build.c +++ b/src/build.c @@ -33,7 +33,7 @@ ** COPY ** VACUUM ** -** $Id: build.c,v 1.15 2000/06/05 18:54:46 drh Exp $ +** $Id: build.c,v 1.16 2000/06/07 23:51:50 drh Exp $ */ #include "sqliteInt.h" @@ -60,6 +60,7 @@ void sqliteExec(Parse *pParse){ } sqliteVdbeDelete(pParse->pVdbe); pParse->pVdbe = 0; + pParse->colNamesSet = 0; } } @@ -330,11 +331,9 @@ void sqliteEndTable(Parse *pParse, Token *pEnd){ { OP_Close, 0, 0, 0}, }; int n, base; - Vdbe *v = pParse->pVdbe; + Vdbe *v; - if( v==0 ){ - v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); - } + v = sqliteGetVdbe(pParse); if( v==0 ) return; n = (int)pEnd->z - (int)pParse->sFirstToken.z + 1; base = sqliteVdbeAddOpList(v, ArraySize(addTable), addTable); @@ -379,10 +378,7 @@ void sqliteDropTable(Parse *pParse, Token *pName){ } /* 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); - } + v = sqliteGetVdbe(pParse); if( v ){ static VdbeOp dropTable[] = { { OP_Open, 0, 1, MASTER_NAME }, @@ -576,9 +572,7 @@ void sqliteCreateIndex( int lbl1, lbl2; int i; - if( v==0 ){ - v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); - } + v = sqliteGetVdbe(pParse); if( v==0 ) goto exit_create_index; sqliteVdbeAddOp(v, OP_Open, 0, 0, pTab->zName, 0); sqliteVdbeAddOp(v, OP_Open, 1, 1, pIndex->zName, 0); @@ -637,7 +631,7 @@ void sqliteDropIndex(Parse *pParse, Token *pName){ } /* Generate code to remove the index and from the master table */ - v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); + v = sqliteGetVdbe(pParse); if( v ){ static VdbeOp dropIndex[] = { { OP_Open, 0, 1, MASTER_NAME}, @@ -808,7 +802,7 @@ void sqliteCopy( pParse->nErr++; goto copy_cleanup; } - v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); + v = sqliteGetVdbe(pParse); if( v ){ addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0, 0, 0); sqliteVdbeChangeP3(v, addr, pFilename->z, pFilename->n); @@ -872,7 +866,7 @@ void sqliteVacuum(Parse *pParse, Token *pTableName){ pParse->nErr++; goto vacuum_cleanup; } - v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); + v = sqliteGetVdbe(pParse); if( v==0 ) goto vacuum_cleanup; if( zName ){ sqliteVdbeAddOp(v, OP_Reorganize, 0, 0, zName, 0); diff --git a/src/delete.c b/src/delete.c index 3f64043064..493d3990ce 100644 --- a/src/delete.c +++ b/src/delete.c @@ -24,7 +24,7 @@ ** This file contains C code routines that are called by the parser ** to handle DELETE FROM statements. ** -** $Id: delete.c,v 1.3 2000/06/06 13:54:15 drh Exp $ +** $Id: delete.c,v 1.4 2000/06/07 23:51:50 drh Exp $ */ #include "sqliteInt.h" @@ -82,10 +82,7 @@ void sqliteDeleteFrom( /* Begin generating code. */ - v = pParse->pVdbe; - if( v==0 ){ - v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); - } + v = sqliteGetVdbe(pParse); if( v==0 ) goto delete_from_cleanup; /* Begin the database scan diff --git a/src/expr.c b/src/expr.c index 2870a484d9..d5e4f747c5 100644 --- a/src/expr.c +++ b/src/expr.c @@ -23,7 +23,7 @@ ************************************************************************* ** This file contains C code routines used for processing expressions ** -** $Id: expr.c,v 1.10 2000/06/06 17:27:05 drh Exp $ +** $Id: expr.c,v 1.11 2000/06/07 23:51:50 drh Exp $ */ #include "sqliteInt.h" @@ -206,10 +206,7 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){ } case TK_IN: { - Vdbe *v = pParse->pVdbe; - if( v==0 ){ - v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); - } + Vdbe *v = sqliteGetVdbe(pParse); if( v==0 ) return 1; if( sqliteExprResolveIds(pParse, pTabList, pExpr->pLeft) ){ return 1; @@ -766,7 +763,7 @@ void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest){ ** Do a deep comparison of two expression trees. Return TRUE (non-zero) ** if they are identical and return FALSE if they differ in any way. */ -static int exprDeepCompare(Expr *pA, Expr *pB){ +int sqliteExprCompare(Expr *pA, Expr *pB){ int i; if( pA==0 ){ return pB==0; @@ -774,13 +771,13 @@ static int exprDeepCompare(Expr *pA, Expr *pB){ return 0; } if( pA->op!=pB->op ) return 0; - if( !exprDeepCompare(pA->pLeft, pB->pLeft) ) return 0; - if( !exprDeepCompare(pA->pRight, pB->pRight) ) return 0; + if( !sqliteExprCompare(pA->pLeft, pB->pLeft) ) return 0; + if( !sqliteExprCompare(pA->pRight, pB->pRight) ) return 0; if( pA->pList ){ if( pB->pList==0 ) return 0; if( pA->pList->nExpr!=pB->pList->nExpr ) return 0; for(i=0; ipList->nExpr; i++){ - if( !exprDeepCompare(pA->pList->a[i].pExpr, pB->pList->a[i].pExpr) ){ + if( !sqliteExprCompare(pA->pList->a[i].pExpr, pB->pList->a[i].pExpr) ){ return 0; } } @@ -868,7 +865,7 @@ int sqliteExprAnalyzeAggregates(Parse *pParse, Expr *pExpr){ aAgg = pParse->aAgg; for(i=0; inAgg; i++){ if( !aAgg[i].isAgg ) continue; - if( exprDeepCompare(aAgg[i].pExpr, pExpr) ){ + if( sqliteExprCompare(aAgg[i].pExpr, pExpr) ){ break; } } diff --git a/src/insert.c b/src/insert.c index 204c6facd3..2d838d48f5 100644 --- a/src/insert.c +++ b/src/insert.c @@ -24,7 +24,7 @@ ** This file contains C code routines that are called by the parser ** to handle INSERT statements. ** -** $Id: insert.c,v 1.8 2000/06/07 15:11:27 drh Exp $ +** $Id: insert.c,v 1.9 2000/06/07 23:51:50 drh Exp $ */ #include "sqliteInt.h" @@ -67,10 +67,7 @@ void sqliteInsert( pParse->nErr++; goto insert_cleanup; } - v = pParse->pVdbe; - if( v==0 ){ - v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); - } + v = sqliteGetVdbe(pParse); if( v==0 ) goto insert_cleanup; if( pSelect ){ int rc; diff --git a/src/main.c b/src/main.c index 8a388a01f2..3c81beca33 100644 --- a/src/main.c +++ b/src/main.c @@ -26,7 +26,7 @@ ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: main.c,v 1.11 2000/06/07 15:11:27 drh Exp $ +** $Id: main.c,v 1.12 2000/06/07 23:51:50 drh Exp $ */ #include "sqliteInt.h" @@ -129,6 +129,10 @@ static int sqliteInit(sqlite *db, char **pzErrMsg){ ** the program. The delete the virtual machine. */ vdbe = sqliteVdbeCreate(db->pBe); + if( vdbe==0 ){ + sqliteSetString(pzErrMsg, "out of memory",0); + return 1; + } sqliteVdbeAddOpList(vdbe, sizeof(initProg)/sizeof(initProg[0]), initProg); rc = sqliteVdbeExec(vdbe, sqliteOpenCb, db, pzErrMsg); sqliteVdbeDelete(vdbe); diff --git a/src/parse.y b/src/parse.y index 049a631734..864bce4cff 100644 --- a/src/parse.y +++ b/src/parse.y @@ -26,7 +26,7 @@ ** the parser. Lemon will also generate a header file containing ** numeric codes for all of the tokens. ** -** @(#) $Id: parse.y,v 1.15 2000/06/07 14:42:27 drh Exp $ +** @(#) $Id: parse.y,v 1.16 2000/06/07 23:51:50 drh Exp $ */ %token_prefix TK_ %token_type {Token} @@ -211,11 +211,11 @@ orderby_opt(A) ::= . {A = 0;} orderby_opt(A) ::= ORDER BY sortlist(X). {A = X;} sortlist(A) ::= sortlist(X) COMMA sortitem(Y) sortorder(Z). { A = sqliteExprListAppend(X,Y,0); - A->a[A->nExpr-1].idx = Z; /* 0 for ascending order, 1 for decending */ + A->a[A->nExpr-1].sortOrder = Z; /* 0 for ascending order, 1 for decending */ } sortlist(A) ::= sortitem(Y) sortorder(Z). { A = sqliteExprListAppend(0,Y,0); - A->a[0].idx = Z; + A->a[0].sortOrder = Z; } sortitem(A) ::= expr(X). {A = X;} diff --git a/src/select.c b/src/select.c index 1992612e4b..d524b1a17e 100644 --- a/src/select.c +++ b/src/select.c @@ -24,7 +24,7 @@ ** This file contains C code routines that are called by the parser ** to handle SELECT statements. ** -** $Id: select.c,v 1.15 2000/06/07 14:42:27 drh Exp $ +** $Id: select.c,v 1.16 2000/06/07 23:51:50 drh Exp $ */ #include "sqliteInt.h" @@ -135,11 +135,11 @@ static int selectInnerLoop( */ if( pOrderBy ){ char *zSortOrder; - sqliteVdbeAddOp(v, OP_SortMakeRec, pEList->nExpr, 0, 0, 0); + sqliteVdbeAddOp(v, OP_SortMakeRec, nField, 0, 0, 0); zSortOrder = sqliteMalloc( pOrderBy->nExpr + 1 ); if( zSortOrder==0 ) return 1; for(i=0; inExpr; i++){ - zSortOrder[i] = pOrderBy->a[i].idx ? '-' : '+'; + zSortOrder[i] = pOrderBy->a[i].sortOrder ? '-' : '+'; sqliteExprCode(pParse, pOrderBy->a[i].pExpr); } zSortOrder[pOrderBy->nExpr] = 0; @@ -179,7 +179,7 @@ static int selectInnerLoop( ** item into the set table with bogus data. */ if( eDest==SRT_Set ){ - assert( pEList->nExpr==1 ); + assert( nField==1 ); sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0); sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0); }else @@ -190,7 +190,7 @@ static int selectInnerLoop( ** of the scan loop. */ if( eDest==SRT_Mem ){ - assert( pEList->nExpr==1 ); + assert( nField==1 ); sqliteVdbeAddOp(v, OP_MemStore, iParm, 0, 0, 0); sqliteVdbeAddOp(v, OP_Goto, 0, iBreak, 0, 0); }else @@ -203,22 +203,39 @@ static int selectInnerLoop( return 0; } +/* +** If the inner loop was generated using a non-null pOrderBy argument, +** then the results were placed in a sorter. After the loop is terminated +** we need to run the sorter and output the results. The following +** routine generates the code needed to do that. +*/ +static void generateSortTail(Vdbe *v, int nField){ + 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, nField, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0); + sqliteVdbeAddOp(v, OP_SortClose, 0, 0, 0, end); +} + /* ** Generate code that will tell the VDBE how many columns there ** are in the result and the name for each column. This information ** is used to provide "argc" and "azCol[]" values in the callback. */ -static void generateColumnNames(Vdbe *v, IdList *pTabList, ExprList *pEList){ +static +void generateColumnNames(Parse *pParse, IdList *pTabList, ExprList *pEList){ + Vdbe *v = pParse->pVdbe; int i; + if( pParse->colNamesSet ) return; + pParse->colNamesSet = 1; sqliteVdbeAddOp(v, OP_ColumnCount, pEList->nExpr, 0, 0, 0); for(i=0; inExpr; 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); - } + sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0); continue; } p = pEList->a[i].pExpr; @@ -246,6 +263,167 @@ static void generateColumnNames(Vdbe *v, IdList *pTabList, ExprList *pEList){ } } +/* +** Name of the connection operator, used for error messages. +*/ +static const char *selectOpName(int id){ + char *z; + switch( id ){ + case TK_ALL: z = "UNION ALL"; break; + case TK_INTERSECT: z = "INTERSECT"; break; + case TK_EXCEPT: z = "EXCEPT"; break; + default: z = "UNION"; break; + } + return z; +} + +/* +** For the given SELECT statement, do two things. +** +** (1) Fill in the pTab fields of the IdList that defines the set +** of tables we are scanning. +** +** (2) If the columns to be extracted variable (pEList) is NULL +** (meaning that a "*" was used in the SQL statement) then +** create a fake pEList containing the names of all columns +** of all tables. +** +** Return 0 on success. If there are problems, leave an error message +** in pParse and return non-zero. +*/ +static int fillInColumnList(Parse *pParse, Select *p){ + int i, j; + IdList *pTabList = p->pSrc; + ExprList *pEList = p->pEList; + + /* Look up every table in the table list. + */ + for(i=0; inId; i++){ + if( pTabList->a[i].pTab ){ + /* This routine has run before! No need to continue */ + return 0; + } + pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName); + if( pTabList->a[i].pTab==0 ){ + sqliteSetString(&pParse->zErrMsg, "no such table: ", + pTabList->a[i].zName, 0); + pParse->nErr++; + return 1; + } + } + + /* If the list of columns to retrieve is "*" then replace it with + ** a list of all columns from all tables. + */ + if( pEList==0 ){ + for(i=0; inId; i++){ + Table *pTab = pTabList->a[i].pTab; + for(j=0; jnCol; j++){ + Expr *pExpr = sqliteExpr(TK_DOT, 0, 0, 0); + pExpr->pLeft = sqliteExpr(TK_ID, 0, 0, 0); + pExpr->pLeft->token.z = pTab->zName; + pExpr->pLeft->token.n = strlen(pTab->zName); + pExpr->pRight = sqliteExpr(TK_ID, 0, 0, 0); + pExpr->pRight->token.z = pTab->aCol[j].zName; + pExpr->pRight->token.n = strlen(pTab->aCol[j].zName); + pEList = sqliteExprListAppend(pEList, pExpr, 0); + } + } + p->pEList = pEList; + } + return 0; +} + +/* +** This routine associates entries in an ORDER BY expression list with +** columns in a result. For each ORDER BY expression, the opcode of +** the top-level node is changed to TK_FIELD and the iField value of +** the top-level node is filled in with column number and the iTable +** value of the top-level node is filled with iTable parameter. +** +** If there are prior SELECT clauses, they are processed first. A match +** in an earlier SELECT takes precedence over a later SELECT. +** +** Any entry that does not match is flagged as an error. The number +** of errors is returned. +*/ +static int matchOrderbyToColumn( + Parse *pParse, /* A place to leave error messages */ + Select *pSelect, /* Match to result columns of this SELECT */ + ExprList *pOrderBy, /* The ORDER BY values to match against columns */ + int iTable, /* Insert this this value in iTable */ + int mustComplete /* If TRUE all ORDER BYs must match */ +){ + int nErr = 0; + int i, j; + ExprList *pEList; + + assert( pSelect && pOrderBy ); + if( mustComplete ){ + for(i=0; inExpr; i++){ pOrderBy->a[i].done = 0; } + } + if( fillInColumnList(pParse, pSelect) ){ + return 1; + } + if( pSelect->pPrior ){ + matchOrderbyToColumn(pParse, pSelect->pPrior, pOrderBy, iTable, 0); + } + pEList = pSelect->pEList; + for(i=0; inExpr; i++){ + Expr *pE = pOrderBy->a[i].pExpr; + if( pOrderBy->a[i].done ) continue; + for(j=0; jnExpr; j++){ + int match = 0; + if( pEList->a[i].zName && (pE->op==TK_ID || pE->op==TK_STRING) ){ + char *zName = pEList->a[i].zName; + char *zLabel = 0; + sqliteSetString(&zLabel, pE->token.z, pE->token.n, 0); + sqliteDequote(zLabel); + if( sqliteStrICmp(zName, zLabel)==0 ){ + match = 1; + } + } + if( match==0 && sqliteExprCompare(pE, pEList->a[i].pExpr) ){ + match = 1; + } + if( match ){ + pE->op = TK_FIELD; + pE->iField = j; + pE->iTable = iTable; + pOrderBy->a[i].done = 1; + break; + } + } + if( mustComplete ){ + char zBuf[30]; + sprintf(zBuf,"%d",i+1); + sqliteSetString(&pParse->zErrMsg, "ORDER BY term number ", zBuf, + " does not match any result column", 0); + pParse->nErr++; + nErr++; + break; + } + } + return nErr; +} + +/* +** Get a VDBE for the given parser context. Create a new one if necessary. +** If an error occurs, return NULL and leave a message in pParse. +*/ +Vdbe *sqliteGetVdbe(Parse *pParse){ + Vdbe *v = pParse->pVdbe; + if( v==0 ){ + v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); + } + if( v==0 ){ + sqliteSetString(&pParse->zErrMsg, "out of memory", 0); + pParse->nErr++; + } + return v; +} + + /* ** This routine is called to process a query that is really the union ** or intersection of two or more separate queries. @@ -254,62 +432,92 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){ int rc; Select *pPrior; Vdbe *v; - int i; - /* Make sure we have a valid query engine. If not, create a new one. + /* Make sure there is no ORDER BY clause on prior SELECTs. Only the + ** last SELECT in the series may have an ORDER BY. */ - v = pParse->pVdbe; - if( v==0 ){ - v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); - } - if( v==0 ){ - sqliteSetString(&pParse->zErrMsg, "out of memory", 0); + assert( p->pPrior!=0 ); + pPrior = p->pPrior; + if( pPrior->pOrderBy ){ + sqliteSetString(&pParse->zErrMsg,"ORDER BY clause should come after ", + selectOpName(p->op), " not before", 0); pParse->nErr++; return 1; } - assert( p->pPrior!=0 ); - pPrior = p->pPrior; + /* Make sure we have a valid query engine. If not, create a new one. + */ + v = sqliteGetVdbe(pParse); + if( v==0 ) return 1; + + /* Process the UNION or INTERSECTION + */ switch( p->op ){ - case TK_ALL: { - rc = sqliteSelect(pParse, pPrior, eDest, iParm); - if( rc ) return rc; - p->pPrior = 0; - rc = sqliteSelect(pParse, p, eDest, iParm); - p->pPrior = pPrior; - break; - } + case TK_ALL: case TK_EXCEPT: case TK_UNION: { - int unionTab; - int op; + int unionTab; /* Cursor number of the temporary table holding result */ + int op; /* One of the SRT_ operations to apply to self */ + int priorOp; /* The SRT_ operation to apply to prior selects */ - if( eDest==SRT_Union ){ + priorOp = p->op==TK_ALL ? SRT_Table : SRT_Union; + if( eDest==priorOp ){ + /* We can reuse a temporary table generated by a SELECT to our + ** right. This also means we are not the right-most select and so + ** we cannot have an ORDER BY clause + */ unionTab = iParm; + assert( p->pOrderBy==0 ); }else{ - unionTab = pParse->nTab++; + /* We will need to create our own temporary table to hold the + ** intermediate results. + */ + unionTab = pParse->nTab++; + if( p->pOrderBy + && matchOrderbyToColumn(pParse, p, p->pOrderBy, unionTab, 1) ){ + return 1; + } sqliteVdbeAddOp(v, OP_Open, unionTab, 1, 0, 0); - sqliteVdbeAddOp(v, OP_KeyAsData, unionTab, 1, 0, 0); + if( p->op!=TK_ALL ){ + sqliteVdbeAddOp(v, OP_KeyAsData, unionTab, 1, 0, 0); + } } - rc = sqliteSelect(pParse, pPrior, SRT_Union, unionTab); + + /* Code the SELECT statements to our left + */ + rc = sqliteSelect(pParse, pPrior, priorOp, unionTab); if( rc ) return rc; - op = p->op==TK_EXCEPT ? SRT_Except : SRT_Union; + + /* Code the current SELECT statement + */ + switch( p->op ){ + case TK_EXCEPT: op = SRT_Except; break; + case TK_UNION: op = SRT_Union; break; + case TK_ALL: op = SRT_Table; break; + } p->pPrior = 0; rc = sqliteSelect(pParse, p, op, unionTab); p->pPrior = pPrior; if( rc ) return rc; - if( eDest!=SRT_Union ){ + + /* Convert the data in the temporary table into whatever form + ** it is that we currently need. + */ + if( eDest!=priorOp ){ int iCont, iBreak; assert( p->pEList ); - generateColumnNames(v, 0, p->pEList); + generateColumnNames(pParse, 0, p->pEList); iBreak = sqliteVdbeMakeLabel(v); iCont = sqliteVdbeAddOp(v, OP_Next, unionTab, iBreak, 0, 0); rc = selectInnerLoop(pParse, 0, unionTab, p->pEList->nExpr, - 0, -1, eDest, iParm, + p->pOrderBy, -1, eDest, iParm, iCont, iBreak); if( rc ) return 1; sqliteVdbeAddOp(v, OP_Goto, 0, iCont, 0, 0); sqliteVdbeAddOp(v, OP_Close, unionTab, 0, 0, iBreak); + if( p->pOrderBy ){ + generateSortTail(v, p->pEList->nExpr); + } } break; } @@ -317,38 +525,58 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){ int tab1, tab2; int iCont, iBreak; + /* INTERSECT is different from the others since it requires + ** two temporary tables. Hence it has its own case. Begine + ** by allocating the tables we will need. + */ tab1 = pParse->nTab++; tab2 = pParse->nTab++; + if( p->pOrderBy && matchOrderbyToColumn(pParse,p,p->pOrderBy,tab1,1) ){ + return 1; + } sqliteVdbeAddOp(v, OP_Open, tab1, 1, 0, 0); sqliteVdbeAddOp(v, OP_KeyAsData, tab1, 1, 0, 0); + + /* Code the SELECTs to our left into temporary table "tab1". + */ rc = sqliteSelect(pParse, pPrior, SRT_Union, tab1); if( rc ) return rc; + + /* Code the current SELECT into temporary table "tab2" + */ sqliteVdbeAddOp(v, OP_Open, tab2, 1, 0, 0); sqliteVdbeAddOp(v, OP_KeyAsData, tab2, 1, 0, 0); p->pPrior = 0; rc = sqliteSelect(pParse, p, SRT_Union, tab2); p->pPrior = pPrior; if( rc ) return rc; + + /* Generate code to take the intersection of the two temporary + ** tables. + */ assert( p->pEList ); - generateColumnNames(v, 0, p->pEList); + generateColumnNames(pParse, 0, p->pEList); iBreak = sqliteVdbeMakeLabel(v); iCont = sqliteVdbeAddOp(v, OP_Next, tab1, iBreak, 0, 0); sqliteVdbeAddOp(v, OP_Key, tab1, 0, 0, 0); sqliteVdbeAddOp(v, OP_NotFound, tab2, iCont, 0, 0); rc = selectInnerLoop(pParse, 0, tab1, p->pEList->nExpr, - 0, -1, eDest, iParm, + p->pOrderBy, -1, eDest, iParm, iCont, iBreak); if( rc ) return 1; sqliteVdbeAddOp(v, OP_Goto, 0, iCont, 0, 0); sqliteVdbeAddOp(v, OP_Close, tab2, 0, 0, iBreak); sqliteVdbeAddOp(v, OP_Close, tab1, 0, 0, 0); + if( p->pOrderBy ){ + generateSortTail(v, p->pEList->nExpr); + } break; } } assert( p->pEList && pPrior->pEList ); if( p->pEList->nExpr!=pPrior->pEList->nExpr ){ - sqliteSetString(&pParse->zErrMsg, "SELECTs have different numbers " - "of columns and therefore cannot be joined", 0); + sqliteSetString(&pParse->zErrMsg, "SELECTs to the left and right of ", + selectOpName(p->op), " do not have the same number of result columns", 0); pParse->nErr++; return 1; } @@ -386,7 +614,7 @@ int sqliteSelect( int eDest, /* One of: SRT_Callback Mem Set Union Except */ int iParm /* Save result in this memory location, if >=0 */ ){ - int i, j; + int i; WhereInfo *pWInfo; Vdbe *v; int isAgg = 0; /* True for select lists like "count(*)" */ @@ -407,7 +635,6 @@ int sqliteSelect( /* Make local copies of the parameters for this query. */ - pEList = p->pEList; pTabList = p->pSrc; pWhere = p->pWhere; pOrderBy = p->pOrderBy; @@ -422,17 +649,14 @@ int sqliteSelect( if( pParse->nErr>0 ) return 0; sqliteParseInfoReset(pParse); - /* Look up every table in the table list. + /* Look up every table in the table list and create an appropriate + ** columnlist in pEList if there isn't one already. (The parser leaves + ** a NULL in the pEList field if the SQL said "SELECT * FROM ...") */ - for(i=0; inId; i++){ - pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName); - if( pTabList->a[i].pTab==0 ){ - sqliteSetString(&pParse->zErrMsg, "no such table: ", - pTabList->a[i].zName, 0); - pParse->nErr++; - return 1; - } + if( fillInColumnList(pParse, p) ){ + return 1; } + pEList = p->pEList; /* Allocate a temporary table to use for the DISTINCT set, if ** necessary. This must be done early to allocate the cursor before @@ -444,21 +668,6 @@ int sqliteSelect( distinct = -1; } - /* 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; inId; i++){ - Table *pTab = pTabList->a[i].pTab; - for(j=0; jnCol; j++){ - Expr *pExpr = sqliteExpr(TK_FIELD, 0, 0, 0); - pExpr->iTable = i + pParse->nTab; - pExpr->iField = j; - p->pEList = pEList = sqliteExprListAppend(pEList, pExpr, 0); - } - } - } - /* If writing to memory or generating a set ** only a single column may be output. */ @@ -587,7 +796,7 @@ int sqliteSelect( ** step is skipped if the output is going to a table or a memory cell. */ if( eDest==SRT_Callback ){ - generateColumnNames(v, pTabList, pEList); + generateColumnNames(pParse, pTabList, pEList); } /* Reset the aggregator @@ -707,13 +916,7 @@ int sqliteSelect( ** 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_SortClose, 0, 0, 0, end); + generateSortTail(v, pEList->nExpr); } return 0; } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index d2b1347f90..a4530ce79b 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -23,7 +23,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.21 2000/06/07 15:24:40 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.22 2000/06/07 23:51:51 drh Exp $ */ #include "sqlite.h" #include "dbbe.h" @@ -182,8 +182,9 @@ struct ExprList { struct { Expr *pExpr; /* The list of expressions */ char *zName; /* Token associated with this expression */ - int idx; /* ... */ - int isAgg; /* True if this is an aggregate like count(*) */ + char sortOrder; /* 1 for DESC or 0 for ASC */ + char isAgg; /* True if this is an aggregate like count(*) */ + char done; /* A flag to indicate when processing is finished */ } *a; /* One entry for each expression */ }; @@ -279,6 +280,7 @@ struct Parse { Token sLastToken; /* The last token parsed */ Table *pNewTable; /* A table being constructed by CREATE TABLE */ Vdbe *pVdbe; /* An engine for executing database bytecode */ + int colNamesSet; /* TRUE after OP_ColumnCount has been issued to pVdbe */ 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 */ @@ -349,8 +351,10 @@ int sqliteGlobCompare(const char*,const char*); int sqliteLikeCompare(const unsigned char*,const unsigned char*); char *sqliteTableNameFromToken(Token*); int sqliteExprCheck(Parse*, Expr*, int, int*); +int sqliteExprCompare(Expr*, Expr*); int sqliteFuncId(Token*); int sqliteExprResolveIds(Parse*, IdList*, Expr*); void sqliteExprResolveInSelect(Parse*, Expr*); int sqliteExprAnalyzeAggregates(Parse*, Expr*); -void sqlitePArseInfoReset(Parse*); +void sqliteParseInfoReset(Parse*); +Vdbe *sqliteGetVdbe(Parse*); diff --git a/src/update.c b/src/update.c index ca11425418..2b5c444d61 100644 --- a/src/update.c +++ b/src/update.c @@ -24,7 +24,7 @@ ** This file contains C code routines that are called by the parser ** to handle UPDATE statements. ** -** $Id: update.c,v 1.4 2000/06/06 13:54:16 drh Exp $ +** $Id: update.c,v 1.5 2000/06/07 23:51:51 drh Exp $ */ #include "sqliteInt.h" @@ -104,7 +104,7 @@ void sqliteUpdate( } for(j=0; jnCol; j++){ if( strcmp(pTab->aCol[j].zName, pChanges->a[i].zName)==0 ){ - pChanges->a[i].idx = j; + /* pChanges->a[i].idx = j; */ aXRef[j] = i; break; } @@ -138,10 +138,7 @@ void sqliteUpdate( /* Begin generating code. */ - v = pParse->pVdbe; - if( v==0 ){ - v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); - } + v = sqliteGetVdbe(pParse); if( v==0 ) goto update_cleanup; /* Begin the database scan diff --git a/test/in.test b/test/in.test index c30879ef50..01276b79a7 100644 --- a/test/in.test +++ b/test/in.test @@ -23,7 +23,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the IN and BETWEEN operator. # -# $Id: in.test,v 1.1 2000/06/06 13:54:16 drh Exp $ +# $Id: in.test,v 1.2 2000/06/07 23:51:51 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -146,5 +146,20 @@ do_test in-4.2 { execsql {SELECT a FROM t1 ORDER BY a} } {1 2 3 4 5 6 7 8} +# Do an IN with a constant RHS but where the RHS has many, many +# elements. We need to test that collisions in the hash table +# are resolved properly. +# +do_test in-5.1 { + execsql { + INSERT INTO t1 VALUES('hello', 'world'); + SELECT * FROM t1 + WHERE a IN ( + 'Do','an','IN','with','a','constant','RHS','but','where','the', + 'has','many','elements','We','need','to','test','that', + 'collisions','hash','table','are','resolved','properly', + 'This','in-set','contains','thirty','one','entries','hello'); + } +} {hello world} finish_test