/ Check-in [48d201cd]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Remove the experimental sqlite3_transaction_save() and restore() APIs.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | ota-update
Files: files | file ages | folders
SHA1: 48d201cd8b68c0377cf8a2cc6439b893f9462fe2
User & Date: dan 2014-09-15 19:34:04
Context
2014-09-16
20:02
Clarify the effects of the pager_ota_mode pragma. Add tests and fixes for the same. check-in: decaccc3 user: dan tags: ota-update
2014-09-15
19:34
Remove the experimental sqlite3_transaction_save() and restore() APIs. check-in: 48d201cd user: dan tags: ota-update
16:57
Merge latest trunk fixes into this branch. check-in: 5efafef5 user: dan tags: ota-update
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to ext/ota/ota1.test.

215
216
217
218
219
220
221






222
223
224
225
226
227
228
...
251
252
253
254
255
256
257









258
259
260
261
262
263
264
    }
    3 {
      CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
      CREATE INDEX i1 ON t1(b);
      CREATE INDEX i2 ON t1(c, b);
      CREATE INDEX i3 ON t1(c, b, c);
    }






  } {
    reset_db
    execsql $schema
    execsql {
      INSERT INTO t1 VALUES(2, 'hello', 'world');
      INSERT INTO t1 VALUES(4, 'hello', 'planet');
      INSERT INTO t1 VALUES(6, 'hello', 'xyz');
................................................................................
  foreach {tn schema} {
    1 {
      CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d);
    }
    2 {
      CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d);
      CREATE INDEX i1 ON t1(d);









      CREATE INDEX i2 ON t1(d, c);
      CREATE INDEX i3 ON t1(d, c, b);
      CREATE INDEX i4 ON t1(b);
      CREATE INDEX i5 ON t1(c);
      CREATE INDEX i6 ON t1(c, b);
    }
  } {







>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>







215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
...
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
    }
    3 {
      CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
      CREATE INDEX i1 ON t1(b);
      CREATE INDEX i2 ON t1(c, b);
      CREATE INDEX i3 ON t1(c, b, c);
    }
    4 {
      CREATE TABLE t1(a INT PRIMARY KEY, b, c) WITHOUT ROWID;
      CREATE INDEX i1 ON t1(b);
      CREATE INDEX i2 ON t1(c, b);
      CREATE INDEX i3 ON t1(c, b, c);
    }
  } {
    reset_db
    execsql $schema
    execsql {
      INSERT INTO t1 VALUES(2, 'hello', 'world');
      INSERT INTO t1 VALUES(4, 'hello', 'planet');
      INSERT INTO t1 VALUES(6, 'hello', 'xyz');
................................................................................
  foreach {tn schema} {
    1 {
      CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d);
    }
    2 {
      CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d);
      CREATE INDEX i1 ON t1(d);
      CREATE INDEX i2 ON t1(d, c);
      CREATE INDEX i3 ON t1(d, c, b);
      CREATE INDEX i4 ON t1(b);
      CREATE INDEX i5 ON t1(c);
      CREATE INDEX i6 ON t1(c, b);
    }
    3 {
      CREATE TABLE t1(a PRIMARY KEY, b, c, d) WITHOUT ROWID;
      CREATE INDEX i1 ON t1(d);
      CREATE INDEX i2 ON t1(d, c);
      CREATE INDEX i3 ON t1(d, c, b);
      CREATE INDEX i4 ON t1(b);
      CREATE INDEX i5 ON t1(c);
      CREATE INDEX i6 ON t1(c, b);
    }
  } {

Changes to src/main.c.

3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
** Return 1 if database is read-only or 0 if read/write.  Return -1 if
** no such database exists.
*/
int sqlite3_db_readonly(sqlite3 *db, const char *zDbName){
  Btree *pBt = sqlite3DbNameToBtree(db, zDbName);
  return pBt ? sqlite3BtreeIsReadonly(pBt) : -1;
}

int sqlite3_transaction_save(sqlite3 *db, void **ppState, int *pnState){
  Pager *pPager = sqlite3BtreePager(db->aDb[0].pBt);
  return sqlite3PagerSaveState(pPager, ppState, pnState);
}

int sqlite3_transaction_restore(sqlite3 *db, const void *pState, int nState){
  Pager *pPager = sqlite3BtreePager(db->aDb[0].pBt);
  return sqlite3PagerRestoreState(pPager, pState, nState);
}









<
<
<
<
<
<
<
<
<
<
<
<
3469
3470
3471
3472
3473
3474
3475












** Return 1 if database is read-only or 0 if read/write.  Return -1 if
** no such database exists.
*/
int sqlite3_db_readonly(sqlite3 *db, const char *zDbName){
  Btree *pBt = sqlite3DbNameToBtree(db, zDbName);
  return pBt ? sqlite3BtreeIsReadonly(pBt) : -1;
}












Changes to src/pager.c.

7230
7231
7232
7233
7234
7235
7236
7237
7238
7239
7240
7241
7242
7243
7244
7245
7246
7247
7248
7249
7250
7251
7252
7253
7254
7255
7256
7257
7258
7259
7260
7261
7262
7263
7264
7265
7266
7267
7268
7269
7270
7271
7272
7273
7274
7275
7276
7277
7278
    if( rc==SQLITE_OK ){
      rc = sqlite3WalClose(pPager->pWal, pPager->ckptSyncFlags,
                           pPager->pageSize, (u8*)pPager->pTmpSpace);
      pPager->pWal = 0;
      pagerFixMaplimit(pPager);
    }
  }
  return rc;
}

