summaryrefslogtreecommitdiffstats
path: root/test/incrvacuum.test
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 14:07:11 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 14:07:11 +0000
commit63847496f14c813a5d80efd5b7de0f1294ffe1e3 (patch)
tree01c7571c7c762ceee70638549a99834fdd7c411b /test/incrvacuum.test
parentInitial commit. (diff)
downloadsqlite3-63847496f14c813a5d80efd5b7de0f1294ffe1e3.tar.xz
sqlite3-63847496f14c813a5d80efd5b7de0f1294ffe1e3.zip
Adding upstream version 3.45.1.upstream/3.45.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'test/incrvacuum.test')
-rw-r--r--test/incrvacuum.test892
1 files changed, 892 insertions, 0 deletions
diff --git a/test/incrvacuum.test b/test/incrvacuum.test
new file mode 100644
index 0000000..d06005c
--- /dev/null
+++ b/test/incrvacuum.test
@@ -0,0 +1,892 @@
+# 2007 April 26
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this file is testing the incremental vacuum feature.
+#
+# Note: There are also some tests for incremental vacuum and IO
+# errors in incrvacuum_ioerr.test.
+#
+# $Id: incrvacuum.test,v 1.23 2009/02/18 20:31:18 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If this build of the library does not support auto-vacuum, omit this
+# whole file.
+ifcapable {!autovacuum || !pragma} {
+ finish_test
+ return
+}
+
+#---------------------------------------------------------------------
+# Test the pragma on an empty database.
+#
+do_test incrvacuum-1.1 {
+ execsql {
+ pragma auto_vacuum;
+ }
+} $sqlite_options(default_autovacuum)
+do_test incrvacuum-1.2.0 {
+ # File size is sometimes 1 instead of 0 due to the hack we put in
+ # to work around ticket #3260. Search for comments on #3260 in
+ # os_unix.c.
+ expr {[file size test.db] > 1}
+} {0}
+do_test incrvacuum-1.2 {
+ # This command will create the database.
+ execsql {
+ pragma auto_vacuum = 'full';
+ pragma auto_vacuum;
+ }
+} {1}
+do_test incrvacuum-1.2.1 {
+ expr {[file size test.db] > 0}
+} {1}
+do_test incrvacuum-1.3 {
+ execsql {
+ pragma auto_vacuum = 'incremental';
+ pragma auto_vacuum;
+ }
+} {2}
+do_test incrvacuum-1.4 {
+ # In this case the invalid value is ignored and the auto_vacuum
+ # setting remains unchanged.
+ execsql {
+ pragma auto_vacuum = 'invalid';
+ pragma auto_vacuum;
+ }
+} {2}
+do_test incrvacuum-1.5 {
+ execsql {
+ pragma auto_vacuum = 1;
+ pragma auto_vacuum;
+ }
+} {1}
+do_test incrvacuum-1.6 {
+ execsql {
+ pragma auto_vacuum = '2';
+ pragma auto_vacuum;
+ }
+} {2}
+do_test incrvacuum-1.7 {
+ # Invalid value. auto_vacuum setting remains unchanged.
+ execsql {
+ pragma auto_vacuum = 5;
+ pragma auto_vacuum;
+ }
+} {2}
+
+#---------------------------------------------------------------------
+# Test the pragma on a non-empty database. It is possible to toggle
+# the connection between "full" and "incremental" mode, but not to
+# change from either of these to "none", or from "none" to "full" or
+# "incremental".
+#
+do_test incrvacuum-2.1 {
+ execsql {
+ pragma auto_vacuum = 1;
+ CREATE TABLE abc(a, b, c);
+ }
+} {}
+do_test incrvacuum-2.2 {
+ execsql {
+ pragma auto_vacuum = 'none';
+ pragma auto_vacuum;
+ }
+} {1}
+do_test incrvacuum-2.2.1 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ pragma auto_vacuum;
+ }
+} {1}
+do_test incrvacuum-2.3 {
+ execsql {
+ pragma auto_vacuum = 'incremental';
+ pragma auto_vacuum;
+ }
+} {2}
+do_test incrvacuum-2.4 {
+ execsql {
+ pragma auto_vacuum = 'full';
+ pragma auto_vacuum;
+ }
+} {1}
+
+#---------------------------------------------------------------------
+# Test that when the auto_vacuum mode is "incremental", the database
+# does not shrink when pages are removed from it. But it does if
+# the mode is set to "full".
+#
+do_test incrvacuum-3.1 {
+ execsql {
+ pragma auto_vacuum;
+ }
+} {1}
+do_test incrvacuum-3.2 {
+ set ::str [string repeat 1234567890 110]
+ execsql {
+ PRAGMA auto_vacuum = 2;
+ BEGIN;
+ CREATE TABLE tbl2(str);
+ INSERT INTO tbl2 VALUES($::str);
+ COMMIT;
+ }
+ # 5 pages:
+ #
+ # 1 -> database header
+ # 2 -> first back-pointer page
+ # 3 -> table abc
+ # 4 -> table tbl2
+ # 5 -> table tbl2 overflow page.
+ #
+ expr {[file size test.db] / 1024}
+} {5}
+do_test incrvacuum-3.3 {
+ execsql {
+ DROP TABLE abc;
+ DELETE FROM tbl2;
+ }
+ expr {[file size test.db] / 1024}
+} {5}
+do_test incrvacuum-3.4 {
+ execsql {
+ PRAGMA auto_vacuum = 1;
+ INSERT INTO tbl2 VALUES('hello world');
+ }
+ expr {[file size test.db] / 1024}
+} {3}
+
+#---------------------------------------------------------------------
+# Try to run a very simple incremental vacuum. Also verify that
+# PRAGMA incremental_vacuum is a harmless no-op against a database that
+# does not support auto-vacuum.
+#
+do_test incrvacuum-4.1 {
+ set ::str [string repeat 1234567890 110]
+ execsql {
+ PRAGMA auto_vacuum = 2;
+ INSERT INTO tbl2 VALUES($::str);
+ CREATE TABLE tbl1(a, b, c);
+ }
+ expr {[file size test.db] / 1024}
+} {5}
+do_test incrvacuum-4.2 {
+ execsql {
+ DELETE FROM tbl2;
+ DROP TABLE tbl1;
+ }
+ expr {[file size test.db] / 1024}
+} {5}
+do_test incrvacuum-4.3 {
+ set ::nStep 0
+ db eval {pragma incremental_vacuum(10)} {
+ incr ::nStep
+ }
+ list [expr {[file size test.db] / 1024}] $::nStep
+} {3 2}
+
+#---------------------------------------------------------------------
+# The following tests - incrvacuum-5.* - test incremental vacuum
+# from within a transaction.
+#
+do_test incrvacuum-5.1.1 {
+ expr {[file size test.db] / 1024}
+} {3}
+do_test incrvacuum-5.1.2 {
+ execsql {
+ BEGIN;
+ DROP TABLE tbl2;
+ PRAGMA incremental_vacuum;
+ COMMIT;
+ }
+ expr {[file size test.db] / 1024}
+} {1}
+
+do_test incrvacuum-5.2.1 {
+ set ::str [string repeat abcdefghij 110]
+ execsql {
+ BEGIN;
+ CREATE TABLE tbl1(a);
+ INSERT INTO tbl1 VALUES($::str);
+ PRAGMA incremental_vacuum; -- this is a no-op.
+ COMMIT;
+ }
+ expr {[file size test.db] / 1024}
+} {4}
+do_test incrvacuum-5.2.2 {
+ set ::str [string repeat abcdefghij 110]
+ execsql {
+ BEGIN;
+ INSERT INTO tbl1 VALUES($::str);
+ INSERT INTO tbl1 SELECT * FROM tbl1;
+ DELETE FROM tbl1 WHERE oid%2; -- Put 2 overflow pages on free-list.
+ COMMIT;
+ }
+ expr {[file size test.db] / 1024}
+} {7}
+do_test incrvacuum-5.2.3 {
+ execsql {
+ BEGIN;
+ PRAGMA incremental_vacuum; -- Vacuum up the two pages.
+ CREATE TABLE tbl2(b); -- Use one free page as a table root.
+ INSERT INTO tbl2 VALUES('a nice string');
+ COMMIT;
+ }
+ expr {[file size test.db] / 1024}
+} {6}
+do_test incrvacuum-5.2.4 {
+ execsql {
+ SELECT * FROM tbl2;
+ }
+} {{a nice string}}
+do_test incrvacuum-5.2.5 {
+ execsql {
+ DROP TABLE tbl1;
+ DROP TABLE tbl2;
+ PRAGMA incremental_vacuum;
+ }
+ expr {[file size test.db] / 1024}
+} {1}
+
+
+# Test cases incrvacuum-5.3.* use the following list as input data.
+# Two new databases are opened, one with incremental vacuum enabled,
+# the other with no auto-vacuum completely disabled. After executing
+# each element of the following list on both databases, test that
+# the integrity-check passes and the contents of each are identical.
+#
+set TestScriptList [list {
+ BEGIN;
+ CREATE TABLE t1(a, b);
+ CREATE TABLE t2(a, b);
+ CREATE INDEX t1_i ON t1(a);
+ CREATE INDEX t2_i ON t2(a);
+} {
+ INSERT INTO t1 VALUES($::str1, $::str2);
+ INSERT INTO t1 VALUES($::str1||$::str2, $::str2||$::str1);
+ INSERT INTO t2 SELECT b, a FROM t1;
+ INSERT INTO t2 SELECT a, b FROM t1;
+ INSERT INTO t1 SELECT b, a FROM t2;
+ UPDATE t2 SET b = '';
+ PRAGMA incremental_vacuum;
+} {
+ UPDATE t2 SET b = (SELECT b FROM t1 WHERE t1.oid = t2.oid);
+ PRAGMA incremental_vacuum;
+} {
+ CREATE TABLE t3(a, b);
+ INSERT INTO t3 SELECT * FROM t2;
+ DROP TABLE t2;
+ PRAGMA incremental_vacuum;
+} {
+ CREATE INDEX t3_i ON t3(a);
+ COMMIT;
+} {
+ BEGIN;
+ DROP INDEX t3_i;
+ PRAGMA incremental_vacuum;
+ INSERT INTO t3 VALUES('hello', 'world');
+ ROLLBACK;
+} {
+ INSERT INTO t3 VALUES('hello', 'world');
+}
+]
+
+# If this build omits subqueries, step 2 in the above list will not
+# work. Replace it with "" in this case.
+#
+ifcapable !subquery { lset TestScriptList 2 "" }
+
+# Compare the contents of databases $A and $B.
+#
+proc compare_dbs {A B tname} {
+ set tbl_list [execsql {
+ SELECT tbl_name FROM sqlite_master WHERE type = 'table'
+ } $A]
+
+ do_test ${tname}.1 [subst {
+ execsql {
+ SELECT tbl_name FROM sqlite_master WHERE type = 'table'
+ } $B
+ }] $tbl_list
+
+ set tn 1
+ foreach tbl $tbl_list {
+ set control [execsql "SELECT * FROM $tbl" $A]
+ do_test ${tname}.[incr tn] [subst {
+ execsql "SELECT * FROM $tbl" $B
+ }] $control
+ }
+}
+
+set ::str1 [string repeat abcdefghij 130]
+set ::str2 [string repeat 1234567890 105]
+
+forcedelete test1.db test1.db-journal test2.db test2.db-journal
+sqlite3 db1 test1.db
+sqlite3 db2 test2.db
+execsql { PRAGMA auto_vacuum = 'none' } db1
+execsql { PRAGMA auto_vacuum = 'incremental' } db2
+
+set tn 1
+foreach sql $::TestScriptList {
+ execsql $sql db1
+ execsql $sql db2
+
+ compare_dbs db1 db2 incrvacuum-5.3.${tn}
+ do_test incrvacuum-5.3.${tn}.integrity1 {
+ execsql { PRAGMA integrity_check; } db1
+ } {ok}
+ do_test incrvacuum-5.3.${tn}.integrity2 {
+ execsql { PRAGMA integrity_check; } db2
+ } {ok}
+ incr tn
+}
+db1 close
+db2 close
+#
+# End of test cases 5.3.*
+
+#---------------------------------------------------------------------
+# The following tests - incrvacuum-6.* - test running incremental
+# vacuum while another statement (a read) is being executed.
+#
+for {set jj 0} {$jj < 10} {incr jj} {
+ # Build some test data. Two tables are created in an empty
+ # database. tbl1 data is a contiguous block starting at page 5 (pages
+ # 3 and 4 are the table roots). tbl2 is a contiguous block starting
+ # right after tbl1.
+ #
+ # Then drop tbl1 so that when an incr vacuum is run the pages
+ # of tbl2 have to be moved to fill the gap.
+ #
+ do_test incrvacuum-6.${jj}.1 {
+ execsql {
+ DROP TABLE IF EXISTS tbl1;
+ DROP TABLE IF EXISTS tbl2;
+ PRAGMA incremental_vacuum;
+ CREATE TABLE tbl1(a, b);
+ CREATE TABLE tbl2(a, b);
+ BEGIN;
+ }
+ for {set ii 0} {$ii < 1000} {incr ii} {
+ db eval {INSERT INTO tbl1 VALUES($ii, $ii || $ii)}
+ }
+ execsql {
+ INSERT INTO tbl2 SELECT * FROM tbl1;
+ COMMIT;
+ DROP TABLE tbl1;
+ }
+ expr {[file size test.db] / 1024}
+ } {36}
+
+ # Run a linear scan query on tbl2. After reading ($jj*100) rows,
+ # run the incremental vacuum to shrink the database.
+ #
+ do_test incrvacuum-6.${jj}.2 {
+ set ::nRow 0
+ db eval {SELECT a FROM tbl2} {} {
+ if {$a == [expr $jj*100]} {
+ db eval {PRAGMA incremental_vacuum}
+ }
+ incr ::nRow
+ }
+ list [expr {[file size test.db] / 1024}] $nRow
+ } {19 1000}
+}
+
+#---------------------------------------------------------------------
+# This test - incrvacuum-7.* - is to check that the database can be
+# written in the middle of an incremental vacuum.
+#
+set ::iWrite 1
+while 1 {
+ do_test incrvacuum-7.${::iWrite}.1 {
+ execsql {
+ DROP TABLE IF EXISTS tbl1;
+ DROP TABLE IF EXISTS tbl2;
+ PRAGMA incremental_vacuum;
+ CREATE TABLE tbl1(a, b);
+ CREATE TABLE tbl2(a, b);
+ BEGIN;
+ }
+ for {set ii 0} {$ii < 1000} {incr ii} {
+ db eval {INSERT INTO tbl1 VALUES($ii, $ii || $ii)}
+ }
+ execsql {
+ INSERT INTO tbl2 SELECT * FROM tbl1;
+ COMMIT;
+ DROP TABLE tbl1;
+ }
+ expr {[file size test.db] / 1024}
+ } {36}
+
+ do_test incrvacuum-7.${::iWrite}.2 {
+ set ::nRow 0
+ db eval {PRAGMA incremental_vacuum} {
+ incr ::nRow
+ if {$::nRow == $::iWrite} {
+ db eval {
+ CREATE TABLE tbl1(a, b);
+ INSERT INTO tbl1 VALUES('hello', 'world');
+ }
+ }
+ }
+ list [expr {[file size test.db] / 1024}]
+ } {20}
+
+ do_test incrvacuum-7.${::iWrite}.3 {
+ execsql {
+ SELECT * FROM tbl1;
+ }
+ } {hello world}
+
+ if {$::nRow == $::iWrite} break
+ incr ::iWrite
+}
+
+#---------------------------------------------------------------------
+# This test - incrvacuum-8.* - is to check that nothing goes wrong
+# with an incremental-vacuum if it is the first statement executed
+# after an existing database is opened.
+#
+# At one point, this would always return SQLITE_SCHEMA (which
+# causes an infinite loop in tclsqlite.c if using the Tcl interface).
+#
+do_test incrvacuum-8.1 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ PRAGMA incremental_vacuum(50);
+ }
+} {}
+
+#---------------------------------------------------------------------
+# At one point this test case was causing an assert() to fail.
+#
+do_test incrvacuum-9.1 {
+ db close
+ forcedelete test.db test.db-journal
+ sqlite3 db test.db
+
+ execsql {
+ PRAGMA auto_vacuum = 'incremental';
+ CREATE TABLE t1(a, b, c);
+ CREATE TABLE t2(a, b, c);
+ INSERT INTO t2 VALUES(randstr(500,500),randstr(500,500),randstr(500,500));
+ INSERT INTO t1 VALUES(1, 2, 3);
+ INSERT INTO t1 SELECT a||a, b||b, c||c FROM t1;
+ INSERT INTO t1 SELECT a||a, b||b, c||c FROM t1;
+ INSERT INTO t1 SELECT a||a, b||b, c||c FROM t1;
+ INSERT INTO t1 SELECT a||a, b||b, c||c FROM t1;
+ INSERT INTO t1 SELECT a||a, b||b, c||c FROM t1;
+ INSERT INTO t1 SELECT a||a, b||b, c||c FROM t1;
+ INSERT INTO t1 SELECT a||a, b||b, c||c FROM t1;
+ INSERT INTO t1 SELECT a||a, b||b, c||c FROM t1;
+ }
+} {}
+
+do_test incrvacuum-9.2 {
+ execsql {
+ PRAGMA synchronous = 'OFF';
+ BEGIN;
+ UPDATE t1 SET a = a, b = b, c = c;
+ DROP TABLE t2;
+ PRAGMA incremental_vacuum(10);
+ ROLLBACK;
+ }
+} {}
+
+do_test incrvacuum-9.3 {
+ execsql {
+ PRAGMA cache_size = 10;
+ BEGIN;
+ UPDATE t1 SET a = a, b = b, c = c;
+ DROP TABLE t2;
+ PRAGMA incremental_vacuum(10);
+ ROLLBACK;
+ }
+} {}
+
+#---------------------------------------------------------------------
+# Test that the parameter to the incremental_vacuum pragma works. That
+# is, if the user executes "PRAGMA incremental_vacuum(N)", at most
+# N pages are vacuumed.
+#
+do_test incrvacuum-10.1 {
+ execsql {
+ DROP TABLE t1;
+ DROP TABLE t2;
+ }
+ expr [file size test.db] / 1024
+} {29}
+
+do_test incrvacuum-10.2 {
+ execsql {
+ PRAGMA incremental_vacuum(1);
+ }
+ expr [file size test.db] / 1024
+} {28}
+
+do_test incrvacuum-10.3 {
+ execsql {
+ PRAGMA incremental_vacuum(5);
+ }
+ expr [file size test.db] / 1024
+} {23}
+
+do_test incrvacuum-10.4 {
+ execsql {
+ PRAGMA incremental_vacuum('1');
+ }
+ expr [file size test.db] / 1024
+} {22}
+
+do_test incrvacuum-10.5 {
+ execsql {
+ PRAGMA incremental_vacuum("+3");
+ }
+ expr [file size test.db] / 1024
+} {19}
+
+do_test incrvacuum-10.6 {
+ execsql {
+ PRAGMA incremental_vacuum = 1;
+ }
+ expr [file size test.db] / 1024
+} {18}
+
+do_test incrvacuum-10.7 {
+ # Use a really big number as an argument to incremetal_vacuum. Should
+ # be interpreted as "free all possible space".
+ execsql {
+ PRAGMA incremental_vacuum(2147483649);
+ }
+ expr [file size test.db] / 1024
+} {1}
+
+do_test incrvacuum-10.8 {
+ execsql {
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(hex(randomblob(1000)));
+ DROP TABLE t1;
+ }
+ # A negative number means free all possible space.
+ execsql {
+ PRAGMA incremental_vacuum=-1;
+ }
+ expr [file size test.db] / 1024
+} {1}
+
+#----------------------------------------------------------------
+# Test that if we set the auto_vacuum mode to 'incremental', then
+# create a database, thereafter that database defaults to incremental
+# vacuum mode.
+#
+db close
+forcedelete test.db test.db-journal
+sqlite3 db test.db
+
+ifcapable default_autovacuum {
+ do_test incrvacuum-11.1-av-dflt-on {
+ execsql {
+ PRAGMA auto_vacuum;
+ }
+ } $AUTOVACUUM
+} else {
+ do_test incrvacuum-11.1-av-dflt-off {
+ execsql {
+ PRAGMA auto_vacuum;
+ }
+ } {0}
+}
+do_test incrvacuum-11.2 {
+ execsql {
+ PRAGMA auto_vacuum = incremental;
+ }
+} {}
+do_test incrvacuum-11.3 {
+ execsql {
+ PRAGMA auto_vacuum;
+ }
+} {2}
+do_test incrvacuum-11.4 {
+ # The database has now been created.
+ expr {[file size test.db]>0}
+} {1}
+do_test incrvacuum-11.5 {
+ # Close and reopen the connection.
+ db close
+ sqlite3 db test.db
+
+ # Test we are still in incremental vacuum mode.
+ execsql { PRAGMA auto_vacuum; }
+} {2}
+do_test incrvacuum-11.6 {
+ execsql {
+ PRAGMA auto_vacuum = 'full';
+ PRAGMA auto_vacuum;
+ }
+} {1}
+do_test incrvacuum-11.7 {
+ # Close and reopen the connection.
+ db close
+ sqlite3 db test.db
+
+ # Test we are still in "full" auto-vacuum mode.
+ execsql { PRAGMA auto_vacuum; }
+} {1}
+
+#----------------------------------------------------------------------
+# Special case: What happens if the database is locked when a "PRAGMA
+# auto_vacuum = XXX" statement is executed.
+#
+db close
+forcedelete test.db test.db-journal
+sqlite3 db test.db
+
+do_test incrvacuum-12.1 {
+ execsql {
+ PRAGMA auto_vacuum = 1;
+ }
+ expr {[file size test.db]>0}
+} {1}
+
+# Try to change the auto-vacuum from "full" to "incremental" while the
+# database is locked. Nothing should change.
+#
+do_test incrvacuum-12.2 {
+ sqlite3 db2 test.db
+ execsql { BEGIN EXCLUSIVE; } db2
+ catchsql { PRAGMA auto_vacuum = 2; }
+} {1 {database is locked}}
+
+do_test incrvacuum-12.3 {
+ execsql { ROLLBACK; } db2
+ execsql { PRAGMA auto_vacuum }
+} {2} ;# Still 2 because PRAGMA auto_vacuum setting held in case of vacuum
+do_test incrvacuum-12.4 {
+ db close
+ sqlite3 db test.db
+ execsql { PRAGMA auto_vacuum }
+} {1} ;# Revert to 1 because the database file did not change
+
+do_test incrvacuum-12.5 {
+ execsql { SELECT * FROM sqlite_master }
+ execsql { PRAGMA auto_vacuum }
+} {1}
+
+#----------------------------------------------------------------------
+# Special case #2: What if one process prepares a "PRAGMA auto_vacuum = XXX"
+# statement when the database is empty, but doesn't execute it until
+# after some other process has created the database.
+#
+db2 close
+db close
+forcedelete test.db test.db-journal
+sqlite3 db test.db ; set ::DB [sqlite3_connection_pointer db]
+sqlite3 db2 test.db
+
+do_test incrvacuum-13.1 {
+ # File size is sometimes 1 instead of 0 due to the hack we put in
+ # to work around ticket #3260. Search for comments on #3260 in
+ # os_unix.c.
+ expr {[file size test.db]>1}
+} {0}
+do_test incrvacuum-13.2 {
+ set ::STMT [sqlite3_prepare $::DB {PRAGMA auto_vacuum = 2} -1 DUMMY]
+ execsql {
+ PRAGMA auto_vacuum = none;
+ PRAGMA default_cache_size = 1024;
+ PRAGMA auto_vacuum;
+ } db2
+} {0}
+do_test incrvacuum-13.3 {
+ expr {[file size test.db]>0}
+} {1}
+do_test incrvacuum-13.4 {
+ set rc [sqlite3_step $::STMT]
+ list $rc [sqlite3_finalize $::STMT]
+} {SQLITE_DONE SQLITE_OK}
+do_test incrvacuum-13.5 {
+ execsql {
+ PRAGMA auto_vacuum;
+ }
+} {0}
+
+
+# Verify that the incremental_vacuum pragma fails gracefully if it
+# is used against an invalid database file.
+#
+if {[permutation] == ""} {
+ do_test incrvacuum-14.1 {
+ set out [open invalid.db w]
+ puts $out "This is not an SQLite database file"
+ close $out
+ sqlite3 db3 invalid.db
+ catchsql {
+ PRAGMA incremental_vacuum(10);
+ } db3
+ } {1 {file is not a database}}
+ db3 close
+}
+
+do_test incrvacuum-15.1 {
+ db close
+ db2 close
+ forcedelete test.db
+ sqlite3 db test.db
+
+ set str [string repeat "abcdefghij" 500]
+
+ execsql {
+ PRAGMA cache_size = 10;
+ PRAGMA auto_vacuum = incremental;
+ CREATE TABLE t1(x, y);
+ INSERT INTO t1 VALUES('a', $str);
+ INSERT INTO t1 VALUES('b', $str);
+ INSERT INTO t1 VALUES('c', $str);
+ INSERT INTO t1 VALUES('d', $str);
+ INSERT INTO t1 VALUES('e', $str);
+ INSERT INTO t1 VALUES('f', $str);
+ INSERT INTO t1 VALUES('g', $str);
+ INSERT INTO t1 VALUES('h', $str);
+ INSERT INTO t1 VALUES('i', $str);
+ INSERT INTO t1 VALUES('j', $str);
+ INSERT INTO t1 VALUES('j', $str);
+
+ CREATE TABLE t2(x PRIMARY KEY, y);
+ INSERT INTO t2 VALUES('a', $str);
+ INSERT INTO t2 VALUES('b', $str);
+ INSERT INTO t2 VALUES('c', $str);
+ INSERT INTO t2 VALUES('d', $str);
+
+ BEGIN;
+ DELETE FROM t2;
+ PRAGMA incremental_vacuum;
+ }
+
+ catchsql {INSERT INTO t2 SELECT * FROM t1}
+
+ execsql {
+ COMMIT;
+ PRAGMA integrity_check;
+ }
+} {ok}
+
+#-------------------------------------------------------------------------
+# At one point it was unsafe to truncate a db file on windows while there
+# were outstanding xFetch() references. This test case attempts to hit
+# that case.
+#
+ifcapable mmap {
+ reset_db
+ do_execsql_test incrvacuum-16.0 {
+ PRAGMA auto_vacuum = 2;
+ CREATE TABLE t3(a);
+ INSERT INTO t3 VALUES(1), (2), (3), (4);
+
+ CREATE TABLE t2(x);
+ INSERT INTO t2 VALUES( randomblob(1000) );
+ INSERT INTO t2 VALUES( randomblob(1000) );
+ INSERT INTO t2 VALUES( randomblob(1000) );
+ INSERT INTO t2 VALUES( randomblob(1000) );
+ INSERT INTO t2 VALUES( randomblob(1000) );
+ INSERT INTO t2 VALUES( randomblob(1000) );
+ } {}
+
+ # Reopen db to ensure the page-cache is empty.
+ #
+ db close
+ sqlite3 db test.db
+
+ # Open db in mmap-mode. Open a transaction, delete some data, then run
+ # incremental-vacuum. Do not commit the transaction.
+ #
+ do_execsql_test incrvacuum-16.1 {
+ PRAGMA mmap_size = 1000000;
+ BEGIN;
+ DELETE FROM t2;
+ PRAGMA incremental_vacuum = 1000;
+ } {1000000}
+
+ # Scan through table t3 (which is all clean pages - so mmap is used). Then,
+ # midway through, commit the transaction. This causes the db to be truncated
+ # while there are outstanding xFetch pages.
+ #
+ do_test incrvacuum-16.2 {
+ set res [list]
+ db eval { SELECT a FROM t3 } {
+ if {$a==3} { db eval COMMIT }
+ lappend res $a
+ }
+ set res
+ } {1 2 3 4}
+}
+
+# 2021-04-05 dbsqlfuzz cced0668cfd4da4eb2382cb9dd26c17c64aaff76
+#
+# This is an incremental vacuum database that has one free page that
+# needs to be filled. After removing the last page from the end of
+# the database file to fill the free page slot, the last page that
+# is left is the tail of an overflow chain.
+#
+# But the size of the database file is shorter than the actual data
+# so that after incremental vacuum runs, the file is actually too
+# small to hold the last page of the overflow chain.
+#
+# At one point this caused an assertion fault in
+# sqlite3PagerTruncateImage().
+#
+do_test incrvacuum-17.0 {
+ sqlite3 db {}
+ database_may_be_corrupt
+ db deserialize [decode_hexdb {
+| size 20480 pagesize 4096 filename x2.db
+| page 1 offset 0
+| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
+| 16: 10 00 01 01 00 40 20 20 00 00 00 05 00 00 00 07 .....@ ........
+| 32: 00 00 00 04 00 00 00 01 00 00 00 03 00 00 00 04 ................
+| 48: 00 00 00 00 00 00 00 03 00 00 00 01 00 00 00 00 ................
+| 64: 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 ................
+| 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 ................
+| 96: 00 2e 53 60 0d 0f dc 00 01 0f b8 00 0f b8 0f b8 ..S`............
+| 4016: 00 00 00 00 00 00 00 00 22 02 06 17 11 11 01 31 ...............1
+| 4032: 74 61 62 6c 65 74 32 74 32 03 43 52 45 41 54 45 tablet2t2.CREATE
+| 4048: 20 54 41 42 4c 45 20 74 32 28 79 29 00 00 00 24 TABLE t2(y)...$
+| 4064: 11 11 01 31 74 61 62 6c 65 74 31 74 31 03 43 52 ...1tablet1t1.CR
+| 4080: 45 41 54 45 20 54 41 42 4c 45 20 74 31 28 78 29 EATE TABLE t1(x)
+| page 2 offset 4096
+| 0: 01 00 00 00 00 02 00 00 00 00 03 00 00 00 03 04 ................
+| 16: 00 00 00 05 03 00 00 00 03 00 00 00 00 00 00 00 ................
+| page 3 offset 8192
+| 0: 0d 00 00 00 02 05 47 00 08 dd 05 47 00 00 00 00 ......G....G....
+| 1344: 00 00 00 00 00 00 00 a7 0b 02 03 ce 1c 00 00 00 ................
+| 2256: 00 00 00 00 00 00 00 00 00 00 00 00 07 ce 14 01 ................
+| 2272: 04 81 9c 2c 00 00 00 00 00 00 00 00 00 00 00 00 ...,............
+| 4080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 ................
+| page 4 offset 12288
+| 0: 00 00 00 00 00 00 00 00 08 dd 05 47 00 00 00 00 ...........G....
+| 1344: 00 00 00 00 00 00 00 a7 0b 02 03 ce 1c 00 00 00 ................
+| 2256: 00 00 00 00 00 00 00 00 00 00 00 00 07 ce 14 01 ................
+| 2272: 04 81 9c 2c 00 00 00 00 00 00 00 00 00 00 00 00 ...,............
+| 4080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 ................
+| page 5 offset 16384
+| 0: 00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 ................
+| end x2.db
+}]} {}
+do_catchsql_test incrvacuum-17.1 {
+ PRAGMA writable_schema=ON;
+ PRAGMA incremental_vacuum(10);
+} {0 {}}
+
+finish_test