Compare commits
80 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a31be8fa52 | |||
| ab7365af6d | |||
| b6b86f6ebf | |||
| c3c71c1451 | |||
| 5e87610fdf | |||
| d3698b7fe8 | |||
| 72d2de6eef | |||
| 0df48684bd | |||
| 106ab91fc8 | |||
| 806e04ab8f | |||
| eb888e2497 | |||
| d973196e30 | |||
| f6b6f46538 | |||
| f8f62070f8 | |||
| 73debcccd1 | |||
| 1430806e39 | |||
| 49c7657ac9 | |||
| e046ccb5f0 | |||
| 0b5d397cb4 | |||
| 2833522a9a | |||
| 06526fef2e | |||
| 7e0410951f | |||
| abbad7ef3c | |||
| cdfb4a29f4 | |||
| ce86700aa7 | |||
| 5593a4c26d | |||
| 88e7099875 | |||
| 28c156d6d6 | |||
| e2e8e9b16f | |||
| ff0e1d8d04 | |||
| 98c5bff6c8 | |||
| 3eadd64c0e | |||
| d32261e7a9 | |||
| 6847647dc4 | |||
| ae070eff74 | |||
| f4c01da150 | |||
| 0d46c528dd | |||
| 209f79234b | |||
| 22b16ac3d0 | |||
| 88a96c49f2 | |||
| 85a76ddc12 | |||
| 20a434abd3 | |||
| 9c2b284dbd | |||
| 74e020a164 | |||
| 94dba01337 | |||
| 239f69e204 | |||
| 05a094d4c5 | |||
| aec53fa5ba | |||
| a011145aae | |||
| 13af9ba7af | |||
| 6170a98cfa | |||
| d0932d5358 | |||
| 61155cb70b | |||
| d746d250f0 | |||
| 155a112b81 | |||
| a5150d4f05 | |||
| 64104062a7 | |||
| 1395c92f1f | |||
| 33a37f5ef0 | |||
| 9d65677e57 | |||
| 6758315c8e | |||
| 5944230305 | |||
| a72a438cd7 | |||
| d62150108a | |||
| 2ce6757bd4 | |||
| 33d1394e1a | |||
| 81fb4ef019 | |||
| d4e11dd78f | |||
| 5add6f25e2 | |||
| 30fdc47579 | |||
| 73e6f3adbd | |||
| 73b8e1f2d6 | |||
| 01d6eab9a5 | |||
| e54087db7b | |||
| d000f2286d | |||
| 8874678ecd | |||
| 8612853f70 | |||
| 62012c0b34 | |||
| 7d3159668c | |||
| d6af93a92c |
@@ -416,6 +416,7 @@ TESTSRC = \
|
||||
$(TOP)/src/test_quota.c \
|
||||
$(TOP)/src/test_rtree.c \
|
||||
$(TOP)/src/test_schema.c \
|
||||
$(TOP)/src/test_schemapool.c \
|
||||
$(TOP)/src/test_server.c \
|
||||
$(TOP)/src/test_superlock.c \
|
||||
$(TOP)/src/test_syscall.c \
|
||||
@@ -472,6 +473,7 @@ TESTSRC2 = \
|
||||
$(TOP)/src/bitvec.c \
|
||||
$(TOP)/src/btree.c \
|
||||
$(TOP)/src/build.c \
|
||||
$(TOP)/src/callback.c \
|
||||
$(TOP)/src/ctime.c \
|
||||
$(TOP)/src/date.c \
|
||||
$(TOP)/src/dbpage.c \
|
||||
|
||||
@@ -1523,6 +1523,7 @@ TESTSRC = \
|
||||
$(TOP)\src\test_quota.c \
|
||||
$(TOP)\src\test_rtree.c \
|
||||
$(TOP)\src\test_schema.c \
|
||||
$(TOP)\src\test_schemapool.c \
|
||||
$(TOP)\src\test_server.c \
|
||||
$(TOP)\src\test_superlock.c \
|
||||
$(TOP)\src\test_syscall.c \
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
|
||||
Shared-Schema Mode Notes
|
||||
========================
|
||||
|
||||
The [reuse-schema](/timeline?r=reuse-schema) branch contains changes
|
||||
to allow SQLite connections to share schemas
|
||||
between database connections within the same process in order to save memory.
|
||||
Schemas may be shared between multiple databases attached to the same or
|
||||
distinct connection handles.
|
||||
|
||||
Compile with -DSQLITE\_ENABLE\_SHARED\_SCHEMA in order to enable the
|
||||
shared-schema enhancement. Enabling the shared-schema enhancement causes
|
||||
approximately a 0.1% increase in CPU cycles consumed and about a 3000-byte
|
||||
increase in the size of the library, even if shared-schema is never used.
|
||||
|
||||
Assuming the compile-time requirements are satisfied, the shared-schema
|
||||
feature is engaged by opening the database connection using the
|
||||
sqlite3_open_v2() API with the SQLITE_OPEN_SHARED_SCHEMA
|
||||
flag specified. The main database and any attached databases will then share
|
||||
an in-memory Schema object with any other database opened within the process
|
||||
for which:
|
||||
|
||||
* the contents of the sqlite_master table, including all object names,
|
||||
SQL statements and root pages are identical, and
|
||||
* have the same values for the schema-cookie.
|
||||
|
||||
Temp databases (those populated with "CREATE TEMP TABLE" and similar
|
||||
statements) never share schemas.
|
||||
|
||||
Connections opened with the SQLITE_OPEN_SHARED_SCHEMA flag
|
||||
specified may not modify any database schema except that belonging to the
|
||||
temp database in anyway. This includes creating or dropping database
|
||||
objects, vacuuming the database, or running ANALYZE when the
|
||||
sqlite_stat\[14\] tables do not exist.
|
||||
|
||||
For SQLITE_OPEN_SHARED_SCHEMA connections, the
|
||||
SQLITE_DBSTATUS_SCHEMA_USED sqlite3_db_status() verb
|
||||
distributes the memory used for a shared schema object evenly between all
|
||||
database connections that share it.
|
||||
|
||||
## The ".shared-schema" Command
|
||||
|
||||
The shell tool on this branch contains a special dot-command to help with
|
||||
managing databases. The ".shared-schema" dot-command can be used to test
|
||||
whether or not two databases are similar enough to share in-memory schemas,
|
||||
and to fix minor problems that prevent them from doing so. To test if
|
||||
two or more database are compatible, one database is opened directly using
|
||||
the shell tool and the following command issued:
|
||||
|
||||
.shared-schema check <database-1> [<database-2>]...
|
||||
|
||||
where <database-1> etc. are replaced with the names of database files
|
||||
on disk. For each database specified on the command line, a single line of
|
||||
output is produced. If the database can share an in-memory schema with the
|
||||
main database opened by the shell tool, the output is of the form:
|
||||
|
||||
<database> is compatible
|
||||
|
||||
Otherwise, if the database cannot share a schema with the main db, the output
|
||||
is of the form:
|
||||
|
||||
<database> is NOT compatible (<reason>)
|
||||
|
||||
where <reason> indicates the cause of the incompatibility. <reason>
|
||||
is always one of the following.
|
||||
|
||||
<ul>
|
||||
<li> <b>objects</b> - the databases contain a different set schema objects
|
||||
(tables, indexes, views and triggers).
|
||||
|
||||
<li> <b>SQL</b> - the databases contain the same set of objects, but the SQL
|
||||
statements used to create them were not the same.
|
||||
|
||||
<li> <b>root pages</b> - the databases contain the same set of objects created
|
||||
by the same SQL statements, but the root pages are not the same.
|
||||
|
||||
<li> <b>order of sqlite_master rows</b> - the databases contain the same
|
||||
set of objects created by the same SQL statements with the same root pages,
|
||||
but the order of the rows in the sqlite_master tables are different.
|
||||
|
||||
<li> <b>schema cookie</b> - the database schemas are compatible, but the
|
||||
schema cookie values ("PRAGMA schema_version") are different.
|
||||
</ul>
|
||||
|
||||
The final three problems in the list above can be fixed using the
|
||||
.shared-schema command. To modify such a database so that it can share a
|
||||
schema with the main database, the following shell command is used:
|
||||
|
||||
.shared-schema fix <database-1> [<database-2>]...
|
||||
|
||||
If a database can be modified so that it may share a schema with the main
|
||||
database opened by the shell tool, output is as follows:
|
||||
|
||||
Fixing <database>... <database> is compatible
|
||||
|
||||
If a database does not require modification, or cannot be modified such that
|
||||
it can share a schema with the main database, the output of "fix" is identical
|
||||
to that of the "check" command.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
A single Schema object is never used by more than one database simultaneously,
|
||||
regardless of whether or not those databases are attached to the same or
|
||||
different database handles. Instead, a pool of schema objects is maintained
|
||||
for each unique sqlite_master-contents/schema-cookie combination
|
||||
opened within the process. Each time database schemas are required by a
|
||||
connection, for example as part of an sqlite3_prepare\*(),
|
||||
sqlite3_blob_open() or sqlite3_blob_open() call, it obtains
|
||||
the minimum number of schemas required from the various schema-pools, returning
|
||||
them at the end of the call. This means that a single schema-pool only ever
|
||||
contains more than one copy of the schema if:
|
||||
|
||||
* Two threads require schemas from the same pool at the same time, or
|
||||
* A single sqlite3_prepare\*() call requires schemas for two or more
|
||||
attached databases that use the same schema-pool.
|
||||
|
||||
The size of a schema-pool never shrinks. Each schema pool always maintains
|
||||
a number of schema objects equal to the highwater mark of schema objects
|
||||
simultaneously required by clients.
|
||||
|
||||
This approach is preferred to allowing multiple databases to use the same
|
||||
Schema object simultaneously for three reasons:
|
||||
|
||||
* The Schema object is not completely read-only. For example, the
|
||||
Index.zIdxAff string is allocated lazily.
|
||||
* Throughout the statement compiler, SQLite uses variables like
|
||||
Table.pSchema and Index.pSchema with the sqlite3SchemaToIndex() routine
|
||||
in order to determine which attached database a Table or Index object
|
||||
resides in. This mechanism does not work if the same Schema may be
|
||||
used by two or more attached databases.
|
||||
* It may be easier to modify this approach in order to allow
|
||||
SQLITE_OPEN_SHARED_SCHEMA connections to modify database
|
||||
schemas, should that be required.
|
||||
|
||||
SQLITE_OPEN_SHARED_SCHEMA connections do not store their
|
||||
virtual-table handles in the Table.pVTable list of each table. This would not
|
||||
work, as (a) there is no guarantee that a connection will be assigned the same
|
||||
Schema object each time it requests one from a schema-pool and (b) a single
|
||||
Schema (and therefore Table) object may correspond to tables in two or more
|
||||
databases attached to a single connection. Instead, all virtual-table handles
|
||||
associated with a single database are stored in a linked-list headed at
|
||||
Db.pVTable.
|
||||
@@ -4828,11 +4828,14 @@ static int rbuVfsShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
|
||||
#endif
|
||||
|
||||
assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) );
|
||||
if( pRbu && (pRbu->eStage==RBU_STAGE_OAL || pRbu->eStage==RBU_STAGE_MOVE) ){
|
||||
/* Magic number 1 is the WAL_CKPT_LOCK lock. Preventing SQLite from
|
||||
** taking this lock also prevents any checkpoints from occurring.
|
||||
** todo: really, it's not clear why this might occur, as
|
||||
** wal_autocheckpoint ought to be turned off. */
|
||||
if( pRbu && (
|
||||
pRbu->eStage==RBU_STAGE_OAL
|
||||
|| pRbu->eStage==RBU_STAGE_MOVE
|
||||
|| pRbu->eStage==RBU_STAGE_DONE
|
||||
)){
|
||||
/* Prevent SQLite from taking a shm-lock on the target file when it
|
||||
** is supplying heap memory to the upper layer in place of *-shm
|
||||
** segments. */
|
||||
if( ofst==WAL_LOCK_CKPT && n==1 ) rc = SQLITE_BUSY;
|
||||
}else{
|
||||
int bCapture = 0;
|
||||
|
||||
@@ -341,6 +341,7 @@ TESTSRC = \
|
||||
$(TOP)/src/test_quota.c \
|
||||
$(TOP)/src/test_rtree.c \
|
||||
$(TOP)/src/test_schema.c \
|
||||
$(TOP)/src/test_schemapool.c \
|
||||
$(TOP)/src/test_server.c \
|
||||
$(TOP)/src/test_sqllog.c \
|
||||
$(TOP)/src/test_superlock.c \
|
||||
@@ -392,6 +393,7 @@ TESTSRC2 = \
|
||||
$(TOP)/src/backup.c \
|
||||
$(TOP)/src/btree.c \
|
||||
$(TOP)/src/build.c \
|
||||
$(TOP)/src/callback.c \
|
||||
$(TOP)/src/date.c \
|
||||
$(TOP)/src/dbpage.c \
|
||||
$(TOP)/src/dbstat.c \
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
C Fix\sa\sproblem\sin\ssqlite3CodecQueryParameters()\sthat\swas\sintroduced\sby\sthe\nquery\sparameter\sencoding\schanges\sfor\sthe\s3.31.1\srelease.
|
||||
D 2020-02-01T13:30:39.095
|
||||
C In\sRBU,\savoid\spassing\sVFS\sxShmLock\scalls\sthrough\sto\sthe\sunderlying\sVFS\sin\scases\swhere\sxShmMap\scalls\smay\snot\sbe.\sThis\sfixes\sa\sbad\sinteraction\swith\sZipVFS.
|
||||
D 2021-02-11T16:19:12.056
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
F Makefile.in 9dfc7936f675785309b74d09202bb656732325e65df889e5aaa18cc8932e5b0c
|
||||
F Makefile.in e97846dfaeb3851107b4dcbffbfc1245d54ff953cf08cc27ab85f1044d057bb7
|
||||
F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241
|
||||
F Makefile.msc fab23c6b10cb6f06a7e9c407fc2e35cb184a6d653f1b793bda87fcee2eafa4f6
|
||||
F Makefile.msc a5fc3fada762671dbeb34d1995448c06afb1ec4d287e4bb72c6889e8f1291935
|
||||
F README.md 1514a365ffca3c138e00c5cc839906108a01011a6b082bad19b09781e3aa498a
|
||||
F VERSION c4deaf1a08bf7fa765deb3dbf70f38674f2311ad65fda971a726bfda1d9d5157
|
||||
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
|
||||
@@ -40,6 +40,7 @@ F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
|
||||
F doc/F2FS.txt c1d4a0ae9711cfe0e1d8b019d154f1c29e0d3abfe820787ba1e9ed7691160fcd
|
||||
F doc/lemon.html 24956ab2995e55fe171e55bdd04f22b553957dc8bb43501dbb9311e30187e0d3
|
||||
F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710
|
||||
F doc/shared_schema.md 759fc374709fccf4e5d2d0cbd05f8fedd38fb022bdd8a6c5b5f492684c7023b9
|
||||
F doc/trusted-schema.md 33625008620e879c7bcfbbfa079587612c434fa094d338b08242288d358c3e8a
|
||||
F doc/vfs-shm.txt e101f27ea02a8387ce46a05be2b1a902a021d37a
|
||||
F ext/README.md fd5f78013b0a2bc6f0067afb19e6ad040e89a10179b4f6f03eee58fac5f169bd
|
||||
@@ -370,7 +371,7 @@ F ext/rbu/rbuvacuum.test 55e101e90168c2b31df6c9638fe73dc7f7cc666b6142266d1563697
|
||||
F ext/rbu/rbuvacuum2.test b8e5b51dc8b2c0153373d024c0936be3f66f9234acbd6d0baab0869d56b14e6b
|
||||
F ext/rbu/rbuvacuum3.test 8addd82e4b83b4c93fa47428eae4fd0dbf410f8512c186f38e348feb49ba03dc
|
||||
F ext/rbu/rbuvacuum4.test a78898e438a44803eb2bc897ba3323373c9f277418e2d6d76e90f2f1dbccfd10
|
||||
F ext/rbu/sqlite3rbu.c 77a47f3231f5f363b2c584dba3e310a7efdaf073ad8c18728ab846b38de2879c
|
||||
F ext/rbu/sqlite3rbu.c 41424d130e4b66e6c487b9134e3adab0cd0c679bf37321105e700c59a046c2af
|
||||
F ext/rbu/sqlite3rbu.h 1dc88ab7bd32d0f15890ea08d23476c4198d3da3056985403991f8c9cd389812
|
||||
F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f59742821828a
|
||||
F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15
|
||||
@@ -453,7 +454,7 @@ F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
|
||||
F main.mk 7ce055f3df31a4f7d21e38f493f907c21db1f673863a573e231f55e2ab005023
|
||||
F main.mk 4f739b347cce517de440e0c22e6fdd10f9290b716efa6005ae7cfacbbd5f1a77
|
||||
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
|
||||
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
|
||||
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
|
||||
@@ -465,9 +466,9 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
|
||||
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
|
||||
F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
|
||||
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
|
||||
F src/alter.c f48a4423c8f198d7f1ae4940f74b606707d05384ac79fb219be8e3323af2a2de
|
||||
F src/analyze.c b3ceec3fc052df8a96ca8a8c858d455dc5029ba681b4be98bb5c5a9162cfa58c
|
||||
F src/attach.c df0ead9091042c68964856ecc08dba55d5403ad5f3ca865d9d396d71528c511a
|
||||
F src/alter.c 5584426b5622b10d2c4cd6695585fcdc4216c0bd0e33ff9958106096ab770fb0
|
||||
F src/analyze.c caa9cc97a066a9f0cac443d67676cf4883afb89153a4fdc9ccd61a3a8ae8ef9b
|
||||
F src/attach.c 167b341f4c62807853643f9428b1197b4efd691dd443641556776d3c5c1c443c
|
||||
F src/auth.c a3d5bfdba83d25abed1013a8c7a5f204e2e29b0c25242a56bc02bb0c07bf1e06
|
||||
F src/backup.c f70077d40c08b7787bfe934e4d1da8030cb0cc57d46b345fba2294b7d1be23ab
|
||||
F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33
|
||||
@@ -475,17 +476,17 @@ F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
|
||||
F src/btree.c 7af5ff0f88ba856c2681f6eeb457590b24f787e994f18cbdb44c2de2d33f757e
|
||||
F src/btree.h 6111552f19ed7a40f029cf4b33badc6fef9880314fffd80a945f0b7f43ab7471
|
||||
F src/btreeInt.h 6794084fad08c9750b45145743c0e3e5c27c94dee89f26dd8df7073314934fd2
|
||||
F src/build.c 2394d2c853088106dfc1cf485d609f20e6421d7c84892b795824e454f78e50ad
|
||||
F src/callback.c c547d00963ae28100117b4fb1f0f32242109b5804374ee3bfe01138a54da7f76
|
||||
F src/build.c 9edbf33eba02233a9040d43786dce0980f2a4a827b8dffd4b332461f19f6c8ab
|
||||
F src/callback.c a3ed3fefacda1382f1ac4288a2fa15046f1e0d8418e3a281e501c9f0b52d5188
|
||||
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
|
||||
F src/ctime.c 1b0724e66f95f33b160b1af85caaf9cceb325d22abf39bd24df4f54a73982251
|
||||
F src/ctime.c 15d1010037db3577d2e90e793197c539e86860ab272d7a49e4e976425d0eba62
|
||||
F src/date.c 6c408fdd2e9ddf6e8431aba76315a2d061bea2cec8fbb75e25d7c1ba08274712
|
||||
F src/dbpage.c 8a01e865bf8bc6d7b1844b4314443a6436c07c3efe1d488ed89e81719047833a
|
||||
F src/dbstat.c 0f55297469d4244ab7df395849e1af98eb5e95816af7c661e7d2d8402dea23da
|
||||
F src/delete.c a5c59b9c0251cf7682bc52af0d64f09b1aefc6781a63592c8f1136f7b73c66e4
|
||||
F src/expr.c 003c59158b33d7f3b198122cb0d1e13c06517cc3932e56b42283eb0e96696d66
|
||||
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
||||
F src/fkey.c 92a248ec0fa4ed8ab60c98d9b188ce173aaf218f32e7737ba77deb2a684f9847
|
||||
F src/fkey.c 2d234423b9aaf3b59dc8e0ee361f47ddf7806f655c94f75abfa74638c71f2025
|
||||
F src/func.c 108577cebe8a50c86d849a93b99493a54e348dd0b846f00d13b52ca973d5baf4
|
||||
F src/global.c 59601d885a0dbbfbd22ed2d030424a5e7f1b9809a17ca46686058bbc4a55e980
|
||||
F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19
|
||||
@@ -495,7 +496,7 @@ F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
|
||||
F src/insert.c 2fe4d7f67078a68650f16e4efe73207899e21702e6b9d2e8ad1894c76dcad352
|
||||
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
|
||||
F src/loadext.c 8cd803f1747c03a50b32fe87ebfb5851998d0cdafefe02737daa95e0616b42bb
|
||||
F src/main.c a82239262ef4d926fc00824e1ab4fdb958496d6efe9ee8ebd3e37d6005deccc4
|
||||
F src/main.c df78ddc1dcb9c619bf179991f344f42900e1dd6a92b1c8bb83b182d6ecb71177
|
||||
F src/malloc.c eaa4dc9602ce28b077f7de2eb275db2be270c5cc56d7fec5466301bd9b80e2f5
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de
|
||||
@@ -524,23 +525,23 @@ F src/parse.y c8d2de64db469fd56e0fa24da46cd8ec8523eb98626567d2708df371b47fdc3f
|
||||
F src/pcache.c 385ff064bca69789d199a98e2169445dc16e4291fa807babd61d4890c3b34177
|
||||
F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586
|
||||
F src/pcache1.c 6596e10baf3d8f84cc1585d226cf1ab26564a5f5caf85a15757a281ff977d51a
|
||||
F src/pragma.c 0d49d43b22d66397aa026db505457f6683d8a66cd0a4f9db2e6776156bda716c
|
||||
F src/pragma.h 9f86a3a3a0099e651189521c8ad03768df598974e7bbdc21c7f9bb6125592fbd
|
||||
F src/prepare.c 6049beb71385f017af6fc320d2c75a4e50b75e280c54232442b785fbb83df057
|
||||
F src/pragma.c 76563bedaeba1de2151934c0aa35108fb0cff537bc53df39d50fca02253a8e32
|
||||
F src/pragma.h 3b8fc00c101f379352e1c733ce9c5180ac6826575d0cca118c6e70fd20664d02
|
||||
F src/prepare.c 9e8f3d609602eb51b8224fca3dc9a0d30392937cec147955ca5e11c6cc5f65ef
|
||||
F src/printf.c 9be6945837c839ba57837b4bc3af349eba630920fa5532aa518816defe42a7d4
|
||||
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
|
||||
F src/resolve.c f0781c9e180028b279bc4ff079ad54f4727223d470c8d2343643fcaf79b67740
|
||||
F src/rowset.c d977b011993aaea002cab3e0bb2ce50cf346000dff94e944d547b989f4b1fe93
|
||||
F src/select.c 3f7aecf64b08b018b89e4fe16ea621cc9a0e3f3801e9e5638cfe1a6035fa1581
|
||||
F src/shell.c.in c2e20c43a44fb5588a6c27ce60589538fbf4794fd7686f5b2598eca22eaae1fa
|
||||
F src/sqlite.h.in 75d0304247a2154122d6d06f12219c1e29291d72304f0eeef4c1ec6b1409b443
|
||||
F src/shell.c.in 8a06de3e55e49df49eca5132dacda0057b397d3faeca8a3f943c9b536af35d7f
|
||||
F src/sqlite.h.in ceebdc4c3119b2634e49f04be747515a4d2d551deb15c6d046a343471c465adf
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 27951f294f29cd875c6027f2707d644ef99f469bd97514568b5a8581a114db8c
|
||||
F src/sqliteInt.h 7a29ba700a51eeb925731749a570cf3859f6a58ed94797ecf47508875b0ba279
|
||||
F src/sqliteInt.h be6a29e1fc0b9340b412a616b1b685dfa057ac3c6817b2bdcd3b071d4d4fa355
|
||||
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
|
||||
F src/status.c 9ff2210207c6c3b4d9631a8241a7d45ab1b26a0e9c84cb07a9b5ce2de9a3b278
|
||||
F src/status.c f6e2b6c37873620ce2cb597f5606a1c9adca4c0db1ff3a08a37231e920d21985
|
||||
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
|
||||
F src/tclsqlite.c 97590069efaba5a4928ecffb606e3771dd93ee8e6bf248a62a6507c37a2b2e46
|
||||
F src/tclsqlite.c 400c8d6d7b3d79d0ccbf37966ad8795d23eb994b91d9b9d16e03ffe3378cc3d3
|
||||
F src/test1.c 4d0ab2f67053a4fff87d1d3586ecc0e5322a1fc45dd4119ab11dc96de44f17a1
|
||||
F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
|
||||
F src/test3.c 61798bb0d38b915067a8c8e03f5a534b431181f802659a6616f9b4ff7d872644
|
||||
@@ -556,7 +557,7 @@ F src/test_backup.c bf5da90c9926df0a4b941f2d92825a01bbe090a0
|
||||
F src/test_bestindex.c 78809f11026f18a93fcfd798d9479cba37e1201c830260bf1edc674b2fa9b857
|
||||
F src/test_blob.c ae4a0620b478548afb67963095a7417cd06a4ec0a56adb453542203bfdcb31ce
|
||||
F src/test_btree.c 8b2dc8b8848cf3a4db93f11578f075e82252a274
|
||||
F src/test_config.c e25826d693039cdd45963de378cbf39e3af0e8aa7a8a6fc159876f4e7b5a4f8c
|
||||
F src/test_config.c 940d67253281cd7a0afba3617bdbd5ba37f5c4d6c3496ab6d6e674ecd056d882
|
||||
F src/test_delete.c e2fe07646dff6300b48d49b2fee2fe192ed389e834dd635e3b3bac0ce0bf9f8f
|
||||
F src/test_demovfs.c 86142ba864d4297d54c5b2e972e74f3141ae4b30f05b3a95824184ed2d3d7f91
|
||||
F src/test_devsym.c 6109b45c3db3ef7b002320947ed448c027356ab8b885156ff535fd8684d4a571
|
||||
@@ -570,7 +571,7 @@ F src/test_journal.c a0b9709b2f12b1ec819eea8a1176f283bca6d688a6d4a502bd6fd79786f
|
||||
F src/test_loadext.c 337056bae59f80b9eb00ba82088b39d0f4fe6dfd
|
||||
F src/test_malloc.c dec0aa821b230773aeb3dd11d652c1193f7cedb18a20b25659bc672288115242
|
||||
F src/test_md5.c 7268e1e8c399d4a5e181b64ac20e1e6f3bc4dd9fc87abac02db145a3d951fa8c
|
||||
F src/test_multiplex.c e054459f7633f3ff8ce1245da724f9a8be189e4e
|
||||
F src/test_multiplex.c e5fac104a0eebf935e6732cda6abce79ea0b4b10949518d5dac7b0293173a40f
|
||||
F src/test_multiplex.h 5436d03f2d0501d04f3ed50a75819e190495b635
|
||||
F src/test_mutex.c 7f4337ba23ee6b1d2ec81c189653608cb069926a
|
||||
F src/test_onefile.c f31e52e891c5fef6709b9fcef54ce660648a34172423a9cbdf4cbce3ba0049f4
|
||||
@@ -580,11 +581,12 @@ F src/test_quota.c 6cb9297115b551f433a9ad1741817a9831abed99
|
||||
F src/test_quota.h 2a8ad1952d1d2ca9af0ce0465e56e6c023b5e15d
|
||||
F src/test_rtree.c 671f3fae50ff116ef2e32a3bf1fe21b5615b4b7b
|
||||
F src/test_schema.c f575932cb6274d12147a77e13ea4b49d52408513
|
||||
F src/test_schemapool.c ae21a79f9bc7e9099ae78dbd46d5c28ee75330e62e3256a9fde25564ff9df492
|
||||
F src/test_server.c a2615049954cbb9cfb4a62e18e2f0616e4dc38fe
|
||||
F src/test_sqllog.c 11e6ce7575f489155c604ac4b439f2ac1d3d5aef
|
||||
F src/test_superlock.c 4839644b9201da822f181c5bc406c0b2385f672e
|
||||
F src/test_syscall.c 1073306ba2e9bfc886771871a13d3de281ed3939
|
||||
F src/test_tclsh.c eeafce33ad2136d57e5dec10f1e9a4347447eb72ffd504a1c7b9c6bfe2e71578
|
||||
F src/test_tclsh.c 6cbaaf30c4fed76c7b35baa42c521fd225c49563ff580f736d77def9e216b31b
|
||||
F src/test_tclvar.c 33ff42149494a39c5fbb0df3d25d6fafb2f668888e41c0688d07273dcb268dfc
|
||||
F src/test_thread.c 911d15fb14e19c0c542bdc8aabf981c2f10a4858
|
||||
F src/test_vdbecov.c f60c6f135ec42c0de013a1d5136777aa328a776d33277f92abac648930453d43
|
||||
@@ -595,24 +597,24 @@ F src/test_windirent.h 90dfbe95442c9762357fe128dc7ae3dc199d006de93eb33ba3972e0a9
|
||||
F src/test_window.c cdae419fdcea5bad6dcd9368c685abdad6deb59e9fc8b84b153de513d394ba3f
|
||||
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
|
||||
F src/tokenize.c 7b17f6e2f20f6cbcb0b215025a86b7457c38451fc7622f705e553d7a488c572d
|
||||
F src/tokenize.c 1f2627ae84b327c8a55425d0e53568e56a86a304ea4c91e016a58283549e8367
|
||||
F src/treeview.c 438c1000587b33faba35e87596bebcf7f40638d98f33781cdd9e04711b18b09c
|
||||
F src/trigger.c a40d50e88bd3355f1d2a73f0a3b2d6b42eae26ca4219001b82ef0d064439badc
|
||||
F src/trigger.c 8256290482385fad62daaf9b8f0b1e2c0a74931d34a92d0ccd5c675e16b37c68
|
||||
F src/update.c 9ad19af96aff95dc02a923a99f97c1bc0b909009a29a2914b796f786b9ac0c60
|
||||
F src/upsert.c 2920de71b20f04fe25eb00b655d086f0ba60ea133c59d7fa3325c49838818e78
|
||||
F src/utf.c 736ff76753236ffbc8b5b939f5e0607f28aeaa7c780b3a56b419228f0a81c87b
|
||||
F src/util.c d035b09df9cecbc0e8f07c34b815acbf0d43c8adc8d2c540e3dc92eecb27855a
|
||||
F src/vacuum.c 82dcec9e7b1afa980288718ad11bc499651c722d7b9f32933c4d694d91cb6ebf
|
||||
F src/vdbe.c e3dd230ece613409507523e68436764cc20638bb77ba2f416097de5b37235ce1
|
||||
F src/vdbe.h defd693289c7bb8d325f109be9490c77138061211a116827da7244b6015a4934
|
||||
F src/vacuum.c 5b9b4771ffb47aaef038c4ad5f1acd76dfbc9e0a4f101c1ba7f7ac9c2ba96374
|
||||
F src/vdbe.c bc12e1fe236bea3ed08a2b7a03a0482858ec66d2c3548ea28db0d6c3a56f257a
|
||||
F src/vdbe.h dfe0c985275820175d9ffb21372de10a651823957ce8aadd055c812bc76db83c
|
||||
F src/vdbeInt.h 30d3e8b991547cdf39025e416a0a737b0416d46747af70ae058f60e2e0466fe7
|
||||
F src/vdbeapi.c 1252d80c548711e47a6d84dae88ed4e95d3fbb4e7bd0eaa1347299af7efddf02
|
||||
F src/vdbeaux.c ff690e6c9314ef281de7c06f8c8c33393f0afca80aabb1fe69836dcf2d60b0bf
|
||||
F src/vdbeblob.c 253ed82894924c362a7fa3079551d3554cd1cdace39aa833da77d3bc67e7c1b1
|
||||
F src/vdbeaux.c eafd650c15e201bdd796b5f5f4e573a652b5a5ee69f6b24fcc5b89e7ebc2d977
|
||||
F src/vdbeblob.c 40028e015fe557a945c99edb6cbf844ea96d853c3e8ac4eb5c1e49bff59f154e
|
||||
F src/vdbemem.c 6200af702c87105d5b00d8ac5f5fa2c6d8f796aa974dbe2d15dcd95379ba1fa7
|
||||
F src/vdbesort.c a3be032cc3fee0e3af31773af4a7a6f931b7230a34f53282ccf1d9a2a72343be
|
||||
F src/vdbetrace.c fa3bf238002f0bbbdfb66cc8afb0cea284ff9f148d6439bc1f6f2b4c3b7143f0
|
||||
F src/vtab.c 7b704a90515a239c6cdba6a66b1bb3a385e62326cceb5ecb05ec7a091d6b8515
|
||||
F src/vtab.c 0119233719474ebefeea02f3bc9c08edcb2e409dbf7ed0820060223be8122aff
|
||||
F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
|
||||
F src/wal.c dbc77159e6734c2d64343cb8624ad245d89dd79a5010750fce8118b3fa7be2e8
|
||||
F src/wal.h 606292549f5a7be50b6227bd685fa76e3a4affad71bb8ac5ce4cb5c79f6a176a
|
||||
@@ -1261,6 +1263,13 @@ F test/releasetest.tcl fb76d8fcc95ac29d6356cd9e52b726ab9e43a24082897618dfbcb7c2b
|
||||
F test/releasetest_data.tcl 9919fc6ac5bc92f8878fecfd1840db15999f660a6c9f609240b41aa62b885c88
|
||||
F test/resetdb.test 8062cf10a09d8c048f8de7711e94571c38b38168db0e5877ba7561789e5eeb2b
|
||||
F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb
|
||||
F test/reuse1.test faa2ce5ff566d936b8a10d9e22ba2ee66a54ce89fdcf8aef561df6b15b0ff3d3
|
||||
F test/reuse2.test 5dd9c98579358f0d5a90d25e36dd6e678a03e23446b6c7f2630a8da22ae7ca94
|
||||
F test/reuse3.test 876d15c42d61b1f6ab718cd8169912514ad314d7fe1cd9788080aa62168ba0d1
|
||||
F test/reuse4.test adaad66253aea6cc748674328abe69b650c5c78b8676ed1162d3de09742519f9
|
||||
F test/reuse5.test bbe6cf7384ef90f134392edd93d626385ef0bf6f40eefc3d993535cd0861d83b
|
||||
F test/reuse6.test f4674f9967aee2a0b49c02832120b8b495291c0d87aae084c54e6c0cc18132fe
|
||||
F test/reusefault.test ef646a0fb51d50ddfb4b7cd872f88e7d36eaba64bde7797b3418c3774e1c8d14
|
||||
F test/rollback.test 06680159bc6746d0f26276e339e3ae2f951c64812468308838e0a3362d911eaa
|
||||
F test/rollback2.test bc868d57899dc6972e2b4483faae0e03365a0556941474eec487ae21d8d38bb6
|
||||
F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a
|
||||
@@ -1404,7 +1413,7 @@ F test/tabfunc01.test 5ca6d004157a3e886a55a9387b960cc0db41acd88753eb597ff409ec6c
|
||||
F test/table.test eb3463b7add9f16a5bb836badf118cf391b809d09fdccd1f79684600d07ec132
|
||||
F test/tableapi.test ecbcc29c4ab62c1912c3717c48ea5c5e59f7d64e4a91034e6148bd2b82f177f4
|
||||
F test/tableopts.test dba698ba97251017b7c80d738c198d39ab747930
|
||||
F test/tclsqlite.test 79a473f5797e317c08f2c4f8192edb3eea6a67329b1087453328b66a7cb31070
|
||||
F test/tclsqlite.test 9ad3f8d337ae8e460b25d82094ff1b4f180afc3ca73856ec5a90ac215f43906c
|
||||
F test/tempdb.test 4cdaa23ddd8acb4d79cbb1b68ccdfd09b0537aaba909ca69a876157c2a2cbd08
|
||||
F test/tempdb2.test 353864e96fd3ae2f70773d0ffbf8b1fe48589b02c2ec05013b540879410c3440
|
||||
F test/tempfault.test 0c0d349c9a99bf5f374655742577f8712c647900
|
||||
@@ -1423,7 +1432,7 @@ F test/thread2.test f35d2106452b77523b3a2b7d1dcde2e5ee8f9e46
|
||||
F test/thread_common.tcl 334639cadcb9f912bf82aa73f49efd5282e6cadd
|
||||
F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b
|
||||
F test/threadtest2.c a70a8e94bef23339d34226eb9521015ef99f4df8
|
||||
F test/threadtest3.c 38a612ea62854349ed66372f330a40d73c5cf956
|
||||
F test/threadtest3.c 4de1edf7cddac6195acfaea56cccb8572df803854531251784f6b8ca46f256e3
|
||||
F test/threadtest4.c c1e67136ceb6c7ec8184e56ac61db28f96bd2925
|
||||
F test/time-wordcount.sh 8e0b0f8109367827ad5d58f5cc849705731e4b90
|
||||
F test/tkt-02a8e81d44.test 6c80d9c7514e2a42d4918bf87bf6bc54f379110c
|
||||
@@ -1595,11 +1604,12 @@ F test/triggerE.test ede2e4bce4ba802337bd69d39447fa04a938e06d84a8bfc53c76850fc36
|
||||
F test/triggerF.test 5d76f0a8c428ff87a4d5ed52da06f6096a2c787a1e21b846111dfac4123de3ad
|
||||
F test/triggerG.test 2b816093c91ba73c733cfa8aedcc210ad819d72a98b1da30768a3c56505233e9
|
||||
F test/trustschema1.test 4e970aef0bfe0cee139703cc7209d0e0f07725d999b180ba50770f49edef1494
|
||||
F test/tt3_checkpoint.c 9e75cf7c1c364f52e1c47fd0f14c4340a9db0fe1
|
||||
F test/tt3_index.c 39eec10a35f57672225be4d182862152896dee4a
|
||||
F test/tt3_lookaside1.c 0377e202c3c2a50d688cb65ba203afeda6fafeb9
|
||||
F test/tt3_stress.c c57d804716165811d979d4a719e05baccd79277f
|
||||
F test/tt3_vacuum.c 1753f45917699c9c1f66b64c717a717c9379f776
|
||||
F test/tt3_checkpoint.c 9a7fe00e07700af027769d83ef67ab727927ae6c865ecdc71fe8011194200c53
|
||||
F test/tt3_index.c 95592839426dc85ce5a7a57b41be2cbf3c2ec3457b9cd841a06ed5877f712c7c
|
||||
F test/tt3_lookaside1.c 2ddd99bfffeef288f0786827ef68f912f6f47ce3d3184e62f05808d8e13b920e
|
||||
F test/tt3_reuseschema.c 4d52e141f89f009028d8ab0bd1f0697d0edffa94bafc1fff0f7ad4d9d9baa549
|
||||
F test/tt3_stress.c bbd430f23fa64f466ea8f212756f5f7135a2f8ff5accad85550c0ba73be76b16
|
||||
F test/tt3_vacuum.c ca42adcf8a671abbe34338b828464269e21758a6b4857b889dabfd39a3206d98
|
||||
F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff
|
||||
F test/types2.test 1aeb81976841a91eef292723649b5c4fe3bc3cac
|
||||
F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a
|
||||
@@ -1790,7 +1800,7 @@ F tool/mkmsvcmin.tcl 6ecab9fe22c2c8de4d82d4c46797bda3d2deac8e763885f5a38d0c44a89
|
||||
F tool/mkopcodec.tcl d1b6362bd3aa80d5520d4d6f3765badf01f6c43c
|
||||
F tool/mkopcodeh.tcl 352a4319c0ad869eb26442bf7c3b015aa15594c21f1cce5a6420dbe999367c21
|
||||
F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa
|
||||
F tool/mkpragmatab.tcl ca12b1c718ececdab2d3aacb437bc3c81ebf68467f19d7974e17f18844a3a48f
|
||||
F tool/mkpragmatab.tcl 85493320069444f15553b98cc274517b853519c188ead3e5eb61045babfb0c4e
|
||||
F tool/mkshellc.tcl 70a9978e363b0f3280ca9ce1c46d72563ff479c1930a12a7375e3881b7325712
|
||||
F tool/mksourceid.c 36aa8020014aed0836fd13c51d6dc9219b0df1761d6b5f58ff5b616211b079b9
|
||||
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
|
||||
@@ -1857,10 +1867,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 3bfa9cc97da10598521b342961df8f5f68c7388fa117345eeb516eaa837bb4d6
|
||||
R 37dff97ea1827bf789931484e75996b9
|
||||
T *branch * branch-3.31
|
||||
T *sym-branch-3.31 *
|
||||
T -sym-trunk *
|
||||
U drh
|
||||
Z b11b538d7cc74f2de7b61f8de517a681
|
||||
P 6dbb8cb0e3dd86c54579531fbb27db7608af556bcd766633239b814630cd0231
|
||||
Q +bd1e9e0a4c0e07901ef59fe3b7e6f7b9cc66ccfcd5192f576e1620820891de99
|
||||
R ef6994f4568a49b87305403cf06b5d34
|
||||
U dan
|
||||
Z 6cab51a154d7fe0b4a3ec2eeef25076d
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
cc65ca541265bd7061ed8f5ec9a54f3c384c41019c5ea1c68dcaabeff3495839
|
||||
0c3838d951423be4a53bbc7a53233897e1a4df006584b91311c39788edd966de
|
||||
+2
-2
@@ -80,8 +80,8 @@ static void renameReloadSchema(Parse *pParse, int iDb){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
if( v ){
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0);
|
||||
if( iDb!=1 ) sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0);
|
||||
sqlite3VdbeAddParseSchemaOp(pParse, iDb, 0);
|
||||
if( iDb!=1 ) sqlite3VdbeAddParseSchemaOp(pParse, 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -206,6 +206,7 @@ static void openStatTable(
|
||||
** side-effect of the CREATE TABLE statement is to leave the rootpage
|
||||
** of the new table in register pParse->regRoot. This is important
|
||||
** because the OpenWrite opcode below will be needing it. */
|
||||
sqlite3SchemaWritable(pParse, iDb);
|
||||
sqlite3NestedParse(pParse,
|
||||
"CREATE TABLE %Q.%s(%s)", pDb->zDbSName, zTab, aTable[i].zCols
|
||||
);
|
||||
|
||||
+5
-4
@@ -231,14 +231,14 @@ static void attachFunc(
|
||||
** way we found it.
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3BtreeEnterAll(db);
|
||||
db->init.iDb = 0;
|
||||
db->mDbFlags &= ~(DBFLAG_SchemaKnownOk);
|
||||
if( !REOPEN_AS_MEMDB(db) ){
|
||||
if( !IsSharedSchema(db) && !REOPEN_AS_MEMDB(db) ){
|
||||
sqlite3BtreeEnterAll(db);
|
||||
rc = sqlite3Init(db, &zErrDyn);
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
assert( zErrDyn==0 || rc!=SQLITE_OK );
|
||||
}
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
assert( zErrDyn==0 || rc!=SQLITE_OK );
|
||||
}
|
||||
#ifdef SQLITE_USER_AUTHENTICATION
|
||||
if( rc==SQLITE_OK && !REOPEN_AS_MEMDB(db) ){
|
||||
@@ -336,6 +336,7 @@ static void detachFunc(
|
||||
pEntry = sqliteHashNext(pEntry);
|
||||
}
|
||||
|
||||
sqlite3SchemaDisconnect(db, i, 0);
|
||||
sqlite3BtreeClose(pDb->pBt);
|
||||
pDb->pBt = 0;
|
||||
pDb->pSchema = 0;
|
||||
|
||||
+69
-12
@@ -287,6 +287,39 @@ int sqlite3UserAuthTable(const char *zTable){
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
/*
|
||||
** If this database connection was opened with the SQLITE_OPEN_SHARED_SCHEMA
|
||||
** flag specified, then ensure that the database schema for database iDb
|
||||
** is loaded. Either by obtaining a Schema object from the schema-pool, or
|
||||
** by reading the contents of the sqlite_master table. Unless it is NULL,
|
||||
** the location indicated by parameter pbUnload is set to 1 if a shared-schema
|
||||
** is loaded.
|
||||
**
|
||||
** If the database handle was not opened with SQLITE_OPEN_SHARED_SCHEMA, or
|
||||
** if the schema for database iDb is already loaded, this function is a no-op.
|
||||
**
|
||||
** SQLITE_OK is returned if successful, or an SQLite error code otherwise. If
|
||||
** an error code is returned, (*pzErr) may be set to point to a buffer
|
||||
** containing an error message. It is the responsibility of the caller to
|
||||
** eventually free this buffer using sqlite3_free().
|
||||
*/
|
||||
int sqlite3SchemaLoad(sqlite3 *db, int iDb, int *pbUnload, char **pzErr){
|
||||
int rc = SQLITE_OK;
|
||||
if( IsSharedSchema(db)
|
||||
&& DbHasProperty(db, iDb, DB_SchemaLoaded)==0
|
||||
&& (db->init.busy==0 || (iDb!=1 && db->init.iDb==1))
|
||||
){
|
||||
struct sqlite3InitInfo sv = db->init;
|
||||
memset(&db->init, 0, sizeof(struct sqlite3InitInfo));
|
||||
rc = sqlite3InitOne(db, iDb, pzErr, 0);
|
||||
db->init = sv;
|
||||
if( pbUnload && rc==SQLITE_OK && iDb!=1 ) *pbUnload = 1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Locate the in-memory structure that describes a particular database
|
||||
** table given the name of that table and (optionally) the name of the
|
||||
@@ -316,9 +349,20 @@ Table *sqlite3FindTable(sqlite3 *db, const char *zName, const char *zDatabase){
|
||||
for(i=OMIT_TEMPDB; i<db->nDb; i++){
|
||||
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
|
||||
if( zDatabase==0 || sqlite3StrICmp(zDatabase, db->aDb[j].zDbSName)==0 ){
|
||||
int bUnload = 0;
|
||||
assert( sqlite3SchemaMutexHeld(db, j, 0) );
|
||||
if( IsSharedSchema(db) ){
|
||||
Parse *pParse = db->pParse;
|
||||
if( pParse && pParse->nErr==0 ){
|
||||
pParse->rc = sqlite3SchemaLoad(db, j, &bUnload, &pParse->zErrMsg);
|
||||
if( pParse->rc ) pParse->nErr++;
|
||||
}
|
||||
}
|
||||
p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName);
|
||||
if( p ) return p;
|
||||
if( bUnload ){
|
||||
sqlite3SchemaRelease(db, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Not found. If the name we were looking for was temp.sqlite_master
|
||||
@@ -352,6 +396,7 @@ Table *sqlite3LocateTable(
|
||||
/* Read the database schema. If an error occurs, leave an error message
|
||||
** and code in pParse and return NULL. */
|
||||
if( (db->mDbFlags & DBFLAG_SchemaKnownOk)==0
|
||||
&& !IsSharedSchema(db)
|
||||
&& SQLITE_OK!=sqlite3ReadSchema(pParse)
|
||||
){
|
||||
return 0;
|
||||
@@ -368,8 +413,18 @@ Table *sqlite3LocateTable(
|
||||
if( pMod==0 && sqlite3_strnicmp(zName, "pragma_", 7)==0 ){
|
||||
pMod = sqlite3PragmaVtabRegister(db, zName);
|
||||
}
|
||||
if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){
|
||||
return pMod->pEpoTab;
|
||||
if( pMod ){
|
||||
if( IsSharedSchema(db) && pParse->nErr==0 ){
|
||||
int bDummy = 0;
|
||||
pParse->rc = sqlite3SchemaLoad(db, 0, &bDummy, &pParse->zErrMsg);
|
||||
if( pParse->rc ) pParse->nErr++;
|
||||
}
|
||||
if( sqlite3VtabEponymousTableInit(pParse, pMod) ){
|
||||
Table *pEpoTab = pMod->pEpoTab;
|
||||
assert( IsSharedSchema(db) || pEpoTab->pSchema==db->aDb[0].pSchema );
|
||||
pEpoTab->pSchema = db->aDb[0].pSchema; /* For SHARED_SCHEMA mode */
|
||||
return pEpoTab;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -379,7 +434,7 @@ Table *sqlite3LocateTable(
|
||||
p = 0;
|
||||
}
|
||||
|
||||
if( p==0 ){
|
||||
if( p==0 && (!IsSharedSchema(db) || pParse->nErr==0) ){
|
||||
const char *zMsg = flags & LOCATE_VIEW ? "no such view" : "no such table";
|
||||
if( zDbase ){
|
||||
sqlite3ErrorMsg(pParse, "%s: %s.%s", zMsg, zDbase, zName);
|
||||
@@ -538,11 +593,10 @@ void sqlite3ResetOneSchema(sqlite3 *db, int iDb){
|
||||
DbSetProperty(db, 1, DB_ResetWanted);
|
||||
db->mDbFlags &= ~DBFLAG_SchemaKnownOk;
|
||||
}
|
||||
|
||||
if( db->nSchemaLock==0 ){
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( DbHasProperty(db, i, DB_ResetWanted) ){
|
||||
sqlite3SchemaClear(db->aDb[i].pSchema);
|
||||
sqlite3SchemaClearOrDisconnect(db, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -555,16 +609,17 @@ void sqlite3ResetOneSchema(sqlite3 *db, int iDb){
|
||||
void sqlite3ResetAllSchemasOfConnection(sqlite3 *db){
|
||||
int i;
|
||||
sqlite3BtreeEnterAll(db);
|
||||
for(i=0; i<db->nDb; i++){
|
||||
for(i=0; i<db->nDb; i=(i?i+1:2)){
|
||||
Db *pDb = &db->aDb[i];
|
||||
if( pDb->pSchema ){
|
||||
if( db->nSchemaLock==0 ){
|
||||
sqlite3SchemaClear(pDb->pSchema);
|
||||
sqlite3SchemaClearOrDisconnect(db, i);
|
||||
}else{
|
||||
DbSetProperty(db, i, DB_ResetWanted);
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3SchemaClear(db->aDb[1].pSchema);
|
||||
db->mDbFlags &= ~(DBFLAG_SchemaChange|DBFLAG_SchemaKnownOk);
|
||||
sqlite3VtabUnlockList(db);
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
@@ -1058,7 +1113,7 @@ void sqlite3StartTable(
|
||||
*/
|
||||
if( !IN_SPECIAL_PARSE ){
|
||||
char *zDb = db->aDb[iDb].zDbSName;
|
||||
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
|
||||
if( !IsSharedSchema(db) && SQLITE_OK!=sqlite3ReadSchema(pParse) ){
|
||||
goto begin_table_error;
|
||||
}
|
||||
pTable = sqlite3FindTable(db, zName, zDb);
|
||||
@@ -2428,7 +2483,7 @@ void sqlite3EndTable(
|
||||
#endif
|
||||
|
||||
/* Reparse everything to update our internal data structures */
|
||||
sqlite3VdbeAddParseSchemaOp(v, iDb,
|
||||
sqlite3VdbeAddParseSchemaOp(pParse, iDb,
|
||||
sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName));
|
||||
}
|
||||
|
||||
@@ -2962,7 +3017,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
|
||||
}
|
||||
assert( pParse->nErr==0 );
|
||||
assert( pName->nSrc==1 );
|
||||
if( sqlite3ReadSchema(pParse) ) goto exit_drop_table;
|
||||
if( !IsSharedSchema(db) && sqlite3ReadSchema(pParse) ) goto exit_drop_table;
|
||||
if( noErr ) db->suppressErr++;
|
||||
assert( isView==0 || isView==LOCATE_VIEW );
|
||||
pTab = sqlite3LocateTableItem(pParse, isView, &pName->a[0]);
|
||||
@@ -2974,6 +3029,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
|
||||
}
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
assert( iDb>=0 && iDb<db->nDb );
|
||||
sqlite3SchemaWritable(pParse, iDb);
|
||||
|
||||
/* If pTab is a virtual table, call ViewGetColumnNames() to ensure
|
||||
** it is initialized.
|
||||
@@ -3422,7 +3478,7 @@ void sqlite3CreateIndex(
|
||||
if( IN_DECLARE_VTAB && idxType!=SQLITE_IDXTYPE_PRIMARYKEY ){
|
||||
goto exit_create_index;
|
||||
}
|
||||
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
|
||||
if( !IsSharedSchema(db) && SQLITE_OK!=sqlite3ReadSchema(pParse) ){
|
||||
goto exit_create_index;
|
||||
}
|
||||
if( sqlite3HasExplicitNulls(pParse, pList) ){
|
||||
@@ -3921,7 +3977,7 @@ void sqlite3CreateIndex(
|
||||
if( pTblName ){
|
||||
sqlite3RefillIndex(pParse, pIndex, iMem);
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
sqlite3VdbeAddParseSchemaOp(v, iDb,
|
||||
sqlite3VdbeAddParseSchemaOp(pParse, iDb,
|
||||
sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName));
|
||||
sqlite3VdbeAddOp2(v, OP_Expire, 0, 1);
|
||||
}
|
||||
@@ -4044,6 +4100,7 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
|
||||
goto exit_drop_index;
|
||||
}
|
||||
iDb = sqlite3SchemaToIndex(db, pIndex->pSchema);
|
||||
sqlite3SchemaWritable(pParse, iDb);
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
{
|
||||
int code = SQLITE_DROP_INDEX;
|
||||
|
||||
+365
-6
@@ -16,6 +16,63 @@
|
||||
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** Connections opened with the SQLITE_OPEN_SHARED_SCHEMA flag specified
|
||||
** may use SchemaPool objects for any database that is not the temp db
|
||||
** (iDb==1). For such databases (type "struct Db") there are three states
|
||||
** the Schema/SchemaPool object may be in.
|
||||
**
|
||||
** 1) pSPool==0, pSchema points to an empty object allocated by
|
||||
** sqlite3_malloc(). DB_SchemaLoaded flag is clear.
|
||||
**
|
||||
** 2) pSPool!=0, pSchema points to a populated object owned by the
|
||||
** SchemaPool. DB_SchemaLoaded flag is set.
|
||||
**
|
||||
** 3) pSPool!=0, pSchema points to the SchemaPool's static object
|
||||
** (SchemaPool.sSchema).
|
||||
*/
|
||||
struct SchemaPool {
|
||||
int nRef; /* Number of pointers to this object */
|
||||
int nDelete; /* Schema objects deleted by ReleaseAll() */
|
||||
u64 cksum; /* Checksum for this Schema contents */
|
||||
Schema *pSchema; /* Linked list of Schema objects */
|
||||
Schema sSchema; /* The single dummy schema object */
|
||||
SchemaPool *pNext; /* Next element in schemaPoolList */
|
||||
};
|
||||
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
#ifdef SQLITE_DEBUG
|
||||
static void assert_schema_state_ok(sqlite3 *db){
|
||||
if( IsSharedSchema(db) && db->magic!=SQLITE_MAGIC_ZOMBIE ){
|
||||
int i;
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( i!=1 ){
|
||||
Db *pDb = &db->aDb[i];
|
||||
Btree *pBt = pDb->pBt;
|
||||
if( pBt==0 ) continue;
|
||||
assert( sqlite3BtreeSchema(pBt, 0, 0)==0 );
|
||||
assert( pDb->pSchema );
|
||||
if( pDb->pSPool ){
|
||||
if( DbHasProperty(db, i, DB_SchemaLoaded)==0 ){
|
||||
assert( pDb->pSchema->tblHash.count==0 );
|
||||
assert( pDb->pSchema==&pDb->pSPool->sSchema );
|
||||
}else{
|
||||
assert( pDb->pSchema!=&pDb->pSPool->sSchema );
|
||||
}
|
||||
}else{
|
||||
assert( DbHasProperty(db, i, DB_SchemaLoaded)==0 );
|
||||
assert( pDb->pSchema->tblHash.count==0 );
|
||||
assert( pDb->pSchema!=&pDb->pSPool->sSchema );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define assert_schema_state_ok(x)
|
||||
#endif
|
||||
#endif /* ifdef SQLITE_ENABLE_SHARED_SCHEMA */
|
||||
|
||||
/*
|
||||
** Invoke the 'collation needed' callback to request a collation sequence
|
||||
** in the encoding enc of name zName, length nName.
|
||||
@@ -500,15 +557,317 @@ void sqlite3SchemaClear(void *p){
|
||||
}
|
||||
|
||||
/*
|
||||
** Find and return the schema associated with a BTree. Create
|
||||
** a new one if necessary.
|
||||
** If this database was opened with the SQLITE_OPEN_SHARED_SCHEMA flag
|
||||
** and iDb!=1, then disconnect from the schema-pool associated with
|
||||
** database iDb. Otherwise, clear the Schema object belonging to
|
||||
** database iDb.
|
||||
**
|
||||
** If an OOM error occurs while disconnecting from a schema-pool,
|
||||
** the db->mallocFailed flag is set.
|
||||
*/
|
||||
void sqlite3SchemaClearOrDisconnect(sqlite3 *db, int iDb){
|
||||
Db *pDb = &db->aDb[iDb];
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
if( IsSharedSchema(db) && iDb!=1 && pDb->pSPool ){
|
||||
sqlite3SchemaDisconnect(db, iDb, 1);
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
sqlite3SchemaClear(pDb->pSchema);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
/*
|
||||
** Global linked list of SchemaPool objects. Read and write access must
|
||||
** be protected by the SQLITE_MUTEX_STATIC_MASTER mutex.
|
||||
*/
|
||||
static SchemaPool *SQLITE_WSD schemaPoolList = 0;
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
/*
|
||||
** Return a pointer to the head of the linked list of SchemaPool objects.
|
||||
** This is used by the virtual table in file test_schemapool.c.
|
||||
*/
|
||||
SchemaPool *sqlite3SchemaPoolList(void){ return schemaPoolList; }
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Database handle db was opened with the SHARED_SCHEMA flag, and database
|
||||
** iDb is currently connected to a schema-pool. When this function is called,
|
||||
** (*pnByte) is set to nInit plus the amount of memory used to store a
|
||||
** single instance of the Schema objects managed by the schema-pool.
|
||||
** This function adjusts (*pnByte) sot hat it is set to nInit plus
|
||||
** (nSchema/nRef) of the amount of memory used by a single Schema object,
|
||||
** where nSchema is the number of Schema objects allocated by this pool,
|
||||
** and nRef is the number of connections to the schema-pool.
|
||||
*/
|
||||
void sqlite3SchemaAdjustUsed(sqlite3 *db, int iDb, int nInit, int *pnByte){
|
||||
SchemaPool *pSPool = db->aDb[iDb].pSPool;
|
||||
int nSchema = 0;
|
||||
Schema *p;
|
||||
sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
|
||||
for(p=pSPool->pSchema; p; p=p->pNext){
|
||||
nSchema++;
|
||||
}
|
||||
*pnByte = nInit + ((*pnByte - nInit) * nSchema) / pSPool->nRef;
|
||||
sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
|
||||
}
|
||||
|
||||
/*
|
||||
** Check that the schema of db iDb is writable (either because it is the
|
||||
** temp db schema or because the db handle was opened without
|
||||
** SQLITE_OPEN_SHARED_SCHEMA). If so, do nothing. Otherwise, leave an
|
||||
** error in the Parse object.
|
||||
*/
|
||||
void sqlite3SchemaWritable(Parse *pParse, int iDb){
|
||||
if( iDb!=1 && IsSharedSchema(pParse->db) && IN_DECLARE_VTAB==0 ){
|
||||
sqlite3ErrorMsg(pParse, "attempt to modify read-only schema");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** The schema object passed as the only argument was allocated using
|
||||
** sqlite3_malloc() and then populated using the usual mechanism. This
|
||||
** function frees both the Schema object and its contents.
|
||||
*/
|
||||
static void schemaDelete(Schema *pSchema){
|
||||
sqlite3SchemaClear((void*)pSchema);
|
||||
sqlite3_free(pSchema);
|
||||
}
|
||||
|
||||
/*
|
||||
** When this function is called, the database connection Db must be
|
||||
** using a schema-pool (Db.pSPool!=0) and must currently have Db.pSchema
|
||||
** set to point to a populated schema object checked out from the
|
||||
** schema-pool. It is also assumed that the STATIC_MASTER mutex is held.
|
||||
** This function returns the Schema object to the schema-pool and sets
|
||||
** Db.pSchema to point to the schema-pool's static, empty, Schema object.
|
||||
*/
|
||||
static void schemaRelease(sqlite3 *db, Db *pDb){
|
||||
Schema *pRelease = pDb->pSchema;
|
||||
SchemaPool *pSPool = pDb->pSPool;
|
||||
|
||||
assert( pDb->pSchema->iGeneration==pSPool->sSchema.iGeneration );
|
||||
pDb->pSchema = &pSPool->sSchema;
|
||||
|
||||
assert( pDb->pSPool && pRelease );
|
||||
assert( pRelease->schemaFlags & DB_SchemaLoaded );
|
||||
assert( (pDb->pSchema->schemaFlags & DB_SchemaLoaded)==0 );
|
||||
assert( sqlite3_mutex_held(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)) );
|
||||
|
||||
/* If the DBFLAG_FreeSchema flag is set and the database connection holds
|
||||
** at least one other copy of the schema being released, delete it instead
|
||||
** of returning it to the schema-pool. */
|
||||
if( db->mDbFlags & DBFLAG_FreeSchema ){
|
||||
int i;
|
||||
for(i=0; i<db->nDb; i++){
|
||||
Db *p = &db->aDb[i];
|
||||
if( p!=pDb && p->pSchema!=&pSPool->sSchema && pDb->pSPool==p->pSPool ){
|
||||
pSPool->nDelete++;
|
||||
schemaDelete(pRelease);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pRelease->pNext = pDb->pSPool->pSchema;
|
||||
pDb->pSPool->pSchema = pRelease;
|
||||
}
|
||||
|
||||
/*
|
||||
** The schema for database iDb of database handle db, which was opened
|
||||
** with SQLITE_OPEN_SHARED_SCHEMA, has just been parsed. This function either
|
||||
** finds a matching SchemaPool object on the global list (schemaPoolList) or
|
||||
** else allocates a new one and sets the Db.pSPool variable accordingly.
|
||||
**
|
||||
** SQLITE_OK is returned if no error occurs, or an SQLite error code
|
||||
** (SQLITE_NOMEM) otherwise.
|
||||
*/
|
||||
int sqlite3SchemaConnect(sqlite3 *db, int iDb, u64 cksum){
|
||||
Schema *pSchema = db->aDb[iDb].pSchema;
|
||||
SchemaPool *p;
|
||||
|
||||
assert( pSchema && iDb!=1 && db->aDb[iDb].pSPool==0 );
|
||||
|
||||
sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
|
||||
|
||||
/* Search for a matching SchemaPool object */
|
||||
for(p=schemaPoolList; p; p=p->pNext){
|
||||
if( p->cksum==cksum && p->sSchema.schema_cookie==pSchema->schema_cookie ){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( !p ){
|
||||
/* No SchemaPool object found. Allocate a new one. */
|
||||
p = (SchemaPool*)sqlite3_malloc(sizeof(SchemaPool));
|
||||
if( p ){
|
||||
memset(p, 0, sizeof(SchemaPool));
|
||||
p->cksum = cksum;
|
||||
p->pNext = schemaPoolList;
|
||||
schemaPoolList = p;
|
||||
|
||||
p->sSchema.schema_cookie = pSchema->schema_cookie;
|
||||
p->sSchema.iGeneration = pSchema->iGeneration;
|
||||
p->sSchema.file_format = pSchema->file_format;
|
||||
p->sSchema.enc = pSchema->enc;
|
||||
p->sSchema.cache_size = pSchema->cache_size;
|
||||
}
|
||||
}
|
||||
|
||||
if( p ) p->nRef++;
|
||||
|
||||
/* If the SchemaPool contains one or more free schemas at the moment,
|
||||
** delete one of them. */
|
||||
if( p && p->pSchema ){
|
||||
Schema *pDel = p->pSchema;
|
||||
p->pSchema = pDel->pNext;
|
||||
schemaDelete(pDel);
|
||||
}
|
||||
|
||||
sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
|
||||
|
||||
db->aDb[iDb].pSPool = p;
|
||||
return (p ? SQLITE_OK : SQLITE_NOMEM);
|
||||
}
|
||||
|
||||
/*
|
||||
** If parameter iDb is 1 (the temp db), or if connection handle db was not
|
||||
** opened with the SQLITE_OPEN_SHARED_SCHEMA flag, this function is a no-op.
|
||||
** Otherwise, it disconnects from the schema-pool associated with database
|
||||
** iDb, assuming it is connected.
|
||||
**
|
||||
** If parameter bNew is true, then Db.pSchema is set to point to a new, empty,
|
||||
** Schema object obtained from sqlite3_malloc(). Or, if bNew is false, then
|
||||
** Db.pSchema is set to NULL before returning.
|
||||
**
|
||||
** If the bNew parameter is true, then this function may allocate memory.
|
||||
** If the allocation attempt fails, then SQLITE_NOMEM is returned and the
|
||||
** schema-pool is not disconnected from. Or, if no OOM error occurs,
|
||||
** SQLITE_OK is returned.
|
||||
*/
|
||||
int sqlite3SchemaDisconnect(sqlite3 *db, int iDb, int bNew){
|
||||
int rc = SQLITE_OK;
|
||||
if( IsSharedSchema(db) ){
|
||||
Db *pDb = &db->aDb[iDb];
|
||||
SchemaPool *pSPool = pDb->pSPool;
|
||||
assert_schema_state_ok(db);
|
||||
assert( pDb->pSchema );
|
||||
|
||||
if( pSPool==0 ){
|
||||
assert( pDb->pVTable==0 );
|
||||
assert( bNew==0 );
|
||||
schemaDelete(pDb->pSchema);
|
||||
pDb->pSchema = 0;
|
||||
}else{
|
||||
VTable *p;
|
||||
VTable *pNext;
|
||||
for(p=pDb->pVTable; p; p=pNext){
|
||||
pNext = p->pNext;
|
||||
sqlite3VtabUnlock(p);
|
||||
}
|
||||
pDb->pVTable = 0;
|
||||
sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
|
||||
if( DbHasProperty(db, iDb, DB_SchemaLoaded) ){
|
||||
schemaRelease(db, pDb);
|
||||
}
|
||||
if( bNew ){
|
||||
Schema *pNew = sqlite3SchemaGet(db, 0);
|
||||
if( pNew==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
pDb->pSchema = pNew;
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
assert( pSPool->nRef>=1 );
|
||||
pDb->pSPool = 0;
|
||||
pSPool->nRef--;
|
||||
if( pSPool->nRef<=0 ){
|
||||
SchemaPool **pp;
|
||||
while( pSPool->pSchema ){
|
||||
Schema *pNext = pSPool->pSchema->pNext;
|
||||
schemaDelete(pSPool->pSchema);
|
||||
pSPool->pSchema = pNext;
|
||||
}
|
||||
for(pp=&schemaPoolList; (*pp)!=pSPool; pp=&((*pp)->pNext));
|
||||
*pp = pSPool->pNext;
|
||||
sqlite3_free(pSPool);
|
||||
}
|
||||
}
|
||||
sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Extract and return a pointer to a schema object from the SchemaPool passed
|
||||
** as the only argument, if one is available. If one is not available, return
|
||||
** NULL.
|
||||
*/
|
||||
Schema *sqlite3SchemaExtract(SchemaPool *pSPool){
|
||||
Schema *pRet = 0;
|
||||
sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
|
||||
if( pSPool->pSchema ){
|
||||
pRet = pSPool->pSchema;
|
||||
pSPool->pSchema = pRet->pNext;
|
||||
pRet->pNext = 0;
|
||||
}
|
||||
sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
|
||||
return pRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return all sharable schemas held by database handle db back to their
|
||||
** respective schema-pools. Db.pSchema variables are left pointing to
|
||||
** the static, empty, Schema object owned by each schema-pool.
|
||||
*/
|
||||
void sqlite3SchemaReleaseAll(sqlite3 *db){
|
||||
int i;
|
||||
assert_schema_state_ok(db);
|
||||
sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( i!=1 ){
|
||||
Db *pDb = &db->aDb[i];
|
||||
if( pDb->pSPool && DbHasProperty(db,i,DB_SchemaLoaded) ){
|
||||
schemaRelease(db, pDb);
|
||||
}
|
||||
}
|
||||
}
|
||||
db->mDbFlags &= ~DBFLAG_FreeSchema;
|
||||
sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
|
||||
}
|
||||
|
||||
/*
|
||||
** Release any sharable schema held by connection iDb of database handle
|
||||
** db. Db.pSchema is left pointing to the static, empty, Schema object
|
||||
** owned by the schema-pool.
|
||||
*/
|
||||
void sqlite3SchemaRelease(sqlite3 *db, int iDb){
|
||||
Db *pDb = &db->aDb[iDb];
|
||||
assert( iDb!=1 );
|
||||
assert_schema_state_ok(db);
|
||||
sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
|
||||
schemaRelease(db, pDb);
|
||||
sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
|
||||
}
|
||||
|
||||
#endif /* ifdef SQLITE_ENABLE_SHARED_SCHEMA */
|
||||
|
||||
/*
|
||||
** In most cases, this function finds and returns the schema associated
|
||||
** with BTree handle pBt, creating a new one if necessary. However, if
|
||||
** the database handle was opened with the SQLITE_OPEN_SHARED_SCHEMA flag
|
||||
** specified, a new, empty, Schema object in memory obtained by
|
||||
** sqlite3_malloc() is always returned.
|
||||
*/
|
||||
Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){
|
||||
Schema * p;
|
||||
if( pBt ){
|
||||
p = (Schema *)sqlite3BtreeSchema(pBt, sizeof(Schema), sqlite3SchemaClear);
|
||||
Schema *p;
|
||||
if( pBt && IsSharedSchema(db)==0 ){
|
||||
p = (Schema*)sqlite3BtreeSchema(pBt, sizeof(Schema), sqlite3SchemaClear);
|
||||
}else{
|
||||
p = (Schema *)sqlite3DbMallocZero(0, sizeof(Schema));
|
||||
p = (Schema*)sqlite3DbMallocZero(0, sizeof(Schema));
|
||||
}
|
||||
if( !p ){
|
||||
sqlite3OomFault(db);
|
||||
|
||||
@@ -295,6 +295,9 @@ static const char * const sqlite3azCompileOpt[] = {
|
||||
#if SQLITE_ENABLE_SESSION
|
||||
"ENABLE_SESSION",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_SHARED_SCHEMA
|
||||
"ENABLE_SHARED_SCHEMA",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_SNAPSHOT
|
||||
"ENABLE_SNAPSHOT",
|
||||
#endif
|
||||
|
||||
+2
-2
@@ -1412,9 +1412,9 @@ void sqlite3FkDelete(sqlite3 *db, Table *pTab){
|
||||
FKey *pFKey; /* Iterator variable */
|
||||
FKey *pNext; /* Copy of pFKey->pNextFrom */
|
||||
|
||||
assert( db==0 || IsVirtual(pTab)
|
||||
|| sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) );
|
||||
for(pFKey=pTab->pFKey; pFKey; pFKey=pNext){
|
||||
assert( db==0 || IsVirtual(pTab)
|
||||
|| sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) );
|
||||
|
||||
/* Remove the FK from the fkeyHash hash table. */
|
||||
if( !db || db->pnBytesFreed==0 ){
|
||||
|
||||
+34
-6
@@ -1092,6 +1092,17 @@ static void disconnectAllVtab(sqlite3 *db){
|
||||
if( IsVirtual(pTab) ) sqlite3VtabDisconnect(db, pTab);
|
||||
}
|
||||
}
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
if( IsSharedSchema(db) && i!=1 ){
|
||||
VTable *pVTable;
|
||||
VTable *pNext;
|
||||
for(pVTable=db->aDb[i].pVTable; pVTable; pVTable=pNext){
|
||||
pNext = pVTable->pNext;
|
||||
sqlite3VtabUnlock(pVTable);
|
||||
}
|
||||
db->aDb[i].pVTable = 0;
|
||||
}
|
||||
#endif /* ifdef SQLITE_ENABLE_SHARED_SCHEMA */
|
||||
}
|
||||
for(p=sqliteHashFirst(&db->aModule); p; p=sqliteHashNext(p)){
|
||||
Module *pMod = (Module *)sqliteHashData(p);
|
||||
@@ -1230,6 +1241,7 @@ void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){
|
||||
sqlite3BtreeClose(pDb->pBt);
|
||||
pDb->pBt = 0;
|
||||
if( j!=1 ){
|
||||
sqlite3SchemaDisconnect(db, j, 0);
|
||||
pDb->pSchema = 0;
|
||||
}
|
||||
}
|
||||
@@ -3697,7 +3709,7 @@ int sqlite3_table_column_metadata(
|
||||
int *pPrimaryKey, /* OUTPUT: True if column part of PK */
|
||||
int *pAutoinc /* OUTPUT: True if column is auto-increment */
|
||||
){
|
||||
int rc;
|
||||
int rc = SQLITE_OK;
|
||||
char *zErrMsg = 0;
|
||||
Table *pTab = 0;
|
||||
Column *pCol = 0;
|
||||
@@ -3707,7 +3719,7 @@ int sqlite3_table_column_metadata(
|
||||
int notnull = 0;
|
||||
int primarykey = 0;
|
||||
int autoinc = 0;
|
||||
|
||||
int bUnlock;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) || zTableName==0 ){
|
||||
@@ -3717,14 +3729,29 @@ int sqlite3_table_column_metadata(
|
||||
|
||||
/* Ensure the database schema has been loaded */
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
bUnlock = sqlite3LockReusableSchema(db);
|
||||
sqlite3BtreeEnterAll(db);
|
||||
rc = sqlite3Init(db, &zErrMsg);
|
||||
if( SQLITE_OK!=rc ){
|
||||
goto error_out;
|
||||
if( IsSharedSchema(db)==0 ){
|
||||
rc = sqlite3Init(db, &zErrMsg);
|
||||
}
|
||||
|
||||
/* Locate the table in question */
|
||||
pTab = sqlite3FindTable(db, zTableName, zDbName);
|
||||
if( rc==SQLITE_OK ){
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
Parse sParse; /* Fake Parse object for FindTable */
|
||||
Parse *pSaved = db->pParse;
|
||||
memset(&sParse, 0, sizeof(sParse));
|
||||
db->pParse = &sParse;
|
||||
#endif
|
||||
pTab = sqlite3FindTable(db, zTableName, zDbName);
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
sqlite3_free(sParse.zErrMsg);
|
||||
rc = sParse.rc;
|
||||
db->pParse = pSaved;
|
||||
#endif
|
||||
}
|
||||
if( SQLITE_OK!=rc ) goto error_out;
|
||||
|
||||
if( !pTab || pTab->pSelect ){
|
||||
pTab = 0;
|
||||
goto error_out;
|
||||
@@ -3797,6 +3824,7 @@ error_out:
|
||||
sqlite3ErrorWithMsg(db, rc, (zErrMsg?"%s":0), zErrMsg);
|
||||
sqlite3DbFree(db, zErrMsg);
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
sqlite3UnlockReusableSchema(db, bUnlock);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
+10
-3
@@ -469,7 +469,13 @@ void sqlite3Pragma(
|
||||
|
||||
/* Make sure the database schema is loaded if the pragma requires that */
|
||||
if( (pPragma->mPragFlg & PragFlg_NeedSchema)!=0 ){
|
||||
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
|
||||
if( IsSharedSchema(db) && (zDb || (pPragma->mPragFlg & PragFlg_OneSchema)) ){
|
||||
assert( iDb>=0 && iDb<db->nDb );
|
||||
pParse->rc = sqlite3SchemaLoad(db, iDb, 0, &pParse->zErrMsg);
|
||||
if( pParse->rc ) goto pragma_out;
|
||||
}else{
|
||||
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Register the result column names for pragmas that return results */
|
||||
@@ -1885,9 +1891,10 @@ void sqlite3Pragma(
|
||||
** applications for any purpose.
|
||||
*/
|
||||
case PragTyp_HEADER_VALUE: {
|
||||
int iCookie = pPragma->iArg; /* Which cookie to read or write */
|
||||
int iCookie; /* Which cookie to read or write */
|
||||
iCookie = pPragma->iArg & PRAGMA_HEADER_VALUE_MASK;
|
||||
sqlite3VdbeUsesBtree(v, iDb);
|
||||
if( zRight && (pPragma->mPragFlg & PragFlg_ReadOnly)==0 ){
|
||||
if( zRight && (pPragma->iArg & PRAGMA_HEADER_VALUE_READONLY)==0 ){
|
||||
/* Write the specified cookie value */
|
||||
static const VdbeOpList setCookie[] = {
|
||||
{ OP_Transaction, 0, 1, 0}, /* 0 */
|
||||
|
||||
+25
-18
@@ -55,12 +55,19 @@
|
||||
#define PragFlg_NeedSchema 0x01 /* Force schema load before running */
|
||||
#define PragFlg_NoColumns 0x02 /* OP_ResultRow called with zero columns */
|
||||
#define PragFlg_NoColumns1 0x04 /* zero columns if RHS argument is present */
|
||||
#define PragFlg_ReadOnly 0x08 /* Read-only HEADER_VALUE */
|
||||
#define PragFlg_OneSchema 0x08 /* Only a single schema required */
|
||||
#define PragFlg_Result0 0x10 /* Acts as query when no argument */
|
||||
#define PragFlg_Result1 0x20 /* Acts as query when has one argument */
|
||||
#define PragFlg_SchemaOpt 0x40 /* Schema restricts name search if present */
|
||||
#define PragFlg_SchemaReq 0x80 /* Schema required - "main" is default */
|
||||
|
||||
/* For PragTyp_HEADER_VALUE pragmas the Pragma.iArg value is set
|
||||
** to the index of the header field to access (always 10 or less).
|
||||
** Ored with HEADER_VALUE_READONLY if the field is read only. */
|
||||
#define PRAGMA_HEADER_VALUE_READONLY 0x0100
|
||||
#define PRAGMA_HEADER_VALUE_MASK 0x00FF
|
||||
|
||||
|
||||
/* Names of columns for pragmas that return multi-column result
|
||||
** or that return single-column results where the name of the
|
||||
** result column is different from the name of the pragma
|
||||
@@ -150,7 +157,7 @@ static const PragmaName aPragmaName[] = {
|
||||
#if !defined(SQLITE_OMIT_AUTOVACUUM)
|
||||
{/* zName: */ "auto_vacuum",
|
||||
/* ePragTyp: */ PragTyp_AUTO_VACUUM,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1|PragFlg_OneSchema,
|
||||
/* ColNames: */ 0, 0,
|
||||
/* iArg: */ 0 },
|
||||
#endif
|
||||
@@ -171,7 +178,7 @@ static const PragmaName aPragmaName[] = {
|
||||
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
|
||||
{/* zName: */ "cache_size",
|
||||
/* ePragTyp: */ PragTyp_CACHE_SIZE,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1|PragFlg_OneSchema,
|
||||
/* ColNames: */ 0, 0,
|
||||
/* iArg: */ 0 },
|
||||
#endif
|
||||
@@ -232,21 +239,21 @@ static const PragmaName aPragmaName[] = {
|
||||
#if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
|
||||
{/* zName: */ "data_version",
|
||||
/* ePragTyp: */ PragTyp_HEADER_VALUE,
|
||||
/* ePragFlg: */ PragFlg_ReadOnly|PragFlg_Result0,
|
||||
/* ePragFlg: */ PragFlg_Result0,
|
||||
/* ColNames: */ 0, 0,
|
||||
/* iArg: */ BTREE_DATA_VERSION },
|
||||
/* iArg: */ BTREE_DATA_VERSION|PRAGMA_HEADER_VALUE_READONLY },
|
||||
#endif
|
||||
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
|
||||
{/* zName: */ "database_list",
|
||||
/* ePragTyp: */ PragTyp_DATABASE_LIST,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_OneSchema,
|
||||
/* ColNames: */ 41, 3,
|
||||
/* iArg: */ 0 },
|
||||
#endif
|
||||
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
|
||||
{/* zName: */ "default_cache_size",
|
||||
/* ePragTyp: */ PragTyp_DEFAULT_CACHE_SIZE,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1|PragFlg_OneSchema,
|
||||
/* ColNames: */ 49, 1,
|
||||
/* iArg: */ 0 },
|
||||
#endif
|
||||
@@ -276,14 +283,14 @@ static const PragmaName aPragmaName[] = {
|
||||
#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
|
||||
{/* zName: */ "foreign_key_check",
|
||||
/* ePragTyp: */ PragTyp_FOREIGN_KEY_CHECK,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_OneSchema,
|
||||
/* ColNames: */ 37, 4,
|
||||
/* iArg: */ 0 },
|
||||
#endif
|
||||
#if !defined(SQLITE_OMIT_FOREIGN_KEY)
|
||||
{/* zName: */ "foreign_key_list",
|
||||
/* ePragTyp: */ PragTyp_FOREIGN_KEY_LIST,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt|PragFlg_OneSchema,
|
||||
/* ColNames: */ 0, 8,
|
||||
/* iArg: */ 0 },
|
||||
#endif
|
||||
@@ -299,9 +306,9 @@ static const PragmaName aPragmaName[] = {
|
||||
#if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
|
||||
{/* zName: */ "freelist_count",
|
||||
/* ePragTyp: */ PragTyp_HEADER_VALUE,
|
||||
/* ePragFlg: */ PragFlg_ReadOnly|PragFlg_Result0,
|
||||
/* ePragFlg: */ PragFlg_Result0,
|
||||
/* ColNames: */ 0, 0,
|
||||
/* iArg: */ BTREE_FREE_PAGE_COUNT },
|
||||
/* iArg: */ BTREE_FREE_PAGE_COUNT|PRAGMA_HEADER_VALUE_READONLY },
|
||||
#endif
|
||||
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
|
||||
{/* zName: */ "full_column_names",
|
||||
@@ -353,7 +360,7 @@ static const PragmaName aPragmaName[] = {
|
||||
#if !defined(SQLITE_OMIT_AUTOVACUUM)
|
||||
{/* zName: */ "incremental_vacuum",
|
||||
/* ePragTyp: */ PragTyp_INCREMENTAL_VACUUM,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_NoColumns,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_NoColumns|PragFlg_OneSchema,
|
||||
/* ColNames: */ 0, 0,
|
||||
/* iArg: */ 0 },
|
||||
#endif
|
||||
@@ -384,7 +391,7 @@ static const PragmaName aPragmaName[] = {
|
||||
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
|
||||
{/* zName: */ "journal_mode",
|
||||
/* ePragTyp: */ PragTyp_JOURNAL_MODE,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_OneSchema,
|
||||
/* ColNames: */ 0, 0,
|
||||
/* iArg: */ 0 },
|
||||
{/* zName: */ "journal_size_limit",
|
||||
@@ -429,7 +436,7 @@ static const PragmaName aPragmaName[] = {
|
||||
/* iArg: */ 0 },
|
||||
{/* zName: */ "max_page_count",
|
||||
/* ePragTyp: */ PragTyp_PAGE_COUNT,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_OneSchema,
|
||||
/* ColNames: */ 0, 0,
|
||||
/* iArg: */ 0 },
|
||||
{/* zName: */ "mmap_size",
|
||||
@@ -457,7 +464,7 @@ static const PragmaName aPragmaName[] = {
|
||||
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
|
||||
{/* zName: */ "page_count",
|
||||
/* ePragTyp: */ PragTyp_PAGE_COUNT,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_OneSchema,
|
||||
/* ColNames: */ 0, 0,
|
||||
/* iArg: */ 0 },
|
||||
{/* zName: */ "page_size",
|
||||
@@ -565,14 +572,14 @@ static const PragmaName aPragmaName[] = {
|
||||
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && defined(SQLITE_DEBUG)
|
||||
{/* zName: */ "stats",
|
||||
/* ePragTyp: */ PragTyp_STATS,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_OneSchema,
|
||||
/* ColNames: */ 27, 5,
|
||||
/* iArg: */ 0 },
|
||||
#endif
|
||||
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
|
||||
{/* zName: */ "synchronous",
|
||||
/* ePragTyp: */ PragTyp_SYNCHRONOUS,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1|PragFlg_OneSchema,
|
||||
/* ColNames: */ 0, 0,
|
||||
/* iArg: */ 0 },
|
||||
#endif
|
||||
@@ -668,7 +675,7 @@ static const PragmaName aPragmaName[] = {
|
||||
/* iArg: */ 0 },
|
||||
{/* zName: */ "wal_checkpoint",
|
||||
/* ePragTyp: */ PragTyp_WAL_CHECKPOINT,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_OneSchema,
|
||||
/* ColNames: */ 44, 3,
|
||||
/* iArg: */ 0 },
|
||||
#endif
|
||||
|
||||
+126
-10
@@ -34,6 +34,11 @@ static void corruptSchema(
|
||||
pData->rc = SQLITE_ERROR;
|
||||
}else if( db->flags & SQLITE_WriteSchema ){
|
||||
pData->rc = SQLITE_CORRUPT_BKPT;
|
||||
}else if( IsSharedSchema(db)
|
||||
&& 0==sqlite3StrNICmp(zExtra, "malformed database schema", 17)
|
||||
){
|
||||
pData->rc = SQLITE_CORRUPT_BKPT;
|
||||
*pData->pzErrMsg = sqlite3DbStrDup(db, zExtra);
|
||||
}else{
|
||||
char *z;
|
||||
if( zObj==0 ) zObj = "?";
|
||||
@@ -44,6 +49,28 @@ static void corruptSchema(
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
/*
|
||||
** Update the Schema.cksum checksum to account for the database object
|
||||
** specified by the three arguments following the first.
|
||||
*/
|
||||
static void schemaUpdateChecksum(
|
||||
InitData *pData, /* Schema parse context */
|
||||
const char *zName, /* Name of new database object */
|
||||
const char *zRoot, /* Root page of new database object */
|
||||
const char *zSql /* SQL used to create new database object */
|
||||
){
|
||||
int i;
|
||||
u64 cksum = pData->cksum;
|
||||
if( zName ){
|
||||
for(i=0; zName[i]; i++) cksum += (cksum<<3) + zName[i];
|
||||
}
|
||||
if( zRoot ) for(i=0; zRoot[i]; i++) cksum += (cksum<<3) + zRoot[i];
|
||||
if( zSql ) for(i=0; zSql[i]; i++) cksum += (cksum<<3) + zSql[i];
|
||||
pData->cksum = cksum;
|
||||
}
|
||||
#endif /* ifdef SQLITE_ENABLE_SHARED_SCHEMA */
|
||||
|
||||
/*
|
||||
** Check to see if any sibling index (another index on the same table)
|
||||
** of pIndex has the same root page number, and if it does, return true.
|
||||
@@ -67,6 +94,15 @@ static int sqlite3Prepare(
|
||||
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
|
||||
const char **pzTail /* OUT: End of parsed string */
|
||||
);
|
||||
static int sqlite3LockAndPrepare(
|
||||
sqlite3 *db, /* Database handle. */
|
||||
const char *zSql, /* UTF-8 encoded SQL statement. */
|
||||
int nBytes, /* Length of zSql in bytes. */
|
||||
u32 prepFlags, /* Zero or more SQLITE_PREPARE_* flags */
|
||||
Vdbe *pReprepare, /* VM being reprepared */
|
||||
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
|
||||
const char **pzTail /* OUT: End of parsed string */
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
@@ -119,7 +155,11 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
||||
db->init.orphanTrigger = 0;
|
||||
db->init.azInit = argv;
|
||||
pStmt = 0;
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
TESTONLY(rcp = ) sqlite3LockAndPrepare(db, argv[4], -1, 0, 0, &pStmt, 0);
|
||||
#else
|
||||
TESTONLY(rcp = ) sqlite3Prepare(db, argv[4], -1, 0, 0, &pStmt, 0);
|
||||
#endif
|
||||
rc = db->errCode;
|
||||
assert( (rc&0xFF)==(rcp&0xFF) );
|
||||
db->init.iDb = saved_iDb;
|
||||
@@ -131,7 +171,12 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
||||
if( rc > pData->rc ) pData->rc = rc;
|
||||
if( rc==SQLITE_NOMEM ){
|
||||
sqlite3OomFault(db);
|
||||
}else if( rc!=SQLITE_INTERRUPT && (rc&0xFF)!=SQLITE_LOCKED ){
|
||||
}else if( rc!=SQLITE_INTERRUPT
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
&& (rc&0xFF)!=SQLITE_LOCKED
|
||||
&& (rc&0xFF)!=SQLITE_IOERR
|
||||
#endif
|
||||
){
|
||||
corruptSchema(pData, argv[1], sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
@@ -156,6 +201,12 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
||||
corruptSchema(pData, argv[1], pIndex?"invalid rootpage":"orphan index");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
if( IsSharedSchema(db) && iDb!=1 ){
|
||||
schemaUpdateChecksum(pData, argv[0], argv[1], argv[2]);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -182,10 +233,28 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){
|
||||
|
||||
assert( (db->mDbFlags & DBFLAG_SchemaKnownOk)==0 );
|
||||
assert( iDb>=0 && iDb<db->nDb );
|
||||
assert( db->aDb[iDb].pSchema );
|
||||
assert( db->aDb[iDb].pSchema || (IsSharedSchema(db) && iDb!=1) );
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
assert( iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) );
|
||||
|
||||
pDb = &db->aDb[iDb];
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
assert( pDb->pSPool==0 || IsSharedSchema(db) );
|
||||
if( pDb->pSPool ){
|
||||
/* See if there is a free schema object in the schema-pool. If not,
|
||||
** disconnect from said schema pool and continue. This function will
|
||||
** connect to a (possibly different) schema-pool before returning. */
|
||||
Schema *pNew = sqlite3SchemaExtract(pDb->pSPool);
|
||||
if( pNew ){
|
||||
pDb->pSchema = pNew;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
rc = sqlite3SchemaDisconnect(db, iDb, 1);
|
||||
if( rc!=SQLITE_OK ) goto error_out;
|
||||
assert( pDb->pSchema && pDb->pSPool==0 );
|
||||
}
|
||||
#endif
|
||||
|
||||
db->init.busy = 1;
|
||||
|
||||
/* Construct the in-memory representation schema tables (sqlite_master or
|
||||
@@ -206,6 +275,7 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){
|
||||
initData.pzErrMsg = pzErrMsg;
|
||||
initData.mInitFlags = mFlags;
|
||||
initData.nInitRow = 0;
|
||||
initData.cksum = 0;
|
||||
sqlite3InitCallback(&initData, 5, (char **)azArg, 0);
|
||||
if( initData.rc ){
|
||||
rc = initData.rc;
|
||||
@@ -214,7 +284,6 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){
|
||||
|
||||
/* Create a cursor to hold the database open
|
||||
*/
|
||||
pDb = &db->aDb[iDb];
|
||||
if( pDb->pBt==0 ){
|
||||
assert( iDb==1 );
|
||||
DbSetProperty(db, 1, DB_SchemaLoaded);
|
||||
@@ -370,6 +439,10 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && iDb!=1 && IsSharedSchema(db) ){
|
||||
rc = sqlite3SchemaConnect(db, iDb, initData.cksum);
|
||||
}
|
||||
|
||||
/* Jump here for an error that occurs after successfully allocating
|
||||
** curMain and calling sqlite3BtreeEnter(). For an error that occurs
|
||||
** before that point, jump to error_out.
|
||||
@@ -391,6 +464,35 @@ error_out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
/*
|
||||
** If this is a SHARED_SCHEMA connection and the DBFLAG_SchemaInUse flag
|
||||
** is not currently set, set it and return non-zero. Otherwise, return 0.
|
||||
*/
|
||||
int sqlite3LockReusableSchema(sqlite3 *db){
|
||||
if( IsSharedSchema(db) && (db->mDbFlags & DBFLAG_SchemaInuse)==0 ){
|
||||
db->mDbFlags |= DBFLAG_SchemaInuse;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif /* ifdef SQLITE_ENABLE_SHARED_SCHEMA */
|
||||
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
/*
|
||||
** This function is a no-op for non-SHARED_SCHEMA connections, or if bRelease
|
||||
** is zero. Otherwise, clear the DBFLAG_SchemaInuse flag and release all
|
||||
** schema references currently held.
|
||||
*/
|
||||
void sqlite3UnlockReusableSchema(sqlite3 *db, int bRelease){
|
||||
if( bRelease ){
|
||||
db->mDbFlags &= ~DBFLAG_SchemaInuse;
|
||||
sqlite3SchemaReleaseAll(db);
|
||||
}
|
||||
}
|
||||
#endif /* ifdef SQLITE_ENABLE_SHARED_SCHEMA */
|
||||
|
||||
/*
|
||||
** Initialize all database files - the main database file, the file
|
||||
** used to store temporary tables, and any additional database files
|
||||
@@ -402,8 +504,12 @@ error_out:
|
||||
** file was of zero-length, then the DB_Empty flag is also set.
|
||||
*/
|
||||
int sqlite3Init(sqlite3 *db, char **pzErrMsg){
|
||||
int i, rc;
|
||||
int rc = SQLITE_OK;
|
||||
int bReleaseSchema;
|
||||
int i;
|
||||
int commit_internal = !(db->mDbFlags&DBFLAG_SchemaChange);
|
||||
|
||||
bReleaseSchema = sqlite3LockReusableSchema(db);
|
||||
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
assert( sqlite3BtreeHoldsMutex(db->aDb[0].pBt) );
|
||||
@@ -413,20 +519,19 @@ int sqlite3Init(sqlite3 *db, char **pzErrMsg){
|
||||
/* Do the main schema first */
|
||||
if( !DbHasProperty(db, 0, DB_SchemaLoaded) ){
|
||||
rc = sqlite3InitOne(db, 0, pzErrMsg, 0);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
/* All other schemas after the main schema. The "temp" schema must be last */
|
||||
for(i=db->nDb-1; i>0; i--){
|
||||
for(i=db->nDb-1; rc==SQLITE_OK && i>0; i--){
|
||||
assert( i==1 || sqlite3BtreeHoldsMutex(db->aDb[i].pBt) );
|
||||
if( !DbHasProperty(db, i, DB_SchemaLoaded) ){
|
||||
rc = sqlite3InitOne(db, i, pzErrMsg, 0);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
}
|
||||
if( commit_internal ){
|
||||
if( rc==SQLITE_OK && commit_internal ){
|
||||
sqlite3CommitInternalChanges(db);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
sqlite3UnlockReusableSchema(db, bReleaseSchema);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -438,11 +543,12 @@ int sqlite3ReadSchema(Parse *pParse){
|
||||
sqlite3 *db = pParse->db;
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
if( !db->init.busy ){
|
||||
db->mDbFlags |= DBFLAG_FreeSchema; /* For sharable-schema mode */
|
||||
rc = sqlite3Init(db, &pParse->zErrMsg);
|
||||
if( rc!=SQLITE_OK ){
|
||||
pParse->rc = rc;
|
||||
pParse->nErr++;
|
||||
}else if( db->noSharedCache ){
|
||||
}else if( db->noSharedCache && !IsSharedSchema(db) ){
|
||||
db->mDbFlags |= DBFLAG_SchemaKnownOk;
|
||||
}
|
||||
}
|
||||
@@ -468,6 +574,10 @@ static void schemaIsValid(Parse *pParse){
|
||||
Btree *pBt = db->aDb[iDb].pBt; /* Btree database to read cookie from */
|
||||
if( pBt==0 ) continue;
|
||||
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
if( IsSharedSchema(db) && iDb!=1 && db->aDb[iDb].pSPool==0 ) continue;
|
||||
#endif
|
||||
|
||||
/* If there is not already a read-only (or read-write) transaction opened
|
||||
** on the b-tree database, open one now. If a transaction is opened, it
|
||||
** will be closed immediately after reading the meta-value. */
|
||||
@@ -697,6 +807,7 @@ static int sqlite3LockAndPrepare(
|
||||
){
|
||||
int rc;
|
||||
int cnt = 0;
|
||||
int bReleaseSchema = 0;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( ppStmt==0 ) return SQLITE_MISUSE_BKPT;
|
||||
@@ -706,6 +817,7 @@ static int sqlite3LockAndPrepare(
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
bReleaseSchema = sqlite3LockReusableSchema(db);
|
||||
sqlite3BtreeEnterAll(db);
|
||||
do{
|
||||
/* Make multiple attempts to compile the SQL, until it either succeeds
|
||||
@@ -715,7 +827,11 @@ static int sqlite3LockAndPrepare(
|
||||
assert( rc==SQLITE_OK || *ppStmt==0 );
|
||||
}while( rc==SQLITE_ERROR_RETRY
|
||||
|| (rc==SQLITE_SCHEMA && (sqlite3ResetOneSchema(db,-1), cnt++)==0) );
|
||||
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
|
||||
sqlite3UnlockReusableSchema(db, bReleaseSchema);
|
||||
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
assert( (rc&db->errMask)==rc );
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
|
||||
+263
-7
@@ -1076,13 +1076,14 @@ struct ShellState {
|
||||
|
||||
/* Allowed values for ShellState.openMode
|
||||
*/
|
||||
#define SHELL_OPEN_UNSPEC 0 /* No open-mode specified */
|
||||
#define SHELL_OPEN_NORMAL 1 /* Normal database file */
|
||||
#define SHELL_OPEN_APPENDVFS 2 /* Use appendvfs */
|
||||
#define SHELL_OPEN_ZIPFILE 3 /* Use the zipfile virtual table */
|
||||
#define SHELL_OPEN_READONLY 4 /* Open a normal database read-only */
|
||||
#define SHELL_OPEN_DESERIALIZE 5 /* Open using sqlite3_deserialize() */
|
||||
#define SHELL_OPEN_HEXDB 6 /* Use "dbtotxt" output as data source */
|
||||
#define SHELL_OPEN_UNSPEC 0 /* No open-mode specified */
|
||||
#define SHELL_OPEN_NORMAL 1 /* Normal database file */
|
||||
#define SHELL_OPEN_APPENDVFS 2 /* Use appendvfs */
|
||||
#define SHELL_OPEN_ZIPFILE 3 /* Use the zipfile virtual table */
|
||||
#define SHELL_OPEN_READONLY 4 /* Open a normal database read-only */
|
||||
#define SHELL_OPEN_DESERIALIZE 5 /* Open using sqlite3_deserialize() */
|
||||
#define SHELL_OPEN_HEXDB 6 /* Use "dbtotxt" output as data source */
|
||||
#define SHELL_OPEN_SHAREDSCHEMA 7 /* Open for schema reuse */
|
||||
|
||||
/* Allowed values for ShellState.eTraceType
|
||||
*/
|
||||
@@ -3632,6 +3633,12 @@ static const char *(azHelp[]) = {
|
||||
" --sha3-384 Use the sha3-384 algorithm",
|
||||
" --sha3-512 Use the sha3-512 algorithm",
|
||||
" Any other argument is a LIKE pattern for tables to hash",
|
||||
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
|
||||
".shared-schema CMD DB1 DB2 ...",
|
||||
" Commands:",
|
||||
" check Determine if DB1, DB2, etc have identical schemas",
|
||||
" fix Attempt to make DB1, DB2, etc compatible",
|
||||
#endif
|
||||
#ifndef SQLITE_NOHAVE_SYSTEM
|
||||
".shell CMD ARGS... Run CMD ARGS... in a system shell",
|
||||
#endif
|
||||
@@ -4149,6 +4156,11 @@ static void open_db(ShellState *p, int openFlags){
|
||||
SQLITE_OPEN_READONLY|p->openFlags, 0);
|
||||
break;
|
||||
}
|
||||
case SHELL_OPEN_SHAREDSCHEMA: {
|
||||
sqlite3_open_v2(p->zDbFilename, &p->db,
|
||||
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_SHARED_SCHEMA,0);
|
||||
break;
|
||||
}
|
||||
case SHELL_OPEN_UNSPEC:
|
||||
case SHELL_OPEN_NORMAL: {
|
||||
sqlite3_open_v2(p->zDbFilename, &p->db,
|
||||
@@ -6312,6 +6324,237 @@ static char *shellMPrintf(int *pRc, const char *zFmt, ...){
|
||||
}
|
||||
return z;
|
||||
}
|
||||
static int sharedSchemaFix(ShellState *pState, const char *zDb, int eFix){
|
||||
int rc = SQLITE_OK;
|
||||
i64 iLast = 0;
|
||||
int iCookie = 0;
|
||||
int iAutoVacuum = 0;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
|
||||
shellExecPrintf(pState->db, &rc, "ATTACH '%q' AS _shared_schema_tmp", zDb);
|
||||
shellExecPrintf(pState->db, &rc, "PRAGMA writable_schema = 1");
|
||||
shellExecPrintf(pState->db, &rc, "BEGIN");
|
||||
shellPreparePrintf(pState->db, &rc, &pStmt,
|
||||
"SELECT max(rowid) FROM _shared_schema_tmp.sqlite_master"
|
||||
);
|
||||
sqlite3_step(pStmt);
|
||||
iLast = sqlite3_column_int64(pStmt, 0);
|
||||
shellFinalize(&rc, pStmt);
|
||||
shellPreparePrintf(pState->db, &rc, &pStmt,
|
||||
"INSERT INTO _shared_schema_tmp.sqlite_master SELECT "
|
||||
" type, name, tbl_name, ("
|
||||
" SELECT rootpage FROM _shared_schema_tmp.sqlite_master WHERE "
|
||||
" type IS o.type AND name IS o.name AND rowid<=?"
|
||||
" ), sql FROM main.sqlite_master AS o"
|
||||
);
|
||||
sqlite3_bind_int64(pStmt, 1, iLast);
|
||||
sqlite3_step(pStmt);
|
||||
shellFinalize(&rc, pStmt);
|
||||
|
||||
shellExecPrintf(pState->db, &rc,
|
||||
"DELETE FROM _shared_schema_tmp.sqlite_master WHERE rowid<=%lld",
|
||||
iLast
|
||||
);
|
||||
shellExecPrintf(pState->db, &rc, "COMMIT");
|
||||
sqlite3_exec(pState->db, "PRAGMA writable_schema = 0", 0, 0, 0);
|
||||
|
||||
/* Copy the auto-vacuum setting from main to the target db */
|
||||
shellPreparePrintf(pState->db, &rc, &pStmt, "PRAGMA main.auto_vacuum");
|
||||
sqlite3_step(pStmt);
|
||||
iAutoVacuum = sqlite3_column_int(pStmt, 0);
|
||||
shellFinalize(&rc, pStmt);
|
||||
shellExecPrintf(pState->db, &rc,
|
||||
"PRAGMA _shared_schema_tmp.auto_vacuum = %d", iAutoVacuum
|
||||
);
|
||||
|
||||
/* Vacuum the db in order to standardize the rootpage numbers. */
|
||||
shellExecPrintf(pState->db, &rc, "VACUUM _shared_schema_tmp");
|
||||
|
||||
/* Set the schema-cookie value to the same as database "main" */
|
||||
shellPreparePrintf(pState->db, &rc, &pStmt, "PRAGMA main.schema_version");
|
||||
sqlite3_step(pStmt);
|
||||
iCookie = sqlite3_column_int(pStmt, 0);
|
||||
shellFinalize(&rc, pStmt);
|
||||
shellExecPrintf(pState->db, &rc,
|
||||
"PRAGMA _shared_schema_tmp.schema_version = %d", iCookie
|
||||
);
|
||||
|
||||
sqlite3_exec(pState->db, "DETACH _shared_schema_tmp", 0, 0, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sharedSchemaCheck(ShellState *pState, const char *zDb, int *peFix){
|
||||
int rc = SQLITE_OK;
|
||||
int bFailed = 0;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
|
||||
if( peFix ) *peFix = 0;
|
||||
shellExecPrintf(pState->db, &rc, "ATTACH '%q' AS _shared_schema_tmp", zDb);
|
||||
|
||||
/* Check if this database has the same set of objects as the current db */
|
||||
shellPreparePrintf(pState->db, &rc, &pStmt,
|
||||
"SELECT type, name FROM _shared_schema_tmp.sqlite_master AS o "
|
||||
"WHERE NOT EXISTS ("
|
||||
" SELECT 1 FROM main.sqlite_master "
|
||||
" WHERE name IS o.name AND type IS o.type"
|
||||
")"
|
||||
" UNION ALL "
|
||||
"SELECT type, name FROM main.sqlite_master AS o "
|
||||
"WHERE NOT EXISTS ("
|
||||
" SELECT 1 FROM _shared_schema_tmp.sqlite_master "
|
||||
" WHERE name IS o.name AND type IS o.type"
|
||||
")"
|
||||
);
|
||||
if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
utf8_printf(pState->out, "%s is NOT compatible (objects)\n", zDb);
|
||||
bFailed = 1;
|
||||
}
|
||||
shellFinalize(&rc, pStmt);
|
||||
|
||||
/* Check if this database has the same set of SQL statements as the
|
||||
** current db. */
|
||||
if( bFailed==0 ){
|
||||
shellPreparePrintf(pState->db, &rc, &pStmt,
|
||||
"SELECT 1 FROM _shared_schema_tmp.sqlite_master AS o "
|
||||
"WHERE sql IS NOT ("
|
||||
" SELECT sql FROM main.sqlite_master "
|
||||
" WHERE name IS o.name AND type IS o.type"
|
||||
")"
|
||||
);
|
||||
if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
utf8_printf(pState->out, "%s is NOT compatible (SQL)\n", zDb);
|
||||
bFailed = 1;
|
||||
}
|
||||
shellFinalize(&rc, pStmt);
|
||||
}
|
||||
|
||||
/* Check if this database has the same set of root pages as the current
|
||||
** db. */
|
||||
if( bFailed==0 ){
|
||||
shellPreparePrintf(pState->db, &rc, &pStmt,
|
||||
"SELECT 1 FROM _shared_schema_tmp.sqlite_master AS o "
|
||||
"WHERE rootpage IS NOT ("
|
||||
" SELECT rootpage FROM main.sqlite_master "
|
||||
" WHERE name IS o.name AND type IS o.type"
|
||||
")"
|
||||
);
|
||||
if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
if( peFix==0 ){
|
||||
utf8_printf(pState->out, "%s is NOT compatible (root pages)\n", zDb);
|
||||
}
|
||||
bFailed = 1;
|
||||
if( peFix ) *peFix = 1;
|
||||
}
|
||||
shellFinalize(&rc, pStmt);
|
||||
}
|
||||
|
||||
if( bFailed==0 ){
|
||||
shellPreparePrintf(pState->db, &rc, &pStmt,
|
||||
"SELECT 1 WHERE ("
|
||||
" SELECT group_concat(rootpage || '.' || name || '.' || sql, '.') "
|
||||
" FROM _shared_schema_tmp.sqlite_master"
|
||||
") IS NOT ("
|
||||
" SELECT group_concat(rootpage || '.' || name || '.' || sql, '.') "
|
||||
" FROM main.sqlite_master"
|
||||
")"
|
||||
);
|
||||
if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
if( peFix==0 ){
|
||||
utf8_printf(pState->out,
|
||||
"%s is NOT compatible (order of sqlite_master rows)\n", zDb
|
||||
);
|
||||
}
|
||||
bFailed = 1;
|
||||
if( peFix ) *peFix = 2;
|
||||
}
|
||||
shellFinalize(&rc, pStmt);
|
||||
}
|
||||
|
||||
if( bFailed==0 ){
|
||||
int iMain = -1;
|
||||
int iNew = +1;
|
||||
shellPreparePrintf(pState->db, &rc, &pStmt,
|
||||
"PRAGMA main.schema_version"
|
||||
);
|
||||
if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
iMain = sqlite3_column_int(pStmt, 0);
|
||||
}
|
||||
shellFinalize(&rc, pStmt);
|
||||
shellPreparePrintf(pState->db, &rc, &pStmt,
|
||||
"PRAGMA _shared_schema_tmp.schema_version"
|
||||
);
|
||||
if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
iNew = sqlite3_column_int(pStmt, 0);
|
||||
}
|
||||
shellFinalize(&rc, pStmt);
|
||||
if( rc==SQLITE_OK && iMain!=iNew ){
|
||||
if( peFix==0 ){
|
||||
utf8_printf(pState->out,
|
||||
"%s is NOT compatible (schema cookie)\n", zDb
|
||||
);
|
||||
}
|
||||
bFailed = 1;
|
||||
if( peFix ) *peFix = 3;
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && bFailed==0 ){
|
||||
utf8_printf(pState->out, "%s is compatible\n", zDb);
|
||||
}
|
||||
|
||||
sqlite3_exec(pState->db, "DETACH _shared_schema_tmp", 0, 0, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** .shared-schema check|fix DB1 DB2...
|
||||
*/
|
||||
static int sharedSchemaDotCommand(
|
||||
ShellState *pState, /* Current shell tool state */
|
||||
char **azArg, /* Array of arguments passed to dot command */
|
||||
int nArg /* Number of entries in azArg[] */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
int bFix = 0; /* Fix databases if possible */
|
||||
int n1;
|
||||
int i;
|
||||
if( nArg<3 ){
|
||||
goto shared_schema_usage;
|
||||
}
|
||||
|
||||
n1 = (int)strlen(azArg[1]);
|
||||
if( n1>0 && n1<=3 && memcmp("fix", azArg[1], n1)==0 ){
|
||||
bFix = 1;
|
||||
}else if( n1==0 || n1>5 || memcmp("check", azArg[1], n1) ){
|
||||
goto shared_schema_usage;
|
||||
}
|
||||
|
||||
for(i=2; rc==SQLITE_OK && i<nArg; i++){
|
||||
int eFix = 0;
|
||||
rc = sharedSchemaCheck(pState, azArg[i], bFix ? &eFix : 0);
|
||||
if( rc==SQLITE_OK && bFix && eFix ){
|
||||
utf8_printf(pState->out, "Fixing %s... ", azArg[i]);
|
||||
fflush(pState->out);
|
||||
rc = sharedSchemaFix(pState, azArg[i], eFix);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sharedSchemaCheck(pState, azArg[i], &eFix);
|
||||
if( rc==SQLITE_OK && eFix ){
|
||||
utf8_printf(pState->out, "VACUUMing main... ");
|
||||
fflush(pState->out);
|
||||
rc = sqlite3_exec(pState->db, "VACUUM main", 0, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sharedSchemaCheck(pState, azArg[i], 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
shared_schema_usage:
|
||||
raw_printf(stderr, "usage: .shared-schema check|fix DB1 DB2...\n");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
** When running the ".recover" command, each output table, and the special
|
||||
@@ -8066,6 +8309,8 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
p->openMode = SHELL_OPEN_APPENDVFS;
|
||||
}else if( optionMatch(z, "readonly") ){
|
||||
p->openMode = SHELL_OPEN_READONLY;
|
||||
}else if( optionMatch(z, "sharedschema") ){
|
||||
p->openMode = SHELL_OPEN_SHAREDSCHEMA;
|
||||
}else if( optionMatch(z, "nofollow") ){
|
||||
p->openFlags |= SQLITE_OPEN_NOFOLLOW;
|
||||
#ifdef SQLITE_ENABLE_DESERIALIZE
|
||||
@@ -9020,6 +9265,13 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
sqlite3_free(zSql);
|
||||
}else
|
||||
|
||||
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
|
||||
if( c=='s' && strncmp(azArg[0], "shared-schema", n)==0 ){
|
||||
open_db(p, 0);
|
||||
sharedSchemaDotCommand(p, azArg, nArg);
|
||||
}else
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_NOHAVE_SYSTEM
|
||||
if( c=='s'
|
||||
&& (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0)
|
||||
@@ -10304,6 +10556,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
|
||||
#endif
|
||||
}else if( strcmp(z,"-readonly")==0 ){
|
||||
data.openMode = SHELL_OPEN_READONLY;
|
||||
}else if( strcmp(z,"-sharedschema")==0 ){
|
||||
data.openMode = SHELL_OPEN_SHAREDSCHEMA;
|
||||
}else if( strcmp(z,"-nofollow")==0 ){
|
||||
data.openFlags = SQLITE_OPEN_NOFOLLOW;
|
||||
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
|
||||
@@ -10409,6 +10663,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
|
||||
#endif
|
||||
}else if( strcmp(z,"-readonly")==0 ){
|
||||
data.openMode = SHELL_OPEN_READONLY;
|
||||
}else if( strcmp(z,"-sharedschema")==0 ){
|
||||
data.openMode = SHELL_OPEN_SHAREDSCHEMA;
|
||||
}else if( strcmp(z,"-nofollow")==0 ){
|
||||
data.openFlags |= SQLITE_OPEN_NOFOLLOW;
|
||||
}else if( strcmp(z,"-ascii")==0 ){
|
||||
|
||||
@@ -574,6 +574,8 @@ int sqlite3_exec(
|
||||
#define SQLITE_OPEN_NOFOLLOW 0x01000000 /* Ok for sqlite3_open_v2() */
|
||||
|
||||
/* Reserved: 0x00F00000 */
|
||||
#define SQLITE_OPEN_SHARED_SCHEMA 0x01000000 /* Ok for sqlite3_open_v2() */
|
||||
|
||||
|
||||
/*
|
||||
** CAPI3REF: Device Characteristics
|
||||
|
||||
+47
-1
@@ -1077,6 +1077,7 @@ typedef struct CollSeq CollSeq;
|
||||
typedef struct Column Column;
|
||||
typedef struct Db Db;
|
||||
typedef struct Schema Schema;
|
||||
typedef struct SchemaPool SchemaPool;
|
||||
typedef struct Expr Expr;
|
||||
typedef struct ExprList ExprList;
|
||||
typedef struct FKey FKey;
|
||||
@@ -1210,6 +1211,10 @@ struct Db {
|
||||
u8 safety_level; /* How aggressive at syncing data to disk */
|
||||
u8 bSyncSet; /* True if "PRAGMA synchronous=N" has been run */
|
||||
Schema *pSchema; /* Pointer to database schema (possibly shared) */
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
SchemaPool *pSPool; /* For REUSE_SCHEMA mode */
|
||||
VTable *pVTable; /* List of all VTable objects (REUSE_SCHEMA mode only) */
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -1241,6 +1246,9 @@ struct Schema {
|
||||
u8 enc; /* Text encoding used by this database */
|
||||
u16 schemaFlags; /* Flags associated with this schema */
|
||||
int cache_size; /* Number of pages to use in the cache */
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
Schema *pNext; /* Next Schema object SchemaPool (REUSE_SCHEMA) */
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -1553,6 +1561,12 @@ struct sqlite3 {
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
# define IsSharedSchema(db) (((db)->openFlags & SQLITE_OPEN_SHARED_SCHEMA)!=0)
|
||||
#else
|
||||
# define IsSharedSchema(db) 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
** A macro to discover the encoding of a database.
|
||||
*/
|
||||
@@ -1632,6 +1646,9 @@ struct sqlite3 {
|
||||
#define DBFLAG_SchemaKnownOk 0x0010 /* Schema is known to be valid */
|
||||
#define DBFLAG_InternalFunc 0x0020 /* Allow use of internal functions */
|
||||
|
||||
#define DBFLAG_SchemaInuse 0x0020 /* Do not release sharable schemas */
|
||||
#define DBFLAG_FreeSchema 0x0040 /* Free extra shared schemas on release */
|
||||
|
||||
/*
|
||||
** Bits of the sqlite3.dbOptFlags field that are used by the
|
||||
** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface to
|
||||
@@ -2070,6 +2087,9 @@ struct VTable {
|
||||
u8 eVtabRisk; /* Riskiness of allowing hacker access */
|
||||
int iSavepoint; /* Depth of the SAVEPOINT stack */
|
||||
VTable *pNext; /* Next in linked list (see above) */
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
char *zName; /* Table name (REUSE_SCHEMA mode) */
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Allowed values for VTable.eVtabRisk
|
||||
@@ -3286,7 +3306,6 @@ struct Parse {
|
||||
AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */
|
||||
Parse *pToplevel; /* Parse structure for main program (or NULL) */
|
||||
Table *pTriggerTab; /* Table triggers are being coded for */
|
||||
Parse *pParentParse; /* Parent parser if this parser is nested */
|
||||
int addrCrTab; /* Address of OP_CreateBtree opcode on CREATE TABLE */
|
||||
u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */
|
||||
u32 oldmask; /* Mask of old.* columns referenced */
|
||||
@@ -3450,6 +3469,9 @@ struct Trigger {
|
||||
Schema *pTabSchema; /* Schema containing the table */
|
||||
TriggerStep *step_list; /* Link list of trigger program steps */
|
||||
Trigger *pNext; /* Next trigger associated with the table */
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
char *zTabSchema; /* Temp triggers in IsSharedSchema() dbs only */
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -3561,6 +3583,7 @@ typedef struct {
|
||||
int rc; /* Result code stored here */
|
||||
u32 mInitFlags; /* Flags controlling error messages */
|
||||
u32 nInitRow; /* Number of rows processed */
|
||||
u64 cksum; /* Schema checksum for REUSE_SCHEMA mode */
|
||||
} InitData;
|
||||
|
||||
/*
|
||||
@@ -4554,6 +4577,29 @@ void sqlite3DefaultRowEst(Index*);
|
||||
void sqlite3RegisterLikeFunctions(sqlite3*, int);
|
||||
int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*);
|
||||
void sqlite3SchemaClear(void *);
|
||||
void sqlite3SchemaClearOrDisconnect(sqlite3*, int);
|
||||
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
int sqlite3SchemaConnect(sqlite3*, int, u64);
|
||||
int sqlite3SchemaDisconnect(sqlite3 *, int, int);
|
||||
Schema *sqlite3SchemaExtract(SchemaPool*);
|
||||
int sqlite3SchemaLoad(sqlite3*, int, int*, char**);
|
||||
void sqlite3SchemaReleaseAll(sqlite3*);
|
||||
void sqlite3SchemaRelease(sqlite3*, int);
|
||||
void sqlite3SchemaAdjustUsed(sqlite3*, int, int, int*);
|
||||
void sqlite3SchemaWritable(Parse*, int);
|
||||
void sqlite3UnlockReusableSchema(sqlite3 *db, int bRelease);
|
||||
int sqlite3LockReusableSchema(sqlite3 *db);
|
||||
#else
|
||||
# define sqlite3SchemaWritable(x,y)
|
||||
# define sqlite3UnlockReusableSchema(x,y)
|
||||
# define sqlite3LockReusableSchema(x) 0
|
||||
# define sqlite3SchemaDisconnect(x,y,z) SQLITE_OK
|
||||
# define sqlite3SchemaLoad(w,x,y,z) SQLITE_OK
|
||||
# define sqlite3SchemaRelease(y,z)
|
||||
# define sqlite3SchemaConnect(x,y,z) SQLITE_OK
|
||||
#endif
|
||||
|
||||
Schema *sqlite3SchemaGet(sqlite3 *, Btree *);
|
||||
int sqlite3SchemaToIndex(sqlite3 *db, Schema *);
|
||||
KeyInfo *sqlite3KeyInfoAlloc(sqlite3*,int,int);
|
||||
|
||||
+21
-1
@@ -288,11 +288,24 @@ int sqlite3_db_status(
|
||||
case SQLITE_DBSTATUS_SCHEMA_USED: {
|
||||
int i; /* Used to iterate through schemas */
|
||||
int nByte = 0; /* Used to accumulate return value */
|
||||
int bReleaseSchema;
|
||||
|
||||
sqlite3BtreeEnterAll(db);
|
||||
bReleaseSchema = sqlite3LockReusableSchema(db);
|
||||
db->pnBytesFreed = &nByte;
|
||||
for(i=0; i<db->nDb; i++){
|
||||
Schema *pSchema = db->aDb[i].pSchema;
|
||||
Schema *pSchema;
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
int bUnload = 0;
|
||||
int nUsed = nByte;
|
||||
if( db->aDb[i].pSPool ){
|
||||
char *zDummy = 0;
|
||||
rc = sqlite3SchemaLoad(db, i, &bUnload, &zDummy);
|
||||
sqlite3_free(zDummy);
|
||||
if( rc ) break;
|
||||
}
|
||||
#endif /* ifdef SQLITE_ENABLE_SHARED_SCHEMA */
|
||||
pSchema = db->aDb[i].pSchema;
|
||||
if( ALWAYS(pSchema!=0) ){
|
||||
HashElem *p;
|
||||
|
||||
@@ -314,7 +327,14 @@ int sqlite3_db_status(
|
||||
sqlite3DeleteTable(db, (Table *)sqliteHashData(p));
|
||||
}
|
||||
}
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
if( db->aDb[i].pSPool ){
|
||||
if( bUnload ) sqlite3SchemaRelease(db, i);
|
||||
sqlite3SchemaAdjustUsed(db, i, nUsed, &nByte);
|
||||
}
|
||||
#endif /* ifdef SQLITE_ENABLE_SHARED_SCHEMA */
|
||||
}
|
||||
sqlite3UnlockReusableSchema(db, bReleaseSchema);
|
||||
db->pnBytesFreed = 0;
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
|
||||
|
||||
@@ -3678,6 +3678,9 @@ static int sqliteCmdUsage(
|
||||
"HANDLE ?FILENAME? ?-vfs VFSNAME? ?-readonly BOOLEAN? ?-create BOOLEAN?"
|
||||
" ?-nofollow BOOLEAN?"
|
||||
" ?-nomutex BOOLEAN? ?-fullmutex BOOLEAN? ?-uri BOOLEAN?"
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
" ?-shared-schema BOOLEAN?"
|
||||
#endif
|
||||
#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL)
|
||||
" ?-key CODECKEY?"
|
||||
#endif
|
||||
@@ -3821,6 +3824,16 @@ static int SQLITE_TCLAPI DbMain(
|
||||
}else{
|
||||
flags &= ~SQLITE_OPEN_URI;
|
||||
}
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
}else if( strcmp(zArg, "-shared-schema")==0 ){
|
||||
int b;
|
||||
if( Tcl_GetBooleanFromObj(interp, objv[i], &b) ) return TCL_ERROR;
|
||||
if( b ){
|
||||
flags |= SQLITE_OPEN_SHARED_SCHEMA;
|
||||
}else{
|
||||
flags &= ~SQLITE_OPEN_SHARED_SCHEMA;
|
||||
}
|
||||
#endif /* ifdef SQLITE_ENABLE_SHARED_SCHEMA */
|
||||
}else{
|
||||
Tcl_AppendResult(interp, "unknown option: ", zArg, (char*)0);
|
||||
return TCL_ERROR;
|
||||
|
||||
@@ -768,6 +768,12 @@ Tcl_SetVar2(interp, "sqlite_options", "mergesort", "1", TCL_GLOBAL_ONLY);
|
||||
Tcl_SetVar2(interp, "sqlite_options", "windowfunc", "1", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
Tcl_SetVar2(interp, "sqlite_options", "sharedschema", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "sharedschema", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#define LINKVAR(x) { \
|
||||
static const int cv_ ## x = SQLITE_ ## x; \
|
||||
Tcl_LinkVar(interp, "SQLITE_" #x, (char *)&(cv_ ## x), \
|
||||
|
||||
@@ -530,7 +530,7 @@ static int multiplexOpen(
|
||||
pGroup->szChunk += 65536;
|
||||
}
|
||||
}
|
||||
pGroup->flags = flags;
|
||||
pGroup->flags = (flags & ~SQLITE_OPEN_URI);
|
||||
rc = multiplexSubFilename(pGroup, 1);
|
||||
if( rc==SQLITE_OK ){
|
||||
pSubOpen = multiplexSubOpen(pGroup, 0, &rc, pOutFlags, 0);
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
** 2006 June 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.
|
||||
**
|
||||
*************************************************************************
|
||||
** Code for testing the virtual table interfaces. This code
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
*/
|
||||
|
||||
/*
|
||||
** None of this works unless we have virtual tables.
|
||||
*/
|
||||
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_TEST)
|
||||
|
||||
#include <tcl.h>
|
||||
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/* The code in this file defines a sqlite3 virtual-table module with
|
||||
** the following schema.
|
||||
*/
|
||||
#define SCHEMAPOOL_SCHEMA \
|
||||
"CREATE TABLE x(" \
|
||||
" cksum INTEGER, " \
|
||||
" nref INTEGER, " \
|
||||
" nschema INTEGER, " \
|
||||
" ndelete INTEGER " \
|
||||
")"
|
||||
|
||||
#define SCHEMAPOOL_NFIELD 4
|
||||
|
||||
typedef struct schemapool_vtab schemapool_vtab;
|
||||
typedef struct schemapool_cursor schemapool_cursor;
|
||||
|
||||
/* A schema table object */
|
||||
struct schemapool_vtab {
|
||||
sqlite3_vtab base;
|
||||
};
|
||||
|
||||
/* A schema table cursor object */
|
||||
struct schemapool_cursor {
|
||||
sqlite3_vtab_cursor base;
|
||||
sqlite3_int64 *aData;
|
||||
int iRow;
|
||||
int nRow;
|
||||
};
|
||||
|
||||
/*
|
||||
** Table destructor for the schema module.
|
||||
*/
|
||||
static int schemaPoolDestroy(sqlite3_vtab *pVtab){
|
||||
sqlite3_free(pVtab);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Table constructor for the schema module.
|
||||
*/
|
||||
static int schemaPoolCreate(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
int rc = SQLITE_NOMEM;
|
||||
schemapool_vtab *pVtab = sqlite3_malloc(sizeof(schemapool_vtab));
|
||||
if( pVtab ){
|
||||
memset(pVtab, 0, sizeof(schemapool_vtab));
|
||||
rc = sqlite3_declare_vtab(db, SCHEMAPOOL_SCHEMA);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3_free(pVtab);
|
||||
pVtab = 0;
|
||||
}
|
||||
}
|
||||
*ppVtab = (sqlite3_vtab *)pVtab;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a new cursor on the schema table.
|
||||
*/
|
||||
static int schemaPoolOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
int rc = SQLITE_NOMEM;
|
||||
schemapool_cursor *pCur;
|
||||
pCur = sqlite3_malloc(sizeof(schemapool_cursor));
|
||||
if( pCur ){
|
||||
memset(pCur, 0, sizeof(schemapool_cursor));
|
||||
*ppCursor = (sqlite3_vtab_cursor*)pCur;
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a schema table cursor.
|
||||
*/
|
||||
static int schemaPoolClose(sqlite3_vtab_cursor *cur){
|
||||
schemapool_cursor *pCur = (schemapool_cursor*)cur;
|
||||
sqlite3_free(pCur->aData);
|
||||
sqlite3_free(pCur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Retrieve a column of data.
|
||||
*/
|
||||
static int schemaPoolColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
|
||||
schemapool_cursor *pCur = (schemapool_cursor*)cur;
|
||||
assert( i==0 || i==1 || i==2 || i==3 );
|
||||
sqlite3_result_int64(ctx, pCur->aData[pCur->iRow*SCHEMAPOOL_NFIELD + i]);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Retrieve the current rowid.
|
||||
*/
|
||||
static int schemaPoolRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
schemapool_cursor *pCur = (schemapool_cursor*)cur;
|
||||
*pRowid = pCur->iRow + 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int schemaPoolEof(sqlite3_vtab_cursor *cur){
|
||||
schemapool_cursor *pCur = (schemapool_cursor*)cur;
|
||||
return pCur->iRow>=pCur->nRow;
|
||||
}
|
||||
|
||||
/*
|
||||
** Advance the cursor to the next row.
|
||||
*/
|
||||
static int schemaPoolNext(sqlite3_vtab_cursor *cur){
|
||||
schemapool_cursor *pCur = (schemapool_cursor*)cur;
|
||||
pCur->iRow++;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
struct SchemaPool {
|
||||
int nRef; /* Number of pointers to this object */
|
||||
int nDelete; /* Schema objects deleted by ReleaseAll() */
|
||||
u64 cksum; /* Checksum for this Schema contents */
|
||||
Schema *pSchema; /* Linked list of Schema objects */
|
||||
Schema sSchema; /* The single dummy schema object */
|
||||
SchemaPool *pNext; /* Next element in schemaPoolList */
|
||||
};
|
||||
extern SchemaPool *sqlite3SchemaPoolList(void);
|
||||
|
||||
/*
|
||||
** Reset a schemaPool table cursor.
|
||||
*/
|
||||
static int schemaPoolFilter(
|
||||
sqlite3_vtab_cursor *pVtabCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
SchemaPool *pSPool;
|
||||
schemapool_cursor *pCur = (schemapool_cursor*)pVtabCursor;
|
||||
|
||||
sqlite3_free(pCur->aData);
|
||||
pCur->aData = 0;
|
||||
pCur->nRow = 0;
|
||||
pCur->iRow = 0;
|
||||
|
||||
for(pSPool = sqlite3SchemaPoolList(); pSPool; pSPool=pSPool->pNext){
|
||||
pCur->nRow++;
|
||||
}
|
||||
|
||||
if( pCur->nRow ){
|
||||
int iRow = 0;
|
||||
int nByte = SCHEMAPOOL_NFIELD * pCur->nRow * sizeof(i64);
|
||||
pCur->aData = (i64*)sqlite3_malloc(nByte);
|
||||
if( pCur->aData==0 ) return SQLITE_NOMEM;
|
||||
for(pSPool = sqlite3SchemaPoolList(); pSPool; pSPool=pSPool->pNext){
|
||||
Schema *p;
|
||||
i64 nSchema = 0;
|
||||
for(p=pSPool->pSchema; p; p=p->pNext){
|
||||
nSchema++;
|
||||
}
|
||||
pCur->aData[0 + iRow*SCHEMAPOOL_NFIELD] = pSPool->cksum;
|
||||
pCur->aData[1 + iRow*SCHEMAPOOL_NFIELD] = (i64)pSPool->nRef;
|
||||
pCur->aData[2 + iRow*SCHEMAPOOL_NFIELD] = nSchema;
|
||||
pCur->aData[3 + iRow*SCHEMAPOOL_NFIELD] = (i64)pSPool->nDelete;
|
||||
iRow++;
|
||||
}
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Analyse the WHERE condition.
|
||||
*/
|
||||
static int schemaPoolBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** A virtual table module that merely echos method calls into TCL
|
||||
** variables.
|
||||
*/
|
||||
static sqlite3_module schemaPoolModule = {
|
||||
0, /* iVersion */
|
||||
schemaPoolCreate,
|
||||
schemaPoolCreate,
|
||||
schemaPoolBestIndex,
|
||||
schemaPoolDestroy,
|
||||
schemaPoolDestroy,
|
||||
schemaPoolOpen, /* xOpen - open a cursor */
|
||||
schemaPoolClose, /* xClose - close a cursor */
|
||||
schemaPoolFilter, /* xFilter - configure scan constraints */
|
||||
schemaPoolNext, /* xNext - advance a cursor */
|
||||
schemaPoolEof, /* xEof */
|
||||
schemaPoolColumn, /* xColumn - read data */
|
||||
schemaPoolRowid, /* xRowid - read data */
|
||||
0, /* xUpdate */
|
||||
0, /* xBegin */
|
||||
0, /* xSync */
|
||||
0, /* xCommit */
|
||||
0, /* xRollback */
|
||||
0, /* xFindMethod */
|
||||
0, /* xRename */
|
||||
};
|
||||
|
||||
/*
|
||||
** Decode a pointer to an sqlite3 object.
|
||||
*/
|
||||
extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
|
||||
|
||||
/*
|
||||
** Register the schema virtual table module.
|
||||
*/
|
||||
static int SQLITE_TCLAPI register_schemapool_module(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3 *db;
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
sqlite3_create_module(db, "schemapool", &schemaPoolModule, 0);
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
#endif /* ifdef SQLITE_ENABLE_SHARED_SCHEMA */
|
||||
#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_TEST) */
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
*/
|
||||
int Sqlitetestschemapool_Init(Tcl_Interp *interp){
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
static struct {
|
||||
char *zName;
|
||||
Tcl_ObjCmdProc *xProc;
|
||||
void *clientData;
|
||||
} aObjCmd[] = {
|
||||
{ "register_schemapool_module", register_schemapool_module, 0 },
|
||||
};
|
||||
int i;
|
||||
for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
|
||||
Tcl_CreateObjCommand(interp, aObjCmd[i].zName,
|
||||
aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
|
||||
}
|
||||
#endif /* ifdef SQLITE_ENABLE_SHARED_SCHEMA */
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
@@ -77,6 +77,7 @@ const char *sqlite3TestInit(Tcl_Interp *interp){
|
||||
extern int Sqlitetest_malloc_Init(Tcl_Interp*);
|
||||
extern int Sqlitetest_mutex_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestschema_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestschemapool_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestsse_Init(Tcl_Interp*);
|
||||
extern int Sqlitetesttclvar_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestfs_Init(Tcl_Interp*);
|
||||
@@ -147,6 +148,7 @@ const char *sqlite3TestInit(Tcl_Interp *interp){
|
||||
Sqlitetest_malloc_Init(interp);
|
||||
Sqlitetest_mutex_Init(interp);
|
||||
Sqlitetestschema_Init(interp);
|
||||
Sqlitetestschemapool_Init(interp);
|
||||
Sqlitetesttclvar_Init(interp);
|
||||
Sqlitetestfs_Init(interp);
|
||||
SqlitetestThread_Init(interp);
|
||||
|
||||
+2
-3
@@ -560,6 +560,7 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
|
||||
int lastTokenParsed = -1; /* type of the previous token */
|
||||
sqlite3 *db = pParse->db; /* The database connection */
|
||||
int mxSqlLen; /* Max length of an SQL string */
|
||||
Parse *pParentParse = db->pParse;
|
||||
#ifdef sqlite3Parser_ENGINEALWAYSONSTACK
|
||||
yyParser sEngine; /* Space to hold the Lemon-generated Parser object */
|
||||
#endif
|
||||
@@ -595,7 +596,6 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
|
||||
assert( pParse->pNewTrigger==0 );
|
||||
assert( pParse->nVar==0 );
|
||||
assert( pParse->pVList==0 );
|
||||
pParse->pParentParse = db->pParse;
|
||||
db->pParse = pParse;
|
||||
while( 1 ){
|
||||
n = sqlite3GetToken((u8*)zSql, &tokenType);
|
||||
@@ -722,8 +722,7 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
|
||||
pParse->pZombieTab = p->pNextZombie;
|
||||
sqlite3DeleteTable(db, p);
|
||||
}
|
||||
db->pParse = pParse->pParentParse;
|
||||
pParse->pParentParse = 0;
|
||||
db->pParse = pParentParse;
|
||||
assert( nErr==0 || pParse->rc!=SQLITE_OK );
|
||||
return nErr;
|
||||
}
|
||||
|
||||
+32
-7
@@ -55,15 +55,30 @@ Trigger *sqlite3TriggerList(Parse *pParse, Table *pTab){
|
||||
}
|
||||
|
||||
if( pTmpSchema!=pTab->pSchema ){
|
||||
sqlite3 *db = pParse->db;
|
||||
HashElem *p;
|
||||
assert( sqlite3SchemaMutexHeld(pParse->db, 0, pTmpSchema) );
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
char *zSchema = 0;
|
||||
if( IsSharedSchema(db) ){
|
||||
zSchema = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName;
|
||||
}
|
||||
#endif
|
||||
assert( sqlite3SchemaMutexHeld(db, 0, pTmpSchema) );
|
||||
for(p=sqliteHashFirst(&pTmpSchema->trigHash); p; p=sqliteHashNext(p)){
|
||||
Trigger *pTrig = (Trigger *)sqliteHashData(p);
|
||||
if( pTrig->pTabSchema==pTab->pSchema
|
||||
&& 0==sqlite3StrICmp(pTrig->table, pTab->zName)
|
||||
){
|
||||
pTrig->pNext = (pList ? pList : pTab->pTrigger);
|
||||
pList = pTrig;
|
||||
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
if( (zSchema==0 && pTrig->pTabSchema==pTab->pSchema)
|
||||
|| (zSchema!=0 && 0==sqlite3StrICmp(pTrig->zTabSchema, zSchema)) )
|
||||
#else
|
||||
if( pTrig->pTabSchema==pTab->pSchema )
|
||||
#endif
|
||||
{
|
||||
if( 0==sqlite3StrICmp(pTrig->table, pTab->zName) ){
|
||||
pTrig->pTabSchema = pTab->pSchema;
|
||||
pTrig->pNext = (pList ? pList : pTab->pTrigger);
|
||||
pList = pTrig;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -248,6 +263,12 @@ void sqlite3BeginTrigger(
|
||||
pTrigger->zName = zName;
|
||||
zName = 0;
|
||||
pTrigger->table = sqlite3DbStrDup(db, pTableName->a[0].zName);
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
if( IsSharedSchema(db) && iDb==1 ){
|
||||
int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
pTrigger->zTabSchema = sqlite3DbStrDup(db, db->aDb[iTabDb].zDbSName);
|
||||
}
|
||||
#endif /* ifdef SQLITE_ENABLE_SHARED_SCHEMA */
|
||||
pTrigger->pSchema = db->aDb[iDb].pSchema;
|
||||
pTrigger->pTabSchema = pTab->pSchema;
|
||||
pTrigger->op = (u8)op;
|
||||
@@ -336,7 +357,7 @@ void sqlite3FinishTrigger(
|
||||
pTrig->table, z);
|
||||
sqlite3DbFree(db, z);
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
sqlite3VdbeAddParseSchemaOp(v, iDb,
|
||||
sqlite3VdbeAddParseSchemaOp(pParse, iDb,
|
||||
sqlite3MPrintf(db, "type='trigger' AND name='%q'", zName));
|
||||
}
|
||||
|
||||
@@ -549,6 +570,9 @@ void sqlite3DeleteTrigger(sqlite3 *db, Trigger *pTrigger){
|
||||
sqlite3DeleteTriggerStep(db, pTrigger->step_list);
|
||||
sqlite3DbFree(db, pTrigger->zName);
|
||||
sqlite3DbFree(db, pTrigger->table);
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
sqlite3DbFree(db, pTrigger->zTabSchema);
|
||||
#endif
|
||||
sqlite3ExprDelete(db, pTrigger->pWhen);
|
||||
sqlite3IdListDelete(db, pTrigger->pColumns);
|
||||
sqlite3DbFree(db, pTrigger);
|
||||
@@ -620,6 +644,7 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
|
||||
|
||||
iDb = sqlite3SchemaToIndex(pParse->db, pTrigger->pSchema);
|
||||
assert( iDb>=0 && iDb<db->nDb );
|
||||
sqlite3SchemaWritable(pParse, iDb);
|
||||
pTable = tableOfTrigger(pTrigger);
|
||||
assert( (pTable && pTable->pSchema==pTrigger->pSchema) || iDb==1 );
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
|
||||
@@ -125,6 +125,7 @@ void sqlite3Vacuum(Parse *pParse, Token *pNm, Expr *pInto){
|
||||
}
|
||||
if( iDb!=1 ){
|
||||
int iIntoReg = 0;
|
||||
sqlite3SchemaWritable(pParse, iDb);
|
||||
if( pInto && sqlite3ResolveSelfReference(pParse,0,0,pInto,0)==0 ){
|
||||
iIntoReg = ++pParse->nMem;
|
||||
sqlite3ExprCode(pParse, pInto, iIntoReg);
|
||||
|
||||
+7
-7
@@ -3548,13 +3548,6 @@ case OP_Transaction: {
|
||||
&& (iMeta!=pOp->p3
|
||||
|| db->aDb[pOp->p1].pSchema->iGeneration!=pOp->p4.i)
|
||||
){
|
||||
/*
|
||||
** IMPLEMENTATION-OF: R-03189-51135 As each SQL statement runs, the schema
|
||||
** version is checked to ensure that the schema has not changed since the
|
||||
** SQL statement was prepared.
|
||||
*/
|
||||
sqlite3DbFree(db, p->zErrMsg);
|
||||
p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed");
|
||||
/* If the schema-cookie from the database file matches the cookie
|
||||
** stored with the in-memory representation of the schema, do
|
||||
** not reload the schema from the database file.
|
||||
@@ -3571,6 +3564,13 @@ case OP_Transaction: {
|
||||
if( db->aDb[pOp->p1].pSchema->schema_cookie!=iMeta ){
|
||||
sqlite3ResetOneSchema(db, pOp->p1);
|
||||
}
|
||||
/*
|
||||
** IMPLEMENTATION-OF: R-03189-51135 As each SQL statement runs, the schema
|
||||
** version is checked to ensure that the schema has not changed since the
|
||||
** SQL statement was prepared.
|
||||
*/
|
||||
sqlite3DbFree(db, p->zErrMsg);
|
||||
p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed");
|
||||
p->expired = 1;
|
||||
rc = SQLITE_SCHEMA;
|
||||
}
|
||||
|
||||
+1
-1
@@ -223,7 +223,7 @@ VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp,int iLineno);
|
||||
#else
|
||||
# define sqlite3ExplainBreakpoint(A,B) /*no-op*/
|
||||
#endif
|
||||
void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*);
|
||||
void sqlite3VdbeAddParseSchemaOp(Parse*,int,char*);
|
||||
void sqlite3VdbeChangeOpcode(Vdbe*, int addr, u8);
|
||||
void sqlite3VdbeChangeP1(Vdbe*, int addr, int P1);
|
||||
void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2);
|
||||
|
||||
+3
-1
@@ -471,8 +471,10 @@ void sqlite3VdbeExplainPop(Parse *pParse){
|
||||
** The zWhere string must have been obtained from sqlite3_malloc().
|
||||
** This routine will take ownership of the allocated memory.
|
||||
*/
|
||||
void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere){
|
||||
void sqlite3VdbeAddParseSchemaOp(Parse *pParse, int iDb, char *zWhere){
|
||||
Vdbe *p = pParse->pVdbe;
|
||||
int j;
|
||||
sqlite3SchemaWritable(pParse, iDb);
|
||||
sqlite3VdbeAddOp4(p, OP_ParseSchema, iDb, 0, 0, zWhere, P4_DYNAMIC);
|
||||
for(j=0; j<p->db->nDb; j++) sqlite3VdbeUsesBtree(p, j);
|
||||
}
|
||||
|
||||
@@ -131,7 +131,9 @@ int sqlite3_blob_open(
|
||||
char *zErr = 0;
|
||||
Table *pTab;
|
||||
Incrblob *pBlob = 0;
|
||||
Parse *pParentParse = db->pParse;
|
||||
Parse sParse;
|
||||
int bUnlock; /* True to unlock reusable schemas before returning */
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( ppBlob==0 ){
|
||||
@@ -148,11 +150,13 @@ int sqlite3_blob_open(
|
||||
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
|
||||
bUnlock = sqlite3LockReusableSchema(db);
|
||||
pBlob = (Incrblob *)sqlite3DbMallocZero(db, sizeof(Incrblob));
|
||||
do {
|
||||
memset(&sParse, 0, sizeof(Parse));
|
||||
if( !pBlob ) goto blob_open_out;
|
||||
sParse.db = db;
|
||||
db->pParse = &sParse;
|
||||
sqlite3DbFree(db, zErr);
|
||||
zErr = 0;
|
||||
|
||||
@@ -331,6 +335,8 @@ int sqlite3_blob_open(
|
||||
} while( (++nAttempt)<SQLITE_MAX_SCHEMA_RETRY && rc==SQLITE_SCHEMA );
|
||||
|
||||
blob_open_out:
|
||||
db->pParse = pParentParse;
|
||||
sqlite3UnlockReusableSchema(db, bUnlock);
|
||||
if( rc==SQLITE_OK && db->mallocFailed==0 ){
|
||||
*ppBlob = (sqlite3_blob *)pBlob;
|
||||
}else{
|
||||
|
||||
+46
-8
@@ -192,6 +192,24 @@ void sqlite3VtabLock(VTable *pVTab){
|
||||
VTable *sqlite3GetVTable(sqlite3 *db, Table *pTab){
|
||||
VTable *pVtab;
|
||||
assert( IsVirtual(pTab) );
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
if( IsSharedSchema(db) ){
|
||||
int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
if( iDb!=1 ){
|
||||
VTable **pp;
|
||||
for(pp=&db->aDb[iDb].pVTable; *pp; pp=&(*pp)->pNext){
|
||||
if( sqlite3StrICmp(pTab->zName, (*pp)->zName)==0 ) break;
|
||||
}
|
||||
pVtab = *pp;
|
||||
if( pVtab && pTab->nCol<=0 ){
|
||||
*pp = pVtab->pNext;
|
||||
sqlite3VtabUnlock(pVtab);
|
||||
pVtab = 0;
|
||||
}
|
||||
return pVtab;
|
||||
}
|
||||
}
|
||||
#endif /* ifdef SQLITE_ENABLE_SHARED_SCHEMA */
|
||||
for(pVtab=pTab->pVTable; pVtab && pVtab->db!=db; pVtab=pVtab->pNext);
|
||||
return pVtab;
|
||||
}
|
||||
@@ -489,7 +507,7 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
|
||||
|
||||
sqlite3VdbeAddOp0(v, OP_Expire);
|
||||
zWhere = sqlite3MPrintf(db, "name=%Q AND sql=%Q", pTab->zName, zStmt);
|
||||
sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere);
|
||||
sqlite3VdbeAddParseSchemaOp(pParse, iDb, zWhere);
|
||||
sqlite3DbFree(db, zStmt);
|
||||
|
||||
iReg = ++pParse->nMem;
|
||||
@@ -563,6 +581,7 @@ static int vtabCallConstructor(
|
||||
char *zModuleName;
|
||||
int iDb;
|
||||
VtabCtx *pCtx;
|
||||
int nByte; /* Bytes of space to allocate */
|
||||
|
||||
/* Check that the virtual-table is not already being initialized */
|
||||
for(pCtx=db->pVtabCtx; pCtx; pCtx=pCtx->pPrior){
|
||||
@@ -579,7 +598,11 @@ static int vtabCallConstructor(
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
|
||||
pVTable = sqlite3MallocZero(sizeof(VTable));
|
||||
nByte = sizeof(VTable);
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
nByte += sqlite3Strlen30(pTab->zName) + 1;
|
||||
#endif
|
||||
pVTable = (VTable*)sqlite3MallocZero(nByte);
|
||||
if( !pVTable ){
|
||||
sqlite3OomFault(db);
|
||||
sqlite3DbFree(db, zModuleName);
|
||||
@@ -587,6 +610,10 @@ static int vtabCallConstructor(
|
||||
}
|
||||
pVTable->db = db;
|
||||
pVTable->pMod = pMod;
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
pVTable->zName = (char*)&pVTable[1];
|
||||
memcpy(pVTable->zName, pTab->zName, nByte-sizeof(VTable));
|
||||
#endif
|
||||
pVTable->eVtabRisk = SQLITE_VTABRISK_Normal;
|
||||
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
@@ -629,12 +656,22 @@ static int vtabCallConstructor(
|
||||
int iCol;
|
||||
u16 oooHidden = 0;
|
||||
/* If everything went according to plan, link the new VTable structure
|
||||
** into the linked list headed by pTab->pVTable. Then loop through the
|
||||
** columns of the table to see if any of them contain the token "hidden".
|
||||
** If so, set the Column COLFLAG_HIDDEN flag and remove the token from
|
||||
** the type string. */
|
||||
pVTable->pNext = pTab->pVTable;
|
||||
pTab->pVTable = pVTable;
|
||||
** into the linked list headed by pTab->pVTable. Or, if this is a
|
||||
** reusable schema, into the linked list headed by Db.pVTable.
|
||||
**
|
||||
** Then loop through the columns of the table to see if any of them
|
||||
** contain the token "hidden". If so, set the Column COLFLAG_HIDDEN flag
|
||||
** and remove the token from the type string. */
|
||||
#ifdef SQLITE_ENABLE_SHARED_SCHEMA
|
||||
if( IsSharedSchema(db) && iDb!=1 ){
|
||||
pVTable->pNext = db->aDb[iDb].pVTable;
|
||||
db->aDb[iDb].pVTable = pVTable;
|
||||
}else
|
||||
#endif /* ifdef SQLITE_ENABLE_SHARED_SCHEMA */
|
||||
{
|
||||
pVTable->pNext = pTab->pVTable;
|
||||
pTab->pVTable = pVTable;
|
||||
}
|
||||
|
||||
for(iCol=0; iCol<pTab->nCol; iCol++){
|
||||
char *zType = sqlite3ColumnType(&pTab->aCol[iCol], "");
|
||||
@@ -687,6 +724,7 @@ int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){
|
||||
|
||||
assert( pTab );
|
||||
if( !IsVirtual(pTab) || sqlite3GetVTable(db, pTab) ){
|
||||
assert( !IsVirtual(pTab) || pTab->nCol>0 );
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,394 @@
|
||||
# 2017 August 9
|
||||
#
|
||||
# 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 reuse1
|
||||
|
||||
ifcapable !sharedschema {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
forcedelete test.db2
|
||||
sqlite3 db2 test.db2
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(x INTEGER PRIMARY KEY, y UNIQUE, z);
|
||||
CREATE INDEX i1 ON t1(z);
|
||||
PRAGMA schema_version;
|
||||
} {2}
|
||||
|
||||
do_execsql_test -db db2 1.1 {
|
||||
CREATE TABLE t1(x INTEGER PRIMARY KEY, y UNIQUE, z);
|
||||
CREATE INDEX i1 ON t1(z);
|
||||
PRAGMA schema_version;
|
||||
} {2}
|
||||
|
||||
do_test 1.2 {
|
||||
db close
|
||||
db2 close
|
||||
sqlite3 db2 test.db2 -shared-schema 1
|
||||
sqlite3 db test.db -shared-schema 1
|
||||
} {}
|
||||
|
||||
do_execsql_test -db db2 1.3.1 {
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(4, 5, 6);
|
||||
}
|
||||
|
||||
do_execsql_test 1.3.2 {
|
||||
SELECT * FROM t1;
|
||||
PRAGMA integrity_check;
|
||||
} {ok}
|
||||
|
||||
do_execsql_test -db db2 1.3.3 {
|
||||
SELECT * FROM t1;
|
||||
PRAGMA integrity_check;
|
||||
} {1 2 3 4 5 6 ok}
|
||||
|
||||
sqlite3 db3 test.db2
|
||||
do_execsql_test -db db3 1.4.1 {
|
||||
ALTER TABLE t1 ADD COLUMN a;
|
||||
}
|
||||
do_execsql_test -db db2 1.4.2 {
|
||||
SELECT * FROM t1;
|
||||
} {1 2 3 {} 4 5 6 {}}
|
||||
do_execsql_test 1.4.3 {
|
||||
SELECT * FROM t1;
|
||||
} {}
|
||||
|
||||
db3 close
|
||||
sqlite3 db3 test.db
|
||||
do_execsql_test -db db3 1.5.0 {
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
|
||||
SELECT 1, 2, 3;
|
||||
END;
|
||||
}
|
||||
|
||||
# Check that the schema cannot be modified if the db was opened with
|
||||
# SQLITE_OPEN_REUSE_SCHEMA.
|
||||
#
|
||||
foreach {tn sql} {
|
||||
1 { CREATE TABLE t2(x, y) }
|
||||
2 { CREATE INDEX i2 ON t1(z) }
|
||||
3 { CREATE VIEW v2 AS SELECT * FROM t2 }
|
||||
4 { ALTER TABLE t1 RENAME TO t3 }
|
||||
5 { ALTER TABLE t1 ADD COLUMN xyz }
|
||||
6 { VACUUM }
|
||||
7 { DROP INDEX i1 }
|
||||
8 { DROP TABLE t1 }
|
||||
9 { DROP TRIGGER tr1 }
|
||||
10 { ANALYZE }
|
||||
11 { ALTER TABLE t1 RENAME z TO zzz }
|
||||
} {
|
||||
do_catchsql_test 1.5.$tn $sql {1 {attempt to modify read-only schema}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
forcedelete test.db2
|
||||
ifcapable fts5 {
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(a);
|
||||
INSERT INTO ft VALUES('one'), ('two'), ('three');
|
||||
ATTACH 'test.db2' AS aux;
|
||||
CREATE VIRTUAL TABLE aux.ft USING fts5(a);
|
||||
INSERT INTO aux.ft VALUES('aux1'), ('aux2'), ('aux3');
|
||||
}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db -shared-schema 1
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
ATTACH 'test.db2' AS aux;
|
||||
SELECT * FROM main.ft;
|
||||
} {one two three}
|
||||
|
||||
breakpoint
|
||||
do_execsql_test 2.2 {
|
||||
SELECT * FROM aux.ft;
|
||||
} {aux1 aux2 aux3}
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
SELECT * FROM aux.ft_content;
|
||||
} {1 aux1 2 aux2 3 aux3}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
forcedelete test.db2
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b, c);
|
||||
CREATE VIEW v1 AS SELECT * FROM t1;
|
||||
CREATE TRIGGER v1_ins INSTEAD OF INSERT ON v1 BEGIN
|
||||
INSERT INTO t1 VALUES(new.a, new.b, new.c);
|
||||
END;
|
||||
CREATE TRIGGER v1_del INSTEAD OF DELETE ON v1 BEGIN
|
||||
DELETE FROM t1 WHERE a=old.a;
|
||||
END;
|
||||
CREATE TRIGGER v1_up INSTEAD OF UPDATE ON v1 BEGIN
|
||||
UPDATE t1 SET a=new.a, b=new.b, c=new.c WHERE a=old.a;
|
||||
END;
|
||||
}
|
||||
forcecopy test.db test.db2
|
||||
|
||||
do_test 3.1 {
|
||||
sqlite3 db2 test.db2
|
||||
execsql { INSERT INTO t1 VALUES(1, 2, 3) } db
|
||||
execsql { INSERT INTO t1 VALUES(4, 5, 6) } db2
|
||||
db2 close
|
||||
execsql { ATTACH 'test.db2' AS aux; }
|
||||
} {}
|
||||
|
||||
do_execsql_test 3.2 {
|
||||
SELECT * FROM main.v1;
|
||||
} {1 2 3}
|
||||
|
||||
do_execsql_test 3.3 {
|
||||
SELECT * FROM aux.v1;
|
||||
} {4 5 6}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db -shared-schema 1
|
||||
|
||||
do_execsql_test 3.4 { ATTACH 'test.db2' AS aux } {}
|
||||
do_execsql_test 3.5 { SELECT * FROM main.v1 } {1 2 3}
|
||||
do_execsql_test 3.6 { SELECT * FROM aux.v1 } {4 5 6}
|
||||
|
||||
do_execsql_test 3.7.1 { INSERT INTO aux.t1 VALUES(8, 9, 10); }
|
||||
do_execsql_test 3.7.2 { SELECT * FROM main.v1 } {1 2 3}
|
||||
do_execsql_test 3.7.3 { SELECT * FROM aux.v1 } {4 5 6 8 9 10}
|
||||
|
||||
do_execsql_test 3.8.1 { DELETE FROM aux.t1 WHERE b=5 }
|
||||
do_execsql_test 3.8.2 { SELECT * FROM main.v1 } {1 2 3}
|
||||
do_execsql_test 3.8.3 { SELECT * FROM aux.v1 } {8 9 10}
|
||||
|
||||
do_execsql_test 3.9.1 { UPDATE aux.t1 SET b='abc' }
|
||||
do_execsql_test 3.9.2 { SELECT * FROM main.v1 } {1 2 3}
|
||||
do_execsql_test 3.9.3 { SELECT * FROM aux.v1 } {8 abc 10}
|
||||
|
||||
do_execsql_test 3.10.1 { INSERT INTO aux.v1 VALUES(11, 12, 13) }
|
||||
do_execsql_test 3.10.2 { SELECT * FROM main.v1 } {1 2 3}
|
||||
do_execsql_test 3.10.3 { SELECT * FROM aux.v1 } {8 abc 10 11 12 13}
|
||||
|
||||
do_execsql_test 3.11.1 { DELETE FROM aux.v1 WHERE b='abc' }
|
||||
do_execsql_test 3.11.2 { SELECT * FROM main.v1 } {1 2 3}
|
||||
do_execsql_test 3.11.3 { SELECT * FROM aux.v1 } {11 12 13}
|
||||
|
||||
do_execsql_test 3.12.1 { UPDATE aux.v1 SET b='def' }
|
||||
do_execsql_test 3.12.2 { SELECT * FROM main.v1 } {1 2 3}
|
||||
do_execsql_test 3.12.3 { SELECT * FROM aux.v1 } {11 def 13}
|
||||
|
||||
do_execsql_test 3.13.1 {
|
||||
CREATE TEMP TRIGGER xyz AFTER INSERT ON aux.t1 BEGIN
|
||||
INSERT INTO v1 VALUES(new.a, new.b, new.c);
|
||||
END
|
||||
}
|
||||
do_execsql_test 3.13.2 {
|
||||
INSERT INTO aux.v1 VALUES('x', 'y', 'z');
|
||||
}
|
||||
do_execsql_test 3.13.3 {
|
||||
SELECT * FROM v1;
|
||||
} {1 2 3 x y z}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
forcedelete test.db2
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b, c UNIQUE);
|
||||
CREATE TABLE del(a, b, c);
|
||||
CREATE TRIGGER tr1 AFTER DELETE ON t1 BEGIN
|
||||
INSERT INTO del VALUES(old.a, old.b, old.c);
|
||||
END;
|
||||
}
|
||||
forcecopy test.db test.db2
|
||||
|
||||
db close
|
||||
sqlite3 db test.db -shared-schema 1
|
||||
execsql {
|
||||
ATTACH 'test.db2' AS aux;
|
||||
PRAGMA recursive_triggers = 1;
|
||||
}
|
||||
|
||||
do_execsql_test 4.1 {
|
||||
INSERT INTO main.t1 VALUES(1, 2, 3);
|
||||
INSERT INTO aux.t1 VALUES(4, 5, 6);
|
||||
}
|
||||
|
||||
do_execsql_test 4.2.1 {
|
||||
INSERT OR REPLACE INTO aux.t1 VALUES('a', 'b', 6);
|
||||
SELECT * FROM aux.t1;
|
||||
} {a b 6}
|
||||
do_execsql_test 4.2.2 { SELECT * FROM aux.del } {4 5 6}
|
||||
do_execsql_test 4.2.3 { SELECT * FROM main.del } {}
|
||||
|
||||
do_execsql_test 4.3.1 {
|
||||
INSERT INTO aux.t1 VALUES('x', 'y', 'z');
|
||||
UPDATE OR REPLACE aux.t1 SET c='z' WHERE a='a';
|
||||
} {}
|
||||
do_execsql_test 4.3.2 { SELECT * FROM aux.del } {4 5 6 x y z}
|
||||
do_execsql_test 4.3.3 { SELECT * FROM main.del } {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 5.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||
CREATE TABLE t2(a INTEGER PRIMARY KEY, b, c);
|
||||
CREATE INDEX i1 ON t1(b);
|
||||
INSERT INTO t1 VALUES(1, 2, 3), (4, 5, 6);
|
||||
ANALYZE;
|
||||
PRAGMA writable_schema = 1;
|
||||
DELETE FROM sqlite_stat1;
|
||||
}
|
||||
db close
|
||||
forcecopy test.db test.db2
|
||||
sqlite3 db test.db -shared-schema 1
|
||||
execsql { ATTACH 'test.db2' AS aux }
|
||||
|
||||
foreach {tn sql} {
|
||||
1 { CREATE TABLE t3(x) }
|
||||
2 { DROP TABLE t2 }
|
||||
3 { CREATE INDEX i2 ON t2(b) }
|
||||
4 { DROP INDEX i1 }
|
||||
5 { ALTER TABLE t1 ADD COLUMN d }
|
||||
6 { ALTER TABLE t1 RENAME TO t3 }
|
||||
7 { ALTER TABLE t1 RENAME c TO d }
|
||||
} {
|
||||
do_catchsql_test 5.1.$tn $sql {1 {attempt to modify read-only schema}}
|
||||
}
|
||||
|
||||
do_execsql_test 5.2.1 { ANALYZE aux.t1 } {}
|
||||
do_execsql_test 5.2.2 { SELECT * FROM aux.sqlite_stat1 } {t1 i1 {2 1}}
|
||||
do_execsql_test 5.2.3 { SELECT * FROM main.sqlite_stat1 } {}
|
||||
|
||||
do_test 5.3.0 {
|
||||
sqlite3 db2 test.db2
|
||||
db2 eval {
|
||||
PRAGMA writable_schema = 1;
|
||||
DELETE FROM sqlite_stat1;
|
||||
}
|
||||
} {}
|
||||
|
||||
do_execsql_test 5.3.1 { SELECT * FROM aux.sqlite_stat1 } {}
|
||||
do_execsql_test 5.3.2 { ANALYZE aux } {}
|
||||
do_execsql_test 5.3.3 { SELECT * FROM aux.sqlite_stat1 } {t1 i1 {2 1}}
|
||||
do_execsql_test 5.3.4 { SELECT * FROM main.sqlite_stat1 } {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Attempting to run ANALYZE when the required sqlite_statXX functions
|
||||
# are missing is an error (because it would modify the database schema).
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 5.4 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||
CREATE TABLE t2(a INTEGER PRIMARY KEY, b, c);
|
||||
CREATE INDEX i1 ON t1(b);
|
||||
INSERT INTO t1 VALUES(1, 2, 3), (4, 5, 6);
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db -shared-schema 1
|
||||
foreach {tn sql} {
|
||||
1 { ANALYZE }
|
||||
2 { ANALYZE t1 }
|
||||
3 { ANALYZE i1 }
|
||||
4 { ANALYZE main }
|
||||
5 { ANALYZE main.t1 }
|
||||
6 { ANALYZE main.i1 }
|
||||
} {
|
||||
do_catchsql_test 5.4.$tn $sql {1 {attempt to modify read-only schema}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 6.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||
CREATE VIEW v1 AS SELECT * FROM t1;
|
||||
}
|
||||
db close
|
||||
forcecopy test.db test.db2
|
||||
sqlite3 db test.db -shared-schema 1
|
||||
execsql { ATTACH 'test.db2' AS aux }
|
||||
|
||||
do_execsql_test 6.1 {
|
||||
INSERT INTO main.t1(a) VALUES(1), (2), (3);
|
||||
INSERT INTO aux.t1(a) VALUES(4), (5), (6);
|
||||
CREATE TEMP TABLE t2(i,t);
|
||||
INSERT INTO t2 VALUES(2, 'two'), (5, 'five');
|
||||
}
|
||||
|
||||
do_execsql_test 6.2 {
|
||||
SELECT t FROM t2 WHERE i IN (SELECT a FROM aux.t1)
|
||||
} {five}
|
||||
do_execsql_test 6.3 {
|
||||
SELECT t FROM t2 WHERE i IN (SELECT a FROM aux.v1)
|
||||
} {five}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 7.0 {
|
||||
CREATE TABLE p1(a PRIMARY KEY, b);
|
||||
CREATE TABLE p2(a PRIMARY KEY, b);
|
||||
CREATE TABLE c1(x REFERENCES p1 ON UPDATE CASCADE ON DELETE CASCADE);
|
||||
}
|
||||
|
||||
db close
|
||||
forcecopy test.db test.db2
|
||||
sqlite3 db test.db -shared-schema 1
|
||||
execsql { ATTACH 'test.db2' AS aux }
|
||||
|
||||
do_execsql_test 7.1 {
|
||||
INSERT INTO aux.p1 VALUES(1, 'one');
|
||||
INSERT INTO aux.p1 VALUES(2, 'two');
|
||||
PRAGMA foreign_keys = on;
|
||||
}
|
||||
|
||||
do_execsql_test 7.2 {
|
||||
INSERT INTO aux.c1 VALUES(2);
|
||||
}
|
||||
|
||||
do_execsql_test 7.3.1 {
|
||||
PRAGMA foreign_keys = off;
|
||||
INSERT INTO main.p2 SELECT * FROM aux.p1;
|
||||
}
|
||||
do_execsql_test 7.3.2 {
|
||||
SELECT * FROM main.p2;
|
||||
} {1 one 2 two}
|
||||
|
||||
do_execsql_test 7.3.3 {
|
||||
INSERT INTO aux.p2 VALUES(1, 2);
|
||||
}
|
||||
|
||||
do_execsql_test 7.3.4 {
|
||||
SELECT main.p2.a FROM main.p2, aux.p2;
|
||||
} {1 2}
|
||||
|
||||
do_execsql_test 7.3.5 {
|
||||
SELECT * FROM main.p2, aux.p2;
|
||||
} {1 one 1 2 2 two 1 2}
|
||||
|
||||
do_execsql_test 7.4 {
|
||||
SELECT count(*) FROM aux.p2;
|
||||
} {1}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
# 2017 August 9
|
||||
#
|
||||
# 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 reuse2
|
||||
|
||||
ifcapable !sharedschema {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(x INTEGER PRIMARY KEY, y UNIQUE, z);
|
||||
CREATE INDEX i1 ON t1(z);
|
||||
PRAGMA schema_version;
|
||||
} {2}
|
||||
|
||||
do_test 1.2 {
|
||||
catch { db close }
|
||||
catch { db2 close }
|
||||
sqlite3 db2 test.db -shared-schema 1
|
||||
sqlite3 db test.db -shared-schema 1
|
||||
} {}
|
||||
|
||||
do_execsql_test -db db2 1.3.1 {
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
}
|
||||
|
||||
do_execsql_test -db db2 1.3.2 {
|
||||
INSERT INTO t1 VALUES(4, 5, 6);
|
||||
}
|
||||
|
||||
do_execsql_test 1.3.3 {
|
||||
SELECT * FROM t1;
|
||||
} {1 2 3 4 5 6}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
reset_db
|
||||
ifcapable fts5 {
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(c);
|
||||
INSERT INTO ft VALUES('one two three');
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db -shared-schema 1
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
SELECT * FROM ft
|
||||
} {{one two three}}
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE t1(x INTEGER PRIMARY KEY, y UNIQUE, z);
|
||||
CREATE INDEX i1 ON t1(z);
|
||||
PRAGMA schema_version;
|
||||
} {2}
|
||||
|
||||
do_test 3.1 {
|
||||
sqlite3 db1 test.db -shared-schema 1
|
||||
sqlite3 db2 test.db -shared-schema 1
|
||||
} {}
|
||||
|
||||
do_execsql_test -db db1 3.2.1 { SELECT * FROM t1 }
|
||||
do_execsql_test -db db2 3.2.2 { SELECT * FROM t1 }
|
||||
|
||||
register_schemapool_module db
|
||||
do_execsql_test 3.3 {
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {nref=2 nschema=1 ndelete=0}
|
||||
|
||||
sqlite3 db3 test.db -shared-schema 1
|
||||
register_schemapool_module db3
|
||||
|
||||
do_execsql_test 3.5 {
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {nref=2 nschema=1 ndelete=0}
|
||||
|
||||
do_execsql_test -db db3 3.6 {
|
||||
SELECT * FROM t1;
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {nref=3 nschema=1 ndelete=0}
|
||||
|
||||
do_execsql_test 3.7 {
|
||||
CREATE TABLE t2(x);
|
||||
}
|
||||
|
||||
do_execsql_test 3.8 {
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {nref=3 nschema=1 ndelete=0}
|
||||
|
||||
do_execsql_test -db db1 3.9.1 { SELECT * FROM t1 }
|
||||
do_execsql_test 3.9.2 {
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {nref=1 nschema=1 ndelete=0 nref=2 nschema=1 ndelete=0}
|
||||
|
||||
do_execsql_test -db db2 3.10.1 { SELECT * FROM t1 }
|
||||
do_execsql_test 3.10.2 {
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool ORDER BY 1;
|
||||
} {nref=1 nschema=1 ndelete=0 nref=2 nschema=1 ndelete=0}
|
||||
|
||||
do_execsql_test -db db3 3.11.1 { SELECT * FROM t1 }
|
||||
do_execsql_test 3.11.2 {
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {nref=3 nschema=1 ndelete=0}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
catch {db1 close}
|
||||
catch {db2 close}
|
||||
catch {db3 close}
|
||||
reset_db
|
||||
do_execsql_test 4.0.1 {
|
||||
CREATE TABLE x1(a, b, c);
|
||||
CREATE INDEX x1a ON x1(a);
|
||||
CREATE INDEX x1b ON x1(b);
|
||||
}
|
||||
do_test 4.0.2 {
|
||||
db close
|
||||
for {set i 1} {$i < 6} {incr i} {
|
||||
forcedelete test.db${i}-journal test.db${i}-wal test.db${i}-wal2
|
||||
forcecopy test.db test.db${i}
|
||||
}
|
||||
sqlite3 db test.db
|
||||
sqlite3 db2 test.db -shared-schema 1
|
||||
} {}
|
||||
|
||||
register_schemapool_module db
|
||||
do_execsql_test 4.0.3 {
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {}
|
||||
|
||||
do_test 4.1.1 {
|
||||
execsql {
|
||||
ATTACH 'test.db1' AS db1;
|
||||
ATTACH 'test.db2' AS db2;
|
||||
ATTACH 'test.db3' AS db3;
|
||||
ATTACH 'test.db4' AS db4;
|
||||
ATTACH 'test.db5' AS db5;
|
||||
} db2
|
||||
} {}
|
||||
do_execsql_test 4.1.2 {
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {}
|
||||
do_execsql_test -db db2 4.1.3 {
|
||||
SELECT * FROM db3.x1
|
||||
}
|
||||
do_execsql_test 4.1.4 {
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {nref=1 nschema=1 ndelete=0}
|
||||
do_execsql_test -db db2 4.1.5 {
|
||||
SELECT * FROM db2.x1
|
||||
}
|
||||
do_execsql_test 4.1.6 {
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {nref=2 nschema=1 ndelete=0}
|
||||
do_execsql_test -db db2 4.1.7 {
|
||||
SELECT * FROM x1
|
||||
}
|
||||
do_execsql_test 4.1.8 {
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {nref=6 nschema=1 ndelete=0}
|
||||
|
||||
do_test 4.2.1 {
|
||||
catchsql { SELECT * FROM abc } db2
|
||||
} {1 {no such table: abc}}
|
||||
do_execsql_test 4.2.2 {
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {nref=6 nschema=1 ndelete=0}
|
||||
|
||||
register_schemapool_module db2
|
||||
do_execsql_test -db db2 4.3.1 {
|
||||
INSERT INTO x1 VALUES(1, 2, 3);
|
||||
INSERT INTO db1.x1 VALUES(4, 5, 6);
|
||||
INSERT INTO db2.x1 VALUES(7, 8, 9);
|
||||
INSERT INTO db3.x1 VALUES(10, 11, 12);
|
||||
INSERT INTO db4.x1 VALUES(13, 14, 15);
|
||||
INSERT INTO db5.x1 VALUES(16, 17, 18);
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {nref=6 nschema=1 ndelete=0}
|
||||
|
||||
do_execsql_test -db db2 4.3.2 {
|
||||
SELECT * FROM db5.x1;
|
||||
SELECT * FROM db4.x1;
|
||||
SELECT * FROM db3.x1;
|
||||
SELECT * FROM db2.x1;
|
||||
SELECT * FROM db1.x1;
|
||||
SELECT * FROM x1;
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {
|
||||
16 17 18 13 14 15 10 11 12 7 8 9 4 5 6 1 2 3
|
||||
nref=6 nschema=1 ndelete=0
|
||||
}
|
||||
|
||||
do_execsql_test -db db2 4.3.3 {
|
||||
UPDATE x1 SET a=a+10;
|
||||
UPDATE db5.x1 SET a=a+10;
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {
|
||||
nref=6 nschema=1 ndelete=0
|
||||
}
|
||||
|
||||
do_execsql_test -db db2 4.3.4 {
|
||||
SELECT * FROM db5.x1;
|
||||
SELECT * FROM db4.x1;
|
||||
SELECT * FROM db3.x1;
|
||||
SELECT * FROM db2.x1;
|
||||
SELECT * FROM db1.x1;
|
||||
SELECT * FROM x1;
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {
|
||||
26 17 18 13 14 15 10 11 12 7 8 9 4 5 6 11 2 3
|
||||
nref=6 nschema=1 ndelete=0
|
||||
}
|
||||
|
||||
do_execsql_test -db db2 4.3.5 {
|
||||
DELETE FROM db3.x1;
|
||||
DELETE FROM x1;
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {
|
||||
nref=6 nschema=1 ndelete=0
|
||||
}
|
||||
|
||||
do_execsql_test -db db2 4.3.6 {
|
||||
SELECT * FROM db5.x1;
|
||||
SELECT * FROM db4.x1;
|
||||
SELECT * FROM db3.x1;
|
||||
SELECT * FROM db2.x1;
|
||||
SELECT * FROM db1.x1;
|
||||
SELECT * FROM x1;
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {
|
||||
26 17 18 13 14 15 7 8 9 4 5 6
|
||||
nref=6 nschema=1 ndelete=0
|
||||
}
|
||||
|
||||
do_execsql_test -db db2 4.3.6 {
|
||||
SELECT * FROM db5.x1, db4.x1, db1.x1;
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {26 17 18 13 14 15 4 5 6 nref=6 nschema=3 ndelete=0}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# Test the incremental-blob API with REUSE_SCHEMA connections.
|
||||
#
|
||||
catch {db1 close}
|
||||
catch {db2 close}
|
||||
catch {db3 close}
|
||||
reset_db
|
||||
do_execsql_test 5.0.1 {
|
||||
CREATE TABLE bbb(a INTEGER PRIMARY KEY, b);
|
||||
}
|
||||
db close
|
||||
do_test 5.0.2 {
|
||||
sqlite3 db2 test.db -shared-schema 1
|
||||
register_schemapool_module db2
|
||||
for {set i 1} {$i<6} {incr i} {
|
||||
forcedelete test.db${i}-journal test.db${i}-wal test.db${i}-wal2
|
||||
forcecopy test.db test.db${i}
|
||||
sqlite3 db test.db${i}
|
||||
db eval { INSERT INTO bbb VALUES(123, 'database_' || $i) }
|
||||
db close
|
||||
db2 eval "ATTACH 'test.db${i}' AS db${i}"
|
||||
}
|
||||
execsql {
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} db2
|
||||
} {nref=6 nschema=1 ndelete=0}
|
||||
|
||||
do_test 5.1.1 {
|
||||
set res [list]
|
||||
for {set i 1} {$i<6} {incr i} {
|
||||
set chan [db2 incrblob db${i} bbb b 123]
|
||||
lappend res [gets $chan]
|
||||
close $chan
|
||||
}
|
||||
set res
|
||||
} {database_1 database_2 database_3 database_4 database_5}
|
||||
|
||||
do_execsql_test -db db2 5.1.2 {
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {nref=6 nschema=1 ndelete=0}
|
||||
|
||||
do_test 5.2.1 {
|
||||
sqlite3_table_column_metadata db2 main bbb a
|
||||
} {INTEGER BINARY 0 1 0}
|
||||
do_test 5.2.2 {
|
||||
sqlite3_table_column_metadata db2 main bbb b
|
||||
} {{} BINARY 0 0 0}
|
||||
|
||||
do_execsql_test -db db2 5.2.3 {
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {nref=6 nschema=1 ndelete=0}
|
||||
|
||||
do_execsql_test -db db2 5.2.4 {
|
||||
PRAGMA integrity_check;
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {ok nref=6 nschema=1 ndelete=5}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,355 @@
|
||||
# 2019 February 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix reuse3
|
||||
|
||||
ifcapable !sharedschema {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(x INTEGER PRIMARY KEY, y UNIQUE, z);
|
||||
CREATE INDEX i1 ON t1(z);
|
||||
CREATE TABLE t2(a);
|
||||
} {}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db -shared-schema 1
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
CREATE TEMP VIEW v1 AS SELECT * FROM t1;
|
||||
SELECT * FROM v1;
|
||||
}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
CREATE TEMP TRIGGER tr1 AFTER INSERT ON t1 BEGIN
|
||||
INSERT INTO t2 VALUES(new.x);
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
}
|
||||
|
||||
do_execsql_test 1.4 {
|
||||
SELECT * FROM t2
|
||||
} {1}
|
||||
|
||||
do_execsql_test 1.5 {
|
||||
SELECT * FROM v1
|
||||
} {1 2 3}
|
||||
|
||||
do_execsql_test 1.6 {
|
||||
BEGIN;
|
||||
DROP TRIGGER tr1;
|
||||
ROLLBACK;
|
||||
}
|
||||
|
||||
do_execsql_test 1.7 {
|
||||
SELECT * FROM v1
|
||||
} {1 2 3}
|
||||
|
||||
do_execsql_test 1.8 {
|
||||
INSERT INTO t1 VALUES(4, 5, 6);
|
||||
SELECT * FROM t2
|
||||
} {1 4}
|
||||
|
||||
do_execsql_test 1.9 {
|
||||
SELECT * FROM v1
|
||||
} {1 2 3 4 5 6}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test error messages when parsing the schema with a REUSE_SCHEMA
|
||||
# connection.
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE x1(a, b, c);
|
||||
CREATE TABLE y1(d, e, f);
|
||||
PRAGMA writable_schema = 1;
|
||||
UPDATE sqlite_master SET sql = 'CREATE TBL y1(d, e, f)' WHERE name = 'y1';
|
||||
}
|
||||
db close
|
||||
|
||||
sqlite3 db test.db -shared-schema 1
|
||||
do_catchsql_test 2.1 {
|
||||
SELECT * FROM x1;
|
||||
} {1 {malformed database schema (y1) - near "TBL": syntax error}}
|
||||
|
||||
do_catchsql_test 2.2 {
|
||||
SELECT * FROM x1;
|
||||
} {1 {malformed database schema (y1) - near "TBL": syntax error}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE x1(a, b, c);
|
||||
CREATE INDEX i1 ON x1(a, b, c);
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON x1 BEGIN
|
||||
SELECT 1, 2, 3, 4, 5;
|
||||
END;
|
||||
INSERT INTO x1 VALUES(1, 2, 3);
|
||||
}
|
||||
sqlite3 db1 test.db -shared-schema 1
|
||||
|
||||
do_test 3.1 {
|
||||
execsql { SELECT * FROM x1 } db1
|
||||
set N [lindex [sqlite3_db_status db1 SCHEMA_USED 0] 1]
|
||||
expr $N==$N
|
||||
} 1
|
||||
|
||||
sqlite3 db2 test.db -shared-schema 1
|
||||
do_test 3.2 {
|
||||
execsql { SELECT * FROM x1 } db2
|
||||
set N2 [lindex [sqlite3_db_status db2 SCHEMA_USED 0] 1]
|
||||
expr $N2>($N/2) && $N2<($N/2)+400
|
||||
} 1
|
||||
|
||||
sqlite3 db3 test.db -shared-schema 1
|
||||
sqlite3 db4 test.db -shared-schema 1
|
||||
do_test 3.3 {
|
||||
execsql { SELECT * FROM x1 } db3
|
||||
execsql { SELECT * FROM x1 } db4
|
||||
set N4 [lindex [sqlite3_db_status db2 SCHEMA_USED 0] 1]
|
||||
set M [expr 2*($N-$N2)]
|
||||
set {} {}
|
||||
} {}
|
||||
do_test 3.3.1 { expr {(($M / 4) + $N-$M)} } "#/$N4/"
|
||||
|
||||
catch { db1 close }
|
||||
catch { db2 close }
|
||||
catch { db3 close }
|
||||
catch { db4 close }
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# 4.1 Test the REINDEX command.
|
||||
# 4.2 Test CREATE TEMP ... commands.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 4.1.0 {
|
||||
CREATE TABLE x1(a, b, c);
|
||||
CREATE INDEX x1a ON x1(a);
|
||||
CREATE INDEX x1b ON x1(b);
|
||||
CREATE INDEX x1c ON x1(c);
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db -shared-schema 1
|
||||
|
||||
do_execsql_test 4.1.1 {
|
||||
REINDEX x1;
|
||||
REINDEX x1a;
|
||||
REINDEX x1b;
|
||||
REINDEX x1c;
|
||||
REINDEX;
|
||||
}
|
||||
|
||||
do_test 4.1.2 {
|
||||
for {set i 1} {$i < 5} {incr i} {
|
||||
forcedelete test.db${i} test.db${i}-wal test.db${i}-journal
|
||||
forcecopy test.db test.db${i}
|
||||
execsql "ATTACH 'test.db${i}' AS db${i}"
|
||||
}
|
||||
register_schemapool_module db
|
||||
set {} {}
|
||||
execsql {
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool
|
||||
}
|
||||
} {nref=5 nschema=1 ndelete=0}
|
||||
|
||||
do_execsql_test 4.1.3 {
|
||||
REINDEX x1;
|
||||
REINDEX x1a;
|
||||
REINDEX x1b;
|
||||
REINDEX x1c;
|
||||
REINDEX db1.x1a;
|
||||
REINDEX db2.x1b;
|
||||
REINDEX db3.x1c;
|
||||
}
|
||||
|
||||
do_execsql_test 4.1.4 {
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool
|
||||
} {nref=5 nschema=1 ndelete=28}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
db close
|
||||
sqlite3 db test.db -shared-schema 1
|
||||
register_schemapool_module db
|
||||
do_execsql_test 4.2.0 {
|
||||
ATTACH 'test.db1' AS db1;
|
||||
ATTACH 'test.db2' AS db2;
|
||||
ATTACH 'test.db3' AS db3;
|
||||
ATTACH 'test.db4' AS db4;
|
||||
|
||||
SELECT * FROM db1.x1;
|
||||
SELECT * FROM db2.x1;
|
||||
SELECT * FROM db3.x1;
|
||||
SELECT * FROM db4.x1;
|
||||
}
|
||||
|
||||
do_execsql_test 4.2.1 {
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {nref=5 nschema=1 ndelete=0}
|
||||
|
||||
do_execsql_test 4.2.2 {
|
||||
CREATE TEMP TABLE t1(a, b, c);
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {nref=5 nschema=1 ndelete=0}
|
||||
|
||||
do_execsql_test 4.2.3 {
|
||||
CREATE INDEX t1a ON t1(a);
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {nref=5 nschema=1 ndelete=0}
|
||||
|
||||
do_execsql_test 4.2.4 {
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
|
||||
SELECT 1,2,3,4;
|
||||
END;
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {nref=5 nschema=1 ndelete=0}
|
||||
|
||||
do_execsql_test 4.2.5 {
|
||||
DROP TABLE t1;
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {nref=5 nschema=1 ndelete=0}
|
||||
|
||||
do_execsql_test 4.2.6 {
|
||||
CREATE TEMP TRIGGER tr1 AFTER INSERT ON db2.x1 BEGIN
|
||||
SELECT 1,2,3,4;
|
||||
END;
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {nref=5 nschema=1 ndelete=0}
|
||||
|
||||
do_execsql_test 4.2.7 {
|
||||
DROP TRIGGER tr1;
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema, 'ndelete=' || nDelete
|
||||
FROM schemapool;
|
||||
} {nref=5 nschema=1 ndelete=4}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 5.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE t2(a, b);
|
||||
CREATE TABLE t3(a, b);
|
||||
}
|
||||
|
||||
sqlite3 db2 test.db -shared-schema 1
|
||||
register_schemapool_module db2
|
||||
|
||||
do_execsql_test 5.1 {
|
||||
PRAGMA writable_schema = 1;
|
||||
UPDATE sqlite_master SET sql='CREATE TABLE t3 a,b' WHERE name = 't3';
|
||||
}
|
||||
|
||||
do_test 5.2 {
|
||||
catchsql { SELECT * FROM t1 } db2
|
||||
} {1 {malformed database schema (t3) - near "a": syntax error}}
|
||||
|
||||
do_test 5.3 {
|
||||
catchsql { SELECT nref,nschema FROM schemapool } db2
|
||||
} {1 {vtable constructor failed: schemapool}}
|
||||
|
||||
do_execsql_test 5.4 {
|
||||
PRAGMA writable_schema = 1;
|
||||
UPDATE sqlite_master SET sql='CREATE TABLE t3(a,b)' WHERE name = 't3';
|
||||
}
|
||||
|
||||
do_test 5.5 {
|
||||
catchsql { SELECT nref,nschema FROM schemapool } db2
|
||||
} {0 {1 1}}
|
||||
|
||||
db2 close
|
||||
db close
|
||||
do_test 5.6.1 {
|
||||
forcedelete test.db2 test.db2-wal test.db2-journal
|
||||
forcecopy test.db test.db2
|
||||
sqlite3 db test.db
|
||||
sqlite3 db2 test.db -shared-schema 1
|
||||
sqlite3 db3 test.db2 -shared-schema 1
|
||||
register_schemapool_module db
|
||||
} {}
|
||||
|
||||
do_execsql_test -db db2 5.6.2 { SELECT * FROM t1 }
|
||||
do_execsql_test -db db3 5.6.3 { SELECT * FROM t1 }
|
||||
do_execsql_test 5.6.4 {
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema FROM schemapool;
|
||||
CREATE TABLE t4(x);
|
||||
DROP TABLE t4;
|
||||
} {nref=2 nschema=1}
|
||||
do_execsql_test -db db2 5.6.5 { SELECT * FROM t1 }
|
||||
do_execsql_test -db db3 5.6.6 { SELECT * FROM t1 }
|
||||
do_execsql_test 5.6.7 {
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema FROM schemapool;
|
||||
ATTACH 'test.db2' AS db2;
|
||||
CREATE TABLE db2.t4(x);
|
||||
DROP TABLE db2.t4;
|
||||
} {nref=1 nschema=1 nref=1 nschema=1}
|
||||
do_execsql_test -db db2 5.6.8 { SELECT * FROM t1 }
|
||||
do_execsql_test -db db3 5.6.9 { SELECT * FROM t1 }
|
||||
do_execsql_test 5.6.10 {
|
||||
SELECT 'nref=' || nRef, 'nschema=' || nSchema FROM schemapool;
|
||||
} {nref=2 nschema=1}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 6.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE t2(a, b);
|
||||
CREATE TABLE t3(a, b);
|
||||
}
|
||||
|
||||
do_test 6.1 {
|
||||
db close
|
||||
sqlite3 db test.db -shared-schema 1
|
||||
for {set i 1} {$i < 5} {incr i} {
|
||||
set base "test.db$i"
|
||||
set nm "aux$i"
|
||||
forcedelete $base $base-wal $base-journal
|
||||
forcecopy test.db $base
|
||||
execsql "ATTACH '$base' AS $nm"
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test 6.2 {
|
||||
set N1 [lindex [sqlite3_db_status db SCHEMA_USED 0] 1]
|
||||
set N2 [lindex [sqlite3_db_status db SCHEMA_USED 0] 1]
|
||||
expr ($N1==0 && $N2==0)
|
||||
} {1}
|
||||
|
||||
do_test 6.3 {
|
||||
execsql { SELECT * FROM main.t1 }
|
||||
set N1 [lindex [sqlite3_db_status db SCHEMA_USED 0] 1]
|
||||
set N2 [lindex [sqlite3_db_status db SCHEMA_USED 0] 1]
|
||||
expr {$N1>0 && $N2>0 && $N1==$N2}
|
||||
} {1}
|
||||
|
||||
do_test 6.4 {
|
||||
execsql { SELECT * FROM aux1.t1 }
|
||||
set N3 [lindex [sqlite3_db_status db SCHEMA_USED 0] 1]
|
||||
set N4 [lindex [sqlite3_db_status db SCHEMA_USED 0] 1]
|
||||
list $N3 $N4
|
||||
} "#/$N1 $N1/"
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
# 2019 February 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix reuse4
|
||||
|
||||
ifcapable !sharedschema {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
foreach {tn sharedschema} {
|
||||
1 0
|
||||
2 1
|
||||
} {
|
||||
reset_db
|
||||
|
||||
do_execsql_test 1.$tn.0 {
|
||||
CREATE TABLE x1(a, b);
|
||||
CREATE INDEX x1a ON x1(a);
|
||||
CREATE INDEX x1b ON x1(b);
|
||||
CREATE TABLE x2(a, b);
|
||||
}
|
||||
db close
|
||||
|
||||
do_test 1.$tn.1 {
|
||||
for {set i 1} {$i<4} {incr i} {
|
||||
forcedelete test.db$i test.db$i-journal test.db$i-wal
|
||||
forcecopy test.db test.db$i
|
||||
}
|
||||
|
||||
sqlite3 db test.db -shared-schema $sharedschema
|
||||
for {set i 1} {$i<4} {incr i} {
|
||||
execsql " ATTACH 'test.db$i' AS db$i "
|
||||
}
|
||||
} {}
|
||||
|
||||
do_execsql_test 1.$tn.2 {
|
||||
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<10 )
|
||||
INSERT INTO x1 SELECT i, i FROM s;
|
||||
|
||||
INSERT INTO db3.x2 SELECT * FROM x1;
|
||||
INSERT INTO db2.x1 SELECT * FROM db3.x2;
|
||||
CREATE TEMP TRIGGER tr1 AFTER INSERT ON db2.x2 BEGIN
|
||||
INSERT INTO x1 VALUES(new.a, new.b);
|
||||
END;
|
||||
INSERT INTO db2.x2 SELECT * FROM x1 WHERE a%2;
|
||||
DELETE FROM x1 WHERE a<3;
|
||||
INSERT INTO db3.x1 SELECT * FROM db2.x2;
|
||||
|
||||
DETACH db3;
|
||||
ATTACH 'test.db3' AS db3;
|
||||
|
||||
UPDATE db3.x1 SET a=a-10 WHERE b NOT IN (SELECT b FROM db2.x2);
|
||||
|
||||
CREATE TEMP TABLE x1(a, b);
|
||||
INSERT INTO db2.x2 VALUES(50, 60), (60, 70), (80, 90);
|
||||
ALTER TABLE x1 RENAME TO x2;
|
||||
ALTER TABLE x2 ADD COLUMN c;
|
||||
ALTER TABLE x2 RENAME a TO aaa;
|
||||
DELETE FROM x1 WHERE b>8;
|
||||
UPDATE db3.x2 SET b=b*10;
|
||||
|
||||
BEGIN;
|
||||
CREATE TEMP TABLE x5(x);
|
||||
INSERT INTO x5 VALUES(1);
|
||||
ROLLBACK;
|
||||
|
||||
INSERT INTO main.x2 VALUES(123, 456);
|
||||
}
|
||||
|
||||
integrity_check 1.$tn.3
|
||||
|
||||
do_execsql_test 1.$tn.4 {
|
||||
SELECT * FROM main.x1; SELECT 'xxx';
|
||||
SELECT * FROM main.x2; SELECT 'xxx';
|
||||
SELECT * FROM temp.x2; SELECT 'xxx';
|
||||
|
||||
SELECT * FROM db1.x1; SELECT 'xxx';
|
||||
SELECT * FROM db1.x2; SELECT 'xxx';
|
||||
SELECT * FROM db2.x1; SELECT 'xxx';
|
||||
SELECT * FROM db2.x2; SELECT 'xxx';
|
||||
SELECT * FROM db3.x1; SELECT 'xxx';
|
||||
SELECT * FROM db3.x2; SELECT 'xxx';
|
||||
} {
|
||||
3 3 4 4 5 5 6 6 7 7 8 8 3 3 5 5 7 7 xxx
|
||||
123 456 xxx
|
||||
50 60 {} 60 70 {} 80 90 {} xxx
|
||||
xxx
|
||||
xxx
|
||||
1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 xxx
|
||||
1 1 3 3 5 5 7 7 9 9 50 60 60 70 80 90 xxx
|
||||
1 1 3 3 5 5 7 7 9 9 xxx
|
||||
1 10 2 20 3 30 4 40 5 50 6 60 7 70 8 80 9 90 10 100 xxx
|
||||
}
|
||||
|
||||
do_test 1.$tn.5.1 {
|
||||
sqlite3 db2 test.db
|
||||
db2 eval { CREATE TABLE x3(x) }
|
||||
} {}
|
||||
do_execsql_test 1.$tn.5.2 {
|
||||
SELECT * FROM main.x1; SELECT 'xxx';
|
||||
SELECT * FROM main.x2; SELECT 'xxx';
|
||||
SELECT * FROM main.x3; SELECT 'xxx';
|
||||
} {
|
||||
3 3 4 4 5 5 6 6 7 7 8 8 3 3 5 5 7 7 xxx
|
||||
123 456 xxx
|
||||
xxx
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test some PRAGMA statements with shared-schema connections.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE INDEX t1abc ON t1(a, b, c);
|
||||
}
|
||||
|
||||
foreach {tn pragma nSchema nDelete} {
|
||||
1 "PRAGMA synchronous = OFF" 1 0
|
||||
2 "PRAGMA cache_size = 200" 1 0
|
||||
3 "PRAGMA aux2.integrity_check" 1 0
|
||||
4 "PRAGMA integrity_check" 1 5
|
||||
5 "PRAGMA index_info=t1abc" 1 5
|
||||
6 "PRAGMA aux3.index_info=t1abc" 1 0
|
||||
7 "PRAGMA journal_mode" 1 0
|
||||
8 "PRAGMA aux2.wal_checkpoint" 1 0
|
||||
9 "PRAGMA wal_checkpoint" 1 0
|
||||
} {
|
||||
do_test 2.$tn.1 {
|
||||
catch { db close }
|
||||
catch { db2 close }
|
||||
for {set i 1} {$i < 6} {incr i} {
|
||||
forcedelete "test.db$i" "test.db${i}-wal" "test.db${i}-journal"
|
||||
forcecopy test.db test.db$i
|
||||
}
|
||||
sqlite3 db2 test.db -shared-schema 1
|
||||
for {set i 1} {$i < 6} {incr i} {
|
||||
execsql "ATTACH 'test.db$i' AS aux$i" db2
|
||||
}
|
||||
} {}
|
||||
|
||||
sqlite3 db test.db
|
||||
register_schemapool_module db
|
||||
|
||||
do_test 2.$tn.2 {
|
||||
execsql $pragma db2
|
||||
execsql { SELECT 'nschema='||nschema, 'ndelete='||nDelete FROM schemapool }
|
||||
} "nschema=$nSchema ndelete=$nDelete"
|
||||
|
||||
do_test 2.$tn.3 {
|
||||
execsql {
|
||||
SELECT * FROM main.t1,aux1.t1,aux2.t1,aux3.t1,aux4.t1,aux5.t1
|
||||
} db2
|
||||
execsql { SELECT 'nschema=' || nschema, 'nref=' || nref FROM schemapool }
|
||||
} "nschema=6 nref=6"
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
# 2019 February 26
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix reuse5
|
||||
set CLI [test_find_cli]
|
||||
|
||||
ifcapable !sharedschema {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(x, y);
|
||||
CREATE TABLE t2(a, b, c);
|
||||
CREATE INDEX t1x ON t1(x);
|
||||
CREATE INDEX t1y ON t1(y);
|
||||
CREATE VIEW v1 AS SELECT * FROM t2;
|
||||
}
|
||||
|
||||
foreach {tn sql out1 out2} {
|
||||
1 {
|
||||
CREATE TABLE t1(x, y);
|
||||
CREATE TABLE t2(a, b, c);
|
||||
CREATE INDEX t1x ON t1(x);
|
||||
CREATE INDEX t1y ON t1(y);
|
||||
CREATE VIEW v1 AS SELECT * FROM t2;
|
||||
} {
|
||||
test.db2 is compatible
|
||||
} {}
|
||||
|
||||
2 {
|
||||
CREATE TABLE t1(x, y);
|
||||
CREATE TABLE t2(a, b, c);
|
||||
CREATE INDEX t1x ON t1(x);
|
||||
CREATE INDEX t1y ON t1(y);
|
||||
CREATE VIEW v1 AS SELECT * FROM t2;
|
||||
CREATE TABLE x1(x);
|
||||
DROP TABLE x1;
|
||||
} {
|
||||
test.db2 is NOT compatible (schema cookie)
|
||||
} {
|
||||
Fixing test.db2... test.db2 is compatible
|
||||
}
|
||||
|
||||
3 {
|
||||
CREATE TABLE t1(x, y);
|
||||
CREATE TABLE t2(a, b, c);
|
||||
CREATE INDEX t1y ON t1(y);
|
||||
CREATE VIEW v1 AS SELECT * FROM t2;
|
||||
} {
|
||||
test.db2 is NOT compatible (objects)
|
||||
} {}
|
||||
|
||||
4 {
|
||||
CREATE TABLE t1(x, y);
|
||||
CREATE TABLE t2(a, b, c);
|
||||
CREATE INDEX t1x ON t1(X);
|
||||
CREATE INDEX t1y ON t1(y);
|
||||
CREATE VIEW v1 AS SELECT * FROM t2;
|
||||
} {
|
||||
test.db2 is NOT compatible (SQL)
|
||||
} {}
|
||||
|
||||
5 {
|
||||
CREATE TABLE t1(x, y);
|
||||
CREATE TABLE t2(a, b, c);
|
||||
CREATE INDEX t1y ON t1(y);
|
||||
CREATE INDEX t1x ON t1(x);
|
||||
CREATE VIEW v1 AS SELECT * FROM t2;
|
||||
} {
|
||||
test.db2 is NOT compatible (root pages)
|
||||
} {
|
||||
Fixing test.db2... test.db2 is compatible
|
||||
}
|
||||
|
||||
6 {
|
||||
CREATE TABLE t1(x, y);
|
||||
CREATE TABLE t2(a, b, c);
|
||||
CREATE INDEX t1x ON t1(x);
|
||||
CREATE INDEX t1y ON t1(y);
|
||||
CREATE VIEW v1 AS SELECT * FROM t2;
|
||||
DROP INDEX t1x;
|
||||
CREATE INDEX t1x ON t1(x);
|
||||
} {
|
||||
test.db2 is NOT compatible (order of sqlite_master rows)
|
||||
} {
|
||||
Fixing test.db2... test.db2 is compatible
|
||||
}
|
||||
|
||||
} {
|
||||
forcedelete test.db2
|
||||
sqlite3 db2 test.db2
|
||||
db2 eval $sql
|
||||
db2 close
|
||||
|
||||
if {$out2==""} {set out2 $out1}
|
||||
|
||||
do_test 1.$tn.1 {
|
||||
catchcmd test.db ".shared-schema check test.db2"
|
||||
} [list 0 [string trim $out1]]
|
||||
|
||||
do_test 1.$tn.2 {
|
||||
catchcmd test.db ".shared-schema fix test.db2"
|
||||
} [list 0 [string trim $out2]]
|
||||
|
||||
do_test 1.$tn.3 {
|
||||
catchcmd test.db2 "PRAGMA integrity_check"
|
||||
} [list 0 ok]
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
# 2019 February 26
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix reuse6
|
||||
|
||||
ifcapable !sharedschema {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(x, y);
|
||||
CREATE TABLE t2(a, b, c);
|
||||
CREATE INDEX t1x ON t1(x);
|
||||
CREATE INDEX t1y ON t1(y);
|
||||
CREATE VIEW v1 AS SELECT * FROM t2;
|
||||
|
||||
INSERT INTO t1 VALUES(1, 2), (3, 4), (5, 6);
|
||||
INSERT INTO t2 VALUES('a', 'b', 'c'), ('d', 'e', 'f'), ('g', 'h', 'i');
|
||||
|
||||
ATTACH 'test.db2' AS aux;
|
||||
CREATE TABLE t3(i, ii);
|
||||
INSERT INTO t3 VALUES(10, 20);
|
||||
}
|
||||
|
||||
sqlite3 db1 test.db -shared-schema 1
|
||||
sqlite3 db2 test.db -shared-schema 1
|
||||
|
||||
do_execsql_test -db db1 1.1 {
|
||||
ATTACH 'test.db2' AS aux;
|
||||
}
|
||||
|
||||
do_test 1.2 {
|
||||
execsql {SELECT * FROM t3} db1
|
||||
} {10 20}
|
||||
|
||||
do_execsql_test -db db2 1.3 {
|
||||
ATTACH 'test.db2' AS aux;
|
||||
}
|
||||
|
||||
do_test 1.3 {
|
||||
execsql {SELECT * FROM t3} db1
|
||||
} {10 20}
|
||||
|
||||
do_execsql_test -db db2 1.5 {
|
||||
SELECT * FROM t3;
|
||||
} {10 20}
|
||||
|
||||
do_test 1.6 {
|
||||
execsql {SELECT * FROM t3} db1
|
||||
} {10 20}
|
||||
|
||||
db1 close
|
||||
db2 close
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
forcedelete test.db2
|
||||
forcedelete test.db3
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE t1(x, y);
|
||||
ATTACH 'test.db2' AS aux2;
|
||||
CREATE TABLE aux2.t2(x, y);
|
||||
ATTACH 'test.db3' AS aux3;
|
||||
CREATE TABLE aux3.t3(x, y);
|
||||
}
|
||||
|
||||
sqlite3 db1 test.db -shared-schema 1
|
||||
do_execsql_test -db db1 2.1 {
|
||||
ATTACH 'test.db2' AS aux2;
|
||||
ATTACH 'test.db3' AS aux3;
|
||||
}
|
||||
|
||||
do_test 2.2.1 {
|
||||
catchsql { SELECT * FROM aux2.nosuchtable } db1
|
||||
} {1 {no such table: aux2.nosuchtable}}
|
||||
do_test 2.2.2 {
|
||||
sqlite3_errcode db1
|
||||
} {SQLITE_ERROR}
|
||||
db1 close
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
forcedelete test.db2
|
||||
ifcapable fts5 {
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(a, b);
|
||||
ATTACH 'test.db2' AS aux;
|
||||
CREATE TABLE aux.t1(x, y, z);
|
||||
}
|
||||
|
||||
sqlite3 db1 test.db -shared-schema 1
|
||||
do_execsql_test -db db1 3.1 {
|
||||
ATTACH 'test.db2' AS aux;
|
||||
}
|
||||
|
||||
do_execsql_test -db db1 3.2 {
|
||||
SELECT * FROM main.ft, aux.t1;
|
||||
}
|
||||
db1 close
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
forcedelete test.db2
|
||||
ifcapable fts5 {
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(a, b);
|
||||
}
|
||||
forcecopy test.db test.db2
|
||||
|
||||
sqlite3 db1 test.db -shared-schema 1
|
||||
do_execsql_test -db db1 4.1 {
|
||||
ATTACH 'test.db2' AS aux;
|
||||
SELECT * FROM main.ft;
|
||||
SELECT * FROM aux.ft;
|
||||
}
|
||||
|
||||
do_execsql_test -db db1 4.2 {
|
||||
SELECT * FROM main.ft, aux.ft
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# 2019 February 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix reusefault
|
||||
|
||||
ifcapable !sharedschema {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
PRAGMA cache_size = 10;
|
||||
CREATE TABLE t1(a UNIQUE, b UNIQUE);
|
||||
INSERT INTO t1 VALUES(1, 2), (3, 4);
|
||||
}
|
||||
faultsim_save_and_close
|
||||
|
||||
do_faultsim_test 1.1 -prep {
|
||||
faultsim_restore
|
||||
sqlite3 db test.db -shared-schema 1
|
||||
} -body {
|
||||
execsql { SELECT * FROM t1 }
|
||||
} -test {
|
||||
faultsim_test_result {0 {1 2 3 4}}
|
||||
}
|
||||
|
||||
do_faultsim_test 1.2 -prep {
|
||||
faultsim_restore
|
||||
sqlite3 db test.db -shared-schema 1
|
||||
execsql { SELECT * FROM t1 }
|
||||
sqlite3 db2 test.db
|
||||
db2 eval {CREATE TABLE a(a)}
|
||||
db2 close
|
||||
} -body {
|
||||
execsql { SELECT * FROM t1 }
|
||||
} -test {
|
||||
faultsim_test_result {0 {1 2 3 4}}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -26,6 +26,9 @@ set testprefix tcl
|
||||
# Check the error messages generated by tclsqlite
|
||||
#
|
||||
set r "sqlite_orig HANDLE ?FILENAME? ?-vfs VFSNAME? ?-readonly BOOLEAN? ?-create BOOLEAN? ?-nofollow BOOLEAN? ?-nomutex BOOLEAN? ?-fullmutex BOOLEAN? ?-uri BOOLEAN?"
|
||||
ifcapable sharedschema {
|
||||
append r " ?-shared-schema BOOLEAN?"
|
||||
}
|
||||
if {[sqlite3 -has-codec]} {
|
||||
append r " ?-key CODECKEY?"
|
||||
}
|
||||
|
||||
+24
-19
@@ -38,7 +38,7 @@
|
||||
#define SEL(e) ((e)->iLine = ((e)->rc ? (e)->iLine : __LINE__))
|
||||
|
||||
/* Database functions */
|
||||
#define opendb(w,x,y,z) (SEL(w), opendb_x(w,x,y,z))
|
||||
#define opendb(w,x,y,z,f) (SEL(w), opendb_x(w,x,y,z,f))
|
||||
#define closedb(y,z) (SEL(y), closedb_x(y,z))
|
||||
|
||||
/* Functions to execute SQL */
|
||||
@@ -520,11 +520,14 @@ static void opendb_x(
|
||||
Error *pErr, /* IN/OUT: Error code */
|
||||
Sqlite *pDb, /* OUT: Database handle */
|
||||
const char *zFile, /* Database file name */
|
||||
int bDelete /* True to delete db file before opening */
|
||||
int bDelete, /* True to delete db file before opening */
|
||||
int flags
|
||||
){
|
||||
if( pErr->rc==SQLITE_OK ){
|
||||
int rc;
|
||||
int flags = SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_URI;
|
||||
if( flags==0 ){
|
||||
flags = SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_URI;
|
||||
}
|
||||
if( bDelete ) unlink(zFile);
|
||||
rc = sqlite3_open_v2(zFile, &pDb->db, flags, 0);
|
||||
if( rc ){
|
||||
@@ -926,7 +929,7 @@ static char *walthread1_thread(int iTid, void *pArg){
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
int nIter = 0; /* Iterations so far */
|
||||
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
opendb(&err, &db, "test.db", 0, 0);
|
||||
while( !timetostop(&err) ){
|
||||
const char *azSql[] = {
|
||||
"SELECT md5sum(x) FROM t1 WHERE rowid != (SELECT max(rowid) FROM t1)",
|
||||
@@ -965,7 +968,7 @@ static char *walthread1_ckpt_thread(int iTid, void *pArg){
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
int nCkpt = 0; /* Checkpoints so far */
|
||||
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
opendb(&err, &db, "test.db", 0, 0);
|
||||
while( !timetostop(&err) ){
|
||||
usleep(500*1000);
|
||||
execsql(&err, &db, "PRAGMA wal_checkpoint");
|
||||
@@ -984,7 +987,7 @@ static void walthread1(int nMs){
|
||||
Threadset threads = {0}; /* Test threads */
|
||||
int i; /* Iterator variable */
|
||||
|
||||
opendb(&err, &db, "test.db", 1);
|
||||
opendb(&err, &db, "test.db", 1, 0);
|
||||
sql_script(&err, &db,
|
||||
"PRAGMA journal_mode = WAL;"
|
||||
"CREATE TABLE t1(x PRIMARY KEY);"
|
||||
@@ -1017,7 +1020,7 @@ static char *walthread2_thread(int iTid, void *pArg){
|
||||
int journal_exists = 0;
|
||||
int wal_exists = 0;
|
||||
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
opendb(&err, &db, "test.db", 0, 0);
|
||||
|
||||
sql_script(&err, &db, zJournal);
|
||||
clear_error(&err, SQLITE_BUSY);
|
||||
@@ -1047,7 +1050,7 @@ static void walthread2(int nMs){
|
||||
Sqlite db = {0};
|
||||
Threadset threads = {0};
|
||||
|
||||
opendb(&err, &db, "test.db", 1);
|
||||
opendb(&err, &db, "test.db", 1, 0);
|
||||
sql_script(&err, &db, "CREATE TABLE t1(x INTEGER PRIMARY KEY, y UNIQUE)");
|
||||
closedb(&err, &db);
|
||||
|
||||
@@ -1067,7 +1070,7 @@ static char *walthread3_thread(int iTid, void *pArg){
|
||||
i64 iNextWrite; /* Next value this thread will write */
|
||||
int iArg = PTR2INT(pArg);
|
||||
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
opendb(&err, &db, "test.db", 0, 0);
|
||||
sql_script(&err, &db, "PRAGMA wal_autocheckpoint = 10");
|
||||
|
||||
iNextWrite = iArg+1;
|
||||
@@ -1104,7 +1107,7 @@ static void walthread3(int nMs){
|
||||
Threadset threads = {0};
|
||||
int i;
|
||||
|
||||
opendb(&err, &db, "test.db", 1);
|
||||
opendb(&err, &db, "test.db", 1, 0);
|
||||
sql_script(&err, &db,
|
||||
"PRAGMA journal_mode = WAL;"
|
||||
"CREATE TABLE t1(cnt PRIMARY KEY, sum1, sum2);"
|
||||
@@ -1127,7 +1130,7 @@ static char *walthread4_reader_thread(int iTid, void *pArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
opendb(&err, &db, "test.db", 0, 0);
|
||||
while( !timetostop(&err) ){
|
||||
integrity_check(&err, &db);
|
||||
}
|
||||
@@ -1142,7 +1145,7 @@ static char *walthread4_writer_thread(int iTid, void *pArg){
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
i64 iRow = 1;
|
||||
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
opendb(&err, &db, "test.db", 0, 0);
|
||||
sql_script(&err, &db, "PRAGMA wal_autocheckpoint = 15;");
|
||||
while( !timetostop(&err) ){
|
||||
execsql_i64(
|
||||
@@ -1162,7 +1165,7 @@ static void walthread4(int nMs){
|
||||
Sqlite db = {0};
|
||||
Threadset threads = {0};
|
||||
|
||||
opendb(&err, &db, "test.db", 1);
|
||||
opendb(&err, &db, "test.db", 1, 0);
|
||||
sql_script(&err, &db,
|
||||
"PRAGMA journal_mode = WAL;"
|
||||
"CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE);"
|
||||
@@ -1182,7 +1185,7 @@ static char *walthread5_thread(int iTid, void *pArg){
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
i64 nRow;
|
||||
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
opendb(&err, &db, "test.db", 0, 0);
|
||||
nRow = execsql_i64(&err, &db, "SELECT count(*) FROM t1");
|
||||
closedb(&err, &db);
|
||||
|
||||
@@ -1195,7 +1198,7 @@ static void walthread5(int nMs){
|
||||
Sqlite db = {0};
|
||||
Threadset threads = {0};
|
||||
|
||||
opendb(&err, &db, "test.db", 1);
|
||||
opendb(&err, &db, "test.db", 1, 0);
|
||||
sql_script(&err, &db,
|
||||
"PRAGMA wal_autocheckpoint = 0;"
|
||||
"PRAGMA page_size = 1024;"
|
||||
@@ -1286,7 +1289,7 @@ static void cgt_pager_1(int nMs){
|
||||
Error err = {0};
|
||||
Sqlite db = {0};
|
||||
|
||||
opendb(&err, &db, "test.db", 1);
|
||||
opendb(&err, &db, "test.db", 1, 0);
|
||||
sql_script(&err, &db,
|
||||
"PRAGMA cache_size = 2000;"
|
||||
"PRAGMA page_size = 1024;"
|
||||
@@ -1315,7 +1318,7 @@ static char *dynamic_triggers_1(int iTid, void *pArg){
|
||||
int nDrop = 0;
|
||||
int nCreate = 0;
|
||||
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
opendb(&err, &db, "test.db", 0, 0);
|
||||
while( !timetostop(&err) ){
|
||||
int i;
|
||||
|
||||
@@ -1368,7 +1371,7 @@ static char *dynamic_triggers_2(int iTid, void *pArg){
|
||||
int nInsert = 0;
|
||||
int nDelete = 0;
|
||||
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
opendb(&err, &db, "test.db", 0, 0);
|
||||
while( !timetostop(&err) ){
|
||||
do {
|
||||
iVal = (iVal+1)%100;
|
||||
@@ -1393,7 +1396,7 @@ static void dynamic_triggers(int nMs){
|
||||
Sqlite db = {0};
|
||||
Threadset threads = {0};
|
||||
|
||||
opendb(&err, &db, "test.db", 1);
|
||||
opendb(&err, &db, "test.db", 1, 0);
|
||||
sql_script(&err, &db,
|
||||
"PRAGMA page_size = 1024;"
|
||||
"PRAGMA journal_mode = WAL;"
|
||||
@@ -1433,6 +1436,7 @@ static void dynamic_triggers(int nMs){
|
||||
#include "tt3_lookaside1.c"
|
||||
#include "tt3_vacuum.c"
|
||||
#include "tt3_stress.c"
|
||||
#include "tt3_reuseschema.c"
|
||||
|
||||
int main(int argc, char **argv){
|
||||
struct ThreadTest {
|
||||
@@ -1457,6 +1461,7 @@ int main(int argc, char **argv){
|
||||
{ vacuum1, "vacuum1", 10000 },
|
||||
{ stress1, "stress1", 10000 },
|
||||
{ stress2, "stress2", 60000 },
|
||||
{ reuse_schema_1, "reuse_schema_1", 20000 },
|
||||
};
|
||||
static char *substArgv[] = { 0, "*", 0 };
|
||||
int i, iArg;
|
||||
|
||||
@@ -70,7 +70,7 @@ static char *checkpoint_starvation_reader(int iTid, void *pArg){
|
||||
Error err = {0};
|
||||
Sqlite db = {0};
|
||||
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
opendb(&err, &db, "test.db", 0, 0);
|
||||
while( !timetostop(&err) ){
|
||||
i64 iCount1, iCount2;
|
||||
sql_script(&err, &db, "BEGIN");
|
||||
@@ -96,7 +96,7 @@ static void checkpoint_starvation_main(int nMs, CheckpointStarvationCtx *p){
|
||||
int nInsert = 0;
|
||||
int i;
|
||||
|
||||
opendb(&err, &db, "test.db", 1);
|
||||
opendb(&err, &db, "test.db", 1, 0);
|
||||
sql_script(&err, &db,
|
||||
"PRAGMA page_size = 1024;"
|
||||
"PRAGMA journal_mode = WAL;"
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@ static char *create_drop_index_thread(int iTid, void *pArg){
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
|
||||
while( !timetostop(&err) ){
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
opendb(&err, &db, "test.db", 0, 0);
|
||||
|
||||
sql_script(&err, &db,
|
||||
"DROP INDEX IF EXISTS i1;"
|
||||
@@ -51,7 +51,7 @@ static void create_drop_index_1(int nMs){
|
||||
Sqlite db = {0};
|
||||
Threadset threads = {0};
|
||||
|
||||
opendb(&err, &db, "test.db", 1);
|
||||
opendb(&err, &db, "test.db", 1, 0);
|
||||
sql_script(&err, &db,
|
||||
"CREATE TABLE t11(a, b, c, d);"
|
||||
"WITH data(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM data WHERE x<100) "
|
||||
|
||||
@@ -22,7 +22,7 @@ static char *lookaside1_thread_reader(int iTid, void *pArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
opendb(&err, &db, "test.db", 0, 0);
|
||||
|
||||
while( !timetostop(&err) ){
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
@@ -47,7 +47,7 @@ static char *lookaside1_thread_writer(int iTid, void *pArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
opendb(&err, &db, "test.db", 0, 0);
|
||||
|
||||
do{
|
||||
sql_script(&err, &db,
|
||||
@@ -68,7 +68,7 @@ static void lookaside1(int nMs){
|
||||
Sqlite db = {0};
|
||||
Threadset threads = {0};
|
||||
|
||||
opendb(&err, &db, "test.db", 1);
|
||||
opendb(&err, &db, "test.db", 1, 0);
|
||||
sql_script(&err, &db,
|
||||
"CREATE TABLE t1(x PRIMARY KEY) WITHOUT ROWID;"
|
||||
"WITH data(x,y) AS ("
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
** 2014 December 9
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** reuse_schema_1
|
||||
*/
|
||||
|
||||
|
||||
static char *reuse_schema_thread(int iTid, void *pArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
int iRep = 0;
|
||||
|
||||
while( !timetostop(&err) ){
|
||||
int f = SQLITE_OPEN_READWRITE|SQLITE_OPEN_SHARED_SCHEMA;
|
||||
opendb(&err, &db, "test.db", 0, f);
|
||||
|
||||
execsql_i64(&err, &db, "SELECT count(*) FROM t1");
|
||||
sql_script(&err, &db, "ATTACH 'test.db2' AS aux");
|
||||
execsql_i64(&err, &db, "SELECT count(*) FROM t1");
|
||||
|
||||
closedb(&err, &db);
|
||||
iRep++;
|
||||
}
|
||||
|
||||
print_and_free_err(&err);
|
||||
return sqlite3_mprintf("%d", iRep);
|
||||
}
|
||||
|
||||
static void reuse_schema_1(int nMs){
|
||||
Error err = {0};
|
||||
Sqlite db = {0};
|
||||
Threadset threads = {0};
|
||||
|
||||
opendb(&err, &db, "test.db", 1, 0);
|
||||
sql_script(&err, &db,
|
||||
"CREATE TABLE t1(a, b, c, d);"
|
||||
"WITH data(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM data WHERE x<100) "
|
||||
"INSERT INTO t1 SELECT x,x,x,x FROM data;"
|
||||
);
|
||||
closedb(&err, &db);
|
||||
opendb(&err, &db, "test.db2", 1, 0);
|
||||
sql_script(&err, &db,
|
||||
#ifdef SQLITE_ENABLE_FTS5
|
||||
"CREATE VIRTUAL TABLE t2 USING fts5(a, b, c, d);"
|
||||
#else
|
||||
"CREATE TABLE t2(a, b, c, d);"
|
||||
#endif
|
||||
"WITH data(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM data WHERE x<100) "
|
||||
"INSERT INTO t2 SELECT x*2,x*2,x*2,x*2 FROM data;"
|
||||
);
|
||||
closedb(&err, &db);
|
||||
|
||||
setstoptime(&err, nMs);
|
||||
|
||||
launch_thread(&err, &threads, reuse_schema_thread, 0);
|
||||
launch_thread(&err, &threads, reuse_schema_thread, 0);
|
||||
launch_thread(&err, &threads, reuse_schema_thread, 0);
|
||||
launch_thread(&err, &threads, reuse_schema_thread, 0);
|
||||
launch_thread(&err, &threads, reuse_schema_thread, 0);
|
||||
|
||||
join_all_threads(&err, &threads);
|
||||
sqlite3_enable_shared_cache(0);
|
||||
print_and_free_err(&err);
|
||||
}
|
||||
+10
-10
@@ -21,7 +21,7 @@ static char *stress_thread_1(int iTid, void *pArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
opendb(&err, &db, "test.db", 0, 0);
|
||||
while( !timetostop(&err) ){
|
||||
sql_script(&err, &db, "CREATE TABLE IF NOT EXISTS t1(a PRIMARY KEY, b)");
|
||||
clear_error(&err, SQLITE_LOCKED);
|
||||
@@ -40,7 +40,7 @@ static char *stress_thread_2(int iTid, void *pArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
while( !timetostop(&err) ){
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
opendb(&err, &db, "test.db", 0, 0);
|
||||
sql_script(&err, &db, "SELECT * FROM sqlite_master;");
|
||||
clear_error(&err, SQLITE_LOCKED);
|
||||
closedb(&err, &db);
|
||||
@@ -59,7 +59,7 @@ static char *stress_thread_3(int iTid, void *pArg){
|
||||
int i1 = 0;
|
||||
int i2 = 0;
|
||||
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
opendb(&err, &db, "test.db", 0, 0);
|
||||
while( !timetostop(&err) ){
|
||||
sql_script(&err, &db, "SELECT * FROM t1 ORDER BY a;");
|
||||
i1++;
|
||||
@@ -82,11 +82,11 @@ static char *stress_thread_4(int iTid, void *pArg){
|
||||
int i2 = 0;
|
||||
int iArg = PTR2INT(pArg);
|
||||
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
opendb(&err, &db, "test.db", 0, 0);
|
||||
while( !timetostop(&err) ){
|
||||
if( iArg ){
|
||||
closedb(&err, &db);
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
opendb(&err, &db, "test.db", 0, 0);
|
||||
}
|
||||
sql_script(&err, &db,
|
||||
"WITH loop(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM loop LIMIT 200) "
|
||||
@@ -113,12 +113,12 @@ static char *stress_thread_5(int iTid, void *pArg){
|
||||
int i1 = 0;
|
||||
int i2 = 0;
|
||||
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
opendb(&err, &db, "test.db", 0, 0);
|
||||
while( !timetostop(&err) ){
|
||||
i64 i = (i1 % 4);
|
||||
if( iArg ){
|
||||
closedb(&err, &db);
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
opendb(&err, &db, "test.db", 0, 0);
|
||||
}
|
||||
execsql(&err, &db, "DELETE FROM t1 WHERE (rowid % 4)==:i", &i);
|
||||
i1++;
|
||||
@@ -265,7 +265,7 @@ static char *stress2_workload19(int iTid, void *pArg){
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
const char *zDb = (const char*)pArg;
|
||||
while( !timetostop(&err) ){
|
||||
opendb(&err, &db, zDb, 0);
|
||||
opendb(&err, &db, zDb, 0, 0);
|
||||
sql_script(&err, &db, "SELECT * FROM sqlite_master;");
|
||||
clear_error(&err, SQLITE_LOCKED);
|
||||
closedb(&err, &db);
|
||||
@@ -290,7 +290,7 @@ static char *stress2_thread_wrapper(int iTid, void *pArg){
|
||||
|
||||
while( !timetostop(&err) ){
|
||||
int cnt;
|
||||
opendb(&err, &db, pCtx->zDb, 0);
|
||||
opendb(&err, &db, pCtx->zDb, 0, 0);
|
||||
for(cnt=0; err.rc==SQLITE_OK && cnt<STRESS2_TABCNT; cnt++){
|
||||
pCtx->xProc(&err, &db, i1);
|
||||
i2 += (err.rc==SQLITE_OK);
|
||||
@@ -342,7 +342,7 @@ static void stress2(int nMs){
|
||||
Threadset threads = {0};
|
||||
|
||||
/* To make sure the db file is empty before commencing */
|
||||
opendb(&err, &db, zDb, 1);
|
||||
opendb(&err, &db, zDb, 1, 0);
|
||||
sql_script(&err, &db,
|
||||
"CREATE TABLE IF NOT EXISTS t0(x PRIMARY KEY, y, z);"
|
||||
"CREATE INDEX IF NOT EXISTS i0 ON t0(y);"
|
||||
|
||||
+3
-3
@@ -23,7 +23,7 @@
|
||||
static char *vacuum1_thread_writer(int iTid, void *pArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
opendb(&err, &db, "test.db", 0, 0);
|
||||
i64 i = 0;
|
||||
|
||||
while( !timetostop(&err) ){
|
||||
@@ -52,7 +52,7 @@ static char *vacuum1_thread_writer(int iTid, void *pArg){
|
||||
static char *vacuum1_thread_vacuumer(int iTid, void *pArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
opendb(&err, &db, "test.db", 0, 0);
|
||||
|
||||
do{
|
||||
sql_script(&err, &db, "VACUUM");
|
||||
@@ -69,7 +69,7 @@ static void vacuum1(int nMs){
|
||||
Sqlite db = {0};
|
||||
Threadset threads = {0};
|
||||
|
||||
opendb(&err, &db, "test.db", 1);
|
||||
opendb(&err, &db, "test.db", 1, 0);
|
||||
sql_script(&err, &db,
|
||||
"CREATE TABLE t1(x PRIMARY KEY, y BLOB);"
|
||||
"CREATE INDEX i1 ON t1(y);"
|
||||
|
||||
+24
-18
@@ -12,7 +12,7 @@
|
||||
|
||||
# Flag meanings:
|
||||
set flagMeaning(NeedSchema) {Force schema load before running}
|
||||
set flagMeaning(ReadOnly) {Read-only HEADER_VALUE}
|
||||
set flagMeaning(OneSchema) {Only a single schema required}
|
||||
set flagMeaning(Result0) {Acts as query when no argument}
|
||||
set flagMeaning(Result1) {Acts as query when has one argument}
|
||||
set flagMeaning(SchemaReq) {Schema required - "main" is default}
|
||||
@@ -150,7 +150,7 @@ set pragma_def {
|
||||
ARG: SQLITE_CellSizeCk
|
||||
|
||||
NAME: default_cache_size
|
||||
FLAG: NeedSchema Result0 SchemaReq NoColumns1
|
||||
FLAG: NeedSchema Result0 SchemaReq NoColumns1 OneSchema
|
||||
COLS: cache_size
|
||||
IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
|
||||
|
||||
@@ -163,12 +163,12 @@ set pragma_def {
|
||||
IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
|
||||
|
||||
NAME: page_count
|
||||
FLAG: NeedSchema Result0 SchemaReq
|
||||
FLAG: NeedSchema Result0 SchemaReq OneSchema
|
||||
IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
|
||||
|
||||
NAME: max_page_count
|
||||
TYPE: PAGE_COUNT
|
||||
FLAG: NeedSchema Result0 SchemaReq
|
||||
FLAG: NeedSchema Result0 SchemaReq OneSchema
|
||||
IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
|
||||
|
||||
NAME: locking_mode
|
||||
@@ -176,7 +176,7 @@ set pragma_def {
|
||||
IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
|
||||
|
||||
NAME: journal_mode
|
||||
FLAG: NeedSchema Result0 SchemaReq
|
||||
FLAG: NeedSchema Result0 SchemaReq OneSchema
|
||||
IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
|
||||
|
||||
NAME: journal_size_limit
|
||||
@@ -184,18 +184,18 @@ set pragma_def {
|
||||
IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
|
||||
|
||||
NAME: cache_size
|
||||
FLAG: NeedSchema Result0 SchemaReq NoColumns1
|
||||
FLAG: NeedSchema Result0 SchemaReq NoColumns1 OneSchema
|
||||
IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
|
||||
|
||||
NAME: mmap_size
|
||||
IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
|
||||
|
||||
NAME: auto_vacuum
|
||||
FLAG: NeedSchema Result0 SchemaReq NoColumns1
|
||||
FLAG: NeedSchema Result0 SchemaReq NoColumns1 OneSchema
|
||||
IF: !defined(SQLITE_OMIT_AUTOVACUUM)
|
||||
|
||||
NAME: incremental_vacuum
|
||||
FLAG: NeedSchema NoColumns
|
||||
FLAG: NeedSchema NoColumns OneSchema
|
||||
IF: !defined(SQLITE_OMIT_AUTOVACUUM)
|
||||
|
||||
NAME: temp_store
|
||||
@@ -215,7 +215,7 @@ set pragma_def {
|
||||
IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) && SQLITE_ENABLE_LOCKING_STYLE
|
||||
|
||||
NAME: synchronous
|
||||
FLAG: NeedSchema Result0 SchemaReq NoColumns1
|
||||
FLAG: NeedSchema Result0 SchemaReq NoColumns1 OneSchema
|
||||
IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
|
||||
|
||||
NAME: table_info
|
||||
@@ -232,7 +232,7 @@ set pragma_def {
|
||||
IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
|
||||
|
||||
NAME: stats
|
||||
FLAG: NeedSchema Result0 SchemaReq
|
||||
FLAG: NeedSchema Result0 SchemaReq OneSchema
|
||||
COLS: tbl idx wdth hght flgs
|
||||
IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && defined(SQLITE_DEBUG)
|
||||
|
||||
@@ -256,7 +256,7 @@ set pragma_def {
|
||||
IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
|
||||
|
||||
NAME: database_list
|
||||
FLAG: NeedSchema Result0
|
||||
FLAG: NeedSchema Result0 OneSchema
|
||||
COLS: seq name file
|
||||
IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
|
||||
|
||||
@@ -284,12 +284,12 @@ set pragma_def {
|
||||
IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
|
||||
|
||||
NAME: foreign_key_list
|
||||
FLAG: NeedSchema Result1 SchemaOpt
|
||||
FLAG: NeedSchema Result1 SchemaOpt OneSchema
|
||||
COLS: id seq table from to on_update on_delete match
|
||||
IF: !defined(SQLITE_OMIT_FOREIGN_KEY)
|
||||
|
||||
NAME: foreign_key_check
|
||||
FLAG: NeedSchema Result0
|
||||
FLAG: NeedSchema Result0 OneSchema
|
||||
COLS: table rowid parent fkid
|
||||
IF: !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
|
||||
|
||||
@@ -330,14 +330,14 @@ set pragma_def {
|
||||
|
||||
NAME: data_version
|
||||
TYPE: HEADER_VALUE
|
||||
ARG: BTREE_DATA_VERSION
|
||||
FLAG: ReadOnly Result0
|
||||
ARG: BTREE_DATA_VERSION|PRAGMA_HEADER_VALUE_READONLY
|
||||
FLAG: Result0
|
||||
IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
|
||||
|
||||
NAME: freelist_count
|
||||
TYPE: HEADER_VALUE
|
||||
ARG: BTREE_FREE_PAGE_COUNT
|
||||
FLAG: ReadOnly Result0
|
||||
ARG: BTREE_FREE_PAGE_COUNT|PRAGMA_HEADER_VALUE_READONLY
|
||||
FLAG: Result0
|
||||
IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
|
||||
|
||||
NAME: application_id
|
||||
@@ -351,7 +351,7 @@ set pragma_def {
|
||||
IF: !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS)
|
||||
|
||||
NAME: wal_checkpoint
|
||||
FLAG: NeedSchema
|
||||
FLAG: NeedSchema OneSchema
|
||||
COLS: busy log checkpointed
|
||||
IF: !defined(SQLITE_OMIT_WAL)
|
||||
|
||||
@@ -537,6 +537,12 @@ foreach f [lsort [array names allflags]] {
|
||||
set fv [expr {$fv*2}]
|
||||
}
|
||||
|
||||
puts $fd "\n/* For PragTyp_HEADER_VALUE pragmas the Pragma.iArg value is set"
|
||||
puts $fd "** to the index of the header field to access (always 10 or less)."
|
||||
puts $fd "** Ored with HEADER_VALUE_READONLY if the field is read only. */"
|
||||
puts $fd "#define PRAGMA_HEADER_VALUE_READONLY 0x0100"
|
||||
puts $fd "#define PRAGMA_HEADER_VALUE_MASK 0x00FF\n"
|
||||
|
||||
# Sort the column lists so that longer column lists occur first
|
||||
#
|
||||
proc colscmp {a b} {
|
||||
|
||||
Reference in New Issue
Block a user