/ Check-in [29741941]
Login

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

Overview
Comment:Merge changes from trunk, especially the SQLITE_DEFAULT_SYNCHRONOUS enhancements.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | apple-osx
Files: files | file ages | folders
SHA1:29741941238643dc0e41660d98b176d81f9fd6f1
User & Date: drh 2016-03-08 16:35:17
Context
2016-03-15
12:45
Merge updates from trunk - FTS5 fixes and enhancemenets to the tests scripts so that they work with SEE. check-in: f41a7361 user: drh tags: apple-osx
2016-03-08
16:35
Merge changes from trunk, especially the SQLITE_DEFAULT_SYNCHRONOUS enhancements. check-in: 29741941 user: drh tags: apple-osx
16:07
Drop support for SQLITE_EXTRA_DURABLE. The new SQLITE_DEFAULT_SYNCHRONOUS compile-time option is a more general replacement. check-in: f6d3156b user: drh tags: trunk
14:01
Merge coverage improvements and comment fixes from trunk. check-in: 58023bfc user: drh tags: apple-osx
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to ext/fts3/fts3_write.c.

4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847

4848
4849
4850
4851
4852
4853
4854
....
4992
4993
4994
4995
4996
4997
4998
4999
5000
5001
5002
5003
5004
5005
5006
    ** nSeg to nMin. If no level with at least nMin segments can be found, 
    ** set nSeg to -1.
    */
    rc = fts3SqlStmt(p, SQL_FIND_MERGE_LEVEL, &pFindLevel, 0);
    sqlite3_bind_int(pFindLevel, 1, MAX(2, nMin));
    if( sqlite3_step(pFindLevel)==SQLITE_ROW ){
      iAbsLevel = sqlite3_column_int64(pFindLevel, 0);
      if( nMin<2 ){
        nSeg = sqlite3_column_int(pFindLevel, 1);
      }else{
        nSeg = nMin;
      }

    }else{
      nSeg = -1;
    }
    rc = sqlite3_reset(pFindLevel);

    /* If the hint read from the %_stat table is not empty, check if the
    ** last entry in it specifies a relative level smaller than or equal
................................................................................
  /* If the first integer value is followed by a ',',  read the second
  ** integer value. */
  if( z[0]==',' && z[1]!='\0' ){
    z++;
    nMin = fts3Getint(&z);
  }

  if( z[0]!='\0' || nMin<0 || nMin==1 ){
    rc = SQLITE_ERROR;
  }else{
    rc = SQLITE_OK;
    if( !p->bHasStat ){
      assert( p->bFts4==0 );
      sqlite3Fts3CreateStatTable(&rc, p);
    }







<
|
<
<
<
>







 







|







4836
4837
4838
4839
4840
4841
4842

4843



4844
4845
4846
4847
4848
4849
4850
4851
....
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
5001
5002
5003
    ** nSeg to nMin. If no level with at least nMin segments can be found, 
    ** set nSeg to -1.
    */
    rc = fts3SqlStmt(p, SQL_FIND_MERGE_LEVEL, &pFindLevel, 0);
    sqlite3_bind_int(pFindLevel, 1, MAX(2, nMin));
    if( sqlite3_step(pFindLevel)==SQLITE_ROW ){
      iAbsLevel = sqlite3_column_int64(pFindLevel, 0);

      nSeg = sqlite3_column_int(pFindLevel, 1);



      assert( nSeg>=2 );
    }else{
      nSeg = -1;
    }
    rc = sqlite3_reset(pFindLevel);

    /* If the hint read from the %_stat table is not empty, check if the
    ** last entry in it specifies a relative level smaller than or equal
................................................................................
  /* If the first integer value is followed by a ',',  read the second
  ** integer value. */
  if( z[0]==',' && z[1]!='\0' ){
    z++;
    nMin = fts3Getint(&z);
  }

  if( z[0]!='\0' || nMin<2 ){
    rc = SQLITE_ERROR;
  }else{
    rc = SQLITE_OK;
    if( !p->bHasStat ){
      assert( p->bFts4==0 );
      sqlite3Fts3CreateStatTable(&rc, p);
    }

Added ext/rbu/rbuC.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
# 2016 March 7
#
# 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.
#
#***********************************************************************
# Tests for RBU focused on the REPLACE operation (rbu_control column
# contains integer value 2).
#

source [file join [file dirname [info script]] rbu_common.tcl]
set ::testprefix rbuC

#-------------------------------------------------------------------------
# This test is actually of an UPDATE directive. Just to establish that
# these work with UNIQUE indexes before preceding to REPLACE.
#
do_execsql_test 1.0 {
  CREATE TABLE t1(i INTEGER PRIMARY KEY, a, b, c UNIQUE);
  INSERT INTO t1 VALUES(1, 'a', 'b', 'c');
}

forcedelete rbu.db
do_execsql_test 1.1 {
  ATTACH 'rbu.db' AS rbu;
  CREATE TABLE rbu.data_t1(i, a, b, c, rbu_control);
  INSERT INTO data_t1 VALUES(1, 'a', 'b', 'c', '.xxx');
}

do_test 1.2 {
  step_rbu test.db rbu.db
} {SQLITE_DONE}

do_execsql_test 1.3 {
  SELECT * FROM t1
} {
  1 a b c
}

#-------------------------------------------------------------------------
#
foreach {tn schema} {
  1 {
    CREATE TABLE t1(i INTEGER PRIMARY KEY, a, b, c UNIQUE);
    CREATE INDEX t1a ON t1(a);
  }
  2 {
    CREATE TABLE t1(i PRIMARY KEY, a, b, c UNIQUE);
    CREATE INDEX t1a ON t1(a);
  }
  3 {
    CREATE TABLE t1(i PRIMARY KEY, a, b, c UNIQUE) WITHOUT ROWID;
    CREATE INDEX t1a ON t1(a);
  }
} {
  reset_db
  forcedelete rbu.db
  execsql $schema

  do_execsql_test 2.$tn.0 {
    INSERT INTO t1 VALUES(1, 'a', 'b', 'c');
    INSERT INTO t1 VALUES(2, 'b', 'c', 'd');
    INSERT INTO t1 VALUES(3, 'c', 'd', 'e');
  }
  
  do_execsql_test 2.$tn.1 {
    ATTACH 'rbu.db' AS rbu;
    CREATE TABLE rbu.data_t1(i, a, b, c, rbu_control);
    INSERT INTO data_t1 VALUES(1, 1, 2, 3, 2);
    INSERT INTO data_t1 VALUES(3, 'c', 'd', 'e', 2);
    INSERT INTO data_t1 VALUES(4, 'd', 'e', 'f', 2);
  }
  
  do_test 2.$tn.2 {
    step_rbu test.db rbu.db
  } {SQLITE_DONE}
  
  do_execsql_test 2.$tn.3 {
    SELECT * FROM t1 ORDER BY i
  } {
    1 1 2 3
    2 b c d
    3 c d e
    4 d e f
  }
  
  integrity_check 2.$tn.4
}

foreach {tn schema} {
  1 {
    CREATE TABLE t1(a, b, c UNIQUE);
    CREATE INDEX t1a ON t1(a);
  }

  2 {
    CREATE VIRTUAL TABLE t1 USING fts5(a, b, c);
  }
} {
  if {$tn==2} { ifcapable !fts5 break }
  reset_db
  forcedelete rbu.db
  execsql $schema

  do_execsql_test 3.$tn.0 {
    INSERT INTO t1 VALUES('a', 'b', 'c');
    INSERT INTO t1 VALUES('b', 'c', 'd');
    INSERT INTO t1 VALUES('c', 'd', 'e');
  }
  
  do_execsql_test 3.$tn.1 {
    ATTACH 'rbu.db' AS rbu;
    CREATE TABLE rbu.data_t1(rbu_rowid, a, b, c, rbu_control);
    INSERT INTO data_t1 VALUES(1, 1, 2, 3, 2);
    INSERT INTO data_t1 VALUES(3, 'c', 'd', 'e', 2);
    INSERT INTO data_t1 VALUES(4, 'd', 'e', 'f', 2);
  }
  
  do_test 3.$tn.2 {
    step_rbu test.db rbu.db
  } {SQLITE_DONE}
  
  do_execsql_test 3.$tn.3 {
    SELECT rowid, * FROM t1 ORDER BY 1
  } {
    1 1 2 3
    2 b c d
    3 c d e
    4 d e f
  }
  
  integrity_check 3.$tn.4
}



finish_test

Changes to ext/rbu/sqlite3rbu.c.

276
277
278
279
280
281
282

283
284
285
286

287
288
289
290
291
292
293
....
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915

1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
....
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
....
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516

2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
....
2550
2551
2552
2553
2554
2555
2556





























































2557
2558
2559
2560
2561
2562
2563
....
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596

2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632

2633
2634
2635
2636


2637
2638
2639
2640
2641
2642
2643

2644
2645
2646
2647
2648
2649
2650
....
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674

/*
** Within the RBU_STAGE_OAL stage, each call to sqlite3rbu_step() performs
** one of the following operations.
*/
#define RBU_INSERT     1          /* Insert on a main table b-tree */
#define RBU_DELETE     2          /* Delete a row from a main table b-tree */

#define RBU_IDX_DELETE 3          /* Delete a row from an aux. index b-tree */
#define RBU_IDX_INSERT 4          /* Insert on an aux. index b-tree */
#define RBU_UPDATE     5          /* Update a row in a main table b-tree */



/*
** A single step of an incremental checkpoint - frame iWalFrame of the wal
** file should be copied to page iDbPage of the database file.
*/
struct RbuFrame {
  u32 iDbPage;
................................................................................
          zSql = sqlite3_mprintf(
              "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' ORDER BY %s%s",
              zCollist, p->zStateDb, pIter->zDataTbl,
              zCollist, zLimit
          );
        }else{
          zSql = sqlite3_mprintf(
              "SELECT %s, rbu_control FROM '%q' "
              "WHERE typeof(rbu_control)='integer' AND rbu_control!=1 "
              "UNION ALL "
              "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' "

              "ORDER BY %s%s",
              zCollist, pIter->zDataTbl, 
              zCollist, p->zStateDb, pIter->zDataTbl, 
              zCollist, zLimit
          );
        }
        p->rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pSelect, pz, zSql);
      }

      sqlite3_free(zImposterCols);
