-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
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
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
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
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
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
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
-e14b0c82f3514f41934a7c0d173b6fdb186aafc8
\ No newline at end of file
+d5d3e79cc58da5bd315cc1fea1f7cbf46274da16
\ No newline at end of file
** DROP TABLE
** CREATE INDEX
** DROP INDEX
-** creating expressions and ID lists
+** creating ID lists
** COPY
** VACUUM
** BEGIN TRANSACTION
** 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>
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);
}
** 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"
/* 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) ){
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);
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.
** 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);
** 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"
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);
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.
*/
** 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
*/
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:
**
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;
}
if( cnt==0 && sqliteIsRowid(z) ){
pExpr->iColumn = -1;
- pExpr->iTable = pParse->nTab;
+ pExpr->iTable = base;
cnt = 1 + (pTabList->nId>1);
pExpr->op = TK_COLUMN;
}
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;
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 ){
** 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)
**
** 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;
/* 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;
}
}
** 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"
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;
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;
}
}
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
** 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}
//////////////////////// The SELECT statement /////////////////////////////////
//
cmd ::= select(X). {
- sqliteSelect(pParse, X, SRT_Callback, 0);
+ sqliteSelect(pParse, X, SRT_Callback, 0, 0, 0, 0);
sqliteSelectDelete(X);
}
** 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"
** 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;
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;
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);
/* 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
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;
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);
/* 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"
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;
** 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);
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
** 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.
+**
+** Flattening is only attempted if all of the following are true:
**
-** 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.
+** (1) The subquery and the outer query do not both use aggregates.
**
-** 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.
+** (2) The subquery is not an aggregate or the outer query is not a join.
**
-** All of the expression analysis must occur before this routine runs.
-** This routine depends on the results of the expression analysis.
+** (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. */
+ IdList *pSrc, *pSubSrc;
+ ExprList *pList;
+ int i;
+ int iParent, iSub;
+ Expr *pWhere;
+
+ /* 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);
+ }
}
- pSub = p->pSrc->a[0].pSelect;
- if( pSub==0 ){
- return 0; /* Nothing to optimize: There is no subquery. */
+ 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( pSub->isDistinct ){
- return 0; /* Subquery contains DISTINCT keyword */
+ 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);
+ }
}
- if( pSub->pGroupBy ){
- return 0; /* Subquery contains a GROUP BY clause */
+ p->isDistinct = p->isDistinct || pSub->isDistinct;
+ if( pSrc->a[iFrom].pTab && pSrc->a[iFrom].pTab->isTransient ){
+ sqliteDeleteTable(0, pSrc->a[iFrom].pTab);
}
- if( pSub->pPrior ){
- return 0; /* Subquery is the union of two or more queries */
- }
-
- return 0;
-}
+ 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
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
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 ){
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;
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
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.
*/
/* 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) ){
}
}
if( pWhere ){
- if( sqliteExprResolveIds(pParse, pTabList, pEList, pWhere) ){
+ if( sqliteExprResolveIds(pParse, base, pTabList, pEList, pWhere) ){
goto select_end;
}
if( sqliteExprCheck(pParse, pWhere, 0, 0) ){
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) ){
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) ){
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) ){
}
}
- /* 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.
*/
/* 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.
*/
** 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
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
if( pOrderBy ){
generateSortTail(v, pEList->nExpr);
}
- pParse->nTab = base;
/* Issue a null callback if that is what the user wants.
** successful coding of the SELECT.
*/
select_end:
+ pParse->nTab = base;
sqliteAggregateInfoReset(pParse);
return rc;
}
*************************************************************************
** 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"
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 */
};
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 */
};
/*
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 */
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*);
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);
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);
** 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"
** 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) ){
}
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) ){
/* 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.
** 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 ){
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
** 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"
** 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;
** 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;
*/
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 */
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 */
}
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
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;
}
}
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;
}
# 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
}
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
#
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
#
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.
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
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}
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
# 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
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
#
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
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
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
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
<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)} {