summaryrefslogtreecommitdiffstats
path: root/test/vacuum-into.test
blob: d559b7fb398f86aee952af1b58b8a3fc73abb207 (plain)
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# 2018-12-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 VACUUM INTO statement.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# If the VACUUM statement is disabled in the current build, skip all
# the tests in this file.
#
ifcapable {!vacuum} {
  omit_test vacuum.test {Compiled with SQLITE_OMIT_VACUUM}
  finish_test
  return
}

forcedelete out.db
do_execsql_test vacuum-into-100 {
  CREATE TABLE t1(
    a INTEGER PRIMARY KEY,
    b ANY,
    c INT AS (b+1),                          --- See "2024-04-09" block
    CHECK( typeof(b)!='integer' OR b>a-5 )   --- comment below
  );
  WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100)
  INSERT INTO t1(a,b) SELECT x, randomblob(600) FROM c;
  CREATE INDEX t1b ON t1(b);
  DELETE FROM t1 WHERE a%2;
  SELECT count(*), sum(a), sum(length(b)) FROM t1;
} {50 2550 30000}

# Update 2024-04-09 for forum post eec177d68fe7fa2c.
#
# VACUUM INTO is sensitive to tables holding both generated columns
# and CHECK constraints.  
#
# CHECK constraints are ignored for read-only databases in order to save
# memory (see check-in 34ddf02d3d21151b on 2014-05-21).  But the xfer
# optimization normally only works if CHECK constraints match between the
# source and destination tables.  So the xfer optimization was not
# working for VACUUM INTO when the source was a read-only database and the
# table held CHECK constraints.  But if the table has generated columns,
# then the xfer optimization is required or else VACUUM will raise an
# error.
#
# Fix this by ignoring CHECK constraints when determining whether or not
# the xfer optimization can run while doing VACUUM.

do_execsql_test vacuum-into-110 {
  VACUUM main INTO 'out.db';
} {}
sqlite3 db2 out.db
do_test vacuum-into-120 {
  db2 eval {SELECT count(*), sum(a), sum(length(b)) FROM t1}
} {50 2550 30000}
do_catchsql_test vacuum-into-130 {
  VACUUM INTO 'out.db';
} {1 {output file already exists}}
forcedelete out2.db
do_catchsql_test vacuum-into-140 {
  VACUUM INTO 'out2.db';
} {0 {}}
do_catchsql_test vacuum-into-150 {
  VACUUM INTO 'out2.db';
} {1 {output file already exists}}

do_catchsql_test vacuum-into-200 {
  VACUUM main INTO ':memory:';
} {0 {}}

# The INTO argument can be an arbitrary expression.
#
do_execsql_test vacuum-into-300 {
  CREATE TABLE t2(name TEXT);
  INSERT INTO t2 VALUES(':memory:');
  VACUUM main INTO (SELECT name FROM t2);
} {}
do_catchsql_test vacuum-into-310 {
  VACUUM INTO null;
} {1 {non-text filename}}
do_catchsql_test vacuum-into-320 {
  VACUUM INTO x;
} {1 {no such column: x}}
do_catchsql_test vacuum-into-330 {
  VACUUM INTO t1.nosuchcol;
} {1 {no such column: t1.nosuchcol}}
do_catchsql_test vacuum-into-340 {
  VACUUM INTO main.t1.nosuchcol;
} {1 {no such column: main.t1.nosuchcol}}

forcedelete test.db2
db func target target
proc target {} { return "test.db2" }
do_test vacuum-into-410 {
  execsql { VACUUM INTO target() }
  file exists test.db2
} 1
do_catchsql_test vacuum-into-420 {
  VACUUM INTO target2()
} {1 {no such function: target2}}

# The ability to VACUUM INTO a read-only database
db close
if {$tcl_platform(platform)=="windows"} {
  file attributes test.db -readonly 1
} else {
  file attributes test.db -permissions 292  ;# 292 == 0444
}
sqlite3 db test.db -readonly 1
forcedelete test.db2
do_execsql_test vacuum-into-500 {
  VACUUM INTO 'test.db2';
}
if {$tcl_platform(platform)=="windows"} {
  file attributes test.db -readonly 0
} else {
  file attributes test.db -permissions 420   ;# 420 = 0644
}
sqlite3 db2 test.db2
do_test vacuum-into-510 {
  db2 eval {SELECT name FROM sqlite_master ORDER BY 1}
} {t1 t1b t2}
db2 close
db close

# Change the page-size on a VACUUM INTO even if the original
# database is in WAL mode.
#
if {[wal_is_capable]} {
  forcedelete test.db
  forcedelete test.db2
  do_test vacuum-into-600 {
    sqlite3 db test.db
    db eval {
      PRAGMA page_size=4096;
      PRAGMA journal_mode=WAL;
      CREATE TABLE t1(a);
      INSERT INTO t1 VALUES(19);
      CREATE INDEX t1a ON t1(a);
      PRAGMA integrity_check;
    }
  } {wal ok}
  do_execsql_test vacuum-into-610 {
    PRAGMA page_size;
  } {4096}
  do_execsql_test vacuum-into-620 {
    PRAGMA page_size=1024;
    VACUUM INTO 'test.db2';
  } {}
  do_test vacuum-into-630 {
    sqlite3 db test.db2
    db eval {
      PRAGMA page_size;
      PRAGMA integrity_check;
    }
  } {1024 ok}
}

#-------------------------------------------------------------------------

testvfs tvfs -default 1
tvfs filter xSync
tvfs script xSyncCb
proc xSyncCb {method file fileid flags} {
  incr ::sync($flags)
}

reset_db

do_execsql_test vacuum-into-700 {
  CREATE TABLE t1(a, b);
  INSERT INTO t1 VALUES(1, 2);
}

foreach {tn pragma res} {
  710 {
    PRAGMA synchronous = normal
  } {normal 2}
  720 {
    PRAGMA synchronous = full
  } {normal 3}
  730 {
    PRAGMA synchronous = off
  } {}
  740 {
    PRAGMA synchronous = extra;
  } {normal 3}
  750 {
    PRAGMA fullfsync = 1;
    PRAGMA synchronous = full;
  } {full|dataonly 1 full 2}
} {

  forcedelete test.db2
  array unset ::sync
  do_execsql_test vacuum-into-$tn.1 "
    $pragma ;
    VACUUM INTO 'test.db2'
  "

  do_test vacuum-into-$tn.2 {
    array get ::sync
  } $res
}

db close
tvfs delete


finish_test