/ Check-in [2954ab50]
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:Add an experimental extension for applying bulk updates to databases.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | ota-update
Files: files | file ages | folders
SHA1: 2954ab501049968430011b63d046eb42ff37a56c
User & Date: dan 2014-09-02 19:59:40
Context
2014-09-03
08:25
Add a command line program that uses the extension. This serves as example code and is also useful for performance testing. check-in: ffa1524e user: dan tags: ota-update
2014-09-02
19:59
Add an experimental extension for applying bulk updates to databases. check-in: 2954ab50 user: dan tags: ota-update
2014-09-01
23:06
Update comments in the ANALYZE command that describe how the Stat4Accum objecct is passed around within the VDBE. No changes to functional code. check-in: 9779c7a9 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Show Whitespace Changes Patch

Added ext/ota/ota1.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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
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
# 2014 August 30
#
# 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.
#
#***********************************************************************
#

set testdir [file join [file dirname $argv0] .. .. test]
source $testdir/tester.tcl
set ::testprefix ota1


# Create a simple OTA database. That expects to write to a table:
#
#   CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
#
proc create_ota1 {filename} {
  forcedelete $filename
  sqlite3 ota1 $filename  
  ota1 eval {
    CREATE TABLE data_t1(a, b, c, ota_control);
    INSERT INTO data_t1 VALUES(1, 2, 3, 0);
    INSERT INTO data_t1 VALUES(2, 'two', 'three', 0);
    INSERT INTO data_t1 VALUES(3, NULL, 8.2, 0);
  }
  ota1 close
  return $filename
}

# Create an empty target database suitable for the OTA created by 
# [create_ota1].
#
#   CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
#
proc create_target1 {filename} {
  forcedelete $filename
  sqlite3 target1 $filename  
  target1 eval { CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c) }
  target1 close
  return $filename
}

# Run the OTA in file $ota on target database $target until completion.
#
proc run_ota {target ota} {
  sqlite3ota ota $target $ota
  while { [ota step]=="SQLITE_OK" } {}
  ota close
}

proc step_ota {target ota} {
  while 1 {
    sqlite3ota ota $target $ota
    set rc [ota step]
    ota close
    if {$rc != "SQLITE_OK"} break
  }
  set rc
}

foreach {tn2 cmd} {1 step_ota 2 run_ota} {
  foreach {tn schema} {
    1 {
      CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
    }
    2 { 
      CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
      CREATE INDEX i1 ON t1(b);
    }
    3 { 
      CREATE TABLE t1(a PRIMARY KEY, b, c) WITHOUT ROWID;
    }
    4 { 
      CREATE TABLE t1(a PRIMARY KEY, b, c) WITHOUT ROWID;
      CREATE INDEX i1 ON t1(b);
    }
    5 { 
      CREATE TABLE t1(a, b, c, PRIMARY KEY(a, c)) WITHOUT ROWID;
      CREATE INDEX i1 ON t1(b);
    }
    6 { 
      CREATE TABLE t1(a, b, c, PRIMARY KEY(c)) WITHOUT ROWID;
      CREATE INDEX i1 ON t1(b, a);
    }
    7 { 
      CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
      CREATE INDEX i1 ON t1(b, c);
      CREATE INDEX i2 ON t1(c, b);
      CREATE INDEX i3 ON t1(a, b, c, a, b, c);
    }
  } {
    reset_db
    execsql $schema
  
    do_test 1.$tn2.$tn.1 {
      create_ota1 ota.db
      $cmd test.db ota.db
    } {SQLITE_DONE}
    
    do_execsql_test 1.$tn2.$tn.2 {
      SELECT * FROM t1 ORDER BY a ASC;
    } {
      1 2 3 
      2 two three 
      3 {} 8.2
    }
  
    do_execsql_test 1.$tn2.$tn.3 { PRAGMA integrity_check } ok
  }
}

#-------------------------------------------------------------------------
# Check that an OTA cannot be applied to a table that has no PK.
#
reset_db
create_ota1 ota.db
do_execsql_test 2.1 { CREATE TABLE t1(a, b, c) }
do_test 2.2 {
  sqlite3ota ota test.db ota.db
  ota step
} {SQLITE_ERROR}
do_test 2.3 {
  list [catch { ota close } msg] $msg
} {1 {SQLITE_ERROR - table t1 has no PRIMARY KEY}}

reset_db
do_execsql_test 2.4 { CREATE TABLE t1(a PRIMARY KEY, b, c) }
do_test 2.5 {
  sqlite3ota ota test.db ota.db
  ota step
} {SQLITE_ERROR}
do_test 2.6 {
  list [catch { ota close } msg] $msg
} {1 {SQLITE_ERROR - table t1 has no PRIMARY KEY}}

#-------------------------------------------------------------------------
# Check that if a UNIQUE constraint is violated the current and all 
# subsequent [ota step] calls return SQLITE_CONSTRAINT. And that the OTA 
# transaction is rolled back by the [ota close] that deletes the ota 
# handle.
#
foreach {tn errcode errmsg schema} {
  1 SQLITE_CONSTRAINT "UNIQUE constraint failed: t1.a" {
    CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
    INSERT INTO t1 VALUES(3, 2, 1);
  } 

  2 SQLITE_CONSTRAINT "UNIQUE constraint failed: t1.c" {
    CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c UNIQUE);
    INSERT INTO t1 VALUES(4, 2, 'three');
  } 

} {
  reset_db
  execsql $schema
  set cksum [dbcksum db main]

  do_test 3.$tn.1 {
    create_ota1 ota.db
    sqlite3ota ota test.db ota.db
    while {[set res [ota step]]=="SQLITE_OK"} {}
    set res
  } $errcode

  do_test 3.$tn.2 { ota step } $errcode

  do_test 3.$tn.3 { 
    list [catch { ota close } msg] $msg
  } [list 1 "$errcode - $errmsg"]

  do_test 3.$tn.4 { dbcksum db main } $cksum
}


finish_test

Added ext/ota/ota2.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
# 2014 August 30
#
# 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.
#
#***********************************************************************
#

set testdir [file join [file dirname $argv0] .. .. test]
source $testdir/tester.tcl
set ::testprefix ota2


do_execsql_test 1.0 {
  PRAGMA ota_mode = 1;
  PRAGMA journal_mode = wal;
  CREATE TABLE t1(a, b);
  BEGIN;
    INSERT INTO t1 VALUES(1, 2);
} {wal}

do_test 1.1 {
  set state [sqlite3_transaction_save db]
  db close
  file exists test.db-wal
} {1}

do_test 1.2 {
  sqlite3 db test.db
  db eval {SELECT * FROM t1}
} {}

do_test 1.3 {
  execsql {BEGIN IMMEDIATE}
  sqlite3_transaction_restore db $::state
  db eval {SELECT * FROM t1}
} {1 2}

do_test 1.4 {
  execsql {
    INSERT INTO t1 VALUES(3, 4);
    COMMIT;
    SELECT * FROM t1;
  }
} {1 2 3 4}

do_test 1.5 {
  db close
  file exists test.db-wal
} {0}

do_test 1.5 {
  sqlite3 db test.db
  db eval {SELECT * FROM t1}
} {1 2 3 4}

finish_test

Added ext/ota/sqlite3ota.c.























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
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
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
/*
** 2014 August 30
**
** 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.
**
*************************************************************************
*/

#include <assert.h>
#include <string.h>

#include "sqlite3.h"
#include "sqlite3ota.h"


/*
** The ota_state table is used to save the state of a partially applied
** update so that it can be resumed later. The table contains at most a
** single row:
**
**   "wal_state" -> Blob to use with sqlite3_transaction_restore().
**
**   "tbl"       -> Table currently being written (target database names).
**
**   "idx"       -> Index currently being written (target database names).
**                  Or, if the main table is being written, a NULL value.
**
**   "row"       -> Last rowid processed from ota database table (i.e. data_%).
**
**   "progress"  -> total number of key/value b-tree operations performed
**                  so far as part of this ota update.
*/
#define OTA_CREATE_STATE "CREATE TABLE IF NOT EXISTS ota_state"        \
                             "(wal_state, tbl, idx, row, progress)"

typedef struct OtaTblIter OtaTblIter;
typedef struct OtaIdxIter OtaIdxIter;

/*
** Iterator used to iterate through all data tables in the OTA. As follows:
**
**   OtaTblIter iter;
**   for(rc=tblIterFirst(db, &iter); 
**       rc==SQLITE_OK && iter.zTarget; 
**       rc=tblIterNext(&iter)
**   ){
**   }
*/
struct OtaTblIter {
  sqlite3_stmt *pTblIter;         /* Iterate through tables */
  int iEntry;                     /* Index of current entry (from 1) */

  /* Output varibles. zTarget==0 implies EOF. */
  const char *zTarget;            /* Name of target table */
  const char *zSource;            /* Name of source table */

  /* Useful things populated by a call to tblIterPrepareAll() */
  int nCol;                       /* Number of columns in this table */
  char **azCol;                   /* Array of quoted column names */
  sqlite3_stmt *pSelect;          /* PK b-tree SELECT statement */
  sqlite3_stmt *pInsert;          /* PK b-tree INSERT statement */
};

/*
** API is:
**
**     idxIterFirst()
**     idxIterNext()
**     idxIterFinalize()
**     idxIterPrepareAll()
*/
struct OtaIdxIter {
  sqlite3_stmt *pIdxIter;         /* Iterate through indexes */
  int iEntry;                     /* Index of current entry (from 1) */

  /* Output varibles. zTarget==0 implies EOF. */
  const char *zIndex;             /* Name of index */

  int nCol;                       /* Number of columns in index */
  int *aiCol;                     /* Array of column indexes */
  sqlite3_stmt *pWriter;          /* Index writer */
  sqlite3_stmt *pSelect;          /* Select to read values in index order */
};


