gitlab.com/CoiaPrant/sqlite3@v1.19.1/testdata/tcl/incrvacuum.test (about)

     1  # 2007 April 26
     2  #
     3  # The author disclaims copyright to this source code.  In place of
     4  # a legal notice, here is a blessing:
     5  #
     6  #    May you do good and not evil.
     7  #    May you find forgiveness for yourself and forgive others.
     8  #    May you share freely, never taking more than you give.
     9  #
    10  #***********************************************************************
    11  # This file implements regression tests for SQLite library.  The
    12  # focus of this file is testing the incremental vacuum feature.
    13  #
    14  # Note: There are also some tests for incremental vacuum and IO 
    15  # errors in incrvacuum_ioerr.test.
    16  #
    17  # $Id: incrvacuum.test,v 1.23 2009/02/18 20:31:18 drh Exp $
    18  
    19  set testdir [file dirname $argv0]
    20  source $testdir/tester.tcl
    21  
    22  # If this build of the library does not support auto-vacuum, omit this
    23  # whole file.
    24  ifcapable {!autovacuum || !pragma} {
    25    finish_test
    26    return
    27  }
    28  
    29  #---------------------------------------------------------------------
    30  # Test the pragma on an empty database.
    31  #
    32  do_test incrvacuum-1.1 {
    33    execsql {
    34      pragma auto_vacuum;
    35    }
    36  } $sqlite_options(default_autovacuum)
    37  do_test incrvacuum-1.2.0 {
    38    # File size is sometimes 1 instead of 0 due to the hack we put in
    39    # to work around ticket #3260.  Search for comments on #3260 in
    40    # os_unix.c.
    41    expr {[file size test.db] > 1}
    42  } {0}
    43  do_test incrvacuum-1.2 {
    44    # This command will create the database.
    45    execsql {
    46      pragma auto_vacuum = 'full';
    47      pragma auto_vacuum;
    48    }
    49  } {1}
    50  do_test incrvacuum-1.2.1 {
    51    expr {[file size test.db] > 0}
    52  } {1}
    53  do_test incrvacuum-1.3 {
    54    execsql {
    55      pragma auto_vacuum = 'incremental';
    56      pragma auto_vacuum;
    57    }
    58  } {2}
    59  do_test incrvacuum-1.4 {
    60    # In this case the invalid value is ignored and the auto_vacuum
    61    # setting remains unchanged.
    62    execsql {
    63      pragma auto_vacuum = 'invalid';
    64      pragma auto_vacuum;
    65    }
    66  } {2}
    67  do_test incrvacuum-1.5 {
    68    execsql {
    69      pragma auto_vacuum = 1;
    70      pragma auto_vacuum;
    71    }
    72  } {1}
    73  do_test incrvacuum-1.6 {
    74    execsql {
    75      pragma auto_vacuum = '2';
    76      pragma auto_vacuum;
    77    }
    78  } {2}
    79  do_test incrvacuum-1.7 {
    80    # Invalid value. auto_vacuum setting remains unchanged.
    81    execsql {
    82      pragma auto_vacuum = 5;
    83      pragma auto_vacuum;
    84    }
    85  } {2}
    86  
    87  #---------------------------------------------------------------------
    88  # Test the pragma on a non-empty database. It is possible to toggle
    89  # the connection between "full" and "incremental" mode, but not to
    90  # change from either of these to "none", or from "none" to "full" or
    91  # "incremental".
    92  #
    93  do_test incrvacuum-2.1 {
    94    execsql {
    95      pragma auto_vacuum = 1;
    96      CREATE TABLE abc(a, b, c);
    97    }
    98  } {}
    99  do_test incrvacuum-2.2 {
   100    execsql {
   101      pragma auto_vacuum = 'none';
   102      pragma auto_vacuum;
   103    }
   104  } {1}
   105  do_test incrvacuum-2.2.1 {
   106    db close
   107    sqlite3 db test.db
   108    execsql {
   109      pragma auto_vacuum;
   110    }
   111  } {1}
   112  do_test incrvacuum-2.3 {
   113    execsql {
   114      pragma auto_vacuum = 'incremental';
   115      pragma auto_vacuum;
   116    }
   117  } {2}
   118  do_test incrvacuum-2.4 {
   119    execsql {
   120      pragma auto_vacuum = 'full';
   121      pragma auto_vacuum;
   122    }
   123  } {1}
   124  
   125  #---------------------------------------------------------------------
   126  # Test that when the auto_vacuum mode is "incremental", the database
   127  # does not shrink when pages are removed from it. But it does if
   128  # the mode is set to "full".
   129  #
   130  do_test incrvacuum-3.1 {
   131    execsql {
   132      pragma auto_vacuum;
   133    }
   134  } {1}
   135  do_test incrvacuum-3.2 {
   136    set ::str [string repeat 1234567890 110]
   137    execsql {
   138      PRAGMA auto_vacuum = 2;
   139      BEGIN;
   140      CREATE TABLE tbl2(str);
   141      INSERT INTO tbl2 VALUES($::str);
   142      COMMIT;
   143    }
   144    # 5 pages:
   145    #
   146    #   1 -> database header
   147    #   2 -> first back-pointer page
   148    #   3 -> table abc
   149    #   4 -> table tbl2
   150    #   5 -> table tbl2 overflow page.
   151    #
   152    expr {[file size test.db] / 1024}
   153  } {5}
   154  do_test incrvacuum-3.3 {
   155    execsql {
   156      DROP TABLE abc;
   157      DELETE FROM tbl2;
   158    }
   159    expr {[file size test.db] / 1024}
   160  } {5}
   161  do_test incrvacuum-3.4 {
   162    execsql {
   163      PRAGMA auto_vacuum = 1;
   164      INSERT INTO tbl2 VALUES('hello world');
   165    }
   166    expr {[file size test.db] / 1024}
   167  } {3}
   168  
   169  #---------------------------------------------------------------------
   170  # Try to run a very simple incremental vacuum. Also verify that 
   171  # PRAGMA incremental_vacuum is a harmless no-op against a database that
   172  # does not support auto-vacuum.
   173  #
   174  do_test incrvacuum-4.1 {
   175    set ::str [string repeat 1234567890 110]
   176    execsql {
   177      PRAGMA auto_vacuum = 2;
   178      INSERT INTO tbl2 VALUES($::str);
   179      CREATE TABLE tbl1(a, b, c);
   180    }
   181    expr {[file size test.db] / 1024}
   182  } {5}
   183  do_test incrvacuum-4.2 {
   184    execsql {
   185      DELETE FROM tbl2;
   186      DROP TABLE tbl1;
   187    }
   188    expr {[file size test.db] / 1024}
   189  } {5}
   190  do_test incrvacuum-4.3 {
   191    set ::nStep 0
   192    db eval {pragma incremental_vacuum(10)} {
   193      incr ::nStep
   194    }
   195    list [expr {[file size test.db] / 1024}] $::nStep
   196  } {3 2}
   197  
   198  #---------------------------------------------------------------------
   199  # The following tests - incrvacuum-5.* - test incremental vacuum
   200  # from within a transaction.
   201  #
   202  do_test incrvacuum-5.1.1 {
   203    expr {[file size test.db] / 1024}
   204  } {3}
   205  do_test incrvacuum-5.1.2 {
   206    execsql {
   207      BEGIN;
   208      DROP TABLE tbl2;
   209      PRAGMA incremental_vacuum;
   210      COMMIT;
   211    }
   212    expr {[file size test.db] / 1024}
   213  } {1}
   214  
   215  do_test incrvacuum-5.2.1 {
   216    set ::str [string repeat abcdefghij 110]
   217    execsql {
   218      BEGIN;
   219      CREATE TABLE tbl1(a);
   220      INSERT INTO tbl1 VALUES($::str);
   221      PRAGMA incremental_vacuum;                 -- this is a no-op.
   222      COMMIT;
   223    }
   224    expr {[file size test.db] / 1024}
   225  } {4}
   226  do_test incrvacuum-5.2.2 {
   227    set ::str [string repeat abcdefghij 110]
   228    execsql {
   229      BEGIN;
   230      INSERT INTO tbl1 VALUES($::str);
   231      INSERT INTO tbl1 SELECT * FROM tbl1;
   232      DELETE FROM tbl1 WHERE oid%2;        -- Put 2 overflow pages on free-list.
   233      COMMIT;
   234    }
   235    expr {[file size test.db] / 1024}
   236  } {7}
   237  do_test incrvacuum-5.2.3 {
   238    execsql {
   239      BEGIN;
   240      PRAGMA incremental_vacuum;           -- Vacuum up the two pages.
   241      CREATE TABLE tbl2(b);                -- Use one free page as a table root.
   242      INSERT INTO tbl2 VALUES('a nice string');
   243      COMMIT;
   244    }
   245    expr {[file size test.db] / 1024}
   246  } {6}
   247  do_test incrvacuum-5.2.4 {
   248    execsql {
   249      SELECT * FROM tbl2;
   250    }
   251  } {{a nice string}}
   252  do_test incrvacuum-5.2.5 {
   253    execsql {
   254      DROP TABLE tbl1;
   255      DROP TABLE tbl2;
   256      PRAGMA incremental_vacuum;
   257    }
   258    expr {[file size test.db] / 1024}
   259  } {1}
   260  
   261  
   262  # Test cases incrvacuum-5.3.* use the following list as input data.
   263  # Two new databases are opened, one with incremental vacuum enabled,
   264  # the other with no auto-vacuum completely disabled. After executing
   265  # each element of the following list on both databases, test that
   266  # the integrity-check passes and the contents of each are identical.
   267  # 
   268  set TestScriptList [list {
   269    BEGIN;
   270    CREATE TABLE t1(a, b);
   271    CREATE TABLE t2(a, b);
   272    CREATE INDEX t1_i ON t1(a);
   273    CREATE INDEX t2_i ON t2(a);
   274  } {
   275    INSERT INTO t1 VALUES($::str1, $::str2);
   276    INSERT INTO t1 VALUES($::str1||$::str2, $::str2||$::str1);
   277    INSERT INTO t2 SELECT b, a FROM t1;
   278    INSERT INTO t2 SELECT a, b FROM t1;
   279    INSERT INTO t1 SELECT b, a FROM t2;
   280    UPDATE t2 SET b = '';
   281    PRAGMA incremental_vacuum;
   282  } {
   283    UPDATE t2 SET b = (SELECT b FROM t1 WHERE t1.oid = t2.oid);
   284    PRAGMA incremental_vacuum;
   285  } {
   286    CREATE TABLE t3(a, b);
   287    INSERT INTO t3 SELECT * FROM t2;
   288    DROP TABLE t2;
   289    PRAGMA incremental_vacuum;
   290  } {
   291    CREATE INDEX t3_i ON t3(a);
   292    COMMIT;
   293  } {
   294    BEGIN;
   295    DROP INDEX t3_i;
   296    PRAGMA incremental_vacuum;
   297    INSERT INTO t3 VALUES('hello', 'world');
   298    ROLLBACK;
   299  } {
   300    INSERT INTO t3 VALUES('hello', 'world');
   301  }
   302  ]
   303  
   304  # If this build omits subqueries, step 2 in the above list will not
   305  # work. Replace it with "" in this case. 
   306  #
   307  ifcapable !subquery { lset TestScriptList 2 "" }
   308  
   309  # Compare the contents of databases $A and $B.
   310  #
   311  proc compare_dbs {A B tname} {
   312    set tbl_list [execsql {
   313      SELECT tbl_name FROM sqlite_master WHERE type = 'table'
   314    } $A]
   315  
   316    do_test ${tname}.1 [subst {
   317      execsql {
   318        SELECT tbl_name FROM sqlite_master WHERE type = 'table'
   319      } $B
   320    }] $tbl_list
   321  
   322    set tn 1
   323    foreach tbl $tbl_list {
   324      set control [execsql "SELECT * FROM $tbl" $A]
   325      do_test ${tname}.[incr tn] [subst {
   326        execsql "SELECT * FROM $tbl" $B
   327      }] $control
   328    }
   329  }
   330  
   331  set ::str1 [string repeat abcdefghij 130]
   332  set ::str2 [string repeat 1234567890 105]
   333  
   334  forcedelete test1.db test1.db-journal test2.db test2.db-journal
   335  sqlite3 db1 test1.db
   336  sqlite3 db2 test2.db
   337  execsql { PRAGMA auto_vacuum = 'none' } db1
   338  execsql { PRAGMA auto_vacuum = 'incremental' } db2
   339  
   340  set tn 1
   341  foreach sql $::TestScriptList {
   342    execsql $sql db1
   343    execsql $sql db2
   344  
   345    compare_dbs db1 db2 incrvacuum-5.3.${tn}
   346    do_test incrvacuum-5.3.${tn}.integrity1 {
   347      execsql { PRAGMA integrity_check; } db1
   348    } {ok}
   349    do_test incrvacuum-5.3.${tn}.integrity2 {
   350      execsql { PRAGMA integrity_check; } db2
   351    } {ok}
   352    incr tn
   353  }
   354  db1 close
   355  db2 close
   356  #
   357  # End of test cases 5.3.*
   358  
   359  #---------------------------------------------------------------------
   360  # The following tests - incrvacuum-6.* - test running incremental 
   361  # vacuum while another statement (a read) is being executed.
   362  #
   363  for {set jj 0} {$jj < 10} {incr jj} {
   364    # Build some test data. Two tables are created in an empty
   365    # database. tbl1 data is a contiguous block starting at page 5 (pages
   366    # 3 and 4 are the table roots). tbl2 is a contiguous block starting 
   367    # right after tbl1.
   368    #
   369    # Then drop tbl1 so that when an incr vacuum is run the pages
   370    # of tbl2 have to be moved to fill the gap.
   371    #
   372    do_test incrvacuum-6.${jj}.1 {
   373      execsql {
   374        DROP TABLE IF EXISTS tbl1;
   375        DROP TABLE IF EXISTS tbl2;
   376        PRAGMA incremental_vacuum;
   377        CREATE TABLE tbl1(a, b);
   378        CREATE TABLE tbl2(a, b);
   379        BEGIN;
   380      }
   381      for {set ii 0} {$ii < 1000} {incr ii} {
   382        db eval {INSERT INTO tbl1 VALUES($ii, $ii || $ii)}
   383      }
   384      execsql {
   385        INSERT INTO tbl2 SELECT * FROM tbl1;
   386        COMMIT;
   387        DROP TABLE tbl1;
   388      }
   389      expr {[file size test.db] / 1024}
   390    } {36}
   391  
   392    # Run a linear scan query on tbl2. After reading ($jj*100) rows, 
   393    # run the incremental vacuum to shrink the database.
   394    #
   395    do_test incrvacuum-6.${jj}.2 {
   396      set ::nRow 0
   397      db eval {SELECT a FROM tbl2} {} {
   398        if {$a == [expr $jj*100]} {
   399          db eval {PRAGMA incremental_vacuum}
   400        }
   401        incr ::nRow
   402      }
   403      list [expr {[file size test.db] / 1024}] $nRow
   404    } {19 1000}
   405  }
   406  
   407  #---------------------------------------------------------------------
   408  # This test - incrvacuum-7.* - is to check that the database can be
   409  # written in the middle of an incremental vacuum.
   410  #
   411  set ::iWrite 1
   412  while 1 {
   413    do_test incrvacuum-7.${::iWrite}.1 {
   414      execsql {
   415        DROP TABLE IF EXISTS tbl1;
   416        DROP TABLE IF EXISTS tbl2;
   417        PRAGMA incremental_vacuum;
   418        CREATE TABLE tbl1(a, b);
   419        CREATE TABLE tbl2(a, b);
   420        BEGIN;
   421      }
   422      for {set ii 0} {$ii < 1000} {incr ii} {
   423        db eval {INSERT INTO tbl1 VALUES($ii, $ii || $ii)}
   424      }
   425      execsql {
   426        INSERT INTO tbl2 SELECT * FROM tbl1;
   427        COMMIT;
   428        DROP TABLE tbl1;
   429      }
   430      expr {[file size test.db] / 1024}
   431    } {36}
   432  
   433    do_test incrvacuum-7.${::iWrite}.2 {
   434      set ::nRow 0
   435      db eval {PRAGMA incremental_vacuum} {
   436        incr ::nRow
   437        if {$::nRow == $::iWrite} {
   438          db eval {
   439            CREATE TABLE tbl1(a, b);
   440            INSERT INTO tbl1 VALUES('hello', 'world');
   441          }
   442        }
   443      }
   444      list [expr {[file size test.db] / 1024}]
   445    } {20}
   446  
   447    do_test incrvacuum-7.${::iWrite}.3 {
   448      execsql {
   449        SELECT * FROM tbl1;
   450      }
   451    } {hello world}
   452  
   453    if {$::nRow == $::iWrite} break
   454    incr ::iWrite
   455  }
   456  
   457  #---------------------------------------------------------------------
   458  # This test - incrvacuum-8.* - is to check that nothing goes wrong
   459  # with an incremental-vacuum if it is the first statement executed
   460  # after an existing database is opened.
   461  #
   462  # At one point, this would always return SQLITE_SCHEMA (which 
   463  # causes an infinite loop in tclsqlite.c if using the Tcl interface).
   464  #
   465  do_test incrvacuum-8.1 {
   466    db close
   467    sqlite3 db test.db
   468    execsql {
   469      PRAGMA incremental_vacuum(50);
   470    }
   471  } {}
   472  
   473  #---------------------------------------------------------------------
   474  # At one point this test case was causing an assert() to fail.
   475  #
   476  do_test incrvacuum-9.1 {
   477    db close
   478    forcedelete test.db test.db-journal
   479    sqlite3 db test.db
   480  
   481    execsql {
   482      PRAGMA auto_vacuum = 'incremental';
   483      CREATE TABLE t1(a, b, c);
   484      CREATE TABLE t2(a, b, c);
   485      INSERT INTO t2 VALUES(randstr(500,500),randstr(500,500),randstr(500,500));
   486      INSERT INTO t1 VALUES(1, 2, 3);
   487      INSERT INTO t1 SELECT a||a, b||b, c||c FROM t1;
   488      INSERT INTO t1 SELECT a||a, b||b, c||c FROM t1;
   489      INSERT INTO t1 SELECT a||a, b||b, c||c FROM t1;
   490      INSERT INTO t1 SELECT a||a, b||b, c||c FROM t1;
   491      INSERT INTO t1 SELECT a||a, b||b, c||c FROM t1;
   492      INSERT INTO t1 SELECT a||a, b||b, c||c FROM t1;
   493      INSERT INTO t1 SELECT a||a, b||b, c||c FROM t1;
   494      INSERT INTO t1 SELECT a||a, b||b, c||c FROM t1;
   495    }
   496  } {}
   497  
   498  do_test incrvacuum-9.2 {
   499    execsql {
   500      PRAGMA synchronous = 'OFF';
   501      BEGIN;
   502      UPDATE t1 SET a = a, b = b, c = c;
   503      DROP TABLE t2;
   504      PRAGMA incremental_vacuum(10);
   505      ROLLBACK;
   506    }
   507  } {}
   508  
   509  do_test incrvacuum-9.3 {
   510    execsql {
   511      PRAGMA cache_size = 10;
   512      BEGIN;
   513      UPDATE t1 SET a = a, b = b, c = c;
   514      DROP TABLE t2;
   515      PRAGMA incremental_vacuum(10);
   516      ROLLBACK;
   517    }
   518  } {}
   519  
   520  #---------------------------------------------------------------------
   521  # Test that the parameter to the incremental_vacuum pragma works. That
   522  # is, if the user executes "PRAGMA incremental_vacuum(N)", at most
   523  # N pages are vacuumed.
   524  #
   525  do_test incrvacuum-10.1 {
   526    execsql {
   527      DROP TABLE t1;
   528      DROP TABLE t2;
   529    }
   530    expr [file size test.db] / 1024
   531  } {29}
   532  
   533  do_test incrvacuum-10.2 {
   534    execsql {
   535      PRAGMA incremental_vacuum(1);
   536    }
   537    expr [file size test.db] / 1024
   538  } {28}
   539  
   540  do_test incrvacuum-10.3 {
   541    execsql {
   542      PRAGMA incremental_vacuum(5);
   543    }
   544    expr [file size test.db] / 1024
   545  } {23}
   546  
   547  do_test incrvacuum-10.4 {
   548    execsql {
   549      PRAGMA incremental_vacuum('1');
   550    }
   551    expr [file size test.db] / 1024
   552  } {22}
   553  
   554  do_test incrvacuum-10.5 {
   555    execsql {
   556      PRAGMA incremental_vacuum("+3");
   557    }
   558    expr [file size test.db] / 1024
   559  } {19}
   560  
   561  do_test incrvacuum-10.6 {
   562    execsql {
   563      PRAGMA incremental_vacuum = 1;
   564    }
   565    expr [file size test.db] / 1024
   566  } {18}
   567  
   568  do_test incrvacuum-10.7 {
   569    # Use a really big number as an argument to incremetal_vacuum. Should
   570    # be interpreted as "free all possible space".
   571    execsql {
   572      PRAGMA incremental_vacuum(2147483649);
   573    }
   574    expr [file size test.db] / 1024
   575  } {1}
   576  
   577  do_test incrvacuum-10.8 {
   578    execsql {
   579      CREATE TABLE t1(x);
   580      INSERT INTO t1 VALUES(hex(randomblob(1000)));
   581      DROP TABLE t1;
   582    }
   583    # A negative number means free all possible space.
   584    execsql {
   585      PRAGMA incremental_vacuum=-1;
   586    }
   587    expr [file size test.db] / 1024
   588  } {1}
   589  
   590  #----------------------------------------------------------------
   591  # Test that if we set the auto_vacuum mode to 'incremental', then
   592  # create a database, thereafter that database defaults to incremental 
   593  # vacuum mode.
   594  #
   595  db close
   596  forcedelete test.db test.db-journal
   597  sqlite3 db test.db
   598  
   599  ifcapable default_autovacuum {
   600    do_test incrvacuum-11.1-av-dflt-on {
   601      execsql {
   602        PRAGMA auto_vacuum;
   603      }
   604    } $AUTOVACUUM
   605  } else {
   606    do_test incrvacuum-11.1-av-dflt-off {
   607      execsql {
   608        PRAGMA auto_vacuum;
   609      }
   610    } {0}
   611  }
   612  do_test incrvacuum-11.2 {
   613    execsql {
   614      PRAGMA auto_vacuum = incremental;
   615    }
   616  } {}
   617  do_test incrvacuum-11.3 {
   618    execsql {
   619      PRAGMA auto_vacuum;
   620    }
   621  } {2}
   622  do_test incrvacuum-11.4 {
   623    # The database has now been created.
   624    expr {[file size test.db]>0}
   625  } {1}
   626  do_test incrvacuum-11.5 {
   627    # Close and reopen the connection.
   628    db close
   629    sqlite3 db test.db
   630  
   631    # Test we are still in incremental vacuum mode.
   632    execsql { PRAGMA auto_vacuum; }
   633  } {2}
   634  do_test incrvacuum-11.6 {
   635    execsql {
   636      PRAGMA auto_vacuum = 'full';
   637      PRAGMA auto_vacuum;
   638    }
   639  } {1}
   640  do_test incrvacuum-11.7 {
   641    # Close and reopen the connection.
   642    db close
   643    sqlite3 db test.db
   644  
   645    # Test we are still in "full" auto-vacuum mode.
   646    execsql { PRAGMA auto_vacuum; }
   647  } {1}
   648  
   649  #----------------------------------------------------------------------
   650  # Special case: What happens if the database is locked when a "PRAGMA
   651  # auto_vacuum = XXX" statement is executed.
   652  #
   653  db close
   654  forcedelete test.db test.db-journal
   655  sqlite3 db test.db
   656  
   657  do_test incrvacuum-12.1 {
   658    execsql {
   659      PRAGMA auto_vacuum = 1;
   660    }
   661    expr {[file size test.db]>0}
   662  } {1}
   663  
   664  # Try to change the auto-vacuum from "full" to "incremental" while the
   665  # database is locked. Nothing should change.
   666  #
   667  do_test incrvacuum-12.2 {
   668    sqlite3 db2 test.db
   669    execsql { BEGIN EXCLUSIVE; } db2
   670    catchsql { PRAGMA auto_vacuum = 2; }
   671  } {1 {database is locked}}
   672  
   673  do_test incrvacuum-12.3 {
   674    execsql { ROLLBACK; } db2
   675    execsql { PRAGMA auto_vacuum }
   676  } {2}   ;# Still 2 because PRAGMA auto_vacuum setting held in case of vacuum
   677  do_test incrvacuum-12.4 {
   678    db close
   679    sqlite3 db test.db
   680    execsql { PRAGMA auto_vacuum }
   681  } {1}   ;# Revert to 1 because the database file did not change
   682  
   683  do_test incrvacuum-12.5 {
   684    execsql { SELECT * FROM sqlite_master }
   685    execsql { PRAGMA auto_vacuum }
   686  } {1}
   687  
   688  #----------------------------------------------------------------------
   689  # Special case #2: What if one process prepares a "PRAGMA auto_vacuum = XXX"
   690  # statement when the database is empty, but doesn't execute it until
   691  # after some other process has created the database.
   692  #
   693  db2 close
   694  db close
   695  forcedelete test.db test.db-journal
   696  sqlite3 db test.db  ;  set ::DB [sqlite3_connection_pointer db]
   697  sqlite3 db2 test.db
   698  
   699  do_test incrvacuum-13.1 {
   700    # File size is sometimes 1 instead of 0 due to the hack we put in
   701    # to work around ticket #3260.  Search for comments on #3260 in
   702    # os_unix.c.
   703    expr {[file size test.db]>1}
   704  } {0}
   705  do_test incrvacuum-13.2 {
   706    set ::STMT [sqlite3_prepare $::DB {PRAGMA auto_vacuum = 2} -1 DUMMY]
   707    execsql {
   708      PRAGMA auto_vacuum = none;
   709      PRAGMA default_cache_size = 1024;
   710      PRAGMA auto_vacuum;
   711    } db2
   712  } {0}
   713  do_test incrvacuum-13.3 {
   714    expr {[file size test.db]>0}
   715  } {1}
   716  do_test incrvacuum-13.4 {
   717    set rc [sqlite3_step $::STMT]
   718    list $rc [sqlite3_finalize $::STMT]
   719  } {SQLITE_DONE SQLITE_OK}
   720  do_test incrvacuum-13.5 {
   721    execsql {
   722      PRAGMA auto_vacuum;
   723    }
   724  } {0}
   725  
   726  
   727  # Verify that the incremental_vacuum pragma fails gracefully if it
   728  # is used against an invalid database file.
   729  #
   730  if {[permutation] == ""} {
   731    do_test incrvacuum-14.1 {
   732      set out [open invalid.db w]
   733      puts $out "This is not an SQLite database file"
   734      close $out
   735      sqlite3 db3 invalid.db
   736      catchsql {
   737        PRAGMA incremental_vacuum(10);
   738      } db3
   739    } {1 {file is not a database}}
   740    db3 close
   741  }
   742  
   743  do_test incrvacuum-15.1 {
   744    db close
   745    db2 close
   746    forcedelete test.db
   747    sqlite3 db test.db
   748  
   749    set str [string repeat "abcdefghij" 500]
   750  
   751    execsql {
   752      PRAGMA cache_size = 10;
   753      PRAGMA auto_vacuum = incremental;
   754      CREATE TABLE t1(x, y);
   755      INSERT INTO t1 VALUES('a', $str);
   756      INSERT INTO t1 VALUES('b', $str);
   757      INSERT INTO t1 VALUES('c', $str);
   758      INSERT INTO t1 VALUES('d', $str);
   759      INSERT INTO t1 VALUES('e', $str);
   760      INSERT INTO t1 VALUES('f', $str);
   761      INSERT INTO t1 VALUES('g', $str);
   762      INSERT INTO t1 VALUES('h', $str);
   763      INSERT INTO t1 VALUES('i', $str);
   764      INSERT INTO t1 VALUES('j', $str);
   765      INSERT INTO t1 VALUES('j', $str);
   766  
   767      CREATE TABLE t2(x PRIMARY KEY, y);
   768      INSERT INTO t2 VALUES('a', $str);
   769      INSERT INTO t2 VALUES('b', $str);
   770      INSERT INTO t2 VALUES('c', $str);
   771      INSERT INTO t2 VALUES('d', $str);
   772  
   773      BEGIN;
   774        DELETE FROM t2;
   775        PRAGMA incremental_vacuum;
   776    }
   777  
   778    catchsql {INSERT INTO t2 SELECT * FROM t1}
   779  
   780    execsql { 
   781      COMMIT;
   782      PRAGMA integrity_check;
   783    }
   784  } {ok}
   785  
   786  #-------------------------------------------------------------------------
   787  # At one point it was unsafe to truncate a db file on windows while there
   788  # were outstanding xFetch() references. This test case attempts to hit
   789  # that case.
   790  #
   791  ifcapable mmap {
   792    reset_db
   793    do_execsql_test incrvacuum-16.0 {
   794      PRAGMA auto_vacuum = 2;
   795      CREATE TABLE t3(a);
   796      INSERT INTO t3 VALUES(1), (2), (3), (4);
   797    
   798      CREATE TABLE t2(x);
   799      INSERT INTO t2 VALUES( randomblob(1000) );
   800      INSERT INTO t2 VALUES( randomblob(1000) );
   801      INSERT INTO t2 VALUES( randomblob(1000) );
   802      INSERT INTO t2 VALUES( randomblob(1000) );
   803      INSERT INTO t2 VALUES( randomblob(1000) );
   804      INSERT INTO t2 VALUES( randomblob(1000) );
   805    } {}
   806    
   807    # Reopen db to ensure the page-cache is empty.
   808    #
   809    db close
   810    sqlite3 db test.db
   811    
   812    # Open db in mmap-mode. Open a transaction, delete some data, then run
   813    # incremental-vacuum. Do not commit the transaction. 
   814    #
   815    do_execsql_test incrvacuum-16.1 {
   816      PRAGMA mmap_size = 1000000;
   817      BEGIN;
   818      DELETE FROM t2;
   819      PRAGMA incremental_vacuum = 1000;
   820    } {1000000}
   821  
   822    # Scan through table t3 (which is all clean pages - so mmap is used). Then,
   823    # midway through, commit the transaction. This causes the db to be truncated
   824    # while there are outstanding xFetch pages.
   825    #
   826    do_test incrvacuum-16.2 {
   827      set res [list]
   828      db eval { SELECT a FROM t3 } {
   829        if {$a==3} { db eval COMMIT }
   830        lappend res $a
   831      }
   832      set res
   833    } {1 2 3 4}
   834  }
   835  
   836  # 2021-04-05 dbsqlfuzz cced0668cfd4da4eb2382cb9dd26c17c64aaff76
   837  #
   838  # This is an incremental vacuum database that has one free page that
   839  # needs to be filled.  After removing the last page from the end of
   840  # the database file to fill the free page slot, the last page that
   841  # is left is the tail of an overflow chain.
   842  #
   843  # But the size of the database file is shorter than the actual data
   844  # so that after incremental vacuum runs, the file is actually too
   845  # small to hold the last page of the overflow chain.
   846  #
   847  # At one point this caused an assertion fault in 
   848  # sqlite3PagerTruncateImage().
   849  #
   850  do_test incrvacuum-17.0 {
   851    sqlite3 db {}
   852    database_may_be_corrupt
   853    db deserialize [decode_hexdb {
   854  | size 20480 pagesize 4096 filename x2.db
   855  | page 1 offset 0
   856  |      0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00   SQLite format 3.
   857  |     16: 10 00 01 01 00 40 20 20 00 00 00 05 00 00 00 07   .....@  ........
   858  |     32: 00 00 00 04 00 00 00 01 00 00 00 03 00 00 00 04   ................
   859  |     48: 00 00 00 00 00 00 00 03 00 00 00 01 00 00 00 00   ................
   860  |     64: 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00   ................
   861  |     80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05   ................
   862  |     96: 00 2e 53 60 0d 0f dc 00 01 0f b8 00 0f b8 0f b8   ..S`............
   863  |   4016: 00 00 00 00 00 00 00 00 22 02 06 17 11 11 01 31   ...............1
   864  |   4032: 74 61 62 6c 65 74 32 74 32 03 43 52 45 41 54 45   tablet2t2.CREATE
   865  |   4048: 20 54 41 42 4c 45 20 74 32 28 79 29 00 00 00 24    TABLE t2(y)...$
   866  |   4064: 11 11 01 31 74 61 62 6c 65 74 31 74 31 03 43 52   ...1tablet1t1.CR
   867  |   4080: 45 41 54 45 20 54 41 42 4c 45 20 74 31 28 78 29   EATE TABLE t1(x)
   868  | page 2 offset 4096
   869  |      0: 01 00 00 00 00 02 00 00 00 00 03 00 00 00 03 04   ................
   870  |     16: 00 00 00 05 03 00 00 00 03 00 00 00 00 00 00 00   ................
   871  | page 3 offset 8192
   872  |      0: 0d 00 00 00 02 05 47 00 08 dd 05 47 00 00 00 00   ......G....G....
   873  |   1344: 00 00 00 00 00 00 00 a7 0b 02 03 ce 1c 00 00 00   ................
   874  |   2256: 00 00 00 00 00 00 00 00 00 00 00 00 07 ce 14 01   ................
   875  |   2272: 04 81 9c 2c 00 00 00 00 00 00 00 00 00 00 00 00   ...,............
   876  |   4080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05   ................
   877  | page 4 offset 12288
   878  |      0: 00 00 00 00 00 00 00 00 08 dd 05 47 00 00 00 00   ...........G....
   879  |   1344: 00 00 00 00 00 00 00 a7 0b 02 03 ce 1c 00 00 00   ................
   880  |   2256: 00 00 00 00 00 00 00 00 00 00 00 00 07 ce 14 01   ................
   881  |   2272: 04 81 9c 2c 00 00 00 00 00 00 00 00 00 00 00 00   ...,............
   882  |   4080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05   ................
   883  | page 5 offset 16384
   884  |      0: 00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00   ................
   885  | end x2.db
   886  }]} {}
   887  do_catchsql_test incrvacuum-17.1 {
   888    PRAGMA writable_schema=ON;
   889    PRAGMA incremental_vacuum(10);
   890  } {0 {}}
   891  
   892  finish_test