sqlite/test/incrvacuum.test
drh b7af4452d9 Allow CREATE TABLE to occur while other queries are running. DROP TABLE is
still prohibited, however, since we do not want to delete a table out
from under an running query. (CVS 3902)

FossilOrigin-Name: 5b4bf1fce4d589337cfb1b46d75c751bbdc607d5
2007-05-02 17:54:55 +00:00

440 lines
10 KiB
Plaintext

# 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.5 2007/05/02 17:54:56 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;
}
} {0}
do_test incrvacuum-1.2 {
execsql {
pragma auto_vacuum = 'full';
pragma auto_vacuum;
}
} {1}
do_test incrvacuum-1.3 {
execsql {
pragma auto_vacuum = 'incremental';
pragma auto_vacuum;
}
} {2}
do_test incrvacuum-1.4 {
execsql {
pragma auto_vacuum = 'invalid';
pragma auto_vacuum;
}
} {0}
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 {
execsql {
pragma auto_vacuum = 5;
pragma auto_vacuum;
}
} {0}
#---------------------------------------------------------------------
# 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
# 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 {INCREMENTAL VACUUM} {
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;
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);
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;
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;
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 = '';
INCREMENTAL VACUUM;
} {
UPDATE t2 SET b = (SELECT b FROM t1 WHERE t1.oid = t2.oid);
INCREMENTAL VACUUM;
} {
CREATE TABLE t3(a, b);
INSERT INTO t3 SELECT * FROM t2;
DROP TABLE t2;
INCREMENTAL VACUUM;
} {
CREATE INDEX t3_i ON t3(a);
COMMIT;
} {
BEGIN;
DROP INDEX t3_i;
INCREMENTAL VACUUM;
INSERT INTO t3 VALUES('hello', 'world');
ROLLBACK;
} {
INSERT INTO t3 VALUES('hello', 'world');
}
]
# 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]
file delete -force 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;
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 {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;
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 {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
}
finish_test