| # 2018-01-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 the "memdb" VFS | 
 | # | 
 |  | 
 | set testdir [file dirname $argv0] | 
 | source $testdir/tester.tcl | 
 | set testprefix memdb1 | 
 | do_not_use_codec | 
 |  | 
 | ifcapable !deserialize { | 
 |   finish_test | 
 |   return | 
 | } | 
 |  | 
 | # Create a MEMDB and populate it with some dummy data. | 
 | # Then extract the database into the $::db1 variable. | 
 | # Verify that the size of $::db1 is the same as the size of | 
 | # the database. | 
 | # | 
 | unset -nocomplain db1 | 
 | unset -nocomplain sz1 | 
 | unset -nocomplain pgsz | 
 | do_test 100 { | 
 |   db eval { | 
 |     CREATE TABLE t1(a,b); | 
 |     INSERT INTO t1 VALUES(1,2); | 
 |   } | 
 |   set ::pgsz [db one {PRAGMA page_size}] | 
 |   set ::sz1 [expr {$::pgsz*[db one {PRAGMA page_count}]}] | 
 |   set ::db1 [db serialize] | 
 |   expr {[string length $::db1]==$::sz1} | 
 | } 1 | 
 | set fd [open db1.db wb] | 
 | puts -nonewline $fd $db1 | 
 | close $fd | 
 |  | 
 | # Create a new MEMDB and initialize it to the content of $::db1 | 
 | # Verify that the content is the same. | 
 | # | 
 | db close | 
 | sqlite3 db | 
 | db deserialize $db1 | 
 | do_execsql_test 110 { | 
 |   SELECT * FROM t1; | 
 | } {1 2} | 
 |  | 
 | # What happens when we try to VACUUM a MEMDB database? | 
 | # | 
 | do_execsql_test 120 { | 
 |   PRAGMA auto_vacuum = off; | 
 |   VACUUM; | 
 | } {} | 
 | do_execsql_test 130 { | 
 |   CREATE TABLE t2(x, y); | 
 |   WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100) | 
 |    INSERT INTO t2(x, y) SELECT x, randomblob(1000) FROM c; | 
 |   DROP TABLE t2; | 
 |   PRAGMA page_count; | 
 | } {116} | 
 | do_execsql_test 140 { | 
 |   VACUUM; | 
 |   PRAGMA page_count; | 
 | } {2} | 
 |  | 
 | do_test 150 { | 
 |   catch {db deserialize -unknown 1 $db1} msg | 
 |   set msg | 
 | } {unknown option: -unknown} | 
 | do_test 151 { | 
 |   db deserialize -readonly 1 $db1 | 
 |   db eval {SELECT * FROM t1} | 
 | } {1 2} | 
 | do_test 152 { | 
 |   catchsql {INSERT INTO t1 VALUES(3,4);} | 
 | } {1 {attempt to write a readonly database}} | 
 |  | 
 | breakpoint | 
 | do_test 160 { | 
 |   db deserialize -maxsize 32768 $db1 | 
 |   db eval {SELECT * FROM t1} | 
 | } {1 2} | 
 | do_test 161 { | 
 |   db eval {INSERT INTO t1 VALUES(3,4); SELECT * FROM t1} | 
 | } {1 2 3 4} | 
 | do_test 162 { | 
 |   catchsql {INSERT INTO t1 VALUES(5,randomblob(100000))} | 
 | } {1 {database or disk is full}} | 
 |  | 
 |  | 
 | # Build a largish on-disk database and serialize it.  Verify that the | 
 | # serialization works. | 
 | # | 
 | db close | 
 | forcedelete test.db | 
 | sqlite3 db test.db | 
 | do_execsql_test 200 { | 
 |   CREATE TABLE t3(x, y); | 
 |   WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<400) | 
 |    INSERT INTO t3(x, y) SELECT x, randomblob(1000) FROM c; | 
 |   PRAGMA quick_check; | 
 | } {ok} | 
 | set fd [open test.db rb] | 
 | unset -nocomplain direct | 
 | set direct [read $fd] | 
 | close $fd | 
 | do_test 210 { | 
 |   string length [db serialize] | 
 | } [string length $direct] | 
 | do_test 220 { | 
 |   db eval {ATTACH ':memory:' AS aux1} | 
 |   db deserialize aux1 $::direct | 
 |   db eval { | 
 |      SELECT x, y FROM main.t3 EXCEPT SELECT x, y FROM aux1.t3; | 
 |   } | 
 | } {} | 
 | unset -nocomplain direct | 
 |  | 
 | # Do the same with a :memory: database. | 
 | # | 
 | db close | 
 | sqlite3 db :memory: | 
 | do_execsql_test 300 { | 
 |   CREATE TABLE t3(x, y); | 
 |   WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<400) | 
 |    INSERT INTO t3(x, y) SELECT x, randomblob(1000) FROM c; | 
 |   PRAGMA quick_check; | 
 | } {ok} | 
 | do_test 310 { | 
 |   db eval {ATTACH ':memory:' AS aux1} | 
 |   db deserialize aux1 [db serialize main] | 
 |   db eval { | 
 |      SELECT x, y FROM main.t3 EXCEPT SELECT x, y FROM aux1.t3; | 
 |   } | 
 | } {} | 
 |  | 
 | # Deserialize an empty database | 
 | # | 
 | db close | 
 | sqlite3 db | 
 | db deserialize {} | 
 | do_execsql_test 400 { | 
 |   PRAGMA integrity_check; | 
 | } {ok} | 
 | do_execsql_test 410 { | 
 |   CREATE TABLE t4(a,b); | 
 |   INSERT INTO t4 VALUES('hello','world!'); | 
 |   PRAGMA integrity_check; | 
 |   SELECT * FROM t4; | 
 | } {ok hello world!} | 
 | do_execsql_test 420 { | 
 |   PRAGMA journal_mode=TRUNCATE; | 
 |   PRAGMA journal_mode=OFF; | 
 |   PRAGMA journal_mode=DELETE; | 
 |   PRAGMA journal_mode=WAL; | 
 |   PRAGMA journal_mode=PERSIST; | 
 |   PRAGMA journal_mode=MEMORY; | 
 |   PRAGMA journal_mode=OFF; | 
 |   PRAGMA journal_mode=DELETE; | 
 | } {truncate off delete delete persist memory off delete} | 
 |  | 
 | # Deserialize something that is not a database. | 
 | # | 
 | db close | 
 | sqlite3 db | 
 | do_test 500 { | 
 |   set rc [catch {db deserialize not-a-database} msg] | 
 |   lappend rc $msg | 
 | } {0 {}} | 
 | do_catchsql_test 510 { | 
 |   PRAGMA integrity_check; | 
 | } {1 {file is not a database}} | 
 |  | 
 | # Abuse the serialize and deserialize commands.  Make sure errors are caught. | 
 | # | 
 | do_test 600 { | 
 |   set rc [catch {db deserialize} msg] | 
 |   lappend rc $msg | 
 | } {1 {wrong # args: should be "db deserialize ?DATABASE? VALUE"}} | 
 | do_test 610 { | 
 |   set rc [catch {db deserialize a b c} msg] | 
 |   lappend rc $msg | 
 | } {1 {unknown option: a}} | 
 | do_test 620 { | 
 |   set rc [catch {db serialize a b} msg] | 
 |   lappend rc $msg | 
 | } {1 {wrong # args: should be "db serialize ?DATABASE?"}} | 
 |  | 
 | # 2021-07-19 https://sqlite.org/forum/forumpost/e1cbb5f450b98aa6 | 
 | # The TEMP database cannot participate in serialization or | 
 | # deserialization. | 
 | # | 
 | reset_db | 
 | do_test 650 { | 
 |   db eval { | 
 |     CREATE TEMP TABLE t0(a); | 
 |     CREATE TABLE t1(x); | 
 |     WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<1000) | 
 |     INSERT INTO t1(x) SELECT random() FROM c; | 
 |   } | 
 |   set rc [catch {db deserialize temp [db serialize main]} err] | 
 |   lappend rc err | 
 | } {1 err} | 
 |  | 
 | #------------------------------------------------------------------------- | 
 | ifcapable vtab { | 
 |   reset_db | 
 |   do_execsql_test 700 { | 
 |     CREATE TABLE t1(a, b); | 
 |     PRAGMA schema_version = 0; | 
 |   } | 
 |   do_test 710 { | 
 |     set ser [db serialize main] | 
 |     db close | 
 |     sqlite3 db | 
 |     db deserialize main $ser | 
 |     catchsql { | 
 |       CREATE VIRTUAL TABLE t1 USING rtree(id, a, b, c, d); | 
 |     } | 
 |   } {1 {table t1 already exists}} | 
 | } | 
 |  | 
 |  | 
 | #------------------------------------------------------------------------- | 
 | # dbsqlfuzz  0a13dfb474d4f2f11a48a2ea57075c96fb456dd7 | 
 | # | 
 | if {[wal_is_capable]} { | 
 |   reset_db | 
 |   do_execsql_test 800 { | 
 |     PRAGMA auto_vacuum = 0; | 
 |     PRAGMA page_size = 8192; | 
 |     PRAGMA journal_mode = wal; | 
 |     CREATE TABLE t1(x, y); | 
 |     INSERT INTO t1 VALUES(1, 2); | 
 |     CREATE TABLE t2(x, y); | 
 |   } {wal} | 
 |   db close | 
 |    | 
 |   set fd [open test.db] | 
 |   fconfigure $fd -translation binary -encoding binary | 
 |   set data [read $fd [expr 20*1024]] | 
 |    | 
 |   sqlite3 db "" | 
 |   db deserialize $data | 
 |    | 
 |   do_execsql_test 810 { | 
 |     PRAGMA locking_mode = exclusive; | 
 |     SELECT * FROM t1 | 
 |   } {exclusive 1 2} | 
 |    | 
 |   do_execsql_test 820 { | 
 |     INSERT INTO t1 VALUES(3, 4); | 
 |     SELECT * FROM t1; | 
 |   } {1 2 3 4} | 
 |    | 
 |   do_catchsql_test 830 { | 
 |     PRAGMA wal_checkpoint; | 
 |   } {1 {database disk image is malformed}} | 
 | } | 
 |  | 
 | finish_test |