Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix a problem causing sqlite3changeset_invert() to effectively drop UPDATE changes. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | sessions |
Files: | files | file ages | folders |
SHA1: |
bb3e65d9724dcecdc54b4c9fb0448f95 |
User & Date: | dan 2011-07-26 15:50:36.271 |
Context
2011-07-26
| ||
15:57 | Fix a test case in sessionfault.test. No changes to code. (check-in: dc6ecacd77 user: dan tags: sessions) | |
15:50 | Fix a problem causing sqlite3changeset_invert() to effectively drop UPDATE changes. (check-in: bb3e65d972 user: dan tags: sessions) | |
2011-07-22
| ||
12:49 | Merge the latest trunk changes into the sessions branch. (check-in: 110cfd6920 user: drh tags: sessions) | |
Changes
Changes to ext/session/session1.test.
︙ | ︙ | |||
138 139 140 141 142 143 144 | {DELETE t1 0 X. {i 3 t Thonburi} {}} {INSERT t1 0 X. {} {i 20 t Thapae}} } do_changeset_invert_test 2.3.3 S { {DELETE t1 0 X. {i 10 t Sukhothai} {}} {INSERT t1 0 X. {} {i 1 t Sukhothai}} | | | 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | {DELETE t1 0 X. {i 3 t Thonburi} {}} {INSERT t1 0 X. {} {i 20 t Thapae}} } do_changeset_invert_test 2.3.3 S { {DELETE t1 0 X. {i 10 t Sukhothai} {}} {INSERT t1 0 X. {} {i 1 t Sukhothai}} {UPDATE t1 0 X. {i 2 t Surin} {{} {} t Ayutthaya}} {INSERT t1 0 X. {} {i 3 t Thonburi}} {DELETE t1 0 X. {i 20 t Thapae} {}} } do_test 2.3.4 { S delete } {} do_test 2.4.1 { sqlite3session S db main |
︙ | ︙ | |||
488 489 490 491 492 493 494 495 496 | sqlite3session S db main S attach t5 execsql { DELETE FROM t5 } S isempty } {1} do_test 8.4 { S delete } {} catch { db2 close } finish_test | > > > > > > > > > > > > > | 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 | sqlite3session S db main S attach t5 execsql { DELETE FROM t5 } S isempty } {1} do_test 8.4 { S delete } {} #------------------------------------------------------------------------- # do_execsql_test 9.1 { CREATE TABLE t7(a, b, c, d, e PRIMARY KEY, f, g); INSERT INTO t7 VALUES(1, 1, 1, 1, 1, 1, 1); } do_test 9.2 { sqlite3session S db main S attach * execsql { UPDATE t7 SET b=2, d=2 } } {} do_changeset_test 9.2 S {{UPDATE t7 0 ....X.. {{} {} i 1 {} {} i 1 i 1 {} {} {} {}} {{} {} i 2 {} {} i 2 {} {} {} {} {} {}}}} S delete catch { db2 close } finish_test |
Added ext/session/session8.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | # 2011 July 13 # # 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 SQLite library. # 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 ifcapable !session {finish_test; return} set testprefix session8 proc noop {args} {} # Like [dbcksum] in tester.tcl. Except this version is not sensitive # to changes in the value of implicit IPK columns. # proc udbcksum {db dbname} { if {$dbname=="temp"} { set master sqlite_temp_master } else { set master $dbname.sqlite_master } set alltab [$db eval "SELECT name FROM $master WHERE type='table'"] set txt [$db eval "SELECT * FROM $master"]\n foreach tab $alltab { append txt [lsort [$db eval "SELECT * FROM $dbname.$tab"]]\n } return [md5 $txt] } proc do_then_undo {tn sql} { set ck1 [udbcksum db main] sqlite3session S db main S attach * db eval $sql set ck2 [udbcksum db main] set invert [sqlite3changeset_invert [S changeset]] S delete sqlite3changeset_apply db $invert noop set ck3 [udbcksum db main] set a [expr {$ck1==$ck2}] set b [expr {$ck1==$ck3}] uplevel [list do_test $tn.1 "set {} $a" 0] uplevel [list do_test $tn.2 "set {} $b" 1] } do_execsql_test 1.1 { CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES(1, 2); INSERT INTO t1 VALUES("abc", "xyz"); } do_then_undo 1.2 { INSERT INTO t1 VALUES(3, 4); } do_then_undo 1.3 { DELETE FROM t1 WHERE b=2; } do_then_undo 1.4 { UPDATE t1 SET b = 3 WHERE a = 1; } do_execsql_test 2.1 { CREATE TABLE t2(a, b PRIMARY KEY); INSERT INTO t2 VALUES(1, 2); INSERT INTO t2 VALUES('abc', 'xyz'); } do_then_undo 1.2 { INSERT INTO t2 VALUES(3, 4); } do_then_undo 1.3 { DELETE FROM t2 WHERE b=2; } do_then_undo 1.4 { UPDATE t1 SET a = '123' WHERE b = 'xyz'; } do_execsql_test 3.1 { CREATE TABLE t3(a, b, c, d, e, PRIMARY KEY(c, e)); INSERT INTO t3 VALUES('x', 45, 0.0, 'abcdef', 12); INSERT INTO t3 VALUES(45, 0.0, 'abcdef', 12, 'x'); INSERT INTO t3 VALUES(0.0, 'abcdef', 12, 'x', 45); } do_then_undo 3.2 { UPDATE t3 SET b=b||b WHERE e!='x' } do_then_undo 3.3 { UPDATE t3 SET a = 46 } finish_test |
Changes to ext/session/sqlite3session.c.
︙ | ︙ | |||
2128 2129 2130 2131 2132 2133 2134 | } /* ** Invert a changeset object. */ int sqlite3changeset_invert( int nChangeset, /* Number of bytes in input */ | | > | > > | 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 | } /* ** Invert a changeset object. */ int sqlite3changeset_invert( int nChangeset, /* Number of bytes in input */ const void *pChangeset, /* Input changeset */ int *pnInverted, /* OUT: Number of bytes in output changeset */ void **ppInverted /* OUT: Inverse of pChangeset */ ){ int rc = SQLITE_OK; /* Return value */ u8 *aOut; u8 *aIn; int i; int nCol = 0; /* Number of cols in current table */ u8 *abPK = 0; /* PK array for current table */ sqlite3_value **apVal = 0; /* Space for values for UPDATE inversion */ /* Zero the output variables in case an error occurs. */ *ppInverted = 0; *pnInverted = 0; if( nChangeset==0 ) return SQLITE_OK; aOut = (u8 *)sqlite3_malloc(nChangeset); |
︙ | ︙ | |||
2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 | ** ** * A constant 'T' character, ** * Number of columns in said table (a varint), ** * An array of nCol bytes (abPK), ** * A nul-terminated table name. */ int nByte = 1 + sessionVarintGet(&aIn[i+1], &nCol); nByte += nCol; nByte += 1 + sqlite3Strlen30((char *)&aIn[i+nByte]); memcpy(&aOut[i], &aIn[i], nByte); i += nByte; break; } case SQLITE_INSERT: case SQLITE_DELETE: { int nByte; u8 *aEnd = &aIn[i+2]; sessionReadRecord(&aEnd, nCol, 0); aOut[i] = (eType==SQLITE_DELETE ? SQLITE_INSERT : SQLITE_DELETE); aOut[i+1] = aIn[i+1]; nByte = (int)(aEnd - &aIn[i+2]); memcpy(&aOut[i+2], &aIn[i+2], nByte); i += 2 + nByte; break; } case SQLITE_UPDATE: { | > > > | | | > > > > > > > > > > | < > | < | > > > | > > > > > > > | > > > > > > | | > > > > > > > > > > < | > > > > > > > > | | | 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 | ** ** * A constant 'T' character, ** * Number of columns in said table (a varint), ** * An array of nCol bytes (abPK), ** * A nul-terminated table name. */ int nByte = 1 + sessionVarintGet(&aIn[i+1], &nCol); abPK = &aIn[i+nByte]; nByte += nCol; nByte += 1 + sqlite3Strlen30((char *)&aIn[i+nByte]); memcpy(&aOut[i], &aIn[i], nByte); i += nByte; sqlite3_free(apVal); apVal = 0; break; } case SQLITE_INSERT: case SQLITE_DELETE: { int nByte; u8 *aEnd = &aIn[i+2]; sessionReadRecord(&aEnd, nCol, 0); aOut[i] = (eType==SQLITE_DELETE ? SQLITE_INSERT : SQLITE_DELETE); aOut[i+1] = aIn[i+1]; nByte = (int)(aEnd - &aIn[i+2]); memcpy(&aOut[i+2], &aIn[i+2], nByte); i += 2 + nByte; break; } case SQLITE_UPDATE: { int iCol; int nWrite = 0; u8 *aEnd = &aIn[i+2]; if( 0==apVal ){ apVal = (sqlite3_value **)sqlite3_malloc(sizeof(apVal[0])*nCol*2); if( 0==apVal ){ rc = SQLITE_NOMEM; goto finished_invert; } memset(apVal, 0, sizeof(apVal[0])*nCol*2); } /* Read the old.* and new.* records for the update change. */ rc = sessionReadRecord(&aEnd, nCol, &apVal[0]); if( rc==SQLITE_OK ){ rc = sessionReadRecord(&aEnd, nCol, &apVal[nCol]); } /* Write the header for the new UPDATE change. Same as the original. */ aOut[i] = SQLITE_UPDATE; aOut[i+1] = aIn[i+1]; nWrite = 2; /* Write the new old.* record. Consists of the PK columns from the ** original old.* record, and the other values from the original ** new.* record. */ for(iCol=0; rc==SQLITE_OK && iCol<nCol; iCol++){ sqlite3_value *pVal = apVal[iCol + (abPK[iCol] ? 0 : nCol)]; rc = sessionSerializeValue(&aOut[i+nWrite], pVal, &nWrite); } /* Write the new new.* record. Consists of a copy of all values ** from the original old.* record, except for the PK columns, which ** are set to "undefined". */ for(iCol=0; rc==SQLITE_OK && iCol<nCol; iCol++){ sqlite3_value *pVal = (abPK[iCol] ? 0 : apVal[iCol]); rc = sessionSerializeValue(&aOut[i+nWrite], pVal, &nWrite); } for(iCol=0; iCol<nCol*2; iCol++){ sqlite3ValueFree(apVal[iCol]); } memset(apVal, 0, sizeof(apVal[0])*nCol*2); if( rc!=SQLITE_OK ){ goto finished_invert; } i += nWrite; assert( &aIn[i]==aEnd ); break; } default: rc = SQLITE_CORRUPT; goto finished_invert; } } assert( rc==SQLITE_OK ); *pnInverted = nChangeset; *ppInverted = (void *)aOut; finished_invert: if( rc!=SQLITE_OK ){ sqlite3_free(aOut); } sqlite3_free(apVal); return rc; } typedef struct SessionApplyCtx SessionApplyCtx; struct SessionApplyCtx { sqlite3 *db; sqlite3_stmt *pDelete; /* DELETE statement */ sqlite3_stmt *pUpdate; /* UPDATE statement */ sqlite3_stmt *pInsert; /* INSERT statement */ sqlite3_stmt *pSelect; /* SELECT statement */ int nCol; /* Size of azCol[] and abPK[] arrays */ const char **azCol; /* Array of column names */ u8 *abPK; /* Boolean array - true if column is in PK */ }; |
︙ | ︙ | |||
2991 2992 2993 2994 2995 2996 2997 | assert( op2==SQLITE_INSERT ); pNew->op = SQLITE_UPDATE; if( 0==sessionMergeUpdate(&aCsr, pTab, pExist->aRecord, 0, aRec, 0) ){ sqlite3_free(pNew); pNew = 0; } }else if( op2==SQLITE_UPDATE ){ /* UPDATE + UPDATE */ | < > | 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 | assert( op2==SQLITE_INSERT ); pNew->op = SQLITE_UPDATE; if( 0==sessionMergeUpdate(&aCsr, pTab, pExist->aRecord, 0, aRec, 0) ){ sqlite3_free(pNew); pNew = 0; } }else if( op2==SQLITE_UPDATE ){ /* UPDATE + UPDATE */ u8 *a1 = pExist->aRecord; u8 *a2 = aRec; assert( op1==SQLITE_UPDATE ); sessionReadRecord(&a1, pTab->nCol, 0); sessionReadRecord(&a2, pTab->nCol, 0); pNew->op = SQLITE_UPDATE; if( 0==sessionMergeUpdate(&aCsr, pTab, aRec, pExist->aRecord, a1, a2) ){ sqlite3_free(pNew); pNew = 0; } |
︙ | ︙ |
Changes to ext/session/sqlite3session.h.
︙ | ︙ | |||
541 542 543 544 545 546 547 | ** on the *ppOut pointer to free the buffer allocation following a successful ** call to this function. ** ** WARNING/TODO: This function currently assumes that the input is a valid ** changeset. If it is not, the results are undefined. */ int sqlite3changeset_invert( | | | 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 | ** on the *ppOut pointer to free the buffer allocation following a successful ** call to this function. ** ** WARNING/TODO: This function currently assumes that the input is a valid ** changeset. If it is not, the results are undefined. */ int sqlite3changeset_invert( int nIn, const void *pIn, /* Input changeset */ int *pnOut, void **ppOut /* OUT: Inverse of input */ ); /* ** CAPI3REF: Concatenate Two Changeset Objects ** ** This function is used to concatenate two changesets, A and B, into a |
︙ | ︙ |