diff --git a/ext/session/sqlite3session.c b/ext/session/sqlite3session.c
index 50c436b8c4..78d2b6d86c 100644
--- a/ext/session/sqlite3session.c
+++ b/ext/session/sqlite3session.c
@@ -2756,6 +2756,10 @@ int sqlite3changeset_apply(
sqlite3 *db, /* Apply change to "main" db of this handle */
int nChangeset, /* Size of changeset in bytes */
void *pChangeset, /* Changeset blob */
+ int(*xFilter)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ const char *zTab /* Table name */
+ ),
int(*xConflict)(
void *pCtx, /* Copy of fifth arg to _apply() */
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
@@ -2788,7 +2792,6 @@ int sqlite3changeset_apply(
if( zTab==0 || sqlite3_strnicmp(zNew, zTab, nTab+1) ){
u8 *abPK;
- schemaMismatch = 0;
sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */
sqlite3_finalize(sApply.pDelete);
sqlite3_finalize(sApply.pUpdate);
@@ -2797,40 +2800,50 @@ int sqlite3changeset_apply(
memset(&sApply, 0, sizeof(sApply));
sApply.db = db;
- sqlite3changeset_pk(pIter, &abPK, 0);
- rc = sessionTableInfo(
- db, "main", zNew, &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK
- );
- if( rc!=SQLITE_OK ) break;
-
- if( sApply.nCol==0 ){
- schemaMismatch = 1;
- sqlite3_log(SQLITE_SCHEMA,
- "sqlite3changeset_apply(): no such table: %s", zTab
+ /* If an xFilter() callback was specified, invoke it now. If the
+ ** xFilter callback returns zero, skip this table. If it returns
+ ** non-zero, proceed. */
+ schemaMismatch = (xFilter && (0==xFilter(pCtx, zNew)));
+ if( schemaMismatch ){
+ zTab = sqlite3_mprintf("%s", zNew);
+ nTab = strlen(zTab);
+ sApply.azCol = (const char **)zTab;
+ }else{
+ sqlite3changeset_pk(pIter, &abPK, 0);
+ rc = sessionTableInfo(
+ db, "main", zNew, &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK
);
+ if( rc!=SQLITE_OK ) break;
+
+ if( sApply.nCol==0 ){
+ schemaMismatch = 1;
+ sqlite3_log(SQLITE_SCHEMA,
+ "sqlite3changeset_apply(): no such table: %s", zTab
+ );
+ }
+ else if( sApply.nCol!=nCol ){
+ schemaMismatch = 1;
+ sqlite3_log(SQLITE_SCHEMA,
+ "sqlite3changeset_apply(): table %s has %d columns, expected %d",
+ zTab, sApply.nCol, nCol
+ );
+ }
+ else if( memcmp(sApply.abPK, abPK, nCol)!=0 ){
+ schemaMismatch = 1;
+ sqlite3_log(SQLITE_SCHEMA, "sqlite3changeset_apply(): "
+ "primary key mismatch for table %s", zTab
+ );
+ }
+ else if(
+ (rc = sessionSelectRow(db, zTab, &sApply))
+ || (rc = sessionUpdateRow(db, zTab, &sApply))
+ || (rc = sessionDeleteRow(db, zTab, &sApply))
+ || (rc = sessionInsertRow(db, zTab, &sApply))
+ ){
+ break;
+ }
+ nTab = sqlite3Strlen30(zTab);
}
- else if( sApply.nCol!=nCol ){
- schemaMismatch = 1;
- sqlite3_log(SQLITE_SCHEMA,
- "sqlite3changeset_apply(): table %s has %d columns, expected %d",
- zTab, sApply.nCol, nCol
- );
- }
- else if( memcmp(sApply.abPK, abPK, nCol)!=0 ){
- schemaMismatch = 1;
- sqlite3_log(SQLITE_SCHEMA,
- "sqlite3changeset_apply(): primary key mismatch for table %s", zTab
- );
- }
- else if(
- (rc = sessionSelectRow(db, zTab, &sApply))
- || (rc = sessionUpdateRow(db, zTab, &sApply))
- || (rc = sessionDeleteRow(db, zTab, &sApply))
- || (rc = sessionInsertRow(db, zTab, &sApply))
- ){
- break;
- }
- nTab = sqlite3Strlen30(zTab);
}
/* If there is a schema mismatch on the current table, proceed to the
diff --git a/ext/session/sqlite3session.h b/ext/session/sqlite3session.h
index 38164401f4..d8db1d6137 100644
--- a/ext/session/sqlite3session.h
+++ b/ext/session/sqlite3session.h
@@ -610,9 +610,19 @@ int sqlite3changeset_concat(
** "main" database attached to handle db with the changes found in the
** changeset passed via the second and third arguments.
**
-** For each change in the changeset, this function tests that the target
-** database contains a compatible table. A table is considered compatible
-** if all of the following are true:
+** The fourth argument (xFilter) passed to this function is the "filter
+** callback". If it is not NULL, then for each table affected by at least one
+** change in the changeset, the filter callback is invoked with
+** the table name as the second argument, and a copy of the context pointer
+** passed as the sixth argument to this function as the first. If the "filter
+** callback" returns zero, then no attempt is made to apply any changes to
+** the table. Otherwise, if the return value is non-zero or the xFilter
+** argument to this function is NULL, all changes related to the table are
+** attempted.
+**
+** For each table that is not excluded by the filter callback, this function
+** tests that the target database contains a compatible table. A table is
+** considered compatible if all of the following are true:
**
**
** - The table has the same name as the name recorded in the
@@ -623,17 +633,17 @@ int sqlite3changeset_concat(
** recorded in the changeset.
**
**
-** If there is no compatible table, it is not an error, but the change is
-** not applied. A warning message is issued via the sqlite3_log() mechanism
-** with the error code SQLITE_SCHEMA. At most one such warning is issued for
-** each table in the changeset.
+** If there is no compatible table, it is not an error, but none of the
+** changes associated with the table are applied. A warning message is issued
+** via the sqlite3_log() mechanism with the error code SQLITE_SCHEMA. At most
+** one such warning is issued for each table in the changeset.
**
-** Otherwise, if there is a compatible table, an attempt is made to modify
-** the table contents according to the UPDATE, INSERT or DELETE change.
-** If a change cannot be applied cleanly, the conflict handler function
-** passed as the fourth argument to sqlite3changeset_apply() may be invoked.
-** A description of exactly when the conflict handler is invoked for each
-** type of change is below.
+** For each change for which there is a compatible table, an attempt is made
+** to modify the table contents according to the UPDATE, INSERT or DELETE
+** change. If a change cannot be applied cleanly, the conflict handler
+** function passed as the fifth argument to sqlite3changeset_apply() may be
+** invoked. A description of exactly when the conflict handler is invoked for
+** each type of change is below.
**
** Each time the conflict handler function is invoked, it must return one
** of [SQLITE_CHANGESET_OMIT], [SQLITE_CHANGESET_ABORT] or
@@ -729,8 +739,12 @@ int sqlite3changeset_apply(
sqlite3 *db, /* Apply change to "main" db of this handle */
int nChangeset, /* Size of changeset in bytes */
void *pChangeset, /* Changeset blob */
+ int(*xFilter)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ const char *zTab /* Table name */
+ ),
int(*xConflict)(
- void *pCtx, /* Copy of fifth arg to _apply() */
+ void *pCtx, /* Copy of sixth arg to _apply() */
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
sqlite3_changeset_iter *p /* Handle describing change and conflict */
),
diff --git a/ext/session/test_session.c b/ext/session/test_session.c
index 0993073328..9817b3c2f4 100644
--- a/ext/session/test_session.c
+++ b/ext/session/test_session.c
@@ -185,7 +185,8 @@ static void test_append_value(Tcl_Obj *pList, sqlite3_value *pVal){
typedef struct TestConflictHandler TestConflictHandler;
struct TestConflictHandler {
Tcl_Interp *interp;
- Tcl_Obj *pScript;
+ Tcl_Obj *pConflictScript;
+ Tcl_Obj *pFilterScript;
};
static int test_obj_eq_string(Tcl_Obj *p, const char *z){
@@ -199,6 +200,29 @@ static int test_obj_eq_string(Tcl_Obj *p, const char *z){
return (nObj==n && (n==0 || 0==memcmp(zObj, z, n)));
}
+static int test_filter_handler(
+ void *pCtx, /* Pointer to TestConflictHandler structure */
+ const char *zTab /* Table name */
+){
+ TestConflictHandler *p = (TestConflictHandler *)pCtx;
+ int res = 1;
+ Tcl_Obj *pEval;
+ Tcl_Interp *interp = p->interp;
+
+ pEval = Tcl_DuplicateObj(p->pFilterScript);
+ Tcl_IncrRefCount(pEval);
+
+ if( TCL_OK!=Tcl_ListObjAppendElement(0, pEval, Tcl_NewStringObj(zTab, -1))
+ || TCL_OK!=Tcl_EvalObjEx(interp, pEval, TCL_EVAL_GLOBAL)
+ || TCL_OK!=Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &res)
+ ){
+ Tcl_BackgroundError(interp);
+ }
+
+ Tcl_DecrRefCount(pEval);
+ return res;
+}
+
static int test_conflict_handler(
void *pCtx, /* Pointer to TestConflictHandler structure */
int eConf, /* DATA, MISSING, CONFLICT, CONSTRAINT */
@@ -213,7 +237,7 @@ static int test_conflict_handler(
const char *zTab; /* Name of table conflict is on */
int nCol; /* Number of columns in table zTab */
- pEval = Tcl_DuplicateObj(p->pScript);
+ pEval = Tcl_DuplicateObj(p->pConflictScript);
Tcl_IncrRefCount(pEval);
sqlite3changeset_op(pIter, &zTab, &nCol, &op, 0);
@@ -342,7 +366,7 @@ static int test_conflict_handler(
}
/*
-** sqlite3changeset_apply DB CHANGESET SCRIPT
+** sqlite3changeset_apply DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?
*/
static int test_sqlite3changeset_apply(
void * clientData,
@@ -357,8 +381,10 @@ static int test_sqlite3changeset_apply(
int nChangeset; /* Size of buffer aChangeset in bytes */
TestConflictHandler ctx;
- if( objc!=4 ){
- Tcl_WrongNumArgs(interp, 1, objv, "DB CHANGESET SCRIPT");
+ if( objc!=4 && objc!=5 ){
+ Tcl_WrongNumArgs(interp, 1, objv,
+ "DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?"
+ );
return TCL_ERROR;
}
if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &info) ){
@@ -367,11 +393,12 @@ static int test_sqlite3changeset_apply(
}
db = *(sqlite3 **)info.objClientData;
pChangeset = (void *)Tcl_GetByteArrayFromObj(objv[2], &nChangeset);
- ctx.pScript = objv[3];
+ ctx.pConflictScript = objv[3];
+ ctx.pFilterScript = objc==5 ? objv[4] : 0;
ctx.interp = interp;
- rc = sqlite3changeset_apply(
- db, nChangeset, pChangeset, test_conflict_handler, (void *)&ctx
+ rc = sqlite3changeset_apply(db, nChangeset, pChangeset,
+ (objc==5) ? test_filter_handler : 0, test_conflict_handler, (void *)&ctx
);
if( rc!=SQLITE_OK ){
return test_session_error(interp, rc);
diff --git a/manifest b/manifest
index 31ce25fa38..f77afa1ffe 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Modifications\sso\sthat\sthe\ssessions\sextension\sworks\swith\sblob\shandles.
-D 2011-07-11T19:45:38.954
+C Add\sthe\sxFilter\scallback\sto\sthe\ssqlite3changeset_apply()\sfunction.\sThis\scallback\sallows\sthe\sapplication\sto\saccept\sor\sreject\schanges\son\sa\sper-table\sbasis\swhen\sapplying\sa\schangeset.
+D 2011-07-13T15:21:02.741
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in c1d7a7f4fd8da6b1815032efca950e3d5125407e
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -110,9 +110,9 @@ F ext/session/session5.test 8fdfaf9dba28a2f1c6b89b06168bdab1fef2d478
F ext/session/session6.test 443789bc2fca12e4f7075cf692c60b8a2bea1a26
F ext/session/session_common.tcl 1539d8973b2aea0025c133eb0cc4c89fcef541a5
F ext/session/sessionfault.test 401045278298a242cbc2e4bc986c102f01ff2180
-F ext/session/sqlite3session.c 26de50c3e34d89ae62e97024ad07e772e1c52db2
-F ext/session/sqlite3session.h 665f5591562e3c71eb3d0da26f1a1efae26f7bcf
-F ext/session/test_session.c 311e5b9228374d0b5780448f289847ff1cf7d388
+F ext/session/sqlite3session.c c05d28332fcdd66c82de85f5459d71554e2259d7
+F ext/session/sqlite3session.h f34905c818569779ddaea1bbef43469177614c69
+F ext/session/test_session.c 209f13fa8f4a597ffcc15fd0f8a3f27ed079c5e5
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F main.mk f56d9895882f5cdd9f9f9ba8f8a679e9202288c1
@@ -961,7 +961,7 @@ F tool/symbols.sh bc2a3709940d47c8ac8e0a1fdf17ec801f015a00
F tool/tostr.awk 11760e1b94a5d3dcd42378f3cc18544c06cfa576
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
F tool/warnings.sh 347d974d143cf132f953b565fbc03026f19fcb4d
-P 840bf9c2d92192ee3cc2aa7c0e9bdb805a066fd4
-R c823c2479e73fc426d6fab6fb150638b
+P 82ac16c4f873d3bd7c22f36ba7b974b4903a2d50
+R c22fa4ebad64b5fe1347a3f0a2dcb7e8
U dan
-Z 6339a08530525dea9c6675e0fc9a27f5
+Z 6a509799e9e665bc4a33861bc6dba39f
diff --git a/manifest.uuid b/manifest.uuid
index fecbc08bba..527039d08d 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-82ac16c4f873d3bd7c22f36ba7b974b4903a2d50
\ No newline at end of file
+282474c42f24f0e66c69b576b72ef8ce764d49e2
\ No newline at end of file