struct sqlite3ota {
  sqlite3 *dbDest;                /* Target db */
  sqlite3 *dbOta;                 /* Ota db */

  int rc;                         /* Value returned by last ota_step() call */
  char *zErrmsg;                  /* Error message if rc!=SQLITE_OK */

  OtaTblIter tbliter;             /* Used to iterate through tables */
  OtaIdxIter idxiter;             /* Used to iterate through indexes */
};

static int prepareAndCollectError(
  sqlite3 *db, 
  const char *zSql, 
  sqlite3_stmt **ppStmt,
  char **pzErrmsg
){
  int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0);
  if( rc!=SQLITE_OK ){
    *pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
    *ppStmt = 0;
  }
  return rc;
}

/*
** Unless it is NULL, argument zSql points to a buffer allocated using
** sqlite3_malloc containing an SQL statement. This function prepares the SQL
** statement against database db and frees the buffer. If statement 
** compilation is successful, *ppStmt is set to point to the new statement 
** handle and SQLITE_OK is returned. 
**
** Otherwise, if an error occurs, *ppStmt is set to NULL and an error code
** returned. In this case, *pzErrmsg may also be set to point to an error
** message. It is the responsibility of the caller to free this error message
** buffer using sqlite3_free().
**
** If argument zSql is NULL, this function assumes that an OOM has occurred.
** In this case SQLITE_NOMEM is returned and *ppStmt set to NULL.
*/
static int prepareFreeAndCollectError(
  sqlite3 *db, 
  char *zSql, 
  sqlite3_stmt **ppStmt,
  char **pzErrmsg
){
  int rc;
  assert( *pzErrmsg==0 );
  if( zSql==0 ){
    rc = SQLITE_NOMEM;
    *ppStmt = 0;
  }else{
    rc = prepareAndCollectError(db, zSql, ppStmt, pzErrmsg);
    sqlite3_free(zSql);
  }
  return rc;
}

static char *quoteSqlName(const char *zName){
  int nName = strlen(zName);
  char *zRet = sqlite3_malloc(nName * 2 + 2 + 1);
  if( zRet ){
    int i;
    char *p = zRet;
    *p++ = '"';
    for(i=0; i<nName; i++){
      if( zName[i]=='"' ) *p++ = '"';
      *p++ = zName[i];
    }
    *p++ = '"';
    *p++ = '\0';
  }
  return zRet;
}

static int tblIterPrepareAll(sqlite3ota *p){
  OtaTblIter *pIter = &p->tbliter;
  int rc = SQLITE_OK;
  char *zCol = 0;
  char *zBindings = 0;
  char *zSql;
  sqlite3_stmt *pPragma = 0;
  int i;
  int bSeenPk = 0;                /* Set to true once PK column seen */

  /* Allocate and populate the azCol[] array */
  zSql = sqlite3_mprintf("PRAGMA main.table_info(%Q)", pIter->zTarget);
  rc = prepareFreeAndCollectError(p->dbDest, zSql, &pPragma, &p->zErrmsg);
  pIter->nCol = 0;
  if( rc==SQLITE_OK ){
    while( SQLITE_ROW==sqlite3_step(pPragma) ){
      const char *zName = (const char*)sqlite3_column_text(pPragma, 1);
      if( (pIter->nCol % 4)==0 ){
        int nByte = sizeof(char*) * (pIter->nCol+4);
        char **azNew = (char**)sqlite3_realloc(pIter->azCol, nByte);
        if( azNew==0 ){
          rc = SQLITE_NOMEM;
          break;
        }
        pIter->azCol = azNew;
      }
      pIter->azCol[pIter->nCol] = quoteSqlName(zName);
      if( pIter->azCol[pIter->nCol]==0 ){
        rc = SQLITE_NOMEM;
        break;
      }
      pIter->nCol++;
      if( sqlite3_column_int(pPragma, 5) ) bSeenPk = 1;
    }
    if( rc==SQLITE_OK ){
      rc = sqlite3_finalize(pPragma);
    }else{
      sqlite3_finalize(pPragma);
    }
  }

  /* If the table has no PRIMARY KEY, throw an exception. */
  if( bSeenPk==0 ){
    p->zErrmsg = sqlite3_mprintf("table %s has no PRIMARY KEY", pIter->zTarget);
    rc = SQLITE_ERROR;
  }

  /* Populate the zCol variable */
  for(i=0; rc==SQLITE_OK && i<pIter->nCol; i++){
    zCol = sqlite3_mprintf("%z%s%s", zCol, (i==0?"":", "), pIter->azCol[i]);
    if( zCol==0 ){
      rc = SQLITE_NOMEM;
    }
  }

  /* Allocate and populate zBindings */
  if( rc==SQLITE_OK ){
    zBindings = (char*)sqlite3_malloc(pIter->nCol * 2);
    if( zBindings==0 ){
      rc = SQLITE_NOMEM;
    }else{
      int i;
      for(i=0; i<pIter->nCol; i++){
        zBindings[i*2] = '?';
        zBindings[i*2+1] = ',';
      }
      zBindings[pIter->nCol*2-1] = '\0';
    }
  }

  /* Create OtaTblIter.pSelect */
  if( rc==SQLITE_OK ){
    zSql = sqlite3_mprintf("SELECT rowid, %s FROM %Q", zCol, pIter->zSource);
    rc = prepareFreeAndCollectError(p->dbOta,zSql,&pIter->pSelect, &p->zErrmsg);
  }

  /* Create OtaTblIter.pInsert */
  if( rc==SQLITE_OK ){
    zSql = sqlite3_mprintf("INSERT INTO %Q(%s) VALUES(%s)", 
        pIter->zTarget, zCol, zBindings
    );
    rc = prepareFreeAndCollectError(p->dbDest,zSql,&pIter->pInsert,&p->zErrmsg);
  }

  sqlite3_free(zCol);
  sqlite3_free(zBindings);
  return rc;
}

static void tblIterFreeAll(OtaTblIter *pIter){
  int i;

  sqlite3_finalize(pIter->pSelect);
  sqlite3_finalize(pIter->pInsert);
  for(i=0; i<pIter->nCol; i++) sqlite3_free(pIter->azCol[i]);
  sqlite3_free(pIter->azCol);
  pIter->azCol = 0;
  pIter->pSelect = 0;
  pIter->pInsert = 0;
  pIter->nCol = 0;
}

static int tblIterNext(OtaTblIter *pIter){
  int rc;

  tblIterFreeAll(pIter);
  assert( pIter->pTblIter );
  rc = sqlite3_step(pIter->pTblIter);
  if( rc==SQLITE_ROW ){
    pIter->zSource = (const char*)sqlite3_column_text(pIter->pTblIter, 0);
    pIter->zTarget = &pIter->zSource[5]; assert( 5==strlen("data_") );
    pIter->iEntry++;
  }else{
    pIter->zSource = 0;
    pIter->zTarget = 0;
  }

  if( rc==SQLITE_ROW || rc==SQLITE_DONE ) rc = SQLITE_OK;
  return rc;
}

static int tblIterFirst(sqlite3 *db, OtaTblIter *pIter){
  int rc;                         /* return code */
  memset(pIter, 0, sizeof(OtaTblIter));
  rc = sqlite3_prepare_v2(db, 
      "SELECT name FROM sqlite_master "
      "WHERE type='table' AND name LIKE 'data_%'", -1, &pIter->pTblIter, 0
  );
  if( rc==SQLITE_OK ){
    rc = tblIterNext(pIter);
  }
  return rc;
}


static void tblIterFinalize(OtaTblIter *pIter){
  tblIterFreeAll(pIter);
  sqlite3_finalize(pIter->pTblIter);
  memset(pIter, 0, sizeof(OtaTblIter));
}

static void idxIterFreeAll(OtaIdxIter *pIter){
  sqlite3_finalize(pIter->pWriter);
  sqlite3_finalize(pIter->pSelect);
  pIter->pWriter = 0;
  pIter->pSelect = 0;
  pIter->aiCol = 0;
  pIter->nCol = 0;
}

static int idxIterPrepareAll(sqlite3ota *p){
  int rc;
  int i;                          /* Iterator variable */
  char *zSql = 0;
  char *zCols = 0;                /* Columns list */
  OtaIdxIter *pIter = &p->idxiter;

  /* Prepare the writer statement to write (insert) entries into the index. */
  rc = sqlite3_index_writer(
      p->dbDest, 0, pIter->zIndex, &pIter->pWriter, &pIter->aiCol, &pIter->nCol
  );

  /* Prepare a SELECT statement to read values from the source table in 
  ** the same order as they are stored in the current index. The statement 
  ** is:
  **
  **     SELECT rowid, <cols> FROM data_<tbl> ORDER BY <cols>
  */
  for(i=0; rc==SQLITE_OK && i<pIter->nCol; i++){
    const char *zQuoted = p->tbliter.azCol[ pIter->aiCol[i] ];
    zCols = sqlite3_mprintf("%z%s%s", zCols, zCols?", ":"", zQuoted);
    if( !zCols ){
      rc = SQLITE_NOMEM;
    }
  }
  if( rc==SQLITE_OK ){
    const char *zFmt = "SELECT rowid, %s FROM %Q ORDER BY %s";
    zSql = sqlite3_mprintf(zFmt, zCols, p->tbliter.zSource, zCols);
    if( zSql ){
      sqlite3_stmt **pp = &p->idxiter.pSelect;
      rc = prepareFreeAndCollectError(p->dbOta, zSql, pp, &p->zErrmsg);
    }else{
      rc = SQLITE_NOMEM;
    }
  }

  sqlite3_free(zCols);
  return rc;
}

