mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-08 14:02:16 +03:00
:-) (CVS 52)
FossilOrigin-Name: c02268bdf4c28edc2542ce0ca1ba24fd6b5058fa
This commit is contained in:
33
manifest
33
manifest
@@ -1,38 +1,39 @@
|
|||||||
C separate\sSelect\sstructure\s(CVS\s51)
|
C :-)\s(CVS\s52)
|
||||||
D 2000-06-05T16:01:39
|
D 2000-06-05T18:54:46
|
||||||
F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
|
F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
|
||||||
F Makefile.in 17ba1ccf8d2d40c627796bba8f72952365d6d644
|
F Makefile.in 17ba1ccf8d2d40c627796bba8f72952365d6d644
|
||||||
F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
|
F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
|
||||||
F configure 00a5b5c82147a576fa6e82d7c1b0d55c321d6d2c x
|
F configure 00a5b5c82147a576fa6e82d7c1b0d55c321d6d2c x
|
||||||
F configure.in 6ccfd5fc80517f7cfe605a7fc7e0f62d962a233c
|
F configure.in 6ccfd5fc80517f7cfe605a7fc7e0f62d962a233c
|
||||||
F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47
|
F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47
|
||||||
F src/build.c 15c4f3844774baa882435223119a18c33810ee94
|
F src/build.c 6c9454b2e2b866979527fb41b19ad8bc49c27a20
|
||||||
F src/dbbe.c ae8b5d2cdb4fa7dd11313059984be9457fa77f63
|
F src/dbbe.c ae8b5d2cdb4fa7dd11313059984be9457fa77f63
|
||||||
F src/dbbe.h a8a46f71238e0f09f3ec08fd9d1c8c7f4cdc49bf
|
F src/dbbe.h a8a46f71238e0f09f3ec08fd9d1c8c7f4cdc49bf
|
||||||
F src/delete.c e11433c14ed5cc8553cba14296b3baa3c23054bc
|
F src/delete.c e11433c14ed5cc8553cba14296b3baa3c23054bc
|
||||||
F src/expr.c 793c15de4ce2911fa1a74999750bd3c0c9ca513f
|
F src/expr.c 7e87558f88e7a52a902d78446843bda25015531e
|
||||||
F src/insert.c ddae33b3dea1b4e743092d04240a20def9f88b72
|
F src/insert.c 5e69dd70c3f91cf5ec5090f39fd6cd8e135af9bf
|
||||||
F src/main.c 93a7ad14bb5a82ad13ad59da23ef674a94b0c3d6
|
F src/main.c 93a7ad14bb5a82ad13ad59da23ef674a94b0c3d6
|
||||||
F src/parse.y 020e5da71e14b63860c97589700eb11bf4699981
|
F src/parse.y 79b403240985c71d3f80abe3943e57482287428f
|
||||||
F src/select.c 98f417b72e2edd277602cc14eb5c23743e616e60
|
F src/select.c ab379f969283819ac81b72d0bf7c7aa3fc9389db
|
||||||
F src/shell.c 5fa24c0bb678782ffe9070128e3e160674f297eb
|
F src/shell.c 5fa24c0bb678782ffe9070128e3e160674f297eb
|
||||||
F src/sqlite.h 58da0a8590133777b741f9836beaef3d58f40268
|
F src/sqlite.h 58da0a8590133777b741f9836beaef3d58f40268
|
||||||
F src/sqliteInt.h da8e0abf204438b812e315279acce05122166440
|
F src/sqliteInt.h c01eef2760cebee09c8e1710faf2ebd6fc8a16b6
|
||||||
F src/tclsqlite.c 9f358618ae803bedf4fb96da5154fd45023bc1f7
|
F src/tclsqlite.c 9f358618ae803bedf4fb96da5154fd45023bc1f7
|
||||||
F src/tokenize.c 15c229fee77325334c6814652e429b0930eba6c1
|
F src/tokenize.c 15c229fee77325334c6814652e429b0930eba6c1
|
||||||
F src/update.c 3f05d5082fd2c34f15d1e4a4db17355ad8807a78
|
F src/update.c 3f05d5082fd2c34f15d1e4a4db17355ad8807a78
|
||||||
F src/util.c 33f9baa01e45394ef0cf85361a0e872987884315
|
F src/util.c 33f9baa01e45394ef0cf85361a0e872987884315
|
||||||
F src/vdbe.c 108f0e58beee1361bcb8dc2a59c7d54e5f4360bc
|
F src/vdbe.c b8b78d0eaae9aa68e96b9d37da7d4571905396ca
|
||||||
F src/vdbe.h f20a3140905c385237e0891122beccde779c78c7
|
F src/vdbe.h 5fd02cb52af0efa0165dd67b6e0036b853e1620f
|
||||||
F src/where.c bed9a8360cbfbf712bdc397c8e22216a5e5f9800
|
F src/where.c 6b840a726b06b5122f112e3bc3c142a230af6251
|
||||||
F test/all.test 0950c135cab7e60c07bd745ccfad1476211e5bd7
|
F test/all.test 0950c135cab7e60c07bd745ccfad1476211e5bd7
|
||||||
F test/copy.test a06548d8dd6e8a006c42c9906fb200b7dee2cc17
|
F test/copy.test 73c3783535db538c8ebd8fffb931376864fc3226
|
||||||
F test/delete.test 30451333f89479d2deb5410edd3f3cce67339944
|
F test/delete.test 30451333f89479d2deb5410edd3f3cce67339944
|
||||||
F test/expr.test db6984d2a6e86118dfce68edade6539495f29022
|
F test/expr.test db6984d2a6e86118dfce68edade6539495f29022
|
||||||
F test/index.test 9f99dca2d904b8de330863a978587f136e2df65a
|
F test/index.test 9f99dca2d904b8de330863a978587f136e2df65a
|
||||||
F test/insert.test b4c186ffa4b97a231643726f3bcee29815b24eaf
|
F test/insert.test b4c186ffa4b97a231643726f3bcee29815b24eaf
|
||||||
F test/select1.test a0b00df77e85adff75c338e487718c5d31f69e3a
|
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/table.test 85d6f410d127ec508c6640f02d7c40d218414e81
|
||||||
F test/tester.tcl 44690d463c1dc83a4c76ccde07cc146a988600f6
|
F test/tester.tcl 44690d463c1dc83a4c76ccde07cc146a988600f6
|
||||||
F test/update.test 69459302ea75cafac1479e60b0e36efb88123c0e
|
F test/update.test 69459302ea75cafac1479e60b0e36efb88123c0e
|
||||||
@@ -47,7 +48,7 @@ F www/c_interface.tcl 8867d76ddd416d2fbd41e4cb3de8efa9cef105a5
|
|||||||
F www/changes.tcl 567cc6066d87460bdedff8e5bbc20f41ddaadf77
|
F www/changes.tcl 567cc6066d87460bdedff8e5bbc20f41ddaadf77
|
||||||
F www/index.tcl f8189a7898f6d06307c34047b9d7e00860026e44
|
F www/index.tcl f8189a7898f6d06307c34047b9d7e00860026e44
|
||||||
F www/sqlite.tcl 2f933ce18cffd34a0a020a82435ab937137970fd
|
F www/sqlite.tcl 2f933ce18cffd34a0a020a82435ab937137970fd
|
||||||
P 1cf2873d55b471bb3e397f90dc0868dd88c440a0
|
P ce45dea902f9010a1c2c9ba3550dd789e7c15fcd
|
||||||
R dfbce0b035783f6473b57dede04bcdd9
|
R 1d9a27ba1e9706d5d412063b9a982ec3
|
||||||
U drh
|
U drh
|
||||||
Z 4195201472d7632b2259e7fe18f40c66
|
Z 9480381052dbb4a62eb24ab4005b2230
|
||||||
|
@@ -1 +1 @@
|
|||||||
ce45dea902f9010a1c2c9ba3550dd789e7c15fcd
|
c02268bdf4c28edc2542ce0ca1ba24fd6b5058fa
|
@@ -33,7 +33,7 @@
|
|||||||
** COPY
|
** COPY
|
||||||
** VACUUM
|
** 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"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -108,6 +108,8 @@ void sqliteExprDelete(Expr *p){
|
|||||||
if( p==0 ) return;
|
if( p==0 ) return;
|
||||||
if( p->pLeft ) sqliteExprDelete(p->pLeft);
|
if( p->pLeft ) sqliteExprDelete(p->pLeft);
|
||||||
if( p->pRight ) sqliteExprDelete(p->pRight);
|
if( p->pRight ) sqliteExprDelete(p->pRight);
|
||||||
|
if( p->pList ) sqliteExprListDelete(p->pList);
|
||||||
|
if( p->pSelect ) sqliteSelectDelete(p->pSelect);
|
||||||
sqliteFree(p);
|
sqliteFree(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
27
src/expr.c
27
src/expr.c
@@ -23,7 +23,7 @@
|
|||||||
*************************************************************************
|
*************************************************************************
|
||||||
** This file contains C code routines used for processing expressions
|
** 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"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -32,8 +32,13 @@
|
|||||||
** table fields. Nodes of the form ID.ID or ID resolve into an
|
** 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
|
** 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
|
** 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
|
** to the index of the referenced table in pTabList plus the pParse->nTab
|
||||||
** is changed to the index of the field of the referenced table.
|
** 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
|
** Unknown fields or tables provoke an error. The function returns
|
||||||
** the number of errors seen and leaves an error message on pParse->zErrMsg.
|
** 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; j<pTab->nCol; j++){
|
for(j=0; j<pTab->nCol; j++){
|
||||||
if( sqliteStrICmp(pTab->aCol[j].zName, z)==0 ){
|
if( sqliteStrICmp(pTab->aCol[j].zName, z)==0 ){
|
||||||
cnt++;
|
cnt++;
|
||||||
pExpr->iTable = i;
|
pExpr->iTable = i + pParse->nTab;
|
||||||
pExpr->iField = j;
|
pExpr->iField = j;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -104,7 +109,7 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){
|
|||||||
for(j=0; j<pTab->nCol; j++){
|
for(j=0; j<pTab->nCol; j++){
|
||||||
if( sqliteStrICmp(pTab->aCol[j].zName, zRight)==0 ){
|
if( sqliteStrICmp(pTab->aCol[j].zName, zRight)==0 ){
|
||||||
cnt++;
|
cnt++;
|
||||||
pExpr->iTable = i;
|
pExpr->iTable = i + pParse->nTab;
|
||||||
pExpr->iField = j;
|
pExpr->iField = j;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,6 +137,14 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){
|
|||||||
break;
|
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 */
|
/* For all else, just recursively walk the tree */
|
||||||
default: {
|
default: {
|
||||||
if( pExpr->pLeft
|
if( pExpr->pLeft
|
||||||
@@ -385,6 +398,10 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case TK_SELECT: {
|
||||||
|
sqliteVdbeAddOp(v, OP_MemLoad, pExpr->iField, 0, 0, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -24,7 +24,7 @@
|
|||||||
** This file contains C code routines that are called by the parser
|
** This file contains C code routines that are called by the parser
|
||||||
** to handle INSERT statements.
|
** 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"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -101,7 +101,10 @@ void sqliteInsert(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
v = pParse->pVdbe;
|
||||||
|
if( v==0 ){
|
||||||
v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
|
v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
|
||||||
|
}
|
||||||
if( v ){
|
if( v ){
|
||||||
Index *pIdx;
|
Index *pIdx;
|
||||||
sqliteVdbeAddOp(v, OP_Open, 0, 1, pTab->zName, 0);
|
sqliteVdbeAddOp(v, OP_Open, 0, 1, pTab->zName, 0);
|
||||||
|
@@ -26,7 +26,7 @@
|
|||||||
** the parser. Lemon will also generate a header file containing
|
** the parser. Lemon will also generate a header file containing
|
||||||
** numeric codes for all of the tokens.
|
** 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_prefix TK_
|
||||||
%token_type {Token}
|
%token_type {Token}
|
||||||
@@ -133,7 +133,7 @@ cmd ::= DROP TABLE id(X). {sqliteDropTable(pParse,&X);}
|
|||||||
// The select statement
|
// The select statement
|
||||||
//
|
//
|
||||||
cmd ::= select(X). {
|
cmd ::= select(X). {
|
||||||
sqliteSelect(pParse, X, 0, 0);
|
sqliteSelect(pParse, X, -1, -1);
|
||||||
sqliteSelectDelete(X);
|
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) ::= 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) ::= MINUS expr(X). [UMINUS] {A = sqliteExpr(TK_UMINUS, X, 0, 0);}
|
||||||
expr(A) ::= PLUS expr(X). [UMINUS] {A = X;}
|
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*}
|
%type exprlist {ExprList*}
|
||||||
%destructor exprlist {sqliteExprListDelete($$);}
|
%destructor exprlist {sqliteExprListDelete($$);}
|
||||||
|
102
src/select.c
102
src/select.c
@@ -24,7 +24,7 @@
|
|||||||
** This file contains C code routines that are called by the parser
|
** This file contains C code routines that are called by the parser
|
||||||
** to handle SELECT statements.
|
** 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"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -71,11 +71,12 @@ void sqliteSelectDelete(Select *p){
|
|||||||
/*
|
/*
|
||||||
** Generate code for the given SELECT statement.
|
** Generate code for the given SELECT statement.
|
||||||
**
|
**
|
||||||
** If pDest==0 and iMem<0, then the results of the query are sent to
|
** If iDest<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 callback function. If iDest>=0 then the results are written to
|
||||||
** the single table specified. If pDest==0 and iMem>=0 then the result
|
** an open cursor with the index iDest. The calling function is
|
||||||
** should be a single value which is then stored in memory location iMem
|
** responsible for having that cursor open. If iDest<0 and iMem>=0
|
||||||
** of the virtual machine.
|
** 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
|
** This routine returns the number of errors. If any errors are
|
||||||
** encountered, then an appropriate error message is left in
|
** encountered, then an appropriate error message is left in
|
||||||
@@ -87,7 +88,7 @@ void sqliteSelectDelete(Select *p){
|
|||||||
int sqliteSelect(
|
int sqliteSelect(
|
||||||
Parse *pParse, /* The parser context */
|
Parse *pParse, /* The parser context */
|
||||||
Select *p, /* The SELECT statement being coded. */
|
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 iMem /* Save result in this memory location, if >=0 */
|
||||||
){
|
){
|
||||||
int i, j;
|
int i, j;
|
||||||
@@ -98,14 +99,14 @@ int sqliteSelect(
|
|||||||
IdList *pTabList; /* List of tables to select from */
|
IdList *pTabList; /* List of tables to select from */
|
||||||
Expr *pWhere; /* The WHERE clause. May be NULL */
|
Expr *pWhere; /* The WHERE clause. May be NULL */
|
||||||
ExprList *pOrderBy; /* The ORDER BY 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;
|
pEList = p->pEList;
|
||||||
pTabList = p->pSrc;
|
pTabList = p->pSrc;
|
||||||
pWhere = p->pWhere;
|
pWhere = p->pWhere;
|
||||||
pOrderBy = p->pOrderBy;
|
pOrderBy = p->pOrderBy;
|
||||||
distinct = p->isDistinct;
|
isDistinct = p->isDistinct;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Do not even attempt to generate any code if we have already seen
|
** 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
|
/* If the list of fields to retrieve is "*" then replace it with
|
||||||
** a list of all fields from all tables.
|
** a list of all fields from all tables.
|
||||||
*/
|
*/
|
||||||
@@ -133,13 +141,22 @@ int sqliteSelect(
|
|||||||
Table *pTab = pTabList->a[i].pTab;
|
Table *pTab = pTabList->a[i].pTab;
|
||||||
for(j=0; j<pTab->nCol; j++){
|
for(j=0; j<pTab->nCol; j++){
|
||||||
Expr *pExpr = sqliteExpr(TK_FIELD, 0, 0, 0);
|
Expr *pExpr = sqliteExpr(TK_FIELD, 0, 0, 0);
|
||||||
pExpr->iTable = i;
|
pExpr->iTable = i + pParse->nTab;
|
||||||
pExpr->iField = j;
|
pExpr->iField = j;
|
||||||
pEList = sqliteExprListAppend(pEList, pExpr, 0);
|
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.
|
/* Resolve the field names and do a semantics check on all the expressions.
|
||||||
*/
|
*/
|
||||||
for(i=0; i<pEList->nExpr; i++){
|
for(i=0; i<pEList->nExpr; i++){
|
||||||
@@ -180,17 +197,25 @@ int sqliteSelect(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ORDER BY is ignored if this is an aggregate query like count(*)
|
/* ORDER BY is ignored if
|
||||||
|
**
|
||||||
|
** (1) this is an aggregate query like count(*)
|
||||||
** since only one row will be returned.
|
** 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;
|
pOrderBy = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Turn off distinct if this is an aggregate
|
/* Turn off distinct if this is an aggregate or writing to memory.
|
||||||
*/
|
*/
|
||||||
if( isAgg ){
|
if( isAgg || iMem>=0 ){
|
||||||
distinct = 0;
|
isDistinct = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Begin generating code.
|
/* Begin generating code.
|
||||||
@@ -208,8 +233,10 @@ int sqliteSelect(
|
|||||||
sqliteVdbeAddOp(v, OP_SortOpen, 0, 0, 0, 0);
|
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.
|
||||||
*/
|
*/
|
||||||
|
if( iDest<0 && iMem<0 ){
|
||||||
sqliteVdbeAddOp(v, OP_ColumnCount, pEList->nExpr, 0, 0, 0);
|
sqliteVdbeAddOp(v, OP_ColumnCount, pEList->nExpr, 0, 0, 0);
|
||||||
for(i=0; i<pEList->nExpr; i++){
|
for(i=0; i<pEList->nExpr; i++){
|
||||||
Expr *p;
|
Expr *p;
|
||||||
@@ -239,7 +266,9 @@ int sqliteSelect(
|
|||||||
sqliteFree(zName);
|
sqliteFree(zName);
|
||||||
}else{
|
}else{
|
||||||
Table *pTab = pTabList->a[0].pTab;
|
Table *pTab = pTabList->a[0].pTab;
|
||||||
sqliteVdbeAddOp(v, OP_ColumnName, i, 0, pTab->aCol[p->iField].zName, 0);
|
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
|
/* Begin the database scan
|
||||||
*/
|
*/
|
||||||
if( distinct ){
|
if( isDistinct ){
|
||||||
distinct = pTabList->nId*2+1;
|
|
||||||
sqliteVdbeAddOp(v, OP_Open, distinct, 1, 0, 0);
|
sqliteVdbeAddOp(v, OP_Open, distinct, 1, 0, 0);
|
||||||
}
|
}
|
||||||
pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 0);
|
pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 0);
|
||||||
@@ -283,13 +318,13 @@ int sqliteSelect(
|
|||||||
/* If the current result is not distinct, script the remainder
|
/* If the current result is not distinct, script the remainder
|
||||||
** of this processing.
|
** of this processing.
|
||||||
*/
|
*/
|
||||||
if( distinct ){
|
if( isDistinct ){
|
||||||
int isDistinct = sqliteVdbeMakeLabel(v);
|
int lbl = sqliteVdbeMakeLabel(v);
|
||||||
sqliteVdbeAddOp(v, OP_MakeKey, pEList->nExpr, 1, 0, 0);
|
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_Pop, pEList->nExpr+1, 0, 0, 0);
|
||||||
sqliteVdbeAddOp(v, OP_Goto, 0, pWInfo->iContinue, 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);
|
sqliteVdbeAddOp(v, OP_Put, distinct, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,6 +365,14 @@ int sqliteSelect(
|
|||||||
}
|
}
|
||||||
sqliteVdbeAddOp(v, op, p1, 0, 0, 0);
|
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{
|
}else{
|
||||||
sqliteVdbeAddOp(v, OP_Callback, pEList->nExpr, 0, 0, 0);
|
sqliteVdbeAddOp(v, OP_Callback, pEList->nExpr, 0, 0, 0);
|
||||||
}
|
}
|
||||||
@@ -348,14 +391,23 @@ int sqliteSelect(
|
|||||||
addr = sqliteVdbeAddOp(v, OP_SortNext, 0, end, 0, 0);
|
addr = sqliteVdbeAddOp(v, OP_SortNext, 0, end, 0, 0);
|
||||||
sqliteVdbeAddOp(v, OP_SortCallback, pEList->nExpr, 0, 0, 0);
|
sqliteVdbeAddOp(v, OP_SortCallback, pEList->nExpr, 0, 0, 0);
|
||||||
sqliteVdbeAddOp(v, OP_Goto, 0, addr, 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
|
/* If this is an aggregate, then we need to invoke the callback
|
||||||
** exactly once.
|
** exactly once.
|
||||||
*/
|
*/
|
||||||
if( isAgg ){
|
if( isAgg ){
|
||||||
|
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);
|
sqliteVdbeAddOp(v, OP_Callback, pEList->nExpr, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@@ -23,7 +23,7 @@
|
|||||||
*************************************************************************
|
*************************************************************************
|
||||||
** Internal interface definitions for SQLite.
|
** 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 "sqlite.h"
|
||||||
#include "dbbe.h"
|
#include "dbbe.h"
|
||||||
@@ -162,6 +162,7 @@ struct Expr {
|
|||||||
Token token; /* An operand token */
|
Token token; /* An operand token */
|
||||||
int iTable, iField; /* When op==TK_FIELD, then this node means the
|
int iTable, iField; /* When op==TK_FIELD, then this node means the
|
||||||
** iField-th field of the iTable-th table */
|
** 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 *zName; /* Text of the identifier. */
|
||||||
char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */
|
char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */
|
||||||
Table *pTab; /* Table corresponding to zName */
|
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 */
|
} *a; /* One entry for each identifier on the list */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -204,9 +205,11 @@ struct IdList {
|
|||||||
*/
|
*/
|
||||||
struct WhereInfo {
|
struct WhereInfo {
|
||||||
Parse *pParse;
|
Parse *pParse;
|
||||||
IdList *pTabList;
|
IdList *pTabList; /* List of tables in the join */
|
||||||
int iContinue;
|
int iContinue; /* Jump here to continue with next record */
|
||||||
int iBreak;
|
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 explain; /* True if the EXPLAIN flag is found on the query */
|
||||||
int initFlag; /* True if reparsing CREATE TABLEs */
|
int initFlag; /* True if reparsing CREATE TABLEs */
|
||||||
int nErr; /* Number of errors seen */
|
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 sqliteIdListDelete(IdList*);
|
||||||
void sqliteCreateIndex(Parse*, Token*, Token*, IdList*, Token*, Token*);
|
void sqliteCreateIndex(Parse*, Token*, Token*, IdList*, Token*, Token*);
|
||||||
void sqliteDropIndex(Parse*, 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);
|
Select *sqliteSelectNew(ExprList*,IdList*,Expr*,ExprList*,Expr*,ExprList*,int);
|
||||||
void sqliteSelectDelete(Select*);
|
void sqliteSelectDelete(Select*);
|
||||||
void sqliteDeleteFrom(Parse*, Token*, Expr*);
|
void sqliteDeleteFrom(Parse*, Token*, Expr*);
|
||||||
|
109
src/vdbe.c
109
src/vdbe.c
@@ -41,7 +41,7 @@
|
|||||||
** But other routines are also provided to help in building up
|
** But other routines are also provided to help in building up
|
||||||
** a program instruction by instruction.
|
** 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 "sqliteInt.h"
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@@ -96,6 +96,16 @@ struct Stack {
|
|||||||
};
|
};
|
||||||
typedef struct Stack Stack;
|
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
|
** Allowed values for Stack.flags
|
||||||
*/
|
*/
|
||||||
@@ -133,6 +143,8 @@ struct Vdbe {
|
|||||||
char **azField; /* Data for each file field */
|
char **azField; /* Data for each file field */
|
||||||
char *zLine; /* A single line from the input file */
|
char *zLine; /* A single line from the input file */
|
||||||
int nLineAlloc; /* Number of spaces allocated for zLine */
|
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);
|
sqliteFree(p->aTab);
|
||||||
p->aTab = 0;
|
p->aTab = 0;
|
||||||
p->nTable = 0;
|
p->nTable = 0;
|
||||||
|
for(i=0; i<p->nMem; 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; i<p->nList; i++){
|
for(i=0; i<p->nList; i++){
|
||||||
if( p->apList[i] ){
|
if( p->apList[i] ){
|
||||||
sqliteDbbeCloseTempFile(p->pBe, p->apList[i]);
|
sqliteDbbeCloseTempFile(p->pBe, p->apList[i]);
|
||||||
@@ -536,20 +556,21 @@ static char *zOpName[] = { 0,
|
|||||||
"Put", "Distinct", "Delete", "Field",
|
"Put", "Distinct", "Delete", "Field",
|
||||||
"Key", "Rewind", "Next", "Destroy",
|
"Key", "Rewind", "Next", "Destroy",
|
||||||
"Reorganize", "ResetIdx", "NextIdx", "PutIdx",
|
"Reorganize", "ResetIdx", "NextIdx", "PutIdx",
|
||||||
"DeleteIdx", "ListOpen", "ListWrite", "ListRewind",
|
"DeleteIdx", "MemLoad", "MemStore", "ListOpen",
|
||||||
"ListRead", "ListClose", "SortOpen", "SortPut",
|
"ListWrite", "ListRewind", "ListRead", "ListClose",
|
||||||
"SortMakeRec", "SortMakeKey", "Sort", "SortNext",
|
"SortOpen", "SortPut", "SortMakeRec", "SortMakeKey",
|
||||||
"SortKey", "SortCallback", "SortClose", "FileOpen",
|
"Sort", "SortNext", "SortKey", "SortCallback",
|
||||||
"FileRead", "FileField", "FileClose", "MakeRecord",
|
"SortClose", "FileOpen", "FileRead", "FileField",
|
||||||
"MakeKey", "Goto", "If", "Halt",
|
"FileClose", "MakeRecord", "MakeKey", "Goto",
|
||||||
"ColumnCount", "ColumnName", "Callback", "Integer",
|
"If", "Halt", "ColumnCount", "ColumnName",
|
||||||
"String", "Null", "Pop", "Dup",
|
"Callback", "Integer", "String", "Null",
|
||||||
"Pull", "Add", "AddImm", "Subtract",
|
"Pop", "Dup", "Pull", "Add",
|
||||||
"Multiply", "Divide", "Min", "Max",
|
"AddImm", "Subtract", "Multiply", "Divide",
|
||||||
"Like", "Glob", "Eq", "Ne",
|
"Min", "Max", "Like", "Glob",
|
||||||
"Lt", "Le", "Gt", "Ge",
|
"Eq", "Ne", "Lt", "Le",
|
||||||
"IsNull", "NotNull", "Negative", "And",
|
"Gt", "Ge", "IsNull", "NotNull",
|
||||||
"Or", "Not", "Concat", "Noop",
|
"Negative", "And", "Or", "Not",
|
||||||
|
"Concat", "Noop",
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -2402,6 +2423,64 @@ int sqliteVdbeExec(
|
|||||||
break;
|
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( nOld<p->nMem ){
|
||||||
|
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...
|
/* An other opcode is illegal...
|
||||||
*/
|
*/
|
||||||
default: {
|
default: {
|
||||||
|
117
src/vdbe.h
117
src/vdbe.h
@@ -27,7 +27,7 @@
|
|||||||
** or VDBE. The VDBE implements an abstract machine that runs a
|
** or VDBE. The VDBE implements an abstract machine that runs a
|
||||||
** simple program to access and modify the underlying database.
|
** 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_
|
#ifndef _SQLITE_VDBE_H_
|
||||||
#define _SQLITE_VDBE_H_
|
#define _SQLITE_VDBE_H_
|
||||||
@@ -91,70 +91,73 @@ typedef struct VdbeOp VdbeOp;
|
|||||||
#define OP_PutIdx 16
|
#define OP_PutIdx 16
|
||||||
#define OP_DeleteIdx 17
|
#define OP_DeleteIdx 17
|
||||||
|
|
||||||
#define OP_ListOpen 18
|
#define OP_MemLoad 18
|
||||||
#define OP_ListWrite 19
|
#define OP_MemStore 19
|
||||||
#define OP_ListRewind 20
|
|
||||||
#define OP_ListRead 21
|
|
||||||
#define OP_ListClose 22
|
|
||||||
|
|
||||||
#define OP_SortOpen 23
|
#define OP_ListOpen 20
|
||||||
#define OP_SortPut 24
|
#define OP_ListWrite 21
|
||||||
#define OP_SortMakeRec 25
|
#define OP_ListRewind 22
|
||||||
#define OP_SortMakeKey 26
|
#define OP_ListRead 23
|
||||||
#define OP_Sort 27
|
#define OP_ListClose 24
|
||||||
#define OP_SortNext 28
|
|
||||||
#define OP_SortKey 29
|
|
||||||
#define OP_SortCallback 30
|
|
||||||
#define OP_SortClose 31
|
|
||||||
|
|
||||||
#define OP_FileOpen 32
|
#define OP_SortOpen 25
|
||||||
#define OP_FileRead 33
|
#define OP_SortPut 26
|
||||||
#define OP_FileField 34
|
#define OP_SortMakeRec 27
|
||||||
#define OP_FileClose 35
|
#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_FileOpen 34
|
||||||
#define OP_MakeKey 37
|
#define OP_FileRead 35
|
||||||
|
#define OP_FileField 36
|
||||||
|
#define OP_FileClose 37
|
||||||
|
|
||||||
#define OP_Goto 38
|
#define OP_MakeRecord 38
|
||||||
#define OP_If 39
|
#define OP_MakeKey 39
|
||||||
#define OP_Halt 40
|
|
||||||
|
|
||||||
#define OP_ColumnCount 41
|
#define OP_Goto 40
|
||||||
#define OP_ColumnName 42
|
#define OP_If 41
|
||||||
#define OP_Callback 43
|
#define OP_Halt 42
|
||||||
|
|
||||||
#define OP_Integer 44
|
#define OP_ColumnCount 43
|
||||||
#define OP_String 45
|
#define OP_ColumnName 44
|
||||||
#define OP_Null 46
|
#define OP_Callback 45
|
||||||
#define OP_Pop 47
|
|
||||||
#define OP_Dup 48
|
|
||||||
#define OP_Pull 49
|
|
||||||
|
|
||||||
#define OP_Add 50
|
#define OP_Integer 46
|
||||||
#define OP_AddImm 51
|
#define OP_String 47
|
||||||
#define OP_Subtract 52
|
#define OP_Null 48
|
||||||
#define OP_Multiply 53
|
#define OP_Pop 49
|
||||||
#define OP_Divide 54
|
#define OP_Dup 50
|
||||||
#define OP_Min 55
|
#define OP_Pull 51
|
||||||
#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_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
|
** Prototypes for the VDBE interface. See comments on the implementation
|
||||||
|
60
src/where.c
60
src/where.c
@@ -25,7 +25,7 @@
|
|||||||
** the WHERE clause of SQL statements. Also found here are subroutines
|
** the WHERE clause of SQL statements. Also found here are subroutines
|
||||||
** to generate VDBE code to evaluate expressions.
|
** 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"
|
#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
|
** In order for this routine to work, the calling function must have
|
||||||
** previously invoked sqliteExprResolveIds() on the expression. See
|
** previously invoked sqliteExprResolveIds() on the expression. See
|
||||||
** the header comment on that routine for additional information.
|
** 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;
|
unsigned int mask = 0;
|
||||||
if( p==0 ) return 0;
|
if( p==0 ) return 0;
|
||||||
if( p->op==TK_FIELD ){
|
if( p->op==TK_FIELD ){
|
||||||
return 1<<p->iTable;
|
return 1<< (p->iTable - base);
|
||||||
}
|
}
|
||||||
if( p->pRight ){
|
if( p->pRight ){
|
||||||
mask = exprTableUsage(p->pRight);
|
mask = exprTableUsage(base, p->pRight);
|
||||||
}
|
}
|
||||||
if( p->pLeft ){
|
if( p->pLeft ){
|
||||||
mask |= exprTableUsage(p->pLeft);
|
mask |= exprTableUsage(base, p->pLeft);
|
||||||
}
|
}
|
||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
@@ -107,21 +111,25 @@ static int exprTableUsage(Expr *p){
|
|||||||
** "p" field filled in. The job of this routine is to analyze the
|
** "p" field filled in. The job of this routine is to analyze the
|
||||||
** subexpression and populate all the other fields of the ExprInfo
|
** subexpression and populate all the other fields of the ExprInfo
|
||||||
** structure.
|
** 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;
|
Expr *pExpr = pInfo->p;
|
||||||
pInfo->prereqLeft = exprTableUsage(pExpr->pLeft);
|
pInfo->prereqLeft = exprTableUsage(base, pExpr->pLeft);
|
||||||
pInfo->prereqRight = exprTableUsage(pExpr->pRight);
|
pInfo->prereqRight = exprTableUsage(base, pExpr->pRight);
|
||||||
pInfo->indexable = 0;
|
pInfo->indexable = 0;
|
||||||
pInfo->idxLeft = -1;
|
pInfo->idxLeft = -1;
|
||||||
pInfo->idxRight = -1;
|
pInfo->idxRight = -1;
|
||||||
if( pExpr->op==TK_EQ && (pInfo->prereqRight & pInfo->prereqLeft)==0 ){
|
if( pExpr->op==TK_EQ && (pInfo->prereqRight & pInfo->prereqLeft)==0 ){
|
||||||
if( pExpr->pRight->op==TK_FIELD ){
|
if( pExpr->pRight->op==TK_FIELD ){
|
||||||
pInfo->idxRight = pExpr->pRight->iTable;
|
pInfo->idxRight = pExpr->pRight->iTable - base;
|
||||||
pInfo->indexable = 1;
|
pInfo->indexable = 1;
|
||||||
}
|
}
|
||||||
if( pExpr->pLeft->op==TK_FIELD ){
|
if( pExpr->pLeft->op==TK_FIELD ){
|
||||||
pInfo->idxLeft = pExpr->pLeft->iTable;
|
pInfo->idxLeft = pExpr->pLeft->iTable - base;
|
||||||
pInfo->indexable = 1;
|
pInfo->indexable = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -150,6 +158,7 @@ WhereInfo *sqliteWhereBegin(
|
|||||||
int nExpr; /* Number of subexpressions in the WHERE clause */
|
int nExpr; /* Number of subexpressions in the WHERE clause */
|
||||||
int loopMask; /* One bit set for each outer loop */
|
int loopMask; /* One bit set for each outer loop */
|
||||||
int haveKey; /* True if KEY is on the stack */
|
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. */
|
Index *aIdx[32]; /* Index to use on each nested loop. */
|
||||||
ExprInfo aExpr[50]; /* The WHERE clause is divided into these expressions */
|
ExprInfo aExpr[50]; /* The WHERE clause is divided into these expressions */
|
||||||
|
|
||||||
@@ -166,6 +175,7 @@ WhereInfo *sqliteWhereBegin(
|
|||||||
}
|
}
|
||||||
pWInfo->pParse = pParse;
|
pWInfo->pParse = pParse;
|
||||||
pWInfo->pTabList = pTabList;
|
pWInfo->pTabList = pTabList;
|
||||||
|
base = pWInfo->base = pParse->nTab;
|
||||||
|
|
||||||
/* Split the WHERE clause into as many as 32 separate subexpressions
|
/* Split the WHERE clause into as many as 32 separate subexpressions
|
||||||
** where each subexpression is separated by an AND operator. Any additional
|
** where each subexpression is separated by an AND operator. Any additional
|
||||||
@@ -180,7 +190,7 @@ WhereInfo *sqliteWhereBegin(
|
|||||||
/* Analyze all of the subexpressions.
|
/* Analyze all of the subexpressions.
|
||||||
*/
|
*/
|
||||||
for(i=0; i<nExpr; i++){
|
for(i=0; i<nExpr; i++){
|
||||||
exprAnalyze(&aExpr[i]);
|
exprAnalyze(pParse->nTab, &aExpr[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Figure out a good nesting order for the tables. aOrder[0] will
|
/* 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[].
|
/* Open all tables in the pTabList and all indices in aIdx[].
|
||||||
*/
|
*/
|
||||||
for(i=0; i<pTabList->nId; i++){
|
for(i=0; i<pTabList->nId; 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( i<ARRAYSIZE(aIdx) && aIdx[i]!=0 ){
|
if( i<ARRAYSIZE(aIdx) && aIdx[i]!=0 ){
|
||||||
sqliteVdbeAddOp(v, OP_Open, pTabList->nId+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
|
/* 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
|
/* Case 1: There was no usable index. We must do a complete
|
||||||
** scan of the table.
|
** scan of the table.
|
||||||
*/
|
*/
|
||||||
sqliteVdbeAddOp(v, OP_Next, idx, brk, 0, cont);
|
sqliteVdbeAddOp(v, OP_Next, base+idx, brk, 0, cont);
|
||||||
haveKey = 0;
|
haveKey = 0;
|
||||||
}else{
|
}else{
|
||||||
/* Case 2: We do have a usable index in pIdx.
|
/* 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_MakeKey, pIdx->nField, 0, 0, 0);
|
||||||
sqliteVdbeAddOp(v, OP_Fetch, pTabList->nId+i, 0, 0, 0);
|
sqliteVdbeAddOp(v, OP_Fetch, base+pTabList->nId+i, 0, 0, 0);
|
||||||
sqliteVdbeAddOp(v, OP_NextIdx, pTabList->nId+i, brk, 0, cont);
|
sqliteVdbeAddOp(v, OP_NextIdx, base+pTabList->nId+i, brk, 0, cont);
|
||||||
if( i==pTabList->nId-1 && pushKey ){
|
if( i==pTabList->nId-1 && pushKey ){
|
||||||
haveKey = 1;
|
haveKey = 1;
|
||||||
}else{
|
}else{
|
||||||
@@ -327,7 +338,7 @@ WhereInfo *sqliteWhereBegin(
|
|||||||
if( (aExpr[j].prereqRight & loopMask)!=aExpr[j].prereqRight ) continue;
|
if( (aExpr[j].prereqRight & loopMask)!=aExpr[j].prereqRight ) continue;
|
||||||
if( (aExpr[j].prereqLeft & loopMask)!=aExpr[j].prereqLeft ) continue;
|
if( (aExpr[j].prereqLeft & loopMask)!=aExpr[j].prereqLeft ) continue;
|
||||||
if( haveKey ){
|
if( haveKey ){
|
||||||
sqliteVdbeAddOp(v, OP_Fetch, idx, 0, 0, 0);
|
sqliteVdbeAddOp(v, OP_Fetch, base+idx, 0, 0, 0);
|
||||||
haveKey = 0;
|
haveKey = 0;
|
||||||
}
|
}
|
||||||
sqliteExprIfFalse(pParse, aExpr[j].p, cont);
|
sqliteExprIfFalse(pParse, aExpr[j].p, cont);
|
||||||
@@ -348,8 +359,21 @@ WhereInfo *sqliteWhereBegin(
|
|||||||
*/
|
*/
|
||||||
void sqliteWhereEnd(WhereInfo *pWInfo){
|
void sqliteWhereEnd(WhereInfo *pWInfo){
|
||||||
Vdbe *v = pWInfo->pParse->pVdbe;
|
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_Goto, 0, pWInfo->iContinue, 0, 0);
|
||||||
sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, pWInfo->iBreak);
|
for(i=0; i<pWInfo->pTabList->nId; i++){
|
||||||
|
sqliteVdbeAddOp(v, OP_Close, base+i, 0, 0, brk);
|
||||||
|
brk = 0;
|
||||||
|
if( i<ARRAYSIZE(pWInfo->aIdx) && 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);
|
sqliteFree(pWInfo);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -23,7 +23,7 @@
|
|||||||
# This file implements regression tests for SQLite library. The
|
# This file implements regression tests for SQLite library. The
|
||||||
# focus of this file is testing the COPY statement.
|
# 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]
|
set testdir [file dirname $argv0]
|
||||||
source $testdir/tester.tcl
|
source $testdir/tester.tcl
|
||||||
@@ -81,7 +81,6 @@ do_test copy-1.4 {
|
|||||||
execsql {COPY test1 FROM 'data2.txt'}
|
execsql {COPY test1 FROM 'data2.txt'}
|
||||||
execsql {SELECT * FROM test1 ORDER BY one}
|
execsql {SELECT * FROM test1 ORDER BY one}
|
||||||
} {11 22 33}
|
} {11 22 33}
|
||||||
return
|
|
||||||
|
|
||||||
# Test out the USING DELIMITERS clause
|
# Test out the USING DELIMITERS clause
|
||||||
#
|
#
|
||||||
|
@@ -23,7 +23,7 @@
|
|||||||
# This file implements regression tests for SQLite library. The
|
# This file implements regression tests for SQLite library. The
|
||||||
# focus of this file is testing the SELECT statement.
|
# 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]
|
set testdir [file dirname $argv0]
|
||||||
source $testdir/tester.tcl
|
source $testdir/tester.tcl
|
||||||
@@ -71,14 +71,16 @@ do_test select2-1.2 {
|
|||||||
|
|
||||||
# Create a largish table
|
# Create a largish table
|
||||||
#
|
#
|
||||||
execsql {CREATE TABLE tbl2(f1 int, f2 int, f3 int)}
|
do_test select2-2.0 {
|
||||||
set f [open ./testdata1.txt w]
|
execsql {CREATE TABLE tbl2(f1 int, f2 int, f3 int)}
|
||||||
for {set i 1} {$i<=30000} {incr i} {
|
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}]"
|
puts $f "$i\t[expr {$i*2}]\t[expr {$i*3}]"
|
||||||
}
|
}
|
||||||
close $f
|
close $f
|
||||||
execsql {COPY tbl2 FROM './testdata1.txt'}
|
execsql {COPY tbl2 FROM './testdata1.txt'}
|
||||||
file delete -force ./testdata1.txt
|
file delete -force ./testdata1.txt
|
||||||
|
} {}
|
||||||
|
|
||||||
do_test select2-2.1 {
|
do_test select2-2.1 {
|
||||||
execsql {SELECT count(*) FROM tbl2}
|
execsql {SELECT count(*) FROM tbl2}
|
||||||
@@ -91,9 +93,11 @@ do_test select2-3.1 {
|
|||||||
execsql {SELECT f1 FROM tbl2 WHERE f2==1000}
|
execsql {SELECT f1 FROM tbl2 WHERE f2==1000}
|
||||||
} {500}
|
} {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}
|
execsql {SELECT f1 FROM tbl2 WHERE f2==1000}
|
||||||
} {500}
|
} {500}
|
||||||
|
|
||||||
|
90
test/subselect.test
Normal file
90
test/subselect.test
Normal file
@@ -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
|
Reference in New Issue
Block a user