................................................................................
            , (pIter->eType==RBU_PK_EXTERNAL ? ", 0 AS rbu_rowid" : "")
            , pIter->zDataTbl
        );

        rbuMPrintfExec(p, p->dbMain,
            "CREATE TEMP TRIGGER rbu_delete_tr BEFORE DELETE ON \"%s%w\" "
            "BEGIN "
            "  SELECT rbu_tmp_insert(2, %s);"
            "END;"

            "CREATE TEMP TRIGGER rbu_update1_tr BEFORE UPDATE ON \"%s%w\" "
            "BEGIN "
            "  SELECT rbu_tmp_insert(2, %s);"
            "END;"

            "CREATE TEMP TRIGGER rbu_update2_tr AFTER UPDATE ON \"%s%w\" "
            "BEGIN "
            "  SELECT rbu_tmp_insert(3, %s);"
            "END;",
            zWrite, zTbl, zOldlist,
            zWrite, zTbl, zOldlist,
            zWrite, zTbl, zNewlist
        );

        if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){
................................................................................
static int rbuStepType(sqlite3rbu *p, const char **pzMask){
  int iCol = p->objiter.nCol;     /* Index of rbu_control column */
  int res = 0;                    /* Return value */

  switch( sqlite3_column_type(p->objiter.pSelect, iCol) ){
    case SQLITE_INTEGER: {
      int iVal = sqlite3_column_int(p->objiter.pSelect, iCol);
      if( iVal==0 ){
        res = RBU_INSERT;
      }else if( iVal==1 ){
        res = RBU_DELETE;
      }else if( iVal==2 ){

        res = RBU_IDX_DELETE;
      }else if( iVal==3 ){
        res = RBU_IDX_INSERT;
      }
      break;
    }

    case SQLITE_TEXT: {
      const unsigned char *z = sqlite3_column_text(p->objiter.pSelect, iCol);
      if( z==0 ){
................................................................................
static void assertColumnName(sqlite3_stmt *pStmt, int iCol, const char *zName){
  const char *zCol = sqlite3_column_name(pStmt, iCol);
  assert( 0==sqlite3_stricmp(zName, zCol) );
}
#else
# define assertColumnName(x,y,z)
#endif






























































/*
** This function does the work for an sqlite3rbu_step() call.
**
** The object-iterator (p->objiter) currently points to a valid object,
** and the input cursor (p->objiter.pSelect) currently points to a valid
** input row. Perform whatever processing is required and return.
................................................................................
** If no  error occurs, SQLITE_OK is returned. Otherwise, an error code
** and message is left in the RBU handle and a copy of the error code
** returned.
*/
static int rbuStep(sqlite3rbu *p){
  RbuObjIter *pIter = &p->objiter;
  const char *zMask = 0;
  int i;
  int eType = rbuStepType(p, &zMask);

  if( eType ){
    assert( eType!=RBU_UPDATE || pIter->zIdx==0 );

    if( pIter->zIdx==0 && eType==RBU_IDX_DELETE ){
      rbuBadControlError(p);
    }
    else if( 
        eType==RBU_INSERT 
     || eType==RBU_DELETE
     || eType==RBU_IDX_DELETE 
     || eType==RBU_IDX_INSERT
    ){
      sqlite3_value *pVal;
      sqlite3_stmt *pWriter;

      assert( eType!=RBU_UPDATE );
      assert( eType!=RBU_DELETE || pIter->zIdx==0 );

      if( eType==RBU_IDX_DELETE || eType==RBU_DELETE ){
        pWriter = pIter->pDelete;
      }else{
        pWriter = pIter->pInsert;

      }

      for(i=0; i<pIter->nCol; i++){
        /* If this is an INSERT into a table b-tree and the table has an
        ** explicit INTEGER PRIMARY KEY, check that this is not an attempt
        ** to write a NULL into the IPK column. That is not permitted.  */
        if( eType==RBU_INSERT 
         && pIter->zIdx==0 && pIter->eType==RBU_PK_IPK && pIter->abTblPk[i] 
         && sqlite3_column_type(pIter->pSelect, i)==SQLITE_NULL
        ){
          p->rc = SQLITE_MISMATCH;
          p->zErrmsg = sqlite3_mprintf("datatype mismatch");
          goto step_out;
        }

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

        pVal = sqlite3_column_value(pIter->pSelect, i);
        p->rc = sqlite3_bind_value(pWriter, i+1, pVal);
        if( p->rc ) goto step_out;
      }
      if( pIter->zIdx==0
       && (pIter->eType==RBU_PK_VTAB || pIter->eType==RBU_PK_NONE) 
      ){
        /* For a virtual table, or a table with no primary key, the 
        ** SELECT statement is:
        **
        **   SELECT <cols>, rbu_control, rbu_rowid FROM ....
        **
        ** Hence column_value(pIter->nCol+1).
        */
        assertColumnName(pIter->pSelect, pIter->nCol+1, "rbu_rowid");
        pVal = sqlite3_column_value(pIter->pSelect, pIter->nCol+1);
        p->rc = sqlite3_bind_value(pWriter, pIter->nCol+1, pVal);

      }
      if( p->rc==SQLITE_OK ){
        sqlite3_step(pWriter);
        p->rc = resetAndCollectError(pWriter, &p->zErrmsg);


      }
    }else{
      sqlite3_value *pVal;
      sqlite3_stmt *pUpdate = 0;
      assert( eType==RBU_UPDATE );
      rbuGetUpdateStmt(p, pIter, zMask, &pUpdate);
      if( pUpdate ){

        for(i=0; p->rc==SQLITE_OK && i<pIter->nCol; i++){
          char c = zMask[pIter->aiSrcOrder[i]];
          pVal = sqlite3_column_value(pIter->pSelect, i);
          if( pIter->abTblPk[i] || c!='.' ){
            p->rc = sqlite3_bind_value(pUpdate, i+1, pVal);
          }
        }
................................................................................
        if( p->rc==SQLITE_OK ){
          sqlite3_step(pUpdate);
          p->rc = resetAndCollectError(pUpdate, &p->zErrmsg);
        }
      }
    }
  }

 step_out:
  return p->rc;
}

/*
** Increment the schema cookie of the main database opened by p->dbMain.
*/
static void rbuIncrSchemaCookie(sqlite3rbu *p){







>
|
|
<

>







 







|
<

|
>

|
|







 







|




|




|







 







|
|
<
|
<
>
|
<
|







 







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







 







<



<
<
<
<
<
<
<
|
|
|
|
<
<
<
|
<

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





>







 







<
<







276
277
278
279
280
281
282
283
284
285

286
287
288
289
290
291
292
293
294
....
1906
1907
1908
1909
1910
1911
1912
1913

1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
....
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
....
2506
2507
2508
2509
2510
2511
2512
2513
2514

2515

2516
2517

2518
2519
2520
2521
2522
2523
2524
2525
....
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
....
2625
2626
2627
2628
2629
2630
2631

2632
2633
2634







2635
2636
2637
2638



2639

2640
2641



2642
2643





2644
















2645












2646
2647



2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
....
2673
2674
2675
2676
2677
2678
2679


2680
2681
2682
2683
2684
2685
2686

/*
** Within the RBU_STAGE_OAL stage, each call to sqlite3rbu_step() performs
** one of the following operations.
*/
#define RBU_INSERT     1          /* Insert on a main table b-tree */
#define RBU_DELETE     2          /* Delete a row from a main table b-tree */
#define RBU_REPLACE    3          /* Delete and then insert a row */
#define RBU_IDX_DELETE 4          /* Delete a row from an aux. index b-tree */
#define RBU_IDX_INSERT 5          /* Insert on an aux. index b-tree */


#define RBU_UPDATE     6          /* Update a row in a main table b-tree */

/*
** A single step of an incremental checkpoint - frame iWalFrame of the wal
** file should be copied to page iDbPage of the database file.
*/
struct RbuFrame {
  u32 iDbPage;
................................................................................
          zSql = sqlite3_mprintf(
              "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' ORDER BY %s%s",
              zCollist, p->zStateDb, pIter->zDataTbl,
              zCollist, zLimit
          );
        }else{
          zSql = sqlite3_mprintf(
              "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' "

              "UNION ALL "
              "SELECT %s, rbu_control FROM '%q' "
              "WHERE typeof(rbu_control)='integer' AND rbu_control!=1 "
              "ORDER BY %s%s",
              zCollist, p->zStateDb, pIter->zDataTbl, 
              zCollist, pIter->zDataTbl, 
              zCollist, zLimit
          );
        }
        p->rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pSelect, pz, zSql);
      }

      sqlite3_free(zImposterCols);
................................................................................
            , (pIter->eType==RBU_PK_EXTERNAL ? ", 0 AS rbu_rowid" : "")
            , pIter->zDataTbl
        );

        rbuMPrintfExec(p, p->dbMain,
            "CREATE TEMP TRIGGER rbu_delete_tr BEFORE DELETE ON \"%s%w\" "
            "BEGIN "
            "  SELECT rbu_tmp_insert(3, %s);"
            "END;"

            "CREATE TEMP TRIGGER rbu_update1_tr BEFORE UPDATE ON \"%s%w\" "
            "BEGIN "
            "  SELECT rbu_tmp_insert(3, %s);"
            "END;"

            "CREATE TEMP TRIGGER rbu_update2_tr AFTER UPDATE ON \"%s%w\" "
            "BEGIN "
            "  SELECT rbu_tmp_insert(4, %s);"
            "END;",
            zWrite, zTbl, zOldlist,
            zWrite, zTbl, zOldlist,
            zWrite, zTbl, zNewlist
        );

        if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){
................................................................................
static int rbuStepType(sqlite3rbu *p, const char **pzMask){
  int iCol = p->objiter.nCol;     /* Index of rbu_control column */
  int res = 0;                    /* Return value */

  switch( sqlite3_column_type(p->objiter.pSelect, iCol) ){
    case SQLITE_INTEGER: {
      int iVal = sqlite3_column_int(p->objiter.pSelect, iCol);
      switch( iVal ){
        case 0: res = RBU_INSERT;     break;

        case 1: res = RBU_DELETE;     break;

        case 2: res = RBU_REPLACE;    break;
        case 3: res = RBU_IDX_DELETE; break;

        case 4: res = RBU_IDX_INSERT; break;
      }
      break;
    }

    case SQLITE_TEXT: {
      const unsigned char *z = sqlite3_column_text(p->objiter.pSelect, iCol);
      if( z==0 ){
................................................................................
static void assertColumnName(sqlite3_stmt *pStmt, int iCol, const char *zName){
  const char *zCol = sqlite3_column_name(pStmt, iCol);
  assert( 0==sqlite3_stricmp(zName, zCol) );
}
#else
# define assertColumnName(x,y,z)
#endif

/*
** Argument eType must be one of RBU_INSERT, RBU_DELETE, RBU_IDX_INSERT or
** RBU_IDX_DELETE. This function performs the work of a single
** sqlite3rbu_step() call for the type of operation specified by eType.
*/
static void rbuStepOneOp(sqlite3rbu *p, int eType){
  RbuObjIter *pIter = &p->objiter;
  sqlite3_value *pVal;
  sqlite3_stmt *pWriter;
  int i;

  assert( p->rc==SQLITE_OK );
  assert( eType!=RBU_DELETE || pIter->zIdx==0 );

  if( eType==RBU_IDX_DELETE || eType==RBU_DELETE ){
    pWriter = pIter->pDelete;
  }else{
    pWriter = pIter->pInsert;
  }

  for(i=0; i<pIter->nCol; i++){
    /* If this is an INSERT into a table b-tree and the table has an
    ** explicit INTEGER PRIMARY KEY, check that this is not an attempt
    ** to write a NULL into the IPK column. That is not permitted.  */
    if( eType==RBU_INSERT 
     && pIter->zIdx==0 && pIter->eType==RBU_PK_IPK && pIter->abTblPk[i] 
     && sqlite3_column_type(pIter->pSelect, i)==SQLITE_NULL
    ){
      p->rc = SQLITE_MISMATCH;
      p->zErrmsg = sqlite3_mprintf("datatype mismatch");
      return;
    }

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

    pVal = sqlite3_column_value(pIter->pSelect, i);
    p->rc = sqlite3_bind_value(pWriter, i+1, pVal);
    if( p->rc ) return;
  }
  if( pIter->zIdx==0
   && (pIter->eType==RBU_PK_VTAB || pIter->eType==RBU_PK_NONE) 
  ){
    /* For a virtual table, or a table with no primary key, the 
    ** SELECT statement is:
    **
    **   SELECT <cols>, rbu_control, rbu_rowid FROM ....
    **
    ** Hence column_value(pIter->nCol+1).
    */
    assertColumnName(pIter->pSelect, pIter->nCol+1, "rbu_rowid");
    pVal = sqlite3_column_value(pIter->pSelect, pIter->nCol+1);
    p->rc = sqlite3_bind_value(pWriter, pIter->nCol+1, pVal);
  }
  if( p->rc==SQLITE_OK ){
    sqlite3_step(pWriter);
    p->rc = resetAndCollectError(pWriter, &p->zErrmsg);
  }
}

/*
** This function does the work for an sqlite3rbu_step() call.
**
** The object-iterator (p->objiter) currently points to a valid object,
** and the input cursor (p->objiter.pSelect) currently points to a valid
** input row. Perform whatever processing is required and return.
................................................................................
** If no  error occurs, SQLITE_OK is returned. Otherwise, an error code
** and message is left in the RBU handle and a copy of the error code
** returned.
*/
static int rbuStep(sqlite3rbu *p){
  RbuObjIter *pIter = &p->objiter;
  const char *zMask = 0;

  int eType = rbuStepType(p, &zMask);

  if( eType ){







    assert( eType==RBU_INSERT     || eType==RBU_DELETE
         || eType==RBU_REPLACE    || eType==RBU_IDX_DELETE
         || eType==RBU_IDX_INSERT || eType==RBU_UPDATE
    );



    assert( eType!=RBU_UPDATE || pIter->zIdx==0 );


    if( pIter->zIdx==0 && eType==RBU_IDX_DELETE ){



      rbuBadControlError(p);
    }





    else if( eType==RBU_REPLACE ){
















      if( pIter->zIdx==0 ) rbuStepOneOp(p, RBU_DELETE);












      if( p->rc==SQLITE_OK ) rbuStepOneOp(p, RBU_INSERT);
    }



    else if( eType!=RBU_UPDATE ){
      rbuStepOneOp(p, eType);
    }
    else{
      sqlite3_value *pVal;
      sqlite3_stmt *pUpdate = 0;
      assert( eType==RBU_UPDATE );
      rbuGetUpdateStmt(p, pIter, zMask, &pUpdate);
      if( pUpdate ){
        int i;
        for(i=0; p->rc==SQLITE_OK && i<pIter->nCol; i++){
          char c = zMask[pIter->aiSrcOrder[i]];
          pVal = sqlite3_column_value(pIter->pSelect, i);
          if( pIter->abTblPk[i] || c!='.' ){
            p->rc = sqlite3_bind_value(pUpdate, i+1, pVal);
          }
        }
................................................................................
        if( p->rc==SQLITE_OK ){
          sqlite3_step(pUpdate);
          p->rc = resetAndCollectError(pUpdate, &p->zErrmsg);
        }
      }
    }
  }


  return p->rc;
}

/*
** Increment the schema cookie of the main database opened by p->dbMain.
*/
static void rbuIncrSchemaCookie(sqlite3rbu *p){

Changes to src/attach.c.

157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
                             sqlite3BtreeSecureDelete(db->aDb[0].pBt,-1) );
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
    sqlite3BtreeSetPagerFlags(aNew->pBt,
                      PAGER_SYNCHRONOUS_FULL | (db->flags & PAGER_FLAGS_MASK));
#endif
    sqlite3BtreeLeave(aNew->pBt);
  }
  aNew->safety_level = 3;
  aNew->zName = sqlite3DbStrDup(db, zName);
  if( rc==SQLITE_OK && aNew->zName==0 ){
    rc = SQLITE_NOMEM_BKPT;
  }


#ifdef SQLITE_HAS_CODEC







|







157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
                             sqlite3BtreeSecureDelete(db->aDb[0].pBt,-1) );
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
    sqlite3BtreeSetPagerFlags(aNew->pBt,
                      PAGER_SYNCHRONOUS_FULL | (db->flags & PAGER_FLAGS_MASK));
#endif
    sqlite3BtreeLeave(aNew->pBt);
  }
  aNew->safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1;
  aNew->zName = sqlite3DbStrDup(db, zName);
  if( rc==SQLITE_OK && aNew->zName==0 ){
    rc = SQLITE_NOMEM_BKPT;
  }


#ifdef SQLITE_HAS_CODEC

Changes to src/btree.c.

2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878



2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891


2892
2893
2894

2895
2896

2897
2898
2899
2900
2901
2902
2903
    ** file.
    */
    if( page1[19]==2 && (pBt->btsFlags & BTS_NO_WAL)==0 ){
      int isOpen = 0;
      rc = sqlite3PagerOpenWal(pBt->pPager, &isOpen);
      if( rc!=SQLITE_OK ){
        goto page1_init_failed;
      }else if( isOpen==0 ){
#ifdef SQLITE_DEFAULT_WAL_SAFETYLEVEL
        /* Default to specified safety_level for WAL mode */
        if( pBt->db!=0 && pBt->db->aDb!=0 ){
          int iDb;
          sqlite3 *db = pBt->db;



          Db *aDb = db->aDb;
          u8 level = 0;
          for(iDb=0; iDb<db->nDb; iDb++){
            if( aDb[iDb].pBt && aDb[iDb].pBt->pBt==pBt ) break;
          }
          assert( iDb<db->nDb );
          level = db->aDb[iDb].safety_level;
          if( !SQLITE_DbSafetyLevelIsFixed(level) && 
             (SQLITE_DbSafetyLevelValue(level) != SQLITE_DEFAULT_WAL_SAFETYLEVEL) ){
            aDb[iDb].safety_level = SQLITE_DEFAULT_WAL_SAFETYLEVEL;
            sqlite3PagerSetSafetyLevel(pBt->pPager, SQLITE_DEFAULT_WAL_SAFETYLEVEL, 
                                       (db->flags&SQLITE_FullFSync)!=0,
                                       (db->flags&SQLITE_CkptFullFSync)!=0);


          }
        }
#endif

        releasePage(pPage1);
        return SQLITE_OK;

      }
      rc = SQLITE_NOTADB;
    }
#endif

    /* EVIDENCE-OF: R-15465-20813 The maximum and minimum embedded payload
    ** fractions and the leaf payload fraction values must be 64, 32, and 32.







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



>
|
|
>







2866
2867
2868
2869
2870
2871
2872
2873
2874



2875
2876
2877
2878
2879
2880
2881






2882



2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
    ** file.
    */
    if( page1[19]==2 && (pBt->btsFlags & BTS_NO_WAL)==0 ){
      int isOpen = 0;
      rc = sqlite3PagerOpenWal(pBt->pPager, &isOpen);
      if( rc!=SQLITE_OK ){
        goto page1_init_failed;
      }else{
#if SQLITE_DEFAULT_SYNCHRONOUS!=SQLITE_DEFAULT_WAL_SYNCHRONOUS



        sqlite3 *db;
        Db *pDb;
        if( (db=pBt->db)!=0 && (pDb=db->aDb)!=0 ){
          while( pDb->pBt==0 || pDb->pBt->pBt!=pBt ){ pDb++; }
          if( pDb->bSyncSet==0
           && pDb->safety_level==SQLITE_DEFAULT_SYNCHRONOUS+1
          ){






            pDb->safety_level = SQLITE_DEFAULT_WAL_SYNCHRONOUS+1;



            sqlite3PagerSetFlags(pBt->pPager,
               pDb->safety_level | (db->flags & PAGER_FLAGS_MASK));
          }
        }
#endif
        if( isOpen==0 ){
          releasePage(pPage1);
          return SQLITE_OK;
        }
      }
      rc = SQLITE_NOTADB;
    }
#endif

    /* EVIDENCE-OF: R-15465-20813 The maximum and minimum embedded payload
    ** fractions and the leaf payload fraction values must be 64, 32, and 32.

Changes to src/main.c.

2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
  sqlite3BtreeLeave(db->aDb[0].pBt);
  db->aDb[1].pSchema = sqlite3SchemaGet(db, 0);

  /* The default safety_level for the main database is FULL; for the temp
  ** database it is OFF. This matches the pager layer defaults.  
  */
  db->aDb[0].zName = "main";
  db->aDb[0].safety_level = PAGER_SYNCHRONOUS_FULL;
  db->aDb[1].zName = "temp";
  db->aDb[1].safety_level = PAGER_SYNCHRONOUS_OFF;

  db->magic = SQLITE_MAGIC_OPEN;
  if( db->mallocFailed ){
    goto opendb_out;
  }







|







2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
  sqlite3BtreeLeave(db->aDb[0].pBt);
  db->aDb[1].pSchema = sqlite3SchemaGet(db, 0);

  /* The default safety_level for the main database is FULL; for the temp
  ** database it is OFF. This matches the pager layer defaults.  
  */
  db->aDb[0].zName = "main";
  db->aDb[0].safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1;
  db->aDb[1].zName = "temp";
  db->aDb[1].safety_level = PAGER_SYNCHRONOUS_OFF;

  db->magic = SQLITE_MAGIC_OPEN;
  if( db->mallocFailed ){
    goto opendb_out;
  }

Changes to src/pager.c.

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
....
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
** The maximum allowed sector size. 64KiB. If the xSectorsize() method 
** returns a value larger than this, then MAX_SECTOR_SIZE is used instead.
** This could conceivably cause corruption following a power failure on
** such a system. This is currently an undocumented limit.
*/
#define MAX_SECTOR_SIZE 0x10000

/*
** If the option SQLITE_EXTRA_DURABLE option is set at compile-time, then
** SQLite will do extra fsync() operations when synchronous==FULL to help
** ensure that transactions are durable across a power failure.  Most
** applications are happy as long as transactions are consistent across
** a power failure, and are perfectly willing to lose the last transaction
** in exchange for the extra performance of avoiding directory syncs.
** And so the default SQLITE_EXTRA_DURABLE setting is off.
*/
#ifndef SQLITE_EXTRA_DURABLE
# define SQLITE_EXTRA_DURABLE 0
#endif


/*
** An instance of the following structure is allocated for each active
** savepoint and statement transaction in the system. All such structures
** are stored in the Pager.aSavepoint[] array, which is allocated and
** resized using sqlite3Realloc().
**
................................................................................
    assert( pPager->fullSync==0 );
    assert( pPager->extraSync==0 );
    assert( pPager->syncFlags==0 );
    assert( pPager->walSyncFlags==0 );
    assert( pPager->ckptSyncFlags==0 );
  }else{
    pPager->fullSync = 1;
#if SQLITE_EXTRA_DURABLE
    pPager->extraSync = 1;
#else
    pPager->extraSync = 0;
#endif
    pPager->syncFlags = SQLITE_SYNC_NORMAL;
    pPager->walSyncFlags = SQLITE_SYNC_NORMAL | WAL_SYNC_TRANSACTIONS;
    pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL;
  }
  /* pPager->pFirst = 0; */
  /* pPager->pFirstSynced = 0; */
  /* pPager->pLast = 0; */







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







 







<
<
<

<







424
425
426
427
428
429
430













431
432
433
434
435
436
437
....
4821
4822
4823
4824
4825
4826
4827



4828

4829
4830
4831
4832
4833
4834
4835
** The maximum allowed sector size. 64KiB. If the xSectorsize() method 
** returns a value larger than this, then MAX_SECTOR_SIZE is used instead.
** This could conceivably cause corruption following a power failure on
** such a system. This is currently an undocumented limit.
*/
#define MAX_SECTOR_SIZE 0x10000















/*
** An instance of the following structure is allocated for each active
** savepoint and statement transaction in the system. All such structures
** are stored in the Pager.aSavepoint[] array, which is allocated and
** resized using sqlite3Realloc().
**
................................................................................
    assert( pPager->fullSync==0 );
    assert( pPager->extraSync==0 );
    assert( pPager->syncFlags==0 );
    assert( pPager->walSyncFlags==0 );
    assert( pPager->ckptSyncFlags==0 );
  }else{
    pPager->fullSync = 1;



    pPager->extraSync = 0;

    pPager->syncFlags = SQLITE_SYNC_NORMAL;
    pPager->walSyncFlags = SQLITE_SYNC_NORMAL | WAL_SYNC_TRANSACTIONS;
    pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL;
  }
  /* pPager->pFirst = 0; */
  /* pPager->pFirstSynced = 0; */
  /* pPager->pLast = 0; */

Changes to src/pcache1.c.

344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
...
361
362
363
364
365
366
367

368
369
370
371

372
373
374
375
376
377
378
  return p;
}

/*
** Free an allocated buffer obtained from pcache1Alloc().
*/
static void pcache1Free(void *p){
  int nFreed = 0;
  if( p==0 ) return;
  if( SQLITE_WITHIN(p, pcache1.pStart, pcache1.pEnd) ){
    PgFreeslot *pSlot;
    sqlite3_mutex_enter(pcache1.mutex);
    sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1);
    pSlot = (PgFreeslot*)p;
    pSlot->pNext = pcache1.pFree;
................................................................................
    pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve;
    assert( pcache1.nFreeSlot<=pcache1.nSlot );
    sqlite3_mutex_leave(pcache1.mutex);
  }else{
    assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
    sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS

    nFreed = sqlite3MallocSize(p);
    sqlite3_mutex_enter(pcache1.mutex);
    sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_OVERFLOW, nFreed);
    sqlite3_mutex_leave(pcache1.mutex);

#endif
    sqlite3_free(p);
  }
}

#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
/*







<







 







>
|
|
|
|
>







344
345
346
347
348
349
350

351
352
353
354
355
356
357
...
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
  return p;
}

/*
** Free an allocated buffer obtained from pcache1Alloc().
*/
static void pcache1Free(void *p){

  if( p==0 ) return;
  if( SQLITE_WITHIN(p, pcache1.pStart, pcache1.pEnd) ){
    PgFreeslot *pSlot;
    sqlite3_mutex_enter(pcache1.mutex);
    sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1);
    pSlot = (PgFreeslot*)p;
    pSlot->pNext = pcache1.pFree;
................................................................................
    pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve;
    assert( pcache1.nFreeSlot<=pcache1.nSlot );
    sqlite3_mutex_leave(pcache1.mutex);
  }else{
    assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
    sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS
    {
      int nFreed = sqlite3MallocSize(p);
      sqlite3_mutex_enter(pcache1.mutex);
      sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_OVERFLOW, nFreed);
      sqlite3_mutex_leave(pcache1.mutex);
    }
#endif
    sqlite3_free(p);
  }
}

