]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add a new row type to RBU (a peer of insert, update and delete) - "delete then insert". rbu-delete-then-insert
authordan <dan@noemail.net>
Mon, 7 Mar 2016 17:39:30 +0000 (17:39 +0000)
committerdan <dan@noemail.net>
Mon, 7 Mar 2016 17:39:30 +0000 (17:39 +0000)
FossilOrigin-Name: 169311c85b30f625bdb6986c9cd11db70942d73b

ext/rbu/rbuC.test [new file with mode: 0644]
ext/rbu/sqlite3rbu.c
manifest
manifest.uuid

diff --git a/ext/rbu/rbuC.test b/ext/rbu/rbuC.test
new file mode 100644 (file)
index 0000000..89fd015
--- /dev/null
@@ -0,0 +1,142 @@
+# 2016 March 7
+#
+# 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.
+#
+#***********************************************************************
+# Tests for RBU focused on the REPLACE operation (rbu_control column
+# contains integer value 2).
+#
+
+source [file join [file dirname [info script]] rbu_common.tcl]
+set ::testprefix rbuC
+
+#-------------------------------------------------------------------------
+# This test is actually of an UPDATE directive. Just to establish that
+# these work with UNIQUE indexes before preceding to REPLACE.
+#
+do_execsql_test 1.0 {
+  CREATE TABLE t1(i INTEGER PRIMARY KEY, a, b, c UNIQUE);
+  INSERT INTO t1 VALUES(1, 'a', 'b', 'c');
+}
+
+forcedelete rbu.db
+do_execsql_test 1.1 {
+  ATTACH 'rbu.db' AS rbu;
+  CREATE TABLE rbu.data_t1(i, a, b, c, rbu_control);
+  INSERT INTO data_t1 VALUES(1, 'a', 'b', 'c', '.xxx');
+}
+
+do_test 1.2 {
+  step_rbu test.db rbu.db
+} {SQLITE_DONE}
+
+do_execsql_test 1.3 {
+  SELECT * FROM t1
+} {
+  1 a b c
+}
+
+#-------------------------------------------------------------------------
+#
+foreach {tn schema} {
+  1 {
+    CREATE TABLE t1(i INTEGER PRIMARY KEY, a, b, c UNIQUE);
+    CREATE INDEX t1a ON t1(a);
+  }
+  2 {
+    CREATE TABLE t1(i PRIMARY KEY, a, b, c UNIQUE);
+    CREATE INDEX t1a ON t1(a);
+  }
+  3 {
+    CREATE TABLE t1(i PRIMARY KEY, a, b, c UNIQUE) WITHOUT ROWID;
+    CREATE INDEX t1a ON t1(a);
+  }
+} {
+  reset_db
+  forcedelete rbu.db
+  execsql $schema
+
+  do_execsql_test 2.$tn.0 {
+    INSERT INTO t1 VALUES(1, 'a', 'b', 'c');
+    INSERT INTO t1 VALUES(2, 'b', 'c', 'd');
+    INSERT INTO t1 VALUES(3, 'c', 'd', 'e');
+  }
+  
+  do_execsql_test 2.$tn.1 {
+    ATTACH 'rbu.db' AS rbu;
+    CREATE TABLE rbu.data_t1(i, a, b, c, rbu_control);
+    INSERT INTO data_t1 VALUES(1, 1, 2, 3, 2);
+    INSERT INTO data_t1 VALUES(3, 'c', 'd', 'e', 2);
+    INSERT INTO data_t1 VALUES(4, 'd', 'e', 'f', 2);
+  }
+  
+  do_test 2.$tn.2 {
+    step_rbu test.db rbu.db
+  } {SQLITE_DONE}
+  
+  do_execsql_test 2.$tn.3 {
+    SELECT * FROM t1 ORDER BY i
+  } {
+    1 1 2 3
+    2 b c d
+    3 c d e
+    4 d e f
+  }
+  
+  integrity_check 2.$tn.4
+}
+
+foreach {tn schema} {
+  1 {
+    CREATE TABLE t1(a, b, c UNIQUE);
+    CREATE INDEX t1a ON t1(a);
+  }
+
+  2 {
+    CREATE VIRTUAL TABLE t1 USING fts5(a, b, c);
+  }
+} {
+  if {$tn==2} { ifcapable !fts5 break }
+  reset_db
+  forcedelete rbu.db
+  execsql $schema
+
+  do_execsql_test 3.$tn.0 {
+    INSERT INTO t1 VALUES('a', 'b', 'c');
+    INSERT INTO t1 VALUES('b', 'c', 'd');
+    INSERT INTO t1 VALUES('c', 'd', 'e');
+  }
+  
+  do_execsql_test 3.$tn.1 {
+    ATTACH 'rbu.db' AS rbu;
+    CREATE TABLE rbu.data_t1(rbu_rowid, a, b, c, rbu_control);
+    INSERT INTO data_t1 VALUES(1, 1, 2, 3, 2);
+    INSERT INTO data_t1 VALUES(3, 'c', 'd', 'e', 2);
+    INSERT INTO data_t1 VALUES(4, 'd', 'e', 'f', 2);
+  }
+  
+  do_test 3.$tn.2 {
+    step_rbu test.db rbu.db
+  } {SQLITE_DONE}
+  
+  do_execsql_test 3.$tn.3 {
+    SELECT rowid, * FROM t1 ORDER BY 1
+  } {
+    1 1 2 3
+    2 b c d
+    3 c d e
+    4 d e f
+  }
+  
+  integrity_check 3.$tn.4
+}
+
+
+
+finish_test
+
index 682becbb5c279ade27cb63bff617233dc5717ef7..7f2739998807a73ffc45a79133b0829350e5d50b 100644 (file)
@@ -280,10 +280,11 @@ struct RbuObjIter {
 */
 #define RBU_INSERT     1          /* Insert on a main table b-tree */
 #define RBU_DELETE     2          /* Delete a row from a main table b-tree */
-#define RBU_IDX_DELETE 3          /* Delete a row from an aux. index b-tree */
-#define RBU_IDX_INSERT 4          /* Insert on an aux. index b-tree */
-#define RBU_UPDATE     5          /* Update a row in a main table b-tree */
+#define RBU_REPLACE    3          /* Delete and then insert a row */
+#define RBU_IDX_DELETE 4          /* Delete a row from an aux. index b-tree */
+#define RBU_IDX_INSERT 5          /* Insert on an aux. index b-tree */
 
+#define RBU_UPDATE     6          /* Update a row in a main table b-tree */
 
 /*
 ** A single step of an incremental checkpoint - frame iWalFrame of the wal
@@ -1909,13 +1910,13 @@ static int rbuObjIterPrepareAll(
           );
         }else{
           zSql = sqlite3_mprintf(
+              "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' "
+              "UNION ALL "
               "SELECT %s, rbu_control FROM '%q' "
               "WHERE typeof(rbu_control)='integer' AND rbu_control!=1 "
-              "UNION ALL "
-              "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' "
               "ORDER BY %s%s",
-              zCollist, pIter->zDataTbl, 
               zCollist, p->zStateDb, pIter->zDataTbl, 
+              zCollist, pIter->zDataTbl, 
               zCollist, zLimit
           );
         }
@@ -1981,17 +1982,17 @@ static int rbuObjIterPrepareAll(
         rbuMPrintfExec(p, p->dbMain,
             "CREATE TEMP TRIGGER rbu_delete_tr BEFORE DELETE ON \"%s%w\" "
             "BEGIN "
-            "  SELECT rbu_tmp_insert(2, %s);"
+            "  SELECT rbu_tmp_insert(3, %s);"
             "END;"
 
             "CREATE TEMP TRIGGER rbu_update1_tr BEFORE UPDATE ON \"%s%w\" "
             "BEGIN "
-            "  SELECT rbu_tmp_insert(2, %s);"
+            "  SELECT rbu_tmp_insert(3, %s);"
             "END;"
 
             "CREATE TEMP TRIGGER rbu_update2_tr AFTER UPDATE ON \"%s%w\" "
             "BEGIN "
-            "  SELECT rbu_tmp_insert(3, %s);"
+            "  SELECT rbu_tmp_insert(4, %s);"
             "END;",
             zWrite, zTbl, zOldlist,
             zWrite, zTbl, zOldlist,
@@ -2509,14 +2510,12 @@ static int rbuStepType(sqlite3rbu *p, const char **pzMask){
   switch( sqlite3_column_type(p->objiter.pSelect, iCol) ){
     case SQLITE_INTEGER: {
       int iVal = sqlite3_column_int(p->objiter.pSelect, iCol);
-      if( iVal==0 ){
-        res = RBU_INSERT;
-      }else if( iVal==1 ){
-        res = RBU_DELETE;
-      }else if( iVal==2 ){
-        res = RBU_IDX_DELETE;
-      }else if( iVal==3 ){
-        res = RBU_IDX_INSERT;
+      switch( iVal ){
+        case 0: res = RBU_INSERT;     break;
+        case 1: res = RBU_DELETE;     break;
+        case 2: res = RBU_REPLACE;    break;
+        case 3: res = RBU_IDX_DELETE; break;
+        case 4: res = RBU_IDX_INSERT; break;
       }
       break;
     }
@@ -2555,6 +2554,67 @@ static void assertColumnName(sqlite3_stmt *pStmt, int iCol, const char *zName){
 # define assertColumnName(x,y,z)
 #endif
 
+/*
+** Argument eType must be one of RBU_INSERT, RBU_DELETE, RBU_IDX_INSERT or
+** RBU_IDX_DELETE. This function performs the work of a single
+** sqlite3rbu_step() call for the type of operation specified by eType.
+*/
+static void rbuStepOneOp(sqlite3rbu *p, int eType){
+  RbuObjIter *pIter = &p->objiter;
+  sqlite3_value *pVal;
+  sqlite3_stmt *pWriter;
+  int i;
+
+  assert( p->rc==SQLITE_OK );
+  assert( eType!=RBU_DELETE || pIter->zIdx==0 );
+
+  if( eType==RBU_IDX_DELETE || eType==RBU_DELETE ){
+    pWriter = pIter->pDelete;
+  }else{
+    pWriter = pIter->pInsert;
+  }
+
+  for(i=0; i<pIter->nCol; i++){
+    /* If this is an INSERT into a table b-tree and the table has an
+    ** explicit INTEGER PRIMARY KEY, check that this is not an attempt
+    ** to write a NULL into the IPK column. That is not permitted.  */
+    if( eType==RBU_INSERT 
+     && pIter->zIdx==0 && pIter->eType==RBU_PK_IPK && pIter->abTblPk[i] 
+     && sqlite3_column_type(pIter->pSelect, i)==SQLITE_NULL
+    ){
+      p->rc = SQLITE_MISMATCH;
+      p->zErrmsg = sqlite3_mprintf("datatype mismatch");
+      return;
+    }
+
+    if( eType==RBU_DELETE && pIter->abTblPk[i]==0 ){
+      continue;
+    }
+
+    pVal = sqlite3_column_value(pIter->pSelect, i);
+    p->rc = sqlite3_bind_value(pWriter, i+1, pVal);
+    if( p->rc ) return;
+  }
+  if( pIter->zIdx==0
+   && (pIter->eType==RBU_PK_VTAB || pIter->eType==RBU_PK_NONE) 
+  ){
+    /* For a virtual table, or a table with no primary key, the 
+    ** SELECT statement is:
+    **
+    **   SELECT <cols>, rbu_control, rbu_rowid FROM ....
+    **
+    ** Hence column_value(pIter->nCol+1).
+    */
+    assertColumnName(pIter->pSelect, pIter->nCol+1, "rbu_rowid");
+    pVal = sqlite3_column_value(pIter->pSelect, pIter->nCol+1);
+    p->rc = sqlite3_bind_value(pWriter, pIter->nCol+1, pVal);
+  }
+  if( p->rc==SQLITE_OK ){
+    sqlite3_step(pWriter);
+    p->rc = resetAndCollectError(pWriter, &p->zErrmsg);
+  }
+}
+
 /*
 ** This function does the work for an sqlite3rbu_step() call.
 **
@@ -2569,78 +2629,32 @@ static void assertColumnName(sqlite3_stmt *pStmt, int iCol, const char *zName){
 static int rbuStep(sqlite3rbu *p){
   RbuObjIter *pIter = &p->objiter;
   const char *zMask = 0;
-  int i;
   int eType = rbuStepType(p, &zMask);
 
   if( eType ){
+    assert( eType==RBU_INSERT     || eType==RBU_DELETE
+         || eType==RBU_REPLACE    || eType==RBU_IDX_DELETE
+         || eType==RBU_IDX_INSERT || eType==RBU_UPDATE
+    );
     assert( eType!=RBU_UPDATE || pIter->zIdx==0 );
 
     if( pIter->zIdx==0 && eType==RBU_IDX_DELETE ){
       rbuBadControlError(p);
     }
-    else if( 
-        eType==RBU_INSERT 
-     || eType==RBU_DELETE
-     || eType==RBU_IDX_DELETE 
-     || eType==RBU_IDX_INSERT
-    ){
-      sqlite3_value *pVal;
-      sqlite3_stmt *pWriter;
-
-      assert( eType!=RBU_UPDATE );
-      assert( eType!=RBU_DELETE || pIter->zIdx==0 );
-
-      if( eType==RBU_IDX_DELETE || eType==RBU_DELETE ){
-        pWriter = pIter->pDelete;
-      }else{
-        pWriter = pIter->pInsert;
-      }
-
-      for(i=0; i<pIter->nCol; i++){
-        /* If this is an INSERT into a table b-tree and the table has an
-        ** explicit INTEGER PRIMARY KEY, check that this is not an attempt
-        ** to write a NULL into the IPK column. That is not permitted.  */
-        if( eType==RBU_INSERT 
-         && pIter->zIdx==0 && pIter->eType==RBU_PK_IPK && pIter->abTblPk[i] 
-         && sqlite3_column_type(pIter->pSelect, i)==SQLITE_NULL
-        ){
-          p->rc = SQLITE_MISMATCH;
-          p->zErrmsg = sqlite3_mprintf("datatype mismatch");
-          goto step_out;
-        }
-
-        if( eType==RBU_DELETE && pIter->abTblPk[i]==0 ){
-          continue;
-        }
-
-        pVal = sqlite3_column_value(pIter->pSelect, i);
-        p->rc = sqlite3_bind_value(pWriter, i+1, pVal);
-        if( p->rc ) goto step_out;
-      }
-      if( pIter->zIdx==0
-       && (pIter->eType==RBU_PK_VTAB || pIter->eType==RBU_PK_NONE) 
-      ){
-        /* For a virtual table, or a table with no primary key, the 
-        ** SELECT statement is:
-        **
-        **   SELECT <cols>, rbu_control, rbu_rowid FROM ....
-        **
-        ** Hence column_value(pIter->nCol+1).
-        */
-        assertColumnName(pIter->pSelect, pIter->nCol+1, "rbu_rowid");
-        pVal = sqlite3_column_value(pIter->pSelect, pIter->nCol+1);
-        p->rc = sqlite3_bind_value(pWriter, pIter->nCol+1, pVal);
-      }
-      if( p->rc==SQLITE_OK ){
-        sqlite3_step(pWriter);
-        p->rc = resetAndCollectError(pWriter, &p->zErrmsg);
-      }
-    }else{
+    else if( eType==RBU_REPLACE ){
+      if( pIter->zIdx==0 ) rbuStepOneOp(p, RBU_DELETE);
+      if( p->rc==SQLITE_OK ) rbuStepOneOp(p, RBU_INSERT);
+    }
+    else if( eType!=RBU_UPDATE ){
+      rbuStepOneOp(p, eType);
+    }
+    else{
       sqlite3_value *pVal;
       sqlite3_stmt *pUpdate = 0;
       assert( eType==RBU_UPDATE );
       rbuGetUpdateStmt(p, pIter, zMask, &pUpdate);
       if( pUpdate ){
+        int i;
         for(i=0; p->rc==SQLITE_OK && i<pIter->nCol; i++){
           char c = zMask[pIter->aiSrcOrder[i]];
           pVal = sqlite3_column_value(pIter->pSelect, i);
index 6cf99353de90fd4b6b25eb69cfd078865d72cd0a..9153631b3a84675b2a858062dd0852f8150d9cea 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sthe\sSQLITE_CONFIG_STMTJRNL_SPILL\soption\sfor\ssqlite3_config().
-D 2016-03-07T17:37:37.281
+C Add\sa\snew\srow\stype\sto\sRBU\s(a\speer\sof\sinsert,\supdate\sand\sdelete)\s-\s"delete\sthen\sinsert".
+D 2016-03-07T17:39:30.317
 F Makefile.in f53429fb2f313c099283659d0df6f20f932c861f
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
 F Makefile.msc df0bf9ff7f8b3f4dd9fb4cc43f92fe58f6ec5c66
@@ -234,6 +234,7 @@ F ext/rbu/rbu8.test 3bbf2c35d71a843c463efe93946f14ad10c3ede0
 F ext/rbu/rbu9.test 0806d1772c9f4981774ff028de6656e4183082af
 F ext/rbu/rbuA.test c1a7b3e2d926b8f8448bb3b4ae787e314ee4b2b3
 F ext/rbu/rbuB.test c25bc325b8072a766e56bb76c001866b405925c2
+F ext/rbu/rbuC.test efe47db508a0269b683cb2a1913a425ffd39a831
 F ext/rbu/rbu_common.tcl 0398545fed614f807d5f0ba55a85a51f08ba8f1a
 F ext/rbu/rbucrash.test 8d2ed5d4b05fef6c00c2a6b5f7ead71fa172a695
 F ext/rbu/rbudiff.test 6cc806dc36389292f2a8f5842d0103721df4a07d
@@ -241,7 +242,7 @@ F ext/rbu/rbufault.test cc0be8d5d392d98b0c2d6a51be377ea989250a89
 F ext/rbu/rbufault2.test 9a7f19edd6ea35c4c9f807d8a3db0a03a5670c06
 F ext/rbu/rbufts.test 828cd689da825f0a7b7c53ffc1f6f7fdb6fa5bda
 F ext/rbu/rbusave.test 0f43b6686084f426ddd040b878426452fd2c2f48
-F ext/rbu/sqlite3rbu.c 371e8bf06cfb3f691adac47eb15ab1073ed92dcf
+F ext/rbu/sqlite3rbu.c 0d901d773bf4c9e7d101daaf545263044e7e6615
 F ext/rbu/sqlite3rbu.h 0bdeb3be211aaba7d85445fa36f4701a25a3dbde
 F ext/rbu/test_rbu.c 4a4cdcef4ef9379fc2a21f008805c80b27bcf573
 F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
@@ -1453,7 +1454,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P d8d89d69a490a708b83147945f74134ae0e4b387
-R bbf6d9fea009c5097a1f1af27ba30392
-U drh
-Z 638bda7b36ac0921edfae65baff4e139
+P b6c4202432dc96f8f1740f52d0bf872116357fcc
+R 56e1f15e83703ebf857ebd55a98bbcfe
+T *branch * rbu-delete-then-insert
+T *sym-rbu-delete-then-insert *
+T -sym-trunk *
+U dan
+Z 681ea677d41a7fd6408ffd5219102c46
index 9a5e54bf8523571aeb77216acbb320520c1cd0c9..683dc4199a0081a2d4b77ea071ee32df53a80fcf 100644 (file)
@@ -1 +1 @@
-b6c4202432dc96f8f1740f52d0bf872116357fcc
\ No newline at end of file
+169311c85b30f625bdb6986c9cd11db70942d73b
\ No newline at end of file