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 | SQL 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 |
Context
2015-12-12
| ||
19:23 | Update some stale comments in delete.c. No changes to code. Leaf check-in: f59a3326 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: 21526012 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: 57b700ba 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
...
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
|
** 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); ................................................................................ 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 ) break; 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 */ |
>
|
|
|
>
|
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
...
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
|
** 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); ................................................................................ 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 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 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*); |
| |
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
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
....
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
|
** 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, NULL is returned. In this case the mallocFailed ** field of the database handle (pWInfo->pParse->db->mallocFailed) is set ** to record the error. */ 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; ................................................................................ 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; |
|
|
|
>
>
>
>
>
>
>
>
>
|
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
....
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
|
** 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; ................................................................................ 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 840 841 842 843 844 845 846 847 848 849 |
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. # do_execsql_test 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} finish_test |
> | |
833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 |
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} finish_test |