#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
/*

Changes to src/pragma.c.

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
...
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012

1013
1014
1015
1016
1017
1018
1019
      }
    }
    if( eMode==PAGER_JOURNALMODE_QUERY && pId2->n==0 ){
      /* Convert "PRAGMA journal_mode" into "PRAGMA main.journal_mode" */
      iDb = 0;
      pId2->n = 1;
    }
#ifdef SQLITE_DEFAULT_WAL_SAFETYLEVEL
    if( ! SQLITE_DbSafetyLevelIsFixed(pDb->safety_level) ){
      if( eMode == PAGER_JOURNALMODE_WAL ){
        /* when entering wal mode, immediately switch the safety_level
        ** so that a query to pragma synchronous returns the correct value */
      
        pDb->safety_level = SQLITE_DEFAULT_WAL_SAFETYLEVEL;
      }else{
        /* If the user hasn't overridden the synchronous setting, use the 
        ** default for non-wal databases */
        pDb->safety_level = 3;
      }
    }
#endif /* SQLITE_DEFAULT_WAL_SAFETYLEVEL */
    for(ii=db->nDb-1; ii>=0; ii--){
      if( db->aDb[ii].pBt && (ii==iDb || pId2->n==0) ){
        sqlite3VdbeUsesBtree(v, ii);
        sqlite3VdbeAddOp3(v, OP_JournalMode, ii, 1, eMode);
      }
    }
    sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