int sqlite3PagerSaveState(Pager *pPager, void **ppState, int *pnState){
  int rc = SQLITE_OK;
  *ppState = 0;
  *pnState = 0;
  if( pPager->pWal==0 || pPager->eState<PAGER_WRITER_LOCKED ){
    rc = SQLITE_ERROR;
  }else{
    /* Flush all dirty pages to the wal. */
    PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache);
    rc = sqlite3WalFrames(pPager->pWal, 
        pPager->pageSize, pList, 0, 0, pPager->walSyncFlags
    );
    if( rc==SQLITE_OK ){
      rc = sqlite3WalSaveState(pPager->pWal, ppState, pnState);
    }
  }
  return rc;
}

int sqlite3PagerRestoreState(Pager *pPager, const void *pState, int nState){
  int rc = SQLITE_OK;
  if( pPager->pWal==0 
   || pPager->eState<PAGER_WRITER_LOCKED 
   || sqlite3PcacheDirtyList(pPager->pPCache)
  ){
    rc = SQLITE_ERROR;
  }else{
    sqlite3PcacheTruncate(pPager->pPCache, 1);
    rc = sqlite3WalRestoreState(pPager->pWal, pState, nState);
    pPager->eState = PAGER_WRITER_CACHEMOD;
  }

  return rc;
}

#endif /* !SQLITE_OMIT_WAL */

#ifdef SQLITE_ENABLE_ZIPVFS
/*







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







7230
7231
7232
7233
7234
7235
7236



































7237
7238
7239
7240
7241
7242
7243
    if( rc==SQLITE_OK ){
      rc = sqlite3WalClose(pPager->pWal, pPager->ckptSyncFlags,
                           pPager->pageSize, (u8*)pPager->pTmpSpace);
      pPager->pWal = 0;
      pagerFixMaplimit(pPager);
    }
  }



































  return rc;
}

#endif /* !SQLITE_OMIT_WAL */

#ifdef SQLITE_ENABLE_ZIPVFS
/*

Changes to src/pager.h.

203
204
205
206
207
208
209
210
211
212
213
214
215
  void disable_simulated_io_errors(void);
  void enable_simulated_io_errors(void);
#else
# define disable_simulated_io_errors()
# define enable_simulated_io_errors()
#endif

int sqlite3PagerSaveState(Pager *pPager, void **ppState, int *pnState);
int sqlite3PagerRestoreState(Pager *pPager, const void *pState, int nState);

int sqlite3PagerSetOtaMode(Pager *pPager, int bOta);

#endif /* _PAGER_H_ */







<
<
<



203
204
205
206
207
208
209



210
211
212
  void disable_simulated_io_errors(void);
  void enable_simulated_io_errors(void);
#else
# define disable_simulated_io_errors()
# define enable_simulated_io_errors()
#endif




int sqlite3PagerSetOtaMode(Pager *pPager, int bOta);

#endif /* _PAGER_H_ */

Changes to src/pragma.c.

478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
  { /* zName:     */ "writable_schema",
    /* ePragTyp:  */ PragTyp_FLAG,
    /* ePragFlag: */ 0,
    /* iArg:      */ SQLITE_WriteSchema|SQLITE_RecoveryMode },
#endif
};
/* Number of pragmas: 59 on by default, 72 total. */
/* Number of pragmas: 58 on by default, 71 total. */
/* End of the automatically generated pragma table.
***************************************************************************/

