1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

Experimental change to the handling of foreign key constraint violations when applying a changeset: all foreign keys, immediate and deferred, are deferred until the end of the transaction (or sub-transaction) opened by the sqlite3changeset_apply(). A single call to the conflict-handler (if any) is made if any FK constraint violations are still present in the database at this point. The conflict-handler may choose to rollback the changeset, or to apply it, constraint violations and all.

FossilOrigin-Name: 1d44e5d3c2b1dc958442f9114a960b256e002ed3
This commit is contained in:
dan
2013-07-03 19:53:05 +00:00
parent 35e2858e98
commit cb3e4b797e
16 changed files with 391 additions and 136 deletions

View File

@ -1,6 +1,5 @@
#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
#include "sqlite3session.h"
#include <assert.h>
#include <string.h>
@ -2110,6 +2109,26 @@ int sqlite3changeset_conflict(
return SQLITE_OK;
}
/*
** This function may only be called with an iterator passed to an
** SQLITE_CHANGESET_FOREIGN_KEY conflict handler callback. In this case
** it sets the output variable to the total number of known foreign key
** violations in the destination database and returns SQLITE_OK.
**
** In all other cases this function returns SQLITE_MISUSE.
*/
int sqlite3changeset_fk_conflicts(
sqlite3_changeset_iter *pIter, /* Changeset iterator */
int *pnOut /* OUT: Number of FK violations */
){
if( pIter->pConflict || pIter->apValue ){
return SQLITE_MISUSE;
}
*pnOut = pIter->nCol;
return SQLITE_OK;
}
/*
** Finalize an iterator allocated with sqlite3changeset_start().
**
@ -2845,6 +2864,9 @@ int sqlite3changeset_apply(
sqlite3_mutex_enter(sqlite3_db_mutex(db));
rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0);
if( rc==SQLITE_OK ){
rc = sqlite3_exec(db, "PRAGMA defer_foreign_keys = 1", 0, 0, 0);
}
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3changeset_next(pIter) ){
int nCol;
int op;
@ -2948,6 +2970,23 @@ int sqlite3changeset_apply(
sqlite3changeset_finalize(pIter);
}
if( rc==SQLITE_OK ){
int nFk = sqlite3_foreign_key_check(db);
if( nFk>0 ){
int res = SQLITE_CHANGESET_ABORT;
if( xConflict ){
sqlite3_changeset_iter sIter;
memset(&sIter, 0, sizeof(sIter));
sIter.nCol = nFk;
res = xConflict(pCtx, SQLITE_CHANGESET_FOREIGN_KEY, &sIter);
}
if( res!=SQLITE_CHANGESET_OMIT ){
rc = SQLITE_CONSTRAINT;
}
}
}
sqlite3_exec(db, "PRAGMA defer_foreign_keys = 0", 0, 0, 0);
if( rc==SQLITE_OK ){
rc = sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
}else{