................................................................................
  ** Return or set the local value of the synchronous flag.  Changing
  ** the local value does not make changes to the disk file and the
  ** default value will be restored the next time the database is
  ** opened.
  */
  case PragTyp_SYNCHRONOUS: {
    if( !zRight ){
      returnSingleInt(v, "synchronous", 
            SQLITE_DbSafetyLevelValue(pDb->safety_level)-1);
    }else{
      if( !db->autoCommit ){
        sqlite3ErrorMsg(pParse, 
            "Safety level may not be changed inside a transaction");
      }else{
        int iLevel = (getSafetyLevel(zRight,0,1)+1)
                                      | SQLITE_SAFETYLEVEL_FIXED;
        iLevel &= PAGER_SYNCHRONOUS_MASK;
        if( iLevel==0 ) iLevel = 1;
        pDb->safety_level = iLevel;

        setAllPagerFlags(db);
      }
    }
    break;
  }
#endif /* SQLITE_OMIT_PAGER_PRAGMAS */








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







 







|
<





|
<
<


>







616
617
618
619
620
621
622














623
624
625
626
627
628
629
...
980
981
982
983
984
985
986
987

988
989
990
991
992
993


994
995
996
997
998
999
1000
1001
1002
1003
      }
    }
    if( eMode==PAGER_JOURNALMODE_QUERY && pId2->n==0 ){
      /* Convert "PRAGMA journal_mode" into "PRAGMA main.journal_mode" */
      iDb = 0;
      pId2->n = 1;
    }














    for(ii=db->nDb-1; ii>=0; ii--){
      if( db->aDb[ii].pBt && (ii==iDb || pId2->n==0) ){
        sqlite3VdbeUsesBtree(v, ii);
        sqlite3VdbeAddOp3(v, OP_JournalMode, ii, 1, eMode);
      }
    }
    sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
................................................................................
  ** Return or set the local value of the synchronous flag.  Changing
  ** the local value does not make changes to the disk file and the
  ** default value will be restored the next time the database is
  ** opened.
  */
  case PragTyp_SYNCHRONOUS: {
    if( !zRight ){
      returnSingleInt(v, "synchronous", pDb->safety_level-1);

    }else{
      if( !db->autoCommit ){
        sqlite3ErrorMsg(pParse, 
            "Safety level may not be changed inside a transaction");
      }else{
        int iLevel = (getSafetyLevel(zRight,0,1)+1) & PAGER_SYNCHRONOUS_MASK;


        if( iLevel==0 ) iLevel = 1;
        pDb->safety_level = iLevel;
        pDb->bSyncSet = 1;
        setAllPagerFlags(db);
      }
    }
    break;
  }
#endif /* SQLITE_OMIT_PAGER_PRAGMAS */

Changes to src/sqliteInt.h.

998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014


1015
1016
1017



1018




















1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030

1031
1032
1033
1034
1035
1036
1037
** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque
** pointer types (i.e. FuncDef) defined above.
*/
#include "btree.h"
#include "vdbe.h"
#include "pager.h"
#include "pcache.h"

#include "os.h"
#include "mutex.h"

/* When using a default wal safety level, the safety level should only 
** change with the journal mode if the user hasn't manually specified 
** pragma synchronous, if they have the defaults shouldn't be applied.
** The SQLITE_SAFETYLEVEL_FIXED value is ORed into the Db->safety_level
** field when the user has specified a synchronous setting via pragma.
*/


#define SQLITE_SAFETYLEVEL_FIXED 0x10
#define SQLITE_SAFETYLEVEL_VALUE_MASK 0x07
#define SQLITE_DbSafetyLevelValue(level) (level&SQLITE_SAFETYLEVEL_VALUE_MASK)



#define SQLITE_DbSafetyLevelIsFixed(level) (level&SQLITE_SAFETYLEVEL_FIXED)





















/*
** Each database file to be accessed by the system is an instance
** of the following structure.  There are normally two of these structures
** in the sqlite.aDb[] array.  aDb[0] is the main database file and
** aDb[1] is the database file used to hold temporary tables.  Additional
** databases may be attached.
*/
struct Db {
  char *zName;         /* Name of this database */
  Btree *pBt;          /* The B*Tree structure for this database file */
  u8 safety_level;     /* How aggressive at syncing data to disk */

  Schema *pSchema;     /* Pointer to database schema (possibly shared) */
};

/*
** An instance of the following structure stores a database schema.
**
** Most Schema objects are associated with a Btree.  The exception is







<



|
|
<
<
<

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












>







998
999
1000
1001
1002
1003
1004

1005
1006
1007
1008
1009



1010
1011
1012
1013


1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque
** pointer types (i.e. FuncDef) defined above.
*/
#include "btree.h"
#include "vdbe.h"
#include "pager.h"
#include "pcache.h"

#include "os.h"
#include "mutex.h"

