Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Make sure that INSERT INTO ... SELECT ... always uses an ephemeral intermediate table if the SELECT clause refers to the destination table, even if the SELECT clause is compound or uses the destination table in a subquery. This fixes a long-standing bug that can cause an infinite loop for some SQL statements. (CVS 4552) |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
492b39b6a8bf4ad8792d7a7949f77827 |
User & Date: | drh 2007-11-23 15:02:19.000 |
Context
2007-11-23
| ||
15:12 | Remove most documentation from the source code repository. Documentation configuration management is now handled by a separate repository at http://www.sqlite.org/docsrc. (CVS 4553) (check-in: b20f3a6fed user: drh tags: trunk) | |
15:02 | Make sure that INSERT INTO ... SELECT ... always uses an ephemeral intermediate table if the SELECT clause refers to the destination table, even if the SELECT clause is compound or uses the destination table in a subquery. This fixes a long-standing bug that can cause an infinite loop for some SQL statements. (CVS 4552) (check-in: 492b39b6a8 user: drh tags: trunk) | |
13:42 | Avoid a double-free in an out-of-memory situation with a USING clause or NATURAL JOIN. Ticket #2789. (CVS 4551) (check-in: 596694752c user: drh tags: trunk) | |
Changes
Changes to src/insert.c.
︙ | ︙ | |||
8 9 10 11 12 13 14 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains C code routines that are called by the parser ** to handle INSERT statements in SQLite. ** | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains C code routines that are called by the parser ** to handle INSERT statements in SQLite. ** ** $Id: insert.c,v 1.193 2007/11/23 15:02:19 drh Exp $ */ #include "sqliteInt.h" /* ** Set P3 of the most recently inserted opcode to a column affinity ** string for index pIdx. A column affinity string has one character ** for each column in the table, according to the affinity of the column: |
︙ | ︙ | |||
95 96 97 98 99 100 101 | pTab->zColAff = zColAff; } sqlite3VdbeChangeP3(v, -1, pTab->zColAff, 0); } /* | | > | | | < < | > > > > > > > > > | > | < > > | | > > > > > | > | | 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | pTab->zColAff = zColAff; } sqlite3VdbeChangeP3(v, -1, pTab->zColAff, 0); } /* ** Return non-zero if the table pTab in database iDb or any of its indices ** have been opened at any point in the VDBE program beginning at location ** iStartAddr throught the end of the program. This is used to see if ** a statement of the form "INSERT INTO <iDb, pTab> SELECT ..." can ** run without using temporary table for the results of the SELECT. */ static int readsTable(Vdbe *v, int iStartAddr, int iDb, Table *pTab){ int i; int iEnd = sqlite3VdbeCurrentAddr(v); for(i=iStartAddr; i<iEnd; i++){ VdbeOp *pOp = sqlite3VdbeGetOp(v, i); if( pOp->opcode==OP_OpenRead ){ VdbeOp *pPrior = &pOp[-1]; int tnum = pOp->p2; assert( i>iStartAddr ); assert( pPrior->opcode==OP_Integer ); if( pPrior->p1==iDb ){ Index *pIndex; if( tnum==pTab->tnum ){ return 1; } for(pIndex=pTab->pIndex; pIndex; pIndex=pIndex->pNext){ if( tnum==pIndex->tnum ){ return 1; } } } } if( pOp->opcode==OP_VOpen && pOp->p3==(const char*)pTab->pVtab ){ assert( pOp->p3!=0 ); assert( pOp->p3type==P3_VTAB ); return 1; } } return 0; } #ifndef SQLITE_OMIT_AUTOINCREMENT /* |
︙ | ︙ | |||
477 478 479 480 481 482 483 | ** 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. */ | | | 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 | ** 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. */ if( triggers_exist || readsTable(v, iSelectLoop, iDb, pTab) ){ useTempTable = 1; } if( useTempTable ){ /* Generate the subroutine that SELECT calls to process each row of ** the result. Store the result in a temporary table */ |
︙ | ︙ | |||
513 514 515 516 517 518 519 | /* This is the case if the data for the INSERT is coming from a VALUES ** clause */ NameContext sNC; memset(&sNC, 0, sizeof(sNC)); sNC.pParse = pParse; srcTab = -1; | | | 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 | /* This is the case if the data for the INSERT is coming from a VALUES ** clause */ NameContext sNC; memset(&sNC, 0, sizeof(sNC)); sNC.pParse = pParse; srcTab = -1; assert( useTempTable==0 ); nColumn = pList ? pList->nExpr : 0; for(i=0; i<nColumn; i++){ if( sqlite3ExprResolveNames(&sNC, pList->a[i].pExpr) ){ goto insert_cleanup; } } } |
︙ | ︙ |
Added test/insert5.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | # 2007 November 23 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # The tests in this file ensure that a temporary table is used # when required by an "INSERT INTO ... SELECT ..." statement. # # $Id: insert5.test,v 1.1 2007/11/23 15:02:19 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Return true if the compilation of the sql passed as an argument # includes the opcode OpenEphemeral. An "INSERT INTO ... SELECT" # statement includes such an opcode if a temp-table is used # to store intermediate results. # proc uses_temp_table {sql} { return [expr {[lsearch [execsql "EXPLAIN $sql"] OpenEphemeral]>=0}] } # Construct the sample database. # do_test insert5-1.0 { file delete -force test2.db test2.db-journal execsql { CREATE TABLE MAIN(Id INTEGER, Id1 INTEGER); CREATE TABLE B(Id INTEGER, Id1 INTEGER); CREATE VIEW v1 AS SELECT * FROM B; CREATE VIEW v2 AS SELECT * FROM MAIN; INSERT INTO MAIN(Id,Id1) VALUES(2,3); INSERT INTO B(Id,Id1) VALUES(2,3); } } {} # Run the query. # do_test insert5-1.1 { execsql { INSERT INTO B SELECT * FROM B UNION ALL SELECT * FROM MAIN WHERE exists (select * FROM B WHERE B.Id = MAIN.Id); SELECT * FROM B; } } {2 3 2 3 2 3} do_test insert5-2.1 { uses_temp_table { INSERT INTO b SELECT * FROM main } } {0} do_test insert5-2.2 { uses_temp_table { INSERT INTO b SELECT * FROM b } } {1} do_test insert5-2.3 { uses_temp_table { INSERT INTO b SELECT (SELECT id FROM b), id1 FROM main } } {1} do_test insert5-2.4 { uses_temp_table { INSERT INTO b SELECT id1, (SELECT id FROM b) FROM main } } {1} do_test insert5-2.5 { uses_temp_table { INSERT INTO b SELECT * FROM main WHERE id = (SELECT id1 FROM b WHERE main.id = b.id) } } {1} do_test insert5-2.6 { uses_temp_table { INSERT INTO b SELECT * FROM v1 } } {1} do_test insert5-2.7 { uses_temp_table { INSERT INTO b SELECT * FROM v2 } } {0} do_test insert5-2.8 { uses_temp_table { INSERT INTO b SELECT * FROM main WHERE id > 10 AND max(id1, (SELECT id FROM b)) > 10; } } {1} do_test insert5-2.9 { uses_temp_table { INSERT INTO b SELECT * FROM main WHERE id > 10 AND (SELECT count(*) FROM v2 GROUP BY main.id) } } {1} finish_test |