/ Check-in [21526012]
Login

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: 21526012c274e709d672dbafb9e16158c0749341
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
Hide Diffs Unified Diffs Ignore Whitespace Patch

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