/*
** Interpret the given string as a safety level.  Return 0 for OFF,
** 1 for ON or NORMAL and 2 for FULL.  Return 1 for an empty or 
** unrecognized string argument.  The FULL option is disallowed







<







478
479
480
481
482
483
484

485
486
487
488
489
490
491
  { /* zName:     */ "writable_schema",
    /* ePragTyp:  */ PragTyp_FLAG,
    /* ePragFlag: */ 0,
    /* iArg:      */ SQLITE_WriteSchema|SQLITE_RecoveryMode },
#endif
};
/* Number of pragmas: 59 on by default, 72 total. */

/* End of the automatically generated pragma table.
***************************************************************************/

/*
** Interpret the given string as a safety level.  Return 0 for OFF,
** 1 for ON or NORMAL and 2 for FULL.  Return 1 for an empty or 
** unrecognized string argument.  The FULL option is disallowed

Changes to src/prepare.c.

792
793
794
795
796
797
798

799
800
801
802
803
804
805
  const char **pzTail       /* OUT: End of parsed string */
){
  int rc;
  rc = sqlite3LockAndPrepare(db,zSql,nBytes,1,0,ppStmt,pzTail);
  assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 );  /* VERIFY: F13021 */
  return rc;
}


#ifndef SQLITE_OMIT_UTF16
/*
** Compile the UTF-16 encoded SQL statement zSql into a statement handle.
*/
static int sqlite3Prepare16(
  sqlite3 *db,              /* Database handle. */ 







>







792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
  const char **pzTail       /* OUT: End of parsed string */
){
  int rc;
  rc = sqlite3LockAndPrepare(db,zSql,nBytes,1,0,ppStmt,pzTail);
  assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 );  /* VERIFY: F13021 */
  return rc;
}


#ifndef SQLITE_OMIT_UTF16
/*
** Compile the UTF-16 encoded SQL statement zSql into a statement handle.
*/
static int sqlite3Prepare16(
  sqlite3 *db,              /* Database handle. */ 

Changes to src/sqlite.h.in.

7460
7461
7462
7463
7464
7465
7466
7467
7468
7469
7470
7471
7472
7473
7474
7475
7476
7477
7478
7479
7480
7481
7482
7483
7484
7485
7486
7487
7488
7489
7490
7491
7492
7493
7494
7495
7496
7497
  int bDelete,                    /* Zero for insert, non-zero for delete */
  const char *zIndex,             /* Index to write to */
  sqlite3_stmt**,                 /* OUT: New statement handle */
  const char ***pazColl,          /* OUT: Collation sequences for each column */
  int **paiCol, int *pnCol        /* OUT: See above */
);

/*
** This function is used to save the state of an ongoing WAL mode write 
** transaction on the "main" database of the supplied database handle.
**
** If successful, SQLITE_OK is returned and output variable (*ppState)
** is set to point to a buffer containing the transaction state data. 
** (*pnState) is set to the size of that buffer in bytes. Otherwise, if
** an error occurs, an SQLite error code is returned and both output
** variables are zeroed.
**
** A transaction state may be saved if: 
**
**   * the transaction does not contain any schema modifications.
**   * there are no open sub-transactions.
*/
int sqlite3_transaction_save(sqlite3 *db, void **ppState, int *pnState);

int sqlite3_transaction_restore(sqlite3 *db, const void *pState, int nState);

/*
** Undo the hack that converts floating point types to integer for
** builds on processors without floating point support.
*/
#ifdef SQLITE_OMIT_FLOATING_POINT
# undef double
#endif

#ifdef __cplusplus
}  /* End of the 'extern "C"' block */
#endif
#endif /* _SQLITE3_H_ */







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<












7460
7461
7462
7463
7464
7465
7466



















7467
7468
7469
7470
7471
7472
7473
7474
7475
7476
7477
7478
  int bDelete,                    /* Zero for insert, non-zero for delete */
  const char *zIndex,             /* Index to write to */
  sqlite3_stmt**,                 /* OUT: New statement handle */
  const char ***pazColl,          /* OUT: Collation sequences for each column */
  int **paiCol, int *pnCol        /* OUT: See above */
);




















/*
** Undo the hack that converts floating point types to integer for
** builds on processors without floating point support.
*/
#ifdef SQLITE_OMIT_FLOATING_POINT
# undef double
#endif

#ifdef __cplusplus
}  /* End of the 'extern "C"' block */
#endif
#endif /* _SQLITE3_H_ */

