From 048c530c01a058956e049a5f68360b39a5a65ee5 Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 3 Apr 2003 01:50:44 +0000 Subject: [PATCH] Use a intermediate table when inserting a TEMP table from a SELECT that reads from that same TEMP table. Ticket #275. (CVS 895) FossilOrigin-Name: 087d1e83af12b3a9aedd4945f02774a1043b1eb4 --- manifest | 20 +++++++++--------- manifest.uuid | 2 +- src/insert.c | 21 +++++++++++++++---- src/vdbe.c | 15 +++++++++++--- src/vdbe.h | 3 ++- test/insert.test | 54 ++++++++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 94 insertions(+), 21 deletions(-) diff --git a/manifest b/manifest index f99a0d3469..30dd63c5fe 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sinfrastructure\sto\ssuport\smultiple\sbtree\simplementations\s(CVS\s894) -D 2003-04-01T21:16:42 +C Use\sa\sintermediate\stable\swhen\sinserting\sa\sTEMP\stable\sfrom\sa\sSELECT\sthat\nreads\sfrom\sthat\ssame\sTEMP\stable.\s\sTicket\s#275.\s(CVS\s895) +D 2003-04-03T01:50:44 F Makefile.in 3c4ba24253e61c954d67adbbb4245e7117c5357e F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd @@ -29,7 +29,7 @@ F src/expr.c b8daee83f837b24a22d889200bdd74973ca2d8db F src/func.c 882c3ed5a02be18cd904715c7ec62947a34a3605 F src/hash.c 4fc39feb7b7711f6495ee9f2159559bedb043e1f F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8 -F src/insert.c dad4d914dc729a94e48de171802c23587a62c34f +F src/insert.c e2f5e7feecb507d904a7da48874595f440b715aa F src/main.c 6d9a38491fdc40c041df64a7399244c364481a09 F src/md5.c fe4f9c9c6f71dfc26af8da63e4d04489b1430565 F src/os.c dfed46091f69cd2d1e601f8a214d41344f2b00b6 @@ -54,8 +54,8 @@ F src/tokenize.c 675b4718d17c69fe7609dc8e85e426ef002be811 F src/trigger.c bd5a5b234b47f28f9f21a46243dcaf1c5b2383a3 F src/update.c b368369f1fbe6d7f56a53e5ffad3b75dae9e3e1a F src/util.c 8953c612a036e30f24c1c1f5a1498176173daa37 -F src/vdbe.c 7171dbe873760f403b2501e96fd3d1bd852b3ce8 -F src/vdbe.h ed43771f1dc2b994d5c484fdf2eab357c6ef0ee3 +F src/vdbe.c b3b840b555b5238926b7ebb01cd47526a29bb853 +F src/vdbe.h 985c24f312d10f9ef8f9a8b8ea62fcdf68e82f21 F src/where.c e5733f7d5e9cc4ed3590dc3401f779e7b7bb8127 F test/all.test 569a92a8ee88f5300c057cc4a8f50fbbc69a3242 F test/auth.test 33e8b9680eb0ce521c54096fff1c9ab506c7dfb8 @@ -75,7 +75,7 @@ F test/format3.test 64ab6c4db132b28a645996d413530f7b2a462cc2 F test/func.test 000515779001ac6899eec4b54e65c6e2501279d4 F test/in.test 3171a2b3170a8223665c1a4f26be5f3eda36cc4b F test/index.test faeb1bcf776e3ff9ba1b4be1eadd1fece708aa7b -F test/insert.test a122afb86911e77c181d912348866a5b1a61eeab +F test/insert.test 5697ba098e4d8a6f0151f281b7e39dec9c439e05 F test/insert2.test c288375a64dad3295044714f0dfed4a193cf067f F test/intpkey.test 39f49fd993350f7f3ab255e5cfbf9a09d8f8800e F test/ioerr.test 45c8feebe608d7f456fea27ff27a0aaaf0b9c636 @@ -155,7 +155,7 @@ F www/speed.tcl cb4c10a722614aea76d2c51f32ee43400d5951be F www/sqlite.tcl ae3dcfb077e53833b59d4fcc94d8a12c50a44098 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218 -P 11378c5bf988412f8564cdc0314fc241793b292e -R cc8e4c3807eb9ac6929fb512790ca871 -U paul -Z 048b3196e99f227ca8551c2b49ca1411 +P 79b3aed2a74a67cbad631c4e2e4a43469d80c162 +R 6e7a48442426732645524eccf6cea407 +U drh +Z b83fa935d7c88982a4c85635851f4582 diff --git a/manifest.uuid b/manifest.uuid index 43967b3e83..534960f4e9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -79b3aed2a74a67cbad631c4e2e4a43469d80c162 \ No newline at end of file +087d1e83af12b3a9aedd4945f02774a1043b1eb4 \ No newline at end of file diff --git a/src/insert.c b/src/insert.c index 705bd8e9d2..2e376fa6ac 100644 --- a/src/insert.c +++ b/src/insert.c @@ -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.77 2003/03/31 02:12:47 drh Exp $ +** $Id: insert.c,v 1.78 2003/04/03 01:50:44 drh Exp $ */ #include "sqliteInt.h" @@ -177,7 +177,6 @@ void sqliteInsert( /* Data is coming from a SELECT. Generate code to implement that SELECT */ int rc, iInitCode; - int opCode; iInitCode = sqliteVdbeAddOp(v, OP_Goto, 0, 0); iSelectLoop = sqliteVdbeCurrentAddr(v); iInsertBlock = sqliteVdbeMakeLabel(v); @@ -191,9 +190,23 @@ void sqliteInsert( /* Set useTempTable to TRUE if the result of the SELECT statement ** should be written into a temporary table. Set to FALSE if each ** row of the SELECT can be written directly into the result table. + ** + ** A temp table must be used if the table being updated is also one + ** of the tables being read by the SELECT statement. Also use a + ** temp table in the case of row triggers. */ - opCode = pTab->iDb==1 ? OP_OpenTemp : OP_OpenRead; - useTempTable = row_triggers_exist || sqliteVdbeFindOp(v,opCode,pTab->tnum); + if( row_triggers_exist ){ + useTempTable = 1; + }else{ + int addr = sqliteVdbeFindOp(v, OP_OpenRead, pTab->tnum); + useTempTable = 0; + if( addr>0 ){ + VdbeOp *pOp = sqliteVdbeGetOp(v, addr-2); + if( pOp->opcode==OP_Integer && pOp->p1==pTab->iDb ){ + useTempTable = 1; + } + } + } if( useTempTable ){ /* Generate the subroutine that SELECT calls to process each row of diff --git a/src/vdbe.c b/src/vdbe.c index 8e088b786d..f1c0aa6f8e 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -36,7 +36,7 @@ ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. ** -** $Id: vdbe.c,v 1.209 2003/03/20 01:16:59 drh Exp $ +** $Id: vdbe.c,v 1.210 2003/04/03 01:50:45 drh Exp $ */ #include "sqliteInt.h" #include @@ -603,17 +603,26 @@ void sqliteVdbeCompressSpace(Vdbe *p, int addr){ /* ** Search for the current program for the given opcode and P2 -** value. Return 1 if found and 0 if not found. +** value. Return the address plus 1 if found and 0 if not found. */ int sqliteVdbeFindOp(Vdbe *p, int op, int p2){ int i; assert( p->magic==VDBE_MAGIC_INIT ); for(i=0; inOp; i++){ - if( p->aOp[i].opcode==op && p->aOp[i].p2==p2 ) return 1; + if( p->aOp[i].opcode==op && p->aOp[i].p2==p2 ) return i+1; } return 0; } +/* +** Return the opcode for a given address. +*/ +VdbeOp *sqliteVdbeGetOp(Vdbe *p, int addr){ + assert( p->magic==VDBE_MAGIC_INIT ); + assert( addr>=0 && addrnOp ); + return &p->aOp[addr]; +} + /* ** The following group or routines are employed by installable functions ** to return their results. diff --git a/src/vdbe.h b/src/vdbe.h index e66fb3ed93..3cdbd7e753 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -15,7 +15,7 @@ ** or VDBE. The VDBE implements an abstract machine that runs a ** simple program to access and modify the underlying database. ** -** $Id: vdbe.h,v 1.64 2003/01/29 18:46:53 drh Exp $ +** $Id: vdbe.h,v 1.65 2003/04/03 01:50:47 drh Exp $ */ #ifndef _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_ @@ -81,6 +81,7 @@ void sqliteVdbeChangeP2(Vdbe*, int addr, int P2); void sqliteVdbeChangeP3(Vdbe*, int addr, const char *zP1, int N); void sqliteVdbeDequoteP3(Vdbe*, int addr); int sqliteVdbeFindOp(Vdbe*, int, int); +VdbeOp *sqliteVdbeGetOp(Vdbe*, int); int sqliteVdbeMakeLabel(Vdbe*); void sqliteVdbeDelete(Vdbe*); void sqliteVdbeMakeReady(Vdbe*,sqlite_callback,void*,int); diff --git a/test/insert.test b/test/insert.test index 7a013a3de3..f551e4e4ae 100644 --- a/test/insert.test +++ b/test/insert.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the INSERT statement. # -# $Id: insert.test,v 1.11 2002/06/25 13:16:04 drh Exp $ +# $Id: insert.test,v 1.12 2003/04/03 01:50:48 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -202,6 +202,56 @@ do_test insert-4.7 { } } {1 3 99} -# Test +# Test the ability to insert from a temporary table into itself. +# Ticket #275. +# +do_test insert-5.1 { + execsql { + CREATE TEMP TABLE t4(x); + INSERT INTO t4 VALUES(1); + SELECT * FROM t4; + } +} {1} +do_test insert-5.2 { + execsql { + INSERT INTO t4 SELECT x+1 FROM t4; + SELECT * FROM t4; + } +} {1 2} +do_test insert-5.3 { + # verify that a temporary table is used to copy t4 to t4 + set x [execsql { + EXPLAIN INSERT INTO t4 SELECT x+2 FROM t4; + }] + expr {[lsearch $x OpenTemp]>0} +} {1} +do_test insert-5.4 { + # Verify that table "test1" begins on page 3. This should be the same + # page number used by "t4" above. + execsql { + SELECT rootpage FROM sqlite_master WHERE name='test1'; + } +} {3} +do_test insert-5.5 { + # Verify that "t4" begins on page 3. + execsql { + SELECT rootpage FROM sqlite_temp_master WHERE name='t4'; + } +} {3} +do_test insert-5.6 { + # This should not use an intermediate temporary table. + execsql { + INSERT INTO t4 SELECT one FROM test1 WHERE three=7; + SELECT * FROM t4 + } +} {1 2 8} +do_test insert-5.7 { + # verify that no temporary table is used to copy test1 to t4 + set x [execsql { + EXPLAIN INSERT INTO t4 SELECT one FROM test1; + }] + expr {[lsearch $x OpenTemp]>0} +} {0} + finish_test