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:
@ -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{
|
||||
|
Reference in New Issue
Block a user