Changes to src/test1.c.

6493
6494
6495
6496
6497
6498
6499
6500
6501
6502
6503
6504
6505
6506
6507
6508
6509
6510
6511
6512
6513
6514
6515
6516
6517
6518
6519
6520
6521
6522
6523
6524
6525
6526
6527
6528
6529
6530
6531
6532
6533
6534
6535
6536
6537
6538
6539
6540
6541
6542
6543
6544
6545
6546
6547
6548
6549
6550
6551
6552
6553
6554
6555
6556
6557
6558
6559
6560
6561
6562
6563
6564
6565
6566
6567
6568
6569
6570
....
6920
6921
6922
6923
6924
6925
6926
6927
6928
6929
6930
6931
6932
6933
6934
6935
  return TCL_OK;
 sql_error:
  Tcl_AppendResult(interp, "sql error: ", sqlite3_errmsg(db), 0);
  return TCL_ERROR;
}


/*
** tclcmd: sqlite3_transaction_save DB
*/
static int testTransactionSave(
  void * clientData,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  void *pState;
  int nState;
  sqlite3 *db;
  int rc;

  if( objc!=2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "DB");
    return TCL_ERROR;
  }
  if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;

  rc = sqlite3_transaction_save(db, &pState, &nState);
  if( rc==SQLITE_OK ){
    Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pState, nState));
  }else{
    Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
    return TCL_ERROR;
  }

  sqlite3_free(pState);
  return TCL_OK;
}

/*
** tclcmd: sqlite3_transaction_restore DB BLOB
*/
static int testTransactionRestore(
  void * clientData,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  void *pState;
  int nState;
  sqlite3 *db;
  int rc;

  if( objc!=3 ){
    Tcl_WrongNumArgs(interp, 1, objv, "DB BLOB");
    return TCL_ERROR;
  }
  if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
  pState = (void*)Tcl_GetByteArrayFromObj(objv[2], &nState);

  rc = sqlite3_transaction_restore(db, pState, nState);
  if( rc==SQLITE_OK ){
    Tcl_ResetResult(interp);
  }else{
    Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
    return TCL_ERROR;
  }

  return TCL_OK;
}

#ifdef SQLITE_USER_AUTHENTICATION
#include "sqlite3userauth.h"
/*
** tclcmd:  sqlite3_user_authenticate DB USERNAME PASSWORD
*/
static int test_user_authenticate(
  ClientData clientData, /* Unused */
................................................................................
     { "sqlite3_test_control", test_test_control },
#if SQLITE_OS_UNIX
     { "getrusage", test_getrusage },
#endif
     { "load_static_extension", tclLoadStaticExtensionCmd },
     { "sorter_test_fakeheap", sorter_test_fakeheap },
     { "sorter_test_sort4_helper", sorter_test_sort4_helper },
     { "sqlite3_transaction_save",    testTransactionSave },
     { "sqlite3_transaction_restore", testTransactionRestore },
#ifdef SQLITE_USER_AUTHENTICATION
     { "sqlite3_user_authenticate", test_user_authenticate, 0 },
     { "sqlite3_user_add",          test_user_add,          0 },
     { "sqlite3_user_change",       test_user_change,       0 },
     { "sqlite3_user_delete",       test_user_delete,       0 },
#endif
  };







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







<
<







6493
6494
6495
6496
6497
6498
6499
































































6500
6501
6502
6503
6504
6505
6506
....
6856
6857
6858
6859
6860
6861
6862


6863
6864
6865
6866
6867
6868
6869
  return TCL_OK;
 sql_error:
  Tcl_AppendResult(interp, "sql error: ", sqlite3_errmsg(db), 0);
  return TCL_ERROR;
}


































































#ifdef SQLITE_USER_AUTHENTICATION
#include "sqlite3userauth.h"
/*
** tclcmd:  sqlite3_user_authenticate DB USERNAME PASSWORD
*/
static int test_user_authenticate(
  ClientData clientData, /* Unused */
................................................................................
     { "sqlite3_test_control", test_test_control },
#if SQLITE_OS_UNIX
     { "getrusage", test_getrusage },
#endif
     { "load_static_extension", tclLoadStaticExtensionCmd },
     { "sorter_test_fakeheap", sorter_test_fakeheap },
     { "sorter_test_sort4_helper", sorter_test_sort4_helper },


#ifdef SQLITE_USER_AUTHENTICATION
     { "sqlite3_user_authenticate", test_user_authenticate, 0 },
     { "sqlite3_user_add",          test_user_add,          0 },
     { "sqlite3_user_change",       test_user_change,       0 },
     { "sqlite3_user_delete",       test_user_delete,       0 },
#endif
  };

