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

Fix handling of schema mismatches in sqlite3session.c so that it matches the docs in sqlite3session.h.

FossilOrigin-Name: 506a0d7a710e1ff2f367821e73f5080fcf63fbc5
This commit is contained in:
dan
2011-03-24 16:04:54 +00:00
parent 244593c846
commit ca62ad57f0
5 changed files with 142 additions and 23 deletions

88
ext/session/session3.test Normal file
View File

@ -0,0 +1,88 @@
# 2011 March 24
#
# 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 implements regression tests for the session module.
#
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
set testprefix session3
# These tests - session3-1.* - verify that the session module behaves
# correctly when confronted with a schema mismatch when applying a
# changeset (in function sqlite3changeset_apply()).
#
# session3-1.1.*: Table does not exist in target db.
# session3-1.2.*: Table has wrong number of columns in target db.
# session3-1.3.*: Table has wrong PK columns in target db.
#
db close
sqlite3_shutdown
test_sqlite3_log log
sqlite3 db test.db
proc log {code msg} { lappend ::log $code $msg }
forcedelete test.db2
sqlite3 db2 test.db2
do_execsql_test 1.0 {
CREATE TABLE t1(a PRIMARY KEY, b);
}
do_test 1.1 {
set ::log {}
do_then_apply_sql {
INSERT INTO t1 VALUES(1, 2);
INSERT INTO t1 VALUES(3, 4);
}
set ::log
} {SQLITE_SCHEMA {sqlite3changeset_apply(): no such table: t1}}
do_test 1.2.0 {
execsql { CREATE TABLE t1(a PRIMARY KEY, b, c) } db2
} {}
do_test 1.2.1 {
set ::log {}
do_then_apply_sql {
INSERT INTO t1 VALUES(5, 6);
INSERT INTO t1 VALUES(7, 8);
}
set ::log
} {SQLITE_SCHEMA {sqlite3changeset_apply(): table t1 has 3 columns, expected 2}}
do_test 1.3.0 {
execsql {
DROP TABLE t1;
CREATE TABLE t1(a, b PRIMARY KEY);
} db2
} {}
do_test 1.3.1 {
set ::log {}
do_then_apply_sql {
INSERT INTO t1 VALUES(9, 10);
INSERT INTO t1 VALUES(11, 12);
}
set ::log
} {SQLITE_SCHEMA {sqlite3changeset_apply(): primary key mismatch for table t1}}
catch { db close }
catch { db2 close }
sqlite3_shutdown
test_sqlite3_log
sqlite3_initialize
finish_test

View File

