From ca62ad57f0b945ee0c9484bb8a8b533363e2901e Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 24 Mar 2011 16:04:54 +0000 Subject: [PATCH] Fix handling of schema mismatches in sqlite3session.c so that it matches the docs in sqlite3session.h. FossilOrigin-Name: 506a0d7a710e1ff2f367821e73f5080fcf63fbc5 --- ext/session/session3.test | 88 ++++++++++++++++++++++++++++++++++++ ext/session/sqlite3session.c | 58 ++++++++++++++++++------ ext/session/sqlite3session.h | 2 +- manifest | 15 +++--- manifest.uuid | 2 +- 5 files changed, 142 insertions(+), 23 deletions(-) create mode 100644 ext/session/session3.test diff --git a/ext/session/session3.test b/ext/session/session3.test new file mode 100644 index 0000000000..e07032fccd --- /dev/null +++ b/ext/session/session3.test @@ -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 + diff --git a/ext/session/sqlite3session.c b/ext/session/sqlite3session.c index 4ab8bb32ce..c64f567f90 100644 --- a/ext/session/sqlite3session.c +++ b/ext/session/sqlite3session.c @@ -544,7 +544,7 @@ static int sessionTableInfo( sqlite3 *db, /* Database connection */ const char *zDb, /* Name of attached database (e.g. "main") */ 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 ***pazCol, /* OUT: Array of column names for table */ u8 **pabPK /* OUT: Array of booleans - true for PK col */ @@ -577,9 +577,6 @@ static int sessionTableInfo( } rc = sqlite3_reset(pStmt); - if( nDbCol!=nCol ){ - rc = SQLITE_SCHEMA; - } if( rc==SQLITE_OK ){ nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1); pAlloc = sqlite3_malloc(nByte); @@ -589,9 +586,9 @@ static int sessionTableInfo( } if( rc==SQLITE_OK ){ azCol = (char **)pAlloc; - pAlloc = (u8 *)&azCol[nCol]; + pAlloc = (u8 *)&azCol[nDbCol]; abPK = (u8 *)pAlloc; - pAlloc = &abPK[nCol]; + pAlloc = &abPK[nDbCol]; if( pzTab ){ memcpy(pAlloc, zThis, nThis+1); *pzTab = (char *)pAlloc; @@ -619,9 +616,11 @@ static int sessionTableInfo( if( rc==SQLITE_OK ){ *pazCol = (const char **)azCol; *pabPK = abPK; + *pnCol = nDbCol; }else{ *pazCol = 0; *pabPK = 0; + *pnCol = 0; if( pzTab ) *pzTab = 0; sqlite3_free(azCol); } @@ -648,11 +647,13 @@ static int sessionTableInfo( static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){ if( pTab->nCol==0 ){ assert( pTab->azCol==0 || pTab->abPK==0 ); - pTab->nCol = sqlite3_preupdate_count(pSession->db); 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; } return pSession->rc; @@ -2402,6 +2403,7 @@ int sqlite3changeset_apply( ), void *pCtx /* First argument passed to xConflict */ ){ + int schemaMismatch = 0; sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */ int rc; /* Return code */ const char *zTab = 0; /* Name of current table */ @@ -2420,9 +2422,13 @@ int sqlite3changeset_apply( int bReplace = 0; int bRetry = 0; const char *zNew; + sqlite3changeset_op(pIter, &zNew, &nCol, &op, 0); if( zTab==0 || sqlite3_strnicmp(zNew, zTab, nTab+1) ){ + u8 *abPK; + + schemaMismatch = 0; sqlite3_free(sApply.azCol); sqlite3_finalize(sApply.pDelete); sqlite3_finalize(sApply.pUpdate); @@ -2430,23 +2436,47 @@ int sqlite3changeset_apply( sqlite3_finalize(sApply.pSelect); memset(&sApply, 0, sizeof(sApply)); sApply.db = db; - sApply.nCol = nCol; + sqlite3changeset_pk(pIter, &abPK, 0); 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 - || (rc = sessionSelectRow(db, zTab, &sApply)) + 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 = 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); if( rc==SQLITE_OK && bRetry ){ diff --git a/ext/session/sqlite3session.h b/ext/session/sqlite3session.h index b6ea72522d..5d19379ec8 100644 --- a/ext/session/sqlite3session.h +++ b/ext/session/sqlite3session.h @@ -48,7 +48,7 @@ typedef struct sqlite3_changeset_iter sqlite3_changeset_iter; ** ** 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 -** 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. */ int sqlite3session_create( diff --git a/manifest b/manifest index edccb68924..e59c27fb75 100644 --- a/manifest +++ b/manifest @@ -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. -D 2011-03-24T11:22:59 +C Fix\shandling\sof\sschema\smismatches\sin\ssqlite3session.c\sso\sthat\sit\smatches\sthe\sdocs\sin\ssqlite3session.h. +D 2011-03-24T16:04:55 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 27701a1653595a1f2187dc61c8117e00a6c1d50f F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -101,10 +101,11 @@ F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024 F ext/session/session1.test b2da15b9d727d7f4e5fe95599b32b92d93b5a970 F ext/session/session2.test 8da094318ac88953478c43d0bfb0aa723ee0e379 +F ext/session/session3.test b8b9ff7efcb19234892c406dba8bd56792560efe F ext/session/session_common.tcl fb91560b6dbd086010df8b3a137a452f1ac21a28 F ext/session/sessionfault.test d7e6154a30e85622d0733b1a1e3c63e9b8b7004b -F ext/session/sqlite3session.c 886827f10de75576baf9f9d860414fa155e1c8c1 -F ext/session/sqlite3session.h 8d3e00c0a2e323e6f47b1204ec9ff714ca3bee4a +F ext/session/sqlite3session.c cf91fe0efb0728c219c8bc2b2174a49758fbd3f8 +F ext/session/sqlite3session.h 900d900bb6a827f84754fc252a05638e0f413a6e F ext/session/test_session.c e0f500ec5e20478afc2c7998133e8acea7ec5104 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 @@ -923,7 +924,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P 9c3a6e479988e96086bef00c79dbce508a14da0d -R f502a856cffb82507c875b00a2fb9d4d +P 54298ee5ed183d1f1c49524f25e8ae1407f3d4b5 +R 73cd0f1e5ce098769e9cee2c7d1f1ebc U dan -Z 3fdb3d84abab55a6e9fe1eeceb839a7c +Z 8a40e5171d2567a1ae6ca1b697973f9a diff --git a/manifest.uuid b/manifest.uuid index 12934424cd..e8475cfcd0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -54298ee5ed183d1f1c49524f25e8ae1407f3d4b5 \ No newline at end of file +506a0d7a710e1ff2f367821e73f5080fcf63fbc5 \ No newline at end of file