1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-30 19:03:16 +03:00

Bug fixes and additional tests for the subquery flattener. (CVS 411)

FossilOrigin-Name: 2c05389eda391e38894fc6969e29766df82a8fec
This commit is contained in:
drh
2002-03-03 02:49:51 +00:00
parent 30e58750c1
commit 1b2e032999
9 changed files with 251 additions and 96 deletions

View File

@ -1,5 +1,5 @@
C Pager\soptimization:\sdo\snot\swrite\sor\sjournal\sfree\spages.\s\sThis\sresults\sin\na\s2x\sperformance\sgain\sfor\slarge\sINSERTs\sand\sa\s5x\sperformance\sgain\sfor\nlarge\sDELETEs.\s(CVS\s410)
D 2002-03-02T20:41:58
C Bug\sfixes\sand\sadditional\stests\sfor\sthe\ssubquery\sflattener.\s(CVS\s411)
D 2002-03-03T02:49:51
F Makefile.in 50f1b3351df109b5774771350d8c1b8d3640130d
F Makefile.template 89e373b2dad0321df00400fa968dc14b61a03296
F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
@ -37,11 +37,11 @@ F src/pager.h feb18aab2f6dea439393f23a382699b9b1053c32
F src/parse.y d62960cdee2d2e7821f277d2fe63d823c86602ba
F src/printf.c 300a90554345751f26e1fc0c0333b90a66110a1d
F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
F src/select.c b99de04f6f2414bb21a065150e0748ccd8329519
F src/select.c 285a9cfa670c3fc47d8f42c9873be88860a015a1
F src/shell.c 9f8249ca5b8f8aad40becd778c151b58c0d6109e
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
F src/sqlite.h.in a9b5772604265f98f3120573ef29e37b9d917216
F src/sqliteInt.h df68f09091ab37d904020ac48ca2ea29e4985442
F src/sqliteInt.h 272c356245e3fdb33e91e87a3f7c0324d118cac9
F src/table.c 203a09d5d0009eeeb1f670370d52b4ce163a3b52
F src/tclsqlite.c b9cf346e95291cb4c4f1bf5ac1d77db6b8ad023d
F src/test1.c 33efd350dca27c52c58c553c04fd3a6a51f13c1f
@ -51,7 +51,7 @@ F src/threadtest.c 81f0598e0f031c1bd506af337fdc1b7e8dff263f
F src/tokenize.c 4b5d30590a744b9bb5605a92d1f620ab2e7e75af
F src/update.c 943b821901efb796dc82d91653621aeeb48d223b
F src/util.c 00a35b421c92ae0d7cfa51bd87f7d4995f464d19
F src/vdbe.c 91311e99efe980459a78e8f5b9e5456d772c9e23
F src/vdbe.c 4989cd3e6f4f699c9b08cd1980d1b588cede5c1f
F src/vdbe.h f9be1f6e9a336c3ff4d14ea7489ee976e07460cc
F src/where.c 34d91fd5d822c2663caeb023f72d60df316ebf29
F test/all.test 7a8a8a7a579ed2bb4d8976d55402f21eacd58049
@ -67,7 +67,7 @@ F test/func.test 4359344586067e79abf4c710c4737d67ed3cf963
F test/in.test c09312672e3f0709fa02c8e2e9cd8fb4bd6269aa
F test/index.test c8a471243bbf878974b99baf5badd59407237cf3
F test/insert.test c36d534a4ab58c2cd452a273e51b2b0dd1ede1f9
F test/insert2.test 65c2b2aae0bae7a7bbe500f77981cd916b81e91b
F test/insert2.test 2f02b1e0dbfba3e8c76496209be5f4010b584181
F test/intpkey.test 101ec266222e88b24e6f1e204b9b6873404cd4dc
F test/ioerr.test 57d9bffaca18b34f9e976f786eadc2591d6efc6a
F test/limit.test a930f3eba2a7691c8397ccab33710b931589566a
@ -87,7 +87,7 @@ F test/select2.test ed2c1882857106b85478f54f67000e14966be4c4
F test/select3.test 9469c332250a75a0ef1771fb5da62dc04ec77f18
F test/select4.test 29a2ffb187f3d8b6ca42a0a6b619e9cabe12e228
F test/select5.test c2a6c4a003316ee42cbbd689eebef8fdce0db2ac
F test/select6.test d9fb417d6cab75a072b547ba6303120f327fd6fd
F test/select6.test 8ccbcd3b53df4735a5bb3a8cd4bf4e495cae26c0
F test/sort.test 3b996ce7ca385f9cd559944ac0f4027a23aa546b
F test/subselect.test 335d3dad8d585726c447dfee8d9c4f7383c76b78
F test/table.test 17b0b6eafa3faaee5545b7a94e6c1ff73f0880f3
@ -98,10 +98,10 @@ F test/tester.tcl 96db1b49157388edb57e11bf33285e3811a897e4
F test/trans.test 9e49495c06b1c41f889bf4f0fb195a015b126de0
F test/unique.test 07776624b82221a80c8b4138ce0dd8b0853bb3ea
F test/update.test 3cf1ca0565f678063c2dfa9a7948d2d66ae1a778
F test/vacuum.test 8acf8669f3b627e54149b25165b034aa06c2432e
F test/vacuum.test 059871b312eb910bbe49dafde1d01490cc2c6bbe
F test/view.test 4619ebede587102e577e19ce52e9852ec8293cbc
F test/where.test 032d581c3de4893eba33b569e581c46b941bb02a
F tool/lemon.c d4b4e127e70633be1bbea49c2ccba5cdb4b77a5f
F tool/lemon.c a26214e008a7351c0c9fc57c5aab44b403c36c42
F tool/lempar.c 2ff255186fffb38a43a9f7b010e87eee6308edcc
F tool/memleak.awk 296dfbce7a9ca499b95ce04e30334e64a50052e0
F tool/opNames.awk 5ba1f48aa854ee3b7c3d2b54233665bc3e649ea2
@ -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 abbb999d4fc3fe142567b6ede5e625e7bf0da714
R 65be3ccaeb081927d6806e4071f74e0f
P cf1ebcfb741786f84a596c406f4c492f68cbe881
R 15fdb1a16caac3bbe9fb9691324e3176
U drh
Z 3493ea99db08effd4dc1d635e5b15238
Z 2b80ac05372abd52845156b2368b8bf6

