Subquery flattening is implemented and passes all regression tests.
We still need to add addition tests to the suite to further exercise the flattener, however. (CVS 408) FossilOrigin-Name: d5d3e79cc58da5bd315cc1fea1f7cbf46274da16
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
C Bug\sfix\swhen\s-DMEMORY_DEBUG\sis\soff.\s(CVS\s407)
|
||||
D 2002-02-28T04:10:30
|
||||
C Subquery\sflattening\sis\simplemented\sand\spasses\sall\sregression\stests.\nWe\sstill\sneed\sto\sadd\saddition\stests\sto\sthe\ssuite\sto\sfurther\sexercise\nthe\sflattener,\showever.\s(CVS\s408)
|
||||
D 2002-03-02T17:04:08
|
||||
F Makefile.in 50f1b3351df109b5774771350d8c1b8d3640130d
|
||||
F Makefile.template 89e373b2dad0321df00400fa968dc14b61a03296
|
||||
F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
|
||||
@@ -21,27 +21,27 @@ F sqlite.1 2e2bb0529ef468ade9e4322bd609d0695fb9ded9
|
||||
F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6
|
||||
F src/btree.c 495275fe14f3b718cf2f691dce979d4c0e1f8e5d
|
||||
F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3
|
||||
F src/build.c 7ada2426caba70cb1072ba268bedb694b5018065
|
||||
F src/delete.c 950d8f9097361419f1963875f9943344b469cf02
|
||||
F src/expr.c b70bedaffd27ea24c5c2a197a88b07e82dfa4967
|
||||
F src/build.c 2f6d3136e6b824b2b446c54db2d2be5703033203
|
||||
F src/delete.c bf569eeb66dc851966b5681e5154d5fe2aee92c2
|
||||
F src/expr.c 17e3db6f115d60530a55530e3046312196c5eb36
|
||||
F src/func.c 0db438ba17f3394dc5a3ffcd2ee41ca0c8e80b21
|
||||
F src/hash.c cc259475e358baaf299b00a2c7370f2b03dda892
|
||||
F src/hash.h dca065dda89d4575f3176e75e9a3dc0f4b4fb8b9
|
||||
F src/insert.c 164d2d5e943268a8ff0594e1947599e04df0ce11
|
||||
F src/insert.c 4fb3428f591afe106f6e60029a61f87fa0e79920
|
||||
F src/main.c 5651146585ae613e759fcf372ee064e4940c2463
|
||||
F src/md5.c 52f677bfc590e09f71d07d7e327bd59da738d07c
|
||||
F src/os.c f6bc9b7ab530346bb7fef2ed39f2f1f214bc14ea
|
||||
F src/os.h a17596ecc7f38a228b83ecdb661fb03ce44726d6
|
||||
F src/pager.c 9761c79ccb844bf29ffc5cbed4fa1a32e0740147
|
||||
F src/pager.h b28f004e2f5541dc60cc32db01bf80cf4d056283
|
||||
F src/parse.y fc460cda6f475beae963c7f9c737cf13f44f3420
|
||||
F src/parse.y d62960cdee2d2e7821f277d2fe63d823c86602ba
|
||||
F src/printf.c 300a90554345751f26e1fc0c0333b90a66110a1d
|
||||
F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
|
||||
F src/select.c 1851abd794ad81639bb7d8ac4b24c8c70cd0ec17
|
||||
F src/select.c b99de04f6f2414bb21a065150e0748ccd8329519
|
||||
F src/shell.c 9f8249ca5b8f8aad40becd778c151b58c0d6109e
|
||||
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
|
||||
F src/sqlite.h.in a9b5772604265f98f3120573ef29e37b9d917216
|
||||
F src/sqliteInt.h 9cd512d5be2d2838950e5ace7f92e14ab4804e70
|
||||
F src/sqliteInt.h df68f09091ab37d904020ac48ca2ea29e4985442
|
||||
F src/table.c 203a09d5d0009eeeb1f670370d52b4ce163a3b52
|
||||
F src/tclsqlite.c b9cf346e95291cb4c4f1bf5ac1d77db6b8ad023d
|
||||
F src/test1.c 33efd350dca27c52c58c553c04fd3a6a51f13c1f
|
||||
@@ -49,11 +49,11 @@ F src/test2.c d410dbd8a90faa466c3ab694fa0aa57f5a773aa6
|
||||
F src/test3.c 4e52fff8b01f08bd202f7633feda5639b7ba2b5e
|
||||
F src/threadtest.c 81f0598e0f031c1bd506af337fdc1b7e8dff263f
|
||||
F src/tokenize.c 4b5d30590a744b9bb5605a92d1f620ab2e7e75af
|
||||
F src/update.c 18971d265b0341574b7e3f73116e7947ddab0997
|
||||
F src/update.c 943b821901efb796dc82d91653621aeeb48d223b
|
||||
F src/util.c 00a35b421c92ae0d7cfa51bd87f7d4995f464d19
|
||||
F src/vdbe.c 91311e99efe980459a78e8f5b9e5456d772c9e23
|
||||
F src/vdbe.h f9be1f6e9a336c3ff4d14ea7489ee976e07460cc
|
||||
F src/where.c 664be01b0ce9ffaecbde609afbd4d8d3e5ed1585
|
||||
F src/where.c 34d91fd5d822c2663caeb023f72d60df316ebf29
|
||||
F test/all.test 7a8a8a7a579ed2bb4d8976d55402f21eacd58049
|
||||
F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578
|
||||
F test/btree.test bf326f546a666617367a7033fa2c07451bd4f8e1
|
||||
@@ -63,7 +63,7 @@ F test/conflict.test c794c6c8f6e59918107dbab2d201ae454bb47db8
|
||||
F test/copy.test b3cefcb520c64d7e7dfedbab06b4d4c31fa5b99a
|
||||
F test/delete.test c904a62129fe102b314a96111a8417f10249e4d8
|
||||
F test/expr.test c8a495050dcec3f9e68538c3ef466726933302c1
|
||||
F test/func.test 13572d84cb0f5d4dbd9a51bf767eac047e6f9779
|
||||
F test/func.test 4359344586067e79abf4c710c4737d67ed3cf963
|
||||
F test/in.test c09312672e3f0709fa02c8e2e9cd8fb4bd6269aa
|
||||
F test/index.test c8a471243bbf878974b99baf5badd59407237cf3
|
||||
F test/insert.test c36d534a4ab58c2cd452a273e51b2b0dd1ede1f9
|
||||
@@ -82,7 +82,7 @@ F test/printf.test 3cb415073754cb8ff076f26173143c3cd293a9da
|
||||
F test/quick.test 6f023c7a73fc413e6d65b7a1879c79764038dc05
|
||||
F test/quote.test 286db944717afa9a9bf829dd85e59185c65d5435
|
||||
F test/rowid.test 4c55943300cddf73dd0f88d40a268cab14c83274
|
||||
F test/select1.test ec4c20514598ae9b52abbd5e17488c8dbcdc74d1
|
||||
F test/select1.test 572d53f7b28c35a1efdce207f7579523358ba637
|
||||
F test/select2.test ed2c1882857106b85478f54f67000e14966be4c4
|
||||
F test/select3.test 9469c332250a75a0ef1771fb5da62dc04ec77f18
|
||||
F test/select4.test 29a2ffb187f3d8b6ca42a0a6b619e9cabe12e228
|
||||
@@ -112,7 +112,7 @@ F www/arch.fig d5f9752a4dbf242e9cfffffd3f5762b6c63b3bcf
|
||||
F www/arch.png 82ef36db1143828a7abc88b1e308a5f55d4336f4
|
||||
F www/arch.tcl 72a0c80e9054cc7025a50928d28d9c75c02c2b8b
|
||||
F www/c_interface.tcl 63efc40f09e2f0d8fea43d174103248b160fdf0e
|
||||
F www/changes.tcl 0c40569ef5c92af1fb211ea1f679113a44c60f19
|
||||
F www/changes.tcl 18df8bc8dbcefbfd9538ce476d4fcbc193ac3352
|
||||
F www/conflict.tcl 81dd21f9a679e60aae049e9dd8ab53d59570cda2
|
||||
F www/crosscompile.tcl 3622ebbe518927a3854a12de51344673eb2dd060
|
||||
F www/download.tcl a6d75b8b117cd33dcb090bef7e80d7556d28ebe0
|
||||
@@ -127,7 +127,7 @@ F www/speed.tcl 83457b2bf6bb430900bd48ca3dd98264d9a916a5
|
||||
F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279
|
||||
F www/tclsqlite.tcl 829b393d1ab187fd7a5e978631b3429318885c49
|
||||
F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
|
||||
P e9fd9e7b0fed445b48e7024ecde0354fff1478a6
|
||||
R 963d95bca45cb4c9e29d8cc361ba95c2
|
||||
P e14b0c82f3514f41934a7c0d173b6fdb186aafc8
|
||||
R f60c0ec90c7ca3b07c137c446692b495
|
||||
U drh
|
||||
Z bd4f48a03082cea631000ad6c4df51a3
|
||||
Z 28d7598277d139ff75ed8a6bf05f4a82
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
e14b0c82f3514f41934a7c0d173b6fdb186aafc8
|
||||
d5d3e79cc58da5bd315cc1fea1f7cbf46274da16
|
||||
+3
-3
@@ -17,7 +17,7 @@
|
||||
** DROP TABLE
|
||||
** CREATE INDEX
|
||||
** DROP INDEX
|
||||
** creating expressions and ID lists
|
||||
** creating ID lists
|
||||
** COPY
|
||||
** VACUUM
|
||||
** BEGIN TRANSACTION
|
||||
@@ -25,7 +25,7 @@
|
||||
** ROLLBACK
|
||||
** PRAGMA
|
||||
**
|
||||
** $Id: build.c,v 1.80 2002/02/27 01:47:12 drh Exp $
|
||||
** $Id: build.c,v 1.81 2002/03/02 17:04:08 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
@@ -791,7 +791,7 @@ void sqliteEndTable(Parse *pParse, Token *pEnd, Select *pSelect){
|
||||
int op = p->isTemp ? OP_OpenWrAux : OP_OpenWrite;
|
||||
sqliteVdbeAddOp(v, op, 1, 0);
|
||||
pParse->nTab = 2;
|
||||
sqliteSelect(pParse, pSelect, SRT_Table, 1);
|
||||
sqliteSelect(pParse, pSelect, SRT_Table, 1, 0, 0, 0);
|
||||
}
|
||||
sqliteEndWriteOperation(pParse);
|
||||
}
|
||||
|
||||
+5
-5
@@ -12,7 +12,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle DELETE FROM statements.
|
||||
**
|
||||
** $Id: delete.c,v 1.27 2002/02/23 02:32:10 drh Exp $
|
||||
** $Id: delete.c,v 1.28 2002/03/02 17:04:08 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -102,9 +102,9 @@ void sqliteDeleteFrom(
|
||||
|
||||
/* Resolve the column names in all the expressions.
|
||||
*/
|
||||
base = pParse->nTab++;
|
||||
if( pWhere ){
|
||||
sqliteExprResolveInSelect(pParse, pWhere);
|
||||
if( sqliteExprResolveIds(pParse, pTabList, 0, pWhere) ){
|
||||
if( sqliteExprResolveIds(pParse, base, pTabList, 0, pWhere) ){
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
if( sqliteExprCheck(pParse, pWhere, 0, 0) ){
|
||||
@@ -135,6 +135,7 @@ void sqliteDeleteFrom(
|
||||
int endOfLoop = sqliteVdbeMakeLabel(v);
|
||||
int addr;
|
||||
openOp = pTab->isTemp ? OP_OpenAux : OP_Open;
|
||||
assert( base==0 );
|
||||
sqliteVdbeAddOp(v, openOp, 0, pTab->tnum);
|
||||
sqliteVdbeAddOp(v, OP_Rewind, 0, sqliteVdbeCurrentAddr(v)+2);
|
||||
addr = sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
|
||||
@@ -154,7 +155,7 @@ void sqliteDeleteFrom(
|
||||
else{
|
||||
/* Begin the database scan
|
||||
*/
|
||||
pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 1);
|
||||
pWInfo = sqliteWhereBegin(pParse, base, pTabList, pWhere, 1);
|
||||
if( pWInfo==0 ) goto delete_from_cleanup;
|
||||
|
||||
/* Remember the key of every item to be deleted.
|
||||
@@ -172,7 +173,6 @@ void sqliteDeleteFrom(
|
||||
** database scan. We have to delete items after the scan is complete
|
||||
** because deleting an item can change the scan order.
|
||||
*/
|
||||
base = pParse->nTab;
|
||||
sqliteVdbeAddOp(v, OP_ListRewind, 0, 0);
|
||||
openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite;
|
||||
sqliteVdbeAddOp(v, openOp, base, pTab->tnum);
|
||||
|
||||
+19
-46
@@ -12,7 +12,7 @@
|
||||
** This file contains routines used for analyzing expressions and
|
||||
** for generating VDBE code that evaluates expressions in SQLite.
|
||||
**
|
||||
** $Id: expr.c,v 1.51 2002/02/28 03:04:48 drh Exp $
|
||||
** $Id: expr.c,v 1.52 2002/03/02 17:04:08 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -172,6 +172,9 @@ Expr *sqliteExprDup(Expr *p){
|
||||
pNew->pLeft = sqliteExprDup(p->pLeft);
|
||||
pNew->pRight = sqliteExprDup(p->pRight);
|
||||
pNew->pList = sqliteExprListDup(p->pList);
|
||||
pNew->iTable = p->iTable;
|
||||
pNew->iColumn = p->iColumn;
|
||||
pNew->iAgg = p->iAgg;
|
||||
pNew->token = p->token;
|
||||
pNew->span = p->span;
|
||||
pNew->pSelect = sqliteSelectDup(p->pSelect);
|
||||
@@ -310,40 +313,6 @@ int sqliteExprIsConstant(Expr *p){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Walk the expression tree and process operators of the form:
|
||||
**
|
||||
** expr IN (SELECT ...)
|
||||
**
|
||||
** These operators have to be processed before column names are
|
||||
** resolved because each such operator increments pParse->nTab
|
||||
** to reserve cursor numbers for its own use. But pParse->nTab
|
||||
** needs to be constant once we begin resolving column names. For
|
||||
** that reason, this procedure needs to be called on every expression
|
||||
** before sqliteExprResolveIds() is called on any expression.
|
||||
**
|
||||
** Actually, the processing of IN-SELECT is only started by this
|
||||
** routine. This routine allocates a cursor number to the IN-SELECT
|
||||
** and then moves on. The code generation is done by
|
||||
** sqliteExprResolveIds() which must be called afterwards.
|
||||
*/
|
||||
void sqliteExprResolveInSelect(Parse *pParse, Expr *pExpr){
|
||||
if( pExpr==0 ) return;
|
||||
if( pExpr->op==TK_IN && pExpr->pSelect!=0 ){
|
||||
pExpr->iTable = pParse->nTab++;
|
||||
}else{
|
||||
if( pExpr->pLeft ) sqliteExprResolveInSelect(pParse, pExpr->pLeft);
|
||||
if( pExpr->pRight ) sqliteExprResolveInSelect(pParse, pExpr->pRight);
|
||||
if( pExpr->pList ){
|
||||
int i;
|
||||
ExprList *pList = pExpr->pList;
|
||||
for(i=0; i<pList->nExpr; i++){
|
||||
sqliteExprResolveInSelect(pParse, pList->a[i].pExpr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if the given string is a row-id column name.
|
||||
*/
|
||||
@@ -360,7 +329,7 @@ static int sqliteIsRowid(const char *z){
|
||||
** index to the table in the table list and a column offset. The
|
||||
** Expr.opcode for such nodes is changed to TK_COLUMN. The Expr.iTable
|
||||
** value is changed to the index of the referenced table in pTabList
|
||||
** plus the pParse->nTab value. This value will ultimately become the
|
||||
** plus the "base" value. The base value will ultimately become the
|
||||
** VDBE cursor number for a cursor that is pointing into the referenced
|
||||
** table. The Expr.iColumn value is changed to the index of the column
|
||||
** of the referenced table. The Expr.iColumn value for the special
|
||||
@@ -387,11 +356,13 @@ static int sqliteIsRowid(const char *z){
|
||||
*/
|
||||
int sqliteExprResolveIds(
|
||||
Parse *pParse, /* The parser context */
|
||||
int base, /* VDBE cursor number for first entry in pTabList */
|
||||
IdList *pTabList, /* List of tables used to resolve column names */
|
||||
ExprList *pEList, /* List of expressions used to resolve "AS" */
|
||||
Expr *pExpr /* The expression to be analyzed. */
|
||||
){
|
||||
if( pExpr==0 || pTabList==0 ) return 0;
|
||||
assert( base+pTabList->nId<=pParse->nTab );
|
||||
switch( pExpr->op ){
|
||||
/* A lone identifier. Try and match it as follows:
|
||||
**
|
||||
@@ -418,7 +389,7 @@ int sqliteExprResolveIds(
|
||||
for(j=0; j<pTab->nCol; j++){
|
||||
if( sqliteStrICmp(pTab->aCol[j].zName, z)==0 ){
|
||||
cnt++;
|
||||
pExpr->iTable = i + pParse->nTab;
|
||||
pExpr->iTable = i + base;
|
||||
if( j==pTab->iPKey ){
|
||||
/* Substitute the record number for the INTEGER PRIMARY KEY */
|
||||
pExpr->iColumn = -1;
|
||||
@@ -444,7 +415,7 @@ int sqliteExprResolveIds(
|
||||
}
|
||||
if( cnt==0 && sqliteIsRowid(z) ){
|
||||
pExpr->iColumn = -1;
|
||||
pExpr->iTable = pParse->nTab;
|
||||
pExpr->iTable = base;
|
||||
cnt = 1 + (pTabList->nId>1);
|
||||
pExpr->op = TK_COLUMN;
|
||||
}
|
||||
@@ -496,11 +467,11 @@ int sqliteExprResolveIds(
|
||||
zTab = pTab->zName;
|
||||
}
|
||||
if( sqliteStrICmp(zTab, zLeft)!=0 ) continue;
|
||||
if( 0==(cntTab++) ) pExpr->iTable = i + pParse->nTab;
|
||||
if( 0==(cntTab++) ) pExpr->iTable = i + base;
|
||||
for(j=0; j<pTab->nCol; j++){
|
||||
if( sqliteStrICmp(pTab->aCol[j].zName, zRight)==0 ){
|
||||
cnt++;
|
||||
pExpr->iTable = i + pParse->nTab;
|
||||
pExpr->iTable = i + base;
|
||||
if( j==pTab->iPKey ){
|
||||
/* Substitute the record number for the INTEGER PRIMARY KEY */
|
||||
pExpr->iColumn = -1;
|
||||
@@ -540,7 +511,7 @@ int sqliteExprResolveIds(
|
||||
case TK_IN: {
|
||||
Vdbe *v = sqliteGetVdbe(pParse);
|
||||
if( v==0 ) return 1;
|
||||
if( sqliteExprResolveIds(pParse, pTabList, pEList, pExpr->pLeft) ){
|
||||
if( sqliteExprResolveIds(pParse, base, pTabList, pEList, pExpr->pLeft) ){
|
||||
return 1;
|
||||
}
|
||||
if( pExpr->pSelect ){
|
||||
@@ -550,8 +521,9 @@ int sqliteExprResolveIds(
|
||||
** table. The cursor number of the temporary table has already
|
||||
** been put in iTable by sqliteExprResolveInSelect().
|
||||
*/
|
||||
pExpr->iTable = pParse->nTab++;
|
||||
sqliteVdbeAddOp(v, OP_OpenTemp, pExpr->iTable, 1);
|
||||
if( sqliteSelect(pParse, pExpr->pSelect, SRT_Set, pExpr->iTable) );
|
||||
sqliteSelect(pParse, pExpr->pSelect, SRT_Set, pExpr->iTable, 0,0,0);
|
||||
}else if( pExpr->pList ){
|
||||
/* Case 2: expr IN (exprlist)
|
||||
**
|
||||
@@ -601,7 +573,7 @@ int sqliteExprResolveIds(
|
||||
** of the memory cell in iColumn.
|
||||
*/
|
||||
pExpr->iColumn = pParse->nMem++;
|
||||
if( sqliteSelect(pParse, pExpr->pSelect, SRT_Mem, pExpr->iColumn) ){
|
||||
if( sqliteSelect(pParse, pExpr->pSelect, SRT_Mem, pExpr->iColumn,0,0,0) ){
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
@@ -610,18 +582,19 @@ int sqliteExprResolveIds(
|
||||
/* For all else, just recursively walk the tree */
|
||||
default: {
|
||||
if( pExpr->pLeft
|
||||
&& sqliteExprResolveIds(pParse, pTabList, pEList, pExpr->pLeft) ){
|
||||
&& sqliteExprResolveIds(pParse, base, pTabList, pEList, pExpr->pLeft) ){
|
||||
return 1;
|
||||
}
|
||||
if( pExpr->pRight
|
||||
&& sqliteExprResolveIds(pParse, pTabList, pEList, pExpr->pRight) ){
|
||||
&& sqliteExprResolveIds(pParse, base, pTabList, pEList, pExpr->pRight) ){
|
||||
return 1;
|
||||
}
|
||||
if( pExpr->pList ){
|
||||
int i;
|
||||
ExprList *pList = pExpr->pList;
|
||||
for(i=0; i<pList->nExpr; i++){
|
||||
if( sqliteExprResolveIds(pParse,pTabList,pEList,pList->a[i].pExpr) ){
|
||||
Expr *pArg = pList->a[i].pExpr;
|
||||
if( sqliteExprResolveIds(pParse, base, pTabList, pEList, pArg) ){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-6
@@ -12,7 +12,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle INSERT statements in SQLite.
|
||||
**
|
||||
** $Id: insert.c,v 1.45 2002/02/23 02:32:10 drh Exp $
|
||||
** $Id: insert.c,v 1.46 2002/03/02 17:04:08 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -84,7 +84,7 @@ void sqliteInsert(
|
||||
int rc;
|
||||
srcTab = pParse->nTab++;
|
||||
sqliteVdbeAddOp(v, OP_OpenTemp, srcTab, 0);
|
||||
rc = sqliteSelect(pParse, pSelect, SRT_Table, srcTab);
|
||||
rc = sqliteSelect(pParse, pSelect, SRT_Table, srcTab, 0,0,0);
|
||||
if( rc || pParse->nErr || sqlite_malloc_failed ) goto insert_cleanup;
|
||||
assert( pSelect->pEList );
|
||||
nColumn = pSelect->pEList->nExpr;
|
||||
@@ -94,12 +94,9 @@ void sqliteInsert(
|
||||
srcTab = -1;
|
||||
assert( pList );
|
||||
nColumn = pList->nExpr;
|
||||
for(i=0; i<nColumn; i++){
|
||||
sqliteExprResolveInSelect(pParse, pList->a[i].pExpr);
|
||||
}
|
||||
dummy.nId = 0;
|
||||
for(i=0; i<nColumn; i++){
|
||||
if( sqliteExprResolveIds(pParse, &dummy, 0, pList->a[i].pExpr) ){
|
||||
if( sqliteExprResolveIds(pParse, 0, &dummy, 0, pList->a[i].pExpr) ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
}
|
||||
@@ -183,6 +180,7 @@ void sqliteInsert(
|
||||
sqliteVdbeAddOp(v, openOp, idx+base, pIdx->tnum);
|
||||
sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC);
|
||||
}
|
||||
pParse->nTab += idx;
|
||||
|
||||
/* If the data source is a SELECT statement, then we have to create
|
||||
** a loop because there might be multiple rows of data. If the data
|
||||
|
||||
+2
-2
@@ -14,7 +14,7 @@
|
||||
** the parser. Lemon will also generate a header file containing
|
||||
** numeric codes for all of the tokens.
|
||||
**
|
||||
** @(#) $Id: parse.y,v 1.53 2002/02/23 02:32:10 drh Exp $
|
||||
** @(#) $Id: parse.y,v 1.54 2002/03/02 17:04:08 drh Exp $
|
||||
*/
|
||||
%token_prefix TK_
|
||||
%token_type {Token}
|
||||
@@ -202,7 +202,7 @@ cmd ::= DROP VIEW ids(X). {
|
||||
//////////////////////// The SELECT statement /////////////////////////////////
|
||||
//
|
||||
cmd ::= select(X). {
|
||||
sqliteSelect(pParse, X, SRT_Callback, 0);
|
||||
sqliteSelect(pParse, X, SRT_Callback, 0, 0, 0, 0);
|
||||
sqliteSelectDelete(X);
|
||||
}
|
||||
|
||||
|
||||
+225
-101
@@ -12,7 +12,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle SELECT statements in SQLite.
|
||||
**
|
||||
** $Id: select.c,v 1.70 2002/02/28 01:46:13 drh Exp $
|
||||
** $Id: select.c,v 1.71 2002/03/02 17:04:08 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -231,8 +231,12 @@ static void generateSortTail(Vdbe *v, int nColumn){
|
||||
** are in the result and the name for each column. This information
|
||||
** is used to provide "argc" and "azCol[]" values in the callback.
|
||||
*/
|
||||
static
|
||||
void generateColumnNames(Parse *pParse, IdList *pTabList, ExprList *pEList){
|
||||
static void generateColumnNames(
|
||||
Parse *pParse, /* Parser context */
|
||||
int base, /* VDBE cursor corresponding to first entry in pTabList */
|
||||
IdList *pTabList, /* List of tables */
|
||||
ExprList *pEList /* Expressions defining the result set */
|
||||
){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int i;
|
||||
if( pParse->colNamesSet || v==0 || sqlite_malloc_failed ) return;
|
||||
@@ -255,7 +259,7 @@ void generateColumnNames(Parse *pParse, IdList *pTabList, ExprList *pEList){
|
||||
sqliteVdbeChangeP3(v, -1, p->span.z, p->span.n);
|
||||
sqliteVdbeCompressSpace(v, addr);
|
||||
}else if( p->op==TK_COLUMN && pTabList ){
|
||||
Table *pTab = pTabList->a[p->iTable - pParse->nTab].pTab;
|
||||
Table *pTab = pTabList->a[p->iTable - base].pTab;
|
||||
char *zCol;
|
||||
int iCol = p->iColumn;
|
||||
if( iCol<0 ) iCol = pTab->iPKey;
|
||||
@@ -265,7 +269,7 @@ void generateColumnNames(Parse *pParse, IdList *pTabList, ExprList *pEList){
|
||||
char *zName = 0;
|
||||
char *zTab;
|
||||
|
||||
zTab = pTabList->a[p->iTable - pParse->nTab].zAlias;
|
||||
zTab = pTabList->a[p->iTable - base].zAlias;
|
||||
if( showFullNames || zTab==0 ) zTab = pTab->zName;
|
||||
sqliteSetString(&zName, zTab, ".", zCol, 0);
|
||||
sqliteVdbeAddOp(v, OP_ColumnName, i, 0);
|
||||
@@ -640,7 +644,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
|
||||
|
||||
/* Code the SELECT statements to our left
|
||||
*/
|
||||
rc = sqliteSelect(pParse, pPrior, priorOp, unionTab);
|
||||
rc = sqliteSelect(pParse, pPrior, priorOp, unionTab, 0, 0, 0);
|
||||
if( rc ) return rc;
|
||||
|
||||
/* Code the current SELECT statement
|
||||
@@ -651,7 +655,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
|
||||
case TK_ALL: op = SRT_Table; break;
|
||||
}
|
||||
p->pPrior = 0;
|
||||
rc = sqliteSelect(pParse, p, op, unionTab);
|
||||
rc = sqliteSelect(pParse, p, op, unionTab, 0, 0, 0);
|
||||
p->pPrior = pPrior;
|
||||
if( rc ) return rc;
|
||||
|
||||
@@ -661,7 +665,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
|
||||
if( eDest!=priorOp ){
|
||||
int iCont, iBreak, iStart;
|
||||
assert( p->pEList );
|
||||
generateColumnNames(pParse, 0, p->pEList);
|
||||
generateColumnNames(pParse, p->base, 0, p->pEList);
|
||||
iBreak = sqliteVdbeMakeLabel(v);
|
||||
iCont = sqliteVdbeMakeLabel(v);
|
||||
sqliteVdbeAddOp(v, OP_Rewind, unionTab, iBreak);
|
||||
@@ -698,7 +702,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
|
||||
|
||||
/* Code the SELECTs to our left into temporary table "tab1".
|
||||
*/
|
||||
rc = sqliteSelect(pParse, pPrior, SRT_Union, tab1);
|
||||
rc = sqliteSelect(pParse, pPrior, SRT_Union, tab1, 0, 0, 0);
|
||||
if( rc ) return rc;
|
||||
|
||||
/* Code the current SELECT into temporary table "tab2"
|
||||
@@ -706,7 +710,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
|
||||
sqliteVdbeAddOp(v, OP_OpenTemp, tab2, 1);
|
||||
sqliteVdbeAddOp(v, OP_KeyAsData, tab2, 1);
|
||||
p->pPrior = 0;
|
||||
rc = sqliteSelect(pParse, p, SRT_Union, tab2);
|
||||
rc = sqliteSelect(pParse, p, SRT_Union, tab2, 0, 0, 0);
|
||||
p->pPrior = pPrior;
|
||||
if( rc ) return rc;
|
||||
|
||||
@@ -714,7 +718,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
|
||||
** tables.
|
||||
*/
|
||||
assert( p->pEList );
|
||||
generateColumnNames(pParse, 0, p->pEList);
|
||||
generateColumnNames(pParse, p->base, 0, p->pEList);
|
||||
iBreak = sqliteVdbeMakeLabel(v);
|
||||
iCont = sqliteVdbeMakeLabel(v);
|
||||
sqliteVdbeAddOp(v, OP_Rewind, tab1, iBreak);
|
||||
@@ -746,6 +750,74 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Recursively scan through an expression tree. For every reference
|
||||
** to a column in table number iFrom, change that reference to the
|
||||
** same column in table number iTo.
|
||||
*/
|
||||
static void changeTables(Expr *pExpr, int iFrom, int iTo){
|
||||
if( pExpr==0 ) return;
|
||||
if( pExpr->op==TK_COLUMN && pExpr->iTable==iFrom ){
|
||||
pExpr->iTable = iTo;
|
||||
}else{
|
||||
changeTables(pExpr->pLeft, iFrom, iTo);
|
||||
changeTables(pExpr->pRight, iFrom, iTo);
|
||||
if( pExpr->pList ){
|
||||
int i;
|
||||
for(i=0; i<pExpr->pList->nExpr; i++){
|
||||
changeTables(pExpr->pList->a[i].pExpr, iFrom, iTo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Scan through the expression pExpr. Replace every reference to
|
||||
** a column in table number iTable with a copy of the corresponding
|
||||
** entry in pEList. When make a copy of pEList, change references
|
||||
** to columns in table iSub into references to table iTable.
|
||||
**
|
||||
** This routine is part of the flattening procedure. A subquery
|
||||
** whose result set is defined by pEList appears as entry in the
|
||||
** FROM clause of a SELECT such that the VDBE cursor assigned to that
|
||||
** FORM clause entry is iTable. This routine make the necessary
|
||||
** changes to pExpr so that it refers directly to the source table
|
||||
** of the subquery rather the result set of the subquery.
|
||||
*/
|
||||
static void substExpr(Expr *pExpr, int iTable, ExprList *pEList, int iSub){
|
||||
if( pExpr==0 ) return;
|
||||
if( pExpr->op==TK_COLUMN && pExpr->iTable==iTable ){
|
||||
Expr *pNew;
|
||||
assert( pEList!=0 && pExpr->iColumn>=0 && pExpr->iColumn<pEList->nExpr );
|
||||
assert( pExpr->pLeft==0 && pExpr->pRight==0 && pExpr->pList==0 );
|
||||
pNew = pEList->a[pExpr->iColumn].pExpr;
|
||||
assert( pNew!=0 );
|
||||
pExpr->op = pNew->op;
|
||||
pExpr->pLeft = sqliteExprDup(pNew->pLeft);
|
||||
pExpr->pRight = sqliteExprDup(pNew->pRight);
|
||||
pExpr->pList = sqliteExprListDup(pNew->pList);
|
||||
pExpr->iTable = pNew->iTable;
|
||||
pExpr->iColumn = pNew->iColumn;
|
||||
pExpr->iAgg = pNew->iAgg;
|
||||
if( iSub!=iTable ){
|
||||
changeTables(pExpr, iSub, iTable);
|
||||
}
|
||||
}else{
|
||||
static void substExprList(ExprList*,int,ExprList*,int);
|
||||
substExpr(pExpr->pLeft, iTable, pEList, iSub);
|
||||
substExpr(pExpr->pRight, iTable, pEList, iSub);
|
||||
substExprList(pExpr->pList, iTable, pEList, iSub);
|
||||
}
|
||||
}
|
||||
static void
|
||||
substExprList(ExprList *pList, int iTable, ExprList *pEList, int iSub){
|
||||
int i;
|
||||
if( pList==0 ) return;
|
||||
for(i=0; i<pList->nExpr; i++){
|
||||
substExpr(pList->a[i].pExpr, iTable, pEList, iSub);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine attempts to flatten subqueries in order to speed
|
||||
** execution. It returns 1 if it makes changes and 0 if no flattening
|
||||
@@ -761,48 +833,115 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
|
||||
** run the outer query on that temporary table. This requires two
|
||||
** passes over the data. Furthermore, because the temporary table
|
||||
** has no indices, the WHERE clause on the outer query cannot be
|
||||
** optimized using indices.
|
||||
** optimized.
|
||||
**
|
||||
** This routine attempts to write queries such as the above into
|
||||
** This routine attempts to rewrite queries such as the above into
|
||||
** a single flat select, like this:
|
||||
**
|
||||
** SELECT x+y AS a FROM t1 WHERE z<100 AND a>5
|
||||
**
|
||||
** The code generated for this simpification gives the same result
|
||||
** but only has to scan the data once.
|
||||
** but only has to scan the data once. And because indices might
|
||||
** exist on the table t1, a complete scan of the data might be
|
||||
** avoided.
|
||||
**
|
||||
** Generally speaking, flattening is only possible if the subquery
|
||||
** query is a simple query without a GROUP BY clause or the DISTINCT
|
||||
** keyword and the outer query is not a join.
|
||||
** Flattening is only attempted if all of the following are true:
|
||||
**
|
||||
** If flattening is not possible, this routine is a no-op and return 0.
|
||||
** If flattening is possible, this routine rewrites the query into
|
||||
** the simplified form and return 1.
|
||||
** (1) The subquery and the outer query do not both use aggregates.
|
||||
**
|
||||
** All of the expression analysis must occur before this routine runs.
|
||||
** This routine depends on the results of the expression analysis.
|
||||
** (2) The subquery is not an aggregate or the outer query is not a join.
|
||||
**
|
||||
** (3) The subquery is not a join.
|
||||
**
|
||||
** (4) The subquery is not DISTINCT or the outer query is not a join.
|
||||
**
|
||||
** (5) The subquery is not DISTINCT or the outer query does not use
|
||||
** aggregates.
|
||||
**
|
||||
** (6) The subquery does not use aggregates or the outer query is not
|
||||
** DISTINCT.
|
||||
**
|
||||
** In this routine, the "p" parameter is a pointer to the outer query.
|
||||
** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query
|
||||
** uses aggregates and subqueryIsAgg is true if the subquery uses aggregates.
|
||||
**
|
||||
** If flattening is not attempted, this routine is a no-op and return 0.
|
||||
** If flattening is attempted this routine returns 1.
|
||||
**
|
||||
** All of the expression analysis must occur on both the outer query and
|
||||
** the subquery before this routine runs.
|
||||
*/
|
||||
int flattenSubqueries(Select *p){
|
||||
int flattenSubquery(Select *p, int iFrom, int isAgg, int subqueryIsAgg){
|
||||
Select *pSub;
|
||||
if( p->pSrc->nId>1 ){
|
||||
return 0; /* Cannot optimize: The outer query is a join. */
|
||||
}
|
||||
pSub = p->pSrc->a[0].pSelect;
|
||||
if( pSub==0 ){
|
||||
return 0; /* Nothing to optimize: There is no subquery. */
|
||||
}
|
||||
if( pSub->isDistinct ){
|
||||
return 0; /* Subquery contains DISTINCT keyword */
|
||||
}
|
||||
if( pSub->pGroupBy ){
|
||||
return 0; /* Subquery contains a GROUP BY clause */
|
||||
}
|
||||
if( pSub->pPrior ){
|
||||
return 0; /* Subquery is the union of two or more queries */
|
||||
}
|
||||
IdList *pSrc, *pSubSrc;
|
||||
ExprList *pList;
|
||||
int i;
|
||||
int iParent, iSub;
|
||||
Expr *pWhere;
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* Check to see if flattening is permitted. Return 0 if not.
|
||||
*/
|
||||
if( p==0 ) return 0;
|
||||
pSrc = p->pSrc;
|
||||
assert( pSrc && iFrom>=0 && iFrom<pSrc->nId );
|
||||
pSub = pSrc->a[iFrom].pSelect;
|
||||
assert( pSub!=0 );
|
||||
if( isAgg && subqueryIsAgg ) return 0;
|
||||
if( subqueryIsAgg && pSrc->nId>1 ) return 0;
|
||||
pSubSrc = pSub->pSrc;
|
||||
assert( pSubSrc );
|
||||
if( pSubSrc->nId>1 ) return 0;
|
||||
if( pSub->isDistinct && pSrc->nId>1 ) return 0;
|
||||
if( pSub->isDistinct && isAgg ) return 0;
|
||||
if( p->isDistinct && subqueryIsAgg ) return 0;
|
||||
|
||||
/* If we reach this point, it means flatting is permitted for the
|
||||
** i-th entry of the FROM clause in the outer query.
|
||||
*/
|
||||
iParent = p->base + iFrom;
|
||||
iSub = pSub->base;
|
||||
substExprList(p->pEList, iParent, pSub->pEList, iSub);
|
||||
pList = p->pEList;
|
||||
for(i=0; i<pList->nExpr; i++){
|
||||
if( pList->a[i].zName==0 ){
|
||||
Expr *pExpr = pList->a[i].pExpr;
|
||||
pList->a[i].zName = sqliteStrNDup(pExpr->span.z, pExpr->span.n);
|
||||
}
|
||||
}
|
||||
substExprList(p->pGroupBy, iParent, pSub->pEList, iSub);
|
||||
substExpr(p->pHaving, iParent, pSub->pEList, iSub);
|
||||
substExprList(p->pOrderBy, iParent, pSub->pEList, iSub);
|
||||
if( pSub->pWhere ){
|
||||
pWhere = sqliteExprDup(pSub->pWhere);
|
||||
if( iParent!=iSub ){
|
||||
changeTables(pWhere, iSub, iParent);
|
||||
}
|
||||
}else{
|
||||
pWhere = 0;
|
||||
}
|
||||
if( subqueryIsAgg ){
|
||||
assert( p->pHaving==0 );
|
||||
p->pHaving = pWhere;
|
||||
substExpr(p->pHaving, iParent, pSub->pEList, iSub);
|
||||
}else if( p->pWhere==0 ){
|
||||
p->pWhere = pWhere;
|
||||
}else{
|
||||
substExpr(p->pWhere, iParent, pSub->pEList, iSub);
|
||||
if( pWhere ){
|
||||
p->pWhere = sqliteExpr(TK_AND, p->pWhere, pWhere, 0);
|
||||
}
|
||||
}
|
||||
p->isDistinct = p->isDistinct || pSub->isDistinct;
|
||||
if( pSrc->a[iFrom].pTab && pSrc->a[iFrom].pTab->isTransient ){
|
||||
sqliteDeleteTable(0, pSrc->a[iFrom].pTab);
|
||||
}
|
||||
pSrc->a[iFrom].pTab = pSubSrc->a[0].pTab;
|
||||
pSubSrc->a[0].pTab = 0;
|
||||
pSrc->a[iFrom].pSelect = pSubSrc->a[0].pSelect;
|
||||
pSubSrc->a[0].pSelect = 0;
|
||||
sqliteSelectDelete(pSub);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Analyze the SELECT statement passed in as an argument to see if it
|
||||
@@ -881,7 +1020,7 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
|
||||
v = sqliteGetVdbe(pParse);
|
||||
if( v==0 ) return 0;
|
||||
if( eDest==SRT_Callback ){
|
||||
generateColumnNames(pParse, p->pSrc, p->pEList);
|
||||
generateColumnNames(pParse, p->base, p->pSrc, p->pEList);
|
||||
}
|
||||
|
||||
/* Generating code to find the min or the max. Basically all we have
|
||||
@@ -894,7 +1033,7 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
|
||||
pParse->schemaVerified = 1;
|
||||
}
|
||||
openOp = pTab->isTemp ? OP_OpenAux : OP_Open;
|
||||
base = pParse->nTab;
|
||||
base = p->base;
|
||||
sqliteVdbeAddOp(v, openOp, base, pTab->tnum);
|
||||
sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
|
||||
if( pIdx==0 ){
|
||||
@@ -949,7 +1088,10 @@ int sqliteSelect(
|
||||
Parse *pParse, /* The parser context */
|
||||
Select *p, /* The SELECT statement being coded. */
|
||||
int eDest, /* One of: SRT_Callback Mem Set Union Except */
|
||||
int iParm /* Save result in this memory location, if >=0 */
|
||||
int iParm, /* Save result in this memory location, if >=0 */
|
||||
Select *pParent, /* Another SELECT for which this is a sub-query */
|
||||
int parentTab, /* Index in pParent->pSrc of this query */
|
||||
int parentAgg /* True if pParent uses aggregate functions */
|
||||
){
|
||||
int i;
|
||||
WhereInfo *pWInfo;
|
||||
@@ -983,10 +1125,12 @@ int sqliteSelect(
|
||||
pHaving = p->pHaving;
|
||||
isDistinct = p->isDistinct;
|
||||
|
||||
/* Save the current value of pParse->nTab. Restore this value before
|
||||
** we exit.
|
||||
/* Allocate a block of VDBE cursors, one for each table in the FROM clause.
|
||||
** The WHERE processing requires that the cursors for the tables in the
|
||||
** FROM clause be consecutive.
|
||||
*/
|
||||
base = pParse->nTab;
|
||||
base = p->base = pParse->nTab;
|
||||
pParse->nTab += pTabList->nId;
|
||||
|
||||
/*
|
||||
** Do not even attempt to generate any code if we have already seen
|
||||
@@ -1004,16 +1148,6 @@ int sqliteSelect(
|
||||
pEList = p->pEList;
|
||||
if( pEList==0 ) goto select_end;
|
||||
|
||||
/* Allocate a temporary table to use for the DISTINCT set, if
|
||||
** necessary. This must be done early to allocate the cursor before
|
||||
** any calls to sqliteExprResolveIds().
|
||||
*/
|
||||
if( isDistinct ){
|
||||
distinct = pParse->nTab++;
|
||||
}else{
|
||||
distinct = -1;
|
||||
}
|
||||
|
||||
/* If writing to memory or generating a set
|
||||
** only a single column may be output.
|
||||
*/
|
||||
@@ -1027,35 +1161,17 @@ int sqliteSelect(
|
||||
/* ORDER BY is ignored if we are not sending the result to a callback.
|
||||
*/
|
||||
if( eDest!=SRT_Callback ){
|
||||
pOrderBy = 0;
|
||||
sqliteExprListDelete(p->pOrderBy);
|
||||
pOrderBy = p->pOrderBy = 0;
|
||||
}
|
||||
|
||||
/* Allocate cursors for "expr IN (SELECT ...)" constructs.
|
||||
*/
|
||||
for(i=0; i<pEList->nExpr; i++){
|
||||
sqliteExprResolveInSelect(pParse, pEList->a[i].pExpr);
|
||||
}
|
||||
if( pWhere ) sqliteExprResolveInSelect(pParse, pWhere);
|
||||
if( pOrderBy ){
|
||||
for(i=0; i<pOrderBy->nExpr; i++){
|
||||
sqliteExprResolveInSelect(pParse, pOrderBy->a[i].pExpr);
|
||||
}
|
||||
}
|
||||
if( pGroupBy ){
|
||||
for(i=0; i<pGroupBy->nExpr; i++){
|
||||
sqliteExprResolveInSelect(pParse, pGroupBy->a[i].pExpr);
|
||||
}
|
||||
}
|
||||
if( pHaving ) sqliteExprResolveInSelect(pParse, pHaving);
|
||||
|
||||
/* At this point, we should have allocated all the cursors that we
|
||||
** need to handle subquerys and temporary tables. From here on we
|
||||
** are committed to keeping the same value for pParse->nTab.
|
||||
** need to handle subquerys and temporary tables.
|
||||
**
|
||||
** Resolve the column names and do a semantics check on all the expressions.
|
||||
*/
|
||||
for(i=0; i<pEList->nExpr; i++){
|
||||
if( sqliteExprResolveIds(pParse, pTabList, 0, pEList->a[i].pExpr) ){
|
||||
if( sqliteExprResolveIds(pParse, base, pTabList, 0, pEList->a[i].pExpr) ){
|
||||
goto select_end;
|
||||
}
|
||||
if( sqliteExprCheck(pParse, pEList->a[i].pExpr, 1, &isAgg) ){
|
||||
@@ -1063,7 +1179,7 @@ int sqliteSelect(
|
||||
}
|
||||
}
|
||||
if( pWhere ){
|
||||
if( sqliteExprResolveIds(pParse, pTabList, pEList, pWhere) ){
|
||||
if( sqliteExprResolveIds(pParse, base, pTabList, pEList, pWhere) ){
|
||||
goto select_end;
|
||||
}
|
||||
if( sqliteExprCheck(pParse, pWhere, 0, 0) ){
|
||||
@@ -1079,7 +1195,7 @@ int sqliteSelect(
|
||||
pParse->nErr++;
|
||||
goto select_end;
|
||||
}
|
||||
if( sqliteExprResolveIds(pParse, pTabList, pEList, pE) ){
|
||||
if( sqliteExprResolveIds(pParse, base, pTabList, pEList, pE) ){
|
||||
goto select_end;
|
||||
}
|
||||
if( sqliteExprCheck(pParse, pE, isAgg, 0) ){
|
||||
@@ -1096,7 +1212,7 @@ int sqliteSelect(
|
||||
pParse->nErr++;
|
||||
goto select_end;
|
||||
}
|
||||
if( sqliteExprResolveIds(pParse, pTabList, pEList, pE) ){
|
||||
if( sqliteExprResolveIds(pParse, base, pTabList, pEList, pE) ){
|
||||
goto select_end;
|
||||
}
|
||||
if( sqliteExprCheck(pParse, pE, isAgg, 0) ){
|
||||
@@ -1111,7 +1227,7 @@ int sqliteSelect(
|
||||
pParse->nErr++;
|
||||
goto select_end;
|
||||
}
|
||||
if( sqliteExprResolveIds(pParse, pTabList, pEList, pHaving) ){
|
||||
if( sqliteExprResolveIds(pParse, base, pTabList, pEList, pHaving) ){
|
||||
goto select_end;
|
||||
}
|
||||
if( sqliteExprCheck(pParse, pHaving, isAgg, 0) ){
|
||||
@@ -1119,14 +1235,6 @@ int sqliteSelect(
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to merge subqueries in the FROM clause into the main
|
||||
** query.
|
||||
*/
|
||||
if( flattenSubqueries(p) ){
|
||||
pEList = p->pEList;
|
||||
pWhere = p->pWhere;
|
||||
}
|
||||
|
||||
/* Check for the special case of a min() or max() function by itself
|
||||
** in the result set.
|
||||
*/
|
||||
@@ -1143,15 +1251,25 @@ int sqliteSelect(
|
||||
/* Generate code for all sub-queries in the FROM clause
|
||||
*/
|
||||
for(i=0; i<pTabList->nId; i++){
|
||||
int oldNTab;
|
||||
if( pTabList->a[i].pSelect==0 ) continue;
|
||||
oldNTab = pParse->nTab;
|
||||
pParse->nTab += i+1;
|
||||
sqliteVdbeAddOp(v, OP_OpenTemp, oldNTab+i, 0);
|
||||
sqliteSelect(pParse, pTabList->a[i].pSelect, SRT_Table, oldNTab+i);
|
||||
pParse->nTab = oldNTab;
|
||||
sqliteVdbeAddOp(v, OP_OpenTemp, base+i, 0);
|
||||
sqliteSelect(pParse, pTabList->a[i].pSelect, SRT_Table, base+i,
|
||||
p, i, isAgg);
|
||||
}
|
||||
|
||||
/* Check to see if this is a subquery that can be "flattened" into its parent.
|
||||
** If flattening is a possiblity, do so and return immediately.
|
||||
*/
|
||||
if( flattenSubquery(pParent, parentTab, parentAgg, isAgg) ){
|
||||
return rc;
|
||||
}
|
||||
pTabList = p->pSrc;
|
||||
pWhere = p->pWhere;
|
||||
pOrderBy = p->pOrderBy;
|
||||
pGroupBy = p->pGroupBy;
|
||||
pHaving = p->pHaving;
|
||||
isDistinct = p->isDistinct;
|
||||
|
||||
/* Do an analysis of aggregate expressions.
|
||||
*/
|
||||
sqliteAggregateInfoReset(pParse);
|
||||
@@ -1195,7 +1313,7 @@ int sqliteSelect(
|
||||
** step is skipped if the output is going to a table or a memory cell.
|
||||
*/
|
||||
if( eDest==SRT_Callback ){
|
||||
generateColumnNames(pParse, pTabList, pEList);
|
||||
generateColumnNames(pParse, p->base, pTabList, pEList);
|
||||
}
|
||||
|
||||
/* Reset the aggregator
|
||||
@@ -1222,12 +1340,18 @@ int sqliteSelect(
|
||||
sqliteVdbeAddOp(v, OP_MemStore, iParm, 1);
|
||||
}
|
||||
|
||||
/* Begin the database scan
|
||||
/* Open a temporary table to use for the distinct set.
|
||||
*/
|
||||
if( isDistinct ){
|
||||
distinct = pParse->nTab++;
|
||||
sqliteVdbeAddOp(v, OP_OpenTemp, distinct, 1);
|
||||
}else{
|
||||
distinct = -1;
|
||||
}
|
||||
pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 0);
|
||||
|
||||
/* Begin the database scan
|
||||
*/
|
||||
pWInfo = sqliteWhereBegin(pParse, p->base, pTabList, pWhere, 0);
|
||||
if( pWInfo==0 ) goto select_end;
|
||||
|
||||
/* Use the standard inner loop if we are not dealing with
|
||||
@@ -1309,7 +1433,6 @@ int sqliteSelect(
|
||||
if( pOrderBy ){
|
||||
generateSortTail(v, pEList->nExpr);
|
||||
}
|
||||
pParse->nTab = base;
|
||||
|
||||
|
||||
/* Issue a null callback if that is what the user wants.
|
||||
@@ -1327,6 +1450,7 @@ int sqliteSelect(
|
||||
** successful coding of the SELECT.
|
||||
*/
|
||||
select_end:
|
||||
pParse->nTab = base;
|
||||
sqliteAggregateInfoReset(pParse);
|
||||
return rc;
|
||||
}
|
||||
|
||||
+8
-6
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** Internal interface definitions for SQLite.
|
||||
**
|
||||
** @(#) $Id: sqliteInt.h,v 1.96 2002/02/28 00:41:11 drh Exp $
|
||||
** @(#) $Id: sqliteInt.h,v 1.97 2002/03/02 17:04:08 drh Exp $
|
||||
*/
|
||||
#include "sqlite.h"
|
||||
#include "hash.h"
|
||||
@@ -434,6 +434,8 @@ struct WhereInfo {
|
||||
int iBreak; /* Jump here to break out of the loop */
|
||||
int base; /* Index of first Open opcode */
|
||||
int nLevel; /* Number of nested loop */
|
||||
int savedNTab; /* Value of pParse->nTab before WhereBegin() */
|
||||
int peakNTab; /* Value of pParse->nTab after WhereBegin() */
|
||||
WhereLevel a[1]; /* Information about each nest loop in the WHERE */
|
||||
};
|
||||
|
||||
@@ -461,6 +463,7 @@ struct Select {
|
||||
Select *pPrior; /* Prior select in a compound select statement */
|
||||
int nLimit, nOffset; /* LIMIT and OFFSET values. -1 means not used */
|
||||
char *zSelect; /* Complete text of the SELECT command */
|
||||
int base; /* Index of VDBE cursor for left-most FROM table */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -521,7 +524,7 @@ struct Parse {
|
||||
int nameClash; /* A permanent table name clashes with temp table name */
|
||||
int newTnum; /* Table number to use when reparsing CREATE TABLEs */
|
||||
int nErr; /* Number of errors seen */
|
||||
int nTab; /* Number of previously allocated cursors */
|
||||
int nTab; /* Number of previously allocated VDBE cursors */
|
||||
int nMem; /* Number of memory cells used so far */
|
||||
int nSet; /* Number of sets used so far */
|
||||
int nAgg; /* Number of aggregate expressions */
|
||||
@@ -586,7 +589,7 @@ void sqliteIdListAddAlias(IdList*, Token*);
|
||||
void sqliteIdListDelete(IdList*);
|
||||
void sqliteCreateIndex(Parse*, Token*, Token*, IdList*, int, Token*, Token*);
|
||||
void sqliteDropIndex(Parse*, Token*);
|
||||
int sqliteSelect(Parse*, Select*, int, int);
|
||||
int sqliteSelect(Parse*, Select*, int, int, Select*, int, int);
|
||||
Select *sqliteSelectNew(ExprList*,IdList*,Expr*,ExprList*,Expr*,ExprList*,
|
||||
int,int,int);
|
||||
void sqliteSelectDelete(Select*);
|
||||
@@ -595,7 +598,7 @@ Table *sqliteTableNameToTable(Parse*, const char*);
|
||||
IdList *sqliteTableTokenToIdList(Parse*, Token*);
|
||||
void sqliteDeleteFrom(Parse*, Token*, Expr*);
|
||||
void sqliteUpdate(Parse*, Token*, ExprList*, Expr*, int);
|
||||
WhereInfo *sqliteWhereBegin(Parse*, IdList*, Expr*, int);
|
||||
WhereInfo *sqliteWhereBegin(Parse*, int, IdList*, Expr*, int);
|
||||
void sqliteWhereEnd(WhereInfo*);
|
||||
void sqliteExprCode(Parse*, Expr*);
|
||||
void sqliteExprIfTrue(Parse*, Expr*, int);
|
||||
@@ -611,8 +614,7 @@ char *sqliteTableNameFromToken(Token*);
|
||||
int sqliteExprCheck(Parse*, Expr*, int, int*);
|
||||
int sqliteExprCompare(Expr*, Expr*);
|
||||
int sqliteFuncId(Token*);
|
||||
int sqliteExprResolveIds(Parse*, IdList*, ExprList*, Expr*);
|
||||
void sqliteExprResolveInSelect(Parse*, Expr*);
|
||||
int sqliteExprResolveIds(Parse*, int, IdList*, ExprList*, Expr*);
|
||||
int sqliteExprAnalyzeAggregates(Parse*, Expr*);
|
||||
Vdbe *sqliteGetVdbe(Parse*);
|
||||
int sqliteRandomByte(void);
|
||||
|
||||
+7
-11
@@ -12,7 +12,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle UPDATE statements.
|
||||
**
|
||||
** $Id: update.c,v 1.34 2002/02/23 02:32:10 drh Exp $
|
||||
** $Id: update.c,v 1.35 2002/03/02 17:04:09 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -66,14 +66,9 @@ void sqliteUpdate(
|
||||
** WHERE clause and in the new values. Also find the column index
|
||||
** for each column to be updated in the pChanges array.
|
||||
*/
|
||||
base = pParse->nTab++;
|
||||
if( pWhere ){
|
||||
sqliteExprResolveInSelect(pParse, pWhere);
|
||||
}
|
||||
for(i=0; i<pChanges->nExpr; i++){
|
||||
sqliteExprResolveInSelect(pParse, pChanges->a[i].pExpr);
|
||||
}
|
||||
if( pWhere ){
|
||||
if( sqliteExprResolveIds(pParse, pTabList, 0, pWhere) ){
|
||||
if( sqliteExprResolveIds(pParse, base, pTabList, 0, pWhere) ){
|
||||
goto update_cleanup;
|
||||
}
|
||||
if( sqliteExprCheck(pParse, pWhere, 0, 0) ){
|
||||
@@ -82,7 +77,7 @@ void sqliteUpdate(
|
||||
}
|
||||
chngRecno = 0;
|
||||
for(i=0; i<pChanges->nExpr; i++){
|
||||
if( sqliteExprResolveIds(pParse, pTabList, 0, pChanges->a[i].pExpr) ){
|
||||
if( sqliteExprResolveIds(pParse, base, pTabList, 0, pChanges->a[i].pExpr) ){
|
||||
goto update_cleanup;
|
||||
}
|
||||
if( sqliteExprCheck(pParse, pChanges->a[i].pExpr, 0, 0) ){
|
||||
@@ -150,7 +145,7 @@ void sqliteUpdate(
|
||||
|
||||
/* Begin the database scan
|
||||
*/
|
||||
pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 1);
|
||||
pWInfo = sqliteWhereBegin(pParse, base, pTabList, pWhere, 1);
|
||||
if( pWInfo==0 ) goto update_cleanup;
|
||||
|
||||
/* Remember the index of every item to be updated.
|
||||
@@ -174,7 +169,6 @@ void sqliteUpdate(
|
||||
** to be deleting some records.
|
||||
*/
|
||||
sqliteVdbeAddOp(v, OP_ListRewind, 0, 0);
|
||||
base = pParse->nTab;
|
||||
openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite;
|
||||
sqliteVdbeAddOp(v, openOp, base, pTab->tnum);
|
||||
if( onError==OE_Replace ){
|
||||
@@ -191,7 +185,9 @@ void sqliteUpdate(
|
||||
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
|
||||
if( openAll || aIdxUsed[i] ){
|
||||
sqliteVdbeAddOp(v, openOp, base+i+1, pIdx->tnum);
|
||||
assert( pParse->nTab==base+i+1 );
|
||||
}
|
||||
pParse->nTab++;
|
||||
}
|
||||
|
||||
/* Loop over every record that needs updating. We have to load
|
||||
|
||||
+11
-12
@@ -13,7 +13,7 @@
|
||||
** the WHERE clause of SQL statements. Also found here are subroutines
|
||||
** to generate VDBE code to evaluate expressions.
|
||||
**
|
||||
** $Id: where.c,v 1.37 2002/02/23 02:32:10 drh Exp $
|
||||
** $Id: where.c,v 1.38 2002/03/02 17:04:09 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -76,8 +76,7 @@ static int exprSplit(int nSlot, ExprInfo *aSlot, Expr *pExpr){
|
||||
** 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.
|
||||
** corresponds to the first entry in the table list.
|
||||
*/
|
||||
static int exprTableUsage(int base, Expr *p){
|
||||
unsigned int mask = 0;
|
||||
@@ -119,8 +118,7 @@ static int allowedOp(int op){
|
||||
** structure.
|
||||
**
|
||||
** "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.
|
||||
** corresponds to the first entry in the table list.
|
||||
*/
|
||||
static void exprAnalyze(int base, ExprInfo *pInfo){
|
||||
Expr *pExpr = pInfo->p;
|
||||
@@ -152,6 +150,7 @@ static void exprAnalyze(int base, ExprInfo *pInfo){
|
||||
*/
|
||||
WhereInfo *sqliteWhereBegin(
|
||||
Parse *pParse, /* The parser context */
|
||||
int base, /* VDBE cursor index for left-most table in pTabList */
|
||||
IdList *pTabList, /* A list of all tables */
|
||||
Expr *pWhere, /* The WHERE clause */
|
||||
int pushKey /* If TRUE, leave the table key on the stack */
|
||||
@@ -164,8 +163,6 @@ 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 */
|
||||
int nCur; /* Next unused cursor number */
|
||||
int aDirect[32]; /* If TRUE, then index this table using ROWID */
|
||||
int iDirectEq[32]; /* Term of the form ROWID==X for the N-th table */
|
||||
int iDirectLt[32]; /* Term of the form ROWID<X or ROWID<=X */
|
||||
@@ -186,9 +183,8 @@ WhereInfo *sqliteWhereBegin(
|
||||
}
|
||||
pWInfo->pParse = pParse;
|
||||
pWInfo->pTabList = pTabList;
|
||||
base = pWInfo->base = pParse->nTab;
|
||||
nCur = base + pTabList->nId;
|
||||
pParse->nTab += nCur*2;
|
||||
pWInfo->base = base;
|
||||
pWInfo->peakNTab = pWInfo->savedNTab = pParse->nTab;
|
||||
|
||||
/* Split the WHERE clause into as many as 32 separate subexpressions
|
||||
** where each subexpression is separated by an AND operator. Any additional
|
||||
@@ -388,7 +384,8 @@ WhereInfo *sqliteWhereBegin(
|
||||
pWInfo->a[i].score = bestScore;
|
||||
loopMask |= 1<<idx;
|
||||
if( pBestIdx ){
|
||||
pWInfo->a[i].iCur = nCur++;
|
||||
pWInfo->a[i].iCur = pParse->nTab++;
|
||||
pWInfo->peakNTab = pParse->nTab;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -794,7 +791,9 @@ void sqliteWhereEnd(WhereInfo *pWInfo){
|
||||
sqliteVdbeAddOp(v, OP_Close, pLevel->iCur, 0);
|
||||
}
|
||||
}
|
||||
pWInfo->pParse->nTab = base;
|
||||
if( pWInfo->pParse->nTab==pWInfo->peakNTab ){
|
||||
pWInfo->pParse->nTab = pWInfo->savedNTab;
|
||||
}
|
||||
sqliteFree(pWInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
+68
-1
@@ -11,7 +11,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing built-in functions.
|
||||
#
|
||||
# $Id: func.test,v 1.8 2002/02/28 03:04:48 drh Exp $
|
||||
# $Id: func.test,v 1.9 2002/03/02 17:04:09 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -25,6 +25,17 @@ do_test func-0.0 {
|
||||
}
|
||||
execsql {SELECT t1 FROM tbl1 ORDER BY t1}
|
||||
} {free is program software this}
|
||||
do_test func-0.1 {
|
||||
execsql {
|
||||
CREATE TABLE t2(a);
|
||||
INSERT INTO t2 VALUES(1);
|
||||
INSERT INTO t2 VALUES(NULL);
|
||||
INSERT INTO t2 VALUES(345);
|
||||
INSERT INTO t2 VALUES(NULL);
|
||||
INSERT INTO t2 VALUES(67890);
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {1 {} 345 {} 67890}
|
||||
|
||||
# Check out the length() function
|
||||
#
|
||||
@@ -43,6 +54,9 @@ do_test func-1.3 {
|
||||
execsql {SELECT length(t1), count(*) FROM tbl1 GROUP BY length(t1)
|
||||
ORDER BY length(t1)}
|
||||
} {2 1 4 2 7 1 8 1}
|
||||
do_test func-1.4 {
|
||||
execsql {SELECT length(a) FROM t2}
|
||||
} {1 0 3 0 5}
|
||||
|
||||
# Check out the substr() function
|
||||
#
|
||||
@@ -73,6 +87,12 @@ do_test func-2.7 {
|
||||
do_test func-2.8 {
|
||||
execsql {SELECT t1 FROM tbl1 ORDER BY substr(t1,2,20)}
|
||||
} {this software free program is}
|
||||
do_test func-2.9 {
|
||||
execsql {SELECT substr(a,1,1) FROM t2}
|
||||
} {1 {} 3 {} 6}
|
||||
do_test func-2.10 {
|
||||
execsql {SELECT substr(a,2,2) FROM t2}
|
||||
} {{} {} 45 {} 78}
|
||||
|
||||
# Only do the following tests if TCL has UTF-8 capabilities and
|
||||
# the UTF-8 encoding is turned on in the SQLite library.
|
||||
@@ -118,6 +138,13 @@ do_test func-3.9 {
|
||||
do_test func-3.10 {
|
||||
execsql {SELECT substr(t1,-4,3) FROM tbl1 ORDER BY t1}
|
||||
} "ter ain i\u1234h TF-"
|
||||
do_test func-3.99 {
|
||||
execsql {DELETE FROM tbl1}
|
||||
foreach word {this program is free software} {
|
||||
execsql "INSERT INTO tbl1 VALUES('$word')"
|
||||
}
|
||||
execsql {SELECT t1 FROM tbl1}
|
||||
} {this program is free software}
|
||||
|
||||
} ;# End [sqlite -encoding]==UTF-8 and \u1234!=u1234
|
||||
|
||||
@@ -141,6 +168,12 @@ do_test func-4.3 {
|
||||
do_test func-4.4 {
|
||||
catchsql {SELECT abs(c) FROM t1 ORDER BY a}
|
||||
} {0 {3 12345.67890 5}}
|
||||
do_test func-4.4.1 {
|
||||
execsql {SELECT abs(a) FROM t2}
|
||||
} {1 {} 345 {} 67890}
|
||||
do_test func-4.4.2 {
|
||||
execsql {SELECT abs(t1) FROM tbl1}
|
||||
} {this program is free software}
|
||||
|
||||
do_test func-4.5 {
|
||||
catchsql {SELECT round(a,b,c) FROM t1}
|
||||
@@ -163,5 +196,39 @@ do_test func-4.10 {
|
||||
do_test func-4.11 {
|
||||
catchsql {SELECT round() FROM t1 ORDER BY a}
|
||||
} {1 {wrong number of arguments to function round()}}
|
||||
do_test func-4.12 {
|
||||
execsql {SELECT round(a,2) FROM t2}
|
||||
} {1.00 0.00 345.00 0.00 67890.00}
|
||||
do_test func-4.13 {
|
||||
execsql {SELECT round(t1,2) FROM tbl1}
|
||||
} {0.00 0.00 0.00 0.00 0.00}
|
||||
|
||||
# Test the upper() and lower() functions
|
||||
#
|
||||
do_test func-5.1 {
|
||||
execsql {SELECT upper(t1) FROM tbl1}
|
||||
} {THIS PROGRAM IS FREE SOFTWARE}
|
||||
do_test func-5.2 {
|
||||
execsql {SELECT lower(upper(t1)) FROM tbl1}
|
||||
} {this program is free software}
|
||||
do_test func-5.3 {
|
||||
execsql {SELECT upper(a), lower(a) FROM t2}
|
||||
} {1 1 {} {} 345 345 {} {} 67890 67890}
|
||||
do_test func-5.4 {
|
||||
catchsql {SELECT upper(a,5) FROM t2}
|
||||
} {1 {wrong number of arguments to function upper()}}
|
||||
do_test func-5.5 {
|
||||
catchsql {SELECT upper(*) FROM t2}
|
||||
} {1 {wrong number of arguments to function upper()}}
|
||||
|
||||
# Test the coalesce() function
|
||||
#
|
||||
do_test func-6.1 {
|
||||
execsql {SELECT coalesce(a,'xyz') FROM t2}
|
||||
} {1 xyz 345 xyz 67890}
|
||||
do_test func-6.2 {
|
||||
execsql {SELECT coalesce(upper(a),'nil') FROM t2}
|
||||
} {1 nil 345 nil 67890}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
+44
-5
@@ -11,7 +11,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing the SELECT statement.
|
||||
#
|
||||
# $Id: select1.test,v 1.21 2002/02/28 03:14:18 drh Exp $
|
||||
# $Id: select1.test,v 1.22 2002/03/02 17:04:09 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -97,10 +97,22 @@ do_test select1-1.13 {
|
||||
FROM test1, test2}
|
||||
} {1.1 22}
|
||||
|
||||
execsql {DROP TABLE test2}
|
||||
execsql {DELETE FROM test1}
|
||||
execsql {INSERT INTO test1 VALUES(11,22)}
|
||||
execsql {INSERT INTO test1 VALUES(33,44)}
|
||||
set long {This is a string that is too big to fit inside a NBFS buffer}
|
||||
do_test select1-2.0 {
|
||||
execsql "
|
||||
DROP TABLE test2;
|
||||
DELETE FROM test1;
|
||||
INSERT INTO test1 VALUES(11,22);
|
||||
INSERT INTO test1 VALUES(33,44);
|
||||
CREATE TABLE t3(a,b);
|
||||
INSERT INTO t3 VALUES('abc',NULL);
|
||||
INSERT INTO t3 VALUES(NULL,'xyz');
|
||||
INSERT INTO t3 SELECT * FROM test1;
|
||||
CREATE TABLE t4(a,b);
|
||||
INSERT INTO t4 VALUES(NULL,'$long');
|
||||
SELECT * FROM t3;
|
||||
"
|
||||
} {abc {} {} xyz 11 22 33 44}
|
||||
|
||||
# Error messges from sqliteExprCheck
|
||||
#
|
||||
@@ -124,6 +136,15 @@ do_test select1-2.5 {
|
||||
set v [catch {execsql {SELECT COUNT(*)+1 FROM test1}} msg]
|
||||
lappend v $msg
|
||||
} {0 3}
|
||||
do_test select1-2.5.1 {
|
||||
execsql {SELECT count(*),count(a),count(b) FROM t3}
|
||||
} {4 3 3}
|
||||
do_test select1-2.5.2 {
|
||||
execsql {SELECT count(*),count(a),count(b) FROM t4}
|
||||
} {1 0 1}
|
||||
do_test select1-2.5.3 {
|
||||
execsql {SELECT count(*),count(a),count(b) FROM t4 WHERE b=5}
|
||||
} {0 0 0}
|
||||
do_test select1-2.6 {
|
||||
set v [catch {execsql {SELECT min(*) FROM test1}} msg]
|
||||
lappend v $msg
|
||||
@@ -136,6 +157,15 @@ do_test select1-2.8 {
|
||||
set v [catch {execsql {SELECT MIN(f1,f2) FROM test1}} msg]
|
||||
lappend v [lsort $msg]
|
||||
} {0 {11 33}}
|
||||
do_test select1-2.8.1 {
|
||||
execsql {SELECT coalesce(min(a),'xyzzy') FROM t3}
|
||||
} {xyzzy}
|
||||
do_test select1-2.8.2 {
|
||||
execsql {SELECT min(coalesce(a,'xyzzy')) FROM t3}
|
||||
} {11}
|
||||
do_test select1-2.8.3 {
|
||||
execsql {SELECT min(b), min(b) FROM t4}
|
||||
} [list $long $long]
|
||||
do_test select1-2.9 {
|
||||
set v [catch {execsql {SELECT MAX(*) FROM test1}} msg]
|
||||
lappend v $msg
|
||||
@@ -156,6 +186,12 @@ do_test select1-2.13 {
|
||||
set v [catch {execsql {SELECT MAX(f1)+1 FROM test1}} msg]
|
||||
lappend v $msg
|
||||
} {0 34}
|
||||
do_test select1-2.13.1 {
|
||||
execsql {SELECT coalesce(max(a),'xyzzy') FROM t3}
|
||||
} {abc}
|
||||
do_test select1-2.13.1 {
|
||||
execsql {SELECT max(coalesce(a,'xyzzy')) FROM t3}
|
||||
} {xyzzy}
|
||||
do_test select1-2.14 {
|
||||
set v [catch {execsql {SELECT SUM(*) FROM test1}} msg]
|
||||
lappend v $msg
|
||||
@@ -172,6 +208,9 @@ do_test select1-2.17 {
|
||||
set v [catch {execsql {SELECT SUM(f1)+1 FROM test1}} msg]
|
||||
lappend v $msg
|
||||
} {0 45}
|
||||
do_test select1-2.17.1 {
|
||||
execsql {SELECT sum(a) FROM t3}
|
||||
} {44}
|
||||
do_test select1-2.18 {
|
||||
set v [catch {execsql {SELECT XYZZY(f1) FROM test1}} msg]
|
||||
lappend v $msg
|
||||
|
||||
@@ -28,6 +28,8 @@ chng {2002 Feb * (2.3.4)} {
|
||||
<li>Modifications to the "lemon" parser generator so that the parser tables
|
||||
are 4 times smaller.</li>
|
||||
<li>Added support for user-defined functions implemented in C.</li>
|
||||
<li>Added support for VIEWs.</li>
|
||||
<li>Added the subquery flattening optimizer.</li>
|
||||
}
|
||||
|
||||
chng {2002 Feb 18 (2.3.3)} {
|
||||
|
||||
Reference in New Issue
Block a user