]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add the sqlite3session_table_filter API to the sessions extension.
authordan <dan@noemail.net>
Fri, 23 Aug 2013 17:43:32 +0000 (17:43 +0000)
committerdan <dan@noemail.net>
Fri, 23 Aug 2013 17:43:32 +0000 (17:43 +0000)
FossilOrigin-Name: b7e4dd889d37c8f57c2d3c7900e802f644aac3ea

ext/session/sessionA.test [new file with mode: 0644]
ext/session/sqlite3session.c
ext/session/sqlite3session.h
ext/session/test_session.c
manifest
manifest.uuid

diff --git a/ext/session/sessionA.test b/ext/session/sessionA.test
new file mode 100644 (file)
index 0000000..1ca0f13
--- /dev/null
@@ -0,0 +1,69 @@
+# 2013 July 04
+#
+# 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 tests that the sessions module handles foreign key constraint
+# violations when applying changesets as required.
+#
+
+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 sessionA
+
+
+forcedelete test.db2
+sqlite3 db2 test.db2
+foreach {tn db} {1 db 2 db2} {
+  do_test 1.$tn.1 {
+    execsql {
+      CREATE TABLE t1(a PRIMARY KEY, b);
+      CREATE TABLE t2(a PRIMARY KEY, b);
+      CREATE TABLE t3(a PRIMARY KEY, b);
+    } $db
+  } {}
+}
+
+proc tbl_filter {zTbl} {
+  return $::table_filter($zTbl)
+}
+
+do_test 2.1 {
+  set ::table_filter(t1) 1
+  set ::table_filter(t2) 0
+  set ::table_filter(t3) 1
+
+  sqlite3session S db main
+  S table_filter tbl_filter 
+
+  execsql {
+    INSERT INTO t1 VALUES('a', 'b');
+    INSERT INTO t2 VALUES('c', 'd');
+    INSERT INTO t3 VALUES('e', 'f');
+  }
+
+  set changeset [S changeset]
+  S delete
+  sqlite3changeset_apply db2 $changeset xConflict
+
+  execsql {
+    SELECT * FROM t1;
+    SELECT * FROM t2;
+    SELECT * FROM t3;
+  } db2
+} {a b e f}
+
+
+finish_test
+
+
index 6fa8107408ab62cc83247613ccb6b92f4df534bb..801ea60717b5dbc6a40a13000a9f289cf3004266 100644 (file)
@@ -23,6 +23,8 @@ struct sqlite3_session {
   int bIndirect;                  /* True if all changes are indirect */
   int bAutoAttach;                /* True to auto-attach tables */
   int rc;                         /* Non-zero if an error has occurred */
+  void *pFilterCtx;               /* First argument to pass to xTableFilter */
+  int (*xTableFilter)(void *pCtx, const char *zTab);
   sqlite3_session *pNext;         /* Next session object on same db. */
   SessionTable *pTable;           /* List of attached tables */
 };