Changes to src/wal.c.

1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
....
1142
1143
1144
1145
1146
1147
1148

1149
1150
1151
1152
1153
1154


1155
1156
1157



1158














1159


1160




















1161
1162
1163
1164
1165
1166
1167
....
1829
1830
1831
1832
1833
1834
1835



1836
1837
1838
1839
1840
1841
1842
....
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
#endif /* SQLITE_ENABLE_EXPENSIVE_ASSERT */
  }


  return rc;
}

static int walFileReadHdr(Wal *pWal, int *pbValid){
  u8 aBuf[WAL_HDRSIZE];           /* Buffer to load WAL header into */
  int rc;                         /* Return code */
  u32 magic;                      /* Magic value read from WAL header */
  int szPage;                     /* Page size according to the log */
  u32 version;                    /* Magic value read from WAL header */

  *pbValid = 0;

  /* Read in the WAL header. */
  rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0);
  if( rc!=SQLITE_OK ){
    return rc;
  }

  /* If the database page size is not a power of two, or is greater than
  ** SQLITE_MAX_PAGE_SIZE, conclude that the WAL file contains no valid 
  ** data. Similarly, if the 'magic' value is invalid, ignore the whole
  ** WAL file.
  */
  magic = sqlite3Get4byte(&aBuf[0]);
  szPage = sqlite3Get4byte(&aBuf[8]);
  if( (magic&0xFFFFFFFE)!=WAL_MAGIC 
      || szPage&(szPage-1) 
      || szPage>SQLITE_MAX_PAGE_SIZE 
      || szPage<512 
  ){
    return SQLITE_OK;
  }

  pWal->hdr.bigEndCksum = (u8)(magic&0x00000001);
  pWal->szPage = szPage;
  pWal->nCkpt = sqlite3Get4byte(&aBuf[12]);
  memcpy(&pWal->hdr.aSalt, &aBuf[16], 8);

  /* Verify that the WAL header checksum is correct */
  walChecksumBytes(pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN, 
      aBuf, WAL_HDRSIZE-2*4, 0, pWal->hdr.aFrameCksum
  );
  if( pWal->hdr.aFrameCksum[0]!=sqlite3Get4byte(&aBuf[24])
      || pWal->hdr.aFrameCksum[1]!=sqlite3Get4byte(&aBuf[28])
  ){
    return SQLITE_OK;
  }

  /* Verify that the version number on the WAL format is one that
  ** are able to understand */
  version = sqlite3Get4byte(&aBuf[4]);
  if( version!=WAL_MAX_VERSION ){
    return SQLITE_CANTOPEN_BKPT;
  }

  *pbValid = 1;
  return SQLITE_OK;
}


/*
** Recover the wal-index by reading the write-ahead log file. 
**
** This routine first tries to establish an exclusive lock on the
** wal-index to prevent other threads/processes from doing anything
** with the WAL or wal-index while recovery is running.  The
................................................................................

  rc = sqlite3OsFileSize(pWal->pWalFd, &nSize);
  if( rc!=SQLITE_OK ){
    goto recovery_error;
  }

  if( nSize>WAL_HDRSIZE ){

    u8 *aFrame = 0;               /* Malloc'd buffer to load entire frame */
    int szFrame;                  /* Number of bytes in buffer aFrame[] */
    u8 *aData;                    /* Pointer to data part of aFrame buffer */
    int iFrame;                   /* Index of last frame read */
    i64 iOffset;                  /* Next offset to read from log file */
    int szPage;                   /* Page size according to the log */


    int isValid;                  /* True if this frame is valid */

    rc = walFileReadHdr(pWal, &isValid);



    if( rc!=SQLITE_OK ) goto recovery_error;














    if( isValid==0 ) goto finished;


    szPage = pWal->szPage;





















    /* Malloc a buffer to read frames into. */
    szFrame = szPage + WAL_FRAME_HDRSIZE;
    aFrame = (u8 *)sqlite3_malloc(szFrame);
    if( !aFrame ){
      rc = SQLITE_NOMEM;
      goto recovery_error;
................................................................................
  if( rx ){
    sqlite3_log(rx, "cannot limit WAL size: %s", pWal->zWalName);
  }
}

