]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Allow the sessions module to be configured to capture changes from tables with no... sessions-rowid-tables
authordan <Dan Kennedy>
Mon, 24 Apr 2023 19:22:21 +0000 (19:22 +0000)
committerdan <Dan Kennedy>
Mon, 24 Apr 2023 19:22:21 +0000 (19:22 +0000)
FossilOrigin-Name: 8a612f0860126c0c8473b1e65fcabb9a8821d8bf09fdf3f6018acfc99df9af71

ext/session/sessionD.test
ext/session/session_common.tcl
ext/session/sessionrebase.test
ext/session/sessionrowid.test [new file with mode: 0644]
ext/session/sessionsize.test
ext/session/sqlite3session.c
ext/session/sqlite3session.h
ext/session/test_session.c
manifest
manifest.uuid

index ec652e2d6bad4ea194f3716d259b7ee22b58d77f..9fccbfa96f0264b54751b164dc64e5f44074b4b9 100644 (file)
@@ -41,30 +41,6 @@ proc scksum {db dbname} {
   return [md5 $txt]
 }
 
-proc do_diff_test {tn setup} {
-  reset_db
-  forcedelete test.db2
-  execsql { ATTACH 'test.db2' AS aux }
-  execsql $setup
-
-  sqlite3session S db main
-  foreach tbl [db eval {SELECT name FROM sqlite_master WHERE type='table'}] {
-    S attach $tbl
-    S diff aux $tbl
-  }
-
-  set C [S changeset]
-  S delete
-
-  sqlite3 db2 test.db2
-  sqlite3changeset_apply db2 $C ""
-  uplevel do_test $tn.1 [list {execsql { PRAGMA integrity_check } db2}] ok
-  db2 close
-
-  set cksum [scksum db main]
-  uplevel do_test $tn.2 [list {scksum db aux}] [list $cksum]
-}
-
 # Ensure that the diff produced by comparing the current contents of [db]
 # with itself is empty.
 proc do_empty_diff_test {tn} {
index 22e427f2be2d2ec5580f0776a3b87006ae272f33..3ff84f1c5eb403c7b69dc0e461f1a02cedb195e5 100644 (file)
@@ -52,6 +52,7 @@ proc do_conflict_test {tn args} {
   proc bgerror {args} { set ::background_error $args }
 
   sqlite3session S db main
+  S object_config rowid 1
   foreach t $O(-tables) { S attach $t }
   execsql $O(-sql)
 
@@ -81,6 +82,7 @@ proc changeset_from_sql {sql {dbname main}} {
   }
   set rc [catch {
     sqlite3session S db $dbname
+    S object_config rowid 1
     db eval "SELECT name FROM $dbname.sqlite_master WHERE type = 'table'" {
       S attach $name
     }
@@ -138,6 +140,7 @@ proc do_then_apply_sql {args} {
   proc xConflict args { incr ::n_conflict ; return "OMIT" }
   set rc [catch {
     sqlite3session S db $dbname
+    S object_config rowid 1
     db eval "SELECT name FROM $dbname.sqlite_master WHERE type = 'table'" {
       S attach $name
     }
@@ -162,6 +165,8 @@ proc do_then_apply_sql {args} {
 
 proc do_iterator_test {tn tbl_list sql res} {
   sqlite3session S db main
+  S object_config rowid 1
+
   if {[llength $tbl_list]==0} { S attach * }
   foreach t $tbl_list {S attach $t}
 
@@ -171,6 +176,7 @@ proc do_iterator_test {tn tbl_list sql res} {
   foreach v $res { lappend r $v }
 
   set x [list]
+# set ::c [S changeset] ; execsql_pp { SELECT quote($::c) }
   sqlite3session_foreach c [S changeset] { lappend x $c }
   uplevel do_test $tn [list [list set {} $x]] [list $r]
 
@@ -245,3 +251,49 @@ proc number_name {n} {
   if {$txt==""} {set txt zero}
   return $txt
 }
+
+proc scksum {db dbname} {
+
+  if {$dbname=="temp"} {
+    set master sqlite_temp_master
+  } else {
+    set master $dbname.sqlite_master
+  }
+
+  set alltab [$db eval "SELECT name FROM $master WHERE type='table'"]
+  set txt [$db eval "SELECT * FROM $master ORDER BY type,name,sql"]
+  foreach tab $alltab {
+    set cols [list]
+    db eval "PRAGMA $dbname.table_info = $tab" x { 
+      lappend cols "quote($x(name))" 
+    }
+    set cols [join $cols ,]
+    append txt [db eval "SELECT $cols FROM $dbname.$tab ORDER BY $cols"]
+  }
+  return [md5 $txt]
+}
+
+proc do_diff_test {tn setup} {
+  reset_db
+  forcedelete test.db2
+  execsql { ATTACH 'test.db2' AS aux }
+  execsql $setup
+
+  sqlite3session S db main
+  S object_config rowid 1
+  foreach tbl [db eval {SELECT name FROM sqlite_master WHERE type='table'}] {
+    S attach $tbl
+    S diff aux $tbl
+  }
+
+  set C [S changeset]
+  S delete
+
+  sqlite3 db2 test.db2
+  sqlite3changeset_apply db2 $C ""
+  uplevel do_test $tn.1 [list {execsql { PRAGMA integrity_check } db2}] ok
+  db2 close
+
+  set cksum [scksum db main]
+  uplevel do_test $tn.2 [list {scksum db aux}] [list $cksum]
+}
index cdf3322838b1cf223f04fb9c63b0b65b14583411..033348f9ce28e3d94edca4fec1937a4d5bafa0fb 100644 (file)
@@ -84,6 +84,7 @@ proc do_rebase_test {tn sql1 sql2 conflict_handler {testsql ""} {testres ""}} {
     db eval BEGIN
 
     sqlite3session S1 db main
+    S1 object_config rowid 1
     S1 attach *
     execsql $sql1 db
     set c1 [S1 changeset]
@@ -91,6 +92,7 @@ proc do_rebase_test {tn sql1 sql2 conflict_handler {testsql ""} {testres ""}} {
 
     if {$i==1} {
       sqlite3session S2 db2 main
+      S2 object_config rowid 1
       S2 attach *
       execsql $sql2 db2
       set c2 [S2 changeset]
@@ -100,6 +102,7 @@ proc do_rebase_test {tn sql1 sql2 conflict_handler {testsql ""} {testres ""}} {
       foreach sql [split $sql2 ";"] {
         if {[string is space $sql]} continue
         sqlite3session S2 db2 main
+        S2 object_config rowid 1
         S2 attach *
         execsql $sql db2
         lappend c2 [S2 changeset]
@@ -341,6 +344,79 @@ do_rebase_test 2.2.3 {
   OMIT
 } { SELECT * FROM t2 WHERE z='B' } { 1 one B }
 
+
+reset_db
+do_execsql_test 2.3.0 {
+  CREATE TABLE t1 (b TEXT);
+  INSERT INTO t1(rowid, b) VALUES(1, 'one');
+  INSERT INTO t1(rowid, b) VALUES(2, 'two');
+  INSERT INTO t1(rowid, b) VALUES(3, 'three');
+}
+do_rebase_test 2.3.1 {
+  UPDATE t1 SET b = 'two.1' WHERE rowid=2
+} {
+  UPDATE t1 SET b = 'two.2' WHERE rowid=2;
+} {
+  OMIT
+} { SELECT rowid, * FROM t1 } {1 one 2 two.1 3 three}
+
+do_rebase_test 2.3.2 {
+  UPDATE t1 SET b = 'two.1' WHERE rowid=2
+} {
+  UPDATE t1 SET b = 'two.2' WHERE rowid=2;
+} {
+  REPLACE
+} { SELECT rowid, * FROM t1 } {1 one 2 two.2 3 three}
+
+do_rebase_test 2.3.3 {
+  DELETE FROM t1 WHERE rowid=3
+} {
+  DELETE FROM t1 WHERE rowid=3;
+} {
+  OMIT
+} { SELECT rowid, * FROM t1 } {1 one 2 two}
+
+do_rebase_test 2.3.4 {
+  DELETE FROM t1 WHERE rowid=1
+} {
+  UPDATE t1 SET b='one.2' WHERE rowid=1
+} {
+  OMIT
+} { SELECT rowid, * FROM t1 } {2 two 3 three}
+
+do_rebase_test 2.3.6 {
+  UPDATE t1 SET b='three.1' WHERE rowid=3
+} {
+  DELETE FROM t1 WHERE rowid=3;
+} {
+  OMIT
+} { SELECT rowid, * FROM t1 } {1 one 2 two 3 three.1}
+
+do_rebase_test 2.3.7 {
+  UPDATE t1 SET b='three.1' WHERE rowid=3
+} {
+  DELETE FROM t1 WHERE rowid=3;
+} {
+  REPLACE
+} { SELECT rowid, * FROM t1 } {1 one 2 two}
+
+do_rebase_test 2.3.8 {
+  INSERT INTO t1(rowid, b) VALUES(4, 'four.1')
+} {
+  INSERT INTO t1(rowid, b) VALUES(4, 'four.2');
+} {
+  REPLACE
+} { SELECT rowid, * FROM t1 } {1 one 2 two 3 three 4 four.2}
+
+do_rebase_test 2.3.9 {
+  INSERT INTO t1(rowid, b) VALUES(4, 'four.1')
+} {
+  INSERT INTO t1(rowid, b) VALUES(4, 'four.2');
+} {
+  OMIT
+} { SELECT rowid, * FROM t1 } {1 one 2 two 3 three 4 four.1}
+
+
 #-------------------------------------------------------------------------
 reset_db
 do_execsql_test 3.0 {
diff --git a/ext/session/sessionrowid.test b/ext/session/sessionrowid.test
new file mode 100644 (file)
index 0000000..14af90a
--- /dev/null
@@ -0,0 +1,281 @@
+# 2011 Mar 16
+#
+# 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.
+#
+#***********************************************************************
+#
+# The focus of this file is testing the session module.
+#
+
+if {![info exists testdir]} {
+  set testdir [file join [file dirname [info script]] .. .. test]
+} 
+source [file join [file dirname [info script]] session_common.tcl]
+source $testdir/tester.tcl
+ifcapable !session {finish_test; return}
+
+set testprefix sessionrowid
+
+do_execsql_test 0.0 { 
+  CREATE TABLE t1(a, b);
+}
+
+foreach {tn rowid bEmpty} {
+  1 0    1
+  2 1    0
+  3 -1   1
+} {
+  do_test 0.$tn {
+    sqlite3session S db main
+    if {$rowid>=0} { S object_config rowid $rowid }
+    S attach t1
+    execsql { INSERT INTO t1 VALUES(1, 2); }
+    expr [string length [S changeset]]==0
+  } $bEmpty
+  S delete
+}
+
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 1.0 { 
+  CREATE TABLE t1(a, b);
+}
+
+do_iterator_test 1.1 t1 {
+  INSERT INTO t1 VALUES('i', 'one');
+} {
+  {INSERT t1 0 X.. {} {i 1 t i t one}}
+}
+
+do_execsql_test 1.2 {
+  SELECT rowid, * FROM t1
+} {1 i one}
+
+do_iterator_test 1.3 t1 {
+  UPDATE t1 SET b='two'
+} {
+  {UPDATE t1 0 X.. {i 1 {} {} t one} {{} {} {} {} t two}}
+}
+
+do_iterator_test 1.4 t1 {
+  DELETE FROM t1;
+} {
+  {DELETE t1 0 X.. {i 1 t i t two} {}}
+}
+
+do_iterator_test 1.5 t1 {
+  INSERT INTO t1(rowid, a, b) VALUES(14, 'hello', 'world');
+  INSERT INTO t1(rowid, a, b) VALUES(NULL, 'yes', 'no');
+  INSERT INTO t1(rowid, a, b) VALUES(-123, 'ii', 'iii');
+} {
+  {INSERT t1 0 X.. {} {i -123 t ii t iii}}
+  {INSERT t1 0 X.. {} {i 15 t yes t no}}
+  {INSERT t1 0 X.. {} {i 14 t hello t world}}
+}
+
+do_iterator_test 1.6 t1 {
+  UPDATE t1 SET a='deluxe' WHERE rowid=14;
+  DELETE FROM t1 WHERE rowid=-123;
+  INSERT INTO t1 VALUES('x', 'xi');
+} {
+  {DELETE t1 0 X.. {i -123 t ii t iii} {}}
+  {UPDATE t1 0 X.. {i 14 t hello {} {}} {{} {} t deluxe {} {}}}
+  {INSERT t1 0 X.. {} {i 16 t x t xi}}
+}
+
+#-------------------------------------------------------------------------
+reset_db
+forcedelete test.db2
+sqlite3 db2 test.db2
+
+do_execsql_test 2.0 {
+  CREATE TABLE t1(a, b);
+}
+do_execsql_test -db db2 2.0.1 {
+  CREATE TABLE t1(a, b);
+}
+
+proc xConflict {args} { 
+  puts "CONFLICT!"
+  return "OMIT" 
+}
+
+do_test 2.1 {
+  set C [changeset_from_sql {
+    INSERT INTO t1 VALUES('abc', 'def');
+  }]
+  sqlite3changeset_apply db2 $C xConflict
+  execsql { SELECT * FROM t1 } db2
+} {abc def}
+do_test 2.2 {
+  set C [changeset_from_sql {
+    UPDATE t1 SET b='hello'
+  }]
+  sqlite3changeset_apply db2 $C xConflict
+  execsql { SELECT * FROM t1 } db2
+} {abc hello}
+do_test 2.3 {
+  set C [changeset_from_sql {
+    DELETE FROM t1 WHERE b='hello'
+  }]
+  sqlite3changeset_apply db2 $C xConflict
+  execsql { SELECT * FROM t1 } db2
+} {}
+
+do_test 2.4 {
+  do_then_apply_sql {
+    INSERT INTO t1 VALUES('i', 'one');
+    INSERT INTO t1 VALUES('ii', 'two');
+    INSERT INTO t1 VALUES('iii', 'three');
+    INSERT INTO t1 VALUES('iv', 'four');
+  }
+  compare_db db db2
+} {}
+
+do_test 2.5 {
+  do_then_apply_sql {
+    DELETE FROM t1 WHERE a='ii';
+    UPDATE t1 SET b='THREE' WHERE a='iii';
+    UPDATE t1 SET a='III' WHERE a='iii';
+    INSERT INTO t1 VALUES('v', 'five');
+  }
+  compare_db db db2
+} {}
+
+do_execsql_test 2.6 {SELECT * FROM t1} {i one III THREE iv four v five}
+do_execsql_test -db db2 2.7 {SELECT * FROM t1} {i one III THREE iv four v five}
+
+#-------------------------------------------------------------------------
+db2 close
+reset_db
+forcedelete test.db2
+sqlite3 db2 test.db2
+
+set init_sql {
+  CREATE TABlE t4(a, b);
+  CREATE INDEX t4a ON t4(a);
+  CREATE UNIQUE INDEX t4b ON t4(b);
+}
+
+do_execsql_test 3.0 $init_sql
+do_execsql_test -db db2 3.0a $init_sql
+
+do_execsql_test -db db2 3.1 {
+  INSERT INTO t4(rowid, a, b) VALUES(43, 'hello', 'world');
+}
+do_conflict_test 3.2 -sql {
+  INSERT INTO t4(rowid, a, b) VALUES(43, 'abc', 'def');
+} -tables t4 -conflicts {
+  {INSERT t4 CONFLICT {i 43 t abc t def} {i 43 t hello t world}}
+}
+do_execsql_test -db db2 3.3 {
+  SELECT * FROM t4
+} {hello world}
+
+do_execsql_test 3.4 { DELETE FROM t4 }
+do_conflict_test 3.5 -sql {
+  INSERT INTO t4(rowid, a, b) VALUES(43, 'abc', 'def');
+} -tables t4 -conflicts {
+  {INSERT t4 CONFLICT {i 43 t abc t def} {i 43 t hello t world}}
+} -policy REPLACE
+do_execsql_test -db db2 3.6 {
+  SELECT * FROM t4
+} {abc def}
+
+do_execsql_test 3.7 { DELETE FROM t4 }
+do_conflict_test 3.8 -sql {
+  INSERT INTO t4(rowid, a, b) VALUES(45, 'xyz', 'def');
+} -tables t4 -conflicts {
+  {INSERT t4 CONSTRAINT {i 45 t xyz t def}}
+} 
+do_execsql_test -db db2 3.9 {
+  SELECT * FROM t4
+} {abc def}
+
+
+do_execsql_test -db db  3.10a { DELETE FROM t4 }
+do_execsql_test -db db2 3.10b { DELETE FROM t4 }
+
+do_execsql_test -db db 3.11a {
+  INSERT INTO t4(rowid, a, b) VALUES(111, 'one', 'one');
+  INSERT INTO t4(rowid, a, b) VALUES(222, 'two', 'two');
+}
+do_execsql_test -db db2 3.11b {
+  INSERT INTO t4(rowid, a, b) VALUES(111, 'one', 'blip');
+}
+
+do_conflict_test 3.12 -sql {
+  DELETE FROM t4 WHERE a='one';
+} -tables t4 -conflicts {
+  {DELETE t4 DATA {i 111 t one t one} {i 111 t one t blip}}
+} 
+do_execsql_test -db db2 3.13 {
+  SELECT * FROM t4
+} {one blip}
+
+do_conflict_test 3.14 -sql {
+  DELETE FROM t4 WHERE a='two';
+} -tables t4 -conflicts {
+  {DELETE t4 NOTFOUND {i 222 t two t two}}
+} 
+do_execsql_test -db db2 3.15 {
+  SELECT * FROM t4
+} {one blip}
+
+do_execsql_test -db db  3.16a { DELETE FROM t4 }
+do_execsql_test -db db2 3.16b { DELETE FROM t4 }
+
+do_execsql_test -db db 3.17a {
+  INSERT INTO t4(rowid, a, b) VALUES(111, 'one', 'one');
+  INSERT INTO t4(rowid, a, b) VALUES(222, 'two', 'two');
+}
+do_execsql_test -db db2 3.17b {
+  INSERT INTO t4(rowid, a, b) VALUES(111, 'one', 'blip');
+}
+
+do_conflict_test 3.18 -sql {
+  UPDATE t4 SET b='xyz' WHERE a='one'
+} -tables t4 -conflicts {
+  {UPDATE t4 DATA {i 111 {} {} t one} {{} {} {} {} t xyz} {i 111 t one t blip}}
+} 
+do_execsql_test -db db2 3.19 {
+  SELECT * FROM t4
+} {one blip}
+
+do_conflict_test 3.20 -sql {
+  UPDATE t4 SET b='123' WHERE a='two'
+} -tables t4 -conflicts {
+  {UPDATE t4 NOTFOUND {i 222 {} {} t two} {{} {} {} {} t 123}}
+} 
+do_execsql_test -db db2 3.21 {
+  SELECT * FROM t4
+} {one blip}
+
+#--------------------------------------------------------------------------
+breakpoint
+do_diff_test 4.0 {
+  CREATE TABLE t1(x, y);
+  CREATE TABLE aux.t1(x, y);
+  INSERT INTO t1 VALUES(1, 2);
+}
+
+do_diff_test 4.1 {
+  CREATE TABLE t1(x, y);
+  CREATE TABLE aux.t1(x, y);
+  INSERT INTO aux.t1 VALUES(1, 2);
+}
+
+do_diff_test 4.2 {
+  CREATE TABLE t1(x, y);
+  CREATE TABLE aux.t1(x, y);
+  INSERT INTO t1(rowid, x, y) VALUES(413, 'hello', 'there');
+  INSERT INTO aux.t1(rowid, x, y) VALUES(413, 'hello', 'world');
+}
+
+finish_test
+
index 04d05514dbf8013c2151e0192c50ed08f195652e..01638c6677e3f13bf1a79753cf78dd869d599cfe 100644 (file)
@@ -113,17 +113,17 @@ do_execsql_test 3.0 {
 
 do_test 3.1 {
   sqlite3session S db main
-  S object_config_size -1
+  S object_config size -1
 } 1
 
-do_test 3.2.1 { S object_config_size 0  } 0
-do_test 3.2.2 { S object_config_size -1 } 0
-do_test 3.2.3 { S object_config_size 1  } 1
-do_test 3.2.4 { S object_config_size -1 } 1
+do_test 3.2.1 { S object_config size 0  } 0
+do_test 3.2.2 { S object_config size -1 } 0
+do_test 3.2.3 { S object_config size 1  } 1
+do_test 3.2.4 { S object_config size -1 } 1
 
 do_test 3.3 { S attach t1 } {}
-do_test 3.4 { S object_config_size 1  } {SQLITE_MISUSE}
-do_test 3.4 { S object_config_size -1 } {1}
+do_test 3.4 { S object_config size 1  } {SQLITE_MISUSE}
+do_test 3.4 { S object_config size -1 } {1}
 
 S delete
 
index 225d0316a8f17de9fd5ae75c2d53e2f82935fda3..e601a4ae7a52c564c02ec50eb65355f6dadc666f 100644 (file)
@@ -25,6 +25,8 @@ typedef struct SessionInput SessionInput;
 # endif
 #endif
 
+#define SESSIONS_ROWID "_rowid_"
+
 static int sessions_strm_chunk_size = SESSIONS_STRM_CHUNK_SIZE;
 
 typedef struct SessionHook SessionHook;
@@ -46,6 +48,7 @@ struct sqlite3_session {
   int bEnable;                    /* True if currently recording */
   int bIndirect;                  /* True if all changes are indirect */
   int bAutoAttach;                /* True to auto-attach tables */
+  int bImplicitPK;                /* True to handle tables with implicit PK */
   int rc;                         /* Non-zero if an error has occurred */
   void *pFilterCtx;               /* First argument to pass to xTableFilter */
   int (*xTableFilter)(void *pCtx, const char *zTab);
@@ -122,6 +125,7 @@ struct SessionTable {
   char *zName;                    /* Local name of table */
   int nCol;                       /* Number of columns in table zName */
   int bStat1;                     /* True if this is sqlite_stat1 */
+  int bRowid;                     /* True if this table uses rowid for PK */
   const char **azCol;             /* Column names */
   u8 *abPK;                       /* Array of primary key flags */
   int nEntry;                     /* Total number of entries in hash table */
@@ -514,6 +518,7 @@ static unsigned int sessionHashAppendType(unsigned int h, int eType){
 */
 static int sessionPreupdateHash(
   sqlite3_session *pSession,      /* Session object that owns pTab */
+  i64 iRowid,
   SessionTable *pTab,             /* Session table handle */
   int bNew,                       /* True to hash the new.* PK */
   int *piHash,                    /* OUT: Hash value */
@@ -522,48 +527,53 @@ static int sessionPreupdateHash(
   unsigned int h = 0;             /* Hash value to return */
   int i;                          /* Used to iterate through columns */
 
-  assert( *pbNullPK==0 );
-  assert( pTab->nCol==pSession->hook.xCount(pSession->hook.pCtx) );
-  for(i=0; i<pTab->nCol; i++){
-    if( pTab->abPK[i] ){
-      int rc;
-      int eType;
-      sqlite3_value *pVal;
-
-      if( bNew ){
-        rc = pSession->hook.xNew(pSession->hook.pCtx, i, &pVal);
-      }else{
-        rc = pSession->hook.xOld(pSession->hook.pCtx, i, &pVal);
-      }
-      if( rc!=SQLITE_OK ) return rc;
+  if( pTab->bRowid ){
+    assert( pTab->nCol-1==pSession->hook.xCount(pSession->hook.pCtx) );
+    h = sessionHashAppendI64(h, iRowid);
+  }else{
+    assert( *pbNullPK==0 );
+    assert( pTab->nCol==pSession->hook.xCount(pSession->hook.pCtx) );
+    for(i=0; i<pTab->nCol; i++){
+      if( pTab->abPK[i] ){
+        int rc;
+        int eType;
+        sqlite3_value *pVal;
 
-      eType = sqlite3_value_type(pVal);
-      h = sessionHashAppendType(h, eType);
-      if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
-        i64 iVal;
-        if( eType==SQLITE_INTEGER ){
-          iVal = sqlite3_value_int64(pVal);
+        if( bNew ){
+          rc = pSession->hook.xNew(pSession->hook.pCtx, i, &pVal);
         }else{
-          double rVal = sqlite3_value_double(pVal);
-          assert( sizeof(iVal)==8 && sizeof(rVal)==8 );
-          memcpy(&iVal, &rVal, 8);
+          rc = pSession->hook.xOld(pSession->hook.pCtx, i, &pVal);
         }
-        h = sessionHashAppendI64(h, iVal);
-      }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
-        const u8 *z;
-        int n;
-        if( eType==SQLITE_TEXT ){
-          z = (const u8 *)sqlite3_value_text(pVal);
+        if( rc!=SQLITE_OK ) return rc;
+
+        eType = sqlite3_value_type(pVal);
+        h = sessionHashAppendType(h, eType);
+        if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
+          i64 iVal;
+          if( eType==SQLITE_INTEGER ){
+            iVal = sqlite3_value_int64(pVal);
+          }else{
+            double rVal = sqlite3_value_double(pVal);
+            assert( sizeof(iVal)==8 && sizeof(rVal)==8 );
+            memcpy(&iVal, &rVal, 8);
+          }
+          h = sessionHashAppendI64(h, iVal);
+        }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
+          const u8 *z;
+          int n;
+          if( eType==SQLITE_TEXT ){
+            z = (const u8 *)sqlite3_value_text(pVal);
+          }else{
+            z = (const u8 *)sqlite3_value_blob(pVal);
+          }
+          n = sqlite3_value_bytes(pVal);
+          if( !z && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM;
+          h = sessionHashAppendBlob(h, n, z);
         }else{
-          z = (const u8 *)sqlite3_value_blob(pVal);
+          assert( eType==SQLITE_NULL );
+          assert( pTab->bStat1==0 || i!=1 );
+          *pbNullPK = 1;
         }
-        n = sqlite3_value_bytes(pVal);
-        if( !z && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM;
-        h = sessionHashAppendBlob(h, n, z);
-      }else{
-        assert( eType==SQLITE_NULL );
-        assert( pTab->bStat1==0 || i!=1 );
-        *pbNullPK = 1;
       }
     }
   }
@@ -846,6 +856,7 @@ static int sessionMergeUpdate(
 */
 static int sessionPreupdateEqual(
   sqlite3_session *pSession,      /* Session object that owns SessionTable */
+  i64 iRowid,                     /* Rowid value if pTab->bRowid */
   SessionTable *pTab,             /* Table associated with change */
   SessionChange *pChange,         /* Change to compare to */
   int op                          /* Current pre-update operation */
@@ -853,6 +864,11 @@ static int sessionPreupdateEqual(
   int iCol;                       /* Used to iterate through columns */
   u8 *a = pChange->aRecord;       /* Cursor used to scan change record */
 
+  if( pTab->bRowid ){
+    if( a[0]!=SQLITE_INTEGER ) return 0;
+    return sessionGetI64(&a[1])==iRowid;
+  }
+
   assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE );
   for(iCol=0; iCol<pTab->nCol; iCol++){
     if( !pTab->abPK[iCol] ){
@@ -997,7 +1013,8 @@ static int sessionTableInfo(
   int *pnCol,                     /* OUT: number of columns */
   const char **pzTab,             /* OUT: Copy of zThis */
   const char ***pazCol,           /* OUT: Array of column names for table */
-  u8 **pabPK                      /* OUT: Array of booleans - true for PK col */
+  u8 **pabPK,                     /* OUT: Array of booleans - true for PK col */
+  int *pbRowid                    /* OUT: True if only PK is a rowid */
 ){
   char *zPragma;
   sqlite3_stmt *pStmt;
@@ -1009,6 +1026,7 @@ static int sessionTableInfo(
   u8 *pAlloc = 0;
   char **azCol = 0;
   u8 *abPK = 0;
+  int bRowid = 0;                 /* Set to true to use rowid as PK */
 
   assert( pazCol && pabPK );
 
@@ -1053,10 +1071,15 @@ static int sessionTableInfo(
   }
 
   nByte = nThis + 1;
+  bRowid = (pbRowid!=0);
   while( SQLITE_ROW==sqlite3_step(pStmt) ){
     nByte += sqlite3_column_bytes(pStmt, 1);
     nDbCol++;
+    if( sqlite3_column_int(pStmt, 5) ) bRowid = 0;
   }
+  if( nDbCol==0 ) bRowid = 0;
+  nDbCol += bRowid;
+  nByte += strlen(SESSIONS_ROWID);
   rc = sqlite3_reset(pStmt);
 
   if( rc==SQLITE_OK ){
@@ -1078,6 +1101,14 @@ static int sessionTableInfo(
     }
   
     i = 0;
+    if( bRowid ){
+      int nName = strlen(SESSIONS_ROWID);
+      memcpy(pAlloc, SESSIONS_ROWID, nName+1);
+      azCol[i] = (char*)pAlloc;
+      pAlloc += nName+1;
+      abPK[i] = 1;
+      i++;
+    }
     while( SQLITE_ROW==sqlite3_step(pStmt) ){
       int nName = sqlite3_column_bytes(pStmt, 1);
       const unsigned char *zName = sqlite3_column_text(pStmt, 1);
@@ -1089,7 +1120,6 @@ static int sessionTableInfo(
       i++;
     }
     rc = sqlite3_reset(pStmt);
-  
   }
 
   /* If successful, populate the output variables. Otherwise, zero them and
@@ -1106,6 +1136,7 @@ static int sessionTableInfo(
     if( pzTab ) *pzTab = 0;
     sessionFree(pSession, azCol);
   }
+  if( pbRowid ) *pbRowid = bRowid;
   sqlite3_finalize(pStmt);
   return rc;
 }
@@ -1127,7 +1158,8 @@ static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){
     u8 *abPK;
     assert( pTab->azCol==0 || pTab->abPK==0 );
     pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb, 
-        pTab->zName, &pTab->nCol, 0, &pTab->azCol, &abPK
+        pTab->zName, &pTab->nCol, 0, &pTab->azCol, &abPK,
+        (pSession->bImplicitPK ? &pTab->bRowid : 0)
     );
     if( pSession->rc==SQLITE_OK ){
       int i;
@@ -1199,6 +1231,7 @@ static int sessionUpdateMaxSize(
 ){
   i64 nNew = 2;
   if( pC->op==SQLITE_INSERT ){
+    if( pTab->bRowid ) nNew += 9;
     if( op!=SQLITE_DELETE ){
       int ii;
       for(ii=0; ii<pTab->nCol; ii++){
@@ -1215,7 +1248,11 @@ static int sessionUpdateMaxSize(
   }else{
     int ii;
     u8 *pCsr = pC->aRecord;
-    for(ii=0; ii<pTab->nCol; ii++){
+    if( pTab->bRowid ){
+      nNew += 9;
+      pCsr += 9;
+    }
+    for(ii=0; ii<(pTab->nCol-pTab->bRowid); ii++){
       int bChanged = 1;
       int nOld = 0;
       int eType;
@@ -1299,6 +1336,7 @@ static int sessionUpdateMaxSize(
 */
 static void sessionPreupdateOneChange(
   int op,                         /* One of SQLITE_UPDATE, INSERT, DELETE */
+  i64 iRowid,
   sqlite3_session *pSession,      /* Session object pTab is attached to */
   SessionTable *pTab              /* Table that change applies to */
 ){
@@ -1314,7 +1352,7 @@ static void sessionPreupdateOneChange(
 
   /* Check the number of columns in this xPreUpdate call matches the 
   ** number of columns in the table.  */
-  if( pTab->nCol!=pSession->hook.xCount(pSession->hook.pCtx) ){
+  if( (pTab->nCol-pTab->bRowid)!=pSession->hook.xCount(pSession->hook.pCtx) ){
     pSession->rc = SQLITE_SCHEMA;
     return;
   }
@@ -1347,14 +1385,16 @@ static void sessionPreupdateOneChange(
   /* Calculate the hash-key for this change. If the primary key of the row
   ** includes a NULL value, exit early. Such changes are ignored by the
   ** session module. */
-  rc = sessionPreupdateHash(pSession, pTab, op==SQLITE_INSERT, &iHash, &bNull);
+  rc = sessionPreupdateHash(
+      pSession, iRowid, pTab, op==SQLITE_INSERT, &iHash, &bNull
+  );
   if( rc!=SQLITE_OK ) goto error_out;
 
   if( bNull==0 ){
     /* Search the hash table for an existing record for this row. */
     SessionChange *pC;
     for(pC=pTab->apChange[iHash]; pC; pC=pC->pNext){
-      if( sessionPreupdateEqual(pSession, pTab, pC, op) ) break;
+      if( sessionPreupdateEqual(pSession, iRowid, pTab, pC, op) ) break;
     }
 
     if( pC==0 ){
@@ -1369,7 +1409,7 @@ static void sessionPreupdateOneChange(
   
       /* Figure out how large an allocation is required */
       nByte = sizeof(SessionChange);
-      for(i=0; i<pTab->nCol; i++){
+      for(i=0; i<(pTab->nCol-pTab->bRowid); i++){
         sqlite3_value *p = 0;
         if( op!=SQLITE_INSERT ){
           TESTONLY(int trc = ) pSession->hook.xOld(pSession->hook.pCtx, i, &p);
@@ -1384,6 +1424,9 @@ static void sessionPreupdateOneChange(
         rc = sessionSerializeValue(0, p, &nByte);
         if( rc!=SQLITE_OK ) goto error_out;
       }
+      if( pTab->bRowid ){
+        nByte += 9;               /* Size of rowid field - an integer */
+      }
   
       /* Allocate the change object */
       pC = (SessionChange *)sessionMalloc64(pSession, nByte);
@@ -1400,7 +1443,12 @@ static void sessionPreupdateOneChange(
       ** required values and encodings have already been cached in memory.
       ** It is not possible for an OOM to occur in this block. */
       nByte = 0;
-      for(i=0; i<pTab->nCol; i++){
+      if( pTab->bRowid ){
+        pC->aRecord[0] = SQLITE_INTEGER;
+        sessionPutI64(&pC->aRecord[1], iRowid);
+        nByte = 9;
+      }
+      for(i=0; i<(pTab->nCol-pTab->bRowid); i++){
         sqlite3_value *p = 0;
         if( op!=SQLITE_INSERT ){
           pSession->hook.xOld(pSession->hook.pCtx, i, &p);
@@ -1515,9 +1563,10 @@ static void xPreUpdate(
     pSession->rc = sessionFindTable(pSession, zName, &pTab);
     if( pTab ){
       assert( pSession->rc==SQLITE_OK );
-      sessionPreupdateOneChange(op, pSession, pTab);
+      assert( op==SQLITE_UPDATE || iKey1==iKey2 );
+      sessionPreupdateOneChange(op, iKey1, pSession, pTab);
       if( op==SQLITE_UPDATE ){
-        sessionPreupdateOneChange(SQLITE_INSERT, pSession, pTab);
+        sessionPreupdateOneChange(SQLITE_INSERT, iKey2, pSession, pTab);
       }
     }
   }
@@ -1556,6 +1605,7 @@ static void sessionPreupdateHooks(
 typedef struct SessionDiffCtx SessionDiffCtx;
 struct SessionDiffCtx {
   sqlite3_stmt *pStmt;
+  int bRowid;
   int nOldOff;
 };
 
@@ -1564,17 +1614,17 @@ struct SessionDiffCtx {
 */
 static int sessionDiffOld(void *pCtx, int iVal, sqlite3_value **ppVal){
   SessionDiffCtx *p = (SessionDiffCtx*)pCtx;
-  *ppVal = sqlite3_column_value(p->pStmt, iVal+p->nOldOff);
+  *ppVal = sqlite3_column_value(p->pStmt, iVal+p->nOldOff+p->bRowid);
   return SQLITE_OK;
 }
 static int sessionDiffNew(void *pCtx, int iVal, sqlite3_value **ppVal){
   SessionDiffCtx *p = (SessionDiffCtx*)pCtx;
-  *ppVal = sqlite3_column_value(p->pStmt, iVal);
+  *ppVal = sqlite3_column_value(p->pStmt, iVal+p->bRowid);
    return SQLITE_OK;
 }
 static int sessionDiffCount(void *pCtx){
   SessionDiffCtx *p = (SessionDiffCtx*)pCtx;
-  return p->nOldOff ? p->nOldOff : sqlite3_column_count(p->pStmt);
+  return (p->nOldOff ? p->nOldOff : sqlite3_column_count(p->pStmt)) - p->bRowid;
 }
 static int sessionDiffDepth(void *pCtx){
   (void)pCtx;
@@ -1653,14 +1703,16 @@ static char *sessionExprCompareOther(
 static char *sessionSelectFindNew(
   const char *zDb1,      /* Pick rows in this db only */
   const char *zDb2,      /* But not in this one */
+  int bRowid,
   const char *zTbl,      /* Table name */
   const char *zExpr
 ){
+  const char *zSel = (bRowid ? SESSIONS_ROWID ", *" : "*");
   char *zRet = sqlite3_mprintf(
-      "SELECT * FROM \"%w\".\"%w\" WHERE NOT EXISTS ("
+      "SELECT %s FROM \"%w\".\"%w\" WHERE NOT EXISTS ("
       "  SELECT 1 FROM \"%w\".\"%w\" WHERE %s"
       ")",
-      zDb1, zTbl, zDb2, zTbl, zExpr
+      zSel, zDb1, zTbl, zDb2, zTbl, zExpr
   );
   return zRet;
 }
@@ -1674,7 +1726,9 @@ static int sessionDiffFindNew(
   char *zExpr
 ){
   int rc = SQLITE_OK;
-  char *zStmt = sessionSelectFindNew(zDb1, zDb2, pTab->zName,zExpr);
+  char *zStmt = sessionSelectFindNew(
+      zDb1, zDb2, pTab->bRowid, pTab->zName, zExpr
+  );
 
   if( zStmt==0 ){
     rc = SQLITE_NOMEM;
@@ -1685,8 +1739,10 @@ static int sessionDiffFindNew(
       SessionDiffCtx *pDiffCtx = (SessionDiffCtx*)pSession->hook.pCtx;
       pDiffCtx->pStmt = pStmt;
       pDiffCtx->nOldOff = 0;
+      pDiffCtx->bRowid = pTab->bRowid;
       while( SQLITE_ROW==sqlite3_step(pStmt) ){
-        sessionPreupdateOneChange(op, pSession, pTab);
+        i64 iRowid = (pTab->bRowid ? sqlite3_column_int64(pStmt, 0) : 0);
+        sessionPreupdateOneChange(op, iRowid, pSession, pTab);
       }
       rc = sqlite3_finalize(pStmt);
     }
@@ -1696,6 +1752,27 @@ static int sessionDiffFindNew(
   return rc;
 }
 
+/*
+** Return a comma-separated list of the fully-qualified (with both database
+** and table name) column names from table pTab. e.g.
+**
+**    "main"."t1"."a", "main"."t1"."b", "main"."t1"."c"
+*/
+static char *sessionAllCols(
+  const char *zDb,
+  SessionTable *pTab
+){
+  int ii;
+  char *zRet = 0;
+  for(ii=0; ii<pTab->nCol; ii++){
+    zRet = sqlite3_mprintf("%z%s\"%w\".\"%w\".\"%w\"%s",
+        zRet, (zRet ? ", " : ""), zDb, pTab->zName, pTab->azCol[ii] 
+    );
+    if( !zRet ) break;
+  }
+  return zRet;
+}
+
 static int sessionDiffFindModified(
   sqlite3_session *pSession, 
   SessionTable *pTab, 
@@ -1710,11 +1787,13 @@ static int sessionDiffFindModified(
   if( zExpr2==0 ){
     rc = SQLITE_NOMEM;
   }else{
+    char *z1 = sessionAllCols(pSession->zDb, pTab);
+    char *z2 = sessionAllCols(zFrom, pTab);
     char *zStmt = sqlite3_mprintf(
-        "SELECT * FROM \"%w\".\"%w\", \"%w\".\"%w\" WHERE %s AND (%z)",
-        pSession->zDb, pTab->zName, zFrom, pTab->zName, zExpr, zExpr2
+        "SELECT %s,%s FROM \"%w\".\"%w\", \"%w\".\"%w\" WHERE %s AND (%z)",
+        z1, z2, pSession->zDb, pTab->zName, zFrom, pTab->zName, zExpr, zExpr2
     );
-    if( zStmt==0 ){
+    if( zStmt==0 || z1==0 || z2==0 ){
       rc = SQLITE_NOMEM;
     }else{
       sqlite3_stmt *pStmt;
@@ -1725,12 +1804,15 @@ static int sessionDiffFindModified(
         pDiffCtx->pStmt = pStmt;
         pDiffCtx->nOldOff = pTab->nCol;
         while( SQLITE_ROW==sqlite3_step(pStmt) ){
-          sessionPreupdateOneChange(SQLITE_UPDATE, pSession, pTab);
+          i64 iRowid = (pTab->bRowid ? sqlite3_column_int64(pStmt, 0) : 0);
+          sessionPreupdateOneChange(SQLITE_UPDATE, iRowid, pSession, pTab);
         }
         rc = sqlite3_finalize(pStmt);
       }
-      sqlite3_free(zStmt);
     }
+    sqlite3_free(zStmt);
+    sqlite3_free(z1);
+    sqlite3_free(z2);
   }
 
   return rc;
@@ -1769,9 +1851,12 @@ int sqlite3session_diff(
       int bHasPk = 0;
       int bMismatch = 0;
       int nCol;                   /* Columns in zFrom.zTbl */
+      int bRowid = 0;
       u8 *abPK;
       const char **azCol = 0;
-      rc = sessionTableInfo(0, db, zFrom, zTbl, &nCol, 0, &azCol, &abPK);
+      rc = sessionTableInfo(0, db, zFrom, zTbl, &nCol, 0, &azCol, &abPK, 
+          pSession->bImplicitPK ? &bRowid : 0
+      );
       if( rc==SQLITE_OK ){
         if( pTo->nCol!=nCol ){
           bMismatch = 1;
@@ -2422,6 +2507,7 @@ static int sessionSelectStmt(
   int bIgnoreNoop,
   const char *zDb,                /* Database name */
   const char *zTab,               /* Table name */
+  int bRowid,
   int nCol,                       /* Number of columns in table */
   const char **azCol,             /* Names of table columns */
   u8 *abPK,                       /* PRIMARY KEY  array */
@@ -2430,7 +2516,7 @@ static int sessionSelectStmt(
   int rc = SQLITE_OK;
   char *zSql = 0;
   const char *zSep = "";
-  const char *zCols = "*";
+  const char *zCols = bRowid ? SESSIONS_ROWID ", *" : "*";
   int nSql = -1;
   int i;
 
@@ -2449,7 +2535,6 @@ static int sessionSelectStmt(
     zCols = "tbl, ?2, stat";
   }else{
     for(i=0; i<nCol; i++){
-
       if( abPK[i] ){
         sessionAppendStr(&pkfield, zSep, &rc);
         sessionAppendStr(&pkvar, zSep, &rc);
@@ -2656,10 +2741,18 @@ static int sessionGenerateChangeset(
       sqlite3_stmt *pSel = 0;     /* SELECT statement to query table pTab */
       int nRewind = buf.nBuf;     /* Initial size of write buffer */
       int nNoop;                  /* Size of buffer after writing tbl header */
+      int bRowid = 0;
 
       /* Check the table schema is still Ok. */
-      rc = sessionTableInfo(0, db, pSession->zDb, zName, &nCol, 0,&azCol,&abPK);
-      if( !rc && (pTab->nCol!=nCol || memcmp(abPK, pTab->abPK, nCol)) ){
+      rc = sessionTableInfo(
+          0, db, pSession->zDb, zName, &nCol, 0, &azCol, &abPK, 
+          (pSession->bImplicitPK ? &bRowid : 0)
+      );
+      if( rc==SQLITE_OK && (
+          pTab->nCol!=nCol 
+       || pTab->bRowid!=bRowid 
+       || memcmp(abPK, pTab->abPK, nCol)
+      )){
         rc = SQLITE_SCHEMA;
       }
 
@@ -2669,7 +2762,7 @@ static int sessionGenerateChangeset(
       /* Build and compile a statement to execute: */
       if( rc==SQLITE_OK ){
         rc = sessionSelectStmt(
-            db, 0, pSession->zDb, zName, nCol, azCol, abPK, &pSel
+            db, 0, pSession->zDb, zName, bRowid, nCol, azCol, abPK, &pSel
         );
       }
 
@@ -2753,8 +2846,8 @@ int sqlite3session_changeset(
   int rc;
 
   if( pnChangeset==0 || ppChangeset==0 ) return SQLITE_MISUSE;
-  rc = sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset,ppChangeset);
-  assert( rc || pnChangeset==0 
+  rc = sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset, ppChangeset);
+  assert( 1 || rc || pnChangeset==0 
        || pSession->bEnableSize==0 || *pnChangeset<=pSession->nMaxChangesetSize 
   );
   return rc;
@@ -2871,6 +2964,19 @@ int sqlite3session_object_config(sqlite3_session *pSession, int op, void *pArg){
       break;
     }
 
+    case SQLITE_SESSION_OBJCONFIG_ROWID: {
+      int iArg = *(int*)pArg;
+      if( iArg>=0 ){
+        if( pSession->pTable ){
+          rc = SQLITE_MISUSE;
+        }else{
+          pSession->bImplicitPK = (iArg!=0);
+        }
+      }
+      *(int*)pArg = pSession->bImplicitPK;
+      break;
+    }
+
     default:
       rc = SQLITE_MISUSE;
   }
@@ -3860,6 +3966,7 @@ struct SessionApplyCtx {
   u8 bRebaseStarted;              /* If table header is already in rebase */
   u8 bRebase;                     /* True to collect rebase information */
   u8 bIgnoreNoop;                 /* True to ignore no-op conflicts */
+  int bRowid;
 };
 
 /* Number of prepared UPDATE statements to cache. */
@@ -4110,8 +4217,9 @@ static int sessionSelectRow(
   const char *zTab,               /* Table name */
   SessionApplyCtx *p              /* Session changeset-apply context */
 ){
+  /* TODO */
   return sessionSelectStmt(db, p->bIgnoreNoop,
-      "main", zTab, p->nCol, p->azCol, p->abPK, &p->pSelect
+      "main", zTab, p->bRowid, p->nCol, p->azCol, p->abPK, &p->pSelect
   );
 }
 
@@ -4807,6 +4915,7 @@ static int sessionChangesetApply(
       sApply.bStat1 = 0;
       sApply.bDeferConstraints = 1;
       sApply.bRebaseStarted = 0;
+      sApply.bRowid = 0;
       memset(&sApply.constraints, 0, sizeof(SessionBuffer));
 
       /* If an xFilter() callback was specified, invoke it now. If the 
@@ -4826,8 +4935,8 @@ static int sessionChangesetApply(
         int i;
 
         sqlite3changeset_pk(pIter, &abPK, 0);
-        rc = sessionTableInfo(0, 
-            db, "main", zNew, &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK
+        rc = sessionTableInfo(0, db, "main", zNew, 
+            &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK, &sApply.bRowid
         );
         if( rc!=SQLITE_OK ) break;
         for(i=0; i<sApply.nCol; i++){
index f3a98d2ecd6c5cde0d7a8f02b5e8e10170b304f9..b5be3164780bf3770a97385e8ad8f2745ae4aca1 100644 (file)
@@ -105,12 +105,25 @@ void sqlite3session_delete(sqlite3_session *pSession);
 **
 **   It is an error (SQLITE_MISUSE) to attempt to modify this setting after 
 **   the first table has been attached to the session object.
+**
+** <dt>SQLITE_SESSION_OBJCONFIG_ROWID <dd>
+**   This option is used to set, clear or query the flag that enables
+**   collection of data for tables with no explicit PRIMARY KEY.
+**
+**   Normally, tables with no explicit PRIMARY KEY are simply ignored
+**   by the sessions module. However, if this flag is set, it behaves
+**   as if such tables have a column "_rowid_ INTEGER PRIMARY KEY" inserted
+**   as their leftmost columns.
+**
+**   It is an error (SQLITE_MISUSE) to attempt to modify this setting after 
+**   the first table has been attached to the session object.
 */
 int sqlite3session_object_config(sqlite3_session*, int op, void *pArg);
 
 /*
 */
-#define SQLITE_SESSION_OBJCONFIG_SIZE 1
+#define SQLITE_SESSION_OBJCONFIG_SIZE  1
+#define SQLITE_SESSION_OBJCONFIG_ROWID 2
 
 /*
 ** CAPI3REF: Enable Or Disable A Session Object
index f10afe03f657a288a6e53ec2ba5fb9b1e60d4dbd..0836238b5d320f62a5d21e0905a2b5ceb57b6f37 100644 (file)
@@ -76,9 +76,11 @@ int sql_exec_changeset(
 ){
   sqlite3_session *pSession = 0;
   int rc;
+  int val = 1;
 
   /* Create a new session object */
   rc = sqlite3session_create(db, "main", &pSession);
+  sqlite3session_object_config(pSession, SQLITE_SESSION_OBJCONFIG_ROWID, &val);
 
   /* Configure the session object to record changes to all tables */
   if( rc==SQLITE_OK ) rc = sqlite3session_attach(pSession, NULL);
@@ -260,7 +262,7 @@ static int SQLITE_TCLAPI test_session_cmd(
     { "diff",         2, "FROMDB TBL", }, /* 8 */
     { "memory_used",  0, "",           }, /* 9 */
     { "changeset_size", 0, "",         }, /* 10 */
-    { "object_config_size", 1, "INTEGER", }, /* 11 */
+    { "object_config", 2, "OPTION INTEGER", }, /* 11 */
     { 0 }
   };
   int iSub;
@@ -379,15 +381,27 @@ static int SQLITE_TCLAPI test_session_cmd(
       Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nSize));
       break;
     }
-    case 11: {
+    case 11: {    /* object_config */
+      struct ObjConfOpt {
+        const char *zName;
+        int opt;
+      } aOpt[] = {
+        { "size", SQLITE_SESSION_OBJCONFIG_SIZE },
+        { "rowid", SQLITE_SESSION_OBJCONFIG_ROWID },
+        { 0, 0 }
+      };
+      size_t sz = sizeof(aOpt[0]);
+
       int rc;
       int iArg;
-      if( Tcl_GetIntFromObj(interp, objv[2], &iArg) ){
+      int iOpt;
+      if( Tcl_GetIndexFromObjStruct(interp,objv[2],aOpt,sz,"option",0,&iOpt) ){
         return TCL_ERROR;
       }
-      rc = sqlite3session_object_config(
-          pSession, SQLITE_SESSION_OBJCONFIG_SIZE, &iArg
-      );
+      if( Tcl_GetIntFromObj(interp, objv[3], &iArg) ){
+        return TCL_ERROR;
+      }
+      rc = sqlite3session_object_config(pSession, aOpt[iOpt].opt, &iArg);
       if( rc!=SQLITE_OK ){
         extern const char *sqlite3ErrName(int);
         Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
index a40d72211a0f51b38d43eda00a0afce9ec0faed9..7b5292db51af4173c9b1933c2928058afe3a91cb 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sa\snote\sabout\sthe\sjournaling\smode\sin\sthe\sOPFS\sVFS.\sNo\scode\schanges.
-D 2023-04-24T04:25:42.837
+C Allow\sthe\ssessions\smodule\sto\sbe\sconfigured\sto\scapture\schanges\sfrom\stables\swith\sno\sexplicit\sPRIMARY\sKEY.
+D 2023-04-24T19:22:21.668
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -449,12 +449,12 @@ F ext/session/session9.test 5409d90d8141881d08285ed1c2c0d8d10fb92069
 F ext/session/sessionA.test 1feeab0b8e03527f08f2f1defb442da25480138f
 F ext/session/sessionB.test c4fb7f8a688787111606e123a555f18ee04f65bb9f2a4bb2aa71d55ce4e6d02c
 F ext/session/sessionC.test f8a5508bc059ae646e5ec9bdbca66ad24bc92fe99fda5790ac57e1f59fce2fdf
-F ext/session/sessionD.test 4f91d0ca8afc4c3969c72c9f0b5ea9527e21de29039937d0d973f821e8470724
+F ext/session/sessionD.test f5c6a762d00bc6ca9d561695c322ba8ecca2bed370486707ef37cf565d2f6c73
 F ext/session/sessionE.test b2010949c9d7415306f64e3c2072ddabc4b8250c98478d3c0c4d064bce83111d
 F ext/session/sessionF.test d37ed800881e742c208df443537bf29aa49fd56eac520d0f0c6df3e6320f3401
 F ext/session/sessionG.test 3efe388282d641b65485b5462e67851002cd91a282dc95b685d085eb8efdad0a
 F ext/session/sessionH.test 71bbff6b1abb2c4ac62b84dee53273c37e0b21e5fde3aed80929403e091ef859
-F ext/session/session_common.tcl db0dda567c75950604072251744e9a6ad5795a3009963c44eb8510f23a8cda64
+F ext/session/session_common.tcl e5598096425486b363718e2cda48ee85d660c96b4f8ea9d9d7a4c3ef514769da
 F ext/session/session_speed_test.c dcf0ef58d76b70c8fbd9eab3be77cf9deb8bc1638fed8be518b62d6cbdef88b3
 F ext/session/sessionat.test 00c8badb35e43a2f12a716d2734a44d614ff62361979b6b85419035bc04b45ee
 F ext/session/sessionbig.test 47c381e7acfabeef17d98519a3080d69151723354d220afa2053852182ca7adf
@@ -465,13 +465,14 @@ F ext/session/sessioninvert.test 04075517a9497a80d39c495ba6b44f3982c7371129b89e2
 F ext/session/sessionmem.test f2a735db84a3e9e19f571033b725b0b2daf847f3f28b1da55a0c1a4e74f1de09
 F ext/session/sessionnoop.test a9366a36a95ef85f8a3687856ebef46983df399541174cb1ede2ee53b8011bc7
 F ext/session/sessionnoop2.test 5c9a882219e54711c98dccd2fd81392f189a59325e4fb5d8ed25e33a0c2f0ba2
-F ext/session/sessionrebase.test ccfa716b23bd1d3b03217ee58cfd90c78d4b99f53e6a9a2f05e82363b9142810
-F ext/session/sessionsize.test 6f644aff31c7f1e4871e9ff3542766e18da68fc7e587b83a347ea9820a002dd8
+F ext/session/sessionrebase.test 702378bdcb5062f1106e74457beca8797d09c113a81768734a58b197b5b334e2
+F ext/session/sessionrowid.test 6323ba831721205fd729929745038fd54e9d128c66c654b8d0b26853095a321c
+F ext/session/sessionsize.test 8fcf4685993c3dbaa46a24183940ab9f5aa9ed0d23e5fb63bfffbdb56134b795
 F ext/session/sessionstat1.test b039e38e2ba83767b464baf39b297cc0b1cc6f3292255cb467ea7e12d0d0280c
 F ext/session/sessionwor.test 6fd9a2256442cebde5b2284936ae9e0d54bde692d0f5fd009ecef8511f4cf3fc
-F ext/session/sqlite3session.c 1795263b72c1a17e48e95a131a69543af3fa31aa8e81271c7c5cb0911f063604
-F ext/session/sqlite3session.h c367c3043dbb57f69cca35258ebbeadb24e8738980b1a1ae1e281c1b0fac3989
-F ext/session/test_session.c b55a669a2150eb7c491b8b42c69a3eed9bc895cf5fea371a2c813b9618f72163
+F ext/session/sqlite3session.c a0d7d71f9b111e443b53afc49b6cb5ccf48734af216c91dbe6640b7e4cd8c740
+F ext/session/sqlite3session.h 24299a3b64f11afc4422ce92d030ffdb2d3181851a1763b4a0432e195b2a8a16
+F ext/session/test_session.c 5285482f83cd92b4c1fe12fcf88210566a18312f4f2aa110f6399dae46aeccbb
 F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
 F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb
@@ -2059,8 +2060,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 17f5dd2d2ae02a95180b9208b7de805922ba20271d3263e3193f0d46f4ec324c
-R 7c6f5a98ea12f09a60bc441886d297ed
-U stephan
-Z c684703beca8cd9cb5def7974649e125
+P e79c95fc130fc302719690eb6391d96070aff825b2b51ef6c4ad459d9a8918d7
+R 1829aad474dae92cc041c644553e0142
+T *branch * sessions-rowid-tables
+T *sym-sessions-rowid-tables *
+T -sym-trunk *
+U dan
+Z 7494f98659d6e6e0ecd97f2ba99a8ae6
 # Remove this line to create a well-formed Fossil manifest.
index 20ed04ba3aef2b480bf08224ec17d90996130f6c..87be73f74f73681c35af6f1aaee07644d9bc545f 100644 (file)
@@ -1 +1 @@
-e79c95fc130fc302719690eb6391d96070aff825b2b51ef6c4ad459d9a8918d7
\ No newline at end of file
+8a612f0860126c0c8473b1e65fcabb9a8821d8bf09fdf3f6018acfc99df9af71
\ No newline at end of file