@@ -1066,6 +1068,16 @@ static void xPreUpdate(
       if( !pTab ){
         /* This branch is taken if table zName has not yet been attached to
         ** this session and the auto-attach flag is set.  */
+
+        /* If there is a table-filter configured, invoke it. If it returns 0,
+        ** this change will not be recorded. Break out of the loop early in
+        ** this case.  */
+        if( pSession->xTableFilter 
+         && pSession->xTableFilter(pSession->pFilterCtx, zName)==0
+        ){
+          break;
+        }
+
         pSession->rc = sqlite3session_attach(pSession,zName);
         if( pSession->rc ) break;
         pTab = pSession->pTable;
@@ -1170,6 +1182,19 @@ void sqlite3session_delete(sqlite3_session *pSession){
   sqlite3_free(pSession);
 }
 
+/*
+** Set a table filter on a Session Object.
+*/
+void sqlite3session_table_filter(
+  sqlite3_session *pSession, 
+  int(*xFilter)(void*, const char*),
+  void *pCtx                      /* First argument passed to xFilter */
+){
+  pSession->bAutoAttach = 1;
+  pSession->pFilterCtx = pCtx;
+  pSession->xTableFilter = xFilter;
+}
+
 /*
 ** Attach a table to a session. All subsequent changes made to the table
 ** while the session object is enabled will be recorded.
index f1a7052bcd74f06af14b08039c5c1292f5908719..974a770c7b271dc21cee20f8f795c221ce9ecd6d 100644 (file)
@@ -71,6 +71,7 @@ int sqlite3session_create(
 */
 void sqlite3session_delete(sqlite3_session *pSession);
 
+
 /*
 ** CAPI3REF: Enable Or Disable A Session Object
 **
@@ -152,6 +153,24 @@ int sqlite3session_attach(
   const char *zTab                /* Table name */
 );
 
+/*
+** CAPI3REF: Set a table filter on a Session Object.
+**
+** The second argument (xFilter) is the "filter callback". For changes to rows 
+** in tables that are not attached to the Session oject, the filter is called
+** to determine whether changes to the table's rows should be tracked or not. 
+** If xFilter returns 0, changes is not tracked. Note that once a table is 
+** attached, xFilter will not be called again.
+*/
+void sqlite3session_table_filter(
+  sqlite3_session *pSession,      /* Session object */
+  int(*xFilter)(
+    void *pCtx,                   /* Copy of third arg to _filter_table() */
+    const char *zTab              /* Table name */
+  ),
+  void *pCtx                      /* First argument passed to xFilter */
+);
+
 /*
 ** CAPI3REF: Generate A Changeset From A Session Object
 **
index 49e45c5fb3f1404dead69b536f761e1873f539e3..4340921a67ffc7cb4486ae9e3e26249993e6a367 100644 (file)
@@ -7,18 +7,50 @@
 #include <string.h>
 #include <tcl.h>
 
+typedef struct TestSession TestSession;
+struct TestSession {
+  sqlite3_session *pSession;
+  Tcl_Interp *interp;
+  Tcl_Obj *pFilterScript;
+};
+
 static int test_session_error(Tcl_Interp *interp, int rc){
   extern const char *sqlite3ErrName(int);
   Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
   return TCL_ERROR;
 }
 
+static int test_table_filter(void *pCtx, const char *zTbl){
+  TestSession *p = (TestSession*)pCtx;
+  Tcl_Obj *pEval;
+  int rc;
+  int bRes = 0;
+
+  pEval = Tcl_DuplicateObj(p->pFilterScript);
+  Tcl_IncrRefCount(pEval);
+  rc = Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zTbl, -1));
+  if( rc==TCL_OK ){
+    rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL);
+  }
+  if( rc==TCL_OK ){
+    rc = Tcl_GetBooleanFromObj(p->interp, Tcl_GetObjResult(p->interp), &bRes);
+  }
+  if( rc!=TCL_OK ){
+    /* printf("error: %s\n", Tcl_GetStringResult(p->interp)); */
+    Tcl_BackgroundError(p->interp);
+  }
+  Tcl_DecrRefCount(pEval);
+
+  return bRes;
+}
+
 /*
 ** Tclcmd:  $session attach TABLE
 **          $session changeset
 **          $session delete
 **          $session enable BOOL
 **          $session indirect INTEGER
+**          $session table_filter SCRIPT
 */
 static int test_session_cmd(
   void *clientData,
@@ -26,19 +58,21 @@ static int test_session_cmd(
   int objc,
   Tcl_Obj *CONST objv[]
 ){
-  sqlite3_session *pSession = (sqlite3_session *)clientData;
+  TestSession *p = (TestSession*)clientData;
+  sqlite3_session *pSession = p->pSession;
   struct SessionSubcmd {
     const char *zSub;
     int nArg;
     const char *zMsg;
     int iSub;
   } aSub[] = {
-    { "attach",    1, "TABLE", }, /* 0 */
-    { "changeset", 0, "",      }, /* 1 */
-    { "delete",    0, "",      }, /* 2 */
-    { "enable",    1, "BOOL",  }, /* 3 */
-    { "indirect",  1, "BOOL",  }, /* 4 */
-    { "isempty",   0, "",      }, /* 5 */
+    { "attach",       1, "TABLE",  }, /* 0 */
+    { "changeset",    0, "",       }, /* 1 */
+    { "delete",       0, "",       }, /* 2 */
+    { "enable",       1, "BOOL",   }, /* 3 */
+    { "indirect",     1, "BOOL",   }, /* 4 */
+    { "isempty",      0, "",       }, /* 5 */
+    { "table_filter", 1, "SCRIPT", }, /* 6 */
     { 0 }
   };
   int iSub;
@@ -65,8 +99,8 @@ static int test_session_cmd(
       if( rc!=SQLITE_OK ){
         return test_session_error(interp, rc);
       }
-    }
       break;
+    }
 
     case 1: {      /* changeset */
       int nChange;
@@ -107,14 +141,25 @@ static int test_session_cmd(
       Tcl_SetObjResult(interp, Tcl_NewBooleanObj(val));
       break;
     }
+            
+    case 6: {      /* table_filter */
+      if( p->pFilterScript ) Tcl_DecrRefCount(p->pFilterScript);
+      p->interp = interp;
+      p->pFilterScript = Tcl_DuplicateObj(objv[2]);
+      Tcl_IncrRefCount(p->pFilterScript);
+      sqlite3session_table_filter(pSession, test_table_filter, clientData);
+      break;
+    }
   }
 
   return TCL_OK;
 }
 
 static void test_session_del(void *clientData){
-  sqlite3_session *pSession = (sqlite3_session *)clientData;
-  sqlite3session_delete(pSession);
+  TestSession *p = (TestSession*)clientData;
+  if( p->pFilterScript ) Tcl_DecrRefCount(p->pFilterScript);
+  sqlite3session_delete(p->pSession);
+  ckfree(p);
 }
 
 /*
@@ -129,7 +174,7 @@ static int test_sqlite3session(
   sqlite3 *db;
   Tcl_CmdInfo info;
   int rc;                         /* sqlite3session_create() return code */