View File

@ -1 +1 @@
cf1ebcfb741786f84a596c406f4c492f68cbe881
2c05389eda391e38894fc6969e29766df82a8fec

View File

@ -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.71 2002/03/02 17:04:08 drh Exp $
** $Id: select.c,v 1.72 2002/03/03 02:49:51 drh Exp $
*/
#include "sqliteInt.h"
@ -760,13 +760,17 @@ static void changeTables(Expr *pExpr, int iFrom, int iTo){
if( pExpr->op==TK_COLUMN && pExpr->iTable==iFrom ){
pExpr->iTable = iTo;
}else{
static void changeTablesInList(ExprList*, int, int);
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);
changeTablesInList(pExpr->pList, iFrom, iTo);
}
}
static void changeTablesInList(ExprList *pList, int iFrom, int iTo){
if( pList ){
int i;
for(i=0; i<pList->nExpr; i++){
changeTables(pList->a[i].pExpr, iFrom, iTo);
}
}
}
@ -799,6 +803,7 @@ static void substExpr(Expr *pExpr, int iTable, ExprList *pEList, int iSub){
pExpr->iTable = pNew->iTable;
pExpr->iColumn = pNew->iColumn;
pExpr->iAgg = pNew->iAgg;
pExpr->token = pNew->token;
if( iSub!=iTable ){
changeTables(pExpr, iSub, iTable);
}
@ -908,8 +913,10 @@ int flattenSubquery(Select *p, int iFrom, int isAgg, int subqueryIsAgg){
pList->a[i].zName = sqliteStrNDup(pExpr->span.z, pExpr->span.n);
}
}
if( isAgg ){
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);
@ -921,8 +928,25 @@ int flattenSubquery(Select *p, int iFrom, int isAgg, int subqueryIsAgg){
}
if( subqueryIsAgg ){
assert( p->pHaving==0 );
p->pHaving = pWhere;
p->pHaving = p->pWhere;
p->pWhere = pWhere;
substExpr(p->pHaving, iParent, pSub->pEList, iSub);
if( pSub->pHaving ){
Expr *pHaving = sqliteExprDup(pSub->pHaving);
if( iParent!=iSub ){
changeTables(pHaving, iSub, iParent);
}
if( p->pHaving ){
p->pHaving = sqliteExpr(TK_AND, p->pHaving, pHaving, 0);
}else{
p->pHaving = pHaving;
}
}
assert( p->pGroupBy==0 );
p->pGroupBy = sqliteExprListDup(pSub->pGroupBy);
if( iParent!=iSub ){
changeTablesInList(p->pGroupBy, iSub, iParent);
}
}else if( p->pWhere==0 ){
p->pWhere = pWhere;
}else{
@ -1083,6 +1107,13 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
**
** This routine does NOT free the Select structure passed in. The
** calling function needs to do that.
**
** The pParent, parentTab, and *pParentAgg fields are filled in if this
** SELECT is a subquery. This routine may try to combine this SELECT
** with its parent to form a single flat query. In so doing, it might
** change the parent query from a non-aggregate to an aggregate query.
** For that reason, the pParentAgg flag is passed as a pointer, so it
** can be changed.
*/
int sqliteSelect(
Parse *pParse, /* The parser context */
@ -1091,7 +1122,7 @@ int sqliteSelect(
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 *pParentAgg /* True if pParent uses aggregate functions */
){
int i;
WhereInfo *pWInfo;
@ -1254,21 +1285,23 @@ int sqliteSelect(
if( pTabList->a[i].pSelect==0 ) continue;
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;
}
p, i, &isAgg);
pTabList = p->pSrc;
pWhere = p->pWhere;
pOrderBy = p->pOrderBy;
pGroupBy = p->pGroupBy;
pHaving = p->pHaving;
isDistinct = p->isDistinct;
}
/* 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( pParent && pParentAgg &&
flattenSubquery(pParent, parentTab, *pParentAgg, isAgg) ){
if( isAgg ) *pParentAgg = 1;
return rc;
}
/* Do an analysis of aggregate expressions.
*/

View File

@ -11,7 +11,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.97 2002/03/02 17:04:08 drh Exp $
** @(#) $Id: sqliteInt.h,v 1.98 2002/03/03 02:49:51 drh Exp $
*/
#include "sqlite.h"
#include "hash.h"
@ -589,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, 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*);

View File

@ -30,7 +30,7 @@
** But other routines are also provided to help in building up
** a program instruction by instruction.
**
** $Id: vdbe.c,v 1.129 2002/02/28 03:31:11 drh Exp $
** $Id: vdbe.c,v 1.130 2002/03/03 02:49:51 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@ -1096,6 +1096,7 @@ int sqliteVdbeList(
char zAddr[20];
char zP1[20];
char zP2[20];
char zP3[40];
static char *azColumnNames[] = {
"addr", "opcode", "p1", "p2", "p3", 0
};
@ -1116,7 +1117,12 @@ int sqliteVdbeList(
sprintf(zAddr,"%d",i);
sprintf(zP1,"%d", p->aOp[i].p1);
sprintf(zP2,"%d", p->aOp[i].p2);
azValue[4] = p->aOp[i].p3type!=P3_POINTER ? p->aOp[i].p3 : "";
if( p->aOp[i].p3type==P3_POINTER ){
sprintf(zP3, "ptr(%#x)", (int)p->aOp[i].p3);
azValue[4] = zP3;
}else{
azValue[4] = p->aOp[i].p3;
}
azValue[1] = zOpName[p->aOp[i].opcode];
if( xCallback(pArg, 5, azValue, azColumnNames) ){
rc = SQLITE_ABORT;

View File

@ -12,7 +12,7 @@
# focus of this file is testing the INSERT statement that takes is
# result from a SELECT.
#
# $Id: insert2.test,v 1.7 2002/02/19 13:39:23 drh Exp $
# $Id: insert2.test,v 1.8 2002/03/03 02:49:52 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@ -112,10 +112,75 @@ do_test insert2-2.3 {
}
} {hi 2 1}
do_test insert2-4.0 {
do_test insert2-3.0 {
set x [execsql {PRAGMA integrity_check}]
if {$x==""} {set x ok}
set x
} {ok}
# File table t4 with lots of data
#
do_test insert2-3.1 {
execsql {
SELECT * from t4;
}
} {1 2}
do_test insert2-3.2 {
execsql {
BEGIN;
INSERT INTO t4 VALUES(2,4);
INSERT INTO t4 VALUES(3,6);
INSERT INTO t4 VALUES(4,8);
INSERT INTO t4 VALUES(5,10);
INSERT INTO t4 VALUES(6,12);
INSERT INTO t4 VALUES(7,14);
INSERT INTO t4 VALUES(8,16);
INSERT INTO t4 VALUES(9,18);
INSERT INTO t4 VALUES(10,20);
COMMIT;
SELECT count(*) FROM t4;
}
} {10}
do_test insert2-3.3 {
execsql {
BEGIN;
INSERT INTO t4 SELECT x+(SELECT max(x) FROM t4),y FROM t4;
INSERT INTO t4 SELECT x+(SELECT max(x) FROM t4),y FROM t4;
INSERT INTO t4 SELECT x+(SELECT max(x) FROM t4),y FROM t4;
INSERT INTO t4 SELECT x+(SELECT max(x) FROM t4),y FROM t4;
COMMIT;
SELECT count(*) FROM t4;
}
} {160}
do_test insert2-3.4 {
execsql {
BEGIN;
UPDATE t4 SET y='lots of data for the row where x=' || x
|| ' and y=' || y || ' - even more data to fill space';
COMMIT;
SELECT count(*) FROM t4;
}
} {160}
do_test insert2-3.5 {
execsql {
BEGIN;
INSERT INTO t4 SELECT x+(SELECT max(x)+1 FROM t4),y FROM t4;
SELECT count(*) from t4;
ROLLBACK;
}
} {320}
do_test insert2-3.6 {
execsql {
SELECT count(*) FROM t4;
}
} {160}
do_test insert2-3.7 {
execsql {
BEGIN;
DELETE FROM t4 WHERE x!=123;
SELECT count(*) FROM t4;
ROLLBACK;
}
} {1}
finish_test

View File

@ -12,7 +12,7 @@
# focus of this file is testing SELECT statements that contain
# subqueries in their FROM clause.
#
# $Id: select6.test,v 1.2 2002/02/18 13:35:33 drh Exp $
# $Id: select6.test,v 1.3 2002/03/03 02:49:52 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@ -166,4 +166,103 @@ do_test sqlite6-3.2 {
ORDER BY [a.q]
}
} {1 1 1 2 2 3 3 4 7 4 8 15 5 5 20}
do_test select6-3.3 {
execsql {
SELECT a,b,a+b FROM (SELECT avg(x) as 'a', avg(y) as 'b' FROM t1)
}
} {10.5 3.7 14.2}
do_test select6-3.4 {
execsql {
SELECT a,b,a+b FROM (SELECT avg(x) as 'a', avg(y) as 'b' FROM t1 WHERE y=4)
}
} {11.5 4 15.5}
do_test select6-3.5 {
execsql {
SELECT x,y,x+y FROM (SELECT avg(a) as 'x', avg(b) as 'y' FROM t2 WHERE a=4)
}
} {4 3 7}
do_test select6-3.6 {
execsql {
SELECT a,b,a+b FROM (SELECT avg(x) as 'a', avg(y) as 'b' FROM t1)
WHERE a>10
}
} {10.5 3.7 14.2}
do_test select6-3.7 {
execsql {
SELECT a,b,a+b FROM (SELECT avg(x) as 'a', avg(y) as 'b' FROM t1)
WHERE a<10
}
} {}
do_test select6-3.8 {
execsql {
SELECT a,b,a+b FROM (SELECT avg(x) as 'a', avg(y) as 'b' FROM t1 WHERE y=4)
WHERE a>10
}
} {11.5 4 15.5}
do_test select6-3.9 {
execsql {
SELECT a,b,a+b FROM (SELECT avg(x) as 'a', avg(y) as 'b' FROM t1 WHERE y=4)
WHERE a<10
}
} {}
do_test select6-3.10 {
execsql {
SELECT a,b,a+b FROM (SELECT avg(x) as 'a', y as 'b' FROM t1 GROUP BY b)
ORDER BY a
}
} {1 1 2 2.5 2 4.5 5.5 3 8.5 11.5 4 15.5 18 5 23}
do_test select6-3.11 {
execsql {
SELECT a,b,a+b FROM
(SELECT avg(x) as 'a', y as 'b' FROM t1 GROUP BY b)
WHERE b<4 ORDER BY a
}
} {1 1 2 2.5 2 4.5 5.5 3 8.5}
do_test select6-3.12 {
execsql {
SELECT a,b,a+b FROM
(SELECT avg(x) as 'a', y as 'b' FROM t1 GROUP BY b HAVING a>1)
WHERE b<4 ORDER BY a
}
} {2.5 2 4.5 5.5 3 8.5}
do_test select6-3.13 {
execsql {
SELECT a,b,a+b FROM
(SELECT avg(x) as 'a', y as 'b' FROM t1 GROUP BY b HAVING a>1)
ORDER BY a
}
} {2.5 2 4.5 5.5 3 8.5 11.5 4 15.5 18 5 23}
do_test select6-3.14 {
execsql {
SELECT [count(*)],y FROM (SELECT count(*), y FROM t1 GROUP BY y)
ORDER BY [count(*)]
}
} {1 1 2 2 4 3 5 5 8 4}
do_test select6-3.15 {
execsql {
SELECT [count(*)],y FROM (SELECT count(*), y FROM t1 GROUP BY y)
ORDER BY y
}
} {1 1 2 2 4 3 8 4 5 5}
do_test select6-4.1 {
execsql {
SELECT a,b,c FROM
(SELECT x AS 'a', y AS 'b', x+y AS 'c' FROM t1 WHERE y=4)
WHERE a<10 ORDER BY a;
}
} {8 4 12 9 4 13}
do_test select6-4.2 {
execsql {
SELECT y FROM (SELECT DISTINCT y FROM t1) WHERE y<5 ORDER BY y
}
} {1 2 3 4}
do_test select6-4.3 {
execsql {
SELECT DISTINCT y FROM (SELECT y FROM t1) WHERE y<5 ORDER BY y
}
} {1 2 3 4}
finish_test

View File

@ -11,63 +11,12 @@
# This file implements regression tests for SQLite library. The
# focus of this file is testing the VACUUM statement.
#
# $Id: vacuum.test,v 1.6 2001/09/16 00:13:28 drh Exp $
# $Id: vacuum.test,v 1.7 2002/03/03 02:49:52 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
if {0} {
# The vacuum command no longer functions. There is
# nothing to test.
# Try to vacuum a non-existant table.
#
do_test vacuum-1.1 {
set v [catch {execsql {VACUUM dummy1}} msg]
lappend v $msg
} {1 {no such table or index: dummy1}}
# It is OK to vacuum sqlite_master...
#
do_test vacuum-1.2 {
set v [catch {execsql {VACUUM sqlite_master}} msg]
lappend v $msg
} {0 {}}
# Create some tables and indices to test against.
#
execsql {CREATE TABLE test1(a int)}
execsql {CREATE TABLE test2(b int)}
execsql {CREATE INDEX index1 ON test1(a)}
execsql {INSERT INTO test1 VALUES(1)}
execsql {INSERT INTO test1 VALUES(1)}
execsql {INSERT INTO test1 VALUES(2)}
execsql {INSERT INTO test1 VALUES(3)}
execsql {INSERT INTO test2 VALUES(4)}
do_test vacuum-1.3 {
set b1 [file mtime testdb/test1.tbl]
set b2 [file mtime testdb/test2.tbl]
set b3 [file mtime testdb/index1.tbl]
after 1000
execsql {VACUUM test1}
set a1 [file mtime testdb/test1.tbl]
set a2 [file mtime testdb/test2.tbl]
set a3 [file mtime testdb/index1.tbl]
expr {$a1>$b1 && $a2==$b2 && $a3==$b3}
} {1}
if {$::tcl_platform(platform)!="windows"} {
do_test vacuum-1.4 {
set b1 [file mtime testdb/test1.tbl]
set b2 [file mtime testdb/test2.tbl]
set b3 [file mtime testdb/index1.tbl]
after 1000
execsql {VACUUM}
set a1 [file mtime testdb/test1.tbl]
set a2 [file mtime testdb/test2.tbl]
set a3 [file mtime testdb/index1.tbl]
expr {$a1>$b1 && $a2>$b2 && $a3>$b3}
} {1}
} ;# End if( platform!=windows )
finish_test
}
# finish_test

View File

@ -3103,13 +3103,16 @@ int mhflag; /* Output in makeheaders format if true */
}
/* Print the hash table */
if( tablesize>0 ){
fprintf(out,"/* State %d */\n",stp->index); lineno++;
}
for(j=0; j<tablesize; j++){
assert( table[j]!=0 );
fprintf(out," {%4d,%4d,%4d}, /* ",
fprintf(out," {%4d,%4d,%4d}, /* %2d: ",
table[j]->sp->index,
collide[j]+1,
compute_action(lemp,table[j]));
compute_action(lemp,table[j]),
j+1);
PrintAction(table[j],out,22);
fprintf(out," */\n");
lineno++;