/* The SQLITE_EXTRA_DURABLE compile-time option used to set the default
** synchronous setting to EXTRA.  It is no longer supported.



*/
#ifdef SQLITE_EXTRA_DURABLE
# warning Use SQLITE_DEFAULT_SYNCHRONOUS=3 instead of SQLITE_EXTRA_DURABLE
# define SQLITE_DEFAULT_SYNCHRONOUS 3


#endif

/*
** Default synchronous levels.
**
** Note that (for historcal reasons) the PAGER_SYNCHRONOUS_* macros differ
** from the SQLITE_DEFAULT_SYNCHRONOUS value by 1.
**
**           PAGER_SYNCHRONOUS       DEFAULT_SYNCHRONOUS
**   OFF           1                         0
**   NORMAL        2                         1
**   FULL          3                         2
**   EXTRA         4                         3
**
** The "PRAGMA synchronous" statement also uses the zero-based numbers.
** In other words, the zero-based numbers are used for all external interfaces
** and the one-based values are used internally.
*/
#ifndef SQLITE_DEFAULT_SYNCHRONOUS
# define SQLITE_DEFAULT_SYNCHRONOUS (PAGER_SYNCHRONOUS_FULL-1)
#endif
#ifndef SQLITE_DEFAULT_WAL_SYNCHRONOUS
# define SQLITE_DEFAULT_WAL_SYNCHRONOUS SQLITE_DEFAULT_SYNCHRONOUS
#endif

/*
** Each database file to be accessed by the system is an instance
** of the following structure.  There are normally two of these structures
** in the sqlite.aDb[] array.  aDb[0] is the main database file and
** aDb[1] is the database file used to hold temporary tables.  Additional
** databases may be attached.
*/
struct Db {
  char *zName;         /* Name of this database */
  Btree *pBt;          /* The B*Tree structure for this database file */
  u8 safety_level;     /* How aggressive at syncing data to disk */
  u8 bSyncSet;         /* True if "PRAGMA synchronous=N" has been run */
  Schema *pSchema;     /* Pointer to database schema (possibly shared) */
};

/*
** An instance of the following structure stores a database schema.
**
** Most Schema objects are associated with a Btree.  The exception is

Changes to src/test1.c.

1898
1899
1900
1901
1902
1903
1904

1905
1906

1907
1908
1909
1910
1911
1912
1913

1914
1915
1916
1917

1918
1919
1920
1921
1922
1923
1924
  int objc,              /* Number of arguments */
  Tcl_Obj *CONST objv[]  /* Command arguments */
){
  Tcl_CmdInfo cmdInfo;
  sqlite3 *db;
  int rc;
  char *zDb;

  char *zFile;
  char *zProc = 0;

  char *zErr = 0;

  if( objc!=4 && objc!=3 ){
    Tcl_WrongNumArgs(interp, 1, objv, "DB-HANDLE FILE ?PROC?");
    return TCL_ERROR;
  }
  zDb = Tcl_GetString(objv[1]);

  zFile = Tcl_GetString(objv[2]);
  if( objc==4 ){
    zProc = Tcl_GetString(objv[3]);
  }


  /* Extract the C database handle from the Tcl command name */
  if( !Tcl_GetCommandInfo(interp, zDb, &cmdInfo) ){
    Tcl_AppendResult(interp, "command not found: ", zDb, (char*)0);
    return TCL_ERROR;
  }
  db = ((struct SqliteDb*)cmdInfo.objClientData)->db;







>


>







>




>







1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
  int objc,              /* Number of arguments */
  Tcl_Obj *CONST objv[]  /* Command arguments */
){
  Tcl_CmdInfo cmdInfo;
  sqlite3 *db;
  int rc;
  char *zDb;
#ifndef SQLITE_OMIT_LOAD_EXTENSION
  char *zFile;
  char *zProc = 0;
#endif
  char *zErr = 0;

  if( objc!=4 && objc!=3 ){
    Tcl_WrongNumArgs(interp, 1, objv, "DB-HANDLE FILE ?PROC?");
    return TCL_ERROR;
  }
  zDb = Tcl_GetString(objv[1]);
#ifndef SQLITE_OMIT_LOAD_EXTENSION
  zFile = Tcl_GetString(objv[2]);
  if( objc==4 ){
    zProc = Tcl_GetString(objv[3]);
  }
#endif

  /* Extract the C database handle from the Tcl command name */
  if( !Tcl_GetCommandInfo(interp, zDb, &cmdInfo) ){
    Tcl_AppendResult(interp, "command not found: ", zDb, (char*)0);
    return TCL_ERROR;
  }
  db = ((struct SqliteDb*)cmdInfo.objClientData)->db;

Changes to src/test_config.c.

561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
  Tcl_SetVar2(interp,"sqlite_options","enable_purgeable_pcache","0",TCL_GLOBAL_ONLY);
#endif
#if SQLITE_DEFAULT_CKPTFULLFSYNC
  Tcl_SetVar2(interp,"sqlite_options","default_ckptfullfsync","1",TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp,"sqlite_options","default_ckptfullfsync","0",TCL_GLOBAL_ONLY);
#endif
#if SQLITE_DEFAULT_WAL_SAFETYLEVEL
  Tcl_SetVar2(interp,"sqlite_options","default_wal_safetylevel",
              STRINGVALUE(SQLITE_DEFAULT_WAL_SAFETYLEVEL),TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp,"sqlite_options","default_wal_safetylevel","0",TCL_GLOBAL_ONLY);
#endif
#if SQLITE_ENABLE_PERSIST_WAL
  Tcl_SetVar2(interp,"sqlite_options","enable_persist_wal","1",TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp,"sqlite_options","enable_persist_wal","0",TCL_GLOBAL_ONLY);
#endif
    
#ifdef SQLITE_OMIT_SHARED_CACHE







<
<
<
<
<
<







561
562
563
564
565
566
567






568
569
570
571
572
573
574
  Tcl_SetVar2(interp,"sqlite_options","enable_purgeable_pcache","0",TCL_GLOBAL_ONLY);
#endif
#if SQLITE_DEFAULT_CKPTFULLFSYNC
  Tcl_SetVar2(interp,"sqlite_options","default_ckptfullfsync","1",TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp,"sqlite_options","default_ckptfullfsync","0",TCL_GLOBAL_ONLY);
#endif






#if SQLITE_ENABLE_PERSIST_WAL
  Tcl_SetVar2(interp,"sqlite_options","enable_persist_wal","1",TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp,"sqlite_options","enable_persist_wal","0",TCL_GLOBAL_ONLY);
#endif
    
#ifdef SQLITE_OMIT_SHARED_CACHE

Changes to src/wal.c.

2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
  PgHdr *pPage,               /* The page of the frame to be written */
  int nTruncate,              /* The commit flag.  Usually 0.  >0 for commit */
  sqlite3_int64 iOffset       /* Byte offset at which to write */
){
  int rc;                         /* Result code from subfunctions */
  void *pData;                    /* Data actually written */
#if defined(SQLITE_WRITE_WALFRAME_PREBUFFERED)
  void *aFrame;

  aFrame = p->aFrameBuf;
#else
  u8 aFrame[WAL_FRAME_HDRSIZE];   /* Buffer to assemble frame-header in */
#endif
  
#if defined(SQLITE_HAS_CODEC)
  if( (pData = sqlite3PagerCodec(pPage))==0 ) return SQLITE_NOMEM_BKPT;
#else







<
<
|







2886
2887
2888
2889
2890
2891
2892


2893
2894
2895
2896
2897
2898
2899
2900
  PgHdr *pPage,               /* The page of the frame to be written */
  int nTruncate,              /* The commit flag.  Usually 0.  >0 for commit */
  sqlite3_int64 iOffset       /* Byte offset at which to write */
){
  int rc;                         /* Result code from subfunctions */
  void *pData;                    /* Data actually written */
#if defined(SQLITE_WRITE_WALFRAME_PREBUFFERED)


  u8 *aFrame = p->aFrameBuf;
#else
  u8 aFrame[WAL_FRAME_HDRSIZE];   /* Buffer to assemble frame-header in */
#endif
  
#if defined(SQLITE_HAS_CODEC)
  if( (pData = sqlite3PagerCodec(pPage))==0 ) return SQLITE_NOMEM_BKPT;
#else

Changes to test/backcompat.test.

59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
...
416
417
418
419
420
421
422






423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
  code2 { sqlite3 db test.db }

  foreach c {code1 code2} {
    $c {
      set v [split [db version] .]
      if {[llength $v]==3} {lappend v 0}
      set ::sqlite_libversion [format \
        "%d%.2d%.2d%2d" [lindex $v 0] [lindex $v 1] [lindex $v 2] [lindex $v 3]
      ]
    }
  }

  uplevel $script

  catch { code1 { db close } }
................................................................................
          SELECT level, group_concat(idx, ' ') FROM t2_segdir GROUP BY level;
        }
      } {0 {0 1 2 3 4 5}}

      if {[code1 { set ::sqlite_libversion }] >=3071200 
       && [code2 { set ::sqlite_libversion }] >=3071200 
      } {






        do_test backcompat-3.9 {
          sql1 { INSERT INTO t2(t2) VALUES('merge=100,4'); }
          sql2 { INSERT INTO t2(t2) VALUES('merge=100,4'); }
          sql1 { INSERT INTO t2(t2) VALUES('merge=100,4'); }
          sql2 { INSERT INTO t2(t2) VALUES('merge=2500,4'); }
          sql2 {
            SELECT level, group_concat(idx, ' ') FROM t2_segdir GROUP BY level;
          }
        } {0 {0 1} 1 0}

        do_test backcompat-3.10 {
          sql1 { INSERT INTO t2(t2) VALUES('integrity-check') }
          sql2 { INSERT INTO t2(t2) VALUES('integrity-check') }
        } {}
      }
    }







|







 







>
>
>
>
>
>








|







59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
...
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
  code2 { sqlite3 db test.db }

  foreach c {code1 code2} {
    $c {
      set v [split [db version] .]
      if {[llength $v]==3} {lappend v 0}
      set ::sqlite_libversion [format \
        "%d%.2d%.2d%.2d" [lindex $v 0] [lindex $v 1] [lindex $v 2] [lindex $v 3]
      ]
    }
  }

  uplevel $script

  catch { code1 { db close } }
................................................................................
          SELECT level, group_concat(idx, ' ') FROM t2_segdir GROUP BY level;
        }
      } {0 {0 1 2 3 4 5}}

      if {[code1 { set ::sqlite_libversion }] >=3071200 
       && [code2 { set ::sqlite_libversion }] >=3071200 
      } {
        if {[code1 { set ::sqlite_libversion }]<3120000} {
          set res {0 {0 1} 1 0}
        } else {
          set res {1 0}
        }

        do_test backcompat-3.9 {
          sql1 { INSERT INTO t2(t2) VALUES('merge=100,4'); }
          sql2 { INSERT INTO t2(t2) VALUES('merge=100,4'); }
          sql1 { INSERT INTO t2(t2) VALUES('merge=100,4'); }
          sql2 { INSERT INTO t2(t2) VALUES('merge=2500,4'); }
          sql2 {
            SELECT level, group_concat(idx, ' ') FROM t2_segdir GROUP BY level;
          }
        } $res

        do_test backcompat-3.10 {
          sql1 { INSERT INTO t2(t2) VALUES('integrity-check') }
          sql2 { INSERT INTO t2(t2) VALUES('integrity-check') }
        } {}
      }
    }