/*
** Close a connection to a log file.



*/
int sqlite3WalClose(
  Wal *pWal,                      /* Wal to close */
  int sync_flags,                 /* Flags to pass to OsSync() (or 0) */
  int nBuf,
  u8 *zBuf                        /* Buffer of at least nBuf bytes */
){
................................................................................
** heap-memory for the wal-index. Otherwise, if the argument is NULL or the
** WAL module is using shared-memory, return false. 
*/
int sqlite3WalHeapMemory(Wal *pWal){
  return (pWal && pWal->exclusiveMode==WAL_HEAPMEMORY_MODE );
}

/*
** Save current transaction state.
**
** The transaction state consists of a series of 32-bit big-endian integers:
**
**     * initial number of frames in WAL file.
**     * initial checksum values (2 integers).
**     * current number of frames.
**     * current checksum values (2 integers).
*/
int sqlite3WalSaveState(Wal *pWal, void **ppState, int *pnState){
  int rc = SQLITE_OK;

  *ppState = 0;
  *pnState = 0;
  if( pWal->writeLock==0 ){
    /* Must be in a write transaction to call this function. */
    rc = SQLITE_ERROR;
  }else{
    WalIndexHdr *pOrig = (WalIndexHdr*)walIndexHdr(pWal);
    int nBuf = 6 * 4;             /* Bytes of space to allocate */
    u8 *aBuf;

    aBuf = sqlite3_malloc(nBuf);
    if( aBuf==0 ){
      rc = SQLITE_NOMEM;
    }else{
      sqlite3Put4byte(&aBuf[0], pOrig->mxFrame);
      sqlite3Put4byte(&aBuf[4], pOrig->aFrameCksum[0]);
      sqlite3Put4byte(&aBuf[8], pOrig->aFrameCksum[1]);
      sqlite3Put4byte(&aBuf[12], pWal->hdr.mxFrame);
      sqlite3Put4byte(&aBuf[16], pWal->hdr.aFrameCksum[0]);
      sqlite3Put4byte(&aBuf[20], pWal->hdr.aFrameCksum[1]);
      *ppState = (void*)aBuf;
      *pnState = nBuf;
    }
  }

  return rc;
}

static int walUndoNoop(void *pUndoCtx, Pgno pgno){
  UNUSED_PARAMETER(pUndoCtx);
  UNUSED_PARAMETER(pgno);
  return SQLITE_OK;
}

/*
** If possible, restore the state of the curent transaction to that 
** described by the second and third arguments.
*/
int sqlite3WalRestoreState(Wal *pWal, const void *pState, int nState){
  int rc = SQLITE_OK;

  if( pWal->writeLock==0 ){
    /* Must have opened a write transaction to call this */
    rc = SQLITE_ERROR;
  }else{
    u8 *aBuf = (u8*)pState;
    int szFrame;                    /* Size of each frame in WAL file */
    u8 *aFrame = 0;                 /* Buffer to read data into */
    u8 *aData;                      /* Data part of aFrame[] buffer */
    u32 mxFrame;                    /* Maximum frame following restoration */
    int i;                          /* Iterator variable */

    WalIndexHdr *pOrig = (WalIndexHdr*)walIndexHdr(pWal);

    /* Check that no dirty pages have been written to the WAL file since
    ** the current transaction was opened.  */
    if( pOrig->mxFrame!=pWal->hdr.mxFrame 
     || pOrig->aFrameCksum[0]!=pWal->hdr.aFrameCksum[0] 
     || pOrig->aFrameCksum[1]!=pWal->hdr.aFrameCksum[1] 
    ){
      rc = SQLITE_ERROR;
    }

    /* Check that the WAL file is in the same state that it was when the
    ** transaction was saved. If not, return SQLITE_MISMATCH - cannot 
    ** resume this transaction  */
    if( rc==SQLITE_OK && (
          pWal->hdr.mxFrame!=sqlite3Get4byte(&aBuf[0])
       || pWal->hdr.aFrameCksum[0]!=sqlite3Get4byte(&aBuf[4])
       || pWal->hdr.aFrameCksum[1]!=sqlite3Get4byte(&aBuf[8])
    )){
      rc = SQLITE_MISMATCH;
    }

    if( rc==SQLITE_OK && pWal->readLock==0 ){
      int cnt = 0;
      walUnlockShared(pWal, WAL_READ_LOCK(0));
      pWal->readLock = -1;
      do{
        int notUsed;
        rc = walTryBeginRead(pWal, &notUsed, 1, ++cnt);
      }while( rc==WAL_RETRY );
      
      if( rc==SQLITE_OK ){
        int bValid;
        rc = walFileReadHdr(pWal, &bValid);
        if( rc==SQLITE_OK && bValid==0 ) rc = SQLITE_MISMATCH;
        pWal->hdr.szPage = (u16)((pWal->szPage&0xff00) | (pWal->szPage>>16));
      }
    }

    /* Malloc a buffer to read frames into. */
    if( rc==SQLITE_OK ){
      szFrame = pWal->szPage + WAL_FRAME_HDRSIZE;
      aFrame = (u8*)sqlite3_malloc(szFrame);
      if( !aFrame ){
        rc = SQLITE_NOMEM;
      }else{
        aData = &aFrame[WAL_FRAME_HDRSIZE];
      }
    }

    mxFrame = sqlite3Get4byte(&aBuf[12]);
    for(i=pWal->hdr.mxFrame+1; rc==SQLITE_OK && i<=mxFrame; i++){
      sqlite3_int64 iOff = walFrameOffset(i, pWal->szPage);
      rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOff);
      if( rc==SQLITE_OK ){
        u32 iPg;
        u32 dummy;
        if( 0==walDecodeFrame(pWal, &iPg, &dummy, aData, aFrame) ){
          rc = SQLITE_MISMATCH;
        }else{
          rc = walIndexAppend(pWal, i, iPg);
          if( iPg>pWal->hdr.nPage ) pWal->hdr.nPage = iPg;
        }
        pWal->hdr.mxFrame = i;
      }
    }
    sqlite3_free(aFrame);

    if( rc==SQLITE_OK ){
      assert( pWal->hdr.mxFrame==mxFrame );
      if( pWal->hdr.aFrameCksum[0]!=sqlite3Get4byte(&aBuf[16])
       || pWal->hdr.aFrameCksum[1]!=sqlite3Get4byte(&aBuf[20])
      ){
        rc = SQLITE_MISMATCH;
      }
    }


    if( rc!=SQLITE_OK ){
      sqlite3WalUndo(pWal, walUndoNoop, 0);
    }
  }

  return rc;
}

