From 19a775c249980dcd92c96fe79133aa070cdd786d Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 5 Jun 2000 18:54:46 +0000 Subject: [PATCH] :-) (CVS 52) FossilOrigin-Name: c02268bdf4c28edc2542ce0ca1ba24fd6b5058fa --- manifest | 33 ++++----- manifest.uuid | 2 +- src/build.c | 4 +- src/expr.c | 27 ++++++-- src/insert.c | 7 +- src/parse.y | 8 ++- src/select.c | 158 +++++++++++++++++++++++++++++--------------- src/sqliteInt.h | 17 +++-- src/vdbe.c | 111 ++++++++++++++++++++++++++----- src/vdbe.h | 117 ++++++++++++++++---------------- src/where.c | 60 ++++++++++++----- test/copy.test | 3 +- test/select2.test | 26 +++++--- test/subselect.test | 90 +++++++++++++++++++++++++ 14 files changed, 473 insertions(+), 190 deletions(-) create mode 100644 test/subselect.test diff --git a/manifest b/manifest index 1d1c1fcb90..6a1c2e8e2d 100644 --- a/manifest +++ b/manifest @@ -1,38 +1,39 @@ -C separate\sSelect\sstructure\s(CVS\s51) -D 2000-06-05T16:01:39 +C :-)\s(CVS\s52) +D 2000-06-05T18:54:46 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 15c4f3844774baa882435223119a18c33810ee94 +F src/build.c 6c9454b2e2b866979527fb41b19ad8bc49c27a20 F src/dbbe.c ae8b5d2cdb4fa7dd11313059984be9457fa77f63 F src/dbbe.h a8a46f71238e0f09f3ec08fd9d1c8c7f4cdc49bf F src/delete.c e11433c14ed5cc8553cba14296b3baa3c23054bc -F src/expr.c 793c15de4ce2911fa1a74999750bd3c0c9ca513f -F src/insert.c ddae33b3dea1b4e743092d04240a20def9f88b72 +F src/expr.c 7e87558f88e7a52a902d78446843bda25015531e +F src/insert.c 5e69dd70c3f91cf5ec5090f39fd6cd8e135af9bf F src/main.c 93a7ad14bb5a82ad13ad59da23ef674a94b0c3d6 -F src/parse.y 020e5da71e14b63860c97589700eb11bf4699981 -F src/select.c 98f417b72e2edd277602cc14eb5c23743e616e60 +F src/parse.y 79b403240985c71d3f80abe3943e57482287428f +F src/select.c ab379f969283819ac81b72d0bf7c7aa3fc9389db F src/shell.c 5fa24c0bb678782ffe9070128e3e160674f297eb F src/sqlite.h 58da0a8590133777b741f9836beaef3d58f40268 -F src/sqliteInt.h da8e0abf204438b812e315279acce05122166440 +F src/sqliteInt.h c01eef2760cebee09c8e1710faf2ebd6fc8a16b6 F src/tclsqlite.c 9f358618ae803bedf4fb96da5154fd45023bc1f7 F src/tokenize.c 15c229fee77325334c6814652e429b0930eba6c1 F src/update.c 3f05d5082fd2c34f15d1e4a4db17355ad8807a78 F src/util.c 33f9baa01e45394ef0cf85361a0e872987884315 -F src/vdbe.c 108f0e58beee1361bcb8dc2a59c7d54e5f4360bc -F src/vdbe.h f20a3140905c385237e0891122beccde779c78c7 -F src/where.c bed9a8360cbfbf712bdc397c8e22216a5e5f9800 +F src/vdbe.c b8b78d0eaae9aa68e96b9d37da7d4571905396ca +F src/vdbe.h 5fd02cb52af0efa0165dd67b6e0036b853e1620f +F src/where.c 6b840a726b06b5122f112e3bc3c142a230af6251 F test/all.test 0950c135cab7e60c07bd745ccfad1476211e5bd7 -F test/copy.test a06548d8dd6e8a006c42c9906fb200b7dee2cc17 +F test/copy.test 73c3783535db538c8ebd8fffb931376864fc3226 F test/delete.test 30451333f89479d2deb5410edd3f3cce67339944 F test/expr.test db6984d2a6e86118dfce68edade6539495f29022 F test/index.test 9f99dca2d904b8de330863a978587f136e2df65a F test/insert.test b4c186ffa4b97a231643726f3bcee29815b24eaf F test/select1.test a0b00df77e85adff75c338e487718c5d31f69e3a -F test/select2.test ebb5497babf02982304841f2eacc985bcc17691e +F test/select2.test 3cd3c0f9d67e98b1b54af5853679b4a111224410 +F test/subselect.test 6fbfeaa9e3e91c915618336f91163b60487546d2 F test/table.test 85d6f410d127ec508c6640f02d7c40d218414e81 F test/tester.tcl 44690d463c1dc83a4c76ccde07cc146a988600f6 F test/update.test 69459302ea75cafac1479e60b0e36efb88123c0e @@ -47,7 +48,7 @@ F www/c_interface.tcl 8867d76ddd416d2fbd41e4cb3de8efa9cef105a5 F www/changes.tcl 567cc6066d87460bdedff8e5bbc20f41ddaadf77 F www/index.tcl f8189a7898f6d06307c34047b9d7e00860026e44 F www/sqlite.tcl 2f933ce18cffd34a0a020a82435ab937137970fd -P 1cf2873d55b471bb3e397f90dc0868dd88c440a0 -R dfbce0b035783f6473b57dede04bcdd9 +P ce45dea902f9010a1c2c9ba3550dd789e7c15fcd +R 1d9a27ba1e9706d5d412063b9a982ec3 U drh -Z 4195201472d7632b2259e7fe18f40c66 +Z 9480381052dbb4a62eb24ab4005b2230 diff --git a/manifest.uuid b/manifest.uuid index b3958a948e..54dd59194f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ce45dea902f9010a1c2c9ba3550dd789e7c15fcd \ No newline at end of file +c02268bdf4c28edc2542ce0ca1ba24fd6b5058fa \ No newline at end of file diff --git a/src/build.c b/src/build.c index 5144f13310..d88e57cd45 100644 --- a/src/build.c +++ b/src/build.c @@ -33,7 +33,7 @@ ** COPY ** VACUUM ** -** $Id: build.c,v 1.14 2000/06/03 18:06:52 drh Exp $ +** $Id: build.c,v 1.15 2000/06/05 18:54:46 drh Exp $ */ #include "sqliteInt.h" @@ -108,6 +108,8 @@ void sqliteExprDelete(Expr *p){ if( p==0 ) return; if( p->pLeft ) sqliteExprDelete(p->pLeft); if( p->pRight ) sqliteExprDelete(p->pRight); + if( p->pList ) sqliteExprListDelete(p->pList); + if( p->pSelect ) sqliteSelectDelete(p->pSelect); sqliteFree(p); } diff --git a/src/expr.c b/src/expr.c index a993834969..5fd5bbb346 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.5 2000/06/04 12:58:37 drh Exp $ +** $Id: expr.c,v 1.6 2000/06/05 18:54:46 drh Exp $ */ #include "sqliteInt.h" @@ -32,8 +32,13 @@ ** 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. +** to the index of the referenced table in pTabList plus the pParse->nTab +** value. The iField value is changed to the index of the field of the +** referenced table. +** +** This routine also looks for SELECTs that are part of an expression. +** If it finds any, it generates code to write the value of that select +** into a memory cell. ** ** Unknown fields or tables provoke an error. The function returns ** the number of errors seen and leaves an error message on pParse->zErrMsg. @@ -54,7 +59,7 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){ for(j=0; jnCol; j++){ if( sqliteStrICmp(pTab->aCol[j].zName, z)==0 ){ cnt++; - pExpr->iTable = i; + pExpr->iTable = i + pParse->nTab; pExpr->iField = j; } } @@ -104,7 +109,7 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){ for(j=0; jnCol; j++){ if( sqliteStrICmp(pTab->aCol[j].zName, zRight)==0 ){ cnt++; - pExpr->iTable = i; + pExpr->iTable = i + pParse->nTab; pExpr->iField = j; } } @@ -132,6 +137,14 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){ break; } + case TK_SELECT: { + pExpr->iField = pParse->nMem++; + if( sqliteSelect(pParse, pExpr->pSelect, -1, pExpr->iField) ){ + return 1; + } + break; + } + /* For all else, just recursively walk the tree */ default: { if( pExpr->pLeft @@ -385,6 +398,10 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){ } break; } + case TK_SELECT: { + sqliteVdbeAddOp(v, OP_MemLoad, pExpr->iField, 0, 0, 0); + break; + } } return; } diff --git a/src/insert.c b/src/insert.c index 24fcff6705..112ba3386c 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.5 2000/06/04 12:58:38 drh Exp $ +** $Id: insert.c,v 1.6 2000/06/05 18:54:46 drh Exp $ */ #include "sqliteInt.h" @@ -101,7 +101,10 @@ void sqliteInsert( } } } - v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); + v = pParse->pVdbe; + if( v==0 ){ + v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); + } if( v ){ Index *pIdx; sqliteVdbeAddOp(v, OP_Open, 0, 1, pTab->zName, 0); diff --git a/src/parse.y b/src/parse.y index 001b03d0bc..125a140511 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.9 2000/06/05 16:01:39 drh Exp $ +** @(#) $Id: parse.y,v 1.10 2000/06/05 18:54:46 drh Exp $ */ %token_prefix TK_ %token_type {Token} @@ -133,7 +133,7 @@ cmd ::= DROP TABLE id(X). {sqliteDropTable(pParse,&X);} // The select statement // cmd ::= select(X). { - sqliteSelect(pParse, X, 0, 0); + sqliteSelect(pParse, X, -1, -1); sqliteSelectDelete(X); } @@ -311,6 +311,10 @@ expr(A) ::= expr(X) NOTNULL. {A = sqliteExpr(TK_NOTNULL, X, 0, 0);} expr(A) ::= NOT expr(X). {A = sqliteExpr(TK_NOT, X, 0, 0);} expr(A) ::= MINUS expr(X). [UMINUS] {A = sqliteExpr(TK_UMINUS, X, 0, 0);} expr(A) ::= PLUS expr(X). [UMINUS] {A = X;} +expr(A) ::= LP select(X) RP. { + A = sqliteExpr(TK_SELECT, 0, 0, 0); + A->pSelect = X; +} %type exprlist {ExprList*} %destructor exprlist {sqliteExprListDelete($$);} diff --git a/src/select.c b/src/select.c index e1f94891ec..5317452e6d 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.7 2000/06/05 16:01:39 drh Exp $ +** $Id: select.c,v 1.8 2000/06/05 18:54:46 drh Exp $ */ #include "sqliteInt.h" @@ -71,11 +71,12 @@ void sqliteSelectDelete(Select *p){ /* ** Generate code for the given SELECT statement. ** -** If pDest==0 and iMem<0, then the results of the query are sent to -** the callback function. If pDest!=0 then the results are written to -** the single table specified. If pDest==0 and iMem>=0 then the result -** should be a single value which is then stored in memory location iMem -** of the virtual machine. +** If iDest<0 and iMem<0, then the results of the query are sent to +** the callback function. If iDest>=0 then the results are written to +** an open cursor with the index iDest. The calling function is +** responsible for having that cursor open. If iDest<0 and iMem>=0 +** then the result should be a single value which is then stored in +** memory location iMem of the virtual machine. ** ** This routine returns the number of errors. If any errors are ** encountered, then an appropriate error message is left in @@ -87,7 +88,7 @@ void sqliteSelectDelete(Select *p){ int sqliteSelect( Parse *pParse, /* The parser context */ Select *p, /* The SELECT statement being coded. */ - Table *pDest, /* Write results here, if not NULL */ + int iDest, /* Write results to this cursor */ int iMem /* Save result in this memory location, if >=0 */ ){ int i, j; @@ -98,14 +99,14 @@ int sqliteSelect( 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 distinct; /* If true, only output distinct results */ - + int isDistinct; /* True if the DISTINCT keyword is present */ + int distinct; /* Table to use for the distinct set */ pEList = p->pEList; pTabList = p->pSrc; pWhere = p->pWhere; pOrderBy = p->pOrderBy; - distinct = p->isDistinct; + isDistinct = p->isDistinct; /* ** Do not even attempt to generate any code if we have already seen @@ -125,6 +126,13 @@ int sqliteSelect( } } + /* Allocate a temporary table to use for the DISTINCT set, if + ** necessary. + */ + if( isDistinct ){ + distinct = pParse->nTab++; + } + /* If the list of fields to retrieve is "*" then replace it with ** a list of all fields from all tables. */ @@ -133,13 +141,22 @@ int sqliteSelect( Table *pTab = pTabList->a[i].pTab; for(j=0; jnCol; j++){ Expr *pExpr = sqliteExpr(TK_FIELD, 0, 0, 0); - pExpr->iTable = i; + pExpr->iTable = i + pParse->nTab; pExpr->iField = j; pEList = sqliteExprListAppend(pEList, pExpr, 0); } } } + /* If writing to memory, only a single column may be output. + */ + if( iMem>=0 && pEList->nExpr>1 ){ + sqliteSetString(&pParse->zErrMsg, "only a single result allowed for " + "a SELECT that is part of an expression", 0); + pParse->nErr++; + return 1; + } + /* Resolve the field names and do a semantics check on all the expressions. */ for(i=0; inExpr; i++){ @@ -180,17 +197,25 @@ int sqliteSelect( } } - /* ORDER BY is ignored if this is an aggregate query like count(*) - ** since only one row will be returned. + /* ORDER BY is ignored if + ** + ** (1) this is an aggregate query like count(*) + ** since only one row will be returned. + ** + ** (2) We are writing the result to another table, since the + ** order will get scrambled again after inserting. + ** + ** (3) We are writing to a memory cell, since there is only + ** one result. */ - if( isAgg && pOrderBy ){ + if( isAgg || iDest>=0 || iMem>=0 ){ pOrderBy = 0; } - /* Turn off distinct if this is an aggregate + /* Turn off distinct if this is an aggregate or writing to memory. */ - if( isAgg ){ - distinct = 0; + if( isAgg || iMem>=0 ){ + isDistinct = 0; } /* Begin generating code. @@ -208,38 +233,42 @@ int sqliteSelect( sqliteVdbeAddOp(v, OP_SortOpen, 0, 0, 0, 0); } - /* Identify column names + /* Identify column names if we will be using a callback. This + ** step is skipped if the output is going to a table or a memory cell. */ - 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); + if( iDest<0 && iMem<0 ){ + 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); + } + continue; } - 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; - char *zTab; - - zTab = pTabList->a[p->iTable].zAlias; - if( zTab==0 ) zTab = pTab->zName; - sqliteSetString(&zName, zTab, ".", pTab->aCol[p->iField].zName, 0); + 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); - sqliteFree(zName); }else{ - Table *pTab = pTabList->a[0].pTab; - sqliteVdbeAddOp(v, OP_ColumnName, i, 0, pTab->aCol[p->iField].zName, 0); + if( pTabList->nId>1 ){ + char *zName = 0; + Table *pTab = pTabList->a[p->iTable].pTab; + char *zTab; + + zTab = pTabList->a[p->iTable].zAlias; + if( zTab==0 ) zTab = pTab->zName; + sqliteSetString(&zName, zTab, ".", pTab->aCol[p->iField].zName, 0); + sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0); + sqliteFree(zName); + }else{ + Table *pTab = pTabList->a[0].pTab; + char *zName = pTab->aCol[p->iField].zName; + sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0); + } } } } @@ -263,10 +292,16 @@ int sqliteSelect( } } + /* Initialize the memory cell to NULL + */ + if( iMem>=0 ){ + sqliteVdbeAddOp(v, OP_Null, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_MemStore, iMem, 0, 0, 0); + } + /* Begin the database scan */ - if( distinct ){ - distinct = pTabList->nId*2+1; + if( isDistinct ){ sqliteVdbeAddOp(v, OP_Open, distinct, 1, 0, 0); } pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 0); @@ -283,13 +318,13 @@ int sqliteSelect( /* If the current result is not distinct, script the remainder ** of this processing. */ - if( distinct ){ - int isDistinct = sqliteVdbeMakeLabel(v); + if( isDistinct ){ + int lbl = sqliteVdbeMakeLabel(v); sqliteVdbeAddOp(v, OP_MakeKey, pEList->nExpr, 1, 0, 0); - sqliteVdbeAddOp(v, OP_Distinct, distinct, isDistinct, 0, 0); + sqliteVdbeAddOp(v, OP_Distinct, distinct, lbl, 0, 0); sqliteVdbeAddOp(v, OP_Pop, pEList->nExpr+1, 0, 0, 0); sqliteVdbeAddOp(v, OP_Goto, 0, pWInfo->iContinue, 0, 0); - sqliteVdbeAddOp(v, OP_String, 0, 0, "", isDistinct); + sqliteVdbeAddOp(v, OP_String, 0, 0, "", lbl); sqliteVdbeAddOp(v, OP_Put, distinct, 0, 0, 0); } @@ -330,6 +365,14 @@ int sqliteSelect( } sqliteVdbeAddOp(v, op, p1, 0, 0, 0); } + }else if( iDest>=0 ){ + sqliteVdbeAddOp(v, OP_MakeRecord, pEList->nExpr, 0, 0, 0); + sqliteVdbeAddOp(v, OP_New, iDest, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Pull, 1, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Put, iDest, 0, 0, 0); + }else if( iMem>=0 ){ + sqliteVdbeAddOp(v, OP_MemStore, iMem, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Goto, 0, pWInfo->iBreak, 0, 0); }else{ sqliteVdbeAddOp(v, OP_Callback, pEList->nExpr, 0, 0, 0); } @@ -348,14 +391,23 @@ int sqliteSelect( 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); + sqliteVdbeAddOp(v, OP_SortClose, 0, 0, 0, end); } /* If this is an aggregate, then we need to invoke the callback ** exactly once. */ if( isAgg ){ - sqliteVdbeAddOp(v, OP_Callback, pEList->nExpr, 0, 0, 0); + if( iDest>=0 ){ + sqliteVdbeAddOp(v, OP_MakeRecord, pEList->nExpr, 0, 0, 0); + sqliteVdbeAddOp(v, OP_New, iDest, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Pull, 1, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Put, iDest, 0, 0, 0); + }else if( iMem>=0 ){ + sqliteVdbeAddOp(v, OP_MemStore, iMem, 0, 0, 0); + }else{ + sqliteVdbeAddOp(v, OP_Callback, pEList->nExpr, 0, 0, 0); + } } return 0; } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index a0a6f319d0..eca54c49f8 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -23,7 +23,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.14 2000/06/05 16:01:39 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.15 2000/06/05 18:54:46 drh Exp $ */ #include "sqlite.h" #include "dbbe.h" @@ -162,6 +162,7 @@ struct Expr { 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 */ + Select *pSelect; /* When the expression is a sub-select */ }; /* @@ -191,7 +192,7 @@ struct IdList { 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 */ + int idx; /* Index of a field named zName in a table */ } *a; /* One entry for each identifier on the list */ }; @@ -204,9 +205,11 @@ struct IdList { */ struct WhereInfo { Parse *pParse; - IdList *pTabList; - int iContinue; - int iBreak; + IdList *pTabList; /* List of tables in the join */ + int iContinue; /* Jump here to continue with next record */ + int iBreak; /* Jump here to break out of the loop */ + int base; /* Index of first Open opcode */ + Index *aIdx[32]; /* Indices used for each table */ }; /* @@ -239,6 +242,8 @@ struct Parse { 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 */ + int nTab; /* Number of previously allocated cursors */ + int nMem; /* Number of memory cells used so far */ }; /* @@ -281,7 +286,7 @@ void sqliteIdListAddAlias(IdList*, Token*); void sqliteIdListDelete(IdList*); void sqliteCreateIndex(Parse*, Token*, Token*, IdList*, Token*, Token*); void sqliteDropIndex(Parse*, Token*); -int sqliteSelect(Parse*, Select*, Table*, int); +int sqliteSelect(Parse*, Select*, int, int); Select *sqliteSelectNew(ExprList*,IdList*,Expr*,ExprList*,Expr*,ExprList*,int); void sqliteSelectDelete(Select*); void sqliteDeleteFrom(Parse*, Token*, Expr*); diff --git a/src/vdbe.c b/src/vdbe.c index d302a031b6..1b836bb8fb 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -41,7 +41,7 @@ ** But other routines are also provided to help in building up ** a program instruction by instruction. ** -** $Id: vdbe.c,v 1.15 2000/06/05 16:01:39 drh Exp $ +** $Id: vdbe.c,v 1.16 2000/06/05 18:54:47 drh Exp $ */ #include "sqliteInt.h" #include @@ -92,10 +92,20 @@ struct Stack { int i; /* Integer value */ int n; /* Number of characters in string value, including '\0' */ int flags; /* Some combination of STK_Null, STK_Str, STK_Dyn, etc. */ - double r; /* Real value */ + double r; /* Real value */ }; typedef struct Stack Stack; +/* +** Memory cells use the same structure as the stack except that space +** for an arbitrary string is added. +*/ +struct Mem { + Stack s; /* All values of the memory cell besides string */ + char *z; /* String value for this memory cell */ +}; +typedef struct Mem Mem; + /* ** Allowed values for Stack.flags */ @@ -133,6 +143,8 @@ struct Vdbe { char **azField; /* Data for each file field */ char *zLine; /* A single line from the input file */ int nLineAlloc; /* Number of spaces allocated for zLine */ + int nMem; /* Number of memory locations currently allocated */ + Mem *aMem; /* The memory locations */ }; /* @@ -464,6 +476,14 @@ static void Cleanup(Vdbe *p){ sqliteFree(p->aTab); p->aTab = 0; p->nTable = 0; + for(i=0; inMem; i++){ + if( p->aMem[i].s.flags & STK_Dyn ){ + sqliteFree(p->aMem[i].z); + } + } + sqliteFree(p->aMem); + p->aMem = 0; + p->nMem = 0; for(i=0; inList; i++){ if( p->apList[i] ){ sqliteDbbeCloseTempFile(p->pBe, p->apList[i]); @@ -536,20 +556,21 @@ static char *zOpName[] = { 0, "Put", "Distinct", "Delete", "Field", "Key", "Rewind", "Next", "Destroy", "Reorganize", "ResetIdx", "NextIdx", "PutIdx", - "DeleteIdx", "ListOpen", "ListWrite", "ListRewind", - "ListRead", "ListClose", "SortOpen", "SortPut", - "SortMakeRec", "SortMakeKey", "Sort", "SortNext", - "SortKey", "SortCallback", "SortClose", "FileOpen", - "FileRead", "FileField", "FileClose", "MakeRecord", - "MakeKey", "Goto", "If", "Halt", - "ColumnCount", "ColumnName", "Callback", "Integer", - "String", "Null", "Pop", "Dup", - "Pull", "Add", "AddImm", "Subtract", - "Multiply", "Divide", "Min", "Max", - "Like", "Glob", "Eq", "Ne", - "Lt", "Le", "Gt", "Ge", - "IsNull", "NotNull", "Negative", "And", - "Or", "Not", "Concat", "Noop", + "DeleteIdx", "MemLoad", "MemStore", "ListOpen", + "ListWrite", "ListRewind", "ListRead", "ListClose", + "SortOpen", "SortPut", "SortMakeRec", "SortMakeKey", + "Sort", "SortNext", "SortKey", "SortCallback", + "SortClose", "FileOpen", "FileRead", "FileField", + "FileClose", "MakeRecord", "MakeKey", "Goto", + "If", "Halt", "ColumnCount", "ColumnName", + "Callback", "Integer", "String", "Null", + "Pop", "Dup", "Pull", "Add", + "AddImm", "Subtract", "Multiply", "Divide", + "Min", "Max", "Like", "Glob", + "Eq", "Ne", "Lt", "Le", + "Gt", "Ge", "IsNull", "NotNull", + "Negative", "And", "Or", "Not", + "Concat", "Noop", }; /* @@ -2402,6 +2423,64 @@ int sqliteVdbeExec( break; } + /* Opcode: MemStore P1 * * + ** + ** Pop a single value of the stack and store that value into memory + ** location P1. P1 should be a small integer since space is allocated + ** for all memory locations between 0 and P1 inclusive. + */ + case OP_MemStore: { + int i = pOp->p1; + int tos = p->tos; + Mem *pMem; + if( tos<0 ) goto not_enough_stack; + if( i>=p->nMem ){ + int nOld = p->nMem; + p->nMem = i + 5; + p->aMem = sqliteRealloc(p->aMem, p->nMem*sizeof(p->aMem[0])); + if( p->aMem==0 ) goto no_mem; + if( nOldnMem ){ + memset(&p->aMem[nOld], 0, sizeof(p->aMem[0])*(p->nMem-nOld)); + } + } + pMem = &p->aMem[i]; + if( pMem->s.flags & STK_Dyn ){ + sqliteFree(pMem->z); + } + pMem->s = p->aStack[tos]; + if( pMem->s.flags & STK_Str ){ + pMem->z = 0; + sqliteSetString(&pMem->z, p->zStack[tos], 0); + pMem->s.flags |= STK_Dyn; + } + PopStack(p, 1); + break; + } + + /* Opcode: MemLoad P1 * * + ** + ** Push a copy of the value in memory location P1 onto the stack. + */ + case OP_MemLoad: { + int tos = ++p->tos; + int i = pOp->p1; + if( NeedStack(p, tos) ) goto no_mem; + if( i<0 || i>=p->nMem ){ + p->aStack[tos].flags = STK_Null; + p->zStack[tos] = 0; + }else{ + p->aStack[tos] = p->aMem[i].s; + if( p->aStack[tos].flags & STK_Str ){ + char *z = sqliteMalloc(p->aStack[tos].n); + if( z==0 ) goto no_mem; + memcpy(z, p->aMem[i].z, p->aStack[tos].n); + p->zStack[tos] = z; + p->aStack[tos].flags |= STK_Dyn; + } + } + break; + } + /* An other opcode is illegal... */ default: { diff --git a/src/vdbe.h b/src/vdbe.h index 3abba05695..c85a1d3a81 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -27,7 +27,7 @@ ** 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.5 2000/06/04 12:58:39 drh Exp $ +** $Id: vdbe.h,v 1.6 2000/06/05 18:54:47 drh Exp $ */ #ifndef _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_ @@ -91,70 +91,73 @@ typedef struct VdbeOp VdbeOp; #define OP_PutIdx 16 #define OP_DeleteIdx 17 -#define OP_ListOpen 18 -#define OP_ListWrite 19 -#define OP_ListRewind 20 -#define OP_ListRead 21 -#define OP_ListClose 22 +#define OP_MemLoad 18 +#define OP_MemStore 19 -#define OP_SortOpen 23 -#define OP_SortPut 24 -#define OP_SortMakeRec 25 -#define OP_SortMakeKey 26 -#define OP_Sort 27 -#define OP_SortNext 28 -#define OP_SortKey 29 -#define OP_SortCallback 30 -#define OP_SortClose 31 +#define OP_ListOpen 20 +#define OP_ListWrite 21 +#define OP_ListRewind 22 +#define OP_ListRead 23 +#define OP_ListClose 24 -#define OP_FileOpen 32 -#define OP_FileRead 33 -#define OP_FileField 34 -#define OP_FileClose 35 +#define OP_SortOpen 25 +#define OP_SortPut 26 +#define OP_SortMakeRec 27 +#define OP_SortMakeKey 28 +#define OP_Sort 29 +#define OP_SortNext 30 +#define OP_SortKey 31 +#define OP_SortCallback 32 +#define OP_SortClose 33 -#define OP_MakeRecord 36 -#define OP_MakeKey 37 +#define OP_FileOpen 34 +#define OP_FileRead 35 +#define OP_FileField 36 +#define OP_FileClose 37 -#define OP_Goto 38 -#define OP_If 39 -#define OP_Halt 40 +#define OP_MakeRecord 38 +#define OP_MakeKey 39 -#define OP_ColumnCount 41 -#define OP_ColumnName 42 -#define OP_Callback 43 +#define OP_Goto 40 +#define OP_If 41 +#define OP_Halt 42 -#define OP_Integer 44 -#define OP_String 45 -#define OP_Null 46 -#define OP_Pop 47 -#define OP_Dup 48 -#define OP_Pull 49 +#define OP_ColumnCount 43 +#define OP_ColumnName 44 +#define OP_Callback 45 -#define OP_Add 50 -#define OP_AddImm 51 -#define OP_Subtract 52 -#define OP_Multiply 53 -#define OP_Divide 54 -#define OP_Min 55 -#define OP_Max 56 -#define OP_Like 57 -#define OP_Glob 58 -#define OP_Eq 59 -#define OP_Ne 60 -#define OP_Lt 61 -#define OP_Le 62 -#define OP_Gt 63 -#define OP_Ge 64 -#define OP_IsNull 65 -#define OP_NotNull 66 -#define OP_Negative 67 -#define OP_And 68 -#define OP_Or 69 -#define OP_Not 70 -#define OP_Concat 71 -#define OP_Noop 72 +#define OP_Integer 46 +#define OP_String 47 +#define OP_Null 48 +#define OP_Pop 49 +#define OP_Dup 50 +#define OP_Pull 51 -#define OP_MAX 72 +#define OP_Add 52 +#define OP_AddImm 53 +#define OP_Subtract 54 +#define OP_Multiply 55 +#define OP_Divide 56 +#define OP_Min 57 +#define OP_Max 58 +#define OP_Like 59 +#define OP_Glob 60 +#define OP_Eq 61 +#define OP_Ne 62 +#define OP_Lt 63 +#define OP_Le 64 +#define OP_Gt 65 +#define OP_Ge 66 +#define OP_IsNull 67 +#define OP_NotNull 68 +#define OP_Negative 69 +#define OP_And 70 +#define OP_Or 71 +#define OP_Not 72 +#define OP_Concat 73 +#define OP_Noop 74 + +#define OP_MAX 74 /* ** Prototypes for the VDBE interface. See comments on the implementation diff --git a/src/where.c b/src/where.c index 188d172ba1..69c7e16127 100644 --- a/src/where.c +++ b/src/where.c @@ -25,7 +25,7 @@ ** the WHERE clause of SQL statements. Also found here are subroutines ** to generate VDBE code to evaluate expressions. ** -** $Id: where.c,v 1.5 2000/05/31 15:34:54 drh Exp $ +** $Id: where.c,v 1.6 2000/06/05 18:54:47 drh Exp $ */ #include "sqliteInt.h" @@ -86,18 +86,22 @@ static int exprSplit(int nSlot, ExprInfo *aSlot, Expr *pExpr){ ** 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. +** +** "base" is the cursor number (the value of the iTable field) that +** corresponds to the first entry in the table list. This is the +** same as pParse->nTab. */ -static int exprTableUsage(Expr *p){ +static int exprTableUsage(int base, Expr *p){ unsigned int mask = 0; if( p==0 ) return 0; if( p->op==TK_FIELD ){ - return 1<iTable; + return 1<< (p->iTable - base); } if( p->pRight ){ - mask = exprTableUsage(p->pRight); + mask = exprTableUsage(base, p->pRight); } if( p->pLeft ){ - mask |= exprTableUsage(p->pLeft); + mask |= exprTableUsage(base, p->pLeft); } return mask; } @@ -107,21 +111,25 @@ static int exprTableUsage(Expr *p){ ** "p" field filled in. The job of this routine is to analyze the ** subexpression and populate all the other fields of the ExprInfo ** structure. +** +** "base" is the cursor number (the value of the iTable field) that +** corresponds to the first entyr in the table list. This is the +** same as pParse->nTab. */ -static void exprAnalyze(ExprInfo *pInfo){ +static void exprAnalyze(int base, ExprInfo *pInfo){ Expr *pExpr = pInfo->p; - pInfo->prereqLeft = exprTableUsage(pExpr->pLeft); - pInfo->prereqRight = exprTableUsage(pExpr->pRight); + pInfo->prereqLeft = exprTableUsage(base, pExpr->pLeft); + pInfo->prereqRight = exprTableUsage(base, 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->idxRight = pExpr->pRight->iTable - base; pInfo->indexable = 1; } if( pExpr->pLeft->op==TK_FIELD ){ - pInfo->idxLeft = pExpr->pLeft->iTable; + pInfo->idxLeft = pExpr->pLeft->iTable - base; pInfo->indexable = 1; } } @@ -150,6 +158,7 @@ WhereInfo *sqliteWhereBegin( 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 */ + int base; /* First available index for OP_Open opcodes */ Index *aIdx[32]; /* Index to use on each nested loop. */ ExprInfo aExpr[50]; /* The WHERE clause is divided into these expressions */ @@ -166,6 +175,7 @@ WhereInfo *sqliteWhereBegin( } pWInfo->pParse = pParse; pWInfo->pTabList = pTabList; + base = pWInfo->base = pParse->nTab; /* Split the WHERE clause into as many as 32 separate subexpressions ** where each subexpression is separated by an AND operator. Any additional @@ -180,7 +190,7 @@ WhereInfo *sqliteWhereBegin( /* Analyze all of the subexpressions. */ for(i=0; inTab, &aExpr[i]); } /* Figure out a good nesting order for the tables. aOrder[0] will @@ -261,11 +271,12 @@ WhereInfo *sqliteWhereBegin( /* Open all tables in the pTabList and all indices in aIdx[]. */ for(i=0; inId; i++){ - sqliteVdbeAddOp(v, OP_Open, i, 0, pTabList->a[i].pTab->zName, 0); + sqliteVdbeAddOp(v, OP_Open, base+i, 0, pTabList->a[i].pTab->zName, 0); if( inId+i, 0, aIdx[i]->zName, 0); + sqliteVdbeAddOp(v, OP_Open, base+pTabList->nId+i, 0, aIdx[i]->zName, 0); } } + memcpy(pWInfo->aIdx, aIdx, sizeof(aIdx)); /* Generate the code to do the search */ @@ -281,7 +292,7 @@ WhereInfo *sqliteWhereBegin( /* Case 1: There was no usable index. We must do a complete ** scan of the table. */ - sqliteVdbeAddOp(v, OP_Next, idx, brk, 0, cont); + sqliteVdbeAddOp(v, OP_Next, base+idx, brk, 0, cont); haveKey = 0; }else{ /* Case 2: We do have a usable index in pIdx. @@ -308,8 +319,8 @@ WhereInfo *sqliteWhereBegin( } } 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); + sqliteVdbeAddOp(v, OP_Fetch, base+pTabList->nId+i, 0, 0, 0); + sqliteVdbeAddOp(v, OP_NextIdx, base+pTabList->nId+i, brk, 0, cont); if( i==pTabList->nId-1 && pushKey ){ haveKey = 1; }else{ @@ -327,7 +338,7 @@ WhereInfo *sqliteWhereBegin( 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); + sqliteVdbeAddOp(v, OP_Fetch, base+idx, 0, 0, 0); haveKey = 0; } sqliteExprIfFalse(pParse, aExpr[j].p, cont); @@ -348,8 +359,21 @@ WhereInfo *sqliteWhereBegin( */ void sqliteWhereEnd(WhereInfo *pWInfo){ Vdbe *v = pWInfo->pParse->pVdbe; + int i; + int brk = pWInfo->iBreak; + int base = pWInfo->base; + sqliteVdbeAddOp(v, OP_Goto, 0, pWInfo->iContinue, 0, 0); - sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, pWInfo->iBreak); + for(i=0; ipTabList->nId; i++){ + sqliteVdbeAddOp(v, OP_Close, base+i, 0, 0, brk); + brk = 0; + if( iaIdx) && pWInfo->aIdx[i]!=0 ){ + sqliteVdbeAddOp(v, OP_Close, base+pWInfo->pTabList->nId+i, 0, 0, 0); + } + } + if( brk!=0 ){ + sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, brk); + } sqliteFree(pWInfo); return; } diff --git a/test/copy.test b/test/copy.test index ebbbd557cb..97b42e5173 100644 --- a/test/copy.test +++ b/test/copy.test @@ -23,7 +23,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the COPY statement. # -# $Id: copy.test,v 1.3 2000/06/05 02:07:05 drh Exp $ +# $Id: copy.test,v 1.4 2000/06/05 18:54:47 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -81,7 +81,6 @@ do_test copy-1.4 { execsql {COPY test1 FROM 'data2.txt'} execsql {SELECT * FROM test1 ORDER BY one} } {11 22 33} -return # Test out the USING DELIMITERS clause # diff --git a/test/select2.test b/test/select2.test index 65439abc36..6b5c29bed5 100644 --- a/test/select2.test +++ b/test/select2.test @@ -23,7 +23,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the SELECT statement. # -# $Id: select2.test,v 1.4 2000/06/02 15:05:33 drh Exp $ +# $Id: select2.test,v 1.5 2000/06/05 18:54:47 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -71,14 +71,16 @@ do_test select2-1.2 { # Create a largish table # -execsql {CREATE TABLE tbl2(f1 int, f2 int, f3 int)} -set f [open ./testdata1.txt w] -for {set i 1} {$i<=30000} {incr i} { - puts $f "$i\t[expr {$i*2}]\t[expr {$i*3}]" -} -close $f -execsql {COPY tbl2 FROM './testdata1.txt'} -file delete -force ./testdata1.txt +do_test select2-2.0 { + execsql {CREATE TABLE tbl2(f1 int, f2 int, f3 int)} + set f [open ./testdata1.txt w] + for {set i 1} {$i<=30000} {incr i} { + puts $f "$i\t[expr {$i*2}]\t[expr {$i*3}]" + } + close $f + execsql {COPY tbl2 FROM './testdata1.txt'} + file delete -force ./testdata1.txt +} {} do_test select2-2.1 { execsql {SELECT count(*) FROM tbl2} @@ -91,9 +93,11 @@ do_test select2-3.1 { execsql {SELECT f1 FROM tbl2 WHERE f2==1000} } {500} -execsql {CREATE INDEX idx1 ON tbl2(f2)} +do_test select2-3.2a { + execsql {CREATE INDEX idx1 ON tbl2(f2)} +} {} -do_test select2-3.2 { +do_test select2-3.2b { execsql {SELECT f1 FROM tbl2 WHERE f2==1000} } {500} diff --git a/test/subselect.test b/test/subselect.test new file mode 100644 index 0000000000..c3b4e8bae0 --- /dev/null +++ b/test/subselect.test @@ -0,0 +1,90 @@ +# 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 implements regression tests for SQLite library. The +# focus of this file is testing SELECT statements that are part of +# expressions. +# +# $Id: subselect.test,v 1.1 2000/06/05 18:54:47 drh Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# Basic sanity checking. Try a simple subselect. +# +do_test subselect-1.1 { + execsql { + CREATE TABLE t1(a int, b int); + INSERT INTO t1 VALUES(1,2); + INSERT INTO t1 VALUES(3,4); + INSERT INTO t1 VALUES(5,6); + } + execsql {SELECT * FROM t1 WHERE a = (SELECT count(*) FROM t1)} +} {3 4} + +# Try a select with more than one result column. +# +do_test subselect-1.2 { + set v [catch {execsql {SELECT * FROM t1 WHERE a = (SELECT * FROM t1)}} msg] + lappend v $msg +} {1 {only a single result allowed for a SELECT this is part of an expression}} + +# A subselect without an aggregate. +# +do_test subselect-1.3a { + execsql {SELECT b from t1 where a = (SELECT a FROM t1 WHERE b=2)} +} {2} +do_test subselect-1.3b { + execsql {SELECT b from t1 where a = (SELECT a FROM t1 WHERE b=4)} +} {4} +do_test subselect-1.3c { + execsql {SELECT b from t1 where a = (SELECT a FROM t1 WHERE b=6)} +} {6} +do_test subselect-1.3c { + execsql {SELECT b from t1 where a = (SELECT a FROM t1 WHERE b=8)} +} {} + +# What if the subselect doesn't return any value. We should get +# NULL as the result. Check it out. +# +do_test subselect-1.4 { + execsql {INSERT INTO t1 VALUES(NULL,8)} + execsql {SELECT b from t1 where a = (SELECT a FROM t1 WHERE b=5)} +} {8} + +# Try multiple subselects within a single expression. +# +do_test subselect-1.5 { + execsql { + CREATE TABLE t2(x int, y int); + INSERT INTO t2 VALUES(1,2); + INSERT INTO t2 VALUES(2,4); + INSERT INTO t2 VALUES(3,8); + INSERT INTO t2 VALUES(4,16); + } + execsql { + SELECT y from t2 + WHERE x = (SELECT sum(b) FROM t1 where a notnull) - (SELECT sum(a) FROM t1) + } +} {8} + +finish_test