static int idxIterNext(OtaIdxIter *pIter){
  int rc;

  idxIterFreeAll(pIter);
  assert( pIter->pIdxIter );
  rc = sqlite3_step(pIter->pIdxIter);
  if( rc==SQLITE_ROW ){
    pIter->zIndex = (const char*)sqlite3_column_text(pIter->pIdxIter, 0);
    pIter->iEntry++;
  }else{
    pIter->zIndex = 0;
    rc = sqlite3_finalize(pIter->pIdxIter);
    pIter->pIdxIter = 0;
  }

  if( rc==SQLITE_ROW ) rc = SQLITE_OK;
  return rc;
}

static int idxIterFirst(sqlite3 *db, const char *zTable, OtaIdxIter *pIter){
  int rc;                         /* return code */
  memset(pIter, 0, sizeof(OtaIdxIter));
  rc = sqlite3_prepare_v2(db, 
      "SELECT name FROM sqlite_master "
      "WHERE type='index' AND tbl_name = ?", -1, &pIter->pIdxIter, 0
  );
  if( rc==SQLITE_OK ){
    rc = sqlite3_bind_text(pIter->pIdxIter, 1, zTable, -1, SQLITE_TRANSIENT);
  }
  if( rc==SQLITE_OK ){
    rc = idxIterNext(pIter);
  }
  return rc;
}

static void idxIterFinalize(OtaIdxIter *pIter){
  idxIterFreeAll(pIter);
  sqlite3_finalize(pIter->pIdxIter);
  memset(pIter, 0, sizeof(OtaIdxIter));
}

/*
** Call sqlite3_reset() on the SQL statement passed as the second argument.
** If it returns anything other than SQLITE_OK, store the error code and
** error message in the OTA handle.
*/
static void otaResetStatement(sqlite3ota *p, sqlite3_stmt *pStmt){
  assert( p->rc==SQLITE_OK );
  assert( p->zErrmsg==0 );
  p->rc = sqlite3_reset(pStmt);
  if( p->rc!=SQLITE_OK ){
    sqlite3 *db = sqlite3_db_handle(pStmt);
    p->zErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
  }
}

/* 
** Check that all SQL statements required to process the current 
** table and index have been prepared. If not, prepare them. If
** an error occurs, store the error code and message in the OTA
** handle before returning.
*/
static int otaPrepareAll(sqlite3ota *p){
  assert( p->rc==SQLITE_OK );
  assert( p->zErrmsg==0 );
  assert( p->tbliter.zTarget );

  if( p->tbliter.pSelect==0 ){
    p->rc = tblIterPrepareAll(p);
  }
  if( p->rc==SQLITE_OK && p->idxiter.zIndex && 0==p->idxiter.pSelect ){
    p->rc = idxIterPrepareAll(p);
  }
  return p->rc;
}

int sqlite3ota_step(sqlite3ota *p){
  if( p ){
    while( p && p->rc==SQLITE_OK && p->tbliter.zTarget ){
      sqlite3_stmt *pSelect;
      int i;

      otaPrepareAll(p);
      pSelect = (p->idxiter.zIndex ? p->idxiter.pSelect : p->tbliter.pSelect);

      /* Advance to the next input row. */
      if( p->rc==SQLITE_OK ){
        int rc = sqlite3_step(pSelect);
        if( rc!=SQLITE_ROW ){
          otaResetStatement(p, pSelect);

          /* Go to the next index. */
          if( p->rc==SQLITE_OK ){
            if( p->idxiter.zIndex ){
              p->rc = idxIterNext(&p->idxiter);
            }else{
              p->rc = idxIterFirst(p->dbDest, p->tbliter.zTarget, &p->idxiter);
            }
          }

          /* If there is no next index, go to the next table. */
          if( p->rc==SQLITE_OK && p->idxiter.zIndex==0 ){
            p->rc = tblIterNext(&p->tbliter);
          }
          continue;
        }
      }

      /* Update the target database PK table according to the row that 
      ** tbliter.pSelect currently points to. 
      **
      ** todo: For now, we assume all rows are INSERT commands - this will 
      ** change.  */
      if( p->rc==SQLITE_OK ){
        sqlite3_stmt *pInsert;
        int nCol;
        if( p->idxiter.zIndex ){
          pInsert = p->idxiter.pWriter;
          nCol = p->idxiter.nCol;
        }else{
          pInsert = p->tbliter.pInsert;
          nCol = p->tbliter.nCol;
        }

        for(i=0; i<nCol; i++){
          sqlite3_value *pVal = sqlite3_column_value(pSelect, i+1);
          sqlite3_bind_value(pInsert, i+1, pVal);
        }

        sqlite3_step(pInsert);
        otaResetStatement(p, pInsert);
      }
      
      break;
    }

    if( p->rc==SQLITE_OK && p->tbliter.zTarget==0 ) p->rc = SQLITE_DONE;
  }

  return (p ? p->rc : SQLITE_NOMEM);
}

static void otaOpenDatabase(sqlite3ota *p, sqlite3 **pDb, const char *zFile){
  if( p->rc==SQLITE_OK ){
    p->rc = sqlite3_open(zFile, pDb);
    if( p->rc ){
      const char *zErr = sqlite3_errmsg(*pDb);
      p->zErrmsg = sqlite3_mprintf("sqlite3_open(): %s", zErr);
    }
  }
}

static void otaSaveTransactionState(sqlite3ota *p){
  sqlite3_stmt *pStmt = 0;
  void *pWalState = 0;
  int nWalState = 0;
  int rc;

  const char *zInsert = 
    "INSERT INTO ota_state(wal_state, tbl, idx, row, progress)"
    "VALUES(:wal_state, :tbl, :idx, :row, :progress)";

  rc = sqlite3_transaction_save(p->dbDest, &pWalState, &nWalState);
  if( rc==SQLITE_OK ){
    rc = sqlite3_exec(p->dbOta, "DELETE FROM ota_state", 0, 0, 0);
  }
  if( rc==SQLITE_OK ){
    rc = prepareAndCollectError(p->dbOta, zInsert, &pStmt, &p->zErrmsg);
  }
  if( rc==SQLITE_OK ){
    sqlite3_stmt *pSelect;
    pSelect = (p->idxiter.zIndex ? p->idxiter.pSelect : p->tbliter.pSelect);
    sqlite3_bind_blob(pStmt, 1, pWalState, nWalState, SQLITE_STATIC);
    sqlite3_bind_text(pStmt, 2, p->tbliter.zTarget, -1, SQLITE_STATIC);
    if( p->idxiter.zIndex ){
      sqlite3_bind_text(pStmt, 3, p->idxiter.zIndex, -1, SQLITE_STATIC);
    }
    sqlite3_bind_int64(pStmt, 4, sqlite3_column_int64(pSelect, 0));
    sqlite3_step(pStmt);
    rc = sqlite3_finalize(pStmt);
    if( rc==SQLITE_OK ){
      rc = sqlite3_exec(p->dbOta, "COMMIT", 0, 0, 0);
    }
    if( rc!=SQLITE_OK ){
      p->zErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(p->dbOta));
    }
  }
  sqlite3_free(pWalState);
  assert( p->rc==SQLITE_OK );
  p->rc = rc;
}

static void otaLoadTransactionState(sqlite3ota *p){
  sqlite3_stmt *pStmt = 0;
  int rc;

  const char *zSelect = 
    "SELECT wal_state, tbl, idx, row, progress FROM ota_state";

  rc = prepareAndCollectError(p->dbOta, zSelect, &pStmt, &p->zErrmsg);
  if( rc==SQLITE_OK ){
    if( SQLITE_ROW==sqlite3_step(pStmt) ){
      const void *pWalState = 0;
      int nWalState = 0;
      const char *zTbl;
      const char *zIdx;
      sqlite3_int64 iRowid;

      pWalState = sqlite3_column_blob(pStmt, 0);
      nWalState = sqlite3_column_bytes(pStmt, 0);
      zTbl = (const char*)sqlite3_column_text(pStmt, 1);
      zIdx = (const char*)sqlite3_column_text(pStmt, 2);
      iRowid = sqlite3_column_int64(pStmt, 3);
      rc = sqlite3_transaction_restore(p->dbDest, pWalState, nWalState);

      while( rc==SQLITE_OK 
          && p->tbliter.zTarget 
          && sqlite3_stricmp(p->tbliter.zTarget, zTbl) 
      ){
        rc = tblIterNext(&p->tbliter);
      }
      if( rc==SQLITE_OK && !p->tbliter.zTarget ){
        rc = SQLITE_ERROR;
        p->zErrmsg = sqlite3_mprintf("ota_state mismatch error");
      }

      if( rc==SQLITE_OK && zIdx ){
        rc = idxIterFirst(p->dbDest, p->tbliter.zTarget, &p->idxiter);
        while( rc==SQLITE_OK 
            && p->idxiter.zIndex 
            && sqlite3_stricmp(p->idxiter.zIndex, zIdx) 
        ){
          rc = idxIterNext(&p->idxiter);
        }
        if( rc==SQLITE_OK && !p->idxiter.zIndex ){
          rc = SQLITE_ERROR;
          p->zErrmsg = sqlite3_mprintf("ota_state mismatch error");
        }
      }

      if( rc==SQLITE_OK ){
        rc = otaPrepareAll(p);
      }

      if( rc==SQLITE_OK ){
        sqlite3_stmt *pSelect;
        pSelect = (p->idxiter.zIndex ? p->idxiter.pSelect : p->tbliter.pSelect);
        while( sqlite3_column_int64(pSelect, 0)!=iRowid ){
          rc = sqlite3_step(pSelect);
          if( rc!=SQLITE_ROW ) break;
        }
        if( rc==SQLITE_ROW ){
          rc = SQLITE_OK;
        }else{
          rc = SQLITE_ERROR;
          p->zErrmsg = sqlite3_mprintf("ota_state mismatch error");
        }
      }
    }
    if( rc==SQLITE_OK ){
      rc = sqlite3_finalize(pStmt);
    }else{
      sqlite3_finalize(pStmt);
    }
  }
  p->rc = rc;
}


