SQLite

Check-in [ff69f823f2]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Fix problems related to savepoint rollback and fts3.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | vtab-conflict
Files: files | file ages | folders
SHA1: ff69f823f23e6fb6e8b2857c4576d9c0732d9572
User & Date: dan 2011-04-27 12:08:04.566
Context
2011-04-27
16:02
Add documentation for the newly introduced sqlite3_vtab_config() and on_conflict() API functions. Test that encountering an SQLITE_MISMATCH in fts3 does not corrupt the full text index. (check-in: abdd70ae04 user: dan tags: vtab-conflict)
12:08
Fix problems related to savepoint rollback and fts3. (check-in: ff69f823f2 user: dan tags: vtab-conflict)
2011-04-26
19:21
Extra tests for fts3. And fixes for conflict-handling related problems in fts3. (check-in: fb4a355871 user: dan tags: vtab-conflict)
Changes
Side-by-Side Diff Ignore Whitespace Patch
Changes to src/sqliteInt.h.
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
867
868
869
870
871
872
873



874
875
876
877
878
879
880







-
-
-







#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
  int (*xProgress)(void *);     /* The progress callback */
  void *pProgressArg;           /* Argument to the progress callback */
  int nProgressOps;             /* Number of opcodes for progress callback */
#endif
#ifndef SQLITE_OMIT_VIRTUALTABLE
  Hash aModule;                 /* populated by sqlite3_create_module() */
#if 0
  Table *pVTab;                 /* vtab with active Connect/Create method */
#endif
  VtabCtx *pVtabCtx;            /* Context for active vtab connect/create */
  VTable **aVTrans;             /* Virtual tables with open transactions */
  int nVTrans;                  /* Allocated size of aVTrans */
  VTable *pDisconnect;    /* Disconnect these in next sqlite3_prepare() */
#endif
  FuncDefHash aFunc;            /* Hash table of connection functions */
  Hash aCollSeq;                /* All collating sequences */
Changes to src/test1.c.
10
11
12
13
14
15
16

17
18
19
20
21
22
23
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24







+







**
*************************************************************************
** Code for testing all sorts of SQLite interfaces.  This code
** is not included in the SQLite library.  It is used for automated
** testing of the SQLite library.
*/
#include "sqliteInt.h"
#include "vdbeInt.h"
#include "tcl.h"
#include <stdlib.h>
#include <string.h>

/*
** This is a copy of the first part of the SqliteDb structure in 
** tclsqlite.c.  We need it here so that the get_sqlite_pointer routine
2321
2322
2323
2324
2325
2326
2327


























2328
2329
2330
2331
2332
2333
2334
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  }

  if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
  rc = sqlite3_stmt_readonly(pStmt);
  Tcl_SetObjResult(interp, Tcl_NewBooleanObj(rc));
  return TCL_OK;
}

/*
** Usage:  uses_stmt_journal  STMT
**
** Return true if STMT uses a statement journal.
*/
static int uses_stmt_journal(
  void * clientData,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  sqlite3_stmt *pStmt;
  int rc;

  if( objc!=2 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"",
        Tcl_GetStringFromObj(objv[0], 0), " STMT", 0);
    return TCL_ERROR;
  }

  if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
  rc = sqlite3_stmt_readonly(pStmt);
  Tcl_SetObjResult(interp, Tcl_NewBooleanObj(((Vdbe *)pStmt)->usesStmtJournal));
  return TCL_OK;
}


/*
** Usage:  sqlite3_reset  STMT 
**
** Reset a statement handle.
*/
5579
5580
5581
5582
5583
5584
5585

5586
5587
5588
5589
5590
5591
5592
5606
5607
5608
5609
5610
5611
5612
5613
5614
5615
5616
5617
5618
5619
5620







+







     { "sqlite3_expired",               test_expired       ,0 },
     { "sqlite3_transfer_bindings",     test_transfer_bind ,0 },
     { "sqlite3_changes",               test_changes       ,0 },
     { "sqlite3_step",                  test_step          ,0 },
     { "sqlite3_sql",                   test_sql           ,0 },
     { "sqlite3_next_stmt",             test_next_stmt     ,0 },
     { "sqlite3_stmt_readonly",         test_stmt_readonly ,0 },
     { "uses_stmt_journal",             uses_stmt_journal ,0 },

     { "sqlite3_release_memory",        test_release_memory,     0},
     { "sqlite3_soft_heap_limit",       test_soft_heap_limit,    0},
     { "sqlite3_thread_cleanup",        test_thread_cleanup,     0},
     { "sqlite3_pager_refcounts",       test_pager_refcounts,    0},

     { "sqlite3_load_extension",        test_load_extension,     0},
