1
0
Fork 0

Adding upstream version 1.37.0.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-17 09:46:10 +02:00
parent 42613ad5c6
commit 271b368104
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
1329 changed files with 4727104 additions and 0 deletions

2
testdata/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
sqlite-*
SQLite-*

1474
testdata/mptest.c vendored Normal file

File diff suppressed because it is too large Load diff

439
testdata/overlay/malloc5.test vendored Normal file
View file

@ -0,0 +1,439 @@
# 2005 November 30
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file contains test cases focused on the two memory-management APIs,
# sqlite3_soft_heap_limit() and sqlite3_release_memory().
#
# Prior to version 3.6.2, calling sqlite3_release_memory() or exceeding
# the configured soft heap limit could cause sqlite to upgrade database
# locks and flush dirty pages to the file system. As of 3.6.2, this is
# no longer the case. In version 3.6.2, sqlite3_release_memory() only
# reclaims clean pages. This test file has been updated accordingly.
#
# $Id: malloc5.test,v 1.22 2009/04/11 19:09:54 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
db close
# Only run these tests if memory debugging is turned on.
#
if {!$MEMDEBUG} {
puts "Skipping malloc5 tests: not compiled with -DSQLITE_MEMDEBUG..."
finish_test
return
}
# Skip these tests if OMIT_MEMORY_MANAGEMENT was defined at compile time.
ifcapable !memorymanage {
finish_test
return
}
# The sizes of memory allocations from system malloc() might vary,
# depending on the memory allocator algorithms used. The following
# routine is designed to support answers that fall within a range
# of values while also supplying easy-to-understand "expected" values
# when errors occur.
#
proc value_in_range {target x args} {
set v [lindex $args 0]
if {$v!=""} {
if {$v<$target*$x} {return $v}
if {$v>$target/$x} {return $v}
}
return "number between [expr {int($target*$x)}] and [expr {int($target/$x)}]"
}
set mrange 0.98 ;# plus or minus 2%
test_set_config_pagecache 0 100
sqlite3_soft_heap_limit 0
sqlite3 db test.db
# db eval {PRAGMA cache_size=1}
do_test malloc5-1.1 {
# Simplest possible test. Call sqlite3_release_memory when there is exactly
# one unused page in a single pager cache. The page cannot be freed, as
# it is dirty. So sqlite3_release_memory() returns 0.
#
execsql {
PRAGMA auto_vacuum=OFF;
BEGIN;
CREATE TABLE abc(a, b, c);
}
sqlite3_release_memory
} {0}
do_test malloc5-1.2 {
# Test that the transaction started in the above test is still active.
# The lock on the database file should not have been upgraded (this was
# not the case before version 3.6.2).
#
sqlite3 db2 test.db
execsql {PRAGMA cache_size=2; SELECT * FROM sqlite_master } db2
} {}
do_test malloc5-1.3 {
# Call [sqlite3_release_memory] when there is exactly one unused page
# in the cache belonging to db2.
#
set ::pgalloc [sqlite3_release_memory]
value_in_range 1288 0.75
} [value_in_range 1288 0.75]
do_test malloc5-1.4 {
# Commit the transaction and open a new one. Read 1 page into the cache.
# Because the page is not dirty, it is eligible for collection even
# before the transaction is concluded.
#
execsql {
COMMIT;
BEGIN;
SELECT * FROM abc;
}
value_in_range $::pgalloc $::mrange [sqlite3_release_memory]
} [value_in_range $::pgalloc $::mrange]
do_test malloc5-1.5 {
# Conclude the transaction opened in the previous [do_test] block. This
# causes another page (page 1) to become eligible for recycling.
#
execsql { COMMIT }
value_in_range $::pgalloc $::mrange [sqlite3_release_memory]
} [value_in_range $::pgalloc $::mrange]
do_test malloc5-1.6 {
# Manipulate the cache so that it contains two unused pages. One requires
# a journal-sync to free, the other does not.
db2 close
execsql {
BEGIN;
CREATE TABLE def(d, e, f);
SELECT * FROM abc;
}
value_in_range $::pgalloc $::mrange [sqlite3_release_memory 500]
} [value_in_range $::pgalloc $::mrange]
do_test malloc5-1.7 {
# Database should not be locked this time.
sqlite3 db2 test.db
catchsql { SELECT * FROM abc } db2
} {0 {}}
do_test malloc5-1.8 {
# Try to release another block of memory. This will fail as the only
# pages currently in the cache are dirty (page 3) or pinned (page 1).
db2 close
sqlite3_release_memory 500
} 0
do_test malloc5-1.8 {
# Database is still not locked.
#
sqlite3 db2 test.db
catchsql { SELECT * FROM abc } db2
} {0 {}}
do_test malloc5-1.9 {
execsql {
COMMIT;
}
} {}
do_test malloc5-2.1 {
# Put some data in tables abc and def. Both tables are still wholly
# contained within their root pages.
execsql {
INSERT INTO abc VALUES(1, 2, 3);
INSERT INTO abc VALUES(4, 5, 6);
INSERT INTO def VALUES(7, 8, 9);
INSERT INTO def VALUES(10,11,12);
}
} {}
do_test malloc5-2.2 {
# Load the root-page for table def into the cache. Then query table abc.
# Halfway through the query call sqlite3_release_memory(). The goal of this
# test is to make sure we don't free pages that are in use (specifically,
# the root of table abc).
sqlite3_release_memory
set nRelease 0
execsql {
BEGIN;
SELECT * FROM def;
}
set data [list]
db eval {SELECT * FROM abc} {
incr nRelease [sqlite3_release_memory]
lappend data $a $b $c
}
execsql {
COMMIT;
}
value_in_range $::pgalloc $::mrange $nRelease
} [value_in_range $::pgalloc $::mrange]
do_test malloc5-2.2.1 {
set data
} {1 2 3 4 5 6}
do_test malloc5-3.1 {
# Simple test to show that if two pagers are opened from within this
# thread, memory is freed from both when sqlite3_release_memory() is
# called.
execsql {
BEGIN;
SELECT * FROM abc;
}
execsql {
SELECT * FROM sqlite_master;
BEGIN;
SELECT * FROM def;
} db2
value_in_range [expr $::pgalloc*2] 0.99 [sqlite3_release_memory]
} [value_in_range [expr $::pgalloc * 2] 0.99]
do_test malloc5-3.2 {
concat \
[execsql {SELECT * FROM abc; COMMIT}] \
[execsql {SELECT * FROM def; COMMIT} db2]
} {1 2 3 4 5 6 7 8 9 10 11 12}
db2 close
puts "Highwater mark: [sqlite3_memory_highwater]"
# The following two test cases each execute a transaction in which
# 10000 rows are inserted into table abc. The first test case is used
# to ensure that more than 1MB of dynamic memory is used to perform
# the transaction.
#
# The second test case sets the "soft-heap-limit" to 100,000 bytes (0.1 MB)
# and tests to see that this limit is not exceeded at any point during
# transaction execution.
#
# Before executing malloc5-4.* we save the value of the current soft heap
# limit in variable ::soft_limit. The original value is restored after
# running the tests.
#
set ::soft_limit [sqlite3_soft_heap_limit -1]
execsql {PRAGMA cache_size=2000}
# Test requires sqliteconfig.FbMemstat = 1 to measure highwater mark.
# We are not built with that enabled, currently
# -DSQLITE_DEFAULT_MEMSTATUS=0
if {$::tcl_platform(platform)!="windows"} {
do_test malloc5-4.1 {
execsql {BEGIN;}
execsql {DELETE FROM abc;}
for {set i 0} {$i < 10000} {incr i} {
execsql "INSERT INTO abc VALUES($i, $i, '[string repeat X 100]');"
}
execsql {COMMIT;}
db cache flush
sqlite3_release_memory
sqlite3_memory_highwater 1
execsql {SELECT * FROM abc}
set nMaxBytes [sqlite3_memory_highwater 1]
puts -nonewline " (Highwater mark: $nMaxBytes) "
expr $nMaxBytes > 1000000
} {1}
do_test malloc5-4.2 {
db eval {PRAGMA cache_size=1}
db cache flush
sqlite3_release_memory
sqlite3_soft_heap_limit 200000
sqlite3_memory_highwater 1
execsql {SELECT * FROM abc}
set nMaxBytes [sqlite3_memory_highwater 1]
puts -nonewline " (Highwater mark: $nMaxBytes) "
expr $nMaxBytes <= 210000
} {1}
do_test malloc5-4.3 {
# Check that the content of table abc is at least roughly as expected.
execsql {
SELECT count(*), sum(a), sum(b) FROM abc;
}
} [list 10000 [expr int(10000.0 * 4999.5)] [expr int(10000.0 * 4999.5)]]
}
# Restore the soft heap limit.
sqlite3_soft_heap_limit $::soft_limit
# Test that there are no problems calling sqlite3_release_memory when
# there are open in-memory databases.
#
# At one point these tests would cause a seg-fault.
#
do_test malloc5-5.1 {
db close
sqlite3 db :memory:
execsql {
BEGIN;
CREATE TABLE abc(a, b, c);
INSERT INTO abc VALUES('abcdefghi', 1234567890, NULL);
INSERT INTO abc SELECT * FROM abc;
INSERT INTO abc SELECT * FROM abc;
INSERT INTO abc SELECT * FROM abc;
INSERT INTO abc SELECT * FROM abc;
INSERT INTO abc SELECT * FROM abc;
INSERT INTO abc SELECT * FROM abc;
INSERT INTO abc SELECT * FROM abc;
}
sqlite3_release_memory
} 0
do_test malloc5-5.2 {
sqlite3_soft_heap_limit 5000
execsql {
COMMIT;
PRAGMA temp_store = memory;
SELECT * FROM abc ORDER BY a;
}
expr 1
} {1}
sqlite3_soft_heap_limit $::soft_limit
#-------------------------------------------------------------------------
# The following test cases (malloc5-6.*) test the new global LRU list
# used to determine the pages to recycle when sqlite3_release_memory is
# called and there is more than one pager open.
#
proc nPage {db} {
set bt [btree_from_db $db]
array set stats [btree_pager_stats $bt]
set stats(page)
}
db close
forcedelete test.db test.db-journal test2.db test2.db-journal
# This block of test-cases (malloc5-6.1.*) prepares two database files
# for the subsequent tests.
do_test malloc5-6.1.1 {
sqlite3 db test.db
execsql {
PRAGMA page_size=1024;
PRAGMA default_cache_size=2;
}
execsql {
PRAGMA temp_store = memory;
BEGIN;
CREATE TABLE abc(a PRIMARY KEY, b, c);
INSERT INTO abc VALUES(randstr(50,50), randstr(75,75), randstr(100,100));
INSERT INTO abc
SELECT randstr(50,50), randstr(75,75), randstr(100,100) FROM abc;
INSERT INTO abc
SELECT randstr(50,50), randstr(75,75), randstr(100,100) FROM abc;
INSERT INTO abc
SELECT randstr(50,50), randstr(75,75), randstr(100,100) FROM abc;
INSERT INTO abc
SELECT randstr(50,50), randstr(75,75), randstr(100,100) FROM abc;
INSERT INTO abc
SELECT randstr(50,50), randstr(75,75), randstr(100,100) FROM abc;
INSERT INTO abc
SELECT randstr(50,50), randstr(75,75), randstr(100,100) FROM abc;
COMMIT;
}
forcecopy test.db test2.db
sqlite3 db2 test2.db
db2 eval {PRAGMA cache_size=2}
list \
[expr ([file size test.db]/1024)>20] [expr ([file size test2.db]/1024)>20]
} {1 1}
do_test malloc5-6.1.2 {
list [execsql {PRAGMA cache_size}] [execsql {PRAGMA cache_size} db2]
} {2 2}
do_test malloc5-6.2.1 {
execsql {SELECT * FROM abc} db2
execsql {SELECT * FROM abc} db
expr [nPage db] + [nPage db2]
} {4}
# Our min-useable malloc block-size appears to be 2k (actual)
# Because this test attempts to measure actual memory freed
# causing 2 blocks to be freed will free 4K, failing the tests
if {$::tcl_platform(platform)!="windows"} {
do_test malloc5-6.2.2 {
# If we now try to reclaim some memory, it should come from the db2 cache.
sqlite3_release_memory 3000
expr [nPage db] + [nPage db2]
} {1}
do_test malloc5-6.2.3 {
# Access the db2 cache again, so that all the db2 pages have been used
# more recently than all the db pages. Then try to reclaim 3000 bytes.
# This time, 3 pages should be pulled from the db cache.
execsql { SELECT * FROM abc } db2
sqlite3_release_memory 3000
expr [nPage db] + [nPage db2]
} {0}
}
do_test malloc5-6.3.1 {
# Now open a transaction and update 2 pages in the db2 cache. Then
# do a SELECT on the db cache so that all the db pages are more recently
# used than the db2 pages. When we try to free memory, SQLite should
# free the non-dirty db2 pages, then the db pages, then finally use
# sync() to free up the dirty db2 pages. The only page that cannot be
# freed is page1 of db2. Because there is an open transaction, the
# btree layer holds a reference to page 1 in the db2 cache.
#
# UPDATE: No longer. As release_memory() does not cause a sync()
execsql {
BEGIN;
UPDATE abc SET c = randstr(100,100)
WHERE rowid = 1 OR rowid = (SELECT max(rowid) FROM abc);
} db2
execsql { SELECT * FROM abc } db
expr [nPage db] + [nPage db2]
} {4}
do_test malloc5-6.3.2 {
# Try to release 7700 bytes. This should release all the
# non-dirty pages held by db2.
sqlite3_release_memory [expr 7*1132]
list [nPage db] [nPage db2]
} {0 3}
do_test malloc5-6.3.3 {
# Try to release another 1000 bytes. This should come fromt the db
# cache, since all three pages held by db2 are either in-use or diry.
sqlite3_release_memory 1000
list [nPage db] [nPage db2]
} {0 3}
do_test malloc5-6.3.4 {
# Now release 9900 more (about 9 pages worth). This should expunge
# the rest of the db cache. But the db2 cache remains intact, because
# SQLite tries to avoid calling sync().
if {$::tcl_platform(wordSize)==8} {
sqlite3_release_memory 10500
} else {
sqlite3_release_memory 9900
}
list [nPage db] [nPage db2]
} {0 3}
do_test malloc5-6.3.5 {
# But if we are really insistent, SQLite will consent to call sync()
# if there is no other option. UPDATE: As of 3.6.2, SQLite will not
# call sync() in this scenario. So no further memory can be reclaimed.
sqlite3_release_memory 1000
list [nPage db] [nPage db2]
} {0 3}
do_test malloc5-6.3.6 {
# The referenced page (page 1 of the db2 cache) will not be freed no
# matter how much memory we ask for:
sqlite3_release_memory 31459
list [nPage db] [nPage db2]
} {0 3}
db2 close
sqlite3_soft_heap_limit $::soft_limit
test_restore_config_pagecache
finish_test
catch {db close}

258
testdata/overlay/snapshot_fault.test vendored Normal file
View file

@ -0,0 +1,258 @@
# 2015 December 10
#
# 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 the sqlite3_snapshot_xxx() APIs.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
ifcapable !snapshot {finish_test; return}
set testprefix snapshot_fault
#-------------------------------------------------------------------------
# Check that an sqlite3_snapshot_open() client cannot be tricked into
# reading a corrupt snapshot even if a second client fails while
# checkpointing the db.
#
# This test relies on a forcedelete of an open file
# resulting in: error deleting "test.db": permission denied
# Not possible to remove the open file
if {$::tcl_platform(platform)!="windows"} {
do_faultsim_test 1.0 -prep {
faultsim_delete_and_reopen
sqlite3 db2 test.db
db2 eval {
CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500));
INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500));
PRAGMA journal_mode = wal;
INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500));
BEGIN;
SELECT a FROM t1;
}
set ::snapshot [sqlite3_snapshot_get db2 main]
db2 eval COMMIT
db2 eval {
UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1;
INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500));
INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500));
INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500));
}
} -body {
db eval { PRAGMA wal_checkpoint }
} -test {
db2 eval BEGIN
if {[catch { sqlite3_snapshot_open db2 main $::snapshot } msg]} {
if {$msg != "SQLITE_ERROR_SNAPSHOT" && $msg != "SQLITE_BUSY"} {
error "error is $msg"
}
} else {
set res [db2 eval {
SELECT a FROM t1;
PRAGMA integrity_check;
}]
if {$res != "1 2 3 ok"} { error "res is $res" }
}
sqlite3_snapshot_free $::snapshot
}
}
#-------------------------------------------------------------------------
# This test is similar to the previous one. Except, after the
# "PRAGMA wal_checkpoint" command fails the db is closed and reopened
# so as to require wal file recovery. It should not be possible to open
# a snapshot that is part of the body of a recovered wal file.
#
do_faultsim_test 2.0 -prep {
faultsim_delete_and_reopen
db eval {
CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500));
INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500));
PRAGMA journal_mode = wal;
INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500));
BEGIN;
SELECT a FROM t1;
}
set ::snapshot [sqlite3_snapshot_get db main]
db eval COMMIT
db eval {
UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1;
INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500));
INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500));
INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500));
}
} -body {
db eval { PRAGMA wal_checkpoint }
} -test {
db_save
db close
db_restore_and_reopen
db eval { SELECT * FROM t1 }
db eval BEGIN
if {[catch { sqlite3_snapshot_open db main $::snapshot } msg]} {
if {$msg != "SQLITE_ERROR_SNAPSHOT" && $msg != "SQLITE_BUSY"} {
error "error is $msg"
}
} else {
# This branch should actually never be taken. But it was useful in
# determining whether or not this test was actually working (by
# running a modified version of SQLite that allowed snapshots to be
# opened following a recovery).
error "TEST HAS FAILED"
set res [db eval {
SELECT a FROM t1;
PRAGMA integrity_check;
}]
if {$res != "1 2 3 ok"} { error "res is $res" }
}
sqlite3_snapshot_free $::snapshot
}
#-------------------------------------------------------------------------
# Test the handling of faults that occur within sqlite3_snapshot_open().
#
do_faultsim_test 3.0 -prep {
faultsim_delete_and_reopen
db eval {
CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500));
INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500));
PRAGMA journal_mode = wal;
INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500));
BEGIN;
SELECT a FROM t1;
}
set ::snapshot [sqlite3_snapshot_get db main]
db eval COMMIT
db eval {
UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1;
INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500));
INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500));
INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500));
BEGIN;
}
} -body {
if { [catch { sqlite3_snapshot_open db main $::snapshot } msg] } {
error $msg
}
} -test {
faultsim_test_result {0 {}} {1 SQLITE_IOERR} {1 SQLITE_NOMEM} \
{1 SQLITE_IOERR_NOMEM} {1 SQLITE_IOERR_READ}
if {$testrc==0} {
set res [db eval {
SELECT a FROM t1;
PRAGMA integrity_check;
}]
if {$res != "1 2 3 ok"} { error "res is $res" }
}
sqlite3_snapshot_free $::snapshot
}
#-------------------------------------------------------------------------
# Test the handling of faults that occur within sqlite3_snapshot_recover().
#
reset_db
do_execsql_test 4.0 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(zzz);
INSERT INTO t1 VALUES('abc');
INSERT INTO t1 VALUES('def');
} {wal}
faultsim_save_and_close
do_test 4.0.1 {
faultsim_restore_and_reopen
db eval { SELECT * FROM sqlite_master }
sqlite3_snapshot_recover db main
} {}
db close
do_faultsim_test 4.0 -faults oom* -prep {
faultsim_restore_and_reopen
db eval { SELECT * FROM sqlite_master }
} -body {
sqlite3_snapshot_recover db main
} -test {
faultsim_test_result {0 {}} {1 SQLITE_NOMEM} {1 SQLITE_IOERR_NOMEM}
}
# The following test cases contrive to call sqlite3_snapshot_recover()
# before all pages of the *-shm file have been mapped. This tests an
# extra branch of error handling logic in snapshot_recover().
#
reset_db
do_execsql_test 4.1.0 {
PRAGMA page_size = 512;
PRAGMA journal_mode = wal;
PRAGMA wal_autocheckpoint = 0;
CREATE TABLE t1(zzz);
INSERT INTO t1 VALUES(randomblob( 500 * 9500 ));
PRAGMA user_version = 211;
} {wal 0}
do_test 4.1.1 {
list [file size test.db-shm] [file size test.db]
} {98304 512}
faultsim_save_and_close
do_faultsim_test 4.1 -faults shm* -prep {
catch { db2 close }
catch { db close }
faultsim_restore_and_reopen
sqlite3 db2 test.db
db2 eval { SELECT * FROM sqlite_master }
db eval BEGIN
sqlite3_snapshot_get_blob db main
db eval COMMIT
} -body {
sqlite3_snapshot_recover db main
} -test {
faultsim_test_result {0 {}} {1 SQLITE_IOERR}
}
#-------------------------------------------------------------------------
# Test the handling of faults that occur within sqlite3_snapshot_get().
#
reset_db
do_execsql_test 5.0 {
PRAGMA page_size = 512;
PRAGMA journal_mode = wal;
PRAGMA wal_autocheckpoint = 0;
CREATE TABLE t1(zzz);
INSERT INTO t1 VALUES(randomblob( 5000 ));
PRAGMA user_version = 211;
} {wal 0}
faultsim_save_and_close
do_faultsim_test 5 -prep {
faultsim_restore_and_reopen
execsql { SELECT count(*) FROM sqlite_master }
execsql BEGIN
} -body {
sqlite3_snapshot_get_blob db main
set {} {}
} -test {
execsql END
faultsim_test_result {0 {}} {1 SQLITE_IOERR} {1 SQLITE_NOMEM}
}
finish_test

166
testdata/overlay/win32longpath.test vendored Normal file
View file

@ -0,0 +1,166 @@
# 2013 August 27
#
# 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 script is testing the file name handling provided
# by the "win32-longpath" VFS.
#
if {$tcl_platform(platform)!="windows"} return
proc get_goversion {} {
if {$::tcl_platform(platform) eq "windows"} {
if {[info exists ::env(ComSpec)]} {
set comSpec $::env(ComSpec)
} else {
# NOTE: Hard-code the typical default value.
set comSpec {C:\Windows\system32\cmd.exe}
}
return [string map [list \\ /] \
[string trim [exec -- $comSpec /c go version ]]]
} else {
return [go version]
}
}
set goVer [get_goversion]
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix win32longpath
do_test 1.0 {
file_control_vfsname db
} win32
db close
set rawPath [get_pwd]
set path [file nativename $rawPath]
sqlite3 db [file join $path test.db] -vfs win32-longpath
do_test 1.1 {
file_control_vfsname db
} win32-longpath
do_test 1.2 {
db eval {
BEGIN EXCLUSIVE;
CREATE TABLE t1(x);
INSERT INTO t1 VALUES(1);
INSERT INTO t1 VALUES(2);
INSERT INTO t1 VALUES(3);
INSERT INTO t1 VALUES(4);
SELECT x FROM t1 ORDER BY x;
COMMIT;
}
} {1 2 3 4}
set longPath(1) \\\\?\\$path\\[pid]
set uriPath(1a) %5C%5C%3F%5C$path\\[pid]
set uriPath(1b) %5C%5C%3F%5C$rawPath/[pid]
make_win32_dir $longPath(1)
set longPath(2) $longPath(1)\\[string repeat X 255]
set uriPath(2a) $uriPath(1a)\\[string repeat X 255]
set uriPath(2b) $uriPath(1b)/[string repeat X 255]
make_win32_dir $longPath(2)
set longPath(3) $longPath(2)\\[string repeat Y 255]
set uriPath(3a) $uriPath(2a)\\[string repeat Y 255]
set uriPath(3b) $uriPath(2b)/[string repeat Y 255]
make_win32_dir $longPath(3)
set fileName $longPath(3)\\test.db
set uri(1a) file:$uriPath(3a)\\test.db
set uri(1b) file:$uriPath(3b)/test.db
set uri(1c) file:///$uriPath(3a)\\test.db
set uri(1d) file:///$uriPath(3b)/test.db
set uri(1e) file://localhost/$uriPath(3a)\\test.db
set uri(1f) file://localhost/$uriPath(3b)/test.db
# Starting with Windows 10 v1607 OSBuild 14393, long paths are supported
# Go 1.17+ utilizes this capability and a result this test will fail
# because the path CAN be created.
#
# 2022-12-10: As Go 1.16 or older is no more supported, disable this test entirely.
#
# if { ([string first "1.17" $goVer] < 0) && ([string first "1.18" $goVer] < 0) && ([string first "1.19" $goVer] < 0) } {
#
# do_test 1.3 {
# list [catch {sqlite3 db2 [string range $fileName 4 end]} msg] $msg
# } {1 {unable to open database file}}
# } else {
puts "win32longpath-1.3... skipped"
# }
sqlite3 db3 $fileName -vfs win32-longpath
do_test 1.4 {
db3 eval {
BEGIN EXCLUSIVE;
CREATE TABLE t1(x);
INSERT INTO t1 VALUES(5);
INSERT INTO t1 VALUES(6);
INSERT INTO t1 VALUES(7);
INSERT INTO t1 VALUES(8);
SELECT x FROM t1 ORDER BY x;
COMMIT;
}
} {5 6 7 8}
db3 close
# puts " Database exists \{[exists_win32_path $fileName]\}"
sqlite3 db3 $fileName -vfs win32-longpath
do_test 1.5 {
db3 eval {
PRAGMA journal_mode = WAL;
}
} {wal}
do_test 1.6 {
db3 eval {
BEGIN EXCLUSIVE;
INSERT INTO t1 VALUES(9);
INSERT INTO t1 VALUES(10);
INSERT INTO t1 VALUES(11);
INSERT INTO t1 VALUES(12);
SELECT x FROM t1 ORDER BY x;
COMMIT;
}
} {5 6 7 8 9 10 11 12}
db3 close
# puts " Database exists \{[exists_win32_path $fileName]\}"
foreach tn {1a 1b 1c 1d 1e 1f} {
sqlite3 db3 $uri($tn) -vfs win32-longpath -uri 1 -translatefilename 0
do_test 1.7.$tn {
db3 eval {
SELECT x FROM t1 ORDER BY x;
}
} {5 6 7 8 9 10 11 12}
db3 close
}
do_delete_win32_file $fileName
# puts " Files remaining \{[find_win32_file $longPath(3)\\*]\}"
do_remove_win32_dir $longPath(3)
do_remove_win32_dir $longPath(2)
do_remove_win32_dir $longPath(1)
finish_test

197
testdata/tcl/8_3_names.test vendored Normal file
View file

@ -0,0 +1,197 @@
# 2011 May 17
#
# 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.
#
#***********************************************************************
#
# Test cases for the SQLITE_ENABLE_8_3_NAMES feature that forces all
# filename extensions to be limited to 3 characters. Some embedded
# systems need this to work around microsoft FAT patents, but this
# feature should be disabled on most deployments.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
ifcapable !8_3_names {
finish_test
return
}
db close
sqlite3_shutdown
sqlite3_config_uri 1
do_test 8_3_names-1.0 {
forcedelete test.db test.nal test.db-journal
sqlite3 db test.db
db eval {
PRAGMA cache_size=10;
CREATE TABLE t1(x);
INSERT INTO t1 VALUES(randomblob(20000));
BEGIN;
DELETE FROM t1;
INSERT INTO t1 VALUES(randomblob(15000));
}
file exists test.db-journal
} 1
do_test 8_3_names-1.1 {
file exists test.nal
} 0
do_test 8_3_names-1.2 {
db eval {
ROLLBACK;
SELECT length(x) FROM t1
}
} 20000
db close
do_test 8_3_names-2.0 {
forcedelete test.db test.nal test.db-journal
sqlite3 db file:./test.db?8_3_names=1
db eval {
PRAGMA cache_size=10;
CREATE TABLE t1(x);
INSERT INTO t1 VALUES(randomblob(20000));
BEGIN;
DELETE FROM t1;
INSERT INTO t1 VALUES(randomblob(15000));
}
file exists test.db-journal
} 0
do_test 8_3_names-2.1 {
file exists test.nal
} 1
forcedelete test2.db test2.nal test2.db-journal
copy_file test.db test2.db
copy_file test.nal test2.nal
do_test 8_3_names-2.2 {
db eval {
COMMIT;
SELECT length(x) FROM t1
}
} 15000
do_test 8_3_names-2.3 {
sqlite3 db2 file:./test2.db?8_3_names=1
db2 eval {
PRAGMA integrity_check;
SELECT length(x) FROM t1;
}
} {ok 20000}
db close
do_test 8_3_names-3.0 {
forcedelete test.db test.nal test.db-journal
sqlite3 db file:./test.db?8_3_names=0
db eval {
PRAGMA cache_size=10;
CREATE TABLE t1(x);
INSERT INTO t1 VALUES(randomblob(20000));
BEGIN;
DELETE FROM t1;
INSERT INTO t1 VALUES(randomblob(15000));
}
file exists test.db-journal
} 1
do_test 8_3_names-3.1 {
file exists test.nal
} 0
forcedelete test2.db test2.nal test2.db-journal
copy_file test.db test2.db
copy_file test.db-journal test2.db-journal
do_test 8_3_names-3.2 {
db eval {
COMMIT;
SELECT length(x) FROM t1
}
} 15000
do_test 8_3_names-3.3 {
sqlite3 db2 file:./test2.db?8_3_names=0
db2 eval {
PRAGMA integrity_check;
SELECT length(x) FROM t1;
}
} {ok 20000}
##########################################################################
# Master journals.
#
db close
forcedelete test.db test2.db
do_test 8_3_names-4.0 {
sqlite3 db file:./test.db?8_3_names=1
db eval {
CREATE TABLE t1(x);
INSERT INTO t1 VALUES(1);
ATTACH 'file:./test2.db?8_3_names=1' AS db2;
CREATE TABLE db2.t2(y);
INSERT INTO t2 VALUES(2);
BEGIN;
INSERT INTO t1 VALUES(3);
INSERT INTO t2 VALUES(4);
COMMIT;
SELECT * FROM t1, t2 ORDER BY x, y
}
} {1 2 1 4 3 2 3 4}
##########################################################################
# WAL mode.
#
ifcapable !wal {
finish_test
return
}
db close
forcedelete test.db
do_test 8_3_names-5.0 {
sqlite3 db file:./test.db?8_3_names=1
load_static_extension db wholenumber
db eval {
PRAGMA journal_mode=WAL;
CREATE TABLE t1(x);
CREATE VIRTUAL TABLE nums USING wholenumber;
INSERT INTO t1 SELECT value FROM nums WHERE value BETWEEN 1 AND 1000;
BEGIN;
UPDATE t1 SET x=x*2;
}
sqlite3 db2 file:./test.db?8_3_names=1
load_static_extension db2 wholenumber
db2 eval {
BEGIN;
SELECT sum(x) FROM t1;
}
} {500500}
do_test 8_3_names-5.1 {
file exists test.db-wal
} 0
do_test 8_3_names-5.2 {
file exists test.wal
} 1
do_test 8_3_names-5.3 {
file exists test.db-shm
} 0
do_test 8_3_names-5.4 {
file exists test.shm
} 1
do_test 8_3_names-5.5 {
db eval {
COMMIT;
SELECT sum(x) FROM t1;
}
} {1001000}
do_test 8_3_names-5.6 {
db2 eval {
SELECT sum(x) FROM t1;
}
} {500500}
finish_test

136
testdata/tcl/affinity2.test vendored Normal file
View file

@ -0,0 +1,136 @@
# 2015-06-02
#
# 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 type affinity in comparison operations.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix affinity2
do_execsql_test affinity2-100 {
CREATE TABLE t1(
xi INTEGER,
xr REAL,
xb BLOB,
xn NUMERIC,
xt TEXT
);
INSERT INTO t1(rowid,xi,xr,xb,xn,xt) VALUES(1,1,1,1,1,1);
INSERT INTO t1(rowid,xi,xr,xb,xn,xt) VALUES(2,'2','2','2','2','2');
INSERT INTO t1(rowid,xi,xr,xb,xn,xt) VALUES(3,'03','03','03','03','03');
} {}
do_execsql_test affinity2-110 {
SELECT xi, typeof(xi) FROM t1 ORDER BY rowid;
} {1 integer 2 integer 3 integer}
do_execsql_test affinity2-120 {
SELECT xr, typeof(xr) FROM t1 ORDER BY rowid;
} {1.0 real 2.0 real 3.0 real}
do_execsql_test affinity2-130 {
SELECT xb, typeof(xb) FROM t1 ORDER BY rowid;
} {1 integer 2 text 03 text}
do_execsql_test affinity2-140 {
SELECT xn, typeof(xn) FROM t1 ORDER BY rowid;
} {1 integer 2 integer 3 integer}
do_execsql_test affinity2-150 {
SELECT xt, typeof(xt) FROM t1 ORDER BY rowid;
} {1 text 2 text 03 text}
do_execsql_test affinity2-200 {
SELECT rowid, xi==xt, xi==xb, xi==+xt FROM t1 ORDER BY rowid;
} {1 1 1 1 2 1 1 1 3 1 1 1}
do_execsql_test affinity2-210 {
SELECT rowid, xr==xt, xr==xb, xr==+xt FROM t1 ORDER BY rowid;
} {1 1 1 1 2 1 1 1 3 1 1 1}
do_execsql_test affinity2-220 {
SELECT rowid, xn==xt, xn==xb, xn==+xt FROM t1 ORDER BY rowid;
} {1 1 1 1 2 1 1 1 3 1 1 1}
do_execsql_test affinity2-300 {
SELECT rowid, xt==+xi, xt==xi, xt==xb FROM t1 ORDER BY rowid;
} {1 1 1 0 2 1 1 1 3 0 1 1}
#-------------------------------------------------------------------------
do_execsql_test 400 {
CREATE TABLE ttt(c0, c1);
CREATE INDEX ii ON ttt(CAST(c0 AS NUMERIC));
INSERT INTO ttt VALUES('abc', '-1');
}
do_execsql_test 410 {
SELECT * FROM ttt WHERE CAST(c0 AS NUMERIC) > c1 GROUP BY rowid;
} {abc -1}
do_execsql_test 420 {
SELECT * FROM ttt INDEXED BY ii WHERE CAST(c0 AS NUMERIC) > c1 GROUP BY rowid;
} {abc -1}
do_execsql_test 430 {
CREATE TABLE t3(a, b, c INTEGER);
CREATE INDEX t3ac ON t3(a, c-1);
INSERT INTO t3 VALUES(1, 1, 1);
INSERT INTO t3 VALUES(2, 1, 0);
INSERT INTO t3 VALUES(3, 1, 1);
INSERT INTO t3 VALUES(4, 1, 0);
INSERT INTO t3 VALUES(5, 1, 1);
}
do_execsql_test 440 {
SELECT * FROM t3 WHERE c='0' ORDER BY a;
} {2 1 0 4 1 0}
# 2019-08-22 ticket https://sqlite.org/src/info/d99f1ffe836c591ac57f
# False positive in sqlite3ExprNeedsNoAffinityChange()
#
do_execsql_test 500 {
DROP TABLE IF EXISTS t0;
CREATE TABLE t0(c0 TEXT UNIQUE, c1);
INSERT INTO t0(c0) VALUES (-1);
SELECT quote(- x'ce'), quote(t0.c0), quote(- x'ce' >= t0.c0) FROM t0;
} {0 '-1' 1}
do_execsql_test 501 {
SELECT * FROM t0 WHERE - x'ce' >= t0.c0;
} {-1 {}}
do_execsql_test 502 {
SELECT quote(+-+x'ce'), quote(t0.c0), quote(+-+x'ce' >= t0.c0) FROM t0;
} {0 '-1' 1}
do_execsql_test 503 {
SELECT * FROM t0 WHERE +-+x'ce' >= t0.c0;
} {-1 {}}
do_execsql_test 504 {
SELECT quote(- 'ce'), quote(t0.c0), quote(- 'ce' >= t0.c0) FROM t0;
} {0 '-1' 1}
do_execsql_test 505 {
SELECT * FROM t0 WHERE - 'ce' >= t0.c0;
} {-1 {}}
do_execsql_test 506 {
SELECT quote(+-+'ce'), quote(t0.c0), quote(+-+'ce' >= t0.c0) FROM t0;
} {0 '-1' 1}
do_execsql_test 507 {
SELECT * FROM t0 WHERE +-+'ce' >= t0.c0;
} {-1 {}}
# 2019-08-30 ticket https://www.sqlite.org/src/info/40812aea1fde9594
#
# Due to some differences in floating point computations, these tests do not
# work under valgrind.
#
if {![info exists ::G(valgrind)]} {
do_execsql_test 600 {
DROP TABLE IF EXISTS t0;
CREATE TABLE t0(c0 REAL UNIQUE);
INSERT INTO t0(c0) VALUES (3175546974276630385);
SELECT 3175546974276630385 < c0 FROM t0;
} {1}
do_execsql_test 601 {
SELECT 1 FROM t0 WHERE 3175546974276630385 < c0;
} {1}
}
finish_test

123
testdata/tcl/affinity3.test vendored Normal file
View file

@ -0,0 +1,123 @@
# 2017-01-16
#
# 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.
#
#***********************************************************************
#
# Test cases for bugs:
#
# https://www.sqlite.org/src/info/91e2e8ba6ff2e2
# https://www.sqlite.org/src/info/7ffd1ca1d2ad4ecf
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# Ticket https://www.sqlite.org/src/info/91e2e8ba6ff2e2 (2011-09-19)
# Automatic index causes undesired type conversions
#
do_execsql_test affinity3-100 {
CREATE TABLE customer (id INT PRIMARY KEY);
CREATE TABLE apr (id INT PRIMARY KEY, apr REAL);
CREATE VIEW v1 AS
SELECT c.id, i.apr
FROM customer c
LEFT JOIN apr i ON i.id=c.id;
CREATE VIEW v1rj AS
SELECT c.id, i.apr
FROM apr i
RIGHT JOIN customer c ON i.id=c.id;
CREATE VIEW v2 AS
SELECT c.id, v1.apr
FROM customer c
LEFT JOIN v1 ON v1.id=c.id;
CREATE VIEW v2rj AS
SELECT c.id, v1.apr
FROM v1 RIGHT JOIN customer c ON v1.id=c.id;
CREATE VIEW v2rjrj AS
SELECT c.id, v1rj.apr
FROM v1rj RIGHT JOIN customer c ON v1rj.id=c.id;
INSERT INTO customer (id) VALUES (1);
INSERT INTO apr (id, apr) VALUES (1, 12);
INSERT INTO customer (id) VALUES (2);
INSERT INTO apr (id, apr) VALUES (2, 12.01);
}
do_execsql_test affinity3-110 {
PRAGMA automatic_index=ON;
SELECT id, (apr / 100), typeof(apr) apr_type FROM v1;
} {1 0.12 real 2 0.1201 real}
do_execsql_test affinity3-111 {
PRAGMA automatic_index=ON;
SELECT id, (apr / 100), typeof(apr) apr_type FROM v1rj;
} {1 0.12 real 2 0.1201 real}
do_execsql_test affinity3-120 {
SELECT id, (apr / 100), typeof(apr) apr_type FROM v2;
} {1 0.12 real 2 0.1201 real}
do_execsql_test affinity3-121 {
SELECT id, (apr / 100), typeof(apr) apr_type FROM v2rj;
} {1 0.12 real 2 0.1201 real}
do_execsql_test affinity3-122 {
SELECT id, (apr / 100), typeof(apr) apr_type FROM v2rjrj;
} {1 0.12 real 2 0.1201 real}
do_execsql_test affinity3-130 {
PRAGMA automatic_index=OFF;
SELECT id, (apr / 100), typeof(apr) apr_type FROM v1;
} {1 0.12 real 2 0.1201 real}
do_execsql_test affinity3-131 {
SELECT id, (apr / 100), typeof(apr) apr_type FROM v1rj;
} {1 0.12 real 2 0.1201 real}
do_execsql_test affinity3-140 {
SELECT id, (apr / 100), typeof(apr) apr_type FROM v2;
} {1 0.12 real 2 0.1201 real}
do_execsql_test affinity3-141 {
SELECT id, (apr / 100), typeof(apr) apr_type FROM v2rj;
} {1 0.12 real 2 0.1201 real}
do_execsql_test affinity3-142 {
SELECT id, (apr / 100), typeof(apr) apr_type FROM v2rjrj;
} {1 0.12 real 2 0.1201 real}
# Ticket https://www.sqlite.org/src/info/7ffd1ca1d2ad4ecf (2017-01-16)
# Incorrect affinity when using automatic indexes
#
do_execsql_test affinity3-200 {
CREATE TABLE map_integer (id INT, name);
INSERT INTO map_integer VALUES(1,'a');
CREATE TABLE map_text (id TEXT, name);
INSERT INTO map_text VALUES('4','e');
CREATE TABLE data (id TEXT, name);
INSERT INTO data VALUES(1,'abc');
INSERT INTO data VALUES('4','xyz');
CREATE VIEW idmap as
SELECT * FROM map_integer
UNION SELECT * FROM map_text;
CREATE TABLE mzed AS SELECT * FROM idmap;
}
do_execsql_test affinity3-210 {
PRAGMA automatic_index=ON;
SELECT * FROM data JOIN idmap USING(id);
} {4 xyz e}
do_execsql_test affinity3-220 {
SELECT * FROM data JOIN mzed USING(id);
} {4 xyz e}
do_execsql_test affinity3-250 {
PRAGMA automatic_index=OFF;
SELECT * FROM data JOIN idmap USING(id);
} {4 xyz e}
do_execsql_test affinity3-260 {
SELECT * FROM data JOIN mzed USING(id);
} {4 xyz e}
finish_test

78
testdata/tcl/aggerror.test vendored Normal file
View file

@ -0,0 +1,78 @@
# 2006 January 20
#
# 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.
#
# This file implements tests for calling sqlite3_result_error()
# from within an aggregate function implementation.
#
# $Id: aggerror.test,v 1.3 2006/05/03 23:34:06 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# Add the x_count aggregate function to the database handle.
# x_count will error out if its input is 40 or 41 or if its
# final results is 42. Make sure that such errors are handled
# appropriately.
#
do_test aggerror-1.1 {
set DB [sqlite3_connection_pointer db]
sqlite3_create_aggregate $DB
execsql {
CREATE TABLE t1(a);
INSERT INTO t1 VALUES(1);
INSERT INTO t1 VALUES(2);
INSERT INTO t1 SELECT a+2 FROM t1;
INSERT INTO t1 SELECT a+4 FROM t1;
INSERT INTO t1 SELECT a+8 FROM t1;
INSERT INTO t1 SELECT a+16 FROM t1;
INSERT INTO t1 SELECT a+32 FROM t1 ORDER BY a LIMIT 7;
SELECT x_count(*) FROM t1;
}
} {39}
do_test aggerror-1.2 {
execsql {
INSERT INTO t1 VALUES(40);
SELECT x_count(*) FROM t1;
}
} {40}
do_test aggerror-1.3 {
catchsql {
SELECT x_count(a) FROM t1;
}
} {1 {value of 40 handed to x_count}}
ifcapable utf16 {
do_test aggerror-1.4 {
execsql {
UPDATE t1 SET a=41 WHERE a=40
}
catchsql {
SELECT x_count(a) FROM t1;
}
} {1 abc}
}
do_test aggerror-1.5 {
execsql {
SELECT x_count(*) FROM t1
}
} 40
do_test aggerror-1.6 {
execsql {
INSERT INTO t1 VALUES(40);
INSERT INTO t1 VALUES(42);
}
catchsql {
SELECT x_count(*) FROM t1;
}
} {1 {x_count totals to 42}}
finish_test

365
testdata/tcl/aggnested.test vendored Normal file
View file

@ -0,0 +1,365 @@
# 2012-08-23
#
# 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.
#
# This file implements tests for processing aggregate queries with
# subqueries in which the subqueries hold the aggregate functions
# or in which the subqueries are themselves aggregate queries
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix aggnested
do_test aggnested-1.1 {
db eval {
CREATE TABLE t1(a1 INTEGER);
INSERT INTO t1 VALUES(1), (2), (3);
CREATE TABLE t2(b1 INTEGER);
INSERT INTO t2 VALUES(4), (5);
SELECT (SELECT group_concat(a1,'x') FROM t2) FROM t1;
}
} {1x2x3}
do_test aggnested-1.2 {
db eval {
SELECT
(SELECT group_concat(a1,'x') || '-' || group_concat(b1,'y') FROM t2)
FROM t1;
}
} {1x2x3-4y5}
do_test aggnested-1.3 {
db eval {
SELECT (SELECT group_concat(b1,a1) FROM t2) FROM t1;
}
} {415 425 435}
do_test aggnested-1.4 {
db eval {
SELECT (SELECT group_concat(a1,b1) FROM t2) FROM t1;
}
} {151 252 353}
# This test case is a copy of the one in
# http://www.mail-archive.com/sqlite-users@sqlite.org/msg70787.html
#
do_test aggnested-2.0 {
sqlite3 db2 :memory:
db2 eval {
CREATE TABLE t1 (A1 INTEGER NOT NULL,A2 INTEGER NOT NULL,A3 INTEGER NOT
NULL,A4 INTEGER NOT NULL,PRIMARY KEY(A1));
REPLACE INTO t1 VALUES(1,11,111,1111);
REPLACE INTO t1 VALUES(2,22,222,2222);
REPLACE INTO t1 VALUES(3,33,333,3333);
CREATE TABLE t2 (B1 INTEGER NOT NULL,B2 INTEGER NOT NULL,B3 INTEGER NOT
NULL,B4 INTEGER NOT NULL,PRIMARY KEY(B1));
REPLACE INTO t2 VALUES(1,88,888,8888);
REPLACE INTO t2 VALUES(2,99,999,9999);
SELECT (SELECT GROUP_CONCAT(CASE WHEN a1=1 THEN'A' ELSE 'B' END) FROM t2),
t1.*
FROM t1;
}
} {A,B,B 1 11 111 1111}
db2 close
##################### Test cases for ticket [bfbf38e5e9956ac69f] ############
#
# This first test case is the original problem report:
do_test aggnested-3.0 {
db eval {
CREATE TABLE AAA (
aaa_id INTEGER PRIMARY KEY AUTOINCREMENT
);
CREATE TABLE RRR (
rrr_id INTEGER PRIMARY KEY AUTOINCREMENT,
rrr_date INTEGER NOT NULL,
rrr_aaa INTEGER
);
CREATE TABLE TTT (
ttt_id INTEGER PRIMARY KEY AUTOINCREMENT,
target_aaa INTEGER NOT NULL,
source_aaa INTEGER NOT NULL
);
insert into AAA (aaa_id) values (2);
insert into TTT (ttt_id, target_aaa, source_aaa)
values (4469, 2, 2);
insert into TTT (ttt_id, target_aaa, source_aaa)
values (4476, 2, 1);
insert into RRR (rrr_id, rrr_date, rrr_aaa)
values (0, 0, NULL);
insert into RRR (rrr_id, rrr_date, rrr_aaa)
values (2, 4312, 2);
SELECT i.aaa_id,
(SELECT sum(CASE WHEN (t.source_aaa == i.aaa_id) THEN 1 ELSE 0 END)
FROM TTT t
) AS segfault
FROM
(SELECT curr.rrr_aaa as aaa_id
FROM RRR curr
-- you also can comment out the next line
-- it causes segfault to happen after one row is outputted
INNER JOIN AAA a ON (curr.rrr_aaa = aaa_id)
LEFT JOIN RRR r ON (r.rrr_id <> 0 AND r.rrr_date < curr.rrr_date)
GROUP BY curr.rrr_id
HAVING r.rrr_date IS NULL
) i;
}
} {2 1}
# Further variants of the test case, as found in the ticket
#
do_test aggnested-3.1 {
db eval {
DROP TABLE IF EXISTS t1;
DROP TABLE IF EXISTS t2;
CREATE TABLE t1 (
id1 INTEGER PRIMARY KEY AUTOINCREMENT,
value1 INTEGER
);
INSERT INTO t1 VALUES(4469,2),(4476,1);
CREATE TABLE t2 (
id2 INTEGER PRIMARY KEY AUTOINCREMENT,
value2 INTEGER
);
INSERT INTO t2 VALUES(0,1),(2,2);
SELECT
(SELECT sum(value2==xyz) FROM t2)
FROM
(SELECT curr.value1 as xyz
FROM t1 AS curr LEFT JOIN t1 AS other
GROUP BY curr.id1);
}
} {1 1}
do_test aggnested-3.1-rj {
db eval {
SELECT
(SELECT sum(value2==xyz) FROM t2)
FROM
(SELECT curr.value1 as xyz
FROM t1 AS other RIGHT JOIN t1 AS curr
GROUP BY curr.id1);
}
} {1 1}
do_test aggnested-3.2 {
db eval {
DROP TABLE IF EXISTS t1;
DROP TABLE IF EXISTS t2;
CREATE TABLE t1 (
id1 INTEGER,
value1 INTEGER,
x1 INTEGER
);
INSERT INTO t1 VALUES(4469,2,98),(4469,1,99),(4469,3,97);
CREATE TABLE t2 (
value2 INTEGER
);
INSERT INTO t2 VALUES(1);
SELECT
(SELECT sum(value2==xyz) FROM t2)
FROM
(SELECT value1 as xyz, max(x1) AS pqr
FROM t1
GROUP BY id1);
SELECT
(SELECT sum(value2<>xyz) FROM t2)
FROM
(SELECT value1 as xyz, max(x1) AS pqr
FROM t1
GROUP BY id1);
}
} {1 0}
do_test aggnested-3.3 {
db eval {
DROP TABLE IF EXISTS t1;
DROP TABLE IF EXISTS t2;
CREATE TABLE t1(id1, value1);
INSERT INTO t1 VALUES(4469,2),(4469,1);
CREATE TABLE t2 (value2);
INSERT INTO t2 VALUES(1);
SELECT (SELECT sum(value2=value1) FROM t2), max(value1)
FROM t1
GROUP BY id1;
}
} {0 2}
# A batch of queries all doing approximately the same operation involving
# two nested aggregate queries.
#
do_test aggnested-3.11 {
db eval {
DROP TABLE IF EXISTS t1;
DROP TABLE IF EXISTS t2;
CREATE TABLE t1(id1, value1);
INSERT INTO t1 VALUES(4469,12),(4469,11),(4470,34);
CREATE INDEX t1id1 ON t1(id1);
CREATE TABLE t2 (value2);
INSERT INTO t2 VALUES(12),(34),(34);
INSERT INTO t2 SELECT value2 FROM t2;
SELECT max(value1), (SELECT count(*) FROM t2 WHERE value2=max(value1))
FROM t1
GROUP BY id1;
}
} {12 2 34 4}
do_test aggnested-3.12 {
db eval {
SELECT max(value1), (SELECT count(*) FROM t2 WHERE value2=value1)
FROM t1
GROUP BY id1;
}
} {12 2 34 4}
do_test aggnested-3.13 {
db eval {
SELECT value1, (SELECT sum(value2=value1) FROM t2)
FROM t1;
}
} {12 2 11 0 34 4}
do_test aggnested-3.14 {
db eval {
SELECT value1, (SELECT sum(value2=value1) FROM t2)
FROM t1
WHERE value1 IN (SELECT max(value1) FROM t1 GROUP BY id1);
}
} {12 2 34 4}
do_test aggnested-3.15 {
# FIXME: If case 3.16 works, then this case really ought to work too...
catchsql {
SELECT max(value1), (SELECT sum(value2=max(value1)) FROM t2)
FROM t1
GROUP BY id1;
}
} {1 {misuse of aggregate function max()}}
do_test aggnested-3.16 {
db eval {
SELECT max(value1), (SELECT sum(value2=value1) FROM t2)
FROM t1
GROUP BY id1;
}
} {12 2 34 4}
# 2019-08-31
# Problem found by dbsqlfuzz
#
do_execsql_test aggnested-4.1 {
DROP TABLE IF EXISTS aa;
DROP TABLE IF EXISTS bb;
CREATE TABLE aa(x INT); INSERT INTO aa(x) VALUES(123);
CREATE TABLE bb(y INT); INSERT INTO bb(y) VALUES(456);
SELECT (SELECT sum(x+(SELECT y)) FROM bb) FROM aa;
} {579}
do_execsql_test aggnested-4.2 {
SELECT (SELECT sum(x+y) FROM bb) FROM aa;
} {579}
do_execsql_test aggnested-4.3 {
DROP TABLE IF EXISTS tx;
DROP TABLE IF EXISTS ty;
CREATE TABLE tx(x INT);
INSERT INTO tx VALUES(1),(2),(3),(4),(5);
CREATE TABLE ty(y INT);
INSERT INTO ty VALUES(91),(92),(93);
SELECT min((SELECT count(y) FROM ty)) FROM tx;
} {3}
do_execsql_test aggnested-4.4 {
SELECT max((SELECT a FROM (SELECT count(*) AS a FROM ty) AS s)) FROM tx;
} {3}
#--------------------------------------------------------------------------
#
reset_db
do_execsql_test 5.0 {
CREATE TABLE x1(a, b);
INSERT INTO x1 VALUES(1, 2);
CREATE TABLE x2(x);
INSERT INTO x2 VALUES(NULL), (NULL), (NULL);
}
# At one point, aggregate "total()" in the query below was being processed
# as part of the outer SELECT, not as part of the sub-select with no FROM
# clause.
do_execsql_test 5.1 {
SELECT ( SELECT total( (SELECT b FROM x1) ) ) FROM x2;
} {2.0 2.0 2.0}
do_execsql_test 5.2 {
SELECT ( SELECT total( (SELECT 2 FROM x1) ) ) FROM x2;
} {2.0 2.0 2.0}
do_execsql_test 5.3 {
CREATE TABLE t1(a);
CREATE TABLE t2(b);
}
do_execsql_test 5.4 {
SELECT(
SELECT max(b) LIMIT (
SELECT total( (SELECT a FROM t1) )
)
)
FROM t2;
} {{}}
do_execsql_test 5.5 {
CREATE TABLE a(b);
WITH c AS(SELECT a)
SELECT(SELECT(SELECT group_concat(b, b)
LIMIT(SELECT 0.100000 *
AVG(DISTINCT(SELECT 0 FROM a ORDER BY b, b, b))))
FROM a GROUP BY b,
b, b) FROM a EXCEPT SELECT b FROM a ORDER BY b,
b, b;
}
#-------------------------------------------------------------------------
# dbsqlfuzz a779227f721a834df95f4f42d0c31550a1f8b8a2
#
reset_db
do_execsql_test 6.0 {
CREATE TABLE t1(a);
CREATE TABLE t2(b);
INSERT INTO t1 VALUES('x');
INSERT INTO t2 VALUES(1);
}
do_execsql_test 6.1.1 {
SELECT (
SELECT t2.b FROM (SELECT t2.b AS c FROM t1) GROUP BY 1 HAVING t2.b
)
FROM t2 GROUP BY 'constant_string';
} {1}
do_execsql_test 6.1.2 {
SELECT (
SELECT c FROM (SELECT t2.b AS c FROM t1) GROUP BY c HAVING t2.b
)
FROM t2 GROUP BY 'constant_string';
} {1}
do_execsql_test 6.2.0 {
UPDATE t2 SET b=0
}
do_execsql_test 6.2.1 {
SELECT (
SELECT t2.b FROM (SELECT t2.b AS c FROM t1) GROUP BY 1 HAVING t2.b
)
FROM t2 GROUP BY 'constant_string';
} {{}}
do_execsql_test 6.2.2 {
SELECT (
SELECT c FROM (SELECT t2.b AS c FROM t1) GROUP BY c HAVING t2.b
)
FROM t2 GROUP BY 'constant_string';
} {{}}
finish_test

140
testdata/tcl/alias.test vendored Normal file
View file

@ -0,0 +1,140 @@
# 2008 August 28
#
# 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 script is correct code generation of aliased result-set
# values. See ticket #3343.
#
# $Id: alias.test,v 1.3 2009/04/23 13:22:44 drh Exp $
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# Aliases are currently evaluated twice. We might try to change this
# in the future. But not now.
return
# A procedure to return a sequence of increasing integers.
#
namespace eval ::seq {
variable counter 0
proc value {args} {
variable counter
incr counter
return $counter
}
proc reset {} {
variable counter
set counter 0
}
}
do_test alias-1.1 {
db function sequence ::seq::value
db eval {
CREATE TABLE t1(x);
INSERT INTO t1 VALUES(9);
INSERT INTO t1 VALUES(8);
INSERT INTO t1 VALUES(7);
SELECT x, sequence() FROM t1;
}
} {9 1 8 2 7 3}
do_test alias-1.2 {
::seq::reset
db eval {
SELECT x, sequence() AS y FROM t1 WHERE y>0
}
} {9 1 8 2 7 3}
do_test alias-1.3 {
::seq::reset
db eval {
SELECT x, sequence() AS y FROM t1 WHERE y>0 AND y<99
}
} {9 1 8 2 7 3}
do_test alias-1.4 {
::seq::reset
db eval {
SELECT x, sequence() AS y FROM t1 WHERE y>0 AND y<99 AND y!=55
}
} {9 1 8 2 7 3}
do_test alias-1.5 {
::seq::reset
db eval {
SELECT x, sequence() AS y FROM t1
WHERE y>0 AND y<99 AND y!=55 AND y NOT IN (56,57,58)
AND y NOT LIKE 'abc%' AND y%10==2
}
} {8 2}
do_test alias-1.6 {
::seq::reset
db eval {
SELECT x, sequence() AS y FROM t1 WHERE y BETWEEN 0 AND 99
}
} {9 1 8 2 7 3}
#do_test alias-1.7 {
# ::seq::reset
# db eval {
# SELECT x, sequence() AS y FROM t1 WHERE y IN (55,66,3)
# }
#} {7 3}
do_test alias-1.8 {
::seq::reset
db eval {
SELECT x, 1-sequence() AS y FROM t1 ORDER BY y
}
} {7 -2 8 -1 9 0}
do_test alias-1.9 {
::seq::reset
db eval {
SELECT x, sequence() AS y FROM t1 ORDER BY -y
}
} {7 3 8 2 9 1}
do_test alias-1.10 {
::seq::reset
db eval {
SELECT x, sequence() AS y FROM t1 ORDER BY x%2, y
}
} {8 2 9 1 7 3}
unset -nocomplain random_int_list
set random_int_list [db eval {
SELECT random()&2147483647 AS r FROM t1, t1, t1, t1 ORDER BY r
}]
do_test alias-1.11 {
lsort -integer $::random_int_list
} $random_int_list
do_test alias-2.1 {
db eval {
SELECT 4 UNION SELECT 1 ORDER BY 1
}
} {1 4}
do_test alias-2.2 {
db eval {
SELECT 4 UNION SELECT 1 UNION SELECT 9 ORDER BY 1
}
} {1 4 9}
if 0 {
# Aliases in the GROUP BY clause cause the expression to be evaluated
# twice in the current implementation. This might change in the future.
#
do_test alias-3.1 {
::seq::reset
db eval {
SELECT sequence(*) AS y, count(*) AS z FROM t1 GROUP BY y ORDER BY z, y
}
} {1 1 2 1 3 1}
}
finish_test

51
testdata/tcl/all.test vendored Normal file
View file

@ -0,0 +1,51 @@
# 2001 September 15
#
# 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 runs all tests.
#
set testdir [file dirname $argv0]
source $testdir/permutations.test
run_test_suite full
ifcapable rbu { run_test_suite rbu }
run_test_suite no_optimization
run_test_suite memsubsys1
run_test_suite memsubsys2
run_test_suite singlethread
run_test_suite multithread
run_test_suite onefile
run_test_suite utf16
run_test_suite exclusive
run_test_suite persistent_journal
run_test_suite persistent_journal_error
run_test_suite no_journal
run_test_suite no_journal_error
run_test_suite autovacuum_ioerr
run_test_suite no_mutex_try
run_test_suite fullmutex
run_test_suite journaltest
run_test_suite inmemory_journal
run_test_suite pcache0
run_test_suite pcache10
run_test_suite pcache50
run_test_suite pcache90
run_test_suite pcache100
run_test_suite prepare
run_test_suite mmap
if {$::tcl_platform(platform)=="unix"} {
ifcapable !default_autovacuum {
run_test_suite autovacuum_crash
}
}
finish_test

938
testdata/tcl/alter.test vendored Normal file
View file

@ -0,0 +1,938 @@
# 2004 November 10
#
# 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 script is testing the ALTER TABLE statement.
#
# $Id: alter.test,v 1.32 2009/03/24 15:08:10 drh Exp $
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
ifcapable !altertable {
finish_test
return
}
#----------------------------------------------------------------------
# Test organization:
#
# alter-1.1.* - alter-1.7.*: Basic tests of ALTER TABLE, including tables
# with implicit and explicit indices. These tests came from an earlier
# fork of SQLite that also supported ALTER TABLE.
# alter-1.8.*: Tests for ALTER TABLE when the table resides in an
# attached database.
# alter-1.9.*: Tests for ALTER TABLE when their is whitespace between the
# table name and left parenthesis token. i.e:
# "CREATE TABLE abc (a, b, c);"
# alter-2.*: Test error conditions and messages.
# alter-3.*: Test ALTER TABLE on tables that have TRIGGERs attached to them.
# alter-4.*: Test ALTER TABLE on tables that have AUTOINCREMENT fields.
# ...
# alter-12.*: Test ALTER TABLE on views.
#
# Create some tables to rename. Be sure to include some TEMP tables
# and some tables with odd names.
#
do_test alter-1.1 {
ifcapable tempdb {
set ::temp TEMP
} else {
set ::temp {}
}
execsql [subst -nocommands {
CREATE TABLE t1(a,b);
INSERT INTO t1 VALUES(1,2);
CREATE TABLE [t1'x1](c UNIQUE, b PRIMARY KEY);
INSERT INTO [t1'x1] VALUES(3,4);
CREATE INDEX t1i1 ON T1(B);
CREATE INDEX t1i2 ON t1(a,b);
CREATE INDEX i3 ON [t1'x1](b,c);
CREATE $::temp TABLE "temp table"(e,f,g UNIQUE);
CREATE INDEX i2 ON [temp table](f);
INSERT INTO [temp table] VALUES(5,6,7);
}]
execsql {
SELECT 't1', * FROM t1;
SELECT 't1''x1', * FROM "t1'x1";
SELECT * FROM [temp table];
}
} {t1 1 2 t1'x1 3 4 5 6 7}
do_test alter-1.2 {
execsql [subst {
CREATE $::temp TABLE objlist(type, name, tbl_name);
INSERT INTO objlist SELECT type, name, tbl_name
FROM sqlite_master WHERE NAME!='objlist';
}]
ifcapable tempdb {
execsql {
INSERT INTO objlist SELECT type, name, tbl_name
FROM temp.sqlite_master WHERE NAME!='objlist';
}
}
execsql {
SELECT type, name, tbl_name FROM objlist ORDER BY tbl_name, type desc, name;
}
} [list \
table t1 t1 \
index t1i1 t1 \
index t1i2 t1 \
table t1'x1 t1'x1 \
index i3 t1'x1 \
index {sqlite_autoindex_t1'x1_1} t1'x1 \
index {sqlite_autoindex_t1'x1_2} t1'x1 \
table {temp table} {temp table} \
index i2 {temp table} \
index {sqlite_autoindex_temp table_1} {temp table} \
]
# Make some changes
#
integrity_check alter-1.3.0
do_test alter-1.3 {
execsql {
ALTER TABLE [T1] RENAME to [-t1-];
ALTER TABLE "t1'x1" RENAME TO T2;
ALTER TABLE [temp table] RENAME to TempTab;
}
} {}
integrity_check alter-1.3.1
do_test alter-1.4 {
execsql {
SELECT 't1', * FROM [-t1-];
SELECT 't2', * FROM t2;
SELECT * FROM temptab;
}
} {t1 1 2 t2 3 4 5 6 7}
do_test alter-1.5 {
execsql {
DELETE FROM objlist;
INSERT INTO objlist SELECT type, name, tbl_name
FROM sqlite_master WHERE NAME!='objlist';
}
catchsql {
INSERT INTO objlist SELECT type, name, tbl_name
FROM sqlite_temp_master WHERE NAME!='objlist';
}
execsql {
SELECT type, name, tbl_name FROM objlist ORDER BY tbl_name, type desc, name;
}
} [list \
table -t1- -t1- \
index t1i1 -t1- \
index t1i2 -t1- \
table T2 T2 \
index i3 T2 \
index {sqlite_autoindex_T2_1} T2 \
index {sqlite_autoindex_T2_2} T2 \
table {TempTab} {TempTab} \
index i2 {TempTab} \
index {sqlite_autoindex_TempTab_1} {TempTab} \
]
# Make sure the changes persist after restarting the database.
# (The TEMP table will not persist, of course.)
#
ifcapable tempdb {
do_test alter-1.6 {
db close
sqlite3 db test.db
set DB [sqlite3_connection_pointer db]
execsql {
CREATE TEMP TABLE objlist(type, name, tbl_name);
INSERT INTO objlist SELECT type, name, tbl_name FROM sqlite_master;
INSERT INTO objlist
SELECT type, name, tbl_name FROM temp.sqlite_master
WHERE NAME!='objlist';
SELECT type, name, tbl_name FROM objlist
ORDER BY tbl_name, type desc, name;
}
} [list \
table -t1- -t1- \
index t1i1 -t1- \
index t1i2 -t1- \
table T2 T2 \
index i3 T2 \
index {sqlite_autoindex_T2_1} T2 \
index {sqlite_autoindex_T2_2} T2 \
]
} else {
execsql {
DROP TABLE TempTab;
}
}
# Create bogus application-defined functions for functions used
# internally by ALTER TABLE, to ensure that ALTER TABLE falls back
# to the built-in functions.
#
proc failing_app_func {args} {error "bad function"}
do_test alter-1.7-prep {
db func substr failing_app_func
db func like failing_app_func
db func sqlite_rename_table failing_app_func
db func sqlite_rename_trigger failing_app_func
db func sqlite_rename_parent failing_app_func
catchsql {SELECT substr(name,1,3) FROM sqlite_master}
} {1 {bad function}}
# Make sure the ALTER TABLE statements work with the
# non-callback API
#
do_test alter-1.7 {
stepsql $DB {
ALTER TABLE [-t1-] RENAME to [*t1*];
ALTER TABLE T2 RENAME TO [<t2>];
}
execsql {
DELETE FROM objlist;
INSERT INTO objlist SELECT type, name, tbl_name
FROM sqlite_master WHERE NAME!='objlist';
}
catchsql {
INSERT INTO objlist SELECT type, name, tbl_name
FROM sqlite_temp_master WHERE NAME!='objlist';
}
execsql {
SELECT type, name, tbl_name FROM objlist ORDER BY tbl_name, type desc, name;
}
} [list \
table *t1* *t1* \
index t1i1 *t1* \
index t1i2 *t1* \
table <t2> <t2> \
index i3 <t2> \
index {sqlite_autoindex_<t2>_1} <t2> \
index {sqlite_autoindex_<t2>_2} <t2> \
]
# Check that ALTER TABLE works on attached databases.
#
ifcapable attach {
do_test alter-1.8.1 {
forcedelete test2.db
forcedelete test2.db-journal
execsql {
ATTACH 'test2.db' AS aux;
}
} {}
do_test alter-1.8.2 {
execsql {
CREATE TABLE t4(a PRIMARY KEY, b, c);
CREATE TABLE aux.t4(a PRIMARY KEY, b, c);
CREATE INDEX i4 ON t4(b);
CREATE INDEX aux.i4 ON t4(b);
}
} {}
do_test alter-1.8.3 {
execsql {
INSERT INTO t4 VALUES('main', 'main', 'main');
INSERT INTO aux.t4 VALUES('aux', 'aux', 'aux');
SELECT * FROM t4 WHERE a = 'main';
}
} {main main main}
do_test alter-1.8.4 {
execsql {
ALTER TABLE t4 RENAME TO t5;
SELECT * FROM t4 WHERE a = 'aux';
}
} {aux aux aux}
do_test alter-1.8.5 {
execsql {
SELECT * FROM t5;
}
} {main main main}
do_test alter-1.8.6 {
execsql {
SELECT * FROM t5 WHERE b = 'main';
}
} {main main main}
do_test alter-1.8.7 {
execsql {
ALTER TABLE aux.t4 RENAME TO t5;
SELECT * FROM aux.t5 WHERE b = 'aux';
}
} {aux aux aux}
}
do_test alter-1.9.1 {
execsql {
CREATE TABLE tbl1 (a, b, c);
INSERT INTO tbl1 VALUES(1, 2, 3);
}
} {}
do_test alter-1.9.2 {
execsql {
SELECT * FROM tbl1;
}
} {1 2 3}
do_test alter-1.9.3 {
execsql {
ALTER TABLE tbl1 RENAME TO tbl2;
SELECT * FROM tbl2;
}
} {1 2 3}
do_test alter-1.9.4 {
execsql {
DROP TABLE tbl2;
}
} {}
# Test error messages
#
do_test alter-2.1 {
catchsql {
ALTER TABLE none RENAME TO hi;
}
} {1 {no such table: none}}
do_test alter-2.2 {
execsql {
CREATE TABLE t3(p,q,r);
}
catchsql {
ALTER TABLE [<t2>] RENAME TO t3;
}
} {1 {there is already another table or index with this name: t3}}
do_test alter-2.3 {
catchsql {
ALTER TABLE [<t2>] RENAME TO i3;
}
} {1 {there is already another table or index with this name: i3}}
do_test alter-2.4 {
catchsql {
ALTER TABLE SqLiTe_master RENAME TO master;
}
} {1 {table sqlite_master may not be altered}}
do_test alter-2.5 {
catchsql {
ALTER TABLE t3 RENAME TO sqlite_t3;
}
} {1 {object name reserved for internal use: sqlite_t3}}
do_test alter-2.6 {
catchsql {
ALTER TABLE t3 ADD COLUMN (ALTER TABLE t3 ADD COLUMN);
}
} {1 {near "(": syntax error}}
# If this compilation does not include triggers, omit the alter-3.* tests.
ifcapable trigger {
#-----------------------------------------------------------------------
# Tests alter-3.* test ALTER TABLE on tables that have triggers.
#
# alter-3.1.*: ALTER TABLE with triggers.
# alter-3.2.*: Test that the ON keyword cannot be used as a database,
# table or column name unquoted. This is done because part of the
# ALTER TABLE code (specifically the implementation of SQL function
# "sqlite_alter_trigger") will break in this case.
# alter-3.3.*: ALTER TABLE with TEMP triggers (todo).
#
# An SQL user-function for triggers to fire, so that we know they
# are working.
proc trigfunc {args} {
set ::TRIGGER $args
}
db func trigfunc trigfunc
do_test alter-3.1.0 {
execsql {
CREATE TABLE t6(a, b, c);
-- Different case for the table name in the trigger.
CREATE TRIGGER trig1 AFTER INSERT ON T6 BEGIN
SELECT trigfunc('trig1', new.a, new.b, new.c);
END;
}
} {}
do_test alter-3.1.1 {
execsql {
INSERT INTO t6 VALUES(1, 2, 3);
}
set ::TRIGGER
} {trig1 1 2 3}
do_test alter-3.1.2 {
execsql {
ALTER TABLE t6 RENAME TO t7;
INSERT INTO t7 VALUES(4, 5, 6);
}
set ::TRIGGER
} {trig1 4 5 6}
do_test alter-3.1.3 {
execsql {
DROP TRIGGER trig1;
}
} {}
do_test alter-3.1.4 {
execsql {
CREATE TRIGGER trig2 AFTER INSERT ON main.t7 BEGIN
SELECT trigfunc('trig2', new.a, new.b, new.c);
END;
INSERT INTO t7 VALUES(1, 2, 3);
}
set ::TRIGGER
} {trig2 1 2 3}
do_test alter-3.1.5 {
execsql {
ALTER TABLE t7 RENAME TO t8;
INSERT INTO t8 VALUES(4, 5, 6);
}
set ::TRIGGER
} {trig2 4 5 6}
do_test alter-3.1.6 {
execsql {
DROP TRIGGER trig2;
}
} {}
do_test alter-3.1.7 {
execsql {
CREATE TRIGGER trig3 AFTER INSERT ON main.'t8'BEGIN
SELECT trigfunc('trig3', new.a, new.b, new.c);
END;
INSERT INTO t8 VALUES(1, 2, 3);
}
set ::TRIGGER
} {trig3 1 2 3}
do_test alter-3.1.8 {
execsql {
ALTER TABLE t8 RENAME TO t9;
INSERT INTO t9 VALUES(4, 5, 6);
}
set ::TRIGGER
} {trig3 4 5 6}
# Make sure "ON" cannot be used as a database, table or column name without
# quoting. Otherwise the sqlite_alter_trigger() function might not work.
forcedelete test3.db
forcedelete test3.db-journal
ifcapable attach {
do_test alter-3.2.1 {
catchsql {
ATTACH 'test3.db' AS ON;
}
} {1 {near "ON": syntax error}}
do_test alter-3.2.2 {
catchsql {
ATTACH 'test3.db' AS 'ON';
}
} {0 {}}
do_test alter-3.2.3 {
catchsql {
CREATE TABLE ON.t1(a, b, c);
}
} {1 {near "ON": syntax error}}
do_test alter-3.2.4 {
catchsql {
CREATE TABLE 'ON'.t1(a, b, c);
}
} {0 {}}
do_test alter-3.2.4 {
catchsql {
CREATE TABLE 'ON'.ON(a, b, c);
}
} {1 {near "ON": syntax error}}
do_test alter-3.2.5 {
catchsql {
CREATE TABLE 'ON'.'ON'(a, b, c);
}
} {0 {}}
}
do_test alter-3.2.6 {
catchsql {
CREATE TABLE t10(a, ON, c);
}
} {1 {near "ON": syntax error}}
do_test alter-3.2.7 {
catchsql {
CREATE TABLE t10(a, 'ON', c);
}
} {0 {}}
do_test alter-3.2.8 {
catchsql {
CREATE TRIGGER trig4 AFTER INSERT ON ON BEGIN SELECT 1; END;
}
} {1 {near "ON": syntax error}}
ifcapable attach {
do_test alter-3.2.9 {
catchsql {
CREATE TRIGGER 'on'.trig4 AFTER INSERT ON 'ON' BEGIN SELECT 1; END;
}
} {0 {}}
}
do_test alter-3.2.10 {
execsql {
DROP TABLE t10;
}
} {}
do_test alter-3.3.1 {
execsql [subst {
CREATE TABLE tbl1(a, b, c);
CREATE $::temp TRIGGER trig1 AFTER INSERT ON tbl1 BEGIN
SELECT trigfunc('trig1', new.a, new.b, new.c);
END;
}]
} {}
do_test alter-3.3.2 {
execsql {
INSERT INTO tbl1 VALUES('a', 'b', 'c');
}
set ::TRIGGER
} {trig1 a b c}
do_test alter-3.3.3 {
execsql {
ALTER TABLE tbl1 RENAME TO tbl2;
INSERT INTO tbl2 VALUES('d', 'e', 'f');
}
set ::TRIGGER
} {trig1 d e f}
do_test alter-3.3.4 {
execsql [subst {
CREATE $::temp TRIGGER trig2 AFTER UPDATE ON tbl2 BEGIN
SELECT trigfunc('trig2', new.a, new.b, new.c);
END;
}]
} {}
do_test alter-3.3.5 {
execsql {
ALTER TABLE tbl2 RENAME TO tbl3;
INSERT INTO tbl3 VALUES('g', 'h', 'i');
}
set ::TRIGGER
} {trig1 g h i}
do_test alter-3.3.6 {
execsql {
UPDATE tbl3 SET a = 'G' where a = 'g';
}
set ::TRIGGER
} {trig2 G h i}
do_test alter-3.3.7 {
execsql {
DROP TABLE tbl3;
}
} {}
ifcapable tempdb {
do_test alter-3.3.8 {
execsql {
SELECT * FROM temp.sqlite_master WHERE type = 'trigger';
}
} {}
}
} ;# ifcapable trigger
# If the build does not include AUTOINCREMENT fields, omit alter-4.*.
ifcapable autoinc {
do_test alter-4.1 {
execsql {
CREATE TABLE tbl1(a INTEGER PRIMARY KEY AUTOINCREMENT);
INSERT INTO tbl1 VALUES(10);
}
} {}
do_test alter-4.2 {
execsql {
INSERT INTO tbl1 VALUES(NULL);
SELECT a FROM tbl1;
}
} {10 11}
do_test alter-4.3 {
execsql {
ALTER TABLE tbl1 RENAME TO tbl2;
DELETE FROM tbl2;
INSERT INTO tbl2 VALUES(NULL);
SELECT a FROM tbl2;
}
} {12}
do_test alter-4.4 {
execsql {
DROP TABLE tbl2;
}
} {}
} ;# ifcapable autoinc
# Test that it is Ok to execute an ALTER TABLE immediately after
# opening a database.
do_test alter-5.1 {
execsql {
CREATE TABLE tbl1(a, b, c);
INSERT INTO tbl1 VALUES('x', 'y', 'z');
}
} {}
do_test alter-5.2 {
sqlite3 db2 test.db
execsql {
ALTER TABLE tbl1 RENAME TO tbl2;
SELECT * FROM tbl2;
} db2
} {x y z}
do_test alter-5.3 {
db2 close
} {}
foreach tblname [execsql {
SELECT name FROM sqlite_master
WHERE type='table' AND name NOT GLOB 'sqlite*'
}] {
execsql "DROP TABLE \"$tblname\""
}
set ::tbl_name "abc\uABCDdef"
do_test alter-6.1 {
string length $::tbl_name
} {7}
do_test alter-6.2 {
execsql "
CREATE TABLE ${tbl_name}(a, b, c);
"
set ::oid [execsql {SELECT max(oid) FROM sqlite_master}]
execsql "
SELECT sql FROM sqlite_master WHERE oid = $::oid;
"
} "{CREATE TABLE ${::tbl_name}(a, b, c)}"
execsql "
SELECT * FROM ${::tbl_name}
"
set ::tbl_name2 "abcXdef"
do_test alter-6.3 {
execsql "
ALTER TABLE $::tbl_name RENAME TO $::tbl_name2
"
execsql "
SELECT sql FROM sqlite_master WHERE oid = $::oid
"
} "{CREATE TABLE \"${::tbl_name2}\"(a, b, c)}"
do_test alter-6.4 {
execsql "
ALTER TABLE $::tbl_name2 RENAME TO $::tbl_name
"
execsql "
SELECT sql FROM sqlite_master WHERE oid = $::oid
"
} "{CREATE TABLE \"${::tbl_name}\"(a, b, c)}"
set ::col_name ghi\1234\jkl
do_test alter-6.5 {
execsql "
ALTER TABLE $::tbl_name ADD COLUMN $::col_name VARCHAR
"
execsql "
SELECT sql FROM sqlite_master WHERE oid = $::oid
"
} "{CREATE TABLE \"${::tbl_name}\"(a, b, c, $::col_name VARCHAR)}"
set ::col_name2 B\3421\A
do_test alter-6.6 {
db close
sqlite3 db test.db
execsql "
ALTER TABLE $::tbl_name ADD COLUMN $::col_name2
"
execsql "
SELECT sql FROM sqlite_master WHERE oid = $::oid
"
} "{CREATE TABLE \"${::tbl_name}\"(a, b, c, $::col_name VARCHAR, $::col_name2)}"
do_test alter-6.7 {
execsql "
INSERT INTO ${::tbl_name} VALUES(1, 2, 3, 4, 5);
SELECT $::col_name, $::col_name2 FROM $::tbl_name;
"
} {4 5}
# Ticket #1665: Make sure ALTER TABLE ADD COLUMN works on a table
# that includes a COLLATE clause.
#
do_realnum_test alter-7.1 {
execsql {
CREATE TABLE t1(a TEXT COLLATE BINARY);
ALTER TABLE t1 ADD COLUMN b INTEGER COLLATE NOCASE;
INSERT INTO t1 VALUES(1,'-2');
INSERT INTO t1 VALUES(5.4e-08,'5.4e-08');
SELECT typeof(a), a, typeof(b), b FROM t1;
}
} {text 1 integer -2 text 5.4e-08 real 5.4e-08}
# Make sure that when a column is added by ALTER TABLE ADD COLUMN and has
# a default value that the default value is used by aggregate functions.
#
do_test alter-8.1 {
execsql {
CREATE TABLE t2(a INTEGER);
INSERT INTO t2 VALUES(1);
INSERT INTO t2 VALUES(1);
INSERT INTO t2 VALUES(2);
ALTER TABLE t2 ADD COLUMN b INTEGER DEFAULT 9;
SELECT sum(b) FROM t2;
}
} {27}
do_test alter-8.2 {
execsql {
SELECT a, sum(b) FROM t2 GROUP BY a;
}
} {1 18 2 9}
#--------------------------------------------------------------------------
# alter-9.X - Special test: Make sure the sqlite_rename_column() and
# rename_table() functions do not crash when handed bad input.
#
sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS db
do_test alter-9.1 {
execsql {SELECT SQLITE_RENAME_COLUMN(0,0,0,0,0,0,0,0,0)}
} {{}}
foreach {tn sql} {
1 { SELECT SQLITE_RENAME_TABLE(0,0,0,0,0,0,0) }
2 { SELECT SQLITE_RENAME_TABLE(10,20,30,40,50,60,70) }
3 { SELECT SQLITE_RENAME_TABLE('foo','foo','foo','foo','foo','foo','foo') }
} {
do_test alter-9.2.$tn {
catch { execsql $sql }
} 1
}
sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS db
# If the INTERNAL_FUNCTIONS test-control is disabled (which is the default),
# then the sqlite_rename_table() SQL function is not accessible to ordinary SQL.
#
do_catchsql_test alter-9.3 {
SELECT sqlite_rename_table(0,0,0,0,0,0,0);
} {1 {no such function: sqlite_rename_table}}
#------------------------------------------------------------------------
# alter-10.X - Make sure ALTER TABLE works with multi-byte UTF-8 characters
# in the names.
#
do_test alter-10.1 {
execsql "CREATE TABLE xyz(x UNIQUE)"
execsql "ALTER TABLE xyz RENAME TO xyz\u1234abc"
execsql {SELECT name FROM sqlite_master WHERE name GLOB 'xyz*'}
} [list xyz\u1234abc]
do_test alter-10.2 {
execsql {SELECT name FROM sqlite_master WHERE name GLOB 'sqlite_autoindex*'}
} [list sqlite_autoindex_xyz\u1234abc_1]
do_test alter-10.3 {
execsql "ALTER TABLE xyz\u1234abc RENAME TO xyzabc"
execsql {SELECT name FROM sqlite_master WHERE name GLOB 'xyz*'}
} [list xyzabc]
do_test alter-10.4 {
execsql {SELECT name FROM sqlite_master WHERE name GLOB 'sqlite_autoindex*'}
} [list sqlite_autoindex_xyzabc_1]
do_test alter-11.1 {
sqlite3_exec db {CREATE TABLE t11(%c6%c6)}
execsql {
ALTER TABLE t11 ADD COLUMN abc;
}
catchsql {
ALTER TABLE t11 ADD COLUMN abc;
}
} {1 {duplicate column name: abc}}
set isutf16 [regexp 16 [db one {PRAGMA encoding}]]
if {!$isutf16} {
do_test alter-11.2 {
execsql {INSERT INTO t11 VALUES(1,2)}
sqlite3_exec db {SELECT %c6%c6 AS xyz, abc FROM t11}
} {0 {xyz abc 1 2}}
}
do_test alter-11.3 {
sqlite3_exec db {CREATE TABLE t11b("%81%82%83" text)}
execsql {
ALTER TABLE t11b ADD COLUMN abc;
}
catchsql {
ALTER TABLE t11b ADD COLUMN abc;
}
} {1 {duplicate column name: abc}}
if {!$isutf16} {
do_test alter-11.4 {
execsql {INSERT INTO t11b VALUES(3,4)}
sqlite3_exec db {SELECT %81%82%83 AS xyz, abc FROM t11b}
} {0 {xyz abc 3 4}}
do_test alter-11.5 {
sqlite3_exec db {SELECT [%81%82%83] AS xyz, abc FROM t11b}
} {0 {xyz abc 3 4}}
do_test alter-11.6 {
sqlite3_exec db {SELECT "%81%82%83" AS xyz, abc FROM t11b}
} {0 {xyz abc 3 4}}
}
do_test alter-11.7 {
sqlite3_exec db {CREATE TABLE t11c(%81%82%83 text)}
execsql {
ALTER TABLE t11c ADD COLUMN abc;
}
catchsql {
ALTER TABLE t11c ADD COLUMN abc;
}
} {1 {duplicate column name: abc}}
if {!$isutf16} {
do_test alter-11.8 {
execsql {INSERT INTO t11c VALUES(5,6)}
sqlite3_exec db {SELECT %81%82%83 AS xyz, abc FROM t11c}
} {0 {xyz abc 5 6}}
do_test alter-11.9 {
sqlite3_exec db {SELECT [%81%82%83] AS xyz, abc FROM t11c}
} {0 {xyz abc 5 6}}
do_test alter-11.10 {
sqlite3_exec db {SELECT "%81%82%83" AS xyz, abc FROM t11c}
} {0 {xyz abc 5 6}}
}
do_test alter-12.1 {
execsql {
CREATE TABLE t12(a, b, c);
CREATE VIEW v1 AS SELECT * FROM t12;
}
} {}
do_test alter-12.2 {
catchsql {
ALTER TABLE v1 RENAME TO v2;
}
} {1 {view v1 may not be altered}}
do_test alter-12.3 {
execsql { SELECT * FROM v1; }
} {}
do_test alter-12.4 {
db close
sqlite3 db test.db
execsql { SELECT * FROM v1; }
} {}
do_test alter-12.5 {
catchsql {
ALTER TABLE v1 ADD COLUMN new_column;
}
} {1 {Cannot add a column to a view}}
# Ticket #3102:
# Verify that comments do not interfere with the table rename
# algorithm.
#
do_test alter-13.1 {
execsql {
CREATE TABLE /* hi */ t3102a(x);
CREATE TABLE t3102b -- comment
(y);
CREATE INDEX t3102c ON t3102a(x);
SELECT name FROM sqlite_master WHERE name GLOB 't3102*' ORDER BY 1;
}
} {t3102a t3102b t3102c}
do_test alter-13.2 {
execsql {
ALTER TABLE t3102a RENAME TO t3102a_rename;
SELECT name FROM sqlite_master WHERE name GLOB 't3102*' ORDER BY 1;
}
} {t3102a_rename t3102b t3102c}
do_test alter-13.3 {
execsql {
ALTER TABLE t3102b RENAME TO t3102b_rename;
SELECT name FROM sqlite_master WHERE name GLOB 't3102*' ORDER BY 1;
}
} {t3102a_rename t3102b_rename t3102c}
# Ticket #3651
do_test alter-14.1 {
catchsql {
CREATE TABLE t3651(a UNIQUE);
INSERT INTO t3651 VALUES(5);
ALTER TABLE t3651 ADD COLUMN b UNIQUE;
}
} {1 {Cannot add a UNIQUE column}}
do_test alter-14.2 {
catchsql {
ALTER TABLE t3651 ADD COLUMN b PRIMARY KEY;
}
} {1 {Cannot add a PRIMARY KEY column}}
#-------------------------------------------------------------------------
# Test that it is not possible to use ALTER TABLE on any system table.
#
set system_table_list {1 sqlite_master}
catchsql ANALYZE
ifcapable analyze { lappend system_table_list 2 sqlite_stat1 }
ifcapable stat4 { lappend system_table_list 4 sqlite_stat4 }
foreach {tn tbl} $system_table_list {
do_test alter-15.$tn.1 {
catchsql "ALTER TABLE $tbl RENAME TO xyz"
} [list 1 "table $tbl may not be altered"]
do_test alter-15.$tn.2 {
catchsql "ALTER TABLE $tbl ADD COLUMN xyz"
} [list 1 "table $tbl may not be altered"]
}
#------------------------------------------------------------------------
# Verify that ALTER TABLE works on tables with the WITHOUT rowid option.
#
do_execsql_test alter-16.1 {
CREATE TABLE t16a(a TEXT, b REAL, c INT, PRIMARY KEY(a,b)) WITHOUT rowid;
INSERT INTO t16a VALUES('abc',1.25,99);
ALTER TABLE t16a ADD COLUMN d TEXT DEFAULT 'xyzzy';
INSERT INTO t16a VALUES('cba',5.5,98,'fizzle');
SELECT * FROM t16a ORDER BY a;
} {abc 1.25 99 xyzzy cba 5.5 98 fizzle}
do_execsql_test alter-16.2 {
ALTER TABLE t16a RENAME TO t16a_rn;
SELECT * FROM t16a_rn ORDER BY a;
} {abc 1.25 99 xyzzy cba 5.5 98 fizzle}
# 2018-09-16 ticket b41031ea2b5372378cb3d2d43cf9fe2a4a5c2510
#
ifcapable rtree {
db close
sqlite3 db :memory:
do_execsql_test alter-17.100 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
CREATE VIRTUAL TABLE t2 USING rtree(id,x0,x1);
INSERT INTO t1 VALUES(1,'apple'),(2,'fig'),(3,'pear');
INSERT INTO t2 VALUES(1,1.0,2.0),(2,2.0,3.0),(3,1.5,3.5);
CREATE TRIGGER r1 AFTER UPDATE ON t1 BEGIN
DELETE FROM t2 WHERE id = OLD.a;
END;
ALTER TABLE t1 RENAME TO t3;
UPDATE t3 SET b='peach' WHERE a=2;
SELECT * FROM t2 ORDER BY 1;
} {1 1.0 2.0 3 1.5 3.5}
}
# 2021-03-08 dbsqlfuzz 3f0a7245b69cd08617d7d7781ebaedb0fe765a93
reset_db
do_catchsql_test alter-18.1 {
CREATE TABLE t1(a,b,c);
CREATE TABLE log(a INTEGER PRIMARY KEY,b,c);
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
INSERT INTO logx(a,b,c) VALUES(new.a,new.b,new.c)
ON CONFLICT(a) DO UPDATE SET c=excluded.c, b=new.b;
END;
ALTER TABLE log RENAME COLUMN a TO x;
} {1 {error in trigger tr1: no such table: main.logx}}
# 2021-10-13 dbsqlfuzz e89174cbfad2d904f06b5e24df0a22510b6a1c1e
reset_db
do_execsql_test alter-19.1 {
CREATE TABLE t1(x);
CREATE TABLE t2(c);
CREATE TRIGGER r1 AFTER INSERT ON t2 BEGIN
UPDATE t2 SET (c)=(
EXISTS(SELECT 1 WHERE (WITH cte1(a) AS (SELECT 1 FROM t1 WHERE (SELECT 1 WHERE (WITH cte2(b) AS (VALUES(1))SELECT b FROM cte2)))SELECT a FROM cte1))
);
END;
ALTER TABLE t2 RENAME TO t3;
} {}
do_execsql_test alter-19.2 {
SELECT name FROM sqlite_schema WHERE sql LIKE '%t2%';
} {}
do_execsql_test alter-19.3 {
SELECT name FROM sqlite_schema WHERE sql LIKE '%t3%' ORDER BY name;
} {r1 t3}
finish_test

470
testdata/tcl/alter2.test vendored Normal file
View file

@ -0,0 +1,470 @@
# 2005 February 18
#
# 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 script is testing that SQLite can handle a subtle
# file format change that may be used in the future to implement
# "ALTER TABLE ... ADD COLUMN".
#
# $Id: alter2.test,v 1.14 2009/04/07 14:14:22 danielk1977 Exp $
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# We have to have pragmas in order to do this test
ifcapable {!pragma} return
# Do not use a codec for tests in this file, as the database file is
# manipulated directly using tcl scripts. See proc [set_file_format].
#
do_not_use_codec
# The file format change affects the way row-records stored in tables (but
# not indices) are interpreted. Before version 3.1.3, a row-record for a
# table with N columns was guaranteed to contain exactly N fields. As
# of version 3.1.3, the record may contain up to N fields. In this case
# the M fields that are present are the values for the left-most M
# columns. The (N-M) rightmost columns contain NULL.
#
# If any records in the database contain less fields than their table
# has columns, then the file-format meta value should be set to (at least) 2.
#
# This procedure sets the value of the file-format in file 'test.db'
# to $newval. Also, the schema cookie is incremented.
#
proc set_file_format {newval} {
hexio_write test.db 44 [hexio_render_int32 $newval]
set schemacookie [hexio_get_int [hexio_read test.db 40 4]]
incr schemacookie
hexio_write test.db 40 [hexio_render_int32 $schemacookie]
return {}
}
# This procedure returns the value of the file-format in file 'test.db'.
#
proc get_file_format {{fname test.db}} {
return [hexio_get_int [hexio_read $fname 44 4]]
}
# This procedure sets the SQL statement stored for table $tbl in the
# sqlite_master table of file 'test.db' to $sql. Also set the file format
# to the supplied value. This is 2 if the added column has a default that is
# NULL, or 3 otherwise.
#
proc alter_table {tbl sql {file_format 2}} {
sqlite3 dbat test.db
set s [string map {' ''} $sql]
set t [string map {' ''} $tbl]
sqlite3_db_config dbat DEFENSIVE 0
dbat eval [subst {
PRAGMA writable_schema = 1;
UPDATE sqlite_master SET sql = '$s' WHERE name = '$t' AND type = 'table';
PRAGMA writable_schema = 0;
}]
dbat close
set_file_format 2
}
# Create bogus application-defined functions for functions used
# internally by ALTER TABLE, to ensure that ALTER TABLE falls back
# to the built-in functions.
#
proc failing_app_func {args} {error "bad function"}
do_test alter2-1.0 {
db func substr failing_app_func
db func like failing_app_func
db func sqlite_rename_table failing_app_func
db func sqlite_rename_trigger failing_app_func
db func sqlite_rename_parent failing_app_func
catchsql {SELECT substr('abcdefg',1,3)}
} {1 {bad function}}
#-----------------------------------------------------------------------
# Some basic tests to make sure short rows are handled.
#
sqlite3_db_config db DEFENSIVE 0
do_test alter2-1.1 {
execsql {
CREATE TABLE abc(a, b);
INSERT INTO abc VALUES(1, 2);
INSERT INTO abc VALUES(3, 4);
INSERT INTO abc VALUES(5, 6);
}
} {}
do_test alter2-1.2 {
# ALTER TABLE abc ADD COLUMN c;
alter_table abc {CREATE TABLE abc(a, b, c);}
} {}
do_test alter2-1.3 {
execsql {
SELECT * FROM abc;
}
} {1 2 {} 3 4 {} 5 6 {}}
do_test alter2-1.4 {
execsql {
UPDATE abc SET c = 10 WHERE a = 1;
SELECT * FROM abc;
}
} {1 2 10 3 4 {} 5 6 {}}
do_test alter2-1.5 {
execsql {
CREATE INDEX abc_i ON abc(c);
}
} {}
do_test alter2-1.6 {
execsql {
SELECT c FROM abc ORDER BY c;
}
} {{} {} 10}
do_test alter2-1.7 {
execsql {
SELECT * FROM abc WHERE c = 10;
}
} {1 2 10}
do_test alter2-1.8 {
execsql {
SELECT sum(a), c FROM abc GROUP BY c;
}
} {8 {} 1 10}
do_test alter2-1.9 {
# ALTER TABLE abc ADD COLUMN d;
alter_table abc {CREATE TABLE abc(a, b, c, d);}
if {[permutation] == "prepare"} { db cache flush }
execsql { SELECT * FROM abc; }
execsql {
UPDATE abc SET d = 11 WHERE c IS NULL AND a<4;
SELECT * FROM abc;
}
} {1 2 10 {} 3 4 {} 11 5 6 {} {}}
do_test alter2-1.10 {
execsql {
SELECT typeof(d) FROM abc;
}
} {null integer null}
do_test alter2-1.99 {
execsql {
DROP TABLE abc;
}
} {}
#-----------------------------------------------------------------------
# Test that views work when the underlying table structure is changed.
#
ifcapable view {
do_test alter2-2.1 {
execsql {
CREATE TABLE abc2(a, b, c);
INSERT INTO abc2 VALUES(1, 2, 10);
INSERT INTO abc2 VALUES(3, 4, NULL);
INSERT INTO abc2 VALUES(5, 6, NULL);
CREATE VIEW abc2_v AS SELECT * FROM abc2;
SELECT * FROM abc2_v;
}
} {1 2 10 3 4 {} 5 6 {}}
do_test alter2-2.2 {
# ALTER TABLE abc ADD COLUMN d;
alter_table abc2 {CREATE TABLE abc2(a, b, c, d);}
execsql {
SELECT * FROM abc2_v;
}
} {1 2 10 {} 3 4 {} {} 5 6 {} {}}
do_test alter2-2.3 {
execsql {
DROP TABLE abc2;
DROP VIEW abc2_v;
}
} {}
}
#-----------------------------------------------------------------------
# Test that triggers work when a short row is copied to the old.*
# trigger pseudo-table.
#
ifcapable trigger {
do_test alter2-3.1 {
execsql {
CREATE TABLE abc3(a, b);
CREATE TABLE blog(o, n);
CREATE TRIGGER abc3_t AFTER UPDATE OF b ON abc3 BEGIN
INSERT INTO blog VALUES(old.b, new.b);
END;
}
} {}
do_test alter2-3.2 {
execsql {
INSERT INTO abc3 VALUES(1, 4);
UPDATE abc3 SET b = 2 WHERE b = 4;
SELECT * FROM blog;
}
} {4 2}
do_test alter2-3.3 {
execsql {
INSERT INTO abc3 VALUES(3, 4);
INSERT INTO abc3 VALUES(5, 6);
}
alter_table abc3 {CREATE TABLE abc3(a, b, c);}
execsql {
SELECT * FROM abc3;
}
} {1 2 {} 3 4 {} 5 6 {}}
do_test alter2-3.4 {
execsql {
UPDATE abc3 SET b = b*2 WHERE a<4;
SELECT * FROM abc3;
}
} {1 4 {} 3 8 {} 5 6 {}}
do_test alter2-3.5 {
execsql {
SELECT * FROM blog;
}
} {4 2 2 4 4 8}
do_test alter2-3.6 {
execsql {
CREATE TABLE clog(o, n);
CREATE TRIGGER abc3_t2 AFTER UPDATE OF c ON abc3 BEGIN
INSERT INTO clog VALUES(old.c, new.c);
END;
UPDATE abc3 SET c = a*2;
SELECT * FROM clog;
}
} {{} 2 {} 6 {} 10}
} else {
execsql { CREATE TABLE abc3(a, b); }
}
#---------------------------------------------------------------------
# Check that an error occurs if the database is upgraded to a file
# format that SQLite does not support (in this case 5). Note: The
# file format is checked each time the schema is read, so changing the
# file format requires incrementing the schema cookie.
#
do_test alter2-4.1 {
db close
set_file_format 5
catch { sqlite3 db test.db }
set {} {}
} {}
do_test alter2-4.2 {
# We have to run two queries here because the Tcl interface uses
# sqlite3_prepare_v2(). In this case, the first query encounters an
# SQLITE_SCHEMA error. Then, when trying to recompile the statement, the
# "unsupported file format" error is encountered. So the error code
# returned is SQLITE_SCHEMA, not SQLITE_ERROR as required by the following
# test case.
#
# When the query is attempted a second time, the same error message is
# returned but the error code is SQLITE_ERROR, because the unsupported
# file format was detected during a call to sqlite3_prepare(), not
# sqlite3_step().
#
catchsql { SELECT * FROM sqlite_master; }
catchsql { SELECT * FROM sqlite_master; }
} {1 {unsupported file format}}
do_test alter2-4.3 {
sqlite3_errcode db
} {SQLITE_ERROR}
do_test alter2-4.4 {
set ::DB [sqlite3_connection_pointer db]
catchsql {
SELECT * FROM sqlite_master;
}
} {1 {unsupported file format}}
do_test alter2-4.5 {
sqlite3_errcode db
} {SQLITE_ERROR}
#---------------------------------------------------------------------
# Check that executing VACUUM on a file with file-format version 2
# resets the file format to 1.
#
set default_file_format [expr $SQLITE_DEFAULT_FILE_FORMAT==4 ? 4 : 1]
ifcapable vacuum {
do_test alter2-5.1 {
set_file_format 2
db close
sqlite3 db test.db
execsql {SELECT 1 FROM sqlite_master LIMIT 1;}
get_file_format
} {2}
do_test alter2-5.2 {
execsql { VACUUM }
} {}
do_test alter2-5.3 {
get_file_format
} $default_file_format
}
#---------------------------------------------------------------------
# Test that when a database with file-format 2 is opened, new
# databases are still created with file-format 1.
#
do_test alter2-6.1 {
db close
set_file_format 2
sqlite3 db test.db
get_file_format
} {2}
ifcapable attach {
do_test alter2-6.2 {
forcedelete test2.db-journal
forcedelete test2.db
execsql {
ATTACH 'test2.db' AS aux;
CREATE TABLE aux.t1(a, b);
}
get_file_format test2.db
} $default_file_format
}
do_test alter2-6.3 {
execsql {
CREATE TABLE t1(a, b);
}
get_file_format
} {2}
#---------------------------------------------------------------------
# Test that types and values for columns added with default values
# other than NULL work with SELECT statements.
#
do_test alter2-7.1 {
execsql {
DROP TABLE t1;
CREATE TABLE t1(a);
INSERT INTO t1 VALUES(1);
INSERT INTO t1 VALUES(2);
INSERT INTO t1 VALUES(3);
INSERT INTO t1 VALUES(4);
SELECT * FROM t1;
}
} {1 2 3 4}
do_test alter2-7.2 {
set sql {CREATE TABLE t1(a, b DEFAULT '123', c INTEGER DEFAULT '123')}
alter_table t1 $sql 3
execsql {
SELECT * FROM t1 LIMIT 1;
}
} {1 123 123}
do_test alter2-7.3 {
execsql {
SELECT a, typeof(a), b, typeof(b), c, typeof(c) FROM t1 LIMIT 1;
}
} {1 integer 123 text 123 integer}
do_test alter2-7.4 {
execsql {
SELECT a, typeof(a), b, typeof(b), c, typeof(c) FROM t1 LIMIT 1;
}
} {1 integer 123 text 123 integer}
do_test alter2-7.5 {
set sql {CREATE TABLE t1(a, b DEFAULT -123.0, c VARCHAR(10) default 5)}
alter_table t1 $sql 3
execsql {
SELECT a, typeof(a), b, typeof(b), c, typeof(c) FROM t1 LIMIT 1;
}
} {1 integer -123 integer 5 text}
#-----------------------------------------------------------------------
# Test that UPDATE trigger tables work with default values, and that when
# a row is updated the default values are correctly transfered to the
# new row.
#
ifcapable trigger {
db function set_val {set ::val}
do_test alter2-8.1 {
execsql {
CREATE TRIGGER trig1 BEFORE UPDATE ON t1 BEGIN
SELECT set_val(
old.b||' '||typeof(old.b)||' '||old.c||' '||typeof(old.c)||' '||
new.b||' '||typeof(new.b)||' '||new.c||' '||typeof(new.c)
);
END;
}
list
} {}
}
do_test alter2-8.2 {
execsql {
UPDATE t1 SET c = 10 WHERE a = 1;
SELECT a, typeof(a), b, typeof(b), c, typeof(c) FROM t1 LIMIT 1;
}
} {1 integer -123 integer 10 text}
ifcapable trigger {
do_test alter2-8.3 {
set ::val
} {-123 integer 5 text -123 integer 10 text}
}
#-----------------------------------------------------------------------
# Test that DELETE trigger tables work with default values, and that when
# a row is updated the default values are correctly transfered to the
# new row.
#
ifcapable trigger {
do_test alter2-9.1 {
execsql {
CREATE TRIGGER trig2 BEFORE DELETE ON t1 BEGIN
SELECT set_val(
old.b||' '||typeof(old.b)||' '||old.c||' '||typeof(old.c)
);
END;
}
list
} {}
do_test alter2-9.2 {
execsql {
DELETE FROM t1 WHERE a = 2;
}
set ::val
} {-123 integer 5 text}
}
#-----------------------------------------------------------------------
# Test creating an index on a column added with a default value.
#
ifcapable bloblit {
do_test alter2-10.1 {
execsql {
CREATE TABLE t2(a);
INSERT INTO t2 VALUES('a');
INSERT INTO t2 VALUES('b');
INSERT INTO t2 VALUES('c');
INSERT INTO t2 VALUES('d');
}
alter_table t2 {CREATE TABLE t2(a, b DEFAULT X'ABCD', c DEFAULT NULL);} 3
catchsql {
SELECT * FROM sqlite_master;
}
execsql {
SELECT quote(a), quote(b), quote(c) FROM t2 LIMIT 1;
}
} {'a' X'ABCD' NULL}
do_test alter2-10.2 {
execsql {
CREATE INDEX i1 ON t2(b);
SELECT a FROM t2 WHERE b = X'ABCD';
}
} {a b c d}
do_test alter2-10.3 {
execsql {
DELETE FROM t2 WHERE a = 'c';
SELECT a FROM t2 WHERE b = X'ABCD';
}
} {a b d}
do_test alter2-10.4 {
execsql {
SELECT count(b) FROM t2 WHERE b = X'ABCD';
}
} {3}
}
finish_test

443
testdata/tcl/alter3.test vendored Normal file
View file

@ -0,0 +1,443 @@
# 2005 February 19
#
# 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 script is testing that SQLite can handle a subtle
# file format change that may be used in the future to implement
# "ALTER TABLE ... ADD COLUMN".
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
ifcapable !altertable {
finish_test
return
}
# Determine if there is a codec available on this test.
#
if {[catch {sqlite3 -has-codec} r] || $r} {
set has_codec 1
} else {
set has_codec 0
}
# Test Organisation:
# ------------------
#
# alter3-1.*: Test that ALTER TABLE correctly modifies the CREATE TABLE sql.
# alter3-2.*: Test error messages.
# alter3-3.*: Test adding columns with default value NULL.
# alter3-4.*: Test adding columns with default values other than NULL.
# alter3-5.*: Test adding columns to tables in ATTACHed databases.
# alter3-6.*: Test that temp triggers are not accidentally dropped.
# alter3-7.*: Test that VACUUM resets the file-format.
#
# This procedure returns the value of the file-format in file 'test.db'.
#
proc get_file_format {{fname test.db}} {
return [hexio_get_int [hexio_read $fname 44 4]]
}
do_test alter3-1.1 {
sqlite3_db_config db LEGACY_FILE_FORMAT 1
execsql {
CREATE TABLE abc(a, b, c);
SELECT sql FROM sqlite_master;
}
} {{CREATE TABLE abc(a, b, c)}}
do_test alter3-1.2 {
execsql {ALTER TABLE abc ADD d INTEGER;}
execsql {
SELECT sql FROM sqlite_master;
}
} {{CREATE TABLE abc(a, b, c, d INTEGER)}}
do_test alter3-1.3 {
execsql {ALTER TABLE abc ADD e}
execsql {
SELECT sql FROM sqlite_master;
}
} {{CREATE TABLE abc(a, b, c, d INTEGER, e)}}
do_test alter3-1.4 {
execsql {
CREATE TABLE main.t1(a, b);
ALTER TABLE t1 ADD c;
SELECT sql FROM sqlite_master WHERE tbl_name = 't1';
}
} {{CREATE TABLE t1(a, b, c)}}
do_test alter3-1.5 {
execsql {
ALTER TABLE t1 ADD d CHECK (a>d);
SELECT sql FROM sqlite_master WHERE tbl_name = 't1';
}
} {{CREATE TABLE t1(a, b, c, d CHECK (a>d))}}
ifcapable foreignkey {
do_test alter3-1.6 {
execsql {
CREATE TABLE t2(a, b, UNIQUE(a, b));
ALTER TABLE t2 ADD c REFERENCES t1(c) ;
SELECT sql FROM sqlite_master WHERE tbl_name = 't2' AND type = 'table';
}
} {{CREATE TABLE t2(a, b, c REFERENCES t1(c), UNIQUE(a, b))}}
}
do_test alter3-1.7 {
execsql {
CREATE TABLE t3(a, b, UNIQUE(a, b));
ALTER TABLE t3 ADD COLUMN c VARCHAR(10, 20);
SELECT sql FROM sqlite_master WHERE tbl_name = 't3' AND type = 'table';
}
} {{CREATE TABLE t3(a, b, c VARCHAR(10, 20), UNIQUE(a, b))}}
do_test alter3-1.99 {
catchsql {
# May not exist if foriegn-keys are omitted at compile time.
DROP TABLE t2;
}
execsql {
DROP TABLE abc;
DROP TABLE t1;
DROP TABLE t3;
}
} {}
do_test alter3-2.1 {
execsql {
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1,2);
}
catchsql {
ALTER TABLE t1 ADD c PRIMARY KEY;
}
} {1 {Cannot add a PRIMARY KEY column}}
do_test alter3-2.2 {
catchsql {
ALTER TABLE t1 ADD c UNIQUE
}
} {1 {Cannot add a UNIQUE column}}
do_test alter3-2.3 {
catchsql {
ALTER TABLE t1 ADD b VARCHAR(10)
}
} {1 {duplicate column name: b}}
do_test alter3-2.3 {
catchsql {
ALTER TABLE t1 ADD c NOT NULL;
}
} {1 {Cannot add a NOT NULL column with default value NULL}}
do_test alter3-2.4 {
catchsql {
ALTER TABLE t1 ADD c NOT NULL DEFAULT 10;
}
} {0 {}}
ifcapable view {
do_test alter3-2.5 {
execsql {
CREATE VIEW v1 AS SELECT * FROM t1;
}
catchsql {
alter table v1 add column d;
}
} {1 {Cannot add a column to a view}}
}
do_test alter3-2.6 {
catchsql {
alter table t1 add column d DEFAULT CURRENT_TIME;
}
} {1 {Cannot add a column with non-constant default}}
do_test alter3-2.99 {
execsql {
DROP TABLE t1;
}
} {}
do_test alter3-3.1 {
execsql {
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 100);
INSERT INTO t1 VALUES(2, 300);
SELECT * FROM t1;
}
} {1 100 2 300}
do_test alter3-3.1 {
execsql {
PRAGMA schema_version = 10;
}
} {}
do_test alter3-3.2 {
execsql {
ALTER TABLE t1 ADD c;
SELECT * FROM t1;
}
} {1 100 {} 2 300 {}}
if {!$has_codec} {
do_test alter3-3.3 {
get_file_format
} {3}
}
ifcapable schema_version {
do_test alter3-3.4 {
execsql {
PRAGMA schema_version;
}
} {11}
}
do_test alter3-4.1 {
db close
forcedelete test.db
set ::DB [sqlite3 db test.db]
sqlite3_db_config db LEGACY_FILE_FORMAT 1
execsql {
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 100);
INSERT INTO t1 VALUES(2, 300);
SELECT * FROM t1;
}
} {1 100 2 300}
do_test alter3-4.1 {
execsql {
PRAGMA schema_version = 20;
}
} {}
do_test alter3-4.2 {
execsql {
ALTER TABLE t1 ADD c DEFAULT 'hello world';
SELECT * FROM t1;
}
} {1 100 {hello world} 2 300 {hello world}}
if {!$has_codec} {
do_test alter3-4.3 {
get_file_format
} {3}
}
ifcapable schema_version {
do_test alter3-4.4 {
execsql {
PRAGMA schema_version;
}
} {21}
}
do_test alter3-4.99 {
execsql {
DROP TABLE t1;
}
} {}
ifcapable attach {
do_test alter3-5.1 {
forcedelete test2.db
forcedelete test2.db-journal
execsql {
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 'one');
INSERT INTO t1 VALUES(2, 'two');
ATTACH 'test2.db' AS aux;
CREATE TABLE aux.t1 AS SELECT * FROM t1;
PRAGMA aux.schema_version = 30;
SELECT sql FROM aux.sqlite_master;
}
} {{CREATE TABLE t1(a,b)}}
do_test alter3-5.2 {
execsql {
ALTER TABLE aux.t1 ADD COLUMN c VARCHAR(128);
SELECT sql FROM aux.sqlite_master;
}
} {{CREATE TABLE t1(a,b, c VARCHAR(128))}}
do_test alter3-5.3 {
execsql {
SELECT * FROM aux.t1;
}
} {1 one {} 2 two {}}
ifcapable schema_version {
do_test alter3-5.4 {
execsql {
PRAGMA aux.schema_version;
}
} {31}
}
if {!$has_codec} {
do_test alter3-5.5 {
list [get_file_format test2.db] [get_file_format]
} {3 3}
}
do_test alter3-5.6 {
execsql {
ALTER TABLE aux.t1 ADD COLUMN d DEFAULT 1000;
SELECT sql FROM aux.sqlite_master;
}
} {{CREATE TABLE t1(a,b, c VARCHAR(128), d DEFAULT 1000)}}
do_test alter3-5.7 {
execsql {
SELECT * FROM aux.t1;
}
} {1 one {} 1000 2 two {} 1000}
ifcapable schema_version {
do_test alter3-5.8 {
execsql {
PRAGMA aux.schema_version;
}
} {32}
}
do_test alter3-5.9 {
execsql {
SELECT * FROM t1;
}
} {1 one 2 two}
do_test alter3-5.99 {
execsql {
DROP TABLE aux.t1;
DROP TABLE t1;
}
} {}
}
#----------------------------------------------------------------
# Test that the table schema is correctly reloaded when a column
# is added to a table.
#
ifcapable trigger&&tempdb {
do_test alter3-6.1 {
execsql {
CREATE TABLE t1(a, b);
CREATE TABLE log(trig, a, b);
CREATE TRIGGER t1_a AFTER INSERT ON t1 BEGIN
INSERT INTO log VALUES('a', new.a, new.b);
END;
CREATE TEMP TRIGGER t1_b AFTER INSERT ON t1 BEGIN
INSERT INTO log VALUES('b', new.a, new.b);
END;
INSERT INTO t1 VALUES(1, 2);
SELECT * FROM log;
}
} {b 1 2 a 1 2}
do_test alter3-6.2 {
execsql {
ALTER TABLE t1 ADD COLUMN c DEFAULT 'c';
INSERT INTO t1(a, b) VALUES(3, 4);
SELECT * FROM log;
}
} {b 1 2 a 1 2 b 3 4 a 3 4}
}
if {!$has_codec} {
ifcapable vacuum {
do_test alter3-7.1 {
execsql {
VACUUM;
}
get_file_format
} {1}
do_test alter3-7.2 {
execsql {
CREATE TABLE abc(a, b, c);
ALTER TABLE abc ADD d DEFAULT NULL;
}
get_file_format
} {3}
do_test alter3-7.3 {
execsql {
ALTER TABLE abc ADD e DEFAULT 10;
}
get_file_format
} {3}
do_test alter3-7.4 {
execsql {
ALTER TABLE abc ADD f DEFAULT NULL;
}
get_file_format
} {3}
do_test alter3-7.5 {
execsql {
VACUUM;
}
get_file_format
} {1}
}
}
# Ticket #1183 - Make sure adding columns to large tables does not cause
# memory corruption (as was the case before this bug was fixed).
do_test alter3-8.1 {
execsql {
CREATE TABLE t4(c1);
}
} {}
set ::sql ""
do_test alter3-8.2 {
set cols c1
for {set i 2} {$i < 100} {incr i} {
execsql "
ALTER TABLE t4 ADD c$i
"
lappend cols c$i
}
set ::sql "CREATE TABLE t4([join $cols {, }])"
list
} {}
do_test alter3-8.2 {
execsql {
SELECT sql FROM sqlite_master WHERE name = 't4';
}
} [list $::sql]
# 2021-07-20: Add support for detecting CHECK and NOT NULL constraint
# violations in ALTER TABLE ADD COLUMN
#
reset_db
do_execsql_test alter3-9.1 {
CREATE TABLE t1(a,b);
INSERT INTO t1 VALUES(1, 2), ('null!',NULL), (3,4);
} {}
do_catchsql_test alter3-9.2 {
ALTER TABLE t1 ADD COLUMN c CHECK(a!=1);
} {1 {CHECK constraint failed}}
do_catchsql_test alter3-9.3 {
ALTER TABLE t1 ADD COLUMN c CHECK(a!=3);
} {1 {CHECK constraint failed}}
do_catchsql_test alter3-9.4 {
ALTER TABLE t1 ADD COLUMN c CHECK(a!=2);
} {0 {}}
do_catchsql_test alter3-9.5 {
ALTER TABLE t1 ADD COLUMN d AS (b+1) NOT NULL;
} {1 {NOT NULL constraint failed}}
do_catchsql_test alter3-9.6 {
ALTER TABLE t1 ADD COLUMN d AS (b+1) NOT NULL CHECK(a!=1);
} {1 {CHECK constraint failed}}
do_catchsql_test alter3-9.7 {
ALTER TABLE t1 ADD COLUMN d AS (b+1) NOT NULL CHECK(a!=3);
} {1 {NOT NULL constraint failed}}
do_execsql_test alter3-9.10 {
CREATE TEMP TABLE t0(m,n);
INSERT INTO t0 VALUES(1, 2), ('null!',NULL), (3,4);
ATTACH ':memory:' AS aux1;
CREATE TABLE aux1.t2(x,y);
INSERT INTO t2 VALUES(1, 2), ('null!',NULL), (3,4);
} {}
do_catchsql_test alter3-9.11 {
ALTER TABLE t0 ADD COLUMN xtra1 AS (n+1) NOT NULL CHECK(m!=1);
} {1 {CHECK constraint failed}}
do_catchsql_test alter3-9.12 {
ALTER TABLE t0 ADD COLUMN xtra1 AS (n+1) NOT NULL CHECK(m!=3);
} {1 {NOT NULL constraint failed}}
do_catchsql_test alter3-9.13 {
ALTER TABLE t2 ADD COLUMN xtra1 AS (y+1) NOT NULL CHECK(x!=1);
} {1 {CHECK constraint failed}}
do_catchsql_test alter3-9.14 {
ALTER TABLE t2 ADD COLUMN xtra1 AS (y+1) NOT NULL CHECK(x!=3);
} {1 {NOT NULL constraint failed}}
finish_test

427
testdata/tcl/alter4.test vendored Normal file
View file

@ -0,0 +1,427 @@
# 2009 February 2
#
# 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 script is testing that SQLite can handle a subtle
# file format change that may be used in the future to implement
# "ALTER TABLE ... ADD COLUMN".
#
# $Id: alter4.test,v 1.1 2009/02/02 18:03:22 drh Exp $
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
ifcapable !altertable {
finish_test
return
}
# Test Organisation:
# ------------------
#
# alter4-1.*: Test that ALTER TABLE correctly modifies the CREATE TABLE sql.
# alter4-2.*: Test error messages.
# alter4-3.*: Test adding columns with default value NULL.
# alter4-4.*: Test adding columns with default values other than NULL.
# alter4-5.*: Test adding columns to tables in ATTACHed databases.
# alter4-6.*: Test that temp triggers are not accidentally dropped.
# alter4-7.*: Test that VACUUM resets the file-format.
#
do_test alter4-1.1 {
execsql {
CREATE TEMP TABLE abc(a, b, c);
SELECT sql FROM sqlite_temp_master;
}
} {{CREATE TABLE abc(a, b, c)}}
do_test alter4-1.1b {
execsql {
SELECT sql FROM temp.sqlite_master;
}
} {{CREATE TABLE abc(a, b, c)}}
do_test alter4-1.2 {
execsql {ALTER TABLE abc ADD d INTEGER;}
execsql {
SELECT sql FROM sqlite_temp_master;
}
} {{CREATE TABLE abc(a, b, c, d INTEGER)}}
do_test alter4-1.2b {
execsql {
SELECT sql FROM temp.sqlite_master;
}
} {{CREATE TABLE abc(a, b, c, d INTEGER)}}
do_test alter4-1.3 {
execsql {ALTER TABLE abc ADD e}
execsql {
SELECT sql FROM sqlite_temp_master;
}
} {{CREATE TABLE abc(a, b, c, d INTEGER, e)}}
do_test alter4-1.3b {
execsql {
SELECT sql FROM temp.sqlite_master;
}
} {{CREATE TABLE abc(a, b, c, d INTEGER, e)}}
do_test alter4-1.4 {
execsql {
CREATE TABLE temp.t1(a, b);
ALTER TABLE t1 ADD c;
SELECT sql FROM sqlite_temp_master WHERE tbl_name = 't1';
}
} {{CREATE TABLE t1(a, b, c)}}
do_test alter4-1.4b {
execsql {
SELECT sql FROM temp.sqlite_master WHERE tbl_name = 't1';
}
} {{CREATE TABLE t1(a, b, c)}}
do_test alter4-1.5 {
execsql {
ALTER TABLE t1 ADD d CHECK (a>d);
SELECT sql FROM sqlite_temp_master WHERE tbl_name = 't1';
}
} {{CREATE TABLE t1(a, b, c, d CHECK (a>d))}}
ifcapable foreignkey {
do_test alter4-1.6 {
execsql {
CREATE TEMP TABLE t2(a, b, UNIQUE(a, b));
ALTER TABLE t2 ADD c REFERENCES t1(c) ;
SELECT sql FROM sqlite_temp_master
WHERE tbl_name = 't2' AND type = 'table';
}
} {{CREATE TABLE t2(a, b, c REFERENCES t1(c), UNIQUE(a, b))}}
}
do_test alter4-1.7 {
execsql {
CREATE TEMPORARY TABLE t3(a, b, UNIQUE(a, b));
ALTER TABLE t3 ADD COLUMN c VARCHAR(10, 20);
SELECT sql FROM sqlite_temp_master
WHERE tbl_name = 't3' AND type = 'table';
}
} {{CREATE TABLE t3(a, b, c VARCHAR(10, 20), UNIQUE(a, b))}}
do_test alter4-1.99 {
catchsql {
# May not exist if foriegn-keys are omitted at compile time.
DROP TABLE t2;
}
execsql {
DROP TABLE abc;
DROP TABLE t1;
DROP TABLE t3;
}
} {}
do_test alter4-2.1 {
execsql {
CREATE TABLE temp.t1(a, b);
INSERT INTO t1 VALUES(1,2);
}
catchsql {
ALTER TABLE t1 ADD c PRIMARY KEY;
}
} {1 {Cannot add a PRIMARY KEY column}}
do_test alter4-2.2 {
catchsql {
ALTER TABLE t1 ADD c UNIQUE
}
} {1 {Cannot add a UNIQUE column}}
do_test alter4-2.3 {
catchsql {
ALTER TABLE t1 ADD b VARCHAR(10)
}
} {1 {duplicate column name: b}}
do_test alter4-2.3 {
catchsql {
ALTER TABLE t1 ADD c NOT NULL;
}
} {1 {Cannot add a NOT NULL column with default value NULL}}
do_test alter4-2.4 {
catchsql {
ALTER TABLE t1 ADD c NOT NULL DEFAULT 10;
}
} {0 {}}
ifcapable view {
do_test alter4-2.5 {
execsql {
CREATE TEMPORARY VIEW v1 AS SELECT * FROM t1;
}
catchsql {
alter table v1 add column d;
}
} {1 {Cannot add a column to a view}}
}
do_test alter4-2.6 {
catchsql {
alter table t1 add column d DEFAULT CURRENT_TIME;
}
} {1 {Cannot add a column with non-constant default}}
do_test alter4-2.7 {
catchsql {
alter table t1 add column d default (-5+1);
}
} {1 {Cannot add a column with non-constant default}}
do_test alter4-2.99 {
execsql {
DROP TABLE t1;
}
} {}
do_test alter4-3.1 {
execsql {
CREATE TEMP TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 100);
INSERT INTO t1 VALUES(2, 300);
SELECT * FROM t1;
}
} {1 100 2 300}
do_test alter4-3.1 {
execsql {
PRAGMA schema_version = 10;
}
} {}
do_test alter4-3.2 {
execsql {
ALTER TABLE t1 ADD c;
SELECT * FROM t1;
}
} {1 100 {} 2 300 {}}
ifcapable schema_version {
do_test alter4-3.4 {
execsql {
PRAGMA schema_version;
}
} {10}
}
do_test alter4-4.1 {
db close
forcedelete test.db
set ::DB [sqlite3 db test.db]
execsql {
CREATE TEMP TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 100);
INSERT INTO t1 VALUES(2, 300);
SELECT * FROM t1;
}
} {1 100 2 300}
do_test alter4-4.1 {
execsql {
PRAGMA schema_version = 20;
}
} {}
do_test alter4-4.2 {
execsql {
ALTER TABLE t1 ADD c DEFAULT 'hello world';
SELECT * FROM t1;
}
} {1 100 {hello world} 2 300 {hello world}}
ifcapable schema_version {
do_test alter4-4.4 {
execsql {
PRAGMA schema_version;
}
} {20}
}
do_test alter4-4.99 {
execsql {
DROP TABLE t1;
}
} {}
ifcapable attach {
do_test alter4-5.1 {
forcedelete test2.db
forcedelete test2.db-journal
execsql {
CREATE TEMP TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 'one');
INSERT INTO t1 VALUES(2, 'two');
ATTACH 'test2.db' AS aux;
CREATE TABLE aux.t1 AS SELECT * FROM t1;
PRAGMA aux.schema_version = 30;
SELECT sql FROM aux.sqlite_master;
}
} {{CREATE TABLE t1(a,b)}}
do_test alter4-5.2 {
execsql {
ALTER TABLE aux.t1 ADD COLUMN c VARCHAR(128);
SELECT sql FROM aux.sqlite_master;
}
} {{CREATE TABLE t1(a,b, c VARCHAR(128))}}
do_test alter4-5.3 {
execsql {
SELECT * FROM aux.t1;
}
} {1 one {} 2 two {}}
ifcapable schema_version {
do_test alter4-5.4 {
execsql {
PRAGMA aux.schema_version;
}
} {31}
}
do_test alter4-5.6 {
execsql {
ALTER TABLE aux.t1 ADD COLUMN d DEFAULT 1000;
SELECT sql FROM aux.sqlite_master;
}
} {{CREATE TABLE t1(a,b, c VARCHAR(128), d DEFAULT 1000)}}
do_test alter4-5.7 {
execsql {
SELECT * FROM aux.t1;
}
} {1 one {} 1000 2 two {} 1000}
ifcapable schema_version {
do_test alter4-5.8 {
execsql {
PRAGMA aux.schema_version;
}
} {32}
}
do_test alter4-5.9 {
execsql {
SELECT * FROM t1;
}
} {1 one 2 two}
do_test alter4-5.99 {
execsql {
DROP TABLE aux.t1;
DROP TABLE t1;
}
} {}
}
#----------------------------------------------------------------
# Test that the table schema is correctly reloaded when a column
# is added to a table.
#
ifcapable trigger&&tempdb {
do_test alter4-6.1 {
execsql {
CREATE TEMP TABLE t1(a, b);
CREATE TEMP TABLE log(trig, a, b);
CREATE TRIGGER t1_a AFTER INSERT ON t1 BEGIN
INSERT INTO log VALUES('a', new.a, new.b);
END;
CREATE TEMP TRIGGER t1_b AFTER INSERT ON t1 BEGIN
INSERT INTO log VALUES('b', new.a, new.b);
END;
INSERT INTO t1 VALUES(1, 2);
SELECT * FROM log ORDER BY trig, a, b;
}
} {a 1 2 b 1 2}
do_test alter4-6.2 {
execsql {
ALTER TABLE t1 ADD COLUMN c DEFAULT 'c';
INSERT INTO t1(a, b) VALUES(3, 4);
SELECT * FROM log ORDER BY trig, a, b;
}
} {a 1 2 a 3 4 b 1 2 b 3 4}
}
# Ticket #1183 - Make sure adding columns to large tables does not cause
# memory corruption (as was the case before this bug was fixed).
do_test alter4-8.1 {
execsql {
CREATE TEMP TABLE t4(c1);
}
} {}
set ::sql ""
do_test alter4-8.2 {
set cols c1
for {set i 2} {$i < 100} {incr i} {
execsql "
ALTER TABLE t4 ADD c$i
"
lappend cols c$i
}
set ::sql "CREATE TABLE t4([join $cols {, }])"
list
} {}
do_test alter4-8.2 {
execsql {
SELECT sql FROM sqlite_temp_master WHERE name = 't4';
}
} [list $::sql]
# Test that a default value equal to -1 multipied by the smallest possible
# 64-bit integer is correctly converted to a real.
do_execsql_test alter4-9.1 {
CREATE TABLE t5(
a INTEGER DEFAULT -9223372036854775808,
b INTEGER DEFAULT (-(-9223372036854775808))
);
INSERT INTO t5 DEFAULT VALUES;
}
do_execsql_test alter4-9.2 { SELECT typeof(a), a, typeof(b), b FROM t5; } {
integer -9223372036854775808
real 9.22337203685478e+18
}
do_execsql_test alter4-9.3 {
ALTER TABLE t5 ADD COLUMN c INTEGER DEFAULT (-(-9223372036854775808));
SELECT typeof(c), c FROM t5;
} {real 9.22337203685478e+18}
# Confirm that doing an ALTER TABLE on a legacy format database
# does not corrupt DESC indexes.
#
# Ticket https://www.sqlite.org/src/tktview/f68bf68513a1c
#
do_test alter4-10.1 {
db close
sqlite3 db :memory:
sqlite3_db_config db LEGACY_FILE_FORMAT 1
db eval {
CREATE TABLE t1(a,b,c);
CREATE INDEX t1a ON t1(a DESC);
INSERT INTO t1 VALUES(1,2,3);
INSERT INTO t1 VALUES(2,3,4);
ALTER TABLE t1 ADD COLUMN d;
PRAGMA integrity_check;
}
} {ok}
reset_db
do_execsql_test alter4-11.0 {
CREATE TABLE t1(c INTEGER PRIMARY KEY, d);
INSERT INTO t1(c,d) VALUES(1,2);
PRAGMA foreign_keys = on;
ALTER TABLE t1 ADD COLUMN e;
}
do_execsql_test alter4-11.1 {
ALTER TABLE t1 ADD COLUMN f REFERENCES t1;
}
do_catchsql_test alter4-11.2 {
ALTER TABLE t1 ADD COLUMN g REFERENCES t1 DEFAULT 4;
} {1 {Cannot add a REFERENCES column with non-NULL default value}}
do_catchsql_test alter4-11.3 {
ALTER TABLE t2 ADD COLUMN g;
} {1 {no such table: t2}}
ifcapable fts5 {
do_execsql_test alter4-11.4 {
CREATE VIRTUAL TABLE fff USING fts5(f);
}
do_catchsql_test alter4-11.2 {
ALTER TABLE fff ADD COLUMN g;
} {1 {virtual tables may not be altered}}
}
finish_test

72
testdata/tcl/alterauth.test vendored Normal file
View file

@ -0,0 +1,72 @@
# 2018 September 2
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#*************************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
ifcapable !altertable {
finish_test
return
}
set testprefix alterauth
set ::auth [list]
proc xAuth {type args} {
if {$type == "SQLITE_ALTER_TABLE"} {
lappend ::auth [concat $type [lrange $args 0 3]]
}
return SQLITE_OK
}
db auth xAuth
do_execsql_test 1.0 { CREATE TABLE t1(a, b, c); }
do_test 1.1 {
set ::auth [list]
execsql { ALTER TABLE t1 RENAME TO t2 }
set ::auth
} {{SQLITE_ALTER_TABLE main t1 {} {}}}
do_test 1.2 {
set ::auth [list]
execsql { ALTER TABLE t2 RENAME c TO ccc }
set ::auth
} {{SQLITE_ALTER_TABLE main t2 {} {}}}
do_test 1.3 {
set ::auth [list]
execsql { ALTER TABLE t2 ADD COLUMN d }
set ::auth
} {{SQLITE_ALTER_TABLE main t2 {} {}}}
proc xAuth {type args} {
if {$type == "SQLITE_ALTER_TABLE"} {
return SQLITE_DENY
}
return SQLITE_OK
}
do_test 2.1 {
catchsql { ALTER TABLE t2 RENAME TO t3 }
} {1 {not authorized}}
do_test 2.2 {
catchsql { ALTER TABLE t2 RENAME d TO ddd }
} {1 {not authorized}}
do_test 2.3 {
catchsql { ALTER TABLE t2 ADD COLUMN e }
} {1 {not authorized}}
finish_test

119
testdata/tcl/alterauth2.test vendored Normal file
View file

@ -0,0 +1,119 @@
# 2018 October 6
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#*************************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
ifcapable !altertable {
finish_test
return
}
set testprefix alterauth2
set ::auth [list]
proc xAuth {type args} {
lappend ::auth [concat $type [lrange $args 0 3]]
if {$type=="SQLITE_READ" && [lindex $args 0] == "t2"} breakpoint
return SQLITE_OK
}
db auth xAuth
proc do_auth_test {tn sql authcode} {
set script "
set ::auth \[list\]
execsql {$sql}
lsort -unique \[set ::auth\]
"
set normal [list {*}$authcode]
uplevel [list do_test $tn $script $normal]
}
do_execsql_test 1.0 {
CREATE TABLE t1(a, b, c);
CREATE VIEW v1 AS SELECT * FROM t1;
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
DELETE FROM t1 WHERE a<new.a;
END;
CREATE TEMP TRIGGER tr2 AFTER UPDATE OF a, b ON t1 BEGIN
UPDATE t1 SET a=a+1 WHERE new.b<b;
END;
}
do_auth_test 1.1 {
ALTER TABLE t1 RENAME TO t2;
} {
{SQLITE_ALTER_TABLE main t1 {} {}}
{SQLITE_FUNCTION {} like {} {}}
{SQLITE_FUNCTION {} sqlite_rename_table {} {}}
{SQLITE_FUNCTION {} sqlite_rename_test {} {}}
{SQLITE_FUNCTION {} substr {} {}}
{SQLITE_READ sqlite_master name main {}}
{SQLITE_READ sqlite_master sql main {}}
{SQLITE_READ sqlite_master tbl_name main {}}
{SQLITE_READ sqlite_master type main {}}
{SQLITE_READ sqlite_temp_master name temp {}}
{SQLITE_READ sqlite_temp_master sql temp {}}
{SQLITE_READ sqlite_temp_master tbl_name temp {}}
{SQLITE_READ sqlite_temp_master type temp {}}
{SQLITE_SELECT {} {} {} {}}
{SQLITE_UPDATE sqlite_master name main {}}
{SQLITE_UPDATE sqlite_master sql main {}}
{SQLITE_UPDATE sqlite_master tbl_name main {}}
{SQLITE_UPDATE sqlite_temp_master sql temp {}}
{SQLITE_UPDATE sqlite_temp_master tbl_name temp {}}
}
do_auth_test 1.2 {
ALTER TABLE t2 RENAME a TO aaa;
} {
{SQLITE_ALTER_TABLE main t2 {} {}}
{SQLITE_FUNCTION {} like {} {}}
{SQLITE_FUNCTION {} sqlite_rename_column {} {}}
{SQLITE_FUNCTION {} sqlite_rename_quotefix {} {}}
{SQLITE_FUNCTION {} sqlite_rename_test {} {}}
{SQLITE_READ sqlite_master name main {}}
{SQLITE_READ sqlite_master sql main {}}
{SQLITE_READ sqlite_master tbl_name main {}}
{SQLITE_READ sqlite_master type main {}}
{SQLITE_READ sqlite_temp_master name temp {}}
{SQLITE_READ sqlite_temp_master sql temp {}}
{SQLITE_READ sqlite_temp_master type temp {}}
{SQLITE_SELECT {} {} {} {}}
{SQLITE_UPDATE sqlite_master sql main {}}
{SQLITE_UPDATE sqlite_temp_master sql temp {}}
}
do_auth_test 1.3 {
ALTER TABLE t2 DROP COLUMN c;
} {
{SQLITE_ALTER_TABLE main t2 c {}}
{SQLITE_FUNCTION {} like {} {}}
{SQLITE_FUNCTION {} sqlite_drop_column {} {}}
{SQLITE_FUNCTION {} sqlite_rename_quotefix {} {}}
{SQLITE_FUNCTION {} sqlite_rename_test {} {}}
{SQLITE_READ sqlite_master name main {}}
{SQLITE_READ sqlite_master sql main {}}
{SQLITE_READ sqlite_master tbl_name main {}}
{SQLITE_READ sqlite_master type main {}}
{SQLITE_READ sqlite_temp_master name temp {}}
{SQLITE_READ sqlite_temp_master sql temp {}}
{SQLITE_READ sqlite_temp_master type temp {}}
{SQLITE_SELECT {} {} {} {}}
{SQLITE_UPDATE sqlite_master sql main {}}
{SQLITE_UPDATE sqlite_temp_master sql temp {}}
}
finish_test

934
testdata/tcl/altercol.test vendored Normal file
View file

@ -0,0 +1,934 @@
# 2009 February 2
#
# 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 script is testing that SQLite can handle a subtle
# file format change that may be used in the future to implement
# "ALTER TABLE ... RENAME COLUMN ... TO".
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix altercol
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
ifcapable !altertable {
finish_test
return
}
# Drop all the tables and views in the 'main' database of database connect
# [db]. Sort the objects by name before dropping them.
#
proc drop_all_tables_and_views {db} {
set SQL {
SELECT name, type FROM sqlite_master
WHERE type IN ('table', 'view') AND name NOT LIKE 'sqlite_%'
ORDER BY 1
}
foreach {z t} [db eval $SQL] {
db eval "DROP $t $z"
}
}
foreach {tn before after} {
1 {CREATE TABLE t1(a INTEGER, b TEXT, c BLOB)}
{CREATE TABLE t1(a INTEGER, d TEXT, c BLOB)}
2 {CREATE TABLE t1(a INTEGER, x TEXT, "b" BLOB)}
{CREATE TABLE t1(a INTEGER, x TEXT, "d" BLOB)}
3 {CREATE TABLE t1(a INTEGER, b TEXT, c BLOB, CHECK(b!=''))}
{CREATE TABLE t1(a INTEGER, d TEXT, c BLOB, CHECK(d!=''))}
4 {CREATE TABLE t1(a INTEGER, b TEXT, c BLOB, CHECK(t1.b!=''))}
{CREATE TABLE t1(a INTEGER, d TEXT, c BLOB, CHECK(t1.d!=''))}
5 {CREATE TABLE t1(a INTEGER, b TEXT, c BLOB, CHECK( coalesce(b,c) ))}
{CREATE TABLE t1(a INTEGER, d TEXT, c BLOB, CHECK( coalesce(d,c) ))}
6 {CREATE TABLE t1(a INTEGER, "b"TEXT, c BLOB, CHECK( coalesce(b,c) ))}
{CREATE TABLE t1(a INTEGER, "d"TEXT, c BLOB, CHECK( coalesce(d,c) ))}
7 {CREATE TABLE t1(a INTEGER, b TEXT, c BLOB, PRIMARY KEY(b, c))}
{CREATE TABLE t1(a INTEGER, d TEXT, c BLOB, PRIMARY KEY(d, c))}
8 {CREATE TABLE t1(a INTEGER, b TEXT PRIMARY KEY, c BLOB)}
{CREATE TABLE t1(a INTEGER, d TEXT PRIMARY KEY, c BLOB)}
9 {CREATE TABLE t1(a, b TEXT, c, PRIMARY KEY(a, b), UNIQUE("B"))}
{CREATE TABLE t1(a, d TEXT, c, PRIMARY KEY(a, d), UNIQUE("d"))}
10 {CREATE TABLE t1(a, b, c); CREATE INDEX t1i ON t1(a, c)}
{{CREATE TABLE t1(a, d, c)} {CREATE INDEX t1i ON t1(a, c)}}
11 {CREATE TABLE t1(a, b, c); CREATE INDEX t1i ON t1(b, c)}
{{CREATE TABLE t1(a, d, c)} {CREATE INDEX t1i ON t1(d, c)}}
12 {CREATE TABLE t1(a, b, c); CREATE INDEX t1i ON t1(b+b+b+b, c) WHERE b>0}
{{CREATE TABLE t1(a, d, c)} {CREATE INDEX t1i ON t1(d+d+d+d, c) WHERE d>0}}
13 {CREATE TABLE t1(a, b, c, FOREIGN KEY (b) REFERENCES t2)}
{CREATE TABLE t1(a, d, c, FOREIGN KEY (d) REFERENCES t2)}
14 {CREATE TABLE t1(a INTEGER, b TEXT, c BLOB, PRIMARY KEY(b))}
{CREATE TABLE t1(a INTEGER, d TEXT, c BLOB, PRIMARY KEY(d))}
15 {CREATE TABLE t1(a INTEGER, b INTEGER, c BLOB, PRIMARY KEY(b))}
{CREATE TABLE t1(a INTEGER, d INTEGER, c BLOB, PRIMARY KEY(d))}
16 {CREATE TABLE t1(a INTEGER, b INTEGER PRIMARY KEY, c BLOB)}
{CREATE TABLE t1(a INTEGER, d INTEGER PRIMARY KEY, c BLOB)}
17 {CREATE TABLE t1(a INTEGER, b INTEGER PRIMARY KEY, c BLOB, FOREIGN KEY (b) REFERENCES t2)}
{CREATE TABLE t1(a INTEGER, d INTEGER PRIMARY KEY, c BLOB, FOREIGN KEY (d) REFERENCES t2)}
} {
reset_db
do_execsql_test 1.$tn.0 $before
do_execsql_test 1.$tn.1 {
INSERT INTO t1 VALUES(1, 2, 3);
}
do_execsql_test 1.$tn.2 {
ALTER TABLE t1 RENAME COLUMN b TO d;
}
do_execsql_test 1.$tn.3 {
SELECT * FROM t1;
} {1 2 3}
if {[string first INDEX $before]>0} {
set res $after
} else {
set res [list $after]
}
do_execsql_test 1.$tn.4 {
SELECT sql FROM sqlite_master WHERE tbl_name='t1' AND sql!=''
} $res
}
#-------------------------------------------------------------------------
#
do_execsql_test 2.0 {
CREATE TABLE t3(a, b, c, d, e, f, g, h, i, j, k, l, m, FOREIGN KEY (b, c, d, e, f, g, h, i, j, k, l, m) REFERENCES t4);
}
sqlite3 db2 test.db
do_execsql_test -db db2 2.1 { SELECT b FROM t3 }
do_execsql_test 2.2 {
ALTER TABLE t3 RENAME b TO biglongname;
SELECT sql FROM sqlite_master WHERE name='t3';
} {{CREATE TABLE t3(a, biglongname, c, d, e, f, g, h, i, j, k, l, m, FOREIGN KEY (biglongname, c, d, e, f, g, h, i, j, k, l, m) REFERENCES t4)}}
do_execsql_test -db db2 2.3 { SELECT biglongname FROM t3 }
#-------------------------------------------------------------------------
#
do_execsql_test 3.0 {
CREATE TABLE t4(x, y, z);
CREATE TRIGGER ttt AFTER INSERT ON t4 WHEN new.y<0 BEGIN
SELECT x, y, z FROM t4;
DELETE FROM t4 WHERE y=32;
UPDATE t4 SET x=y+1, y=0 WHERE y=32;
INSERT INTO t4(x, y, z) SELECT 4, 5, 6 WHERE 0;
END;
INSERT INTO t4 VALUES(3, 2, 1);
}
do_execsql_test 3.1 {
ALTER TABLE t4 RENAME y TO abc;
SELECT sql FROM sqlite_master WHERE name='t4';
} {{CREATE TABLE t4(x, abc, z)}}
do_execsql_test 3.2 {
SELECT * FROM t4;
} {3 2 1}
do_execsql_test 3.3 { INSERT INTO t4 VALUES(6, 5, 4); } {}
do_execsql_test 3.4 { SELECT sql FROM sqlite_master WHERE type='trigger' } {
{CREATE TRIGGER ttt AFTER INSERT ON t4 WHEN new.abc<0 BEGIN
SELECT x, abc, z FROM t4;
DELETE FROM t4 WHERE abc=32;
UPDATE t4 SET x=abc+1, abc=0 WHERE abc=32;
INSERT INTO t4(x, abc, z) SELECT 4, 5, 6 WHERE 0;
END}
}
#-------------------------------------------------------------------------
#
do_execsql_test 4.0 {
CREATE TABLE c1(a, b, FOREIGN KEY (a, b) REFERENCES p1(c, d));
CREATE TABLE p1(c, d, PRIMARY KEY(c, d));
PRAGMA foreign_keys = 1;
INSERT INTO p1 VALUES(1, 2);
INSERT INTO p1 VALUES(3, 4);
}
do_execsql_test 4.1 {
ALTER TABLE p1 RENAME d TO "silly name";
SELECT sql FROM sqlite_master WHERE name IN ('c1', 'p1');
} {
{CREATE TABLE c1(a, b, FOREIGN KEY (a, b) REFERENCES p1(c, "silly name"))}
{CREATE TABLE p1(c, "silly name", PRIMARY KEY(c, "silly name"))}
}
do_execsql_test 4.2 { INSERT INTO c1 VALUES(1, 2); }
do_execsql_test 4.3 {
CREATE TABLE c2(a, b, FOREIGN KEY (a, b) REFERENCES p1);
}
do_execsql_test 4.4 {
ALTER TABLE p1 RENAME "silly name" TO reasonable;
SELECT sql FROM sqlite_master WHERE name IN ('c1', 'c2', 'p1');
} {
{CREATE TABLE c1(a, b, FOREIGN KEY (a, b) REFERENCES p1(c, "reasonable"))}
{CREATE TABLE p1(c, "reasonable", PRIMARY KEY(c, "reasonable"))}
{CREATE TABLE c2(a, b, FOREIGN KEY (a, b) REFERENCES p1)}
}
#-------------------------------------------------------------------------
do_execsql_test 5.0 {
CREATE TABLE t5(a, b, c);
CREATE INDEX t5a ON t5(a);
INSERT INTO t5 VALUES(1, 2, 3), (4, 5, 6);
ANALYZE;
}
do_execsql_test 5.1 {
ALTER TABLE t5 RENAME b TO big;
SELECT big FROM t5;
} {2 5}
do_catchsql_test 6.1 {
ALTER TABLE sqlite_stat1 RENAME tbl TO thetable;
} {1 {table sqlite_stat1 may not be altered}}
#-------------------------------------------------------------------------
#
do_execsql_test 6.0 {
CREATE TABLE blob(
rid INTEGER PRIMARY KEY,
rcvid INTEGER,
size INTEGER,
uuid TEXT UNIQUE NOT NULL,
content BLOB,
CHECK( length(uuid)>=40 AND rid>0 )
);
}
do_execsql_test 6.1 {
ALTER TABLE "blob" RENAME COLUMN "rid" TO "a1";
}
do_catchsql_test 6.2 {
ALTER TABLE "blob" RENAME COLUMN "a1" TO [where];
} {0 {}}
do_execsql_test 6.3 {
SELECT "where" FROM blob;
} {}
#-------------------------------------------------------------------------
# Triggers.
#
db close
db2 close
reset_db
do_execsql_test 7.0 {
CREATE TABLE c(x);
INSERT INTO c VALUES(0);
CREATE TABLE t6("col a", "col b", "col c");
CREATE TRIGGER zzz AFTER UPDATE OF "col a", "col c" ON t6 BEGIN
UPDATE c SET x=x+1;
END;
}
do_execsql_test 7.1.1 {
INSERT INTO t6 VALUES(0, 0, 0);
UPDATE t6 SET "col c" = 1;
SELECT * FROM c;
} {1}
do_execsql_test 7.1.2 {
ALTER TABLE t6 RENAME "col c" TO "col 3";
}
do_execsql_test 7.1.3 {
UPDATE t6 SET "col 3" = 0;
SELECT * FROM c;
} {2}
#-------------------------------------------------------------------------
# Views.
#
reset_db
do_execsql_test 8.0 {
CREATE TABLE a1(x INTEGER, y TEXT, z BLOB, PRIMARY KEY(x));
CREATE TABLE a2(a, b, c);
CREATE VIEW v1 AS SELECT x, y, z FROM a1;
}
do_execsql_test 8.1 {
ALTER TABLE a1 RENAME y TO yyy;
SELECT sql FROM sqlite_master WHERE type='view';
} {{CREATE VIEW v1 AS SELECT x, yyy, z FROM a1}}
do_execsql_test 8.2.1 {
DROP VIEW v1;
CREATE VIEW v2 AS SELECT x, x+x, a, a+a FROM a1, a2;
} {}
do_execsql_test 8.2.2 {
ALTER TABLE a1 RENAME x TO xxx;
}
do_execsql_test 8.2.3 {
SELECT sql FROM sqlite_master WHERE type='view';
} {{CREATE VIEW v2 AS SELECT xxx, xxx+xxx, a, a+a FROM a1, a2}}
do_execsql_test 8.3.1 {
DROP TABLE a2;
DROP VIEW v2;
CREATE TABLE a2(a INTEGER PRIMARY KEY, b, c);
CREATE VIEW v2 AS SELECT xxx, xxx+xxx, a, a+a FROM a1, a2;
} {}
do_execsql_test 8.3.2 {
ALTER TABLE a1 RENAME xxx TO x;
}
do_execsql_test 8.3.3 {
SELECT sql FROM sqlite_master WHERE type='view';
} {{CREATE VIEW v2 AS SELECT x, x+x, a, a+a FROM a1, a2}}
do_execsql_test 8.4.0 {
CREATE TABLE b1(a, b, c);
CREATE TABLE b2(x, y, z);
}
do_execsql_test 8.4.1 {
CREATE VIEW vvv AS SELECT c+c || coalesce(c, c) FROM b1, b2 WHERE x=c GROUP BY c HAVING c>0;
ALTER TABLE b1 RENAME c TO "a;b";
SELECT sql FROM sqlite_master WHERE name='vvv';
} {{CREATE VIEW vvv AS SELECT "a;b"+"a;b" || coalesce("a;b", "a;b") FROM b1, b2 WHERE x="a;b" GROUP BY "a;b" HAVING "a;b">0}}
do_execsql_test 8.4.2 {
CREATE VIEW www AS SELECT b FROM b1 UNION ALL SELECT y FROM b2;
ALTER TABLE b1 RENAME b TO bbb;
SELECT sql FROM sqlite_master WHERE name='www';
} {{CREATE VIEW www AS SELECT bbb FROM b1 UNION ALL SELECT y FROM b2}}
db collate nocase {string compare}
do_execsql_test 8.4.3 {
CREATE VIEW xxx AS SELECT a FROM b1 UNION SELECT x FROM b2 ORDER BY 1 COLLATE nocase;
}
do_execsql_test 8.4.4 {
ALTER TABLE b2 RENAME x TO hello;
SELECT sql FROM sqlite_master WHERE name='xxx';
} {{CREATE VIEW xxx AS SELECT a FROM b1 UNION SELECT hello FROM b2 ORDER BY 1 COLLATE nocase}}
do_catchsql_test 8.4.5 {
CREATE VIEW zzz AS SELECT george, ringo FROM b1;
ALTER TABLE b1 RENAME a TO aaa;
} {1 {error in view zzz: no such column: george}}
#-------------------------------------------------------------------------
# More triggers.
#
proc do_rename_column_test {tn old new lSchema} {
for {set i 0} {$i < 2} {incr i} {
drop_all_tables_and_views db
set lSorted [list]
foreach sql $lSchema {
execsql $sql
lappend lSorted [string trim $sql]
}
set lSorted [lsort $lSorted]
do_execsql_test $tn.$i.1 {
SELECT sql FROM sqlite_master WHERE sql!='' ORDER BY 1
} $lSorted
if {$i==1} {
db close
sqlite3 db test.db
}
do_execsql_test $tn.$i.2 "ALTER TABLE t1 RENAME $old TO $new"
do_execsql_test $tn.$i.3 {
SELECT sql FROM sqlite_master ORDER BY 1
} [string map [list $old $new] $lSorted]
}
}
foreach {tn old new lSchema} {
1 _x_ _xxx_ {
{ CREATE TABLE t1(a, b, _x_) }
{ CREATE TRIGGER AFTER INSERT ON t1 BEGIN
SELECT _x_ FROM t1;
END }
}
2 _x_ _xxx_ {
{ CREATE TABLE t1(a, b, _x_) }
{ CREATE TABLE t2(c, d, e) }
{ CREATE TRIGGER ttt AFTER INSERT ON t2 BEGIN
SELECT _x_ FROM t1;
END }
}
3 _x_ _xxx_ {
{ CREATE TABLE t1(a, b, _x_ INTEGER, PRIMARY KEY(_x_), CHECK(_x_>0)) }
{ CREATE TABLE t2(c, d, e) }
{ CREATE TRIGGER ttt AFTER UPDATE ON t1 BEGIN
INSERT INTO t2 VALUES(new.a, new.b, new._x_);
END }
}
4 _x_ _xxx_ {
{ CREATE TABLE t1(a, b, _x_ INTEGER, PRIMARY KEY(_x_), CHECK(_x_>0)) }
{ CREATE TRIGGER ttt AFTER UPDATE ON t1 BEGIN
INSERT INTO t1 VALUES(new.a, new.b, new._x_)
ON CONFLICT (_x_) WHERE _x_>10 DO UPDATE SET _x_ = _x_+1;
END }
}
4 _x_ _xxx_ {
{ CREATE TABLE t1(a, b, _x_ INTEGER, PRIMARY KEY(_x_), CHECK(_x_>0)) }
{ CREATE TRIGGER ttt AFTER UPDATE ON t1 BEGIN
INSERT INTO t1 VALUES(new.a, new.b, new._x_)
ON CONFLICT (_x_) WHERE _x_>10 DO NOTHING;
END }
}
} {
do_rename_column_test 9.$tn $old $new $lSchema
}
#-------------------------------------------------------------------------
# Test that views can be edited even if there are missing collation
# sequences or user defined functions.
#
reset_db
ifcapable vtab {
foreach {tn old new lSchema} {
1 _x_ _xxx_ {
{ CREATE TABLE t1(a, b, _x_) }
{ CREATE VIEW s1 AS SELECT a, b, _x_ FROM t1 WHERE _x_='abc' COLLATE xyz }
}
2 _x_ _xxx_ {
{ CREATE TABLE t1(a, b, _x_) }
{ CREATE VIEW v1 AS SELECT a, b, _x_ FROM t1 WHERE scalar(_x_) }
}
3 _x_ _xxx_ {
{ CREATE TABLE t1(a, b, _x_) }
{ CREATE VIEW v1 AS SELECT a, b, _x_ FROM t1 WHERE _x_ = unicode(1, 2, 3) }
}
4 _x_ _xxx_ {
{ CREATE TABLE t1(a, b, _x_) }
{ CREATE VIRTUAL TABLE e1 USING echo(t1) }
}
} {
register_echo_module db
do_rename_column_test 10.$tn $old $new $lSchema
}
#--------------------------------------------------------------------------
# Test that if a view or trigger refers to a virtual table for which the
# module is not available, RENAME COLUMN cannot proceed.
#
reset_db
register_echo_module db
do_execsql_test 11.0 {
CREATE TABLE x1(a, b, c);
CREATE VIRTUAL TABLE e1 USING echo(x1);
}
db close
sqlite3 db test.db
do_execsql_test 11.1 {
ALTER TABLE x1 RENAME b TO bbb;
SELECT sql FROM sqlite_master;
} { {CREATE TABLE x1(a, bbb, c)} {CREATE VIRTUAL TABLE e1 USING echo(x1)} }
do_execsql_test 11.2 {
CREATE VIEW v1 AS SELECT e1.*, x1.c FROM e1, x1;
}
do_catchsql_test 11.3 {
ALTER TABLE x1 RENAME c TO ccc;
} {1 {error in view v1: no such module: echo}}
}
#-------------------------------------------------------------------------
# Test some error conditions:
#
# 1. Renaming a column of a system table,
# 2. Renaming a column of a VIEW,
# 3. Renaming a column of a virtual table.
# 4. Renaming a column that does not exist.
# 5. Renaming a column of a table that does not exist.
#
reset_db
do_execsql_test 12.1.1 {
CREATE TABLE t1(a, b);
CREATE INDEX t1a ON t1(a);
INSERT INTO t1 VALUES(1, 1), (2, 2), (3, 4);
ANALYZE;
}
do_catchsql_test 12.1.2 {
ALTER TABLE sqlite_stat1 RENAME idx TO theindex;
} {1 {table sqlite_stat1 may not be altered}}
do_execsql_test 12.1.3 {
SELECT sql FROM sqlite_master WHERE tbl_name = 'sqlite_stat1'
} {{CREATE TABLE sqlite_stat1(tbl,idx,stat)}}
do_execsql_test 12.2.1 {
CREATE VIEW v1 AS SELECT * FROM t1;
CREATE VIEW v2(c, d) AS SELECT * FROM t1;
}
do_catchsql_test 12.2.2 {
ALTER TABLE v1 RENAME a TO z;
} {1 {cannot rename columns of view "v1"}}
do_catchsql_test 12.2.3 {
ALTER TABLE v2 RENAME c TO y;
} {1 {cannot rename columns of view "v2"}}
ifcapable fts5 {
do_execsql_test 12.3.1 {
CREATE VIRTUAL TABLE ft USING fts5(a, b, c);
}
do_catchsql_test 12.3.2 {
ALTER TABLE ft RENAME a TO z;
} {1 {cannot rename columns of virtual table "ft"}}
}
do_execsql_test 12.4.1 {
CREATE TABLE t2(x, y, z);
}
do_catchsql_test 12.4.2 {
ALTER TABLE t2 RENAME COLUMN a TO b;
} {1 {no such column: "a"}}
do_catchsql_test 12.5.1 {
ALTER TABLE t3 RENAME COLUMN a TO b;
} {1 {no such table: t3}}
#-------------------------------------------------------------------------
# Test the effect of some parse/resolve errors.
#
reset_db
do_execsql_test 13.1.1 {
CREATE TABLE x1(i INTEGER, t TEXT UNIQUE);
CREATE TRIGGER tr1 AFTER INSERT ON x1 BEGIN
SELECT * FROM nosuchtable;
END;
}
do_catchsql_test 13.1.2 {
ALTER TABLE x1 RENAME COLUMN t TO ttt;
} {1 {error in trigger tr1: no such table: main.nosuchtable}}
do_execsql_test 13.1.3 {
DROP TRIGGER tr1;
CREATE INDEX x1i ON x1(i);
SELECT sql FROM sqlite_master WHERE name='x1i';
} {{CREATE INDEX x1i ON x1(i)}}
sqlite3_db_config db DEFENSIVE 0
do_execsql_test 13.1.4 {
PRAGMA writable_schema = ON;
UPDATE sqlite_master SET sql = 'CREATE INDEX x1i ON x1(j)' WHERE name='x1i';
PRAGMA writable_schema = OFF;
} {}
do_catchsql_test 13.1.5 {
ALTER TABLE x1 RENAME COLUMN t TO ttt;
} {1 {error in index x1i: no such column: j}}
do_execsql_test 13.1.6 {
PRAGMA writable_schema = ON;
UPDATE sqlite_master SET sql = '' WHERE name='x1i';
PRAGMA writable_schema = OFF;
} {}
do_catchsql_test 13.1.7 {
ALTER TABLE x1 RENAME COLUMN t TO ttt;
} {1 {error in index x1i: }}
do_execsql_test 13.1.8 {
PRAGMA writable_schema = ON;
DELETE FROM sqlite_master WHERE name = 'x1i';
PRAGMA writable_schema = OFF;
}
do_execsql_test 13.2.0 {
CREATE TABLE data(x UNIQUE, y, z);
}
foreach {tn trigger error} {
1 {
CREATE TRIGGER tr1 AFTER INSERT ON x1 BEGIN
UPDATE data SET x=x+1 WHERE zzz=new.i;
END;
} {no such column: zzz}
2 {
CREATE TRIGGER tr1 AFTER INSERT ON x1 BEGIN
INSERT INTO data(x, y) VALUES(new.i, new.t, 1)
ON CONFLICT (x) DO UPDATE SET z=zz+1;
END;
} {no such column: zz}
3 {
CREATE TRIGGER tr1 AFTER INSERT ON x1 BEGIN
INSERT INTO x1(i, t) VALUES(new.i+1, new.t||'1')
ON CONFLICT (tttttt) DO UPDATE SET t=i+1;
END;
} {no such column: tttttt}
4 {
CREATE TRIGGER tr1 AFTER INSERT ON x1 BEGIN
INSERT INTO nosuchtable VALUES(new.i, new.t);
END;
} {no such table: main.nosuchtable}
} {
do_execsql_test 13.2.$tn.1 "
DROP TRIGGER IF EXISTS tr1;
$trigger
"
do_catchsql_test 13.2.$tn.2 {
ALTER TABLE x1 RENAME COLUMN t TO ttt;
} "1 {error in trigger tr1: $error}"
}
#-------------------------------------------------------------------------
# Passing invalid parameters directly to sqlite_rename_column().
#
sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS db
do_execsql_test 14.1 {
CREATE TABLE ddd(sql, type, object, db, tbl, icol, znew, bquote);
INSERT INTO ddd VALUES(
'CREATE TABLE x1(i INTEGER, t TEXT)',
'table', 'x1', 'main', 'x1', -1, 'zzz', 0
), (
'CREATE TABLE x1(i INTEGER, t TEXT)',
'table', 'x1', 'main', 'x1', 2, 'zzz', 0
), (
'CREATE TABLE x1(i INTEGER, t TEXT)',
'table', 'x1', 'main', 'notable', 0, 'zzz', 0
), (
'CREATE TABLE x1(i INTEGER, t TEXT)',
'table', 'x1', 'main', 'ddd', -1, 'zzz', 0
);
} {}
do_execsql_test 14.2 {
SELECT
sqlite_rename_column(sql, type, object, db, tbl, icol, znew, bquote, 0)
FROM ddd;
} {{} {} {} {}}
sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS db
# If the INTERNAL_FUNCTIONS test-control is disabled (which is the default)
# then the sqlite_rename_table() SQL function is not accessible to
# ordinary SQL.
#
do_catchsql_test 14.3 {
SELECT sqlite_rename_column(0,0,0,0,0,0,0,0,0);
} {1 {no such function: sqlite_rename_column}}
#-------------------------------------------------------------------------
#
reset_db
do_execsql_test 15.0 {
CREATE TABLE xxx(a, b, c);
SELECT a AS d FROM xxx WHERE d=0;
}
do_execsql_test 15.1 {
CREATE VIEW vvv AS SELECT a AS d FROM xxx WHERE d=0;
ALTER TABLE xxx RENAME a TO xyz;
}
do_execsql_test 15.2 {
SELECT sql FROM sqlite_master WHERE type='view';
} {{CREATE VIEW vvv AS SELECT xyz AS d FROM xxx WHERE d=0}}
#-------------------------------------------------------------------------
#
do_execsql_test 16.1.0 {
CREATE TABLE t1(a,b,c);
CREATE TABLE t2(d,e,f);
INSERT INTO t1 VALUES(1,2,3);
INSERT INTO t2 VALUES(4,5,6);
CREATE VIEW v4 AS SELECT a, d FROM t1, t2;
SELECT * FROM v4;
} {1 4}
do_catchsql_test 16.1.1 {
ALTER TABLE t2 RENAME d TO a;
} {1 {error in view v4 after rename: ambiguous column name: a}}
do_execsql_test 16.1.2 {
SELECT * FROM v4;
} {1 4}
do_execsql_test 16.1.3 {
CREATE UNIQUE INDEX t2d ON t2(d);
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
INSERT INTO t2 VALUES(new.a, new.b, new.c)
ON CONFLICT(d) DO UPDATE SET f = excluded.f;
END;
}
do_execsql_test 16.1.4 {
INSERT INTO t1 VALUES(4, 8, 456);
SELECT * FROM t2;
} {4 5 456}
do_execsql_test 16.1.5 {
ALTER TABLE t2 RENAME COLUMN f TO "big f";
INSERT INTO t1 VALUES(4, 0, 20456);
SELECT * FROM t2;
} {4 5 20456}
do_execsql_test 16.1.6 {
ALTER TABLE t1 RENAME COLUMN c TO "big c";
INSERT INTO t1 VALUES(4, 0, 0);
SELECT * FROM t2;
} {4 5 0}
do_execsql_test 16.2.1 {
CREATE VIEW temp.v5 AS SELECT "big c" FROM t1;
SELECT * FROM v5;
} {3 456 20456 0}
do_execsql_test 16.2.2 {
ALTER TABLE t1 RENAME COLUMN "big c" TO reallybigc;
} {}
do_execsql_test 16.2.3 {
SELECT * FROM v5;
} {3 456 20456 0}
#-------------------------------------------------------------------------
#
do_execsql_test 17.0 {
CREATE TABLE u7(x, y, z);
CREATE TRIGGER u7t AFTER INSERT ON u7 BEGIN
INSERT INTO u8 VALUES(new.x, new.y, new.z);
END;
} {}
do_catchsql_test 17.1 {
ALTER TABLE u7 RENAME x TO xxx;
} {1 {error in trigger u7t: no such table: main.u8}}
do_execsql_test 17.2 {
CREATE TEMP TABLE uu7(x, y, z);
CREATE TRIGGER uu7t AFTER INSERT ON uu7 BEGIN
INSERT INTO u8 VALUES(new.x, new.y, new.z);
END;
} {}
do_catchsql_test 17.3 {
ALTER TABLE uu7 RENAME x TO xxx;
} {1 {error in trigger uu7t: no such table: u8}}
reset_db
forcedelete test.db2
do_execsql_test 18.0 {
ATTACH 'test.db2' AS aux;
CREATE TABLE t1(a);
CREATE TABLE aux.log(v);
CREATE TEMP TRIGGER tr1 AFTER INSERT ON t1 BEGIN
INSERT INTO log VALUES(new.a);
END;
INSERT INTO t1 VALUES(111);
SELECT v FROM log;
} {111}
do_execsql_test 18.1 {
ALTER TABLE t1 RENAME a TO b;
}
reset_db
do_execsql_test 19.0 {
CREATE TABLE t1(a, b);
CREATE TABLE t2(c, d);
CREATE VIEW v2(e) AS SELECT coalesce(t2.c,t1.a) FROM t1, t2 WHERE t1.b=t2.d;
}
do_execsql_test 19.1 {
ALTER TABLE t1 RENAME a TO f;
SELECT sql FROM sqlite_master WHERE name = 'v2';
} {
{CREATE VIEW v2(e) AS SELECT coalesce(t2.c,t1.f) FROM t1, t2 WHERE t1.b=t2.d}
}
# 2019-01-08: https://www.sqlite.org/src/tktview/bc8d94f0fbd633fd9a051e3
#
# ALTER TABLE RENAME COLUMN does not work for tables that have redundant
# UNIQUE constraints.
#
sqlite3 db :memory:
do_execsql_test 20.100 {
CREATE TABLE t1(aaa,b,c,UNIQUE(aaA),PRIMARY KEY(aAa),UNIQUE(aAA));
ALTER TABLE t1 RENAME aaa TO bbb;
SELECT sql FROM sqlite_master WHERE name='t1';
} {{CREATE TABLE t1(bbb,b,c,UNIQUE(bbb),PRIMARY KEY(bbb),UNIQUE(bbb))}}
do_execsql_test 20.105 {
DROP TABLE t1;
CREATE TABLE t1(aaa,b,c,UNIQUE(aaA),PRIMARY KEY(aAa),UNIQUE(aAA))WITHOUT ROWID;
ALTER TABLE t1 RENAME aaa TO bbb;
SELECT sql FROM sqlite_master WHERE name='t1';
} {{CREATE TABLE t1(bbb,b,c,UNIQUE(bbb),PRIMARY KEY(bbb),UNIQUE(bbb))WITHOUT ROWID}}
do_execsql_test 20.110 {
DROP TABLE t1;
CREATE TABLE t1(aa UNIQUE,bb UNIQUE,cc UNIQUE,UNIQUE(aA),PRIMARY KEY(bB),UNIQUE(cC));
ALTER TABLE t1 RENAME aa TO xx;
ALTER TABLE t1 RENAME bb TO yy;
ALTER TABLE t1 RENAME cc TO zz;
SELECT sql FROM sqlite_master WHERE name='t1';
} {{CREATE TABLE t1(xx UNIQUE,yy UNIQUE,zz UNIQUE,UNIQUE(xx),PRIMARY KEY(yy),UNIQUE(zz))}}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 21.0 {
CREATE TABLE t1(a, b, c NOT NULL);
CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN new.c IS NOT NULL BEGIN
SELECT c NOT NULL FROM t1;
END;
}
do_execsql_test 21.1 {
ALTER TABLE t1 RENAME c TO d;
}
do_execsql_test 21.2 {
SELECT sql FROM sqlite_schema WHERE name IS 'tr1'
} {{CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN new.d IS NOT NULL BEGIN
SELECT d NOT NULL FROM t1;
END}
}
#-------------------------------------------------------------------------
#
reset_db
do_execsql_test 22.0 {
CREATE TABLE t1(a, b);
CREATE TABLE t2(c, othername, extra AS (c + 1));
ALTER TABLE t1 RENAME a to othername;
SELECT sql FROM sqlite_schema;
} {
{CREATE TABLE t1(othername, b)}
{CREATE TABLE t2(c, othername, extra AS (c + 1))}
}
#-------------------------------------------------------------------------
#
reset_db
sqlite3_db_config db SQLITE_DBCONFIG_DQS_DDL 1
sqlite3_db_config db SQLITE_DBCONFIG_DQS_DML 1
do_execsql_test 22.0 {
CREATE TABLE t1(a, b);
CREATE INDEX x1 on t1("c"=b);
INSERT INTO t1 VALUES('a', 'a');
INSERT INTO t1 VALUES('b', 'b');
INSERT INTO t1 VALUES('c', 'c');
ALTER TABLE t1 RENAME COLUMN a TO "c";
PRAGMA integrity_check;
} {ok}
reset_db
do_execsql_test 23.0 {
CREATE TABLE t1('a'"b",c);
CREATE INDEX i1 ON t1('a');
INSERT INTO t1 VALUES(1,2), (3,4);
ALTER TABLE t1 RENAME COLUMN a TO x;
PRAGMA integrity_check;
SELECT sql FROM sqlite_schema WHERE name='t1';
} {ok {CREATE TABLE t1("x" "b",c)}}
# 2022-02-04
# Do not complain about syntax errors in the schema if
# in PRAGMA writable_schema=ON mode.
#
reset_db
do_execsql_test 23.0 {
CREATE TABLE t1(a INT, b REAL, c TEXT, d BLOB, e ANY);
CREATE INDEX t1abx ON t1(a, b, a+b) WHERE c IS NOT NULL;
CREATE VIEW t2 AS SELECT a+10, b*5.0, xyz FROM t1; -- unknown column "xyz"
CREATE TABLE schema_copy(name TEXT, sql TEXT);
INSERT INTO schema_copy(name,sql) SELECT name, sql FROM sqlite_schema WHERE sql IS NOT NULL;
} {}
do_catchsql_test 23.1 {
ALTER TABLE t1 RENAME COLUMN e TO eeee;
} {1 {error in view t2: no such column: xyz}}
do_execsql_test 23.2 {
SELECT name, sql FROM sqlite_master
EXCEPT SELECT name, sql FROM schema_copy;
} {}
do_execsql_test 23.3 {
BEGIN;
PRAGMA writable_schema=ON;
ALTER TABLE t1 RENAME COLUMN e TO eeee;
PRAGMA writable_schema=OFF;
SELECT name FROM sqlite_master
WHERE (name, sql) NOT IN (SELECT name, sql FROM schema_copy);
ROLLBACK;
} {t1}
do_execsql_test 23.10 {
DROP VIEW t2;
CREATE TRIGGER r3 AFTER INSERT ON t1 BEGIN
INSERT INTO t3(x,y) VALUES(new.a, new.b);
INSERT INTO t4(p) VALUES(new.c); -- no such table "t4"
END;
DELETE FROM schema_copy;
INSERT INTO schema_copy(name,sql) SELECT name, sql FROM sqlite_schema WHERE sql IS NOT NULL;
} {}
do_catchsql_test 23.11 {
ALTER TABLE t1 RENAME COLUMN e TO eeee;
} {1 {error in trigger r3: no such table: main.t3}}
do_execsql_test 23.12 {
SELECT name, sql FROM sqlite_master
EXCEPT SELECT name, sql FROM schema_copy;
} {}
do_execsql_test 23.13 {
BEGIN;
PRAGMA writable_schema=ON;
ALTER TABLE t1 RENAME COLUMN e TO eeee;
PRAGMA writable_schema=OFF;
SELECT name FROM sqlite_master
WHERE (name, sql) NOT IN (SELECT name, sql FROM schema_copy);
ROLLBACK;
} {t1}
do_execsql_test 23.20 {
CREATE TABLE t4(id INTEGER PRIMARY KEY, c1 INT, c2 INT);
CREATE VIEW t4v1 AS SELECT id, c1, c99 FROM t4;
DELETE FROM schema_copy;
INSERT INTO schema_copy SELECT name, sql FROM sqlite_schema;
BEGIN;
PRAGMA writable_schema=ON;
ALTER TABLE t4 RENAME to t4new;
SELECT name FROM sqlite_schema WHERE (name,sql) NOT IN (SELECT * FROM schema_copy);
ROLLBACK;
} {t4new}
finish_test

181
testdata/tcl/altercorrupt.test vendored Normal file
View file

@ -0,0 +1,181 @@
# 2019-01-11
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix altercorrupt
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
ifcapable !altertable {
finish_test
return
}
database_may_be_corrupt
#--------------------------------------------------------------------------
reset_db
do_test 1.0 {
sqlite3 db {}
db deserialize [decode_hexdb {
.open --hexdb
| size 24576 pagesize 4096 filename crash-685346d89b5e5f.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 00 00 00 00 06 .....@ ........
| 32: 00 00 63 00 00 05 f0 00 00 00 00 04 10 00 00 04 ..c.............
| 48: 00 00 00 00 00 00 0f f0 00 00 00 01 00 00 00 00 ................
| 64: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
| 96: 00 00 00 00 0d 0f f8 00 05 0e cf 00 0f 79 0f d3 .............y..
| 112: 0f 2e 0e f3 0e cf 00 00 00 00 00 00 00 00 00 00 ................
| 3776: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 22 ................
| 3792: 05 06 17 11 11 01 31 74 61 62 6c 65 74 34 74 34 ......1tablet4t4
| 3808: 06 43 52 45 41 54 45 20 54 41 42 4c 45 20 74 34 .CREATE TABLE t4
| 3824: 28 7a 29 39 04 06 17 11 11 01 5f 74 61 62 6c 65 (z)9......_table
| 3840: 74 33 74 33 05 43 52 45 41 54 45 20 54 41 42 4c t3t3.CREATE TABL
| 3856: 45 20 74 33 28 78 20 49 4e 54 45 47 45 52 20 50 E t3(x INTEGER P
| 3872: 52 49 4d 41 52 59 20 4b 45 59 2c 20 79 29 49 03 RIMARY KEY, y)I.
| 3888: 06 17 11 11 01 7f 74 61 62 6c 65 74 32 74 32 04 ......tablet2t2.
| 3904: 43 52 45 41 54 45 20 54 41 42 4c 45 20 74 32 28 CREATE TABLE t2(
| 3920: 61 2c 62 2c 63 20 50 52 49 4d 41 52 59 20 4b 45 a,b,c PRIMARY KE
| 3936: 59 2c 20 64 2c 20 65 2c 20 66 29 20 57 49 54 48 Y, d, e, f) WITH
| 3952: 4f 55 54 20 52 4f 57 49 44 58 03 07 17 11 11 01 OUT ROWIDX......
| 3968: 81 1b 74 61 62 6c 65 74 31 74 31 02 43 52 45 41 ..tablet1t1.CREA
| 3984: 54 45 20 54 41 42 4c 45 20 74 31 28 61 2c 62 2c TE TABLE t1(a,b,
| 4000: 63 20 41 53 20 28 2d 62 29 20 56 49 52 54 55 41 c AS (-b) VIRTUA
| 4016: 4c 2c 64 20 43 48 45 43 4b 28 64 3e 35 29 2c 65 L,d CHECK(d>5),e
| 4032: 20 55 4e 49 51 55 45 2c 20 66 20 41 53 20 28 2b UNIQUE, f AS (+
| 4048: 62 29 29 23 02 06 17 37 11 01 00 69 6e 64 65 78 b))#...7...index
| 4064: 73 71 6c 69 74 65 5f 61 75 74 6f 69 6e 64 65 78 sqlite_autoindex
| 4080: 5f 74 31 5f 31 74 31 03 00 00 00 08 00 00 00 00 _t1_1t1.........
| page 2 offset 4096
| 0: 0d 00 00 00 0a 0f 93 00 0f f6 0f eb 0f e0 0f d5 ................
| 16: 0f ca 0f 8f 0f b4 0f a9 0f 9e 0f 93 00 00 00 00 ................
| 3984: 00 00 00 09 0a 05 01 01 01 01 0a 64 6e 14 09 09 ...........dn...
| 4000: 05 01 01 01 01 09 5a 6d 12 09 08 05 01 01 01 01 ......Zm........
| 4016: 08 50 6c 10 09 07 05 01 01 01 01 07 46 6b 0e 09 .Pl.........Fk..
| 4032: 06 05 01 01 01 01 06 3c 6a 0c 09 05 05 01 01 01 .......<j.......
| 4048: 01 05 32 69 0a 09 04 05 01 01 01 01 04 28 68 08 ..2i.........(h.
| 4064: 09 03 05 01 01 01 01 03 1e 67 06 09 02 05 01 01 .........g......
| 4080: 01 01 02 14 66 04 08 01 05 09 01 01 01 0a 65 02 ....f.........e.
| page 3 offset 8192
| 0: 0a 00 00 00 0a 0f c5 00 0f fb 0f f5 0f ef 0f e9 ................
| 16: 0f e3 0f dd 0f d7 0f d1 0f cb 0f c5 00 00 00 00 ................
| 4032: 00 00 00 00 00 05 03 01 01 14 0a 05 03 01 01 12 ................
| 4048: 09 05 03 01 01 10 08 05 03 01 01 0e 07 05 03 01 ................
| 4064: 01 0c 06 05 03 01 01 0a 05 05 03 01 01 08 04 05 ................
| 4080: 03 01 01 06 03 05 03 01 01 04 02 04 03 01 09 02 ................
| page 4 offset 12288
| 0: 0a 00 00 00 0a 0f 75 00 0f 75 0f 83 0f 91 0f 9f ......u..u......
| 16: 0f ad 0f bb 0f 00 00 00 00 00 00 00 00 00 00 00 ................
| 3952: 00 00 00 00 00 0d 07 01 01 01 01 01 01 9c 0a 64 ...............d
| 3968: 6e 14 64 0d 07 02 01 01 01 01 01 a6 09 5a 6d 12 n.d..........Zm.
| 3984: 5a 0d 07 01 01 01 01 01 01 b0 08 50 6c 10 50 0d Z..........Pl.P.
| 4000: 07 01 01 01 01 01 01 ba 07 46 6b 0e 46 0d 07 01 .........Fk.F...
| 4016: 01 01 01 01 01 c4 06 3c 6a 0c 3c 0d 07 01 01 01 .......<j.<.....
| 4032: 01 01 01 ce 05 32 69 0a 32 0d 07 01 01 01 01 01 .....2i.2.......
| 4048: 01 d8 04 28 68 08 28 0d 07 01 01 01 01 01 01 e2 ...(h.(.........
| 4064: 03 1e 67 06 1e 0d 07 01 01 01 01 01 01 ec 02 14 ..g.............
| 4080: 66 04 14 0c 07 01 09 01 01 01 01 f6 0a 65 02 0a f............e..
| page 5 offset 16384
| 0: 0d 00 00 00 03 0f e9 00 0f e9 0f fb 0f f6 00 00 ................
| 16: 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
| 4064: 00 00 00 00 00 00 00 00 00 03 ff ff ff ff ff ff ................
| 4080: ff ff 9c 03 00 00 03 64 03 00 00 03 01 03 00 00 .......d........
| page 6 offset 20480
| 0: 0d 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 ................
| end crash-685346d89b5e5f.db
}]} {}
do_catchsql_test 1.1 {
ALTER TABLE t2 DROP COLUMN e;
ALTER TABLE t1 DROP COLUMN f;
} {1 {database disk image is malformed}}
#--------------------------------------------------------------------------
reset_db
do_test 2.0 {
sqlite3 db {}
db deserialize [decode_hexdb {
.open --hexdb
| size 24576 pagesize 4096 filename crash-0572db8f391431.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 00 00 00 00 06 .....@ ........
| 32: 00 00 63 00 10 05 f0 00 00 00 00 04 10 00 00 04 ..c.............
| 48: 00 00 00 00 00 00 0f f0 00 00 00 00 00 00 00 00 ................
| 96: 00 00 00 00 0d 0f f8 00 05 0e cf 00 0f 79 0f d3 .............y..
| 112: 0f 2e 0e f3 0e cf 00 00 00 00 00 00 00 00 00 00 ................
| 3776: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 22 ................
| 3792: 05 06 17 11 11 01 31 74 61 62 6c 65 74 34 74 34 ......1tablet4t4
| 3808: 06 43 52 45 41 54 45 20 54 41 42 4c 45 20 74 34 .CREATE TABLE t4
| 3824: 28 7a 29 39 04 06 17 11 11 01 5f 74 61 62 6c 65 (z)9......_table
| 3840: 74 33 74 33 05 43 52 45 41 54 45 20 54 41 42 4c t3t3.CREATE TABL
| 3856: 45 20 74 33 28 78 20 49 4e 54 55 47 45 52 20 50 E t3(x INTUGER P
| 3872: 52 49 4d 41 52 59 20 4b 45 59 2c 20 79 29 49 03 RIMARY KEY, y)I.
| 3888: 06 17 11 11 01 7f 74 61 62 6c 65 74 32 74 32 04 ......tablet2t2.
| 3904: 43 52 45 41 54 45 20 54 41 42 4c 45 20 74 32 28 CREATE TABLE t2(
| 3920: 61 2c 62 2c 63 20 50 52 49 4d 41 52 59 20 4b 45 a,b,c PRIMARY KE
| 3936: 59 2c 20 64 2c 20 65 2c 20 66 29 20 57 49 54 48 Y, d, e, f) WITH
| 3952: 4f 55 54 20 52 4f 57 49 44 58 05 07 17 11 11 01 OUT ROWIDX......
| 3968: 81 1b 74 61 62 6c 65 74 31 74 31 02 43 52 45 41 ..tablet1t1.CREA
| 3984: 54 45 20 54 41 42 4c 45 20 74 31 28 61 2c 62 2c TE TABLE t1(a,b,
| 4000: 63 20 41 53 20 28 2d 62 29 20 56 49 52 54 55 41 c AS (-b) VIRTUA
| 4016: 4c 2c 64 20 43 48 45 43 4b 28 64 3e 35 29 2c 65 L,d CHECK(d>5),e
| 4032: 20 55 4e 49 51 55 45 2c 20 66 20 41 53 20 28 2b UNIQUE, f AS (+
| 4048: 62 29 29 23 02 06 17 37 11 01 00 69 6e 64 65 78 b))#...7...index
| 4064: 73 71 6c 69 74 65 5f 61 75 74 6f 69 6e 64 65 78 sqlite_autoindex
| 4080: 5f 74 31 5f 31 84 31 03 01 00 00 08 00 00 00 00 _t1_1.1.........
| page 2 offset 4096
| 0: 0d 00 00 00 0a 0f 93 00 0f f6 0f eb 0f e0 0f d5 ................
| 16: 0f ca 0f 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
| 3984: 00 00 00 09 0a 05 01 01 01 01 0a 64 6e 14 09 09 ...........dn...
| 4000: 05 01 01 01 01 09 5a 6d 12 09 08 05 01 00 f1 01 ......Zm........
| 4016: 08 50 6c 10 09 07 05 01 01 01 01 07 46 6b 0e 09 .Pl.........Fk..
| 4032: 06 05 01 00 f1 01 06 3c 6a 0c 09 05 05 01 01 01 .......<j.......
| 4048: 01 05 32 69 0a 09 04 05 01 01 01 01 04 28 68 08 ..2i.........(h.
| 4064: 57 03 05 01 01 01 01 03 1e 67 06 09 02 05 01 01 W........g......
| 4080: 01 01 02 14 66 04 08 01 05 09 01 01 01 0a 65 02 ....f.........e.
| page 3 offset 8192
| 0: 09 ff ff ff fa 0f c5 00 0f fb 0f f5 0f ef 0f e9 ................
| 16: 0f e3 0f dd 0f d7 00 00 00 00 00 00 00 00 00 00 ................
| 4032: 00 00 00 00 00 05 03 01 01 14 0a 05 03 01 01 12 ................
| 4048: 09 05 03 01 01 10 08 05 03 01 01 0e 07 05 03 01 ................
| 4064: 01 0c 06 05 03 01 01 0a 05 05 03 01 01 08 04 05 ................
| 4080: 03 01 01 06 03 05 03 01 01 04 02 04 03 01 09 02 ................
| page 4 offset 12288
| 0: 0a 00 00 00 0a 0f 75 00 0f 75 0f 83 0f 91 0f 9f ......u..u......
| 16: 0f ad 0f bb 0f 00 00 00 00 00 01 00 00 00 00 00 ................
| 3952: 00 00 00 00 00 0d 07 01 01 01 01 01 01 9c 0a 64 ...............d
| 3968: 6e 14 64 0d 07 02 01 01 01 01 01 a6 09 5a 6d 12 n.d..........Zm.
| 3984: 5a 0d 07 01 01 01 01 d4 01 b0 08 50 6c 10 50 0d Z..........Pl.P.
| 4000: 07 01 01 01 01 01 01 ba 07 46 6b 0e 46 0d 07 00 .........Fk.F...
| 4016: 01 01 01 01 01 c4 06 3c 6a 0c 3c 0d 07 01 01 01 .......<j.<.....
| 4032: 01 01 01 ce 05 32 69 0a 32 0d 07 01 01 01 01 01 .....2i.2.......
| 4048: 01 d8 04 28 68 08 28 0d 07 01 01 01 01 01 01 e2 ...(h.(.........
| 4064: 03 1e 67 06 1e 0d 07 01 01 01 01 01 01 ec 02 14 ..g.............
| 4080: 66 04 14 0c 07 01 09 01 01 00 f1 f6 0a 65 02 0a f............e..
| page 5 offset 16384
| 0: 0d 00 00 00 03 0f e9 00 0f e9 0f fb 0f f6 00 00 ................
| 4064: 00 00 00 00 00 00 00 00 00 03 ff ff ff ff ff ff ................
| 4080: ff ff 9c 03 00 00 03 64 03 00 01 03 01 03 00 00 .......d........
| page 6 offset 20480
| 0: 0d 00 10 00 00 10 01 00 00 00 00 00 00 00 00 00 ................
| end crash-0572db8f391431.db
}]} {}
do_catchsql_test 2.1 {
ALTER TABLE t1 DROP COLUMN a;
} {1 {database disk image is malformed}}
finish_test

339
testdata/tcl/alterdropcol.test vendored Normal file
View file

@ -0,0 +1,339 @@
# 2021 February 16
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#*************************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix alterdropcol
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
ifcapable !altertable {
finish_test
return
}
do_execsql_test 1.0 {
CREATE TABLE t1(a, b, c);
CREATE VIEW v1 AS SELECT * FROM t1;
CREATE TABLE t2(x INTEGER PRIMARY KEY, y, z UNIQUE);
CREATE INDEX t2y ON t2(y);
CREATE TABLE t3(q, r, s);
CREATE INDEX t3rs ON t3(r+s);
}
do_catchsql_test 1.1 {
ALTER TABLE nosuch DROP COLUMN z;
} {1 {no such table: nosuch}}
do_catchsql_test 1.2 {
ALTER TABLE v1 DROP COLUMN c;
} {1 {cannot drop column from view "v1"}}
ifcapable fts5 {
do_execsql_test 1.3.1 {
CREATE VIRTUAL TABLE ft1 USING fts5(one, two);
}
do_catchsql_test 1.3.2 {
ALTER TABLE ft1 DROP COLUMN two;
} {1 {cannot drop column from virtual table "ft1"}}
}
do_catchsql_test 1.4 {
ALTER TABLE sqlite_schema DROP COLUMN sql;
} {1 {table sqlite_master may not be altered}}
do_catchsql_test 1.5 {
ALTER TABLE t1 DROP COLUMN d;
} {1 {no such column: "d"}}
do_execsql_test 1.6.1 {
ALTER TABLE t1 DROP COLUMN b;
}
do_execsql_test 1.6.2 {
SELECT sql FROM sqlite_schema WHERE name = 't1'
} {{CREATE TABLE t1(a, c)}}
do_execsql_test 1.7.1 {
ALTER TABLE t1 DROP COLUMN c;
}
do_execsql_test 1.7.2 {
SELECT sql FROM sqlite_schema WHERE name = 't1'
} {{CREATE TABLE t1(a)}}
do_catchsql_test 1.7.3 {
ALTER TABLE t1 DROP COLUMN a;
} {1 {cannot drop column "a": no other columns exist}}
do_catchsql_test 1.8 {
ALTER TABLE t2 DROP COLUMN z
} {1 {cannot drop UNIQUE column: "z"}}
do_catchsql_test 1.9 {
ALTER TABLE t2 DROP COLUMN x
} {1 {cannot drop PRIMARY KEY column: "x"}}
do_catchsql_test 1.10 {
ALTER TABLE t2 DROP COLUMN y
} {1 {error in index t2y after drop column: no such column: y}}
do_catchsql_test 1.11 {
ALTER TABLE t3 DROP COLUMN s
} {1 {error in index t3rs after drop column: no such column: s}}
#-------------------------------------------------------------------------
foreach {tn wo} {
1 {}
2 {WITHOUT ROWID}
} { eval [string map [list %TN% $tn %WO% $wo] {
reset_db
do_execsql_test 2.%TN%.0 {
CREATE TABLE t1(x, y INTEGER PRIMARY KEY, z) %WO% ;
INSERT INTO t1 VALUES(1, 2, 3);
INSERT INTO t1 VALUES(4, 5, 6);
INSERT INTO t1 VALUES(7, 8, 9);
}
do_execsql_test 2.%TN%.1 {
ALTER TABLE t1 DROP COLUMN x;
SELECT * FROM t1;
} {
2 3 5 6 8 9
}
do_execsql_test 2.%TN%.2 {
ALTER TABLE t1 DROP COLUMN z;
SELECT * FROM t1;
} {
2 5 8
}
}]}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 3.0 {
CREATE TABLE t12(a, b, c, CHECK(c>10));
CREATE TABLE t13(a, b, c CHECK(c>10));
}
do_catchsql_test 3.1 {
ALTER TABLE t12 DROP COLUMN c;
} {1 {error in table t12 after drop column: no such column: c}}
do_catchsql_test 3.2 {
ALTER TABLE t13 DROP COLUMN c;
} {0 {}}
#-------------------------------------------------------------------------
# Test that generated columns can be dropped. And that other columns from
# tables that contain generated columns can be dropped.
#
foreach {tn wo vs} {
1 "" ""
2 "" VIRTUAL
3 "" STORED
4 "WITHOUT ROWID" STORED
5 "WITHOUT ROWID" VIRTUAL
} {
reset_db
do_execsql_test 4.$tn.0 "
CREATE TABLE 'my table'(a, b PRIMARY KEY, c AS (a+b) $vs, d) $wo
"
do_execsql_test 4.$tn.1 {
INSERT INTO "my table"(a, b, d) VALUES(1, 2, 'hello');
INSERT INTO "my table"(a, b, d) VALUES(3, 4, 'world');
SELECT * FROM "my table"
} {
1 2 3 hello
3 4 7 world
}
do_execsql_test 4.$tn.2 {
ALTER TABLE "my table" DROP COLUMN c;
}
do_execsql_test 4.$tn.3 {
SELECT * FROM "my table"
} {
1 2 hello
3 4 world
}
do_execsql_test 4.$tn.4 "
CREATE TABLE x1(a, b, c PRIMARY KEY, d AS (b+c) $vs, e) $wo
"
do_execsql_test 4.$tn.5 {
INSERT INTO x1(a, b, c, e) VALUES(1, 2, 3, 4);
INSERT INTO x1(a, b, c, e) VALUES(5, 6, 7, 8);
INSERT INTO x1(a, b, c, e) VALUES(9, 10, 11, 12);
SELECT * FROM x1;
} {
1 2 3 5 4
5 6 7 13 8
9 10 11 21 12
}
do_execsql_test 4.$tn.6 {
ALTER TABLE x1 DROP COLUMN a
}
do_execsql_test 4.$tn.7 {
SELECT * FROM x1
} {
2 3 5 4
6 7 13 8
10 11 21 12
}
do_execsql_test 4.$tn.8 {
ALTER TABLE x1 DROP COLUMN e
}
do_execsql_test 4.$tn.9 {
SELECT * FROM x1
} {
2 3 5
6 7 13
10 11 21
}
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 5.0 {
CREATE TABLE p1(a PRIMARY KEY, b UNIQUE);
CREATE TABLE c1(x, y, z REFERENCES p1(c));
CREATE TABLE c2(x, y, z, w REFERENCES p1(b));
}
do_execsql_test 5.1 {
ALTER TABLE c1 DROP COLUMN z;
ALTER TABLE c2 DROP COLUMN z;
SELECT sql FROM sqlite_schema WHERE name IN ('c1', 'c2');
} {
{CREATE TABLE c1(x, y)}
{CREATE TABLE c2(x, y, w REFERENCES p1(b))}
}
do_execsql_test 5.2.1 {
CREATE VIEW v1 AS SELECT d, e FROM p1
}
do_catchsql_test 5.2.2 {
ALTER TABLE c1 DROP COLUMN x
} {1 {error in view v1: no such column: d}}
do_execsql_test 5.3.1 {
DROP VIEW v1;
CREATE VIEW v1 AS SELECT x, y FROM c1;
}
do_catchsql_test 5.3.2 {
ALTER TABLE c1 DROP COLUMN x
} {1 {error in view v1 after drop column: no such column: x}}
do_execsql_test 5.4.1 {
CREATE TRIGGER tr AFTER INSERT ON c1 BEGIN
INSERT INTO p1 VALUES(new.y, new.xyz);
END;
}
do_catchsql_test 5.4.2 {
ALTER TABLE c1 DROP COLUMN y
} {1 {error in trigger tr: no such column: new.xyz}}
do_execsql_test 5.5.1 {
DROP TRIGGER tr;
CREATE TRIGGER tr AFTER INSERT ON c1 BEGIN
INSERT INTO p1 VALUES(new.y, new.z);
END;
}
do_catchsql_test 5.5.2 {
ALTER TABLE c1 DROP COLUMN y
} {1 {error in trigger tr: no such column: new.z}}
# 2021-03-06 dbsqlfuzz crash-419aa525df93db6e463772c686ac6da27b46da9e
reset_db
do_catchsql_test 6.0 {
CREATE TABLE t1(a,b,c);
CREATE TABLE t2(x,y,z);
PRAGMA writable_schema=ON;
UPDATE sqlite_schema SET sql='CREATE INDEX t1b ON t1(b)' WHERE name='t2';
PRAGMA writable_schema=OFF;
ALTER TABLE t2 DROP COLUMN z;
} {1 {database disk image is malformed}}
reset_db
do_catchsql_test 6.1 {
CREATE TABLE t1(a,b,c);
CREATE TABLE t2(x,y,z);
PRAGMA writable_schema=ON;
UPDATE sqlite_schema SET sql='CREATE VIEW t2(x,y,z) AS SELECT b,a,c FROM t1'
WHERE name='t2';
PRAGMA writable_schema=OFF;
ALTER TABLE t2 DROP COLUMN z;
} {1 {database disk image is malformed}}
# 2021-04-06 dbsqlfuzz crash-331c5c29bb76257b198f1318eef3288f9624c8ce
reset_db
do_execsql_test 7.0 {
CREATE TABLE t1(a, b, c, PRIMARY KEY(a COLLATE nocase, a)) WITHOUT ROWID;
INSERT INTO t1 VALUES(1, 2, 3);
INSERT INTO t1 VALUES(4, 5, 6);
}
do_execsql_test 7.1 {
ALTER TABLE t1 DROP COLUMN c;
}
do_execsql_test 7.2 {
SELECT sql FROM sqlite_schema;
} {{CREATE TABLE t1(a, b, PRIMARY KEY(a COLLATE nocase, a)) WITHOUT ROWID}}
do_execsql_test 7.3 {
SELECT * FROM t1;
} {1 2 4 5}
reset_db
do_execsql_test 8.0 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
PRAGMA writable_schema = 1;
UPDATE sqlite_schema
SET sql = 'CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT, b)'
}
db close
sqlite3 db test.db
do_execsql_test 8.1 {
ALTER TABLE t1 DROP COLUMN b;
}
do_execsql_test 8.2 {
SELECT sql FROM sqlite_schema;
} {{CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT)}}
#-------------------------------------------------------------------------
foreach {tn wo} {
1 {}
2 {WITHOUT ROWID}
} {
reset_db
do_execsql_test 9.$tn.0 "
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c) $wo;
"
do_execsql_test 9.$tn.1 {
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<50000
)
INSERT INTO t1(a, b, c) SELECT i, 123, 456 FROM s;
}
do_execsql_test 9.$tn.2 {
ALTER TABLE t1 DROP COLUMN b;
}
do_execsql_test 9.$tn.3 {
SELECT count(*), c FROM t1 GROUP BY c;
} {50000 456}
}
finish_test

222
testdata/tcl/alterdropcol2.test vendored Normal file
View file

@ -0,0 +1,222 @@
# 2021 February 19
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#*************************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix alterdropcol2
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
ifcapable !altertable {
finish_test
return
}
# EVIDENCE-OF: R-58318-35349 The DROP COLUMN syntax is used to remove an
# existing column from a table.
do_execsql_test 1.0 {
CREATE TABLE t1(c, b, a, PRIMARY KEY(b, a)) WITHOUT ROWID;
INSERT INTO t1 VALUES(1, 2, 3), (4, 5, 6);
}
do_execsql_test 1.1 {
ALTER TABLE t1 DROP c;
}
# EVIDENCE-OF: The DROP COLUMN command removes the named column from the table,
# and also rewrites the entire table to purge the data associated with that
# column.
do_execsql_test 1.2.1 {
SELECT * FROM t1;
} {2 3 5 6}
do_execsql_test 1.2.2 {
SELECT sql FROM sqlite_schema;
} {
{CREATE TABLE t1(b, a, PRIMARY KEY(b, a)) WITHOUT ROWID}
}
proc do_atdc_error_test {tn schema atdc error} {
reset_db
execsql $schema
uplevel [list do_catchsql_test $tn $atdc [list 1 [string trim $error]]]
}
#-------------------------------------------------------------------------
# Test cases 2.* attempt to verify the following:
#
# EVIDENCE-OF: R-24098-10282 The DROP COLUMN command only works if the column
# is not referenced by any other parts of the schema and is not a PRIMARY KEY
# and does not have a UNIQUE constraint.
#
# EVIDENCE-OF: R-52436-31752 The column is a PRIMARY KEY or part of one.
#
do_atdc_error_test 2.1.1 {
CREATE TABLE x1(a PRIMARY KEY, b, c);
} {
ALTER TABLE x1 DROP COLUMN a
} {
cannot drop PRIMARY KEY column: "a"
}
do_atdc_error_test 2.1.2 {
CREATE TABLE x1(a,b,c,d,e, PRIMARY KEY(b,c,d));
} {
ALTER TABLE x1 DROP COLUMN c
} {
cannot drop PRIMARY KEY column: "c"
}
# EVIDENCE-OF: R-43412-16016 The column has a UNIQUE constraint.
#
do_atdc_error_test 2.2.1 {
CREATE TABLE x1(a PRIMARY KEY, b, c UNIQUE);
} {
ALTER TABLE x1 DROP COLUMN c
} {
cannot drop UNIQUE column: "c"
}
do_atdc_error_test 2.2.2 {
CREATE TABLE x1(a PRIMARY KEY, b, c, UNIQUE(b, c));
} {
ALTER TABLE x1 DROP COLUMN c
} {
error in table x1 after drop column: no such column: c
}
# EVIDENCE-OF: R-46731-08965 The column is indexed.
#
do_atdc_error_test 2.3.1 {
CREATE TABLE 'one two'('x y', 'z 1', 'a b');
CREATE INDEX idx ON 'one two'('z 1');
} {
ALTER TABLE 'one two' DROP COLUMN 'z 1'
} {
error in index idx after drop column: no such column: z 1
}
do_atdc_error_test 2.3.2 {
CREATE TABLE x1(a, b, c);
CREATE INDEX idx ON x1(a);
} {
ALTER TABLE x1 DROP COLUMN a;
} {
error in index idx after drop column: no such column: a
}
# EVIDENCE-OF: R-46731-08965 The column is indexed.
#
do_atdc_error_test 2.4.1 {
CREATE TABLE x1234(a, b, c PRIMARY KEY) WITHOUT ROWID;
CREATE INDEX i1 ON x1234(b) WHERE ((a+5) % 10)==0;
} {
ALTER TABLE x1234 DROP a
} {
error in index i1 after drop column: no such column: a
}
# EVIDENCE-OF: R-47838-03249 The column is named in a table or column
# CHECK constraint not associated with the column being dropped.
#
do_atdc_error_test 2.5.1 {
CREATE TABLE x1234(a, b, c PRIMARY KEY, CHECK(((a+5)%10)!=0)) WITHOUT ROWID;
} {
ALTER TABLE x1234 DROP a
} {
error in table x1234 after drop column: no such column: a
}
# EVIDENCE-OF: R-55640-01652 The column is used in a foreign key constraint.
#
do_atdc_error_test 2.6.1 {
CREATE TABLE p1(x, y UNIQUE);
CREATE TABLE c1(u, v, FOREIGN KEY (v) REFERENCES p1(y))
} {
ALTER TABLE c1 DROP v
} {
error in table c1 after drop column: unknown column "v" in foreign key definition
}
# EVIDENCE-OF: R-20795-39479 The column is used in the expression of a
# generated column.
do_atdc_error_test 2.7.1 {
CREATE TABLE c1(u, v, w AS (u+v));
} {
ALTER TABLE c1 DROP v
} {
error in table c1 after drop column: no such column: v
}
do_atdc_error_test 2.7.2 {
CREATE TABLE c1(u, v, w AS (u+v) STORED);
} {
ALTER TABLE c1 DROP u
} {
error in table c1 after drop column: no such column: u
}
# EVIDENCE-OF: R-01515-49025 The column appears in a trigger or view.
#
do_atdc_error_test 2.8.1 {
CREATE TABLE log(l);
CREATE TABLE c1(u, v, w);
CREATE TRIGGER tr1 AFTER INSERT ON c1 BEGIN
INSERT INTO log VALUES(new.w);
END;
} {
ALTER TABLE c1 DROP w
} {
error in trigger tr1 after drop column: no such column: new.w
}
do_atdc_error_test 2.8.2 {
CREATE TABLE c1(u, v, w);
CREATE VIEW v1 AS SELECT u, v, w FROM c1;
} {
ALTER TABLE c1 DROP w
} {
error in view v1 after drop column: no such column: w
}
do_atdc_error_test 2.8.3 {
CREATE TABLE c1(u, v, w);
CREATE VIEW v1 AS SELECT * FROM c1 WHERE w IS NOT NULL;
} {
ALTER TABLE c1 DROP w
} {
error in view v1 after drop column: no such column: w
}
#-------------------------------------------------------------------------
# Verify that a column that is part of a CHECK constraint may be dropped
# if the CHECK constraint was specified as part of the column definition.
#
# STALE-EVIDENCE: R-60924-11170 However, the column being deleted can be used in a
# column CHECK constraint because the column CHECK constraint is dropped
# together with the column itself.
do_execsql_test 3.0 {
CREATE TABLE yyy(q, w, e CHECK (e > 0), r);
INSERT INTO yyy VALUES(1,1,1,1), (2,2,2,2);
CREATE TABLE zzz(q, w, e, r, CHECK (e > 0));
INSERT INTO zzz VALUES(1,1,1,1), (2,2,2,2);
}
do_catchsql_test 3.1.1 {
INSERT INTO yyy VALUES(0,0,0,0);
} {1 {CHECK constraint failed: e > 0}}
do_catchsql_test 3.1.2 {
INSERT INTO yyy VALUES(0,0,0,0);
} {1 {CHECK constraint failed: e > 0}}
do_execsql_test 3.2.1 {
ALTER TABLE yyy DROP e;
}
do_catchsql_test 3.2.2 {
ALTER TABLE zzz DROP e;
} {1 {error in table zzz after drop column: no such column: e}}
finish_test

41
testdata/tcl/alterfault.test vendored Normal file
View file

@ -0,0 +1,41 @@
# 2021 November 16
#
# 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.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix alterfault
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
ifcapable !altertable {
finish_test
return
}
do_execsql_test 1.0 {
CREATE TABLE t1(a);
}
faultsim_save_and_close
do_faultsim_test 1.1 -faults oom* -prep {
faultsim_restore_and_reopen
} -body {
execsql {
ALTER TABLE t1 ADD COLUMN b CHECK (a!=1)
}
} -test {
faultsim_test_result {0 {}}
}
finish_test

469
testdata/tcl/alterlegacy.test vendored Normal file
View file

@ -0,0 +1,469 @@
# 2018 September 20
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#*************************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix alterlegacy
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
ifcapable !altertable {
finish_test
return
}
do_execsql_test 1.0 {
PRAGMA legacy_alter_table = 1;
CREATE TABLE t1(a, b, CHECK(t1.a != t1.b));
CREATE TABLE t2(a, b);
CREATE INDEX t2expr ON t2(a) WHERE t2.b>0;
}
do_execsql_test 1.1 {
SELECT sql FROM sqlite_master
} {
{CREATE TABLE t1(a, b, CHECK(t1.a != t1.b))}
{CREATE TABLE t2(a, b)}
{CREATE INDEX t2expr ON t2(a) WHERE t2.b>0}
}
# Legacy behavior is to corrupt the schema in this case, as the table name in
# the CHECK constraint is incorrect after "t1" is renamed. This version is
# slightly different - it rejects the change and rolls back the transaction.
do_catchsql_test 1.2 {
ALTER TABLE t1 RENAME TO t1new;
} {1 {error in table t1new after rename: no such column: t1.a}}
do_execsql_test 1.3 {
CREATE TABLE t3(c, d);
ALTER TABLE t3 RENAME TO t3new;
DROP TABLE t3new;
}
do_execsql_test 1.4 {
SELECT sql FROM sqlite_master
} {
{CREATE TABLE t1(a, b, CHECK(t1.a != t1.b))}
{CREATE TABLE t2(a, b)}
{CREATE INDEX t2expr ON t2(a) WHERE t2.b>0}
}
do_catchsql_test 1.3 {
ALTER TABLE t2 RENAME TO t2new;
} {1 {error in index t2expr after rename: no such column: t2.b}}
do_execsql_test 1.4 {
SELECT sql FROM sqlite_master
} {
{CREATE TABLE t1(a, b, CHECK(t1.a != t1.b))}
{CREATE TABLE t2(a, b)}
{CREATE INDEX t2expr ON t2(a) WHERE t2.b>0}
}
#-------------------------------------------------------------------------
reset_db
ifcapable vtab {
register_echo_module db
do_execsql_test 2.0 {
PRAGMA legacy_alter_table = 1;
CREATE TABLE abc(a, b, c);
INSERT INTO abc VALUES(1, 2, 3);
CREATE VIRTUAL TABLE eee USING echo('abc');
SELECT * FROM eee;
} {1 2 3}
do_execsql_test 2.1 {
ALTER TABLE eee RENAME TO fff;
SELECT * FROM fff;
} {1 2 3}
db close
sqlite3 db test.db
do_catchsql_test 2.2 {
ALTER TABLE fff RENAME TO ggg;
} {1 {no such module: echo}}
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 3.0 {
PRAGMA legacy_alter_table = 1;
CREATE TABLE txx(a, b, c);
INSERT INTO txx VALUES(1, 2, 3);
CREATE VIEW vvv AS SELECT main.txx.a, txx.b, c FROM txx;
CREATE VIEW uuu AS SELECT main.one.a, one.b, c FROM txx AS one;
CREATE VIEW temp.ttt AS SELECT main.txx.a, txx.b, one.b, main.one.a FROM txx AS one, txx;
}
do_execsql_test 3.1.1 {
SELECT * FROM vvv;
} {1 2 3}
do_execsql_test 3.1.2a {
ALTER TABLE txx RENAME TO "t xx";
}
do_catchsql_test 3.1.2b {
SELECT * FROM vvv;
} {1 {no such table: main.txx}}
do_execsql_test 3.1.3 {
SELECT sql FROM sqlite_master WHERE name='vvv';
} {{CREATE VIEW vvv AS SELECT main.txx.a, txx.b, c FROM txx}}
do_catchsql_test 3.2.1 {
SELECT * FROM uuu;
} {1 {no such table: main.txx}}
do_execsql_test 3.2.2 {
SELECT sql FROM sqlite_master WHERE name='uuu';;
} {{CREATE VIEW uuu AS SELECT main.one.a, one.b, c FROM txx AS one}}
do_catchsql_test 3.3.1 {
SELECT * FROM ttt;
} {1 {no such table: txx}}
do_execsql_test 3.3.2 {
SELECT sql FROM sqlite_temp_master WHERE name='ttt';
} {{CREATE VIEW ttt AS SELECT main.txx.a, txx.b, one.b, main.one.a FROM txx AS one, txx}}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 4.0 {
PRAGMA legacy_alter_table = 1;
CREATE table t1(x, y);
CREATE table t2(a, b);
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
SELECT t1.x, * FROM t1, t2;
INSERT INTO t2 VALUES(new.x, new.y);
END;
}
do_execsql_test 4.1 {
INSERT INTO t1 VALUES(1, 1);
ALTER TABLE t1 RENAME TO t11;
}
do_catchsql_test 4.1a {
INSERT INTO t11 VALUES(2, 2);
} {1 {no such table: main.t1}}
do_execsql_test 4.1b {
ALTER TABLE t11 RENAME TO t1;
ALTER TABLE t2 RENAME TO t22;
}
do_catchsql_test 4.1c {
INSERT INTO t1 VALUES(3, 3);
} {1 {no such table: main.t2}}
proc squish {a} {
string trim [regsub -all {[[:space:]][[:space:]]*} $a { }]
}
db func squish squish
do_test 4.2 {
execsql { SELECT squish(sql) FROM sqlite_master WHERE name = 'tr1' }
} [list [squish {
CREATE TRIGGER tr1 AFTER INSERT ON "t1" BEGIN
SELECT t1.x, * FROM t1, t2;
INSERT INTO t2 VALUES(new.x, new.y);
END
}]]
#-------------------------------------------------------------------------
reset_db
do_execsql_test 5.0 {
PRAGMA legacy_alter_table = 1;
CREATE TABLE t9(a, b, c);
CREATE TABLE t10(a, b, c);
CREATE TEMP TABLE t9(a, b, c);
CREATE TRIGGER temp.t9t AFTER INSERT ON temp.t9 BEGIN
INSERT INTO t10 VALUES(new.a, new.b, new.c);
END;
INSERT INTO temp.t9 VALUES(1, 2, 3);
SELECT * FROM t10;
} {1 2 3}
do_execsql_test 5.1 {
ALTER TABLE temp.t9 RENAME TO 't1234567890'
}
do_execsql_test 5.2 {
CREATE TABLE t1(a, b);
CREATE TABLE t2(a, b);
INSERT INTO t1 VALUES(1, 2);
INSERT INTO t2 VALUES(3, 4);
CREATE VIEW v AS SELECT one.a, one.b, t2.a, t2.b FROM t1 AS one, t2;
SELECT * FROM v;
} {1 2 3 4}
do_execsql_test 5.3 {
ALTER TABLE t2 RENAME TO one;
} {}
do_catchsql_test 5.4 {
SELECT * FROM v
} {1 {no such table: main.t2}}
do_execsql_test 5.5 {
ALTER TABLE one RENAME TO t2;
DROP VIEW v;
CREATE VIEW temp.vv AS SELECT one.a, one.b, t2.a, t2.b FROM t1 AS one, t2;
SELECT * FROM vv;
} {1 2 3 4}
do_execsql_test 5.6 {
ALTER TABLE t2 RENAME TO one;
} {}
do_catchsql_test 5.7 {
SELECT * FROM vv
} {1 {no such table: t2}}
#-------------------------------------------------------------------------
ifcapable vtab {
register_tcl_module db
proc tcl_command {method args} {
switch -- $method {
xConnect {
return "CREATE TABLE t1(a, b, c)"
}
}
return {}
}
do_execsql_test 6.0 {
CREATE VIRTUAL TABLE x1 USING tcl(tcl_command);
}
do_execsql_test 6.1 {
ALTER TABLE x1 RENAME TO x2;
SELECT sql FROM sqlite_master WHERE name = 'x2'
} {{CREATE VIRTUAL TABLE "x2" USING tcl(tcl_command)}}
do_execsql_test 7.1 {
CREATE TABLE ddd(db, sql, zOld, zNew, bTemp);
INSERT INTO ddd VALUES(
'main', 'CREATE TABLE x1(i INTEGER, t TEXT)', 'ddd', NULL, 0
), (
'main', 'CREATE TABLE x1(i INTEGER, t TEXT)', NULL, 'eee', 0
), (
'main', NULL, 'ddd', 'eee', 0
);
} {}
}
#-------------------------------------------------------------------------
#
reset_db
forcedelete test.db2
do_execsql_test 8.1 {
PRAGMA legacy_alter_table = 1;
ATTACH 'test.db2' AS aux;
PRAGMA foreign_keys = on;
CREATE TABLE aux.p1(a INTEGER PRIMARY KEY, b);
CREATE TABLE aux.c1(x INTEGER PRIMARY KEY, y REFERENCES p1(a));
INSERT INTO aux.p1 VALUES(1, 1);
INSERT INTO aux.p1 VALUES(2, 2);
INSERT INTO aux.c1 VALUES(NULL, 2);
CREATE TABLE aux.c2(x INTEGER PRIMARY KEY, y REFERENCES c1(a));
}
do_execsql_test 8.2 {
ALTER TABLE aux.p1 RENAME TO ppp;
}
do_execsql_test 8.2 {
INSERT INTO aux.c1 VALUES(NULL, 1);
SELECT sql FROM aux.sqlite_master WHERE name = 'c1';
} {{CREATE TABLE c1(x INTEGER PRIMARY KEY, y REFERENCES "ppp"(a))}}
reset_db
do_execsql_test 9.0 {
PRAGMA legacy_alter_table = 1;
CREATE TABLE t1(a, b, c);
CREATE VIEW v1 AS SELECT * FROM t2;
}
do_execsql_test 9.1 {
ALTER TABLE t1 RENAME TO t3;
} {}
do_execsql_test 9.1b {
ALTER TABLE t3 RENAME TO t1;
} {}
do_execsql_test 9.2 {
DROP VIEW v1;
CREATE TRIGGER tr AFTER INSERT ON t1 BEGIN
INSERT INTO t2 VALUES(new.a);
END;
}
do_execsql_test 9.3 {
ALTER TABLE t1 RENAME TO t3;
} {}
forcedelete test.db2
do_execsql_test 9.4 {
ALTER TABLE t3 RENAME TO t1;
DROP TRIGGER tr;
ATTACH 'test.db2' AS aux;
CREATE TRIGGER tr AFTER INSERT ON t1 WHEN new.a IS NULL BEGIN SELECT 1, 2, 3; END;
CREATE TABLE aux.t1(x);
CREATE TEMP TRIGGER tr AFTER INSERT ON aux.t1 BEGIN SELECT 1, 2, 3; END;
}
do_execsql_test 9.5 {
ALTER TABLE main.t1 RENAME TO t3;
}
do_execsql_test 9.6 {
SELECT sql FROM sqlite_temp_master;
SELECT sql FROM sqlite_master WHERE type='trigger';
} {
{CREATE TRIGGER tr AFTER INSERT ON aux.t1 BEGIN SELECT 1, 2, 3; END}
{CREATE TRIGGER tr AFTER INSERT ON "t3" WHEN new.a IS NULL BEGIN SELECT 1, 2, 3; END}
}
#-------------------------------------------------------------------------
reset_db
ifcapable fts5 {
do_execsql_test 10.0 {
PRAGMA legacy_alter_table = 1;
CREATE VIRTUAL TABLE fff USING fts5(x, y, z);
}
do_execsql_test 10.1 {
BEGIN;
INSERT INTO fff VALUES('a', 'b', 'c');
ALTER TABLE fff RENAME TO ggg;
COMMIT;
}
do_execsql_test 10.2 {
SELECT * FROM ggg;
} {a b c}
}
#-------------------------------------------------------------------------
reset_db
forcedelete test.db2
db func trigger trigger
set ::trigger [list]
proc trigger {args} {
lappend ::trigger $args
}
do_execsql_test 11.0 {
PRAGMA legacy_alter_table = 1;
ATTACH 'test.db2' AS aux;
CREATE TABLE aux.t1(a, b, c);
CREATE TABLE main.t1(a, b, c);
CREATE TEMP TRIGGER tr AFTER INSERT ON aux.t1 BEGIN
SELECT trigger(new.a, new.b, new.c);
END;
}
do_execsql_test 11.1 {
INSERT INTO main.t1 VALUES(1, 2, 3);
INSERT INTO aux.t1 VALUES(4, 5, 6);
}
do_test 11.2 { set ::trigger } {{4 5 6}}
do_execsql_test 11.3 {
SELECT name, tbl_name FROM sqlite_temp_master;
} {tr t1}
do_execsql_test 11.4 {
ALTER TABLE main.t1 RENAME TO t2;
SELECT name, tbl_name FROM sqlite_temp_master;
} {tr t1}
do_execsql_test 11.5 {
ALTER TABLE aux.t1 RENAME TO t2;
SELECT name, tbl_name FROM sqlite_temp_master;
} {tr t2}
do_execsql_test 11.6 {
INSERT INTO aux.t2 VALUES(7, 8, 9);
}
do_test 11.7 { set ::trigger } {{4 5 6} {7 8 9}}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 12.0 {
PRAGMA legacy_alter_table = 1;
CREATE TABLE t1(a);
CREATE TABLE t2(w);
CREATE TRIGGER temp.r1 AFTER INSERT ON main.t2 BEGIN
INSERT INTO t1(a) VALUES(new.w);
END;
CREATE TEMP TABLE t2(x);
}
do_execsql_test 12.1 {
ALTER TABLE main.t2 RENAME TO t3;
}
do_execsql_test 12.2 {
INSERT INTO t3 VALUES('WWW');
SELECT * FROM t1;
} {WWW}
#-------------------------------------------------------------------------
reset_db
ifcapable rtree {
do_execsql_test 14.0 {
PRAGMA legacy_alter_table = 1;
CREATE VIRTUAL TABLE rt USING rtree(id, minx, maxx, miny, maxy);
CREATE TABLE "mytable" ( "fid" INTEGER PRIMARY KEY, "geom" BLOB);
CREATE TRIGGER tr1 AFTER UPDATE OF "geom" ON "mytable"
WHEN OLD."fid" = NEW."fid" AND NEW."geom" IS NULL BEGIN
DELETE FROM rt WHERE id = OLD."fid";
END;
INSERT INTO mytable VALUES(1, X'abcd');
}
do_execsql_test 14.1 {
UPDATE mytable SET geom = X'1234'
}
do_execsql_test 14.2 {
ALTER TABLE mytable RENAME TO mytable_renamed;
}
do_execsql_test 14.3 {
CREATE TRIGGER tr2 AFTER INSERT ON mytable_renamed BEGIN
DELETE FROM rt WHERE id=(SELECT min(id) FROM rt);
END;
}
do_execsql_test 14.4 {
ALTER TABLE mytable_renamed RENAME TO mytable2;
}
}
reset_db
do_execsql_test 14.5 {
PRAGMA legacy_alter_table = 1;
CREATE TABLE t1(a, b, c);
CREATE VIEW v1 AS SELECT * FROM t1;
CREATE TRIGGER xyz AFTER INSERT ON t1 BEGIN
SELECT a, b FROM v1;
END;
}
do_execsql_test 14.6 {
ALTER TABLE t1 RENAME TO tt1;
}
finish_test

71
testdata/tcl/altermalloc.test vendored Normal file
View file

@ -0,0 +1,71 @@
# 2005 September 19
#
# 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 script is testing the ALTER TABLE statement and
# specifically out-of-memory conditions within that command.
#
# $Id: altermalloc.test,v 1.10 2008/10/30 17:21:13 danielk1977 Exp $
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
ifcapable !altertable {
finish_test
return
}
source $testdir/malloc_common.tcl
do_malloc_test altermalloc-1 -tclprep {
db close
} -tclbody {
if {[catch {sqlite3 db test.db}]} {
error "out of memory"
}
sqlite3_db_config_lookaside db 0 0 0
sqlite3_extended_result_codes db 1
} -sqlbody {
CREATE TABLE t1(a int);
ALTER TABLE t1 ADD COLUMN b INTEGER DEFAULT NULL;
ALTER TABLE t1 ADD COLUMN c TEXT DEFAULT 'default-text';
ALTER TABLE t1 RENAME TO t2;
ALTER TABLE t2 ADD COLUMN d BLOB DEFAULT X'ABCD';
}
# Test malloc() failure on an ALTER TABLE on a virtual table.
#
ifcapable vtab {
do_malloc_test altermalloc-vtab -tclprep {
sqlite3 db2 test.db
sqlite3_db_config_lookaside db2 0 0 0
sqlite3_extended_result_codes db2 1
register_echo_module [sqlite3_connection_pointer db2]
db2 eval {
CREATE TABLE t1(a, b VARCHAR, c INTEGER);
CREATE VIRTUAL TABLE t1echo USING echo(t1);
}
db2 close
register_echo_module [sqlite3_connection_pointer db]
} -tclbody {
set rc [catch {db eval { ALTER TABLE t1echo RENAME TO t1_echo }} msg]
if {$msg eq "vtable constructor failed: t1echo"} {
set msg "out of memory"
}
if {$rc} {
error $msg
}
}
}
finish_test

127
testdata/tcl/altermalloc2.test vendored Normal file
View file

@ -0,0 +1,127 @@
# 2018 August 20
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#*************************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
set testprefix altermalloc2
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
ifcapable !altertable {
finish_test
return
}
do_execsql_test 1.0 {
CREATE TABLE t1(abcd, efgh);
}
faultsim_save_and_close
set ::TMPDBERROR [list 1 \
{unable to open a temporary database file for storing temporary tables}
]
do_faultsim_test 1 -prep {
faultsim_restore_and_reopen
} -body {
execsql {
ALTER TABLE t1 RENAME abcd TO dcba
}
} -test {
faultsim_test_result {0 {}} $::TMPDBERROR
}
catch {db close}
forcedelete test.db
sqlite3 db test.db
do_execsql_test 2.0 {
PRAGMA encoding = 'utf-16';
CREATE TABLE t1(abcd, efgh);
}
faultsim_save_and_close
do_faultsim_test 2 -prep {
faultsim_restore_and_reopen
} -body {
execsql {
ALTER TABLE t1 RENAME abcd TO dcba
}
} -test {
faultsim_test_result {0 {}} $::TMPDBERROR
}
reset_db
do_execsql_test 3.0 {
CREATE TABLE t1(abcd, efgh);
CREATE VIEW v1 AS SELECT * FROM t1 WHERE abcd>efgh;
}
faultsim_save_and_close
do_faultsim_test 3 -prep {
faultsim_restore_and_reopen
} -body {
execsql {
ALTER TABLE t1 RENAME abcd TO dcba
}
} -test {
faultsim_test_result {0 {}} $::TMPDBERROR
}
reset_db
do_execsql_test 4.0 {
CREATE TABLE rr(a, b);
CREATE VIEW vv AS SELECT * FROM rr;
CREATE TRIGGER vv1 INSTEAD OF INSERT ON vv BEGIN
SELECT 1, 2, 3;
END;
CREATE TRIGGER tr1 AFTER INSERT ON rr BEGIN
INSERT INTO vv VALUES(new.a, new.b);
END;
} {}
faultsim_save_and_close
do_faultsim_test 4 -faults oom-* -prep {
faultsim_restore_and_reopen
execsql { SELECT * FROM sqlite_master }
} -body {
execsql {
ALTER TABLE rr RENAME a TO c;
}
} -test {
faultsim_test_result {0 {}} $::TMPDBERROR
}
reset_db
do_execsql_test 5.0 {
CREATE TABLE rr(a, b);
CREATE VIEW vv AS SELECT * FROM (
WITH abc(d, e) AS (SELECT * FROM rr)
SELECT * FROM abc
);
} {}
faultsim_save_and_close
do_faultsim_test 5 -faults oom-* -prep {
faultsim_restore_and_reopen
execsql { SELECT * FROM sqlite_master }
} -body {
execsql {
ALTER TABLE rr RENAME TO c;
}
} -test {
faultsim_test_result {0 {}} $::TMPDBERROR
}
finish_test

89
testdata/tcl/altermalloc3.test vendored Normal file
View file

@ -0,0 +1,89 @@
# 2021 February 18
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#*************************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
set testprefix altermalloc3
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
ifcapable !altertable {
finish_test
return
}
set ::TMPDBERROR [list 1 \
{unable to open a temporary database file for storing temporary tables}
]
do_execsql_test 1.0 {
CREATE TABLE x1(
one, two, three, PRIMARY KEY(one),
CHECK (three!="xyz"), CHECK (two!="one")
) WITHOUT ROWID;
CREATE INDEX x1i ON x1(one+"two"+"four") WHERE "five";
CREATE TEMP TRIGGER AFTER INSERT ON x1 BEGIN
UPDATE x1 SET two=new.three || "new" WHERE one=new.one||"";
END;
CREATE TABLE t1(a, b, c, d, PRIMARY KEY(d, b)) WITHOUT ROWID;
INSERT INTO t1 VALUES(1, 2, 3, 4);
}
faultsim_save_and_close
do_faultsim_test 1 -prep {
faultsim_restore_and_reopen
} -body {
execsql { ALTER TABLE t1 DROP COLUMN c }
} -test {
faultsim_test_result {0 {}} $::TMPDBERROR
}
#-------------------------------------------------------------------------
# dbsqlfuzz e3dd84cda3848016a6a6024c7249d09bc2ef2615
#
reset_db
do_execsql_test 2.0 {
CREATE TABLE t2(k,v);
CREATE TRIGGER r2 AFTER INSERT ON t2 BEGIN
UPDATE t2 SET (k,v)= (
(WITH cte1(a) AS ( SELECT 1 FROM ( SELECT * FROM t2 ) )
SELECT a FROM cte1
), 1);
END;
CREATE TRIGGER r1 AFTER INSERT ON t2 BEGIN
UPDATE t2 SET k=1 FROM t2 AS one, t2 AS two NATURAL JOIN t2 AS three
WHERE one.k=two.v;
END;
}
faultsim_save_and_close
faultsim_restore_and_reopen
do_execsql_test 2.1 {
ALTER TABLE t2 RENAME TO t2x;
}
do_faultsim_test 2.2 -prep {
faultsim_restore_and_reopen
db eval { SELECT * FROM sqlite_master }
} -body {
execsql {
ALTER TABLE t2 RENAME TO t2x;
}
} -test {
faultsim_test_result {0 {}} $::TMPDBERROR
}
finish_test

120
testdata/tcl/alterqf.test vendored Normal file
View file

@ -0,0 +1,120 @@
# 2021 March 16
#
# 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. This
# script focuses on testing internal function sqlite_rename_quotefix().
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix alterqf
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
ifcapable !altertable {
finish_test
return
}
sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS db
sqlite3_db_config db SQLITE_DBCONFIG_DQS_DDL 1
sqlite3_db_config db SQLITE_DBCONFIG_DQS_DML 1
do_execsql_test 1.0 {
CREATE TABLE t1(a, b, c);
}
foreach {tn before after} {
1 {CREATE VIEW v1 AS SELECT "a", "b", "notacolumn!", "c" FROM t1}
{CREATE VIEW v1 AS SELECT "a", "b", 'notacolumn!', "c" FROM t1}
2 {CREATE VIEW v1 AS SELECT "a", "b", "not'a'column!", "c" FROM t1}
{CREATE VIEW v1 AS SELECT "a", "b", 'not''a''column!', "c" FROM t1}
3 {CREATE VIEW v1 AS SELECT "a", "b", "not""a""column!", "c" FROM t1}
{CREATE VIEW v1 AS SELECT "a", "b", 'not"a"column!', "c" FROM t1}
4 {CREATE VIEW v1 AS SELECT "val", count("b") FROM t1 GROUP BY "abc"}
{CREATE VIEW v1 AS SELECT 'val', count("b") FROM t1 GROUP BY 'abc'}
5 {CREATE TABLE xyz(a CHECK (a!="str"), b AS (a||"str"))}
{CREATE TABLE xyz(a CHECK (a!='str'), b AS (a||'str'))}
6 {CREATE INDEX i1 ON t1(a || "str", "b", "val")}
{CREATE INDEX i1 ON t1(a || 'str', "b", 'val')}
7 {CREATE TRIGGER tr AFTER INSERT ON t1 BEGIN SELECT "abcd"; END}
{CREATE TRIGGER tr AFTER INSERT ON t1 BEGIN SELECT 'abcd'; END}
8 {CREATE VIEW v1 AS SELECT "string"'alias' FROM t1}
{CREATE VIEW v1 AS SELECT 'string' 'alias' FROM t1}
9 {CREATE INDEX i1 ON t1(a) WHERE "b"="bb"}
{CREATE INDEX i1 ON t1(a) WHERE "b"='bb'}
10 {CREATE TABLE t2(abc, xyz CHECK (xyz != "123"))}
{CREATE TABLE t2(abc, xyz CHECK (xyz != '123'))}
11 {CREATE TRIGGER ott AFTER UPDATE ON t1 BEGIN
SELECT max("str", new."a") FROM t1
WHERE group_concat("b", ",") OVER (ORDER BY c||"str");
UPDATE t1 SET c= b + "str";
DELETE FROM t1 WHERE EXISTS (
SELECT 1 FROM t1 AS o WHERE o."a" = "o.a" AND t1.b IN("t1.b")
);
END;
} {CREATE TRIGGER ott AFTER UPDATE ON t1 BEGIN
SELECT max('str', new."a") FROM t1
WHERE group_concat("b", ',') OVER (ORDER BY c||'str');
UPDATE t1 SET c= b + 'str';
DELETE FROM t1 WHERE EXISTS (
SELECT 1 FROM t1 AS o WHERE o."a" = 'o.a' AND t1.b IN('t1.b')
);
END;
}
} {
do_execsql_test 1.$tn {
SELECT sqlite_rename_quotefix('main', $before)
} [list $after]
}
#-------------------------------------------------------------------------
reset_db
sqlite3_db_config db SQLITE_DBCONFIG_DQS_DDL 1
sqlite3_db_config db SQLITE_DBCONFIG_DQS_DML 1
do_execsql_test 2.0 {
CREATE TABLE x1(
one, two, three, PRIMARY KEY(one),
CHECK (three!="xyz"), CHECK (two!="one")
) WITHOUT ROWID;
CREATE INDEX x1i ON x1(one+"two"+"four") WHERE "five";
CREATE TEMP TRIGGER AFTER INSERT ON x1 BEGIN
UPDATE x1 SET two=new.three || "new" WHERE one=new.one||"";
END;
}
do_execsql_test 2.1 {
ALTER TABLE x1 RENAME two TO 'four';
SELECT sql FROM sqlite_schema;
SELECT sql FROM sqlite_temp_schema;
} {{CREATE TABLE x1(
one, "four", three, PRIMARY KEY(one),
CHECK (three!='xyz'), CHECK ("four"!="one")
) WITHOUT ROWID}
{CREATE INDEX x1i ON x1(one+"four"+'four') WHERE 'five'}
{CREATE TRIGGER AFTER INSERT ON x1 BEGIN
UPDATE x1 SET "four"=new.three || 'new' WHERE one=new.one||'';
END}
}
finish_test

984
testdata/tcl/altertab.test vendored Normal file
View file

@ -0,0 +1,984 @@
# 2018 August 24
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#*************************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix altertab
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
ifcapable !altertable {
finish_test
return
}
do_execsql_test 1.0 {
CREATE TABLE t1(a, b, CHECK(t1.a != t1.b));
CREATE TABLE t2(a, b);
CREATE INDEX t2expr ON t2(a) WHERE t2.b>0;
}
do_execsql_test 1.1 {
SELECT sql FROM sqlite_master
} {
{CREATE TABLE t1(a, b, CHECK(t1.a != t1.b))}
{CREATE TABLE t2(a, b)}
{CREATE INDEX t2expr ON t2(a) WHERE t2.b>0}
}
do_execsql_test 1.2 {
ALTER TABLE t1 RENAME TO t1new;
}
do_execsql_test 1.3 {
CREATE TABLE t3(c, d);
ALTER TABLE t3 RENAME TO t3new;
DROP TABLE t3new;
}
do_execsql_test 1.4 {
SELECT sql FROM sqlite_master
} {
{CREATE TABLE "t1new"(a, b, CHECK("t1new".a != "t1new".b))}
{CREATE TABLE t2(a, b)}
{CREATE INDEX t2expr ON t2(a) WHERE t2.b>0}
}
do_execsql_test 1.3 {
ALTER TABLE t2 RENAME TO t2new;
}
do_execsql_test 1.4 {
SELECT sql FROM sqlite_master
} {
{CREATE TABLE "t1new"(a, b, CHECK("t1new".a != "t1new".b))}
{CREATE TABLE "t2new"(a, b)}
{CREATE INDEX t2expr ON "t2new"(a) WHERE "t2new".b>0}
}
#-------------------------------------------------------------------------
reset_db
ifcapable vtab {
register_echo_module db
do_execsql_test 2.0 {
CREATE TABLE abc(a, b, c);
INSERT INTO abc VALUES(1, 2, 3);
CREATE VIRTUAL TABLE eee USING echo('abc');
SELECT * FROM eee;
} {1 2 3}
do_execsql_test 2.1 {
ALTER TABLE eee RENAME TO fff;
SELECT * FROM fff;
} {1 2 3}
db close
sqlite3 db test.db
do_catchsql_test 2.2 {
ALTER TABLE fff RENAME TO ggg;
} {1 {no such module: echo}}
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 3.0 {
CREATE TABLE txx(a, b, c);
INSERT INTO txx VALUES(1, 2, 3);
CREATE VIEW vvv AS SELECT main.txx.a, txx.b, c FROM txx;
CREATE VIEW uuu AS SELECT main.one.a, one.b, c FROM txx AS one;
CREATE VIEW temp.ttt AS SELECT main.txx.a, txx.b, one.b, main.one.a FROM txx AS one, txx;
}
do_execsql_test 3.1.1 {
SELECT * FROM vvv;
} {1 2 3}
do_execsql_test 3.1.2 {
ALTER TABLE txx RENAME TO "t xx";
SELECT * FROM vvv;
} {1 2 3}
do_execsql_test 3.1.3 {
SELECT sql FROM sqlite_master WHERE name='vvv';
} {{CREATE VIEW vvv AS SELECT main."t xx".a, "t xx".b, c FROM "t xx"}}
do_execsql_test 3.2.1 {
SELECT * FROM uuu;
} {1 2 3}
do_execsql_test 3.2.2 {
SELECT sql FROM sqlite_master WHERE name='uuu';;
} {{CREATE VIEW uuu AS SELECT main.one.a, one.b, c FROM "t xx" AS one}}
do_execsql_test 3.3.1 {
SELECT * FROM ttt;
} {1 2 2 1}
do_execsql_test 3.3.2 {
SELECT sql FROM sqlite_temp_master WHERE name='ttt';
} {{CREATE VIEW ttt AS SELECT main."t xx".a, "t xx".b, one.b, main.one.a FROM "t xx" AS one, "t xx"}}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 4.0 {
CREATE table t1(x, y);
CREATE table t2(a, b);
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
SELECT t1.x, * FROM t1, t2;
INSERT INTO t2 VALUES(new.x, new.y);
END;
}
do_execsql_test 4.1 {
INSERT INTO t1 VALUES(1, 1);
ALTER TABLE t1 RENAME TO t11;
INSERT INTO t11 VALUES(2, 2);
ALTER TABLE t2 RENAME TO t22;
INSERT INTO t11 VALUES(3, 3);
}
proc squish {a} {
string trim [regsub -all {[[:space:]][[:space:]]*} $a { }]
}
db func squish squish
do_test 4.2 {
execsql { SELECT squish(sql) FROM sqlite_master WHERE name = 'tr1' }
} [list [squish {
CREATE TRIGGER tr1 AFTER INSERT ON "t11" BEGIN
SELECT "t11".x, * FROM "t11", "t22";
INSERT INTO "t22" VALUES(new.x, new.y);
END
}]]
#-------------------------------------------------------------------------
reset_db
do_execsql_test 5.0 {
CREATE TABLE t9(a, b, c);
CREATE TABLE t10(a, b, c);
CREATE TEMP TABLE t9(a, b, c);
CREATE TRIGGER temp.t9t AFTER INSERT ON temp.t9 BEGIN
INSERT INTO t10 VALUES(new.a, new.b, new.c);
END;
INSERT INTO temp.t9 VALUES(1, 2, 3);
SELECT * FROM t10;
} {1 2 3}
do_execsql_test 5.1 {
ALTER TABLE temp.t9 RENAME TO 't1234567890'
}
do_execsql_test 5.2 {
CREATE TABLE t1(a, b);
CREATE TABLE t2(a, b);
INSERT INTO t1 VALUES(1, 2);
INSERT INTO t2 VALUES(3, 4);
CREATE VIEW v AS SELECT one.a, one.b, t2.a, t2.b FROM t1 AS one, t2;
SELECT * FROM v;
} {1 2 3 4}
do_catchsql_test 5.3 {
ALTER TABLE t2 RENAME TO one;
} {1 {error in view v after rename: ambiguous column name: one.a}}
do_execsql_test 5.4 {
SELECT * FROM v
} {1 2 3 4}
do_execsql_test 5.5 {
DROP VIEW v;
CREATE VIEW temp.vv AS SELECT one.a, one.b, t2.a, t2.b FROM t1 AS one, t2;
SELECT * FROM vv;
} {1 2 3 4}
do_catchsql_test 5.6 {
ALTER TABLE t2 RENAME TO one;
} {1 {error in view vv after rename: ambiguous column name: one.a}}
#-------------------------------------------------------------------------
ifcapable vtab {
register_tcl_module db
proc tcl_command {method args} {
switch -- $method {
xConnect {
return "CREATE TABLE t1(a, b, c)"
}
}
return {}
}
do_execsql_test 6.0 {
CREATE VIRTUAL TABLE x1 USING tcl(tcl_command);
}
do_execsql_test 6.1 {
ALTER TABLE x1 RENAME TO x2;
SELECT sql FROM sqlite_master WHERE name = 'x2'
} {{CREATE VIRTUAL TABLE "x2" USING tcl(tcl_command)}}
do_execsql_test 7.1 {
CREATE TABLE ddd(db, sql, zOld, zNew, bTemp);
INSERT INTO ddd VALUES(
'main', 'CREATE TABLE x1(i INTEGER, t TEXT)', 'ddd', NULL, 0
), (
'main', 'CREATE TABLE x1(i INTEGER, t TEXT)', NULL, 'eee', 0
), (
'main', NULL, 'ddd', 'eee', 0
);
} {}
sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS db
do_execsql_test 7.2 {
SELECT
sqlite_rename_table(db, 0, 0, sql, zOld, zNew, bTemp)
FROM ddd;
} {{} {} {}}
sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS db
}
#-------------------------------------------------------------------------
#
reset_db
forcedelete test.db2
do_execsql_test 8.1 {
ATTACH 'test.db2' AS aux;
PRAGMA foreign_keys = on;
CREATE TABLE aux.p1(a INTEGER PRIMARY KEY, b);
CREATE TABLE aux.c1(x INTEGER PRIMARY KEY, y REFERENCES p1(a));
INSERT INTO aux.p1 VALUES(1, 1);
INSERT INTO aux.p1 VALUES(2, 2);
INSERT INTO aux.c1 VALUES(NULL, 2);
CREATE TABLE aux.c2(x INTEGER PRIMARY KEY, y REFERENCES c1(a));
}
do_execsql_test 8.2 {
ALTER TABLE aux.p1 RENAME TO ppp;
}
do_execsql_test 8.2 {
INSERT INTO aux.c1 VALUES(NULL, 1);
SELECT sql FROM aux.sqlite_master WHERE name = 'c1';
} {{CREATE TABLE c1(x INTEGER PRIMARY KEY, y REFERENCES "ppp"(a))}}
reset_db
do_execsql_test 9.0 {
CREATE TABLE t1(a, b, c);
CREATE VIEW v1 AS SELECT * FROM t2;
}
do_catchsql_test 9.1 {
ALTER TABLE t1 RENAME TO t3;
} {1 {error in view v1: no such table: main.t2}}
do_execsql_test 9.2 {
DROP VIEW v1;
CREATE TRIGGER tr AFTER INSERT ON t1 BEGIN
INSERT INTO t2 VALUES(new.a);
END;
}
do_catchsql_test 9.3 {
ALTER TABLE t1 RENAME TO t3;
} {1 {error in trigger tr: no such table: main.t2}}
forcedelete test.db2
do_execsql_test 9.4 {
DROP TRIGGER tr;
ATTACH 'test.db2' AS aux;
CREATE TRIGGER tr AFTER INSERT ON t1 WHEN new.a IS NULL BEGIN SELECT 1, 2, 3; END;
CREATE TABLE aux.t1(x);
CREATE TEMP TRIGGER tr AFTER INSERT ON aux.t1 BEGIN SELECT 1, 2, 3; END;
}
do_execsql_test 9.5 {
ALTER TABLE main.t1 RENAME TO t3;
}
do_execsql_test 9.6 {
SELECT sql FROM sqlite_temp_master;
SELECT sql FROM sqlite_master WHERE type='trigger';
} {
{CREATE TRIGGER tr AFTER INSERT ON aux.t1 BEGIN SELECT 1, 2, 3; END}
{CREATE TRIGGER tr AFTER INSERT ON "t3" WHEN new.a IS NULL BEGIN SELECT 1, 2, 3; END}
}
#-------------------------------------------------------------------------
reset_db
ifcapable fts5 {
do_execsql_test 10.0 {
CREATE VIRTUAL TABLE fff USING fts5(x, y, z);
}
do_execsql_test 10.1 {
BEGIN;
INSERT INTO fff VALUES('a', 'b', 'c');
ALTER TABLE fff RENAME TO ggg;
COMMIT;
}
do_execsql_test 10.2 {
SELECT * FROM ggg;
} {a b c}
}
#-------------------------------------------------------------------------
reset_db
forcedelete test.db2
db func trigger trigger
set ::trigger [list]
proc trigger {args} {
lappend ::trigger $args
}
do_execsql_test 11.0 {
ATTACH 'test.db2' AS aux;
CREATE TABLE aux.t1(a, b, c);
CREATE TABLE main.t1(a, b, c);
CREATE TEMP TRIGGER tr AFTER INSERT ON aux.t1 BEGIN
SELECT trigger(new.a, new.b, new.c);
END;
}
do_execsql_test 11.1 {
INSERT INTO main.t1 VALUES(1, 2, 3);
INSERT INTO aux.t1 VALUES(4, 5, 6);
}
do_test 11.2 { set ::trigger } {{4 5 6}}
do_execsql_test 11.3 {
SELECT name, tbl_name FROM sqlite_temp_master;
} {tr t1}
do_execsql_test 11.4 {
ALTER TABLE main.t1 RENAME TO t2;
SELECT name, tbl_name FROM sqlite_temp_master;
} {tr t1}
do_execsql_test 11.5 {
ALTER TABLE aux.t1 RENAME TO t2;
SELECT name, tbl_name FROM sqlite_temp_master;
} {tr t2}
do_execsql_test 11.6 {
INSERT INTO aux.t2 VALUES(7, 8, 9);
}
do_test 11.7 { set ::trigger } {{4 5 6} {7 8 9}}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 12.0 {
CREATE TABLE t1(a);
CREATE TABLE t2(w);
CREATE TRIGGER temp.r1 AFTER INSERT ON main.t2 BEGIN
INSERT INTO t1(a) VALUES(new.w);
END;
CREATE TEMP TABLE t2(x);
}
do_execsql_test 12.1 {
ALTER TABLE main.t2 RENAME TO t3;
}
do_execsql_test 12.2 {
INSERT INTO t3 VALUES('WWW');
SELECT * FROM t1;
} {WWW}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 13.0 {
CREATE TABLE t1(x, y);
CREATE TABLE t2(a, b);
CREATE TABLE log(c);
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
INSERT INTO log SELECT y FROM t1, t2;
END;
}
do_execsql_test 13.1 {
INSERT INTO t1 VALUES(1, 2);
}
do_catchsql_test 13.2 {
ALTER TABLE t2 RENAME b TO y;
} {1 {error in trigger tr1 after rename: ambiguous column name: y}}
#-------------------------------------------------------------------------
reset_db
ifcapable rtree {
do_execsql_test 14.0 {
CREATE VIRTUAL TABLE rt USING rtree(id, minx, maxx, miny, maxy);
CREATE TABLE "mytable" ( "fid" INTEGER PRIMARY KEY, "geom" BLOB);
CREATE TRIGGER tr1 AFTER UPDATE OF "geom" ON "mytable"
WHEN OLD."fid" = NEW."fid" AND NEW."geom" IS NULL BEGIN
DELETE FROM rt WHERE id = OLD."fid";
END;
INSERT INTO mytable VALUES(1, X'abcd');
}
do_execsql_test 14.1 {
UPDATE mytable SET geom = X'1234'
}
do_execsql_test 14.2 {
ALTER TABLE mytable RENAME TO mytable_renamed;
}
do_execsql_test 14.3 {
CREATE TRIGGER tr2 AFTER INSERT ON mytable_renamed BEGIN
DELETE FROM rt WHERE id=(SELECT min(id) FROM rt);
END;
}
do_execsql_test 14.4 {
ALTER TABLE mytable_renamed RENAME TO mytable2;
}
}
reset_db
do_execsql_test 14.5 {
CREATE TABLE t1(a, b, c);
CREATE VIEW v1 AS SELECT * FROM t1;
CREATE TRIGGER xyz AFTER INSERT ON t1 BEGIN
SELECT a, b FROM v1;
END;
}
do_execsql_test 14.6 {
ALTER TABLE t1 RENAME TO tt1;
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 15.0 {
CREATE TABLE t1(a integer NOT NULL PRIMARY KEY);
CREATE VIEW v1 AS SELECT a FROM t1;
CREATE TRIGGER tr1 INSTEAD OF INSERT ON v1 BEGIN
UPDATE t1 SET a = NEW.a;
END;
CREATE TRIGGER tr2 INSTEAD OF INSERT ON v1 BEGIN
SELECT new.a;
END;
CREATE TABLE t2 (b);
}
do_execsql_test 15.1 {
INSERT INTO v1 VALUES(1);
ALTER TABLE t2 RENAME TO t3;
}
do_execsql_test 15.2 {
CREATE TABLE x(f1 integer NOT NULL);
CREATE VIEW y AS SELECT f1 AS f1 FROM x;
CREATE TRIGGER t INSTEAD OF UPDATE OF f1 ON y BEGIN
UPDATE x SET f1 = NEW.f1;
END;
CREATE TABLE z (f1 integer NOT NULL PRIMARY KEY);
ALTER TABLE z RENAME TO z2;
}
do_execsql_test 15.3 {
INSERT INTO x VALUES(1), (2), (3);
ALTER TABLE x RENAME f1 TO f2;
SELECT * FROM x;
} {1 2 3}
do_execsql_test 15.4 {
UPDATE y SET f1 = 'x' WHERE f1 = 1;
SELECT * FROM x;
} {x x x}
do_execsql_test 15.5 {
SELECT sql FROM sqlite_master WHERE name = 'y';
} {{CREATE VIEW y AS SELECT f2 AS f1 FROM x}}
#-------------------------------------------------------------------------
# Test that it is not possible to rename a shadow table in DEFENSIVE mode.
#
ifcapable fts3 {
proc vtab_command {method args} {
switch -- $method {
xConnect {
if {[info exists ::vtab_connect_sql]} {
execsql $::vtab_connect_sql
}
return "CREATE TABLE t1(a, b, c)"
}
xBestIndex {
set clist [lindex $args 0]
if {[llength $clist]!=1} { error "unexpected constraint list" }
catch { array unset C }
array set C [lindex $clist 0]
if {$C(usable)} {
return "omit 0 cost 0 rows 1 idxnum 555 idxstr eq!"
} else {
return "cost 1000000 rows 0 idxnum 0 idxstr scan..."
}
}
}
return {}
}
register_tcl_module db
sqlite3_db_config db DEFENSIVE 1
do_execsql_test 16.0 {
CREATE VIRTUAL TABLE y1 USING fts3;
VACUUM;
}
do_catchsql_test 16.10 {
INSERT INTO y1_segments VALUES(1, X'1234567890');
} {1 {table y1_segments may not be modified}}
do_catchsql_test 16.20 {
DROP TABLE y1_segments;
} {1 {table y1_segments may not be dropped}}
do_catchsql_test 16.20 {
ALTER TABLE y1_segments RENAME TO abc;
} {1 {table y1_segments may not be altered}}
sqlite3_db_config db DEFENSIVE 0
do_catchsql_test 16.22 {
ALTER TABLE y1_segments RENAME TO abc;
} {0 {}}
sqlite3_db_config db DEFENSIVE 1
do_catchsql_test 16.23 {
CREATE TABLE y1_segments AS SELECT * FROM abc;
} {1 {object name reserved for internal use: y1_segments}}
do_catchsql_test 16.24 {
CREATE VIEW y1_segments AS SELECT * FROM abc;
} {1 {object name reserved for internal use: y1_segments}}
sqlite3_db_config db DEFENSIVE 0
do_catchsql_test 16.25 {
ALTER TABLE abc RENAME TO y1_segments;
} {0 {}}
sqlite3_db_config db DEFENSIVE 1
do_execsql_test 16.30 {
ALTER TABLE y1 RENAME TO z1;
}
do_execsql_test 16.40 {
SELECT * FROM z1_segments;
}
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 17.0 {
CREATE TABLE sqlite1234 (id integer);
ALTER TABLE sqlite1234 RENAME TO User;
SELECT name, sql FROM sqlite_master WHERE sql IS NOT NULL;
} {
User {CREATE TABLE "User" (id integer)}
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 18.1.0 {
CREATE TABLE t0 (c0 INTEGER, PRIMARY KEY(c0)) WITHOUT ROWID;
}
do_execsql_test 18.1.1 {
ALTER TABLE t0 RENAME COLUMN c0 TO c1;
}
do_execsql_test 18.1.2 {
SELECT sql FROM sqlite_master;
} {{CREATE TABLE t0 (c1 INTEGER, PRIMARY KEY(c1)) WITHOUT ROWID}}
reset_db
do_execsql_test 18.2.0 {
CREATE TABLE t0 (c0 INTEGER, PRIMARY KEY(c0));
}
do_execsql_test 18.2.1 {
ALTER TABLE t0 RENAME COLUMN c0 TO c1;
}
do_execsql_test 18.2.2 {
SELECT sql FROM sqlite_master;
} {{CREATE TABLE t0 (c1 INTEGER, PRIMARY KEY(c1))}}
# 2020-02-23 ticket f50af3e8a565776b
reset_db
do_execsql_test 19.100 {
CREATE TABLE t1(x);
CREATE VIEW t2 AS SELECT 1 FROM t1, (t1 AS a0, t1);
ALTER TABLE t1 RENAME TO t3;
SELECT sql FROM sqlite_master;
} {{CREATE TABLE "t3"(x)} {CREATE VIEW t2 AS SELECT 1 FROM "t3", ("t3" AS a0, "t3")}}
do_execsql_test 19.110 {
INSERT INTO t3(x) VALUES(123);
SELECT * FROM t2;
} {1}
do_execsql_test 19.120 {
INSERT INTO t3(x) VALUES('xyz');
SELECT * FROM t2;
} {1 1 1 1 1 1 1 1}
# Ticket 4722bdab08cb14
reset_db
do_execsql_test 20.0 {
CREATE TABLE a(a);
CREATE VIEW b AS SELECT(SELECT *FROM c JOIN a USING(d, a, a, a) JOIN a) IN();
}
do_execsql_test 20.1 {
ALTER TABLE a RENAME a TO e;
} {}
reset_db
do_execsql_test 21.0 {
CREATE TABLE a(b);
CREATE VIEW c AS
SELECT NULL INTERSECT
SELECT NULL ORDER BY
likelihood(NULL, (d, (SELECT c)));
} {}
do_catchsql_test 21.1 {
SELECT likelihood(NULL, (d, (SELECT c)));
} {1 {second argument to likelihood() must be a constant between 0.0 and 1.0}}
do_catchsql_test 21.2 {
SELECT * FROM c;
} {1 {1st ORDER BY term does not match any column in the result set}}
do_catchsql_test 21.3 {
ALTER TABLE a RENAME TO e;
} {1 {error in view c: 1st ORDER BY term does not match any column in the result set}}
# After forum thread https://sqlite.org/forum/forumpost/ddbe1c7efa
# Ensure that PRAGMA schema_version=N causes a full schema reload.
#
reset_db
do_execsql_test 22.0 {
CREATE TABLE t1(a INT, b TEXT NOT NULL);
INSERT INTO t1 VALUES(1,2),('a','b');
BEGIN;
PRAGMA writable_schema=ON;
UPDATE sqlite_schema SET sql='CREATE TABLE t1(a INT, b TEXT)' WHERE name LIKE 't1';
PRAGMA schema_version=1234;
COMMIT;
PRAGMA integrity_check;
} {ok}
do_execsql_test 22.1 {
ALTER TABLE t1 ADD COLUMN c INT DEFAULT 78;
SELECT * FROM t1;
} {1 2 78 a b 78}
#-------------------------------------------------------------------------
reset_db
db collate compare64 compare64
do_execsql_test 23.1 {
CREATE TABLE gigo(a text);
CREATE TABLE idx(x text COLLATE compare64);
CREATE VIEW v1 AS SELECT * FROM idx WHERE x='abc';
}
db close
sqlite3 db test.db
do_execsql_test 23.2 {
alter table gigo rename to ggiiggoo;
alter table idx rename to idx2;
}
do_execsql_test 23.3 {
SELECT sql FROM sqlite_master;
} {
{CREATE TABLE "ggiiggoo"(a text)}
{CREATE TABLE "idx2"(x text COLLATE compare64)}
{CREATE VIEW v1 AS SELECT * FROM "idx2" WHERE x='abc'}
}
do_execsql_test 23.4 {
ALTER TABLE idx2 RENAME x TO y;
SELECT sql FROM sqlite_master;
} {
{CREATE TABLE "ggiiggoo"(a text)}
{CREATE TABLE "idx2"(y text COLLATE compare64)}
{CREATE VIEW v1 AS SELECT * FROM "idx2" WHERE y='abc'}
}
#-------------------------------------------------------------------------
#
reset_db
do_execsql_test 24.1.0 {
CREATE TABLE t1(a, b);
CREATE TRIGGER AFTER INSERT ON t1 BEGIN
INSERT INTO nosuchtable VALUES(new.a) ON CONFLICT(a) DO NOTHING;
END;
}
do_catchsql_test 24.1.1 {
ALTER TABLE t1 RENAME TO t2;
} {1 {error in trigger AFTER: no such table: main.nosuchtable}}
reset_db
do_execsql_test 24.2.0 {
CREATE TABLE t1(a, b);
CREATE TRIGGER AFTER INSERT ON t1 BEGIN
INSERT INTO v1 VALUES(new.a) ON CONFLICT(a) DO NOTHING;
END;
CREATE VIEW v1 AS SELECT * FROM nosuchtable;
}
do_catchsql_test 24.2.1 {
ALTER TABLE t1 RENAME TO t2;
} {1 {error in trigger AFTER: no such table: main.nosuchtable}}
#--------------------------------------------------------------------------
#
reset_db
do_execsql_test 25.1 {
CREATE TABLE xx(x);
CREATE VIEW v3(b) AS WITH b AS (SELECT b FROM (SELECT * FROM t2)) VALUES(1);
}
ifcapable json1&&vtab {
do_catchsql_test 25.2 {
ALTER TABLE json_each RENAME TO t4;
} {1 {table json_each may not be altered}}
}
# 2021-05-01 dbsqlfuzz bc17a306a09329bba0ecc61547077f6178bcf321
# Remove a NEVER() inserted on 2019-12-09 that is reachable after all.
#
reset_db
do_execsql_test 26.1 {
CREATE TABLE t1(k,v);
CREATE TABLE t2_a(k,v);
CREATE VIEW t2 AS SELECT * FROM t2_a;
CREATE TRIGGER r2 AFTER INSERT ON t1 BEGIN
UPDATE t1
SET (k,v)=((WITH cte1(a) AS (SELECT 1 FROM t2) SELECT t2.k FROM t2, cte1),1);
END;
ALTER TABLE t1 RENAME TO t1x;
INSERT INTO t2_a VALUES(2,3);
INSERT INTO t1x VALUES(98,99);
SELECT * FROM t1x;
} {2 1}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 27.1 {
create table t_sa (
c_muyat INTEGER NOT NULL,
c_d4u TEXT
);
create table t2 ( abc );
CREATE TRIGGER trig AFTER DELETE ON t_sa
BEGIN
DELETE FROM t_sa WHERE (
SELECT 123 FROM t2
WINDOW oamat7fzf AS ( PARTITION BY t_sa.c_d4u )
);
END;
}
do_execsql_test 27.2 {
alter table t_sa rename column c_muyat to c_dg;
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 29.1 {
CREATE TABLE t1(a, b, c);
INSERT INTO t1 VALUES('a', 'b', 'c');
CREATE VIEW v0 AS
WITH p AS ( SELECT 1 FROM t1 ),
g AS ( SELECT 1 FROM p, t1 )
SELECT 1 FROM g;
}
do_execsql_test 29.2 {
SELECT * FROM v0
} 1
do_execsql_test 29.2 {
ALTER TABLE t1 RENAME TO t2
}
do_execsql_test 29.3 {
SELECT sql FROM sqlite_schema WHERE name='v0'
} {{CREATE VIEW v0 AS
WITH p AS ( SELECT 1 FROM "t2" ),
g AS ( SELECT 1 FROM p, "t2" )
SELECT 1 FROM g}}
do_execsql_test 29.4 {
CREATE VIEW v2 AS
WITH p AS ( SELECT 1 FROM t2 ),
g AS ( SELECT 1 FROM (
WITH i AS (SELECT 1 FROM p, t2)
SELECT * FROM i
)
)
SELECT 1 FROM g;
}
do_execsql_test 29.4 {
SELECT * FROM v2;
} 1
do_execsql_test 29.5 {
ALTER TABLE t2 RENAME TO t3;
}
do_execsql_test 29.5 {
SELECT sql FROM sqlite_schema WHERE name='v2'
} {{CREATE VIEW v2 AS
WITH p AS ( SELECT 1 FROM "t3" ),
g AS ( SELECT 1 FROM (
WITH i AS (SELECT 1 FROM p, "t3")
SELECT * FROM i
)
)
SELECT 1 FROM g}}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 28.1 {
CREATE TABLE t1(a);
CREATE TABLE t2(b,c);
CREATE TABLE t4(b,c);
INSERT INTO t2 VALUES(1,2),(1,3),(2,5);
INSERT INTO t4 VALUES(1,2),(1,3),(2,5);
CREATE VIEW v3 AS
WITH RECURSIVE t3(x,y,z) AS (
SELECT b,c,NULL FROM t4
UNION
SELECT x,y,NULL FROM t3, t2
)
SELECT * FROM t3 AS xyz;
}
do_execsql_test 28.2 {
SELECT * FROM v3
} {
1 2 {} 1 3 {} 2 5 {}
}
do_execsql_test 28.3 {
ALTER TABLE t1 RENAME a TO a2; -- fails in v3
}
do_execsql_test 28.4 {
ALTER TABLE t2 RENAME TO t5;
}
do_execsql_test 28.5 {
SELECT sql FROM sqlite_schema WHERE name='v3'
} {{CREATE VIEW v3 AS
WITH RECURSIVE t3(x,y,z) AS (
SELECT b,c,NULL FROM t4
UNION
SELECT x,y,NULL FROM t3, "t5"
)
SELECT * FROM t3 AS xyz}}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 30.0 {
CREATE TABLE t1(a,b,c,d,e,f);
CREATE TABLE t2(a,b,c);
CREATE INDEX t1abc ON t1(a,b,c+d+e);
CREATE VIEW v1(x,y) AS
SELECT t1.b,t2.b FROM t1,t2 WHERE t1.a=t2.a
GROUP BY 1 HAVING t2.c NOT NULL LIMIT 10;
CREATE TRIGGER r1 AFTER INSERT ON t1 WHEN 'no' NOT NULL BEGIN
INSERT INTO t2(a,a,b,c) VALUES(new.b,new.a,new.c-7);
WITH c1(x) AS (
VALUES(0)
UNION ALL
SELECT current_time+x FROM c1 WHERE x
UNION ALL
SELECT 1+x FROM c1 WHERE x<1
), c2(x) AS (VALUES(0),(1))
SELECT * FROM c1 AS x1, c2 AS x2, (
SELECT x+1 FROM c1 WHERE x IS NOT TRUE
UNION ALL
SELECT 1+x FROM c1 WHERE 1<x
) AS x3, c2 x5;
END;
}
do_execsql_test 30.1 {
ALTER TABLE t1 RENAME TO t1x;
}
do_execsql_test 30.2 {
SELECT sql FROM sqlite_schema ORDER BY rowid
} {
{CREATE TABLE "t1x"(a,b,c,d,e,f)}
{CREATE TABLE t2(a,b,c)}
{CREATE INDEX t1abc ON "t1x"(a,b,c+d+e)}
{CREATE VIEW v1(x,y) AS
SELECT "t1x".b,t2.b FROM "t1x",t2 WHERE "t1x".a=t2.a
GROUP BY 1 HAVING t2.c NOT NULL LIMIT 10}
{CREATE TRIGGER r1 AFTER INSERT ON "t1x" WHEN 'no' NOT NULL BEGIN
INSERT INTO t2(a,a,b,c) VALUES(new.b,new.a,new.c-7);
WITH c1(x) AS (
VALUES(0)
UNION ALL
SELECT current_time+x FROM c1 WHERE x
UNION ALL
SELECT 1+x FROM c1 WHERE x<1
), c2(x) AS (VALUES(0),(1))
SELECT * FROM c1 AS x1, c2 AS x2, (
SELECT x+1 FROM c1 WHERE x IS NOT TRUE
UNION ALL
SELECT 1+x FROM c1 WHERE 1<x
) AS x3, c2 x5;
END}
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 31.0 {
CREATE TABLE t1(q);
CREATE VIEW vvv AS WITH x AS (WITH y AS (SELECT * FROM x) SELECT 1) SELECT 1;
}
do_execsql_test 31.1 {
SELECT * FROM vvv;
} {1}
do_execsql_test 31.2 {
ALTER TABLE t1 RENAME TO t1x;
}
do_execsql_test 31.3 {
ALTER TABLE t1x RENAME q TO x;
}
# 2021-07-02 OSSFuzz https://oss-fuzz.com/testcase-detail/5517690440646656
# Bad assert() statement
#
reset_db
do_catchsql_test 32.0 {
CREATE TABLE t1(x);
CREATE TRIGGER r1 BEFORE INSERT ON t1 BEGIN
UPDATE t1 SET x=x FROM (SELECT*);
END;
ALTER TABLE t1 RENAME TO x;
} {1 {error in trigger r1: no tables specified}}
finish_test

363
testdata/tcl/altertab2.test vendored Normal file
View file

@ -0,0 +1,363 @@
# 2018 September 30
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#*************************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix altertab2
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
ifcapable !altertable {
finish_test
return
}
ifcapable fts5 {
do_execsql_test 1.0 {
CREATE TABLE rr(a, b);
CREATE VIRTUAL TABLE ff USING fts5(a, b);
CREATE TRIGGER tr1 AFTER INSERT ON rr BEGIN
INSERT INTO ff VALUES(new.a, new.b);
END;
INSERT INTO rr VALUES('hello', 'world');
SELECT * FROM ff;
} {hello world}
do_execsql_test 1.1 {
ALTER TABLE ff RENAME TO ffff;
}
do_execsql_test 1.2 {
INSERT INTO rr VALUES('in', 'tcl');
SELECT * FROM ffff;
} {hello world in tcl}
}
#-------------------------------------------------------------------------
# Check that table names that appear in REFERENCES clauses are updated
# when a table is renamed unless:
#
# a) "PRAGMA legacy_alter_table" is true, and
# b) "PRAGMA foreign_keys" is false.
#
do_execsql_test 2.0 {
CREATE TABLE p1(a PRIMARY KEY, b);
CREATE TABLE c1(x REFERENCES p1);
CREATE TABLE c2(x, FOREIGN KEY (x) REFERENCES p1);
CREATE TABLE c3(x, FOREIGN KEY (x) REFERENCES p1(a));
}
do_execsql_test 2.1 {
ALTER TABLE p1 RENAME TO p2;
SELECT sql FROM sqlite_master WHERE name LIKE 'c%';
} {
{CREATE TABLE c1(x REFERENCES "p2")}
{CREATE TABLE c2(x, FOREIGN KEY (x) REFERENCES "p2")}
{CREATE TABLE c3(x, FOREIGN KEY (x) REFERENCES "p2"(a))}
}
do_execsql_test 2.2 {
PRAGMA legacy_alter_table = 1;
ALTER TABLE p2 RENAME TO p3;
SELECT sql FROM sqlite_master WHERE name LIKE 'c%';
} {
{CREATE TABLE c1(x REFERENCES "p2")}
{CREATE TABLE c2(x, FOREIGN KEY (x) REFERENCES "p2")}
{CREATE TABLE c3(x, FOREIGN KEY (x) REFERENCES "p2"(a))}
}
do_execsql_test 2.3 {
ALTER TABLE p3 RENAME TO p2;
PRAGMA foreign_keys = 1;
ALTER TABLE p2 RENAME TO p3;
SELECT sql FROM sqlite_master WHERE name LIKE 'c%';
} {
{CREATE TABLE c1(x REFERENCES "p3")}
{CREATE TABLE c2(x, FOREIGN KEY (x) REFERENCES "p3")}
{CREATE TABLE c3(x, FOREIGN KEY (x) REFERENCES "p3"(a))}
}
#-------------------------------------------------------------------------
# Table name in WITH clauses that are part of views or triggers.
#
foreach {tn schema} {
1 {
CREATE TABLE log_entry(col1, y);
CREATE INDEX i1 ON log_entry(col1);
}
2 {
CREATE TABLE t1(a, b, c);
CREATE TABLE t2(x);
CREATE TABLE log_entry(col1);
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
INSERT INTO t2 SELECT col1 FROM log_entry;
END;
}
3 {
CREATE TABLE t1(a, b, c);
CREATE TABLE t2(x);
CREATE TABLE log_entry(col1);
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
INSERT INTO t2
WITH xyz(x) AS (SELECT col1 FROM log_entry)
SELECT x FROM xyz;
END;
}
4 {
CREATE TABLE log_entry(col1);
CREATE VIEW ttt AS
WITH xyz(x) AS (SELECT col1 FROM log_entry)
SELECT x FROM xyz;
}
} {
reset_db
do_execsql_test 3.$tn.1 $schema
set expect [db eval "SELECT sql FROM sqlite_master"]
set expect [string map {log_entry {"newname"}} $expect]
do_execsql_test 3.$tn.2 {
ALTER TABLE log_entry RENAME TO newname;
SELECT sql FROM sqlite_master;
} $expect
reset_db
do_execsql_test 3.$tn.3 $schema
set expect [db eval "SELECT sql FROM sqlite_master"]
set expect [string map {col1 newname} $expect]
do_execsql_test 3.$tn.4 {
ALTER TABLE log_entry RENAME col1 TO newname;
SELECT sql FROM sqlite_master;
} $expect
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 4.0 {
CREATE TABLE t1(a,b,c,d,e,f);
CREATE TRIGGER r1 AFTER INSERT ON t1 WHEN new.a NOT NULL BEGIN
UPDATE t1 SET (c,d)=(a,b);
END;
}
do_execsql_test 4.1 {
ALTER TABLE t1 RENAME TO t1x;
SELECT sql FROM sqlite_master WHERE type = 'trigger';
} {
{CREATE TRIGGER r1 AFTER INSERT ON "t1x" WHEN new.a NOT NULL BEGIN
UPDATE "t1x" SET (c,d)=(a,b);
END}
}
do_execsql_test 4.2 {
ALTER TABLE t1x RENAME a TO aaa;
SELECT sql FROM sqlite_master WHERE type = 'trigger';
} {
{CREATE TRIGGER r1 AFTER INSERT ON "t1x" WHEN new.aaa NOT NULL BEGIN
UPDATE "t1x" SET (c,d)=(aaa,b);
END}
}
do_execsql_test 4.3 {
ALTER TABLE t1x RENAME d TO ddd;
SELECT sql FROM sqlite_master WHERE type = 'trigger';
} {
{CREATE TRIGGER r1 AFTER INSERT ON "t1x" WHEN new.aaa NOT NULL BEGIN
UPDATE "t1x" SET (c,ddd)=(aaa,b);
END}
}
#-------------------------------------------------------------------------
ifcapable windowfunc {
do_execsql_test 5.0 {
CREATE TABLE t2(a);
CREATE TRIGGER r2 AFTER INSERT ON t2 WHEN new.a NOT NULL BEGIN
SELECT a, sum(a) OVER w1 FROM t2
WINDOW w1 AS (
PARTITION BY a ORDER BY a
ROWS BETWEEN 2 PRECEDING AND 3 FOLLOWING
),
w2 AS (
PARTITION BY a
ORDER BY rowid ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
);
END;
} {}
do_execsql_test 5.0.1 {
INSERT INTO t2 VALUES(1);
} {}
do_execsql_test 5.1 {
ALTER TABLE t2 RENAME TO t2x;
SELECT sql FROM sqlite_master WHERE name = 'r2';
} {
{CREATE TRIGGER r2 AFTER INSERT ON "t2x" WHEN new.a NOT NULL BEGIN
SELECT a, sum(a) OVER w1 FROM "t2x"
WINDOW w1 AS (
PARTITION BY a ORDER BY a
ROWS BETWEEN 2 PRECEDING AND 3 FOLLOWING
),
w2 AS (
PARTITION BY a
ORDER BY rowid ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
);
END}
}
do_execsql_test 5.2 {
ALTER TABLE t2x RENAME a TO aaaa;
SELECT sql FROM sqlite_master WHERE name = 'r2';
} {
{CREATE TRIGGER r2 AFTER INSERT ON "t2x" WHEN new.aaaa NOT NULL BEGIN
SELECT aaaa, sum(aaaa) OVER w1 FROM "t2x"
WINDOW w1 AS (
PARTITION BY aaaa ORDER BY aaaa
ROWS BETWEEN 2 PRECEDING AND 3 FOLLOWING
),
w2 AS (
PARTITION BY aaaa
ORDER BY rowid ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
);
END}
}
do_execsql_test 5.3 {
INSERT INTO t2x VALUES(1);
} {}
} ;# windowfunc
#-------------------------------------------------------------------------
do_execsql_test 6.0 {
CREATE TABLE t3(a,b,c,d);
CREATE TRIGGER r3 AFTER INSERT ON t3 WHEN new.a NOT NULL BEGIN
SELECT a,b,c FROM t3 EXCEPT SELECT a,b,c FROM t3 ORDER BY a;
SELECT rowid, * FROM t3;
END;
} {}
do_execsql_test 6.1 {
ALTER TABLE t3 RENAME TO t3x;
SELECT sql FROM sqlite_master WHERE name = 'r3';
} {
{CREATE TRIGGER r3 AFTER INSERT ON "t3x" WHEN new.a NOT NULL BEGIN
SELECT a,b,c FROM "t3x" EXCEPT SELECT a,b,c FROM "t3x" ORDER BY a;
SELECT rowid, * FROM "t3x";
END}
}
do_execsql_test 6.2 {
ALTER TABLE t3x RENAME a TO abcd;
SELECT sql FROM sqlite_master WHERE name = 'r3';
} {
{CREATE TRIGGER r3 AFTER INSERT ON "t3x" WHEN new.abcd NOT NULL BEGIN
SELECT abcd,b,c FROM "t3x" EXCEPT SELECT abcd,b,c FROM "t3x" ORDER BY abcd;
SELECT rowid, * FROM "t3x";
END}
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 7.0 {
CREATE TABLE t1(a,b,c,d,e,f);
INSERT INTO t1 VALUES(1,2,3,4,5,6);
CREATE TABLE t2(x,y,z);
}
do_execsql_test 7.1 {
SELECT a,b,c FROM t1 UNION SELECT d,e,f FROM t1 ORDER BY b,c;
} {1 2 3 4 5 6}
do_execsql_test 7.2 {
CREATE TRIGGER r1 AFTER INSERT ON t1 BEGIN
INSERT INTO t2
SELECT a,b,c FROM t1 UNION SELECT d,e,f FROM t1 ORDER BY b,c;
END;
INSERT INTO t1 VALUES(2,3,4,5,6,7);
SELECT * FROM t2;
} {1 2 3 2 3 4 4 5 6 5 6 7}
do_execsql_test 7.3 {
ALTER TABLE t1 RENAME TO xyzzy;
SELECT sql FROM sqlite_master WHERE name='r1'
} {
{CREATE TRIGGER r1 AFTER INSERT ON "xyzzy" BEGIN
INSERT INTO t2
SELECT a,b,c FROM "xyzzy" UNION SELECT d,e,f FROM "xyzzy" ORDER BY b,c;
END}
}
do_execsql_test 7.3 {
ALTER TABLE xyzzy RENAME c TO ccc;
SELECT sql FROM sqlite_master WHERE name='r1'
} {
{CREATE TRIGGER r1 AFTER INSERT ON "xyzzy" BEGIN
INSERT INTO t2
SELECT a,b,ccc FROM "xyzzy" UNION SELECT d,e,f FROM "xyzzy" ORDER BY b,ccc;
END}
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 8.0 {
CREATE TABLE t1(a, b, c);
CREATE TABLE t2(a, b, c);
CREATE TABLE t3(d, e, f);
CREATE VIEW v1 AS SELECT * FROM t1;
CREATE TRIGGER tr AFTER INSERT ON t3 BEGIN
UPDATE t2 SET a = new.d;
SELECT a, b, c FROM v1;
END;
}
do_execsql_test 8.1 {
INSERT INTO t3 VALUES(1, 2, 3);
}
# The following ALTER TABLE fails as if column "t1.a" is renamed the "a"
# in the "SELECT a, b, c FROM v1" within the trigger can no longer be
# resolved. But at one point there was a bug allowing the ALTER TABLE
# succeed. Which meant the subsequent INSERT statement would fail.
do_catchsql_test 8.2 {
ALTER TABLE t1 RENAME a TO aaa;
} {1 {error in trigger tr after rename: no such column: a}}
do_execsql_test 8.3 {
INSERT INTO t3 VALUES(4, 5, 6);
}
do_execsql_test 8.4 {
CREATE TABLE t4(a, b);
CREATE VIEW v4 AS SELECT * FROM t4 WHERE (a=1 AND 0) OR b=2;
}
# Branches of an expression tree that are optimized out by the AND
# optimization are renamed.
#
do_execsql_test 8.5 {
ALTER TABLE t4 RENAME a TO c;
SELECT sql FROM sqlite_master WHERE name = 'v4'
} {{CREATE VIEW v4 AS SELECT * FROM t4 WHERE (c=1 AND 0) OR b=2}}
# 2019-06-10 https://www.sqlite.org/src/info/533010b8cacebe82
reset_db
do_catchsql_test 8.6 {
CREATE TABLE t0(c0);
CREATE INDEX i0 ON t0(likelihood(1,2) AND 0);
ALTER TABLE t0 RENAME TO t1;
SELECT sql FROM sqlite_master WHERE name='i0';
} {1 {error in index i0: second argument to likelihood() must be a constant between 0.0 and 1.0}}
finish_test

739
testdata/tcl/altertab3.test vendored Normal file
View file

@ -0,0 +1,739 @@
# 2019 January 23
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#*************************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix altertab3
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
ifcapable !altertable {
finish_test
return
}
ifcapable windowfunc {
do_execsql_test 1.0 {
CREATE TABLE t1(a, b);
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
SELECT sum(b) OVER w FROM t1 WINDOW w AS (ORDER BY a);
END;
}
do_execsql_test 1.1 {
ALTER TABLE t1 RENAME a TO aaa;
}
do_execsql_test 1.2 {
SELECT sql FROM sqlite_master WHERE name='tr1'
} {{CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
SELECT sum(b) OVER w FROM t1 WINDOW w AS (ORDER BY aaa);
END}}
do_execsql_test 1.3 {
INSERT INTO t1 VALUES(1, 2);
}
} ;# windowfunc
#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.0 {
CREATE TABLE t1(a,b,c);
CREATE TABLE t2(a,b,c);
CREATE TRIGGER r1 AFTER INSERT ON t1 WHEN new.a NOT NULL BEGIN
SELECT a,b, a name FROM t1
INTERSECT
SELECT a,b,c FROM t1 WHERE b>='d' ORDER BY name;
SELECT new.c;
END;
}
do_execsql_test 2.1 {
ALTER TABLE t1 RENAME TO t1x;
SELECT sql FROM sqlite_master WHERE name = 'r1';
} {{CREATE TRIGGER r1 AFTER INSERT ON "t1x" WHEN new.a NOT NULL BEGIN
SELECT a,b, a name FROM "t1x"
INTERSECT
SELECT a,b,c FROM "t1x" WHERE b>='d' ORDER BY name;
SELECT new.c;
END}}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 3.0 {
CREATE TABLE t1(a, b, c, d);
CREATE VIEW v1 AS SELECT * FROM t1 WHERE a=1 OR (b IN ());
}
do_execsql_test 3.1 {
ALTER TABLE t1 RENAME b TO bbb;
}
do_execsql_test 3.2 {
SELECT sql FROM sqlite_master WHERE name = 'v1'
} {{CREATE VIEW v1 AS SELECT * FROM t1 WHERE a=1 OR (b IN ())}}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 4.0 {
CREATE TABLE t1(a, b);
CREATE TABLE t3(e, f);
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
INSERT INTO t2 VALUES(new.a, new.b);
END;
}
do_catchsql_test 4.1.2 {
BEGIN;
ALTER TABLE t3 RENAME TO t4;
} {1 {error in trigger tr1: no such table: main.t2}}
do_execsql_test 4.1.2 {
COMMIT;
}
do_execsql_test 4.1.3 {
SELECT type, name, tbl_name, sql
FROM sqlite_master WHERE type='table' AND name!='t1';
} {table t3 t3 {CREATE TABLE t3(e, f)}}
do_catchsql_test 4.2.1 {
BEGIN;
ALTER TABLE t3 RENAME e TO eee;
} {1 {error in trigger tr1: no such table: main.t2}}
do_execsql_test 4.2.2 {
COMMIT;
}
do_execsql_test 4.2.3 {
SELECT type, name, tbl_name, sql
FROM sqlite_master WHERE type='table' AND name!='t1';
} {table t3 t3 {CREATE TABLE t3(e, f)}}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 5.0 {
CREATE TABLE t1 (
c1 integer, c2, PRIMARY KEY(c1 collate rtrim),
UNIQUE(c2)
)
}
do_execsql_test 5.1 {
ALTER TABLE t1 RENAME c1 TO c3;
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 6.0 {
CREATE TEMPORARY TABLE Table0 (
Col0 INTEGER,
PRIMARY KEY(Col0 COLLATE RTRIM),
FOREIGN KEY (Col0) REFERENCES Table0
);
}
do_execsql_test 6.1 {
ALTER TABLE Table0 RENAME Col0 TO Col0;
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 7.1.0 {
CREATE TABLE t1(a,b,c);
CREATE TRIGGER AFTER INSERT ON t1 BEGIN
SELECT a, rank() OVER w1 FROM t1
WINDOW w1 AS (PARTITION BY b, percent_rank() OVER w1);
END;
}
do_execsql_test 7.1.2 {
ALTER TABLE t1 RENAME TO t1x;
SELECT sql FROM sqlite_master;
} {
{CREATE TABLE "t1x"(a,b,c)}
{CREATE TRIGGER AFTER INSERT ON "t1x" BEGIN
SELECT a, rank() OVER w1 FROM "t1x"
WINDOW w1 AS (PARTITION BY b, percent_rank() OVER w1);
END}
}
do_execsql_test 7.2.1 {
DROP TRIGGER after;
CREATE TRIGGER AFTER INSERT ON t1x BEGIN
SELECT a, rank() OVER w1 FROM t1x
WINDOW w1 AS (PARTITION BY b, percent_rank() OVER w1 ORDER BY d);
END;
}
do_catchsql_test 7.2.2 {
ALTER TABLE t1x RENAME TO t1;
} {1 {error in trigger AFTER: no such column: d}}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 8.0 {
CREATE TABLE t0(c0);
CREATE INDEX i0 ON t0('1' IN ());
}
do_execsql_test 8.1 {
ALTER TABLE t0 RENAME TO t1;
SELECT sql FROM sqlite_master;
} {
{CREATE TABLE "t1"(c0)}
{CREATE INDEX i0 ON "t1"('1' IN ())}
}
do_execsql_test 8.2.1 {
CREATE TABLE t2 (c0);
CREATE INDEX i2 ON t2((LIKELIHOOD(c0, 100) IN ()));
ALTER TABLE t2 RENAME COLUMN c0 TO c1;
}
do_execsql_test 8.2.2 {
SELECT sql FROM sqlite_master WHERE tbl_name = 't2';
} {
{CREATE TABLE t2 (c1)}
{CREATE INDEX i2 ON t2((LIKELIHOOD(c0, 100) IN ()))}
}
do_test 8.2.3 {
sqlite3 db2 test.db
db2 eval { INSERT INTO t2 VALUES (1), (2), (3) }
db close
} {}
db2 close
#-------------------------------------------------------------------------
reset_db
do_execsql_test 9.1 {
CREATE TABLE t1(a,b,c);
CREATE TRIGGER AFTER INSERT ON t1 WHEN new.a NOT NULL BEGIN
SELECT true WHERE (SELECT a, b FROM (t1)) IN ();
END;
}
do_execsql_test 9.2 {
ALTER TABLE t1 RENAME TO t1x;
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 10.1 {
CREATE TABLE t1(a, b, c);
CREATE TABLE t2(a, b, c);
CREATE VIEW v1 AS SELECT * FROM t1 WHERE (
SELECT t1.a FROM t1, t2
) IN () OR t1.a=5;
}
do_execsql_test 10.2 {
ALTER TABLE t2 RENAME TO t3;
SELECT sql FROM sqlite_master WHERE name='v1';
} {
{CREATE VIEW v1 AS SELECT * FROM t1 WHERE (
SELECT t1.a FROM t1, t2
) IN () OR t1.a=5}
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 11.1 {
CREATE TABLE t1(
a,b,c,d,e,f,g,h,j,jj,jjb,k,aa,bb,cc,dd,ee DEFAULT 3.14,
ff DEFAULT('hiccup'),Wg NOD NULL DEFAULT(false)
);
CREATE TRIGGER b AFTER INSERT ON t1 WHEN new.a BEGIN
SELECT a, sum() w3 FROM t1
WINDOW b AS (ORDER BY NOT EXISTS(SELECT 1 FROM abc));
END;
}
do_catchsql_test 11.2 {
ALTER TABLE t1 RENAME TO t1x;
} {1 {error in trigger b: no such table: main.abc}}
do_execsql_test 11.3 {
DROP TRIGGER b;
CREATE TRIGGER b AFTER INSERT ON t1 WHEN new.a BEGIN
SELECT a, sum() w3 FROM t1
WINDOW b AS (ORDER BY NOT EXISTS(SELECT 1 FROM t1));
END;
} {}
do_execsql_test 11.4 {
ALTER TABLE t1 RENAME TO t1x;
SELECT sql FROM sqlite_master WHERE name = 'b';
} {
{CREATE TRIGGER b AFTER INSERT ON "t1x" WHEN new.a BEGIN
SELECT a, sum() w3 FROM "t1x"
WINDOW b AS (ORDER BY NOT EXISTS(SELECT 1 FROM "t1x"));
END}
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 12.1 {
CREATE TABLE t1(a,b,c,d,e,f,g,h,j,jj,Zjj,k,aQ,bb,cc,dd,ee DEFAULT 3.14,
ff DEFAULT('hiccup'),gg NOD NULL DEFAULT(false));
CREATE TRIGGER AFTER INSERT ON t1 WHEN new.a NOT NULL BEGIN
SELECT b () OVER , dense_rank() OVER d, d () OVER w1
FROM t1
WINDOW
w1 AS
( w1 ORDER BY d
ROWS BETWEEN 2 NOT IN(SELECT a, sum(d) w2,max(d)OVER FROM t1
WINDOW
w1 AS
(PARTITION BY d
ROWS BETWEEN '' PRECEDING AND false FOLLOWING),
d AS
(PARTITION BY b ORDER BY d
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
) PRECEDING AND 1 FOLLOWING),
w2 AS
(PARTITION BY b ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW),
w3 AS
(PARTITION BY b ORDER BY d
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
;
SELECT a, sum(d) w2,max(d)OVER FROM t1
WINDOW
w1 AS
(PARTITION BY d
ROWS BETWEEN '' PRECEDING AND false FOLLOWING),
d AS
(PARTITION BY b ORDER BY d
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
;
END;
}
do_execsql_test 12.2 {
ALTER TABLE t1 RENAME TO t1x;
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 13.1 {
CREATE TABLE t1(a);
CREATE TRIGGER r1 INSERT ON t1 BEGIN
SELECT a(*) OVER (ORDER BY (SELECT 1)) FROM t1;
END;
}
do_execsql_test 13.2 {
ALTER TABLE t1 RENAME TO t1x;
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 14.1 {
CREATE TABLE t1(a);
CREATE TABLE t2(b);
CREATE TRIGGER AFTER INSERT ON t1 BEGIN
SELECT sum() FILTER (WHERE (SELECT sum() FILTER (WHERE 0)) AND a);
END;
}
do_catchsql_test 14.2 {
ALTER TABLE t1 RENAME TO t1x;
} {1 {error in trigger AFTER: no such column: a}}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 16.1 {
CREATE TABLE t1(x);
CREATE TRIGGER AFTER INSERT ON t1 BEGIN
SELECT (WITH t2 AS (WITH t3 AS (SELECT true)
SELECT * FROM t3 ORDER BY true COLLATE nocase)
SELECT 11);
WITH t4 AS (SELECT * FROM t1) SELECT 33;
END;
}
do_execsql_test 16.2 {
ALTER TABLE t1 RENAME TO t1x;
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 17.1 {
CREATE TABLE t1(a,b,c);
CREATE TRIGGER AFTER INSERT ON t1 WHEN new.a NOT NULL BEGIN
SELECT a () FILTER (WHERE a>0) FROM t1;
END;
}
do_execsql_test 17.2 {
ALTER TABLE t1 RENAME TO t1x;
ALTER TABLE t1x RENAME a TO aaa;
SELECT sql FROM sqlite_master WHERE type='trigger';
} {
{CREATE TRIGGER AFTER INSERT ON "t1x" WHEN new.aaa NOT NULL BEGIN
SELECT a () FILTER (WHERE aaa>0) FROM "t1x";
END}
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 18.1 {
CREATE TABLE t1(a,b);
CREATE TRIGGER r1 AFTER INSERT ON t1 BEGIN
SELECT a, b FROM t1
INTERSECT SELECT b,a FROM t1
ORDER BY b IN (
SELECT a UNION SELECT b
FROM t1
ORDER BY b COLLATE nocase
)
;
END;
}
do_catchsql_test 18.2 {
SELECT a, b FROM t1
INTERSECT
SELECT b,a FROM t1
ORDER BY b IN (
SELECT a UNION SELECT b
FROM t1
ORDER BY b COLLATE nocase
);
} {1 {1st ORDER BY term does not match any column in the result set}}
do_catchsql_test 18.3 {
ALTER TABLE t1 RENAME TO t1x;
} {1 {error in trigger r1: 1st ORDER BY term does not match any column in the result set}}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 19.0 {
CREATE TABLE a(a,h CONSTRAINT a UNIQUE ON CONFLICT FAIL,CONSTRAINT a);
}
foreach {tn v res} {
1 {
CREATE VIEW q AS SELECT 123
WINDOW x AS (
RANGE BETWEEN UNBOUNDED PRECEDING AND INDEXED() OVER(
PARTITION BY ( WITH x AS(VALUES(col1)) VALUES(453) )
)
FOLLOWING
)
} {1 {error in view q: no such column: col1}}
2 {
CREATE VIEW q AS SELECT
CAST(CAST(CAST(CAST(CAST(CAST(CAST(CAST(CAST(CAST(CAST(RIGHT
AS)AS)AS)AS)AS)AS)AS)AS)AS)AS)AS)WINDOW x AS(RANGE BETWEEN UNBOUNDED
PRECEDING AND INDEXED(*)OVER(PARTITION BY
CROSS,CROSS,NATURAL,sqlite_master(*)OVER a,(WITH a AS(VALUES(LEFT)UNION
VALUES(LEFT)UNION VALUES(LEFT)UNION VALUES(LEFT)UNION VALUES(LEFT)UNION
VALUES(LEFT)UNION VALUES(LEFT))VALUES(LEFT))IN
STORED,LEFT,LEFT,LEFT,LEFT,LEFT,LEFT)*LEFT FOLLOWING)ORDER BY
LEFT,LEFT,LEFT,LEFT,LEFT,LEFT,LEFT,LEFT,LEFT,LEFT,LEFT LIMIT
LEFT,INDEXED(*)OVER(PARTITION BY
CROSS,CROSS,CROSS,LEFT,INDEXED(*)OVER(PARTITION BY
CROSS,CROSS,CROSS),INDEXED(*)OVER(PARTITION BY
LEFT,LEFT,LEFT,LEFT,LEFT,LEFT,LEFT,LEFT,LEFT,LEFT,LEFT),
LEFT,LEFT,INNER,CROSS,CROSS,CROSS,INNER,NATURAL ORDER BY
OUTER,NATURAL,NATURAL,NATURAL,NATURAL,NATURAL,NATURAL,NATURAL,INNER,
INNER,INNER NULLS LAST GROUPS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED
FOLLOWING);
} {1 {error in view q: no such column: LEFT}}
3 {
CREATE VIEW q AS SELECT 99 WINDOW x AS (RANGE BETWEEN UNBOUNDED PRECEDING
AND count(*)OVER(PARTITION BY (WITH a AS(VALUES(2),(x3))VALUES(0)))
FOLLOWING)ORDER BY x2,sum(1)OVER(PARTITION BY avg(5)OVER(PARTITION BY x1));
} {1 {error in view q: no such column: x3}}
} {
do_execsql_test 19.$tn.1 "
DROP VIEW IF EXISTS q;
$v
" {}
do_catchsql_test 19.$tn.2 {
ALTER TABLE a RENAME TO g;
} $res
}
# Verify that the "if( pParse->nErr ) return WRC_Abort" at the top of the
# renameUnmapSelectCb() routine in alter.c (2019-12-04) is really required.
#
sqlite3 db :memory:
do_catchsql_test 20.10 {
CREATE TABLE s(a, b, c);
CREATE INDEX k ON s( (WITH s AS( SELECT * ) VALUES(2) ) IN () );
ALTER TABLE s RENAME a TO a2;
} {1 {error in index k: no tables specified}}
#------------------------------------------------------------------------
#
reset_db
do_execsql_test 21.1 {
CREATE TABLE s(col);
CREATE VIEW v AS SELECT (
WITH x(a) AS(SELECT * FROM s) VALUES(RIGHT)
) IN() ;
CREATE TABLE a(a);
ALTER TABLE a RENAME a TO b;
}
#------------------------------------------------------------------------
#
reset_db
do_execsql_test 22.1 {
CREATE TABLE t1(a);
CREATE VIEW v2(b) AS SELECT * FROM v2;
}
do_catchsql_test 22.2 {
ALTER TABLE t1 RENAME TO t4;
} {1 {error in view v2: view v2 is circularly defined}}
do_execsql_test 22.3 {
DROP VIEW v2;
CREATE VIEW v2(b) AS WITH t3 AS (SELECT b FROM v2) SELECT * FROM t3;
}
do_catchsql_test 22.4 {
ALTER TABLE t1 RENAME TO t4;
} {1 {error in view v2: view v2 is circularly defined}}
do_execsql_test 22.5 {
DROP VIEW v2;
CREATE VIEW v2(b) AS WITH t3 AS (SELECT b FROM v2) VALUES(1);
}
do_catchsql_test 22.6 {
ALTER TABLE t1 RENAME TO t4;
} {0 {}}
#------------------------------------------------------------------------
#
reset_db
do_execsql_test 23.1 {
CREATE TABLE t1(x);
CREATE TRIGGER r1 AFTER INSERT ON t1 BEGIN
UPDATE t1 SET (c,d)=((SELECT 1 FROM t1 JOIN t2 ON b=x),1);
END;
}
do_catchsql_test 23.2 {
ALTER TABLE t1 RENAME TO t1x;
} {1 {error in trigger r1: no such table: main.t2}}
#------------------------------------------------------------------------
#
reset_db
do_execsql_test 23.1 {
CREATE TABLE v0 (a);
CREATE VIEW v2 (v3) AS
WITH x1 AS (SELECT * FROM v2)
SELECT v3 AS x, v3 AS y FROM v2;
}
do_catchsql_test 23.2 {
SELECT * FROM v2
} {1 {view v2 is circularly defined}}
db close
sqlite3 db test.db
do_catchsql_test 23.3 {
ALTER TABLE v0 RENAME TO t3 ;
} {1 {error in view v2: view v2 is circularly defined}}
#------------------------------------------------------------------------
#
reset_db
do_execsql_test 24.1 {
CREATE TABLE v0 (v1);
CREATE TABLE v2 (v3 INTEGER UNIQUE ON CONFLICT ABORT);
CREATE TRIGGER x AFTER INSERT ON v2 WHEN (
( SELECT v1 AS PROMO_REVENUE FROM v2 JOIN v0 USING ( VALUE ) ) AND 0 )
BEGIN
DELETE FROM v2;
END;
}
do_catchsql_test 24.2 {
ALTER TABLE v0 RENAME TO x ;
} {1 {error in trigger x: cannot join using column VALUE - column not present in both tables}}
do_execsql_test 24.3 {
DROP TRIGGER x;
CREATE TRIGGER x AFTER INSERT ON v2 WHEN (
0 AND (SELECT rowid FROM v0)
) BEGIN
DELETE FROM v2;
END;
}
do_execsql_test 24.4 {
ALTER TABLE v0 RENAME TO xyz;
SELECT sql FROM sqlite_master WHERE type='trigger'
} {{CREATE TRIGGER x AFTER INSERT ON v2 WHEN (
0 AND (SELECT rowid FROM "xyz")
) BEGIN
DELETE FROM v2;
END}}
#------------------------------------------------------------------------
#
reset_db
do_execsql_test 25.1 {
CREATE TABLE t1(a, b, c);
CREATE TABLE t2(a, b, c);
CREATE TRIGGER ttt AFTER INSERT ON t1 BEGIN
UPDATE t1 SET a=t2.a FROM t2 WHERE t1.a=t2.a;
END;
}
#do_execsql_test 25.2 {
# ALTER TABLE t2 RENAME COLUMN a TO aaa;
#}
#------------------------------------------------------------------------
#
reset_db
do_execsql_test 26.1 {
CREATE TABLE t1(x);
CREATE TABLE t3(y);
CREATE TABLE t4(z);
CREATE TRIGGER tr1 INSERT ON t3 BEGIN
UPDATE t3 SET y=z FROM (SELECT z FROM t4);
END;
CREATE TRIGGER tr2 INSERT ON t3 BEGIN
UPDATE t3 SET y=abc FROM (SELECT x AS abc FROM t1);
END;
}
do_execsql_test 26.2 {
ALTER TABLE t1 RENAME TO t2;
}
do_execsql_test 26.3 {
ALTER TABLE t2 RENAME x TO xx;
}
do_execsql_test 26.4 {
SELECT sql FROM sqlite_schema WHERE name='tr2'
} {
{CREATE TRIGGER tr2 INSERT ON t3 BEGIN
UPDATE t3 SET y=abc FROM (SELECT xx AS abc FROM "t2");
END}
}
# 2020-11-02 OSSFuzz
#
reset_db
do_execsql_test 26.5 {
CREATE TABLE t1(xx);
CREATE TRIGGER xx INSERT ON t1 BEGIN
UPDATE t1 SET xx=xx FROM(SELECT xx);
END;
} {}
do_catchsql_test 26.6 {
ALTER TABLE t1 RENAME TO t2;
} {1 {error in trigger xx: no such column: xx}}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 27.1 {
CREATE TABLE t1(a, b AS ((WITH w1 (xyz) AS ( SELECT t1.b FROM t1 ) SELECT 123) IN ()), c);
}
do_execsql_test 27.2 {
ALTER TABLE t1 DROP COLUMN c;
SELECT sql FROM sqlite_schema WHERE name = 't1';
} {
{CREATE TABLE t1(a, b AS ((WITH w1 (xyz) AS ( SELECT t1.b FROM t1 ) SELECT 123) IN ()))}
}
do_execsql_test 27.3 {
CREATE TABLE t0(c0 , c1 AS (CASE TRUE NOT IN () WHEN NULL THEN CASE + 0xa ISNULL WHEN NOT + 0x9 THEN t0.c1 ELSE CURRENT_TIME LIKE CAST (t0.c1 REGEXP '-([1-9]\d*.\d*|0\.\d*[1-9]\d*)'ESCAPE (c1) COLLATE BINARY BETWEEN c1 AND c1 NOT IN (WITH t4 (c0) AS (WITH t3 (c0) AS NOT MATERIALIZED (WITH RECURSIVE t2 (c0) AS (WITH RECURSIVE t1 AS (VALUES (x'717171ff71717171' ) ) SELECT DISTINCT t0.c0 FROM t0 NOT INDEXED WHERE t0.c0 =t0.c0 GROUP BY 0x9 ) SELECT DISTINCT t0.c0 FROM t0 NOT INDEXED WHERE t0.c0 =t0.c1 ) SELECT DISTINCT t0.c0 FROM t0 NOT INDEXED WHERE t0.c0 =t0.c0 GROUP BY typeof(0x9 ) ) SELECT DISTINCT t0.c0 FROM t0 NOT INDEXED WHERE t0.c0 =t0.c0 GROUP BY typeof(typeof(0x9 ) ) ) IN t0 BETWEEN typeof(typeof(typeof(hex(*) FILTER (WHERE + x'5ccd1e68' ) ) ) ) AND 1 >0xa AS BLOB (+4.4E4 , -0xe ) ) END <> c1 IN () END ) VIRTUAL , c35 PRIMARY KEY , c60 , c64 NUMERIC (-6.8 , -0xE ) ) WITHOUT ROWID ;
} {}
do_execsql_test 27.4 {
ALTER TABLE t0 DROP COLUMN c60;
} {}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 28.1 {
CREATE TABLE t1(a,b,c,d);
CREATE TRIGGER AFTER INSERT ON t1 BEGIN
UPDATE t1 SET (c,d)=(a,b);
END;
ALTER TABLE t1 RENAME TO t2;
}
do_execsql_test 28.2 {
SELECT sql FROM sqlite_schema WHERE type='trigger'
} {{CREATE TRIGGER AFTER INSERT ON "t2" BEGIN
UPDATE "t2" SET (c,d)=(a,b);
END}}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 29.1 {
CREATE TABLE t1(x, y);
CREATE TRIGGER Trigger1 DELETE ON t1
BEGIN
SELECT t1.*, t1.x FROM t1 ORDER BY t1.x;
END;
}
do_execsql_test 29.2 {
ALTER TABLE t1 RENAME x TO z;
}
do_execsql_test 29.3 {
ALTER TABLE t1 RENAME TO t2;
}
do_execsql_test 29.4 {
CREATE TRIGGER tr2 AFTER DELETE ON t2 BEGIN
SELECT z, y FROM (
SELECT t2.* FROM t2
);
END;
}
do_execsql_test 29.5 {
DELETE FROM t2
}
do_execsql_test 29.6 {
ALTER TABLE t2 RENAME TO t3;
}
do_execsql_test 29.7 {
SELECT sql FROM sqlite_schema WHERE type='trigger'
} {
{CREATE TRIGGER Trigger1 DELETE ON "t3"
BEGIN
SELECT "t3".*, "t3".z FROM "t3" ORDER BY "t3".z;
END}
{CREATE TRIGGER tr2 AFTER DELETE ON "t3" BEGIN
SELECT z, y FROM (
SELECT "t3".* FROM "t3"
);
END}
}
finish_test

163
testdata/tcl/altertrig.test vendored Normal file
View file

@ -0,0 +1,163 @@
# 2022 May 27
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#*************************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix altertrig
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
ifcapable !altertable {
finish_test
return
}
proc collapse_whitespace {in} {
regsub -all {[ \t\n]+} [string trim $in] { }
}
proc do_whitespace_sql_test {tn sql res} {
set got [execsql $sql]
set wgot [list]
set wres [list]
foreach g $got { lappend wgot [collapse_whitespace $g] }
foreach r $res { lappend wres [collapse_whitespace $r] }
uplevel [list do_test $tn [list set {} $wgot] $wres]
}
do_execsql_test 1.0 {
CREATE TABLE t1(x);
CREATE TABLE t2(y);
CREATE TABLE t3(z);
CREATE TABLE t4(a);
CREATE TRIGGER r1 INSERT ON t1 BEGIN
UPDATE t1 SET d='xyz' FROM t2, t3;
END;
}
do_whitespace_sql_test 1.1 {
ALTER TABLE t3 RENAME TO t5;
SELECT sql FROM sqlite_schema WHERE type='trigger';
} {{
CREATE TRIGGER r1 INSERT ON t1 BEGIN
UPDATE t1 SET d='xyz' FROM t2, "t5";
END
}}
do_execsql_test 1.2 {
DROP TRIGGER r1;
CREATE TRIGGER r1 INSERT ON t1 BEGIN
UPDATE t1 SET d='xyz' FROM t2, (SELECT * FROM t5);
END;
}
do_whitespace_sql_test 1.3 {
ALTER TABLE t5 RENAME TO t3;
SELECT sql FROM sqlite_schema WHERE type='trigger';
} {{
CREATE TRIGGER r1 INSERT ON t1 BEGIN
UPDATE t1 SET d='xyz' FROM t2, (SELECT * FROM "t3");
END
}}
foreach {tn alter update final} {
1 {
ALTER TABLE t3 RENAME TO t10
} {
UPDATE t1 SET d='xyz' FROM t2, (SELECT * FROM t3)
} {
UPDATE t1 SET d='xyz' FROM t2, (SELECT * FROM "t10")
}
2 {
ALTER TABLE t3 RENAME TO t10
} {
UPDATE t1 SET a='xyz' FROM t3, (SELECT * FROM (SELECT e FROM t3))
} {
UPDATE t1 SET a='xyz' FROM "t10", (SELECT * FROM (SELECT e FROM "t10"))
}
3 {
ALTER TABLE t3 RENAME e TO abc
} {
UPDATE t1 SET a='xyz' FROM t3, (SELECT * FROM (SELECT e FROM t3))
} {
UPDATE t1 SET a='xyz' FROM t3, (SELECT * FROM (SELECT abc FROM t3))
}
4 {
ALTER TABLE t2 RENAME c TO abc
} {
UPDATE t1 SET a='xyz' FROM t3, (SELECT 1 FROM t2 WHERE c)
} {
UPDATE t1 SET a='xyz' FROM t3, (SELECT 1 FROM t2 WHERE abc)
}
5 {
ALTER TABLE t2 RENAME c TO abc
} {
UPDATE t1 SET a=t2.c FROM t2
} {
UPDATE t1 SET a=t2.abc FROM t2
}
6 {
ALTER TABLE t2 RENAME c TO abc
} {
UPDATE t1 SET a=t2.c FROM t2, t3
} {
UPDATE t1 SET a=t2.abc FROM t2, t3
}
7 {
ALTER TABLE t4 RENAME e TO abc
} {
UPDATE t1 SET a=1 FROM t3 NATURAL JOIN t4 WHERE t4.e=a
} {
UPDATE t1 SET a=1 FROM t3 NATURAL JOIN t4 WHERE t4.abc=a
}
8 {
ALTER TABLE t4 RENAME TO abc
} {
UPDATE t1 SET a=1 FROM t3 NATURAL JOIN t4 WHERE t4.e=a
} {
UPDATE t1 SET a=1 FROM t3 NATURAL JOIN "abc" WHERE "abc".e=a
}
} {
reset_db
do_execsql_test 2.$tn.1 {
CREATE TABLE t1(a,b);
CREATE TABLE t2(c,d);
CREATE TABLE t3(e,f);
CREATE TABLE t4(e,f);
}
do_execsql_test 2.$tn.2 "
CREATE TRIGGER r1 INSERT ON t1 BEGIN
$update;
END
"
do_execsql_test 2.$tn.3 $alter
do_whitespace_sql_test 2.$tn.4 {
SELECT sqL FROM sqlite_schema WHERE type='trigger'
} "{
CREATE TRIGGER r1 INSERT ON t1 BEGIN
$final;
END
}"
}
finish_test

117
testdata/tcl/amatch1.test vendored Normal file
View file

@ -0,0 +1,117 @@
# 2013-09-30
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#*************************************************************************
# This file implements regression tests for "approximate_match" virtual
# table that is in the "amatch.c" extension.
#
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# If SQLITE_ENABLE_FTS4 is defined, omit this file.
ifcapable !fts3 {
finish_test
return
}
# Create the fts_kjv_genesis procedure which fills and FTS3/4 table with
# the complete text of the Book of Genesis.
#
source $testdir/genesis.tcl
do_test amatch1-1.0 {
db eval {
CREATE VIRTUAL TABLE t1 USING fts4(words); --, tokenize porter);
}
fts_kjv_genesis
db eval {
INSERT INTO t1(t1) VALUES('optimize');
CREATE VIRTUAL TABLE temp.t1aux USING fts4aux(main, t1);
SELECT term FROM t1aux WHERE col=0 ORDER BY 1 LIMIT 5
}
} {a abated abel abelmizraim abidah}
do_test amatch1-1.1 {
db eval {
SELECT term FROM t1aux WHERE term>'b' AND col=0 ORDER BY 1 LIMIT 5
}
} {baalhanan babel back backward bad}
do_test amatch1-1.2 {
db eval {
SELECT term FROM t1aux WHERE term>'b' AND col=0 LIMIT 5
}
} {baalhanan babel back backward bad}
# Load the amatch extension
load_static_extension db amatch
do_execsql_test amatch1-2.0 {
CREATE TABLE costs(iLang, cFrom, cTo, Cost);
INSERT INTO costs VALUES(0, '', '?', 100);
INSERT INTO costs VALUES(0, '?', '', 100);
INSERT INTO costs VALUES(0, '?', '?', 150);
CREATE TABLE vocab(w TEXT UNIQUE);
INSERT OR IGNORE INTO vocab SELECT term FROM t1aux;
CREATE VIRTUAL TABLE t2 USING approximate_match(
vocabulary_table=t1aux,
vocabulary_word=term,
edit_distances=costs
);
CREATE VIRTUAL TABLE t3 USING approximate_match(
vocabulary_table=vocab,
vocabulary_word=w,
edit_distances=costs
);
CREATE VIRTUAL TABLE t4 USING approximate_match(
vocabulary_table=vtemp,
vocabulary_word=w,
edit_distances=costs
);
} {}
puts "Query against fts4aux: [time {
do_execsql_test amatch1-2.1 {
SELECT word, distance FROM t2
WHERE word MATCH 'josxph' AND distance<300;
} {joseph 150}} 1]"
puts "Query against ordinary table: [time {
do_execsql_test amatch1-2.2 {
SELECT word, distance FROM t3
WHERE word MATCH 'josxph' AND distance<300;
} {joseph 150}} 1]"
puts "Temp table initialized from fts4aux: [time {
do_execsql_test amatch1-2.3a {
CREATE TEMP TABLE vtemp(w TEXT UNIQUE);
INSERT OR IGNORE INTO vtemp SELECT term FROM t1aux;
} {}} 1]"
puts "Query against temp table: [time {
do_execsql_test amatch1-2.3b {
SELECT word, distance FROM t4
WHERE word MATCH 'josxph' AND distance<300;
} {joseph 150}} 1]"
do_execsql_test amatch1-2.11 {
SELECT word, distance FROM t2
WHERE word MATCH 'joxxph' AND distance<=300;
} {joseph 300}
do_execsql_test amatch1-2.12 {
SELECT word, distance FROM t3
WHERE word MATCH 'joxxph' AND distance<=300;
} {joseph 300}
do_execsql_test amatch1-2.21 {
SELECT word, distance FROM t2
WHERE word MATCH 'joxxph' AND distance<300;
} {}
do_execsql_test amatch1-2.22 {
SELECT word, distance FROM t3
WHERE word MATCH 'joxxph' AND distance<300;
} {}
finish_test

380
testdata/tcl/analyze.test vendored Normal file
View file

@ -0,0 +1,380 @@
# 2005 July 22
#
# 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.
# This file implements tests for the ANALYZE command.
#
# $Id: analyze.test,v 1.9 2008/08/11 18:44:58 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# There is nothing to test if ANALYZE is disable for this build.
#
ifcapable {!analyze} {
finish_test
return
}
# Basic sanity checks.
#
do_test analyze-1.1 {
catchsql {
ANALYZE no_such_table
}
} {1 {no such table: no_such_table}}
do_test analyze-1.2 {
execsql {
SELECT count(*) FROM sqlite_master WHERE name='sqlite_stat1'
}
} {0}
do_test analyze-1.3 {
catchsql {
ANALYZE no_such_db.no_such_table
}
} {1 {unknown database no_such_db}}
do_test analyze-1.4 {
execsql {
SELECT count(*) FROM sqlite_master WHERE name='sqlite_stat1'
}
} {0}
do_test analyze-1.5.1 {
catchsql {
ANALYZE
}
} {0 {}}
do_test analyze-1.5.2 {
catchsql {
PRAGMA empty_result_callbacks=1;
ANALYZE
}
} {0 {}}
do_test analyze-1.6 {
execsql {
SELECT count(*) FROM sqlite_master WHERE name='sqlite_stat1'
}
} {1}
do_test analyze-1.6.2 {
catchsql {
CREATE INDEX stat1idx ON sqlite_stat1(idx);
}
} {1 {table sqlite_stat1 may not be indexed}}
do_test analyze-1.6.3 {
catchsql {
CREATE INDEX main.stat1idx ON SQLite_stat1(idx);
}
} {1 {table sqlite_stat1 may not be indexed}}
do_test analyze-1.7 {
execsql {
SELECT * FROM sqlite_stat1 WHERE idx NOT NULL
}
} {}
do_test analyze-1.8 {
catchsql {
ANALYZE main
}
} {0 {}}
do_test analyze-1.9 {
execsql {
SELECT * FROM sqlite_stat1 WHERE idx NOT NULL
}
} {}
do_test analyze-1.10 {
catchsql {
CREATE TABLE t1(a,b);
ANALYZE main.t1;
}
} {0 {}}
do_test analyze-1.11 {
execsql {
SELECT * FROM sqlite_stat1
}
} {}
do_test analyze-1.12 {
catchsql {
ANALYZE t1;
}
} {0 {}}
do_test analyze-1.13 {
execsql {
SELECT * FROM sqlite_stat1
}
} {}
# Create some indices that can be analyzed. But do not yet add
# data. Without data in the tables, no analysis is done.
#
do_test analyze-2.1 {
execsql {
CREATE INDEX t1i1 ON t1(a);
ANALYZE main.t1;
SELECT * FROM sqlite_stat1 ORDER BY idx;
}
} {}
do_test analyze-2.2 {
execsql {
CREATE INDEX t1i2 ON t1(b);
ANALYZE t1;
SELECT * FROM sqlite_stat1 ORDER BY idx;
}
} {}
do_test analyze-2.3 {
execsql {
CREATE INDEX t1i3 ON t1(a,b);
ANALYZE main;
SELECT * FROM sqlite_stat1 ORDER BY idx;
}
} {}
# Start adding data to the table. Verify that the analysis
# is done correctly.
#
do_test analyze-3.1 {
execsql {
INSERT INTO t1 VALUES(1,2);
INSERT INTO t1 VALUES(1,3);
ANALYZE main.t1;
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
}
} {t1i1 {2 2} t1i2 {2 1} t1i3 {2 2 1}}
do_test analyze-3.2 {
execsql {
INSERT INTO t1 VALUES(1,4);
INSERT INTO t1 VALUES(1,5);
ANALYZE t1;
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
}
} {t1i1 {4 4} t1i2 {4 1} t1i3 {4 4 1}}
do_test analyze-3.3 {
execsql {
INSERT INTO t1 VALUES(2,5);
ANALYZE main;
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
}
} {t1i1 {5 3} t1i2 {5 2} t1i3 {5 3 1}}
do_test analyze-3.4 {
execsql {
CREATE TABLE t2 AS SELECT * FROM t1;
CREATE INDEX t2i1 ON t2(a);
CREATE INDEX t2i2 ON t2(b);
CREATE INDEX t2i3 ON t2(a,b);
ANALYZE;
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
}
} {t1i1 {5 3} t1i2 {5 2} t1i3 {5 3 1} t2i1 {5 3} t2i2 {5 2} t2i3 {5 3 1}}
do_test analyze-3.5 {
execsql {
DROP INDEX t2i3;
ANALYZE t1;
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
}
} {t1i1 {5 3} t1i2 {5 2} t1i3 {5 3 1} t2i1 {5 3} t2i2 {5 2}}
do_test analyze-3.6 {
execsql {
ANALYZE t2;
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
}
} {t1i1 {5 3} t1i2 {5 2} t1i3 {5 3 1} t2i1 {5 3} t2i2 {5 2}}
do_test analyze-3.7 {
execsql {
DROP INDEX t2i2;
ANALYZE t2;
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
}
} {t1i1 {5 3} t1i2 {5 2} t1i3 {5 3 1} t2i1 {5 3}}
do_test analyze-3.8 {
execsql {
CREATE TABLE t3 AS SELECT a, b, rowid AS c, 'hi' AS d FROM t1;
CREATE INDEX t3i1 ON t3(a);
CREATE INDEX t3i2 ON t3(a,b,c,d);
CREATE INDEX t3i3 ON t3(d,b,c,a);
DROP TABLE t1;
DROP TABLE t2;
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
}
} {}
do_test analyze-3.9 {
execsql {
ANALYZE;
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
}
} {t3i1 {5 3} t3i2 {5 3 1 1 1} t3i3 {5 5 2 1 1}}
do_test analyze-3.10 {
execsql {
CREATE TABLE [silly " name](a, b, c);
CREATE INDEX 'foolish '' name' ON [silly " name](a, b);
CREATE INDEX 'another foolish '' name' ON [silly " name](c);
INSERT INTO [silly " name] VALUES(1, 2, 3);
INSERT INTO [silly " name] VALUES(4, 5, 6);
ANALYZE;
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
}
} {{another foolish ' name} {2 1} {foolish ' name} {2 1 1} t3i1 {5 3} t3i2 {5 3 1 1 1} t3i3 {5 5 2 1 1}}
do_test analyze-3.11 {
execsql {
DROP INDEX "foolish ' name";
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
}
} {{another foolish ' name} {2 1} t3i1 {5 3} t3i2 {5 3 1 1 1} t3i3 {5 5 2 1 1}}
do_test analyze-3.11 {
execsql {
DROP TABLE "silly "" name";
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
}
} {t3i1 {5 3} t3i2 {5 3 1 1 1} t3i3 {5 5 2 1 1}}
# Try corrupting the sqlite_stat1 table and make sure the
# database is still able to function.
#
do_test analyze-4.0 {
sqlite3 db2 test.db
db2 eval {
CREATE TABLE t4(x,y,z);
CREATE INDEX t4i1 ON t4(x);
CREATE INDEX t4i2 ON t4(y);
INSERT INTO t4 SELECT a,b,c FROM t3;
}
db2 close
db close
sqlite3 db test.db
execsql {
ANALYZE;
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
}
} {t3i1 {5 3} t3i2 {5 3 1 1 1} t3i3 {5 5 2 1 1} t4i1 {5 3} t4i2 {5 2}}
do_test analyze-4.1 {
execsql {
PRAGMA writable_schema=on;
INSERT INTO sqlite_stat1 VALUES(null,null,null);
PRAGMA writable_schema=off;
}
db close
sqlite3 db test.db
execsql {
SELECT * FROM t4 WHERE x=1234;
}
} {}
do_test analyze-4.2 {
execsql {
PRAGMA writable_schema=on;
DELETE FROM sqlite_stat1;
INSERT INTO sqlite_stat1 VALUES('t4','t4i1','nonsense');
INSERT INTO sqlite_stat1 VALUES('t4','t4i2','120897349817238741092873198273409187234918720394817209384710928374109827172901827349871928741910');
PRAGMA writable_schema=off;
}
db close
sqlite3 db test.db
execsql {
SELECT * FROM t4 WHERE x=1234;
}
} {}
do_test analyze-4.3 {
execsql {
INSERT INTO sqlite_stat1 VALUES('t4','xyzzy','0 1 2 3');
}
db close
sqlite3 db test.db
execsql {
SELECT * FROM t4 WHERE x=1234;
}
} {}
# Verify that DROP TABLE and DROP INDEX remove entries from the
# sqlite_stat1 and sqlite_stat4 tables.
#
do_test analyze-5.0 {
execsql {
DELETE FROM t3;
DELETE FROM t4;
INSERT INTO t3 VALUES(1,2,3,4);
INSERT INTO t3 VALUES(5,6,7,8);
INSERT INTO t3 SELECT a+8, b+8, c+8, d+8 FROM t3;
INSERT INTO t3 SELECT a+16, b+16, c+16, d+16 FROM t3;
INSERT INTO t3 SELECT a+32, b+32, c+32, d+32 FROM t3;
INSERT INTO t3 SELECT a+64, b+64, c+64, d+64 FROM t3;
INSERT INTO t4 SELECT a, b, c FROM t3;
ANALYZE;
SELECT DISTINCT idx FROM sqlite_stat1 ORDER BY 1;
SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1;
}
} {t3i1 t3i2 t3i3 t4i1 t4i2 t3 t4}
ifcapable stat4 {
do_test analyze-5.1 {
execsql {
SELECT DISTINCT idx FROM sqlite_stat4 ORDER BY 1;
SELECT DISTINCT tbl FROM sqlite_stat4 ORDER BY 1;
}
} {t3i1 t3i2 t3i3 t4i1 t4i2 t3 t4}
}
do_test analyze-5.2 {
execsql {
DROP INDEX t3i2;
SELECT DISTINCT idx FROM sqlite_stat1 ORDER BY 1;
SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1;
}
} {t3i1 t3i3 t4i1 t4i2 t3 t4}
ifcapable stat4 {
do_test analyze-5.3 {
execsql {
SELECT DISTINCT idx FROM sqlite_stat4 ORDER BY 1;
SELECT DISTINCT tbl FROM sqlite_stat4 ORDER BY 1;
}
} {t3i1 t3i3 t4i1 t4i2 t3 t4}
}
do_test analyze-5.4 {
execsql {
DROP TABLE t3;
SELECT DISTINCT idx FROM sqlite_stat1 ORDER BY 1;
SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1;
}
} {t4i1 t4i2 t4}
ifcapable stat4 {
do_test analyze-5.5 {
execsql {
SELECT DISTINCT idx FROM sqlite_stat4 ORDER BY 1;
SELECT DISTINCT tbl FROM sqlite_stat4 ORDER BY 1;
}
} {t4i1 t4i2 t4}
}
# This test corrupts the database file so it must be the last test
# in the series.
#
do_test analyze-5.99 {
sqlite3_db_config db DEFENSIVE 0
execsql {
PRAGMA writable_schema=on;
UPDATE sqlite_master SET sql='nonsense' WHERE name='sqlite_stat1';
}
db close
catch { sqlite3 db test.db }
catchsql {
ANALYZE
}
} {1 {malformed database schema (sqlite_stat1)}}
# Verify that tables whose names begin with "sqlite" but not
# "sqlite_" are analyzed.
#
db close
sqlite3 db :memory:
do_execsql_test analyze-6.1 {
CREATE TABLE sqliteDemo(a);
INSERT INTO sqliteDemo(a) VALUES(1),(2),(3),(4),(5);
CREATE TABLE SQLiteDemo2(a INTEGER PRIMARY KEY AUTOINCREMENT);
INSERT INTO SQLiteDemo2 SELECT * FROM sqliteDemo;
CREATE TABLE t1(b);
INSERT INTO t1(b) SELECT a FROM sqliteDemo;
ANALYZE;
SELECT tbl FROM sqlite_stat1 WHERE idx IS NULL ORDER BY tbl;
} {SQLiteDemo2 sqliteDemo t1}
finish_test

739
testdata/tcl/analyze3.test vendored Normal file
View file

@ -0,0 +1,739 @@
# 2009 August 06
#
# 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. This file
# implements tests for range and LIKE constraints that use bound variables
# instead of literal constant arguments.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix analyze3
ifcapable !stat4 {
finish_test
return
}
# This test cannot be run with the sqlite3_prepare() permutation, as it
# tests that stat4 data can be used to influence the plans of queries
# based on bound variable values. And this is not possible when using
# sqlite3_prepare() - as queries cannot be internally re-prepared after
# binding values are available.
if {[permutation]=="prepare"} {
finish_test
return
}
#----------------------------------------------------------------------
# Test Organization:
#
# analyze3-1.*: Test that the values of bound parameters are considered
# in the same way as constants when planning queries that
# use range constraints.
#
# analyze3-2.*: Test that the values of bound parameters are considered
# in the same way as constants when planning queries that
# use LIKE expressions in the WHERE clause.
#
# analyze3-3.*: Test that binding to a variable does not invalidate the
# query plan when there is no way in which replanning the
# query may produce a superior outcome.
#
# analyze3-4.*: Test that SQL or authorization callback errors occuring
# within sqlite3Reprepare() are handled correctly.
#
# analyze3-5.*: Check that the query plans of applicable statements are
# invalidated if the values of SQL parameter are modified
# using the clear_bindings() or transfer_bindings() APIs.
#
# analyze3-6.*: Test that the problem fixed by commit [127a5b776d] is fixed.
#
# analyze3-7.*: Test that some memory leaks discovered by fuzz testing
# have been fixed.
#
proc getvar {varname} { uplevel #0 set $varname }
db function var getvar
proc eqp {sql {db db}} {
uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] $db
}
proc sf_execsql {sql {db db}} {
set ::sqlite_search_count 0
set r [uplevel [list execsql $sql $db]]
concat $::sqlite_search_count [$db status step] $r
}
#-------------------------------------------------------------------------
#
# analyze3-1.1.1:
# Create a table with two columns. Populate the first column (affinity
# INTEGER) with integer values from 100 to 1100. Create an index on this
# column. ANALYZE the table.
#
# analyze3-1.1.2 - 3.1.3
# Show that there are two possible plans for querying the table with
# a range constraint on the indexed column - "full table scan" or "use
# the index". When the range is specified using literal values, SQLite
# is able to pick the best plan based on the samples in sqlite_stat3.
#
# analyze3-1.1.4 - 3.1.9
# Show that using SQL variables produces the same results as using
# literal values to constrain the range scan.
#
# These tests also check that the compiler code considers column
# affinities when estimating the number of rows scanned by the "use
# index strategy".
#
do_test analyze3-1.1.1 {
execsql {
BEGIN;
CREATE TABLE t1(x INTEGER, y);
CREATE INDEX i1 ON t1(x);
}
for {set i 0} {$i < 1000} {incr i} {
execsql { INSERT INTO t1 VALUES($i+100, $i) }
}
execsql {
COMMIT;
ANALYZE;
}
execsql { SELECT count(*)>0 FROM sqlite_stat4; }
} {1}
do_execsql_test analyze3-1.1.x {
SELECT count(*) FROM t1 WHERE x>200 AND x<300;
SELECT count(*) FROM t1 WHERE x>0 AND x<1100;
} {99 1000}
# The first of the following two SELECT statements visits 99 rows. So
# it is better to use the index. But the second visits every row in
# the table (1000 in total) so it is better to do a full-table scan.
#
do_eqp_test analyze3-1.1.2 {
SELECT sum(y) FROM t1 WHERE x>200 AND x<300
} {SEARCH t1 USING INDEX i1 (x>? AND x<?)}
do_eqp_test analyze3-1.1.3 {
SELECT sum(y) FROM t1 WHERE x>0 AND x<1100
} {SCAN t1}
# 2017-06-26: Verify that the SQLITE_DBCONFIG_ENABLE_QPSG setting disables
# the use of bound parameters by STAT4
#
db cache flush
unset -nocomplain l
unset -nocomplain u
do_eqp_test analyze3-1.1.3.100 {
SELECT sum(y) FROM t1 WHERE x>$l AND x<$u
} {SEARCH t1 USING INDEX i1 (x>? AND x<?)}
set l 200
set u 300
do_eqp_test analyze3-1.1.3.101 {
SELECT sum(y) FROM t1 WHERE x>$l AND x<$u
} {SEARCH t1 USING INDEX i1 (x>? AND x<?)}
set l 0
set u 1100
do_eqp_test analyze3-1.1.3.102 {
SELECT sum(y) FROM t1 WHERE x>$l AND x<$u
} {SCAN t1}
db cache flush
sqlite3_db_config db ENABLE_QPSG 1
do_eqp_test analyze3-1.1.3.103 {
SELECT sum(y) FROM t1 WHERE x>$l AND x<$u
} {SEARCH t1 USING INDEX i1 (x>? AND x<?)}
db cache flush
sqlite3_db_config db ENABLE_QPSG 0
do_eqp_test analyze3-1.1.3.104 {
SELECT sum(y) FROM t1 WHERE x>$l AND x<$u
} {SCAN t1}
do_test analyze3-1.1.4 {
sf_execsql { SELECT sum(y) FROM t1 WHERE x>200 AND x<300 }
} {199 0 14850}
do_test analyze3-1.1.5 {
set l [string range "200" 0 end]
set u [string range "300" 0 end]
sf_execsql { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u }
} {199 0 14850}
do_test analyze3-1.1.6 {
set l [expr int(200)]
set u [expr int(300)]
sf_execsql { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u }
} {199 0 14850}
do_test analyze3-1.1.7 {
sf_execsql { SELECT sum(y) FROM t1 WHERE x>0 AND x<1100 }
} {999 999 499500}
do_test analyze3-1.1.8 {
set l [string range "0" 0 end]
set u [string range "1100" 0 end]
sf_execsql { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u }
} {999 999 499500}
do_test analyze3-1.1.9 {
set l [expr int(0)]
set u [expr int(1100)]
sf_execsql { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u }
} {999 999 499500}
# The following tests are similar to the block above. The difference is
# that the indexed column has TEXT affinity in this case. In the tests
# above the affinity is INTEGER.
#
do_test analyze3-1.2.1 {
execsql {
BEGIN;
CREATE TABLE t2(x TEXT, y);
INSERT INTO t2 SELECT * FROM t1;
CREATE INDEX i2 ON t2(x);
COMMIT;
ANALYZE;
}
} {}
do_execsql_test analyze3-2.1.x {
SELECT count(*) FROM t2 WHERE x>1 AND x<2;
SELECT count(*) FROM t2 WHERE x>0 AND x<99;
} {200 990}
do_eqp_test analyze3-1.2.2 {
SELECT sum(y) FROM t2 WHERE x>1 AND x<2
} {SEARCH t2 USING INDEX i2 (x>? AND x<?)}
do_eqp_test analyze3-1.2.3 {
SELECT sum(y) FROM t2 WHERE x>0 AND x<99
} {SCAN t2}
do_test analyze3-1.2.4 {
sf_execsql { SELECT sum(y) FROM t2 WHERE x>12 AND x<20 }
} {161 0 4760}
do_test analyze3-1.2.5 {
set l [string range "12" 0 end]
set u [string range "20" 0 end]
sf_execsql {SELECT typeof($l), typeof($u), sum(y) FROM t2 WHERE x>$l AND x<$u}
} {161 0 text text 4760}
do_test analyze3-1.2.6 {
set l [expr int(12)]
set u [expr int(20)]
sf_execsql {SELECT typeof($l), typeof($u), sum(y) FROM t2 WHERE x>$l AND x<$u}
} {161 0 integer integer 4760}
do_test analyze3-1.2.7 {
sf_execsql { SELECT sum(y) FROM t2 WHERE x>0 AND x<99 }
} {999 999 490555}
do_test analyze3-1.2.8 {
set l [string range "0" 0 end]
set u [string range "99" 0 end]
sf_execsql {SELECT typeof($l), typeof($u), sum(y) FROM t2 WHERE x>$l AND x<$u}
} {999 999 text text 490555}
do_test analyze3-1.2.9 {
set l [expr int(0)]
set u [expr int(99)]
sf_execsql {SELECT typeof($l), typeof($u), sum(y) FROM t2 WHERE x>$l AND x<$u}
} {999 999 integer integer 490555}
# Same tests a third time. This time, column x has INTEGER affinity and
# is not the leftmost column of the table. This triggered a bug causing
# SQLite to use sub-optimal query plans in 3.6.18 and earlier.
#
do_test analyze3-1.3.1 {
execsql {
BEGIN;
CREATE TABLE t3(y TEXT, x INTEGER);
INSERT INTO t3 SELECT y, x FROM t1;
CREATE INDEX i3 ON t3(x);
COMMIT;
ANALYZE;
}
} {}
do_execsql_test analyze3-1.3.x {
SELECT count(*) FROM t3 WHERE x>200 AND x<300;
SELECT count(*) FROM t3 WHERE x>0 AND x<1100
} {99 1000}
do_eqp_test analyze3-1.3.2 {
SELECT sum(y) FROM t3 WHERE x>200 AND x<300
} {SEARCH t3 USING INDEX i3 (x>? AND x<?)}
do_eqp_test analyze3-1.3.3 {
SELECT sum(y) FROM t3 WHERE x>0 AND x<1100
} {SCAN t3}
do_test analyze3-1.3.4 {
sf_execsql { SELECT sum(y) FROM t3 WHERE x>200 AND x<300 }
} {199 0 14850}
do_test analyze3-1.3.5 {
set l [string range "200" 0 end]
set u [string range "300" 0 end]
sf_execsql { SELECT sum(y) FROM t3 WHERE x>$l AND x<$u }
} {199 0 14850}
do_test analyze3-1.3.6 {
set l [expr int(200)]
set u [expr int(300)]
sf_execsql { SELECT sum(y) FROM t3 WHERE x>$l AND x<$u }
} {199 0 14850}
do_test analyze3-1.3.7 {
sf_execsql { SELECT sum(y) FROM t3 WHERE x>0 AND x<1100 }
} {999 999 499500}
do_test analyze3-1.3.8 {
set l [string range "0" 0 end]
set u [string range "1100" 0 end]
sf_execsql { SELECT sum(y) FROM t3 WHERE x>$l AND x<$u }
} {999 999 499500}
do_test analyze3-1.3.9 {
set l [expr int(0)]
set u [expr int(1100)]
sf_execsql { SELECT sum(y) FROM t3 WHERE x>$l AND x<$u }
} {999 999 499500}
#-------------------------------------------------------------------------
# Test that the values of bound SQL variables may be used for the LIKE
# optimization.
#
drop_all_tables
do_test analyze3-2.1 {
execsql {
PRAGMA case_sensitive_like=off;
BEGIN;
CREATE TABLE t1(a, b TEXT COLLATE nocase);
CREATE INDEX i1 ON t1(b);
}
for {set i 0} {$i < 1000} {incr i} {
set t ""
append t [lindex {a b c d e f g h i j} [expr $i/100]]
append t [lindex {a b c d e f g h i j} [expr ($i/10)%10]]
append t [lindex {a b c d e f g h i j} [expr ($i%10)]]
execsql { INSERT INTO t1 VALUES($i, $t) }
}
execsql COMMIT
} {}
do_eqp_test analyze3-2.2 {
SELECT count(a) FROM t1 WHERE b LIKE 'a%'
} {SEARCH t1 USING INDEX i1 (b>? AND b<?)}
do_eqp_test analyze3-2.3 {
SELECT count(a) FROM t1 WHERE b LIKE '%a'
} {SCAN t1}
# Return the first argument if like_match_blobs is true (the default)
# or the second argument if not
#
proc ilmb {a b} {
ifcapable like_match_blobs {return $a}
return $b
}
do_test analyze3-2.4 {
sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE 'a%' }
} [list [ilmb 102 101] 0 100]
do_test analyze3-2.5 {
sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE '%a' }
} {999 999 100}
do_test analyze3-2.6 {
set like "a%"
sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE $like }
} [list [ilmb 102 101] 0 100]
do_test analyze3-2.7 {
set like "%a"
sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE $like }
} {999 999 100}
do_test analyze3-2.8 {
set like "a"
sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE $like }
} [list [ilmb 102 101] 0 0]
do_test analyze3-2.9 {
set like "ab"
sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE $like }
} [list [ilmb 12 11] 0 0]
do_test analyze3-2.10 {
set like "abc"
sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE $like }
} [list [ilmb 3 2] 0 1]
do_test analyze3-2.11 {
set like "a_c"
sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE $like }
} [list [ilmb 102 101] 0 10]
#-------------------------------------------------------------------------
# This block of tests checks that statements are correctly marked as
# expired when the values bound to any parameters that may affect the
# query plan are modified.
#
drop_all_tables
db auth auth
proc auth {args} {
set ::auth 1
return SQLITE_OK
}
do_test analyze3-3.1 {
execsql {
BEGIN;
CREATE TABLE t1(a, b, c);
CREATE INDEX i1 ON t1(b);
}
for {set i 0} {$i < 100} {incr i} {
execsql { INSERT INTO t1 VALUES($i, $i, $i) }
}
execsql COMMIT
execsql ANALYZE
} {}
do_test analyze3-3.2.1 {
set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE b>?" -1 dummy]
sqlite3_expired $S
} {0}
do_test analyze3-3.2.2 {
sqlite3_bind_text $S 1 "abc" 3
sqlite3_expired $S
} {1}
do_test analyze3-3.2.4 {
sqlite3_finalize $S
} {SQLITE_OK}
do_test analyze3-3.2.5 {
set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE b=?" -1 dummy]
sqlite3_expired $S
} {0}
do_test analyze3-3.2.6 {
sqlite3_bind_text $S 1 "abc" 3
sqlite3_expired $S
} {1}
do_test analyze3-3.2.7 {
sqlite3_finalize $S
} {SQLITE_OK}
do_test analyze3-3.4.1 {
set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE a=? AND b>?" -1 dummy]
sqlite3_expired $S
} {0}
do_test analyze3-3.4.2 {
sqlite3_bind_text $S 1 "abc" 3
sqlite3_expired $S
} {0}
do_test analyze3-3.4.3 {
sqlite3_bind_text $S 2 "def" 3
sqlite3_expired $S
} {1}
do_test analyze3-3.4.4 {
sqlite3_bind_text $S 2 "ghi" 3
sqlite3_expired $S
} {1}
do_test analyze3-3.4.5 {
sqlite3_expired $S
} {1}
do_test analyze3-3.4.6 {
sqlite3_finalize $S
} {SQLITE_OK}
do_test analyze3-3.5.1 {
set S [sqlite3_prepare_v2 db {
SELECT * FROM t1 WHERE a IN (
?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
) AND b>?32;
} -1 dummy]
sqlite3_expired $S
} {0}
do_test analyze3-3.5.2 {
sqlite3_bind_text $S 31 "abc" 3
sqlite3_expired $S
} {0}
do_test analyze3-3.5.3 {
sqlite3_bind_text $S 32 "def" 3
sqlite3_expired $S
} {1}
do_test analyze3-3.5.5 {
sqlite3_finalize $S
} {SQLITE_OK}
do_test analyze3-3.6.1 {
set S [sqlite3_prepare_v2 db {
SELECT * FROM t1 WHERE a IN (
?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
) AND b>?33;
} -1 dummy]
sqlite3_expired $S
} {0}
do_test analyze3-3.6.2 {
sqlite3_bind_text $S 32 "abc" 3
sqlite3_expired $S
} {1}
do_test analyze3-3.6.3 {
sqlite3_bind_text $S 33 "def" 3
sqlite3_expired $S
} {1}
do_test analyze3-3.6.5 {
sqlite3_finalize $S
} {SQLITE_OK}
do_test analyze3-3.7.1 {
set S [sqlite3_prepare_v2 db {
SELECT * FROM t1 WHERE a IN (
?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?33,
?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20,
?21, ?22, ?23, ?24, ?25, ?26, ?27, ?28, ?29, ?30, ?31, ?32
) AND b>?10;
} -1 dummy]
sqlite3_expired $S
} {0}
do_test analyze3-3.7.2 {
sqlite3_bind_text $S 32 "abc" 3
sqlite3_expired $S
} {0}
do_test analyze3-3.7.3 {
sqlite3_bind_text $S 33 "def" 3
sqlite3_expired $S
} {0}
do_test analyze3-3.7.4 {
sqlite3_bind_text $S 10 "def" 3
sqlite3_expired $S
} {1}
do_test analyze3-3.7.6 {
sqlite3_finalize $S
} {SQLITE_OK}
do_test analyze3-3.8.1 {
execsql {
CREATE TABLE t4(x, y TEXT COLLATE NOCASE);
CREATE INDEX i4 ON t4(y);
}
} {}
do_test analyze3-3.8.2 {
set S [sqlite3_prepare_v2 db {
SELECT * FROM t4 WHERE x != ? AND y LIKE ?
} -1 dummy]
sqlite3_expired $S
} {0}
do_test analyze3-3.8.3 {
sqlite3_bind_text $S 1 "abc" 3
sqlite3_expired $S
} {0}
do_test analyze3-3.8.4 {
sqlite3_bind_text $S 2 "def" 3
sqlite3_expired $S
} {1}
do_test analyze3-3.8.7 {
sqlite3_bind_text $S 2 "ghi%" 4
sqlite3_expired $S
} {1}
do_test analyze3-3.8.8 {
sqlite3_expired $S
} {1}
do_test analyze3-3.8.9 {
sqlite3_bind_text $S 2 "ghi%def" 7
sqlite3_expired $S
} {1}
do_test analyze3-3.8.10 {
sqlite3_expired $S
} {1}
do_test analyze3-3.8.11 {
sqlite3_bind_text $S 2 "%ab" 3
sqlite3_expired $S
} {1}
do_test analyze3-3.8.12 {
sqlite3_expired $S
} {1}
do_test analyze3-3.8.12 {
sqlite3_bind_text $S 2 "%de" 3
sqlite3_expired $S
} {1}
do_test analyze3-3.8.13 {
sqlite3_expired $S
} {1}
do_test analyze3-3.8.14 {
sqlite3_finalize $S
} {SQLITE_OK}
#-------------------------------------------------------------------------
# These tests check that errors encountered while repreparing an SQL
# statement within sqlite3Reprepare() are handled correctly.
#
# Check a schema error.
#
do_test analyze3-4.1.1 {
set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE a=? AND b>?" -1 dummy]
sqlite3_step $S
} {SQLITE_DONE}
do_test analyze3-4.1.2 {
sqlite3_reset $S
sqlite3_bind_text $S 2 "abc" 3
execsql { DROP TABLE t1 }
sqlite3_step $S
} {SQLITE_ERROR}
do_test analyze3-4.1.3 {
sqlite3_finalize $S
} {SQLITE_ERROR}
# Check an authorization error.
#
do_test analyze3-4.2.1 {
execsql {
BEGIN;
CREATE TABLE t1(a, b, c);
CREATE INDEX i1 ON t1(b);
}
for {set i 0} {$i < 100} {incr i} {
execsql { INSERT INTO t1 VALUES($i, $i, $i) }
}
execsql COMMIT
execsql ANALYZE
set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE a=? AND b>?" -1 dummy]
sqlite3_step $S
} {SQLITE_DONE}
db auth auth
proc auth {args} {
if {[lindex $args 0] == "SQLITE_READ"} {return SQLITE_DENY}
return SQLITE_OK
}
do_test analyze3-4.2.2 {
sqlite3_reset $S
sqlite3_bind_text $S 2 "abc" 3
sqlite3_step $S
} {SQLITE_AUTH}
do_test analyze3-4.2.4 {
sqlite3_finalize $S
} {SQLITE_AUTH}
# Check the effect of an authorization error that occurs in a re-prepare
# performed by sqlite3_step() is the same as one that occurs within
# sqlite3Reprepare().
#
do_test analyze3-4.3.1 {
db auth {}
set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE a=? AND b>?" -1 dummy]
execsql { CREATE TABLE t2(d, e, f) }
db auth auth
sqlite3_step $S
} {SQLITE_AUTH}
do_test analyze3-4.3.2 {
sqlite3_finalize $S
} {SQLITE_AUTH}
db auth {}
#-------------------------------------------------------------------------
# Test that modifying bound variables using the clear_bindings() or
# transfer_bindings() APIs works.
#
# analyze3-5.1.*: sqlite3_clear_bindings()
# analyze3-5.2.*: sqlite3_transfer_bindings()
#
do_test analyze3-5.1.1 {
drop_all_tables
execsql {
CREATE TABLE t1(x TEXT COLLATE NOCASE);
CREATE INDEX i1 ON t1(x);
INSERT INTO t1 VALUES('aaa');
INSERT INTO t1 VALUES('abb');
INSERT INTO t1 VALUES('acc');
INSERT INTO t1 VALUES('baa');
INSERT INTO t1 VALUES('bbb');
INSERT INTO t1 VALUES('bcc');
}
set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE x LIKE ?" -1 dummy]
sqlite3_bind_text $S 1 "a%" 2
set R [list]
while { "SQLITE_ROW" == [sqlite3_step $S] } {
lappend R [sqlite3_column_text $S 0]
}
concat [sqlite3_reset $S] $R
} {SQLITE_OK aaa abb acc}
do_test analyze3-5.1.2 {
sqlite3_clear_bindings $S
set R [list]
while { "SQLITE_ROW" == [sqlite3_step $S] } {
lappend R [sqlite3_column_text $S 0]
}
concat [sqlite3_reset $S] $R
} {SQLITE_OK}
do_test analyze3-5.1.3 {
sqlite3_finalize $S
} {SQLITE_OK}
do_test analyze3-5.1.1 {
set S1 [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE x LIKE ?" -1 dummy]
sqlite3_bind_text $S1 1 "b%" 2
set R [list]
while { "SQLITE_ROW" == [sqlite3_step $S1] } {
lappend R [sqlite3_column_text $S1 0]
}
concat [sqlite3_reset $S1] $R
} {SQLITE_OK baa bbb bcc}
do_test analyze3-5.1.2 {
set S2 [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE x = ?" -1 dummy]
sqlite3_bind_text $S2 1 "a%" 2
sqlite3_transfer_bindings $S2 $S1
set R [list]
while { "SQLITE_ROW" == [sqlite3_step $S1] } {
lappend R [sqlite3_column_text $S1 0]
}
concat [sqlite3_reset $S1] $R
} {SQLITE_OK aaa abb acc}
do_test analyze3-5.1.3 {
sqlite3_finalize $S2
sqlite3_finalize $S1
} {SQLITE_OK}
#-------------------------------------------------------------------------
do_test analyze3-6.1 {
execsql { DROP TABLE IF EXISTS t1 }
execsql BEGIN
execsql { CREATE TABLE t1(a, b, c) }
for {set i 0} {$i < 1000} {incr i} {
execsql "INSERT INTO t1 VALUES([expr $i/100], 'x', [expr $i/10])"
}
execsql {
CREATE INDEX i1 ON t1(a, b);
CREATE INDEX i2 ON t1(c);
}
execsql COMMIT
execsql ANALYZE
} {}
do_eqp_test analyze3-6-3 {
SELECT * FROM t1 WHERE a = 5 AND c = 13;
} {SEARCH t1 USING INDEX i2 (c=?)}
do_eqp_test analyze3-6-2 {
SELECT * FROM t1 WHERE a = 5 AND b > 'w' AND c = 13;
} {SEARCH t1 USING INDEX i2 (c=?)}
#-----------------------------------------------------------------------------
# 2015-04-20.
# Memory leak in sqlite3Stat4ProbeFree(). (Discovered while fuzzing.)
#
do_execsql_test analyze-7.1 {
DROP TABLE IF EXISTS t1;
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
INSERT INTO t1 VALUES(1,1,'0000');
CREATE INDEX t0b ON t1(b);
ANALYZE;
SELECT c FROM t1 WHERE b=3 AND a BETWEEN 30 AND hex(1);
} {}
# At one point duplicate stat1 entries were causing a memory leak.
#
reset_db
do_execsql_test 7.2 {
CREATE TABLE t1(a,b,c);
CREATE INDEX t1a ON t1(a);
ANALYZE;
SELECT * FROM sqlite_stat1;
INSERT INTO sqlite_stat1(tbl,idx,stat) VALUES('t1','t1a','12000');
INSERT INTO sqlite_stat1(tbl,idx,stat) VALUES('t1','t1a','12000');
ANALYZE sqlite_master;
}
finish_test

117
testdata/tcl/analyze4.test vendored Normal file
View file

@ -0,0 +1,117 @@
# 2011 January 04
#
# 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. This file
# implements tests for ANALYZE to verify that multiple rows containing
# a NULL value count as distinct rows for the purposes of analyze
# statistics.
#
# Also include test cases for collating sequences on indices.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
ifcapable !altertable {
finish_test
return
}
do_test analyze4-1.0 {
db eval {
CREATE TABLE t1(a,b);
CREATE INDEX t1a ON t1(a);
CREATE INDEX t1b ON t1(b);
INSERT INTO t1 VALUES(1,NULL);
INSERT INTO t1 SELECT a+1, b FROM t1;
INSERT INTO t1 SELECT a+2, b FROM t1;
INSERT INTO t1 SELECT a+4, b FROM t1;
INSERT INTO t1 SELECT a+8, b FROM t1;
INSERT INTO t1 SELECT a+16, b FROM t1;
INSERT INTO t1 SELECT a+32, b FROM t1;
INSERT INTO t1 SELECT a+64, b FROM t1;
ANALYZE;
}
# Should choose the t1a index since it is more specific than t1b.
db eval {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=5 AND b IS NULL}
} {/*SEARCH t1 USING INDEX t1a (a=?)*/}
# Verify that the t1b index shows that it does not narrow down the
# search any at all.
#
do_test analyze4-1.1 {
db eval {
SELECT idx, stat FROM sqlite_stat1 WHERE tbl='t1' ORDER BY idx;
}
} {t1a {128 1} t1b {128 128}}
# Change half of the b values from NULL to a constant. Verify
# that the number of rows selected in stat1 is half the total
# number of rows.
#
do_test analyze4-1.2 {
db eval {
UPDATE t1 SET b='x' WHERE a%2;
ANALYZE;
SELECT idx, stat FROM sqlite_stat1 WHERE tbl='t1' ORDER BY idx;
}
} {t1a {128 1} t1b {128 64}}
# Change the t1.b values all back to NULL. Add columns t1.c and t1.d.
# Create a multi-column indices using t1.b and verify that ANALYZE
# processes them correctly.
#
do_test analyze4-1.3 {
db eval {
UPDATE t1 SET b=NULL;
ALTER TABLE t1 ADD COLUMN c;
ALTER TABLE t1 ADD COLUMN d;
UPDATE t1 SET c=a/4, d=a/2;
CREATE INDEX t1bcd ON t1(b,c,d);
CREATE INDEX t1cdb ON t1(c,d,b);
CREATE INDEX t1cbd ON t1(c,b,d);
ANALYZE;
SELECT idx, stat FROM sqlite_stat1 WHERE tbl='t1' ORDER BY idx;
}
} {t1a {128 1} t1b {128 128} t1bcd {128 128 4 2} t1cbd {128 4 4 2} t1cdb {128 4 2 2}}
# Verify that collating sequences are taken into account when computing
# ANALYZE statistics.
#
do_test analyze4-2.0 {
db eval {
CREATE TABLE t2(
x INTEGER PRIMARY KEY,
a TEXT COLLATE nocase,
b TEXT COLLATE rtrim,
c TEXT COLLATE binary
);
CREATE INDEX t2a ON t2(a);
CREATE INDEX t2b ON t2(b);
CREATE INDEX t2c ON t2(c);
CREATE INDEX t2c2 ON t2(c COLLATE nocase);
CREATE INDEX t2c3 ON t2(c COLLATE rtrim);
INSERT INTO t2 VALUES(1, 'abc', 'abc', 'abc');
INSERT INTO t2 VALUES(2, 'abC', 'abC', 'abC');
INSERT INTO t2 VALUES(3, 'abc ', 'abc ', 'abc ');
INSERT INTO t2 VALUES(4, 'abC ', 'abC ', 'abC ');
INSERT INTO t2 VALUES(5, 'aBc', 'aBc', 'aBc');
INSERT INTO t2 VALUES(6, 'aBC', 'aBC', 'aBC');
INSERT INTO t2 VALUES(7, 'aBc ', 'aBc ', 'aBc ');
INSERT INTO t2 VALUES(8, 'aBC ', 'aBC ', 'aBC ');
ANALYZE;
SELECT idx, stat FROM sqlite_stat1 WHERE tbl='t2' ORDER BY idx;
}
} {t2a {8 4} t2b {8 2} t2c {8 1} t2c2 {8 4} t2c3 {8 2}}
finish_test

247
testdata/tcl/analyze5.test vendored Normal file
View file

@ -0,0 +1,247 @@
# 2011 January 19
#
# 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 tests for SQLite library. The focus of the tests
# in this file is the use of the sqlite_stat4 histogram data on tables
# with many repeated values and only a few distinct values.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
ifcapable !stat4 {
finish_test
return
}
set testprefix analyze5
proc eqp {sql {db db}} {
uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] $db
}
proc alpha {blob} {
set ret ""
foreach c [split $blob {}] {
if {[string is alpha $c]} {append ret $c}
}
return $ret
}
db func alpha alpha
db func lindex lindex
unset -nocomplain i t u v w x y z
do_test analyze5-1.0 {
db eval {CREATE TABLE t1(t,u,v TEXT COLLATE nocase,w,x,y,z)}
for {set i 0} {$i < 1000} {incr i} {
set y [expr {$i>=25 && $i<=50}]
set z [expr {($i>=400) + ($i>=700) + ($i>=875)}]
set x $z
set w $z
set t [expr {$z+0.5}]
switch $z {
0 {set u "alpha"; unset x}
1 {set u "bravo"}
2 {set u "charlie"}
3 {set u "delta"; unset w}
}
if {$i%2} {set v $u} {set v [string toupper $u]}
db eval {INSERT INTO t1 VALUES($t,$u,$v,$w,$x,$y,$z)}
}
db eval {
CREATE INDEX t1t ON t1(t); -- 0.5, 1.5, 2.5, and 3.5
CREATE INDEX t1u ON t1(u); -- text
CREATE INDEX t1v ON t1(v); -- mixed case text
CREATE INDEX t1w ON t1(w); -- integers 0, 1, 2 and a few NULLs
CREATE INDEX t1x ON t1(x); -- integers 1, 2, 3 and many NULLs
CREATE INDEX t1y ON t1(y); -- integers 0 and very few 1s
CREATE INDEX t1z ON t1(z); -- integers 0, 1, 2, and 3
ANALYZE;
}
db eval {
SELECT DISTINCT lindex(test_decode(sample),0)
FROM sqlite_stat4 WHERE idx='t1u' ORDER BY nlt;
}
} {alpha bravo charlie delta}
do_test analyze5-1.1 {
db eval {
SELECT DISTINCT lower(lindex(test_decode(sample), 0))
FROM sqlite_stat4 WHERE idx='t1v' ORDER BY 1
}
} {alpha bravo charlie delta}
do_test analyze5-1.2 {
db eval {SELECT idx, count(*) FROM sqlite_stat4 GROUP BY 1 ORDER BY 1}
} {t1t 8 t1u 8 t1v 8 t1w 8 t1x 8 t1y 9 t1z 8}
# Verify that range queries generate the correct row count estimates
#
foreach {testid where index rows} {
1 {z>=0 AND z<=0} t1z 400
2 {z>=1 AND z<=1} t1z 300
3 {z>=2 AND z<=2} t1z 175
4 {z>=3 AND z<=3} t1z 125
5 {z>=4 AND z<=4} t1z 1
6 {z>=-1 AND z<=-1} t1z 1
7 {z>1 AND z<3} t1z 175
8 {z>0 AND z<100} t1z 600
9 {z>=1 AND z<100} t1z 600
10 {z>1 AND z<100} t1z 300
11 {z>=2 AND z<100} t1z 300
12 {z>2 AND z<100} t1z 125
13 {z>=3 AND z<100} t1z 125
14 {z>3 AND z<100} t1z 1
15 {z>=4 AND z<100} t1z 1
16 {z>=-100 AND z<=-1} t1z 1
17 {z>=-100 AND z<=0} t1z 400
18 {z>=-100 AND z<0} t1z 1
19 {z>=-100 AND z<=1} t1z 700
20 {z>=-100 AND z<2} t1z 700
21 {z>=-100 AND z<=2} t1z 875
22 {z>=-100 AND z<3} t1z 875
31 {z>=0.0 AND z<=0.0} t1z 400
32 {z>=1.0 AND z<=1.0} t1z 300
33 {z>=2.0 AND z<=2.0} t1z 175
34 {z>=3.0 AND z<=3.0} t1z 125
35 {z>=4.0 AND z<=4.0} t1z 1
36 {z>=-1.0 AND z<=-1.0} t1z 1
37 {z>1.5 AND z<3.0} t1z 174
38 {z>0.5 AND z<100} t1z 599
39 {z>=1.0 AND z<100} t1z 600
40 {z>1.5 AND z<100} t1z 299
41 {z>=2.0 AND z<100} t1z 300
42 {z>2.1 AND z<100} t1z 124
43 {z>=3.0 AND z<100} t1z 125
44 {z>3.2 AND z<100} t1z 1
45 {z>=4.0 AND z<100} t1z 1
46 {z>=-100 AND z<=-1.0} t1z 1
47 {z>=-100 AND z<=0.0} t1z 400
48 {z>=-100 AND z<0.0} t1z 1
49 {z>=-100 AND z<=1.0} t1z 700
50 {z>=-100 AND z<2.0} t1z 700
51 {z>=-100 AND z<=2.0} t1z 875
52 {z>=-100 AND z<3.0} t1z 875
101 {z=-1} t1z 1
102 {z=0} t1z 400
103 {z=1} t1z 300
104 {z=2} t1z 175
105 {z=3} t1z 125
106 {z=4} t1z 1
107 {z=-10.0} t1z 1
108 {z=0.0} t1z 400
109 {z=1.0} t1z 300
110 {z=2.0} t1z 175
111 {z=3.0} t1z 125
112 {z=4.0} t1z 1
113 {z=1.5} t1z 1
114 {z=2.5} t1z 1
201 {z IN (-1)} t1z 1
202 {z IN (0)} t1z 400
203 {z IN (1)} t1z 300
204 {z IN (2)} t1z 175
205 {z IN (3)} t1z 125
206 {z IN (4)} t1z 1
207 {z IN (0.5)} t1z 1
208 {z IN (0,1)} t1z 700
209 {z IN (0,1,2)} t1z 875
210 {z IN (0,1,2,3)} {} 100
211 {z IN (0,1,2,3,4,5)} {} 100
212 {z IN (1,2)} t1z 475
213 {z IN (2,3)} t1z 300
214 {z=3 OR z=2} t1z 300
215 {z IN (-1,3)} t1z 126
216 {z=-1 OR z=3} t1z 126
300 {y=0} t1y 974
301 {y=1} t1y 26
302 {y=0.1} t1y 1
400 {x IS NULL} t1x 400
} {
# Verify that the expected index is used with the expected row count
# No longer valid due to an EXPLAIN QUERY PLAN output format change
# do_test analyze5-1.${testid}a {
# set x [lindex [eqp "SELECT * FROM t1 WHERE $where"] 3]
# set idx {}
# regexp {INDEX (t1.) } $x all idx
# regexp {~([0-9]+) rows} $x all nrow
# list $idx $nrow
# } [list $index $rows]
# Verify that the same result is achieved regardless of whether or not
# the index is used
do_test analyze5-1.${testid}b {
set w2 [string map {y +y z +z} $where]
set a1 [db eval "SELECT rowid FROM t1 NOT INDEXED WHERE $w2\
ORDER BY +rowid"]
set a2 [db eval "SELECT rowid FROM t1 WHERE $where ORDER BY +rowid"]
if {$a1==$a2} {
set res ok
} else {
set res "a1=\[$a1\] a2=\[$a2\]"
}
set res
} {ok}
}
# Increase the number of NULLs in column x
#
db eval {
UPDATE t1 SET x=NULL;
UPDATE t1 SET x=rowid
WHERE rowid IN (SELECT rowid FROM t1 ORDER BY random() LIMIT 5);
ANALYZE;
}
# Verify that range queries generate the correct row count estimates
#
foreach {testid where index rows} {
500 {x IS NULL AND u='charlie'} t1u 17
501 {x=1 AND u='charlie'} t1x 1
502 {x IS NULL} t1x 995
503 {x=1} t1x 1
504 {x IS NOT NULL} t1x 2
505 {+x IS NOT NULL} {} 500
506 {upper(x) IS NOT NULL} {} 500
} {
# Verify that the expected index is used with the expected row count
# No longer valid due to an EXPLAIN QUERY PLAN format change
# do_test analyze5-1.${testid}a {
# set x [lindex [eqp "SELECT * FROM t1 WHERE $where"] 3]
# set idx {}
# regexp {INDEX (t1.) } $x all idx
# regexp {~([0-9]+) rows} $x all nrow
# list $idx $nrow
# } [list $index $rows]
# Verify that the same result is achieved regardless of whether or not
# the index is used
do_test analyze5-1.${testid}b {
set w2 [string map {y +y z +z} $where]
set a1 [db eval "SELECT rowid FROM t1 NOT INDEXED WHERE $w2\
ORDER BY +rowid"]
set a2 [db eval "SELECT rowid FROM t1 WHERE $where ORDER BY +rowid"]
if {$a1==$a2} {
set res ok
} else {
set res "a1=\[$a1\] a2=\[$a2\]"
}
set res
} {ok}
}
finish_test

126
testdata/tcl/analyze6.test vendored Normal file
View file

@ -0,0 +1,126 @@
# 2011 March 3
#
# 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 tests for SQLite library. The focus of the tests
# in this file a corner-case query planner optimization involving the
# join order of two tables of different sizes.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
ifcapable !stat4 {
finish_test
return
}
set testprefix analyze6
proc eqp {sql {db db}} {
uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] $db
}
do_test analyze6-1.0 {
db eval {
CREATE TABLE cat(x INT, yz TEXT);
CREATE UNIQUE INDEX catx ON cat(x);
/* Give cat 16 unique integers */
INSERT INTO cat(x) VALUES(1);
INSERT INTO cat(x) VALUES(2);
INSERT INTO cat(x) SELECT x+2 FROM cat;
INSERT INTO cat(x) SELECT x+4 FROM cat;
INSERT INTO cat(x) SELECT x+8 FROM cat;
CREATE TABLE ev(y INT);
CREATE INDEX evy ON ev(y);
/* ev will hold 32 copies of 16 integers found in cat */
INSERT INTO ev SELECT x FROM cat;
INSERT INTO ev SELECT x FROM cat;
INSERT INTO ev SELECT y FROM ev;
INSERT INTO ev SELECT y FROM ev;
INSERT INTO ev SELECT y FROM ev;
INSERT INTO ev SELECT y FROM ev;
ANALYZE;
SELECT count(*) FROM cat;
SELECT count(*) FROM ev;
}
} {16 512}
# The lowest cost plan is to scan CAT and for each integer there, do a single
# lookup of the first corresponding entry in EV then read off the equal values
# in EV. (Prior to the 2011-03-04 enhancement to where.c, this query would
# have used EV for the outer loop instead of CAT - which was about 3x slower.)
#
do_test analyze6-1.1 {
eqp {SELECT count(*) FROM ev, cat WHERE x=y}
} {/*SCAN cat USING COVERING INDEX catx*SEARCH ev USING COVERING INDEX evy (y=?)*/}
# The same plan is chosen regardless of the order of the tables in the
# FROM clause.
#
do_eqp_test analyze6-1.2 {
SELECT count(*) FROM cat, ev WHERE x=y
} {
QUERY PLAN
|--SCAN cat USING COVERING INDEX catx
`--SEARCH ev USING COVERING INDEX evy (y=?)
}
# Ticket [83ea97620bd3101645138b7b0e71c12c5498fe3d] 2011-03-30
# If ANALYZE is run on an empty table, make sure indices are used
# on the table.
#
do_test analyze6-2.1 {
execsql {
CREATE TABLE t201(x INTEGER PRIMARY KEY, y UNIQUE, z);
CREATE INDEX t201z ON t201(z);
ANALYZE;
}
eqp {SELECT * FROM t201 WHERE z=5}
} {/*SEARCH t201 USING INDEX t201z (z=?)*/}
do_test analyze6-2.2 {
eqp {SELECT * FROM t201 WHERE y=5}
} {/*SEARCH t201 USING INDEX sqlite_autoindex_t201_1 (y=?)*/}
do_test analyze6-2.3 {
eqp {SELECT * FROM t201 WHERE x=5}
} {/*SEARCH t201 USING INTEGER PRIMARY KEY (rowid=?)*/}
do_test analyze6-2.4 {
execsql {
INSERT INTO t201 VALUES(1,2,3),(2,3,4),(3,4,5);
ANALYZE t201;
}
eqp {SELECT * FROM t201 WHERE z=5}
} {/*SEARCH t201 USING INDEX t201z (z=?)*/}
do_test analyze6-2.5 {
eqp {SELECT * FROM t201 WHERE y=5}
} {/*SEARCH t201 USING INDEX sqlite_autoindex_t201_1 (y=?)*/}
do_test analyze6-2.6 {
eqp {SELECT * FROM t201 WHERE x=5}
} {/*SEARCH t201 USING INTEGER PRIMARY KEY (rowid=?)*/}
do_test analyze6-2.7 {
execsql {
INSERT INTO t201 VALUES(4,5,7);
INSERT INTO t201 SELECT x+100, y+100, z+100 FROM t201;
INSERT INTO t201 SELECT x+200, y+200, z+200 FROM t201;
INSERT INTO t201 SELECT x+400, y+400, z+400 FROM t201;
ANALYZE t201;
}
eqp {SELECT * FROM t201 WHERE z=5}
} {/*SEARCH t201 USING INDEX t201z (z=?)*/}
do_test analyze6-2.8 {
eqp {SELECT * FROM t201 WHERE y=5}
} {/*SEARCH t201 USING INDEX sqlite_autoindex_t201_1 (y=?)*/}
do_test analyze6-2.9 {
eqp {SELECT * FROM t201 WHERE x=5}
} {/*SEARCH t201 USING INTEGER PRIMARY KEY (rowid=?)*/}
finish_test

114
testdata/tcl/analyze7.test vendored Normal file
View file

@ -0,0 +1,114 @@
# 2011 April 1
#
# 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.
# This file implements tests for the ANALYZE command when an idnex
# name is given as the argument.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# There is nothing to test if ANALYZE is disable for this build.
#
ifcapable {!analyze||!vtab} {
finish_test
return
}
# Generate some test data
#
do_test analyze7-1.0 {
load_static_extension db wholenumber
execsql {
CREATE TABLE t1(a,b,c,d);
CREATE INDEX t1a ON t1(a);
CREATE INDEX t1b ON t1(b);
CREATE INDEX t1cd ON t1(c,d);
CREATE VIRTUAL TABLE nums USING wholenumber;
INSERT INTO t1 SELECT value, value, value/100, value FROM nums
WHERE value BETWEEN 1 AND 256;
EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123;
}
} {/*SEARCH t1 USING INDEX t1a (a=?)*/}
do_test analyze7-1.1 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b=123;}
} {/*SEARCH t1 USING INDEX t1b (b=?)*/}
do_test analyze7-1.2 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;}
} {/*SEARCH t1 USING INDEX t1cd (c=?)*/}
# Run an analyze on one of the three indices. Verify that this
# effects the row-count estimate on the one query that uses that
# one index.
#
do_test analyze7-2.0 {
execsql {ANALYZE t1a;}
db cache flush
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123;}
} {/*SEARCH t1 USING INDEX t1a (a=?)*/}
do_test analyze7-2.1 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b=123;}
} {/*SEARCH t1 USING INDEX t1b (b=?)*/}
do_test analyze7-2.2 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;}
} {/*SEARCH t1 USING INDEX t1cd (c=?)*/}
# Verify that since the query planner now things that t1a is more
# selective than t1b, it prefers to use t1a.
#
do_test analyze7-2.3 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND b=123}
} {/*SEARCH t1 USING INDEX t1a (a=?)*/}
# Run an analysis on another of the three indices. Verify that this
# new analysis works and does not disrupt the previous analysis.
#
do_test analyze7-3.0 {
execsql {ANALYZE t1cd;}
db cache flush;
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123;}
} {/*SEARCH t1 USING INDEX t1a (a=?)*/}
do_test analyze7-3.1 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b=123;}
} {/*SEARCH t1 USING INDEX t1b (b=?)*/}
do_test analyze7-3.2.1 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=?;}
} {/*SEARCH t1 USING INDEX t1cd (c=?)*/}
ifcapable stat4 {
# If ENABLE_STAT4 is defined, SQLite comes up with a different estimated
# row count for (c=2) than it does for (c=?).
do_test analyze7-3.2.2 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;}
} {/*SEARCH t1 USING INDEX t1cd (c=?)*/}
} else {
# If ENABLE_STAT4 is not defined, the expected row count for (c=2) is the
# same as that for (c=?).
do_test analyze7-3.2.3 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;}
} {/*SEARCH t1 USING INDEX t1cd (c=?)*/}
}
do_test analyze7-3.3 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND b=123}
} {/*SEARCH t1 USING INDEX t1a (a=?)*/}
ifcapable {!stat4} {
do_test analyze7-3.4 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=123 AND b=123}
} {/*SEARCH t1 USING INDEX t1b (b=?)*/}
do_test analyze7-3.5 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND c=123}
} {/*SEARCH t1 USING INDEX t1a (a=?)*/}
}
do_test analyze7-3.6 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=123 AND d=123 AND b=123}
} {/*SEARCH t1 USING INDEX t1cd (c=? AND d=?)*/}
finish_test

115
testdata/tcl/analyze8.test vendored Normal file
View file

@ -0,0 +1,115 @@
# 2011 August 13
#
# 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 tests for SQLite library. The focus of the tests
# in this file is testing the capabilities of sqlite_stat4.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
ifcapable !stat4 {
finish_test
return
}
set testprefix analyze8
proc eqp {sql {db db}} {
uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] $db
}
# Scenario:
#
# Two indices. One has mostly singleton entries, but for a few
# values there are hundreds of entries. The other has 10-20
# entries per value.
#
# Verify that the query planner chooses the first index for the singleton
# entries and the second index for the others.
#
do_test 1.0 {
db eval {
CREATE TABLE t1(a,b,c,d);
CREATE INDEX t1a ON t1(a);
CREATE INDEX t1b ON t1(b);
CREATE INDEX t1c ON t1(c);
}
for {set i 0} {$i<1000} {incr i} {
if {$i%2==0} {set a $i} {set a [expr {($i%8)*100}]}
set b [expr {$i/10}]
set c [expr {$i/8}]
set c [expr {$c*$c*$c}]
db eval {INSERT INTO t1 VALUES($a,$b,$c,$i)}
}
db eval {ANALYZE}
} {}
# The a==100 comparison is expensive because there are many rows
# with a==100. And so for those cases, choose the t1b index.
#
# Buf ro a==99 and a==101, there are far fewer rows so choose
# the t1a index.
#
do_test 1.1 {
eqp {SELECT * FROM t1 WHERE a=100 AND b=55}
} {/*SEARCH t1 USING INDEX t1b (b=?)*/}
do_test 1.2 {
eqp {SELECT * FROM t1 WHERE a=99 AND b=55}
} {/*SEARCH t1 USING INDEX t1a (a=?)*/}
do_test 1.3 {
eqp {SELECT * FROM t1 WHERE a=101 AND b=55}
} {/*SEARCH t1 USING INDEX t1a (a=?)*/}
do_test 1.4 {
eqp {SELECT * FROM t1 WHERE a=100 AND b=56}
} {/*SEARCH t1 USING INDEX t1b (b=?)*/}
do_test 1.5 {
eqp {SELECT * FROM t1 WHERE a=99 AND b=56}
} {/*SEARCH t1 USING INDEX t1a (a=?)*/}
do_test 1.6 {
eqp {SELECT * FROM t1 WHERE a=101 AND b=56}
} {/*SEARCH t1 USING INDEX t1a (a=?)*/}
do_test 2.1 {
eqp {SELECT * FROM t1 WHERE a=100 AND b BETWEEN 50 AND 54}
} {/*SEARCH t1 USING INDEX t1b (b>? AND b<?)*/}
# There are many more values of c between 0 and 100000 than there are
# between 800000 and 900000. So t1c is more selective for the latter
# range.
#
# Test 3.2 is a little unstable. It depends on the planner estimating
# that (b BETWEEN 30 AND 34) will match more rows than (c BETWEEN
# 800000 AND 900000). Which is a pretty close call (50 vs. 32), so
# the planner could get it wrong with an unlucky set of samples. This
# case happens to work, but others ("b BETWEEN 40 AND 44" for example)
# will fail.
#
do_execsql_test 3.0 {
SELECT count(*) FROM t1 WHERE b BETWEEN 30 AND 34;
SELECT count(*) FROM t1 WHERE c BETWEEN 0 AND 100000;
SELECT count(*) FROM t1 WHERE c BETWEEN 800000 AND 900000;
} {50 376 32}
do_test 3.1 {
eqp {SELECT * FROM t1 WHERE b BETWEEN 30 AND 34 AND c BETWEEN 0 AND 100000}
} {/*SEARCH t1 USING INDEX t1b (b>? AND b<?)*/}
do_test 3.2 {
eqp {SELECT * FROM t1
WHERE b BETWEEN 30 AND 34 AND c BETWEEN 800000 AND 900000}
} {/*SEARCH t1 USING INDEX t1c (c>? AND c<?)*/}
do_test 3.3 {
eqp {SELECT * FROM t1 WHERE a=100 AND c BETWEEN 0 AND 100000}
} {/*SEARCH t1 USING INDEX t1a (a=?)*/}
do_test 3.4 {
eqp {SELECT * FROM t1
WHERE a=100 AND c BETWEEN 800000 AND 900000}
} {/*SEARCH t1 USING INDEX t1c (c>? AND c<?)*/}
finish_test

1235
testdata/tcl/analyze9.test vendored Normal file

File diff suppressed because it is too large Load diff

181
testdata/tcl/analyzeC.test vendored Normal file
View file

@ -0,0 +1,181 @@
# 2014-07-22
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file contains automated tests used to verify that the text terms
# at the end of sqlite_stat1.stat are processed correctly.
#
# (1) "unordered" means that the index cannot be used for ORDER BY
# or for range queries
#
# (2) "sz=NNN" sets the relative size of the index entries
#
# (3) All other fields are silently ignored
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix analyzeC
# Baseline case. Range queries work OK. Indexes can be used for
# ORDER BY.
#
do_execsql_test 1.0 {
CREATE TABLE t1(a,b,c);
INSERT INTO t1(a,b,c)
VALUES(1,2,3),(7,8,9),(4,5,6),(10,11,12),(4,8,12),(1,11,111);
CREATE INDEX t1a ON t1(a);
CREATE INDEX t1b ON t1(b);
ANALYZE;
DELETE FROM sqlite_stat1;
INSERT INTO sqlite_stat1(tbl,idx,stat)
VALUES('t1','t1a','12345 2'),('t1','t1b','12345 4');
ANALYZE sqlite_master;
SELECT *, '#' FROM t1 WHERE a BETWEEN 3 AND 8 ORDER BY c;
} {4 5 6 # 7 8 9 # 4 8 12 #}
do_execsql_test 1.1 {
EXPLAIN QUERY PLAN
SELECT *, '#' FROM t1 WHERE a BETWEEN 3 AND 8 ORDER BY c;
} {/.* USING INDEX t1a .a>. AND a<...*/}
do_execsql_test 1.2 {
SELECT c FROM t1 ORDER BY a;
} {3 111 6 12 9 12}
do_execsql_test 1.3 {
EXPLAIN QUERY PLAN
SELECT c FROM t1 ORDER BY a;
} {/.*SCAN t1 USING INDEX t1a.*/}
do_execsql_test 1.3x {
EXPLAIN QUERY PLAN
SELECT c FROM t1 ORDER BY a;
} {~/.*B-TREE FOR ORDER BY.*/}
# Now mark the t1a index as "unordered". Range queries and ORDER BY no
# longer use the index, but equality queries do.
#
do_execsql_test 2.0 {
UPDATE sqlite_stat1 SET stat='12345 2 unordered' WHERE idx='t1a';
ANALYZE sqlite_master;
SELECT *, '#' FROM t1 WHERE a BETWEEN 3 AND 8 ORDER BY c;
} {4 5 6 # 7 8 9 # 4 8 12 #}
do_execsql_test 2.1 {
EXPLAIN QUERY PLAN
SELECT *, '#' FROM t1 WHERE a BETWEEN 3 AND 8 ORDER BY c;
} {~/.*USING INDEX.*/}
do_execsql_test 2.2 {
SELECT c FROM t1 ORDER BY a;
} {3 111 6 12 9 12}
do_execsql_test 2.3 {
EXPLAIN QUERY PLAN
SELECT c FROM t1 ORDER BY a;
} {~/.*USING INDEX.*/}
do_execsql_test 2.3x {
EXPLAIN QUERY PLAN
SELECT c FROM t1 ORDER BY a;
} {/.*B-TREE FOR ORDER BY.*/}
# Ignore extraneous text parameters in the sqlite_stat1.stat field.
#
do_execsql_test 3.0 {
UPDATE sqlite_stat1 SET stat='12345 2 whatever=5 unordered xyzzy=11'
WHERE idx='t1a';
ANALYZE sqlite_master;
SELECT *, '#' FROM t1 WHERE a BETWEEN 3 AND 8 ORDER BY c;
} {4 5 6 # 7 8 9 # 4 8 12 #}
do_execsql_test 3.1 {
EXPLAIN QUERY PLAN
SELECT *, '#' FROM t1 WHERE a BETWEEN 3 AND 8 ORDER BY c;
} {~/.*USING INDEX.*/}
do_execsql_test 3.2 {
SELECT c FROM t1 ORDER BY a;
} {3 111 6 12 9 12}
do_execsql_test 3.3 {
EXPLAIN QUERY PLAN
SELECT c FROM t1 ORDER BY a;
} {~/.*USING INDEX.*/}
do_execsql_test 3.3x {
EXPLAIN QUERY PLAN
SELECT c FROM t1 ORDER BY a;
} {/.*B-TREE FOR ORDER BY.*/}
# The sz=NNN parameter determines which index to scan
#
do_execsql_test 4.0 {
DROP INDEX t1a;
CREATE INDEX t1ab ON t1(a,b);
CREATE INDEX t1ca ON t1(c,a);
DELETE FROM sqlite_stat1;
INSERT INTO sqlite_stat1(tbl,idx,stat)
VALUES('t1','t1ab','12345 3 2 sz=10'),('t1','t1ca','12345 3 2 sz=20');
ANALYZE sqlite_master;
SELECT count(a) FROM t1;
} {6}
do_execsql_test 4.1 {
EXPLAIN QUERY PLAN
SELECT count(a) FROM t1;
} {/.*INDEX t1ab.*/}
do_execsql_test 4.2 {
DELETE FROM sqlite_stat1;
INSERT INTO sqlite_stat1(tbl,idx,stat)
VALUES('t1','t1ab','12345 3 2 sz=20'),('t1','t1ca','12345 3 2 sz=10');
ANALYZE sqlite_master;
SELECT count(a) FROM t1;
} {6}
do_execsql_test 4.3 {
EXPLAIN QUERY PLAN
SELECT count(a) FROM t1;
} {/.*INDEX t1ca.*/}
# 2019-08-15.
# Ticket https://www.sqlite.org/src/tktview/e4598ecbdd18bd82945f602901
# The sz=N parameter in the sqlite_stat1 table needs to have a value of
# 2 or more to avoid a division by zero in the query planner.
#
do_execsql_test 4.4 {
DROP TABLE IF EXISTS t44;
CREATE TABLE t44(a PRIMARY KEY);
INSERT INTO sqlite_stat1 VALUES('t44',null,'sz=0');
ANALYZE sqlite_master;
SELECT 0 FROM t44 WHERE a IN(1,2,3);
} {}
# The sz=NNN parameter works even if there is other extraneous text
# in the sqlite_stat1.stat column.
#
do_execsql_test 5.0 {
DELETE FROM sqlite_stat1;
INSERT INTO sqlite_stat1(tbl,idx,stat)
VALUES('t1','t1ab','12345 3 2 x=5 sz=10 y=10'),
('t1','t1ca','12345 3 2 whatever sz=20 junk');
ANALYZE sqlite_master;
SELECT count(a) FROM t1;
} {6}
do_execsql_test 5.1 {
EXPLAIN QUERY PLAN
SELECT count(a) FROM t1;
} {/.*INDEX t1ab.*/}
do_execsql_test 5.2 {
DELETE FROM sqlite_stat1;
INSERT INTO sqlite_stat1(tbl,idx,stat)
VALUES('t1','t1ca','12345 3 2 x=5 sz=10 y=10'),
('t1','t1ab','12345 3 2 whatever sz=20 junk');
ANALYZE sqlite_master;
SELECT count(a) FROM t1;
} {6}
do_execsql_test 5.3 {
EXPLAIN QUERY PLAN
SELECT count(a) FROM t1;
} {/.*INDEX t1ca.*/}
finish_test

107
testdata/tcl/analyzeD.test vendored Normal file
View file

@ -0,0 +1,107 @@
# 2014-10-04
#
# 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.
# This file implements tests for the ANALYZE command.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix analyzeD
ifcapable {!stat4} {
finish_test
return
}
# Set up a table with the following properties:
#
# * Contains 1000 rows.
# * Column a contains even integers between 0 and 18, inclusive (so that
# a=? for any such integer matches 100 rows).
# * Column b contains integers between 0 and 9, inclusive.
# * Column c contains integers between 0 and 199, inclusive (so that
# for any such integer, c=? matches 5 rows).
# * Then add 7 rows with a new value for "a" - 3001. The stat4 table will
# not contain any samples with a=3001.
#
do_execsql_test 1.0 {
CREATE TABLE t1(a, b, c);
}
do_test 1.1 {
for {set i 1} {$i < 1000} {incr i} {
set c [expr $i % 200]
execsql { INSERT INTO t1(a, b, c) VALUES( 2*($i/100), $i%10, $c ) }
}
execsql {
INSERT INTO t1 VALUES(3001, 3001, 3001);
INSERT INTO t1 VALUES(3001, 3001, 3002);
INSERT INTO t1 VALUES(3001, 3001, 3003);
INSERT INTO t1 VALUES(3001, 3001, 3004);
INSERT INTO t1 VALUES(3001, 3001, 3005);
INSERT INTO t1 VALUES(3001, 3001, 3006);
INSERT INTO t1 VALUES(3001, 3001, 3007);
CREATE INDEX t1_ab ON t1(a, b);
CREATE INDEX t1_c ON t1(c);
ANALYZE;
}
} {}
# With full ANALYZE data, SQLite sees that c=150 (5 rows) is better than
# a=3001 (7 rows).
#
do_eqp_test 1.2 {
SELECT * FROM t1 WHERE a=3001 AND c=150;
} {SEARCH t1 USING INDEX t1_c (c=?)}
do_test 1.3 {
execsql { DELETE FROM sqlite_stat1 }
db close
sqlite3 db test.db
} {}
# Without stat1, because 3001 is larger than all samples in the stat4
# table, SQLite thinks that a=3001 matches just 1 row. So it (incorrectly)
# chooses it over the c=150 index (5 rows). Even with stat1 data, things
# worked this way before commit [e6f7f97dbc].
#
do_eqp_test 1.4 {
SELECT * FROM t1 WHERE a=3001 AND c=150;
} {SEARCH t1 USING INDEX t1_ab (a=?)}
do_test 1.5 {
execsql {
UPDATE t1 SET a=13 WHERE a = 3001;
ANALYZE;
}
} {}
do_eqp_test 1.6 {
SELECT * FROM t1 WHERE a=13 AND c=150;
} {SEARCH t1 USING INDEX t1_c (c=?)}
do_test 1.7 {
execsql { DELETE FROM sqlite_stat1 }
db close
sqlite3 db test.db
} {}
# Same test as 1.4, except this time the 7 rows that match the a=? condition
# do not feature larger values than all rows in the stat4 table. So SQLite
# gets this right, even without stat1 data.
do_eqp_test 1.8 {
SELECT * FROM t1 WHERE a=13 AND c=150;
} {SEARCH t1 USING INDEX t1_c (c=?)}
finish_test

242
testdata/tcl/analyzeE.test vendored Normal file
View file

@ -0,0 +1,242 @@
# 2014-10-08
#
# 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 tests for using STAT4 information
# on a descending index in a range query.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix analyzeE
ifcapable {!stat4} {
finish_test
return
}
# Verify that range queries on an ASCENDING index will use the
# index only if the range covers only a small fraction of the
# entries.
#
do_execsql_test analyzeE-1.0 {
CREATE TABLE t1(a,b);
WITH RECURSIVE
cnt(x) AS (VALUES(1000) UNION ALL SELECT x+1 FROM cnt WHERE x<2000)
INSERT INTO t1(a,b) SELECT x, x FROM cnt;
CREATE INDEX t1a ON t1(a);
ANALYZE;
} {}
do_execsql_test analyzeE-1.1 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a BETWEEN 500 AND 2500;
} {/SCAN t1/}
do_execsql_test analyzeE-1.2 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a BETWEEN 2900 AND 3000;
} {/SEARCH t1 USING INDEX t1a/}
do_execsql_test analyzeE-1.3 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a BETWEEN 1700 AND 1750;
} {/SEARCH t1 USING INDEX t1a/}
do_execsql_test analyzeE-1.4 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a BETWEEN 1 AND 500
} {/SEARCH t1 USING INDEX t1a/}
do_execsql_test analyzeE-1.5 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a BETWEEN 3000 AND 3000000
} {/SEARCH t1 USING INDEX t1a/}
do_execsql_test analyzeE-1.6 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a<500
} {/SEARCH t1 USING INDEX t1a/}
do_execsql_test analyzeE-1.7 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a>2500
} {/SEARCH t1 USING INDEX t1a/}
do_execsql_test analyzeE-1.8 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a>1900
} {/SEARCH t1 USING INDEX t1a/}
do_execsql_test analyzeE-1.9 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a>1100
} {/SCAN t1/}
do_execsql_test analyzeE-1.10 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a<1100
} {/SEARCH t1 USING INDEX t1a/}
do_execsql_test analyzeE-1.11 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a<1900
} {/SCAN t1/}
# Verify that everything works the same on a DESCENDING index.
#
do_execsql_test analyzeE-2.0 {
DROP INDEX t1a;
CREATE INDEX t1a ON t1(a DESC);
ANALYZE;
} {}
do_execsql_test analyzeE-2.1 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a BETWEEN 500 AND 2500;
} {/SCAN t1/}
do_execsql_test analyzeE-2.2 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a BETWEEN 2900 AND 3000;
} {/SEARCH t1 USING INDEX t1a/}
do_execsql_test analyzeE-2.3 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a BETWEEN 1700 AND 1750;
} {/SEARCH t1 USING INDEX t1a/}
do_execsql_test analyzeE-2.4 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a BETWEEN 1 AND 500
} {/SEARCH t1 USING INDEX t1a/}
do_execsql_test analyzeE-2.5 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a BETWEEN 3000 AND 3000000
} {/SEARCH t1 USING INDEX t1a/}
do_execsql_test analyzeE-2.6 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a<500
} {/SEARCH t1 USING INDEX t1a/}
do_execsql_test analyzeE-2.7 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a>2500
} {/SEARCH t1 USING INDEX t1a/}
do_execsql_test analyzeE-2.8 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a>1900
} {/SEARCH t1 USING INDEX t1a/}
do_execsql_test analyzeE-2.9 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a>1100
} {/SCAN t1/}
do_execsql_test analyzeE-2.10 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a<1100
} {/SEARCH t1 USING INDEX t1a/}
do_execsql_test analyzeE-2.11 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a<1900
} {/SCAN t1/}
# Now do a range query on the second term of an ASCENDING index
# where the first term is constrained by equality.
#
do_execsql_test analyzeE-3.0 {
DROP TABLE t1;
CREATE TABLE t1(a,b,c);
WITH RECURSIVE
cnt(x) AS (VALUES(1000) UNION ALL SELECT x+1 FROM cnt WHERE x<2000)
INSERT INTO t1(a,b,c) SELECT x, x, 123 FROM cnt;
CREATE INDEX t1ca ON t1(c,a);
ANALYZE;
} {}
do_execsql_test analyzeE-3.1 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a BETWEEN 500 AND 2500 AND c=123;
} {/SCAN t1/}
do_execsql_test analyzeE-3.2 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a BETWEEN 2900 AND 3000 AND c=123;
} {/SEARCH t1 USING INDEX t1ca/}
do_execsql_test analyzeE-3.3 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a BETWEEN 1700 AND 1750 AND c=123;
} {/SEARCH t1 USING INDEX t1ca/}
do_execsql_test analyzeE-3.4 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a BETWEEN 1 AND 500 AND c=123
} {/SEARCH t1 USING INDEX t1ca/}
do_execsql_test analyzeE-3.5 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a BETWEEN 3000 AND 3000000 AND c=123
} {/SEARCH t1 USING INDEX t1ca/}
do_execsql_test analyzeE-3.6 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a<500 AND c=123
} {/SEARCH t1 USING INDEX t1ca/}
do_execsql_test analyzeE-3.7 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a>2500 AND c=123
} {/SEARCH t1 USING INDEX t1ca/}
do_execsql_test analyzeE-3.8 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a>1900 AND c=123
} {/SEARCH t1 USING INDEX t1ca/}
do_execsql_test analyzeE-3.9 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a>1100 AND c=123
} {/SCAN t1/}
do_execsql_test analyzeE-3.10 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a<1100 AND c=123
} {/SEARCH t1 USING INDEX t1ca/}
do_execsql_test analyzeE-3.11 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a<1900 AND c=123
} {/SCAN t1/}
# Repeat the 3.x tests using a DESCENDING index
#
do_execsql_test analyzeE-4.0 {
DROP INDEX t1ca;
CREATE INDEX t1ca ON t1(c ASC,a DESC);
ANALYZE;
} {}
do_execsql_test analyzeE-4.1 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a BETWEEN 500 AND 2500 AND c=123;
} {/SCAN t1/}
do_execsql_test analyzeE-4.2 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a BETWEEN 2900 AND 3000 AND c=123;
} {/SEARCH t1 USING INDEX t1ca/}
do_execsql_test analyzeE-4.3 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a BETWEEN 1700 AND 1750 AND c=123;
} {/SEARCH t1 USING INDEX t1ca/}
do_execsql_test analyzeE-4.4 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a BETWEEN 1 AND 500 AND c=123
} {/SEARCH t1 USING INDEX t1ca/}
do_execsql_test analyzeE-4.5 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a BETWEEN 3000 AND 3000000 AND c=123
} {/SEARCH t1 USING INDEX t1ca/}
do_execsql_test analyzeE-4.6 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a<500 AND c=123
} {/SEARCH t1 USING INDEX t1ca/}
do_execsql_test analyzeE-4.7 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a>2500 AND c=123
} {/SEARCH t1 USING INDEX t1ca/}
do_execsql_test analyzeE-4.8 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a>1900 AND c=123
} {/SEARCH t1 USING INDEX t1ca/}
do_execsql_test analyzeE-4.9 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a>1100 AND c=123
} {/SCAN t1/}
do_execsql_test analyzeE-4.10 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a<1100 AND c=123
} {/SEARCH t1 USING INDEX t1ca/}
do_execsql_test analyzeE-4.11 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a<1900 AND c=123
} {/SCAN t1/}
finish_test

150
testdata/tcl/analyzeF.test vendored Normal file
View file

@ -0,0 +1,150 @@
# 2015-03-12
#
# 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.
#
#***********************************************************************
# Test that deterministic scalar functions passed constant arguments
# are used with stat4 data.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix analyzeF
ifcapable {!stat4} {
finish_test
return
}
proc isqrt {i} { expr { int(sqrt($i)) } }
db func isqrt isqrt
do_execsql_test 1.0 {
CREATE TABLE t1(x INTEGER, y INTEGER);
WITH data(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM data
)
INSERT INTO t1 SELECT isqrt(i), isqrt(i) FROM data LIMIT 400;
CREATE INDEX t1x ON t1(x);
CREATE INDEX t1y ON t1(y);
ANALYZE;
}
proc str {a} { return $a }
db func str str
# Note: tests 7 to 12 might be unstable - as they assume SQLite will
# prefer the expression to the right of the AND clause. Which of
# course could change.
#
# Note 2: tests 9 and 10 depend on the tcl interface creating functions
# without the SQLITE_DETERMINISTIC flag set.
#
foreach {tn where idx} {
1 "x = 4 AND y = 19" {t1x (x=?)}
2 "x = 19 AND y = 4" {t1y (y=?)}
3 "x = '4' AND y = '19'" {t1x (x=?)}
4 "x = '19' AND y = '4'" {t1y (y=?)}
5 "x = substr('5195', 2, 2) AND y = substr('145', 2, 1)" {t1y (y=?)}
6 "x = substr('145', 2, 1) AND y = substr('5195', 2, 2)" {t1x (x=?)}
7 "x = substr('5195', 2, 2+0) AND y = substr('145', 2, 1+0)" {t1y (y=?)}
8 "x = substr('145', 2, 1+0) AND y = substr('5195', 2, 2+0)" {t1y (y=?)}
9 "x = str('19') AND y = str('4')" {t1y (y=?)}
10 "x = str('4') AND y = str('19')" {t1y (y=?)}
11 "x = nullif('19', 0) AND y = nullif('4', 0)" {t1y (y=?)}
12 "x = nullif('4', 0) AND y = nullif('19', 0)" {t1y (y=?)}
} {
set res "SEARCH t1 USING INDEX $idx"
do_eqp_test 1.$tn "SELECT * FROM t1 WHERE $where" $res
}
# Test that functions that do not exist - "func()" - do not cause an error.
#
do_catchsql_test 2.1 {
SELECT * FROM t1 WHERE x = substr('145', 2, 1) AND y = func(1, 2, 3)
} {1 {no such function: func}}
do_catchsql_test 2.2 {
UPDATE t1 SET y=y+1 WHERE x = substr('145', 2, 1) AND y = func(1, 2, 3)
} {1 {no such function: func}}
# Check that functions that accept zero arguments do not cause problems.
#
proc ret {x} { return $x }
db func det4 -deterministic [list ret 4]
db func nondet4 [list ret 4]
db func det19 -deterministic [list ret 19]
db func nondet19 [list ret 19]
foreach {tn where idx} {
1 "x = det4() AND y = det19()" {t1x (x=?)}
2 "x = det19() AND y = det4()" {t1y (y=?)}
3 "x = nondet4() AND y = nondet19()" {t1y (y=?)}
4 "x = nondet19() AND y = nondet4()" {t1y (y=?)}
} {
set res "SEARCH t1 USING INDEX $idx"
do_eqp_test 3.$tn "SELECT * FROM t1 WHERE $where" $res
}
execsql { DELETE FROM t1 }
proc throw_error {err} { error $err }
db func error -deterministic throw_error
do_catchsql_test 4.1 {
SELECT * FROM t1 WHERE x = error('error one') AND y = 4;
} {1 {error one}}
do_catchsql_test 4.2 {
SELECT * FROM t1 WHERE x = zeroblob(2200000000) AND y = 4;
} {1 {string or blob too big}}
sqlite3_limit db SQLITE_LIMIT_LENGTH 1000000
proc dstr {} { return [string repeat x 1100000] }
db func dstr -deterministic dstr
do_catchsql_test 4.3 {
SELECT * FROM t1 WHERE x = dstr() AND y = 11;
} {1 {string or blob too big}}
do_catchsql_test 4.4 {
SELECT * FROM t1 WHERE x = test_zeroblob(1100000) AND y = 4;
} {1 {string or blob too big}}
# 2016-12-08: Constraints of the form "x=? AND x IS NOT NULL" were being
# mishandled. The sqlite3Stat4ProbeSetValue() routine was assuming that
# valueNew() was returning a Mem object that was preset to NULL, which is
# not the case. The consequence was the the "x IS NOT NULL" constraint
# was used to drive the index (via the "x>NULL" pseudo-constraint) rather
# than the "x=?" constraint.
#
do_execsql_test 5.1 {
DROP TABLE IF EXISTS t1;
CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT, c INT);
WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<10000)
INSERT INTO t1(a, c) SELECT x, x FROM c;
UPDATE t1 SET b=printf('x%02x',a/500) WHERE a>4000;
UPDATE t1 SET b='xyz' where a>=9998;
CREATE INDEX t1b ON t1(b);
ANALYZE;
SELECT count(*), b FROM t1 GROUP BY 2 ORDER BY 2;
} {4000 {} 499 x08 500 x09 500 x0a 500 x0b 500 x0c 500 x0d 500 x0e 500 x0f 500 x10 500 x11 500 x12 498 x13 3 xyz}
do_execsql_test 5.2 {
explain query plan
SELECT * FROM t1 WHERE b='xyz' AND b IS NOT NULL ORDER BY +a;
/* v---- Should be "=", not ">" */
} {/USING INDEX t1b .b=/}
do_execsql_test 5.3 {
SELECT * FROM t1 WHERE b='xyz' AND b IS NOT NULL ORDER BY +a;
} {9998 xyz 9998 9999 xyz 9999 10000 xyz 10000}
finish_test

84
testdata/tcl/analyzeG.test vendored Normal file
View file

@ -0,0 +1,84 @@
# 2020-02-23
#
# 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 functionality related to ANALYZE.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
ifcapable !stat4 {
finish_test
return
}
set testprefix analyzeG
#-------------------------------------------------------------------------
# Test cases 1.* seek to verify that even if an index is not used, its
# stat4 data may be used by the planner to estimate the number of
# rows that match an unindexed constraint on the same column.
#
do_execsql_test 1.0 {
PRAGMA automatic_index = 0;
CREATE TABLE t1(a, x);
CREATE TABLE t2(b, y);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100
)
INSERT INTO t1 SELECT (i%50), NULL FROM s;
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100
)
INSERT INTO t2 SELECT (CASE WHEN i<95 THEN 44 ELSE i END), NULL FROM s;
}
# Join tables t1 and t2. Both contain 100 rows. (a=44) matches 2 rows
# in "t1", (b=44) matches 95 rows in table "t2". But the planner doesn't
# know this, so it has no preference as to which order the tables are
# scanned in. In practice this means that tables are scanned in the order
# they are specified in in the FROM clause.
do_eqp_test 1.1.1 {
SELECT * FROM t1, t2 WHERE a=44 AND b=44;
} {
}
do_eqp_test 1.1.2 {
SELECT * FROM t2, t1 WHERE a=44 AND b=44
} {
QUERY PLAN
|--SCAN t2
`--SCAN t1
}
do_execsql_test 1.2 {
CREATE INDEX t2b ON t2(b);
ANALYZE;
}
# Now, with the ANALYZE data, the planner knows that (b=44) matches a
# large number of rows. So it elects to scan table "t1" first, regardless
# of the order in which the tables are specified in the FROM clause.
do_eqp_test 1.3.1 {
SELECT * FROM t1, t2 WHERE a=44 AND b=44;
} {
QUERY PLAN
|--SCAN t1
`--SCAN t2
}
do_eqp_test 1.3.2 {
SELECT * FROM t2, t1 WHERE a=44 AND b=44
} {
QUERY PLAN
|--SCAN t1
`--SCAN t2
}
finish_test

55
testdata/tcl/analyzer1.test vendored Normal file
View file

@ -0,0 +1,55 @@
# 2015-05-11
#
# 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.
#
#***********************************************************************
#
# Quick tests for the sqlite3_analyzer tool
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
ifcapable !vtab {
finish_test
return
}
if {$tcl_platform(platform)=="windows"} {
set PROG "sqlite3_analyzer.exe"
} else {
set PROG "./sqlite3_analyzer"
}
if {![file exe $PROG]} {
set PROG [file normalize [file join $::cmdlinearg(TESTFIXTURE_HOME) $PROG]]
if {![file exe $PROG]} {
puts "analyzer1 cannot run because $PROG is not available"
finish_test
return
}
}
db close
forcedelete test.db test.db-journal test.db-wal
sqlite3 db test.db
do_test analyzer1-1.0 {
db eval {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
CREATE TABLE t2(a INT PRIMARY KEY, b) WITHOUT ROWID;
WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<250)
INSERT INTO t1(a,b) SELECT x, randomblob(200) FROM c;
INSERT INTO t2(a,b) SELECT a, b FROM t1;
}
set line "exec $PROG test.db"
unset -nocomplain ::MSG
catch {eval $line} ::MSG
} {0}
do_test analyzer1-1.1 {
regexp {^/\*\* Disk-Space Utilization.*COMMIT;\W*$} $::MSG
} {1}
finish_test

90
testdata/tcl/async.test vendored Normal file
View file

@ -0,0 +1,90 @@
#
# 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 runs all tests.
#
# $Id: async.test,v 1.21 2009/06/05 17:09:12 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
if {[info commands sqlite3async_initialize] eq ""} {
# The async logic is not built into this system
finish_test
return
}
rename finish_test async_really_finish_test
proc finish_test {} {
catch {db close}
catch {db2 close}
catch {db3 close}
}
if {[info exists G(isquick)]} { set ASYNC_SAVE_ISQUICK $G(isquick) }
set G(isquick) 1
set ASYNC_INCLUDE {
insert.test
insert2.test
insert3.test
lock.test
lock2.test
lock3.test
select1.test
select2.test
select3.test
select4.test
trans.test
}
# Enable asynchronous IO.
sqlite3async_initialize "" 1
# This proc flushes the contents of the async-IO queue through to the
# underlying VFS. A couple of the test scripts identified in $ASYNC_INCLUDE
# above contain lines like "catch flush_async_queue" in places where
# this is required for the tests to work in async mode.
#
proc flush_async_queue {} {
sqlite3async_control halt idle
sqlite3async_start
sqlite3async_wait
sqlite3async_control halt never
}
rename do_test async_really_do_test
proc do_test {name args} {
uplevel async_really_do_test async_io-$name $args
flush_async_queue
}
foreach testfile [lsort -dictionary [glob $testdir/*.test]] {
set tail [file tail $testfile]
if {[lsearch -exact $ASYNC_INCLUDE $tail]<0} continue
source $testfile
# Make sure everything is flushed through. This is because [source]ing
# the next test file will delete the database file on disk (using
# [delete_file]). If the asynchronous backend still has the file
# open, it will become confused.
#
flush_async_queue
}
# Flush the write-queue and disable asynchronous IO. This should ensure
# all allocated memory is cleaned up.
set sqlite3async_trace 1
flush_async_queue
sqlite3async_shutdown
set sqlite3async_trace 0
rename do_test {}
rename async_really_do_test do_test
rename finish_test {}
rename async_really_finish_test finish_test
if {[info exists ASYNC_SAVE_ISQUICK]} { set G(isquick) $ASYNC_SAVE_ISQUICK }
finish_test

126
testdata/tcl/async2.test vendored Normal file
View file

@ -0,0 +1,126 @@
#
# 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.
#
#***********************************************************************
#
# $Id: async2.test,v 1.12 2009/04/25 08:39:15 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
if {
[info commands sqlite3async_initialize]=="" ||
[info command sqlite3_memdebug_fail]==""
} {
# The async logic is not built into this system
puts "Skipping async2 tests: not compiled with required features"
finish_test
return
}
# Enable asynchronous IO.
set setup_script {
CREATE TABLE counter(c);
INSERT INTO counter(c) VALUES (1);
}
set sql_script {
BEGIN;
UPDATE counter SET c = 2;
CREATE TABLE t1(a PRIMARY KEY, b, c);
CREATE TABLE t2(a PRIMARY KEY, b, c);
COMMIT;
BEGIN;
UPDATE counter SET c = 3;
INSERT INTO t1 VALUES('abcdefghij', 'four', 'score');
INSERT INTO t2 VALUES('klmnopqrst', 'and', 'seven');
COMMIT;
UPDATE counter SET c = 'FIN';
}
db close
foreach err [list ioerr malloc-transient malloc-persistent] {
set ::go 10
for {set n 1} {$::go} {incr n} {
set ::sqlite_io_error_pending 0
sqlite3_memdebug_fail -1
forcedelete test.db test.db-journal
sqlite3 db test.db
execsql $::setup_script
db close
sqlite3async_initialize "" 1
sqlite3 db test.db
sqlite3_db_config_lookaside db 0 0 0
switch -- $err {
ioerr { set ::sqlite_io_error_pending $n }
malloc-persistent { sqlite3_memdebug_fail $n -repeat 1 }
malloc-transient { sqlite3_memdebug_fail $n -repeat 0 }
}
catchsql $::sql_script
db close
sqlite3async_control halt idle
sqlite3async_start
sqlite3async_wait
sqlite3async_control halt never
sqlite3async_shutdown
set ::sqlite_io_error_pending 0
sqlite3_memdebug_fail -1
sqlite3 db test.db
set c [db one {SELECT c FROM counter LIMIT 1}]
switch -- $c {
1 {
do_test async-$err-1.1.$n {
execsql {
SELECT name FROM sqlite_master;
}
} {counter}
}
2 {
do_test async-$err-1.2.$n.1 {
execsql {
SELECT * FROM t1;
}
} {}
do_test async-$err-1.2.$n.2 {
execsql {
SELECT * FROM t2;
}
} {}
}
3 {
do_test async-$err-1.3.$n.1 {
execsql {
SELECT * FROM t1;
}
} {abcdefghij four score}
do_test async-$err-1.3.$n.2 {
execsql {
SELECT * FROM t2;
}
} {klmnopqrst and seven}
}
FIN {
incr ::go -1
}
}
db close
}
}
catch {db close}
finish_test

76
testdata/tcl/async3.test vendored Normal file
View file

@ -0,0 +1,76 @@
# 2007 September 5
#
# 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.
#
#***********************************************************************
#
# The focus of this file is testing the code in test_async.c.
# Specifically, it tests that the xFullPathname() method of
# of the asynchronous vfs works correctly.
#
# $Id: async3.test,v 1.5 2009/04/25 08:39:15 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
if { [info commands sqlite3async_initialize]=="" } {
# The async logic is not built into this system
puts "Skipping async3 tests: not compiled with required features"
finish_test
return
}
db close
sqlite3async_initialize "" 1
#set sqlite3async_trace 1
sqlite3async_start
set paths {
chocolate/banana/vanilla/file.db
chocolate//banana/vanilla/file.db
chocolate/./banana//vanilla/file.db
chocolate/banana/./vanilla/file.db
chocolate/banana/../banana/vanilla/file.db
chocolate/banana/./vanilla/extra_bit/../file.db
}
do_test async3-1.0 {
file mkdir [file join chocolate banana vanilla]
forcedelete chocolate/banana/vanilla/file.db
forcedelete chocolate/banana/vanilla/file.db-journal
} {}
do_test async3-1.1 {
sqlite3 db chocolate/banana/vanilla/file.db
execsql {
CREATE TABLE abc(a, b, c);
BEGIN;
INSERT INTO abc VALUES(1, 2, 3);
}
} {}
set N 2
foreach p $paths {
sqlite3 db2 $p
do_test async3-1.$N.1 {
execsql {SELECT * FROM abc} db2
} {}
do_test async3-1.$N.2 {
catchsql {INSERT INTO abc VALUES(4, 5, 6)} db2
} {1 {database is locked}}
db2 close
incr N
}
db close
sqlite3async_control halt idle
sqlite3async_wait
sqlite3async_control halt never
sqlite3async_shutdown
finish_test

168
testdata/tcl/async4.test vendored Normal file
View file

@ -0,0 +1,168 @@
# 2009 April 25
#
# 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.
#
#***********************************************************************
#
# $Id: async4.test,v 1.4 2009/06/05 17:09:12 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# Do not use a codec for tests in this file, as the database file is
# manipulated directly using tcl scripts (using the [hexio_write] command).
#
do_not_use_codec
# These tests only work for Tcl version 8.5 and later on Windows (for now)
#
if {$tcl_platform(platform)=="windows"} {
scan $::tcl_version %f vx
if {$vx<8.5} {
finish_test
return
}
}
if {[info commands sqlite3async_initialize] eq ""} {
# The async logic is not built into this system
finish_test
return
}
db close
# Test layout:
#
# async4.1.*: Test the lockfiles parameter.
# async4.2.*: Test the delay parameter.
do_test async4.1.1 {
sqlite3async_initialize {} 0
sqlite3async_control lockfiles
} {1}
do_test async4.1.2 {
sqlite3async_control lockfiles false
} {0}
do_test async4.1.3 {
sqlite3async_control lockfiles
} {0}
do_test async4.1.4 {
sqlite3async_control lockfiles true
} {1}
do_test async4.1.5 {
sqlite3 db test.db -vfs sqlite3async
execsql { CREATE TABLE t1(a, b, c) }
} {}
do_test async4.1.6 {
list [file exists test.db] [file size test.db]
} {1 0}
do_test async4.1.7 {
sqlite3 db2 test.db
catchsql { CREATE TABLE t2(a, b, c) } db2
} {1 {database is locked}}
do_test async4.1.8 {
sqlite3async_control halt idle
sqlite3async_start
sqlite3async_wait
} {}
do_test async4.1.9 {
catchsql { CREATE TABLE t2(a, b, c) } db2
} {0 {}}
do_test async4.1.10 {
list [catch {sqlite3async_control lockfiles false} msg] $msg
} {1 SQLITE_MISUSE}
do_test async4.1.11 {
db close
list [catch {sqlite3async_control lockfiles false} msg] $msg
} {1 SQLITE_MISUSE}
do_test async4.1.12 {
sqlite3async_start
sqlite3async_wait
sqlite3async_control lockfiles false
} {0}
do_test async4.1.13 {
sqlite3 db test.db -vfs sqlite3async
execsql { CREATE TABLE t3(a, b, c) } db
} {}
do_test async4.1.14 {
execsql {
CREATE INDEX i1 ON t2(a);
CREATE INDEX i2 ON t1(a);
} db2
} {}
do_test async4.1.15 {
sqlite3async_start
sqlite3async_wait
hexio_write test.db 28 00000000
execsql { pragma integrity_check } db2
} {{*** in database main ***
Page 5 is never used}}
do_test async4.1.16 {
db close
db2 close
sqlite3async_start
sqlite3async_wait
} {}
do_test async4.1.17 {
sqlite3async_control lockfiles true
} {1}
do_test async4.2.1 {
sqlite3async_control delay
} {0}
do_test async4.2.2 {
sqlite3async_control delay 23
} {23}
do_test async4.2.3 {
sqlite3async_control delay
} {23}
do_test async4.2.4 {
sqlite3async_control delay 0
} {0}
do_test async4.2.5 {
sqlite3 db test.db -vfs sqlite3async
execsql { CREATE TABLE t4(a, b) }
set T1 [lindex [time {
sqlite3async_start
sqlite3async_wait
}] 0]
sqlite3async_control delay 100
execsql { CREATE TABLE t5(a, b) }
set T2 [lindex [time {
sqlite3async_start
sqlite3async_wait
}] 0]
expr {($T1+1000000) < $T2}
} {1}
do_test async4.2.6 {
sqlite3async_control delay 0
execsql { CREATE TABLE t6(a, b) }
set T1 [lindex [time {
sqlite3async_start
sqlite3async_wait
}] 0]
expr {($T1+1000000) < $T2}
} {1}
do_test async4.2.7 {
list [catch { sqlite3async_control delay -1 } msg] $msg
} {1 SQLITE_MISUSE}
do_test async4.2.8 {
db close
sqlite3async_start
sqlite3async_wait
} {}
finish_test

68
testdata/tcl/async5.test vendored Normal file
View file

@ -0,0 +1,68 @@
# 2009 July 19
#
# 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 tests that asynchronous IO is compatible with multi-file
# transactions.
#
# $Id: async5.test,v 1.1 2009/07/18 11:52:04 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
if {[info commands sqlite3async_initialize] eq ""} {
# The async logic is not built into this system
finish_test
return
}
db close
forcedelete test2.db
sqlite3async_initialize "" 1
sqlite3async_control halt never
sqlite3 db test.db
do_test async5-1.1 {
execsql {
ATTACH 'test2.db' AS next;
CREATE TABLE main.t1(a, b);
CREATE TABLE next.t2(a, b);
BEGIN;
INSERT INTO t1 VALUES(1, 2);
INSERT INTO t2 VALUES(3, 4);
COMMIT;
}
} {}
do_test async5-1.2 {
execsql { SELECT * FROM t1 }
} {1 2}
do_test async5-1.3 {
execsql { SELECT * FROM t2 }
} {3 4}
do_test async5-1.4 {
execsql {
BEGIN;
INSERT INTO t1 VALUES('a', 'b');
INSERT INTO t2 VALUES('c', 'd');
COMMIT;
}
} {}
do_test async5-1.5 {
execsql { SELECT * FROM t1 }
} {1 2 a b}
do_test async5-1.6 {
execsql { SELECT * FROM t2 }
} {3 4 c d}
db close
sqlite3async_control halt idle
sqlite3async_start
sqlite3async_wait
sqlite3async_control halt never
sqlite3async_shutdown
set sqlite3async_trace 0
finish_test

88
testdata/tcl/atof1.test vendored Normal file
View file

@ -0,0 +1,88 @@
# 2012 June 18
#
# 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 of the sqlite3AtoF() function.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
if {$::longdouble_size<=8} {
finish_test
return
}
if {$::tcl_platform(machine)!="x86_64"} {
finish_test
return
}
expr srand(1)
for {set i 1} {$i<20000} {incr i} {
set pow [expr {int((rand()-0.5)*100)}]
set x [expr {pow((rand()-0.5)*2*rand(),$pow)}]
set xf [format %.32e $x]
# Verify that text->real conversions get exactly same ieee754 floating-
# point value in SQLite as they do in TCL.
#
do_test atof1-1.$i.1 {
set y [db eval "SELECT $xf=\$x"]
if {!$y} {
puts -nonewline \173[db eval "SELECT real2hex($xf), real2hex(\$x)"]\175
db eval "SELECT $xf+0.0 AS a, \$x AS b" {
puts [format "\n%.60e\n%.60e\n%.60e" $x $a $b]
}
}
set y
} {1}
# Verify that round-trip real->text->real conversions using the quote()
# function preserve the bits of the numeric value exactly.
#
do_test atof1-1.$i.2 {
set y [db eval {SELECT $x=CAST(quote($x) AS real)}]
if {!$y} {
db eval {SELECT real2hex($x) a, real2hex(CAST(quote($x) AS real)) b} {}
puts "\nIN: $a $xf"
puts [format {QUOTE: %16s %s} {} [db eval {SELECT quote($x)}]]
db eval {SELECT CAST(quote($x) AS real) c} {}
puts "OUT: $b [format %.32e $c]"
}
set y
} {1}
}
# 2020-01-08 ticket 9eda2697f5cc1aba
# When running sqlite3AtoF() on a blob with an odd number of bytes using
# UTF16, ignore the last byte so that the string has an integer number of
# UTF16 code points.
#
reset_db
do_execsql_test atof1-2.10 {
PRAGMA encoding = 'UTF16be';
CREATE TABLE t1(a, b);
INSERT INTO t1(rowid,a) VALUES (1,x'00'),(2,3);
SELECT substr(a,',') is true FROM t1 ORDER BY rowid;
} {0 1}
do_execsql_test atof1-2.20 {
SELECT substr(a,',') is true FROM t1 ORDER BY rowid DESC;
} {1 0}
do_execsql_test atof1-2.30 {
CREATE INDEX i1 ON t1(a);
SELECT count(*) FROM t1 WHERE substr(a,',');
} {1}
# 2020-08-27 OSSFuzz find related to the above.
do_execsql_test atof1-2.40 {
SELECT randomblob(0) - 1;
} {-1}
finish_test

41
testdata/tcl/atomic.test vendored Normal file
View file

@ -0,0 +1,41 @@
# 2015-11-07
#
# 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 WITH clause.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix atomic
db close
if {[atomic_batch_write test.db]==0} {
puts "No f2fs atomic-batch-write support. Skipping tests..."
finish_test
return
}
reset_db
do_execsql_test 1.0 {
CREATE TABLE t1(x, y);
BEGIN;
INSERT INTO t1 VALUES(1, 2);
}
do_test 1.1 { file exists test.db-journal } {0}
do_execsql_test 1.2 {
COMMIT;
}
finish_test

95
testdata/tcl/atomic2.test vendored Normal file
View file

@ -0,0 +1,95 @@
# 2018-07-15
#
# 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 that if an IO error is encountered
# as part of an atomic F2FS commit, an attempt is made to commit the
# transaction using a legacy journal commit.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
set ::testprefix atomic2
db close
if {[atomic_batch_write test.db]==0} {
puts "No f2fs atomic-batch-write support. Skipping tests..."
finish_test
return
}
reset_db
do_execsql_test 1.0 {
CREATE TABLE t1(x, y);
CREATE INDEX i1x ON t1(x);
CREATE INDEX i2x ON t1(y);
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100 )
INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM s;
}
set setup [list \
-injectstart at_injectstart \
-injectstop at_injectstop \
]
set ::at_fail 0
set ::at_nfail 0
proc at_injectstart {iFail} {
set ::at_fail $iFail
set ::at_nfail 0
}
proc at_injectstop {} {
set ::at_fail 0
return $::at_nfail
}
proc at_vfs_callback {method file z args} {
if {$::at_fail>0} {
incr ::at_fail -1
if {$::at_fail==0} {
incr ::at_nfail
return SQLITE_IOERR
} elseif {$method=="xFileControl" && $z=="COMMIT_ATOMIC_WRITE"} {
set ::at_fail 0
}
}
return SQLITE_OK
}
testvfs tvfs -default 1
tvfs script at_vfs_callback
tvfs filter {xFileControl xWrite}
faultsim_save_and_close
do_one_faultsim_test 2.0 {*}$setup -prep {
faultsim_restore_and_reopen
} -body {
execsql {
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100 )
INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM s;
}
} -test {
faultsim_test_result {0 {}}
set res [execsql {SELECT count(*) FROM t1; PRAGMA integrity_check}]
if {$res!="200 ok"} {
error "expected {200 ok}, got $res"
}
}
db close
tvfs delete
finish_test

150
testdata/tcl/atrc.c vendored Normal file
View file

@ -0,0 +1,150 @@
/*
** This program generates a script that stresses the ALTER TABLE statement.
** Compile like this:
**
** gcc -g -c sqlite3.c
** gcc -g -o atrc atrc.c sqlite3.o -ldl -lpthread
**
** Run the program this way:
**
** ./atrc DATABASE | ./sqlite3 DATABASE
**
** This program "atrc" generates a script that can be fed into an ordinary
** command-line shell. The script performs many ALTER TABLE statements,
** runs ".schema --indent" and "PRAGMA integrity_check;", does more
** ALTER TABLE statements to restore the original schema, and then
** runs "PRAGMA integrity_check" again. Every table and column has its
** name changed. The entire script is contained within BEGIN...ROLLBACK
** so that no changes are ever actually made to the database.
*/
#include "sqlite3.h"
#include <stdio.h>
/*
** Generate the text of ALTER TABLE statements that will rename
** every column in table zTable to a generic name composed from
** zColPrefix and a sequential number. The generated text is
** appended pConvert. If pUndo is not NULL, then SQL text that
** will undo the change is appended to pUndo.
**
** The table to be converted must be in the "main" schema.
*/
int rename_all_columns_of_table(
sqlite3 *db, /* Database connection */
const char *zTab, /* Table whose columns should all be renamed */
const char *zColPrefix, /* Prefix for new column names */
sqlite3_str *pConvert, /* Append ALTER TABLE statements here */
sqlite3_str *pUndo /* SQL to undo the change, if not NULL */
){
sqlite3_stmt *pStmt;
int rc;
int cnt = 0;
rc = sqlite3_prepare_v2(db,
"SELECT name FROM pragma_table_info(?1);",
-1, &pStmt, 0);
if( rc ) return rc;
sqlite3_bind_text(pStmt, 1, zTab, -1, SQLITE_STATIC);
while( sqlite3_step(pStmt)==SQLITE_ROW ){
const char *zCol = (const char*)sqlite3_column_text(pStmt, 0);
cnt++;
sqlite3_str_appendf(pConvert,
"ALTER TABLE \"%w\" RENAME COLUMN \"%w\" TO \"%w%d\";\n",
zTab, zCol, zColPrefix, cnt
);
if( pUndo ){
sqlite3_str_appendf(pUndo,
"ALTER TABLE \"%w\" RENAME COLUMN \"%w%d\" TO \"%w\";\n",
zTab, zColPrefix, cnt, zCol
);
}
}
sqlite3_finalize(pStmt);
return SQLITE_OK;
}
/* Rename all tables and their columns in the main database
*/
int rename_all_tables(
sqlite3 *db, /* Database connection */
sqlite3_str *pConvert, /* Append SQL to do the rename here */
sqlite3_str *pUndo /* Append SQL to undo the rename here */
){
sqlite3_stmt *pStmt;
int rc;
int cnt = 0;
rc = sqlite3_prepare_v2(db,
"SELECT name FROM sqlite_schema WHERE type='table'"
" AND name NOT LIKE 'sqlite_%';",
-1, &pStmt, 0);
if( rc ) return rc;
while( sqlite3_step(pStmt)==SQLITE_ROW ){
const char *zTab = (const char*)sqlite3_column_text(pStmt, 0);
char *zNewTab;
char zPrefix[2];
zPrefix[0] = (cnt%26) + 'a';
zPrefix[1] = 0;
zNewTab = sqlite3_mprintf("tx%d", ++cnt);
if( pUndo ){
sqlite3_str_appendf(pUndo,
"ALTER TABLE \"%s\" RENAME TO \"%w\";\n",
zNewTab, zTab
);
}
rename_all_columns_of_table(db, zTab, zPrefix, pConvert, pUndo);
sqlite3_str_appendf(pConvert,
"ALTER TABLE \"%w\" RENAME TO \"%s\";\n",
zTab, zNewTab
);
sqlite3_free(zNewTab);
}
sqlite3_finalize(pStmt);
return SQLITE_OK;
}
/*
** Generate a script that does this:
**
** (1) Start a transaction
** (2) Rename all tables and columns to use generic names.
** (3) Print the schema after this rename
** (4) Run pragma integrity_check
** (5) Do more ALTER TABLE statements to change the names back
** (6) Run pragma integrity_check again
** (7) Rollback the transaction
*/
int main(int argc, char **argv){
sqlite3 *db;
int rc;
sqlite3_str *pConvert;
sqlite3_str *pUndo;
char *zDbName;
char *zSql1, *zSql2;
if( argc!=2 ){
fprintf(stderr, "Usage: %s DATABASE\n", argv[0]);
}
zDbName = argv[1];
rc = sqlite3_open(zDbName, &db);
if( rc ){
fprintf(stderr, "sqlite3_open() returns %d\n", rc);
return 1;
}
pConvert = sqlite3_str_new(db);
pUndo = sqlite3_str_new(db);
rename_all_tables(db, pConvert, pUndo);
zSql1 = sqlite3_str_finish(pConvert);
zSql2 = sqlite3_str_finish(pUndo);
sqlite3_close(db);
printf("BEGIN;\n");
printf("%s", zSql1);
sqlite3_free(zSql1);
printf(".schema --indent\n");
printf("PRAGMA integrity_check;\n");
printf("%s", zSql2);
sqlite3_free(zSql2);
printf("PRAGMA integrity_check;\n");
printf("ROLLBACK;\n");
return 0;
}

928
testdata/tcl/attach.test vendored Normal file
View file

@ -0,0 +1,928 @@
# 2003 April 4
#
# 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 script is testing the ATTACH and DETACH commands
# and related functionality.
#
# $Id: attach.test,v 1.52 2009/05/29 14:39:08 drh Exp $
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
ifcapable !attach {
finish_test
return
}
for {set i 2} {$i<=15} {incr i} {
forcedelete test$i.db
forcedelete test$i.db-journal
}
do_test attach-1.1 {
execsql {
CREATE TABLE t1(a,b);
INSERT INTO t1 VALUES(1,2);
INSERT INTO t1 VALUES(3,4);
SELECT * FROM t1;
}
} {1 2 3 4}
do_test attach-1.2 {
sqlite3 db2 test2.db
execsql {
CREATE TABLE t2(x,y);
INSERT INTO t2 VALUES(1,'x');
INSERT INTO t2 VALUES(2,'y');
SELECT * FROM t2;
} db2
} {1 x 2 y}
do_test attach-1.3 {
execsql {
ATTACH DATABASE 'test2.db' AS two;
SELECT * FROM two.t2;
}
} {1 x 2 y}
# Tests for the sqlite3_db_filename interface
#
do_test attach-1.3.1 {
file tail [sqlite3_db_filename db main]
} {test.db}
do_test attach-1.3.2 {
file tail [sqlite3_db_filename db MAIN]
} {test.db}
do_test attach-1.3.3 {
file tail [sqlite3_db_filename db temp]
} {}
do_test attach-1.3.4 {
file tail [sqlite3_db_filename db two]
} {test2.db}
do_test attach-1.3.5 {
file tail [sqlite3_db_filename db three]
} {}
do_test attach-1.4 {
execsql {
SELECT * FROM t2;
}
} {1 x 2 y}
do_test attach-1.5 {
execsql {
DETACH DATABASE two;
SELECT * FROM t1;
}
} {1 2 3 4}
do_test attach-1.6 {
catchsql {
SELECT * FROM t2;
}
} {1 {no such table: t2}}
do_test attach-1.7 {
catchsql {
SELECT * FROM two.t2;
}
} {1 {no such table: two.t2}}
do_test attach-1.8 {
catchsql {
ATTACH DATABASE 'test3.db' AS three;
}
} {0 {}}
do_test attach-1.9 {
catchsql {
SELECT * FROM three.sqlite_master;
}
} {0 {}}
do_test attach-1.10 {
catchsql {
DETACH DATABASE [three];
}
} {0 {}}
do_test attach-1.11 {
execsql {
ATTACH 'test.db' AS db2;
ATTACH 'test.db' AS db3;
ATTACH 'test.db' AS db4;
ATTACH 'test.db' AS db5;
ATTACH 'test.db' AS db6;
ATTACH 'test.db' AS db7;
ATTACH 'test.db' AS db8;
ATTACH 'test.db' AS db9;
}
} {}
proc db_list {db} {
set list {}
foreach {idx name file} [execsql {PRAGMA database_list} $db] {
lappend list $idx $name
}
return $list
}
ifcapable schema_pragmas {
do_test attach-1.11b {
db_list db
} {0 main 2 db2 3 db3 4 db4 5 db5 6 db6 7 db7 8 db8 9 db9}
} ;# ifcapable schema_pragmas
do_test attach-1.12 {
catchsql {
ATTACH 'test.db' as db2;
}
} {1 {database db2 is already in use}}
do_test attach-1.12.2 {
db errorcode
} {1}
do_test attach-1.13 {
catchsql {
ATTACH 'test.db' as db5;
}
} {1 {database db5 is already in use}}
do_test attach-1.14 {
catchsql {
ATTACH 'test.db' as db9;
}
} {1 {database db9 is already in use}}
do_catchsql_test attach-1.15 {
ATTACH 'test.db' as main;
} {1 {database main is already in use}}
ifcapable tempdb {
do_test attach-1.16 {
catchsql {
ATTACH 'test.db' as temp;
}
} {1 {database temp is already in use}}
}
do_catchsql_test attach-1.17 {
ATTACH 'test.db' as MAIN;
} {1 {database MAIN is already in use}}
do_test attach-1.18 {
catchsql {
ATTACH 'test.db' as db10;
ATTACH 'test.db' as db11;
}
} {0 {}}
if {$SQLITE_MAX_ATTACHED==10} {
do_test attach-1.19 {
catchsql {
ATTACH 'test.db' as db12;
}
} {1 {too many attached databases - max 10}}
do_test attach-1.19.1 {
db errorcode
} {1}
}
do_test attach-1.20.1 {
execsql {
DETACH db5;
}
} {}
ifcapable schema_pragmas {
do_test attach-1.20.2 {
db_list db
} {0 main 2 db2 3 db3 4 db4 5 db6 6 db7 7 db8 8 db9 9 db10 10 db11}
} ;# ifcapable schema_pragmas
integrity_check attach-1.20.3
ifcapable tempdb {
execsql {select * from temp.sqlite_master}
}
do_test attach-1.21 {
catchsql {
ATTACH 'test.db' as db12;
}
} {0 {}}
if {$SQLITE_MAX_ATTACHED==10} {
do_test attach-1.22 {
catchsql {
ATTACH 'test.db' as db13;
}
} {1 {too many attached databases - max 10}}
do_test attach-1.22.1 {
db errorcode
} {1}
}
do_test attach-1.23 {
catchsql {
DETACH "db14";
}
} {1 {no such database: db14}}
do_test attach-1.24 {
catchsql {
DETACH db12;
}
} {0 {}}
do_test attach-1.25 {
catchsql {
DETACH db12;
}
} {1 {no such database: db12}}
do_test attach-1.26 {
catchsql {
DETACH main;
}
} {1 {cannot detach database main}}
ifcapable tempdb {
do_test attach-1.27 {
catchsql {
DETACH Temp;
}
} {1 {cannot detach database Temp}}
} else {
do_test attach-1.27 {
catchsql {
DETACH Temp;
}
} {1 {no such database: Temp}}
}
do_test attach-1.28 {
catchsql {
DETACH db11;
DETACH db10;
DETACH db9;
DETACH db8;
DETACH db7;
DETACH db6;
DETACH db4;
DETACH db3;
DETACH db2;
}
} {0 {}}
ifcapable schema_pragmas {
ifcapable tempdb {
do_test attach-1.29 {
db_list db
} {0 main 1 temp}
} else {
do_test attach-1.29 {
db_list db
} {0 main}
}
} ;# ifcapable schema_pragmas
ifcapable {trigger} { # Only do the following tests if triggers are enabled
do_test attach-2.1 {
execsql {
CREATE TABLE tx(x1,x2,y1,y2);
CREATE TRIGGER r1 AFTER UPDATE ON t2 FOR EACH ROW BEGIN
INSERT INTO tx(x1,x2,y1,y2) VALUES(OLD.x,NEW.x,OLD.y,NEW.y);
END;
SELECT * FROM tx;
} db2;
} {}
do_test attach-2.2 {
execsql {
UPDATE t2 SET x=x+10;
SELECT * FROM tx;
} db2;
} {1 11 x x 2 12 y y}
do_test attach-2.3 {
execsql {
CREATE TABLE tx(x1,x2,y1,y2);
SELECT * FROM tx;
}
} {}
do_test attach-2.4 {
execsql {
ATTACH 'test2.db' AS db2;
}
} {}
do_test attach-2.5 {
execsql {
UPDATE db2.t2 SET x=x+10;
SELECT * FROM db2.tx;
}
} {1 11 x x 2 12 y y 11 21 x x 12 22 y y}
do_test attach-2.6 {
execsql {
SELECT * FROM main.tx;
}
} {}
do_test attach-2.7 {
execsql {
SELECT type, name, tbl_name FROM db2.sqlite_master;
}
} {table t2 t2 table tx tx trigger r1 t2}
ifcapable schema_pragmas&&tempdb {
do_test attach-2.8 {
db_list db
} {0 main 1 temp 2 db2}
} ;# ifcapable schema_pragmas&&tempdb
ifcapable schema_pragmas&&!tempdb {
do_test attach-2.8 {
db_list db
} {0 main 2 db2}
} ;# ifcapable schema_pragmas&&!tempdb
do_test attach-2.9 {
execsql {
CREATE INDEX i2 ON t2(x);
SELECT * FROM t2 WHERE x>5;
} db2
} {21 x 22 y}
do_test attach-2.10 {
execsql {
SELECT type, name, tbl_name FROM sqlite_master;
} db2
} {table t2 t2 table tx tx trigger r1 t2 index i2 t2}
#do_test attach-2.11 {
# catchsql {
# SELECT * FROM t2 WHERE x>5;
# }
#} {1 {database schema has changed}}
ifcapable schema_pragmas {
ifcapable tempdb {
do_test attach-2.12 {
db_list db
} {0 main 1 temp 2 db2}
} else {
do_test attach-2.12 {
db_list db
} {0 main 2 db2}
}
} ;# ifcapable schema_pragmas
do_test attach-2.13 {
catchsql {
SELECT * FROM t2 WHERE x>5;
}
} {0 {21 x 22 y}}
do_test attach-2.14 {
execsql {
SELECT type, name, tbl_name FROM sqlite_master;
}
} {table t1 t1 table tx tx}
do_test attach-2.15 {
execsql {
SELECT type, name, tbl_name FROM db2.sqlite_master;
}
} {table t2 t2 table tx tx trigger r1 t2 index i2 t2}
do_test attach-2.16 {
db close
sqlite3 db test.db
execsql {
ATTACH 'test2.db' AS db2;
SELECT type, name, tbl_name FROM db2.sqlite_master;
}
} {table t2 t2 table tx tx trigger r1 t2 index i2 t2}
} ;# End of ifcapable {trigger}
do_test attach-3.1 {
db close
db2 close
sqlite3 db test.db
sqlite3 db2 test2.db
execsql {
SELECT * FROM t1
}
} {1 2 3 4}
# If we are testing a version of the code that lacks trigger support,
# adjust the database contents so that they are the same if triggers
# had been enabled.
ifcapable {!trigger} {
db2 eval {
DELETE FROM t2;
INSERT INTO t2 VALUES(21, 'x');
INSERT INTO t2 VALUES(22, 'y');
CREATE TABLE tx(x1,x2,y1,y2);
INSERT INTO tx VALUES(1, 11, 'x', 'x');
INSERT INTO tx VALUES(2, 12, 'y', 'y');
INSERT INTO tx VALUES(11, 21, 'x', 'x');
INSERT INTO tx VALUES(12, 22, 'y', 'y');
CREATE INDEX i2 ON t2(x);
}
}
do_test attach-3.2 {
catchsql {
SELECT * FROM t2
}
} {1 {no such table: t2}}
do_test attach-3.3 {
catchsql {
ATTACH DATABASE 'test2.db' AS db2;
SELECT * FROM t2
}
} {0 {21 x 22 y}}
# Even though 'db' has started a transaction, it should not yet have
# a lock on test2.db so 'db2' should be readable.
do_test attach-3.4 {
execsql BEGIN
catchsql {
SELECT * FROM t2;
} db2;
} {0 {21 x 22 y}}
# Reading from test2.db from db within a transaction should not
# prevent test2.db from being read by db2.
do_test attach-3.5 {
execsql {SELECT * FROM t2}
catchsql {
SELECT * FROM t2;
} db2;
} {0 {21 x 22 y}}
# Making a change to test2.db through db causes test2.db to get
# a reserved lock. It should still be accessible through db2.
do_test attach-3.6 {
execsql {
UPDATE t2 SET x=x+1 WHERE x=50;
}
catchsql {
SELECT * FROM t2;
} db2;
} {0 {21 x 22 y}}
do_test attach-3.7 {
execsql ROLLBACK
execsql {SELECT * FROM t2} db2
} {21 x 22 y}
# Start transactions on both db and db2. Once again, just because
# we make a change to test2.db using db2, only a RESERVED lock is
# obtained, so test2.db should still be readable using db.
#
do_test attach-3.8 {
execsql BEGIN
execsql BEGIN db2
execsql {UPDATE t2 SET x=0 WHERE 0} db2
catchsql {SELECT * FROM t2}
} {0 {21 x 22 y}}
# It is also still accessible from db2.
do_test attach-3.9 {
catchsql {SELECT * FROM t2} db2
} {0 {21 x 22 y}}
do_test attach-3.10 {
execsql {SELECT * FROM t1}
} {1 2 3 4}
do_test attach-3.11 {
catchsql {UPDATE t1 SET a=a+1}
} {0 {}}
do_test attach-3.12 {
execsql {SELECT * FROM t1}
} {2 2 4 4}
# db2 has a RESERVED lock on test2.db, so db cannot write to any tables
# in test2.db.
do_test attach-3.13 {
catchsql {UPDATE t2 SET x=x+1 WHERE x=50}
} {1 {database is locked}}
# Change for version 3. Transaction is no longer rolled back
# for a locked database.
execsql {ROLLBACK}
# db is able to reread its schema because db2 still only holds a
# reserved lock.
do_test attach-3.14 {
catchsql {SELECT * FROM t1}
} {0 {1 2 3 4}}
do_test attach-3.15 {
execsql COMMIT db2
execsql {SELECT * FROM t1}
} {1 2 3 4}
# Ticket #323
do_test attach-4.1 {
execsql {DETACH db2}
db2 close
sqlite3 db2 test2.db
execsql {
CREATE TABLE t3(x,y);
CREATE UNIQUE INDEX t3i1 ON t3(x);
INSERT INTO t3 VALUES(1,2);
SELECT * FROM t3;
} db2;
} {1 2}
do_test attach-4.2 {
execsql {
CREATE TABLE t3(a,b);
CREATE UNIQUE INDEX t3i1b ON t3(a);
INSERT INTO t3 VALUES(9,10);
SELECT * FROM t3;
}
} {9 10}
do_test attach-4.3 {
execsql {
ATTACH DATABASE 'test2.db' AS db2;
SELECT * FROM db2.t3;
}
} {1 2}
do_test attach-4.4 {
execsql {
SELECT * FROM main.t3;
}
} {9 10}
do_test attach-4.5 {
execsql {
INSERT INTO db2.t3 VALUES(9,10);
SELECT * FROM db2.t3;
}
} {1 2 9 10}
execsql {
DETACH db2;
}
ifcapable {trigger} {
do_test attach-4.6 {
execsql {
CREATE TABLE t4(x);
CREATE TRIGGER t3r3 AFTER INSERT ON t3 BEGIN
INSERT INTO t4 VALUES('db2.' || NEW.x);
END;
INSERT INTO t3 VALUES(6,7);
SELECT * FROM t4;
} db2
} {db2.6}
do_test attach-4.7 {
execsql {
CREATE TABLE t4(y);
CREATE TRIGGER t3r3 AFTER INSERT ON t3 BEGIN
INSERT INTO t4 VALUES('main.' || NEW.a);
END;
INSERT INTO main.t3 VALUES(11,12);
SELECT * FROM main.t4;
}
} {main.11}
}
ifcapable {!trigger} {
# When we do not have trigger support, set up the table like they
# would have been had triggers been there. The tests that follow need
# this setup.
execsql {
CREATE TABLE t4(x);
INSERT INTO t3 VALUES(6,7);
INSERT INTO t4 VALUES('db2.6');
INSERT INTO t4 VALUES('db2.13');
} db2
execsql {
CREATE TABLE t4(y);
INSERT INTO main.t3 VALUES(11,12);
INSERT INTO t4 VALUES('main.11');
}
}
# This one is tricky. On the UNION ALL select, we have to make sure
# the schema for both main and db2 is valid before starting to execute
# the first query of the UNION ALL. If we wait to test the validity of
# the schema for main until after the first query has run, that test will
# fail and the query will abort but we will have already output some
# results. When the query is retried, the results will be repeated.
#
ifcapable compound {
do_test attach-4.8 {
execsql {
ATTACH DATABASE 'test2.db' AS db2;
INSERT INTO db2.t3 VALUES(13,14);
SELECT * FROM db2.t4 UNION ALL SELECT * FROM main.t4;
}
} {db2.6 db2.13 main.11}
do_test attach-4.9 {
ifcapable {!trigger} {execsql {INSERT INTO main.t4 VALUES('main.15')}}
execsql {
INSERT INTO main.t3 VALUES(15,16);
SELECT * FROM db2.t4 UNION ALL SELECT * FROM main.t4;
}
} {db2.6 db2.13 main.11 main.15}
} ;# ifcapable compound
ifcapable !compound {
ifcapable {!trigger} {execsql {INSERT INTO main.t4 VALUES('main.15')}}
execsql {
ATTACH DATABASE 'test2.db' AS db2;
INSERT INTO db2.t3 VALUES(13,14);
INSERT INTO main.t3 VALUES(15,16);
}
} ;# ifcapable !compound
ifcapable view {
do_test attach-4.10 {
execsql {
DETACH DATABASE db2;
}
execsql {
CREATE VIEW v3 AS SELECT x*100+y FROM t3;
SELECT * FROM v3;
} db2
} {102 910 607 1314}
do_test attach-4.11 {
execsql {
CREATE VIEW v3 AS SELECT a*100+b FROM t3;
SELECT * FROM v3;
}
} {910 1112 1516}
do_test attach-4.12 {
execsql {
ATTACH DATABASE 'test2.db' AS db2;
SELECT * FROM db2.v3;
}
} {102 910 607 1314}
do_test attach-4.13 {
execsql {
SELECT * FROM main.v3;
}
} {910 1112 1516}
} ;# ifcapable view
# Tests for the sqliteFix...() routines in attach.c
#
ifcapable {trigger} {
do_test attach-5.1 {
db close
sqlite3 db test.db
db2 close
forcedelete test2.db
sqlite3 db2 test2.db
catchsql {
ATTACH DATABASE 'test.db' AS orig;
CREATE TRIGGER r1 AFTER INSERT ON orig.t1 BEGIN
SELECT 'no-op';
END;
} db2
} {1 {trigger r1 cannot reference objects in database orig}}
do_test attach-5.2 {
catchsql {
CREATE TABLE t5(x,y);
CREATE TRIGGER r5 AFTER INSERT ON t5 BEGIN
SELECT 'no-op';
END;
} db2
} {0 {}}
do_test attach-5.3 {
catchsql {
DROP TRIGGER r5;
CREATE TRIGGER r5 AFTER INSERT ON t5 BEGIN
SELECT 'no-op' FROM orig.t1;
END;
} db2
} {1 {trigger r5 cannot reference objects in database orig}}
ifcapable tempdb {
do_test attach-5.4 {
catchsql {
CREATE TEMP TABLE t6(p,q,r);
CREATE TRIGGER r5 AFTER INSERT ON t5 BEGIN
SELECT 'no-op' FROM temp.t6;
END;
} db2
} {1 {trigger r5 cannot reference objects in database temp}}
}
ifcapable subquery {
do_test attach-5.5 {
catchsql {
CREATE TRIGGER r5 AFTER INSERT ON t5 BEGIN
SELECT 'no-op' || (SELECT * FROM temp.t6);
END;
} db2
} {1 {trigger r5 cannot reference objects in database temp}}
do_test attach-5.6 {
catchsql {
CREATE TRIGGER r5 AFTER INSERT ON t5 BEGIN
SELECT 'no-op' FROM t1 WHERE x<(SELECT min(x) FROM temp.t6);
END;
} db2
} {1 {trigger r5 cannot reference objects in database temp}}
do_test attach-5.7 {
catchsql {
CREATE TRIGGER r5 AFTER INSERT ON t5 BEGIN
SELECT 'no-op' FROM t1 GROUP BY 1 HAVING x<(SELECT min(x) FROM temp.t6);
END;
} db2
} {1 {trigger r5 cannot reference objects in database temp}}
do_test attach-5.7 {
catchsql {
CREATE TRIGGER r5 AFTER INSERT ON t5 BEGIN
SELECT max(1,x,(SELECT min(x) FROM temp.t6)) FROM t1;
END;
} db2
} {1 {trigger r5 cannot reference objects in database temp}}
do_test attach-5.8 {
catchsql {
CREATE TRIGGER r5 AFTER INSERT ON t5 BEGIN
INSERT INTO t1 VALUES((SELECT min(x) FROM temp.t6),5);
END;
} db2
} {1 {trigger r5 cannot reference objects in database temp}}
do_test attach-5.9 {
catchsql {
CREATE TRIGGER r5 AFTER INSERT ON t5 BEGIN
DELETE FROM t1 WHERE x<(SELECT min(x) FROM temp.t6);
END;
} db2
} {1 {trigger r5 cannot reference objects in database temp}}
} ;# endif subquery
ifcapable json1&&vtab {
do_test attach-5.10 {
db close
catch {db2 close}
forcedelete test.db
sqlite3 db test.db
db eval {
CREATE TABLE t1(x);
CREATE TABLE t2(a,b);
CREATE TRIGGER x1 AFTER INSERT ON t1 BEGIN
INSERT INTO t2(a,b) SELECT key, value FROM json_each(NEW.x);
END;
INSERT INTO t1(x) VALUES('{"a":1}');
SELECT * FROM t2;
}
} {a 1}
do_test attach-5.11 {
sqlite3 db2 :memory:
db2 eval {
CREATE TABLE t3(y);
ATTACH 'test.db' AS aux;
INSERT INTO aux.t1(x) VALUES('{"b":2}');
SELECT * FROM aux.t2;
}
} {a 1 b 2}
} ;# endif json1
} ;# endif trigger
# Check to make sure we get a sensible error if unable to open
# the file that we are trying to attach.
#
do_test attach-6.1 {
catchsql {
ATTACH DATABASE 'no-such-file' AS nosuch;
}
} {0 {}}
if {$tcl_platform(platform)=="unix"} {
do_test attach-6.2 {
sqlite3 dbx cannot-read
dbx eval {CREATE TABLE t1(a,b,c)}
dbx close
file attributes cannot-read -permission 0000
if {[file writable cannot-read]} {
puts "\n**** Tests do not work when run as root ****"
forcedelete cannot-read
exit 1
}
catchsql {
ATTACH DATABASE 'cannot-read' AS noread;
}
} {1 {unable to open database: cannot-read}}
do_test attach-6.2.2 {
db errorcode
} {14}
forcedelete cannot-read
}
# Check the error message if we try to access a database that has
# not been attached.
do_test attach-6.3 {
catchsql {
CREATE TABLE no_such_db.t1(a, b, c);
}
} {1 {unknown database no_such_db}}
for {set i 2} {$i<=15} {incr i} {
catch {db$i close}
}
db close
forcedelete test2.db
forcedelete no-such-file
ifcapable subquery {
do_test attach-7.1 {
forcedelete test.db test.db-journal
sqlite3 db test.db
catchsql {
DETACH RAISE ( IGNORE ) IN ( SELECT "AAAAAA" . * ORDER BY
REGISTER LIMIT "AAAAAA" . "AAAAAA" OFFSET RAISE ( IGNORE ) NOT NULL )
}
} {1 {no such table: AAAAAA}}
}
# Create a malformed file (a file that is not a valid database)
# and try to attach it
#
do_test attach-8.1 {
set fd [open test2.db w]
puts $fd "This file is not a valid SQLite database"
close $fd
catchsql {
ATTACH 'test2.db' AS t2;
}
} {1 {file is not a database}}
do_test attach-8.2 {
db errorcode
} {26}
forcedelete test2.db
do_test attach-8.3 {
sqlite3 db2 test2.db
db2 eval {CREATE TABLE t1(x); BEGIN EXCLUSIVE}
catchsql {
ATTACH 'test2.db' AS t2;
}
} {1 {database is locked}}
do_test attach-8.4 {
db errorcode
} {5}
db2 close
forcedelete test2.db
# Test that it is possible to attach the same database more than
# once when not in shared-cache mode. That this is not possible in
# shared-cache mode is tested in shared7.test.
do_test attach-9.1 {
forcedelete test4.db
execsql {
ATTACH 'test4.db' AS aux1;
CREATE TABLE aux1.t1(a, b);
INSERT INTO aux1.t1 VALUES(1, 2);
ATTACH 'test4.db' AS aux2;
SELECT * FROM aux2.t1;
}
} {1 2}
do_test attach-9.2 {
catchsql {
BEGIN;
INSERT INTO aux1.t1 VALUES(3, 4);
INSERT INTO aux2.t1 VALUES(5, 6);
}
} {1 {database is locked}}
do_test attach-9.3 {
execsql {
COMMIT;
SELECT * FROM aux2.t1;
}
} {1 2 3 4}
# Ticket [abe728bbc311d81334dae9762f0db87c07a98f79].
# Multi-database commit on an attached TEMP database.
#
do_test attach-10.1 {
execsql {
ATTACH '' AS noname;
ATTACH ':memory:' AS inmem;
BEGIN;
CREATE TABLE noname.noname(x);
CREATE TABLE inmem.inmem(y);
CREATE TABLE main.main(z);
COMMIT;
SELECT name FROM noname.sqlite_master;
SELECT name FROM inmem.sqlite_master;
}
} {noname inmem}
do_test attach-10.2 {
lrange [execsql {
PRAGMA database_list;
}] 9 end
} {4 noname {} 5 inmem {}}
# Attach with a very long URI filename.
#
db close
sqlite3 db test.db -uri 1
do_execsql_test attach-11.1 {
ATTACH printf('file:%09000x/x.db?mode=memory&cache=shared',1) AS aux1;
CREATE TABLE aux1.t1(x,y);
INSERT INTO aux1.t1(x,y) VALUES(1,2),(3,4);
SELECT * FROM aux1.t1;
} {1 2 3 4}
# Ticket https://sqlite.org/src/tktview/a4e06e75a9ab61a1 2017-07-15
# False positive when running integrity_check on a connection with
# attached databases.
#
db close
sqlite3 db :memory:
do_execsql_test attach-12.1 {
CREATE TABLE Table1 (col TEXT NOT NULL PRIMARY KEY);
ATTACH ':memory:' AS db2;
CREATE TABLE db2.Table2(col1 INTEGER, col2 INTEGER, col3 INTEGER, col4);
CREATE UNIQUE INDEX db2.idx_col1_unique ON Table2 (col1);
CREATE UNIQUE INDEX db2.idx_col23_unique ON Table2 (col2, col3);
CREATE INDEX db2.idx_col2 ON Table2 (col2);
INSERT INTO Table2 VALUES(1,2,3,4);
PRAGMA integrity_check;
} {ok}
# 2021-03-10 Forum post https://sqlite.org/forum/forumpost/a006d86f72
#
reset_db
do_test attach-13.1 {
sqlite3 db :memory:
db eval {CREATE TABLE base(x);}
for {set i 0} {$i<$SQLITE_MAX_ATTACHED} {incr i} {
db eval "ATTACH ':memory:' AS a$i"
}
set m "a[expr {$SQLITE_MAX_ATTACHED-1}]"
db eval "CREATE TABLE $m.t1(a INTEGER PRIMARY KEY, b);"
db eval "CREATE TABLE $m.t2(a INTEGER PRIMARY KEY, b);"
db eval {SELECT a FROM t1 WHERE b IN (SELECT a FROM t2);}
} {}
finish_test

451
testdata/tcl/attach2.test vendored Normal file
View file

@ -0,0 +1,451 @@
# 2003 July 1
#
# 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 script is testing the ATTACH and DETACH commands
# and related functionality.
#
# $Id: attach2.test,v 1.38 2007/12/13 21:54:11 drh Exp $
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix attach2
ifcapable !attach {
finish_test
return
}
# Ticket #354
#
# Databases test.db and test2.db contain identical schemas. Make
# sure we can attach test2.db from test.db.
#
do_test attach2-1.1 {
db eval {
CREATE TABLE t1(a,b);
CREATE INDEX x1 ON t1(a);
}
forcedelete test2.db
forcedelete test2.db-journal
sqlite3 db2 test2.db
db2 eval {
CREATE TABLE t1(a,b);
CREATE INDEX x1 ON t1(a);
}
catchsql {
ATTACH 'test2.db' AS t2;
}
} {0 {}}
# Ticket #514
#
proc db_list {db} {
set list {}
foreach {idx name file} [execsql {PRAGMA database_list} $db] {
lappend list $idx $name
}
return $list
}
db eval {DETACH t2}
do_test attach2-2.1 {
# lock test2.db then try to attach it. This is no longer an error because
# db2 just RESERVES the database. It does not obtain a write-lock until
# we COMMIT.
db2 eval {BEGIN}
db2 eval {UPDATE t1 SET a = 0 WHERE 0}
catchsql {
ATTACH 'test2.db' AS t2;
}
} {0 {}}
ifcapable schema_pragmas {
do_test attach2-2.2 {
# make sure test2.db did get attached.
db_list db
} {0 main 2 t2}
} ;# ifcapable schema_pragmas
db2 eval {COMMIT}
do_test attach2-2.5 {
# Make sure we can read test2.db from db
catchsql {
SELECT name FROM t2.sqlite_master;
}
} {0 {t1 x1}}
do_test attach2-2.6 {
# lock test2.db and try to read from it. This should still work because
# the lock is only a RESERVED lock which does not prevent reading.
#
db2 eval BEGIN
db2 eval {UPDATE t1 SET a = 0 WHERE 0}
catchsql {
SELECT name FROM t2.sqlite_master;
}
} {0 {t1 x1}}
do_test attach2-2.7 {
# but we can still read from test1.db even though test2.db is locked.
catchsql {
SELECT name FROM main.sqlite_master;
}
} {0 {t1 x1}}
do_test attach2-2.8 {
# start a transaction on test.db even though test2.db is locked.
catchsql {
BEGIN;
INSERT INTO t1 VALUES(8,9);
}
} {0 {}}
do_test attach2-2.9 {
execsql {
SELECT * FROM t1
}
} {8 9}
do_test attach2-2.10 {
# now try to write to test2.db. the write should fail
catchsql {
INSERT INTO t2.t1 VALUES(1,2);
}
} {1 {database is locked}}
do_test attach2-2.11 {
# when the write failed in the previous test, the transaction should
# have rolled back.
#
# Update for version 3: A transaction is no longer rolled back if a
# database is found to be busy.
execsql {rollback}
db2 eval ROLLBACK
execsql {
SELECT * FROM t1
}
} {}
do_test attach2-2.12 {
catchsql {
COMMIT
}
} {1 {cannot commit - no transaction is active}}
# Ticket #574: Make sure it works using the non-callback API
#
do_test attach2-3.1 {
set DB [sqlite3_connection_pointer db]
set rc [catch {sqlite3_prepare $DB "ATTACH 'test2.db' AS t2" -1 TAIL} VM]
if {$rc} {lappend rc $VM}
sqlite3_step $VM
sqlite3_finalize $VM
set rc
} {0}
do_test attach2-3.2 {
set rc [catch {sqlite3_prepare $DB "DETACH t2" -1 TAIL} VM]
if {$rc} {lappend rc $VM}
sqlite3_step $VM
sqlite3_finalize $VM
set rc
} {0}
db close
for {set i 2} {$i<=15} {incr i} {
catch {db$i close}
}
# A procedure to verify the status of locks on a database.
#
proc lock_status {testnum db expected_result} {
# If the database was compiled with OMIT_TEMPDB set, then
# the lock_status list will not contain an entry for the temp
# db. But the test code doesn't know this, so its easiest
# to filter it out of the $expected_result list here.
ifcapable !tempdb {
set expected_result [concat \
[lrange $expected_result 0 1] \
[lrange $expected_result 4 end] \
]
}
do_test attach2-$testnum [subst {
$db cache flush ;# The lock_status pragma should not be cached
execsql {PRAGMA lock_status} $db
}] $expected_result
}
set sqlite_os_trace 0
# Tests attach2-4.* test that read-locks work correctly with attached
# databases.
do_test attach2-4.1 {
sqlite3 db test.db
sqlite3 db2 test.db
execsql {ATTACH 'test2.db' as file2}
execsql {ATTACH 'test2.db' as file2} db2
} {}
lock_status 4.1.1 db {main unlocked temp closed file2 unlocked}
lock_status 4.1.2 db2 {main unlocked temp closed file2 unlocked}
do_test attach2-4.2 {
# Handle 'db' read-locks test.db
execsql {BEGIN}
execsql {SELECT * FROM t1}
# Lock status:
# db - shared(main)
# db2 -
} {}
lock_status 4.2.1 db {main shared temp closed file2 unlocked}
lock_status 4.2.2 db2 {main unlocked temp closed file2 unlocked}
do_test attach2-4.3 {
# The read lock held by db does not prevent db2 from reading test.db
execsql {SELECT * FROM t1} db2
} {}
lock_status 4.3.1 db {main shared temp closed file2 unlocked}
lock_status 4.3.2 db2 {main unlocked temp closed file2 unlocked}
do_test attach2-4.4 {
# db is holding a read lock on test.db, so we should not be able
# to commit a write to test.db from db2
catchsql {
INSERT INTO t1 VALUES(1, 2)
} db2
} {1 {database is locked}}
lock_status 4.4.1 db {main shared temp closed file2 unlocked}
lock_status 4.4.2 db2 {main unlocked temp closed file2 unlocked}
# We have to make sure that the cache_size and the soft_heap_limit
# are large enough to hold the entire change in memory. If either
# is set too small, then changes will spill to the database, forcing
# a reserved lock to promote to exclusive. That will mess up our
# test results.
set soft_limit [sqlite3_soft_heap_limit 0]
do_test attach2-4.5 {
# Handle 'db2' reserves file2.
execsql {BEGIN} db2
execsql {INSERT INTO file2.t1 VALUES(1, 2)} db2
# Lock status:
# db - shared(main)
# db2 - reserved(file2)
} {}
lock_status 4.5.1 db {main shared temp closed file2 unlocked}
lock_status 4.5.2 db2 {main unlocked temp closed file2 reserved}
do_test attach2-4.6.1 {
# Reads are allowed against a reserved database.
catchsql {
SELECT * FROM file2.t1;
}
# Lock status:
# db - shared(main), shared(file2)
# db2 - reserved(file2)
} {0 {}}
lock_status 4.6.1.1 db {main shared temp closed file2 shared}
lock_status 4.6.1.2 db2 {main unlocked temp closed file2 reserved}
do_test attach2-4.6.2 {
# Writes against a reserved database are not allowed.
catchsql {
UPDATE file2.t1 SET a=0;
}
} {1 {database is locked}}
lock_status 4.6.2.1 db {main shared temp closed file2 shared}
lock_status 4.6.2.2 db2 {main unlocked temp closed file2 reserved}
do_test attach2-4.7 {
# Ensure handle 'db' retains the lock on the main file after
# failing to obtain a write-lock on file2.
catchsql {
INSERT INTO t1 VALUES(1, 2)
} db2
} {0 {}}
lock_status 4.7.1 db {main shared temp closed file2 shared}
lock_status 4.7.2 db2 {main reserved temp closed file2 reserved}
do_test attach2-4.8 {
# We should still be able to read test.db from db2
execsql {SELECT * FROM t1} db2
} {1 2}
lock_status 4.8.1 db {main shared temp closed file2 shared}
lock_status 4.8.2 db2 {main reserved temp closed file2 reserved}
do_test attach2-4.9 {
# Try to upgrade the handle 'db' lock.
catchsql {
INSERT INTO t1 VALUES(1, 2)
}
} {1 {database is locked}}
lock_status 4.9.1 db {main shared temp closed file2 shared}
lock_status 4.9.2 db2 {main reserved temp closed file2 reserved}
do_test attach2-4.10 {
# We cannot commit db2 while db is holding a read-lock
catchsql {COMMIT} db2
} {1 {database is locked}}
lock_status 4.10.1 db {main shared temp closed file2 shared}
lock_status 4.10.2 db2 {main pending temp closed file2 reserved}
set sqlite_os_trace 0
do_test attach2-4.11 {
# db is able to commit.
catchsql {COMMIT}
} {0 {}}
lock_status 4.11.1 db {main unlocked temp closed file2 unlocked}
lock_status 4.11.2 db2 {main pending temp closed file2 reserved}
do_test attach2-4.12 {
# Now we can commit db2
catchsql {COMMIT} db2
} {0 {}}
lock_status 4.12.1 db {main unlocked temp closed file2 unlocked}
lock_status 4.12.2 db2 {main unlocked temp closed file2 unlocked}
do_test attach2-4.13 {
execsql {SELECT * FROM file2.t1}
} {1 2}
do_test attach2-4.14 {
execsql {INSERT INTO t1 VALUES(1, 2)}
} {}
do_test attach2-4.15 {
execsql {SELECT * FROM t1} db2
} {1 2 1 2}
db close
db2 close
forcedelete test2.db
sqlite3_soft_heap_limit $soft_limit
# These tests - attach2-5.* - check that the master journal file is deleted
# correctly when a multi-file transaction is committed or rolled back.
#
# Update: It's not actually created if a rollback occurs, so that test
# doesn't really prove too much.
foreach f [glob test.db*] {forcedelete $f}
do_test attach2-5.1 {
sqlite3 db test.db
execsql {
ATTACH 'test.db2' AS aux;
}
} {}
do_test attach2-5.2 {
execsql {
BEGIN;
CREATE TABLE tbl(a, b, c);
CREATE TABLE aux.tbl(a, b, c);
COMMIT;
}
} {}
do_test attach2-5.3 {
lsort [glob test.db*]
} {test.db test.db2}
do_test attach2-5.4 {
execsql {
BEGIN;
DROP TABLE aux.tbl;
DROP TABLE tbl;
ROLLBACK;
}
} {}
do_test attach2-5.5 {
lsort [glob test.db*]
} {test.db test.db2}
# Check that a database cannot be ATTACHed or DETACHed during a transaction.
do_test attach2-6.1 {
execsql {
BEGIN;
}
} {}
do_test attach2-6.2 {
catchsql {
ATTACH 'test3.db' as aux2;
DETACH aux2;
}
} {0 {}}
# As of version 3.21.0: it is ok to DETACH from within a transaction
#
do_test attach2-6.3 {
catchsql {
DETACH aux;
}
} {0 {}}
db close
ifcapable utf16 {
forcedelete test.db2 ;# utf-16
forcedelete test.db3 ;# utf-16
forcedelete test.db4 ;# utf-8
sqlite3 db2 test.db2
do_execsql_test -db db2 1.1 {
PRAGMA encoding = 'utf16';
CREATE TABLE t2(x);
INSERT INTO t2 VALUES('text2');
}
db2 close
sqlite3 db3 test.db3
do_execsql_test -db db3 1.2 {
PRAGMA encoding = 'utf16';
CREATE TABLE t3(x);
INSERT INTO t3 VALUES('text3');
}
db3 close
sqlite3 db4 test.db4
do_execsql_test -db db4 1.3 {
PRAGMA encoding = 'utf8';
CREATE TABLE t4(x);
INSERT INTO t4 VALUES('text4');
}
db4 close
reset_db
do_execsql_test 2.1 {
PRAGMA encoding = 'utf16';
ATTACH 'test.db2' AS aux;
SELECT * FROM t2;
} {text2}
reset_db
do_execsql_test 2.2 {
ATTACH 'test.db4' AS aux;
SELECT * FROM t4;
} {text4}
db close
sqlite3 db test.db2
do_execsql_test 2.3 {
ATTACH 'test.db3' AS aux;
SELECT * FROM t3;
SELECT * FROM t2;
} {text3 text2}
db close
sqlite3 db test.db2
do_catchsql_test 2.4 {
ATTACH 'test.db4' AS aux;
} {1 {attached databases must use the same text encoding as main database}}
db close
}
finish_test

353
testdata/tcl/attach3.test vendored Normal file
View file

@ -0,0 +1,353 @@
# 2003 July 1
#
# 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 script is testing the ATTACH and DETACH commands
# and schema changes to attached databases.
#
# $Id: attach3.test,v 1.18 2007/10/09 08:29:32 danielk1977 Exp $
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
ifcapable !attach {
finish_test
return
}
# The tests in this file were written before SQLite supported recursive
# trigger invocation, and some tests depend on that to pass. So disable
# recursive triggers for this file.
catchsql { pragma recursive_triggers = off }
# Create tables t1 and t2 in the main database
execsql {
CREATE TABLE t1(a, b);
CREATE TABLE t2(c, d);
}
# Create tables t1 and t2 in database file test2.db
forcedelete test2.db
forcedelete test2.db-journal
sqlite3 db2 test2.db
execsql {
CREATE TABLE t1(a, b);
CREATE TABLE t2(c, d);
} db2
db2 close
# Create a table in the auxilary database.
do_test attach3-1.1 {
execsql {
ATTACH 'test2.db' AS aux;
}
} {}
do_test attach3-1.2 {
execsql {
CREATE TABLE aux.t3(e, f);
}
} {}
do_test attach3-1.3 {
execsql {
SELECT * FROM sqlite_master WHERE name = 't3';
}
} {}
do_test attach3-1.4 {
execsql {
SELECT * FROM aux.sqlite_master WHERE name = 't3';
}
} "table t3 t3 [expr $AUTOVACUUM?5:4] {CREATE TABLE t3(e, f)}"
do_test attach3-1.5 {
execsql {
INSERT INTO t3 VALUES(1, 2);
SELECT * FROM t3;
}
} {1 2}
# Create an index on the auxilary database table.
do_test attach3-2.1 {
execsql {
CREATE INDEX aux.i1 on t3(e);
}
} {}
do_test attach3-2.2 {
execsql {
SELECT * FROM sqlite_master WHERE name = 'i1';
}
} {}
do_test attach3-2.3 {
execsql {
SELECT * FROM aux.sqlite_master WHERE name = 'i1';
}
} "index i1 t3 [expr $AUTOVACUUM?6:5] {CREATE INDEX i1 on t3(e)}"
# Drop the index on the aux database table.
do_test attach3-3.1 {
execsql {
DROP INDEX aux.i1;
SELECT * FROM aux.sqlite_master WHERE name = 'i1';
}
} {}
do_test attach3-3.2 {
execsql {
CREATE INDEX aux.i1 on t3(e);
SELECT * FROM aux.sqlite_master WHERE name = 'i1';
}
} "index i1 t3 [expr $AUTOVACUUM?6:5] {CREATE INDEX i1 on t3(e)}"
do_test attach3-3.3 {
execsql {
DROP INDEX i1;
SELECT * FROM aux.sqlite_master WHERE name = 'i1';
}
} {}
# Drop tables t1 and t2 in the auxilary database.
do_test attach3-4.1 {
execsql {
DROP TABLE aux.t1;
SELECT name FROM aux.sqlite_master;
}
} {t2 t3}
do_test attach3-4.2 {
# This will drop main.t2
execsql {
DROP TABLE t2;
SELECT name FROM aux.sqlite_master;
}
} {t2 t3}
do_test attach3-4.3 {
execsql {
DROP TABLE t2;
SELECT name FROM aux.sqlite_master;
}
} {t3}
# Create a view in the auxilary database.
ifcapable view {
do_test attach3-5.1 {
execsql {
CREATE VIEW aux.v1 AS SELECT * FROM t3;
}
} {}
do_test attach3-5.2 {
execsql {
SELECT * FROM aux.sqlite_master WHERE name = 'v1';
}
} {view v1 v1 0 {CREATE VIEW v1 AS SELECT * FROM t3}}
do_test attach3-5.3 {
execsql {
INSERT INTO aux.t3 VALUES('hello', 'world');
SELECT * FROM v1;
}
} {1 2 hello world}
# Drop the view
do_test attach3-6.1 {
execsql {
DROP VIEW aux.v1;
}
} {}
do_test attach3-6.2 {
execsql {
SELECT * FROM aux.sqlite_master WHERE name = 'v1';
}
} {}
} ;# ifcapable view
ifcapable {trigger} {
# Create a trigger in the auxilary database.
do_test attach3-7.1 {
execsql {
CREATE TRIGGER aux.tr1 AFTER INSERT ON t3 BEGIN
INSERT INTO t3 VALUES(new.e*2, new.f*2);
END;
}
} {}
do_test attach3-7.2 {
execsql {
DELETE FROM t3;
INSERT INTO t3 VALUES(10, 20);
SELECT * FROM t3;
}
} {10 20 20 40}
do_test attach3-5.3 {
execsql {
SELECT * FROM aux.sqlite_master WHERE name = 'tr1';
}
} {trigger tr1 t3 0 {CREATE TRIGGER tr1 AFTER INSERT ON t3 BEGIN
INSERT INTO t3 VALUES(new.e*2, new.f*2);
END}}
# Drop the trigger
do_test attach3-8.1 {
execsql {
DROP TRIGGER aux.tr1;
}
} {}
do_test attach3-8.2 {
execsql {
SELECT * FROM aux.sqlite_master WHERE name = 'tr1';
}
} {}
ifcapable tempdb {
# Try to trick SQLite into dropping the wrong temp trigger.
do_test attach3-9.0 {
execsql {
CREATE TABLE main.t4(a, b, c);
CREATE TABLE aux.t4(a, b, c);
CREATE TEMP TRIGGER tst_trigger BEFORE INSERT ON aux.t4 BEGIN
SELECT 'hello world';
END;
SELECT count(*) FROM temp.sqlite_master;
}
} {1}
do_test attach3-9.1 {
execsql {
DROP TABLE main.t4;
SELECT count(*) FROM sqlite_temp_master;
}
} {1}
do_test attach3-9.2 {
execsql {
DROP TABLE aux.t4;
SELECT count(*) FROM temp.sqlite_master;
}
} {0}
}
} ;# endif trigger
# Make sure the aux.sqlite_master table is read-only
do_test attach3-10.0 {
catchsql {
INSERT INTO aux.sqlite_master VALUES(1, 2, 3, 4, 5);
}
} {1 {table sqlite_master may not be modified}}
# Failure to attach leaves us in a workable state.
# Ticket #811
#
do_test attach3-11.0 {
catchsql {
ATTACH DATABASE '/nodir/nofile.x' AS notadb;
}
} {1 {unable to open database: /nodir/nofile.x}}
do_test attach3-11.1 {
catchsql {
ATTACH DATABASE ':memory:' AS notadb;
}
} {0 {}}
do_test attach3-11.2 {
catchsql {
DETACH DATABASE notadb;
}
} {0 {}}
# Return a list of attached databases
#
proc db_list {} {
set x [execsql {
PRAGMA database_list;
}]
set y {}
foreach {n id file} $x {lappend y $id}
return $y
}
ifcapable schema_pragmas&&tempdb {
ifcapable !trigger {
execsql {create temp table dummy(dummy)}
}
# Ticket #1825
#
do_test attach3-12.1 {
db_list
} {main temp aux}
do_test attach3-12.2 {
execsql {
ATTACH DATABASE ? AS ?
}
db_list
} {main temp aux {}}
do_test attach3-12.3 {
execsql {
DETACH aux
}
db_list
} {main temp {}}
do_test attach3-12.4 {
execsql {
DETACH ?
}
db_list
} {main temp}
do_test attach3-12.5 {
execsql {
ATTACH DATABASE '' AS ''
}
db_list
} {main temp {}}
do_test attach3-12.6 {
execsql {
DETACH ''
}
db_list
} {main temp}
do_test attach3-12.7 {
execsql {
ATTACH DATABASE '' AS ?
}
db_list
} {main temp {}}
do_test attach3-12.8 {
execsql {
DETACH ''
}
db_list
} {main temp}
do_test attach3-12.9 {
execsql {
ATTACH DATABASE '' AS NULL
}
db_list
} {main temp {}}
do_test attach3-12.10 {
execsql {
DETACH ?
}
db_list
} {main temp}
do_test attach3-12.11 {
catchsql {
DETACH NULL
}
} {1 {no such database: }}
do_test attach3-12.12 {
catchsql {
ATTACH null AS null;
ATTACH '' AS '';
}
} {1 {database is already in use}}
do_test attach3-12.13 {
db_list
} {main temp {}}
do_test attach3-12.14 {
execsql {
DETACH '';
}
db_list
} {main temp}
} ;# ifcapable pragma
finish_test

137
testdata/tcl/attach4.test vendored Normal file
View file

@ -0,0 +1,137 @@
# 200 July 1
#
# 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 script is attaching many database files to a single
# connection.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix attach4
ifcapable !attach {
finish_test
return
}
puts "Testing with SQLITE_MAX_ATTACHED=$SQLITE_MAX_ATTACHED"
set files {main test.db}
for {set ii 0} {$ii < $SQLITE_MAX_ATTACHED} {incr ii} {
lappend files aux$ii "test.db$ii"
}
do_test 1.1 {
sqlite3_limit db SQLITE_LIMIT_ATTACHED -1
} $SQLITE_MAX_ATTACHED
do_test 1.2.1 {
db close
foreach {name f} $files { forcedelete $f }
sqlite3 db test.db
foreach {name f} $files {
if {$name == "main"} continue
execsql "ATTACH '$f' AS $name"
}
db eval {PRAGMA database_list} {
lappend L $name [file tail $file]
}
set L
} $files
do_catchsql_test 1.2.2 {
ATTACH 'x.db' AS next;
} [list 1 "too many attached databases - max $SQLITE_MAX_ATTACHED"]
do_test 1.3 {
execsql BEGIN;
foreach {name f} $files {
execsql "CREATE TABLE $name.tbl(x)"
execsql "INSERT INTO $name.tbl VALUES('$f')"
}
execsql COMMIT;
} {}
do_test 1.4 {
set L [list]
foreach {name f} $files {
lappend L $name [execsql "SELECT x FROM $name.tbl"]
}
set L
} $files
set L [list]
set S ""
foreach {name f} $files {
if {[permutation] == "journaltest"} {
set mode delete
} else {
set mode wal
}
ifcapable !wal { set mode delete }
lappend L $mode
append S "
PRAGMA $name.journal_mode = WAL;
UPDATE $name.tbl SET x = '$name';
"
}
do_execsql_test 1.5 $S $L
do_test 1.6 {
set L [list]
foreach {name f} $files {
lappend L [execsql "SELECT x FROM $name.tbl"] $f
}
set L
} $files
do_test 1.7 {
execsql BEGIN;
foreach {name f} $files {
execsql "UPDATE $name.tbl SET x = '$f'"
}
execsql COMMIT;
} {}
do_test 1.8 {
set L [list]
foreach {name f} $files {
lappend L $name [execsql "SELECT x FROM $name.tbl"]
}
set L
} $files
db close
foreach {name f} $files { forcedelete $f }
#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.0 {
ATTACH DATABASE '' AS aux;
CREATE TABLE IF NOT EXISTS aux.t1(a, b);
CREATE TEMPORARY TRIGGER tr1 DELETE ON t1 BEGIN
DELETE FROM t1;
END;
CREATE TABLE temp.t1(a, b);
}
do_execsql_test 2.1 {
DETACH DATABASE aux;
}
do_execsql_test 2.2 {
DROP TRIGGER tr1;
}
finish_test

78
testdata/tcl/attachmalloc.test vendored Normal file
View file

@ -0,0 +1,78 @@
# 2005 September 19
#
# 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 script is testing the ATTACH statement and
# specifically out-of-memory conditions within that command.
#
# $Id: attachmalloc.test,v 1.10 2008/10/22 10:45:38 danielk1977 Exp $
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
ifcapable !attach {
finish_test
return
}
source $testdir/malloc_common.tcl
do_malloc_test attachmalloc-1 -tclprep {
catch { db close }
for {set i 2} {$i<=4} {incr i} {
catch { db$i close }
forcedelete test$i.db
forcedelete test$i.db-journal
}
} -tclbody {
if {[catch {sqlite3 db test.db}]} {
error "out of memory"
}
sqlite3_db_config_lookaside db 0 0 0
sqlite3_extended_result_codes db 1
} -sqlbody {
ATTACH 'test2.db' AS two;
CREATE TABLE two.t1(x);
ATTACH 'test3.db' AS three;
CREATE TABLE three.t1(x);
ATTACH 'test4.db' AS four;
CREATE TABLE four.t1(x);
}
do_malloc_test attachmalloc-2 -tclprep {
forcedelete test2.db
forcedelete test2.db-journal
sqlite3 db2 test2.db
db2 eval {
CREATE TABLE t1(a, b, c);
CREATE INDEX i1 ON t1(a, b);
}
db2 close
} -sqlbody {
CREATE TABLE t1(d, e, f);
ATTACH 'test2.db' AS db1;
}
ifcapable shared_cache {
set enable_shared_cache [sqlite3_enable_shared_cache 1]
sqlite3 dbaux test3.db
dbaux eval {SELECT * FROM sqlite_master}
do_malloc_test attachmalloc-3 -sqlbody {
SELECT * FROM sqlite_master;
ATTACH 'test3.db' AS three;
} -cleanup {
db eval { DETACH three }
}
dbaux close
sqlite3_enable_shared_cache $enable_shared_cache
}
finish_test

2678
testdata/tcl/auth.test vendored Normal file

File diff suppressed because it is too large Load diff

160
testdata/tcl/auth2.test vendored Normal file
View file

@ -0,0 +1,160 @@
# 2006 Aug 24
#
# 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 script is testing the sqlite3_set_authorizer() API
# and related functionality.
#
# $Id: auth2.test,v 1.3 2008/07/02 13:13:53 danielk1977 Exp $
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# disable this test if the SQLITE_OMIT_AUTHORIZATION macro is
# defined during compilation.
if {[catch {db auth {}} msg]} {
finish_test
return
}
do_test auth2-1.1 {
execsql {
CREATE TABLE t1(a,b,c);
INSERT INTO t1 VALUES(1,2,3);
}
set ::flist {}
proc auth {code arg1 arg2 arg3 arg4 args} {
if {$code=="SQLITE_FUNCTION"} {
lappend ::flist $arg2
if {$arg2=="max"} {
return SQLITE_DENY
} elseif {$arg2=="min"} {
return SQLITE_IGNORE
} else {
return SQLITE_OK
}
}
return SQLITE_OK
}
db authorizer ::auth
catchsql {SELECT max(a,b,c) FROM t1}
} {1 {not authorized to use function: max}}
do_test auth2-1.2 {
set ::flist
} max
do_test auth2-1.3 {
set ::flist {}
catchsql {SELECT min(a,b,c) FROM t1}
} {0 {{}}}
do_test auth2-1.4 {
set ::flist
} min
do_test auth2-1.5 {
set ::flist {}
catchsql {SELECT coalesce(min(a,b,c),999) FROM t1}
} {0 999}
do_test auth2-1.6 {
set ::flist
} {coalesce min}
do_test auth2-1.7 {
set ::flist {}
catchsql {SELECT coalesce(a,b,c) FROM t1}
} {0 1}
do_test auth2-1.8 {
set ::flist
} coalesce
# Make sure the authorizer is not called when parsing the schema
# and when computing the result set of a view.
#
db close
sqlite3 db test.db
sqlite3 db2 test.db
proc auth {args} {
global authargs
append authargs [lrange $args 0 4]\n
return SQLITE_OK
}
db auth auth
do_test auth2-2.1 {
set ::authargs {}
db eval {
CREATE TABLE t2(x,y,z);
}
set ::authargs
} {SQLITE_INSERT sqlite_master {} main {}
SQLITE_CREATE_TABLE t2 {} main {}
SQLITE_UPDATE sqlite_master type main {}
SQLITE_UPDATE sqlite_master name main {}
SQLITE_UPDATE sqlite_master tbl_name main {}
SQLITE_UPDATE sqlite_master rootpage main {}
SQLITE_UPDATE sqlite_master sql main {}
SQLITE_READ sqlite_master ROWID main {}
}
do_test auth2-2.2 {
set ::authargs {}
db eval {
CREATE VIEW v2 AS SELECT x+y AS a, y+z AS b from t2;
}
set ::authargs
} {SQLITE_INSERT sqlite_master {} main {}
SQLITE_CREATE_VIEW v2 {} main {}
SQLITE_UPDATE sqlite_master type main {}
SQLITE_UPDATE sqlite_master name main {}
SQLITE_UPDATE sqlite_master tbl_name main {}
SQLITE_UPDATE sqlite_master rootpage main {}
SQLITE_UPDATE sqlite_master sql main {}
SQLITE_READ sqlite_master ROWID main {}
}
do_test auth2-2.3 {
set ::authargs {}
db eval {
SELECT a, b FROM v2;
}
set ::authargs
} {SQLITE_SELECT {} {} {} {}
SQLITE_READ t2 x main v2
SQLITE_READ t2 y main v2
SQLITE_READ t2 y main v2
SQLITE_READ t2 z main v2
SQLITE_READ v2 a main {}
SQLITE_READ v2 b main {}
SQLITE_SELECT {} {} {} v2
}
do_test auth2-2.4 {
db2 eval {
CREATE TABLE t3(p,q,r);
}
set ::authargs {}
db eval {
SELECT b, a FROM v2;
}
set ::authargs
} {SQLITE_SELECT {} {} {} {}
SQLITE_READ t2 x main v2
SQLITE_READ t2 y main v2
SQLITE_READ t2 y main v2
SQLITE_READ t2 z main v2
SQLITE_READ v2 b main {}
SQLITE_READ v2 a main {}
SQLITE_SELECT {} {} {} v2
SQLITE_SELECT {} {} {} {}
SQLITE_READ t2 x main v2
SQLITE_READ t2 y main v2
SQLITE_READ t2 y main v2
SQLITE_READ t2 z main v2
SQLITE_READ v2 b main {}
SQLITE_READ v2 a main {}
SQLITE_SELECT {} {} {} v2
}
db2 close
finish_test

134
testdata/tcl/auth3.test vendored Normal file
View file

@ -0,0 +1,134 @@
# 2008 October 27
#
# 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.
#
#***********************************************************************
#
# Test that the truncate optimization is disabled if the SQLITE_DELETE
# authorization callback returns SQLITE_IGNORE.
#
# Test that authorizer is disabled during schema parsing.
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# disable this test if the SQLITE_OMIT_AUTHORIZATION macro is
# defined during compilation.
if {[catch {db auth {}} msg]} {
finish_test
return
}
# Disable the statement cache for these tests.
#
db cache size 0
db authorizer ::auth
proc auth {code arg1 arg2 arg3 arg4 args} {
if {$code=="SQLITE_DELETE"} {
return $::authcode
}
return SQLITE_OK
}
#--------------------------------------------------------------------------
# The following tests - auth3-1.* - test that return values of SQLITE_DENY,
# SQLITE_IGNORE, SQLITE_OK and <invalid> are correctly handled when returned
# by an SQLITE_DELETE authorization callback triggered by a
# "DELETE FROM <table-name>" statement.
#
do_test auth3-1.1 {
execsql {
CREATE TABLE t1(a,b,c);
INSERT INTO t1 VALUES(1, 2, 3);
INSERT INTO t1 VALUES(4, 5, 6);
}
} {}
do_test auth3.1.2 {
set ::authcode SQLITE_DENY
catchsql { DELETE FROM t1 }
} {1 {not authorized}}
# EVIDENCE-OF: R-64962-58611 If the authorizer callback returns any
# value other than SQLITE_IGNORE, SQLITE_OK, or SQLITE_DENY then the
# sqlite3_prepare_v2() or equivalent call that triggered the authorizer
# will fail with an error message.
do_test auth3.1.3 {
set ::authcode SQLITE_INVALID
catchsql { DELETE FROM t1 }
} {1 {authorizer malfunction}}
do_test auth3.1.4 {
execsql { SELECT * FROM t1 }
} {1 2 3 4 5 6}
do_test auth3-1.5 {
set ::authcode SQLITE_IGNORE
execsql {
DELETE FROM t1;
SELECT * FROM t1;
}
} {}
do_test auth3-1.6 {
set ::authcode SQLITE_OK
execsql {
INSERT INTO t1 VALUES(1, 2, 3);
INSERT INTO t1 VALUES(4, 5, 6);
DELETE FROM t1;
SELECT * FROM t1;
}
} {}
#--------------------------------------------------------------------------
# These tests - auth3-2.* - test that returning SQLITE_IGNORE really does
# disable the truncate optimization.
#
do_test auth3-2.1 {
set ::authcode SQLITE_OK
execsql {
INSERT INTO t1 VALUES(1, 2, 3);
INSERT INTO t1 VALUES(4, 5, 6);
}
set sqlite_search_count 0
execsql {
DELETE FROM t1;
}
set sqlite_search_count
} {0}
do_test auth3-2.2 {
set ::authcode SQLITE_IGNORE
execsql {
INSERT INTO t1 VALUES(1, 2, 3);
INSERT INTO t1 VALUES(4, 5, 6);
}
set sqlite_search_count 0
execsql {
DELETE FROM t1;
}
set sqlite_search_count
} {1}
# 2016-07-28. A problem report from a private client complaining about
# an authorizer failure during an ALTER TABLE. The solution (I think) is
# to disable the authorizer during schema parsing.
#
ifcapable altertable {
proc auth {code args} {
if {$code=="SQLITE_READ" && [regexp {DoNotRead} $args]} {
return SQLITE_DENY
}
return SQLITE_OK
}
do_execsql_test auth3-3.0 {
CREATE TEMPORARY TABLE TempTable (
key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,
value TEXT NOT NULL ON CONFLICT FAIL);
ALTER TABLE TempTable RENAME TO DoNotRead;
SELECT name FROM temp.sqlite_master;
} {DoNotRead sqlite_autoindex_DoNotRead_1}
}
finish_test

123
testdata/tcl/autoanalyze1.test vendored Normal file
View file

@ -0,0 +1,123 @@
# 2017-02-17
#
# 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 tests for the logic used to estimate when
# running ANALYZE would be beneficial.
#
# Note that this test uses some hard-coded bitmask values from sqliteInt.h.
# If any of the following constants changes:
#
# define TF_HasStat1 0x0010
# define TF_StatsUsed 0x0100
#
# then some of the magic numbers in test results below might need to be
# adjusted.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# There is nothing to test if ANALYZE is disable for this build.
# These tests also use "PRAGMA stats" which are only enabled for
# debugging builds.
#
ifcapable {!debug || !analyze || !vtab} {
finish_test
return
}
do_execsql_test autoanalyze1-100 {
-- Build up a test table with some indexes
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d);
CREATE UNIQUE INDEX t1bc ON t1(b,c);
CREATE INDEX t1d ON t1(d);
WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100)
INSERT INTO t1(a,b,c,d) SELECT x, x, x, x FROM c;
-- Verify that the hasStat1 flag is clear on on indexes
SELECT idx, flgs FROM pragma_stats
WHERE idx IS NOT NULL
ORDER BY idx;
-- Verify that the TF_HasStat1 flag is clear on the table
SELECT tbl, (flgs & 0x10)!=0 FROM pragma_stats WHERE tbl='t1' AND idx IS NULL;
} {t1bc 0 t1d 0 t1 0}
# No use of stat1 recorded so far
do_execsql_test autoanalyze1-110 {
SELECT (flgs & 0x0100)!=0 FROM pragma_stats WHERE tbl='t1' AND idx IS NULL;
} {0}
# Access using a unique index does not set the TF_StatsUsed flag.
#
do_execsql_test autoanalyze1-200 {
SELECT * FROM t1 WHERE a=55;
} {55 55 55 55}
do_execsql_test autoanalyze1-201 {
SELECT (flgs & 0x0100)!=0 FROM pragma_stats WHERE tbl='t1' AND idx IS NULL;
} {0}
do_execsql_test autoanalyze1-210 {
SELECT * FROM t1 WHERE a IN (55,199,299);
} {55 55 55 55}
do_execsql_test autoanalyze1-211 {
SELECT (flgs & 0x0100)!=0 FROM pragma_stats WHERE tbl='t1' AND idx IS NULL;
} {0}
do_execsql_test autoanalyze1-220 {
SELECT * FROM t1 WHERE (b,c)=(45,45);
} {45 45 45 45}
do_execsql_test autoanalyze1-221 {
SELECT (flgs & 0x0100)!=0 FROM pragma_stats WHERE tbl='t1' AND idx IS NULL;
} {0}
# Any use of the non-unique t1d index triggers the use of stats.
#
sqlite3 db test.db
do_execsql_test autoanalyze1-300 {
SELECT * FROM t1 WHERE d=45;
} {45 45 45 45}
do_execsql_test autoanalyze1-301 {
SELECT (flgs & 0x0100)!=0 FROM pragma_stats WHERE tbl='t1' AND idx IS NULL;
} {1}
sqlite3 db test.db
do_execsql_test autoanalyze1-310 {
SELECT * FROM t1 WHERE d=45 AND a=45;
} {45 45 45 45}
do_execsql_test autoanalyze1-311 {
SELECT (flgs & 0x0100)!=0 FROM pragma_stats WHERE tbl='t1' AND idx IS NULL;
} {0} ;# The ROWID lookup short-circuits the d=45 constraint
sqlite3 db test.db
do_execsql_test autoanalyze1-320 {
SELECT * FROM t1 WHERE d=45 AND a IN (45,46);
} {45 45 45 45}
do_execsql_test autoanalyze1-321 {
SELECT (flgs & 0x0100)!=0 FROM pragma_stats WHERE tbl='t1' AND idx IS NULL;
} {1}
# Any use of prefix of a unique index triggers the use of stats
#
sqlite3 db test.db
do_execsql_test autoanalyze1-400 {
SELECT * FROM t1 WHERE b=45;
} {45 45 45 45}
do_execsql_test autoanalyze1-401 {
SELECT (flgs & 0x0100)!=0 FROM pragma_stats WHERE tbl='t1' AND idx IS NULL;
} {1}
# The TF_StatsUsed flag is reset when the database is reopened
#
sqlite3 db test.db
do_execsql_test autoanalyze1-500 {
SELECT (flgs & 0x0100)!=0 FROM pragma_stats WHERE tbl='t1' AND idx IS NULL;
} {0}
finish_test

883
testdata/tcl/autoinc.test vendored Normal file
View file

@ -0,0 +1,883 @@
# 2004 November 12
#
# 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 script is testing the AUTOINCREMENT features.
#
# $Id: autoinc.test,v 1.14 2009/06/23 20:28:54 drh Exp $
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix autoinc
# If the library is not compiled with autoincrement support then
# skip all tests in this file.
#
ifcapable {!autoinc} {
finish_test
return
}
if {[permutation]=="inmemory_journal"} {
finish_test
return
}
sqlite3_db_config_lookaside db 0 0 0
# The database is initially empty.
#
do_test autoinc-1.1 {
execsql {
SELECT name FROM sqlite_master WHERE type='table';
}
} {}
# Add a table with the AUTOINCREMENT feature. Verify that the
# SQLITE_SEQUENCE table gets created.
#
do_test autoinc-1.2 {
execsql {
CREATE TABLE t1(x INTEGER PRIMARY KEY AUTOINCREMENT, y);
SELECT name FROM sqlite_master WHERE type='table';
}
} {t1 sqlite_sequence}
# The SQLITE_SEQUENCE table is initially empty
#
do_test autoinc-1.3 {
execsql {
SELECT * FROM sqlite_sequence;
}
} {}
do_test autoinc-1.3.1 {
catchsql {
CREATE INDEX seqidx ON sqlite_sequence(name)
}
} {1 {table sqlite_sequence may not be indexed}}
# Close and reopen the database. Verify that everything is still there.
#
do_test autoinc-1.4 {
db close
sqlite3 db test.db
execsql {
SELECT * FROM sqlite_sequence;
}
} {}
# We are not allowed to drop the sqlite_sequence table.
#
do_test autoinc-1.5 {
catchsql {DROP TABLE sqlite_sequence}
} {1 {table sqlite_sequence may not be dropped}}
do_test autoinc-1.6 {
execsql {SELECT name FROM sqlite_master WHERE type='table'}
} {t1 sqlite_sequence}
# Insert an entries into the t1 table and make sure the largest key
# is always recorded in the sqlite_sequence table.
#
do_test autoinc-2.1 {
execsql {
SELECT * FROM sqlite_sequence
}
} {}
do_test autoinc-2.2 {
execsql {
INSERT INTO t1 VALUES(12,34);
SELECT * FROM sqlite_sequence;
}
} {t1 12}
do_test autoinc-2.3 {
execsql {
INSERT INTO t1 VALUES(1,23);
SELECT * FROM sqlite_sequence;
}
} {t1 12}
do_test autoinc-2.4 {
execsql {
INSERT INTO t1 VALUES(123,456);
SELECT * FROM sqlite_sequence;
}
} {t1 123}
do_test autoinc-2.5 {
execsql {
INSERT INTO t1 VALUES(NULL,567);
SELECT * FROM sqlite_sequence;
}
} {t1 124}
do_test autoinc-2.6 {
execsql {
DELETE FROM t1 WHERE y=567;
SELECT * FROM sqlite_sequence;
}
} {t1 124}
do_test autoinc-2.7 {
execsql {
INSERT INTO t1 VALUES(NULL,567);
SELECT * FROM sqlite_sequence;
}
} {t1 125}
do_test autoinc-2.8 {
execsql {
DELETE FROM t1;
SELECT * FROM sqlite_sequence;
}
} {t1 125}
do_test autoinc-2.9 {
execsql {
INSERT INTO t1 VALUES(12,34);
SELECT * FROM sqlite_sequence;
}
} {t1 125}
do_test autoinc-2.10 {
execsql {
INSERT INTO t1 VALUES(125,456);
SELECT * FROM sqlite_sequence;
}
} {t1 125}
do_test autoinc-2.11 {
execsql {
INSERT INTO t1 VALUES(-1234567,-1);
SELECT * FROM sqlite_sequence;
}
} {t1 125}
do_test autoinc-2.12 {
execsql {
INSERT INTO t1 VALUES(234,5678);
SELECT * FROM sqlite_sequence;
}
} {t1 234}
do_test autoinc-2.13 {
execsql {
DELETE FROM t1;
INSERT INTO t1 VALUES(NULL,1);
SELECT * FROM sqlite_sequence;
}
} {t1 235}
do_test autoinc-2.14 {
execsql {
SELECT * FROM t1;
}
} {235 1}
# Manually change the autoincrement values in sqlite_sequence.
#
do_test autoinc-2.20 {
execsql {
UPDATE sqlite_sequence SET seq=1234 WHERE name='t1';
INSERT INTO t1 VALUES(NULL,2);
SELECT * FROM t1;
}
} {235 1 1235 2}
do_test autoinc-2.21 {
execsql {
SELECT * FROM sqlite_sequence;
}
} {t1 1235}
do_test autoinc-2.22 {
execsql {
UPDATE sqlite_sequence SET seq=NULL WHERE name='t1';
INSERT INTO t1 VALUES(NULL,3);
SELECT * FROM t1;
}
} {235 1 1235 2 1236 3}
do_test autoinc-2.23 {
execsql {
SELECT * FROM sqlite_sequence;
}
} {t1 1236}
do_test autoinc-2.24 {
execsql {
UPDATE sqlite_sequence SET seq='a-string' WHERE name='t1';
INSERT INTO t1 VALUES(NULL,4);
SELECT * FROM t1;
}
} {235 1 1235 2 1236 3 1237 4}
do_test autoinc-2.25 {
execsql {
SELECT * FROM sqlite_sequence;
}
} {t1 1237}
do_test autoinc-2.26 {
execsql {
DELETE FROM sqlite_sequence WHERE name='t1';
INSERT INTO t1 VALUES(NULL,5);
SELECT * FROM t1;
}
} {235 1 1235 2 1236 3 1237 4 1238 5}
do_test autoinc-2.27 {
execsql {
SELECT * FROM sqlite_sequence;
}
} {t1 1238}
do_test autoinc-2.28 {
execsql {
UPDATE sqlite_sequence SET seq='-12345678901234567890'
WHERE name='t1';
INSERT INTO t1 VALUES(NULL,6);
SELECT * FROM t1;
}
} {235 1 1235 2 1236 3 1237 4 1238 5 1239 6}
do_test autoinc-2.29 {
execsql {
SELECT * FROM sqlite_sequence;
}
} {t1 1239}
# Test multi-row inserts
#
do_test autoinc-2.50 {
execsql {
DELETE FROM t1 WHERE y>=3;
INSERT INTO t1 SELECT NULL, y+2 FROM t1;
SELECT * FROM t1;
}
} {235 1 1235 2 1240 3 1241 4}
do_test autoinc-2.51 {
execsql {
SELECT * FROM sqlite_sequence
}
} {t1 1241}
ifcapable tempdb {
do_test autoinc-2.52 {
execsql {
CREATE TEMP TABLE t2 AS SELECT y FROM t1;
}
execsql {
INSERT INTO t1 SELECT NULL, y+4 FROM t2;
SELECT * FROM t1;
}
} {235 1 1235 2 1240 3 1241 4 1242 5 1243 6 1244 7 1245 8}
do_test autoinc-2.53 {
execsql {
SELECT * FROM sqlite_sequence
}
} {t1 1245}
do_test autoinc-2.54 {
execsql {
DELETE FROM t1;
INSERT INTO t1 SELECT NULL, y FROM t2;
SELECT * FROM t1;
}
} {1246 1 1247 2 1248 3 1249 4}
do_test autoinc-2.55 {
execsql {
SELECT * FROM sqlite_sequence
}
} {t1 1249}
}
# Create multiple AUTOINCREMENT tables. Make sure all sequences are
# tracked separately and do not interfere with one another.
#
do_test autoinc-2.70 {
catchsql {
DROP TABLE t2;
}
execsql {
CREATE TABLE t2(d, e INTEGER PRIMARY KEY AUTOINCREMENT, f);
INSERT INTO t2(d) VALUES(1);
SELECT * FROM sqlite_sequence;
}
} [ifcapable tempdb {list t1 1249 t2 1} else {list t1 1241 t2 1}]
do_test autoinc-2.71 {
execsql {
INSERT INTO t2(d) VALUES(2);
SELECT * FROM sqlite_sequence;
}
} [ifcapable tempdb {list t1 1249 t2 2} else {list t1 1241 t2 2}]
do_test autoinc-2.72 {
execsql {
INSERT INTO t1(x) VALUES(10000);
SELECT * FROM sqlite_sequence;
}
} {t1 10000 t2 2}
do_test autoinc-2.73 {
execsql {
CREATE TABLE t3(g INTEGER PRIMARY KEY AUTOINCREMENT, h);
INSERT INTO t3(h) VALUES(1);
SELECT * FROM sqlite_sequence;
}
} {t1 10000 t2 2 t3 1}
do_test autoinc-2.74 {
execsql {
INSERT INTO t2(d,e) VALUES(3,100);
SELECT * FROM sqlite_sequence;
}
} {t1 10000 t2 100 t3 1}
# When a table with an AUTOINCREMENT is deleted, the corresponding entry
# in the SQLITE_SEQUENCE table should also be deleted. But the SQLITE_SEQUENCE
# table itself should remain behind.
#
do_test autoinc-3.1 {
execsql {SELECT name FROM sqlite_sequence}
} {t1 t2 t3}
do_test autoinc-3.2 {
execsql {
DROP TABLE t1;
SELECT name FROM sqlite_sequence;
}
} {t2 t3}
do_test autoinc-3.3 {
execsql {
DROP TABLE t3;
SELECT name FROM sqlite_sequence;
}
} {t2}
do_test autoinc-3.4 {
execsql {
DROP TABLE t2;
SELECT name FROM sqlite_sequence;
}
} {}
# AUTOINCREMENT on TEMP tables.
#
ifcapable tempdb {
do_test autoinc-4.1 {
execsql {
SELECT 1, name FROM sqlite_master WHERE type='table';
SELECT 2, name FROM temp.sqlite_master WHERE type='table';
}
} {1 sqlite_sequence}
do_test autoinc-4.2 {
execsql {
CREATE TABLE t1(x INTEGER PRIMARY KEY AUTOINCREMENT, y);
CREATE TEMP TABLE t3(a INTEGER PRIMARY KEY AUTOINCREMENT, b);
SELECT 1, name FROM sqlite_master WHERE type='table';
SELECT 2, name FROM sqlite_temp_master WHERE type='table';
}
} {1 sqlite_sequence 1 t1 2 t3 2 sqlite_sequence}
do_test autoinc-4.3 {
execsql {
SELECT 1, * FROM main.sqlite_sequence;
SELECT 2, * FROM temp.sqlite_sequence;
}
} {}
do_test autoinc-4.4 {
execsql {
INSERT INTO t1 VALUES(10,1);
INSERT INTO t3 VALUES(20,2);
INSERT INTO t1 VALUES(NULL,3);
INSERT INTO t3 VALUES(NULL,4);
}
} {}
ifcapable compound {
do_test autoinc-4.4.1 {
execsql {
SELECT * FROM t1 UNION ALL SELECT * FROM t3;
}
} {10 1 11 3 20 2 21 4}
} ;# ifcapable compound
do_test autoinc-4.5 {
execsql {
SELECT 1, * FROM main.sqlite_sequence;
SELECT 2, * FROM temp.sqlite_sequence;
}
} {1 t1 11 2 t3 21}
do_test autoinc-4.6 {
execsql {
INSERT INTO t1 SELECT * FROM t3;
SELECT 1, * FROM main.sqlite_sequence;
SELECT 2, * FROM temp.sqlite_sequence;
}
} {1 t1 21 2 t3 21}
do_test autoinc-4.7 {
execsql {
INSERT INTO t3 SELECT x+100, y FROM t1;
SELECT 1, * FROM main.sqlite_sequence;
SELECT 2, * FROM temp.sqlite_sequence;
}
} {1 t1 21 2 t3 121}
do_test autoinc-4.8 {
execsql {
DROP TABLE t3;
SELECT 1, * FROM main.sqlite_sequence;
SELECT 2, * FROM temp.sqlite_sequence;
}
} {1 t1 21}
do_test autoinc-4.9 {
execsql {
CREATE TEMP TABLE t2(p INTEGER PRIMARY KEY AUTOINCREMENT, q);
INSERT INTO t2 SELECT * FROM t1;
DROP TABLE t1;
SELECT 1, * FROM main.sqlite_sequence;
SELECT 2, * FROM temp.sqlite_sequence;
}
} {2 t2 21}
do_test autoinc-4.10 {
execsql {
DROP TABLE t2;
SELECT 1, * FROM main.sqlite_sequence;
SELECT 2, * FROM temp.sqlite_sequence;
}
} {}
}
# Make sure AUTOINCREMENT works on ATTACH-ed tables.
#
ifcapable tempdb&&attach {
do_test autoinc-5.1 {
forcedelete test2.db
forcedelete test2.db-journal
sqlite3 db2 test2.db
execsql {
CREATE TABLE t4(m INTEGER PRIMARY KEY AUTOINCREMENT, n);
CREATE TABLE t5(o, p INTEGER PRIMARY KEY AUTOINCREMENT);
} db2;
execsql {
ATTACH 'test2.db' as aux;
SELECT 1, * FROM main.sqlite_sequence;
SELECT 2, * FROM temp.sqlite_sequence;
SELECT 3, * FROM aux.sqlite_sequence;
}
} {}
do_test autoinc-5.2 {
execsql {
INSERT INTO t4 VALUES(NULL,1);
SELECT 1, * FROM main.sqlite_sequence;
SELECT 2, * FROM temp.sqlite_sequence;
SELECT 3, * FROM aux.sqlite_sequence;
}
} {3 t4 1}
do_test autoinc-5.3 {
execsql {
INSERT INTO t5 VALUES(100,200);
SELECT * FROM sqlite_sequence
} db2
} {t4 1 t5 200}
do_test autoinc-5.4 {
execsql {
SELECT 1, * FROM main.sqlite_sequence;
SELECT 2, * FROM temp.sqlite_sequence;
SELECT 3, * FROM aux.sqlite_sequence;
}
} {3 t4 1 3 t5 200}
}
# Requirement REQ00310: Make sure an insert fails if the sequence is
# already at its maximum value.
#
ifcapable {rowid32} {
do_test autoinc-6.1 {
execsql {
CREATE TABLE t6(v INTEGER PRIMARY KEY AUTOINCREMENT, w);
INSERT INTO t6 VALUES(2147483647,1);
SELECT seq FROM main.sqlite_sequence WHERE name='t6';
}
} 2147483647
}
ifcapable {!rowid32} {
do_test autoinc-6.1 {
execsql {
CREATE TABLE t6(v INTEGER PRIMARY KEY AUTOINCREMENT, w);
INSERT INTO t6 VALUES(9223372036854775807,1);
SELECT seq FROM main.sqlite_sequence WHERE name='t6';
}
} 9223372036854775807
}
do_test autoinc-6.2 {
catchsql {
INSERT INTO t6 VALUES(NULL,1);
}
} {1 {database or disk is full}}
# Allow the AUTOINCREMENT keyword inside the parentheses
# on a separate PRIMARY KEY designation.
#
do_test autoinc-7.1 {
execsql {
CREATE TABLE t7(x INTEGER, y REAL, PRIMARY KEY(x AUTOINCREMENT));
INSERT INTO t7(y) VALUES(123);
INSERT INTO t7(y) VALUES(234);
DELETE FROM t7;
INSERT INTO t7(y) VALUES(345);
SELECT * FROM t7;
}
} {3 345.0}
# Test that if the AUTOINCREMENT is applied to a non integer primary key
# the error message is sensible.
do_test autoinc-7.2 {
catchsql {
CREATE TABLE t8(x TEXT PRIMARY KEY AUTOINCREMENT);
}
} {1 {AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY}}
# Ticket #1283. Make sure that preparing but never running a statement
# that creates the sqlite_sequence table does not mess up the database.
#
do_test autoinc-8.1 {
catch {db2 close}
catch {db close}
forcedelete test.db
sqlite3 db test.db
set DB [sqlite3_connection_pointer db]
set STMT [sqlite3_prepare $DB {
CREATE TABLE t1(
x INTEGER PRIMARY KEY AUTOINCREMENT
)
} -1 TAIL]
sqlite3_finalize $STMT
set STMT [sqlite3_prepare $DB {
CREATE TABLE t1(
x INTEGER PRIMARY KEY AUTOINCREMENT
)
} -1 TAIL]
sqlite3_step $STMT
sqlite3_finalize $STMT
execsql {
INSERT INTO t1 VALUES(NULL);
SELECT * FROM t1;
}
} {1}
# Ticket #3148
# Make sure the sqlite_sequence table is not damaged when doing
# an empty insert - an INSERT INTO ... SELECT ... where the SELECT
# clause returns an empty set.
#
do_test autoinc-9.1 {
db eval {
CREATE TABLE t2(x INTEGER PRIMARY KEY AUTOINCREMENT, y);
INSERT INTO t2 VALUES(NULL, 1);
CREATE TABLE t3(a INTEGER PRIMARY KEY AUTOINCREMENT, b);
INSERT INTO t3 SELECT * FROM t2 WHERE y>1;
SELECT * FROM sqlite_sequence WHERE name='t3';
}
} {t3 0}
ifcapable trigger {
catchsql { pragma recursive_triggers = off }
# Ticket #3928. Make sure that triggers to not make extra slots in
# the SQLITE_SEQUENCE table.
#
do_test autoinc-3928.1 {
db eval {
CREATE TABLE t3928(a INTEGER PRIMARY KEY AUTOINCREMENT, b);
CREATE TRIGGER t3928r1 BEFORE INSERT ON t3928 BEGIN
INSERT INTO t3928(b) VALUES('before1');
INSERT INTO t3928(b) VALUES('before2');
END;
CREATE TRIGGER t3928r2 AFTER INSERT ON t3928 BEGIN
INSERT INTO t3928(b) VALUES('after1');
INSERT INTO t3928(b) VALUES('after2');
END;
INSERT INTO t3928(b) VALUES('test');
SELECT * FROM t3928 ORDER BY a;
}
} {1 before1 2 after1 3 after2 4 before2 5 after1 6 after2 7 test 8 before1 9 before2 10 after1 11 before1 12 before2 13 after2}
do_test autoinc-3928.2 {
db eval {
SELECT * FROM sqlite_sequence WHERE name='t3928'
}
} {t3928 13}
do_test autoinc-3928.3 {
db eval {
DROP TRIGGER t3928r1;
DROP TRIGGER t3928r2;
CREATE TRIGGER t3928r3 BEFORE UPDATE ON t3928
WHEN typeof(new.b)=='integer' BEGIN
INSERT INTO t3928(b) VALUES('before-int-' || new.b);
END;
CREATE TRIGGER t3928r4 AFTER UPDATE ON t3928
WHEN typeof(new.b)=='integer' BEGIN
INSERT INTO t3928(b) VALUES('after-int-' || new.b);
END;
DELETE FROM t3928 WHERE a!=1;
UPDATE t3928 SET b=456 WHERE a=1;
SELECT * FROM t3928 ORDER BY a;
}
} {1 456 14 before-int-456 15 after-int-456}
do_test autoinc-3928.4 {
db eval {
SELECT * FROM sqlite_sequence WHERE name='t3928'
}
} {t3928 15}
do_test autoinc-3928.5 {
db eval {
CREATE TABLE t3928b(x);
INSERT INTO t3928b VALUES(100);
INSERT INTO t3928b VALUES(200);
INSERT INTO t3928b VALUES(300);
DELETE FROM t3928;
CREATE TABLE t3928c(y INTEGER PRIMARY KEY AUTOINCREMENT, z);
CREATE TRIGGER t3928br1 BEFORE DELETE ON t3928b BEGIN
INSERT INTO t3928(b) VALUES('before-del-'||old.x);
INSERT INTO t3928c(z) VALUES('before-del-'||old.x);
END;
CREATE TRIGGER t3928br2 AFTER DELETE ON t3928b BEGIN
INSERT INTO t3928(b) VALUES('after-del-'||old.x);
INSERT INTO t3928c(z) VALUES('after-del-'||old.x);
END;
DELETE FROM t3928b;
SELECT * FROM t3928 ORDER BY a;
}
} {16 before-del-100 17 after-del-100 18 before-del-200 19 after-del-200 20 before-del-300 21 after-del-300}
do_test autoinc-3928.6 {
db eval {
SELECT * FROM t3928c ORDER BY y;
}
} {1 before-del-100 2 after-del-100 3 before-del-200 4 after-del-200 5 before-del-300 6 after-del-300}
do_test autoinc-3928.7 {
db eval {
SELECT * FROM sqlite_sequence WHERE name LIKE 't3928%' ORDER BY name;
}
} {t3928 21 t3928c 6}
# Ticket [a696379c1f0886615541a48b35bd8181a80e88f8]
do_test autoinc-a69637.1 {
db eval {
CREATE TABLE ta69637_1(x INTEGER PRIMARY KEY AUTOINCREMENT, y);
CREATE TABLE ta69637_2(z);
CREATE TRIGGER ra69637_1 AFTER INSERT ON ta69637_2 BEGIN
INSERT INTO ta69637_1(y) VALUES(new.z+1);
END;
INSERT INTO ta69637_2 VALUES(123);
SELECT * FROM ta69637_1;
}
} {1 124}
do_test autoinc-a69637.2 {
db eval {
CREATE VIEW va69637_2 AS SELECT * FROM ta69637_2;
CREATE TRIGGER ra69637_2 INSTEAD OF INSERT ON va69637_2 BEGIN
INSERT INTO ta69637_1(y) VALUES(new.z+10000);
END;
INSERT INTO va69637_2 VALUES(123);
SELECT * FROM ta69637_1;
}
} {1 124 2 10123}
}
# 2016-10-03 ticket https://www.sqlite.org/src/tktview/7b3328086a5c1
# Make sure autoincrement plays nicely with the xfer optimization
#
do_execsql_test autoinc-10.1 {
DELETE FROM sqlite_sequence;
CREATE TABLE t10a(a INTEGER PRIMARY KEY AUTOINCREMENT, b UNIQUE);
INSERT INTO t10a VALUES(888,9999);
CREATE TABLE t10b(x INTEGER PRIMARY KEY AUTOINCREMENT, y UNIQUE);
INSERT INTO t10b SELECT * FROM t10a;
SELECT * FROM sqlite_sequence;
} {t10a 888 t10b 888}
# 2018-04-21 autoincrement does not cause problems for upsert
#
do_execsql_test autoinc-11.1 {
CREATE TABLE t11(a INTEGER PRIMARY KEY AUTOINCREMENT,b UNIQUE);
INSERT INTO t11(a,b) VALUES(2,3),(5,6),(4,3),(1,2)
ON CONFLICT(b) DO UPDATE SET a=a+1000;
SELECT seq FROM sqlite_sequence WHERE name='t11';
} {5}
# 2018-05-23 ticket d8dc2b3a58cd5dc2918a1d4acbba4676a23ada4c
# Does not crash if the sqlite_sequence table schema is missing
# or corrupt.
#
do_test autoinc-12.1 {
db close
forcedelete test.db
sqlite3 db test.db
sqlite3_db_config db DEFENSIVE 0
db eval {
CREATE TABLE fake_sequence(name TEXT PRIMARY KEY,seq) WITHOUT ROWID;
PRAGMA writable_schema=on;
UPDATE sqlite_master SET
sql=replace(sql,'fake_','sqlite_'),
name='sqlite_sequence',
tbl_name='sqlite_sequence'
WHERE name='fake_sequence';
}
db close
sqlite3 db test.db
set res [catch {db eval {
CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT, b TEXT);
INSERT INTO t1(b) VALUES('one');
}} msg]
lappend res $msg
} {1 {database disk image is malformed}}
do_test autoinc-12.2 {
db close
forcedelete test.db
sqlite3 db test.db
sqlite3_db_config db DEFENSIVE 0
db eval {
CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT, b TEXT);
INSERT INTO t1(b) VALUES('one');
PRAGMA writable_schema=on;
UPDATE sqlite_master SET
sql=replace(sql,'sqlite_','x_'),
name='x_sequence',
tbl_name='x_sequence'
WHERE name='sqlite_sequence';
}
db close
sqlite3 db test.db
set res [catch {db eval {
INSERT INTO t1(b) VALUES('two');
}} msg]
lappend res $msg
} {1 {database disk image is malformed}}
ifcapable vtab {
set err "database disk image is malformed"
} else {
set err {malformed database schema (sqlite_sequence) - near "VIRTUAL": syntax error}
}
do_test autoinc-12.3 {
db close
forcedelete test.db
sqlite3 db test.db
sqlite3_db_config db DEFENSIVE 0
db eval {
CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT, b TEXT);
INSERT INTO t1(b) VALUES('one');
PRAGMA writable_schema=on;
UPDATE sqlite_master SET
sql='CREATE VIRTUAL TABLE sqlite_sequence USING sqlite_dbpage'
WHERE name='sqlite_sequence';
}
db close
sqlite3 db test.db
set res [catch {db eval {
INSERT INTO t1(b) VALUES('two');
}} msg]
lappend res $msg
} [list 1 $err]
do_test autoinc-12.4 {
db close
forcedelete test.db
sqlite3 db test.db
db eval {
CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT, b TEXT);
INSERT INTO t1(b) VALUES('one');
CREATE TABLE fake(name TEXT PRIMARY KEY,seq) WITHOUT ROWID;
}
set root1 [db one {SELECT rootpage FROM sqlite_master
WHERE name='sqlite_sequence'}]
set root2 [db one {SELECT rootpage FROM sqlite_master
WHERE name='fake'}]
sqlite3_db_config db DEFENSIVE 0
db eval {
PRAGMA writable_schema=on;
UPDATE sqlite_master SET rootpage=$root2
WHERE name='sqlite_sequence';
UPDATE sqlite_master SET rootpage=$root1
WHERE name='fake';
}
db close
sqlite3 db test.db
set res [catch {db eval {
INSERT INTO t1(b) VALUES('two');
}} msg]
lappend res $msg
} {1 {database disk image is malformed}}
breakpoint
do_test autoinc-12.5 {
db close
forcedelete test.db
sqlite3 db test.db
sqlite3_db_config db DEFENSIVE 0
db eval {
CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT, b TEXT);
INSERT INTO t1(b) VALUES('one');
PRAGMA writable_schema=on;
UPDATE sqlite_master SET
sql='CREATE TABLE sqlite_sequence(x)'
WHERE name='sqlite_sequence';
}
db close
sqlite3 db test.db
set res [catch {db eval {
INSERT INTO t1(b) VALUES('two');
}} msg]
lappend res $msg
} {1 {database disk image is malformed}}
do_test autoinc-12.6 {
db close
forcedelete test.db
sqlite3 db test.db
sqlite3_db_config db DEFENSIVE 0
db eval {
CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT, b TEXT);
INSERT INTO t1(b) VALUES('one');
PRAGMA writable_schema=on;
UPDATE sqlite_master SET
sql='CREATE TABLE sqlite_sequence(x,y INTEGER PRIMARY KEY)'
WHERE name='sqlite_sequence';
}
db close
sqlite3 db test.db
set res [catch {db eval {
INSERT INTO t1(b) VALUES('two'),('three'),('four');
INSERT INTO t1(b) VALUES('five');
PRAGMA integrity_check;
}} msg]
lappend res $msg
} {0 ok}
do_test autoinc-12.7 {
db close
forcedelete test.db
sqlite3 db test.db
sqlite3_db_config db DEFENSIVE 0
db eval {
CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT, b TEXT);
INSERT INTO t1(b) VALUES('one');
PRAGMA writable_schema=on;
UPDATE sqlite_master SET
sql='CREATE TABLE sqlite_sequence(y INTEGER PRIMARY KEY,x)'
WHERE name='sqlite_sequence';
}
db close
sqlite3 db test.db
set res [catch {db eval {
INSERT INTO t1(b) VALUES('two'),('three'),('four');
INSERT INTO t1(b) VALUES('five');
PRAGMA integrity_check;
}} msg]
lappend res $msg
} {0 ok}
#--------------------------------------------------------------------------
reset_db
do_execsql_test 13.0 {
CREATE TABLE t1(i INTEGER PRIMARY KEY AUTOINCREMENT, j);
CREATE TABLE t2(i INTEGER PRIMARY KEY AUTOINCREMENT, j);
CREATE TABLE t3(i INTEGER PRIMARY KEY AUTOINCREMENT, j);
INSERT INTO t1 VALUES(NULL, 1);
INSERT INTO t2 VALUES(NULL, 2);
INSERT INTO t3 VALUES(NULL, 3);
SELECT name FROM sqlite_sequence;
} {t1 t2 t3}
do_execsql_test 13.1 {
UPDATE sqlite_sequence SET name=NULL WHERE name='t2';
INSERT INTO t3 VALUES(NULL, 4);
DELETE FROM t3;
INSERT INTO t3 VALUES(NULL, 5);
SELECT * FROM t3;
} {3 5}
finish_test

564
testdata/tcl/autoindex1.test vendored Normal file
View file

@ -0,0 +1,564 @@
# 2010 April 07
#
# 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 script is testing automatic index creation logic.
#
# EVIDENCE-OF: R-34271-33106 PRAGMA automatic_index; PRAGMA
# automatic_index = boolean; Query, set, or clear the automatic indexing
# capability.
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# If the library is not compiled with automatic index support then
# skip all tests in this file.
#
ifcapable {!autoindex} {
finish_test
return
}
# Setup for logging
db close
sqlite3_shutdown
test_sqlite3_log [list lappend ::log]
set ::log [list]
sqlite3 db test.db
# With automatic index turned off, we do a full scan of the T2 table
do_test autoindex1-100 {
db eval {
CREATE TABLE t1(a,b);
INSERT INTO t1 VALUES(1,11);
INSERT INTO t1 VALUES(2,22);
INSERT INTO t1 SELECT a+2, b+22 FROM t1;
INSERT INTO t1 SELECT a+4, b+44 FROM t1;
CREATE TABLE t2(c,d);
INSERT INTO t2 SELECT a, 900+b FROM t1;
}
db eval {
PRAGMA automatic_index=OFF;
SELECT b, d FROM t1 JOIN t2 ON a=c ORDER BY b;
}
} {11 911 22 922 33 933 44 944 55 955 66 966 77 977 88 988}
do_test autoindex1-101 {
db status step
} {63}
do_test autoindex1-102 {
db status autoindex
} {0}
# With autoindex turned on, we build an index once and then use that index
# to find T2 values.
do_test autoindex1-110 {
db eval {
PRAGMA automatic_index=ON;
SELECT b, d FROM t1 JOIN t2 ON a=c ORDER BY b;
}
} {11 911 22 922 33 933 44 944 55 955 66 966 77 977 88 988}
do_test autoindex1-111 {
db status step
} {7}
do_test autoindex1-112 {
db status autoindex
} {7}
do_test autoindex1-113 {
set ::log
} {SQLITE_WARNING_AUTOINDEX {automatic index on t2(c)}}
db close
sqlite3_shutdown
test_sqlite3_log
sqlite3_initialize
sqlite3 db test.db
# The same test as above, but this time the T2 query is a subquery rather
# than a join.
do_test autoindex1-200 {
db eval {
PRAGMA automatic_index=OFF;
SELECT b, (SELECT d FROM t2 WHERE c=a) FROM t1;
}
} {11 911 22 922 33 933 44 944 55 955 66 966 77 977 88 988}
do_test autoindex1-201 {
db status step
} {35}
do_test autoindex1-202 {
db status autoindex
} {0}
do_test autoindex1-210 {
db eval {
PRAGMA automatic_index=ON;
ANALYZE;
UPDATE sqlite_stat1 SET stat='10000' WHERE tbl='t1';
-- Table t2 actually contains 8 rows.
UPDATE sqlite_stat1 SET stat='16' WHERE tbl='t2';
ANALYZE sqlite_master;
SELECT b, (SELECT d FROM t2 WHERE c=a) FROM t1;
}
} {11 911 22 922 33 933 44 944 55 955 66 966 77 977 88 988}
do_test autoindex1-211 {
db status step
} {7}
do_test autoindex1-212 {
db status autoindex
} {7}
# Modify the second table of the join while the join is in progress
#
do_execsql_test autoindex1-299 {
UPDATE sqlite_stat1 SET stat='10000' WHERE tbl='t2';
ANALYZE sqlite_master;
EXPLAIN QUERY PLAN
SELECT b, d FROM t1 CROSS JOIN t2 ON (c=a);
} {/AUTOMATIC COVERING INDEX/}
do_test autoindex1-300 {
set r {}
db eval {SELECT b, d FROM t1 CROSS JOIN t2 ON (c=a)} {
lappend r $b $d
db eval {UPDATE t2 SET d=d+1}
}
set r
} {11 911 22 922 33 933 44 944 55 955 66 966 77 977 88 988}
do_test autoindex1-310 {
db eval {SELECT d FROM t2 ORDER BY d}
} {919 930 941 952 963 974 985 996}
# The next test does a 10-way join on unindexed tables. Without
# automatic indices, the join will take a long time to complete.
# With automatic indices, it should only take about a second.
#
do_test autoindex1-400 {
db eval {
CREATE TABLE t4(a, b);
INSERT INTO t4 VALUES(1,2);
INSERT INTO t4 VALUES(2,3);
}
for {set n 2} {$n<4096} {set n [expr {$n+$n}]} {
db eval {INSERT INTO t4 SELECT a+$n, b+$n FROM t4}
}
db eval {
SELECT count(*) FROM t4;
}
} {4096}
do_test autoindex1-401 {
db eval {
SELECT count(*)
FROM t4 AS x1
JOIN t4 AS x2 ON x2.a=x1.b
JOIN t4 AS x3 ON x3.a=x2.b
JOIN t4 AS x4 ON x4.a=x3.b
JOIN t4 AS x5 ON x5.a=x4.b
JOIN t4 AS x6 ON x6.a=x5.b
JOIN t4 AS x7 ON x7.a=x6.b
JOIN t4 AS x8 ON x8.a=x7.b
JOIN t4 AS x9 ON x9.a=x8.b
JOIN t4 AS x10 ON x10.a=x9.b;
}
} {4087}
# Ticket [8011086c85c6c404014c947fcf3eb9f42b184a0d] from 2010-07-08
# Make sure automatic indices are not created for the RHS of an IN expression
# that is not a correlated subquery.
#
do_execsql_test autoindex1-500 {
CREATE TABLE t501(a INTEGER PRIMARY KEY, b);
CREATE TABLE t502(x INTEGER PRIMARY KEY, y);
INSERT INTO sqlite_stat1(tbl,idx,stat) VALUES('t501',null,'1000000');
INSERT INTO sqlite_stat1(tbl,idx,stat) VALUES('t502',null,'1000');
ANALYZE sqlite_master;
}
do_eqp_test autoindex1-500.1 {
SELECT b FROM t501
WHERE t501.a IN (SELECT x FROM t502 WHERE y=?);
} {
QUERY PLAN
|--SEARCH t501 USING INTEGER PRIMARY KEY (rowid=?)
`--LIST SUBQUERY xxxxxx
`--SCAN t502
}
do_eqp_test autoindex1-501 {
SELECT b FROM t501
WHERE t501.a IN (SELECT x FROM t502 WHERE y=t501.b);
} {
QUERY PLAN
|--SCAN t501
`--CORRELATED LIST SUBQUERY xxxxxx
`--SEARCH t502 USING AUTOMATIC COVERING INDEX (y=?)
}
do_eqp_test autoindex1-502 {
SELECT b FROM t501
WHERE t501.a=123
AND t501.a IN (SELECT x FROM t502 WHERE y=t501.b);
} {
QUERY PLAN
|--SEARCH t501 USING INTEGER PRIMARY KEY (rowid=?)
`--CORRELATED LIST SUBQUERY xxxxxx
`--SCAN t502
}
# The following code checks a performance regression reported on the
# mailing list on 2010-10-19. The problem is that the nRowEst field
# of ephermeral tables was not being initialized correctly and so no
# automatic index was being created for the emphemeral table when it was
# used as part of a join.
#
do_execsql_test autoindex1-600 {
CREATE TABLE flock_owner(
owner_rec_id INTEGER CONSTRAINT flock_owner_key PRIMARY KEY,
flock_no VARCHAR(6) NOT NULL REFERENCES flock (flock_no),
owner_person_id INTEGER NOT NULL REFERENCES person (person_id),
owner_change_date TEXT, last_changed TEXT NOT NULL,
CONSTRAINT fo_owner_date UNIQUE (flock_no, owner_change_date)
);
CREATE TABLE sheep (
Sheep_No char(7) NOT NULL,
Date_of_Birth char(8),
Sort_DoB text,
Flock_Book_Vol char(2),
Breeder_No char(6),
Breeder_Person integer,
Originating_Flock char(6),
Registering_Flock char(6),
Tag_Prefix char(9),
Tag_No char(15),
Sort_Tag_No integer,
Breeders_Temp_Tag char(15),
Sex char(1),
Sheep_Name char(32),
Sire_No char(7),
Dam_No char(7),
Register_Code char(1),
Colour char(48),
Colour_Code char(2),
Pattern_Code char(8),
Horns char(1),
Litter_Size char(1),
Coeff_of_Inbreeding real,
Date_of_Registration text,
Date_Last_Changed text,
UNIQUE(Sheep_No));
CREATE INDEX fo_flock_no_index
ON flock_owner (flock_no);
CREATE INDEX fo_owner_change_date_index
ON flock_owner (owner_change_date);
CREATE INDEX fo_owner_person_id_index
ON flock_owner (owner_person_id);
CREATE INDEX sheep_org_flock_index
ON sheep (originating_flock);
CREATE INDEX sheep_reg_flock_index
ON sheep (registering_flock);
}
do_eqp_test autoindex1-600a {
SELECT x.sheep_no, x.registering_flock, x.date_of_registration
FROM sheep x LEFT JOIN
(SELECT s.sheep_no, prev.flock_no, prev.owner_person_id,
s.date_of_registration, prev.owner_change_date
FROM sheep s JOIN flock_owner prev ON s.registering_flock =
prev.flock_no
AND (prev.owner_change_date <= s.date_of_registration || ' 00:00:00')
WHERE NOT EXISTS
(SELECT 'x' FROM flock_owner later
WHERE prev.flock_no = later.flock_no
AND later.owner_change_date > prev.owner_change_date
AND later.owner_change_date <= s.date_of_registration||' 00:00:00')
) y ON x.sheep_no = y.sheep_no
WHERE y.sheep_no IS NULL
ORDER BY x.registering_flock;
} {
QUERY PLAN
|--MATERIALIZE y
| |--SCAN s
| |--SEARCH prev USING INDEX sqlite_autoindex_flock_owner_1 (flock_no=? AND owner_change_date<?)
| `--CORRELATED SCALAR SUBQUERY xxxxxx
| `--SEARCH later USING COVERING INDEX sqlite_autoindex_flock_owner_1 (flock_no=? AND owner_change_date>? AND owner_change_date<?)
|--SCAN x USING INDEX sheep_reg_flock_index
`--SEARCH y USING AUTOMATIC COVERING INDEX (sheep_no=?) LEFT-JOIN
}
do_execsql_test autoindex1-700 {
CREATE TABLE t5(a, b, c);
}
do_eqp_test autoindex1-700a {
SELECT a FROM t5 WHERE b=10 ORDER BY c;
} {
QUERY PLAN
|--SCAN t5
`--USE TEMP B-TREE FOR ORDER BY
}
# The following checks a performance issue reported on the sqlite-dev
# mailing list on 2013-01-10
#
do_execsql_test autoindex1-800 {
CREATE TABLE accounts(
_id INTEGER PRIMARY KEY AUTOINCREMENT,
account_name TEXT,
account_type TEXT,
data_set TEXT
);
CREATE TABLE data(
_id INTEGER PRIMARY KEY AUTOINCREMENT,
package_id INTEGER REFERENCES package(_id),
mimetype_id INTEGER REFERENCES mimetype(_id) NOT NULL,
raw_contact_id INTEGER REFERENCES raw_contacts(_id) NOT NULL,
is_read_only INTEGER NOT NULL DEFAULT 0,
is_primary INTEGER NOT NULL DEFAULT 0,
is_super_primary INTEGER NOT NULL DEFAULT 0,
data_version INTEGER NOT NULL DEFAULT 0,
data1 TEXT,
data2 TEXT,
data3 TEXT,
data4 TEXT,
data5 TEXT,
data6 TEXT,
data7 TEXT,
data8 TEXT,
data9 TEXT,
data10 TEXT,
data11 TEXT,
data12 TEXT,
data13 TEXT,
data14 TEXT,
data15 TEXT,
data_sync1 TEXT,
data_sync2 TEXT,
data_sync3 TEXT,
data_sync4 TEXT
);
CREATE TABLE mimetypes(
_id INTEGER PRIMARY KEY AUTOINCREMENT,
mimetype TEXT NOT NULL
);
CREATE TABLE raw_contacts(
_id INTEGER PRIMARY KEY AUTOINCREMENT,
account_id INTEGER REFERENCES accounts(_id),
sourceid TEXT,
raw_contact_is_read_only INTEGER NOT NULL DEFAULT 0,
version INTEGER NOT NULL DEFAULT 1,
dirty INTEGER NOT NULL DEFAULT 0,
deleted INTEGER NOT NULL DEFAULT 0,
contact_id INTEGER REFERENCES contacts(_id),
aggregation_mode INTEGER NOT NULL DEFAULT 0,
aggregation_needed INTEGER NOT NULL DEFAULT 1,
custom_ringtone TEXT,
send_to_voicemail INTEGER NOT NULL DEFAULT 0,
times_contacted INTEGER NOT NULL DEFAULT 0,
last_time_contacted INTEGER,
starred INTEGER NOT NULL DEFAULT 0,
display_name TEXT,
display_name_alt TEXT,
display_name_source INTEGER NOT NULL DEFAULT 0,
phonetic_name TEXT,
phonetic_name_style TEXT,
sort_key TEXT,
sort_key_alt TEXT,
name_verified INTEGER NOT NULL DEFAULT 0,
sync1 TEXT,
sync2 TEXT,
sync3 TEXT,
sync4 TEXT,
sync_uid TEXT,
sync_version INTEGER NOT NULL DEFAULT 1,
has_calendar_event INTEGER NOT NULL DEFAULT 0,
modified_time INTEGER,
is_restricted INTEGER DEFAULT 0,
yp_source TEXT,
method_selected INTEGER DEFAULT 0,
custom_vibration_type INTEGER DEFAULT 0,
custom_ringtone_path TEXT,
message_notification TEXT,
message_notification_path TEXT
);
CREATE INDEX data_mimetype_data1_index ON data (mimetype_id,data1);
CREATE INDEX data_raw_contact_id ON data (raw_contact_id);
CREATE UNIQUE INDEX mime_type ON mimetypes (mimetype);
CREATE INDEX raw_contact_sort_key1_index ON raw_contacts (sort_key);
CREATE INDEX raw_contact_sort_key2_index ON raw_contacts (sort_key_alt);
CREATE INDEX raw_contacts_contact_id_index ON raw_contacts (contact_id);
CREATE INDEX raw_contacts_source_id_account_id_index
ON raw_contacts (sourceid, account_id);
ANALYZE sqlite_master;
INSERT INTO sqlite_stat1
VALUES('raw_contacts','raw_contact_sort_key2_index','1600 4');
INSERT INTO sqlite_stat1
VALUES('raw_contacts','raw_contact_sort_key1_index','1600 4');
INSERT INTO sqlite_stat1
VALUES('raw_contacts','raw_contacts_source_id_account_id_index',
'1600 1600 1600');
INSERT INTO sqlite_stat1
VALUES('raw_contacts','raw_contacts_contact_id_index','1600 1');
INSERT INTO sqlite_stat1 VALUES('mimetypes','mime_type','12 1');
INSERT INTO sqlite_stat1
VALUES('data','data_mimetype_data1_index','9819 2455 3');
INSERT INTO sqlite_stat1 VALUES('data','data_raw_contact_id','9819 7');
INSERT INTO sqlite_stat1 VALUES('accounts',NULL,'1');
DROP TABLE IF EXISTS sqlite_stat3;
ANALYZE sqlite_master;
EXPLAIN QUERY PLAN
SELECT * FROM
data JOIN mimetypes ON (data.mimetype_id=mimetypes._id)
JOIN raw_contacts ON (data.raw_contact_id=raw_contacts._id)
JOIN accounts ON (raw_contacts.account_id=accounts._id)
WHERE mimetype_id=10 AND data14 IS NOT NULL;
} {/SEARCH data .*SEARCH raw_contacts/}
do_execsql_test autoindex1-801 {
EXPLAIN QUERY PLAN
SELECT * FROM
data JOIN mimetypes ON (data.mimetype_id=mimetypes._id)
JOIN raw_contacts ON (data.raw_contact_id=raw_contacts._id)
JOIN accounts ON (raw_contacts.account_id=accounts._id)
WHERE mimetypes._id=10 AND data14 IS NOT NULL;
} {/SEARCH data .*SEARCH raw_contacts/}
# Another test case from an important user of SQLite. The key feature of
# this test is that the "aggindex" subquery should make use of an
# automatic index. If it does, the query is fast. If it does not, the
# query is deathly slow. It worked OK in 3.7.17 but started going slow
# with version 3.8.0. The problem was fixed for 3.8.7 by reducing the
# cost estimate for automatic indexes on views and subqueries.
#
db close
forcedelete test.db
sqlite3 db test.db
do_execsql_test autoindex1-900 {
CREATE TABLE messages (ROWID INTEGER PRIMARY KEY AUTOINCREMENT, message_id, document_id BLOB, in_reply_to, remote_id INTEGER, sender INTEGER, subject_prefix, subject INTEGER, date_sent INTEGER, date_received INTEGER, date_created INTEGER, date_last_viewed INTEGER, mailbox INTEGER, remote_mailbox INTEGER, original_mailbox INTEGER, flags INTEGER, read, flagged, size INTEGER, color, encoding, type INTEGER, pad, conversation_id INTEGER DEFAULT -1, snippet TEXT DEFAULT NULL, fuzzy_ancestor INTEGER DEFAULT NULL, automated_conversation INTEGER DEFAULT 0, root_status INTEGER DEFAULT -1, conversation_position INTEGER DEFAULT -1);
CREATE INDEX date_index ON messages(date_received);
CREATE INDEX date_last_viewed_index ON messages(date_last_viewed);
CREATE INDEX date_created_index ON messages(date_created);
CREATE INDEX message_message_id_mailbox_index ON messages(message_id, mailbox);
CREATE INDEX message_document_id_index ON messages(document_id);
CREATE INDEX message_read_index ON messages(read);
CREATE INDEX message_flagged_index ON messages(flagged);
CREATE INDEX message_mailbox_index ON messages(mailbox, date_received);
CREATE INDEX message_remote_mailbox_index ON messages(remote_mailbox, remote_id);
CREATE INDEX message_type_index ON messages(type);
CREATE INDEX message_conversation_id_conversation_position_index ON messages(conversation_id, conversation_position);
CREATE INDEX message_fuzzy_ancestor_index ON messages(fuzzy_ancestor);
CREATE INDEX message_subject_fuzzy_ancestor_index ON messages(subject, fuzzy_ancestor);
CREATE INDEX message_sender_subject_automated_conversation_index ON messages(sender, subject, automated_conversation);
CREATE INDEX message_sender_index ON messages(sender);
CREATE INDEX message_root_status ON messages(root_status);
CREATE TABLE subjects (ROWID INTEGER PRIMARY KEY, subject COLLATE RTRIM, normalized_subject COLLATE RTRIM);
CREATE INDEX subject_subject_index ON subjects(subject);
CREATE INDEX subject_normalized_subject_index ON subjects(normalized_subject);
CREATE TABLE addresses (ROWID INTEGER PRIMARY KEY, address COLLATE NOCASE, comment, UNIQUE(address, comment));
CREATE INDEX addresses_address_index ON addresses(address);
CREATE TABLE mailboxes (ROWID INTEGER PRIMARY KEY, url UNIQUE, total_count INTEGER DEFAULT 0, unread_count INTEGER DEFAULT 0, unseen_count INTEGER DEFAULT 0, deleted_count INTEGER DEFAULT 0, unread_count_adjusted_for_duplicates INTEGER DEFAULT 0, change_identifier, source INTEGER, alleged_change_identifier);
CREATE INDEX mailboxes_source_index ON mailboxes(source);
CREATE TABLE labels (ROWID INTEGER PRIMARY KEY, message_id INTEGER NOT NULL, mailbox_id INTEGER NOT NULL, UNIQUE(message_id, mailbox_id));
CREATE INDEX labels_message_id_mailbox_id_index ON labels(message_id, mailbox_id);
CREATE INDEX labels_mailbox_id_index ON labels(mailbox_id);
explain query plan
SELECT messages.ROWID,
messages.message_id,
messages.remote_id,
messages.date_received,
messages.date_sent,
messages.flags,
messages.size,
messages.color,
messages.date_last_viewed,
messages.subject_prefix,
subjects.subject,
sender.comment,
sender.address,
NULL,
messages.mailbox,
messages.original_mailbox,
NULL,
NULL,
messages.type,
messages.document_id,
sender,
NULL,
messages.conversation_id,
messages.conversation_position,
agglabels.labels
FROM mailboxes AS mailbox
JOIN messages ON mailbox.ROWID = messages.mailbox
LEFT OUTER JOIN subjects ON messages.subject = subjects.ROWID
LEFT OUTER JOIN addresses AS sender ON messages.sender = sender.ROWID
LEFT OUTER JOIN (
SELECT message_id, group_concat(mailbox_id) as labels
FROM labels GROUP BY message_id
) AS agglabels ON messages.ROWID = agglabels.message_id
WHERE (mailbox.url = 'imap://email.app@imap.gmail.com/%5BGmail%5D/All%20Mail')
AND (messages.ROWID IN (
SELECT labels.message_id
FROM labels JOIN mailboxes ON labels.mailbox_id = mailboxes.ROWID
WHERE mailboxes.url = 'imap://email.app@imap.gmail.com/INBOX'))
AND messages.mailbox in (6,12,18,24,30,36,42,1,7,13,19,25,31,37,43,2,8,
14,20,26,32,38,3,9,15,21,27,33,39,4,10,16,22,28,
34,40,5,11,17,23,35,41)
ORDER BY date_received DESC;
} {/agglabels USING AUTOMATIC COVERING INDEX/}
# A test case for VIEWs
#
do_execsql_test autoindex1-901 {
CREATE TABLE t1(x INTEGER PRIMARY KEY, y, z);
CREATE TABLE t2(a, b);
CREATE VIEW agg2 AS SELECT a, sum(b) AS m FROM t2 GROUP BY a;
EXPLAIN QUERY PLAN
SELECT t1.z, agg2.m
FROM t1 JOIN agg2 ON t1.y=agg2.m
WHERE t1.x IN (1,2,3);
} {/USING AUTOMATIC COVERING INDEX/}
# 2015-04-15: A NULL CollSeq pointer in automatic index creation.
#
do_execsql_test autoindex1-920 {
CREATE TABLE t920(x);
INSERT INTO t920 VALUES(3),(4),(5);
SELECT * FROM t920,(SELECT 0 FROM t920),(VALUES(9)) WHERE 5 IN (x);
} {5 0 9 5 0 9 5 0 9}
#-------------------------------------------------------------------------
# An IS term from the WHERE clause of a LEFT JOIN cannot be used as an
# index driver for the RHS of a LEFT JOIN. Prior to this being fixed,
# the following SELECT count(*) would incorrectly return 1.
#
do_execsql_test autoindex1-1010 {
CREATE TABLE t11(w);
CREATE TABLE t12(y);
INSERT INTO t11 VALUES(NULL);
INSERT INTO t12 VALUES('notnull');
}
do_execsql_test autoindex1-1020 {
SELECT count(*) FROM t11 LEFT JOIN t12 WHERE t12.y IS t11.w;
} 0
# 2022-04-25
# https://sqlite.org/forum/forumpost/0d3200f4f3bcd3a3
#
reset_db
do_execsql_test autoindex-1100 {
CREATE TABLE t1(a INT, b INT);
CREATE TABLE t2(c INT, d INT);
CREATE TABLE t3(e TEXT, f TEXT);
INSERT INTO t1 VALUES(1, 1);
INSERT INTO t2 VALUES(1, 2);
INSERT INTO t3 VALUES('abc', 'def');
} {}
do_execsql_test autoindex-1110 {
SELECT * FROM t1, t2 LEFT JOIN t3 ON (t2.d=1) WHERE t2.c = +t1.a;
} {1 1 1 2 {} {}}
do_execsql_test autoindex-1120 {
SELECT * FROM t1 LEFT JOIN t2 ON (t2.c=+t1.a) LEFT JOIN t3 ON (t2.d IS NULL);
} {1 1 1 2 {} {}}
finish_test

232
testdata/tcl/autoindex2.test vendored Normal file
View file

@ -0,0 +1,232 @@
# 2014-06-17
#
# 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 script is testing automatic index creation logic.
#
# This file contains a single real-world test case that was giving
# suboptimal performance because of over-use of automatic indexes.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
do_execsql_test autoindex2-100 {
CREATE TABLE t1(
t1_id largeint,
did char(9),
ptime largeint,
exbyte char(4),
pe_id int,
field_id int,
mass float,
param10 float,
param11 float,
exmass float,
deviation float,
trange float,
vstatus int,
commit_status int,
formula char(329),
tier int DEFAULT 2,
ssid int DEFAULT 0,
last_operation largeint DEFAULT 0,
admin_uuid int DEFAULT 0,
previous_value float,
job_id largeint,
last_t1 largeint DEFAULT 0,
data_t1 int,
previous_date largeint DEFAULT 0,
flg8 int DEFAULT 1,
failed_fields char(100)
);
CREATE INDEX t1x0 on t1 (t1_id);
CREATE INDEX t1x1 on t1 (ptime, vstatus);
CREATE INDEX t1x2 on t1 (did, ssid, ptime, vstatus, exbyte, t1_id);
CREATE INDEX t1x3 on t1 (job_id);
CREATE TABLE t2(
did char(9),
client_did char(30),
description char(49),
uid int,
tzid int,
privilege int,
param2 int,
type char(30),
subtype char(32),
dparam1 char(7) DEFAULT '',
param5 char(3) DEFAULT '',
notional float DEFAULT 0.000000,
create_time largeint,
sample_time largeint DEFAULT 0,
param6 largeint,
frequency int,
expiration largeint,
uw_status int,
next_sample largeint,
last_sample largeint,
reserve1 char(29) DEFAULT '',
reserve2 char(29) DEFAULT '',
reserve3 char(29) DEFAULT '',
bxcdr char(19) DEFAULT 'XY',
ssid int DEFAULT 1,
last_t1_id largeint,
reserve4 char(29) DEFAULT '',
reserve5 char(29) DEFAULT '',
param12 int DEFAULT 0,
long_did char(100) DEFAULT '',
gr_code int DEFAULT 0,
drx char(100) DEFAULT '',
parent_id char(9) DEFAULT '',
param13 int DEFAULT 0,
position float DEFAULT 1.000000,
client_did3 char(100) DEFAULT '',
client_did4 char(100) DEFAULT '',
dlib_id char(9) DEFAULT ''
);
CREATE INDEX t2x0 on t2 (did);
CREATE INDEX t2x1 on t2 (client_did);
CREATE INDEX t2x2 on t2 (long_did);
CREATE INDEX t2x3 on t2 (uid);
CREATE INDEX t2x4 on t2 (param2);
CREATE INDEX t2x5 on t2 (type);
CREATE INDEX t2x6 on t2 (subtype);
CREATE INDEX t2x7 on t2 (last_sample);
CREATE INDEX t2x8 on t2 (param6);
CREATE INDEX t2x9 on t2 (frequency);
CREATE INDEX t2x10 on t2 (privilege);
CREATE INDEX t2x11 on t2 (sample_time);
CREATE INDEX t2x12 on t2 (notional);
CREATE INDEX t2x13 on t2 (tzid);
CREATE INDEX t2x14 on t2 (gr_code);
CREATE INDEX t2x15 on t2 (parent_id);
CREATE TABLE t3(
uid int,
param3 int,
uuid int,
acc_id int,
cust_num int,
numerix_id int,
pfy char(29),
param4 char(29),
param15 int DEFAULT 0,
flg7 int DEFAULT 0,
param21 int DEFAULT 0,
bxcdr char(2) DEFAULT 'PC',
c31 int DEFAULT 0,
c33 int DEFAULT 0,
c35 int DEFAULT 0,
c37 int,
mgr_uuid int,
back_up_uuid int,
priv_mars int DEFAULT 0,
is_qc int DEFAULT 0,
c41 int DEFAULT 0,
deleted int DEFAULT 0,
c47 int DEFAULT 1
);
CREATE INDEX t3x0 on t3 (uid);
CREATE INDEX t3x1 on t3 (param3);
CREATE INDEX t3x2 on t3 (uuid);
CREATE INDEX t3x3 on t3 (acc_id);
CREATE INDEX t3x4 on t3 (param4);
CREATE INDEX t3x5 on t3 (pfy);
CREATE INDEX t3x6 on t3 (is_qc);
SELECT count(*) FROM sqlite_master;
} {30}
do_execsql_test autoindex2-110 {
ANALYZE sqlite_master;
INSERT INTO sqlite_stat1 VALUES('t1','t1x3','10747267 260');
INSERT INTO sqlite_stat1 VALUES('t1','t1x2','10747267 121 113 2 2 2 1');
INSERT INTO sqlite_stat1 VALUES('t1','t1x1','10747267 50 40');
INSERT INTO sqlite_stat1 VALUES('t1','t1x0','10747267 1');
INSERT INTO sqlite_stat1 VALUES('t2','t2x15','39667 253');
INSERT INTO sqlite_stat1 VALUES('t2','t2x14','39667 19834');
INSERT INTO sqlite_stat1 VALUES('t2','t2x13','39667 13223');
INSERT INTO sqlite_stat1 VALUES('t2','t2x12','39667 7');
INSERT INTO sqlite_stat1 VALUES('t2','t2x11','39667 17');
INSERT INTO sqlite_stat1 VALUES('t2','t2x10','39667 19834');
INSERT INTO sqlite_stat1 VALUES('t2','t2x9','39667 7934');
INSERT INTO sqlite_stat1 VALUES('t2','t2x8','39667 11');
INSERT INTO sqlite_stat1 VALUES('t2','t2x7','39667 5');
INSERT INTO sqlite_stat1 VALUES('t2','t2x6','39667 242');
INSERT INTO sqlite_stat1 VALUES('t2','t2x5','39667 1984');
INSERT INTO sqlite_stat1 VALUES('t2','t2x4','39667 4408');
INSERT INTO sqlite_stat1 VALUES('t2','t2x3','39667 81');
INSERT INTO sqlite_stat1 VALUES('t2','t2x2','39667 551');
INSERT INTO sqlite_stat1 VALUES('t2','t2x1','39667 2');
INSERT INTO sqlite_stat1 VALUES('t2','t2x0','39667 1');
INSERT INTO sqlite_stat1 VALUES('t3','t3x6','569 285');
INSERT INTO sqlite_stat1 VALUES('t3','t3x5','569 2');
INSERT INTO sqlite_stat1 VALUES('t3','t3x4','569 2');
INSERT INTO sqlite_stat1 VALUES('t3','t3x3','569 5');
INSERT INTO sqlite_stat1 VALUES('t3','t3x2','569 3');
INSERT INTO sqlite_stat1 VALUES('t3','t3x1','569 6');
INSERT INTO sqlite_stat1 VALUES('t3','t3x0','569 1');
ANALYZE sqlite_master;
} {}
do_execsql_test autoindex2-120 {
EXPLAIN QUERY PLAN
SELECT
t1_id,
t1.did,
param2,
param3,
t1.ptime,
t1.trange,
t1.exmass,
t1.mass,
t1.vstatus,
type,
subtype,
t1.deviation,
t1.formula,
dparam1,
reserve1,
reserve2,
param4,
t1.last_operation,
t1.admin_uuid,
t1.previous_value,
t1.job_id,
client_did,
t1.last_t1,
t1.data_t1,
t1.previous_date,
param5,
param6,
mgr_uuid
FROM
t1,
t2,
t3
WHERE
t1.ptime > 1393520400
AND param3<>9001
AND t3.flg7 = 1
AND t1.did = t2.did
AND t2.uid = t3.uid
ORDER BY t1.ptime desc LIMIT 500;
} {~/AUTO/}
#
# ^^^--- Before being fixed, the above was using an automatic covering
# on t3 and reordering the tables so that t3 was in the outer loop and
# implementing the ORDER BY clause using a B-Tree.
#
# This test is sanitized data received from a user. The original unsanitized
# data and STAT4 data is found in the th3private test repository. See one of
# the th3private check-ins on 2016-02-25. The test is much more accurate when
# STAT4 data is used.
finish_test

93
testdata/tcl/autoindex3.test vendored Normal file
View file

@ -0,0 +1,93 @@
# 2014-06-17
#
# 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 script is testing automatic index creation logic,
# and specifically that an automatic index will not be created that
# shadows a declared index.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix autoindex3
# The t1b and t2d indexes are not very selective. It used to be that
# the autoindex mechanism would create automatic indexes on t1(b) or
# t2(d), make assumptions that they were reasonably selective, and use
# them instead of t1b or t2d. But that would be cheating, because the
# automatic index cannot be any more selective than the real index.
#
# This test verifies that the cheat is no longer allowed.
#
do_execsql_test autoindex3-100 {
CREATE TABLE t1(a,b,x);
CREATE TABLE t2(c,d,y);
CREATE INDEX t1b ON t1(b);
CREATE INDEX t2d ON t2(d);
ANALYZE sqlite_master;
INSERT INTO sqlite_stat1 VALUES('t1','t1b','10000 500');
INSERT INTO sqlite_stat1 VALUES('t2','t2d','10000 500');
ANALYZE sqlite_master;
EXPLAIN QUERY PLAN SELECT * FROM t1, t2 WHERE d=b;
} {~/AUTO/}
# Automatic indexes can still be used if existing indexes do not
# participate in == constraints.
#
do_execsql_test autoindex3-110 {
EXPLAIN QUERY PLAN SELECT * FROM t1, t2 WHERE d>b AND x=y;
} {/AUTO/}
do_execsql_test autoindex3-120 {
EXPLAIN QUERY PLAN SELECT * FROM t1, t2 WHERE d<b AND x=y;
} {/AUTO/}
do_execsql_test autoindex3-130 {
EXPLAIN QUERY PLAN SELECT * FROM t1, t2 WHERE d IS NULL AND x=y;
} {/AUTO/}
do_execsql_test autoindex3-140 {
EXPLAIN QUERY PLAN SELECT * FROM t1, t2 WHERE d IN (5,b) AND x=y;
} {/AUTO/}
reset_db
do_execsql_test 210 {
CREATE TABLE v(b, d, e);
CREATE TABLE u(a, b, c);
ANALYZE sqlite_master;
INSERT INTO "sqlite_stat1" VALUES('u','uab','40000 400 1');
INSERT INTO "sqlite_stat1" VALUES('v','vbde','40000 400 1 1');
INSERT INTO "sqlite_stat1" VALUES('v','ve','40000 21');
CREATE INDEX uab on u(a, b);
CREATE INDEX ve on v(e);
CREATE INDEX vbde on v(b,d,e);
DROP TABLE IF EXISTS sqlite_stat4;
ANALYZE sqlite_master;
}
# At one point, SQLite was using the inferior plan:
#
# 0|0|1|SEARCH v USING INDEX ve (e>?)
# 0|1|0|SEARCH u USING COVERING INDEX uab (ANY(a) AND b=?)
#
# on the basis that the real index "uab" must be better than the automatic
# index. This is not right - a skip-scan is not necessarily better than an
# automatic index scan.
#
do_eqp_test 220 {
select count(*) from u, v where u.b = v.b and v.e > 34;
} {
QUERY PLAN
|--SEARCH v USING INDEX ve (e>?)
`--SEARCH u USING AUTOMATIC COVERING INDEX (b=?)
}
finish_test

201
testdata/tcl/autoindex4.test vendored Normal file
View file

@ -0,0 +1,201 @@
# 2014-10-24
#
# 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 script is testing automatic index creation logic,
# and specifically creation of automatic partial indexes.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
do_execsql_test autoindex4-1.0 {
CREATE TABLE t1(a,b);
INSERT INTO t1 VALUES(123,'abc'),(234,'def'),(234,'ghi'),(345,'jkl');
CREATE TABLE t2(x,y);
INSERT INTO t2 VALUES(987,'zyx'),(654,'wvu'),(987,'rqp');
SELECT *, '|' FROM t1, t2 WHERE a=234 AND x=987 ORDER BY +b;
} {234 def 987 rqp | 234 def 987 zyx | 234 ghi 987 rqp | 234 ghi 987 zyx |}
do_execsql_test autoindex4-1.1 {
SELECT *, '|' FROM t1, t2 WHERE a=234 AND x=555;
} {}
do_execsql_test autoindex4-1.2 {
SELECT *, '|' FROM t1 LEFT JOIN t2 ON a=234 AND x=555;
} {123 abc {} {} | 234 def {} {} | 234 ghi {} {} | 345 jkl {} {} |}
do_execsql_test autoindex4-1.2-rj {
SELECT t1.*, t2.*, '|' FROM t2 RIGHT JOIN t1 ON a=234 AND x=555;
} {123 abc {} {} | 234 def {} {} | 234 ghi {} {} | 345 jkl {} {} |}
do_execsql_test autoindex4-1.3 {
SELECT *, '|' FROM t1 LEFT JOIN t2 ON x=555 WHERE a=234;
} {234 def {} {} | 234 ghi {} {} |}
do_execsql_test autoindex4-1.3-rj {
SELECT t1.*, t2.*, '|' FROM t2 RIGHT JOIN t1 ON x=555 WHERE a=234;
} {234 def {} {} | 234 ghi {} {} |}
do_execsql_test autoindex4-1.4 {
SELECT *, '|' FROM t1 LEFT JOIN t2 WHERE a=234 AND x=555;
} {}
do_execsql_test autoindex4-1.4-rj {
SELECT t1.*, t2.*, '|' FROM t2 RIGHT JOIN t1 WHERE a=234 AND x=555;
} {}
do_execsql_test autoindex4-2.0 {
CREATE TABLE t3(e,f);
INSERT INTO t3 VALUES(123,654),(555,444),(234,987);
SELECT (SELECT count(*) FROM t1, t2 WHERE a=e AND x=f), e, f, '|'
FROM t3
ORDER BY rowid;
} {1 123 654 | 0 555 444 | 4 234 987 |}
# Ticket [2326c258d02ead33d]
# Two joins, one with and the other without an ORDER BY clause.
# The one without ORDER BY correctly returns two rows of result.
# The one with ORDER BY returns no rows.
#
do_execsql_test autoindex4-3.0 {
CREATE TABLE A(Name text);
CREATE TABLE Items(ItemName text , Name text);
INSERT INTO Items VALUES('Item1','Parent');
INSERT INTO Items VALUES('Item2','Parent');
CREATE TABLE B(Name text);
SELECT Items.ItemName
FROM Items
LEFT JOIN A ON (A.Name = Items.ItemName and Items.ItemName = 'dummy')
LEFT JOIN B ON (B.Name = Items.ItemName)
WHERE Items.Name = 'Parent'
ORDER BY Items.ItemName;
} {Item1 Item2}
do_execsql_test autoindex4-3.1 {
SELECT Items.ItemName
FROM A
RIGHT JOIN Items ON (A.Name = Items.ItemName and Items.ItemName = 'dummy')
LEFT JOIN B ON (B.Name = Items.ItemName)
WHERE Items.Name = 'Parent'
ORDER BY Items.ItemName;
} {Item1 Item2}
do_execsql_test autoindex4-3.10 {
CREATE INDEX Items_x1 ON Items(ItemName,Name) WHERE ItemName = 'dummy';
SELECT Items.ItemName
FROM Items
LEFT JOIN A ON (A.Name = Items.ItemName and Items.ItemName = 'dummy')
LEFT JOIN B ON (B.Name = Items.ItemName)
WHERE Items.Name = 'Parent'
ORDER BY Items.ItemName;
} {Item1 Item2}
do_execsql_test autoindex4-3.11 {
SELECT Items.ItemName
FROM A
RIGHT JOIN Items ON (A.Name = Items.ItemName and Items.ItemName = 'dummy')
LEFT JOIN B ON (B.Name = Items.ItemName)
WHERE Items.Name = 'Parent'
ORDER BY Items.ItemName;
} {Item1 Item2}
# 2021-11-30 - Enhancement to help the automatic index mechanism to
# create a partial index more often.
#
unset -nocomplain id data1 data2 jointype onclause whereclause answer
foreach {id data1 data2 jointype onclause whereclause answer} {
1
VALUES(1,2),(3,4)
VALUES(1,2),(3,4)
{LEFT JOIN}
a=x
{y=4 OR y IS NULL}
{3 4 3 4}
2
VALUES(1,2),(3,4)
VALUES(1,2),(3,4)
{LEFT JOIN}
{a=x AND y=4}
{coalesce(y,4)==4}
{1 2 {} {} 3 4 3 4}
3
VALUES(1,2),(3,4)
VALUES(1,2),(3,4)
{JOIN}
{a=x}
{y=4 OR y IS NULL}
{3 4 3 4}
4
VALUES(1,2),(3,4)
VALUES(1,2),(3,4)
{JOIN}
{a=x AND y=4}
{coalesce(y,4)==4}
{3 4 3 4}
5
VALUES(1,2),(3,4),(NULL,4)
VALUES(1,2),(3,4)
{LEFT JOIN}
a=x
{y=4 OR y IS NULL}
{3 4 3 4 {} 4 {} {}}
6
VALUES(1,2),(3,4)
VALUES(1,2),(3,4),(NULL,4)
{LEFT JOIN}
{a=x AND y=4}
{coalesce(y,4)==4}
{1 2 {} {} 3 4 3 4}
7
VALUES(1,2),(3,4),(NULL,4)
VALUES(1,2),(3,4),(NULL,4)
{JOIN}
{a=x}
{y=4 OR y IS NULL}
{3 4 3 4}
8
VALUES(1,2),(3,4)
VALUES(1,2),(3,4)
{JOIN}
{a=x AND y=4}
{coalesce(y,4)==4}
{3 4 3 4}
} {
do_test autoindex4-4.$id.0 {
db eval {
DROP TABLE IF EXISTS t1;
CREATE TABLE t1(a INT, b INT);
DROP TABLE IF EXISTS t2;
CREATE TABLE t2(x INT, y INT);
}
db eval "INSERT INTO t1(a,b) $data1;"
db eval "INSERT INTO t2(x,y) $data2;"
} {}
set sql "SELECT * FROM t1 $jointype t2 ON $onclause WHERE $whereclause"
# puts "sql = $sql"
do_test autoindex4-4.$id.1 {
db eval {PRAGMA automatic_index=ON;}
db eval $sql
} $answer
do_test autoindex4-4.$id.2 {
db eval {PRAGMA automatic_index=OFF;}
db eval $sql
} $answer
}
finish_test

234
testdata/tcl/autoindex5.test vendored Normal file
View file

@ -0,0 +1,234 @@
# 2014-10-24
#
# 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 script is testing automatic index creation logic,
# and specifically ensuring that automatic indexes can be used with
# co-routine subqueries.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix autoindex5
# Schema is from the Debian security database
#
do_execsql_test autoindex5-1.0 {
CREATE TABLE source_package_status
(bug_name TEXT NOT NULL,
package INTEGER NOT NULL,
vulnerable INTEGER NOT NULL,
urgency TEXT NOT NULL,
PRIMARY KEY (bug_name, package));
CREATE INDEX source_package_status_package
ON source_package_status(package);
CREATE TABLE source_packages
(name TEXT NOT NULL,
release TEXT NOT NULL,
subrelease TEXT NOT NULL,
archive TEXT NOT NULL,
version TEXT NOT NULL,
version_id INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY (name, release, subrelease, archive));
CREATE TABLE bugs
(name TEXT NOT NULL PRIMARY KEY,
cve_status TEXT NOT NULL
CHECK (cve_status IN
('', 'CANDIDATE', 'ASSIGNED', 'RESERVED', 'REJECTED')),
not_for_us INTEGER NOT NULL CHECK (not_for_us IN (0, 1)),
description TEXT NOT NULL,
release_date TEXT NOT NULL,
source_file TEXT NOT NULL,
source_line INTEGER NOT NULL);
CREATE TABLE package_notes
(id INTEGER NOT NULL PRIMARY KEY,
bug_name TEXT NOT NULL,
package TEXT NOT NULL,
fixed_version TEXT
CHECK (fixed_version IS NULL OR fixed_version <> ''),
fixed_version_id INTEGER NOT NULL DEFAULT 0,
release TEXT NOT NULL,
package_kind TEXT NOT NULL DEFAULT 'unknown',
urgency TEXT NOT NULL,
bug_origin TEXT NOT NULL DEFAULT '');
CREATE INDEX package_notes_package
ON package_notes(package);
CREATE UNIQUE INDEX package_notes_bug
ON package_notes(bug_name, package, release);
CREATE TABLE debian_bugs
(bug INTEGER NOT NULL,
note INTEGER NOT NULL,
PRIMARY KEY (bug, note));
CREATE VIEW debian_cve AS
SELECT DISTINCT debian_bugs.bug, st.bug_name
FROM package_notes, debian_bugs, source_package_status AS st
WHERE package_notes.bug_name = st.bug_name
AND debian_bugs.note = package_notes.id
ORDER BY debian_bugs.bug;
} {}
# The following query should use an automatic index for the view
# in FROM clause of the subquery of the second result column.
#
do_eqp_test autoindex5-1.1 {
SELECT
st.bug_name,
(SELECT ALL debian_cve.bug FROM debian_cve
WHERE debian_cve.bug_name = st.bug_name
ORDER BY debian_cve.bug),
sp.release
FROM
source_package_status AS st,
source_packages AS sp,
bugs
WHERE
sp.rowid = st.package
AND st.bug_name = bugs.name
AND ( st.bug_name LIKE 'CVE-%' OR st.bug_name LIKE 'TEMP-%' )
AND ( sp.release = 'sid' OR sp.release = 'stretch' OR sp.release = 'jessie'
OR sp.release = 'wheezy' OR sp.release = 'squeeze' )
ORDER BY sp.name, st.bug_name, sp.release, sp.subrelease;
} {SEARCH debian_cve USING AUTOMATIC COVERING INDEX (bug_name=?)}
#-------------------------------------------------------------------------
# Test that ticket [8a2adec1] has been fixed.
#
do_execsql_test 2.1 {
CREATE TABLE one(o);
INSERT INTO one DEFAULT VALUES;
CREATE TABLE t1(x, z);
INSERT INTO t1 VALUES('aaa', 4.0);
INSERT INTO t1 VALUES('aaa', 4.0);
CREATE VIEW vvv AS
SELECT * FROM t1
UNION ALL
SELECT 0, 0 WHERE 0;
SELECT (
SELECT sum(z) FROM vvv WHERE x='aaa'
) FROM one;
} {8.0}
# At one point the following was returning "no such column: rowid". This
# was incorrect - "rowid" matches against the rowid of table t1 in this
# query.
do_catchsql_test 2.2 {
DROP TABLE t1;
CREATE TABLE t1(aaa);
INSERT INTO t1(aaa) VALUES(9);
SELECT (
SELECT aaa FROM t1 GROUP BY (
SELECT bbb FROM (
SELECT ccc AS bbb FROM (
SELECT 1 ccc
) WHERE rowid IS NOT 1
) WHERE bbb = 1
)
);
} {0 9}
# Ticket https://www.sqlite.org/src/info/787fa716be3a7f65
# Segfault due to multiple uses of the same subquery where the
# subquery is implemented via coroutine.
#
ifcapable windowfunc {
sqlite3 db :memory:
do_execsql_test 3.0 {
-- This is the original test case reported on the mailing list
CREATE TABLE artists (
id integer NOT NULL PRIMARY KEY AUTOINCREMENT,
name varchar(255)
);
CREATE TABLE albums (
id integer NOT NULL PRIMARY KEY AUTOINCREMENT,
name varchar(255),
artist_id integer REFERENCES artists
);
INSERT INTO artists (name) VALUES ('Ar');
INSERT INTO albums (name, artist_id) VALUES ('Al', 1);
SELECT artists.*
FROM artists
INNER JOIN artists AS 'b' ON (b.id = artists.id)
WHERE (artists.id IN (
SELECT albums.artist_id
FROM albums
WHERE ((name = 'Al')
AND (albums.artist_id IS NOT NULL)
AND (albums.id IN (
SELECT id
FROM (
SELECT albums.id,
row_number() OVER (
PARTITION BY albums.artist_id
ORDER BY name
) AS 'x'
FROM albums
WHERE (name = 'Al')
) AS 't1'
WHERE (x = 1)
))
AND (albums.id IN (1, 2)))
));
} {1 Ar}
} ;# windowfunc
# The remaining test cases were discovered (by Dan) during trouble-shooting
sqlite3 db :memory:
do_execsql_test 3.1 {
CREATE TABLE t1 (a); INSERT INTO t1 (a) VALUES (104);
CREATE TABLE t2 (b); INSERT INTO t2 (b) VALUES (104);
CREATE TABLE t3 (c); INSERT INTO t3 (c) VALUES (104);
CREATE TABLE t4 (d); INSERT INTO t4 (d) VALUES (104);
SELECT *
FROM t1 CROSS JOIN t2 ON (t1.a = t2.b) WHERE t2.b IN (
SELECT t3.c
FROM t3
WHERE t3.c IN (
SELECT d FROM (SELECT DISTINCT d FROM t4) AS x WHERE x.d=104
)
);
} {104 104}
sqlite3 db :memory:
do_execsql_test 3.2 {
CREATE TABLE t5(a, b, c, d);
CREATE INDEX t5a ON t5(a);
CREATE INDEX t5b ON t5(b);
CREATE TABLE t6(e);
INSERT INTO t6 VALUES(1);
INSERT INTO t5 VALUES(1,1,1,1), (2,2,2,2);
SELECT * FROM t5 WHERE (a=1 OR b=2) AND c IN (
SELECT e FROM (SELECT DISTINCT e FROM t6) WHERE e=1
);
} {1 1 1 1}
sqlite3 db :memory:
do_execsql_test 3.3 {
CREATE TABLE t1(a1, a2, a3);
CREATE INDEX t1a2 ON t1(a2, a1);
CREATE INDEX t1a3 ON t1(a3, a1);
CREATE TABLE t2(d);
INSERT INTO t1 VALUES(3, 1, 1), (3, 2, 2);
INSERT INTO t2 VALUES(3);
SELECT *, 'x' FROM t1 WHERE (a2=1 OR a3=2) AND a1 = (
SELECT d FROM (SELECT DISTINCT d FROM t2) WHERE d=3
);
} {3 1 1 x 3 2 2 x}
finish_test

715
testdata/tcl/autovacuum.test vendored Normal file
View file

@ -0,0 +1,715 @@
# 2001 September 15
#
# 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 autovacuum feature.
#
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
}
# Return a string $len characters long. The returned string is $char repeated
# over and over. For example, [make_str abc 8] returns "abcabcab".
proc make_str {char len} {
set str [string repeat $char. $len]
return [string range $str 0 [expr $len-1]]
}
# Return the number of pages in the file test.db by looking at the file system.
proc file_pages {} {
return [expr [file size test.db] / 1024]
}
#-------------------------------------------------------------------------
# Test cases autovacuum-1.* work as follows:
#
# 1. A table with a single indexed field is created.
# 2. Approximately 20 rows are inserted into the table. Each row is long
# enough such that it uses at least 2 overflow pages for both the table
# and index entry.
# 3. The rows are deleted in a psuedo-random order. Sometimes only one row
# is deleted per transaction, sometimes more than one.
# 4. After each transaction the table data is checked to ensure it is correct
# and a "PRAGMA integrity_check" is executed.
# 5. Once all the rows are deleted the file is checked to make sure it
# consists of exactly 4 pages.
#
# Steps 2-5 are repeated for a few different psuedo-random delete patterns
# (defined by the $delete_orders list).
set delete_orders [list]
lappend delete_orders {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}
lappend delete_orders {20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1}
lappend delete_orders {8 18 2 4 14 11 13 3 10 7 9 5 12 17 19 15 20 6 16 1}
lappend delete_orders {10 3 11 17 19 20 7 4 13 6 1 14 16 12 9 18 8 15 5 2}
lappend delete_orders {{1 2 3 4 5 6 7 8 9 10} {11 12 13 14 15 16 17 18 19 20}}
lappend delete_orders {{19 8 17 15} {16 11 9 14} {18 5 3 1} {13 20 7 2} {6 12}}
# The length of each table entry.
# set ENTRY_LEN 3500
set ENTRY_LEN 3500
do_test autovacuum-1.1 {
execsql {
PRAGMA auto_vacuum = 1;
CREATE TABLE av1(a);
CREATE INDEX av1_idx ON av1(a);
}
} {}
set tn 0
foreach delete_order $delete_orders {
incr tn
# Set up the table.
set ::tbl_data [list]
foreach i [lsort -integer [eval concat $delete_order]] {
execsql "INSERT INTO av1 (oid, a) VALUES($i, '[make_str $i $ENTRY_LEN]')"
lappend ::tbl_data [make_str $i $ENTRY_LEN]
}
# Make sure the integrity check passes with the initial data.
ifcapable {integrityck} {
do_test autovacuum-1.$tn.1 {
execsql {
pragma integrity_check
}
} {ok}
}
foreach delete $delete_order {
# Delete one set of rows from the table.
do_test autovacuum-1.$tn.($delete).1 {
execsql "
DELETE FROM av1 WHERE oid = [join $delete " OR oid = "]
"
} {}
# Do the integrity check.
ifcapable {integrityck} {
do_test autovacuum-1.$tn.($delete).2 {
execsql {
pragma integrity_check
}
} {ok}
}
# Ensure the data remaining in the table is what was expected.
foreach d $delete {
set idx [lsearch $::tbl_data [make_str $d $ENTRY_LEN]]
set ::tbl_data [lreplace $::tbl_data $idx $idx]
}
do_test autovacuum-1.$tn.($delete).3 {
execsql {
select a from av1 order by rowid
}
} $::tbl_data
}
# All rows have been deleted. Ensure the file has shrunk to 4 pages.
do_test autovacuum-1.$tn.3 {
file_pages
} {4}
}
#---------------------------------------------------------------------------
# Tests cases autovacuum-2.* test that root pages are allocated
# and deallocated correctly at the start of the file. Operation is roughly as
# follows:
#
# autovacuum-2.1.*: Drop the tables that currently exist in the database.
# autovacuum-2.2.*: Create some tables. Ensure that data pages can be
# moved correctly to make space for new root-pages.
# autovacuum-2.3.*: Drop one of the tables just created (not the last one),
# and check that one of the other tables is moved to
# the free root-page location.
# autovacuum-2.4.*: Check that a table can be created correctly when the
# root-page it requires is on the free-list.
# autovacuum-2.5.*: Check that a table with indices can be dropped. This
# is slightly tricky because dropping one of the
# indices/table btrees could move the root-page of another.
# The code-generation layer of SQLite overcomes this problem
# by dropping the btrees in descending order of root-pages.
# This test ensures that this actually happens.
#
do_test autovacuum-2.1.1 {
execsql {
DROP TABLE av1;
}
} {}
do_test autovacuum-2.1.2 {
file_pages
} {1}
# Create a table and put some data in it.
do_test autovacuum-2.2.1 {
execsql {
CREATE TABLE av1(x);
SELECT rootpage FROM sqlite_master ORDER BY rootpage;
}
} {3}
do_test autovacuum-2.2.2 {
execsql "
INSERT INTO av1 VALUES('[make_str abc 3000]');
INSERT INTO av1 VALUES('[make_str def 3000]');
INSERT INTO av1 VALUES('[make_str ghi 3000]');
INSERT INTO av1 VALUES('[make_str jkl 3000]');
"
set ::av1_data [db eval {select * from av1}]
file_pages
} {15}
# Create another table. Check it is located immediately after the first.
# This test case moves the second page in an over-flow chain.
do_test autovacuum-2.2.3 {
execsql {
CREATE TABLE av2(x);
SELECT rootpage FROM sqlite_master ORDER BY rootpage;
}
} {3 4}
do_test autovacuum-2.2.4 {
file_pages
} {16}
# Create another table. Check it is located immediately after the second.
# This test case moves the first page in an over-flow chain.
do_test autovacuum-2.2.5 {
execsql {
CREATE TABLE av3(x);
SELECT rootpage FROM sqlite_master ORDER BY rootpage;
}
} {3 4 5}
do_test autovacuum-2.2.6 {
file_pages
} {17}
# Create another table. Check it is located immediately after the second.
# This test case moves a btree leaf page.
do_test autovacuum-2.2.7 {
execsql {
CREATE TABLE av4(x);
SELECT rootpage FROM sqlite_master ORDER BY rootpage;
}
} {3 4 5 6}
do_test autovacuum-2.2.8 {
file_pages
} {18}
do_test autovacuum-2.2.9 {
execsql {
select * from av1
}
} $av1_data
do_test autovacuum-2.3.1 {
execsql {
INSERT INTO av2 SELECT 'av1' || x FROM av1;
INSERT INTO av3 SELECT 'av2' || x FROM av1;
INSERT INTO av4 SELECT 'av3' || x FROM av1;
}
set ::av2_data [execsql {select x from av2}]
set ::av3_data [execsql {select x from av3}]
set ::av4_data [execsql {select x from av4}]
file_pages
} {54}
do_test autovacuum-2.3.2 {
execsql {
DROP TABLE av2;
SELECT rootpage FROM sqlite_master ORDER BY rootpage;
}
} {3 4 5}
do_test autovacuum-2.3.3 {
file_pages
} {41}
do_test autovacuum-2.3.4 {
execsql {
SELECT x FROM av3;
}
} $::av3_data
do_test autovacuum-2.3.5 {
execsql {
SELECT x FROM av4;
}
} $::av4_data
# Drop all the tables in the file. This puts all pages except the first 2
# (the sqlite_master root-page and the first pointer map page) on the
# free-list.
do_test autovacuum-2.4.1 {
execsql {
DROP TABLE av1;
DROP TABLE av3;
BEGIN;
DROP TABLE av4;
}
file_pages
} {15}
do_test autovacuum-2.4.2 {
for {set i 3} {$i<=10} {incr i} {
execsql "CREATE TABLE av$i (x)"
}
file_pages
} {15}
do_test autovacuum-2.4.3 {
execsql {
SELECT rootpage FROM sqlite_master ORDER by rootpage
}
} {3 4 5 6 7 8 9 10}
# Right now there are 5 free pages in the database. Consume and then free
# all 520 pages. Then create 520 tables. This ensures that at least some of the
# desired root-pages reside on the second free-list trunk page, and that the
# trunk itself is required at some point.
do_test autovacuum-2.4.4 {
execsql "
INSERT INTO av3 VALUES ('[make_str abcde [expr 1020*520 + 500]]');
DELETE FROM av3;
"
} {}
set root_page_list [list]
set pending_byte_page [expr ($::sqlite_pending_byte / 1024) + 1]
# unusable_pages
# These are either the pending_byte page or the pointer map pages
#
unset -nocomplain unusable_page
if {[sqlite3 -has-codec]} {
array set unusable_page {205 1 408 1}
} else {
array set unusable_page {207 1 412 1}
}
set unusable_page($pending_byte_page) 1
for {set i 3} {$i<=532} {incr i} {
if {![info exists unusable_page($i)]} {
lappend root_page_list $i
}
}
if {$i >= $pending_byte_page} {
lappend root_page_list $i
}
do_test autovacuum-2.4.5 {
for {set i 11} {$i<=530} {incr i} {
execsql "CREATE TABLE av$i (x)"
}
execsql {
SELECT rootpage FROM sqlite_master ORDER by rootpage
}
} $root_page_list
# Just for fun, delete all those tables and see if the database is 1 page.
do_test autovacuum-2.4.6 {
execsql COMMIT;
file_pages
} [expr 561 + (($i >= $pending_byte_page)?1:0)]
integrity_check autovacuum-2.4.6
do_test autovacuum-2.4.7 {
execsql BEGIN
for {set i 3} {$i<=530} {incr i} {
execsql "DROP TABLE av$i"
}
execsql COMMIT
file_pages
} 1
# Create some tables with indices to drop.
do_test autovacuum-2.5.1 {
execsql {
CREATE TABLE av1(a PRIMARY KEY, b, c);
INSERT INTO av1 VALUES('av1 a', 'av1 b', 'av1 c');
CREATE TABLE av2(a PRIMARY KEY, b, c);
CREATE INDEX av2_i1 ON av2(b);
CREATE INDEX av2_i2 ON av2(c);
INSERT INTO av2 VALUES('av2 a', 'av2 b', 'av2 c');
CREATE TABLE av3(a PRIMARY KEY, b, c);
CREATE INDEX av3_i1 ON av3(b);
INSERT INTO av3 VALUES('av3 a', 'av3 b', 'av3 c');
CREATE TABLE av4(a, b, c);
CREATE INDEX av4_i1 ON av4(a);
CREATE INDEX av4_i2 ON av4(b);
CREATE INDEX av4_i3 ON av4(c);
CREATE INDEX av4_i4 ON av4(a, b, c);
INSERT INTO av4 VALUES('av4 a', 'av4 b', 'av4 c');
}
} {}
do_test autovacuum-2.5.2 {
execsql {
SELECT name, rootpage FROM sqlite_master;
}
} [list av1 3 sqlite_autoindex_av1_1 4 \
av2 5 sqlite_autoindex_av2_1 6 av2_i1 7 av2_i2 8 \
av3 9 sqlite_autoindex_av3_1 10 av3_i1 11 \
av4 12 av4_i1 13 av4_i2 14 av4_i3 15 av4_i4 16 \
]
# The following 4 tests are SELECT queries that use the indices created.
# If the root-pages in the internal schema are not updated correctly when
# a table or indice is moved, these queries will fail. They are repeated
# after each table is dropped (i.e. as test cases 2.5.*.[1..4]).
do_test autovacuum-2.5.2.1 {
execsql {
SELECT * FROM av1 WHERE a = 'av1 a';
}
} {{av1 a} {av1 b} {av1 c}}
do_test autovacuum-2.5.2.2 {
execsql {
SELECT * FROM av2 WHERE a = 'av2 a' AND b = 'av2 b' AND c = 'av2 c'
}
} {{av2 a} {av2 b} {av2 c}}
do_test autovacuum-2.5.2.3 {
execsql {
SELECT * FROM av3 WHERE a = 'av3 a' AND b = 'av3 b';
}
} {{av3 a} {av3 b} {av3 c}}
do_test autovacuum-2.5.2.4 {
execsql {
SELECT * FROM av4 WHERE a = 'av4 a' AND b = 'av4 b' AND c = 'av4 c';
}
} {{av4 a} {av4 b} {av4 c}}
# Drop table av3. Indices av4_i2, av4_i3 and av4_i4 are moved to fill the two
# root pages vacated. The operation proceeds as:
# Step 1: Delete av3_i1 (root-page 11). Move root-page of av4_i4 to page 11.
# Step 2: Delete av3 (root-page 10). Move root-page of av4_i3 to page 10.
# Step 3: Delete sqlite_autoindex_av1_3 (root-page 9). Move av4_i2 to page 9.
do_test autovacuum-2.5.3 {
execsql {
DROP TABLE av3;
SELECT name, rootpage FROM sqlite_master;
}
} [list av1 3 sqlite_autoindex_av1_1 4 \
av2 5 sqlite_autoindex_av2_1 6 av2_i1 7 av2_i2 8 \
av4 12 av4_i1 13 av4_i2 9 av4_i3 10 av4_i4 11 \
]
do_test autovacuum-2.5.3.1 {
execsql {
SELECT * FROM av1 WHERE a = 'av1 a';
}
} {{av1 a} {av1 b} {av1 c}}
do_test autovacuum-2.5.3.2 {
execsql {
SELECT * FROM av2 WHERE a = 'av2 a' AND b = 'av2 b' AND c = 'av2 c'
}
} {{av2 a} {av2 b} {av2 c}}
do_test autovacuum-2.5.3.3 {
execsql {
SELECT * FROM av4 WHERE a = 'av4 a' AND b = 'av4 b' AND c = 'av4 c';
}
} {{av4 a} {av4 b} {av4 c}}
# Drop table av1:
# Step 1: Delete av1 (root page 4). Root-page of av4_i1 fills the gap.
# Step 2: Delete sqlite_autoindex_av1_1 (root page 3). Move av4 to the gap.
do_test autovacuum-2.5.4 {
execsql {
DROP TABLE av1;
SELECT name, rootpage FROM sqlite_master;
}
} [list av2 5 sqlite_autoindex_av2_1 6 av2_i1 7 av2_i2 8 \
av4 3 av4_i1 4 av4_i2 9 av4_i3 10 av4_i4 11 \
]
do_test autovacuum-2.5.4.2 {
execsql {
SELECT * FROM av2 WHERE a = 'av2 a' AND b = 'av2 b' AND c = 'av2 c'
}
} {{av2 a} {av2 b} {av2 c}}
do_test autovacuum-2.5.4.4 {
execsql {
SELECT * FROM av4 WHERE a = 'av4 a' AND b = 'av4 b' AND c = 'av4 c';
}
} {{av4 a} {av4 b} {av4 c}}
# Drop table av4:
# Step 1: Delete av4_i4.
# Step 2: Delete av4_i3.
# Step 3: Delete av4_i2.
# Step 4: Delete av4_i1. av2_i2 replaces it.
# Step 5: Delete av4. av2_i1 replaces it.
do_test autovacuum-2.5.5 {
execsql {
DROP TABLE av4;
SELECT name, rootpage FROM sqlite_master;
}
} [list av2 5 sqlite_autoindex_av2_1 6 av2_i1 3 av2_i2 4]
do_test autovacuum-2.5.5.2 {
execsql {
SELECT * FROM av2 WHERE a = 'av2 a' AND b = 'av2 b' AND c = 'av2 c'
}
} {{av2 a} {av2 b} {av2 c}}
#--------------------------------------------------------------------------
# Test cases autovacuum-3.* test the operation of the "PRAGMA auto_vacuum"
# command.
#
do_test autovacuum-3.1 {
execsql {
PRAGMA auto_vacuum;
}
} {1}
do_test autovacuum-3.2 {
db close
sqlite3 db test.db
execsql {
PRAGMA auto_vacuum;
}
} {1}
do_test autovacuum-3.3 {
execsql {
PRAGMA auto_vacuum = 0;
PRAGMA auto_vacuum;
}
} {1}
do_test autovacuum-3.4 {
db close
forcedelete test.db
sqlite3 db test.db
execsql {
PRAGMA auto_vacuum;
}
} $AUTOVACUUM
do_test autovacuum-3.5 {
execsql {
CREATE TABLE av1(x);
PRAGMA auto_vacuum;
}
} $AUTOVACUUM
do_test autovacuum-3.6 {
execsql {
PRAGMA auto_vacuum = 1;
PRAGMA auto_vacuum;
}
} [expr $AUTOVACUUM ? 1 : 0]
do_test autovacuum-3.7 {
execsql {
DROP TABLE av1;
}
file_pages
} [expr $AUTOVACUUM?1:2]
#-----------------------------------------------------------------------
# Test that if a statement transaction around a CREATE INDEX statement is
# rolled back no corruption occurs.
#
do_test autovacuum-4.0 {
# The last round of tests may have left the db in non-autovacuum mode.
# Reset everything just in case.
#
db close
forcedelete test.db test.db-journal
sqlite3 db test.db
execsql {
PRAGMA auto_vacuum = 1;
PRAGMA auto_vacuum;
}
} {1}
do_test autovacuum-4.1 {
execsql {
CREATE TABLE av1(a, b);
BEGIN;
}
for {set i 0} {$i<100} {incr i} {
execsql "INSERT INTO av1 VALUES($i, '[string repeat X 200]');"
}
execsql "INSERT INTO av1 VALUES(99, '[string repeat X 200]');"
execsql {
SELECT sum(a) FROM av1;
}
} {5049}
do_test autovacuum-4.2 {
catchsql {
CREATE UNIQUE INDEX av1_i ON av1(a);
}
} {1 {UNIQUE constraint failed: av1.a}}
do_test autovacuum-4.3 {
execsql {
SELECT sum(a) FROM av1;
}
} {5049}
do_test autovacuum-4.4 {
execsql {
COMMIT;
}
} {}
ifcapable integrityck {
# Ticket #1727
do_test autovacuum-5.1 {
db close
sqlite3 db :memory:
db eval {
PRAGMA auto_vacuum=1;
CREATE TABLE t1(a);
CREATE TABLE t2(a);
DROP TABLE t1;
PRAGMA integrity_check;
}
} ok
}
# Ticket #1728.
#
# In autovacuum mode, when tables or indices are deleted, the rootpage
# values in the symbol table have to be updated. There was a bug in this
# logic so that if an index/table was moved twice, the second move might
# not occur. This would leave the internal symbol table in an inconsistent
# state causing subsequent statements to fail.
#
# The problem is difficult to reproduce. The sequence of statements in
# the following test are carefully designed make it occur and thus to
# verify that this very obscure bug has been resolved.
#
ifcapable integrityck&&memorydb {
do_test autovacuum-6.1 {
db close
sqlite3 db :memory:
db eval {
PRAGMA auto_vacuum=1;
CREATE TABLE t1(a, b);
CREATE INDEX i1 ON t1(a);
CREATE TABLE t2(a);
CREATE INDEX i2 ON t2(a);
CREATE TABLE t3(a);
CREATE INDEX i3 ON t2(a);
CREATE INDEX x ON t1(b);
DROP TABLE t3;
PRAGMA integrity_check;
DROP TABLE t2;
PRAGMA integrity_check;
DROP TABLE t1;
PRAGMA integrity_check;
}
} {ok ok ok}
}
#---------------------------------------------------------------------
# Test cases autovacuum-7.X test the case where a page must be moved
# and the destination location collides with at least one other
# entry in the page hash-table (internal to the pager.c module.
#
do_test autovacuum-7.1 {
db close
forcedelete test.db
forcedelete test.db-journal
sqlite3 db test.db
execsql {
PRAGMA auto_vacuum=1;
CREATE TABLE t1(a, b, PRIMARY KEY(a, b));
INSERT INTO t1 VALUES(randstr(400,400),randstr(400,400));
INSERT INTO t1 SELECT randstr(400,400), randstr(400,400) FROM t1; -- 2
INSERT INTO t1 SELECT randstr(400,400), randstr(400,400) FROM t1; -- 4
INSERT INTO t1 SELECT randstr(400,400), randstr(400,400) FROM t1; -- 8
INSERT INTO t1 SELECT randstr(400,400), randstr(400,400) FROM t1; -- 16
INSERT INTO t1 SELECT randstr(400,400), randstr(400,400) FROM t1; -- 32
}
expr {[file size test.db] / 1024}
} {73}
do_test autovacuum-7.2 {
execsql {
CREATE TABLE t2(a, b, PRIMARY KEY(a, b));
INSERT INTO t2 SELECT randstr(400,400), randstr(400,400) FROM t1; -- 2
CREATE TABLE t3(a, b, PRIMARY KEY(a, b));
INSERT INTO t3 SELECT randstr(400,400), randstr(400,400) FROM t1; -- 2
CREATE TABLE t4(a, b, PRIMARY KEY(a, b));
INSERT INTO t4 SELECT randstr(400,400), randstr(400,400) FROM t1; -- 2
CREATE TABLE t5(a, b, PRIMARY KEY(a, b));
INSERT INTO t5 SELECT randstr(400,400), randstr(400,400) FROM t1; -- 2
}
expr {[file size test.db] / 1024}
} {354}
do_test autovacuum-7.3 {
db close
sqlite3 db test.db
execsql {
BEGIN;
DELETE FROM t4;
COMMIT;
SELECT count(*) FROM t1;
}
expr {[file size test.db] / 1024}
} {286}
#------------------------------------------------------------------------
# Additional tests.
#
# Try to determine the autovacuum setting for a database that is locked.
#
do_test autovacuum-8.1 {
db close
sqlite3 db test.db
sqlite3 db2 test.db
db eval {PRAGMA auto_vacuum}
} {1}
if {[permutation] == ""} {
do_test autovacuum-8.2 {
db eval {BEGIN EXCLUSIVE}
catchsql {PRAGMA auto_vacuum} db2
} {1 {database is locked}}
catch {db2 close}
catch {db eval {COMMIT}}
}
do_test autovacuum-9.1 {
execsql {
DROP TABLE t1;
DROP TABLE t2;
DROP TABLE t3;
DROP TABLE t4;
DROP TABLE t5;
PRAGMA page_count;
}
} {1}
do_test autovacuum-9.2 {
file size test.db
} 1024
do_test autovacuum-9.3 {
execsql {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
INSERT INTO t1 VALUES(NULL, randstr(50,50));
}
for {set ii 0} {$ii < 10} {incr ii} {
db eval { INSERT INTO t1 SELECT NULL, randstr(50,50) FROM t1 }
}
file size test.db
} $::sqlite_pending_byte
do_test autovacuum-9.4 {
execsql { INSERT INTO t1 SELECT NULL, randstr(50,50) FROM t1 }
} {}
do_test autovacuum-9.5 {
execsql { DELETE FROM t1 WHERE rowid > (SELECT max(a)/2 FROM t1) }
file size test.db
} $::sqlite_pending_byte
do_execsql_test autovacuum-10.1 {
DROP TABLE t1;
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
INSERT INTO t1 VALUES(25, randomblob(104));
REPLACE INTO t1 VALUES(25, randomblob(1117));
PRAGMA integrity_check;
} {ok}
finish_test

87
testdata/tcl/autovacuum2.test vendored Normal file
View file

@ -0,0 +1,87 @@
# 2021-10-15
#
# 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 sqlite3_autovacuum_pages() interface
#
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
}
# Demonstrate basic sqlite3_autovacuum_pages functionality
#
do_execsql_test autovacuum2-1.0 {
PRAGMA page_size=1024;
PRAGMA auto_vacuum=FULL;
CREATE TABLE t1(x);
VACUUM;
INSERT INTO t1(x) VALUES(zeroblob(10000));
PRAGMA page_count;
} {12}
proc autovac_page_callback {schema filesize freesize pagesize} {
global autovac_callback_data
lappend autovac_callback_data $schema $filesize $freesize $pagesize
return [expr {$freesize/2}]
}
sqlite3_autovacuum_pages db autovac_page_callback
set autovac_callback_data {}
do_execsql_test autovacuum2-1.1 {
BEGIN;
DELETE FROM t1;
PRAGMA freelist_count;
PRAGMA page_count;
} {9 12}
do_execsql_test autovacuum2-1.2 {
COMMIT;
} {}
do_test autovacuum2-1.3 {
set autovac_callback_data
} {main 12 9 1024}
do_execsql_test autovacuum2-1.4 {
PRAGMA freelist_count;
PRAGMA page_count;
} {5 8}
do_execsql_test autovacuum2-1.5 {
PRAGMA integrity_check;
} {ok}
# Disable the autovacuum-pages callback. Then do any transaction.
# The database should shrink to minimal size
#
sqlite3_autovacuum_pages db
do_execsql_test autovacuum2-1.10 {
CREATE TABLE t2(x);
PRAGMA freelist_count;
} {0}
# Rig the autovacuum-pages callback to always return zero. No
# autovacuum will happen.
#
proc autovac_page_callback_off {schema filesize freesize pagesize} {
return 0
}
sqlite3_autovacuum_pages db autovac_page_callback_off
do_execsql_test autovacuum2-1.20 {
BEGIN;
INSERT INTO t1(x) VALUES(zeroblob(10000));
DELETE FROM t1;
PRAGMA freelist_count;
COMMIT;
PRAGMA freelist_count;
} {9 9}
finish_test

132
testdata/tcl/autovacuum_ioerr2.test vendored Normal file
View file

@ -0,0 +1,132 @@
# 2001 October 12
#
# 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 for correct handling of I/O errors
# such as writes failing because the disk is full.
#
# The tests in this file use special facilities that are only
# available in the SQLite test fixture.
#
# $Id: autovacuum_ioerr2.test,v 1.7 2008/07/12 14:52:20 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} {
finish_test
return
}
do_ioerr_test autovacuum-ioerr2-1 -sqlprep {
PRAGMA auto_vacuum = 1;
CREATE TABLE abc(a);
INSERT INTO abc VALUES(randstr(1500,1500));
} -sqlbody {
CREATE TABLE abc2(a);
BEGIN;
DELETE FROM abc;
INSERT INTO abc VALUES(randstr(1500,1500));
CREATE TABLE abc3(a);
COMMIT;
}
do_ioerr_test autovacuum-ioerr2-2 -tclprep {
execsql {
PRAGMA auto_vacuum = 1;
PRAGMA cache_size = 10;
BEGIN;
CREATE TABLE abc(a);
INSERT INTO abc VALUES(randstr(1100,1100)); -- Page 4 is overflow
INSERT INTO abc VALUES(randstr(1100,1100)); -- Page 5 is overflow
}
for {set i 0} {$i<150} {incr i} {
execsql {
INSERT INTO abc VALUES(randstr(100,100));
}
}
execsql COMMIT
} -sqlbody {
BEGIN;
DELETE FROM abc WHERE length(a)>100;
UPDATE abc SET a = randstr(90,90);
CREATE TABLE abc3(a);
COMMIT;
}
do_ioerr_test autovacuum-ioerr2-3 -sqlprep {
PRAGMA auto_vacuum = 1;
CREATE TABLE abc(a);
CREATE TABLE abc2(b);
} -sqlbody {
BEGIN;
INSERT INTO abc2 VALUES(10);
DROP TABLE abc;
COMMIT;
DROP TABLE abc2;
}
forcedelete backup.db
ifcapable subquery {
do_ioerr_test autovacuum-ioerr2-4 -tclprep {
if {![file exists backup.db]} {
sqlite3 dbb backup.db
execsql {
PRAGMA auto_vacuum = 1;
BEGIN;
CREATE TABLE abc(a);
INSERT INTO abc VALUES(randstr(1100,1100)); -- Page 4 is overflow
INSERT INTO abc VALUES(randstr(1100,1100)); -- Page 5 is overflow
} dbb
for {set i 0} {$i<2500} {incr i} {
execsql {
INSERT INTO abc VALUES(randstr(100,100));
} dbb
}
execsql {
COMMIT;
PRAGMA cache_size = 10;
} dbb
dbb close
}
db close
forcedelete test.db
forcedelete test.db-journal
forcecopy backup.db test.db
set ::DB [sqlite3 db test.db]
execsql {
PRAGMA cache_size = 10;
}
} -sqlbody {
BEGIN;
DELETE FROM abc WHERE oid < 3;
UPDATE abc SET a = randstr(100,100) WHERE oid > 2300;
UPDATE abc SET a = randstr(1100,1100) WHERE oid =
(select max(oid) from abc);
COMMIT;
}
}
do_ioerr_test autovacuum-ioerr2-1 -sqlprep {
PRAGMA auto_vacuum = 1;
CREATE TABLE abc(a);
INSERT INTO abc VALUES(randstr(1500,1500));
} -sqlbody {
CREATE TABLE abc2(a);
BEGIN;
DELETE FROM abc;
INSERT INTO abc VALUES(randstr(1500,1500));
CREATE TABLE abc3(a);
COMMIT;
}
finish_test

395
testdata/tcl/avfs.test vendored Normal file
View file

@ -0,0 +1,395 @@
# 2021-03-06
#
# 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 tests for the appendvfs extension.
#
# Tests performed:
# avfs-1.0. Test that an appendvfs DB can be added to an empty (ZLF) file.
# avfs-1.1. Test that the DB can be read with correct content upon reopen.
# avfs-1.2. Test that an appendvfs DB can be added to a simple text file.
# avfs-1.3. Test that the DB can be read with correct content upon reopen.
# avfs-1.4. Test that appended DB is aligned to default page boundary.
# avfs-2.1. Test that the simple text file retains its initial text.
# avfs-3.1. Test that the appendvfs can grow and shrink, remaining intact.
# avfs-3.2. Test that appendvfs is intact after grow/shrink/close/reopen.
# avfs-3.3. Test that appendvfs can grow by many pages and be written.
# avfs-3.4. Test that grown appendvfs can be reopened and appear intact.
# avfs-3.5. Test that much grown appendvfs can shrink and reopen intact.
# avfs-4.1. Test shell's ability to append to a non-appendvfs file.
# avfs-4.2. Test shell's ability to append to empty or nonexistent file.
# avfs-4.3. Test shell's ability to reopen and alter an appendvfs file.
# avfs-5.1. Test appendvfs refusal to open too-tiny DB appended onto ZLF.
# avfs-5.2. Test appendvfs refusal to open too-tiny DB appended on other.
# ...
# (more to come)
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix avfs
# Do not attempt this test if SQLITE_OMIT_VIRTUALTABLE is defined.
#
ifcapable !vtab {
finish_test
return
}
set CLI [test_find_cli]
db close
# forcedelete test.db
load_static_extension db appendvfs
set ::fa avfs.adb
set ::fza avfs.sdb
forcedelete $::fa $::fza
set ::result {}
proc shellDoesAr {} {
set shdo "sh_app1.sql"
forcedelete $shdo
set fd [open $shdo w]
puts $fd ".help\n.q"
close $fd
set res [catchcmd "-batch -cmd \".read $shdo\""]
return [regexp {^.archive} [lindex $res 1]]
}
set ::vf "&vfs=apndvfs"
# Return file offset of appendvfs portion of a file, or {} if none such.
proc fosAvfs {fname} {
if {[file size $fname] < 25} {
return {}
}
if {[catch {set fd [open $fname rb]}]} {
return {}
}
seek $fd -25 end
set am [read $fd 17]
set ao [read $fd 8]
close $fd
if {$am ne "Start-Of-SQLite3-"} {
return {}
}
binary scan $ao "W" rvo
return $rvo
}
do_test 1.0 {
set results {}
set out [open $::fza wb]
close $out
sqlite3 adb "file:$::fza?mode=rwc$::vf" -uri 1
adb eval {
PRAGMA page_size=1024;
PRAGMA cache_size=10;
CREATE TABLE t1(a TEXT);
INSERT INTO t1 VALUES ('dog'),('cat');
SELECT group_concat(a) as pets FROM (SELECT a FROM t1 ORDER BY a);
} { lappend results $pets }
adb close
lappend results [fosAvfs $fza]
set ::result [join $results " | "]
} {cat,dog | 0}
do_test 1.1 {
set results {}
sqlite3 adb "file:$::fza?mode=rw$::vf" -uri 1
adb eval {
SELECT group_concat(a) as pets FROM (SELECT a FROM t1 ORDER BY a DESC);
} { lappend results $pets }
adb close
set ::result [join $results " | "]
} {dog,cat}
do_test 1.2 {
set results {}
set out [open $::fa wb]
set ::tlo { "Just some text," "and more text," "ending at 3 lines." }
puts $out [join $::tlo "\n"]
close $out
set adbSz [file size $::fa]
sqlite3 adb "file:$::fa?mode=rwc$::vf" -uri 1
adb eval {
PRAGMA auto_vacuum = 0;
PRAGMA page_size=512;
PRAGMA cache_size=0;
CREATE TABLE t1(a TEXT);
INSERT INTO t1 VALUES ('dog'),('cat'),('pig');
SELECT group_concat(a) as pets FROM (SELECT a FROM t1 ORDER BY a);
} { lappend results $pets }
adb close
set adaSz [file size $::fa]
lappend results "Bytes before/after $adbSz/$adaSz"
set ::result [join $results " | "]
} {cat,dog,pig | Bytes before/after 50/5145}
do_test 1.3 {
set results {}
sqlite3 adb "file:$::fa?mode=rw$::vf" -uri 1
adb eval {
SELECT group_concat(a) as pets FROM (SELECT a FROM t1 ORDER BY a DESC);
} { lappend results $pets }
adb close
set ::result [join $results " | "]
} {pig,dog,cat}
do_test 1.4 {
set ::result [fosAvfs $fa]
} {4096}
do_test 2.1 {
set in [open $::fa r]
set tli {}
for {set i [llength $::tlo]} {$i > 0} {incr i -1} {
lappend tli [gets $in]
}
close $in
if { [join $tli ":"] ne [join $::tlo ":"] } {
set ::result "Appendee changed."
} else {
set ::result "Appendee intact."
}
} {Appendee intact.}
# Set of repeatable random integers for a couple tests.
set ::nrint 50000
proc rint {v} {
return [::tcl::mathfunc::int [expr $v * 100000]]
}
array set ::randints [list 0 [rint [::tcl::mathfunc::srand 0]]]
for {set i 1} {$i < $::nrint} {incr i} {
set ::randints($i) [rint [::tcl::mathfunc::rand]]
}
do_test 3.1 {
set results {}
sqlite3 adb "file:$::fa?mode=rw$::vf" -uri 1
adb eval {
DROP TABLE t1;
PRAGMA cache_size=10;
CREATE TABLE ri (i INTEGER);
BEGIN;
}
for {set i 0} {$i < $::nrint} {incr i} {
set r $::randints($i)
set s $::randints([incr i])
set t $::randints([incr i])
set u $::randints([incr i])
set v $::randints([incr i])
adb eval {
INSERT INTO ri VALUES ($r),($s),($t),($u),($v)
}
}
adb eval {
COMMIT;
SELECT integrity_check as ic FROM pragma_integrity_check();
} { lappend results $ic }
set adbSz [file size $::fa]
set qr {}
adb eval {
SELECT count(*) as ic FROM ri;
DELETE FROM ri WHERE (i % 50) <> 25;
SELECT integrity_check as ic FROM pragma_integrity_check();
VACUUM;
SELECT integrity_check as ic FROM pragma_integrity_check();
SELECT count(*) as ic FROM ri;
} { lappend qr $ic }
adb close
set adaSz [file size $::fa]
set adba [expr ($adbSz + 0.1)/$adaSz]
# lappend results $adba
set results [concat $results [lrange $qr 0 2]]
lappend results [expr {$adba > 10.0}]
set ::result [join $results " | "]
} "ok | $::nrint | ok | ok | 1"
do_test 3.2 {
set results {}
sqlite3 adb "file:$::fa?mode=rw$::vf" -uri 1
adb eval {
SELECT integrity_check as ic FROM pragma_integrity_check();
} { lappend results $ic }
adb close
set ::result [join $results " | "]
} {ok}
# avfs-3.3. Test that appendvfs can grow by many pages and be written.
do_test 3.3 {
set results {}
sqlite3 adb "file:$::fa?mode=rw$::vf" -uri 1
set npages 300
adb eval { BEGIN }
while {$npages > 0} {
adb eval { INSERT INTO ri VALUES (randomblob(1500)) }
incr npages -1
}
adb eval { COMMIT }
adb eval {
SELECT integrity_check as ic FROM pragma_integrity_check();
} { lappend results $ic }
adb close
set adaSzr [expr [file size $::fa] / 300.0 / 1500 ]
set okSzr [expr $adaSzr > 1.0 && $adaSzr < 1.3 ]
lappend results $okSzr
set ::result [join $results " | "]
} {ok | 1}
# avfs-3.4. Test that grown appendvfs can be reopened and appear intact.
do_test 3.4 {
set results {}
sqlite3 adb "file:$::fa?mode=rw$::vf" -uri 1
adb eval {
SELECT integrity_check as ic FROM pragma_integrity_check();
} { lappend results $ic }
adb close
set ::result $ic
} {ok}
# avfs-3.5. Test that much grown appendvfs can shrink and reopen intact.
do_test 3.5 {
set results {}
set adbsz [file size $::fa]
sqlite3 adb "file:$::fa?mode=rw$::vf" -uri 1
adb eval {
DELETE FROM ri WHERE rowid % 8 <> 0;
SELECT integrity_check as ic FROM pragma_integrity_check();
VACUUM;
SELECT integrity_check as ic FROM pragma_integrity_check();
} { lappend results $ic }
adb close
set adasz [file size $::fa]
lappend results [expr {$adbsz/$adasz > 5}]
sqlite3 adb "file:$::fa?mode=rw$::vf" -uri 1
adb eval {
SELECT integrity_check as ic FROM pragma_integrity_check();
} { lappend results $ic }
adb close
set ::result [join $results " | "]
} {ok | ok | 1 | ok}
set ::cliDoesAr [shellDoesAr]
do_test 4.1 {
set shdo "sh_app1.sql"
set shod "sh_app1.adb"
forcedelete $shdo $shod
set ofd [open $shdo w]
if {$::cliDoesAr} {
puts $ofd ".ar -c"
} else {
puts $ofd "pragma page_size=512;"
puts $ofd "create table sqlar (a);"
}
puts $ofd ".tables"
puts $ofd ".q"
close $ofd
set ofd [open $shod wb]
puts $ofd "Some text."
close $ofd
set res [catchcmd "-append -batch -init $shdo $shod" ""]
lappend res [fosAvfs $shod]
forcedelete $shdo $shod
set ::result [join $res " | "]
} {0 | sqlar | 4096}
do_test 4.2 {
set shdo "sh_app1.sql"
set shod "sh_app1.adb"
forcedelete $shdo $shod
set ofd [open $shdo w]
if {$::cliDoesAr} {
puts $ofd ".ar -c"
} else {
puts $ofd "pragma page_size=512;"
puts $ofd "create table sqlar (a);"
}
puts $ofd ".tables"
puts $ofd ".q"
close $ofd
set ofd [open $shod wb]
close $ofd
set res [catchcmd "-append -batch -init $shdo $shod" ""]
lappend res [fosAvfs $shod]
forcedelete $shdo ; # Leave $shod for next test.
set ::result [join $res " | "]
} {0 | sqlar | 0}
do_test 4.3 {
set shdo "sh_app1.sql"
set shod "sh_app1.adb" ; # Same as test 4.2, reusing ADB.
forcedelete $shdo
set ofd [open $shdo w]
if {$::cliDoesAr} {
puts $ofd ".ar -u $shdo"
puts $ofd "select count(*) from sqlar where name = '$shdo';"
} else {
puts $ofd "insert into sqlar values (1);"
puts $ofd "select count(*) from sqlar;"
}
puts $ofd ".q"
close $ofd
set res [catchcmd "-append -batch -init $shdo $shod" ""]
sqlite3 adb "file:$shod?mode=rw$::vf" -uri 1
adb eval {
SELECT count(*) as n FROM sqlar
} { lappend res $n }
adb close
forcedelete $shdo $shod;
set ::result [join $res " | "]
} {0 | 1 | 1}
do_test 5.1 {
set fake "faketiny.sdb"
forcedelete $fake
set ofd [open $fake wb]
puts -nonewline $ofd "SQLite format 3"
puts -nonewline $ofd [binary format "c" 0]
puts -nonewline $ofd "Start-Of-SQLite3-"
puts -nonewline $ofd [binary format "W" 0]
close $ofd
if {[catch {sqlite3 adb "file:$fake?mode=rw$::vf" -uri 1}]} {
set res "Open failed."
} else {
adb close
set res "Opened when should not."
}
forcedelete $fake
set ::result $res
} {Open failed.}
do_test 5.2 {
set fake "faketiny.sdb"
forcedelete $fake
set ofd [open $fake wb]
set fakeAppendee "Dog ate my homework.\n"
puts -nonewline $ofd $fakeAppendee
puts -nonewline $ofd "SQLite format 3"
puts -nonewline $ofd [binary format "c" 0]
puts -nonewline $ofd "Start-Of-SQLite3-"
puts -nonewline $ofd [binary format "W" [string length $fakeAppendee]]
close $ofd
if {[catch {sqlite3 adb "file:$fake?mode=rw$::vf" -uri 1}]} {
set res "Open failed."
} else {
adb close
set res "Opened when should not."
}
forcedelete $fake
set ::result $res
} {Open failed.}
forcedelete $::fa $::fza
unset -nocomplain ::fa ::fza ::tlo ::result ::randints ::nrint ::cliDoesAr
finish_test

927
testdata/tcl/avtrans.test vendored Normal file
View file

@ -0,0 +1,927 @@
# 2001 September 15
#
# 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. This
# file is a copy of "trans.test" modified to run under autovacuum mode.
# the point is to stress the autovacuum logic and try to get it to fail.
#
# $Id: avtrans.test,v 1.6 2007/09/12 17:01:45 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# Create several tables to work with.
#
do_test avtrans-1.0 {
execsql { PRAGMA auto_vacuum=full }
wal_set_journal_mode
execsql {
CREATE TABLE one(a int PRIMARY KEY, b text);
INSERT INTO one VALUES(1,'one');
INSERT INTO one VALUES(2,'two');
INSERT INTO one VALUES(3,'three');
SELECT b FROM one ORDER BY a;
}
} {one two three}
do_test avtrans-1.0.1 { execsql { PRAGMA auto_vacuum } } 1
do_test avtrans-1.1 {
execsql {
CREATE TABLE two(a int PRIMARY KEY, b text);
INSERT INTO two VALUES(1,'I');
INSERT INTO two VALUES(5,'V');
INSERT INTO two VALUES(10,'X');
SELECT b FROM two ORDER BY a;
}
} {I V X}
do_test avtrans-1.9 {
sqlite3 altdb test.db
execsql {SELECT b FROM one ORDER BY a} altdb
} {one two three}
do_test avtrans-1.10 {
execsql {SELECT b FROM two ORDER BY a} altdb
} {I V X}
integrity_check avtrans-1.11
wal_check_journal_mode avtrans-1.12
# Basic transactions
#
do_test avtrans-2.1 {
set v [catch {execsql {BEGIN}} msg]
lappend v $msg
} {0 {}}
do_test avtrans-2.2 {
set v [catch {execsql {END}} msg]
lappend v $msg
} {0 {}}
do_test avtrans-2.3 {
set v [catch {execsql {BEGIN TRANSACTION}} msg]
lappend v $msg
} {0 {}}
do_test avtrans-2.4 {
set v [catch {execsql {COMMIT TRANSACTION}} msg]
lappend v $msg
} {0 {}}
do_test avtrans-2.5 {
set v [catch {execsql {BEGIN TRANSACTION 'foo'}} msg]
lappend v $msg
} {0 {}}
do_test avtrans-2.6 {
set v [catch {execsql {ROLLBACK TRANSACTION 'foo'}} msg]
lappend v $msg
} {0 {}}
do_test avtrans-2.10 {
execsql {
BEGIN;
SELECT a FROM one ORDER BY a;
SELECT a FROM two ORDER BY a;
END;
}
} {1 2 3 1 5 10}
integrity_check avtrans-2.11
wal_check_journal_mode avtrans-2.12
# Check the locking behavior
#
sqlite3_soft_heap_limit 0
do_test avtrans-3.1 {
execsql {
BEGIN;
UPDATE one SET a = 0 WHERE 0;
SELECT a FROM one ORDER BY a;
}
} {1 2 3}
do_test avtrans-3.2 {
catchsql {
SELECT a FROM two ORDER BY a;
} altdb
} {0 {1 5 10}}
do_test avtrans-3.3 {
catchsql {
SELECT a FROM one ORDER BY a;
} altdb
} {0 {1 2 3}}
do_test avtrans-3.4 {
catchsql {
INSERT INTO one VALUES(4,'four');
}
} {0 {}}
do_test avtrans-3.5 {
catchsql {
SELECT a FROM two ORDER BY a;
} altdb
} {0 {1 5 10}}
do_test avtrans-3.6 {
catchsql {
SELECT a FROM one ORDER BY a;
} altdb
} {0 {1 2 3}}
do_test avtrans-3.7 {
catchsql {
INSERT INTO two VALUES(4,'IV');
}
} {0 {}}
do_test avtrans-3.8 {
catchsql {
SELECT a FROM two ORDER BY a;
} altdb
} {0 {1 5 10}}
do_test avtrans-3.9 {
catchsql {
SELECT a FROM one ORDER BY a;
} altdb
} {0 {1 2 3}}
do_test avtrans-3.10 {
execsql {END TRANSACTION}
} {}
do_test avtrans-3.11 {
set v [catch {execsql {
SELECT a FROM two ORDER BY a;
} altdb} msg]
lappend v $msg
} {0 {1 4 5 10}}
do_test avtrans-3.12 {
set v [catch {execsql {
SELECT a FROM one ORDER BY a;
} altdb} msg]
lappend v $msg
} {0 {1 2 3 4}}
do_test avtrans-3.13 {
set v [catch {execsql {
SELECT a FROM two ORDER BY a;
} db} msg]
lappend v $msg
} {0 {1 4 5 10}}
do_test avtrans-3.14 {
set v [catch {execsql {
SELECT a FROM one ORDER BY a;
} db} msg]
lappend v $msg
} {0 {1 2 3 4}}
sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit)
integrity_check avtrans-3.15
do_test avtrans-4.1 {
set v [catch {execsql {
COMMIT;
} db} msg]
lappend v $msg
} {1 {cannot commit - no transaction is active}}
do_test avtrans-4.2 {
set v [catch {execsql {
ROLLBACK;
} db} msg]
lappend v $msg
} {1 {cannot rollback - no transaction is active}}
do_test avtrans-4.3 {
catchsql {
BEGIN TRANSACTION;
UPDATE two SET a = 0 WHERE 0;
SELECT a FROM two ORDER BY a;
} db
} {0 {1 4 5 10}}
do_test avtrans-4.4 {
catchsql {
SELECT a FROM two ORDER BY a;
} altdb
} {0 {1 4 5 10}}
do_test avtrans-4.5 {
catchsql {
SELECT a FROM one ORDER BY a;
} altdb
} {0 {1 2 3 4}}
do_test avtrans-4.6 {
catchsql {
BEGIN TRANSACTION;
SELECT a FROM one ORDER BY a;
} db
} {1 {cannot start a transaction within a transaction}}
do_test avtrans-4.7 {
catchsql {
SELECT a FROM two ORDER BY a;
} altdb
} {0 {1 4 5 10}}
do_test avtrans-4.8 {
catchsql {
SELECT a FROM one ORDER BY a;
} altdb
} {0 {1 2 3 4}}
do_test avtrans-4.9 {
set v [catch {execsql {
END TRANSACTION;
SELECT a FROM two ORDER BY a;
} db} msg]
lappend v $msg
} {0 {1 4 5 10}}
do_test avtrans-4.10 {
set v [catch {execsql {
SELECT a FROM two ORDER BY a;
} altdb} msg]
lappend v $msg
} {0 {1 4 5 10}}
do_test avtrans-4.11 {
set v [catch {execsql {
SELECT a FROM one ORDER BY a;
} altdb} msg]
lappend v $msg
} {0 {1 2 3 4}}
integrity_check avtrans-4.12
do_test avtrans-4.98 {
altdb close
execsql {
DROP TABLE one;
DROP TABLE two;
}
} {}
integrity_check avtrans-4.99
# Check out the commit/rollback behavior of the database
#
do_test avtrans-5.1 {
execsql {SELECT name FROM sqlite_master WHERE type='table' ORDER BY name}
} {}
do_test avtrans-5.2 {
execsql {BEGIN TRANSACTION}
execsql {SELECT name FROM sqlite_master WHERE type='table' ORDER BY name}
} {}
do_test avtrans-5.3 {
execsql {CREATE TABLE one(a text, b int)}
execsql {SELECT name FROM sqlite_master WHERE type='table' ORDER BY name}
} {one}
do_test avtrans-5.4 {
execsql {SELECT a,b FROM one ORDER BY b}
} {}
do_test avtrans-5.5 {
execsql {INSERT INTO one(a,b) VALUES('hello', 1)}
execsql {SELECT a,b FROM one ORDER BY b}
} {hello 1}
do_test avtrans-5.6 {
execsql {ROLLBACK}
execsql {SELECT name FROM sqlite_master WHERE type='table' ORDER BY name}
} {}
do_test avtrans-5.7 {
set v [catch {
execsql {SELECT a,b FROM one ORDER BY b}
} msg]
lappend v $msg
} {1 {no such table: one}}
# Test commits and rollbacks of table CREATE TABLEs, CREATE INDEXs
# DROP TABLEs and DROP INDEXs
#
do_test avtrans-5.8 {
execsql {
SELECT name fROM sqlite_master
WHERE type='table' OR type='index'
ORDER BY name
}
} {}
do_test avtrans-5.9 {
execsql {
BEGIN TRANSACTION;
CREATE TABLE t1(a int, b int, c int);
SELECT name fROM sqlite_master
WHERE type='table' OR type='index'
ORDER BY name;
}
} {t1}
do_test avtrans-5.10 {
execsql {
CREATE INDEX i1 ON t1(a);
SELECT name fROM sqlite_master
WHERE type='table' OR type='index'
ORDER BY name;
}
} {i1 t1}
do_test avtrans-5.11 {
execsql {
COMMIT;
SELECT name fROM sqlite_master
WHERE type='table' OR type='index'
ORDER BY name;
}
} {i1 t1}
do_test avtrans-5.12 {
execsql {
BEGIN TRANSACTION;
CREATE TABLE t2(a int, b int, c int);
CREATE INDEX i2a ON t2(a);
CREATE INDEX i2b ON t2(b);
DROP TABLE t1;
SELECT name fROM sqlite_master
WHERE type='table' OR type='index'
ORDER BY name;
}
} {i2a i2b t2}
do_test avtrans-5.13 {
execsql {
ROLLBACK;
SELECT name fROM sqlite_master
WHERE type='table' OR type='index'
ORDER BY name;
}
} {i1 t1}
do_test avtrans-5.14 {
execsql {
BEGIN TRANSACTION;
DROP INDEX i1;
SELECT name fROM sqlite_master
WHERE type='table' OR type='index'
ORDER BY name;
}
} {t1}
do_test avtrans-5.15 {
execsql {
ROLLBACK;
SELECT name fROM sqlite_master
WHERE type='table' OR type='index'
ORDER BY name;
}
} {i1 t1}
do_test avtrans-5.16 {
execsql {
BEGIN TRANSACTION;
DROP INDEX i1;
CREATE TABLE t2(x int, y int, z int);
CREATE INDEX i2x ON t2(x);
CREATE INDEX i2y ON t2(y);
INSERT INTO t2 VALUES(1,2,3);
SELECT name fROM sqlite_master
WHERE type='table' OR type='index'
ORDER BY name;
}
} {i2x i2y t1 t2}
do_test avtrans-5.17 {
execsql {
COMMIT;
SELECT name fROM sqlite_master
WHERE type='table' OR type='index'
ORDER BY name;
}
} {i2x i2y t1 t2}
do_test avtrans-5.18 {
execsql {
SELECT * FROM t2;
}
} {1 2 3}
do_test avtrans-5.19 {
execsql {
SELECT x FROM t2 WHERE y=2;
}
} {1}
do_test avtrans-5.20 {
execsql {
BEGIN TRANSACTION;
DROP TABLE t1;
DROP TABLE t2;
SELECT name fROM sqlite_master
WHERE type='table' OR type='index'
ORDER BY name;
}
} {}
do_test avtrans-5.21 {
set r [catch {execsql {
SELECT * FROM t2
}} msg]
lappend r $msg
} {1 {no such table: t2}}
do_test avtrans-5.22 {
execsql {
ROLLBACK;
SELECT name fROM sqlite_master
WHERE type='table' OR type='index'
ORDER BY name;
}
} {i2x i2y t1 t2}
do_test avtrans-5.23 {
execsql {
SELECT * FROM t2;
}
} {1 2 3}
integrity_check avtrans-5.23
# Try to DROP and CREATE tables and indices with the same name
# within a transaction. Make sure ROLLBACK works.
#
do_test avtrans-6.1 {
execsql2 {
INSERT INTO t1 VALUES(1,2,3);
BEGIN TRANSACTION;
DROP TABLE t1;
CREATE TABLE t1(p,q,r);
ROLLBACK;
SELECT * FROM t1;
}
} {a 1 b 2 c 3}
do_test avtrans-6.2 {
execsql2 {
INSERT INTO t1 VALUES(1,2,3);
BEGIN TRANSACTION;
DROP TABLE t1;
CREATE TABLE t1(p,q,r);
COMMIT;
SELECT * FROM t1;
}
} {}
do_test avtrans-6.3 {
execsql2 {
INSERT INTO t1 VALUES(1,2,3);
SELECT * FROM t1;
}
} {p 1 q 2 r 3}
do_test avtrans-6.4 {
execsql2 {
BEGIN TRANSACTION;
DROP TABLE t1;
CREATE TABLE t1(a,b,c);
INSERT INTO t1 VALUES(4,5,6);
SELECT * FROM t1;
DROP TABLE t1;
}
} {a 4 b 5 c 6}
do_test avtrans-6.5 {
execsql2 {
ROLLBACK;
SELECT * FROM t1;
}
} {p 1 q 2 r 3}
do_test avtrans-6.6 {
execsql2 {
BEGIN TRANSACTION;
DROP TABLE t1;
CREATE TABLE t1(a,b,c);
INSERT INTO t1 VALUES(4,5,6);
SELECT * FROM t1;
DROP TABLE t1;
}
} {a 4 b 5 c 6}
do_test avtrans-6.7 {
catchsql {
COMMIT;
SELECT * FROM t1;
}
} {1 {no such table: t1}}
# Repeat on a table with an automatically generated index.
#
do_test avtrans-6.10 {
execsql2 {
CREATE TABLE t1(a unique,b,c);
INSERT INTO t1 VALUES(1,2,3);
BEGIN TRANSACTION;
DROP TABLE t1;
CREATE TABLE t1(p unique,q,r);
ROLLBACK;
SELECT * FROM t1;
}
} {a 1 b 2 c 3}
do_test avtrans-6.11 {
execsql2 {
BEGIN TRANSACTION;
DROP TABLE t1;
CREATE TABLE t1(p unique,q,r);
COMMIT;
SELECT * FROM t1;
}
} {}
do_test avtrans-6.12 {
execsql2 {
INSERT INTO t1 VALUES(1,2,3);
SELECT * FROM t1;
}
} {p 1 q 2 r 3}
do_test avtrans-6.13 {
execsql2 {
BEGIN TRANSACTION;
DROP TABLE t1;
CREATE TABLE t1(a unique,b,c);
INSERT INTO t1 VALUES(4,5,6);
SELECT * FROM t1;
DROP TABLE t1;
}
} {a 4 b 5 c 6}
do_test avtrans-6.14 {
execsql2 {
ROLLBACK;
SELECT * FROM t1;
}
} {p 1 q 2 r 3}
do_test avtrans-6.15 {
execsql2 {
BEGIN TRANSACTION;
DROP TABLE t1;
CREATE TABLE t1(a unique,b,c);
INSERT INTO t1 VALUES(4,5,6);
SELECT * FROM t1;
DROP TABLE t1;
}
} {a 4 b 5 c 6}
do_test avtrans-6.16 {
catchsql {
COMMIT;
SELECT * FROM t1;
}
} {1 {no such table: t1}}
do_test avtrans-6.20 {
execsql {
CREATE TABLE t1(a integer primary key,b,c);
INSERT INTO t1 VALUES(1,-2,-3);
INSERT INTO t1 VALUES(4,-5,-6);
SELECT * FROM t1;
}
} {1 -2 -3 4 -5 -6}
do_test avtrans-6.21 {
execsql {
CREATE INDEX i1 ON t1(b);
SELECT * FROM t1 WHERE b<1;
}
} {4 -5 -6 1 -2 -3}
do_test avtrans-6.22 {
execsql {
BEGIN TRANSACTION;
DROP INDEX i1;
SELECT * FROM t1 WHERE b<1;
ROLLBACK;
}
} {1 -2 -3 4 -5 -6}
do_test avtrans-6.23 {
execsql {
SELECT * FROM t1 WHERE b<1;
}
} {4 -5 -6 1 -2 -3}
do_test avtrans-6.24 {
execsql {
BEGIN TRANSACTION;
DROP TABLE t1;
ROLLBACK;
SELECT * FROM t1 WHERE b<1;
}
} {4 -5 -6 1 -2 -3}
do_test avtrans-6.25 {
execsql {
BEGIN TRANSACTION;
DROP INDEX i1;
CREATE INDEX i1 ON t1(c);
SELECT * FROM t1 WHERE b<1;
}
} {1 -2 -3 4 -5 -6}
do_test avtrans-6.26 {
execsql {
SELECT * FROM t1 WHERE c<1;
}
} {4 -5 -6 1 -2 -3}
do_test avtrans-6.27 {
execsql {
ROLLBACK;
SELECT * FROM t1 WHERE b<1;
}
} {4 -5 -6 1 -2 -3}
do_test avtrans-6.28 {
execsql {
SELECT * FROM t1 WHERE c<1;
}
} {1 -2 -3 4 -5 -6}
# The following repeats steps 6.20 through 6.28, but puts a "unique"
# constraint the first field of the table in order to generate an
# automatic index.
#
do_test avtrans-6.30 {
execsql {
BEGIN TRANSACTION;
DROP TABLE t1;
CREATE TABLE t1(a int unique,b,c);
COMMIT;
INSERT INTO t1 VALUES(1,-2,-3);
INSERT INTO t1 VALUES(4,-5,-6);
SELECT * FROM t1 ORDER BY a;
}
} {1 -2 -3 4 -5 -6}
do_test avtrans-6.31 {
execsql {
CREATE INDEX i1 ON t1(b);
SELECT * FROM t1 WHERE b<1;
}
} {4 -5 -6 1 -2 -3}
do_test avtrans-6.32 {
execsql {
BEGIN TRANSACTION;
DROP INDEX i1;
SELECT * FROM t1 WHERE b<1;
ROLLBACK;
}
} {1 -2 -3 4 -5 -6}
do_test avtrans-6.33 {
execsql {
SELECT * FROM t1 WHERE b<1;
}
} {4 -5 -6 1 -2 -3}
do_test avtrans-6.34 {
execsql {
BEGIN TRANSACTION;
DROP TABLE t1;
ROLLBACK;
SELECT * FROM t1 WHERE b<1;
}
} {4 -5 -6 1 -2 -3}
do_test avtrans-6.35 {
execsql {
BEGIN TRANSACTION;
DROP INDEX i1;
CREATE INDEX i1 ON t1(c);
SELECT * FROM t1 WHERE b<1;
}
} {1 -2 -3 4 -5 -6}
do_test avtrans-6.36 {
execsql {
SELECT * FROM t1 WHERE c<1;
}
} {4 -5 -6 1 -2 -3}
do_test avtrans-6.37 {
execsql {
DROP INDEX i1;
SELECT * FROM t1 WHERE c<1;
}
} {1 -2 -3 4 -5 -6}
do_test avtrans-6.38 {
execsql {
ROLLBACK;
SELECT * FROM t1 WHERE b<1;
}
} {4 -5 -6 1 -2 -3}
do_test avtrans-6.39 {
execsql {
SELECT * FROM t1 WHERE c<1;
}
} {1 -2 -3 4 -5 -6}
integrity_check avtrans-6.40
ifcapable !floatingpoint {
finish_test
return
}
# Test to make sure rollback restores the database back to its original
# state.
#
do_test avtrans-7.1 {
execsql {BEGIN}
for {set i 0} {$i<1000} {incr i} {
set r1 [expr {rand()}]
set r2 [expr {rand()}]
set r3 [expr {rand()}]
execsql "INSERT INTO t2 VALUES($r1,$r2,$r3)"
}
execsql {COMMIT}
set ::checksum [execsql {SELECT md5sum(x,y,z) FROM t2}]
set ::checksum2 [
execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master}
]
execsql {SELECT count(*) FROM t2}
} {1001}
do_test avtrans-7.2 {
execsql {SELECT md5sum(x,y,z) FROM t2}
} $checksum
do_test avtrans-7.2.1 {
execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master}
} $checksum2
do_test avtrans-7.3 {
execsql {
BEGIN;
DELETE FROM t2;
ROLLBACK;
SELECT md5sum(x,y,z) FROM t2;
}
} $checksum
do_test avtrans-7.4 {
execsql {
BEGIN;
INSERT INTO t2 SELECT * FROM t2;
ROLLBACK;
SELECT md5sum(x,y,z) FROM t2;
}
} $checksum
do_test avtrans-7.5 {
execsql {
BEGIN;
DELETE FROM t2;
ROLLBACK;
SELECT md5sum(x,y,z) FROM t2;
}
} $checksum
do_test avtrans-7.6 {
execsql {
BEGIN;
INSERT INTO t2 SELECT * FROM t2;
ROLLBACK;
SELECT md5sum(x,y,z) FROM t2;
}
} $checksum
do_test avtrans-7.7 {
execsql {
BEGIN;
CREATE TABLE t3 AS SELECT * FROM t2;
INSERT INTO t2 SELECT * FROM t3;
ROLLBACK;
SELECT md5sum(x,y,z) FROM t2;
}
} $checksum
do_test avtrans-7.8 {
execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master}
} $checksum2
ifcapable tempdb {
do_test avtrans-7.9 {
execsql {
BEGIN;
CREATE TEMP TABLE t3 AS SELECT * FROM t2;
INSERT INTO t2 SELECT * FROM t3;
ROLLBACK;
SELECT md5sum(x,y,z) FROM t2;
}
} $checksum
}
do_test avtrans-7.10 {
execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master}
} $checksum2
ifcapable tempdb {
do_test avtrans-7.11 {
execsql {
BEGIN;
CREATE TEMP TABLE t3 AS SELECT * FROM t2;
INSERT INTO t2 SELECT * FROM t3;
DROP INDEX i2x;
DROP INDEX i2y;
CREATE INDEX i3a ON t3(x);
ROLLBACK;
SELECT md5sum(x,y,z) FROM t2;
}
} $checksum
}
do_test avtrans-7.12 {
execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master}
} $checksum2
ifcapable tempdb {
do_test avtrans-7.13 {
execsql {
BEGIN;
DROP TABLE t2;
ROLLBACK;
SELECT md5sum(x,y,z) FROM t2;
}
} $checksum
}
do_test avtrans-7.14 {
execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master}
} $checksum2
integrity_check avtrans-7.15
# Arrange for another process to begin modifying the database but abort
# and die in the middle of the modification. Then have this process read
# the database. This process should detect the journal file and roll it
# back. Verify that this happens correctly.
#
set fd [open test.tcl w]
puts $fd {
sqlite3 db test.db
db eval {
PRAGMA default_cache_size=20;
BEGIN;
CREATE TABLE t3 AS SELECT * FROM t2;
DELETE FROM t2;
}
sqlite_abort
}
close $fd
do_test avtrans-8.1 {
catch {exec [info nameofexec] test.tcl}
execsql {SELECT md5sum(x,y,z) FROM t2}
} $checksum
do_test avtrans-8.2 {
execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master}
} $checksum2
integrity_check avtrans-8.3
# In the following sequence of tests, compute the MD5 sum of the content
# of a table, make lots of modifications to that table, then do a rollback.
# Verify that after the rollback, the MD5 checksum is unchanged.
#
do_test avtrans-9.1 {
execsql {
PRAGMA default_cache_size=10;
}
db close
sqlite3 db test.db
execsql {
BEGIN;
CREATE TABLE t3(x TEXT);
INSERT INTO t3 VALUES(randstr(10,400));
INSERT INTO t3 VALUES(randstr(10,400));
INSERT INTO t3 SELECT randstr(10,400) FROM t3;
INSERT INTO t3 SELECT randstr(10,400) FROM t3;
INSERT INTO t3 SELECT randstr(10,400) FROM t3;
INSERT INTO t3 SELECT randstr(10,400) FROM t3;
INSERT INTO t3 SELECT randstr(10,400) FROM t3;
INSERT INTO t3 SELECT randstr(10,400) FROM t3;
INSERT INTO t3 SELECT randstr(10,400) FROM t3;
INSERT INTO t3 SELECT randstr(10,400) FROM t3;
INSERT INTO t3 SELECT randstr(10,400) FROM t3;
COMMIT;
SELECT count(*) FROM t3;
}
} {1024}
# The following procedure computes a "signature" for table "t3". If
# T3 changes in any way, the signature should change.
#
# This is used to test ROLLBACK. We gather a signature for t3, then
# make lots of changes to t3, then rollback and take another signature.
# The two signatures should be the same.
#
proc signature {} {
return [db eval {SELECT count(*), md5sum(x) FROM t3}]
}
# Repeat the following group of tests 20 times for quick testing and
# 40 times for full testing. Each iteration of the test makes table
# t3 a little larger, and thus takes a little longer, so doing 40 tests
# is more than 2.0 times slower than doing 20 tests. Considerably more.
#
if {[info exists G(isquick)]} {
set limit 20
} else {
set limit 40
}
# Do rollbacks. Make sure the signature does not change.
#
for {set i 2} {$i<=$limit} {incr i} {
set ::sig [signature]
set cnt [lindex $::sig 0]
if {$i%2==0} {
execsql {PRAGMA fullfsync=ON}
} else {
execsql {PRAGMA fullfsync=OFF}
}
set sqlite_sync_count 0
set sqlite_fullsync_count 0
do_test avtrans-9.$i.1-$cnt {
execsql {
BEGIN;
DELETE FROM t3 WHERE random()%10!=0;
INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
ROLLBACK;
}
signature
} $sig
do_test avtrans-9.$i.2-$cnt {
execsql {
BEGIN;
DELETE FROM t3 WHERE random()%10!=0;
INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
DELETE FROM t3 WHERE random()%10!=0;
INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
ROLLBACK;
}
signature
} $sig
if {$i<$limit} {
do_test avtrans-9.$i.3-$cnt {
execsql {
INSERT INTO t3 SELECT randstr(10,400) FROM t3 WHERE random()%10==0;
}
} {}
if {$tcl_platform(platform)=="unix"} {
do_test avtrans-9.$i.4-$cnt {
expr {$sqlite_sync_count>0}
} 1
ifcapable pager_pragmas {
do_test avtrans-9.$i.5-$cnt {
expr {$sqlite_fullsync_count>0}
} [expr {$i%2==0}]
} else {
do_test avtrans-9.$i.5-$cnt {
expr {$sqlite_fullsync_count==0}
} {1}
}
}
wal_check_journal_mode avtrans-9.$i-6.$cnt
}
set ::pager_old_format 0
}
integrity_check avtrans-10.1
wal_check_journal_mode avtrans-10.2
finish_test

524
testdata/tcl/backcompat.test vendored Normal file
View file

@ -0,0 +1,524 @@
# 2010 August 19
#
# 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 that the current version of SQLite
# is capable of reading and writing databases created by previous
# versions, and vice-versa.
#
# To use this test, old versions of the testfixture process should be
# copied into the working directory alongside the new version. The old
# versions should be named "testfixtureXXX" (or testfixtureXXX.exe on
# windows), where XXX can be any string.
#
# This test file uses the tcl code for controlling a second testfixture
# process located in lock_common.tcl. See the commments in lock_common.tcl
# for documentation of the available commands.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/malloc_common.tcl
source $testdir/bc_common.tcl
db close
if {"" == [bc_find_binaries backcompat.test]} {
finish_test
return
}
proc do_backcompat_test {rv bin1 bin2 script} {
forcedelete test.db
if {$bin1 != ""} { set ::bc_chan1 [launch_testfixture $bin1] }
set ::bc_chan2 [launch_testfixture $bin2]
if { $rv } {
proc code2 {tcl} { uplevel #0 $tcl }
if {$bin1 != ""} { proc code2 {tcl} { testfixture $::bc_chan1 $tcl } }
proc code1 {tcl} { testfixture $::bc_chan2 $tcl }
} else {
proc code1 {tcl} { uplevel #0 $tcl }
if {$bin1 != ""} { proc code1 {tcl} { testfixture $::bc_chan1 $tcl } }
proc code2 {tcl} { testfixture $::bc_chan2 $tcl }
}
proc sql1 sql { code1 [list db eval $sql] }
proc sql2 sql { code2 [list db eval $sql] }
code1 { sqlite3 db test.db }
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 } }
catch { code2 { db close } }
catch { close $::bc_chan2 }
catch { close $::bc_chan1 }
}
array set ::incompatible [list]
proc do_allbackcompat_test {script} {
foreach bin $::BC(binaries) {
set nErr [set_test_counter errors]
foreach dir {0 1} {
set bintag $bin
regsub {.*testfixture\.} $bintag {} bintag
set bintag [string map {\.exe {}} $bintag]
if {$bintag == ""} {set bintag self}
set ::bcname ".$bintag.$dir."
rename do_test _do_test
proc do_test {nm sql res} {
set nm [regsub {\.} $nm $::bcname]
uplevel [list _do_test $nm $sql $res]
}
do_backcompat_test $dir {} $bin $script
rename do_test {}
rename _do_test do_test
}
if { $nErr < [set_test_counter errors] } {
set ::incompatible([get_version $bin]) 1
}
}
}
proc read_file {zFile} {
set zData {}
if {[file exists $zFile]} {
set fd [open $zFile]
fconfigure $fd -translation binary -encoding binary
if {[file size $zFile]<=$::sqlite_pending_byte || $zFile != "test.db"} {
set zData [read $fd]
} else {
set zData [read $fd $::sqlite_pending_byte]
append zData [string repeat x 512]
seek $fd [expr $::sqlite_pending_byte+512] start
append zData [read $fd]
}
close $fd
}
return $zData
}
proc write_file {zFile zData} {
set fd [open $zFile w]
fconfigure $fd -translation binary -encoding binary
puts -nonewline $fd $zData
close $fd
}
proc read_file_system {} {
set ret [list]
foreach f {test.db test.db-journal test.db-wal} { lappend ret [read_file $f] }
set ret
}
proc write_file_system {data} {
foreach f {test.db test.db-journal test.db-wal} d $data {
if {[string length $d] == 0} {
forcedelete $f
} else {
write_file $f $d
}
}
}
#-------------------------------------------------------------------------
# Actual tests begin here.
#
# This first block of tests checks to see that the same database and
# journal files can be used by old and new versions. WAL and wal-index
# files are tested separately below.
#
do_allbackcompat_test {
# Test that database files are backwards compatible.
#
do_test backcompat-1.1.1 { sql1 {
CREATE TABLE t1(a PRIMARY KEY, b UNIQUE);
INSERT INTO t1 VALUES('abc', 'def');
} } {}
do_test backcompat-1.1.2 { sql2 { SELECT * FROM t1; } } {abc def}
do_test backcompat-1.1.3 { sql2 { INSERT INTO t1 VALUES('ghi', 'jkl'); } } {}
do_test backcompat-1.1.4 { sql1 { SELECT * FROM t1; } } {abc def ghi jkl}
do_test backcompat-1.1.5 { sql1 { PRAGMA integrity_check } } {ok}
do_test backcompat-1.1.6 { sql2 { PRAGMA integrity_check } } {ok}
# Test that one version can roll back a hot-journal file left in the
# file-system by the other version.
#
# Each test case is named "backcompat-1.X...", where X is either 0 or
# 1. If it is 0, then the current version creates a journal file that
# the old versions try to read. Otherwise, if X is 1, then the old version
# creates the journal file and we try to read it with the current version.
#
do_test backcompat-1.2.1 { sql1 {
PRAGMA cache_size = 10;
BEGIN;
INSERT INTO t1 VALUES(randomblob(400), randomblob(400));
INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM t1;
INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM t1;
INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM t1;
INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM t1;
COMMIT;
} } {}
set cksum1 [sql1 {SELECT md5sum(a), md5sum(b) FROM t1}]
set cksum2 [sql2 {SELECT md5sum(a), md5sum(b) FROM t1}]
do_test backcompat-1.2.2 [list string compare $cksum1 $cksum2] 0
do_test backcompat-1.2.3 { sql1 {
BEGIN;
UPDATE t1 SET a = randomblob(500);
} } {}
set data [read_file_system]
do_test backcompat-1.2.4 { sql1 { COMMIT } } {}
set same [expr {[sql2 {SELECT md5sum(a), md5sum(b) FROM t1}] == $cksum2}]
do_test backcompat-1.2.5 [list set {} $same] 0
code1 { db close }
code2 { db close }
write_file_system $data
code1 { sqlite3 db test.db }
code2 { sqlite3 db test.db }
set same [expr {[sql2 {SELECT md5sum(a), md5sum(b) FROM t1}] == $cksum2}]
do_test backcompat-1.2.6 [list set {} $same] 1
do_test backcompat-1.2.7 { sql1 { PRAGMA integrity_check } } {ok}
do_test backcompat-1.2.8 { sql2 { PRAGMA integrity_check } } {ok}
do_test backcompat-2.1 {
sql1 {
CREATE TABLE t2(a UNIQUE, b PRIMARY KEY, c UNIQUE);
INSERT INTO t2 VALUES(1,9,5);
INSERT INTO t2 VALUES(5,5,1);
INSERT INTO t2 VALUES(9,1,9);
SELECT * FROM t2 ORDER BY a;
}
} {1 9 5 5 5 1 9 1 9}
do_test backcompat-2.2 {
sql2 {
SELECT * FROM sqlite_master WHERE rootpage=-1;
SELECT * FROM t2 ORDER BY a;
}
} {1 9 5 5 5 1 9 1 9}
do_test backcompat-2.3 {
sql1 {
SELECT * FROM t2 ORDER BY b;
}
} {9 1 9 5 5 1 1 9 5}
do_test backcompat-2.4 {
sql2 {
SELECT * FROM t2 ORDER BY b;
}
} {9 1 9 5 5 1 1 9 5}
do_test backcompat-2.5 {
sql1 {
SELECT * FROM t2 ORDER BY c;
}
} {5 5 1 1 9 5 9 1 9}
do_test backcompat-2.6 {
sql2 {
SELECT * FROM t2 ORDER BY c;
}
} {5 5 1 1 9 5 9 1 9}
}
foreach k [lsort [array names ::incompatible]] {
puts "ERROR: Detected journal incompatibility with version $k"
}
unset ::incompatible
#-------------------------------------------------------------------------
# Test that WAL and wal-index files may be shared between different
# SQLite versions.
#
do_allbackcompat_test {
if {[code1 {sqlite3 -version}] >= "3.7.0"
&& [code1 {set ::sqlite_options(wal)}]
&& [code2 {sqlite3 -version}] >= "3.7.0"
&& [code2 {set ::sqlite_options(wal)}]
} {
do_test backcompat-2.1.1 { sql1 {
PRAGMA journal_mode = WAL;
CREATE TABLE t1(a PRIMARY KEY, b UNIQUE);
INSERT INTO t1 VALUES('I', 1);
INSERT INTO t1 VALUES('II', 2);
INSERT INTO t1 VALUES('III', 3);
SELECT * FROM t1;
} } {wal I 1 II 2 III 3}
do_test backcompat-2.1.2 { sql2 {
SELECT * FROM t1;
} } {I 1 II 2 III 3}
set data [read_file_system]
code1 {db close}
code2 {db close}
write_file_system $data
code1 {sqlite3 db test.db}
code2 {sqlite3 db test.db}
# The WAL file now in the file-system was created by the [code1]
# process. Check that the [code2] process can recover the log.
#
do_test backcompat-2.1.3 { sql2 {
SELECT * FROM t1;
} } {I 1 II 2 III 3}
do_test backcompat-2.1.4 { sql1 {
SELECT * FROM t1;
} } {I 1 II 2 III 3}
}
}
#-------------------------------------------------------------------------
# Test that FTS3 tables may be read/written by different versions of
# SQLite.
#
ifcapable fts3 {
set contents {
CREATE VIRTUAL TABLE t1 USING fts3(a, b);
}
foreach {num doc} {
one "jk zm jk eczkjblu urvysbnykk sk gnl jk ttvgf hmjf"
two "jk bnhc jjrxpjkb mjpavjuhw fibokdry igju jk zm zm xh"
three "wxe ogttbykvt uhzq xr iaf zf urvysbnykk aayxpmve oacaxgjoo mjpavjuhw"
four "gazrt jk ephknonq myjp uenvbm wuvajhwqz jk zm xnxhf nvfasfh"
five "zm aayxpmve csjqxhgj xnxhf xr jk aayxpmve xnxhf zm zm"
six "sokcyf zm ogyavjvv jk zm fibokdry zm jk igju igju"
seven "vgsld bvgimjik xuprtlyle jk akmikrqyt jk aayxpmve hkfoudzftq ddjj"
eight "zm uhzq ovkyevlgv zk uenvbm csjqxhgj jk vgsld pgybs jk"
nine "zm agmckuiu zexh fibokdry jk uhzq bu tugflixoex xnxhf sk"
} {
append contents "INSERT INTO t1 VALUES('$num', '$doc');"
}
do_allbackcompat_test {
if {[code1 {set ::sqlite_options(fts3)}]
&& [code2 {set ::sqlite_options(fts3)}]
} {
do_test backcompat-3.1 { sql1 $contents } {}
foreach {n q} {
1 "SELECT * FROM t1 ORDER BY a, b"
2 "SELECT rowid FROM t1 WHERE a MATCH 'five'"
3 "SELECT * FROM t1 WHERE a MATCH 'five'"
4 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'jk'"
5 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'tug* OR eight'"
} {
do_test backcompat-3.2 [list sql1 $q] [sql2 $q]
}
do_test backcompat-3.3 { sql1 {
INSERT INTO t1 SELECT * FROM t1;
INSERT INTO t1 SELECT * FROM t1;
INSERT INTO t1 SELECT * FROM t1;
INSERT INTO t1 SELECT * FROM t1;
INSERT INTO t1 SELECT * FROM t1;
INSERT INTO t1 SELECT * FROM t1;
INSERT INTO t1 SELECT * FROM t1;
INSERT INTO t1 SELECT * FROM t1;
} } {}
foreach {n q} {
1 "SELECT * FROM t1 ORDER BY a, b"
2 "SELECT rowid FROM t1 WHERE a MATCH 'five'"
3 "SELECT * FROM t1 WHERE a MATCH 'five'"
4 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'jk'"
5 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'tug* OR eight'"
} {
do_test backcompat-3.4 [list sql1 $q] [sql2 $q]
}
set alphabet "a b c d e f g h i j k l m n o p q r s t u v w x y z 1 2 3 4"
for {set i 0} {$i < 900} {incr i} {
set term "[lindex $alphabet [expr $i/30]][lindex $alphabet [expr $i%30]] "
sql1 "INSERT INTO t1 VALUES($i, '[string repeat $term 14]')"
}
foreach {n q} {
1 "SELECT * FROM t1 ORDER BY a, b"
2 "SELECT rowid FROM t1 WHERE a MATCH 'five'"
3 "SELECT * FROM t1 WHERE a MATCH 'five'"
4 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'jk'"
5 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'tug* OR eight'"
6 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'aa'"
7 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH '44'"
8 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'a*'"
} {
do_test backcompat-3.5 [list sql1 $q] [sql2 $q]
}
do_test backcompat-3.6 {
sql1 "SELECT optimize(t1) FROM t1 LIMIT 1"
} {{Index optimized}}
foreach {n q} {
1 "SELECT * FROM t1 ORDER BY a, b"
2 "SELECT rowid FROM t1 WHERE a MATCH 'five'"
3 "SELECT * FROM t1 WHERE a MATCH 'five'"
4 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'jk'"
5 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'tug* OR eight'"
6 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'aa'"
7 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH '44'"
8 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'a*'"
} {
do_test backcompat-3.7 [list sql1 $q] [sql2 $q]
}
# Now test that an incremental merge can be started by one version
# and finished by another. And that the integrity-check still
# passes.
do_test backcompat-3.8 {
sql1 {
DROP TABLE IF EXISTS t1;
DROP TABLE IF EXISTS t2;
CREATE TABLE t1(docid, words);
CREATE VIRTUAL TABLE t2 USING fts3(words);
}
code1 [list source $testdir/genesis.tcl]
code1 { fts_kjv_genesis }
sql1 {
INSERT INTO t2 SELECT words FROM t1;
INSERT INTO t2 SELECT words FROM t1;
INSERT INTO t2 SELECT words FROM t1;
INSERT INTO t2 SELECT words FROM t1;
INSERT INTO t2 SELECT words FROM t1;
INSERT INTO t2 SELECT words FROM t1;
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') }
} {}
}
}
}
}
#-------------------------------------------------------------------------
# Test that Rtree tables may be read/written by different versions of
# SQLite.
#
ifcapable rtree {
set contents {
CREATE VIRTUAL TABLE t1 USING rtree(id, x1, x2, y1, y2);
}
foreach {id x1 x2 y1 y2} {
1 -47.64 43.87 33.86 34.42 2 -21.51 17.32 2.05 31.04
3 -43.67 -38.33 -19.79 3.43 4 32.41 35.16 9.12 19.82
5 33.28 34.87 14.78 28.26 6 49.31 116.59 -9.87 75.09
7 -14.93 34.51 -17.64 64.09 8 -43.05 23.43 -1.19 69.44
9 44.79 133.56 28.09 80.30 10 -2.66 81.47 -41.38 -10.46
11 -42.89 -3.54 15.76 71.63 12 -3.50 84.96 -11.64 64.95
13 -45.69 26.25 11.14 55.06 14 -44.09 11.23 17.52 44.45
15 36.23 133.49 -19.38 53.67 16 -17.89 81.54 14.64 50.61
17 -41.97 -24.04 -39.43 28.95 18 -5.85 7.76 -6.38 47.02
19 18.82 27.10 42.82 100.09 20 39.17 113.45 26.14 73.47
21 22.31 103.17 49.92 106.05 22 -43.06 40.38 -1.75 76.08
23 2.43 57.27 -14.19 -3.83 24 -47.57 -4.35 8.93 100.06
25 -37.47 49.14 -29.11 8.81 26 -7.86 75.72 49.34 107.42
27 1.53 45.49 20.36 49.74 28 -48.48 32.54 28.81 54.45
29 2.67 39.77 -4.05 13.67 30 4.11 62.88 -47.44 -5.72
31 -21.47 51.75 37.25 116.09 32 45.59 111.37 -6.43 43.64
33 35.23 48.29 23.54 113.33 34 16.61 68.35 -14.69 65.97
35 13.98 16.60 48.66 102.87 36 19.74 23.84 31.15 77.27
37 -27.61 24.43 7.96 94.91 38 -34.77 12.05 -22.60 -6.29
39 -25.83 8.71 -13.48 -12.53 40 -17.11 -1.01 18.06 67.89
41 14.13 71.72 -3.78 39.25 42 23.75 76.00 -16.30 8.23
43 -39.15 28.63 38.12 125.88 44 48.62 86.09 36.49 102.95
45 -31.39 -21.98 2.52 89.78 46 5.65 56.04 15.94 89.10
47 18.28 95.81 46.46 143.08 48 30.93 102.82 -20.08 37.36
49 -20.78 -3.48 -5.58 35.46 50 49.85 90.58 -24.48 46.29
} {
if {$x1 >= $x2 || $y1 >= $y2} { error "$x1 $x2 $y1 $y2" }
append contents "INSERT INTO t1 VALUES($id, $x1, $x2, $y1, $y2);"
}
set queries {
1 "SELECT id FROM t1 WHERE x1>10 AND x2<44"
2 "SELECT id FROM t1 WHERE y1<100"
3 "SELECT id FROM t1 WHERE y1<100 AND x1>0"
4 "SELECT id FROM t1 WHERE y1>10 AND x1>0 AND x2<50 AND y2<550"
}
do_allbackcompat_test {
if {[code1 {set ::sqlite_options(fts3)}]
&& [code2 {set ::sqlite_options(fts3)}]
} {
do_test backcompat-4.1 { sql1 $contents } {}
foreach {n q} $::queries {
do_test backcompat-4.2.$n [list sql1 $q] [sql2 $q]
}
do_test backcompat-4.3 { sql1 {
INSERT INTO t1 SELECT id+100, x1+10.0, x2+10.0, y1-10.0, y2-10.0 FROM t1;
} } {}
foreach {n q} $::queries {
do_test backcompat-4.4.$n [list sql1 $q] [sql2 $q]
}
do_test backcompat-4.5 { sql2 {
INSERT INTO t1 SELECT id+200, x1+20.0, x2+20.0, y1-20.0, y2-20.0 FROM t1;
} } {}
foreach {n q} $::queries {
do_test backcompat-4.6.$n [list sql1 $q] [sql2 $q]
}
}
}
}
finish_test

981
testdata/tcl/backup.test vendored Normal file
View file

@ -0,0 +1,981 @@
# 2009 January 30
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this file is testing the sqlite3_backup_XXX API.
#
# $Id: backup.test,v 1.11 2009/06/05 17:09:12 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
do_not_use_codec
#---------------------------------------------------------------------
# Test organization:
#
# backup-1.*: Warm-body tests.
#
# backup-2.*: Test backup under various conditions. To and from in-memory
# databases. To and from empty/populated databases. etc.
#
# backup-3.*: Verify that the locking-page (pending byte page) is handled.
#
# backup-4.*: Test various error conditions.
#
# backup-5.*: Test the source database being modified during a backup.
#
# backup-6.*: Test the backup_remaining() and backup_pagecount() APIs.
#
# backup-7.*: Test SQLITE_BUSY and SQLITE_LOCKED errors.
#
# backup-8.*: Test multiple simultaneous backup operations.
#
# backup-9.*: Test that passing a negative argument to backup_step() is
# interpreted as "copy the whole file".
#
# backup-10.*: Test writing the source database mid backup.
#
proc data_checksum {db file} { $db one "SELECT md5sum(a, b) FROM ${file}.t1" }
proc test_contents {name db1 file1 db2 file2} {
$db2 eval {select * from sqlite_master}
$db1 eval {select * from sqlite_master}
set checksum [data_checksum $db2 $file2]
uplevel [list do_test $name [list data_checksum $db1 $file1] $checksum]
}
do_test backup-1.1 {
execsql {
BEGIN;
CREATE TABLE t1(a, b);
CREATE INDEX i1 ON t1(a, b);
INSERT INTO t1 VALUES(1, randstr(1000,1000));
INSERT INTO t1 VALUES(2, randstr(1000,1000));
INSERT INTO t1 VALUES(3, randstr(1000,1000));
INSERT INTO t1 VALUES(4, randstr(1000,1000));
INSERT INTO t1 VALUES(5, randstr(1000,1000));
COMMIT;
}
} {}
# Sanity check to verify that the [test_contents] proc works.
#
test_contents backup-1.2 db main db main
# Check that it is possible to create and finish backup operations.
#
do_test backup-1.3.1 {
delete_file test2.db
sqlite3 db2 test2.db
sqlite3_backup B db2 main db main
} {B}
do_test backup-1.3.2 {
B finish
} {SQLITE_OK}
do_test backup-1.3.3 {
info commands B
} {}
# Simplest backup operation. Backup test.db to test2.db. test2.db is
# initially empty. test.db uses the default page size.
#
do_test backup-1.4.1 {
sqlite3_backup B db2 main db main
} {B}
do_test backup-1.4.2 {
B step 200
} {SQLITE_DONE}
do_test backup-1.4.3 {
B finish
} {SQLITE_OK}
do_test backup-1.4.4 {
info commands B
} {}
test_contents backup-1.4.5 db2 main db main
db close
db2 close
#
# End of backup-1.* tests.
#---------------------------------------------------------------------
#---------------------------------------------------------------------
# The following tests, backup-2.*, are based on the following procedure:
#
# 1) Populate the source database.
# 2) Populate the destination database.
# 3) Run the backup to completion. (backup-2.*.1)
# 4) Integrity check the destination db. (backup-2.*.2)
# 5) Check that the contents of the destination db is the same as that
# of the source db. (backup-2.*.3)
#
# The test is run with all possible combinations of the following
# input parameters, except that if the destination is an in-memory
# database, the only page size tested is 1024 bytes (the same as the
# source page-size).
#
# * Source database is an in-memory database, OR
# * Source database is a file-backed database.
#
# * Target database is an in-memory database, OR
# * Target database is a file-backed database.
#
# * Destination database is a main file, OR
# * Destination database is an attached file, OR
# * Destination database is a temp database.
#
# * Target database is empty (zero bytes), OR
# * Target database is larger than the source, OR
# * Target database is smaller than the source.
#
# * Target database page-size is the same as the source, OR
# * Target database page-size is larger than the source, OR
# * Target database page-size is smaller than the source.
#
# * Each call to step copies a single page, OR
# * A single call to step copies the entire source database.
#
set iTest 1
foreach zSrcFile {test.db :memory:} {
foreach zDestFile {test2.db :memory:} {
foreach zOpenScript [list {
sqlite3 db $zSrcFile
sqlite3 db2 $zSrcFile
db2 eval "ATTACH '$zDestFile' AS bak"
set db_dest db2
set file_dest bak
} {
sqlite3 db $zSrcFile
sqlite3 db2 $zDestFile
set db_dest db2
set file_dest main
} {
sqlite3 db $zSrcFile
sqlite3 db2 $zDestFile
set db_dest db2
set file_dest temp
}] {
foreach rows_dest {0 3 10} {
foreach pgsz_dest {512 1024 2048 4096} {
foreach nPagePerStep {1 200} {
# Open the databases.
catch { delete_file test.db }
catch { delete_file test2.db }
eval $zOpenScript
# Set to true if copying to an in-memory destination. Copying to an
# in-memory destination is only possible if the initial destination
# page size is the same as the source page size (in this case 1024 bytes).
#
set isMemDest [expr { $zDestFile eq ":memory:" || $file_dest eq "temp" }]
if 0 {
puts -nonewline "Test $iTest: src=$zSrcFile dest=$zDestFile"
puts -nonewline " (as $db_dest.$file_dest)"
puts -nonewline " rows_dest=$rows_dest pgsz_dest=$pgsz_dest"
puts ""
}
if { $isMemDest==0 || $pgsz_dest==1024 || $rows_dest==0 } {
# Set up the content of the source database.
execsql {
PRAGMA page_size = 1024;
BEGIN;
CREATE TABLE t1(a, b);
CREATE INDEX i1 ON t1(a, b);
INSERT INTO t1 VALUES(1, randstr(1000,1000));
INSERT INTO t1 VALUES(2, randstr(1000,1000));
INSERT INTO t1 VALUES(3, randstr(1000,1000));
INSERT INTO t1 VALUES(4, randstr(1000,1000));
INSERT INTO t1 VALUES(5, randstr(1000,1000));
COMMIT;
}
# Set up the content of the target database.
execsql "PRAGMA ${file_dest}.page_size = ${pgsz_dest}" $db_dest
if {$rows_dest != 0} {
execsql "
BEGIN;
CREATE TABLE ${file_dest}.t1(a, b);
CREATE INDEX ${file_dest}.i1 ON t1(a, b);
" $db_dest
for {set ii 0} {$ii < $rows_dest} {incr ii} {
execsql "
INSERT INTO ${file_dest}.t1 VALUES(1, randstr(1000,1000))
" $db_dest
}
execsql COMMIT $db_dest
}
# Backup the source database.
do_test backup-2.$iTest.1 {
sqlite3_backup B $db_dest $file_dest db main
while {[B step $nPagePerStep]=="SQLITE_OK"} {}
B finish
} {SQLITE_OK}
# Run integrity check on the backup.
do_test backup-2.$iTest.2 {
execsql "PRAGMA ${file_dest}.integrity_check" $db_dest
} {ok}
test_contents backup-2.$iTest.3 db main $db_dest $file_dest
}
db close
catch {db2 close}
incr iTest
} } } } } }
#
# End of backup-2.* tests.
#---------------------------------------------------------------------
#---------------------------------------------------------------------
# These tests, backup-3.*, ensure that nothing goes wrong if either
# the source or destination database are large enough to include the
# the locking-page (the page that contains the range of bytes that
# the locks are applied to). These tests assume that the pending
# byte is at offset 0x00010000 (64KB offset), as set by tester.tcl,
# not at the 1GB offset as it usually is.
#
# The test procedure is as follows (same procedure as used for
# the backup-2.* tests):
#
# 1) Populate the source database.
# 2) Populate the destination database.
# 3) Run the backup to completion. (backup-3.*.1)
# 4) Integrity check the destination db. (backup-3.*.2)
# 5) Check that the contents of the destination db is the same as that
# of the source db. (backup-3.*.3)
#
# The test procedure is run with the following parameters varied:
#
# * Source database includes pending-byte page.
# * Source database does not include pending-byte page.
#
# * Target database includes pending-byte page.
# * Target database does not include pending-byte page.
#
# * Target database page-size is the same as the source, OR
# * Target database page-size is larger than the source, OR
# * Target database page-size is smaller than the source.
#
set iTest 1
foreach nSrcPg {10 64 65 66 100} {
foreach nDestRow {10 100} {
foreach nDestPgsz {512 1024 2048 4096} {
catch { delete_file test.db }
catch { delete_file test2.db }
sqlite3 db test.db
sqlite3 db2 test2.db
# Set up the content of the two databases.
#
execsql { PRAGMA page_size = 1024 }
execsql "PRAGMA page_size = $nDestPgsz" db2
foreach db {db db2} {
execsql {
BEGIN;
CREATE TABLE t1(a, b);
CREATE INDEX i1 ON t1(a, b);
COMMIT;
} $db
}
while {[file size test.db]/1024 < $nSrcPg} {
execsql { INSERT INTO t1 VALUES($ii, randstr(200,200)) }
}
for {set ii 0} {$ii < $nDestRow} {incr ii} {
execsql { INSERT INTO t1 VALUES($ii, randstr(1000,1000)) } db2
}
# Backup the source database.
do_test backup-3.$iTest.1 {
sqlite3_backup B db main db2 main
while {[B step 10]=="SQLITE_OK"} {}
B finish
} {SQLITE_OK}
# Run integrity check on the backup.
do_test backup-3.$iTest.2 {
execsql "PRAGMA integrity_check" db2
} {ok}
test_contents backup-3.$iTest.3 db main db2 main
db close
db2 close
incr iTest
}
}
}
#--------------------------------------------------------------------
do_test backup-3.$iTest.1 {
catch { forcedelete test.db }
catch { forcedelete test2.db }
sqlite3 db test.db
set iTab 1
db eval { PRAGMA page_size = 512 }
while {[file size test.db] <= $::sqlite_pending_byte} {
db eval "CREATE TABLE t${iTab}(a, b, c)"
incr iTab
}
sqlite3 db2 test2.db
db2 eval { PRAGMA page_size = 4096 }
while {[file size test2.db] < $::sqlite_pending_byte} {
db2 eval "CREATE TABLE t${iTab}(a, b, c)"
incr iTab
}
sqlite3_backup B db2 main db main
B step -1
} {SQLITE_DONE}
do_test backup-3.$iTest.2 {
B finish
} {SQLITE_OK}
#
# End of backup-3.* tests.
#---------------------------------------------------------------------
#---------------------------------------------------------------------
# The following tests, backup-4.*, test various error conditions:
#
# backup-4.1.*: Test invalid database names.
#
# backup-4.2.*: Test that the source database cannot be detached while
# a backup is in progress.
#
# backup-4.3.*: Test that the source database handle cannot be closed
# while a backup is in progress.
#
# backup-4.4.*: Test an attempt to specify the same handle for the
# source and destination databases.
#
# backup-4.5.*: Test that an in-memory destination with a different
# page-size to the source database is an error.
#
sqlite3 db test.db
sqlite3 db2 test2.db
do_test backup-4.1.1 {
catch { sqlite3_backup B db aux db2 main }
} {1}
do_test backup-4.1.2 {
sqlite3_errmsg db
} {unknown database aux}
do_test backup-4.1.3 {
catch { sqlite3_backup B db main db2 aux }
} {1}
do_test backup-4.1.4 {
sqlite3_errmsg db
} {unknown database aux}
do_test backup-4.2.1 {
catch { forcedelete test3.db }
catch { forcedelete test4.db }
execsql {
ATTACH 'test3.db' AS aux1;
CREATE TABLE aux1.t1(a, b);
}
execsql {
ATTACH 'test4.db' AS aux2;
CREATE TABLE aux2.t2(a, b);
} db2
sqlite3_backup B db aux1 db2 aux2
} {B}
do_test backup-4.2.2 {
catchsql { DETACH aux2 } db2
} {1 {database aux2 is locked}}
do_test backup-4.2.3 {
B step 50
} {SQLITE_DONE}
do_test backup-4.2.4 {
B finish
} {SQLITE_OK}
do_test backup-4.3.1 {
sqlite3_backup B db aux1 db2 aux2
} {B}
do_test backup-4.3.2 {
db2 cache flush
sqlite3_close db2
} {SQLITE_BUSY}
do_test backup-4.3.3 {
sqlite3_errmsg db2
} {unable to close due to unfinalized statements or unfinished backups}
do_test backup-4.3.4 {
B step 50
} {SQLITE_DONE}
do_test backup-4.3.5 {
B finish
} {SQLITE_OK}
do_test backup-4.4.1 {
set rc [catch {sqlite3_backup B db main db aux1}]
list $rc [sqlite3_errcode db] [sqlite3_errmsg db]
} {1 SQLITE_ERROR {source and destination must be distinct}}
db close
db2 close
do_test backup-4.5.1 {
catch { forcedelete test.db }
sqlite3 db test.db
sqlite3 db2 :memory:
execsql {
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 2);
}
execsql {
PRAGMA page_size = 4096;
CREATE TABLE t2(a, b);
INSERT INTO t2 VALUES(3, 4);
} db2
sqlite3_backup B db2 main db main
} {B}
do_test backup-4.5.2 {
B step 5000
} {SQLITE_READONLY}
do_test backup-4.5.3 {
B finish
} {SQLITE_READONLY}
db close
db2 close
#
# End of backup-4.* tests.
#---------------------------------------------------------------------
#---------------------------------------------------------------------
# The following tests, backup-5.*, test that the backup works properly
# when the source database is modified during the backup. Test cases
# are organized as follows:
#
# backup-5.x.1.*: Nothing special. Modify the database mid-backup.
#
# backup-5.x.2.*: Modify the database mid-backup so that one or more
# pages are written out due to cache stress. Then
# rollback the transaction.
#
# backup-5.x.3.*: Database is vacuumed.
#
# backup-5.x.4.*: Database is vacuumed and the page-size modified.
#
# backup-5.x.5.*: Database is shrunk via incr-vacuum.
#
# Each test is run three times, in the following configurations:
#
# 1) Backing up file-to-file. The writer writes via an external pager.
# 2) Backing up file-to-file. The writer writes via the same pager as
# is used by the backup operation.
# 3) Backing up memory-to-file.
#
set iTest 0
forcedelete bak.db-wal
foreach {writer file} {db test.db db3 test.db db :memory:} {
incr iTest
catch { delete_file bak.db }
sqlite3 db2 bak.db
catch { delete_file $file }
sqlite3 db $file
sqlite3 db3 $file
do_test backup-5.$iTest.1.1 {
execsql {
BEGIN;
CREATE TABLE t1(a, b);
CREATE INDEX i1 ON t1(a, b);
INSERT INTO t1 VALUES(1, randstr(1000,1000));
INSERT INTO t1 VALUES(2, randstr(1000,1000));
INSERT INTO t1 VALUES(3, randstr(1000,1000));
INSERT INTO t1 VALUES(4, randstr(1000,1000));
INSERT INTO t1 VALUES(5, randstr(1000,1000));
COMMIT;
}
expr {[execsql {PRAGMA page_count}] > 10}
} {1}
do_test backup-5.$iTest.1.2 {
sqlite3_backup B db2 main db main
B step 5
} {SQLITE_OK}
do_test backup-5.$iTest.1.3 {
execsql { UPDATE t1 SET a = a + 1 } $writer
B step 50
} {SQLITE_DONE}
do_test backup-5.$iTest.1.4 {
B finish
} {SQLITE_OK}
integrity_check backup-5.$iTest.1.5 db2
test_contents backup-5.$iTest.1.6 db main db2 main
do_test backup-5.$iTest.2.1 {
execsql {
PRAGMA cache_size = 10;
BEGIN;
INSERT INTO t1 SELECT '', randstr(1000,1000) FROM t1;
INSERT INTO t1 SELECT '', randstr(1000,1000) FROM t1;
INSERT INTO t1 SELECT '', randstr(1000,1000) FROM t1;
INSERT INTO t1 SELECT '', randstr(1000,1000) FROM t1;
COMMIT;
}
} {}
do_test backup-5.$iTest.2.2 {
sqlite3_backup B db2 main db main
B step 50
} {SQLITE_OK}
do_test backup-5.$iTest.2.3 {
execsql {
BEGIN;
UPDATE t1 SET a = a + 1;
ROLLBACK;
} $writer
B step 5000
} {SQLITE_DONE}
do_test backup-5.$iTest.2.4 {
B finish
} {SQLITE_OK}
integrity_check backup-5.$iTest.2.5 db2
test_contents backup-5.$iTest.2.6 db main db2 main
do_test backup-5.$iTest.3.1 {
execsql { UPDATE t1 SET b = randstr(1000,1000) }
} {}
do_test backup-5.$iTest.3.2 {
sqlite3_backup B db2 main db main
B step 50
} {SQLITE_OK}
do_test backup-5.$iTest.3.3 {
execsql { VACUUM } $writer
B step 5000
} {SQLITE_DONE}
do_test backup-5.$iTest.3.4 {
B finish
} {SQLITE_OK}
integrity_check backup-5.$iTest.3.5 db2
test_contents backup-5.$iTest.3.6 db main db2 main
do_test backup-5.$iTest.4.1 {
execsql { UPDATE t1 SET b = randstr(1000,1000) }
} {}
do_test backup-5.$iTest.4.2 {
sqlite3_backup B db2 main db main
B step 50
} {SQLITE_OK}
do_test backup-5.$iTest.4.3 {
execsql {
PRAGMA page_size = 2048;
VACUUM;
} $writer
B step 5000
} {SQLITE_DONE}
do_test backup-5.$iTest.4.4 {
B finish
} {SQLITE_OK}
integrity_check backup-5.$iTest.4.5 db2
test_contents backup-5.$iTest.4.6 db main db2 main
catch {db close}
catch {db2 close}
catch {db3 close}
catch { delete_file bak.db }
sqlite3 db2 bak.db
catch { delete_file $file }
sqlite3 db $file
sqlite3 db3 $file
do_test backup-5.$iTest.5.1 {
execsql {
PRAGMA auto_vacuum = incremental;
BEGIN;
CREATE TABLE t1(a, b);
CREATE INDEX i1 ON t1(a, b);
INSERT INTO t1 VALUES(1, randstr(1000,1000));
INSERT INTO t1 VALUES(2, randstr(1000,1000));
INSERT INTO t1 VALUES(3, randstr(1000,1000));
INSERT INTO t1 VALUES(4, randstr(1000,1000));
INSERT INTO t1 VALUES(5, randstr(1000,1000));
COMMIT;
}
} {}
do_test backup-5.$iTest.5.2 {
sqlite3_backup B db2 main db main
B step 8
} {SQLITE_OK}
do_test backup-5.$iTest.5.3 {
execsql {
DELETE FROM t1;
PRAGMA incremental_vacuum;
} $writer
B step 50
} {SQLITE_DONE}
do_test backup-5.$iTest.5.4 {
B finish
} {SQLITE_OK}
integrity_check backup-5.$iTest.5.5 db2
test_contents backup-5.$iTest.5.6 db main db2 main
catch {db close}
catch {db2 close}
catch {db3 close}
}
#
# End of backup-5.* tests.
#---------------------------------------------------------------------
#---------------------------------------------------------------------
# Test the sqlite3_backup_remaining() and backup_pagecount() APIs.
#
do_test backup-6.1 {
catch { forcedelete test.db }
catch { forcedelete test2.db }
sqlite3 db test.db
sqlite3 db2 test2.db
execsql {
BEGIN;
CREATE TABLE t1(a, b);
CREATE INDEX i1 ON t1(a, b);
INSERT INTO t1 VALUES(1, randstr(1000,1000));
INSERT INTO t1 VALUES(2, randstr(1000,1000));
INSERT INTO t1 VALUES(3, randstr(1000,1000));
INSERT INTO t1 VALUES(4, randstr(1000,1000));
INSERT INTO t1 VALUES(5, randstr(1000,1000));
COMMIT;
}
} {}
do_test backup-6.2 {
set nTotal [expr {[file size test.db]/1024}]
sqlite3_backup B db2 main db main
B step 1
} {SQLITE_OK}
do_test backup-6.3 {
B pagecount
} $nTotal
do_test backup-6.4 {
B remaining
} [expr $nTotal-1]
do_test backup-6.5 {
B step 5
list [B remaining] [B pagecount]
} [list [expr $nTotal-6] $nTotal]
do_test backup-6.6 {
execsql { CREATE TABLE t2(a PRIMARY KEY, b) }
B step 1
list [B remaining] [B pagecount]
} [list [expr $nTotal-5] [expr $nTotal+2]]
do_test backup-6.X {
B finish
} {SQLITE_OK}
catch {db close}
catch {db2 close}
#---------------------------------------------------------------------
# Test cases backup-7.* test that SQLITE_BUSY and SQLITE_LOCKED errors
# are returned correctly:
#
# backup-7.1.*: Source database is externally locked (return SQLITE_BUSY).
#
# backup-7.2.*: Attempt to step the backup process while a
# write-transaction is underway on the source pager (return
# SQLITE_LOCKED).
#
# backup-7.3.*: Destination database is externally locked (return SQLITE_BUSY).
#
do_test backup-7.0 {
catch { forcedelete test.db }
catch { forcedelete test2.db }
sqlite3 db2 test2.db
sqlite3 db test.db
execsql {
CREATE TABLE t1(a, b);
CREATE INDEX i1 ON t1(a, b);
INSERT INTO t1 VALUES(1, randstr(1000,1000));
INSERT INTO t1 SELECT a+ 1, randstr(1000,1000) FROM t1;
INSERT INTO t1 SELECT a+ 2, randstr(1000,1000) FROM t1;
INSERT INTO t1 SELECT a+ 4, randstr(1000,1000) FROM t1;
INSERT INTO t1 SELECT a+ 8, randstr(1000,1000) FROM t1;
INSERT INTO t1 SELECT a+16, randstr(1000,1000) FROM t1;
INSERT INTO t1 SELECT a+32, randstr(1000,1000) FROM t1;
INSERT INTO t1 SELECT a+64, randstr(1000,1000) FROM t1;
}
} {}
do_test backup-7.1.1 {
sqlite3_backup B db2 main db main
B step 5
} {SQLITE_OK}
do_test backup-7.1.2 {
sqlite3 db3 test.db
execsql { BEGIN EXCLUSIVE } db3
B step 5
} {SQLITE_BUSY}
do_test backup-7.1.3 {
execsql { ROLLBACK } db3
B step 5
} {SQLITE_OK}
do_test backup-7.2.1 {
execsql {
BEGIN;
INSERT INTO t1 VALUES(1, 4);
}
} {}
do_test backup-7.2.2 {
B step 5000
} {SQLITE_BUSY}
do_test backup-7.2.3 {
execsql { ROLLBACK }
B step 5000
} {SQLITE_DONE}
do_test backup-7.2.4 {
B finish
} {SQLITE_OK}
test_contents backup-7.2.5 db main db2 main
integrity_check backup-7.3.6 db2
do_test backup-7.3.1 {
db2 close
db3 close
forcedelete test2.db
sqlite3 db2 test2.db
sqlite3 db3 test2.db
sqlite3_backup B db2 main db main
execsql { BEGIN ; CREATE TABLE t2(a, b); } db3
B step 5
} {SQLITE_BUSY}
do_test backup-7.3.2 {
execsql { COMMIT } db3
B step 5000
} {SQLITE_DONE}
do_test backup-7.3.3 {
B finish
} {SQLITE_OK}
test_contents backup-7.3.4 db main db2 main
integrity_check backup-7.3.5 db2
catch { db2 close }
catch { db3 close }
#-----------------------------------------------------------------------
# The following tests, backup-8.*, test attaching multiple backup
# processes to the same source database. Also, reading from the source
# database while a read transaction is active.
#
# These tests reuse the database "test.db" left over from backup-7.*.
#
do_test backup-8.1 {
catch { forcedelete test2.db }
catch { forcedelete test3.db }
sqlite3 db2 test2.db
sqlite3 db3 test3.db
sqlite3_backup B2 db2 main db main
sqlite3_backup B3 db3 main db main
list [B2 finish] [B3 finish]
} {SQLITE_OK SQLITE_OK}
do_test backup-8.2 {
sqlite3_backup B3 db3 main db main
sqlite3_backup B2 db2 main db main
list [B2 finish] [B3 finish]
} {SQLITE_OK SQLITE_OK}
do_test backup-8.3 {
sqlite3_backup B2 db2 main db main
sqlite3_backup B3 db3 main db main
B2 step 5
} {SQLITE_OK}
do_test backup-8.4 {
execsql {
BEGIN;
SELECT * FROM sqlite_master;
}
B3 step 5
} {SQLITE_OK}
do_test backup-8.5 {
list [B3 step 5000] [B3 finish]
} {SQLITE_DONE SQLITE_OK}
do_test backup-8.6 {
list [B2 step 5000] [B2 finish]
} {SQLITE_DONE SQLITE_OK}
test_contents backup-8.7 db main db2 main
test_contents backup-8.8 db main db3 main
do_test backup-8.9 {
execsql { PRAGMA lock_status }
} {main shared temp closed}
do_test backup-8.10 {
execsql COMMIT
} {}
catch { db2 close }
catch { db3 close }
#-----------------------------------------------------------------------
# The following tests, backup-9.*, test that:
#
# * Passing 0 as an argument to sqlite3_backup_step() means no pages
# are backed up (backup-9.1.*), and
# * Passing a negative value as an argument to sqlite3_backup_step() means
# all pages are backed up (backup-9.2.*).
#
# These tests reuse the database "test.db" left over from backup-7.*.
#
do_test backup-9.1.1 {
sqlite3 db2 test2.db
sqlite3_backup B db2 main db main
B step 1
} {SQLITE_OK}
do_test backup-9.1.2 {
set nRemaining [B remaining]
expr {$nRemaining>100}
} {1}
do_test backup-9.1.3 {
B step 0
} {SQLITE_OK}
do_test backup-9.1.4 {
B remaining
} $nRemaining
do_test backup-9.2.1 {
B step -1
} {SQLITE_DONE}
do_test backup-9.2.2 {
B remaining
} {0}
do_test backup-9.2.3 {
B finish
} {SQLITE_OK}
catch {db2 close}
ifcapable memorymanage {
db close
forcedelete test.db
forcedelete bak.db
sqlite3 db test.db
sqlite3 db2 test.db
sqlite3 db3 bak.db
do_test backup-10.1.1 {
execsql {
BEGIN;
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, randstr(1000,1000));
INSERT INTO t1 VALUES(2, randstr(1000,1000));
INSERT INTO t1 VALUES(3, randstr(1000,1000));
INSERT INTO t1 VALUES(4, randstr(1000,1000));
INSERT INTO t1 VALUES(5, randstr(1000,1000));
CREATE INDEX i1 ON t1(a, b);
COMMIT;
}
} {}
do_test backup-10.1.2 {
sqlite3_backup B db3 main db2 main
B step 5
} {SQLITE_OK}
do_test backup-10.1.3 {
execsql {
UPDATE t1 SET b = randstr(500,500);
}
} {}
sqlite3_release_memory [expr 1024*1024]
do_test backup-10.1.3 {
B step 50
} {SQLITE_DONE}
do_test backup-10.1.4 {
B finish
} {SQLITE_OK}
do_test backup-10.1.5 {
execsql { PRAGMA integrity_check } db3
} {ok}
db2 close
db3 close
}
#-----------------------------------------------------------------------
# Test that if the database is written to via the same database handle being
# used as the source by a backup operation:
#
# 10.1.*: If the db is in-memory, the backup is restarted.
# 10.2.*: If the db is a file, the backup is not restarted.
#
db close
forcedelete test.db test.db-journal
foreach {tn file rc} {
1 test.db SQLITE_DONE
2 :memory: SQLITE_OK
} {
do_test backup-10.$tn.1 {
sqlite3 db $file
execsql {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b BLOB);
BEGIN;
INSERT INTO t1 VALUES(NULL, randomblob(200));
INSERT INTO t1 SELECT NULL, randomblob(200) FROM t1;
INSERT INTO t1 SELECT NULL, randomblob(200) FROM t1;
INSERT INTO t1 SELECT NULL, randomblob(200) FROM t1;
INSERT INTO t1 SELECT NULL, randomblob(200) FROM t1;
INSERT INTO t1 SELECT NULL, randomblob(200) FROM t1;
INSERT INTO t1 SELECT NULL, randomblob(200) FROM t1;
INSERT INTO t1 SELECT NULL, randomblob(200) FROM t1;
INSERT INTO t1 SELECT NULL, randomblob(200) FROM t1;
COMMIT;
SELECT count(*) FROM t1;
}
} {256}
do_test backup-10.$tn.2 {
set pgs [execsql {pragma page_count}]
expr {$pgs > 50 && $pgs < 75}
} {1}
do_test backup-10.$tn.3 {
forcedelete bak.db bak.db-journal
sqlite3 db2 bak.db
sqlite3_backup B db2 main db main
B step 50
} {SQLITE_OK}
do_test backup-10.$tn.4 {
execsql { UPDATE t1 SET b = randomblob(200) WHERE a IN (1, 250) }
} {}
do_test backup-10.$tn.5 {
B step 50
} $rc
do_test backup-10.$tn.6 {
B finish
} {SQLITE_OK}
db2 close
}
# 2021-01-31 https://sqlite.org/forum/forumpost/8b39fbf3e7
#
do_test backup-11.1 {
sqlite3 db1 :memory:
sqlite3 db2 :memory:
sqlite3_backup B db1 main db2 temp
B finish
} {SQLITE_OK}
finish_test

187
testdata/tcl/backup2.test vendored Normal file
View file

@ -0,0 +1,187 @@
# 2009 February 4
#
# 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 "backup" and "restore" methods
# of the TCL interface - methods which are based on the
# sqlite3_backup_XXX API.
#
# $Id: backup2.test,v 1.4 2009/04/07 14:14:23 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
do_not_use_codec
ifcapable !trigger||!view { finish_test ; return }
# Fill a database with test data.
#
do_test backup2-1 {
db eval {
CREATE TABLE t1(x);
INSERT INTO t1 VALUES(randstr(8000,8000));
INSERT INTO t1 VALUES(randstr(8000,8000));
INSERT INTO t1 VALUES(randstr(8000,8000));
INSERT INTO t1 VALUES(randstr(8000,8000));
INSERT INTO t1 VALUES(randstr(8000,8000));
CREATE VIEW v1 AS SELECT substr(x,10,10) FROM t1;
CREATE TABLE t2(a,b);
INSERT INTO t2 VALUES(1,2);
INSERT INTO t2 VALUES(2,4);
INSERT INTO t2 SELECT a+2, (a+2)*2 FROM t2;
INSERT INTO t2 SELECT a+4, (a+4)*2 FROM t2;
INSERT INTO t2 SELECT a+8, (a+8)*2 FROM t2;
INSERT INTO t2 SELECT a+16, (a+16)*2 FROM t2;
INSERT INTO t2 SELECT a+32, (a+32)*2 FROM t2;
INSERT INTO t2 SELECT a+64, (a+64)*2 FROM t2;
INSERT INTO t2 SELECT a+128, (a+128)*2 FROM t2;
CREATE INDEX t2i1 ON t2(a,b);
CREATE TRIGGER r1 AFTER INSERT ON t2 BEGIN
SELECT 'hello';
END;
ANALYZE;
PRAGMA integrity_check;
}
} {ok}
# Remember a check-sum on the database file.
#
unset -nocomplain cksum
set cksum [dbcksum db main]
# Make a backup of the test data. Verify that the backup copy
# is identical to the original.
#
do_test backup2-2 {
forcedelete bu1.db
db backup bu1.db
sqlite3 db2 bu1.db
dbcksum db2 main
} $cksum
# Delete the original. Restore from backup. Verify the content is
# unchanged.
#
do_test backup2-3.1 {
db close
forcedelete test.db test.db-journal
sqlite3 db test.db
db2 eval {BEGIN EXCLUSIVE}
set rc [catch {db restore bu1.db} res]
lappend rc $res
db2 eval {ROLLBACK}
set rc
} {1 {restore failed: source database busy}}
do_test backup2-3.2 {
db close
forcedelete test.db test.db-journal
sqlite3 db test.db
db restore bu1.db
dbcksum db main
} $cksum
# Use alternative databases - other than "main".
#
do_test backup2-4 {
db restore temp bu1.db
dbcksum db temp
} $cksum
do_test backup2-5 {
db2 close
forcedelete bu1.db bu2.db
db backup temp bu2.db
sqlite3 db2 bu2.db
dbcksum db2 main
} $cksum
# Try to backup to a readonly file.
#
do_test backup2-6 {
db2 close
catch {file attributes bu2.db -permissions r--------}
catch {file attributes bu2.db -readonly 1}
set rc [catch {db backup temp bu2.db} res]
lappend rc $res
} {1 {backup failed: attempt to write a readonly database}}
# Try to backup to something that is not a database file.
#
do_test backup2-7 {
catch {file attributes bu2.db -readonly 0}
catch {file attributes bu2.db -permissions rw-------}
set out [open bu2.db w]
puts $out "This is not a valid database file"
close $out
set rc [catch {db backup temp bu2.db} res]
lappend rc $res
} {1 {backup failed: file is not a database}}
# Try to backup database that does not exist
#
do_test backup2-8 {
forcedelete bu1.db
set rc [catch {db backup aux1 bu1.db} res]
lappend rc $res
} {1 {backup failed: unknown database aux1}}
# Invalid syntax on the backup method
#
do_test backup2-9 {
set rc [catch {db backup} res]
lappend rc $res
} {1 {wrong # args: should be "db backup ?DATABASE? FILENAME"}}
# Try to restore from an unreadable file.
#
if {$tcl_platform(platform)=="windows"} {
set msg {cannot open source database: unable to open database file}
} elseif {[string match *BSD $tcl_platform(os)]} {
set msg {}
} else {
set msg {cannot open source database: disk I/O error}
}
do_test backup2-10 {
forcedelete bu3.db
file mkdir bu3.db
set rc [catch {db restore temp bu3.db} res]
if {[string match *BSD $tcl_platform(os)]} { set res "" }
list $rc $res
} [list 1 $msg]
# Try to restore from something that is not a database file.
#
do_test backup2-11 {
set rc [catch {db restore temp bu2.db} res]
lappend rc $res
} {1 {restore failed: file is not a database}}
# Try to restore a database that does not exist
#
do_test backup2-12 {
set rc [catch {db restore aux1 bu2.db} res]
lappend rc $res
} {1 {restore failed: unknown database aux1}}
do_test backup2-13 {
forcedelete bu4.db
set rc [catch {db restore bu4.db} res]
lappend rc $res
} {1 {cannot open source database: unable to open database file}}
# Invalid syntax on the restore method
#
do_test backup2-14 {
set rc [catch {db restore} res]
lappend rc $res
} {1 {wrong # args: should be "db restore ?DATABASE? FILENAME"}}
forcedelete bu1.db bu2.db bu3.db bu4.db
finish_test

108
testdata/tcl/backup4.test vendored Normal file
View file

@ -0,0 +1,108 @@
# 2012 October 13
#
# 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.
#
#***********************************************************************
#
# The tests in this file verify that if an empty database (zero bytes in
# size) is used as the source of a backup operation, the final destination
# database is one page in size.
#
# The destination must consist of at least one page as truncating a
# database file to zero bytes is equivalent to resetting the database
# schema cookie and change counter. Doing that could cause other clients
# to become confused and continue using out-of-date cache data.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix backup4
# The codec logic does not work for zero-length database files. A database
# file must contain at least one page in order to be recognized as an
# encrypted database.
do_not_use_codec
#-------------------------------------------------------------------------
# At one point this test was failing because [db] was using an out of
# date schema in test case 1.2.
#
do_execsql_test 1.0 {
CREATE TABLE t1(x, y, UNIQUE(x, y));
INSERT INTO t1 VALUES('one', 'two');
SELECT * FROM t1 WHERE x='one';
PRAGMA integrity_check;
} {one two ok}
do_test 1.1 {
sqlite3 db1 :memory:
db1 backup test.db
sqlite3 db1 test.db
db1 eval {
CREATE TABLE t1(x, y);
INSERT INTO t1 VALUES('one', 'two');
}
db1 close
} {}
do_execsql_test 1.2 {
SELECT * FROM t1 WHERE x='one';
PRAGMA integrity_check;
} {one two ok}
db close
forcedelete test.db
forcedelete test.db2
sqlite3 db test.db
#-------------------------------------------------------------------------
# Test that if the source is zero bytes, the destination database
# consists of a single page only.
#
do_execsql_test 2.1 {
CREATE TABLE t1(a, b);
CREATE INDEX i1 ON t1(a, b);
}
do_test 2.2 { file size test.db } [expr $AUTOVACUUM ? 4096 : 3072]
do_test 2.3 {
sqlite3 db1 test.db2
db1 backup test.db
db1 close
file size test.db
} {1024}
do_test 2.4 { file size test.db2 } 0
db close
forcedelete test.db
forcedelete test.db2
sqlite3 db test.db
#-------------------------------------------------------------------------
# Test that if the destination has a page-size larger than the implicit
# page-size of the source, the final destination database still consists
# of a single page.
#
do_execsql_test 3.1 {
PRAGMA page_size = 4096;
CREATE TABLE t1(a, b);
CREATE INDEX i1 ON t1(a, b);
}
do_test 3.2 { file size test.db } [expr $AUTOVACUUM ? 16384 : 12288]
do_test 3.3 {
sqlite3 db1 test.db2
db1 backup test.db
db1 close
file size test.db
} {1024}
do_test 3.4 { file size test.db2 } 0
finish_test

65
testdata/tcl/backup5.test vendored Normal file
View file

@ -0,0 +1,65 @@
# 2014 November 13
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix backup5
forcedelete test2.db
do_execsql_test 1.0 {
CREATE TABLE t1(a, b);
CREATE TABLE t2(a, b);
INSERT INTO t2 VALUES(1, 1);
INSERT INTO t2 VALUES(2, 2);
INSERT INTO t2 VALUES(3, 3);
}
do_test 1.1 {
forcecopy test.db test.db2
db eval {
DROP TABLE t2;
INSERT INTO t1 VALUES(zeroblob(1000), zeroblob(1000));
INSERT INTO t1 VALUES(randomblob(1000), randomblob(1000));
}
} {}
do_test 1.2 {
sqlite3 db2 test.db2
set stmt [sqlite3_prepare_v2 db2 "SELECT * FROM t2" -1 dummy]
sqlite3_step $stmt
} {SQLITE_ROW}
do_test 1.3 {
list [catch { sqlite3_backup B db2 main db main } msg] $msg
} {1 {sqlite3_backup_init() failed}}
do_test 1.4 {
sqlite3_errmsg db2
} {destination database is in use}
do_test 1.5 {
sqlite3_reset $stmt
sqlite3_backup B db2 main db main
B step 200
B finish
} {SQLITE_OK}
do_test 1.6 {
list [sqlite3_step $stmt] [sqlite3_finalize $stmt]
} {SQLITE_ERROR SQLITE_ERROR}
do_test 1.7 {
sqlite3_errmsg db2
} {no such table: t2}
finish_test

286
testdata/tcl/backup_ioerr.test vendored Normal file
View file

@ -0,0 +1,286 @@
# 2009 January 30
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this file is testing the handling of IO errors by the
# sqlite3_backup_XXX APIs.
#
# $Id: backup_ioerr.test,v 1.3 2009/04/10 18:41:01 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
proc data_checksum {db file} {
$db one "SELECT md5sum(a, b) FROM ${file}.t1"
}
proc test_contents {name db1 file1 db2 file2} {
$db2 eval {select * from sqlite_master}
$db1 eval {select * from sqlite_master}
set checksum [data_checksum $db2 $file2]
uplevel [list do_test $name [list data_checksum $db1 $file1] $checksum]
}
#--------------------------------------------------------------------
# This proc creates a database of approximately 290 pages. Depending
# on whether or not auto-vacuum is configured. Test cases backup_ioerr-1.*
# verify nothing more than this assumption.
#
proc populate_database {db {xtra_large 0}} {
execsql {
BEGIN;
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, randstr(1000,1000));
INSERT INTO t1 SELECT a+ 1, randstr(1000,1000) FROM t1;
INSERT INTO t1 SELECT a+ 2, randstr(1000,1000) FROM t1;
INSERT INTO t1 SELECT a+ 4, randstr(1000,1000) FROM t1;
INSERT INTO t1 SELECT a+ 8, randstr(1000,1000) FROM t1;
INSERT INTO t1 SELECT a+16, randstr(1000,1000) FROM t1;
INSERT INTO t1 SELECT a+32, randstr(1000,1000) FROM t1;
CREATE INDEX i1 ON t1(b);
COMMIT;
} $db
if {$xtra_large} {
execsql { INSERT INTO t1 SELECT a+64, randstr(1000,1000) FROM t1 } $db
}
}
do_test backup_ioerr-1.1 {
populate_database db
set nPage [expr {[file size test.db] / 1024}]
expr {$nPage>130 && $nPage<160}
} {1}
do_test backup_ioerr-1.2 {
expr {[file size test.db] > $sqlite_pending_byte}
} {1}
do_test backup_ioerr-1.3 {
db close
forcedelete test.db
} {}
# Turn off IO error simulation.
#
proc clear_ioerr_simulation {} {
set ::sqlite_io_error_hit 0
set ::sqlite_io_error_hardhit 0
set ::sqlite_io_error_pending 0
set ::sqlite_io_error_persist 0
}
#--------------------------------------------------------------------
# The following procedure runs with SQLite's IO error simulation
# enabled.
#
# 1) Start with a reasonably sized database. One that includes the
# pending-byte (locking) page.
#
# 2) Open a backup process. Set the cache-size for the destination
# database to 10 pages only.
#
# 3) Step the backup process N times to partially backup the database
# file. If an IO error is reported, then the backup process is
# concluded with a call to backup_finish().
#
# If an IO error occurs, verify that:
#
# * the call to backup_step() returns an SQLITE_IOERR_XXX error code.
#
# * after the failed call to backup_step() but before the call to
# backup_finish() the destination database handle error code and
# error message remain unchanged.
#
# * the call to backup_finish() returns an SQLITE_IOERR_XXX error code.
#
# * following the call to backup_finish(), the destination database
# handle has been populated with an error code and error message.
#
# 4) Write to the database via the source database connection. Check
# that:
#
# * If an IO error occurs while writing the source database, the
# write operation should report an IO error. The backup should
# proceed as normal.
#
# * If an IO error occurs while updating the backup, the write
# operation should proceed normally. The error should be reported
# from the next call to backup_step() (in step 5 of this test
# procedure).
#
# 5) Step the backup process to finish the backup. If an IO error is
# reported, then the backup process is concluded with a call to
# backup_finish().
#
# Test that if an IO error occurs, or if one occurred while updating
# the backup database during step 4, then the conditions listed
# under step 3 are all true.
#
# 6) Finish the backup process.
#
# * If the backup succeeds (backup_finish() returns SQLITE_OK), then
# the contents of the backup database should match that of the
# source database.
#
# * If the backup fails (backup_finish() returns other than SQLITE_OK),
# then the contents of the backup database should be as they were
# before the operation was started.
#
# The following factors are varied:
#
# * Destination database is initially larger than the source database, OR
# * Destination database is initially smaller than the source database.
#
# * IO errors are transient, OR
# * IO errors are persistent.
#
# * Destination page-size is smaller than the source.
# * Destination page-size is the same as the source.
# * Destination page-size is larger than the source.
#
set iTest 1
foreach bPersist {0 1} {
foreach iDestPagesize {512 1024 4096} {
foreach zSetupBak [list "" {populate_database ddb 1}] {
incr iTest
set bStop 0
for {set iError 1} {$bStop == 0} {incr iError} {
# Disable IO error simulation.
clear_ioerr_simulation
catch { ddb close }
catch { sdb close }
catch { forcedelete test.db }
catch { forcedelete bak.db }
# Open the source and destination databases.
sqlite3 sdb test.db
sqlite3 ddb bak.db
# Step 1: Populate the source and destination databases.
populate_database sdb
ddb eval "PRAGMA page_size = $iDestPagesize"
ddb eval "PRAGMA cache_size = 10"
eval $zSetupBak
# Step 2: Open the backup process.
sqlite3_backup B ddb main sdb main
# Enable IO error simulation.
set ::sqlite_io_error_pending $iError
set ::sqlite_io_error_persist $bPersist
# Step 3: Partially backup the database. If an IO error occurs, check
# a few things then skip to the next iteration of the loop.
#
set rc [B step 100]
if {$::sqlite_io_error_hardhit} {
do_test backup_ioerr-$iTest.$iError.1 {
string match SQLITE_IOERR* $rc
} {1}
do_test backup_ioerr-$iTest.$iError.2 {
list [sqlite3_errcode ddb] [sqlite3_errmsg ddb]
} {SQLITE_OK {not an error}}
set rc [B finish]
do_test backup_ioerr-$iTest.$iError.3 {
string match SQLITE_IOERR* $rc
} {1}
do_test backup_ioerr-$iTest.$iError.4 {
sqlite3_errmsg ddb
} {disk I/O error}
clear_ioerr_simulation
sqlite3 ddb bak.db
integrity_check backup_ioerr-$iTest.$iError.5 ddb
continue
}
# No IO error was encountered during step 3. Check that backup_step()
# returned SQLITE_OK before proceding.
do_test backup_ioerr-$iTest.$iError.6 {
expr {$rc eq "SQLITE_OK"}
} {1}
# Step 4: Write to the source database.
set rc [catchsql { UPDATE t1 SET b = randstr(1000,1000) WHERE a < 50 } sdb]
if {[lindex $rc 0] && $::sqlite_io_error_persist==0} {
# The IO error occurred while updating the source database. In this
# case the backup should be able to continue.
set rc [B step 5000]
if { $rc != "SQLITE_IOERR_UNLOCK" } {
do_test backup_ioerr-$iTest.$iError.7 {
list [B step 5000] [B finish]
} {SQLITE_DONE SQLITE_OK}
clear_ioerr_simulation
test_contents backup_ioerr-$iTest.$iError.8 ddb main sdb main
integrity_check backup_ioerr-$iTest.$iError.9 ddb
} else {
do_test backup_ioerr-$iTest.$iError.10 {
B finish
} {SQLITE_IOERR_UNLOCK}
}
clear_ioerr_simulation
sqlite3 ddb bak.db
integrity_check backup_ioerr-$iTest.$iError.11 ddb
continue
}
# Step 5: Finish the backup operation. If an IO error occurs, check that
# it is reported correctly and skip to the next iteration of the loop.
#
set rc [B step 5000]
if {$rc != "SQLITE_DONE"} {
do_test backup_ioerr-$iTest.$iError.12 {
string match SQLITE_IOERR* $rc
} {1}
do_test backup_ioerr-$iTest.$iError.13 {
list [sqlite3_errcode ddb] [sqlite3_errmsg ddb]
} {SQLITE_OK {not an error}}
set rc [B finish]
do_test backup_ioerr-$iTest.$iError.14 {
string match SQLITE_IOERR* $rc
} {1}
do_test backup_ioerr-$iTest.$iError.15 {
sqlite3_errmsg ddb
} {disk I/O error}
clear_ioerr_simulation
sqlite3 ddb bak.db
integrity_check backup_ioerr-$iTest.$iError.16 ddb
continue
}
# The backup was successfully completed.
#
do_test backup_ioerr-$iTest.$iError.17 {
list [set rc] [B finish]
} {SQLITE_DONE SQLITE_OK}
clear_ioerr_simulation
sqlite3 sdb test.db
sqlite3 ddb bak.db
test_contents backup_ioerr-$iTest.$iError.18 ddb main sdb main
integrity_check backup_ioerr-$iTest.$iError.19 ddb
set bStop [expr $::sqlite_io_error_pending<=0]
}}}}
catch { sdb close }
catch { ddb close }
finish_test

119
testdata/tcl/backup_malloc.test vendored Normal file
View file

@ -0,0 +1,119 @@
# 2009 January 30
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this file is testing the handling of OOM errors by the
# sqlite3_backup_XXX APIs.
#
# $Id: backup_malloc.test,v 1.2 2009/02/04 22:46:47 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
do_malloc_test backup_malloc-1 -tclprep {
execsql {
PRAGMA cache_size = 10;
BEGIN;
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, randstr(1000,1000));
INSERT INTO t1 SELECT a+ 1, randstr(1000,1000) FROM t1;
INSERT INTO t1 SELECT a+ 2, randstr(1000,1000) FROM t1;
INSERT INTO t1 SELECT a+ 4, randstr(1000,1000) FROM t1;
INSERT INTO t1 SELECT a+ 8, randstr(1000,1000) FROM t1;
INSERT INTO t1 SELECT a+16, randstr(1000,1000) FROM t1;
INSERT INTO t1 SELECT a+32, randstr(1000,1000) FROM t1;
INSERT INTO t1 SELECT a+64, randstr(1000,1000) FROM t1;
CREATE INDEX i1 ON t1(b);
COMMIT;
}
sqlite3 db2 test2.db
execsql { PRAGMA cache_size = 10 } db2
} -tclbody {
# Create a backup object.
#
set rc [catch {sqlite3_backup B db2 main db main}]
if {$rc && [sqlite3_errcode db2] == "SQLITE_NOMEM"} {
error "out of memory"
}
# Run the backup process some.
#
set rc [B step 50]
if {$rc == "SQLITE_NOMEM" || $rc == "SQLITE_IOERR_NOMEM"} {
error "out of memory"
}
# Update the database.
#
execsql { UPDATE t1 SET a = a + 1 }
# Finish doing the backup.
#
set rc [B step 5000]
if {$rc == "SQLITE_NOMEM" || $rc == "SQLITE_IOERR_NOMEM"} {
error "out of memory"
}
# Finalize the backup.
B finish
} -cleanup {
catch { B finish }
catch { db2 close }
}
do_malloc_test backup_malloc-2 -tclprep {
sqlite3 db2 test2.db
} -tclbody {
set rc [catch {sqlite3_backup B db2 temp db main}]
set errcode [sqlite3_errcode db2]
if {$rc && ($errcode == "SQLITE_NOMEM" || $errcode == "SQLITE_IOERR_NOMEM")} {
error "out of memory"
}
} -cleanup {
catch { B finish }
db2 close
}
reset_db
do_execsql_test 3.0 {
PRAGMA page_size = 16384;
BEGIN;
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 2);
COMMIT;
}
do_faultsim_test 3 -faults oom* -prep {
catch { db close }
catch { db2 close }
forcedelete test2.db
sqlite3 db2 test2.db
sqlite3 db test.db
sqlite3_backup B db2 main db main
} -body {
set rc [B step 50]
if {$rc == "SQLITE_NOMEM" || $rc == "SQLITE_IOERR_NOMEM"} {
error "out of memory"
}
} -test {
faultsim_test_result {0 {}}
faultsim_integrity_check
# Finalize the backup.
catch { B finish }
}
finish_test

143
testdata/tcl/badutf.test vendored Normal file
View file

@ -0,0 +1,143 @@
# 2007 May 15
#
# 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.
#
# This file checks to make sure SQLite is able to gracefully
# handle malformed UTF-8.
#
# $Id: badutf.test,v 1.2 2007/09/12 17:01:45 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
do_test badutf-1.1 {
db eval {PRAGMA encoding=UTF8}
sqlite3_exec db {SELECT hex('%80') AS x}
} {0 {x 80}}
do_test badutf-1.2 {
sqlite3_exec db {SELECT hex('%81') AS x}
} {0 {x 81}}
do_test badutf-1.3 {
sqlite3_exec db {SELECT hex('%bf') AS x}
} {0 {x BF}}
do_test badutf-1.4 {
sqlite3_exec db {SELECT hex('%c0') AS x}
} {0 {x C0}}
do_test badutf-1.5 {
sqlite3_exec db {SELECT hex('%e0') AS x}
} {0 {x E0}}
do_test badutf-1.6 {
sqlite3_exec db {SELECT hex('%f0') AS x}
} {0 {x F0}}
do_test badutf-1.7 {
sqlite3_exec db {SELECT hex('%ff') AS x}
} {0 {x FF}}
sqlite3 db2 {}
ifcapable utf16 {
do_test badutf-1.10 {
db2 eval {PRAGMA encoding=UTF16be}
sqlite3_exec db2 {SELECT hex('%80') AS x}
} {0 {x 0080}}
do_test badutf-1.11 {
sqlite3_exec db2 {SELECT hex('%81') AS x}
} {0 {x 0081}}
do_test badutf-1.12 {
sqlite3_exec db2 {SELECT hex('%bf') AS x}
} {0 {x 00BF}}
do_test badutf-1.13 {
sqlite3_exec db2 {SELECT hex('%c0') AS x}
} {0 {x FFFD}}
do_test badutf-1.14 {
sqlite3_exec db2 {SELECT hex('%c1') AS x}
} {0 {x FFFD}}
do_test badutf-1.15 {
sqlite3_exec db2 {SELECT hex('%c0%bf') AS x}
} {0 {x FFFD}}
do_test badutf-1.16 {
sqlite3_exec db2 {SELECT hex('%c1%bf') AS x}
} {0 {x FFFD}}
do_test badutf-1.17 {
sqlite3_exec db2 {SELECT hex('%c3%bf') AS x}
} {0 {x 00FF}}
do_test badutf-1.18 {
sqlite3_exec db2 {SELECT hex('%e0') AS x}
} {0 {x FFFD}}
do_test badutf-1.19 {
sqlite3_exec db2 {SELECT hex('%f0') AS x}
} {0 {x FFFD}}
do_test badutf-1.20 {
sqlite3_exec db2 {SELECT hex('%ff') AS x}
} {0 {x FFFD}}
}
ifcapable bloblit {
do_test badutf-2.1 {
sqlite3_exec db {SELECT '%80'=CAST(x'80' AS text) AS x}
} {0 {x 1}}
do_test badutf-2.2 {
sqlite3_exec db {SELECT CAST('%80' AS blob)=x'80' AS x}
} {0 {x 1}}
}
do_test badutf-3.1 {
sqlite3_exec db {SELECT length('%80') AS x}
} {0 {x 1}}
do_test badutf-3.2 {
sqlite3_exec db {SELECT length('%61%62%63') AS x}
} {0 {x 3}}
do_test badutf-3.3 {
sqlite3_exec db {SELECT length('%7f%80%81') AS x}
} {0 {x 3}}
do_test badutf-3.4 {
sqlite3_exec db {SELECT length('%61%c0') AS x}
} {0 {x 2}}
do_test badutf-3.5 {
sqlite3_exec db {SELECT length('%61%c0%80%80%80%80%80%80%80%80%80%80') AS x}
} {0 {x 2}}
do_test badutf-3.6 {
sqlite3_exec db {SELECT length('%c0%80%80%80%80%80%80%80%80%80%80') AS x}
} {0 {x 1}}
do_test badutf-3.7 {
sqlite3_exec db {SELECT length('%80%80%80%80%80%80%80%80%80%80') AS x}
} {0 {x 10}}
do_test badutf-3.8 {
sqlite3_exec db {SELECT length('%80%80%80%80%80%f0%80%80%80%80') AS x}
} {0 {x 6}}
do_test badutf-3.9 {
sqlite3_exec db {SELECT length('%80%80%80%80%80%f0%80%80%80%ff') AS x}
} {0 {x 7}}
do_test badutf-4.1 {
sqlite3_exec db {SELECT hex(trim('%80%80%80%f0%80%80%80%ff','%80%ff')) AS x}
} {0 {x F0}}
do_test badutf-4.2 {
sqlite3_exec db {SELECT hex(ltrim('%80%80%80%f0%80%80%80%ff','%80%ff')) AS x}
} {0 {x F0808080FF}}
do_test badutf-4.3 {
sqlite3_exec db {SELECT hex(rtrim('%80%80%80%f0%80%80%80%ff','%80%ff')) AS x}
} {0 {x 808080F0}}
do_test badutf-4.4 {
sqlite3_exec db {SELECT hex(trim('%80%80%80%f0%80%80%80%ff','%ff%80')) AS x}
} {0 {x 808080F0808080FF}}
do_test badutf-4.5 {
sqlite3_exec db {SELECT hex(trim('%ff%80%80%f0%80%80%80%ff','%ff%80')) AS x}
} {0 {x 80F0808080FF}}
do_test badutf-4.6 {
sqlite3_exec db {SELECT hex(trim('%ff%80%f0%80%80%80%ff','%ff%80')) AS x}
} {0 {x F0808080FF}}
do_test badutf-4.7 {
sqlite3_exec db {SELECT hex(trim('%ff%80%f0%80%80%80%ff','%ff%80%80')) AS x}
} {0 {x FF80F0808080FF}}
db2 close
finish_test

127
testdata/tcl/badutf2.test vendored Normal file
View file

@ -0,0 +1,127 @@
# 2011 March 15
#
# 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.
#
# This file checks to make sure SQLite is able to gracEFully
# handle malformed UTF-8.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
proc utf8_to_ustr2 {s} {
set r ""
foreach i [split $s ""] {
scan $i %c c
append r [format \\u%04.4X $c]
}
set r
}
proc utf8_to_hstr {in} {
regsub -all -- {(..)} $in {%[format "%s" \1]} out
subst $out
}
proc utf8_to_xstr {in} {
regsub -all -- {(..)} $in {\\\\x[format "%s" \1]} out
subst $out
}
proc utf8_to_ustr {in} {
regsub -all -- {(..)} $in {\\\\u[format "%04.4X" 0x\1]} out
subst $out
}
do_test badutf2-1.0 {
db close
forcedelete test.db
sqlite3 db test.db
db eval "PRAGMA encoding = 'UTF-8'"
} {}
do_test badutf2-4.0 {
set S [sqlite3_prepare_v2 db "SELECT ?" -1 dummy]
sqlite3_expired $S
} {0}
foreach { i len uval xstr ustr u2u } {
1 1 00 \x00 {} {}
2 1 01 \x01 "\\u0001" 01
3 1 3F \x3F "\\u003F" 3F
4 1 7F \x7F "\\u007F" 7F
5 1 80 \x80 "\\u0080" C280
6 1 C3BF \xFF "\\u00FF" C3BF
7 3 EFBFBD \xEF\xBF\xBD "\\uFFFD" {}
} {
set hstr [ utf8_to_hstr $uval ]
ifcapable bloblit {
if {$hstr != "%00"} {
do_test badutf2-2.1.$i {
set sql "SELECT '$hstr'=CAST(x'$uval' AS text) AS x;"
set res [ sqlite3_exec db $sql ]
lindex [ lindex $res 1] 1
} {1}
do_test badutf2-2.2.$i {
set sql "SELECT CAST('$hstr' AS blob)=x'$uval' AS x;"
set res [ sqlite3_exec db $sql ]
lindex [ lindex $res 1] 1
} {1}
}
do_test badutf2-2.3.$i {
set sql "SELECT hex(CAST(x'$uval' AS text)) AS x;"
set res [ sqlite3_exec db $sql ]
lindex [ lindex $res 1] 1
} $uval
do_test badutf2-2.4.$i {
set sql "SELECT hex(CAST(x'$uval' AS text)) AS x;"
set res [ sqlite3_exec db $sql ]
lindex [ lindex $res 1] 1
} $uval
}
if {$hstr != "%00"} {
do_test badutf2-3.1.$i {
set sql "SELECT hex('$hstr') AS x;"
set res [ sqlite3_exec db $sql ]
lindex [ lindex $res 1] 1
} $uval
}
# Tcl 8.7 and later do automatic bad-utf8 correction for
# characters 0x80 thru 0x9f so test case 5 does not work here.
if {$i==5 && $tcl_version>=8.7} {
# no-op
} else {
do_test badutf2-4.1.$i {
sqlite3_reset $S
sqlite3_bind_text $S 1 $xstr $len
sqlite3_step $S
utf8_to_ustr2 [ sqlite3_column_text $S 0 ]
} $ustr
}
ifcapable debug {
do_test badutf2-5.1.$i {
utf8_to_utf8 $uval
} $u2u
}
}
do_test badutf2-4.2 {
sqlite3_finalize $S
} {SQLITE_OK}
finish_test

155
testdata/tcl/basexx1.test vendored Normal file
View file

@ -0,0 +1,155 @@
# 2022 November 22
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix basexx
if {[catch {load_static_extension db basexx} error]} {
puts "Skipping basexx tests, hit load error: $error"
finish_test; return
}
# Empty blobs encode to empty strings.
do_execsql_test 100 {
SELECT base64(x'')||base85(x'');
} {{}}
# Empty strings decode to empty blobs.
do_execsql_test 101 {
SELECT hex(x'01'||base64('')||base85('')||x'02');
} {0102}
# Basic base64 encoding
do_execsql_test 102 {
SELECT base64(x'000102030405');
SELECT base64(x'0001020304');
SELECT base64(x'00010203');
} {{AAECAwQF
} {AAECAwQ=
} {AAECAw==
}}
# Basic base64 decoding with pad chars
do_execsql_test 103 {
SELECT hex(base64('AAECAwQF'));
SELECT hex(base64('AAECAwQ='));
SELECT hex(base64('AAECAw=='));
} {000102030405 0001020304 00010203}
# Basic base64 decoding without pad chars and with whitespace
do_execsql_test 104 {
SELECT hex(base64(' AAECAwQF '));
SELECT hex(base64(' AAECAwQ'));
SELECT hex(base64('AAECAw '));
} {000102030405 0001020304 00010203}
# Basic base85 encoding
do_execsql_test 105 {
SELECT base85(x'000102030405');
SELECT base85(x'0001020304');
SELECT base85(x'00010203');
} {{##/2,#2/
} {##/2,#*
} {##/2,
}}
# Basic base85 decoding with and without whitespace
do_execsql_test 106 {
SELECT hex(base85('##/2,#2/'));
SELECT hex(base85('##/2,#*'));
SELECT hex(base85('##/2,'));
SELECT hex(base85(' ##/2,#2/ '));
SELECT hex(base85(' ##/2,#*'));
SELECT hex(base85('##/2, '));
} {000102030405 0001020304 00010203 000102030405 0001020304 00010203}
# Round-trip some random blobs.
do_execsql_test 107 {
CREATE TEMP TABLE rb( len int, b blob ) STRICT;
INSERT INTO rb(len) VALUES (1),(2),(3),(4),(5),(150),(151),(152),(153),(1054);
UPDATE rb SET b = randomblob(len);
SELECT len, base64(base64(b))=b, base85(base85(b))=b
FROM rb ORDER BY len;
} {1 1 1 2 1 1 3 1 1 4 1 1 5 1 1 150 1 1 151 1 1 152 1 1 153 1 1 1054 1 1}
# Same round-trip but with space or junk prepended and/or appended or not.
do_execsql_test 108 {
CREATE TEMP TABLE junk(j text, rank int);
INSERT INTO junk VALUES ('',0),(' ',1),('~',2);
SELECT len, base64(j.j||base64(b)||j.j)=b, base85(j.j||base85(b)||j.j)=b
FROM rb r, junk j WHERE j.rank=(r.len+r.len/25)%3 ORDER BY len;
} {1 1 1 2 1 1 3 1 1 4 1 1 5 1 1 150 1 1 151 1 1 152 1 1 153 1 1 1054 1 1}
# Exercise the fail-on-too-large result feature.
set inLimit [sqlite3_limit db SQLITE_LIMIT_LENGTH -1]
sqlite3_limit db SQLITE_LIMIT_LENGTH 1300
do_catchsql_test 109 {
SELECT len, base64(b) FROM rb WHERE len>200;
} {1 {blob expanded to base64 too big}}
do_catchsql_test 110 {
SELECT len, base85(b) FROM rb WHERE len>200;
} {1 {blob expanded to base85 too big}}
do_catchsql_test 111 {
SELECT length(base85(b))=1335 FROM rb WHERE len=1054;
} {1 {blob expanded to base85 too big}}
sqlite3_limit db SQLITE_LIMIT_LENGTH $inLimit
# Exercise is_base85(t)
do_execsql_test 112 {
SELECT is_base85(' '||base85(x'123456')||char(10)),
is_base85('#$%&*+,-./0123456789:;<=>?@'
||'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
||'[\]^_`'
||'abcdefghijklmnopqrstuvwxyz'),
is_base85('!'), is_base85('"'), is_base85(''''), is_base85('('),
is_base85(')'), is_base85(char(123)), is_base85('|'), is_base85(char(125)),
is_base85('~'), is_base85(char(127));
} {1 1 0 0 0 0 0 0 0 0 0 0}
do_execsql_test 113 {
SELECT is_base85(NULL) IS NULL;
} {1}
do_catchsql_test 114 {
SELECT is_base85(1);
} {1 {is_base85 accepts only text or NULL}}
do_catchsql_test 115 {
SELECT is_base85(1.1);
} {1 {is_base85 accepts only text or NULL}}
do_catchsql_test 116 {
SELECT is_base85(x'00');
} {1 {is_base85 accepts only text or NULL}}
# Round-trip many bigger random blobs.
do_execsql_test 117 {
CREATE TABLE bs(b blob, num);
INSERT INTO bs SELECT randomblob(4000 + n%3), n
FROM (
WITH RECURSIVE seq(n) AS (
VALUES(1) UNION ALL SELECT n+1
FROM seq WHERE n<100
) SELECT n FROM seq);
SELECT num FROM bs WHERE base64(base64(b))!=b;
SELECT num FROM bs WHERE base85(base85(b))!=b;
} {}
finish_test

75
testdata/tcl/bc_common.tcl vendored Normal file
View file

@ -0,0 +1,75 @@
proc bc_find_binaries {zCaption} {
# Search for binaries to test against. Any executable files that match
# our naming convention are assumed to be testfixture binaries to test
# against.
#
set binaries [list]
set self [info nameofexec]
set pattern "$self?*"
if {$::tcl_platform(platform)=="windows"} {
set pattern [string map {\.exe {}} $pattern]
}
foreach file [glob -nocomplain $pattern] {
if {$file==$self} continue
if {[file executable $file] && [file isfile $file]} {lappend binaries $file}
}
if {[llength $binaries]==0} {
puts "WARNING: No historical binaries to test against."
puts "WARNING: Omitting backwards-compatibility tests"
}
foreach bin $binaries {
puts -nonewline "Testing against $bin - "
flush stdout
puts "version [get_version $bin]"
}
set ::BC(binaries) $binaries
return $binaries
}
proc get_version {binary} {
set chan [launch_testfixture $binary]
set v [testfixture $chan { sqlite3 -version }]
close $chan
set v
}
proc do_bc_test {bin script} {
forcedelete test.db
set ::bc_chan [launch_testfixture $bin]
proc code1 {tcl} { uplevel #0 $tcl }
proc code2 {tcl} { testfixture $::bc_chan $tcl }
proc sql1 sql { code1 [list db eval $sql] }
proc sql2 sql { code2 [list db eval $sql] }
code1 { sqlite3 db test.db }
code2 { sqlite3 db test.db }
set bintag $bin
regsub {.*testfixture\.} $bintag {} bintag
set bintag [string map {\.exe {}} $bintag]
if {$bintag == ""} {set bintag self}
set saved_prefix $::testprefix
append ::testprefix ".$bintag"
uplevel $script
set ::testprefix $saved_prefix
catch { code1 { db close } }
catch { code2 { db close } }
catch { close $::bc_chan }
}
proc do_all_bc_test {script} {
foreach bin $::BC(binaries) {
uplevel [list do_bc_test $bin $script]
}
}

344
testdata/tcl/bestindex1.test vendored Normal file
View file

@ -0,0 +1,344 @@
# 2016-03-01
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix bestindex1
ifcapable !vtab {
finish_test
return
}
register_tcl_module db
proc vtab_command {method args} {
switch -- $method {
xConnect {
return "CREATE TABLE t1(a, b, c)"
}
xBestIndex {
set hdl [lindex $args 0]
set clist [$hdl constraints]
set orderby [$hdl orderby]
if {[llength $clist]!=1} { error "unexpected constraint list" }
catch { array unset C }
array set C [lindex $clist 0]
if {$C(usable)} {
return "omit 0 cost 0 rows 1 idxnum 555 idxstr eq!"
} else {
return "cost 1000000 rows 0 idxnum 0 idxstr scan..."
}
}
}
return {}
}
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE x1 USING tcl(vtab_command);
} {}
do_eqp_test 1.1 {
SELECT * FROM x1 WHERE a = 'abc'
} {SCAN x1 VIRTUAL TABLE INDEX 555:eq!}
do_eqp_test 1.2 {
SELECT * FROM x1 WHERE a IN ('abc', 'def');
} {SCAN x1 VIRTUAL TABLE INDEX 555:eq!}
#-------------------------------------------------------------------------
#
reset_db
register_tcl_module db
# Parameter $mode may be one of:
#
# "omit" - Implement filtering. Set the omit flag.
# "use" - Implement filtering. Use the constraint, but do not set omit.
# "use2" - Do not implement filtering. Use the constraint anyway.
#
#
proc t1_vtab {mode method args} {
switch -- $method {
xConnect {
return "CREATE TABLE t1(a, b)"
}
xBestIndex {
set hdl [lindex $args 0]
set clist [$hdl constraints]
set orderby [$hdl orderby]
set SQL_FILTER {SELECT * FROM t1x WHERE a='%1%'}
set SQL_SCAN {SELECT * FROM t1x}
set idx 0
for {set idx 0} {$idx < [llength $clist]} {incr idx} {
array unset C
array set C [lindex $clist $idx]
if {$C(column)==0 && $C(op)=="eq" && $C(usable)} {
switch -- $mode {
"omit" {
return [list omit $idx rows 10 cost 10 idxstr $SQL_FILTER]
}
"use" {
return [list use $idx rows 10 cost 10 idxstr $SQL_FILTER]
}
"use2" {
return [list use $idx rows 10 cost 10 idxstr $SQL_SCAN]
}
default {
error "Bad mode - $mode"
}
}
}
}
return [list idxstr {SELECT * FROM t1x}]
}
xFilter {
set map [list %1% [lindex $args 2 0]]
set sql [string map $map [lindex $args 1]]
return [list sql $sql]
}
}
return {}
}
do_execsql_test 2.1 {
CREATE TABLE t1x(i INTEGER PRIMARY KEY, a, b);
INSERT INTO t1x VALUES(1, 'one', 1);
INSERT INTO t1x VALUES(2, 'two', 2);
INSERT INTO t1x VALUES(3, 'three', 3);
INSERT INTO t1x VALUES(4, 'four', 4);
}
foreach {tn mode} {
1 use 2 omit 3 use2
} {
do_execsql_test 2.2.$mode.1 "
DROP TABLE IF EXISTS t1;
CREATE VIRTUAL TABLE t1 USING tcl(t1_vtab $mode);
"
do_execsql_test 2.2.$mode.2 {SELECT * FROM t1} {one 1 two 2 three 3 four 4}
do_execsql_test 2.2.$mode.3 {SELECT rowid FROM t1} {1 2 3 4}
do_execsql_test 2.2.$mode.4 {SELECT rowid FROM t1 WHERE a='two'} {2}
do_execsql_test 2.2.$mode.5 {
SELECT rowid FROM t1 WHERE a IN ('one', 'four') ORDER BY +rowid
} {1 4}
set plan(use) {
QUERY PLAN
|--SCAN t1 VIRTUAL TABLE INDEX 0:SELECT * FROM t1x WHERE a='%1%'
`--USE TEMP B-TREE FOR ORDER BY
}
set plan(omit) {
QUERY PLAN
|--SCAN t1 VIRTUAL TABLE INDEX 0:SELECT * FROM t1x WHERE a='%1%'
`--USE TEMP B-TREE FOR ORDER BY
}
set plan(use2) {
QUERY PLAN
|--SCAN t1 VIRTUAL TABLE INDEX 0:SELECT * FROM t1x
`--USE TEMP B-TREE FOR ORDER BY
}
do_eqp_test 2.2.$mode.6 {
SELECT rowid FROM t1 WHERE a IN ('one', 'four') ORDER BY +rowid
} [string map {"\n " "\n"} $plan($mode)]
}
# 2016-04-09.
# Demonstrate a register overwrite problem when using two virtual
# tables where the outer loop uses the IN operator.
#
set G(collist) [list PrimaryKey flagA columnA]
set G(cols) [join $G(collist) ,]
set G(nulls) "NULL"
proc vtab_command {method args} {
global G
switch -- $method {
xConnect {
return "CREATE TABLE t1($G(cols))"
}
xBestIndex {
set hdl [lindex $args 0]
set clist [$hdl constraints]
set orderby [$hdl orderby]
#puts $clist
set W [list]
set U [list]
set i 0
for {set idx 0} {$idx < [llength $clist]} {incr idx} {
array set c [lindex $clist $idx]
if {$c(op)=="eq" && $c(usable)} {
lappend W "[lindex $G(collist) $c(column)] = %$i%"
lappend U use $idx
incr i
}
}
if {$W==""} {
set sql "SELECT rowid, * FROM t1"
} else {
set sql "SELECT rowid, * FROM t1 WHERE [join $W { AND }]"
}
return [concat [list idxstr $sql] $U]
}
xFilter {
foreach {idxnum idxstr vals} $args {}
set map [list]
for {set i 0} {$i < [llength $vals]} {incr i} {
lappend map "%$i%"
set v [lindex $vals $i]
if {[string is integer $v]} {
lappend map $v
} else {
lappend map "'$v'"
}
}
set sql [string map $map $idxstr]
#puts "SQL: $sql"
return [list sql $sql]
}
}
return {}
}
db close
forcedelete test.db
sqlite3 db test.db
register_tcl_module db
do_execsql_test 3.1 "
CREATE TABLE t1($G(cols));
INSERT INTO t1 VALUES(1, 0, 'ValueA');
INSERT INTO t1 VALUES(2, 0, 'ValueA');
INSERT INTO t1 VALUES(3, 0, 'ValueB');
INSERT INTO t1 VALUES(4, 0, 'ValueB');
"
do_execsql_test 3.2 {
CREATE VIRTUAL TABLE VirtualTableA USING tcl(vtab_command);
CREATE VIRTUAL TABLE VirtualTableB USING tcl(vtab_command);
}
do_execsql_test 3.3 { SELECT primarykey FROM VirtualTableA } {1 2 3 4}
do_execsql_test 3.4 {
SELECT * FROM
VirtualTableA a CROSS JOIN VirtualTableB b ON b.PrimaryKey=a.PrimaryKey
WHERE a.ColumnA IN ('ValueA', 'ValueB') AND a.FlagA=0
} {
1 0 ValueA 1 0 ValueA
2 0 ValueA 2 0 ValueA
3 0 ValueB 3 0 ValueB
4 0 ValueB 4 0 ValueB
}
do_execsql_test 3.5 {
SELECT * FROM
VirtualTableA a CROSS JOIN VirtualTableB b ON b.PrimaryKey=a.PrimaryKey
WHERE a.FlagA=0 AND a.ColumnA IN ('ValueA', 'ValueB')
} {
1 0 ValueA 1 0 ValueA
2 0 ValueA 2 0 ValueA
3 0 ValueB 3 0 ValueB
4 0 ValueB 4 0 ValueB
}
#-------------------------------------------------------------------------
# If there is an IN(..) condition in the WHERE clause of a query on a
# virtual table, the xBestIndex method is first invoked with the IN(...)
# represented by a "usable" SQLITE_INDEX_CONSTRAINT_EQ constraint. If
# the virtual table elects to use the IN(...) constraint, then the
# xBestIndex method is invoked again, this time with the IN(...) marked
# as "not usable". Depending on the relative costs of the two plans as
# defined by the virtual table implementation, and the cardinality of the
# IN(...) operator, SQLite chooses the most efficient plan.
#
# At one point the second invocation of xBestIndex() was only being made
# for join queries. The following tests check that this problem has been
# fixed.
#
proc vtab_command {method args} {
switch -- $method {
xConnect {
return "CREATE TABLE t1(a, b, c, d)"
}
xBestIndex {
set hdl [lindex $args 0]
set clist [$hdl constraints]
set orderby [$hdl orderby]
lappend ::bestindex_calls $clist
set ret "cost 1000000 idxnum 555"
for {set i 0} {$i < [llength $clist]} {incr i} {
array set C [lindex $clist $i]
if {$C(usable)} {
lappend ret use $i
}
}
return $ret
}
}
return {}
}
do_execsql_test 4.0 {
CREATE VIRTUAL TABLE x1 USING tcl(vtab_command);
} {}
do_test 4.1 {
set ::bestindex_calls [list]
execsql {
SELECT * FROM x1 WHERE a=? AND b BETWEEN ? AND ? AND c IN (1, 2, 3, 4);
}
set ::bestindex_calls
} [list \
[list {op eq column 0 usable 1} \
{op eq column 2 usable 1} \
{op ge column 1 usable 1} \
{op le column 1 usable 1} \
] \
[list {op eq column 0 usable 1} \
{op eq column 2 usable 0} \
{op ge column 1 usable 1} \
{op le column 1 usable 1}
]
]
do_catchsql_test 5.0 {
SELECT * FROM tcl('abc');
} {1 {wrong number of arguments}}
finish_test

145
testdata/tcl/bestindex2.test vendored Normal file
View file

@ -0,0 +1,145 @@
# 2016 March 3
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix bestindex2
ifcapable !vtab {
finish_test
return
}
#-------------------------------------------------------------------------
# Virtual table callback for table named $tbl, with the columns specified
# by list argument $cols. e.g. if the function is invoked as:
#
# vtab_cmd t1 {a b c} ...
#
# The table created is:
#
# "CREATE TABLE t1 (a, b, c)"
#
# The tables xBestIndex method behaves as if all possible combinations of
# "=" constraints (but no others) may be optimized. The cost of a full table
# scan is:
#
# "WHERE 1" "cost 1000000 rows 1000000"
#
# If one or more "=" constraints are in use, the cost and estimated number
# of rows returned are both is (11 - nCons)*1000, where nCons is the number
# of constraints used. e.g.
#
# "WHERE a=? AND b=?" -> "cost 900 rows 900"
# "WHERE c=? AND b<?" -> "cost 1000 rows 1000"
#
proc vtab_cmd {tbl cols method args} {
switch -- $method {
xConnect {
return "CREATE TABLE $tbl ([join $cols ,])"
}
xBestIndex {
set hdl [lindex $args 0]
set clist [$hdl constraints]
set orderby [$hdl orderby]
set mask [$hdl mask]
set cons [list]
set used [list]
for {set i 0} {$i < [llength $clist]} {incr i} {
array unset C
array set C [lindex $clist $i]
if {$C(op)=="eq" && $C(usable) && [lsearch $cons $C(column)]<0} {
lappend used use $i
lappend cons $C(column)
}
}
set nCons [llength $cons]
if {$nCons==0} {
return "cost 1000000 rows 1000000"
} else {
set cost [expr (11-$nCons) * 1000]
set ret [concat $used "cost $cost rows $cost"]
set txt [list]
foreach c $cons { lappend txt "[lindex $cols $c]=?" }
lappend ret idxstr "indexed([join $txt { AND }])"
return $ret
}
}
}
return ""
}
register_tcl_module db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE t1 USING tcl("vtab_cmd t1 {a b}");
CREATE VIRTUAL TABLE t2 USING tcl("vtab_cmd t2 {c d}");
CREATE VIRTUAL TABLE t3 USING tcl("vtab_cmd t3 {e f}");
}
do_eqp_test 1.1 {
SELECT * FROM t1 WHERE a='abc'
} {SCAN t1 VIRTUAL TABLE INDEX 0:indexed(a=?)}
do_eqp_test 1.2 {
SELECT * FROM t1 WHERE a='abc' AND b='def'
} {SCAN t1 VIRTUAL TABLE INDEX 0:indexed(a=? AND b=?)}
do_eqp_test 1.3 {
SELECT * FROM t1 WHERE a='abc' AND a='def'
} {SCAN t1 VIRTUAL TABLE INDEX 0:indexed(a=?)}
do_eqp_test 1.4 {
SELECT * FROM t1,t2 WHERE c=a
} {
QUERY PLAN
|--SCAN t1 VIRTUAL TABLE INDEX 0:
`--SCAN t2 VIRTUAL TABLE INDEX 0:indexed(c=?)
}
do_eqp_test 1.5 {
SELECT * FROM t1, t2 CROSS JOIN t3 WHERE t2.c = +t1.b AND t3.e=t2.d
} {
QUERY PLAN
|--SCAN t1 VIRTUAL TABLE INDEX 0:
|--SCAN t2 VIRTUAL TABLE INDEX 0:indexed(c=?)
`--SCAN t3 VIRTUAL TABLE INDEX 0:indexed(e=?)
}
do_eqp_test 1.6 {
SELECT * FROM t1, t2, t3 WHERE t2.c = +t1.b AND t3.e = t2.d
} {
QUERY PLAN
|--SCAN t1 VIRTUAL TABLE INDEX 0:
|--SCAN t2 VIRTUAL TABLE INDEX 0:indexed(c=?)
`--SCAN t3 VIRTUAL TABLE INDEX 0:indexed(e=?)
}
do_execsql_test 1.7.1 {
CREATE TABLE x1(a, b);
}
do_eqp_test 1.7.2 {
SELECT * FROM x1 CROSS JOIN t1, t2, t3
WHERE t1.a = t2.c AND t1.b = t3.e
} {
QUERY PLAN
|--SCAN x1
|--SCAN t1 VIRTUAL TABLE INDEX 0:
|--SCAN t2 VIRTUAL TABLE INDEX 0:indexed(c=?)
`--SCAN t3 VIRTUAL TABLE INDEX 0:indexed(e=?)
}
finish_test

185
testdata/tcl/bestindex3.test vendored Normal file
View file

@ -0,0 +1,185 @@
# 2016 May 29
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix bestindex3
ifcapable !vtab {
finish_test
return
}
#-------------------------------------------------------------------------
# Virtual table callback for a virtual table named $tbl.
#
# The table created is:
#
# "CREATE TABLE t1 (a, b, c)"
#
# This virtual table supports both LIKE and = operators on all columns.
#
proc vtab_cmd {bOmit method args} {
switch -- $method {
xConnect {
return "CREATE TABLE t1(a, b, c)"
}
xBestIndex {
set hdl [lindex $args 0]
set clist [$hdl constraints]
set orderby [$hdl orderby]
set mask [$hdl mask]
set ret [list]
set use use
if {$bOmit} {set use omit}
for {set i 0} {$i < [llength $clist]} {incr i} {
array unset C
array set C [lindex $clist $i]
if {$C(usable) && ($C(op)=="like" || $C(op)=="eq")} {
lappend ret $use $i
lappend ret idxstr
lappend ret "[lindex {a b c} $C(column)] [string toupper $C(op)] ?"
break
}
}
if {$ret==""} {
lappend ret cost 1000000 rows 1000000
} else {
lappend ret cost 100 rows 10
}
return $ret
}
xFilter {
foreach {idxnum idxstr param} $args {}
set where ""
if {$bOmit && $idxstr != ""} {
set where " WHERE [string map [list ? '$param' EQ =] $idxstr]"
}
return [list sql "SELECT rowid, * FROM ttt$where"]
}
}
return ""
}
register_tcl_module db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE t1 USING tcl("vtab_cmd 0");
}
do_eqp_test 1.1 {
SELECT * FROM t1 WHERE a LIKE 'abc';
} {SCAN t1 VIRTUAL TABLE INDEX 0:a LIKE ?}
do_eqp_test 1.2 {
SELECT * FROM t1 WHERE a = 'abc';
} {SCAN t1 VIRTUAL TABLE INDEX 0:a EQ ?}
do_eqp_test 1.3 {
SELECT * FROM t1 WHERE a = 'abc' OR b = 'def';
} {
QUERY PLAN
`--MULTI-INDEX OR
|--INDEX 1
| `--SCAN t1 VIRTUAL TABLE INDEX 0:a EQ ?
`--INDEX 2
`--SCAN t1 VIRTUAL TABLE INDEX 0:b EQ ?
}
do_eqp_test 1.4 {
SELECT * FROM t1 WHERE a LIKE 'abc%' OR b = 'def';
} {
QUERY PLAN
`--MULTI-INDEX OR
|--INDEX 1
| `--SCAN t1 VIRTUAL TABLE INDEX 0:a LIKE ?
`--INDEX 2
`--SCAN t1 VIRTUAL TABLE INDEX 0:b EQ ?
}
do_execsql_test 1.5 {
CREATE TABLE ttt(a, b, c);
INSERT INTO ttt VALUES(1, 'two', 'three');
INSERT INTO ttt VALUES(2, 'one', 'two');
INSERT INTO ttt VALUES(3, 'three', 'one');
INSERT INTO ttt VALUES(4, 'y', 'one');
INSERT INTO ttt VALUES(5, 'x', 'two');
INSERT INTO ttt VALUES(6, 'y', 'three');
}
foreach omit {0 1} {
do_execsql_test 1.6.$omit.0 "
DROP TABLE t1;
CREATE VIRTUAL TABLE t1 USING tcl('vtab_cmd $omit');
"
do_execsql_test 1.6.$omit.1 {
SELECT rowid FROM t1 WHERE c LIKE 'o%'
} {3 4}
do_execsql_test 1.6.$omit.2 {
SELECT rowid FROM t1 WHERE c LIKE 'o%' OR b='y'
} {3 4 6}
do_execsql_test 1.6.$omit.3 {
SELECT rowid FROM t1 WHERE c = 'three' OR c LIKE 'o%'
} {1 6 3 4}
}
#-------------------------------------------------------------------------
# Test the same pattern works with ordinary tables.
#
# This test does not work if the ICU extension is enabled. ICU overrides
# LIKE - and this optimization only works with the built-in LIKE function.
#
ifcapable !icu {
do_execsql_test 2.1 {
CREATE TABLE t2(x TEXT COLLATE nocase, y TEXT);
CREATE INDEX t2x ON t2(x COLLATE nocase);
CREATE INDEX t2y ON t2(y);
}
do_eqp_test 2.2 {
SELECT * FROM t2 WHERE x LIKE 'abc%' OR y = 'def'
} [string map {"\n " \n} {
QUERY PLAN
`--MULTI-INDEX OR
|--INDEX 1
| `--SEARCH t2 USING INDEX t2x (x>? AND x<?)
`--INDEX 2
`--SEARCH t2 USING INDEX t2y (y=?)
}]
}
#-------------------------------------------------------------------------
# Test that any PRIMARY KEY within a sqlite3_decl_vtab() CREATE TABLE
# statement is currently ignored.
#
proc vvv_command {method args} {
switch -- $method {
xConnect { return "CREATE TABLE t1(a PRIMARY KEY, b, c)" }
}
}
proc yyy_command {method args} {
switch -- $method {
xConnect { return "CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b))" }
}
}
do_execsql_test 3.1 { CREATE VIRTUAL TABLE t3 USING tcl('vvv_command') }
do_execsql_test 3.2 { CREATE VIRTUAL TABLE t4 USING tcl('yyy_command') }
finish_test

182
testdata/tcl/bestindex4.test vendored Normal file
View file

@ -0,0 +1,182 @@
# 2016 November 11
#
# 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.
#
#***********************************************************************
# Test the virtual table interface. In particular the xBestIndex
# method.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix bestindex4
ifcapable !vtab {
finish_test
return
}
#-------------------------------------------------------------------------
# Virtual table callback for a virtual table named $tbl.
#
# The table created is:
#
# "CREATE TABLE t1 (id, host, class)"
#
# The virtual table supports == operators on a subset of its columns. The
# exact subset depends on the value of bitmask paramater $param.
#
# 0x01 - == on "id" supported
# 0x02 - == on "host" supported
# 0x04 - == on "class" supported
#
# $param also supports the following bits:
#
# 0x08 - ignore the "usable" flag (malfunction)
#
#
#
proc vtab_cmd {param method args} {
switch -- $method {
xConnect {
return "CREATE TABLE t1(id TEXT, host TEXT, class TEXT)"
}
xBestIndex {
set hdl [lindex $args 0]
set clist [$hdl constraints]
set orderby [$hdl orderby]
set mask [$hdl mask]
set ret [list]
set use use
for {set i 0} {$i < [llength $clist]} {incr i} {
array unset C
array set C [lindex $clist $i]
if { ($C(usable) || ($param & 0x08))
&& $C(op)=="eq" && ($param & 1<<$C(column))
} {
lappend ret $use $i
break
}
}
set score 1000000
if {$ret!=""} {
set score [expr $score / [llength $ret]]
}
lappend ret cost $score rows $score
return $ret
}
xFilter {
}
}
return ""
}
register_tcl_module db
for {set param1 0} {$param1<16} {incr param1} {
for {set param2 0} {$param2<16} {incr param2} {
reset_db
register_tcl_module db
do_execsql_test 1.$param1.$param2.1 "
CREATE VIRTUAL TABLE t1 USING tcl('vtab_cmd $param1');
CREATE VIRTUAL TABLE t2 USING tcl('vtab_cmd $param2');
"
foreach {tn sql} {
2 "select t1.id as ID from t1, t2 where t1.id=t2.host and t2.class='xx'"
3 {
select t1.id as ID from t1, t2 where t2.class ='xx' and t2.id = t1.host
}
4 {
select t1.id as ID from t1, t2 where t1.host = t2.id and t2. class ='xx'
}
} {
if {($param1 & 0x08)==0 && ($param2 & 0x08)==0} {
do_execsql_test 1.$param1.$param2.$tn.a $sql {}
} else {
do_test 1.$param1.$param2.$tn.b {
catchsql $sql
set {} {}
} {}
}
}
}
}
#-------------------------------------------------------------------------
# Test that a parameter passed to a table-valued function cannot be
# used to drive an index. i.e. that in the following:
#
# SELECT * FROM tbl, vtab(tbl.x);
#
# The implicit constraint "tbl.x = vtab.hidden" is not optimized using
# an index on tbl.x.
#
reset_db
register_tcl_module db
proc vtab_command {method args} {
switch -- $method {
xConnect {
return "CREATE TABLE t1(a, b, c, d HIDDEN)"
}
xBestIndex {
set hdl [lindex $args 0]
set clist [$hdl constraints]
set orderby [$hdl orderby]
set mask [$hdl mask]
if {[llength $clist]!=1} { error "unexpected constraint list" }
catch { array unset C }
array set C [lindex $clist 0]
if {$C(usable)} {
return [list omit 0 idxnum 555 rows 10 cost 100]
}
return [list cost 100000000]
}
}
return {}
}
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE x1 USING tcl(vtab_command);
CREATE TABLE t1 (x INT PRIMARY KEY);
} {}
do_eqp_test 2.1 {
SELECT * FROM t1, x1 WHERE x1.d=t1.x;
} {
QUERY PLAN
|--SCAN x1 VIRTUAL TABLE INDEX 0:
`--SEARCH t1 USING COVERING INDEX sqlite_autoindex_t1_1 (x=?)
}
do_eqp_test 2.2 {
SELECT * FROM t1, x1(t1.x)
} {
QUERY PLAN
|--SCAN t1
`--SCAN x1 VIRTUAL TABLE INDEX 555:
}
finish_test

252
testdata/tcl/bestindex5.test vendored Normal file
View file

@ -0,0 +1,252 @@
# 2017 September 10
#
# 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.
#
#***********************************************************************
# Test the virtual table interface. In particular the xBestIndex
# method.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix bestindex5
ifcapable !vtab {
finish_test
return
}
#-------------------------------------------------------------------------
# Virtual table callback for a virtual table named $tbl.
#
proc vtab_cmd {method args} {
set binops(ne) !=
set binops(eq) =
set binops(isnot) "IS NOT"
set binops(is) "IS"
set unops(isnotnull) "IS NOT NULL"
set unops(isnull) "IS NULL"
set cols(0) a
set cols(1) b
set cols(2) c
switch -- $method {
xConnect {
return "CREATE TABLE t1(a, b, c)"
}
xBestIndex {
set hdl [lindex $args 0]
set clist [$hdl constraints]
set orderby [$hdl orderby]
set mask [$hdl mask]
set cost 1000000.0
set ret [list]
set str [list]
set v 0
for {set i 0} {$i < [llength $clist]} {incr i} {
array unset C
array set C [lindex $clist $i]
if {$C(usable)} {
if {[info exists binops($C(op))]} {
lappend ret omit $i
lappend str "$cols($C(column)) $binops($C(op)) %$v%"
incr v
set cost [expr $cost / 2]
}
if {[info exists unops($C(op))]} {
lappend ret omit $i
lappend str "$cols($C(column)) $unops($C(op))"
incr v
set cost [expr $cost / 2]
}
}
}
lappend ret idxstr [join $str " AND "]
lappend ret cost $cost
return $ret
}
xFilter {
set q [lindex $args 1]
set a [lindex $args 2]
for {set v 0} {$v < [llength $a]} {incr v} {
set val [lindex $a $v]
set q [string map [list %$v% '$val'] $q]
}
if {$q==""} { set q 1 }
lappend ::xFilterQueries "WHERE $q"
return [list sql "SELECT rowid, * FROM t1x WHERE $q"]
}
}
return ""
}
proc vtab_simple {method args} {
switch -- $method {
xConnect {
return "CREATE TABLE t2(x)"
}
xBestIndex {
return [list cost 999999.0]
}
xFilter {
return [list sql "SELECT rowid, * FROM t2x"]
}
}
return ""
}
register_tcl_module db
proc do_vtab_query_test {tn query result} {
set ::xFilterQueries [list]
uplevel [list
do_test $tn [string map [list %QUERY% $query] {
set r [execsql {%QUERY%}]
set r [concat $::xFilterQueries $r]
set r
}] [list {*}$result]
]
}
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE t1 USING tcl('vtab_cmd');
CREATE TABLE t1x(a INTEGER, b TEXT, c REAL);
INSERT INTO t1x VALUES(1, 2, 3);
INSERT INTO t1x VALUES(4, 5, 6);
INSERT INTO t1x VALUES(7, 8, 9);
CREATE VIRTUAL TABLE t2 USING tcl('vtab_simple');
CREATE TABLE t2x(x INTEGER);
INSERT INTO t2x VALUES(1);
}
do_vtab_query_test 1.1 { SELECT * FROM t1 WHERE a!='hello'; } {
"WHERE a != 'hello'"
1 2 3.0 4 5 6.0 7 8 9.0
}
do_vtab_query_test 1.2.1 { SELECT * FROM t1 WHERE b!=8 } {
"WHERE b != '8'"
1 2 3.0 4 5 6.0
}
do_vtab_query_test 1.2.2 { SELECT * FROM t1 WHERE 8!=b } {
"WHERE b != '8'"
1 2 3.0 4 5 6.0
}
do_vtab_query_test 1.3 { SELECT * FROM t1 WHERE c IS NOT 3 } {
"WHERE c IS NOT '3'"
4 5 6.0 7 8 9.0
}
do_vtab_query_test 1.3.2 { SELECT * FROM t1 WHERE 3 IS NOT c } {
"WHERE c IS NOT '3'"
4 5 6.0 7 8 9.0
}
do_vtab_query_test 1.4.1 { SELECT * FROM t1, t2 WHERE x != a } {
"WHERE a != '1'"
4 5 6.0 1 7 8 9.0 1
}
do_vtab_query_test 1.4.2 { SELECT * FROM t1, t2 WHERE a != x } {
"WHERE a != '1'"
4 5 6.0 1 7 8 9.0 1
}
do_vtab_query_test 1.5.1 { SELECT * FROM t1 WHERE a IS NOT NULL } {
"WHERE a IS NOT NULL"
1 2 3.0 4 5 6.0 7 8 9.0
}
do_vtab_query_test 1.5.2 { SELECT * FROM t1 WHERE NULL IS NOT a } {
"WHERE a IS NOT ''"
1 2 3.0 4 5 6.0 7 8 9.0
}
do_vtab_query_test 1.6.1 { SELECT * FROM t1 WHERE a IS NULL } {
"WHERE a IS NULL"
}
do_vtab_query_test 1.6.2 { SELECT * FROM t1 WHERE NULL IS a } {
"WHERE a IS ''"
}
do_vtab_query_test 1.7.1 { SELECT * FROM t1 WHERE (a, b) IS (1, 2) } {
"WHERE a IS '1' AND b IS '2'"
1 2 3.0
}
do_vtab_query_test 1.7.2 { SELECT * FROM t1 WHERE (5, 4) IS (b, a) } {
{WHERE b IS '5' AND a IS '4'}
4 5 6.0
}
#---------------------------------------------------------------------
do_execsql_test 2.0.0 {
DELETE FROM t1x;
INSERT INTO t1x VALUES('a', 'b', 'c');
}
do_execsql_test 2.0.1 { SELECT * FROM t1 } {a b c}
do_execsql_test 2.0.2 { SELECT * FROM t1 WHERE (a, b) != ('a', 'b'); } {}
do_execsql_test 2.1.0 {
DELETE FROM t1x;
INSERT INTO t1x VALUES(7, 8, 9);
}
do_execsql_test 2.1.1 { SELECT * FROM t1 } {7 8 9.0}
do_execsql_test 2.1.2 { SELECT * FROM t1 WHERE (a, b) != (7, '8') } {}
do_execsql_test 2.1.3 { SELECT * FROM t1 WHERE a!=7 OR b!='8' }
do_execsql_test 2.1.4 { SELECT * FROM t1 WHERE a!=7 OR b!='8' }
do_execsql_test 2.2.1 {
CREATE TABLE t3(a INTEGER, b TEXT);
INSERT INTO t3 VALUES(45, 46);
}
do_execsql_test 2.2.2 { SELECT * FROM t3 WHERE (a, b) != (45, 46); }
do_execsql_test 2.2.3 { SELECT * FROM t3 WHERE (a, b) != ('45', '46'); }
do_execsql_test 2.2.4 { SELECT * FROM t3 WHERE (a, b) == (45, 46); } {45 46}
do_execsql_test 2.2.5 { SELECT * FROM t3 WHERE (a, b) == ('45', '46'); } {45 46}
#---------------------------------------------------------------------
# Test the != operator on a virtual table with column affinities.
#
proc vtab_simple_integer {method args} {
switch -- $method {
xConnect {
return "CREATE TABLE t4(x INTEGER)"
}
xBestIndex {
return [list cost 999999.0]
}
xFilter {
return [list sql "SELECT rowid, * FROM t4x"]
}
}
return ""
}
do_execsql_test 3.0 {
CREATE TABLE t4x(a INTEGER);
INSERT INTO t4x VALUES(245);
CREATE VIRTUAL TABLE t4 USING tcl('vtab_simple_integer');
}
do_execsql_test 3.1 { SELECT rowid, * FROM t4 WHERE x=245; } {1 245}
do_execsql_test 3.2 { SELECT rowid, * FROM t4 WHERE x='245'; } {1 245}
do_execsql_test 3.3 { SELECT rowid, * FROM t4 WHERE x!=245; } {}
do_execsql_test 3.4 { SELECT rowid, * FROM t4 WHERE x!='245'; } {}
do_execsql_test 3.5 { SELECT rowid, * FROM t4 WHERE rowid!=1 OR x!='245'; } {}
finish_test

113
testdata/tcl/bestindex6.test vendored Normal file
View file

@ -0,0 +1,113 @@
# 2018-09-09
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix bestindex6
ifcapable !vtab {
finish_test
return
}
register_tcl_module db
proc vtab_command {src method args} {
switch -- $method {
xConnect {
return [db one {SELECT sql FROM sqlite_master where name = $src}]
}
xBestIndex {
set hdl [lindex $args 0]
set clist [$hdl constraints]
set orderby [$hdl orderby]
set mask [$hdl mask]
set wlist 1
set iCons 0
set ret [list]
foreach cons $clist {
catch { array unset C }
array set C $cons
if {$C(usable)} {
set col [db one {
SELECT name FROM pragma_table_info($src) WHERE cid=$C(column)
}]
switch $C(op) {
isnull {
lappend wlist "$col IS NULL"
lappend ret omit $iCons
}
eq {
lappend wlist "$col = %$iCons%"
lappend ret omit $iCons
}
}
}
incr iCons
}
#puts "xBestIndex: $ret"
lappend ret idxStr [join $wlist " AND "]
return $ret
}
xFilter {
foreach {idxnum idxstr aa} $args {}
set map [list]
for {set iCons 0} {$iCons < [llength $aa]} {incr iCons} {
lappend map %$iCons% [lindex $aa $iCons]
}
set ret [list sql \
"SELECT rowid, * FROM $src WHERE [string map $map $idxstr]"
]
# puts "xFilter: $ret"
return $ret
}
}
return {}
}
do_execsql_test 1.0 {
CREATE TABLE t1(id int, value text);
CREATE TABLE t2(ctx int, id int, value text);
INSERT INTO t1 VALUES(1,'try');
INSERT INTO t2 VALUES(1,1,'good');
INSERT INTO t2 VALUES(2,2,'evil');
CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
CREATE VIRTUAL TABLE vt2 USING tcl(vtab_command t2);
}
do_execsql_test 1.1 {
select * from t2 left join t1 on t1.id=t2.ctx where t1.value is null;
} {2 2 evil {} {}}
do_execsql_test 1.2 {
select * from vt2 left join vt1 on vt1.id=vt2.ctx where vt1.value is null;
} {2 2 evil {} {}}
unset -nocomplain xxx
do_execsql_test 1.3 {
select * from vt2 left join vt1 on vt1.id=vt2.ctx where vt1.value is $xxx;
} {2 2 evil {} {}}
do_execsql_test 1.4 {
select * from t2 left join vt1 on vt1.id=t2.ctx where vt1.value = 3
} {}
finish_test

82
testdata/tcl/bestindex7.test vendored Normal file
View file

@ -0,0 +1,82 @@
# 2020-01-29
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix bestindex7
ifcapable !vtab {
finish_test
return
}
register_tcl_module db
proc vtab_command {src method args} {
switch -- $method {
xConnect {
return "CREATE TABLE xxx(a)"
}
xBestIndex {
set hdl [lindex $args 0]
set clist [$hdl constraints]
set orderby [$hdl orderby]
set mask [$hdl mask]
set iCons 0
set ret [list]
foreach cons $clist {
catch { array unset C }
array set C $cons
if {$C(usable)} {
lappend ret use $iCons
}
incr iCons
}
return $ret
}
xFilter {
return [list sql "SELECT rowid, x FROM $src"]
}
}
return {}
}
do_execsql_test 1.0 {
CREATE TABLE t1(x);
INSERT INTO t1 VALUES(0), (2);
CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
}
do_execsql_test 1.1 { select * from vt1 } {0 2}
do_execsql_test 1.2 { select * from vt1 WHERE a=0 } {0}
do_execsql_test 1.3 { select * from vt1 WHERE a=1 } {}
do_execsql_test 1.4 { select * from vt1 WHERE a=1 OR a=0} {0}
do_execsql_test 1.5 {
UPDATE t1 SET x=NULL WHERE x=2;
}
do_execsql_test 1.6 { select * from vt1 } {0 {}}
do_execsql_test 1.7 { select * from vt1 WHERE a=0 } {0}
do_execsql_test 1.8 { select * from vt1 WHERE a=1 } {}
do_execsql_test 1.9 { select * from vt1 WHERE a=1 OR a=0} {0}
do_execsql_test 1.10 { select * from vt1 WHERE a IN (2) } {}
do_execsql_test 1.10 { select * from vt1 WHERE a IN (0,1,2,3) } {0}
do_execsql_test 1.11 { select * from vt1 WHERE a IN (0, NULL) } {0}
do_execsql_test 1.12 { select * from vt1 WHERE a IN (NULL) } {}
finish_test

463
testdata/tcl/bestindex8.test vendored Normal file
View file

@ -0,0 +1,463 @@
# 2020-01-29
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix bestindex8
ifcapable !vtab {
finish_test
return
}
register_tcl_module db
proc vtab_command {src method args} {
switch -- $method {
xConnect {
return "CREATE TABLE xxx(a, b)"
}
xBestIndex {
set hdl [lindex $args 0]
set clist [$hdl constraints]
set orderby [$hdl orderby]
lappend ::lBestIndexDistinct [$hdl distinct]
#puts "ORDERBY: $orderby"
set iCons 0
set ret [list]
foreach cons $clist {
catch { array unset C }
array set C $cons
if {$C(usable)} {
lappend ret use $iCons
}
incr iCons
}
if {$orderby=="{column 0 desc 0} {column 1 desc 0}"
|| $orderby=="{column 0 desc 0}"
} {
lappend ret orderby 1
lappend ret idxnum 1
set ::lOrderByConsumed 1
}
return $ret
}
xFilter {
set idxnum [lindex $args 0]
if {$idxnum} {
return [list sql "SELECT rowid, a, b FROM $src order by 2, 3"]
}
return [list sql "SELECT rowid, a, b FROM $src"]
}
}
return {}
}
do_execsql_test 1.0 {
CREATE TABLE t1(a, b);
CREATE INDEX i1 ON t1(a, b);
INSERT INTO t1 VALUES('a', 'b'), ('c', 'd');
INSERT INTO t1 VALUES('a', 'b'), ('c', 'd');
CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
CREATE TABLE t0(c0);
INSERT INTO t0(c0) VALUES (1), (0);
}
foreach {tn sql bDistinct idxinsert bConsumed res} {
1 "SELECT a, b FROM vt1" 0 0 0 {a b c d a b c d}
2 "SELECT DISTINCT a, b FROM vt1" 2 1 1 {a b c d}
3 "SELECT DISTINCT a FROM vt1" 2 1 1 {a c}
4 "SELECT DISTINCT b FROM vt1" 2 1 0 {b d}
5 "SELECT DISTINCT b FROM vt1 ORDER BY a" 0 1 1 {b d}
6 "SELECT DISTINCT t0.c0 FROM vt1, t0 ORDER BY vt1.a" 0 1 1 {1 0}
7 "SELECT DISTINCT a, b FROM vt1 ORDER BY a, b" 3 0 1 {a b c d}
8 "SELECT DISTINCT a, b FROM vt1 ORDER BY a" 0 1 1 {a b c d}
9 "SELECT DISTINCT a FROM vt1 ORDER BY a, b" 0 1 1 {a c}
10 "SELECT DISTINCT a, b FROM vt1 WHERE b='b'" 2 1 1 {a b}
11 "SELECT DISTINCT a, b FROM vt1 WHERE +b='b'" 2 1 1 {a b}
} {
set ::lBestIndexDistinct ""
set ::lOrderByConsumed 0
do_execsql_test 1.$tn.1 $sql $res
do_test 1.$tn.2 {
set ::lBestIndexDistinct
} $bDistinct
do_test 1.$tn.3 {
expr {[lsearch [execsql "explain $sql"] IdxInsert]>=0}
} $idxinsert
do_test 1.$tn.4 {
set ::lOrderByConsumed
} $bConsumed
}
#-------------------------------------------------------------------------
reset_db
register_tcl_module db
proc vtab_command {src method args} {
switch -- $method {
xConnect {
return "CREATE TABLE xxx(a, b)"
}
xBestIndex {
set hdl [lindex $args 0]
set ret [list]
set iCons 0
foreach cons [$hdl constraints] {
array set C $cons
if {($C(op)=="limit" || $C(op)=="offset") && $C(usable)} {
lappend ret use $iCons
}
incr iCons
}
return $ret
}
xFilter {
lappend ::lFilterArgs [lindex $args 2]
return [list sql "SELECT rowid, a, b FROM $src"]
}
}
return {}
}
do_execsql_test 2.0 {
CREATE TABLE t1(a, b);
CREATE INDEX i1 ON t1(a, b);
CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
}
do_test 2.1 {
set ::lFilterArgs [list]
execsql { SELECT * FROM vt1 LIMIT 10 }
set ::lFilterArgs
} {10}
do_test 2.2 {
set ::lFilterArgs [list]
execsql { SELECT * FROM vt1 LIMIT 5 OFFSET 50 }
set ::lFilterArgs
} {{5 50}}
do_test 2.3 {
set ::lFilterArgs [list]
execsql { SELECT * FROM vt1 ORDER BY a, b LIMIT 1 OFFSET 1 }
set ::lFilterArgs
} {{1 1}}
do_test 2.4 {
set ::lFilterArgs [list]
execsql { SELECT * FROM vt1 ORDER BY a, +b LIMIT 1 OFFSET 1 }
set ::lFilterArgs
} {{}}
#-------------------------------------------------------------------------
reset_db
register_tcl_module db
proc vtab_command {src method args} {
switch -- $method {
xConnect {
return "CREATE TABLE xxx(a, b)"
}
xBestIndex {
set hdl [lindex $args 0]
set lCons [$hdl constraints]
set ret [list]
for {set i 0} {$i < [llength $lCons]} {incr i} {
array set C [lindex $lCons $i]
if {$C(usable)} {
lappend ret use $i
$hdl in $i 1
}
}
return $ret
}
xFilter {
set lArg [lindex $args 2]
lappend ::lFilterArg {*}$lArg
return [list sql "SELECT rowid, a, b FROM $src"]
}
}
return {}
}
do_execsql_test 3.0 {
CREATE TABLE t1(a, b);
CREATE INDEX i1 ON t1(a, b);
CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
}
foreach {tn sql lfa} {
1 "SELECT * FROM vt1 WHERE b IN (10, 20, 30)" {{10 20 30}}
2 "SELECT * FROM vt1 WHERE b IN ('abc', 'def')" {{abc def}}
3 "SELECT * FROM vt1 WHERE a IS NULL AND b IN ('abc', 'def')" {{} {abc def}}
4 "SELECT * FROM vt1 WHERE a IN (1,2,3) AND b IN ('abc', 'def')"
{{1 2 3} {abc def}}
5 "SELECT * FROM vt1
WHERE a IN (SELECT 1 UNION SELECT 2) AND b IN ('abc', 'def')"
{{1 2} {abc def}}
6 "SELECT * FROM vt1
WHERE b IN ('abc', 'def') AND a IN (SELECT 1 UNION SELECT 2)"
{{abc def} {1 2}}
} {
do_test 3.$tn {
set ::lFilterArg [list]
execsql $sql
set ::lFilterArg
} $lfa
}
#explain_i { SELECT * FROM vt1 WHERE b IN (10, 20, 30) }
#-------------------------------------------------------------------------
reset_db
register_tcl_module db
proc vtab_command {src method args} {
switch -- $method {
xConnect {
return "CREATE TABLE xxx(a, b, c)"
}
xBestIndex {
set hdl [lindex $args 0]
set lCons [$hdl constraints]
set ret [list]
for {set i 0} {$i < [llength $lCons]} {incr i} {
lappend ::lBestIndexRhs [$hdl rhs_value $i -]
}
return $ret
}
xFilter {
return [list sql "SELECT rowid, a, b, c FROM $src"]
}
}
return {}
}
do_execsql_test 4.0 {
CREATE TABLE t1(a, b, c);
CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
}
foreach {tn sql lbir} {
1 "SELECT * FROM vt1 WHERE b = 10" {10}
2 "SELECT * FROM vt1 WHERE a = 'abc' AND b < 30" {abc 30}
3 "SELECT * FROM vt1 WHERE a = 'abc' AND b < 30+2" {abc -}
4 "SELECT * FROM vt1 WHERE a IN (1,2,3) AND b < 30+2" {- -}
5 "SELECT * FROM vt1 WHERE a IS 111 AND b < 30+2" {111 -}
} {
do_test 4.$tn {
set ::lBestIndexRhs [list]
execsql $sql
set ::lBestIndexRhs
} $lbir
}
#-------------------------------------------------------------------------
reset_db
db cache size 0
register_tcl_module db
set ::vtab_handle_in 1
proc vtab_command {src method args} {
switch -- $method {
xConnect {
return "CREATE TABLE xxx(a, b, c)"
}
xBestIndex {
set lCols [list a b c]
set hdl [lindex $args 0]
set lCons [$hdl constraints]
set lOrder [$hdl order]
set L ""
set O ""
set W [list]
set a 0
for {set i 0} {$i < [llength $lCons]} {incr i} {
array set C [lindex $lCons $i]
if {$C(usable)} {
if { $C(op)=="eq" } {
set bIn 0
if {$::vtab_handle_in} { set bIn [$hdl in $i 1] }
if {$bIn} {
lappend W "[lindex $lCols $C(column)] IN (%I$a%)"
} else {
lappend W "[lindex $lCols $C(column)] = %$a%"
}
lappend ret omit $i
}
if { $C(op)=="limit" } { set L " LIMIT %$a%" ; lappend ret use $i }
if { $C(op)=="offset" } { set O " OFFSET %$a%" ; lappend ret use $i }
incr a
}
}
set order ""
set selectlist "rowid, a, b, c"
if {[llength $lOrder]} {
array set sl [list]
set lO [list]
foreach s $lOrder {
array set C $s
set ad ""
if {$C(desc)} { set ad " DESC" }
lappend lO "[lindex $lCols $C(column)]$ad"
set sl($C(column)) 1
}
if {[$hdl distinct]==2} {
set selectlist "DISTINCT 0"
foreach i {0 1 2} {
if {[info exists sl($i)]} {
append selectlist ", [lindex $lCols $i]"
} else {
append selectlist ", 0"
}
}
} else {
set order " ORDER BY [join $lO ,]"
}
}
set where ""
if {[llength $W]} { set where " WHERE [join $W { AND }]" }
set sql "SELECT $selectlist FROM $src$where$order$L$O"
lappend ret idxStr $sql
return $ret
}
xFilter {
foreach {idxnum idxstr lArg} $args {}
set ii 0
set sql $idxstr
foreach a $lArg {
set sql [string map [list %$ii% $a] $sql]
set sql [string map [list %I$ii% [join $a ,]] $sql]
incr ii
}
lappend ::lFilterSql $sql
if {[regexp {OFFSET (.*)$} $sql -> off]} {
set real_sql "
WITH c(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM c WHERE i<$off )
SELECT 0,0,0,0 FROM c
UNION ALL SELECT * FROM (
$sql
)
"
} else {
set real_sql $sql
}
return [list sql $real_sql]
}
}
return {}
}
do_execsql_test 5.0 {
CREATE TABLE t1(a, b, c);
CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
INSERT INTO t1 VALUES(1, 2, 3);
INSERT INTO t1 VALUES(2, 3, 4);
INSERT INTO t1 VALUES(3, 4, 5);
INSERT INTO t1 VALUES(1, 5, 6);
INSERT INTO t1 VALUES(2, 6, 7);
INSERT INTO t1 VALUES(3, 7, 8);
INSERT INTO t1 VALUES(1, 8, 9);
INSERT INTO t1 VALUES(2, 9, 0);
}
proc do_vtab_test {tn sql vtsql {res {}}} {
set ::lFilterSql [list]
uplevel [list do_execsql_test $tn.1 $sql $res]
uplevel [list do_test $tn.2 {set ::lFilterSql} [list {*}$vtsql]]
}
do_vtab_test 5.1.1 {
SELECT DISTINCT a FROM vt1
} {
{SELECT DISTINCT 0, a, 0, 0 FROM t1}
} {1 2 3}
do_vtab_test 5.1.2 {
SELECT DISTINCT a FROM vt1 ORDER BY a
} {
{SELECT rowid, a, b, c FROM t1 ORDER BY a}
} {1 2 3}
do_vtab_test 5.1.3 {
SELECT DISTINCT a FROM vt1 WHERE c IN (4,5,6,7,8)
} {
{SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c IN (4,5,6,7,8)}
} {2 3 1}
set ::vtab_handle_in 0
do_vtab_test 5.1.4 {
SELECT DISTINCT a FROM vt1 WHERE c IN (4,5,6,7,8)
} {
{SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 4}
{SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 5}
{SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 6}
{SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 7}
{SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 8}
} {2 3 1}
set ::vtab_handle_in 1
do_vtab_test 5.1.5a {
SELECT a, b, c FROM vt1 WHERE c IN (4,5,6,7,8) LIMIT 2 OFFSET 2
} {
{SELECT rowid, a, b, c FROM t1 WHERE c IN (4,5,6,7,8) LIMIT 2 OFFSET 2}
} {1 5 6 2 6 7}
set ::vtab_handle_in 0
do_vtab_test 5.1.5b {
SELECT a, b, c FROM vt1 WHERE c IN (4,5,6,7,8) LIMIT 2 OFFSET 2
} {
{SELECT rowid, a, b, c FROM t1 WHERE c = 4}
{SELECT rowid, a, b, c FROM t1 WHERE c = 5}
{SELECT rowid, a, b, c FROM t1 WHERE c = 6}
{SELECT rowid, a, b, c FROM t1 WHERE c = 7}
} {1 5 6 2 6 7}
set ::vtab_handle_in 1
finish_test

108
testdata/tcl/bestindex9.test vendored Normal file
View file

@ -0,0 +1,108 @@
# 2020-01-29
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix bestindex9
ifcapable !vtab {
finish_test
return
}
proc vtab_command {method args} {
switch -- $method {
xConnect {
return $::create_table
}
xBestIndex {
set hdl [lindex $args 0]
set ::vtab_orderby [$hdl orderby]
set ::vtab_distinct [$hdl distinct]
return [list orderby 1]
}
xFilter {
return ""
}
}
return {}
}
proc do_bestindex9_test {tn create select orderby distinct} {
forcedelete test.db2
sqlite3 dbtest test.db2
register_tcl_module dbtest
set ::create_table $create
dbtest eval { CREATE VIRTUAL TABLE t1 USING tcl(vtab_command); }
dbtest eval $select
do_test $tn.orderby [list set {} $::vtab_orderby] $orderby
do_test $tn.distinct [list set {} $::vtab_distinct] $distinct
dbtest close
}
#-------------------------------------------------------------------------
#
do_bestindex9_test 1.0 {
CREATE TABLE x(k1, k2, v1, PRIMARY KEY(k1, k2))
} {
SELECT DISTINCT k1, k2 FROM t1
} {{column 0 desc 0} {column 1 desc 0}} 2
do_bestindex9_test 1.1 {
CREATE TABLE x(k1, k2, v1, PRIMARY KEY(k1, k2)) WITHOUT ROWID
} {
SELECT DISTINCT k1, k2 FROM t1
} {} 0
do_bestindex9_test 1.2 {
CREATE TABLE x(k1 NOT NULL, k2 NOT NULL, v1, PRIMARY KEY(k1, k2))
} {
SELECT DISTINCT k1, k2 FROM t1
} {} 0
#-------------------------------------------------------------------------
#
do_bestindex9_test 2 {
CREATE TABLE x(c1, c2, c3);
} {
SELECT DISTINCT c1 FROM t1 ORDER BY c1
} {{column 0 desc 0}} 3
#-------------------------------------------------------------------------
#
do_bestindex9_test 3 {
CREATE TABLE x(c1, c2, c3);
} {
SELECT DISTINCT c1 FROM t1 GROUP BY c1
} {{column 0 desc 0}} 1
do_bestindex9_test 4 {
CREATE TABLE x(c1, c2, c3);
} {
CREATE TABLE t2(balls);
SELECT DISTINCT c1 FROM t1, t2
} {{column 0 desc 0}} 2
finish_test

138
testdata/tcl/bestindexA.test vendored Normal file
View file

@ -0,0 +1,138 @@
# 2020-01-29
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix bestindexA
ifcapable !vtab {
finish_test
return
}
proc vtab_command {method args} {
switch -- $method {
xConnect {
return "CREATE TABLE x(a, b, c)"
}
xBestIndex {
set hdl [lindex $args 0]
set clist [$hdl constraints]
foreach c $clist {
array set C $c
lappend ::vtab_constraints [list $C(op) $C(column)]
}
return [list]
}
xFilter {
return ""
}
xFindFunction {
foreach {nArg name} $args {}
if {$nArg==2 && $name=="even"} {
return 152
}
return 0
}
}
return {}
}
register_tcl_module db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE t1 USING tcl(vtab_command);
}
proc do_xbestindex_test {tn sql res} {
set script [subst {
execsql "$sql"
set ::vtab_constraints
}]
uplevel [list do_test $tn $script [list {*}$res]]
set ::vtab_constraints [list]
}
do_xbestindex_test 1.1 {
SELECT * FROM t1 WHERE a=?
} {
{eq 0}
}
do_xbestindex_test 1.2 {
SELECT * FROM t1 WHERE a=? LIMIT 10
} {
{eq 0}
{limit 0}
}
do_xbestindex_test 1.3 {
SELECT * FROM t1 WHERE a=? AND (b+1)=? LIMIT 10
} {
{eq 0}
}
proc error_function {args} { error "not a function!" }
db function even error_function
do_xbestindex_test 1.4 {
SELECT * FROM t1 WHERE even(a, ?)
} {
{152 0}
}
do_xbestindex_test 1.5 {
SELECT * FROM t1 WHERE b=10 AND even(a, ?)
} {
{eq 1}
{152 0}
}
do_xbestindex_test 1.6 {
SELECT * FROM t1 WHERE b=10 LIMIT 10
} {
{eq 1}
{limit 0}
}
do_xbestindex_test 1.7 {
SELECT * FROM t1 WHERE even(b,?) LIMIT 10
} {
{152 1}
{limit 0}
}
do_xbestindex_test 1.8 {
SELECT * FROM t1 WHERE b!=? LIMIT 10
} {
{ne 1}
{limit 0}
}
do_xbestindex_test 1.9 {
SELECT * FROM t1 WHERE ?=a LIMIT 10
} {
{eq 0}
{limit 0}
}
finish_test

143
testdata/tcl/between.test vendored Normal file
View file

@ -0,0 +1,143 @@
# 2005 July 28
#
# 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 use of indices in WHERE clauses
# when the WHERE clause contains the BETWEEN operator.
#
# $Id: between.test,v 1.2 2006/01/17 09:35:02 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# Build some test data
#
do_test between-1.0 {
execsql {
BEGIN;
CREATE TABLE t1(w int, x int, y int, z int);
}
for {set i 1} {$i<=100} {incr i} {
set w $i
set x [expr {int(log($i)/log(2))}]
set y [expr {$i*$i + 2*$i + 1}]
set z [expr {$x+$y}]
ifcapable tclvar {
# Random unplanned test of the $varname variable syntax.
execsql {INSERT INTO t1 VALUES($::w,$::x,$::y,$::z)}
} else {
# If the $varname syntax is not available, use the regular variable
# declaration syntax.
execsql {INSERT INTO t1 VALUES(:w,:x,:y,:z)}
}
}
execsql {
CREATE UNIQUE INDEX i1w ON t1(w);
CREATE INDEX i1xy ON t1(x,y);
CREATE INDEX i1zyx ON t1(z,y,x);
COMMIT;
}
} {}
# This procedure executes the SQL. Then it appends to the result the
# "sort" or "nosort" keyword depending on whether or not any sorting
# is done. Then it appends the names of the table and index used.
#
proc queryplan {sql} {
set ::sqlite_sort_count 0
set data [execsql $sql]
if {$::sqlite_sort_count} {set x sort} {set x nosort}
lappend data $x
set eqp [execsql "EXPLAIN QUERY PLAN $sql"]
# puts eqp=$eqp
foreach {a b c x} $eqp {
if {[regexp {(SCAN|SEARCH) (\w+ AS )?(\w+) USING.* INDEX (\w+)\y} \
$x all ss as tab idx]} {
lappend data $tab $idx
} elseif {[regexp {(SCAN|SEARCH) (\w+ AS )?(\w+)\y} $x all ss as tab]} {
lappend data $tab *
}
}
return $data
}
do_test between-1.1.1 {
queryplan {
SELECT * FROM t1 WHERE w BETWEEN 5 AND 6 ORDER BY +w
}
} {5 2 36 38 6 2 49 51 sort t1 i1w}
do_test between-1.1.2 {
queryplan {
SELECT * FROM t1 WHERE +w BETWEEN 5 AND 6 ORDER BY +w
}
} {5 2 36 38 6 2 49 51 sort t1 *}
do_test between-1.2.1 {
queryplan {
SELECT * FROM t1 WHERE w BETWEEN 5 AND 65-y ORDER BY +w
}
} {5 2 36 38 6 2 49 51 sort t1 i1w}
do_test between-1.2.2 {
queryplan {
SELECT * FROM t1 WHERE +w BETWEEN 5 AND 65-y ORDER BY +w
}
} {5 2 36 38 6 2 49 51 sort t1 *}
do_test between-1.3.1 {
queryplan {
SELECT * FROM t1 WHERE w BETWEEN 41-y AND 6 ORDER BY +w
}
} {5 2 36 38 6 2 49 51 sort t1 i1w}
do_test between-1.3.2 {
queryplan {
SELECT * FROM t1 WHERE +w BETWEEN 41-y AND 6 ORDER BY +w
}
} {5 2 36 38 6 2 49 51 sort t1 *}
do_test between-1.4 {
queryplan {
SELECT * FROM t1 WHERE w BETWEEN 41-y AND 65-y ORDER BY +w
}
} {5 2 36 38 6 2 49 51 sort t1 *}
do_test between-1.5.1 {
queryplan {
SELECT * FROM t1 WHERE 26 BETWEEN y AND z ORDER BY +w
}
} {4 2 25 27 sort t1 i1zyx}
do_test between-1.5.2 {
queryplan {
SELECT * FROM t1 WHERE 26 BETWEEN +y AND z ORDER BY +w
}
} {4 2 25 27 sort t1 i1zyx}
do_test between-1.5.3 {
queryplan {
SELECT * FROM t1 WHERE 26 BETWEEN y AND +z ORDER BY +w
}
} {4 2 25 27 sort t1 *}
#-------------------------------------------------------------------------
reset_db
do_execsql_test between-2.0 {
CREATE TABLE t1(x TEXT, y TEXT COLLATE nocase);
INSERT INTO t1 VALUES('0', 'abc');
}
foreach {tn expr res} {
1 "x BETWEEN 1 AND '5'" 0
2 "x COLLATE binary BETWEEN 1 AND '5'" 0
3 "x COLLATE nocase BETWEEN 1 AND '5'" 0
4 "y BETWEEN 'A' AND 'B'" 1
5 "y COLLATE nocase BETWEEN 'A' AND 'B'" 1
6 "y COLLATE binary BETWEEN 'A' AND 'B'" 0
7 "(y COLLATE binary) BETWEEN 'A' AND 'B'" 0
} {
set sql "SELECT $expr FROM t1"
do_execsql_test between-2.1.$tn $sql $res
}
finish_test

203
testdata/tcl/bigfile.test vendored Normal file
View file

@ -0,0 +1,203 @@
# 2002 November 30
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this script testing the ability of SQLite to handle database
# files larger than 4GB.
#
# $Id: bigfile.test,v 1.12 2009/03/05 04:27:08 shane Exp $
#
if {[file exists skip-big-file]} return
if {$tcl_platform(os)=="Darwin"} return
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# Do not use a codec for this file, as the database is manipulated using
# external methods (the [fake_big_file] and [hexio_write] commands).
#
do_not_use_codec
# If SQLITE_DISABLE_LFS is defined, omit this file.
ifcapable !lfs {
finish_test
return
}
# These tests only work for Tcl version 8.4 and later. Prior to 8.4,
# Tcl was unable to handle large files.
#
scan $::tcl_version %f vx
if {$vx<8.4} return
# Mac OS X does not handle large files efficiently. So skip this test
# on that platform.
if {$tcl_platform(os)=="Darwin"} return
# This is the md5 checksum of all the data in table t1 as created
# by the first test. We will use this number to make sure that data
# never changes.
#
set MAGIC_SUM {593f1efcfdbe698c28b4b1b693f7e4cf}
do_test bigfile-1.1 {
execsql {
BEGIN;
CREATE TABLE t1(x);
INSERT INTO t1 VALUES('abcdefghijklmnopqrstuvwxyz');
INSERT INTO t1 SELECT rowid || ' ' || x FROM t1;
INSERT INTO t1 SELECT rowid || ' ' || x FROM t1;
INSERT INTO t1 SELECT rowid || ' ' || x FROM t1;
INSERT INTO t1 SELECT rowid || ' ' || x FROM t1;
INSERT INTO t1 SELECT rowid || ' ' || x FROM t1;
INSERT INTO t1 SELECT rowid || ' ' || x FROM t1;
INSERT INTO t1 SELECT rowid || ' ' || x FROM t1;
COMMIT;
}
execsql {
SELECT md5sum(x) FROM t1;
}
} $::MAGIC_SUM
# Try to create a large file - a file that is larger than 2^32 bytes.
# If this fails, it means that the system being tested does not support
# large files. So skip all of the remaining tests in this file.
#
db close
if {[catch {fake_big_file 4096 [get_pwd]/test.db} msg]} {
puts "**** Unable to create a file larger than 4096 MB. *****"
finish_test
return
}
hexio_write test.db 28 00000000
do_test bigfile-1.2 {
sqlite3 db test.db
execsql {
SELECT md5sum(x) FROM t1;
}
} $::MAGIC_SUM
# The previous test may fail on some systems because they are unable
# to handle large files. If that is so, then skip all of the following
# tests. We will know the above test failed because the "db" command
# does not exist.
#
if {[llength [info command db]]<=0} {
puts "**** Large file support appears to be broken. *****"
finish_test
return
}
do_test bigfile-1.3 {
execsql {
CREATE TABLE t2 AS SELECT * FROM t1;
SELECT md5sum(x) FROM t2;
}
} $::MAGIC_SUM
do_test bigfile-1.4 {
db close
sqlite3 db test.db
execsql {
SELECT md5sum(x) FROM t1;
}
} $::MAGIC_SUM
db close
if {[catch {fake_big_file 8192 [get_pwd]/test.db}]} {
puts "**** Unable to create a file larger than 8192 MB. *****"
finish_test
return
}
hexio_write test.db 28 00000000
do_test bigfile-1.5 {
sqlite3 db test.db
execsql {
SELECT md5sum(x) FROM t1;
}
} $::MAGIC_SUM
do_test bigfile-1.6 {
sqlite3 db test.db
execsql {
SELECT md5sum(x) FROM t2;
}
} $::MAGIC_SUM
do_test bigfile-1.7 {
execsql {
CREATE TABLE t3 AS SELECT * FROM t1;
SELECT md5sum(x) FROM t3;
}
} $::MAGIC_SUM
do_test bigfile-1.8 {
db close
sqlite3 db test.db
execsql {
SELECT md5sum(x) FROM t1;
}
} $::MAGIC_SUM
do_test bigfile-1.9 {
execsql {
SELECT md5sum(x) FROM t2;
}
} $::MAGIC_SUM
db close
if {[catch {fake_big_file 16384 [get_pwd]/test.db}]} {
puts "**** Unable to create a file larger than 16384 MB. *****"
finish_test
return
}
hexio_write test.db 28 00000000
do_test bigfile-1.10 {
sqlite3 db test.db
execsql {
SELECT md5sum(x) FROM t1;
}
} $::MAGIC_SUM
do_test bigfile-1.11 {
sqlite3 db test.db
execsql {
SELECT md5sum(x) FROM t2;
}
} $::MAGIC_SUM
do_test bigfile-1.12 {
sqlite3 db test.db
execsql {
SELECT md5sum(x) FROM t3;
}
} $::MAGIC_SUM
do_test bigfile-1.13 {
execsql {
CREATE TABLE t4 AS SELECT * FROM t1;
SELECT md5sum(x) FROM t4;
}
} $::MAGIC_SUM
do_test bigfile-1.14 {
db close
sqlite3 db test.db
execsql {
SELECT md5sum(x) FROM t1;
}
} $::MAGIC_SUM
do_test bigfile-1.15 {
execsql {
SELECT md5sum(x) FROM t2;
}
} $::MAGIC_SUM
do_test bigfile-1.16 {
execsql {
SELECT md5sum(x) FROM t3;
}
} $::MAGIC_SUM
finish_test

62
testdata/tcl/bigfile2.test vendored Normal file
View file

@ -0,0 +1,62 @@
# 2011 December 20
#
# 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 script testing the ability of SQLite to handle database
# files larger than 4GB.
#
if {[file exists skip-big-file]} return
if {$tcl_platform(os)=="Darwin"} return
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix bigfile2
# Create a small database.
#
do_execsql_test 1.1 {
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 2);
}
# Pad the file out to 4GB in size. Then clear the file-size field in the
# db header. This will cause SQLite to assume that the first 4GB of pages
# are actually in use and new pages will be appended to the file.
#
db close
if {[catch {fake_big_file 4096 [get_pwd]/test.db} msg]} {
puts "**** Unable to create a file larger than 4096 MB. *****"
finish_test
return
}
hexio_write test.db 28 00000000
do_test 1.2 {
file size test.db
} [expr 14 + 4096 * (1<<20)]
# Now insert a large row. The overflow pages will be located past the 4GB
# boundary. Then, after opening and closing the database, test that the row
# can be read back in.
#
set str [string repeat k 30000]
do_test 1.3 {
sqlite3 db test.db
execsql { INSERT INTO t1 VALUES(3, $str) }
db close
sqlite3 db test.db
db one { SELECT b FROM t1 WHERE a = 3 }
} $str
db close
delete_file test.db
finish_test

Some files were not shown because too many files have changed in this diff Show more