/*
** Open and return a new OTA handle. 
*/
sqlite3ota *sqlite3ota_open(const char *zTarget, const char *zOta){
  sqlite3ota *p;

  p = (sqlite3ota*)sqlite3_malloc(sizeof(sqlite3ota));
  if( p ){

    /* Open the target database */
    memset(p, 0, sizeof(sqlite3ota));
    otaOpenDatabase(p, &p->dbDest, zTarget);
    otaOpenDatabase(p, &p->dbOta, zOta);

    /* If it has not already been created, create the ota_state table */
    if( p->rc==SQLITE_OK ){
      p->rc = sqlite3_exec(p->dbOta, OTA_CREATE_STATE, 0, 0, &p->zErrmsg);
    }

    if( p->rc==SQLITE_OK ){
      const char *zScript = 
        "PRAGMA ota_mode=1;"
        "PRAGMA journal_mode=wal;"
        "BEGIN IMMEDIATE;"
      ;
      p->rc = sqlite3_exec(p->dbDest, zScript, 0, 0, &p->zErrmsg);
    }

    if( p->rc==SQLITE_OK ){
      const char *zScript = "BEGIN IMMEDIATE";
      p->rc = sqlite3_exec(p->dbOta, zScript, 0, 0, &p->zErrmsg);
    }

    /* Point the table iterator at the first table */
    if( p->rc==SQLITE_OK ){
      p->rc = tblIterFirst(p->dbOta, &p->tbliter);
    }

    if( p->rc==SQLITE_OK ){
      otaLoadTransactionState(p);
    }
  }

  return p;
}

static void otaCloseHandle(sqlite3 *db){
  int rc = sqlite3_close(db);
  assert( rc==SQLITE_OK );
}

int sqlite3ota_close(sqlite3ota *p, char **pzErrmsg){
  int rc;
  if( p ){

    /* If the update has not been fully applied, save the state in 
    ** the ota db. If successful, this call also commits the open 
    ** transaction on the ota db. */
    assert( p->rc!=SQLITE_ROW );
    if( p->rc==SQLITE_OK ){
      assert( p->zErrmsg==0 );
      otaSaveTransactionState(p);
    }

    /* Close all open statement handles. */
    tblIterFinalize(&p->tbliter);
    idxIterFinalize(&p->idxiter);

    /* If the ota update has been fully applied, commit the transaction
    ** on the target database. */
    if( p->rc==SQLITE_DONE ){
      rc = sqlite3_exec(p->dbDest, "COMMIT", 0, 0, &p->zErrmsg);
      if( rc!=SQLITE_OK ) p->rc = rc;
    }

    rc = p->rc;
    *pzErrmsg = p->zErrmsg;
    otaCloseHandle(p->dbDest);
    otaCloseHandle(p->dbOta);
    sqlite3_free(p);
  }else{
    rc = SQLITE_NOMEM;
    *pzErrmsg = 0;
  }
  return rc;
}


/**************************************************************************/

#ifdef SQLITE_TEST 

#include <tcl.h>

/* From main.c (apparently...) */
extern const char *sqlite3ErrName(int);

static int test_sqlite3ota_cmd(
  ClientData clientData,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  int ret = TCL_OK;
  sqlite3ota *pOta = (sqlite3ota*)clientData;
  const char *azMethod[] = { "step", "close", 0 };
  int iMethod;

  if( objc!=2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "METHOD");
    return TCL_ERROR;
  }
  if( Tcl_GetIndexFromObj(interp, objv[1], azMethod, "method", 0, &iMethod) ){
    return TCL_ERROR;
  }

  switch( iMethod ){
    case 0: /* step */ {
      int rc = sqlite3ota_step(pOta);
      Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
      break;
    }

    case 1: /* close */ {
      char *zErrmsg = 0;
      int rc;
      Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
      rc = sqlite3ota_close(pOta, &zErrmsg);
      if( rc==SQLITE_OK || rc==SQLITE_DONE ){
        Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
        assert( zErrmsg==0 );
      }else{
        Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
        if( zErrmsg ){
          Tcl_AppendResult(interp, " - ", zErrmsg, 0);
          sqlite3_free(zErrmsg);
        }
        ret = TCL_ERROR;
      }
      break;
    }

    default: /* seems unlikely */
      assert( !"cannot happen" );
      break;
  }

  return ret;
}

/*
** Tclcmd: sqlite3ota CMD <target-db> <ota-db>
*/
static int test_sqlite3ota(
  ClientData clientData,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  sqlite3ota *pOta = 0;
  const char *zCmd;
  const char *zTarget;
  const char *zOta;

  if( objc!=4 ){
    Tcl_WrongNumArgs(interp, 1, objv, "NAME TARGET-DB OTA-DB");
    return TCL_ERROR;
  }
  zCmd = Tcl_GetString(objv[1]);
  zTarget = Tcl_GetString(objv[2]);
  zOta = Tcl_GetString(objv[3]);

  pOta = sqlite3ota_open(zTarget, zOta);
  Tcl_CreateObjCommand(interp, zCmd, test_sqlite3ota_cmd, (ClientData)pOta, 0);
  Tcl_SetObjResult(interp, objv[1]);
  return TCL_OK;
}

int SqliteOta_Init(Tcl_Interp *interp){ 
  Tcl_CreateObjCommand(interp, "sqlite3ota", test_sqlite3ota, 0, 0);
  return TCL_OK;
}

#endif                  /* ifdef SQLITE_TEST */



Added ext/ota/sqlite3ota.h.









































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
/*
** 2014 August 30
**
** 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 contains the public interface for the OTA extension. 
*/

/*
** SUMMARY
**
** Writing a transaction containing a large number of operations on 
** b-tree indexes that are collectively larger than the available cache
** memory can be very inefficient. 
**
** The problem is that in order to update a b-tree, the leaf page (at least)
** containing the entry being inserted or deleted must be modified. If the
** working set of leaves is larger than the available cache memory, then a 
** single leaf that is modified more than once as part of the transaction 
** may be loaded from or written to the persistent media more than once. 
** Additionally, because the index updates are likely to be applied in
** random order, access to pages within the databse is also likely to be in 
** random order, which is itself quite inefficient.
**
** One way to improve the situation is to sort the operations on each index
** by index key before applying them to the b-tree. This leads to an IO
** pattern that resembles a single linear scan through the index b-tree,
** and all but guarantees each modified leaf page is loaded and stored 
** exactly once. SQLite uses this trick to improve the performance of
** CREATE INDEX commands. This extension allows it to be used to improve
** the performance of large transactions on existing databases.
**
** Additionally, this extension allows the work involved in writing the 
** large transaction to be broken down into sub-transactions performed 
** sequentially by separate processes. This is useful if the system cannot 
** guarantee that a single update process may run for long enough to apply 
** the entire update, for example because the update is running on a mobile
** device that is frequently rebooted. Even after the writer process has 
** committed one or more sub-transactions, other database clients continue
** to read from the original database snapshot. In other words, partially 
** applied transactions are not visible to other clients. 
**
** "OTA" stands for "Over The Air" update. As in a large database update
** transmitted via a wireless network to a mobile device. A transaction
** applied using this extension is hence refered to as an "OTA update".
**
**
** LIMITATIONS
**
** An "OTA update" transaction is subject to the following limitations:
**
**   * The transaction must consist of INSERT, UPDATE and DELETE operations
**     only.
**
**   * INSERT statements may not use any default values.
**
**   * UPDATE and DELETE statements must identify their target rows by
**     real PRIMARY KEY values - i.e. INTEGER PRIMARY KEY columns or 
**     by the PRIMARY KEY columns of WITHOUT ROWID tables.
**
**   * UPDATE statements may not modify real PRIMARY KEY columns.
**
**   * No triggers will be fired.
**
**   * No foreign key violations are detected or reported.
**
**   * No constraint handling mode except for "OR ROLLBACK" is supported.
**
**
** PREPARATION
**
** An "OTA update" is stored as a separate SQLite database. A database
** containing an OTA update is an "OTA database". For each table in the 
** target database to be updated, the OTA database should contain a table
** named "data_<target name>" containing the same set of columns as the
** target table, and one more - "ota_control". The data_% table should 
** have no PRIMARY KEY or UNIQUE constraints, but each column should have
** the same type as the corresponding column in the target database.
** The "ota_control" column should have no type at all. For example, if
** the target database contains:
**
**   CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT, c UNIQUE);
**
** Then the OTA database should contain:
**
**   CREATE TABLE data_t1(a INTEGER, b TEXT, c, ota_control);
**
** The order of the columns in the data_% table does not matter.
**
** For each row to INSERT into the target database as part of the OTA 
** update, the corresponding data_% table should contain a single record
** with the "ota_control" column set to contain integer value 0. The
** other columns should be set to the values that make up the new record 
** to insert.
**
** For each row to DELETE from the target database as part of the OTA 
** update, the corresponding data_% table should contain a single record
** with the "ota_control" column set to contain integer value 1. The
** real primary key values of the row to delete should be stored in the
** corresponding columns of the data_% table. The values stored in the
** other columns are not used.
**
** For each row to DELETE from the target database as part of the OTA 
** update, the corresponding data_% table should contain a single record
** with the "ota_control" column set to contain a value of type text.
** The real primary key values identifying the row to update should be 
** stored in the corresponding columns of the data_% table row, as should
** the new values of all columns being update. The text value in the 
** "ota_control" column must contain the same number of characters as
** there are column in the target database table, and must consist entirely
** of "x" and "." characters. For each column that is being updated,
** the corresponding character is set to "x". For those that remain as
** they are, the corresponding character of the ota_control value should
** be set to ".". For example, given the tables above, the update 
** statement:
**
**   UPDATE t1 SET c = 'usa' WHERE a = 4;
**
** is represented by the data_t1 row created by:
**
**   INSERT INTO data_t1(a, b, c, ota_control) VALUES(4, NULL, 'usa', '..x');
**
**
** USAGE
**
** The API declared below allows an application to apply an OTA update 
** stored on disk to an existing target database. Essentially, the 
** application:
**
**     1) Opens an OTA handle using the sqlite3ota_open() function.
**
**     2) Calls the sqlite3ota_step() function one or more times on
**        the new handle. Each call to sqlite3ota_step() performs a single
**        b-tree operation, so thousands of calls may be required to apply 
**        a complete update.
**
**     3) Calls sqlite3ota_close() to close the OTA update handle. If
**        sqlite3ota_step() has been called enough times to completely
**        apply the update to the target database, then it is committed
**        and made visible to other database clients at this point. 
**        Otherwise, the state of the OTA update application is saved
**        in the OTA database for later resumption.
**
** See comments below for more detail on APIs.
**
** If an update is only partially applied to the target database by the
** time sqlite3ota_close() is called, various state information is saved 
** within the OTA database. This allows subsequent processes to automatically
** resume the OTA update from where it left off.
**
** To remove all OTA extension state information, returning an OTA database 
** to its original contents, it is sufficient to drop all tables that begin
** with the prefix "ota_"
*/

