From 80fe2d931071b6378969e26fedcc92bf0ff19ef3 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 18 Apr 2011 07:36:27 +0000 Subject: [PATCH] Further coverage tests for the session module. FossilOrigin-Name: 69a01c708bf044eacf21a8951fe9e7d9fb4332c5 --- ext/session/session2.test | 17 ++ ext/session/sessionfault.test | 51 ++++- ext/session/sqlite3session.c | 337 ++++++++++++++++++---------------- manifest | 16 +- manifest.uuid | 2 +- 5 files changed, 254 insertions(+), 169 deletions(-) diff --git a/ext/session/session2.test b/ext/session/session2.test index 5c7ea47d5f..04be5f0917 100644 --- a/ext/session/session2.test +++ b/ext/session/session2.test @@ -390,6 +390,23 @@ do_iterator_test 6.1.7 * { {UPDATE t2 0 X. {i 4 t y} {{} {} t new}} } +do_iterator_test 6.1.8 * { + CREATE TABLE t3(a, b PRIMARY KEY); + CREATE TABLE t4(a, b PRIMARY KEY); + CREATE TRIGGER t4t AFTER UPDATE ON t4 BEGIN + UPDATE t3 SET a = new.a WHERE b = new.b; + END; + + SELECT indirect(1); + INSERT INTO t3 VALUES('one', 1); + INSERT INTO t4 VALUES('one', 1); + SELECT indirect(0); + UPDATE t4 SET a = 'two' WHERE b = 1; +} { + {INSERT t4 0 .X {} {t two i 1}} + {INSERT t3 1 .X {} {t two i 1}} +} + sqlite3session S db main do_execsql_test 6.2.1 { SELECT indirect(0); diff --git a/ext/session/sessionfault.test b/ext/session/sessionfault.test index b9800e1f46..1444a46b36 100644 --- a/ext/session/sessionfault.test +++ b/ext/session/sessionfault.test @@ -20,6 +20,8 @@ source $testdir/tester.tcl set testprefix sessionfault +if 1 { + forcedelete test.db2 sqlite3 db2 test.db2 do_common_sql { @@ -362,7 +364,7 @@ do_faultsim_test 8.2 -faults oom* -body { } faultsim_delete_and_reopen -do_test 9.prep { +do_test 9.1.prep { execsql { PRAGMA encoding = 'utf16'; CREATE TABLE t1(a PRIMARY KEY, b); @@ -370,7 +372,8 @@ do_test 9.prep { } {} faultsim_save_and_close -do_faultsim_test 9 -faults oom-transient -prep { +set answers [list {0 {}} {1 SQLITE_NOMEM} {1 {callback requested query abort}}] +do_faultsim_test 9.1 -faults oom-transient -prep { catch { unset ::c } faultsim_restore_and_reopen sqlite3session S db main @@ -383,9 +386,48 @@ do_faultsim_test 9 -faults oom-transient -prep { set {} {} } -test { S delete - faultsim_test_result {0 {}} {1 SQLITE_NOMEM} {1 {callback requested query abort}} + eval faultsim_test_result $::answers if {[info exists ::c]} { - set expected "{INSERT t1 0 X. {} {t abcdefghijklmnopqrstuv t ABCDEFGHIJKLMNOPQRSTUV}}" + set expected [normalize_list { + {INSERT t1 0 X. {} {t abcdefghijklmnopqrstuv t ABCDEFGHIJKLMNOPQRSTUV}} + }] + if { [changeset_to_list $::c] != $expected } { + error "changeset mismatch" + } + } +} + +} + +faultsim_delete_and_reopen +do_test 9.2.prep { + execsql { + PRAGMA encoding = 'utf16'; + CREATE TABLE t1(a PRIMARY KEY, b); + INSERT INTO t1 VALUES('abcdefghij', 'ABCDEFGHIJKLMNOPQRSTUV'); + } +} {} +faultsim_save_and_close + +set answers [list {0 {}} {1 SQLITE_NOMEM} {1 {callback requested query abort}}] +do_faultsim_test 9.2 -faults oom-transient -prep { + catch { unset ::c } + faultsim_restore_and_reopen + sqlite3session S db main + S attach * +} -body { + execsql { + UPDATE t1 SET b = 'xyz'; + } + set ::c [S changeset] + set {} {} +} -test { + S delete + eval faultsim_test_result $::answers + if {[info exists ::c]} { + set expected [normalize_list { + {UPDATE t1 0 X. {t abcdefghij t ABCDEFGHIJKLMNOPQRSTUV} {{} {} t xyz}} + }] if { [changeset_to_list $::c] != $expected } { error "changeset mismatch" } @@ -393,4 +435,5 @@ do_faultsim_test 9 -faults oom-transient -prep { } + finish_test diff --git a/ext/session/sqlite3session.c b/ext/session/sqlite3session.c index 4f81170621..2232d21938 100644 --- a/ext/session/sqlite3session.c +++ b/ext/session/sqlite3session.c @@ -222,51 +222,68 @@ static int sessionSerializeValue( sqlite3_value *pValue, /* Value to serialize */ int *pnWrite /* IN/OUT: Increment by bytes written */ ){ - int eType; /* Value type (SQLITE_NULL, TEXT etc.) */ int nByte; /* Size of serialized value in bytes */ - eType = sqlite3_value_type(pValue); - if( aBuf ) aBuf[0] = eType; - - switch( eType ){ - case SQLITE_NULL: - nByte = 1; - break; - - case SQLITE_INTEGER: - case SQLITE_FLOAT: - if( aBuf ){ - /* TODO: SQLite does something special to deal with mixed-endian - ** floating point values (e.g. ARM7). This code probably should - ** too. */ - u64 i; - if( eType==SQLITE_INTEGER ){ - i = (u64)sqlite3_value_int64(pValue); - }else{ - double r; - assert( sizeof(double)==8 && sizeof(u64)==8 ); - r = sqlite3_value_double(pValue); - memcpy(&i, &r, 8); + if( pValue ){ + int eType; /* Value type (SQLITE_NULL, TEXT etc.) */ + + eType = sqlite3_value_type(pValue); + if( aBuf ) aBuf[0] = eType; + + switch( eType ){ + case SQLITE_NULL: + nByte = 1; + break; + + case SQLITE_INTEGER: + case SQLITE_FLOAT: + if( aBuf ){ + /* TODO: SQLite does something special to deal with mixed-endian + ** floating point values (e.g. ARM7). This code probably should + ** too. */ + u64 i; + if( eType==SQLITE_INTEGER ){ + i = (u64)sqlite3_value_int64(pValue); + }else{ + double r; + assert( sizeof(double)==8 && sizeof(u64)==8 ); + r = sqlite3_value_double(pValue); + memcpy(&i, &r, 8); + } + sessionPutI64(&aBuf[1], i); } - sessionPutI64(&aBuf[1], i); + nByte = 9; + break; + + default: { + u8 *z; + int n; + int nVarint; + + assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB ); + if( eType==SQLITE_TEXT ){ + z = (u8 *)sqlite3_value_text(pValue); + }else{ + z = (u8 *)sqlite3_value_blob(pValue); + } + if( z==0 ) return SQLITE_NOMEM; + n = sqlite3_value_bytes(pValue); + nVarint = sessionVarintLen(n); + + if( aBuf ){ + sessionVarintPut(&aBuf[1], n); + memcpy(&aBuf[nVarint + 1], eType==SQLITE_TEXT ? + sqlite3_value_text(pValue) : sqlite3_value_blob(pValue), n + ); + } + + nByte = 1 + nVarint + n; + break; } - nByte = 9; - break; - - default: { - int n = sqlite3_value_bytes(pValue); - int nVarint = sessionVarintLen(n); - assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB ); - if( aBuf ){ - sessionVarintPut(&aBuf[1], n); - memcpy(&aBuf[nVarint + 1], eType==SQLITE_TEXT ? - sqlite3_value_text(pValue) : sqlite3_value_blob(pValue), n - ); - } - - nByte = 1 + nVarint + n; - break; } + }else{ + nByte = 1; + if( aBuf ) aBuf[0] = '\0'; } *pnWrite += nByte; @@ -851,18 +868,21 @@ static void sessionPreupdateOneChange( return; } - /* Search the hash table for an existing entry for rowid=iKey2. If - ** one is found, store a pointer to it in pChange and unlink it from - ** the hash table. Otherwise, set pChange to NULL. - */ + /* Calculate the hash-key for this change. If the primary key of the row + ** includes a NULL value, exit early. Such changes are ignored by the + ** session module. */ rc = sessionPreupdateHash(db, pTab, op==SQLITE_INSERT, &iHash, &bNullPk); - if( rc==SQLITE_OK && bNullPk==0 ){ + if( rc!=SQLITE_OK ) goto error_out; + + if( bNullPk==0 ){ + /* Search the hash table for an existing record for this row. */ SessionChange *pC; for(pC=pTab->apChange[iHash]; pC; pC=pC->pNext){ int bEqual; sessionPreupdateEqual(db, pTab, pC, op==SQLITE_INSERT, &bEqual); if( bEqual ) break; } + if( pC==0 ){ /* Create a new change object containing all the old values (if ** this is an SQLITE_UPDATE or SQLITE_DELETE), or just the PK @@ -876,53 +896,57 @@ static void sessionPreupdateOneChange( /* Figure out how large an allocation is required */ nByte = sizeof(SessionChange); - for(i=0; inCol && rc==SQLITE_OK; i++){ + for(i=0; inCol; i++){ sqlite3_value *p = 0; if( op!=SQLITE_INSERT ){ - rc = sqlite3_preupdate_old(pSession->db, i, &p); - }else if( 1 || pTab->abPK[i] ){ - rc = sqlite3_preupdate_new(pSession->db, i, &p); - } - if( p && rc==SQLITE_OK ){ - rc = sessionSerializeValue(0, p, &nByte); + TESTONLY(int trc = ) sqlite3_preupdate_old(pSession->db, i, &p); + assert( trc==SQLITE_OK ); + }else if( pTab->abPK[i] ){ + TESTONLY(int trc = ) sqlite3_preupdate_new(pSession->db, i, &p); + assert( trc==SQLITE_OK ); } + + /* This may fail if SQLite value p contains a utf-16 string that must + ** be converted to utf-8 and an OOM error occurs while doing so. */ + rc = sessionSerializeValue(0, p, &nByte); + if( rc!=SQLITE_OK ) goto error_out; } /* Allocate the change object */ pChange = (SessionChange *)sqlite3_malloc(nByte); if( !pChange ){ rc = SQLITE_NOMEM; + goto error_out; }else{ memset(pChange, 0, sizeof(SessionChange)); pChange->aRecord = (u8 *)&pChange[1]; } - /* Populate the change object */ + /* Populate the change object. None of the preupdate_old(), + ** preupdate_new() or SerializeValue() calls below may fail as all + ** required values and encodings have already been cached in memory. + ** It is not possible for an OOM to occur in this block. */ nByte = 0; - for(i=0; inCol && rc==SQLITE_OK; i++){ + for(i=0; inCol; i++){ sqlite3_value *p = 0; if( op!=SQLITE_INSERT ){ - rc = sqlite3_preupdate_old(pSession->db, i, &p); - }else if( 1 || pTab->abPK[i] ){ - rc = sqlite3_preupdate_new(pSession->db, i, &p); - } - if( p && rc==SQLITE_OK ){ - rc = sessionSerializeValue(&pChange->aRecord[nByte], p, &nByte); + sqlite3_preupdate_old(pSession->db, i, &p); + }else if( pTab->abPK[i] ){ + sqlite3_preupdate_new(pSession->db, i, &p); } + sessionSerializeValue(&pChange->aRecord[nByte], p, &nByte); } - if( rc==SQLITE_OK ){ - /* Add the change back to the hash-table */ - if( pSession->bIndirect || sqlite3_preupdate_depth(pSession->db) ){ - pChange->bIndirect = 1; - } - pChange->nRecord = nByte; - pChange->bInsert = (op==SQLITE_INSERT); - pChange->pNext = pTab->apChange[iHash]; - pTab->apChange[iHash] = pChange; - }else{ - sqlite3_free(pChange); + + /* Add the change to the hash-table */ + if( pSession->bIndirect || sqlite3_preupdate_depth(pSession->db) ){ + pChange->bIndirect = 1; } - }else if( rc==SQLITE_OK && pC->bIndirect ){ + pChange->nRecord = nByte; + pChange->bInsert = (op==SQLITE_INSERT); + pChange->pNext = pTab->apChange[iHash]; + pTab->apChange[iHash] = pChange; + + }else if( pC->bIndirect ){ /* If the existing change is considered "indirect", but this current ** change is "direct", mark the change object as direct. */ if( sqlite3_preupdate_depth(pSession->db)==0 && pSession->bIndirect==0 ){ @@ -932,6 +956,7 @@ static void sessionPreupdateOneChange( } /* If an error has occurred, mark the session object as failed. */ + error_out: if( rc!=SQLITE_OK ){ pSession->rc = rc; } @@ -1125,7 +1150,7 @@ int sqlite3session_attach( ** set *pRc to SQLITE_NOMEM and return non-zero. */ static int sessionBufferGrow(SessionBuffer *p, int nByte, int *pRc){ - if( p->nAlloc-p->nBufnAlloc-p->nBufnAlloc ? p->nAlloc : 128; do { @@ -1135,12 +1160,12 @@ static int sessionBufferGrow(SessionBuffer *p, int nByte, int *pRc){ aNew = (u8 *)sqlite3_realloc(p->aBuf, nNew); if( 0==aNew ){ *pRc = SQLITE_NOMEM; - return 1; + }else{ + p->aBuf = aNew; + p->nAlloc = nNew; } - p->aBuf = aNew; - p->nAlloc = nNew; } - return 0; + return (*pRc!=SQLITE_OK); } /* @@ -1151,7 +1176,7 @@ static int sessionBufferGrow(SessionBuffer *p, int nByte, int *pRc){ ** returning. */ static void sessionAppendByte(SessionBuffer *p, u8 v, int *pRc){ - if( *pRc==SQLITE_OK && 0==sessionBufferGrow(p, 1, pRc) ){ + if( 0==sessionBufferGrow(p, 1, pRc) ){ p->aBuf[p->nBuf++] = v; } } @@ -1164,7 +1189,7 @@ static void sessionAppendByte(SessionBuffer *p, u8 v, int *pRc){ ** returning. */ static void sessionAppendVarint(SessionBuffer *p, int v, int *pRc){ - if( *pRc==SQLITE_OK && 0==sessionBufferGrow(p, 9, pRc) ){ + if( 0==sessionBufferGrow(p, 9, pRc) ){ p->nBuf += sessionVarintPut(&p->aBuf[p->nBuf], v); } } @@ -1182,7 +1207,7 @@ static void sessionAppendBlob( int nBlob, int *pRc ){ - if( *pRc==SQLITE_OK && 0==sessionBufferGrow(p, nBlob, pRc) ){ + if( 0==sessionBufferGrow(p, nBlob, pRc) ){ memcpy(&p->aBuf[p->nBuf], aBlob, nBlob); p->nBuf += nBlob; } @@ -1202,7 +1227,7 @@ static void sessionAppendStr( int *pRc ){ int nStr = sqlite3Strlen30(zStr); - if( *pRc==SQLITE_OK && 0==sessionBufferGrow(p, nStr, pRc) ){ + if( 0==sessionBufferGrow(p, nStr, pRc) ){ memcpy(&p->aBuf[p->nBuf], zStr, nStr); p->nBuf += nStr; } @@ -1241,7 +1266,7 @@ static void sessionAppendIdent( int *pRc /* IN/OUT: Error code */ ){ int nStr = sqlite3Strlen30(zStr)*2 + 2 + 1; - if( *pRc==SQLITE_OK && 0==sessionBufferGrow(p, nStr, pRc) ){ + if( 0==sessionBufferGrow(p, nStr, pRc) ){ char *zOut = (char *)&p->aBuf[p->nBuf]; const char *zIn = zStr; *zOut++ = '"'; @@ -1300,12 +1325,10 @@ static void sessionAppendCol( } /* -** This function is a no-op if *pRc is other than SQLITE_OK when it is -** called. ** -** Otherwse, if *pRc is SQLITE_OK, then it appends an update change to -** the buffer (see the comments under "CHANGESET FORMAT" at the top of the -** file). An update change consists of: +** This function appends an update change to the buffer (see the comments +** under "CHANGESET FORMAT" at the top of the file). An update change +** consists of: ** ** 1 byte: SQLITE_UPDATE (0x17) ** n bytes: old.* record (see RECORD FORMAT) @@ -1323,89 +1346,89 @@ static void sessionAppendCol( ** original values of any fields that have been modified. The new.* record ** contains the new values of only those fields that have been modified. */ -static void sessionAppendUpdate( +static int sessionAppendUpdate( SessionBuffer *pBuf, /* Buffer to append to */ sqlite3_stmt *pStmt, /* Statement handle pointing at new row */ SessionChange *p, /* Object containing old values */ - u8 *abPK, /* Boolean array - true for PK columns */ - int *pRc /* IN/OUT: Error code */ + u8 *abPK /* Boolean array - true for PK columns */ ){ - if( *pRc==SQLITE_OK ){ - SessionBuffer buf2 = {0,0,0}; /* Buffer to accumulate new.* record in */ - int bNoop = 1; /* Set to zero if any values are modified */ - int nRewind = pBuf->nBuf; /* Set to zero if any values are modified */ - int i; /* Used to iterate through columns */ - u8 *pCsr = p->aRecord; /* Used to iterate through old.* values */ + int rc = SQLITE_OK; + SessionBuffer buf2 = {0,0,0}; /* Buffer to accumulate new.* record in */ + int bNoop = 1; /* Set to zero if any values are modified */ + int nRewind = pBuf->nBuf; /* Set to zero if any values are modified */ + int i; /* Used to iterate through columns */ + u8 *pCsr = p->aRecord; /* Used to iterate through old.* values */ - sessionAppendByte(pBuf, SQLITE_UPDATE, pRc); - sessionAppendByte(pBuf, p->bIndirect, pRc); - for(i=0; ibIndirect, &rc); + for(i=0; inBuf = nRewind; + if( bChanged || abPK[i] ){ + sessionAppendBlob(pBuf, pCsr, nAdvance, &rc); }else{ - sessionAppendBlob(pBuf, buf2.aBuf, buf2.nBuf, pRc); + sessionAppendByte(pBuf, 0, &rc); } - sqlite3_free(buf2.aBuf); + + if( bChanged ){ + sessionAppendCol(&buf2, pStmt, i, &rc); + bNoop = 0; + }else{ + sessionAppendByte(&buf2, 0, &rc); + } + + pCsr += nAdvance; } + + if( bNoop ){ + pBuf->nBuf = nRewind; + }else{ + sessionAppendBlob(pBuf, buf2.aBuf, buf2.nBuf, &rc); + } + sqlite3_free(buf2.aBuf); + + return rc; } static int sessionSelectStmt( @@ -1457,6 +1480,7 @@ static int sessionSelectBind( int eType = *a++; switch( eType ){ + case 0: case SQLITE_NULL: assert( abPK[i]==0 ); break; @@ -1580,6 +1604,7 @@ int sqlite3session_changeset( for(p=pTab->apChange[i]; rc==SQLITE_OK && p; p=p->pNext){ rc = sessionSelectBind(pSel, nCol, abPK, p); + if( rc!=SQLITE_OK ) continue; if( sqlite3_step(pSel)==SQLITE_ROW ){ int iCol; if( p->bInsert ){ @@ -1589,7 +1614,7 @@ int sqlite3session_changeset( sessionAppendCol(&buf, pSel, iCol, &rc); } }else{ - sessionAppendUpdate(&buf, pSel, p, abPK, &rc); + rc = sessionAppendUpdate(&buf, pSel, p, abPK); } }else if( !p->bInsert ){ /* A DELETE change */ diff --git a/manifest b/manifest index 1938e24d5e..c0ef6b6a13 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improve\stest\scoverage\sof\ssession\smodule. -D 2011-04-16T19:23:10.456 +C Further\scoverage\stests\sfor\sthe\ssession\smodule. +D 2011-04-18T07:36:27.686 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 7a4d9524721d40ef9ee26f93f9bd6a51dba106f2 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -100,13 +100,13 @@ F ext/rtree/sqlite3rtree.h 1af0899c63a688e272d69d8e746f24e76f10a3f0 F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024 F ext/session/session1.test f5d9f2e362abe2563181389509822bda956516ee -F ext/session/session2.test c3e5f78d5eb988e35cc2ba9ce3678f706283cfdb +F ext/session/session2.test 99ca0da7ddb617d42bafd83adccf99f18ae0384b F ext/session/session3.test a7a9ce59b8d1e49e2cc23d81421ac485be0eea01 F ext/session/session4.test a6ed685da7a5293c5d6f99855bcf41dbc352ca84 F ext/session/session5.test 8fdfaf9dba28a2f1c6b89b06168bdab1fef2d478 F ext/session/session_common.tcl 1539d8973b2aea0025c133eb0cc4c89fcef541a5 -F ext/session/sessionfault.test 759060c36ef424919e3900b885b22aa15caf7f39 -F ext/session/sqlite3session.c 3475df4021c5260f795cf66f67233449795c06ea +F ext/session/sessionfault.test 401045278298a242cbc2e4bc986c102f01ff2180 +F ext/session/sqlite3session.c fd43b8cb35d8112fa77a9e9101f9efe7005e5e23 F ext/session/sqlite3session.h 665f5591562e3c71eb3d0da26f1a1efae26f7bcf F ext/session/test_session.c 311e5b9228374d0b5780448f289847ff1cf7d388 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x @@ -938,7 +938,7 @@ F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P 3dfd1d63bddfa9bd9018eb00bee1d496379630b5 -R 57f77b8ed5537ccfd8e7f64bd8d648b6 +P f46d4b641d613c39a80b12106e6a6ac0efc8be83 +R bc07f4a1348864ef3dbdf87527d99ed2 U dan -Z 35862238c72a43b8ebb72adec800f600 +Z fcc600b67a0d6c899a275e81c6cbea0f diff --git a/manifest.uuid b/manifest.uuid index 387cde32ce..4c438b56bb 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f46d4b641d613c39a80b12106e6a6ac0efc8be83 \ No newline at end of file +69a01c708bf044eacf21a8951fe9e7d9fb4332c5 \ No newline at end of file