--- /dev/null
+# 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
+
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 */
}
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);
}
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;
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);
}
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;
),
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 */
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);
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 ){
-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
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
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