Changes to test/fts4growth.test.

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
...
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
...
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
  } {
    execsql { INSERT INTO x1 VALUES($L) }
  }
  execsql { 
    INSERT INTO x1(x1) VALUES('merge=4,4');
    SELECT level, end_block, length(root) FROM x1_segdir;
  }
} {0 {0 110} 110 0 {0 132} 132 0 {0 129} 129 1 {128 658} 2}

do_execsql_test 1.5 {
  SELECT length(block) FROM x1_segments;
} {658 {}}

do_test 1.6 {
  foreach L {
    {'Twas Mulga Bill, from Eaglehawk, that sought his own abode,}
    {That perched above Dead Man's Creek, beside the mountain road.}
    {He turned the cycle down the hill and mounted for the fray,}
    {But 'ere he'd gone a dozen yards it bolted clean away.}

    {It left the track, and through the trees, just like a silver steak,}
    {It whistled down the awful slope towards the Dead Man's Creek.}
    {It shaved a stump by half an inch, it dodged a big white-box:}
    {The very wallaroos in fright went scrambling up the rocks,}

    {The wombats hiding in their caves dug deeper underground,}
    {As Mulga Bill, as white as chalk, sat tight to every bound.}
    {It struck a stone and gave a spring that cleared a fallen tree,}
    {It raced beside a precipice as close as close could be;}

    {And then as Mulga Bill let out one last despairing shriek}
    {It made a leap of twenty feet into the Dead Man's Creek.}



  } {
    execsql { INSERT INTO x1 VALUES($L) }
  }
  execsql { 
    SELECT level, end_block, length(root) FROM x1_segdir;
  }
} {1 {128 658} 2 1 {130 1377} 6 0 {0 117} 117}

do_execsql_test 1.7 {
  SELECT sum(length(block)) FROM x1_segments WHERE blockid IN (129, 130);
} {1377}

#-------------------------------------------------------------------------
#
do_execsql_test 2.1 { 
  CREATE TABLE t1(docid, words);
  CREATE VIRTUAL TABLE x2 USING fts4;
}
................................................................................

do_execsql_test 2.5 { 
  SELECT end_block FROM x2_segdir WHERE level=3;
  INSERT INTO x2(x2) VALUES('merge=4,4');
  SELECT end_block FROM x2_segdir WHERE level=3;
  INSERT INTO x2(x2) VALUES('merge=4,4');
  SELECT end_block FROM x2_segdir WHERE level=3;
} {{3828 -3430} {3828 -10191} {3828 -14109}}

do_execsql_test 2.6 {
  SELECT sum(length(block)) FROM x2_segdir, x2_segments WHERE 
    blockid BETWEEN start_block AND leaves_end_block
    AND level=3
} {14109}

do_execsql_test 2.7 { 
  INSERT INTO x2(x2) VALUES('merge=1000,4');
  SELECT end_block FROM x2_segdir WHERE level=3;
} {{3828 86120}}

do_execsql_test 2.8 {
  SELECT sum(length(block)) FROM x2_segdir, x2_segments WHERE 
    blockid BETWEEN start_block AND leaves_end_block
    AND level=3
} {86120}

#--------------------------------------------------------------------------
# Test that delete markers are removed from FTS segments when possible.
# It is only possible to remove delete markers when the output of the
# merge operation will become the oldest segment in the index.
#
#   3.1 - when the oldest segment is created by an 'optimize'.
................................................................................

do_execsql_test 7.2 {
  INSERT INTO x6(x6) VALUES('merge=25,4');
  SELECT level, idx, end_block FROM x6_segdir;
} {
  0 0 {118 117483} 0 1 {238 118006} 0 2 {358 118006} 
  0 3 {478 118006} 0 4 {598 118006} 0 5 {718 118006}
  1 0 {16014 -51226}
}

do_execsql_test 7.3 {
  UPDATE x6_segdir SET end_block = first(end_block) WHERE level=1;
  SELECT level, idx, end_block FROM x6_segdir;
} {
  0 0 {118 117483} 0 1 {238 118006} 0 2 {358 118006} 
  0 3 {478 118006} 0 4 {598 118006} 0 5 {718 118006}
  1 0 16014
}

do_execsql_test 7.4 {
  INSERT INTO x6(x6) VALUES('merge=25,4');
  SELECT level, idx, end_block FROM x6_segdir;
} {
  0 0 {118 117483} 0 1 {238 118006} 0 2 {358 118006} 
  0 3 {478 118006} 0 4 {598 118006} 0 5 {718 118006}
  1 0 16014
}

do_execsql_test 7.5 {
  INSERT INTO x6(x6) VALUES('merge=2500,4');
  SELECT level, idx, end_block FROM x6_segdir;
} {
  0 0 {598 118006} 0 1 {718 118006} 1 0 16014
}

do_execsql_test 7.6 {
  INSERT INTO x6(x6) VALUES('merge=2500,2');
  SELECT level, idx, start_block, leaves_end_block, end_block FROM x6_segdir;
} {
  2 0 23695 24147 {41262 633507}
}

do_execsql_test 7.7 {
  SELECT sum(length(block)) FROM x6_segments 
  WHERE blockid BETWEEN 23695 AND 24147
} {633507}



finish_test







|



|







>




>




>


>
>
>






|


|
|







 







|





|




|





|







 







|








|








|




|

|






|




<
|
<



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
...
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
...
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
  } {
    execsql { INSERT INTO x1 VALUES($L) }
  }
  execsql { 
    INSERT INTO x1(x1) VALUES('merge=4,4');
    SELECT level, end_block, length(root) FROM x1_segdir;
  }
} {1 {224 921} 2}

do_execsql_test 1.5 {
  SELECT length(block) FROM x1_segments;
} {921 {}}

do_test 1.6 {
  foreach L {
    {'Twas Mulga Bill, from Eaglehawk, that sought his own abode,}
    {That perched above Dead Man's Creek, beside the mountain road.}
    {He turned the cycle down the hill and mounted for the fray,}
    {But 'ere he'd gone a dozen yards it bolted clean away.}

    {It left the track, and through the trees, just like a silver steak,}
    {It whistled down the awful slope towards the Dead Man's Creek.}
    {It shaved a stump by half an inch, it dodged a big white-box:}
    {The very wallaroos in fright went scrambling up the rocks,}

    {The wombats hiding in their caves dug deeper underground,}
    {As Mulga Bill, as white as chalk, sat tight to every bound.}
    {It struck a stone and gave a spring that cleared a fallen tree,}
    {It raced beside a precipice as close as close could be;}

    {And then as Mulga Bill let out one last despairing shriek}
    {It made a leap of twenty feet into the Dead Man's Creek.}
    {It shaved a stump by half an inch, it dodged a big white-box:}
    {The very wallaroos in fright went scrambling up the rocks,}
    {The wombats hiding in their caves dug deeper underground,}
  } {
    execsql { INSERT INTO x1 VALUES($L) }
  }
  execsql { 
    SELECT level, end_block, length(root) FROM x1_segdir;
  }
} {1 {224 921} 2 1 {226 1230} 7 0 {0 98} 98}

do_execsql_test 1.7 {
  SELECT sum(length(block)) FROM x1_segments WHERE blockid IN (224,225,226)
} {1230}

#-------------------------------------------------------------------------
#
do_execsql_test 2.1 { 
  CREATE TABLE t1(docid, words);
  CREATE VIRTUAL TABLE x2 USING fts4;
}
................................................................................

do_execsql_test 2.5 { 
  SELECT end_block FROM x2_segdir WHERE level=3;
  INSERT INTO x2(x2) VALUES('merge=4,4');
  SELECT end_block FROM x2_segdir WHERE level=3;
  INSERT INTO x2(x2) VALUES('merge=4,4');
  SELECT end_block FROM x2_segdir WHERE level=3;
} {{5588 -3950} {5588 -11766} {5588 -15541}}

do_execsql_test 2.6 {
  SELECT sum(length(block)) FROM x2_segdir, x2_segments WHERE 
    blockid BETWEEN start_block AND leaves_end_block
    AND level=3
} {15541}

do_execsql_test 2.7 { 
  INSERT INTO x2(x2) VALUES('merge=1000,4');
  SELECT end_block FROM x2_segdir WHERE level=3;
} {{5588 127563}}

do_execsql_test 2.8 {
  SELECT sum(length(block)) FROM x2_segdir, x2_segments WHERE 
    blockid BETWEEN start_block AND leaves_end_block
    AND level=3
} {127563}

#--------------------------------------------------------------------------
# Test that delete markers are removed from FTS segments when possible.
# It is only possible to remove delete markers when the output of the
# merge operation will become the oldest segment in the index.
#
#   3.1 - when the oldest segment is created by an 'optimize'.
................................................................................

do_execsql_test 7.2 {
  INSERT INTO x6(x6) VALUES('merge=25,4');
  SELECT level, idx, end_block FROM x6_segdir;
} {
  0 0 {118 117483} 0 1 {238 118006} 0 2 {358 118006} 
  0 3 {478 118006} 0 4 {598 118006} 0 5 {718 118006}
  1 0 {23694 -69477}
}

do_execsql_test 7.3 {
  UPDATE x6_segdir SET end_block = first(end_block) WHERE level=1;
  SELECT level, idx, end_block FROM x6_segdir;
} {
  0 0 {118 117483} 0 1 {238 118006} 0 2 {358 118006} 
  0 3 {478 118006} 0 4 {598 118006} 0 5 {718 118006}
  1 0 23694
}

do_execsql_test 7.4 {
  INSERT INTO x6(x6) VALUES('merge=25,4');
  SELECT level, idx, end_block FROM x6_segdir;
} {
  0 0 {118 117483} 0 1 {238 118006} 0 2 {358 118006} 
  0 3 {478 118006} 0 4 {598 118006} 0 5 {718 118006}
  1 0 23694
}

do_execsql_test 7.5 {
  INSERT INTO x6(x6) VALUES('merge=2500,4');
  SELECT level, idx, start_block, leaves_end_block, end_block FROM x6_segdir;
} {
  1 0 719 1171 23694
}

do_execsql_test 7.6 {
  INSERT INTO x6(x6) VALUES('merge=2500,2');
  SELECT level, idx, start_block, leaves_end_block, end_block FROM x6_segdir;
} {
  1 0 719 1171 23694
}

do_execsql_test 7.7 {
  SELECT sum(length(block)) FROM x6_segments 

} {635247}



finish_test

Changes to test/fts4langid.test.

477
478
479
480
481
482
483
484
485
486
    INSERT INTO t6(t6) VALUES('merge=100,3');
    SELECT docid FROM t6 WHERE t6 MATCH '"zero zero"' AND lid=$lid;
  } {1 2 5}

  do_execsql_test 5.4.$lid.5 {
    SELECT count(*) FROM t6_segdir;
    SELECT count(*) FROM t6_segments;
  } {4 4}
}
finish_test







|


477
478
479
480
481
482
483
484
485
486
    INSERT INTO t6(t6) VALUES('merge=100,3');
    SELECT docid FROM t6 WHERE t6 MATCH '"zero zero"' AND lid=$lid;
  } {1 2 5}

  do_execsql_test 5.4.$lid.5 {
    SELECT count(*) FROM t6_segdir;
    SELECT count(*) FROM t6_segments;
  } {1 2}
}
finish_test

Changes to test/fts4merge.test.

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
...
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
...
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
...
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
      SELECT docid FROM t1 WHERE t1 MATCH 'zero one two three'
    } {123 132 213 231 312 321}
  }
  
  do_execsql_test 1.3 { 
    SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level 
  } {
    0 {0 1 2 3} 
    1 {0 1 2 3 4 5 6} 
    2 {0 1 2 3}
  }
  
  for {set i 0} {$i<100} {incr i} {
    do_execsql_test 1.4.$i { INSERT INTO t1(t1) VALUES('merge=1,4') }
    do_test 1.4.$i.2 { fts3_integrity_check t1 } ok
    do_execsql_test 1.4.$i.3 { 
................................................................................
      SELECT docid FROM t1 WHERE t1 MATCH 'zero one two three'
    } {123 132 213 231 312 321}
  }
  
  do_execsql_test 1.5 { 
    SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level 
  } {
    2 {0 1}
    3 0
  }
  
  #-------------------------------------------------------------------------
  # Test cases 2.* test that errors in the xxx part of the 'merge=xxx' are
  # handled correctly.
  #
