Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Add ota tests to increase code coverage. Fix some minor issues in error handling within the ota code. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | ota-update |
Files: | files | file ages | folders |
SHA1: |
2b10c5d2b8b8b535d3dec0c68a777db1 |
User & Date: | dan 2015-02-18 20:16:15.838 |
Context
2015-02-18
| ||
20:17 | Add new file ota12.test, containing tests for applying ota updates to live databases with other active reader/writer clients. (check-in: 0864d127fe user: dan tags: ota-update) | |
20:16 | Add ota tests to increase code coverage. Fix some minor issues in error handling within the ota code. (check-in: 2b10c5d2b8 user: dan tags: ota-update) | |
17:40 | Fix a problem with OTA updates in the presence of database readers. (check-in: 144bb29ffc user: dan tags: ota-update) | |
Changes
Changes to ext/ota/ota1.test.
︙ | ︙ | |||
523 524 525 526 527 528 529 530 531 532 533 534 535 536 | } {SQLITE_ERROR - table data_t1 may not have ota_rowid column} 6 { CREATE TABLE t1(a, b PRIMARY KEY) WITHOUT ROWID; CREATE TABLE ota.data_t1(a, b, ota_control); INSERT INTO ota.data_t1 VALUES(1, 2, 'x.x'); } {SQLITE_ERROR - invalid ota_control value} } { reset_db forcedelete ota.db execsql { ATTACH 'ota.db' AS ota } execsql $schema | > > > > > > > > > > > > > > > > > > | 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 | } {SQLITE_ERROR - table data_t1 may not have ota_rowid column} 6 { CREATE TABLE t1(a, b PRIMARY KEY) WITHOUT ROWID; CREATE TABLE ota.data_t1(a, b, ota_control); INSERT INTO ota.data_t1 VALUES(1, 2, 'x.x'); } {SQLITE_ERROR - invalid ota_control value} 7 { CREATE TABLE t1(a, b PRIMARY KEY) WITHOUT ROWID; CREATE TABLE ota.data_t1(a, b, ota_control); INSERT INTO ota.data_t1 VALUES(1, 2, NULL); } {SQLITE_ERROR - invalid ota_control value} 8 { CREATE TABLE t1(a, b PRIMARY KEY) WITHOUT ROWID; CREATE TABLE ota.data_t1(a, b, ota_control); INSERT INTO ota.data_t1 VALUES(1, 2, 4); } {SQLITE_ERROR - invalid ota_control value} 9 { CREATE TABLE t1(a, b PRIMARY KEY) WITHOUT ROWID; CREATE TABLE ota.data_t1(a, b, ota_control); INSERT INTO ota.data_t1 VALUES(1, 2, 2); } {SQLITE_ERROR - invalid ota_control value} } { reset_db forcedelete ota.db execsql { ATTACH 'ota.db' AS ota } execsql $schema |
︙ | ︙ |
Changes to ext/ota/ota11.test.
︙ | ︙ | |||
143 144 145 146 147 148 149 150 151 152 | sqlite3ota ota test.db ota.db ota step } {SQLITE_DONE} do_test 4.4 { ota close } {SQLITE_DONE} # Also, check that an invalid state value in the ota_state table is # detected and reported as corruption. | > > > > > > > > > > > > > > > > | | | > > > > > > > > > > > > > > > > > | 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | sqlite3ota ota test.db ota.db ota step } {SQLITE_DONE} do_test 4.4 { ota close } {SQLITE_DONE} do_test 4.5.1 { sqlite3 dbo ota.db dbo eval { INSERT INTO ota_state VALUES(100, 100) } dbo close sqlite3ota ota test.db ota.db ota step } {SQLITE_CORRUPT} do_test 4.5.2 { list [catch {ota close} msg] $msg } {1 SQLITE_CORRUPT} do_test 4.5.3 { sqlite3 dbo ota.db dbo eval { DELETE FROM ota_state WHERE k = 100 } dbo close } {} # Also, check that an invalid state value in the ota_state table is # detected and reported as corruption. do_test 4.6.1 { sqlite3 dbo ota.db dbo eval { UPDATE ota_state SET v = v*-1 WHERE k = 1 } dbo close sqlite3ota ota test.db ota.db ota step } {SQLITE_CORRUPT} do_test 4.6.2 { list [catch {ota close} msg] $msg } {1 SQLITE_CORRUPT} do_test 4.6.3 { sqlite3 dbo ota.db dbo eval { UPDATE ota_state SET v = v*-1 WHERE k = 1 } dbo close } {} do_test 4.7.1 { sqlite3 dbo ota.db dbo eval { UPDATE ota_state SET v = 1 WHERE k = 1 } dbo eval { UPDATE ota_state SET v = 'nosuchtable' WHERE k = 2 } dbo close sqlite3ota ota test.db ota.db ota step } {SQLITE_ERROR} do_test 4.7.2 { list [catch {ota close} msg] $msg } {1 {SQLITE_ERROR - ota_state mismatch error}} finish_test |
Changes to ext/ota/ota3.test.
︙ | ︙ | |||
165 166 167 168 169 170 171 172 173 174 175 | sqlite3ota ota file:test.db?vfs=tvfs ota.db list [catch { ota step } msg] $msg } {0 SQLITE_ERROR} do_test 4.2 { list [catch { ota close } msg] $msg } {1 {SQLITE_ERROR - ota vfs not found}} tvfs delete finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | sqlite3ota ota file:test.db?vfs=tvfs ota.db list [catch { ota step } msg] $msg } {0 SQLITE_ERROR} do_test 4.2 { list [catch { ota close } msg] $msg } {1 {SQLITE_ERROR - ota vfs not found}} tvfs delete #------------------------------------------------------------------------- # Test a large ota update to ensure that wal_autocheckpoint does not get # in the way. # forcedelete ota.db reset_db do_execsql_test 5.1 { CREATE TABLE x1(a, b, c, PRIMARY KEY(a)) WITHOUT ROWID; CREATE INDEX i1 ON x1(a); ATTACH 'ota.db' AS ota; CREATE TABLE ota.data_x1(a, b, c, ota_control); WITH s(a, b, c) AS ( SELECT randomblob(300), randomblob(300), 1 UNION ALL SELECT randomblob(300), randomblob(300), c+1 FROM s WHERE c<2000 ) INSERT INTO data_x1 SELECT a, b, c, 0 FROM s; } do_test 5.2 { sqlite3ota ota test.db ota.db while {[ota step]=="SQLITE_OK" && [file exists test.db-wal]==0} {} ota close } {SQLITE_OK} do_test 5.3 { expr {[file size test.db-wal] > (1024 * 1200)} } 1 finish_test |
Changes to ext/ota/otafault.test.
︙ | ︙ | |||
80 81 82 83 84 85 86 87 88 89 90 91 92 93 | INSERT INTO data_t1 VALUES('D', NULL, NULL, 1); INSERT INTO data_t1 VALUES('A', 'Z', NULL, '.x.'); INSERT INTO data_t1 VALUES('G', 'H', 'I', 0); } { SELECT * FROM t1 ORDER BY a; } {A Z C G H I} } { catch {db close} forcedelete ota.db test.db sqlite3 db test.db execsql { PRAGMA encoding = utf16; ATTACH 'ota.db' AS ota; | > > > > > > > > > > > | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | INSERT INTO data_t1 VALUES('D', NULL, NULL, 1); INSERT INTO data_t1 VALUES('A', 'Z', NULL, '.x.'); INSERT INTO data_t1 VALUES('G', 'H', 'I', 0); } { SELECT * FROM t1 ORDER BY a; } {A Z C G H I} 5 { CREATE TABLE t1(a, b, c); CREATE INDEX t1c ON t1(c, b); CREATE TABLE ota.data_t1(a, b, c, ota_rowid, ota_control); INSERT INTO data_t1 VALUES('a', 'b', 'c', 1, 0); INSERT INTO data_t1 VALUES('d', 'e', 'f', '2', 0); } { SELECT * FROM t1 ORDER BY a; } {a b c d e f} } { catch {db close} forcedelete ota.db test.db sqlite3 db test.db execsql { PRAGMA encoding = utf16; ATTACH 'ota.db' AS ota; |
︙ | ︙ | |||
114 115 116 117 118 119 120 121 122 123 124 125 126 127 | {1 SQLITE_IOERR_WRITE} {1 SQLITE_IOERR_READ} {1 SQLITE_IOERR_FSYNC} {1 {SQLITE_ERROR - SQL logic error or missing database}} {1 {SQLITE_ERROR - unable to open database: ota.db}} {1 {SQLITE_IOERR - unable to open database: ota.db}} } } { catch {db close} sqlite3_shutdown set lookaside_config [sqlite3_config_lookaside 0 0] sqlite3_initialize autoinstall_test_functions | > > > > > > | 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | {1 SQLITE_IOERR_WRITE} {1 SQLITE_IOERR_READ} {1 SQLITE_IOERR_FSYNC} {1 {SQLITE_ERROR - SQL logic error or missing database}} {1 {SQLITE_ERROR - unable to open database: ota.db}} {1 {SQLITE_IOERR - unable to open database: ota.db}} } 3 shmerr-* { {0 SQLITE_DONE} {1 {SQLITE_IOERR - disk I/O error}} {1 SQLITE_IOERR} } } { catch {db close} sqlite3_shutdown set lookaside_config [sqlite3_config_lookaside 0 0] sqlite3_initialize autoinstall_test_functions |
︙ | ︙ |
Changes to ext/ota/sqlite3ota.c.
︙ | ︙ | |||
1636 1637 1638 1639 1640 1641 1642 | } static int otaCaptureWalRead(sqlite3ota *pOta, i64 iOff, int iAmt){ const u32 mReq = (1<<WAL_LOCK_WRITE)|(1<<WAL_LOCK_CKPT)|(1<<WAL_LOCK_READ0); u32 iFrame; if( pOta->mLock!=mReq ){ | > | | 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 | } static int otaCaptureWalRead(sqlite3ota *pOta, i64 iOff, int iAmt){ const u32 mReq = (1<<WAL_LOCK_WRITE)|(1<<WAL_LOCK_CKPT)|(1<<WAL_LOCK_READ0); u32 iFrame; if( pOta->mLock!=mReq ){ pOta->rc = SQLITE_BUSY; return SQLITE_INTERNAL; } pOta->pgsz = iAmt; if( pOta->nFrame==pOta->nFrameAlloc ){ int nNew = (pOta->nFrameAlloc ? pOta->nFrameAlloc : 64) * 2; OtaFrame *aNew; aNew = (OtaFrame*)sqlite3_realloc(pOta->aFrame, nNew * sizeof(OtaFrame)); |
︙ | ︙ | |||
1663 1664 1665 1666 1667 1668 1669 | static int otaCaptureDbWrite(sqlite3ota *pOta, i64 iOff){ pOta->aFrame[pOta->nFrame-1].iDbPage = (u32)(iOff / pOta->pgsz) + 1; return SQLITE_OK; } static void otaCheckpointFrame(sqlite3ota *p, OtaFrame *pFrame){ | < | | | > | | | | | < < | > | | | < | 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 | static int otaCaptureDbWrite(sqlite3ota *pOta, i64 iOff){ pOta->aFrame[pOta->nFrame-1].iDbPage = (u32)(iOff / pOta->pgsz) + 1; return SQLITE_OK; } static void otaCheckpointFrame(sqlite3ota *p, OtaFrame *pFrame){ sqlite3_file *pWal = p->pTargetFd->pWalFd->pReal; sqlite3_file *pDb = p->pTargetFd->pReal; i64 iOff; assert( p->rc==SQLITE_OK ); iOff = (i64)(pFrame->iWalFrame-1) * (p->pgsz + 24) + 32 + 24; p->rc = pWal->pMethods->xRead(pWal, p->aBuf, p->pgsz, iOff); if( p->rc ) return; iOff = (i64)(pFrame->iDbPage-1) * p->pgsz; p->rc = pDb->pMethods->xWrite(pDb, p->aBuf, p->pgsz, iOff); } /* ** Take an EXCLUSIVE lock on the database file. */ static void otaLockDatabase(sqlite3ota *p){ sqlite3_file *pReal = p->pTargetFd->pReal; assert( p->rc==SQLITE_OK ); p->rc = pReal->pMethods->xLock(pReal, SQLITE_LOCK_SHARED); if( p->rc==SQLITE_OK ){ p->rc = pReal->pMethods->xLock(pReal, SQLITE_LOCK_EXCLUSIVE); } } /* ** The OTA handle is currently in OTA_STAGE_OAL state, with a SHARED lock ** on the database file. This proc moves the *-oal file to the *-wal path, ** then reopens the database file (this time in vanilla, non-oal, WAL mode). |
︙ | ︙ | |||
1856 1857 1858 1859 1860 1861 1862 | && sqlite3_column_type(pIter->pSelect, i)==SQLITE_NULL ){ p->rc = SQLITE_MISMATCH; p->zErrmsg = sqlite3_mprintf("datatype mismatch"); goto step_out; } | | < | > | 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 | && sqlite3_column_type(pIter->pSelect, i)==SQLITE_NULL ){ p->rc = SQLITE_MISMATCH; p->zErrmsg = sqlite3_mprintf("datatype mismatch"); goto step_out; } if( eType==OTA_DELETE && pIter->abTblPk[i]==0 ){ continue; } pVal = sqlite3_column_value(pIter->pSelect, i); p->rc = sqlite3_bind_value(pWriter, i+1, pVal); if( p->rc ) goto step_out; } if( pIter->zIdx==0 && (pIter->eType==OTA_PK_VTAB || pIter->eType==OTA_PK_NONE) ){ /* For a virtual table, or a table with no primary key, the ** SELECT statement is: ** ** SELECT <cols>, ota_control, ota_rowid FROM .... ** ** Hence column_value(pIter->nCol+1). */ assertColumnName(pIter->pSelect, pIter->nCol+1, "ota_rowid"); pVal = sqlite3_column_value(pIter->pSelect, pIter->nCol+1); p->rc = sqlite3_bind_value(pWriter, pIter->nCol+1, pVal); } if( p->rc==SQLITE_OK ){ sqlite3_step(pWriter); p->rc = resetAndCollectError(pWriter, &p->zErrmsg); } }else{ sqlite3_value *pVal; sqlite3_stmt *pUpdate = 0; assert( eType==OTA_UPDATE ); otaGetUpdateStmt(p, pIter, zMask, &pUpdate); if( pUpdate ){ for(i=0; p->rc==SQLITE_OK && i<pIter->nCol; i++){ char c = zMask[pIter->aiSrcOrder[i]]; pVal = sqlite3_column_value(pIter->pSelect, i); if( pIter->abTblPk[i] || c=='x' || c=='d' ){ p->rc = sqlite3_bind_value(pUpdate, i+1, pVal); |
︙ | ︙ | |||
1908 1909 1910 1911 1912 1913 1914 | p->rc = sqlite3_bind_value(pUpdate, pIter->nCol+1, pVal); } if( p->rc==SQLITE_OK ){ sqlite3_step(pUpdate); p->rc = resetAndCollectError(pUpdate, &p->zErrmsg); } } | < < < | 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 | p->rc = sqlite3_bind_value(pUpdate, pIter->nCol+1, pVal); } if( p->rc==SQLITE_OK ){ sqlite3_step(pUpdate); p->rc = resetAndCollectError(pUpdate, &p->zErrmsg); } } } } step_out: return p->rc; } |
︙ | ︙ | |||
1990 1991 1992 1993 1994 1995 1996 | ** Step the OTA object. */ int sqlite3ota_step(sqlite3ota *p){ if( p ){ switch( p->eStage ){ case OTA_STAGE_OAL: { OtaObjIter *pIter = &p->objiter; | | | 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 | ** Step the OTA object. */ int sqlite3ota_step(sqlite3ota *p){ if( p ){ switch( p->eStage ){ case OTA_STAGE_OAL: { OtaObjIter *pIter = &p->objiter; while( p->rc==SQLITE_OK && pIter->zTbl ){ if( pIter->bCleanup ){ /* Clean up the ota_tmp_xxx table for the previous table. It ** cannot be dropped as there are currently active SQL statements. ** But the contents can be deleted. */ if( pIter->eType!=OTA_PK_VTAB ){ otaMPrintfExec(p, "DELETE FROM ota.'ota_tmp_%q'", pIter->zTbl); |
︙ | ︙ | |||
2018 2019 2020 2021 2022 2023 2024 | p->nStep = 0; } } otaObjIterNext(p, pIter); } | | > > | | | | | | | | | | | | | | | | | | | | | | | | < | > | 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 | p->nStep = 0; } } otaObjIterNext(p, pIter); } if( p->rc==SQLITE_OK ){ assert( pIter->zTbl==0 ); otaSaveState(p, OTA_STAGE_MOVE); otaIncrSchemaCookie(p); if( p->rc==SQLITE_OK ){ p->rc = sqlite3_exec(p->db, "COMMIT", 0, 0, &p->zErrmsg); } p->eStage = OTA_STAGE_MOVE; } break; } case OTA_STAGE_MOVE: { if( p->rc==SQLITE_OK ){ otaMoveOalFile(p); p->nProgress++; } break; } case OTA_STAGE_CKPT: { if( p->rc==SQLITE_OK ){ if( p->nStep>=p->nFrame ){ sqlite3_file *pDb = p->pTargetFd->pReal; /* Sync the db file */ p->rc = pDb->pMethods->xSync(pDb, SQLITE_SYNC_NORMAL); /* Update nBackfill */ if( p->rc==SQLITE_OK ){ void volatile *ptr; p->rc = pDb->pMethods->xShmMap(pDb, 0, 32*1024, 0, &ptr); if( p->rc==SQLITE_OK ){ ((u32*)ptr)[12] = p->iMaxFrame; } } if( p->rc==SQLITE_OK ){ p->eStage = OTA_STAGE_DONE; p->rc = SQLITE_DONE; } }else{ OtaFrame *pFrame = &p->aFrame[p->nStep]; otaCheckpointFrame(p, pFrame); p->nStep++; } p->nProgress++; } break; } default: break; } return p->rc; |
︙ | ︙ | |||
2165 2166 2167 2168 2169 2170 2171 | static void otaLoadTransactionState(sqlite3ota *p, OtaState *pState){ assert( p->rc==SQLITE_OK ); if( pState->zTbl ){ OtaObjIter *pIter = &p->objiter; int rc = SQLITE_OK; while( rc==SQLITE_OK && pIter->zTbl && (pIter->bCleanup | | | | | | 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 | static void otaLoadTransactionState(sqlite3ota *p, OtaState *pState){ assert( p->rc==SQLITE_OK ); if( pState->zTbl ){ OtaObjIter *pIter = &p->objiter; int rc = SQLITE_OK; while( rc==SQLITE_OK && pIter->zTbl && (pIter->bCleanup || otaStrCompare(pIter->zIdx, pState->zIdx) || otaStrCompare(pIter->zTbl, pState->zTbl) )){ rc = otaObjIterNext(p, pIter); } if( rc==SQLITE_OK && !pIter->zTbl ){ rc = SQLITE_ERROR; p->zErrmsg = sqlite3_mprintf("ota_state mismatch error"); } if( rc==SQLITE_OK ){ p->nStep = pState->nRow; rc = otaObjIterPrepareAll(p, &p->objiter, p->nStep); |
︙ | ︙ | |||
2289 2290 2291 2292 2293 2294 2295 | } } if( p->rc==SQLITE_OK ){ if( p->eStage==OTA_STAGE_OAL ){ /* Open the transaction */ | < | < | 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 | } } if( p->rc==SQLITE_OK ){ if( p->eStage==OTA_STAGE_OAL ){ /* Open the transaction */ p->rc = sqlite3_exec(p->db, "BEGIN IMMEDIATE", 0, 0, &p->zErrmsg); /* Point the object iterator at the first object */ if( p->rc==SQLITE_OK ){ p->rc = otaObjIterFirst(p, &p->objiter); } if( p->rc==SQLITE_OK ){ |
︙ | ︙ |