#ifdef SQLITE_ENABLE_ZIPVFS
/*
** If the argument is not NULL, it points to a Wal object that holds a
** read-lock. This function returns the database page-size if it is known,
** or zero if it is not (or if pWal is NULL).
*/
int sqlite3WalFramesize(Wal *pWal){
  assert( pWal==0 || pWal->readLock>=0 );
  return (pWal ? pWal->szPage : 0);
}
#endif

#endif /* #ifndef SQLITE_OMIT_WAL */







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







>






>
>


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







 







>
>
>







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<













1042
1043
1044
1045
1046
1047
1048
























































1049
1050
1051
1052
1053
1054
1055
....
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103

1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
....
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
....
3080
3081
3082
3083
3084
3085
3086























































































































































3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
#endif /* SQLITE_ENABLE_EXPENSIVE_ASSERT */
  }


  return rc;
}


























































/*
** Recover the wal-index by reading the write-ahead log file. 
**
** This routine first tries to establish an exclusive lock on the
** wal-index to prevent other threads/processes from doing anything
** with the WAL or wal-index while recovery is running.  The
................................................................................

  rc = sqlite3OsFileSize(pWal->pWalFd, &nSize);
  if( rc!=SQLITE_OK ){
    goto recovery_error;
  }

  if( nSize>WAL_HDRSIZE ){
    u8 aBuf[WAL_HDRSIZE];         /* Buffer to load WAL header into */
    u8 *aFrame = 0;               /* Malloc'd buffer to load entire frame */
    int szFrame;                  /* Number of bytes in buffer aFrame[] */
    u8 *aData;                    /* Pointer to data part of aFrame buffer */
    int iFrame;                   /* Index of last frame read */
    i64 iOffset;                  /* Next offset to read from log file */
    int szPage;                   /* Page size according to the log */
    u32 magic;                    /* Magic value read from WAL header */
    u32 version;                  /* Magic value read from WAL header */
    int isValid;                  /* True if this frame is valid */


    /* Read in the WAL header. */
    rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0);
    if( rc!=SQLITE_OK ){
      goto recovery_error;
    }

    /* If the database page size is not a power of two, or is greater than
    ** SQLITE_MAX_PAGE_SIZE, conclude that the WAL file contains no valid 
    ** data. Similarly, if the 'magic' value is invalid, ignore the whole
    ** WAL file.
    */
    magic = sqlite3Get4byte(&aBuf[0]);
    szPage = sqlite3Get4byte(&aBuf[8]);
    if( (magic&0xFFFFFFFE)!=WAL_MAGIC 
     || szPage&(szPage-1) 
     || szPage>SQLITE_MAX_PAGE_SIZE 
     || szPage<512 
    ){
      goto finished;
    }
    pWal->hdr.bigEndCksum = (u8)(magic&0x00000001);
    pWal->szPage = szPage;
    pWal->nCkpt = sqlite3Get4byte(&aBuf[12]);
    memcpy(&pWal->hdr.aSalt, &aBuf[16], 8);

    /* Verify that the WAL header checksum is correct */
    walChecksumBytes(pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN, 
        aBuf, WAL_HDRSIZE-2*4, 0, pWal->hdr.aFrameCksum
    );
    if( pWal->hdr.aFrameCksum[0]!=sqlite3Get4byte(&aBuf[24])
     || pWal->hdr.aFrameCksum[1]!=sqlite3Get4byte(&aBuf[28])
    ){
      goto finished;
    }

    /* Verify that the version number on the WAL format is one that
    ** are able to understand */
    version = sqlite3Get4byte(&aBuf[4]);
    if( version!=WAL_MAX_VERSION ){
      rc = SQLITE_CANTOPEN_BKPT;
      goto finished;
    }

    /* Malloc a buffer to read frames into. */
    szFrame = szPage + WAL_FRAME_HDRSIZE;
    aFrame = (u8 *)sqlite3_malloc(szFrame);
    if( !aFrame ){
      rc = SQLITE_NOMEM;
      goto recovery_error;
................................................................................
  if( rx ){
    sqlite3_log(rx, "cannot limit WAL size: %s", pWal->zWalName);
  }
}