................................................................................
    3 {0 1 2 3 4 5 6}
  }
  
  do_execsql_test 3.3 { 
    INSERT INTO t2(t2) VALUES('merge=1000000,2');
    SELECT level, group_concat(idx, ' ') FROM t2_segdir GROUP BY level 
  } {
    0 0 
    2 0
    3 0 
    4 0
    6 0
  }
  
  #-------------------------------------------------------------------------
  # Test cases 4.*
  #
  reset_db
  do_execsql_test 4.1 "
................................................................................
  }
  
  do_execsql_test 5.3 {
    INSERT INTO t1(t1) VALUES('merge=1,5');
    INSERT INTO t1(t1) VALUES('merge=1,5');
    SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level;
  } {
    0 {0 1 2}
    1 {0 1 2 3 4 5 6 7 8 9 10 11 12 13 14} 
    2 {0 1 2 3}
  }
  
  do_execsql_test 5.4 {SELECT quote(value) from t1_stat WHERE rowid=1} {X'0105'}
  do_test 5.5 {
    foreach docid [execsql {SELECT docid FROM t1}] {
      execsql {INSERT INTO t1 SELECT * FROM t1 WHERE docid=$docid}
    }
  } {}
  
  do_execsql_test 5.6 {SELECT quote(value) from t1_stat WHERE rowid=1} {X'0105'}
  
  do_execsql_test 5.7 {
    SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level;
    SELECT quote(value) from t1_stat WHERE rowid=1;
  } {
    0 {0 1 2 3 4 5 6 7 8 9 10} 
    1 {0 1 2 3 4 5 6 7 8 9 10 11 12} 
    2 {0 1 2 3 4 5 6 7}
    X'0105'
  }
  
  do_execsql_test 5.8 {
    INSERT INTO t1(t1) VALUES('merge=1,6');
    INSERT INTO t1(t1) VALUES('merge=1,6');
    SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level;
    SELECT quote(value) from t1_stat WHERE rowid=1;
  } {
    0 {0 1 2 3 4} 
    1 {0 1 2 3 4 5 6 7 8 9 10 11 12 13} 
    2 {0 1 2 3 4 5 6 7 8} X'0106'
  }
  
  do_test 5.8.1 { fts3_integrity_check t1 } ok
  
  do_test 5.9 {
    set L [expr 16*16*7 + 16*3 + 12]
    foreach docid [execsql {
................................................................................
    }
  } {}
  
  do_execsql_test 5.10 {
    SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level;
    SELECT quote(value) from t1_stat WHERE rowid=1;
  } {
    0 0 1 {0 1} 2 0 3 0 X'0106'
  }
  
  do_execsql_test 5.11 {
    INSERT INTO t1(t1) VALUES('merge=1,6');
    SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level;
    SELECT quote(value) from t1_stat WHERE rowid=1;
  } {
    0 0 1 {0 1} 2 0 3 0 X''
  }
  
  #-------------------------------------------------------------------------
  # Test cases 6.*
  #
  # At one point the following test caused an assert() to fail (because the
  # second 'merge=1,2' operation below actually "merges" a single input







<
<







 







<







 







<
<
<

<







 







<




|






|





|

|
|








<

|







 







|







|







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
...
111
112
113
114
115
116
117



118

119
120
121
122
123
124
125
...
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
...
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
      SELECT docid FROM t1 WHERE t1 MATCH 'zero one two three'
    } {123 132 213 231 312 321}
  }
  
  do_execsql_test 1.3 { 
    SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level 
  } {


    2 {0 1 2 3}
  }
  
  for {set i 0} {$i<100} {incr i} {
    do_execsql_test 1.4.$i { INSERT INTO t1(t1) VALUES('merge=1,4') }
    do_test 1.4.$i.2 { fts3_integrity_check t1 } ok
    do_execsql_test 1.4.$i.3 { 
................................................................................
      SELECT docid FROM t1 WHERE t1 MATCH 'zero one two three'
    } {123 132 213 231 312 321}
  }
  
  do_execsql_test 1.5 { 
    SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level 
  } {

    3 0
  }
  
  #-------------------------------------------------------------------------
  # Test cases 2.* test that errors in the xxx part of the 'merge=xxx' are
  # handled correctly.
  #
................................................................................
    3 {0 1 2 3 4 5 6}
  }
  
  do_execsql_test 3.3 { 
    INSERT INTO t2(t2) VALUES('merge=1000000,2');
    SELECT level, group_concat(idx, ' ') FROM t2_segdir GROUP BY level 
  } {



    4 0

  }
  
  #-------------------------------------------------------------------------
  # Test cases 4.*
  #
  reset_db
  do_execsql_test 4.1 "
................................................................................
  }
  
  do_execsql_test 5.3 {
    INSERT INTO t1(t1) VALUES('merge=1,5');
    INSERT INTO t1(t1) VALUES('merge=1,5');
    SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level;
  } {

    1 {0 1 2 3 4 5 6 7 8 9 10 11 12 13 14} 
    2 {0 1 2 3}
  }
  
  do_execsql_test 5.4 {SELECT quote(value) from t1_stat WHERE rowid=1} {X'010F'}
  do_test 5.5 {
    foreach docid [execsql {SELECT docid FROM t1}] {
      execsql {INSERT INTO t1 SELECT * FROM t1 WHERE docid=$docid}
    }
  } {}
  
  do_execsql_test 5.6 {SELECT quote(value) from t1_stat WHERE rowid=1} {X'010F'}
  
  do_execsql_test 5.7 {
    SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level;
    SELECT quote(value) from t1_stat WHERE rowid=1;
  } {
    0 {0 1 2 3 4 5 6 7} 
    1 {0 1 2 3 4 5 6 7 8 9 10 11 12} 
    2 {0 1 2 3 4 5 6 7} 
    X'010F'
  }
  
  do_execsql_test 5.8 {
    INSERT INTO t1(t1) VALUES('merge=1,6');
    INSERT INTO t1(t1) VALUES('merge=1,6');
    SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level;
    SELECT quote(value) from t1_stat WHERE rowid=1;
  } {

    1 {0 1 2 3 4 5 6 7 8 9 10 11 12 13} 
    2 {0 1 2 3 4 5 6 7 8} X'010E'
  }
  
  do_test 5.8.1 { fts3_integrity_check t1 } ok
  
  do_test 5.9 {
    set L [expr 16*16*7 + 16*3 + 12]
    foreach docid [execsql {
................................................................................
    }
  } {}
  
  do_execsql_test 5.10 {
    SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level;
    SELECT quote(value) from t1_stat WHERE rowid=1;
  } {
    0 {0 1 2 3 4 5 6 7 8 9 10 11} 1 0 2 0 3 0 X'010E'
  }
  
  do_execsql_test 5.11 {
    INSERT INTO t1(t1) VALUES('merge=1,6');
    SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level;
    SELECT quote(value) from t1_stat WHERE rowid=1;
  } {
    1 {0 1} 2 0 3 0 X'010E'
  }
  
  #-------------------------------------------------------------------------
  # Test cases 6.*
  #
  # At one point the following test caused an assert() to fail (because the
  # second 'merge=1,2' operation below actually "merges" a single input

Changes to test/fts4merge3.test.

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
..
93
94
95
96
97
98
99
100
101
102
103
104
105
      do_test 1.6.$i.2 { 
        sql2 "SELECT docid FROM t2 WHERE t2 MATCH 'abc'" 
      } {1485}
    }

    do_test 1.7 { sql2 { 
      SELECT level, count(*) FROM t2_segdir GROUP BY level ORDER BY 1
    } } [list  0 1  2 18  3 5]

    # Using the old connection, insert many rows. 
    do_test 1.8 {
      for {set i 0} {$i < 1500} {incr i} {
        sql2 "INSERT INTO t2 SELECT content FROM t2 WHERE docid = $i"
      }
    } {}

    do_test 1.9 { sql2 { 
      SELECT level, count(*) FROM t2_segdir GROUP BY level ORDER BY 1
    } } [list  0 13  1 13  2 5  3 6]

    # Run a big incr-merge operation on the db.
    do_test 1.10 { sql1 { INSERT INTO t2(t2) VALUES('merge=2000,2') } } {}
    do_test 1.11 { 
      sql2 "SELECT docid FROM t2 WHERE t2 MATCH 'abc'" 
    } {1485 21485}

................................................................................
    do_test 1.14 { 
      sql2 "INSERT INTO t2(t2) VALUES('optimize')"
      sql2 "SELECT docid FROM t2 WHERE t2 MATCH 'abc'" 
    } {1485 21485 22985}

    do_test 1.15 { sql2 { 
      SELECT level, count(*) FROM t2_segdir GROUP BY level ORDER BY 1
    } } {6 1}
  }
}


finish_test







|










|







 