Changes to src/vdbe.c.
2576
2577
2578
2579
2580
2581
2582








2583
2584
2585
2586
2587
2588
2589
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597







+
+
+
+
+
+
+
+







      */
      sqlite3SetString(&p->zErrMsg, db, "cannot open savepoint - "
        "SQL statements in progress");
      rc = SQLITE_BUSY;
    }else{
      nName = sqlite3Strlen30(zName);

      /* This call is Ok even if this savepoint is actually a transaction
      ** savepoint (and therefore should not prompt xSavepoint()) callbacks.
      ** If this is a transaction savepoint being opened, it is guaranteed
      ** that the db->aVTrans[] array is empty.  */
      assert( db->autoCommit==0 || db->nVTrans==0 );
      rc = sqlite3VtabSavepoint(db, SAVEPOINT_BEGIN, p->iStatement);
      if( rc!=SQLITE_OK ) goto abort_due_to_error;

      /* Create a new savepoint structure. */
      pNew = sqlite3DbMallocRaw(db, sizeof(Savepoint)+nName+1);
      if( pNew ){
        pNew->zName = (char *)&pNew[1];
        memcpy(pNew->zName, zName, nName+1);
    
        /* If there is no open transaction, then mark this as a special
2682
2683
2684
2685
2686
2687
2688





2689
2690
2691
2692
2693
2694
2695
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708







+
+
+
+
+







        sqlite3DbFree(db, pSavepoint);
        if( !isTransaction ){
          db->nSavepoint--;
        }
      }else{
        db->nDeferredCons = pSavepoint->nDeferredCons;
      }

      if( !isTransaction ){
        rc = sqlite3VtabSavepoint(db, p1, iSavepoint);
        if( rc!=SQLITE_OK ) goto abort_due_to_error;
      }
    }
  }

  break;
}

/* Opcode: AutoCommit P1 P2 * * *
Changes to src/vtab.c.
652
653
654
655
656
657
658
659

660
661
662
663
664
665
666
667
652
653
654
655
656
657
658

659

660
661
662
663
664
665
666







-
+
-







  Parse *pParse;

  int rc = SQLITE_OK;
  Table *pTab;
  char *zErr = 0;

  sqlite3_mutex_enter(db->mutex);
  pTab = db->pVtabCtx->pTab;
  if( !db->pVtabCtx || !(pTab = db->pVtabCtx->pTab) ){
  if( !pTab ){
    sqlite3Error(db, SQLITE_MISUSE, 0);
    sqlite3_mutex_leave(db->mutex);
    return SQLITE_MISUSE_BKPT;
  }
  assert( (pTab->tabFlags & TF_Virtual)!=0 );

  pParse = sqlite3StackAllocZero(db, sizeof(*pParse));
848
849
850
851
852
853
854















855
856
857
858
859
860
861



862
863
864
865
866
867
868
869
870
871
872
873
874















875
876
877
878
879
880
881
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869

870
871
872


873
874
875













876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-



-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    if( rc==SQLITE_OK ){
      rc = addToVTrans(db, pVTab);
    }
  }
  return rc;
}

/*
** Invoke either the xSavepoint, xRollbackTo or xRelease method of all
** virtual tables that currently have an open transaction. Pass iSavepoint
** as the second argument to the virtual table method invoked.
**
** If op is SAVEPOINT_BEGIN, the xSavepoint method is invoked. If it is
** SAVEPOINT_ROLLBACK, the xRollbackTo method. Otherwise, if op is 
** SAVEPOINT_RELEASE, then the xRelease method of each virtual table with
** an open transaction is invoked.
**
** If any virtual table method returns an error code other than SQLITE_OK, 
** processing is abandoned and the error returned to the caller of this
** function immediately. If all calls to virtual table methods are successful,
** SQLITE_OK is returned.
*/
int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){
  int i;
  int rc = SQLITE_OK;

  assert( op==SAVEPOINT_RELEASE||op==SAVEPOINT_ROLLBACK||op==SAVEPOINT_BEGIN );

  for(i=0; rc==SQLITE_OK && i<db->nVTrans; i++){
  if( db->aVTrans ){
    int i;
    for(i=0; rc==SQLITE_OK && i<db->nVTrans; i++){
    sqlite3_vtab *pVtab = db->aVTrans[i]->pVtab;
    sqlite3_module *pMod = db->aVTrans[i]->pMod->pModule;
    if( pMod->iVersion>=1 ){
      switch( op ){
        case SAVEPOINT_BEGIN:
          rc = pMod->xSavepoint(pVtab, iSavepoint);
          break;
        case SAVEPOINT_ROLLBACK:
          rc = pMod->xRollbackTo(pVtab, iSavepoint);
          break;
        default:
          rc = pMod->xRelease(pVtab, iSavepoint);
          break;
      const sqlite3_module *pMod = db->aVTrans[i]->pMod->pModule;
      if( pMod->iVersion>=1 ){
        int (*xMethod)(sqlite3_vtab *, int);
        switch( op ){
          case SAVEPOINT_BEGIN:
            xMethod = pMod->xSavepoint;
            break;
          case SAVEPOINT_ROLLBACK:
            xMethod = pMod->xRollbackTo;
            break;
          default:
            xMethod = pMod->xRelease;
            break;
        }
        if( xMethod ) rc = xMethod(db->aVTrans[i]->pVtab, iSavepoint);
      }
    }
  }
  return rc;
}

/*
Changes to test/fts3conf.test.
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
92
93
94
95





96
97
98
99
100
101





102
103
104
105
106
107
108
109
110
111
112
113
114
115














116
117
118
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
92





93
94
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







+
+
+
+
+
+
+
+



















-
-
-
-
-
-
+
+
+
+
+
+

-
-
-
-
-
+
+
+
+
+

-
-
-
-
-
+
+
+
+
+

-
-
-
-
-
+
+
+
+
+













-
+
+
+
+
+
+
+
+
+
+
+
+
+
+



    DROP TABLE temp.fts3check2;
    DROP TABLE temp.fts3check3;
  }
  
  uplevel [list do_test $tn [list set {} $m1] $m2]
}

proc sql_uses_stmt {db sql} {
  set stmt [sqlite3_prepare db $sql -1 dummy]
  set uses [uses_stmt_journal $stmt]
  sqlite3_finalize $stmt
  return $uses
}




do_execsql_test 1.0.1 {
  CREATE VIRTUAL TABLE t1 USING fts3(x);
  INSERT INTO t1(rowid, x) VALUES(1, 'a b c d');
  INSERT INTO t1(rowid, x) VALUES(2, 'e f g h');

  CREATE TABLE source(a, b);
  INSERT INTO source VALUES(4, 'z');
  INSERT INTO source VALUES(2, 'y');
}
db_save_and_close

set T1 "INTO t1(rowid, x) VALUES(1, 'x')"
set T2 "INTO t1(rowid, x) SELECT * FROM source"

set T3 "t1 SET docid = 2 WHERE docid = 1"
set T4 "t1 SET docid = CASE WHEN docid = 1 THEN 4 ELSE 3 END WHERE docid <=2"

foreach {tn sql constraint data} [subst {
  1    "INSERT OR ROLLBACK $T1"   1 {{a b c d} {e f g h}}
  2    "INSERT OR ABORT    $T1"   1 {{a b c d} {e f g h} {i j k l}}
  3    "INSERT OR FAIL     $T1"   1 {{a b c d} {e f g h} {i j k l}}
  4    "INSERT OR IGNORE   $T1"   0 {{a b c d} {e f g h} {i j k l}}
  5    "INSERT OR REPLACE  $T1"   0 {x {e f g h} {i j k l}}
foreach {tn sql uses constraint data} [subst {
  1    "INSERT OR ROLLBACK $T1"   0 1 {{a b c d} {e f g h}}
  2    "INSERT OR ABORT    $T1"   0 1 {{a b c d} {e f g h} {i j k l}}
  3    "INSERT OR FAIL     $T1"   0 1 {{a b c d} {e f g h} {i j k l}}
  4    "INSERT OR IGNORE   $T1"   0 0 {{a b c d} {e f g h} {i j k l}}
  5    "INSERT OR REPLACE  $T1"   0 0 {x {e f g h} {i j k l}}

  6    "INSERT OR ROLLBACK $T2"   1 {{a b c d} {e f g h}}
  7    "INSERT OR ABORT    $T2"   1 {{a b c d} {e f g h} {i j k l}}
  8    "INSERT OR FAIL     $T2"   1 {{a b c d} {e f g h} {i j k l} z}
  9    "INSERT OR IGNORE   $T2"   0 {{a b c d} {e f g h} {i j k l} z}
  10   "INSERT OR REPLACE  $T2"   0 {{a b c d} y {i j k l} z}
  6    "INSERT OR ROLLBACK $T2"   1 1 {{a b c d} {e f g h}}
  7    "INSERT OR ABORT    $T2"   1 1 {{a b c d} {e f g h} {i j k l}}
  8    "INSERT OR FAIL     $T2"   1 1 {{a b c d} {e f g h} {i j k l} z}
  9    "INSERT OR IGNORE   $T2"   1 0 {{a b c d} {e f g h} {i j k l} z}
  10   "INSERT OR REPLACE  $T2"   1 0 {{a b c d} y {i j k l} z}

  11   "UPDATE OR ROLLBACK $T3"   1 {{a b c d} {e f g h}}
  12   "UPDATE OR ABORT    $T3"   1 {{a b c d} {e f g h} {i j k l}}
  13   "UPDATE OR FAIL     $T3"   1 {{a b c d} {e f g h} {i j k l}}
  14   "UPDATE OR IGNORE   $T3"   0 {{a b c d} {e f g h} {i j k l}}
  15   "UPDATE OR REPLACE  $T3"   0 {{a b c d} {i j k l}}
  11   "UPDATE OR ROLLBACK $T3"   1 1 {{a b c d} {e f g h}}
  12   "UPDATE OR ABORT    $T3"   1 1 {{a b c d} {e f g h} {i j k l}}
  13   "UPDATE OR FAIL     $T3"   1 1 {{a b c d} {e f g h} {i j k l}}
  14   "UPDATE OR IGNORE   $T3"   1 0 {{a b c d} {e f g h} {i j k l}}
  15   "UPDATE OR REPLACE  $T3"   1 0 {{a b c d} {i j k l}}

  16   "UPDATE OR ROLLBACK $T4"   1 {{a b c d} {e f g h}}
  17   "UPDATE OR ABORT    $T4"   1 {{a b c d} {e f g h} {i j k l}}
  18   "UPDATE OR FAIL     $T4"   1 {{e f g h} {i j k l} {a b c d}}
  19   "UPDATE OR IGNORE   $T4"   0 {{e f g h} {i j k l} {a b c d}}
  20   "UPDATE OR REPLACE  $T4"   0 {{e f g h} {a b c d}}
  16   "UPDATE OR ROLLBACK $T4"   1 1 {{a b c d} {e f g h}}
  17   "UPDATE OR ABORT    $T4"   1 1 {{a b c d} {e f g h} {i j k l}}
  18   "UPDATE OR FAIL     $T4"   1 1 {{e f g h} {i j k l} {a b c d}}
  19   "UPDATE OR IGNORE   $T4"   1 0 {{e f g h} {i j k l} {a b c d}}
  20   "UPDATE OR REPLACE  $T4"   1 0 {{e f g h} {a b c d}}
}] {
  db_restore_and_reopen
  execsql { 
    BEGIN;
      INSERT INTO t1(rowid, x) VALUES(3, 'i j k l');
  }
  set R(0) {0 {}}
  set R(1) {1 {constraint failed}}
  do_catchsql_test 1.$tn.1 $sql $R($constraint)
  do_catchsql_test 1.$tn.2 { SELECT * FROM t1 } [list 0 $data]
  catchsql COMMIT

  fts3_integrity 1.$tn.3 db t1
}

  do_test 1.$tn.4 [list sql_uses_stmt db $sql] $uses
}

do_execsql_test 2.1.1 {
  DELETE FROM t1;
  BEGIN;
    INSERT INTO t1 VALUES('a b c');
    SAVEPOINT a;
      INSERT INTO t1 VALUES('x y z');
    ROLLBACK TO a;
  COMMIT;
}
fts3_integrity 2.1.2 db t1


finish_test