/*
** Close a connection to a log file.
**
** If parameter zBuf is not NULL, attempt to obtain an exclusive lock 
** and run a checkpoint.
*/
int sqlite3WalClose(
  Wal *pWal,                      /* Wal to close */
  int sync_flags,                 /* Flags to pass to OsSync() (or 0) */
  int nBuf,
  u8 *zBuf                        /* Buffer of at least nBuf bytes */
){
................................................................................
** heap-memory for the wal-index. Otherwise, if the argument is NULL or the
** WAL module is using shared-memory, return false. 
*/
int sqlite3WalHeapMemory(Wal *pWal){
  return (pWal && pWal->exclusiveMode==WAL_HEAPMEMORY_MODE );
}
























































































































































#ifdef SQLITE_ENABLE_ZIPVFS
/*
** If the argument is not NULL, it points to a Wal object that holds a
** read-lock. This function returns the database page-size if it is known,
** or zero if it is not (or if pWal is NULL).
*/
int sqlite3WalFramesize(Wal *pWal){
  assert( pWal==0 || pWal->readLock>=0 );
  return (pWal ? pWal->szPage : 0);
}
#endif

#endif /* #ifndef SQLITE_OMIT_WAL */

Changes to src/wal.h.

122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140

/* Return true if the argument is non-NULL and the WAL module is using
** heap-memory for the wal-index. Otherwise, if the argument is NULL or the
** WAL module is using shared-memory, return false. 
*/
int sqlite3WalHeapMemory(Wal *pWal);

int sqlite3WalSaveState(Wal *pWal, void **ppState, int *pnState);
int sqlite3WalRestoreState(Wal *pWal, const void *pState, int nState);

#ifdef SQLITE_ENABLE_ZIPVFS
/* If the WAL file is not empty, return the number of bytes of content
** stored in each frame (i.e. the db page-size when the WAL was created).
*/
int sqlite3WalFramesize(Wal *pWal);
#endif

#endif /* ifndef SQLITE_OMIT_WAL */
#endif /* _WAL_H_ */







<
<
<









122
123
124
125
126
127
128



129
130
131
132
133
134
135
136
137

/* Return true if the argument is non-NULL and the WAL module is using
** heap-memory for the wal-index. Otherwise, if the argument is NULL or the
** WAL module is using shared-memory, return false. 
*/
int sqlite3WalHeapMemory(Wal *pWal);




#ifdef SQLITE_ENABLE_ZIPVFS
/* If the WAL file is not empty, return the number of bytes of content
** stored in each frame (i.e. the db page-size when the WAL was created).
*/
int sqlite3WalFramesize(Wal *pWal);
#endif

#endif /* ifndef SQLITE_OMIT_WAL */
#endif /* _WAL_H_ */