/ Check-in [2b10c5d2]
Login

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 | SQL archive
Timelines: family | ancestors | descendants | both | ota-update
Files: files | file ages | folders
SHA1: 2b10c5d2b8b8b535d3dec0c68a777db16268e1e5
User & Date: dan 2015-02-18 20:16:15
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: 0864d127 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: 2b10c5d2 user: dan tags: ota-update
17:40
Fix a problem with OTA updates in the presence of database readers. check-in: 144bb29f user: dan tags: ota-update
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

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
153
154
155
156
157
158
159
160
161
162

















163
164
165
  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.
do_test 4.5 {
  sqlite3 dbo ota.db
  dbo eval { UPDATE ota_state SET v = -1 WHERE k = 1 }
  dbo close
  sqlite3ota ota test.db ota.db
  ota step
} {SQLITE_CORRUPT}
do_test 4.6 {
  list [catch {ota close} msg] $msg
} {1 SQLITE_CORRUPT}


















finish_test








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



|

|




|


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



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
...
114
115
116
117
118
119
120






121
122
123
124
125
126
127
    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;
................................................................................
      {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







>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>







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
...
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
    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;
................................................................................
      {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

1643
1644
1645
1646
1647
1648
1649
1650
....
1663
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
1701
....
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
....
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892

1893
1894
1895
1896
1897
1898
1899
....
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
....
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
....
2018
2019
2020
2021
2022
2023
2024
2025

2026
2027
2028
2029
2030
2031
2032
....
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
2076
2077
....
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
....
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
}

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 ){

    return SQLITE_BUSY;
  }

  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));
................................................................................

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){
  if( p->rc==SQLITE_OK ){
    sqlite3_file *pWal = p->pTargetFd->pWalFd->pReal;
    sqlite3_file *pDb = p->pTargetFd->pReal;
    i64 iOff;


    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){
  if( p->rc==SQLITE_OK ){
    sqlite3_file *pReal = p->pTargetFd->pReal;

    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).
................................................................................
         && sqlite3_column_type(pIter->pSelect, i)==SQLITE_NULL
        ){
          p->rc = SQLITE_MISMATCH;
          p->zErrmsg = sqlite3_mprintf("datatype mismatch");
          goto step_out;
        }

        if( eType==SQLITE_DELETE && pIter->zIdx==0 && pIter->abTblPk[i]==0 ){
          continue;
        }

        pVal = sqlite3_column_value(pIter->pSelect, i);
        p->rc = sqlite3_bind_value(pWriter, i+1, pVal);
        if( p->rc==SQLITE_RANGE ) p->rc = SQLITE_OK;
        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:
................................................................................
        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 if( eType==OTA_UPDATE ){
      sqlite3_value *pVal;
      sqlite3_stmt *pUpdate = 0;

      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);
................................................................................
          p->rc = sqlite3_bind_value(pUpdate, pIter->nCol+1, pVal);
        }
        if( p->rc==SQLITE_OK ){
          sqlite3_step(pUpdate);
          p->rc = resetAndCollectError(pUpdate, &p->zErrmsg);
        }
      }
    }else{
      /* no-op */
      assert( eType==OTA_DELETE && pIter->zIdx );
    }
  }

 step_out:
  return p->rc;
}

................................................................................
** Step the OTA object.
*/
int sqlite3ota_step(sqlite3ota *p){
  if( p ){
    switch( p->eStage ){
      case OTA_STAGE_OAL: {
        OtaObjIter *pIter = &p->objiter;
        while( p && 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);
................................................................................
              p->nStep = 0;
            }
          }

          otaObjIterNext(p, pIter);
        }

        if( p->rc==SQLITE_OK && 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;
        }
................................................................................
          otaMoveOalFile(p);
          p->nProgress++;
        }
        break;
      }

      case OTA_STAGE_CKPT: {

        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;
................................................................................
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->zTbl, pState->zTbl) 
       || otaStrCompare(pIter->zIdx, pState->zIdx)
    )){
      rc = otaObjIterNext(p, &p->objiter);
    }

    if( rc==SQLITE_OK && !p->objiter.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);
................................................................................
      }
    }

    if( p->rc==SQLITE_OK ){
      if( p->eStage==OTA_STAGE_OAL ){

        /* Open the transaction */
        if( p->rc==SQLITE_OK ){
          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 ){







>
|







 







<
|
|
|

>
|
|
|

|
|
<







<
|
>
|
|
|
<







 







|





<







 







|


>







 







<
<
<







 







|







 







|
>







 







>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<
|
>







 







|
|

|


|







 







<
|
<







1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
....
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
....
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867

1868
1869
1870
1871
1872
1873
1874
....
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
....
1907
1908
1909
1910
1911
1912
1913



1914
1915
1916
1917
1918
1919
1920
....
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
....
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
....
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
....
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
....
2287
2288
2289
2290
2291
2292
2293

2294

2295
2296
2297
2298
2299
2300
2301
}

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));
................................................................................

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).
................................................................................
         && 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:
................................................................................
        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);
................................................................................
          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;
}

................................................................................
** 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);
................................................................................
              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;
        }
................................................................................
          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;
................................................................................
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);
................................................................................
      }
    }

    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 ){