#ifndef _SQLITE3OTA_H
#define _SQLITE3OTA_H

typedef struct sqlite3ota sqlite3ota;

/*
** Open an OTA handle.
**
** Argument zTarget is the path to the target database. Argument zOta is
** the path to the OTA database. Each call to this function must be matched
** by a call to sqlite3ota_close().
*/
sqlite3ota *sqlite3ota_open(const char *zTarget, const char *zOta);

/*
** Do some work towards applying the OTA update to the target db. 
**
** Return SQLITE_DONE if the update has been completely applied, or 
** SQLITE_OK if no error occurs but there remains work to do to apply
** the OTA update. If an error does occur, some other error code is 
** returned. 
**
** Once a call to sqlite3ota_step() has returned a value other than
** SQLITE_OK, all subsequent calls on the same OTA handle are no-ops
** that immediately return the same value.
*/
int sqlite3ota_step(sqlite3ota *pOta);

/*
** Close an OTA handle. 
**
** If the OTA update has been completely applied, commit it to the target 
** database. Otherwise, assuming no error has occurred, save the current 
** state of the OTA update appliation to the OTA database.
**
** If an error has already occurred as part of an sqlite3ota_step()
** or sqlite3ota_open() call, or if one occurs within this function, an
** SQLite error code is returned. Additionally, *pzErrmsg may be set to
** point to a buffer containing a utf-8 formatted English language error
** message. It is the responsibility of the caller to eventually free any 
** such buffer using sqlite3_free().
**
** Otherwise, if no error occurs, this function returns SQLITE_OK if the
** update has been partially applied, or SQLITE_DONE if it has been 
** completely applied.
*/
int sqlite3ota_close(sqlite3ota *pOta, char **pzErrmsg);

#endif /* _SQLITE3OTA_H */

Changes to main.mk.

327
328
329
330
331
332
333
334

335
336
337
338
339
340
341
  $(TOP)/src/where.c \
  parse.c \
  $(TOP)/ext/fts3/fts3.c \
  $(TOP)/ext/fts3/fts3_aux.c \
  $(TOP)/ext/fts3/fts3_expr.c \
  $(TOP)/ext/fts3/fts3_tokenizer.c \
  $(TOP)/ext/fts3/fts3_write.c \
  $(TOP)/ext/async/sqlite3async.c


# Header files used by all library source files.
#
HDR = \
   $(TOP)/src/btree.h \
   $(TOP)/src/btreeInt.h \
   $(TOP)/src/hash.h \







|
>







327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
  $(TOP)/src/where.c \
  parse.c \
  $(TOP)/ext/fts3/fts3.c \
  $(TOP)/ext/fts3/fts3_aux.c \
  $(TOP)/ext/fts3/fts3_expr.c \
  $(TOP)/ext/fts3/fts3_tokenizer.c \
  $(TOP)/ext/fts3/fts3_write.c \
  $(TOP)/ext/async/sqlite3async.c \
  $(TOP)/ext/ota/sqlite3ota.c

# Header files used by all library source files.
#
HDR = \
   $(TOP)/src/btree.h \
   $(TOP)/src/btreeInt.h \
   $(TOP)/src/hash.h \

Changes to src/btree.c.

147
148
149
150
151
152
153

154
155
156
157
158
159
160
161
....
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
....
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
  Pgno iTab = 0;
  BtLock *pLock;

  /* If this database is not shareable, or if the client is reading
  ** and has the read-uncommitted flag set, then no lock is required. 
  ** Return true immediately.
  */

  if( (pBtree->sharable==0)
   || (eLockType==READ_LOCK && (pBtree->db->flags & SQLITE_ReadUncommitted))
  ){
    return 1;
  }

  /* If the client is reading  or writing an index and the schema is
  ** not loaded, then it is too difficult to actually check to see if
................................................................................
  }
#endif
  *ppBtree = p;

btree_open_out:
  if( rc!=SQLITE_OK ){
    if( pBt && pBt->pPager ){
      sqlite3PagerClose(pBt->pPager);
    }
    sqlite3_free(pBt);
    sqlite3_free(p);
    *ppBtree = 0;
  }else{
    /* If the B-Tree was successfully opened, set the pager-cache size to the
    ** default value. Except, when opening on an existing shared pager-cache,
................................................................................
  if( !p->sharable || removeFromSharingList(pBt) ){
    /* The pBt is no longer on the sharing list, so we can access
    ** it without having to hold the mutex.
    **
    ** Clean out and delete the BtShared object.
    */
    assert( !pBt->pCursor );
    sqlite3PagerClose(pBt->pPager);
    if( pBt->xFreeSchema && pBt->pSchema ){
      pBt->xFreeSchema(pBt->pSchema);
    }
    sqlite3DbFree(0, pBt->pSchema);
    freeTempSpace(pBt);
    sqlite3_free(pBt);
  }







>
|







 







|







 







|







147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
....
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
....
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
  Pgno iTab = 0;
  BtLock *pLock;

  /* If this database is not shareable, or if the client is reading
  ** and has the read-uncommitted flag set, then no lock is required. 
  ** Return true immediately.
  */
  if( (pBtree->db->flags & SQLITE_OtaMode)
   || (pBtree->sharable==0)
   || (eLockType==READ_LOCK && (pBtree->db->flags & SQLITE_ReadUncommitted))
  ){
    return 1;
  }

  /* If the client is reading  or writing an index and the schema is
  ** not loaded, then it is too difficult to actually check to see if
................................................................................
  }
#endif
  *ppBtree = p;

btree_open_out:
  if( rc!=SQLITE_OK ){
    if( pBt && pBt->pPager ){
      sqlite3PagerClose(pBt->pPager, 0);
    }
    sqlite3_free(pBt);
    sqlite3_free(p);
    *ppBtree = 0;
  }else{
    /* If the B-Tree was successfully opened, set the pager-cache size to the
    ** default value. Except, when opening on an existing shared pager-cache,
................................................................................
  if( !p->sharable || removeFromSharingList(pBt) ){
    /* The pBt is no longer on the sharing list, so we can access
    ** it without having to hold the mutex.
    **
    ** Clean out and delete the BtShared object.
    */
    assert( !pBt->pCursor );
    sqlite3PagerClose(pBt->pPager, (p->db->flags & SQLITE_OtaMode)!=0);
    if( pBt->xFreeSchema && pBt->pSchema ){
      pBt->xFreeSchema(pBt->pSchema);
    }
    sqlite3DbFree(0, pBt->pSchema);
    freeTempSpace(pBt);
    sqlite3_free(pBt);
  }

Changes to src/insert.c.

1360
1361
1362
1363
1364
1365
1366




1367
1368
1369
1370
1371
1372
1373
....
1552
1553
1554
1555
1556
1557
1558









1559
1560
1561
1562
1563
1564
1565
  ** WITHOUT ROWID table.
  */
  for(ix=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, ix++){
    int regIdx;          /* Range of registers hold conent for pIdx */
    int regR;            /* Range of registers holding conflicting PK */
    int iThisCur;        /* Cursor for this UNIQUE index */
    int addrUniqueOk;    /* Jump here if the UNIQUE constraint is satisfied */





    if( aRegIdx[ix]==0 ) continue;  /* Skip indices that do not change */
    if( bAffinityDone==0 ){
      sqlite3TableAffinity(v, pTab, regNewData+1);
      bAffinityDone = 1;
    }
    iThisCur = iIdxCur+ix;
................................................................................
  u8 bAffinityDone = 0; /* True if OP_Affinity has been run already */

  v = sqlite3GetVdbe(pParse);
  assert( v!=0 );
  assert( pTab->pSelect==0 );  /* This table is not a VIEW */
  for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
    if( aRegIdx[i]==0 ) continue;









    bAffinityDone = 1;
    if( pIdx->pPartIdxWhere ){
      sqlite3VdbeAddOp2(v, OP_IsNull, aRegIdx[i], sqlite3VdbeCurrentAddr(v)+2);
      VdbeCoverage(v);
    }
    sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdxCur+i, aRegIdx[i]);
    pik_flags = 0;







>
>
>
>







 







>
>
>
>
>
>
>
>
>







