Add the xFilter callback to the sqlite3changeset_apply() function. This callback allows the application to accept or reject changes on a per-table basis when applying a changeset.
FossilOrigin-Name: 282474c42f24f0e66c69b576b72ef8ce764d49e2
This commit is contained in:
parent
e437ca5ec0
commit
40368988da
@ -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
|
||||
|
@ -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:
|
||||
**
|
||||
** <ul>
|
||||
** <li> The table has the same name as the name recorded in the
|
||||
@ -623,17 +633,17 @@ int sqlite3changeset_concat(
|
||||
** recorded in the changeset.
|
||||
** </ul>
|
||||
**
|
||||
** 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 */
|
||||
),
|
||||
|
@ -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);
|
||||
|
16
manifest
16
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
|
||||
|
@ -1 +1 @@
|
||||
82ac16c4f873d3bd7c22f36ba7b974b4903a2d50
|
||||
282474c42f24f0e66c69b576b72ef8ce764d49e2
|
Loading…
x
Reference in New Issue
Block a user