Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Add further tests for the changes on this branch. Also fix a memory-leak that could follow a malloc failure. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | onepass-delete-or |
Files: | files | file ages | folders |
SHA1: |
21526012c274e709d672dbafb9e16158 |
User & Date: | dan 2015-12-12 17:31:40.728 |
Context
2015-12-12
| ||
19:23 | Update some stale comments in delete.c. No changes to code. (Leaf check-in: f59a33260c user: dan tags: onepass-delete-or) | |
17:31 | Add further tests for the changes on this branch. Also fix a memory-leak that could follow a malloc failure. (check-in: 21526012c2 user: dan tags: onepass-delete-or) | |
2015-12-11
| ||
13:59 | Merge recent changes from trunk. Also remove unused variables to permit compiling with -Werror. (check-in: 57b700baa6 user: drh tags: onepass-delete-or) | |
Changes
Changes to src/delete.c.
︙ | ︙ | |||
236 237 238 239 240 241 242 243 244 245 246 247 248 249 | ** references within expression pExpr have already been successfully ** resolved to this table. This function sets the pSrc->a[0].colUsed ** field to reflect the set of table columns used by pExpr (and no ** others). */ static void deleteSetColUsed(Parse *pParse, SrcList *pSrc, Expr *pExpr){ Walker w; memset(&w, 0, sizeof(w)); w.pParse = pParse; w.u.pSrcList = pSrc; w.xExprCallback = deleteSetColUsedExpr; w.xSelectCallback2 = deleteSetColUsedSelect; pSrc->a[0].colUsed = 0; sqlite3WalkExpr(&w, pExpr); | > | 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 | ** references within expression pExpr have already been successfully ** resolved to this table. This function sets the pSrc->a[0].colUsed ** field to reflect the set of table columns used by pExpr (and no ** others). */ static void deleteSetColUsed(Parse *pParse, SrcList *pSrc, Expr *pExpr){ Walker w; assert( pSrc->nSrc==1 ); memset(&w, 0, sizeof(w)); w.pParse = pParse; w.u.pSrcList = pSrc; w.xExprCallback = deleteSetColUsedExpr; w.xSelectCallback2 = deleteSetColUsedSelect; pSrc->a[0].colUsed = 0; sqlite3WalkExpr(&w, pExpr); |
︙ | ︙ | |||
354 355 356 357 358 359 360 | int rc = SQLITE_OK; /* Return code */ Expr *pExpr = 0; /* OR'd expression */ int i; /* Counter variable */ assert( !IsVirtual(pTab) && bComplex==0 ); /* This loop iterates once for each OR-connected term in the WHERE clause */ for(i=0; rc==SQLITE_OK && (pExpr=sqlite3WhereSplitExpr(pWInfo, i)); i++){ | | | | > | 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 | int rc = SQLITE_OK; /* Return code */ Expr *pExpr = 0; /* OR'd expression */ int i; /* Counter variable */ assert( !IsVirtual(pTab) && bComplex==0 ); /* This loop iterates once for each OR-connected term in the WHERE clause */ for(i=0; rc==SQLITE_OK && (pExpr=sqlite3WhereSplitExpr(pWInfo, i)); i++){ if( db->mallocFailed==0 ){ deleteSetColUsed(pParse, pTabList, pExpr); rc = deleteFrom(pParse, pTabList, pExpr, 0, 0, 0, memCnt, nIdx); } sqlite3ExprDelete(db, pExpr); } sqlite3WhereInfoFree(db, pWInfo); return rc; } /* Keep track of the number of rows to be deleted */ |
︙ | ︙ |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
3447 3448 3449 3450 3451 3452 3453 | int sqlite3WhereIsSorted(WhereInfo*); int sqlite3WhereContinueLabel(WhereInfo*); int sqlite3WhereBreakLabel(WhereInfo*); int sqlite3WhereOkOnePass(WhereInfo*, int*); #define ONEPASS_OFF 0 /* Use of ONEPASS not allowed */ #define ONEPASS_SINGLE 1 /* ONEPASS valid for a single row update */ #define ONEPASS_MULTI 2 /* ONEPASS is valid for multiple rows */ | | | 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 | int sqlite3WhereIsSorted(WhereInfo*); int sqlite3WhereContinueLabel(WhereInfo*); int sqlite3WhereBreakLabel(WhereInfo*); int sqlite3WhereOkOnePass(WhereInfo*, int*); #define ONEPASS_OFF 0 /* Use of ONEPASS not allowed */ #define ONEPASS_SINGLE 1 /* ONEPASS valid for a single row update */ #define ONEPASS_MULTI 2 /* ONEPASS is valid for multiple rows */ #define ONEPASS_SPLIT_DELETE 3 /* DELETE should be split into multiple ops */ void sqlite3ExprCodeLoadIndexColumn(Parse*, Index*, int, int, int); int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, u8); void sqlite3ExprCodeGetColumnToReg(Parse*, Table*, int, int, int); void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int); void sqlite3ExprCodeMove(Parse*, int, int, int); void sqlite3ExprCacheStore(Parse*, int, int, int); void sqlite3ExprCachePush(Parse*); |
︙ | ︙ |
Changes to src/where.c.
︙ | ︙ | |||
1803 1804 1805 1806 1807 1808 1809 | ** OR-connected sub-expressions are numbered contiguously starting from ** zero. The sub-expression to return is identified by the iExpr parameter ** passed to this function. If iExpr is equal to or greater than the number ** of sub-expressions, NULL is returned. Otherwise, a pointer to a copy of ** sub-expression iExpr is returned. The caller is responsible for eventually ** deleting this object using sqlite3ExprDelete(). ** | | | | > > | 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 | ** OR-connected sub-expressions are numbered contiguously starting from ** zero. The sub-expression to return is identified by the iExpr parameter ** passed to this function. If iExpr is equal to or greater than the number ** of sub-expressions, NULL is returned. Otherwise, a pointer to a copy of ** sub-expression iExpr is returned. The caller is responsible for eventually ** deleting this object using sqlite3ExprDelete(). ** ** If an OOM error occurs, the mallocFailed field of the database handle ** (pWInfo->pParse->db->mallocFailed) is set to record the error. Even ** if an OOM error occurs, this function may return a non-NULL pointer. In ** this case the caller is still responsible for deleting the returned ** object, even though it is not safe to use. */ Expr *sqlite3WhereSplitExpr(WhereInfo *pWInfo, int iExpr){ sqlite3 *db = pWInfo->pParse->db; WhereLoop *pLoop = pWInfo->a[0].pWLoop; WhereTerm *pTerm = pLoop->aLTerm[0]; WhereClause *pOrWC = &pTerm->u.pOrInfo->wc; Expr *pExpr = 0; |
︙ | ︙ | |||
4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 | if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 ){ int wsFlags = pWInfo->a[0].pWLoop->wsFlags; int bOnerow = (wsFlags & WHERE_ONEROW)!=0; if( bOnerow || ( (wctrlFlags & WHERE_ONEPASS_MULTIROW) && 0==(wsFlags & WHERE_VIRTUALTABLE) )){ if( (wsFlags & WHERE_MULTI_OR) && (wctrlFlags & WHERE_ONEPASS_MULTIROW) ){ pWInfo->eOnePass = ONEPASS_SPLIT_DELETE; return pWInfo; } pWInfo->eOnePass = bOnerow ? ONEPASS_SINGLE : ONEPASS_MULTI; if( HasRowid(pTabList->a[0].pTab) && (wsFlags & WHERE_IDX_ONLY) ){ if( wctrlFlags & WHERE_ONEPASS_MULTIROW ){ bFordelete = OPFLAG_FORDELETE; | > > > > > > > | 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 | if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 ){ int wsFlags = pWInfo->a[0].pWLoop->wsFlags; int bOnerow = (wsFlags & WHERE_ONEROW)!=0; if( bOnerow || ( (wctrlFlags & WHERE_ONEPASS_MULTIROW) && 0==(wsFlags & WHERE_VIRTUALTABLE) )){ if( (wsFlags & WHERE_MULTI_OR) && (wctrlFlags & WHERE_ONEPASS_MULTIROW) ){ /* This call is being made as part of a DELETE program and the ** optimizer has indicated that the OR-optimization is the best ** approach. In this case it is better to let the caller generate ** a separate loop for each OR'd term than to actually go ahead ** and code the OR-optimized loop. Set the value returned by ** sqlite3WhereOkOnePass() to ONEPASS_SPLIT_DELETE to communicate ** this to the caller and return early. */ pWInfo->eOnePass = ONEPASS_SPLIT_DELETE; return pWInfo; } pWInfo->eOnePass = bOnerow ? ONEPASS_SINGLE : ONEPASS_MULTI; if( HasRowid(pTabList->a[0].pTab) && (wsFlags & WHERE_IDX_ONLY) ){ if( wctrlFlags & WHERE_ONEPASS_MULTIROW ){ bFordelete = OPFLAG_FORDELETE; |
︙ | ︙ |
Changes to test/delete4.test.
︙ | ︙ | |||
188 189 190 191 192 193 194 195 196 | do_execsql_test 5.$tn.$tn2.2 { SELECT count(*), md5sum(i, a, b, c) FROM t5 } $res($tn2) execsql ROLLBACK } } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 | do_execsql_test 5.$tn.$tn2.2 { SELECT count(*), md5sum(i, a, b, c) FROM t5 } $res($tn2) execsql ROLLBACK } } #------------------------------------------------------------------------- # Test that nested OR optimizations work. # do_execsql_test 6.0 { CREATE TABLE t6(a, b, c, d); CREATE INDEX t6a ON t6(a); CREATE INDEX t6b ON t6(b); CREATE INDEX t6c ON t6(c); WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000 ) INSERT INTO t6 SELECT i, i*2, i*3, i%2 FROM s; } proc do_61_test {tn sql lDel} { uplevel [list do_execsql_test $tn " BEGIN; $sql; WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000 ) SELECT i FROM s EXCEPT SELECT a FROM t6 ORDER BY 1; ROLLBACK; " $lDel] } do_61_test 6.1 { DELETE FROM t6 WHERE a=22 OR b=90 OR (d=0 AND (b=400 OR c=303)); } {22 45 200} do_61_test 6.2 { DELETE FROM t6 WHERE a=22 OR b=90 OR (d=1 AND (b=400 OR c=303)); } {22 45 101} do_61_test 6.3 { DELETE FROM t6 WHERE (d=0 AND (a=100 OR b=150)) OR (d=1 AND (b=50 OR c=603)); } {25 100 201} finish_test |
Added test/delete_fault.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 | # 2015 December 10 # # 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. # #*********************************************************************** # This file implements regression tests for SQLite library. The focus # of this file is fault-injection into DELETE statements. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix delete_fault do_execsql_test 1.0 { CREATE TABLE t6(a, b, c, d); CREATE INDEX t6a ON t6(a); CREATE INDEX t6b ON t6(b); CREATE INDEX t6c ON t6(c); WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000 ) INSERT INTO t6 SELECT i, i*2, i*3, i%2 FROM s; } faultsim_save_and_close proc deleted_t6_rows {} { db eval { WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000 ) SELECT i FROM s EXCEPT SELECT a FROM t6 ORDER BY 1; } } do_faultsim_test 1 -faults oom-t* -prep { faultsim_restore_and_reopen } -body { execsql { DELETE FROM t6 WHERE (d=0 AND (a=100 OR b=150)) OR (d=1 AND (b=50 OR c=603)) } } -test { faultsim_test_result {0 {}} if {$testrc==0} { set lDel [deleted_t6_rows] if {$lDel != "25 100 201"} { error "lDel is $lDel" } } } finish_test |
Changes to test/fordelete.test.
︙ | ︙ | |||
103 104 105 106 107 108 109 110 111 112 113 114 115 116 | 2 { (x=? OR z=?) AND y=? } { t3* t3* t3x* t3x* t3xy t3xy* t3zx* t3zx* t3zy t3zy* } 3 { (y=? OR z=?) AND x=? } { t3 t3x t3xy* t3zx* t3zy* } } { do_adp_test 2.3.$tn "DELETE FROM t3 WHERE $where" $res } #------------------------------------------------------------------------- # Test that a record that consists of the bytes: # # 0x01 0x00 # # is interpreted by OP_Column as a vector of NULL values (assuming the | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 141 142 143 144 145 146 147 148 149 150 | 2 { (x=? OR z=?) AND y=? } { t3* t3* t3x* t3x* t3xy t3xy* t3zx* t3zx* t3zy t3zy* } 3 { (y=? OR z=?) AND x=? } { t3 t3x t3xy* t3zx* t3zy* } } { do_adp_test 2.3.$tn "DELETE FROM t3 WHERE $where" $res } do_execsql_test 2.4.0 { CREATE TABLE t6(a, b, c, d); CREATE INDEX t6a ON t6(a); CREATE INDEX t6b ON t6(b); CREATE INDEX t6c ON t6(c); } do_adp_test 2.4.1 { DELETE FROM t6 WHERE a=22 OR b=90 OR (d=0 AND (b=400 OR c=303)); } [lsort { t6* t6a t6b* t6c* t6* t6a* t6b t6c* t6 t6a* t6b t6c* t6 t6a* t6b* t6c }] do_adp_test 2.4.2 { DELETE FROM t6 WHERE a=22 OR b=90 OR (d=1 AND (b=400 OR c=303)); } [lsort { t6* t6a t6b* t6c* t6* t6a* t6b t6c* t6 t6a* t6b t6c* t6 t6a* t6b* t6c }] do_adp_test 2.4.3 { DELETE FROM t6 WHERE (d=0 AND (a=100 OR b=150)) OR (d=1 AND (b=50 OR c=603)); } [lsort { t6 t6a t6b* t6c* t6 t6a* t6b t6c* t6 t6a* t6b t6c* t6 t6a* t6b* t6c }] #------------------------------------------------------------------------- # Test that a record that consists of the bytes: # # 0x01 0x00 # # is interpreted by OP_Column as a vector of NULL values (assuming the |
︙ | ︙ |
Changes to test/table.test.
︙ | ︙ | |||
833 834 835 836 837 838 839 | CREATE TABLE t19 AS SELECT * FROM sqlite_master; SELECT name FROM t19 ORDER BY name; } {{} savepoint t10 t11 t12 t13 t16 t2 t3 t3\"xyz t4\"abc t7 t8 t9 tablet8 test1 weird} # At one point the DROP TABLE in the following was causing problems on an # experimental branch. # | > | | 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 | CREATE TABLE t19 AS SELECT * FROM sqlite_master; SELECT name FROM t19 ORDER BY name; } {{} savepoint t10 t11 t12 t13 t16 t2 t3 t3\"xyz t4\"abc t7 t8 t9 tablet8 test1 weird} # At one point the DROP TABLE in the following was causing problems on an # experimental branch. # reset_db do_execsql_test table-20.0 { CREATE TABLE t20(a PRIMARY KEY, b, c); CREATE TRIGGER tr6 AFTER INSERT ON t20 BEGIN SELECT 1+1+1; END; ANALYZE; DROP TABLE t20; SELECT name FROM sqlite_master } {sqlite_stat1 sqlite_stat4} |
︙ | ︙ |