1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
....
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
  ** WITHOUT ROWID table.
  */
  for(ix=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, ix++){
    int regIdx;          /* Range of registers hold conent for pIdx */
    int regR;            /* Range of registers holding conflicting PK */
    int iThisCur;        /* Cursor for this UNIQUE index */
    int addrUniqueOk;    /* Jump here if the UNIQUE constraint is satisfied */

    /* If the "ota_mode" flag is set, ignore all indexes except the PK 
    ** index of WITHOUT ROWID tables.  */
    if( (db->flags & SQLITE_OtaMode) && pIdx!=pPk) continue;

    if( aRegIdx[ix]==0 ) continue;  /* Skip indices that do not change */
    if( bAffinityDone==0 ){
      sqlite3TableAffinity(v, pTab, regNewData+1);
      bAffinityDone = 1;
    }
    iThisCur = iIdxCur+ix;
................................................................................
  u8 bAffinityDone = 0; /* True if OP_Affinity has been run already */

  v = sqlite3GetVdbe(pParse);
  assert( v!=0 );
  assert( pTab->pSelect==0 );  /* This table is not a VIEW */
  for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
    if( aRegIdx[i]==0 ) continue;

    /* If the "ota_mode" flag is set, ignore all indexes except the PK 
    ** index of WITHOUT ROWID tables.  */
    if( (pParse->db->flags & SQLITE_OtaMode) 
     && (pTab->iPKey>=0 || pIdx->idxType!=SQLITE_IDXTYPE_PRIMARYKEY) 
    ){
      continue;
    }

    bAffinityDone = 1;
    if( pIdx->pPartIdxWhere ){
      sqlite3VdbeAddOp2(v, OP_IsNull, aRegIdx[i], sqlite3VdbeCurrentAddr(v)+2);
      VdbeCoverage(v);
    }
    sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdxCur+i, aRegIdx[i]);
    pik_flags = 0;

Changes to src/main.c.

3466
3467
3468
3469
3470
3471
3472












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



















>
>
>
>
>
>
>
>
>
>
>
>
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
** 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);
}


Changes to src/pager.c.

3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959

3960

3961
3962
3963
3964
3965
3966
3967
....
7210
7211
7212
7213
7214
7215
7216



































7217
7218
7219
7220
7221
7222
7223
** result in a coredump.
**
** This function always succeeds. If a transaction is active an attempt
** is made to roll it back. If an error occurs during the rollback 
** a hot journal may be left in the filesystem but no error is returned
** to the caller.
*/
int sqlite3PagerClose(Pager *pPager){
  u8 *pTmp = (u8 *)pPager->pTmpSpace;

  assert( assert_pager_state(pPager) );
  disable_simulated_io_errors();
  sqlite3BeginBenignMalloc();
  pagerFreeMapHdrs(pPager);
  /* pPager->errCode = 0; */
  pPager->exclusiveMode = 0;
#ifndef SQLITE_OMIT_WAL

  sqlite3WalClose(pPager->pWal, pPager->ckptSyncFlags, pPager->pageSize, pTmp);

  pPager->pWal = 0;
#endif
  pager_reset(pPager);
  if( MEMDB ){
    pager_unlock(pPager);
  }else{
    /* If it is open, sync the journal file before calling UnlockAndRollback.
................................................................................
    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
/*







|









>
|
>







 







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







3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
....
7212
7213
7214
7215
7216
7217
7218
7219
7220
7221
7222
7223
7224
7225
7226
7227
7228
7229
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
** result in a coredump.
**
** This function always succeeds. If a transaction is active an attempt
** is made to roll it back. If an error occurs during the rollback 
** a hot journal may be left in the filesystem but no error is returned
** to the caller.
*/
int sqlite3PagerClose(Pager *pPager, int bOtaMode){
  u8 *pTmp = (u8 *)pPager->pTmpSpace;

  assert( assert_pager_state(pPager) );
  disable_simulated_io_errors();
  sqlite3BeginBenignMalloc();
  pagerFreeMapHdrs(pPager);
  /* pPager->errCode = 0; */
  pPager->exclusiveMode = 0;
#ifndef SQLITE_OMIT_WAL
  sqlite3WalClose(
      pPager->pWal, pPager->ckptSyncFlags, pPager->pageSize, (bOtaMode?0:pTmp)
  );
  pPager->pWal = 0;
#endif
  pager_reset(pPager);
  if( MEMDB ){
    pager_unlock(pPager);
  }else{
    /* If it is open, sync the journal file before calling UnlockAndRollback.
................................................................................
    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
/*

Changes to src/pager.h.

108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
...
203
204
205
206
207
208
209



210
  Pager **ppPager,
  const char*,
  int,
  int,
  int,
  void(*)(DbPage*)
);
int sqlite3PagerClose(Pager *pPager);
int sqlite3PagerReadFileheader(Pager*, int, unsigned char*);

/* Functions used to configure a Pager object. */
void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *);
int sqlite3PagerSetPagesize(Pager*, u32*, int);
int sqlite3PagerMaxPageCount(Pager*, int);
void sqlite3PagerSetCachesize(Pager*, int);
................................................................................
  void disable_simulated_io_errors(void);
  void enable_simulated_io_errors(void);
#else
# define disable_simulated_io_errors()
# define enable_simulated_io_errors()
#endif




#endif /* _PAGER_H_ */







|







 







>
>
>

108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
...
203
204
205
206
207
208
209
210
211
212
213
  Pager **ppPager,
  const char*,
  int,
  int,
  int,
  void(*)(DbPage*)
);
int sqlite3PagerClose(Pager *pPager, int);
int sqlite3PagerReadFileheader(Pager*, int, unsigned char*);

/* Functions used to configure a Pager object. */
void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *);
int sqlite3PagerSetPagesize(Pager*, u32*, int);
int sqlite3PagerMaxPageCount(Pager*, int);
void sqlite3PagerSetCachesize(Pager*, int);
................................................................................
  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);

#endif /* _PAGER_H_ */

Changes to src/pragma.c.

304
305
306
307
308
309
310






311
312
313
314
315
316
317
....
1462
1463
1464
1465
1466
1467
1468



1469
1470
1471

1472
1473
1474
1475
1476
1477
1478
    /* ePragTyp:  */ PragTyp_PAGE_COUNT,
    /* ePragFlag: */ PragFlag_NeedSchema,
    /* iArg:      */ 0 },
  { /* zName:     */ "mmap_size",
    /* ePragTyp:  */ PragTyp_MMAP_SIZE,
    /* ePragFlag: */ 0,
    /* iArg:      */ 0 },






  { /* zName:     */ "page_count",
    /* ePragTyp:  */ PragTyp_PAGE_COUNT,
    /* ePragFlag: */ PragFlag_NeedSchema,
    /* iArg:      */ 0 },
  { /* zName:     */ "page_size",
    /* ePragTyp:  */ PragTyp_PAGE_SIZE,
    /* ePragFlag: */ 0,
................................................................................
        }else{
          sqlite3VdbeAddOp2(v, OP_Null, 0, 5);
        }
        if( (pCol->colFlags & COLFLAG_PRIMKEY)==0 ){
          k = 0;
        }else if( pPk==0 ){
          k = 1;



        }else{
          for(k=1; ALWAYS(k<=pTab->nCol) && pPk->aiColumn[k-1]!=i; k++){}
        }

        sqlite3VdbeAddOp2(v, OP_Integer, k, 6);
        sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6);
      }
    }
  }
  break;








>
>
>
>
>
>







 







>
>
>



>







304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
....
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
    /* ePragTyp:  */ PragTyp_PAGE_COUNT,
    /* ePragFlag: */ PragFlag_NeedSchema,
    /* iArg:      */ 0 },
  { /* zName:     */ "mmap_size",
    /* ePragTyp:  */ PragTyp_MMAP_SIZE,
    /* ePragFlag: */ 0,
    /* iArg:      */ 0 },
#endif
  { /* zName:     */ "ota_mode",
    /* ePragTyp:  */ PragTyp_FLAG,
    /* ePragFlag: */ 0,
    /* iArg:      */ SQLITE_OtaMode },
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
  { /* zName:     */ "page_count",
    /* ePragTyp:  */ PragTyp_PAGE_COUNT,
    /* ePragFlag: */ PragFlag_NeedSchema,
    /* iArg:      */ 0 },
  { /* zName:     */ "page_size",
    /* ePragTyp:  */ PragTyp_PAGE_SIZE,
    /* ePragFlag: */ 0,
................................................................................
        }else{
          sqlite3VdbeAddOp2(v, OP_Null, 0, 5);
        }
        if( (pCol->colFlags & COLFLAG_PRIMKEY)==0 ){
          k = 0;
        }else if( pPk==0 ){
          k = 1;
        }else{
          if( (db->flags & SQLITE_OtaMode) && HasRowid(pTab) ){
            k = 0;
          }else{
            for(k=1; ALWAYS(k<=pTab->nCol) && pPk->aiColumn[k-1]!=i; k++){}
          }
        }
        sqlite3VdbeAddOp2(v, OP_Integer, k, 6);
        sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6);
      }
    }
  }
  break;

Changes to src/prepare.c.

791
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. */ 







<







791
792
793
794
795
796
797

798
799
800
801
802
803
804
  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.

7359
7360
7361
7362
7363
7364
7365






















































7366


















7367
7368
7369
7370
7371
7372
7373
7374
7375
7376
7377
7378
7379
*/
#define SQLITE_ROLLBACK 1
/* #define SQLITE_IGNORE 2 // Also used by sqlite3_authorizer() callback */
#define SQLITE_FAIL     3
/* #define SQLITE_ABORT 4  // Also an error code */
#define SQLITE_REPLACE  5











































































/*
** 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_ */







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

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