@ -544,7 +544,7 @@ static int sessionTableInfo(
sqlite3 *db, /* Database connection */ sqlite3 *db, /* Database connection */
const char *zDb, /* Name of attached database (e.g. "main") */ const char *zDb, /* Name of attached database (e.g. "main") */
const char *zThis, /* Table name */ const char *zThis, /* Table name */
int nCol, /* Expected number of columns */ int *pnCol, /* OUT: number of columns */
const char **pzTab, /* OUT: Copy of zThis */ const char **pzTab, /* OUT: Copy of zThis */
const char ***pazCol, /* OUT: Array of column names for table */ const char ***pazCol, /* OUT: Array of column names for table */
u8 **pabPK /* OUT: Array of booleans - true for PK col */ u8 **pabPK /* OUT: Array of booleans - true for PK col */
@ -577,9 +577,6 @@ static int sessionTableInfo(
} }
rc = sqlite3_reset(pStmt); rc = sqlite3_reset(pStmt);
if( nDbCol!=nCol ){
rc = SQLITE_SCHEMA;
}
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1); nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1);
pAlloc = sqlite3_malloc(nByte); pAlloc = sqlite3_malloc(nByte);
@ -589,9 +586,9 @@ static int sessionTableInfo(
} }
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
azCol = (char **)pAlloc; azCol = (char **)pAlloc;
pAlloc = (u8 *)&azCol[nCol]; pAlloc = (u8 *)&azCol[nDbCol];
abPK = (u8 *)pAlloc; abPK = (u8 *)pAlloc;
pAlloc = &abPK[nCol]; pAlloc = &abPK[nDbCol];
if( pzTab ){ if( pzTab ){
memcpy(pAlloc, zThis, nThis+1); memcpy(pAlloc, zThis, nThis+1);
*pzTab = (char *)pAlloc; *pzTab = (char *)pAlloc;
@ -619,9 +616,11 @@ static int sessionTableInfo(
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
*pazCol = (const char **)azCol; *pazCol = (const char **)azCol;
*pabPK = abPK; *pabPK = abPK;
*pnCol = nDbCol;
}else{ }else{
*pazCol = 0; *pazCol = 0;
*pabPK = 0; *pabPK = 0;
*pnCol = 0;
if( pzTab ) *pzTab = 0; if( pzTab ) *pzTab = 0;
sqlite3_free(azCol); sqlite3_free(azCol);
} }
@ -648,11 +647,13 @@ static int sessionTableInfo(
static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){ static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){
if( pTab->nCol==0 ){ if( pTab->nCol==0 ){
assert( pTab->azCol==0 || pTab->abPK==0 ); assert( pTab->azCol==0 || pTab->abPK==0 );
pTab->nCol = sqlite3_preupdate_count(pSession->db);
pSession->rc = sessionTableInfo(pSession->db, pSession->zDb, pSession->rc = sessionTableInfo(pSession->db, pSession->zDb,
pTab->zName, pTab->nCol, 0, &pTab->azCol, &pTab->abPK pTab->zName, &pTab->nCol, 0, &pTab->azCol, &pTab->abPK
); );
}else if( pTab->nCol!=sqlite3_preupdate_count(pSession->db) ){ }
if( pSession->rc==SQLITE_OK
&& pTab->nCol!=sqlite3_preupdate_count(pSession->db)
){
pSession->rc = SQLITE_SCHEMA; pSession->rc = SQLITE_SCHEMA;
} }
return pSession->rc; return pSession->rc;
@ -2402,6 +2403,7 @@ int sqlite3changeset_apply(
), ),
void *pCtx /* First argument passed to xConflict */ void *pCtx /* First argument passed to xConflict */
){ ){
int schemaMismatch = 0;
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */ sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
int rc; /* Return code */ int rc; /* Return code */
const char *zTab = 0; /* Name of current table */ const char *zTab = 0; /* Name of current table */
@ -2420,9 +2422,13 @@ int sqlite3changeset_apply(
int bReplace = 0; int bReplace = 0;
int bRetry = 0; int bRetry = 0;
const char *zNew; const char *zNew;
sqlite3changeset_op(pIter, &zNew, &nCol, &op, 0); sqlite3changeset_op(pIter, &zNew, &nCol, &op, 0);
if( zTab==0 || sqlite3_strnicmp(zNew, zTab, nTab+1) ){ if( zTab==0 || sqlite3_strnicmp(zNew, zTab, nTab+1) ){
u8 *abPK;
schemaMismatch = 0;
sqlite3_free(sApply.azCol); sqlite3_free(sApply.azCol);
sqlite3_finalize(sApply.pDelete); sqlite3_finalize(sApply.pDelete);
sqlite3_finalize(sApply.pUpdate); sqlite3_finalize(sApply.pUpdate);
@ -2430,23 +2436,47 @@ int sqlite3changeset_apply(
sqlite3_finalize(sApply.pSelect); sqlite3_finalize(sApply.pSelect);
memset(&sApply, 0, sizeof(sApply)); memset(&sApply, 0, sizeof(sApply));
sApply.db = db; sApply.db = db;
sApply.nCol = nCol;
sqlite3changeset_pk(pIter, &abPK, 0);
rc = sessionTableInfo( rc = sessionTableInfo(
db, "main", zNew, nCol, &zTab, &sApply.azCol, &sApply.abPK); db, "main", zNew, &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK
);
if( rc!=SQLITE_OK ) break;
if( rc!=SQLITE_OK if( sApply.nCol==0 ){
|| (rc = sessionSelectRow(db, zTab, &sApply)) 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 = sessionUpdateRow(db, zTab, &sApply))
|| (rc = sessionDeleteRow(db, zTab, &sApply)) || (rc = sessionDeleteRow(db, zTab, &sApply))
|| (rc = sessionInsertRow(db, zTab, &sApply)) || (rc = sessionInsertRow(db, zTab, &sApply))
){ ){
break; break;
} }
nTab = strlen(zTab); nTab = strlen(zTab);
} }
/* If there is a schema mismatch on the current table, proceed to the
** next change. A log message has already been issued. */
if( schemaMismatch ) continue;
rc = sessionApplyOneOp(pIter, &sApply, xConflict, pCtx, &bReplace, &bRetry); rc = sessionApplyOneOp(pIter, &sApply, xConflict, pCtx, &bReplace, &bRetry);
if( rc==SQLITE_OK && bRetry ){ if( rc==SQLITE_OK && bRetry ){

View File

@ -48,7 +48,7 @@ typedef struct sqlite3_changeset_iter sqlite3_changeset_iter;
** **
** The session object will be used to create changesets for tables in ** The session object will be used to create changesets for tables in
** database zDb, where zDb is either "main", or "temp", or the name of an ** database zDb, where zDb is either "main", or "temp", or the name of an
** attached database. It is not an error if database zDb does not exist ** attached database. It is not an error if database zDb is not attached
** to the database when the session object is created. ** to the database when the session object is created.
*/ */
int sqlite3session_create( int sqlite3session_create(

View File

@ -1,5 +1,5 @@
C Store\sprimary\skey\sdefinitions\sfor\smodified\stables\sin\schangesets.\sAdd\sthe\ssqlite3changeset_pk()\sAPI\sto\sextract\sthis\sdata\sfrom\sa\schangeset\siterator. C Fix\shandling\sof\sschema\smismatches\sin\ssqlite3session.c\sso\sthat\sit\smatches\sthe\sdocs\sin\ssqlite3session.h.
D 2011-03-24T11:22:59 D 2011-03-24T16:04:55
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 27701a1653595a1f2187dc61c8117e00a6c1d50f F Makefile.in 27701a1653595a1f2187dc61c8117e00a6c1d50f
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@ -101,10 +101,11 @@ F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de
F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024 F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
F ext/session/session1.test b2da15b9d727d7f4e5fe95599b32b92d93b5a970 F ext/session/session1.test b2da15b9d727d7f4e5fe95599b32b92d93b5a970
F ext/session/session2.test 8da094318ac88953478c43d0bfb0aa723ee0e379 F ext/session/session2.test 8da094318ac88953478c43d0bfb0aa723ee0e379
F ext/session/session3.test b8b9ff7efcb19234892c406dba8bd56792560efe
F ext/session/session_common.tcl fb91560b6dbd086010df8b3a137a452f1ac21a28 F ext/session/session_common.tcl fb91560b6dbd086010df8b3a137a452f1ac21a28
F ext/session/sessionfault.test d7e6154a30e85622d0733b1a1e3c63e9b8b7004b F ext/session/sessionfault.test d7e6154a30e85622d0733b1a1e3c63e9b8b7004b
F ext/session/sqlite3session.c 886827f10de75576baf9f9d860414fa155e1c8c1 F ext/session/sqlite3session.c cf91fe0efb0728c219c8bc2b2174a49758fbd3f8
F ext/session/sqlite3session.h 8d3e00c0a2e323e6f47b1204ec9ff714ca3bee4a F ext/session/sqlite3session.h 900d900bb6a827f84754fc252a05638e0f413a6e
F ext/session/test_session.c e0f500ec5e20478afc2c7998133e8acea7ec5104 F ext/session/test_session.c e0f500ec5e20478afc2c7998133e8acea7ec5104
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
@ -923,7 +924,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
P 9c3a6e479988e96086bef00c79dbce508a14da0d P 54298ee5ed183d1f1c49524f25e8ae1407f3d4b5
R f502a856cffb82507c875b00a2fb9d4d R 73cd0f1e5ce098769e9cee2c7d1f1ebc
U dan U dan
Z 3fdb3d84abab55a6e9fe1eeceb839a7c Z 8a40e5171d2567a1ae6ca1b697973f9a

View File

@ -1 +1 @@
54298ee5ed183d1f1c49524f25e8ae1407f3d4b5 506a0d7a710e1ff2f367821e73f5080fcf63fbc5