|





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
..
93
94
95
96
97
98
99
100
101
102
103
104
105
      do_test 1.6.$i.2 { 
        sql2 "SELECT docid FROM t2 WHERE t2 MATCH 'abc'" 
      } {1485}
    }

    do_test 1.7 { sql2 { 
      SELECT level, count(*) FROM t2_segdir GROUP BY level ORDER BY 1
    } } {2 15 3 5}

    # Using the old connection, insert many rows. 
    do_test 1.8 {
      for {set i 0} {$i < 1500} {incr i} {
        sql2 "INSERT INTO t2 SELECT content FROM t2 WHERE docid = $i"
      }
    } {}

    do_test 1.9 { sql2 { 
      SELECT level, count(*) FROM t2_segdir GROUP BY level ORDER BY 1
    } } [list  0 12  1 13  2 4  3 6]

    # Run a big incr-merge operation on the db.
    do_test 1.10 { sql1 { INSERT INTO t2(t2) VALUES('merge=2000,2') } } {}
    do_test 1.11 { 
      sql2 "SELECT docid FROM t2 WHERE t2 MATCH 'abc'" 
    } {1485 21485}

................................................................................
    do_test 1.14 { 
      sql2 "INSERT INTO t2(t2) VALUES('optimize')"
      sql2 "SELECT docid FROM t2 WHERE t2 MATCH 'abc'" 
    } {1485 21485 22985}

    do_test 1.15 { sql2 { 
      SELECT level, count(*) FROM t2_segdir GROUP BY level ORDER BY 1
    } } {4 1}
  }
}


finish_test

Changes to test/fts4opt.test.

88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
...
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
  2080 33 
  3104 33
}

do_test 1.6 {
  while 1 {
    set tc1 [db total_changes]
    execsql { INSERT INTO t2(t2) VALUES('merge=5,0') }
    set tc2 [db total_changes]
    if {($tc2 - $tc1) < 2} break
  }
  execsql { SELECT level, count(*) FROM t2_segdir GROUP BY level }
} {33 1 1057 1 2081 1 3105 1}
do_execsql_test 1.7 { INSERT INTO t2(t2) VALUES('integrity-check') }

................................................................................
  2080 37 
  3104 37
}

do_test 2.6 {
  while 1 {
    set tc1 [db total_changes]
    execsql { INSERT INTO t2(t2) VALUES('merge=5,0') }
    set tc2 [db total_changes]
    if {($tc2 - $tc1) < 2} break
  }
  execsql { SELECT level, count(*) FROM t2_segdir GROUP BY level }
} {33 1 1057 1 2081 1 3105 1}
do_execsql_test 2.7 { INSERT INTO t2(t2) VALUES('integrity-check') }

do_execsql_test 2.8 {
  INSERT INTO t2(words) SELECT words FROM t1;
  SELECT level, count(*) FROM t2_segdir GROUP BY level;
} {0 2 1024 2 2048 2 3072 2}

finish_test







|







 







|













88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
...
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
  2080 33 
  3104 33
}

do_test 1.6 {
  while 1 {
    set tc1 [db total_changes]
    execsql { INSERT INTO t2(t2) VALUES('merge=5,2') }
    set tc2 [db total_changes]
    if {($tc2 - $tc1) < 2} break
  }
  execsql { SELECT level, count(*) FROM t2_segdir GROUP BY level }
} {33 1 1057 1 2081 1 3105 1}
do_execsql_test 1.7 { INSERT INTO t2(t2) VALUES('integrity-check') }

................................................................................
  2080 37 
  3104 37
}

do_test 2.6 {
  while 1 {
    set tc1 [db total_changes]
    execsql { INSERT INTO t2(t2) VALUES('merge=5,2') }
    set tc2 [db total_changes]
    if {($tc2 - $tc1) < 2} break
  }
  execsql { SELECT level, count(*) FROM t2_segdir GROUP BY level }
} {33 1 1057 1 2081 1 3105 1}
do_execsql_test 2.7 { INSERT INTO t2(t2) VALUES('integrity-check') }

do_execsql_test 2.8 {
  INSERT INTO t2(words) SELECT words FROM t1;
  SELECT level, count(*) FROM t2_segdir GROUP BY level;
} {0 2 1024 2 2048 2 3072 2}

finish_test

Changes to test/unixexcl.test.

83
84
85
86
87
88
89

90
91
92
93
94
95
96
do_multiclient_test tn {
  do_test unixexcl-3.$tn.1 {
    code1 { db close; sqlite3 db file:test.db?psow=0 -vfs unix-excl -uri 1 }
    code2 { db2 close; sqlite3 db2 file:test.db?psow=0 -vfs unix-excl -uri 1 }
    sql1 {
      PRAGMA auto_vacuum = 0;
      PRAGMA journal_mode = WAL;

      CREATE TABLE t1(a, b);
      INSERT INTO t1 VALUES(1, 2);
    }
  } {wal}

  if {$tn==1} {
    do_test unixexcl-3.$tn.1.multiproc {







>







83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
do_multiclient_test tn {
  do_test unixexcl-3.$tn.1 {
    code1 { db close; sqlite3 db file:test.db?psow=0 -vfs unix-excl -uri 1 }
    code2 { db2 close; sqlite3 db2 file:test.db?psow=0 -vfs unix-excl -uri 1 }
    sql1 {
      PRAGMA auto_vacuum = 0;
      PRAGMA journal_mode = WAL;
      PRAGMA synchronous = FULL;
      CREATE TABLE t1(a, b);
      INSERT INTO t1 VALUES(1, 2);
    }
  } {wal}

  if {$tn==1} {
    do_test unixexcl-3.$tn.1.multiproc {

Changes to test/wal2.test.

1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
    catch { db close }
  }
}

#-------------------------------------------------------------------------
# Test that "PRAGMA checkpoint_fullsync" appears to be working.
#
foreach {tn sql reslist altreslist} {
  1 { }                                 {10 0 4 0 6 0} {7 4 3 2 3 2}
  2 { PRAGMA checkpoint_fullfsync = 1 } {10 4 4 2 6 2} {7 4 3 2 3 2}
  3 { PRAGMA checkpoint_fullfsync = 0 } {10 0 4 0 6 0} {7 0 3 0 3 0}
} {
  faultsim_delete_and_reopen

  execsql {PRAGMA auto_vacuum = 0}
  execsql $sql
  do_execsql_test wal2-14.$tn.0 { PRAGMA page_size = 4096 }   {}
  do_execsql_test wal2-14.$tn.1 { PRAGMA journal_mode = WAL } {wal}

  set sqlite_sync_count 0
  set sqlite_fullsync_count 0

  set useres $reslist
  if $::sqlite_options(default_wal_safetylevel) {
    set useres $altreslist
  }

  do_execsql_test wal2-14.$tn.2 {
    PRAGMA wal_autocheckpoint = 10;
    CREATE TABLE t1(a, b);                -- 2 wal syncs
    INSERT INTO t1 VALUES(1, 2);          -- 2 wal sync
    PRAGMA wal_checkpoint;                -- 1 wal sync, 1 db sync
    BEGIN;







|
|
|
|



|








<
<
<







1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244



1245
1246
1247
1248
1249
1250
1251
    catch { db close }
  }
}

#-------------------------------------------------------------------------
# Test that "PRAGMA checkpoint_fullsync" appears to be working.
#
foreach {tn sql reslist} {
  1 { }                                 {10 0 4 0 6 0}
  2 { PRAGMA checkpoint_fullfsync = 1 } {10 4 4 2 6 2}
  3 { PRAGMA checkpoint_fullfsync = 0 } {10 0 4 0 6 0}
} {
  faultsim_delete_and_reopen

  execsql {PRAGMA auto_vacuum = 0; PRAGMA synchronous = FULL;}
  execsql $sql
  do_execsql_test wal2-14.$tn.0 { PRAGMA page_size = 4096 }   {}
  do_execsql_test wal2-14.$tn.1 { PRAGMA journal_mode = WAL } {wal}

  set sqlite_sync_count 0
  set sqlite_fullsync_count 0

  set useres $reslist




  do_execsql_test wal2-14.$tn.2 {
    PRAGMA wal_autocheckpoint = 10;
    CREATE TABLE t1(a, b);                -- 2 wal syncs
    INSERT INTO t1 VALUES(1, 2);          -- 2 wal sync
    PRAGMA wal_checkpoint;                -- 1 wal sync, 1 db sync
    BEGIN;

Changes to test/wal3.test.

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
#   CREATE TABLE x(y);
#   INSERT INTO x VALUES('z');
#   PRAGMA wal_checkpoint;
#
# in WAL mode the xSync method is invoked as expected for each of
# synchronous=off, synchronous=normal and synchronous=full.
#
foreach {tn syncmode synccount altsynccount} {
  1 off     
    {}
    {}
  2 normal  
    {test.db-wal normal test.db normal}
    {test.db-wal full test.db full}
  3 full    
    {test.db-wal normal test.db-wal normal test.db-wal normal test.db normal}
    {test.db-wal normal test.db-wal normal test.db-wal full test.db full}
} {

  proc sync_counter {args} { 
    foreach {method filename id flags} $args break
    lappend ::syncs [file tail $filename] $flags
  }
  set usecount $synccount
  if $::sqlite_options(default_wal_safetylevel) {
    set usecount $altsynccount
  }

  do_test wal3-3.$tn {
    forcedelete test.db test.db-wal test.db-journal
  
    testvfs T
    T filter {} 
    T script sync_counter







|

<



<


<







<
<
<







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
#   CREATE TABLE x(y);
#   INSERT INTO x VALUES('z');
#   PRAGMA wal_checkpoint;
#
# in WAL mode the xSync method is invoked as expected for each of
# synchronous=off, synchronous=normal and synchronous=full.
#
foreach {tn syncmode synccount} {
  1 off     

    {}
  2 normal  
    {test.db-wal normal test.db normal}

  3 full    
    {test.db-wal normal test.db-wal normal test.db-wal normal test.db normal}

} {

  proc sync_counter {args} { 
    foreach {method filename id flags} $args break
    lappend ::syncs [file tail $filename] $flags
  }
  set usecount $synccount




  do_test wal3-3.$tn {
    forcedelete test.db test.db-wal test.db-journal
  
    testvfs T
    T filter {} 
    T script sync_counter

Changes to test/zerodamage.test.

108
109
110
111
112
113
114

115
116
117
118
119
120
121
  # Repeat the previous with POWERSAFE_OVERWRITE off.  Verify that the WAL file
  # is padded.
  #
  do_test zerodamage-3.1 {
    db close
    sqlite3 db file:test.db?psow=FALSE -uri 1
    db eval {

       UPDATE t1 SET y=randomblob(50) WHERE x=124;
    }
    file size test.db-wal
  } {16800}
}

finish_test







>







108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
  # Repeat the previous with POWERSAFE_OVERWRITE off.  Verify that the WAL file
  # is padded.
  #
  do_test zerodamage-3.1 {
    db close
    sqlite3 db file:test.db?psow=FALSE -uri 1
    db eval {
       PRAGMA synchronous=FULL;
       UPDATE t1 SET y=randomblob(50) WHERE x=124;
    }
    file size test.db-wal
  } {16800}
}

finish_test