7359
7360
7361
7362
7363
7364
7365
7366
7367
7368
7369
7370
7371
7372
7373
7374
7375
7376
7377
7378
7379
7380
7381
7382
7383
7384
7385
7386
7387
7388
7389
7390
7391
7392
7393
7394
7395
7396
7397
7398
7399
7400
7401
7402
7403
7404
7405
7406
7407
7408
7409
7410
7411
7412
7413
7414
7415
7416
7417
7418
7419
7420
7421
7422
7423
7424
7425
7426
7427
7428
7429
7430
7431
7432
7433
7434
7435
7436
7437
7438
7439
7440
7441
7442
7443
7444
7445
7446
7447
7448
7449
7450
7451
*/
#define SQLITE_ROLLBACK 1
/* #define SQLITE_IGNORE 2 // Also used by sqlite3_authorizer() callback */
#define SQLITE_FAIL     3
/* #define SQLITE_ABORT 4  // Also an error code */
#define SQLITE_REPLACE  5

/*
** Allocate a statement handle that may be used to write directly to an
** index b-tree. This allows the user to create a corrupt database. Once
** the statement handle is allocated, it may be used with the same APIs
** as any statement handle created with sqlite3_prepare().
**
** The statement writes to the index specified by parameter zIndex, which
** must be in the "main" database. If argument bDelete is false, then each
** time the statement is sqlite3_step()ed, an entry is inserted into the
** b-tree index. If it is true, then an entry may be deleted (or may not, if 
** the specified key is not found) each time the statement is 
** sqlite3_step()ed.
**
** If statement compilation is successful, *ppStmt is set to point to the 
** new statement handle and SQLITE_OK is returned. Otherwise, if an error
** occurs, *ppStmt is set to NULL and an error code returned. An error
** message may be left in the database handle in this case.
**
** If statement compilation succeeds, output variable *pnCol is set to the
** total number of columns in the index, including the primary key columns
** at the end. Variable *paiCol is set to point to an array *pnCol entries 
** in size. Each entry is the table column index, numbered from zero from left 
** to right, of the corresponding index column. For example, if:
**
**       CREATE TABLE t1(a, b, c, d);
**       CREATE INDEX i1 ON t1(b, c);
**
** then *pnCol is 3 and *paiCol points to an array containing {1, 2, -1}.
** If table t1 had an explicit INTEGER PRIMARY KEY, then the "-1" in the
** *paiCol array would be replaced by its column index. Or if:
**
**       CREATE TABLE t2(a, b, c, d, PRIMARY KEY(d, c)) WITHOUT ROWID;
**       CREATE INDEX i2 ON t2(a);
**
** then (*pnCol) is 3 and *paiCol points to an array containing {0, 3, 2}.
**
** The lifetime of the array is the same as that of the statement handle -
** it is automatically freed when the statement handle is passed to
** sqlite3_finalize().
**
** The statement has (*pnCol) SQL variables that values may be bound to.
** They correspond to the values used to create the index key that is
** inserted or deleted when the statement is stepped.
**
** If the index is a UNIQUE index, the usual checking and error codes apply
** to insert operations.
*/
int sqlite3_index_writer(
  sqlite3 *db, 
  int bDelete,                    /* Zero for insert, non-zero for delete */
  const char *zIndex,             /* Index to write to */
  sqlite3_stmt**,                 /* OUT: New statement handle */
  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_ */

Changes to src/sqliteInt.h.

1137
1138
1139
1140
1141
1142
1143


1144
1145
1146
1147
1148
1149
1150
....
3721
3722
3723
3724
3725
3726
3727

3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
#define SQLITE_PreferBuiltin  0x00200000  /* Preference to built-in funcs */
#define SQLITE_LoadExtension  0x00400000  /* Enable load_extension */
#define SQLITE_EnableTrigger  0x00800000  /* True to enable triggers */
#define SQLITE_DeferFKs       0x01000000  /* Defer all FK constraints */
#define SQLITE_QueryOnly      0x02000000  /* Disable database changes */
#define SQLITE_VdbeEQP        0x04000000  /* Debug EXPLAIN QUERY PLAN */




/*
** Bits of the sqlite3.dbOptFlags field that are used by the
** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface to
** selectively disable various optimizations.
*/
#define SQLITE_QueryFlattener 0x0001   /* Query flattening */
................................................................................
# define sqlite3MemdebugNoType(X,Y)   1
#endif
#define MEMTYPE_HEAP       0x01  /* General heap allocations */
#define MEMTYPE_LOOKASIDE  0x02  /* Might have been lookaside memory */
#define MEMTYPE_SCRATCH    0x04  /* Scratch allocations */
#define MEMTYPE_PCACHE     0x08  /* Page cache allocations */
#define MEMTYPE_DB         0x10  /* Uses sqlite3DbMalloc, not sqlite_malloc */


/*
** Threading interface
*/
#if SQLITE_MAX_WORKER_THREADS>0
int sqlite3ThreadCreate(SQLiteThread**,void*(*)(void*),void*);
int sqlite3ThreadJoin(SQLiteThread*, void**);
#endif

#endif /* _SQLITEINT_H_ */







>
>







 







>










1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
....
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
#define SQLITE_PreferBuiltin  0x00200000  /* Preference to built-in funcs */
#define SQLITE_LoadExtension  0x00400000  /* Enable load_extension */
#define SQLITE_EnableTrigger  0x00800000  /* True to enable triggers */
#define SQLITE_DeferFKs       0x01000000  /* Defer all FK constraints */
#define SQLITE_QueryOnly      0x02000000  /* Disable database changes */
#define SQLITE_VdbeEQP        0x04000000  /* Debug EXPLAIN QUERY PLAN */

#define SQLITE_OtaMode        0x08000000  /* True in "ota mode" */


/*
** Bits of the sqlite3.dbOptFlags field that are used by the
** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface to
** selectively disable various optimizations.
*/
#define SQLITE_QueryFlattener 0x0001   /* Query flattening */
................................................................................
# define sqlite3MemdebugNoType(X,Y)   1
#endif
#define MEMTYPE_HEAP       0x01  /* General heap allocations */
#define MEMTYPE_LOOKASIDE  0x02  /* Might have been lookaside memory */
#define MEMTYPE_SCRATCH    0x04  /* Scratch allocations */
#define MEMTYPE_PCACHE     0x08  /* Page cache allocations */
#define MEMTYPE_DB         0x10  /* Uses sqlite3DbMalloc, not sqlite_malloc */


/*
** Threading interface
*/
#if SQLITE_MAX_WORKER_THREADS>0
int sqlite3ThreadCreate(SQLiteThread**,void*(*)(void*),void*);
int sqlite3ThreadJoin(SQLiteThread*, void**);
#endif

#endif /* _SQLITEINT_H_ */

Changes to src/tclsqlite.c.

3694
3695
3696
3697
3698
3699
3700


3701
3702
3703
3704
3705
3706
3707
....
3736
3737
3738
3739
3740
3741
3742

3743
3744
3745
3746
3747
3748
3749
    extern int Sqlitetestintarray_Init(Tcl_Interp*);
    extern int Sqlitetestvfs_Init(Tcl_Interp *);
    extern int Sqlitetestrtree_Init(Tcl_Interp*);
    extern int Sqlitequota_Init(Tcl_Interp*);
    extern int Sqlitemultiplex_Init(Tcl_Interp*);
    extern int SqliteSuperlock_Init(Tcl_Interp*);
    extern int SqlitetestSyscall_Init(Tcl_Interp*);



#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
    extern int Sqlitetestfts3_Init(Tcl_Interp *interp);
#endif

#ifdef SQLITE_ENABLE_ZIPVFS
    extern int Zipvfs_Init(Tcl_Interp*);
................................................................................
    Sqlitetestintarray_Init(interp);
    Sqlitetestvfs_Init(interp);
    Sqlitetestrtree_Init(interp);
    Sqlitequota_Init(interp);
    Sqlitemultiplex_Init(interp);
    SqliteSuperlock_Init(interp);
    SqlitetestSyscall_Init(interp);


#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
    Sqlitetestfts3_Init(interp);
#endif

    Tcl_CreateObjCommand(
        interp, "load_testfixture_extensions", init_all_cmd, 0, 0







>
>







 







>







3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
....
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
    extern int Sqlitetestintarray_Init(Tcl_Interp*);
    extern int Sqlitetestvfs_Init(Tcl_Interp *);
    extern int Sqlitetestrtree_Init(Tcl_Interp*);
    extern int Sqlitequota_Init(Tcl_Interp*);
    extern int Sqlitemultiplex_Init(Tcl_Interp*);
    extern int SqliteSuperlock_Init(Tcl_Interp*);
    extern int SqlitetestSyscall_Init(Tcl_Interp*);

    extern int SqliteOta_Init(Tcl_Interp*);

#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
    extern int Sqlitetestfts3_Init(Tcl_Interp *interp);
#endif

#ifdef SQLITE_ENABLE_ZIPVFS
    extern int Zipvfs_Init(Tcl_Interp*);
................................................................................
    Sqlitetestintarray_Init(interp);
    Sqlitetestvfs_Init(interp);
    Sqlitetestrtree_Init(interp);
    Sqlitequota_Init(interp);
    Sqlitemultiplex_Init(interp);
    SqliteSuperlock_Init(interp);
    SqlitetestSyscall_Init(interp);
    SqliteOta_Init(interp);

#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
    Sqlitetestfts3_Init(interp);
#endif

    Tcl_CreateObjCommand(
        interp, "load_testfixture_extensions", init_all_cmd, 0, 0

Changes to src/test1.c.

6492
6493
6494
6495
6496
6497
6498
































































6499
6500
6501
6502
6503
6504
6505
....
6730
6731
6732
6733
6734
6735
6736


6737
6738
6739
6740
6741
6742
6743

  return TCL_OK;
 sql_error:
  Tcl_AppendResult(interp, "sql error: ", sqlite3_errmsg(db), 0);
  return TCL_ERROR;
}


































































/*
** Register commands with the TCL interpreter.
*/
int Sqlitetest1_Init(Tcl_Interp *interp){
  extern int sqlite3_search_count;
  extern int sqlite3_found_count;
................................................................................
     { "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 },


  };
  static int bitmask_size = sizeof(Bitmask)*8;
  int i;
  extern int sqlite3_sync_count, sqlite3_fullsync_count;
  extern int sqlite3_opentemp_count;
  extern int sqlite3_like_count;
  extern int sqlite3_xferopt_count;







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







 







>
>







6492
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
....
6794
6795
6796
6797
6798
6799
6800
6801
6802
6803
6804
6805
6806
6807
6808
6809

  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;
}

/*
** Register commands with the TCL interpreter.
*/
int Sqlitetest1_Init(Tcl_Interp *interp){
  extern int sqlite3_search_count;
  extern int sqlite3_found_count;
................................................................................
     { "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 },
  };
  static int bitmask_size = sizeof(Bitmask)*8;
  int i;
  extern int sqlite3_sync_count, sqlite3_fullsync_count;
  extern int sqlite3_opentemp_count;
  extern int sqlite3_like_count;
  extern int sqlite3_xferopt_count;

Changes to src/test2.c.

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
  int rc;
  if( argc!=2 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
       " ID\"", 0);
    return TCL_ERROR;
  }
  pPager = sqlite3TestTextToPtr(argv[1]);
  rc = sqlite3PagerClose(pPager);
  if( rc!=SQLITE_OK ){
    Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
    return TCL_ERROR;
  }
  return TCL_OK;
}








|







85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
  int rc;
  if( argc!=2 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
       " ID\"", 0);
    return TCL_ERROR;
  }
  pPager = sqlite3TestTextToPtr(argv[1]);
  rc = sqlite3PagerClose(pPager, 0);
  if( rc!=SQLITE_OK ){
    Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
    return TCL_ERROR;
  }
  return TCL_OK;
}