-  sqlite3_session *pSession;      /* New session object */
+  TestSession *p;                 /* New wrapper object */
 
   if( objc!=4 ){
     Tcl_WrongNumArgs(interp, 1, objv, "CMD DB-HANDLE DB-NAME");
@@ -142,13 +187,16 @@ static int test_sqlite3session(
   }
   db = *(sqlite3 **)info.objClientData;
 
-  rc = sqlite3session_create(db, Tcl_GetString(objv[3]), &pSession);
+  p = (TestSession*)ckalloc(sizeof(TestSession));
+  memset(p, 0, sizeof(TestSession));
+  rc = sqlite3session_create(db, Tcl_GetString(objv[3]), &p->pSession);
   if( rc!=SQLITE_OK ){
+    ckfree(p);
     return test_session_error(interp, rc);
   }
 
   Tcl_CreateObjCommand(
-      interp, Tcl_GetString(objv[1]), test_session_cmd, (ClientData)pSession,
+      interp, Tcl_GetString(objv[1]), test_session_cmd, (ClientData)p,
       test_session_del
   );
   Tcl_SetObjResult(interp, objv[1]);
index eeb634ab672f83d292bf998717fa632904c4d2e1..11155359affa5ce46c6a635afe39a918a6d5dd7f 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Merge\sin\sminor\sbug\sfixes\sand\sperformance\stweaks\sfrom\strunk\sleading\sup\sto\nthe\sversion\s3.8.0\srelease.
-D 2013-08-22T15:07:08.266
+C Add\sthe\ssqlite3session_table_filter\sAPI\sto\sthe\ssessions\sextension.
+D 2013-08-23T17:43:32.715
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in aff38bc64c582dd147f18739532198372587b0f0
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -144,11 +144,12 @@ F ext/session/session5.test 8fdfaf9dba28a2f1c6b89b06168bdab1fef2d478
 F ext/session/session6.test 443789bc2fca12e4f7075cf692c60b8a2bea1a26
 F ext/session/session8.test 7d35947ad329b8966f095d34f9617a9eff52dc65
 F ext/session/session9.test 43acfdc57647c2ce4b36dbd0769112520ea6af14
+F ext/session/sessionA.test eb05c13e4ef1ca8046a3a6dbf2d5f6f5b04a11d4
 F ext/session/session_common.tcl 1539d8973b2aea0025c133eb0cc4c89fcef541a5
 F ext/session/sessionfault.test 496291b287ba3c0b14ca2e074425e29cc92a64a6
-F ext/session/sqlite3session.c e0345e8425a36fb8ac107175ebae46b4af8873e4
-F ext/session/sqlite3session.h c7db3d8515eba7f41eeb8698a25e58d24cd384bf
-F ext/session/test_session.c 12053e9190653164fa624427cf90d1f46ca7f179
+F ext/session/sqlite3session.c 63eea3741e8ac1574d4c183fd92a6a50b1415357
+F ext/session/sqlite3session.h 6c35057241567ed6319f750ee504a81c459225e1
+F ext/session/test_session.c d38968307c05229cc8cd603722cf305d6f768832
 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
 F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
 F magic.txt f2b23a6bde8f1c6e86b957e4d94eab0add520b0d
@@ -1119,7 +1120,7 @@ F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
 F tool/wherecosttest.c f407dc4c79786982a475261866a161cd007947ae
 F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac
-P 3e4033285deb417bd72c008917729dbf3bf4e90d 0775501acf152dcbf4dd039f4511f3d8c4330d85
-R f6e422c39c44105817eb40d9d54618de
-U drh
-Z f9597afebde4efa08f5ab31e24bede7f
+P 831492dca8bcfb1a1f83a8bb15de9cc94f29f07e
+R cbce615b433685cc6c1cb9210155f0f8
+U dan
+Z 2d1cb3afe326cb66267c2981ea353dde
index 8fadc4e3f692309e197c2f72ed40d669669863c9..023cc42f5d9653dae488b1dbdfb20aac3becf9ef 100644 (file)
@@ -1 +1 @@
-831492dca8bcfb1a1f83a8bb15de9cc94f29f07e
\ No newline at end of file
+b7e4dd889d37c8f57c2d3c7900e802f644aac3ea
\ No newline at end of file