Changes to src/vdbeblob.c.

458
459
460
461
462
463
464
465
















































































































466
  }

  rc = sqlite3ApiExit(db, rc);
  assert( rc==SQLITE_OK || p->pStmt==0 );
  sqlite3_mutex_leave(db->mutex);
  return rc;
}

















































































































#endif /* #ifndef SQLITE_OMIT_INCRBLOB */








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

458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
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
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
  }

  rc = sqlite3ApiExit(db, rc);
  assert( rc==SQLITE_OK || p->pStmt==0 );
  sqlite3_mutex_leave(db->mutex);
  return rc;
}

int sqlite3_index_writer(
  sqlite3 *db, 
  int bDelete,
  const char *zIndex, 
  sqlite3_stmt **ppStmt,
  int **paiCol, int *pnCol
){
  int rc = SQLITE_OK;
  Parse *pParse = 0;
  Index *pIdx = 0;                /* The index to write to */
  Table *pTab;
  int i;                          /* Used to iterate through index columns */
  Vdbe *v = 0;
  int regRec;                     /* Register to assemble record in */
  int *aiCol = 0;

  sqlite3_mutex_enter(db->mutex);
  sqlite3BtreeEnterAll(db);

  /* Allocate the parse context */
  pParse = sqlite3StackAllocRaw(db, sizeof(*pParse));
  if( !pParse ) goto index_writer_out;
  memset(pParse, 0, sizeof(Parse));
  pParse->db = db;

  /* Allocate the Vdbe */
  v = sqlite3GetVdbe(pParse);
  if( v==0 ) goto index_writer_out;

  /* Find the index to write to */
  pIdx = sqlite3FindIndex(db, zIndex, "main");
  if( pIdx==0 ){
    sqlite3ErrorMsg(pParse, "no such index: %s", zIndex);
    goto index_writer_out;
  }
  pTab = pIdx->pTable;

  /* Populate the two output variables, *pnCol and *pnAiCol. */
  *pnCol = pIdx->nColumn;
  *paiCol = aiCol = sqlite3DbMallocZero(db, sizeof(int) * pIdx->nColumn);
  if( aiCol==0 ){
    rc = SQLITE_NOMEM;
    goto index_writer_out;
  }
  for(i=0; i<pIdx->nKeyCol; i++){
    aiCol[i] = pIdx->aiColumn[i];
  }
  if( !HasRowid(pTab) ){
    Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
    assert( pIdx->nColumn==pIdx->nKeyCol+pPk->nKeyCol );
    if( pPk==pIdx ){
      rc = SQLITE_ERROR;
      goto index_writer_out;
    }
    for(i=0; i<pPk->nKeyCol; i++){
      aiCol[pIdx->nKeyCol+i] = pPk->aiColumn[i];
    }
  }else{
    assert( pIdx->nColumn==pIdx->nKeyCol+1 );
    aiCol[i] = pTab->iPKey;
  }

  /* Add an OP_Noop to the VDBE program. Then store a pointer to the 
  ** output array *paiCol as its P4 value. This is so that the array
  ** is automatically deleted when the user finalizes the statement. The
  ** OP_Noop serves no other purpose. */
  sqlite3VdbeAddOp0(v, OP_Noop);
  sqlite3VdbeChangeP4(v, -1, (const char*)aiCol, P4_INTARRAY);

  sqlite3BeginWriteOperation(pParse, 0, 0);

  /* Open a write cursor on the index */
  pParse->nTab = 1;
  sqlite3VdbeAddOp3(v, OP_OpenWrite, 0, pIdx->tnum, 0);
  sqlite3VdbeSetP4KeyInfo(pParse, pIdx);

  /* Create the record to insert into the index. Store it in register regRec. */
  pParse->nVar = pIdx->nColumn;
  pParse->nMem = pIdx->nColumn;
  for(i=1; i<=pIdx->nColumn; i++){
    sqlite3VdbeAddOp2(v, OP_Variable, i, i);
  }
  regRec = ++pParse->nMem;
  sqlite3VdbeAddOp3(v, OP_MakeRecord, 1, pIdx->nColumn, regRec);

  /* If this is a UNIQUE index, check the constraint. */
  if( pIdx->onError ){
    int addr = sqlite3VdbeAddOp4Int(v, OP_NoConflict, 0, 0, 1, pIdx->nKeyCol);
    sqlite3UniqueConstraint(pParse, SQLITE_ABORT, pIdx);
    sqlite3VdbeJumpHere(v, addr);
  }

  /* Code the IdxInsert to write to the b-tree index. */
  sqlite3VdbeAddOp2(v, OP_IdxInsert, 0, regRec);
  sqlite3FinishCoding(pParse);

index_writer_out:
  if( rc==SQLITE_OK && db->mallocFailed==0 ){
    *ppStmt = (sqlite3_stmt*)v;
  }else{
    *ppStmt = 0;
    if( v ) sqlite3VdbeFinalize(v);
  }

  sqlite3ParserReset(pParse);
  sqlite3StackFree(db, pParse);
  sqlite3BtreeLeaveAll(db);
  rc = sqlite3ApiExit(db, rc);
  sqlite3_mutex_leave(db->mutex);
  return rc;
}

#endif /* #ifndef SQLITE_OMIT_INCRBLOB */

Changes to src/wal.c.

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
....
1833
1834
1835
1836
1837
1838
1839

1840
1841
1842
1843
1844
1845
1846
....
1862
1863
1864
1865
1866
1867
1868

1869
1870
1871
1872
1873
1874
1875
....
3074
3075
3076
3077
3078
3079
3080























































































































































3081
3082
3083
3084
3085
3086
3087
#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;
................................................................................
    ** ordinary, rollback-mode locking methods, this guarantees that the
    ** connection associated with this log file is the only connection to
    ** the database. In this case checkpoint the database and unlink both
    ** the wal and wal-index files.
    **
    ** The EXCLUSIVE lock is not released before returning.
    */

    rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE);
    if( rc==SQLITE_OK ){
      if( pWal->exclusiveMode==WAL_NORMAL_MODE ){
        pWal->exclusiveMode = WAL_EXCLUSIVE_MODE;
      }
      rc = sqlite3WalCheckpoint(
          pWal, SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0
................................................................................
          ** non-negative value (pWal->mxWalSize>=0).  Note that we truncate
          ** to zero bytes as truncating to the journal_size_limit might
          ** leave a corrupt WAL file on disk. */
          walLimitSize(pWal, 0);
        }
      }
    }


    walIndexClose(pWal, isDelete);
    sqlite3OsClose(pWal->pWalFd);
    if( isDelete ){
      sqlite3BeginBenignMalloc();
      sqlite3OsDelete(pWal->pVfs, pWal->zWalName, 0);
      sqlite3EndBenignMalloc();
................................................................................
** 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){
  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).
*/







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







 







<






<
<


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







 







>







 







>







 







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







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
....
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
....
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
....
3091
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
#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;
................................................................................
    ** ordinary, rollback-mode locking methods, this guarantees that the
    ** connection associated with this log file is the only connection to
    ** the database. In this case checkpoint the database and unlink both
    ** the wal and wal-index files.
    **
    ** The EXCLUSIVE lock is not released before returning.
    */
    if( zBuf ){
      rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE);
      if( rc==SQLITE_OK ){
        if( pWal->exclusiveMode==WAL_NORMAL_MODE ){
          pWal->exclusiveMode = WAL_EXCLUSIVE_MODE;
        }
        rc = sqlite3WalCheckpoint(
            pWal, SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0
................................................................................
            ** non-negative value (pWal->mxWalSize>=0).  Note that we truncate
            ** to zero bytes as truncating to the journal_size_limit might
            ** leave a corrupt WAL file on disk. */
            walLimitSize(pWal, 0);
          }
        }
      }
    }

    walIndexClose(pWal, isDelete);
    sqlite3OsClose(pWal->pWalFd);
    if( isDelete ){
      sqlite3BeginBenignMalloc();
      sqlite3OsDelete(pWal->pVfs, pWal->zWalName, 0);
      sqlite3EndBenignMalloc();
................................................................................
** 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){
  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).
*/

Changes to src/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_ */







>
>
>









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_ */