Compare commits

..

50 Commits

Author SHA1 Message Date
dan 5f0c6bc3a5 Fix an error in README-server-edition.html.
FossilOrigin-Name: 754ad35cd26da361e2ed736b0e400497714a0db9b7fd05fd24e7803b6f478263
2018-03-31 18:43:20 +00:00
dan bddcb53614 Update and add further detail to README-server-edition.html.
FossilOrigin-Name: 337a0b67e30f1030fdc59f712e5914f4801b0e9e4ae19a1e82c10b73eb3f4773
2018-03-30 20:42:25 +00:00
dan fd971c8b83 Update this branch with latest trunk changes.
FossilOrigin-Name: df52e89fff54dbb650cd1fb2b7afe0467acea96a0056728ef48e0c3fea40eeb2
2018-03-28 15:41:57 +00:00
dan 332faddd99 Add a Tcl script to run the performance tests described in
README-server-edition.html.

FossilOrigin-Name: 1b3df8ffc551df0e4d8bcb633e1549ba769d8866cfcffea4cb62949ecf5c4c99
2017-08-29 17:52:14 +00:00
dan 7c4805fa5c Ensure that write-locks on pages are dropped at the end of each
write transaction, even if there is still a read transaction open.

FossilOrigin-Name: 2dd36ade9ea948de27b217b83cbf704e071e6ea5dddb8566165a645143b0f846
2017-08-19 15:50:45 +00:00
dan 852841cc14 Add tests to this branch.
FossilOrigin-Name: abb6e076c851c0b10f62c02d0d7e54f24d86c75f01036dddbc0a8a7dfc627a0d
2017-08-18 18:55:22 +00:00
dan 528e4e4bbf Do not search for locks to clear when connecting to a db in multi-process mode
unless it looks like the previous user of the client-id crashed.

FossilOrigin-Name: 66fb9e1cb479f1e764f1606f041bd97fd3bd428093832c000ee36b643377e9e2
2017-08-18 16:04:40 +00:00
dan 335ce3575f Add support for crash recovery in multi-process mode. And add test cases for
the same.

FossilOrigin-Name: a8115f95e80cc90c095fdd0a151da51f4d3ee427defdc34780e977585a68776d
2017-08-17 19:32:02 +00:00
dan 4c454ff90b Update this branch with the latest changes from trunk.
FossilOrigin-Name: 380a7b7a458b212b0463c8e128c289cc70f2fdb9b9a63b91fc7542c120eb9c10
2017-08-16 17:06:42 +00:00
dan 7ba5f0e768 Enhance this branch to support page-level-locking (without MVCC) for
multi-process deployments.

FossilOrigin-Name: 04e0cb571dbed00e269a890a755e252d7e8204d6d2ed5a7cfdb3d78d990a2876
2017-08-16 16:52:14 +00:00
dan e278a9bacd Remove code for PLL support in wal mode from this branch.
FossilOrigin-Name: 8e1b28ed3e83eba0b2113316a40ed1165e0e051220b68863cb70734c95a82c2a
2017-08-14 07:16:18 +00:00
dan b585dbf8db Update this branch with latest trunk changes.
FossilOrigin-Name: bc2498d60fa22b587ad463ec697abe82d1c87abb7fd0d2cb60cc7316cfd7cec7
2017-08-14 06:55:22 +00:00
dan cfcd9d34b9 Update this branch with latest trunk changes.
FossilOrigin-Name: 17bc7ded07fe2f1f47857bba646eb297e53f69fb1bafd68556d60700ce008750
2017-08-07 14:15:18 +00:00
dan 44bbccc05a Fix a bug on this branch involving mmap mode and readonly transactions.
FossilOrigin-Name: 355c594e821bb5ad782faf0e8d512305f4e1d7fd60949887a8270beed67d03e3
2017-08-07 14:06:01 +00:00
dan 0a1c5dc414 Add documentation file ./README-server-edition.html.
FossilOrigin-Name: fbc4f4ad259cfe04b28d698ef025f179b6708ac788939976c401db2caf8f92c1
2017-07-31 19:55:56 +00:00
dan 0ee56139e9 Merge latest trunk changes with this branch.
FossilOrigin-Name: b42c87790e2dfdc4e52c42967257decf4b44ded909403ea4ac47fe41acfd7077
2017-07-29 17:10:37 +00:00
dan af12f8e2c9 Update test program "tserver" to use a native pthreads mutex/condition
variable to efficiently manage wal file checkpoints without the wal file
growing indefinitely.

FossilOrigin-Name: 8299bdb7cbede30c665dda131bdcbd1d260b3ae9bd16d9b414d8c3776b08f1b3
2017-07-29 17:01:06 +00:00
dan aa90aad86b Reduce the number of mallocs required of writers in server mode.
FossilOrigin-Name: 60953997f62208f82b1efb53b8a1b0c6a26370411041457f747917e10d9a0e68
2017-07-28 21:02:13 +00:00
dan b4b69facc0 Update this branch with latest changes from trunk.
FossilOrigin-Name: d0719ad757bdf7cf2d7d7a4f7b0e713c262ffb434b91ddbb12e190e479abd19e
2017-07-24 19:43:58 +00:00
dan a12baadd15 Only open a db in server-mode if there is a directory named "db-journal"
in the file-system and the VFS is an exclusive locking VFS.

FossilOrigin-Name: e77d29f64480179d7aae7d88adab2ba9ff042701e1f4e624a3a7775cb56a51cc
2017-07-24 19:23:20 +00:00
dan 11584e6779 Merge latest trunk changes with this branch.
FossilOrigin-Name: be0df0a65fc781fde6e94c9370b4819c703d3b270c707dd59e587a2a6f36c9f4
2017-07-19 18:54:21 +00:00
dan 6af090cc05 Add simple tests for "PRAGMA freelist_format".
FossilOrigin-Name: 98a36f4cf127ceec423952a3d80d8d22163f128a5e6290cf6144c59029332944
2017-07-14 08:15:16 +00:00
dan e1e878687d Make the freelist format a separate feature from the page-level locking.
Freelist format is now configure using "PRAGMA freelist_format".

FossilOrigin-Name: dcc407972aa213d28f95cbaf4da5ab7d89ddbad2740f41cfb44b2c2ce20a3132
2017-07-13 21:06:51 +00:00
dan e83dba9e4d Add SQL function usleep() to test program tserver.c.
FossilOrigin-Name: 8cbe8f2b2fe0a5a246401718da8db8d487ced232ada73b53bdc9f9608c47d50e
2017-07-11 18:38:26 +00:00
dan 26a8ddbcdb Fix a bug causing readonly mvcc connections to read the wrong cache entry in
some cases.

FossilOrigin-Name: b6157267f9d4ecdffbdf4b1e591b8d6baaaa964c418ec247a24ccb82146f9041
2017-07-11 16:47:26 +00:00
dan 087794b136 Support read-only MVCC transactions in server-mode. Started using "BEGIN
READONLY".

FossilOrigin-Name: 5a043aa8dd0751e644c495a59deea5fe05da905f49c664d978fe477f9240bc37
2017-07-08 20:46:17 +00:00
dan ad9b696471 Merge latest trunk changes with this branch.
FossilOrigin-Name: 216c757f9289af818482253f77b0b2c9b09e8a478caf67d70a4a6da2fabbc642
2017-07-07 16:40:28 +00:00
dan f42e5d8917 Have this branch maintain an in-memory hash-table of old pages for read-only
MVCC clients. There is no way to access it yet.

FossilOrigin-Name: a3a9a2e1899cc963315590ef4666972e9d92986843706a551962ed16661a19b2
2017-07-07 16:12:45 +00:00
dan 411c20ad62 Merge tserver fixes with this branch.
FossilOrigin-Name: 58a0aab8fd5f5dcbec867468b7f4c1b67c3e16bbe3aeea6bd21ae6b04c2b93f1
2017-06-28 20:21:15 +00:00
dan 6b0cfe5343 Fix bugs in test program tserver.c.
FossilOrigin-Name: 093b9108ea3532437683547c82f756df2413013a50a2fddad3d928cf8d74f9eb
2017-06-28 20:12:41 +00:00
dan 6f3283a427 Support clients within a single process only.
FossilOrigin-Name: dfa9a4d53e0aa7f7911b11af57a4d37a1df2d835cf60eb9e76d28b6b39c2ba58
2017-06-27 20:23:07 +00:00
dan bab38cbc9a Ensure that test tool "tserver" finalizes all statements before attempting to
close a database handle.

FossilOrigin-Name: d8568aacf083b63cdcf68d744ddb07a7615d14f169be6c632813a266a1e8a1cf
2017-06-20 19:20:07 +00:00
dan 538ed8e376 Add too/tserver.c - the implementation of a simple multi-threaded server
designed for interactive testing of concurrency between connections used by
different threads of the same process.

FossilOrigin-Name: 05b4fc4340011847c8585bb822d339dd7b8351266a12b26fdc85edce38fc9dd3
2017-06-07 15:55:59 +00:00
dan b4c4601adf Add code to this branch to emit a log message after each cumulative second
that the WRITER lock has been held.

FossilOrigin-Name: a726d98122704e1e702cfa7ae61d680497df6a826d98082161e0823e115d40a5
2017-05-22 08:01:58 +00:00
dan 60dd40a62c Avoid writer starvation by adding a RESERVED state to page locks.
FossilOrigin-Name: 9b7f80246f2b9921483ab23457865e783ee70b93f67bcecc0c16516447a05875
2017-05-15 19:32:58 +00:00
dan 2ac63b9812 Avoid running recovery while there is another read/write client.
FossilOrigin-Name: a38858a24c6edaa05966b7e158e603bcbde8b05c6233e3bb005cfb32bc91ea06
2017-05-13 19:07:10 +00:00
dan bf431367f3 Require exclusive access to the db to wrap the wal file. Have "PRAGMA
wal_checkpoint = restart" block for this.

FossilOrigin-Name: cbf44ed9758d577e1450b53e645b73c9ca1ee29d2354ce6375c234a41a063400
2017-05-12 18:52:27 +00:00
dan 730859568d Fix a problem causing a lock to be held past the end of a transaction. Use a
blocking lock to take the read-lock on page 1 taken by all transactions.

FossilOrigin-Name: 2584df3d42ece69d37f31f3655cd0d4760914bea73d4f4ccb7f2a7aa47f80f49
2017-05-10 16:18:05 +00:00
dan c9bcad12ea Use a blocking call to obtain the wal-mode WRITER lock in some cases.
FossilOrigin-Name: 4464ca1d686b5c457995cc885d4a8704e402ad387aa4cc37de199276b28cc08e
2017-05-10 13:46:29 +00:00
dan e376cac1d1 Fix a problem with wrapping the log file in server mode.
FossilOrigin-Name: 270b7d1eacb57827465946262b31c5c890d7fc5b5618ccd9e220bc2f9546de54
2017-05-09 16:32:19 +00:00
dan b7bcf5c16c Add some support for wal mode to the hack on this branch.
FossilOrigin-Name: b733afc1d0abc09861903ce8e27a8f2462ec871967f5d3dc2847b31bb28b55f3
2017-05-08 20:15:23 +00:00
dan 31349393ba Update this branch with latest trunk changes.
FossilOrigin-Name: ed6bad67f530afa2fba1bce339a9772f281bd108666ca6c23642efcefa2d98e5
2017-05-06 16:04:58 +00:00
dan 8df9974169 Update this branch with latest trunk changes.
FossilOrigin-Name: 83b1e163ef6ac01d252268415f26a2a999670de583491becc091d294075980bc
2017-05-01 20:07:59 +00:00
dan f427296520 Avoid reading the file-size before page 1 is read-locked.
FossilOrigin-Name: f6b025bf632501f51e691a05d309225671fd78bc62c9a43dd916d35ba4cf6d0e
2017-04-29 15:48:32 +00:00
dan 920a129de3 Fix various bugs revealed by running performance tests.
FossilOrigin-Name: a40b42c422a53aa99280eca7ba419a3937699e273efa3377e0fefe52582cbbd4
2017-04-28 17:32:57 +00:00
dan 34d02325be Add a missing releasePage() call to the server-mode free-list management code.
FossilOrigin-Name: a5a085483c66b17727cf4d2a15165621016031ab78990cb5977ccdbd231d13ac
2017-04-28 14:09:55 +00:00
dan aa189cf00f Use a different free-list format for server-mode databases in order to reduce
contention.

FossilOrigin-Name: 778e8a102d8dc7b0fa006c2d90b0a8cd36ebc1e16bd17477f2d536fc0cef4bf3
2017-04-28 10:20:03 +00:00
dan 2dab053e22 Do not write master journal filenames into server-mode journal files. Use
SQLITE_MUTEX_STATIC_APP1 to protect critical sections in server.c.

FossilOrigin-Name: 3144ae40d2eb63dfd5587579a49163ea1add5947d624daa478ada13339495af4
2017-04-27 14:12:43 +00:00
dan 0561247d07 If possible, delete the journal file when a database connection is closed.
FossilOrigin-Name: d5b5326df25b85b1c3926cd693bcde1ca08e6e03b8aea151a82d222fc9c23dd6
2017-04-27 13:05:43 +00:00
dan 1381bbc262 Experimental implementation of pessimistic page-level locking based on
rollback mode.

FossilOrigin-Name: 64ecf7c7e512827e8a5a42f9f3ad92ff57ec868820e3943dbc74d5823f9a889d
2017-04-26 20:45:00 +00:00
82 changed files with 4322 additions and 1202 deletions
+1 -1
View File
@@ -1672,7 +1672,7 @@ $(SQLITE3DLL): $(LIBOBJ) $(LIBRESOBJS) $(CORE_LINK_DEP)
sqlite3.def: libsqlite3.lib
echo EXPORTS > sqlite3.def
dumpbin /all libsqlite3.lib \
| $(TCLSH_CMD) $(TOP)\tool\replace.tcl include "^\s+1 _?(sqlite3(?:session|changeset|changegroup|rebaser)?_[^@]*)(?:@\d+)?$$" \1 \
| $(TCLSH_CMD) $(TOP)\tool\replace.tcl include "^\s+1 _?(sqlite3(?:session|changeset|changegroup)?_[^@]*)(?:@\d+)?$$" \1 \
| sort >> sqlite3.def
# <</block2>>
+440
View File
@@ -0,0 +1,440 @@
<html>
<center>
<h1> The "server-process-edition" Branch</h1>
</center>
<p>
The "server-process-edition" branch contains two modifications to stock
SQLite that work together to provide concurrent read/write transactions
using pessimistic page-level-locking. The system runs in two modes:
<ul>
<li><p> Single-process mode - where all clients must be within the
same address space, and
<li><p> Multi-process mode - where clients may be distributed between
multiple OS processes.
</ul>
<p> The system is designed to be most efficient when used with
<a href="https://www.sqlite.org/pragma.html#pragma_synchronous">
"PRAGMA synchronous=OFF"</a>, although it does not require this.
<p>
Up to 16 simultaneous read/write transactions controlled by page-level-locking
are possible. Additionally, in single-process mode there may be any number of
read-only transactions started using the "BEGIN READONLY" command. Read-only
transactions do not block read-write transactions, and read-write transactions
do not block read-only transactions. Read-only transactions access a consistent
snapshot of the database - writes committed by other clients after the
transaction has started are never visible to read-only transactions. In
multi-process mode, the "BEGIN READONLY" command is equivalent to a stock
"BEGIN".
<p>
The two features on this branch are:
<ol>
<li><p> An
<a href=#freelist>alternative layout for the database free-page list</a>.
This is intended to reduce contention between writers when allocating
new database pages, either from the free-list or by extending the
database file.
<li><p> The <a href=#servermode>"server-mode" extension</a>, which
provides read/write page-level-locking concurrency and (in
single-process mode) read-only MVCC concurrency mentioned above.
</ol>
<h2 id=freelist> 1.0 Alternative Free-List Format </h2>
<p>
The alternative free-list format is very similar to the current format. It
differs in the following respects:
<ul>
<li><p>The "total number of free pages" field in the db header is not
maintained. It is always set to zero.
<li><p> Instead of pointing to the first free-list trunk page, the free-list
pointer in the db header points to a page known as the "free-list node".
<li><p> The free-list node page contains N pointers to free-lists stored in the
legacy format (i.e. a linked list of trunk pages each containing
pointers to many free leaf pages).
</ul>
<p>
This effectively means that a database has N free-lists instead of just one. To
allocate a free page, a writer only needs to lock one such free-list, and so up
to N transactions that allocate new pages may proceed concurrently.
<p>
Allocating pages from the end of the db file still locks out all other
read/write transactions (because it involves writing to page 1, which every
transaction needs to read). To minimize the frequency with which this occurs,
when a page must be allocated from the end of the database file, the file is
extended by 2048 pages. These are distributed equally between 16 free-lists
(children of the free-list node page). Additionally, the first trunk page in
each free list is never reused. Doing so would require writing to the
free-list node page - effectively an exclusive lock on the entire
page-allocation system.
<p>
The current format used for the free-list can be modified or queried using a
new pragma:
<pre>
PRAGMA [database.]freelist_format;
PRAGMA [database.]freelist_format = 1|2;
</pre>
<p>
At present, the free-list format may only be modified when the free-list is
completely empty. Which, as the implementation ensures that a free-list that
uses the alternative format is never completely emptied, effectively precludes
changing the format from 2 (alternative) to 1 (legacy).
<p>
For databases that use the "alternative" free-list format, the read and write
versions in the database header (byte offsets 18 and 19) are set to 3 for
rollback mode or 4 for wal mode (instead of 1 and 2 respectively).
<h2 id=servermode> 2.0 Page level locking - "Server Mode" </h2>
<p>
A database client automatically enters "server mode" if there exists a
<i>directory</i> named "&lt;database&gt;-journal" in the file system alongside
the database file "&lt;database&gt;" There is currently no provision for
creating this directory, although it could be safely done for a database in
rollback mode using something like:
<pre>
PRAGMA journal_mode = off;
BEGIN EXCLUSIVE;
&lt;create directory&gt;
END;
</pre>
<p> As well as signalling new clients that they should enter server-mode,
creating a directory named "&lt;database&gt;-journal" has the helpful
side-effect of preventing legacy clients from accessing the database file at
all.
<p> If the VFS is one that takes an exclusive lock on the db file (to
guarantee that no other process accesses the db file), then the system
automatically enters single-process mode. Otherwise, multi-process mode.
<p> In both single and multi-process modes, page-level-locking is managed
by allocating a fixed-size array of "locking slots". Each locking slot is
32-bits in size. By default, the array contains 262144 (2^18) slots. Pages are
assigned to locking slots using the formula (pgno % 262144) - so pages 1,
262145, 524289 etc. share a single locking slot.
<p> In single-process mode, the array of locking slots is allocated on
the process heap and access is protected by a mutex. In multi-process mode, it
is created by memory-mapping a file on disk (similar to the *-shm file in
SQLite wal mode) and access is performed using
<a href="https://en.wikipedia.org/wiki/Compare-and-swap">atomic CAS
primitives</a> exclusively.
<p> Each time a read/write transaction is opened, the client assumes a client
id between 0 and 15 for the duration of the transaction. Client ids are unique
at any point in time - concurrently executing transactions must use different
client ids. So there may exist a maximum of 16 concurrent read/write
transactions at any one time.
<p> Read/write transactions in server-mode are similar to regular SQLite
transactions in rollback mode. The most significant differences are that:
<ul>
<li> <p>Instead of using journal file &lt;database&gt;-journal, server-mode
clients use &lt;database&gt;-journal/&lt;client-id&gt;-journal. If
there are multiple concurrent transactions, each uses a separate
journal file.
<li> <p>No database-wide lock is taken. Instead, individual read and write
locks are taken on the pages accessed by the transaction.
</ul>
<p> Each locking slot is 32-bits in size. A locking slot may simultaneously
support a single write-lock, up to 16 read-locks from read/write clients, and
(in single process mode) up 1024 read-locks from "BEGIN READONLY" clients.
Locking slot bits are used as follows:
<ul>
<li> <p> The least-significant 16-bits are used for read-locks taken by
read/write clients. To take a read-lock, bit &lt;client-id&gt; of the
locking slot is set.
<li> <p> The next 5 bytes are used for the write-lock. If no write-lock
is held on the slot, then this 5 byte integer is set to 0. Otherwise,
it is set to (<i>C</i> + 1), where <i>C</i> is the &lt;client-id&gt; of
the client holding the write-lock.
<li> <p> The next 10 bits contain the total number of read-locks held by
"BEGIN READONLY" clients on the locking slot. See the section below
for a description of how these are used.
</ul>
<p> Currently, if a client requests a lock that cannot be granted due to
a conflicting lock, SQLITE_BUSY is returned to the caller and either the
entire transaction or statement transaction must be rolled back. See
<a href=#problems>Problems and Issues</a> below for more details.
<h3> 2.1 Single-Process Mode </h3>
<p> Single process mode is simpler than multi-process mode because it does
not have to deal with runtime client failure - it is assumed that if one
client fails mid-transaction the entire process crashes. As a result the
only time hot-journal rollback is required in single-process mode is as
part of startup. The first client to connect to a database in single-process
mode attempts to open and rollback all 16 potential hot journal files.
<p> But, in order to support non-blocking "BEGIN READONLY" transactions, it is
also in some ways more complicated than multi-process mode. "BEGIN READONLY"
support works as follows:
<ul>
<li> <p>In single-process mode, writers never spill the cache mid-transaction.
Data is only written to the database as part of committing a transaction.
<li> <p>As well as writing the contents of overwritten pages out to the journal
file, a writer in single-process mode also accumulates a list of buffers
containing the original data for each page overwritten by the current
transaction in main-memory.
<li> <p>When a transaction is ready to be committed, a writer obtains a
transaction-id. Transaction-ids are assigned to writers using a
monotonically increasing function. The writer then adds all of its "old
data" buffers to a hash table accessible to all database clients.
Associated with each hash table entry is the newly assigned transaction-id.
It then waits (spin-locks) for all "BEGIN READONLY" read-locks to clear on
all pages that will be written out by the transaction. Following this, it
commits the transaction as normal (writes out the dirty pages and zeroes
the journal file header).
<li> <p>Clients executing "BEGIN READONLY" transactions are not assigned
a &lt;client-id&gt;. Instead, they are assigned a transaction-id that is
either (a) that of the oldest transaction-id belonging to a writer that has
not yet finished committing, or (b) if there are currently no writers
committing then the value that will be assigned to the next committer.
<li> <p>When a "BEGIN READONLY" transaction reads a page, it first checks
the aforementioned hash table for a suitable entry. A suitable entry
is one with the right page-number and a transaction-id greater than or
equal to that of the "BEGIN READONLY" transaction (i.e. one that had not
finished committing when the BEGIN READONLY transaction started). If such
an entry can be found, the client uses the associated data instead of
reading from the db file. Or, if no such entry is found, the client:
<ol>
<li> Increments the number of BEGIN READONLY read-locks on the page.
<li> Reads the contents of the page from the database file.
<li> Decrements the number of BEGIN READONLY read-locks on the page.
</ol>
<p> The mutex used to protect access to the array of locking slots and
the shared hash table is relinquished for step 2 above.
<li> <p>After each transaction is commited in single-process mode, the
client searches the hash table for entries that can be discarded. An
entry can be discarded if it has a transaction-id older than any still
in use (either by BEGIN READONLY transactions or committers).
</ul>
<h3> 2.2 Multi-Process Mode </h3>
<p> Multi-process mode differs from single-process mode in two important ways:
<ul>
<li> <p>Individual clients may fail mid-transaction and the system must recover
from this.
<li> <p>Partly as a consequence of the above, there are no convenient
primitives like mutexes or malloc() with which to build complicated data
structures like the hash-table used in single-process mode. As a result,
there is no support for "BEGIN READONLY" transactions in multi-process
mode.
</ul>
<p> Unlike single-process mode clients, which may be assigned a different
client-id for each transaction, clients in multi-process mode are assigned a
client-id when they connect to the database and do not relinquish it until
they disconnect. As such, a database in multi-process server-mode supports
at most 16 concurrent client connections.
<p> As well as the array of locking slots, the shared-memory mapping used
by clients in multi-process mode contains 16 "client slots". When a client
connects, it takes a posix WRITE lock on the client slot that corresponds
to its client id. This lock is not released until the client disconnects.
Additionally, whenever a client starts a transaction, it sets the value
in its client locking slot to 1, and clears it again after the transaction
is concluded.
<p> This assists with handling client failure mid-transaction in two ways:
<ul>
<li><p> If client A cannot obtain a lock due to a conflicting lock held by
client B, it can check whether or not client B has failed by attempting a
WRITE lock on its client locking slot. If successful, then client B must
have failed and client A may:
<ul>
<li> Roll back client B's journal, and
<li> By iterating through the entire locking slot array, release all
locks held by client B when it failed.
</ul>
<li><p> When a client first connects and locks its client locking slot, it
can check whether or not the previous user of the client locking slot failed
mid-transaction (since if it did, the locking slot value will still be
non-zero). If it did, the new owner of the client locking slot can release
any locks and roll back any hot-journal before proceeding.
</ul>
<h3> 2.3 Required VFS Support </h3>
<p> The server-mode extension requires that the VFS support various special
file-control commands. Currently support is limited to the "unix" VFS.
<dl>
<dt> SQLITE_FCNTL_SERVER_MODE
<dd><p> This is used by SQLite to query the VFS as to whether the
connection should use single-process server-mode, multi-process server-mode,
or continue in legacy mode.
<p>SQLite invokes this file-control as part of the procedure for detecting a
hot journal (after it has established that there is a file-system entry named
&lt;database&gt;-journal and that no other process holds a RESERVED lock).
If the &lt;database&gt;-journal directory is present in the file-system and
the current VFS takes an exclusive lock on the database file (i.e. is
"unix-excl"), then this file-control indicates that the connection should use
single-process server-mode. Or, if the directory exists but the VFS does not
take an exclusive lock on the database file, that the connection should use
multi-proces server-mode. Or, if there is no directory of the required name,
that the connection should use legacy mode.
<dt> SQLITE_FCNTL_FILEID
<dd><p> Return a 128-bit value that uniquely identifies an open file on disk
from the VFS. This is used to ensure that all connections to the same
database from within a process use the same shared state, even if they
connect to the db using different file-system paths.
<dt> SQLITE_FCNTL_SHMOPEN
<dd>
<dt> SQLITE_FCNTL_SHMOPEN2
<dd>
<dt> SQLITE_FCNTL_SHMLOCK
<dd>
<dt> SQLITE_FCNTL_SHMCLOSE
<dd>
</dl>
<h2 id=problems> 3.0 Problems and Issues </h2>
<ul>
<li> <p>Writer starvation might be the biggest issue. How can it be
prevented?
<li> <p>Blocking locks of some sort would likely improve things. The issue
here is deadlock detection.
<li> <p>The limit of 16 concurrent clients in multi-process mode could be
raised to 27 (since the locking-slot bits used for BEGIN READONLY
locks in single-process mode can be reassigned to support more
read/write client read-locks).
</ul>
<h2> 4.0 Performance Test </h2>
<p>
The test uses a single table with the following schema:
<pre>
CREATE TABLE t1(a INTEGER PRIMARY KEY, b BLOB(16), c BLOB(16), d BLOB(400));
CREATE INDEX i1 ON t1(b);
CREATE INDEX i2 ON t1(c);
</pre>
<p>
The database initially contains 5,000,000 rows. Values for column "a" are
between 1 and 5,000,000, inclusive. Other columns are populated with randomly
generated blob values, each 16, 16, and 400 bytes in size, respectively.
<p>
Read/write transactions used by the test take the following form. Each such
transaction modifies approximately 25 pages (5 in the main table and 10 in each
index), not accounting for tree rebalancing operations.
<pre>
BEGIN;
REPLACE INTO t1 VALUES(abs(random() % 5000000), randomblob(16), randomblob(16), randomblob(400));
REPLACE INTO t1 VALUES(abs(random() % 5000000), randomblob(16), randomblob(16), randomblob(400));
REPLACE INTO t1 VALUES(abs(random() % 5000000), randomblob(16), randomblob(16), randomblob(400));
REPLACE INTO t1 VALUES(abs(random() % 5000000), randomblob(16), randomblob(16), randomblob(400));
REPLACE INTO t1 VALUES(abs(random() % 5000000), randomblob(16), randomblob(16), randomblob(400));
COMMIT;
</pre>
<p>
Read-only transactions are as follows:
<pre>
BEGIN READONLY;
SELECT * FROM t1 WHERE a&gt;abs((random()%5000000)) LIMIT 10;
SELECT * FROM t1 WHERE a&gt;abs((random()%5000000)) LIMIT 10;
SELECT * FROM t1 WHERE a&gt;abs((random()%5000000)) LIMIT 10;
SELECT * FROM t1 WHERE a&gt;abs((random()%5000000)) LIMIT 10;
SELECT * FROM t1 WHERE a&gt;abs((random()%5000000)) LIMIT 10;
END;
</pre>
<p>
The performance test features one or more clients executing read/write
transactions as fast as possible, and zero or more clients executing read-only
transactions, also as fast as possible. All tests use the "unix-excl" VFS and
all clients execute in a separate thread within the same process. The database
is located on a tmpfs file-system.
<p>
In the table below "rw:" refers to the number of read-write clients, and "ro:"
the number of read-only clients used by the test. The TPS values in brackets
are the number of read-only transactions per second. All other values are
read-write transactions per second.
<p>
The collision rate (percentage of attempted transactions that failed due to a
page-level locking conflict) in all tests was between 1 and 2%. Failed
transactions are not included in the TPS counts below.
<p>
<table border=1 width=90% align=center>
<!--
320 jm=persist, rw:1 139675, 135797
320 jm=wal, rw:1 120995, 118889
begin-concurrent, rw:1 119438, 117580
begin-concurrent, rw:2 166923, 166904
begin-concurrent, rw:3 180432, 172825
begin-concurrent, rw:2,ro:1 150427(347319),152087(351601)
server-mode, rw:1 126592, 126742
server-mode, rw:2 228317, 227155
server-mode, rw:3 309712, 306218
server-mode, rw:2,ro:1 213303(576032),210994(556005)
-->
<tr><th> Configuration <th>TPS per client <th> TPS total
<tr><td> 3.20.0, journal_mode=persist, rw:1, ro:0 <td>6886 <td> 6886
<tr><td> 3.20.0, journal_mode=wal, rw:1, ro:0 <td>5997 <td> 5997
<tr><td> begin-concurrent, rw:1, ro:0<td>5925<td> 5925
<tr><td> begin-concurrent, rw:2, ro:0<td>4172 <td> 8345
<tr><td> begin-concurrent, rw:3, ro:0<td>2943 <td> 8831
<tr><td> begin-concurrent, rw:2, ro:1<td>3781 <td> 7562 (17473)
<tr><td> server-mode, rw:1, ro:0 <td>6333 <td> 6333
<tr><td> server-mode, rw:2, ro:0<td> 5693 <td> 11386
<tr><td> server-mode, rw:3, ro:0<td>5132 <td> 15398
<tr><td> server-mode, rw:2, ro:1<td>5303 <td> 10607 (28300)
</table>
+3 -4
View File
@@ -4,10 +4,9 @@ This repository contains the complete source code for the SQLite database
engine. Some test scripts are also included. However, many other test scripts
and most of the documentation are managed separately.
SQLite [does not use Git](https://sqlite.org/whynotgit.html).
If you are reading this on GitHub, then you are looking at an
unofficial mirror. See <https://sqlite.org/src> for the official
repository.
If you are reading this on a Git mirror someplace, you are doing it wrong.
The [official repository](https://www.sqlite.org/src/) is better. Go there
now.
## Obtaining The Code
+1 -1
View File
@@ -1 +1 @@
3.23.1
3.23.0
+1 -1
View File
@@ -966,7 +966,7 @@ Replace.exe:
sqlite3.def: Replace.exe $(LIBOBJ)
echo EXPORTS > sqlite3.def
dumpbin /all $(LIBOBJ) \
| .\Replace.exe "^\s+/EXPORT:_?(sqlite3(?:session|changeset|changegroup|rebaser)?_[^@,]*)(?:@\d+|,DATA)?$$" $$1 true \
| .\Replace.exe "^\s+/EXPORT:_?(sqlite3(?:session|changeset|changegroup)?_[^@,]*)(?:@\d+|,DATA)?$$" $$1 true \
| sort >> sqlite3.def
$(SQLITE3EXE): shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLITE3H)
Vendored
+9 -9
View File
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69 for sqlite 3.23.1.
# Generated by GNU Autoconf 2.69 for sqlite 3.23.0.
#
#
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
@@ -726,8 +726,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='sqlite'
PACKAGE_TARNAME='sqlite'
PACKAGE_VERSION='3.23.1'
PACKAGE_STRING='sqlite 3.23.1'
PACKAGE_VERSION='3.23.0'
PACKAGE_STRING='sqlite 3.23.0'
PACKAGE_BUGREPORT=''
PACKAGE_URL=''
@@ -1465,7 +1465,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures sqlite 3.23.1 to adapt to many kinds of systems.
\`configure' configures sqlite 3.23.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1530,7 +1530,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of sqlite 3.23.1:";;
short | recursive ) echo "Configuration of sqlite 3.23.0:";;
esac
cat <<\_ACEOF
@@ -1655,7 +1655,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
sqlite configure 3.23.1
sqlite configure 3.23.0
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2074,7 +2074,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by sqlite $as_me 3.23.1, which was
It was created by sqlite $as_me 3.23.0, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -12242,7 +12242,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by sqlite $as_me 3.23.1, which was
This file was extended by sqlite $as_me 3.23.0, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -12308,7 +12308,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
sqlite config.status 3.23.1
sqlite config.status 3.23.0
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
+2 -8
View File
@@ -535,12 +535,6 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
aColMap[1] = nCol;
aColMap[2] = nCol+1;
assert( SQLITE_INDEX_CONSTRAINT_EQ<SQLITE_INDEX_CONSTRAINT_MATCH );
assert( SQLITE_INDEX_CONSTRAINT_GT<SQLITE_INDEX_CONSTRAINT_MATCH );
assert( SQLITE_INDEX_CONSTRAINT_LE<SQLITE_INDEX_CONSTRAINT_MATCH );
assert( SQLITE_INDEX_CONSTRAINT_GE<SQLITE_INDEX_CONSTRAINT_MATCH );
assert( SQLITE_INDEX_CONSTRAINT_LE<SQLITE_INDEX_CONSTRAINT_MATCH );
/* Set idxFlags flags for all WHERE clause terms that will be used. */
for(i=0; i<pInfo->nConstraint; i++){
struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
@@ -559,11 +553,11 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
pInfo->estimatedCost = 1e50;
return SQLITE_OK;
}
}else if( p->op<=SQLITE_INDEX_CONSTRAINT_MATCH ){
}else{
int j;
for(j=1; j<ArraySize(aConstraint); j++){
struct Constraint *pC = &aConstraint[j];
if( iCol==aColMap[pC->iCol] && (p->op & pC->op) && p->usable ){
if( iCol==aColMap[pC->iCol] && p->op & pC->op && p->usable ){
pC->iConsIndex = i;
idxFlags |= pC->fts5op;
}
-12
View File
@@ -591,18 +591,6 @@ do_execsql_test 22.1 {
SELECT rowid FROM t9('a*')
} {1}
#-------------------------------------------------------------------------
do_execsql_test 23.0 {
CREATE VIRTUAL TABLE t10 USING fts5(x, detail=%DETAIL%);
CREATE TABLE t11(x);
}
do_execsql_test 23.1 {
SELECT * FROM t11, t10 WHERE t11.x = t10.x AND t10.rowid IS NULL;
}
do_execsql_test 23.2 {
SELECT * FROM t11, t10 WHERE t10.rowid IS NULL;
}
}
expand_all_sql db
-1
View File
@@ -34,7 +34,6 @@ struct EvalResult {
static int callback(void *pCtx, int argc, char **argv, char **colnames){
struct EvalResult *p = (struct EvalResult*)pCtx;
int i;
if( argv==0 ) return 0;
for(i=0; i<argc; i++){
const char *z = argv[i] ? argv[i] : "";
size_t sz = strlen(z);
-9
View File
@@ -270,15 +270,6 @@ static int seriesFilter(
}else{
pCur->iStep = 1;
}
for(i=0; i<argc; i++){
if( sqlite3_value_type(argv[i])==SQLITE_NULL ){
/* If any of the constraints have a NULL value, then return no rows.
** See ticket https://www.sqlite.org/src/info/fac496b61722daf2 */
pCur->mnValue = 1;
pCur->mxValue = 0;
break;
}
}
if( idxNum & 8 ){
pCur->isDesc = 1;
pCur->iValue = pCur->mxValue;
+3 -7
View File
@@ -764,7 +764,7 @@ static int editDist3ConfigLoad(
assert( zTo!=0 || nTo==0 );
if( nFrom>100 || nTo>100 ) continue;
if( iCost<0 ) continue;
if( iCost>=10000 ) continue; /* Costs above 10K are considered infinite */
if( iCost>10000 ) continue; /* Costs above 10K are considered infinite */
if( pLang==0 || iLang!=iLangPrev ){
EditDist3Lang *pNew;
pNew = sqlite3_realloc64(p->a, (p->nLang+1)*sizeof(p->a[0]));
@@ -835,7 +835,6 @@ static int utf8Len(unsigned char c, int N){
** the given string.
*/
static int matchTo(EditDist3Cost *p, const char *z, int n){
assert( n>0 );
if( p->a[p->nFrom]!=z[0] ) return 0;
if( p->nTo>n ) return 0;
if( strncmp(p->a+p->nFrom, z, p->nTo)!=0 ) return 0;
@@ -848,10 +847,8 @@ static int matchTo(EditDist3Cost *p, const char *z, int n){
*/
static int matchFrom(EditDist3Cost *p, const char *z, int n){
assert( p->nFrom<=n );
if( p->nFrom ){
if( p->a[0]!=z[0] ) return 0;
if( strncmp(p->a, z, p->nFrom)!=0 ) return 0;
}
if( p->a[0]!=z[0] ) return 0;
if( strncmp(p->a, z, p->nFrom)!=0 ) return 0;
return 1;
}
@@ -867,7 +864,6 @@ static int matchFromTo(
){
int b1 = pStr->a[n1].nByte;
if( b1>n2 ) return 0;
assert( b1>0 );
if( pStr->z[n1]!=z2[0] ) return 0;
if( strncmp(pStr->z+n1, z2, b1)!=0 ) return 0;
return 1;
-43
View File
@@ -612,49 +612,6 @@ do_iterator_test $tn.12.2 * {
{UPDATE t1 0 X.. {i 3 {} {} i 3} {{} {} {} {} t one}}
}
#-------------------------------------------------------------------------
# Test that no savepoint is used if -nosavepoint is specified.
#
do_execsql_test $tn.13.1 {
CREATE TABLE x1(a INTEGER PRIMARY KEY, b)%WR%;
}
do_test $tn.13.2 {
execsql BEGIN
set C [changeset_from_sql {
INSERT INTO x1 VALUES(1, 'one');
INSERT INTO x1 VALUES(2, 'two');
INSERT INTO x1 VALUES(3, 'three');
}]
execsql ROLLBACK
execsql {
INSERT INTO x1 VALUES(1, 'i');
INSERT INTO x1 VALUES(2, 'ii');
INSERT INTO x1 VALUES(3, 'iii');
}
} {}
proc xConflict {args} {
set ret [lindex $::CONFLICT_HANDLERS 0]
set ::CONFLICT_HANDLERS [lrange $::CONFLICT_HANDLERS 1 end]
set ret
}
do_test $tn.13.3 {
set CONFLICT_HANDLERS [list REPLACE REPLACE ABORT]
execsql BEGIN
catch { sqlite3changeset_apply_v2 db $C xConflict } msg
execsql {
SELECT * FROM x1
}
} {1 i 2 ii 3 iii}
do_test $tn.13.3 {
set CONFLICT_HANDLERS [list REPLACE REPLACE ABORT]
execsql ROLLBACK
execsql BEGIN
catch { sqlite3changeset_apply_v2 -nosavepoint db $C xConflict } msg
execsql { SELECT * FROM x1 }
} {1 one 2 two 3 iii}
execsql ROLLBACK
}]
}
-40
View File
@@ -204,47 +204,7 @@ do_test 5.1 {
}
} {1 2 3 7 8 9}
#-------------------------------------------------------------------------
reset_db
db func number_name number_name
do_execsql_test 6.0 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
CREATE UNIQUE INDEX t1b ON t1(b);
WITH s(i) AS (
SELECT 1
UNION ALL
SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO t1 SELECT i, number_name(i) FROM s;
}
do_test 6.1 {
db eval BEGIN
set ::C [changeset_from_sql {
DELETE FROM t1;
WITH s(i) AS (
SELECT 1
UNION ALL
SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO t1 SELECT i, number_name(i+1) FROM s;
}]
db eval ROLLBACK
execsql { SELECT count(*) FROM t1 WHERE number_name(a) IS NOT b }
} {0}
proc xConflict {args} { exit ; return "OMIT" }
do_test 6.2 {
sqlite3changeset_apply db $C xConflict
} {}
do_execsql_test 6.3 { SELECT count(*) FROM t1; } {1000}
do_execsql_test 6.4 {
SELECT count(*) FROM t1 WHERE number_name(a+1) IS NOT b;
} {0}
# db eval { SELECT * FROM t1 } { puts "$a || $b" }
finish_test
-26
View File
@@ -170,29 +170,3 @@ proc changeset_to_list {c} {
lsort $list
}
set ones {zero one two three four five six seven eight nine
ten eleven twelve thirteen fourteen fifteen sixteen seventeen
eighteen nineteen}
set tens {{} ten twenty thirty forty fifty sixty seventy eighty ninety}
proc number_name {n} {
if {$n>=1000} {
set txt "[number_name [expr {$n/1000}]] thousand"
set n [expr {$n%1000}]
} else {
set txt {}
}
if {$n>=100} {
append txt " [lindex $::ones [expr {$n/100}]] hundred"
set n [expr {$n%100}]
}
if {$n>=20} {
append txt " [lindex $::tens [expr {$n/10}]]"
set n [expr {$n%10}]
}
if {$n>0} {
append txt " [lindex $::ones $n]"
}
set txt [string trim $txt]
if {$txt==""} {set txt zero}
return $txt
}
+16 -23
View File
@@ -68,7 +68,7 @@ struct SessionBuffer {
** sqlite3changeset_start_strm()).
*/
struct SessionInput {
int bNoDiscard; /* If true, do not discard in InputBuffer() */
int bNoDiscard; /* If true, discard no data */
int iCurrent; /* Offset in aData[] of current change */
int iNext; /* Offset in aData[] of next change */
u8 *aData; /* Pointer to buffer containing changeset */
@@ -2593,7 +2593,7 @@ int sqlite3changeset_start_strm(
** object and the buffer is full, discard some data to free up space.
*/
static void sessionDiscardData(SessionInput *pIn){
if( pIn->xInput && pIn->iNext>=SESSIONS_STRM_CHUNK_SIZE ){
if( pIn->bEof && pIn->xInput && pIn->iNext>=SESSIONS_STRM_CHUNK_SIZE ){
int nMove = pIn->buf.nBuf - pIn->iNext;
assert( nMove>=0 );
if( nMove>0 ){
@@ -4234,11 +4234,10 @@ static int sessionChangesetApply(
sqlite3_changeset_iter *p /* Handle describing change and conflict */
),
void *pCtx, /* First argument passed to xConflict */
void **ppRebase, int *pnRebase, /* OUT: Rebase information */
int flags /* SESSION_APPLY_XXX flags */
void **ppRebase, int *pnRebase /* OUT: Rebase information */
){
int schemaMismatch = 0;
int rc = SQLITE_OK; /* Return code */
int rc; /* Return code */
const char *zTab = 0; /* Name of current table */
int nTab = 0; /* Result of sqlite3Strlen30(zTab) */
SessionApplyCtx sApply; /* changeset_apply() context object */
@@ -4249,9 +4248,7 @@ static int sessionChangesetApply(
pIter->in.bNoDiscard = 1;
memset(&sApply, 0, sizeof(sApply));
sqlite3_mutex_enter(sqlite3_db_mutex(db));
if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){
rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0);
}
rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0);
if( rc==SQLITE_OK ){
rc = sqlite3_exec(db, "PRAGMA defer_foreign_keys = 1", 0, 0, 0);
}
@@ -4389,13 +4386,11 @@ static int sessionChangesetApply(
}
sqlite3_exec(db, "PRAGMA defer_foreign_keys = 0", 0, 0, 0);
if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){
if( rc==SQLITE_OK ){
rc = sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
}else{
sqlite3_exec(db, "ROLLBACK TO changeset_apply", 0, 0, 0);
sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
}
if( rc==SQLITE_OK ){
rc = sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
}else{
sqlite3_exec(db, "ROLLBACK TO changeset_apply", 0, 0, 0);
sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
}
if( rc==SQLITE_OK && bPatchset==0 && ppRebase && pnRebase ){
@@ -4432,14 +4427,13 @@ int sqlite3changeset_apply_v2(
sqlite3_changeset_iter *p /* Handle describing change and conflict */
),
void *pCtx, /* First argument passed to xConflict */
void **ppRebase, int *pnRebase,
int flags
void **ppRebase, int *pnRebase
){
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
int rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset);
if( rc==SQLITE_OK ){
rc = sessionChangesetApply(
db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags
db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase
);
}
return rc;
@@ -4466,7 +4460,7 @@ int sqlite3changeset_apply(
void *pCtx /* First argument passed to xConflict */
){
return sqlite3changeset_apply_v2(
db, nChangeset, pChangeset, xFilter, xConflict, pCtx, 0, 0, 0
db, nChangeset, pChangeset, xFilter, xConflict, pCtx, 0, 0
);
}
@@ -4489,14 +4483,13 @@ int sqlite3changeset_apply_v2_strm(
sqlite3_changeset_iter *p /* Handle describing change and conflict */
),
void *pCtx, /* First argument passed to xConflict */
void **ppRebase, int *pnRebase,
int flags
void **ppRebase, int *pnRebase
){
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
int rc = sqlite3changeset_start_strm(&pIter, xInput, pIn);
if( rc==SQLITE_OK ){
rc = sessionChangesetApply(
db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags
db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase
);
}
return rc;
@@ -4517,7 +4510,7 @@ int sqlite3changeset_apply_strm(
void *pCtx /* First argument passed to xConflict */
){
return sqlite3changeset_apply_v2_strm(
db, xInput, pIn, xFilter, xConflict, pCtx, 0, 0, 0
db, xInput, pIn, xFilter, xConflict, pCtx, 0, 0
);
}
+2 -30
View File
@@ -1095,13 +1095,6 @@ void sqlite3changegroup_delete(sqlite3_changegroup*);
** is only allocated and populated if one or more conflicts were encountered
** while applying the patchset. See comments surrounding the sqlite3_rebaser
** APIs for further details.
**
** The behavior of sqlite3changeset_apply_v2() and its streaming equivalent
** may be modified by passing a combination of
** [SQLITE_CHANGESETAPPLY_NOSAVEPOINT | supported flags] as the 9th parameter.
**
** Note that the sqlite3changeset_apply_v2() API is still <b>experimental</b>
** and therefore subject to change.
*/
int sqlite3changeset_apply(
sqlite3 *db, /* Apply change to "main" db of this handle */
@@ -1132,28 +1125,9 @@ int sqlite3changeset_apply_v2(
sqlite3_changeset_iter *p /* Handle describing change and conflict */
),
void *pCtx, /* First argument passed to xConflict */
void **ppRebase, int *pnRebase, /* OUT: Rebase data */
int flags /* Combination of SESSION_APPLY_* flags */
void **ppRebase, int *pnRebase
);
/*
** CAPI3REF: Flags for sqlite3changeset_apply_v2
**
** The following flags may passed via the 9th parameter to
** [sqlite3changeset_apply_v2] and [sqlite3changeset_apply_v2_strm]:
**
** <dl>
** <dt>SQLITE_CHANGESETAPPLY_NOSAVEPOINT <dd>
** Usually, the sessions module encloses all operations performed by
** a single call to apply_v2() or apply_v2_strm() in a [SAVEPOINT]. The
** SAVEPOINT is committed if the changeset or patchset is successfully
** applied, or rolled back if an error occurs. Specifying this flag
** causes the sessions module to omit this savepoint. In this case, if the
** caller has an open transaction or savepoint when apply_v2() is called,
** it may revert the partially applied changeset by rolling it back.
*/
#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001
/*
** CAPI3REF: Constants Passed To The Conflict Handler
**
@@ -1414,7 +1388,6 @@ void sqlite3rebaser_delete(sqlite3_rebaser *p);
** <table border=1 style="margin-left:8ex;margin-right:8ex">
** <tr><th>Streaming function<th>Non-streaming equivalent</th>
** <tr><td>sqlite3changeset_apply_strm<td>[sqlite3changeset_apply]
** <tr><td>sqlite3changeset_apply_strm_v2<td>[sqlite3changeset_apply_v2]
** <tr><td>sqlite3changeset_concat_strm<td>[sqlite3changeset_concat]
** <tr><td>sqlite3changeset_invert_strm<td>[sqlite3changeset_invert]
** <tr><td>sqlite3changeset_start_strm<td>[sqlite3changeset_start]
@@ -1524,8 +1497,7 @@ int sqlite3changeset_apply_v2_strm(
sqlite3_changeset_iter *p /* Handle describing change and conflict */
),
void *pCtx, /* First argument passed to xConflict */
void **ppRebase, int *pnRebase,
int flags
void **ppRebase, int *pnRebase
);
int sqlite3changeset_concat_strm(
int (*xInputA)(void *pIn, void *pData, int *pnData),
+6 -22
View File
@@ -731,34 +731,18 @@ static int SQLITE_TCLAPI testSqlite3changesetApply(
TestStreamInput sStr;
void *pRebase = 0;
int nRebase = 0;
int flags = 0; /* Flags for apply_v2() */
memset(&sStr, 0, sizeof(sStr));
sStr.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
/* Check for the -nosavepoint flag */
if( bV2 && objc>1 ){
const char *z1 = Tcl_GetString(objv[1]);
int n = strlen(z1);
if( n>1 && n<=12 && 0==sqlite3_strnicmp("-nosavepoint", z1, n) ){
flags = SQLITE_CHANGESETAPPLY_NOSAVEPOINT;
objc--;
objv++;
}
}
if( objc!=4 && objc!=5 ){
const char *zMsg;
if( bV2 ){
zMsg = "?-nosavepoint? DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?";
}else{
zMsg = "DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?";
}
Tcl_WrongNumArgs(interp, 1, objv, zMsg);
Tcl_WrongNumArgs(interp, 1, objv,
"DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?"
);
return TCL_ERROR;
}
if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &info) ){
Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(objv[1]), 0);
Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(objv[2]), 0);
return TCL_ERROR;
}
db = *(sqlite3 **)info.objClientData;
@@ -775,7 +759,7 @@ static int SQLITE_TCLAPI testSqlite3changesetApply(
}else{
rc = sqlite3changeset_apply_v2(db, nChangeset, pChangeset,
(objc==5)?test_filter_handler:0, test_conflict_handler, (void *)&ctx,
&pRebase, &nRebase, flags
&pRebase, &nRebase
);
}
}else{
@@ -790,7 +774,7 @@ static int SQLITE_TCLAPI testSqlite3changesetApply(
rc = sqlite3changeset_apply_v2_strm(db, testStreamInput, (void*)&sStr,
(objc==5) ? test_filter_handler : 0,
test_conflict_handler, (void *)&ctx,
&pRebase, &nRebase, flags
&pRebase, &nRebase
);
}
}
+3
View File
@@ -71,6 +71,7 @@ LIBOBJ+= vdbe.o parse.o \
pager.o pcache.o pcache1.o pragma.o prepare.o printf.o \
random.o resolve.o rowset.o rtree.o \
select.o sqlite3rbu.o status.o stmt.o \
server.o \
table.o threads.o tokenize.o treeview.o trigger.o \
update.o userauth.o util.o vacuum.o \
vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbesort.o \
@@ -148,6 +149,8 @@ SRC = \
$(TOP)/src/resolve.c \
$(TOP)/src/rowset.c \
$(TOP)/src/select.c \
$(TOP)/src/server.c \
$(TOP)/src/server.h \
$(TOP)/src/status.c \
$(TOP)/src/shell.c.in \
$(TOP)/src/sqlite.h.in \
+85 -74
View File
@@ -1,19 +1,20 @@
C Avoid\sa\sNULL-pointer\sderef\sfollowing\sOOM.
D 2018-04-18T19:08:44.210
C Fix\san\serror\sin\sREADME-server-edition.html.
D 2018-03-31T18:43:20.756
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F Makefile.in 7016fc56c6b9bfe5daac4f34be8be38d8c0b5fab79ccbfb764d3b23bf1c6fff3
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
F Makefile.msc 43dd6ae2e5a2bb8df7bfc9ed85935129caffeafb8c1803d24c5d038b1e74c8ca
F README.md 7764d56778d567913ef11c82da9ab94aefa0826f7c243351e4e2d7adaef6f373
F VERSION 7169eb6959db9ad1b7004ae3b754ef6e703eb7d8dde3b07d2e63103413eb25fb
F Makefile.msc bdcad21b027a56a73e54a1121cfb9edd0a35c0abfa53aa12c2f996006ff99960
F README-server-edition.html b98409c486d6f02871b20a9e29e1e18cd050a02e03062569ffb051774b4d6861
F README.md 1d5342ebda97420f114283e604e5fe99b0da939d63b76d492eabbaae23488276
F VERSION cdf91ac446255ecf3d8f6d8c3ee40d64123235ae5b3cef29d344e61b45ec3759
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90
F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2
F autoconf/INSTALL 83e4a25da9fd053c7b3665eaaaf7919707915903
F autoconf/Makefile.am 2c274948734e03c51790ff51468f91db8d570bcca864284d9c6d6e777264cd7e
F autoconf/Makefile.msc 6a46d0659d6e4a25346102bcba40a7fb4b0b8b2dc4faabdf0187488c6dd580d6
F autoconf/Makefile.msc 1223d1520e0b833041ad87b377fae61cc3e08d14c5aae4c1a9e36249225bd4e6
F autoconf/README.first 6c4f34fe115ff55d4e8dbfa3cecf04a0188292f7
F autoconf/README.txt 4f04b0819303aabaa35fff5f7b257fb0c1ef95f1
F autoconf/configure.ac 18fca06f884213be062dd5e07c5297079cc45893d9cd3f522ce426e715033e3d
@@ -32,7 +33,7 @@ F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63
F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977
F config.h.in 6376abec766e9a0785178b1823b5a587e9f1ccbc
F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55
F configure 41d0e05b0d289c1c981aafe5c4070713c8e70b5a7d3472360764a3fce08a82a8 x
F configure 2c71f331b463e987567a2dd942f728534f1aa7a174551e08a7b31b328e9da4ff x
F configure.ac d4529ebb26ae046269334f1dac65f2b1d6927c2efe22b2ec24dce24dfe4f83dd
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
F doc/lemon.html 278113807f49d12d04179a93fab92b5b917a08771152ca7949d34e928efa3941
@@ -114,7 +115,7 @@ F ext/fts5/fts5_config.c 5af9c360e99669d29f06492c370892394aba0857
F ext/fts5/fts5_expr.c c23a2e4c14c401a147c4a730460e5b37057627bf4be95515ee281cd87f4d277c
F ext/fts5/fts5_hash.c 32be400cf761868c9db33efe81a06eb19a17c5402ad477ee9efb51301546dd55
F ext/fts5/fts5_index.c 22b71d0e9e4b3ddd123a39ae27174e0012da2806f91b64087a68584f13f189de
F ext/fts5/fts5_main.c da46761a7e9b582083fcb9f5a3ee50086205fb91f4e68d984a9946e64218e297
F ext/fts5/fts5_main.c 24868f88ab2a865defbba7a92eebeb726cc991eb092b71b5f5508f180c72605b
F ext/fts5/fts5_storage.c 4bec8a1b3905978b22a67bca5f4a3cfdb94af234cf51efb36f4f2d733d278634
F ext/fts5/fts5_tcl.c 39bcbae507f594aad778172fa914cad0f585bf92fd3b078c686e249282db0d95
F ext/fts5/fts5_test_mi.c 65864ba1e5c34a61d409c4c587e0bbe0466eb4f8f478d85dc42a92caad1338e6
@@ -126,7 +127,7 @@ F ext/fts5/fts5_vocab.c 1cd79854cb21543e66507b25b0578bc1b20aa6a1349b7feceb8e8fed
F ext/fts5/fts5parse.y eb526940f892ade5693f22ffd6c4f2702543a9059942772526eac1fde256bb05
F ext/fts5/mkportersteps.tcl 5acf962d2e0074f701620bb5308155fa1e4a63ba
F ext/fts5/test/fts5_common.tcl b01c584144b5064f30e6c648145a2dd6bc440841
F ext/fts5/test/fts5aa.test 87f4b50e755b52c6192c76ceccf4247d462bb44b52fa17358f273d8ce5d975f0
F ext/fts5/test/fts5aa.test 6e2fdb0ee667c05f41921e7ec345cae874be651670900918e9ccc539514b9356
F ext/fts5/test/fts5ab.test 9205c839332c908aaad2b01ab8670ece8b161e8f2ec8a9fabf18ca9385880bb7
F ext/fts5/test/fts5ac.test a7aa7e1fefc6e1918aa4d3111d5c44a09177168e962c5fd2cca9620de8a7ed6d
F ext/fts5/test/fts5ad.test e8cf959dfcd57c8e46d6f5f25665686f3b6627130a9a981371dafdf6482790de
@@ -276,7 +277,7 @@ F ext/misc/completion.c 0d0bd16378415b982e7119baddef52a0d2cc25860c238a9d2832b0cc
F ext/misc/compress.c dd4f8a6d0baccff3c694757db5b430f3bbd821d8686d1fc24df55cf9f035b189
F ext/misc/csv.c 1a009b93650732e22334edc92459c4630b9fa703397cbb3c8ca279921a36ca11
F ext/misc/dbdump.c 22018e00eb50e9ebf9067c92d4e7162dc5006a3efc4e0c19bc3829825a1043b0
F ext/misc/eval.c 6ea9b22a5fa0dd973b67ca4e53555be177bc0b7b263aadf1024429457c82c0e3
F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2
F ext/misc/fileio.c 48c7751c78fc4cdd29d8c862fd2f3f98bbfefa2a3cf1ca1496df4bf02eb8cded
F ext/misc/fuzzer.c 7c64b8197bb77b7d64eff7cac7848870235d4c25
F ext/misc/ieee754.c f190d0cc5182529acb15babd177781be1ac1718c
@@ -290,11 +291,11 @@ F ext/misc/regexp.c a68d25c659bd2d893cd1215667bbf75ecb9dc7d4
F ext/misc/remember.c add730f0f7e7436cd15ea3fd6a90fd83c3f706ab44169f7f048438b7d6baa69c
F ext/misc/rot13.c 540a169cb0d74f15522a8930b0cccdcb37a4fd071d219a5a083a319fc6e8db77
F ext/misc/scrub.c db9fff56fed322ca587d73727c6021b11ae79ce3f31b389e1d82891d144f22ad
F ext/misc/series.c c7197db304f7009b08d6459a9de02e7f51ad0e1a3fdacbc1ebf5252a9a346959
F ext/misc/series.c f3c0dba5c5c749ce1782b53076108f87cf0b71041eb6023f727a9c50681da564
F ext/misc/sha1.c 0b9e9b855354910d3ca467bf39099d570e73db56
F ext/misc/shathree.c 9e960ba50483214c6a7a4b1517f8d8cef799e9db381195178c3fd3ad207e10c0
F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
F ext/misc/spellfix.c 54d650f44f3a69a851814791bd4d304575cdbbf78d96d4f0801b44a8f31a58c5
F ext/misc/spellfix.c b3a644285cb008f3c10ed4cf04e17adcbc7d62c3911c79d786dfc91c177534f0
F ext/misc/sqlar.c 57d5bc45cd5492208e451f697404be88f8612527d64c9d42f96b325b64983d74
F ext/misc/stmt.c 6f16443abb3551e3f5813bb13ba19a30e7032830015b0f92fe0c0453045c0a11
F ext/misc/totype.c 4a167594e791abeed95e0a8db028822b5e8fe512
@@ -379,7 +380,7 @@ F ext/rtree/sqlite3rtree.h 9c5777af3d2921c7b4ae4954e8e5697502289d28
F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de
F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
F ext/session/changeset.c 4ccbaa4531944c24584bf6a61ba3a39c62b6267a
F ext/session/session1.test 4532116484f525110eb4cfff7030c59354c0cde9def4d109466b0df2b35ad5cc
F ext/session/session1.test 736d7ff178662f0b717c37f46531b84a5ce0210ccb0c4edf629c55dbcbbc3ea1
F ext/session/session2.test 284de45abae4cc1082bc52012ee81521d5ac58e0
F ext/session/session3.test ce9ce3dfa489473987f899e9f6a0f2db9bde3479
F ext/session/session4.test 6778997065b44d99c51ff9cece047ff9244a32856b328735ae27ddef68979c40
@@ -393,9 +394,9 @@ F ext/session/sessionC.test 97556f5164ac29f2344b24bd7de6a3a35a95c390
F ext/session/sessionD.test d3617e29aa15c9413aee5286d99587633245d58d2ad28f3f331c822735418a22
F ext/session/sessionE.test 0a616c4ad8fd2c05f23217ebb6212ef80b7fef30f5f086a6633a081f93e84637
F ext/session/sessionF.test c2f178d4dfd723a5fd94a730ea2ccb44c669e3ce
F ext/session/sessionG.test 3edde849c4071078d92bd682c836186f6e4e5a3fb6bcf3fc1de1a7caa5e4427d
F ext/session/sessionG.test 63f9a744341d670775af29e4f19c1ef09a4810798400f28cd76704803a2e56ff
F ext/session/sessionH.test 332b60e4c2e0a680105e11936201cabe378216f307e2747803cea56fa7d9ebae
F ext/session/session_common.tcl ee925e0d233677e45e395fb1f559b84068ce7baa8aa1034441739d3e87ee249c
F ext/session/session_common.tcl 748141b02042b942e04a7afad9ffb2212a3997de536ed95f6dec7bb5018ede2c
F ext/session/session_speed_test.c edc1f96fd5e0e4b16eb03e2a73041013d59e8723
F ext/session/sessionat.test efe88965e74ff1bc2af9c310b28358c02d420c1fb2705cc7a28f0c1cc142c3ec
F ext/session/sessiondiff.test ad13dd65664bae26744e1f18eb3cbd5588349b7e9118851d8f9364248d67bcec
@@ -404,16 +405,16 @@ F ext/session/sessionfault2.test 555a8504de03d59b369ef20209585da5aeb2671dedabc45
F ext/session/sessionrebase.test 4e1bcfd26fd8ed8ac571746f56cceeb45184f4d65490ea0d405227cfc8a9cba8
F ext/session/sessionstat1.test 41cd97c2e48619a41cdf8ae749e1b25f34719de638689221aa43971be693bf4e
F ext/session/sessionwor.test 2f3744236dc8b170a695b7d8ddc8c743c7e79fdc
F ext/session/sqlite3session.c 2d29bbd888599b94b2c8b31ff433675e008273a4d225b336508b18e6187fec1d
F ext/session/sqlite3session.h c01820d5b6e73e86d88008f4d1c1c7dfb83422963018292b864028a0400ceccf
F ext/session/test_session.c dba36c6c0153b22501112d3e8882b5c946cf617c955153b6712bd2f8ba1428c0
F ext/session/sqlite3session.c 4e21db8d2abb7960ded6f66e745671442e3ae2156a5ff8f7cf07567c507c324e
F ext/session/sqlite3session.h 85fd2dc3df1532b0695beb345e2ff375c2745a4654b405fcbe33afa18baa6cc7
F ext/session/test_session.c f253742ea01b089326f189b5ae15a5b55c1c9e97452e4a195ee759ba51b404d5
F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
F ext/userauth/userauth.c 3410be31283abba70255d71fd24734e017a4497f
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
F main.mk 63668484c95454af7fc04a384da27ac556f27368d6d0c345e405e1677c66768f
F main.mk 3b8fd9c7783cb6790a22d414ce0653bf1073bf4ebb025abbe18e50a946075ff2
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
@@ -427,35 +428,35 @@ F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
F src/alter.c cf7a8af45cb0ace672f47a1b29ab24092a9e8cd8d945a9974e3b5d925f548594
F src/analyze.c 71fbbeb7b25417592f54d869fe90c28b48e4cecb9926ef9b06d90fb0aec48941
F src/attach.c bbdf97bb366d94d2bafff8ef611b3bee7b5f54d695531790d896a7a17e126317
F src/attach.c f6f212c43dddba79dfcb723fb9470785f3ff55bde8953cd9d2546f3022070a41
F src/auth.c 6277d63837357549fe14e723490d6dc1a38768d71c795c5eb5c0f8a99f918f73
F src/backup.c faf17e60b43233c214aae6a8179d24503a61e83b
F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33
F src/btmutex.c 0e9ce2d56159b89b9bc8e197e023ee11e39ff8ca
F src/btree.c 9eb9531c65346bbfccf5325384b7db1849daf4db6601dcfe21ba5c5b20623b64
F src/btree.h 0866c0a08255142ea0e754aabd211c843cab32045c978a592a43152405ed0c84
F src/btree.c ac5e98b809c0e7e2d1840afa0908f6aa2542cf7bed631aab17199047a35b5588
F src/btree.h 6fb019c0097f90a5c02fffdf8217bc1eb86b8bd5f286a5bdf269e8cfa29ba668
F src/btreeInt.h 620ab4c7235f43572cf3ac2ac8723cbdf68073be4d29da24897c7b77dda5fd96
F src/build.c 91d548027a54044851299e719d60d6a2b6eaf3e3274678098cb73a6ab8c0fa19
F src/build.c 4b085737d385ab2f07e07a8a5ef64d7378dad112ecf36f6150388b80dd2dcdb5
F src/callback.c fe677cb5f5abb02f7a772a62a98c2f516426081df68856e8f2d5f950929b966a
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
F src/ctime.c 849d4cebe008cfc6e4799b034a172b4eaf8856b100739632a852732ba66eee48
F src/ctime.c bd9da3f1ff21b432564a16ef0b154cff03585dc43742842e99c58907c6cb4bef
F src/date.c ebe1dc7c8a347117bb02570f1a931c62dd78f4a2b1b516f4837d45b7d6426957
F src/dbpage.c 8db4c97f630e7d83f884ea75caf1ffd0988c160e9d530194d93721c80821e0f6
F src/dbstat.c edabb82611143727511a45ca0859b8cd037851ebe756ae3db289859dd18b6f91
F src/dbstat.c 7a4ba8518b6369ef3600c49cf9c918ad979acba610b2aebef1b656d649b96720
F src/delete.c 20c8788451dc737a967c87ea53ad43544d617f5b57d32ccce8bd52a0daf9e89b
F src/expr.c 2448a255ce627c4e772bd68cf5529877c2bdfb6b580803d5fadc8528bdf7c1ef
F src/expr.c 51500461dcd4d0873a938bf188d03076d6712d70ff00a0f0d65621edf4e521c6
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c d617daf66b5515e2b42c1405b2b4984c30ca50fb705ab164271a9bf66c69e331
F src/func.c 94f42cba2cc1c34aeaa441022ba0170ec3fec4bba54db4e0ded085c6dc0fdc51
F src/global.c 9bf034fd560bdd514715170ed8460bb7f823cec113f0569ef3f18a20c7ccd128
F src/global.c 01506976bd75e5e7b977207a6a05062e2dd0050012f8071be06bbea22ec6d69a
F src/hash.c a12580e143f10301ed5166ea4964ae2853d3905a511d4e0c44497245c7ce1f7a
F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4
F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
F src/insert.c 752740e4619416d4262f6e9e51cdb6af5965eb0c8e943832a5af77d41e2839c7
F src/insert.c b9ff71cc2913d1d57698a1e22bf853261a9a642baf62bdf40ddeb3809adb85b5
F src/legacy.c 134ab3e3fae00a0f67a5187981d6935b24b337bcf0f4b3e5c9fa5763da95bf4e
F src/loadext.c f6e4e416a736369f9e80eba609f0acda97148a8b0453784d670c78d3eed2f302
F src/main.c 10e3897f5d78cef6bcbd1eedc8ccc3fe9e9783d07e052d9d70e57364ded19274
F src/main.c ffe71c007fc943adfdc638796a80581fb8e2f035bcfa2312b579a2e852d836ec
F src/malloc.c 07295435093ce354c6d9063ac05a2eeae28bd251d2e63c48b3d67c12c76f7e18
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de
@@ -471,38 +472,40 @@ F src/mutex_noop.c 9d4309c075ba9cc7249e19412d3d62f7f94839c4
F src/mutex_unix.c aaf9ebc3f89df28483c52208497a99a02cc3650011422fc9d4c57e4392f7fe58
F src/mutex_w32.c 7670d770c94bbfe8289bec9d7f1394c5a00a57c37f892aab6b6612d085255235
F src/notify.c 9711a7575036f0d3040ba61bc6e217f13a9888e7
F src/os.c 1cb0d1d1b3a4267966dee6e292d2b2cdf88e47c0c59cebff27ecafac052dd165
F src/os.c 750d7dca7eff3d76566fc71057e6960316914e3557776e8f50d4314f01090317
F src/os.h 48388821692e87da174ea198bf96b1b2d9d83be5dfc908f673ee21fafbe0d432
F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85
F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586
F src/os_unix.c 2b53b0b8ddc580db096252c721729e5f5f2f355b4fc056f8f3fb328aeb3c9e8a
F src/os_unix.c e853b5922c4b4bc04bd181289bc2b9756f4bb0a3b5861554bc31937abb0e7a7b
F src/os_win.c eb03c6d52f893bcd7fdd4c6006674c13c1b5e49543fec98d605201af2997171c
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c 1bb6a57fa0465296a4d6109a1a64610a0e7adde1f3acf3ef539a9d972908ce8f
F src/pager.h c571b064df842ec8f2e90855dead9acf4cbe0d1b2c05afe0ef0d0145f7fd0388
F src/parse.y e3c4116efb7d693df412bd42a96c88c6463704d31a29342c2fa671f6e91ecb26
F src/pager.c b99ae56c331ea1129d06c0e6634daa4a7fa544dffe76a4f520febbc9b9afc0a8
F src/pager.h d0fcb55b76087aa8fd605492451edcf03a5aaaa5eac074db8c438ee9185ca832
F src/parse.y a3ab90377e3a58309802ccd866e4df7a9a0eb7d7405ec4c22f05dd352b9c363e
F src/pcache.c 135ef0bc6fb2e3b7178d49ab5c9176254c8a691832c1bceb1156b2fbdd0869bd
F src/pcache.h 072f94d29281cffd99e46c1539849f248c4b56ae7684c1f36626797fee375170
F src/pcache1.c 716975564c15eb6679e97f734cec1bfd6c16ac3d4010f05f1f8e509fc7d19880
F src/pragma.c bea56df3ae0637768c0da4fbbb8f2492f780980d95000034a105ff291bf7ca69
F src/pragma.h bb83728944b42f6d409c77f5838a8edbdb0fe83046c5496ffc9602b40340a324
F src/pragma.c 5a4145c6cef2710c4fc638768d0a6f48b085394c81c5b4b4baf2068d0ae81995
F src/pragma.h a59d572cbc35d210a610264a20403e209165d581bc087ceb3a75b8dc3c2553f7
F src/prepare.c b086fea6a1952db88beca31fdd621201ee5e4ce3f02905248cc3035a8174aa89
F src/printf.c d3b7844ddeb11fbbdd38dd84d09c9c1ac171d21fb038473c3aa97981201cc660
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
F src/resolve.c 66c73fcb7719b8ff0e841b58338f13604ff3e2b50a723f9b8f383595735262f6
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
F src/select.c 9257a7f26e6c9da27deae819098d1005f4e1c37e19b8f029292bd2b1aab35721
F src/shell.c.in 6c9e2c1136f3697eb75f5dce010e7af005f62b4e1fda6d1066c3a473eb922889
F src/sqlite.h.in aa9bd3ae4a077c7002059cb418271abe52214b0227b2a734bc44736b24cbcc40
F src/select.c e51efe5479d1cb4f48defe0b97cdba7391df42a755ba9592b9159510d03cf738
F src/server.c 70421e6acbb2279878606be160b45c7db78933d6ec320317a2e939218496deb9
F src/server.h f46be129ffe407cac9b7018e6d4851b04e685d59b6837c73a1fb69e6aab52e3a
F src/shell.c.in d6a07811aa9f3b10200c15ab8dd4b6b998849a3b0c8b125bfa980329a33c26a6
F src/sqlite.h.in 45150a75c20ad6f9d914cd6e59caf36453206b0f824d514f194b56236f2d63d7
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 83a3c4ce93d650bedfd1aa558cb85a516bd6d094445ee989740827d0d944368d
F src/sqliteInt.h ad14bfeab6c1ada3aa1181c6cc14a3e6a8f24b35ad96a2daa3d11100a5267236
F src/sqliteInt.h 59a8bd112bc0224d2319c8b3b195056d36db1f1e9e993756f934bea663b204ec
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
F src/tclsqlite.c 916a92de77ec5cbe27818ca194d8cf0c58aa7ad5b87527098f6aa5a6068800ce
F src/test1.c b2df6d7ed8ecb53680f7040163ba92b50c471ed8104d128c88c8e969a107887f
F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
F src/test1.c 1ab7cbbb6693e08364c1a9241e2aee17f8c4925e4cc52396be77ae6845a05828
F src/test2.c 824e16d2ff3b57dc3680a5635d049cc889492f95910368ec1ffe2ad44ca45a7f
F src/test3.c b8434949dfb8aff8dfa082c8b592109e77844c2135ed3c492113839b6956255b
F src/test4.c 18ec393bb4d0ad1de729f0b94da7267270f3d8e6
F src/test5.c 328aae2c010c57a9829d255dc099d6899311672d
@@ -516,7 +519,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 097c6189803886a1fb26ec37d8bc62b90512cb53ab79a1fb6d35196c1ec42ded
F src/test_config.c 432d0e740c9927a33094d12e7e2723b71071c93d6e53eca5b05d90b4ec0897af
F src/test_delete.c e2fe07646dff6300b48d49b2fee2fe192ed389e834dd635e3b3bac0ce0bf9f8f
F src/test_demovfs.c a0c3bdd45ed044115c2c9f7779e56eafff18741e
F src/test_devsym.c 1960abbb234b97e9b920f07e99503fc04b443f62bbc3c6ff2c2cea2133e3b8a2
@@ -556,15 +559,15 @@ F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c 5b0c661a85f783d35b9883830736eeb63be4aefc4f6b7d9cd081d48782c041e2
F src/treeview.c 14d5d1254702ec96876aa52642cb31548612384134970409fae333b25b39d6bb
F src/trigger.c a34539c69433276d37b0da9a89c117726ff2d292c0902895af1f393a983cd3a1
F src/update.c 97d4c9514229f540f8c441e124d5af7f93c5b030c9574539d01e99462e273998
F src/update.c a90a32ffc0100265b0693dbbdbe490756447af181f5ea2c138cce515b08c8795
F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5
F src/util.c d9eb0a6c4aae1b00a7369eadd7ca0bbe946cb4c953b6751aa20d357c2f482157
F src/vacuum.c 762ee9bbf8733d87d8cd06f58d950e881982e416f8c767334a40ffd341b6bff5
F src/vdbe.c 066a4e1de2ed83e253adfd2e97a684cf562eaa41d31ee7f3d3e4c8aea4485a55
F src/vdbe.c 6a454c0f6c43275e2fcc0b2ea85374e034dd21b7d5f8f9fe8994ebf6cefe9b61
F src/vdbe.h 134beb7a12a6213c00eba58febaede33447cc4441bc568a0d9c144b33fc3720a
F src/vdbeInt.h 95f7adfdc5c8f1353321f55a6c5ec00a90877e3b85af5159e393afb41ff54110
F src/vdbeapi.c 29d2baf9c1233131ec467d7bed1b7c8a03c27579048d768c4b04acf427838858
F src/vdbeaux.c 2756ac68ac259c416554100598fc291870063288cd7e1af22847f57b3e130e56
F src/vdbeaux.c 4115729898d68209562d5ed084244d893cc537312d877d51475cccef2f2800b2
F src/vdbeblob.c f5c70f973ea3a9e915d1693278a5f890dc78594300cf4d54e64f2b0917c94191
F src/vdbemem.c 414e28d3a7e2a8bee2bb247de115dcbc68e3cbac284d5862d077002f7a93bce1
F src/vdbesort.c 731a09e5cb9e96b70c394c1b7cf3860fbe84acca7682e178615eb941a3a0ef2f
@@ -574,9 +577,9 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c aa9cffc7a2bad6b826a86c8562dd4978398720ed41cb8ee7aa9d054eb8b456a0
F src/wal.h 8de5d2d3de0956d6f6cb48c83a4012d5f227b8fe940f3a349a4b7e85ebcb492a
F src/walker.c da987a20d40145c0a03c07d8fefcb2ed363becc7680d0500d9c79915591f5b1f
F src/where.c d6e5f2056e9a60251e79780fc598a5943e88a3c0fa0019d54922e59f99019287
F src/whereInt.h 2610cb87dd95509995b63decc674c60f2757697a206cfe0c085ee53d9c43cfff
F src/wherecode.c 982b7450c53fb272f61a1d20c93e960260ea4dfe8e2e9bacc190e2a041a1f1a4
F src/where.c 7cae47e813393d70c6d327fdf000fcb30f76b1b0b5a5b52ff6402e0c658de32c
F src/whereInt.h 82c04c5075308abbac59180c8bad5ecb45b07453981f60a53f3c7dee21e1e971
F src/wherecode.c e1aaadd8fec650037cfbf27d1b3470338fb3b58fec34d11082df16fe9a08fbd7
F src/whereexpr.c 53532be687e12f3cd314f1e204cd4fbdac7ad250e918a182b048121e16e828ae
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
@@ -1005,7 +1008,7 @@ F test/ioerr4.test f130fe9e71008577b342b8874d52984bd04ede2c
F test/ioerr5.test 2edfa4fb0f896f733071303b42224df8bedd9da4
F test/ioerr6.test a395a6ab144b26a9e3e21059a1ab6a7149cca65b
F test/istrue.test d6e659764da5ccc03adcdba18fe77d7917ba5e4abd04ef14bd4e4cf43e024b5b
F test/join.test 2ad9d7fe10e0cc06bc7803c22e5533be11cdadbc592f5f95d789a873b57a5a66
F test/join.test 730e3e8d511289531efca01f8684f98da1e6de51eacf95c5960d0c46e77719e3
F test/join2.test f5ea0fd3b0a441c8e439706339dcd17cec63a896a755c04a30bfd442ecce1190
F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0
F test/join4.test 1a352e4e267114444c29266ce79e941af5885916
@@ -1022,7 +1025,7 @@ F test/json102.test eeb54efa221e50b74a2d6fb9259963b48d7414dca3ce2fdfdeed45cb2848
F test/json103.test c5f6b85e69de05f6b3195f9f9d5ce9cd179099a0
F test/json104.test 877d5845f6303899b7889ea5dd1bea99076e3100574d5c536082245c5805dcaa
F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff
F test/kvtest.c 94da54bb66aae7a54e47cf7e4ea4acecc0f217560f79ad3abfcc0361d6d557ba
F test/kvtest.c 23452e653e6b0254dc2fd1d242d4c7c65644504de8951d7d60f7e4291c52c231
F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63
F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200
F test/like.test 11cfd7d4ef8625389df9efc46735ff0b0b41d5e62047ef0f3bc24c380d28a7a6
@@ -1069,7 +1072,7 @@ F test/malloc_common.tcl aac62499b76be719fac31e7a3e54a7fd53272e7f
F test/manydb.test 28385ae2087967aa05c38624cec7d96ec74feb3e
F test/mem5.test c6460fba403c5703141348cd90de1c294188c68f
F test/memdb.test c1f2a343ad14398d5d6debda6ea33e80d0dafcc7
F test/memdb1.test 61aa1dbdeea6320791d2ff42a9a6149d5716be674bf06ee0ffa0aad1bf3eb5f8
F test/memdb1.test fbe47f36c12725ebdd2760f846371e6eb09f403bd7236fbdddb21aa6e3c652b4
F test/memleak.test 10b9c6c57e19fc68c32941495e9ba1c50123f6e2
F test/memsubsys1.test 9e7555a22173b8f1c96c281ce289b338fcba2abe8b157f8798ca195bbf1d347e
F test/memsubsys2.test 3e4a8d0c05fd3e5fa92017c64666730a520c7e08
@@ -1084,7 +1087,7 @@ F test/misc4.test 0d8be3466adf123a7791a66ba2bc8e8d229e87f3
F test/misc5.test 60e1fc758a93cacd19eb2fafcd1d40d150a05047546c7a92389c98047d621901
F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
F test/misc7.test 567e223b6497da2226a0340befaf2d663c91ad57a48aede21a35a984a2882d41
F test/misc8.test 8fb0f31d7a8aed484d759773ab8ad12ec746a477f4a67394a4af0e677494c3ca
F test/misc8.test ba03aaa08f02d62fbb8d3b2f5595c1b33aa9bbc5
F test/misuse.test 9e7f78402005e833af71dcab32d048003869eca5abcaccc985d4f8dc1d86bcc7
F test/mjournal.test 9d86e697dcbc5da2c4e8caba9b176b5765fe65e80c88c278b8c09a917e436795
F test/mmap1.test d2cfc1635171c434dcff0ece2f1c8e0a658807ce
@@ -1141,7 +1144,7 @@ F test/parser1.test 391b9bf9a229547a129c61ac345ed1a6f5eb1854
F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
F test/permutations.test 10793f1de89a226fa22dde9ba9398de22571fee1bfb53a935a11be4aa014704f
F test/permutations.test 17d9cbfce2e7d0e2007a245cf88c3c48ee9531fd6008034442feeb0f11357132
F test/pragma.test 7c8cfc328a1717a95663cf8edb06c52ddfeaf97bb0aee69ae7457132e8d39e7d
F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
F test/pragma3.test 14c12bc5352b1e100e0b6b44f371053a81ccf8ed
@@ -1166,7 +1169,7 @@ F test/rdonly.test 64e2696c322e3538df0b1ed624e21f9a23ed9ff8
F test/regexp1.test 497ea812f264d12b6198d6e50a76be4a1973a9d8
F test/regexp2.test 40e894223b3d6672655481493f1be12012f2b33c
F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
F test/releasetest.tcl 5f15ab8056799e9a6e26a310d49236d2e774d6a30d0ec74601e18d4ce146b79c x
F test/releasetest.tcl 6aaa853f7a7bbdc458d4cb42c0425228729b0f3e5769e9b41088c08eee999a49 x
F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb
F test/rollback.test 06680159bc6746d0f26276e339e3ae2f951c64812468308838e0a3362d911eaa
F test/rollback2.test 8435d6ff0f13f51d2a4181c232e706005fa90fc5
@@ -1219,6 +1222,13 @@ F test/selectE.test a8730ca330fcf40ace158f134f4fe0eb00c7edbf
F test/selectF.test 21c94e6438f76537b72532fa9fd4710cdd455fc3
F test/selectG.test 089f7d3d7e6db91566f00b036cb353107a2cca6220eb1cb264085a836dae8840
F test/server1.test 46803bd3fe8b99b30dbc5ff38ffc756f5c13a118
F test/server2.test 787ba6044b5e9b2c3d60588bc5596054e6a5c3a7dc64bc2fb0e62f6616c142a9
F test/server3.test c3ae4ca7a6e7df870bfcd2450a9815507eaa80b9cdc44ee6c7975d48311505d4
F test/server_common.tcl c491d0f509b94a5cca845d45ca3bb47e464ad3a4bc89641982269112d0f1f3f4
F test/servercrash.test 1cbd2f98cadee2d8d42ed85ad76fbcf48958fedd537c82221838cd9bc6899dae
F test/serverfreelist.test 2e554001145170094a19731a8ce2981d040cf44c947542b35d130e6e31256fca
F test/serverlimit.test 4bc013c0b991956486ddbff6ea3bee78a0d14a3d8091f5ec00e2bd34a7fa9aa7
F test/serverreadonly.test 97040670597948a695b1973537d770417589f1998bcbb3959302aaee3c211250
F test/session.test 78fa2365e93d3663a6e933f86e7afc395adf18be
F test/sessionfuzz-data1.db 1f8d5def831f19b1c74571037f0d53a588ea49a6c4ca2a028fc0c27ef896dbcb
F test/sessionfuzz.c b0fcdcf757451957e17396a3af5171f1fdf9b2babc81da9fa35675df46c4729a
@@ -1261,8 +1271,7 @@ F test/sort.test c2adc635c2564241fefec0b3a68391ef6868fd3b
F test/sort2.test cc23b7c19d684657559e8a55b02f7fcee03851d0
F test/sort3.test 1480ed7c4c157682542224e05e3b75faf4a149e5
F test/sort4.test 5c34d9623a4ae5921d956dfa2b70e77ed0fc6e5c
F test/sort5.test 6b43ae0e2169b5ceed441844492e55ba7f1ae0790528395ddf7888ab3094525d
F test/sorterref.test a13ed207a0eea3c7898f308f979bfb518f68c598ec737d2c494dfd3deaa83506
F test/sort5.test 30cc17768e0c06ecb048e08efec59c11811fd186
F test/sortfault.test d4ccf606a0c77498e2beb542764fd9394acb4d66
F test/speed1.test f2974a91d79f58507ada01864c0e323093065452
F test/speed1p.explain d841e650a04728b39e6740296b852dccdca9b2cb
@@ -1272,7 +1281,7 @@ F test/speed3.test 694affeb9100526007436334cf7d08f3d74b85ef
F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715
F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
F test/speed4p.test 377a0c48e5a92e0b11c1c5ebb1bc9d83a7312c922bc0cb05970ef5d6a96d1f0c
F test/speedtest1.c 20cc4028b0e88392b5a635c2ea5d5e777d569bf7258aead37f8be7a886c38344
F test/speedtest1.c a5faf4cbe5769eee4b721b3875cb3f12520a9b99d9026b1063b47c39603375b8
F test/spellfix.test 951a6405d49d1a23d6b78027d3877b4a33eeb8221dcab5704b499755bb4f552e
F test/spellfix2.test dfc8f519a3fc204cb2dfa8b4f29821ae90f6f8c3
F test/spellfix3.test 0f9efaaa502a0e0a09848028518a6fb096c8ad33
@@ -1286,7 +1295,7 @@ F test/stmt.test 54ed2cc0764bf3e48a058331813c3dbd19fc1d0827c3d8369914a5d8f564ec7
F test/stmtvtab1.test 6873dfb24f8e79cbb5b799b95c2e4349060eb7a3b811982749a84b359468e2d5
F test/subjournal.test 8d4e2572c0ee9a15549f0d8e40863161295107e52f07a3e8012a2e1fdd093c49
F test/subquery.test d7268d193dd33d5505df965399d3a594e76ae13f
F test/subquery2.test 8250dfd6a773b04c7a5c37ac63276f62b329157ce171244d0cbe1acc365e3303
F test/subquery2.test 438f8a7da1457277b22e4176510f7659b286995f
F test/subselect.test 0966aa8e720224dbd6a5e769a3ec2a723e332303
F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a
F test/subtype1.test 7fe09496352f97053af1437150751be2d0a0cae8
@@ -1306,13 +1315,13 @@ F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126
F test/tableopts.test dba698ba97251017b7c80d738c198d39ab747930
F test/tclsqlite.test 5337e8890b96dad1ee541b15fbeec32e6bac2fe7fa096f91089057385aadba9b
F test/tempdb.test 4cdaa23ddd8acb4d79cbb1b68ccdfd09b0537aaba909ca69a876157c2a2cbd08
F test/tempdb2.test 4749545409c6d7438b435c3f05cdd139cf4145a954a6908d19e3443ffd8724b3
F test/tempdb2.test 27e41ed540b2f9b056c2e77e9bddc1b875358507
F test/tempfault.test 0c0d349c9a99bf5f374655742577f8712c647900
F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
F test/temptable2.test d2940417496e2b9548e01d09990763fbe88c316504033256d51493e1f1a5ce6a
F test/temptable2.test cd396beb41117a5302fff61767c35fa4270a0d5e
F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc
F test/tester.tcl 94901a4625d9a2229666dd5c44120ddf7f0fb639470710ef74a4cefc7b039e07
F test/tester.tcl f6342dac83dbc6ca42bac34a5f65a70df4b18e03e98bcb230efd35e621b671c0
F test/thread001.test b61a29dd87cf669f5f6ac96124a7c97d71b0c80d9012746072055877055cf9ef
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
@@ -1488,7 +1497,7 @@ F test/triggerA.test fe5597f47ee21bacb4936dc827994ed94161e332
F test/triggerB.test 56780c031b454abac2340dbb3b71ac5c56c3d7fe
F test/triggerC.test 302d8995f5ffe63bbc15053abb3ef7a39cf5a092
F test/triggerD.test 8e7f3921a92a5797d472732108109e44575fa650
F test/triggerE.test d9e9b364dfd527c84ac0de53045406325487feecb32888d482eca64421a50d99
F test/triggerE.test 15fa63f1097db1f83dd62d121616006978063d1f
F test/triggerF.test 6a8c22bd058cf467f0c7d112afe87f7a8c579c0c4681b914b8f19020f48528a4
F test/triggerG.test d5caeef6144ede2426dd13211fd72248241ff2ebc68e12a4c0bf30f5faa21499
F test/tt3_checkpoint.c 9e75cf7c1c364f52e1c47fd0f14c4340a9db0fe1
@@ -1607,7 +1616,7 @@ F test/with2.test e0030e2f0267a910d6c0e4f46f2dfe941c1cc0d4f659ba69b3597728e7e8f1
F test/with3.test e71604a0e53cba82bc04c703987cb1d6751ec0b6
F test/with4.test 257be66c0c67fee1defbbac0f685c3465e2cad037f21ce65f23f86084f198205
F test/withM.test 693b61765f2b387b5e3e24a4536e2e82de15ff64
F test/without_rowid1.test 1cb47a1a5ba5b2946f18703fabf9fb2a237b0a8180538793ecbaed834d0df765
F test/without_rowid1.test 06b7215130882d6a072233820dd364c874c4fd69221e8fc756ec471009192874
F test/without_rowid2.test af260339f79d13cb220288b67cd287fbcf81ad99
F test/without_rowid3.test 2724c787a51a5dce09d078453a758117b4b728f1
F test/without_rowid4.test 4e08bcbaee0399f35d58b5581881e7a6243d458a
@@ -1617,7 +1626,7 @@ F test/wordcount.c cb589cec469a1d90add05b1f8cee75c7210338d87a5afd65260ed5c0f4bbf
F test/writecrash.test f1da7f7adfe8d7f09ea79b42e5ca6dcc41102f27f8e334ad71539501ddd910cc
F test/zeroblob.test 3857870fe681b8185654414a9bccfde80b62a0fa
F test/zerodamage.test 9c41628db7e8d9e8a0181e59ea5f189df311a9f6ce99cc376dc461f66db6f8dc
F test/zipfile.test a61f6ba6dbaaf4983849df84a31df140c7ddd1362e2fa9ecd3cdf5cd123b7f18
F test/zipfile.test 2a923f6ead6a0f9b61d936881f3ee2aeaabe15fc65c196456f58ea9b4b450f9b
F test/zipfile2.test fc2f08d5ec19c18c83289fbed32e378dc5116519972166e57a244da7bf2e5805
F test/zipfilefault.test 44d4d7a7f7cca7521d569d7f71026b241d65a6b1757aa409c1a168827edbbc2c
F tool/GetFile.cs a15e08acb5dd7539b75ba23501581d7c2b462cb5
@@ -1637,7 +1646,7 @@ F tool/genfkey.README cf68fddd4643bbe3ff8e31b8b6d8b0a1b85e20f4
F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5
F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
F tool/kvtest-speed.sh 4761a9c4b3530907562314d7757995787f7aef8f
F tool/lemon.c c1a87d15983f96851b253707985dba8783fbe41ba21ba1b76720e06c8a262206
F tool/lemon.c 7f7735326ca9c3b48327b241063cee52d35d44e20ebe1b3624a81658052a4d39
F tool/lempar.c 468a155e8729cfbccfe1d85bf60d064f1dab76167a51149ec5c7928a2de63953
F tool/libvers.c caafc3b689638a1d88d44bc5f526c2278760d9b9
F tool/loadfts.c c3c64e4d5e90e8ba41159232c2189dba4be7b862
@@ -1646,17 +1655,17 @@ F tool/max-limits.c cbb635fbb37ae4d05f240bfb5b5270bb63c54439
F tool/mkautoconfamal.sh 422fc365358a2e92876ffc62971a0ff28ed472fc8bcf9de0df921c736fdeca5e
F tool/mkccode.tcl 86463e68ce9c15d3041610fedd285ce32a5cf7a58fc88b3202b8b76837650dbe x
F tool/mkctimec.tcl dd183b73ae1c28249669741c250525f0407e579a70482371668fd5f130d9feb3
F tool/mkkeywordhash.c 2e852ac0dfdc5af18886dc1ce7e9676d11714ae3df0a282dc7d90b3a0fe2033c
F tool/mkmsvcmin.tcl cad0c7b54d7dd92bc87d59f36d4cc4f070eb2e625f14159dc2f5c4204e6a13ea
F tool/mkkeywordhash.c 969c50301da61d73c4c6d0661c1d1abc6c25ba35f1212832d0b12965f90505ab
F tool/mkmsvcmin.tcl 8baf26690b80d861d0ac341b29880eec6ade39e4f11fe690271ded9cb90563a3
F tool/mkopcodec.tcl d1b6362bd3aa80d5520d4d6f3765badf01f6c43c
F tool/mkopcodeh.tcl 4ee2a30ccbd900dc4d5cdb61bdab87cd2166cd2affcc78c9cc0b8d22a65b2eee
F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa
F tool/mkpragmatab.tcl 2144bc8550a6471a029db262a132d2df4b9e0db61b90398bf64f5b7b3f8d92cd
F tool/mkpragmatab.tcl c249dee507fc499d43072b14eb8b0373204dca952e770b9b9924c211bca0169a
F tool/mkshellc.tcl 1f45770aea226ac093a9c72f718efbb88a2a2833409ec2e1c4cecae4202626f5
F tool/mksourceid.c d458f9004c837bee87a6382228ac20d3eae3c49ea3b0a5aace936f8b60748d3b
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
F tool/mksqlite3c-noext.tcl fef88397668ae83166735c41af99d79f56afaabb
F tool/mksqlite3c.tcl a03cee30de81a2e67b93e5c659f24113a003677c557daeb008205c8e6d4345d6
F tool/mksqlite3c.tcl caf7dec7adbdfd0d76893bafa3e09a30401cc649b4c7bfcc4234eadede944469
F tool/mksqlite3h.tcl 080873e3856eceb9d289a08a00c4b30f875ea3feadcbece796bd509b1532792c
F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b
F tool/mkvsix.tcl b9e0777a213c23156b6542842c238479e496ebf5
@@ -1668,6 +1677,7 @@ F tool/replace.tcl 60f91e8dd06ab81f74d213ecbd9c9945f32ac048
F tool/restore_jrnl.tcl 6957a34f8f1f0f8285e07536225ec3b292a9024a
F tool/rollback-test.c 9fc98427d1e23e84429d7e6d07d9094fbdec65a5
F tool/run-speed-test.sh f95d19fd669b68c4c38b6b475242841d47c66076
F tool/se_perf_test.tcl e2a5081f9378f188a431c4a85c5f3f9e99719027df5a6faa405c981b267ad8fd
F tool/showdb.c e6bc9dba233bf1b57ca0a525a2bba762db4e223de84990739db3f09c46151b1e
F tool/showjournal.c 5bad7ae8784a43d2b270d953060423b8bd480818
F tool/showlocks.c 9920bcc64f58378ff1118caead34147201f48c68
@@ -1691,6 +1701,7 @@ F tool/srcck1.c 371de5363b70154012955544f86fdee8f6e5326f
F tool/stack_usage.tcl f8e71b92cdb099a147dad572375595eae55eca43
F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d
F tool/symbols.sh c5a617b8c61a0926747a56c65f5671ef8ac0e148
F tool/tserver.c ac67b8fe175850b780c5bd1888c2bb2f0f5e24096f040db7fc1a2af7a4908f2b
F tool/varint.c 5d94cb5003db9dbbcbcc5df08d66f16071aee003
F tool/vdbe-compress.tcl 5926c71f9c12d2ab73ef35c29376e756eb68361c
F tool/vdbe_profile.tcl 246d0da094856d72d2c12efec03250d71639d19f
@@ -1718,7 +1729,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 902a40897f74ac8a3bc72ef84c2161ab308b5601381cc9eea18147bfefa978ce
R 4bf2a8a4f284f188f9cc406a1fead38f
U drh
Z 857937520ddb8fedd9d522f195cdf4dc
P 337a0b67e30f1030fdc59f712e5914f4801b0e9e4ae19a1e82c10b73eb3f4773
R 5045a1c8302fea1a74fa92a9ce0972dd
U dan
Z d3c495daa23a1fc63d551424dd8817b7
+1 -1
View File
@@ -1 +1 @@
413015c029d850d4ce7e66be1f59b57f291254240a958856378a62f5ac4a5092
754ad35cd26da361e2ed736b0e400497714a0db9b7fd05fd24e7803b6f478263
-3
View File
@@ -502,9 +502,6 @@ int sqlite3FixSrcList(
if( sqlite3FixSelect(pFix, pItem->pSelect) ) return 1;
if( sqlite3FixExpr(pFix, pItem->pOn) ) return 1;
#endif
if( pItem->fg.isTabFunc && sqlite3FixExprList(pFix, pItem->u1.pFuncArg) ){
return 1;
}
}
return 0;
}
+384 -28
View File
@@ -266,6 +266,15 @@ static int hasReadConflicts(Btree *pBtree, Pgno iRoot){
}
#endif /* #ifdef SQLITE_DEBUG */
#ifdef SQLITE_SERVER_EDITION
/*
** Return true if the b-tree uses free-list format 2. Or false otherwise.
*/
static int btreeFreelistFormat2(BtShared *pBt){
return (pBt->pPage1->aData[18] > 2);
}
#endif
/*
** Query to see if Btree handle p may obtain a lock of type eLock
** (READ_LOCK or WRITE_LOCK) on the table with root-page iTab. Return
@@ -2990,7 +2999,7 @@ static int lockBtree(BtShared *pBt){
assert( sqlite3_mutex_held(pBt->mutex) );
assert( pBt->pPage1==0 );
rc = sqlite3PagerSharedLock(pBt->pPager);
rc = sqlite3PagerSharedLock(pBt->pPager, pBt->db->readonlyTrans);
if( rc!=SQLITE_OK ) return rc;
rc = btreeGetPage(pBt, 1, &pPage1, 0);
if( rc!=SQLITE_OK ) return rc;
@@ -3007,6 +3016,15 @@ static int lockBtree(BtShared *pBt){
u32 pageSize;
u32 usableSize;
u8 *page1 = pPage1->aData;
u8 i18 = page1[18];
u8 i19 = page1[19];
#ifdef SQLITE_SERVER_EDITION
if( i18==i19 && i18>2 ){
i18 -= 2;
i19 -= 2;
}
#endif
rc = SQLITE_NOTADB;
/* EVIDENCE-OF: R-43737-39999 Every valid SQLite database file begins
** with the following 16 bytes (in hex): 53 51 4c 69 74 65 20 66 6f 72 6d
@@ -3016,17 +3034,17 @@ static int lockBtree(BtShared *pBt){
}
#ifdef SQLITE_OMIT_WAL
if( page1[18]>1 ){
if( i18>1 ){
pBt->btsFlags |= BTS_READ_ONLY;
}
if( page1[19]>1 ){
if( i19>1 ){
goto page1_init_failed;
}
#else
if( page1[18]>2 ){
if( i18>2 ){
pBt->btsFlags |= BTS_READ_ONLY;
}
if( page1[19]>2 ){
if( i19>2 ){
goto page1_init_failed;
}
@@ -3038,7 +3056,7 @@ static int lockBtree(BtShared *pBt){
** may not be the latest version - there may be a newer one in the log
** file.
*/
if( page1[19]==2 && (pBt->btsFlags & BTS_NO_WAL)==0 ){
if( i19==2 && (pBt->btsFlags & BTS_NO_WAL)==0 ){
int isOpen = 0;
rc = sqlite3PagerOpenWal(pBt->pPager, &isOpen);
if( rc!=SQLITE_OK ){
@@ -5721,6 +5739,255 @@ int sqlite3BtreePrevious(BtCursor *pCur, int flags){
return SQLITE_OK;
}
#ifdef SQLITE_SERVER_EDITION
#define SERVER_DEFAULT_FREELISTS 16
#define SERVER_DEFAULT_FREELIST_SIZE 128
/*
** Allocate the free-node and the first SERVER_DEFAULT_FREELISTS
** trunk pages.
*/
static int allocateServerFreenode(BtShared *pBt){
int rc;
MemPage *pPage1 = pBt->pPage1;
rc = sqlite3PagerWrite(pPage1->pDbPage);
if( rc==SQLITE_OK ){
Pgno pgnoNode = (++pBt->nPage);
MemPage *pNode = 0;
int i;
put4byte(&pPage1->aData[32], pgnoNode);
rc = btreeGetUnusedPage(pBt, pgnoNode, &pNode, PAGER_GET_NOCONTENT);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pNode->pDbPage);
}
if( rc==SQLITE_OK ){
put4byte(&pNode->aData[0], 0);
put4byte(&pNode->aData[4], SERVER_DEFAULT_FREELISTS);
}
for(i=0; rc==SQLITE_OK && i<SERVER_DEFAULT_FREELISTS; i++){
MemPage *pTrunk = 0;
Pgno pgnoTrunk;
if( ++pBt->nPage==PENDING_BYTE_PAGE(pBt) ) pBt->nPage++;
pgnoTrunk = pBt->nPage;
rc = btreeGetUnusedPage(pBt, pgnoTrunk, &pTrunk, PAGER_GET_NOCONTENT);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pTrunk->pDbPage);
}
if( rc==SQLITE_OK ){
memset(pTrunk->aData, 0, 8);
put4byte(&pNode->aData[8+i*4], pgnoTrunk);
}
releasePage(pTrunk);
}
releasePage(pNode);
}
return rc;
}
/*
** Return a reference to the first trunk page in one of the database free-lists.
** Allocate the database free-lists if required.
*/
static int findServerTrunk(BtShared *pBt, int bAlloc, MemPage **ppTrunk){
MemPage *pPage1 = pBt->pPage1;
MemPage *pNode = 0; /* The node page */
MemPage *pTrunk = 0; /* The returned page */
Pgno iNode; /* Page number of node page */
int rc = SQLITE_OK;
/* If the node page and free-list trunks have not yet been allocated, allocate
** them now. */
pPage1 = pBt->pPage1;
iNode = get4byte(&pPage1->aData[32]);
if( iNode==0 ){
rc = allocateServerFreenode(pBt);
iNode = get4byte(&pPage1->aData[32]);
}
/* Grab the node page */
if( rc==SQLITE_OK ){
rc = btreeGetUnusedPage(pBt, iNode, &pNode, 0);
}
if( rc==SQLITE_OK ){
int nList; /* Number of free-lists in this db */
int i;
/* Try to lock a free-list trunk. If bAlloc is true, it has to be a
** free-list trunk with at least one entry in the free-list. */
nList = (int)get4byte(&pNode->aData[4]);
for(i=0; i<nList; i++){
Pgno iTrunk = get4byte(&pNode->aData[8+i*4]);
if( SQLITE_OK==sqlite3PagerPagelock(pBt->pPager, iTrunk, 1) ){
rc = btreeGetUnusedPage(pBt, iTrunk, &pTrunk, 0);
if( rc==SQLITE_OK && bAlloc ){
if( !get4byte(&pTrunk->aData[0]) && !get4byte(&pTrunk->aData[4]) ){
releasePage(pTrunk);
pTrunk = 0;
}
}
if( rc!=SQLITE_OK || pTrunk ) break;
}
}
/* No free pages in any free-list. Or perhaps we were locked out. In
** either case, try to allocate more from the end of the file now. */
if( i==nList ){
assert( rc==SQLITE_OK && pTrunk==0 );
rc = sqlite3PagerWrite(pPage1->pDbPage);
for(i=0; rc==SQLITE_OK && i<nList; i++){
/* Add some free pages to each free-list. No server-locks are required
** to do this as we have a write-lock on page 1 - guaranteeing
** exclusive access to the db file. */
MemPage *pT = 0;
Pgno iTrunk = get4byte(&pNode->aData[8+i*4]);
rc = btreeGetUnusedPage(pBt, iTrunk, &pT, 0);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pT->pDbPage);
}
if( rc==SQLITE_OK ){
int iPg = get4byte(&pT->aData[4]);
for(/*no-op*/; iPg<SERVER_DEFAULT_FREELIST_SIZE; iPg++){
if( ++pBt->nPage==PENDING_BYTE_PAGE(pBt) ) pBt->nPage++;
put4byte(&pT->aData[8+iPg*4], pBt->nPage);
}
put4byte(&pT->aData[4], iPg);
if( pTrunk==0 ){
pTrunk = pT;
pT = 0;
}
}
releasePage(pT);
}
if( rc==SQLITE_OK ){
MemPage *pLast = 0;
rc = btreeGetUnusedPage(pBt, pBt->nPage, &pLast, 0);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pLast->pDbPage);
releasePage(pLast);
put4byte(28 + (u8*)pPage1->aData, pBt->nPage);
}
}
}
}
releasePage(pNode);
if( rc==SQLITE_OK ){
assert( pTrunk );
rc = sqlite3PagerWrite(pTrunk->pDbPage);
}
if( rc!=SQLITE_OK ){
releasePage(pTrunk);
pTrunk = 0;
}
*ppTrunk = pTrunk;
return rc;
}
static int allocateServerPage(
BtShared *pBt, /* The btree */
MemPage **ppPage, /* Store pointer to the allocated page here */
Pgno *pPgno, /* Store the page number here */
Pgno nearby, /* Search for a page near this one */
u8 eMode /* BTALLOC_EXACT, BTALLOC_LT, or BTALLOC_ANY */
){
int rc; /* Return code */
MemPage *pTrunk = 0; /* The node page */
Pgno pgnoNew = 0;
#ifdef SQLITE_DEBUG
int nRef = sqlite3PagerRefcount(pBt->pPager);
#endif
assert( eMode==BTALLOC_ANY );
assert( sqlite3_mutex_held(pBt->mutex) );
*ppPage = 0;
rc = findServerTrunk(pBt, 1, &pTrunk);
if( rc==SQLITE_OK ){
int nFree; /* Number of free pages on this trunk page */
nFree = (int)get4byte(&pTrunk->aData[4]);
if( nFree==0 ){
pgnoNew = get4byte(&pTrunk->aData[0]);
assert( pgnoNew );
}else{
nFree--;
pgnoNew = get4byte(&pTrunk->aData[8+4*nFree]);
put4byte(&pTrunk->aData[4], (u32)nFree);
releasePage(pTrunk);
pTrunk = 0;
}
}
if( rc==SQLITE_OK ){
MemPage *pNew = 0;
int flags = pTrunk ? 0 : PAGER_GET_NOCONTENT;
rc = btreeGetUnusedPage(pBt, pgnoNew, &pNew, flags);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pNew->pDbPage);
if( rc!=SQLITE_OK ){
releasePage(pNew);
pNew = 0;
}
}
if( rc==SQLITE_OK && pTrunk ){
memcpy(pTrunk->aData, pNew->aData, pBt->usableSize);
}
*ppPage = pNew;
*pPgno = pgnoNew;
}
releasePage(pTrunk);
assert( (rc==SQLITE_OK)==(*ppPage!=0) );
assert( sqlite3PagerRefcount(pBt->pPager)==(nRef+(*ppPage!=0)) );
return rc;
}
static int freeServerPage2(BtShared *pBt, MemPage *pPage, Pgno iPage){
int rc; /* Return code */
MemPage *pTrunk = 0; /* The node page */
#ifdef SQLITE_DEBUG
int nRef = sqlite3PagerRefcount(pBt->pPager);
#endif
assert( sqlite3_mutex_held(pBt->mutex) );
rc = findServerTrunk(pBt, 0, &pTrunk);
if( rc==SQLITE_OK ){
int nFree; /* Number of free pages on this trunk page */
nFree = (int)get4byte(&pTrunk->aData[4]);
if( nFree>=((pBt->usableSize / 4) - 2) ){
if( pPage==0 ){
rc = btreeGetUnusedPage(pBt, iPage, &pPage, 0);
}else{
sqlite3PagerRef(pPage->pDbPage);
}
rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc==SQLITE_OK ){
memcpy(pPage->aData, pTrunk->aData, pBt->usableSize);
put4byte(&pTrunk->aData[0], iPage);
put4byte(&pTrunk->aData[4], 0);
}
releasePage(pPage);
}else{
put4byte(&pTrunk->aData[8+nFree*4], iPage);
put4byte(&pTrunk->aData[4], (u32)nFree+1);
}
releasePage(pTrunk);
}
assert( nRef==sqlite3PagerRefcount(pBt->pPager) );
return rc;
}
#else
# define allocateServerPage(v, w, x, y, z) SQLITE_OK
# define freeServerPage2(x, y, z) SQLITE_OK
#endif /* SQLITE_SERVER_EDITION */
/*
** Allocate a new page from the database file.
**
@@ -5758,6 +6025,10 @@ static int allocateBtreePage(
MemPage *pPrevTrunk = 0;
Pgno mxPage; /* Total size of the database file */
if( btreeFreelistFormat2(pBt) ){
return allocateServerPage(pBt, ppPage, pPgno, nearby, eMode);
}
assert( sqlite3_mutex_held(pBt->mutex) );
assert( eMode==BTALLOC_ANY || (nearby>0 && IfNotOmitAV(pBt->autoVacuum)) );
pPage1 = pBt->pPage1;
@@ -6085,12 +6356,6 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
pPage = btreePageLookup(pBt, iPage);
}
/* Increment the free page count on pPage1 */
rc = sqlite3PagerWrite(pPage1->pDbPage);
if( rc ) goto freepage_out;
nFree = get4byte(&pPage1->aData[36]);
put4byte(&pPage1->aData[36], nFree+1);
if( pBt->btsFlags & BTS_SECURE_DELETE ){
/* If the secure_delete option is enabled, then
** always fully overwrite deleted information with zeros.
@@ -6102,6 +6367,17 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
}
memset(pPage->aData, 0, pPage->pBt->pageSize);
}
if( btreeFreelistFormat2(pBt) ){
rc = freeServerPage2(pBt, pPage, iPage);
goto freepage_out;
}
/* Increment the free page count on pPage1 */
rc = sqlite3PagerWrite(pPage1->pDbPage);
if( rc ) goto freepage_out;
nFree = get4byte(&pPage1->aData[36]);
put4byte(&pPage1->aData[36], nFree+1);
/* If the database supports auto-vacuum, write an entry in the pointer-map
** to indicate that the page is free.
@@ -9580,6 +9856,49 @@ end_of_check:
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
#ifndef SQLITE_OMIT_INTEGRITY_CHECK
#if !defined(SQLITE_OMIT_INTEGRITY_CHECK) && defined(SQLITE_SERVER_EDITION)
static void checkServerList(IntegrityCk *pCheck){
u32 pgnoNode = get4byte(&pCheck->pBt->pPage1->aData[32]);
if( pgnoNode ){
DbPage *pNode = 0;
u8 *aNodeData;
u32 nList; /* Number of free-lists */
int i;
checkRef(pCheck, pgnoNode);
if( sqlite3PagerGet(pCheck->pPager, (Pgno)pgnoNode, &pNode, 0) ){
checkAppendMsg(pCheck, "failed to get node page %d", pgnoNode);
return;
}
aNodeData = sqlite3PagerGetData(pNode);
nList = get4byte(&aNodeData[4]);
for(i=0; i<nList; i++){
u32 pgnoTrunk = get4byte(&aNodeData[8+4*i]);
while( pgnoTrunk ){
DbPage *pTrunk = 0;
checkRef(pCheck, pgnoTrunk);
if( sqlite3PagerGet(pCheck->pPager, (Pgno)pgnoTrunk, &pTrunk, 0) ){
checkAppendMsg(pCheck, "failed to get page %d", pgnoTrunk);
pgnoTrunk = 0;
}else{
u8 *aTrunkData = sqlite3PagerGetData(pTrunk);
int nLeaf = (int)get4byte(&aTrunkData[4]);
int iLeaf;
for(iLeaf=0; iLeaf<nLeaf; iLeaf++){
u32 pgnoLeaf = get4byte(&aTrunkData[8+iLeaf*4]);
checkRef(pCheck, pgnoLeaf);
}
pgnoTrunk = get4byte(&aTrunkData[0]);
sqlite3PagerUnref(pTrunk);
}
}
}
sqlite3PagerUnref(pNode);
}
}
#endif
/*
** This routine does a complete check of the given BTree file. aRoot[] is
** an array of pages numbers were each page number is the root page of
@@ -9645,8 +9964,15 @@ char *sqlite3BtreeIntegrityCheck(
/* Check the integrity of the freelist
*/
sCheck.zPfx = "Main freelist: ";
checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]),
get4byte(&pBt->pPage1->aData[36]));
#ifdef SQLITE_SERVER_EDITION
if( btreeFreelistFormat2(pBt) ){
checkServerList(&sCheck);
}else
#endif
{
checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]),
get4byte(&pBt->pPage1->aData[36]));
}
sCheck.zPfx = 0;
/* Check all the tables.
@@ -9915,6 +10241,37 @@ void sqlite3BtreeIncrblobCursor(BtCursor *pCur){
}
#endif
int btreeSetVersion(Btree *pBtree, int iVersion, int iFreelistFmt){
BtShared *pBt = pBtree->pBt;
int rc = sqlite3BtreeBeginTrans(pBtree, 0);
if( rc==SQLITE_OK ){
u8 iVal;
u8 *aData = pBt->pPage1->aData;
assert( (iVersion==0 && (iFreelistFmt==1 || iFreelistFmt==2))
|| (iFreelistFmt==0 && (iVersion==1 || iVersion==2))
);
if( iVersion==0 ){
iVal = ((aData[18] & 0x01) ? 1 : 2) + (u8)(iFreelistFmt==2 ? 2 : 0);
}else{
iVal = (u8)iVersion + (u8)(aData[18]>2 ? 2 : 0);
}
if( aData[18]!=iVal || aData[19]!=iVal ){
rc = sqlite3BtreeBeginTrans(pBtree, 2);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
if( rc==SQLITE_OK ){
aData[18] = iVal;
aData[19] = iVal;
}
}
}
}
return rc;
}
/*
** Set both the "read version" (single byte at byte offset 18) and
** "write version" (single byte at byte offset 19) fields in the database
@@ -9931,23 +10288,22 @@ int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){
*/
pBt->btsFlags &= ~BTS_NO_WAL;
if( iVersion==1 ) pBt->btsFlags |= BTS_NO_WAL;
rc = btreeSetVersion(pBtree, iVersion, 0);
pBt->btsFlags &= ~BTS_NO_WAL;
return rc;
}
rc = sqlite3BtreeBeginTrans(pBtree, 0);
if( rc==SQLITE_OK ){
u8 *aData = pBt->pPage1->aData;
if( aData[18]!=(u8)iVersion || aData[19]!=(u8)iVersion ){
rc = sqlite3BtreeBeginTrans(pBtree, 2);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
if( rc==SQLITE_OK ){
aData[18] = (u8)iVersion;
aData[19] = (u8)iVersion;
}
}
int sqlite3BtreeFreelistFormat(Btree *p, int eParam, int *peFmt){
int rc = SQLITE_OK;
sqlite3BtreeEnter(p);
if( eParam ){
u8 *aData = p->pBt->pPage1->aData;
if( 0==get4byte(&aData[32]) ){
rc = btreeSetVersion(p, 0, eParam);
}
}
pBt->btsFlags &= ~BTS_NO_WAL;
*peFmt = (btreeFreelistFormat2(p->pBt) ? 2 : 1);
sqlite3BtreeLeave(p);
return rc;
}
+3
View File
@@ -371,5 +371,8 @@ void sqlite3BtreeCursorList(Btree*);
# define sqlite3SchemaMutexHeld(X,Y,Z) 1
#endif
#ifdef SQLITE_SERVER_EDITION
int sqlite3BtreeFreelistFormat(Btree *p, int eParam, int *peFmt);
#endif
#endif /* SQLITE_BTREE_H */
+16 -26
View File
@@ -1095,20 +1095,15 @@ void sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){
if( pType->n==0 ){
/* If there is no type specified, columns have the default affinity
** 'BLOB' with a default size of 4 bytes. */
** 'BLOB'. */
pCol->affinity = SQLITE_AFF_BLOB;
pCol->szEst = 1;
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
if( 4>=sqlite3GlobalConfig.szSorterRef ){
pCol->colFlags |= COLFLAG_SORTERREF;
}
#endif
}else{
zType = z + sqlite3Strlen30(z) + 1;
memcpy(zType, pType->z, pType->n);
zType[pType->n] = 0;
sqlite3Dequote(zType);
pCol->affinity = sqlite3AffinityType(zType, pCol);
pCol->affinity = sqlite3AffinityType(zType, &pCol->szEst);
pCol->colFlags |= COLFLAG_HASTYPE;
}
p->nCol++;
@@ -1168,7 +1163,7 @@ void sqlite3AddNotNull(Parse *pParse, int onError){
** If none of the substrings in the above table are found,
** SQLITE_AFF_NUMERIC is returned.
*/
char sqlite3AffinityType(const char *zIn, Column *pCol){
char sqlite3AffinityType(const char *zIn, u8 *pszEst){
u32 h = 0;
char aff = SQLITE_AFF_NUMERIC;
const char *zChar = 0;
@@ -1205,32 +1200,27 @@ char sqlite3AffinityType(const char *zIn, Column *pCol){
}
}
/* If pCol is not NULL, store an estimate of the field size. The
/* If pszEst is not NULL, store an estimate of the field size. The
** estimate is scaled so that the size of an integer is 1. */
if( pCol ){
int v = 0; /* default size is approx 4 bytes */
if( pszEst ){
*pszEst = 1; /* default size is approx 4 bytes */
if( aff<SQLITE_AFF_NUMERIC ){
if( zChar ){
while( zChar[0] ){
if( sqlite3Isdigit(zChar[0]) ){
/* BLOB(k), VARCHAR(k), CHAR(k) -> r=(k/4+1) */
int v = 0;
sqlite3GetInt32(zChar, &v);
v = v/4 + 1;
if( v>255 ) v = 255;
*pszEst = v; /* BLOB(k), VARCHAR(k), CHAR(k) -> r=(k/4+1) */
break;
}
zChar++;
}
}else{
v = 16; /* BLOB, TEXT, CLOB -> r=5 (approx 20 bytes)*/
*pszEst = 5; /* BLOB, TEXT, CLOB -> r=5 (approx 20 bytes)*/
}
}
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
if( v>=sqlite3GlobalConfig.szSorterRef ){
pCol->colFlags |= COLFLAG_SORTERREF;
}
#endif
v = v/4 + 1;
if( v>255 ) v = 255;
pCol->szEst = v;
}
return aff;
}
@@ -1262,7 +1252,7 @@ void sqlite3AddDefaultValue(
pCol->zName);
}else{
/* A copy of pExpr is used instead of the original, as pExpr contains
** tokens that point to volatile memory.
** tokens that point to volatile memory.
*/
Expr x;
sqlite3ExprDelete(db, pCol->pDflt);
@@ -1506,7 +1496,7 @@ void sqlite3ChangeCookie(Parse *pParse, int iDb){
Vdbe *v = pParse->pVdbe;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_SCHEMA_VERSION,
(int)(1+(unsigned)db->aDb[iDb].pSchema->schema_cookie));
db->aDb[iDb].pSchema->schema_cookie+1);
}
/*
@@ -2184,7 +2174,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
int nErr = 0; /* Number of errors encountered */
int n; /* Temporarily holds the number of cursors assigned */
sqlite3 *db = pParse->db; /* Database connection for malloc errors */
#ifndef SQLITE_OMIT_VIRTUALTABLE
#ifndef SQLITE_OMIT_VIRTUALTABLE
int rc;
#endif
#ifndef SQLITE_OMIT_AUTHORIZATION
@@ -3988,13 +3978,13 @@ void sqlite3BeginTransaction(Parse *pParse, int type){
}
v = sqlite3GetVdbe(pParse);
if( !v ) return;
if( type!=TK_DEFERRED ){
if( type!=TK_DEFERRED && type!=TK_READONLY ){
for(i=0; i<db->nDb; i++){
sqlite3VdbeAddOp2(v, OP_Transaction, i, (type==TK_EXCLUSIVE)+1);
sqlite3VdbeUsesBtree(v, i);
}
}
sqlite3VdbeAddOp0(v, OP_AutoCommit);
sqlite3VdbeAddOp3(v, OP_AutoCommit, 0, 0, type);
}
/*
-3
View File
@@ -286,9 +286,6 @@ static const char * const sqlite3azCompileOpt[] = {
#if SQLITE_ENABLE_SNAPSHOT
"ENABLE_SNAPSHOT",
#endif
#if SQLITE_ENABLE_SORTER_REFERENCES
"ENABLE_SORTER_REFERENCES",
#endif
#if SQLITE_ENABLE_SQLLOG
"ENABLE_SQLLOG",
#endif
+1 -1
View File
@@ -424,7 +424,7 @@ static void statSizeAndOffset(StatCursor *pCsr){
*/
fd = sqlite3PagerFile(pPager);
x[0] = pCsr->iPageno;
if( sqlite3OsFileControl(fd, 230440, &x)==SQLITE_OK ){
if( fd->pMethods!=0 && sqlite3OsFileControl(fd, 230440, &x)==SQLITE_OK ){
pCsr->iOffset = x[0];
pCsr->szPage = (int)x[1];
}
-32
View File
@@ -1363,7 +1363,6 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){
pItem->sortOrder = pOldItem->sortOrder;
pItem->done = 0;
pItem->bSpanIsTab = pOldItem->bSpanIsTab;
pItem->bSorterRef = pOldItem->bSorterRef;
pItem->u = pOldItem->u;
}
return pNew;
@@ -4372,12 +4371,6 @@ int sqlite3ExprCodeExprList(
if( !ConstFactorOk(pParse) ) flags &= ~SQLITE_ECEL_FACTOR;
for(pItem=pList->a, i=0; i<n; i++, pItem++){
Expr *pExpr = pItem->pExpr;
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
if( pItem->bSorterRef ){
i--;
n--;
}else
#endif
if( (flags & SQLITE_ECEL_REF)!=0 && (j = pItem->u.x.iOrderByCol)>0 ){
if( flags & SQLITE_ECEL_OMITREF ){
i--;
@@ -5025,16 +5018,12 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
if( ExprHasProperty(pExpr, EP_FromJoin) ) return WRC_Prune;
switch( pExpr->op ){
case TK_ISNOT:
case TK_NOT:
case TK_ISNULL:
case TK_IS:
case TK_OR:
case TK_CASE:
case TK_IN:
case TK_FUNCTION:
testcase( pExpr->op==TK_ISNOT );
testcase( pExpr->op==TK_NOT );
testcase( pExpr->op==TK_ISNULL );
testcase( pExpr->op==TK_IS );
testcase( pExpr->op==TK_OR );
@@ -5048,27 +5037,6 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
return WRC_Abort;
}
return WRC_Prune;
/* Virtual tables are allowed to use constraints like x=NULL. So
** a term of the form x=y does not prove that y is not null if x
** is the column of a virtual table */
case TK_EQ:
case TK_NE:
case TK_LT:
case TK_LE:
case TK_GT:
case TK_GE:
testcase( pExpr->op==TK_EQ );
testcase( pExpr->op==TK_NE );
testcase( pExpr->op==TK_LT );
testcase( pExpr->op==TK_LE );
testcase( pExpr->op==TK_GT );
testcase( pExpr->op==TK_GE );
if( (pExpr->pLeft->op==TK_COLUMN && IsVirtual(pExpr->pLeft->pTab))
|| (pExpr->pRight->op==TK_COLUMN && IsVirtual(pExpr->pRight->pTab))
){
return WRC_Prune;
}
default:
return WRC_Continue;
}
+1 -2
View File
@@ -240,8 +240,7 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
0, /* xTestCallback */
#endif
0, /* bLocaltimeFault */
0x7ffffffe, /* iOnceResetThreshold */
SQLITE_DEFAULT_SORTERREF_SIZE /* szSorterRef */
0x7ffffffe /* iOnceResetThreshold */
};
/*
+8 -7
View File
@@ -1450,13 +1450,14 @@ void sqlite3GenerateConstraintChecks(
regNewData, 1, 0, OE_Replace, 1, -1);
}else{
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
assert( HasRowid(pTab) );
/* This OP_Delete opcode fires the pre-update-hook only. It does
** not modify the b-tree. It is more efficient to let the coming
** OP_Insert replace the existing entry than it is to delete the
** existing entry and then insert a new one. */
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, OPFLAG_ISNOOP);
sqlite3VdbeAppendP4(v, pTab, P4_TABLE);
if( HasRowid(pTab) ){
/* This OP_Delete opcode fires the pre-update-hook only. It does
** not modify the b-tree. It is more efficient to let the coming
** OP_Insert replace the existing entry than it is to delete the
** existing entry and then insert a new one. */
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, OPFLAG_ISNOOP);
sqlite3VdbeAppendP4(v, pTab, P4_TABLE);
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
if( pTab->pIndex ){
sqlite3MultiWrite(pParse);
+3 -15
View File
@@ -642,17 +642,6 @@ int sqlite3_config(int op, ...){
break;
}
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
case SQLITE_CONFIG_SORTERREF_SIZE: {
int iVal = va_arg(ap, int);
if( iVal<0 ){
iVal = SQLITE_DEFAULT_SORTERREF_SIZE;
}
sqlite3GlobalConfig.szSorterRef = (u32)iVal;
break;
}
#endif /* SQLITE_ENABLE_SORTER_REFERENCES */
default: {
rc = SQLITE_ERROR;
break;
@@ -1518,8 +1507,6 @@ static int sqliteDefaultBusyCallback(
return 1;
}
}
#else
UNUSED_PARAMETER(pFile);
#endif
assert( count>=0 );
if( count < NDELAY ){
@@ -1540,7 +1527,6 @@ static int sqliteDefaultBusyCallback(
** must be done in increments of whole seconds */
sqlite3 *db = (sqlite3 *)ptr;
int tmout = ((sqlite3 *)ptr)->busyTimeout;
UNUSED_PARAMETER(pFile);
if( (count+1)*1000 > tmout ){
return 0;
}
@@ -3639,8 +3625,10 @@ int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, void *pArg){
}else if( op==SQLITE_FCNTL_JOURNAL_POINTER ){
*(sqlite3_file**)pArg = sqlite3PagerJrnlFile(pPager);
rc = SQLITE_OK;
}else{
}else if( fd->pMethods ){
rc = sqlite3OsFileControl(fd, op, pArg);
}else{
rc = SQLITE_NOTFOUND;
}
sqlite3BtreeLeave(pBtree);
}
+1 -2
View File
@@ -125,7 +125,6 @@ int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut){
** routine has no return value since the return value would be meaningless.
*/
int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){
if( id->pMethods==0 ) return SQLITE_NOTFOUND;
#ifdef SQLITE_TEST
if( op!=SQLITE_FCNTL_COMMIT_PHASETWO
&& op!=SQLITE_FCNTL_LOCK_TIMEOUT
@@ -146,7 +145,7 @@ int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){
return id->pMethods->xFileControl(id, op, pArg);
}
void sqlite3OsFileControlHint(sqlite3_file *id, int op, void *pArg){
if( id->pMethods ) (void)id->pMethods->xFileControl(id, op, pArg);
(void)id->pMethods->xFileControl(id, op, pArg);
}
int sqlite3OsSectorSize(sqlite3_file *id){
+244
View File
@@ -3841,12 +3841,247 @@ static void unixModeBit(unixFile *pFile, unsigned char mask, int *pArg){
/* Forward declaration */
static int unixGetTempname(int nBuf, char *zBuf);
#ifdef SQLITE_SERVER_EDITION
/*
** Structure passed by SQLite through the (void*) argument to various
** fcntl operations.
*/
struct UnixServerArg {
void *h; /* Handle from SHMOPEN */
void *p; /* Mapping */
int i1; /* Integer value 1 */
int i2; /* Integer value 2 */
};
typedef struct UnixServerArg UnixServerArg;
/*
** Structure used as a server-shm handle.
*/
struct UnixServerShm {
void *pMap; /* Pointer to mapping */
int nMap; /* Size of mapping in bytes */
int fd; /* File descriptor open on *-hma file */
};
typedef struct UnixServerShm UnixServerShm;
/*
** Implementation of SQLITE_FCNTL_FILEID
*/
static int unixFcntlServerFileid(unixFile *pFile, void *pArg){
i64 *aId = (i64*)pArg;
aId[0] = (i64)(pFile->pInode->fileId.dev);
aId[1] = (i64)(pFile->pInode->fileId.ino);
return SQLITE_OK;
}
/*
** Implementation of SQLITE_FCNTL_SERVER_MODE
*/
static int unixFcntlServerMode(unixFile *pFile, void *pArg){
int rc = SQLITE_OK;
int eServer = 0;
char *zJrnl = sqlite3_mprintf("%s-journal", pFile->zPath);
if( zJrnl==0 ){
rc = SQLITE_NOMEM;
}else{
struct stat buf; /* Used to hold return values of stat() */
if( osStat(zJrnl, &buf) ){
rc = SQLITE_IOERR_FSTAT;
}else if( buf.st_mode & S_IFDIR ){
eServer = (pFile->ctrlFlags & UNIXFILE_EXCL) ? 1 : 2;
}
}
sqlite3_free(zJrnl);
*((int*)pArg) = eServer;
return rc;
}
/*
** Implementation of SQLITE_FCNTL_SERVER_SHMOPEN.
**
** The (void*) argument passed to this file control should actually be
** a pointer to a UnixServerArg or equivalent structure. Arguments are
** interpreted as follows:
**
** UnixServerArg.h - OUT: New server shm handle.
** UnixServerArg.p - OUT: New server shm mapping.
** UnixServerArg.i1 - Size of requested mapping in bytes.
** UnixServerArg.i2 - OUT: True if journal rollback + SHMOPEN2 are required.
*/
static int unixFcntlServerShmopen(unixFile *pFd, void *pArg){
int rc = SQLITE_OK;
UnixServerArg *pSArg = (UnixServerArg*)pArg;
UnixServerShm *p;
char *zHma;
p = sqlite3_malloc(sizeof(UnixServerShm));
if( p==0 ) return SQLITE_NOMEM;
memset(p, 0, sizeof(UnixServerShm));
p->fd = -1;
zHma = sqlite3_mprintf("%s-journal/hma", pFd->zPath);
if( zHma==0 ){
rc = SQLITE_NOMEM;
}else{
p->fd = osOpen(zHma, O_RDWR|O_CREAT, 0644);
p->nMap = pSArg->i1;
if( p->fd<0 ){
rc = SQLITE_CANTOPEN;
}else{
int res = ftruncate(p->fd, p->nMap);
if( res!=0 ){
rc = SQLITE_IOERR_TRUNCATE;
}else{
p->pMap = osMmap(0, p->nMap, PROT_READ|PROT_WRITE, MAP_SHARED, p->fd,0);
if( p->pMap==0 ){
rc = SQLITE_IOERR_MMAP;
}
}
}
sqlite3_free(zHma);
}
if( rc==SQLITE_OK ){
int res;
struct flock lock;
memset(&lock, 0, sizeof(struct flock));
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = p->nMap;
lock.l_len = 1;
res = osFcntl(p->fd, F_SETLK, &lock);
if( res==0 ){
pSArg->i2 = 1;
memset(p->pMap, 0, p->nMap);
}else{
pSArg->i2 = 0;
lock.l_type = F_RDLCK;
res = osFcntl(p->fd, F_SETLKW, &lock);
if( res!=0 ){
rc = SQLITE_IOERR_LOCK;
}
}
}
if( rc!=SQLITE_OK ){
if( p->pMap ) osMunmap(p->pMap, p->nMap);
if( p->fd>=0 ) close(p->fd);
sqlite3_free(p);
pSArg->h = pSArg->p = 0;
}else{
pSArg->h = (void*)p;
pSArg->p = (void*)(p->pMap);
}
return rc;
}
/*
** Implementation of SQLITE_FCNTL_SERVER_SHMOPEN2.
**
** The (void*) argument passed to this file control should actually be
** a pointer to a UnixServerArg or equivalent structure. Arguments are
** interpreted as follows:
**
** UnixServerArg.h - Server shm handle (from SHMOPEN).
** UnixServerArg.p - unused.
** UnixServerArg.i1 - unused.
** UnixServerArg.i2 - unused.
*/
static int unixFcntlServerShmopen2(unixFile *pFd, void *pArg){
UnixServerArg *pSArg = (UnixServerArg*)pArg;
UnixServerShm *p = (UnixServerShm*)pSArg->h;
int res;
struct flock lock;
memset(&lock, 0, sizeof(struct flock));
lock.l_type = F_RDLCK;
lock.l_whence = SEEK_SET;
lock.l_start = p->nMap;
lock.l_len = 1;
res = osFcntl(p->fd, F_SETLK, &lock);
return res ? SQLITE_IOERR_LOCK : SQLITE_OK;
}
/*
** Implementation of SQLITE_FCNTL_SERVER_SHMCLOSE.
**
** The (void*) argument passed to this file control should actually be
** a pointer to a UnixServerArg or equivalent structure. Arguments are
** interpreted as follows:
**
** UnixServerArg.h - Server shm handle (from SHMOPEN).
** UnixServerArg.p - unused.
** UnixServerArg.i1 - unused.
** UnixServerArg.i2 - unused.
*/
static int unixFcntlServerShmclose(unixFile *pFd, void *pArg){
UnixServerArg *pSArg = (UnixServerArg*)pArg;
UnixServerShm *p = (UnixServerShm*)pSArg->h;
if( p->pMap ) osMunmap(p->pMap, p->nMap);
if( p->fd>=0 ) close(p->fd);
sqlite3_free(p);
return SQLITE_OK;
}
/*
** Implementation of SQLITE_FCNTL_SERVER_SHMLOCK.
**
** The (void*) argument passed to this file control should actually be
** a pointer to a UnixServerArg or equivalent structure. Arguments are
** interpreted as follows:
**
** UnixServerArg.h - Server shm handle (from SHMOPEN).
** UnixServerArg.p - unused.
** UnixServerArg.i1 - slot to lock.
** UnixServerArg.i2 - true to take the lock, false to release it.
*/
static int unixFcntlServerShmlock(unixFile *pFd, void *pArg){
UnixServerArg *pSArg = (UnixServerArg*)pArg;
UnixServerShm *p = (UnixServerShm*)pSArg->h;
int res;
struct flock lock;
memset(&lock, 0, sizeof(struct flock));
lock.l_type = pSArg->i2 ? F_WRLCK : F_UNLCK;
lock.l_whence = SEEK_SET;
lock.l_start = p->nMap + pSArg->i1 + 1;
lock.l_len = 1;
res = osFcntl(p->fd, F_SETLK, &lock);
return (res==0 ? SQLITE_OK : SQLITE_BUSY);
}
#endif
/*
** Information and control of an open file handle.
*/
static int unixFileControl(sqlite3_file *id, int op, void *pArg){
unixFile *pFile = (unixFile*)id;
switch( op ){
#ifdef SQLITE_SERVER_EDITION
case SQLITE_FCNTL_FILEID:
return unixFcntlServerFileid(pFile, pArg);
case SQLITE_FCNTL_SERVER_MODE:
return unixFcntlServerMode(pFile, pArg);
case SQLITE_FCNTL_SERVER_SHMOPEN:
return unixFcntlServerShmopen(pFile, pArg);
case SQLITE_FCNTL_SERVER_SHMOPEN2:
return unixFcntlServerShmopen2(pFile, pArg);
case SQLITE_FCNTL_SERVER_SHMCLOSE:
return unixFcntlServerShmclose(pFile, pArg);
case SQLITE_FCNTL_SERVER_SHMLOCK:
return unixFcntlServerShmlock(pFile, pArg);
#endif
#if defined(__linux__) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
case SQLITE_FCNTL_BEGIN_ATOMIC_WRITE: {
int rc = osIoctl(pFile->h, F2FS_IOC_START_ATOMIC_WRITE);
@@ -5780,6 +6015,15 @@ static int findCreateFileMode(
zDb[nDb] = '\0';
rc = getFileMode(zDb, pMode, pUid, pGid);
#ifdef SQLITE_SERVER_EDITION
if( rc==SQLITE_IOERR_FSTAT ){
while( nDb && zDb[nDb]!='/' ) nDb--;
if( nDb>8 && memcmp("-journal/", &zDb[nDb-8], 9)==0 ){
zDb[nDb-8] = '\0';
rc = getFileMode(zDb, pMode, pUid, pGid);
}
}
#endif
}else if( flags & SQLITE_OPEN_DELETEONCLOSE ){
*pMode = 0600;
}else if( flags & SQLITE_OPEN_URI ){
+249 -28
View File
@@ -717,6 +717,10 @@ struct Pager {
Wal *pWal; /* Write-ahead log used by "journal_mode=wal" */
char *zWal; /* File name for write-ahead log */
#endif
#ifdef SQLITE_SERVER_EDITION
Server *pServer;
ServerPage *pServerPage;
#endif
};
/*
@@ -848,6 +852,13 @@ int sqlite3PagerUseWal(Pager *pPager, Pgno pgno){
# define pagerBeginReadTransaction(z) SQLITE_OK
#endif
#ifdef SQLITE_SERVER_EDITION
# define pagerIsServer(x) ((x)->pServer!=0)
# define pagerIsProcessServer(x) sqlite3ServerIsSingleProcess((x)->pServer)
#else
# define pagerIsServer(x) 0
#endif
#ifndef NDEBUG
/*
** Usage:
@@ -1056,6 +1067,9 @@ static void setGetterMethod(Pager *pPager){
pPager->xGet = getPageError;
#if SQLITE_MAX_MMAP_SIZE>0
}else if( USEFETCH(pPager)
#ifdef SQLITE_SERVER_EDITION
&& sqlite3ServerIsReadonly(pPager->pServer)==0
#endif
#ifdef SQLITE_HAS_CODEC
&& pPager->xCodec==0
#endif
@@ -1146,6 +1160,7 @@ static int pagerUnlockDb(Pager *pPager, int eLock){
assert( !pPager->exclusiveMode || pPager->eLock==eLock );
assert( eLock==NO_LOCK || eLock==SHARED_LOCK );
assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 );
assert( eLock!=NO_LOCK || pagerIsServer(pPager)==0 );
if( isOpen(pPager->fd) ){
assert( pPager->eLock>=eLock );
rc = pPager->noLock ? SQLITE_OK : sqlite3OsUnlock(pPager->fd, eLock);
@@ -1807,6 +1822,21 @@ static int addToSavepointBitvecs(Pager *pPager, Pgno pgno){
return rc;
}
#ifdef SQLITE_SERVER_EDITION
/*
** Free the linked list of ServerPage objects headed at Pager.pServerPage.
*/
static void pagerFreeServerPage(Pager *pPager){
ServerPage *pPg;
ServerPage *pNext;
for(pPg=pPager->pServerPage; pPg; pPg=pNext){
pNext = pPg->pNext;
sqlite3_free(pPg);
}
pPager->pServerPage = 0;
}
#endif
/*
** This function is a no-op if the pager is in exclusive mode and not
** in the ERROR state. Otherwise, it switches the pager to PAGER_OPEN
@@ -1835,6 +1865,13 @@ static void pager_unlock(Pager *pPager){
pPager->pInJournal = 0;
releaseAllSavepoints(pPager);
#ifdef SQLITE_SERVER_EDITION
if( pagerIsServer(pPager) ){
pagerFreeServerPage(pPager);
sqlite3ServerEnd(pPager->pServer);
pPager->eState = PAGER_OPEN;
}else
#endif
if( pagerUseWal(pPager) ){
assert( !isOpen(pPager->jfd) );
sqlite3WalEndReadTransaction(pPager->pWal);
@@ -2130,11 +2167,16 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
rc = pager_truncate(pPager, pPager->dbSize);
}
if( rc==SQLITE_OK && bCommit ){
if( rc==SQLITE_OK && bCommit && isOpen(pPager->fd) ){
rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_COMMIT_PHASETWO, 0);
if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
}
#ifdef SQLITE_SERVER_EDITION
if( pagerIsServer(pPager) ){
rc2 = sqlite3ServerEndWrite(pPager->pServer);
}else
#endif
if( !pPager->exclusiveMode
&& (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0))
){
@@ -2949,7 +2991,9 @@ end_playback:
** assertion that the transaction counter was modified.
*/
#ifdef SQLITE_DEBUG
sqlite3OsFileControlHint(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0);
if( pPager->fd->pMethods ){
sqlite3OsFileControlHint(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0);
}
#endif
/* If this playback is happening automatically as a result of an IO or
@@ -3029,11 +3073,28 @@ static int readDbPage(PgHdr *pPg){
}else
#endif
{
i64 iOffset = (pPg->pgno-1)*(i64)pPager->pageSize;
rc = sqlite3OsRead(pPager->fd, pPg->pData, pPager->pageSize, iOffset);
if( rc==SQLITE_IOERR_SHORT_READ ){
rc = SQLITE_OK;
#ifdef SQLITE_SERVER_EDITION
u8 *pData = 0;
if( pagerIsServer(pPager) ){
sqlite3ServerReadPage(pPager->pServer, pPg->pgno, &pData);
if( pData ){
memcpy(pPg->pData, pData, pPager->pageSize);
}
}
if( pData==0 ){
#endif
i64 iOffset = (pPg->pgno-1)*(i64)pPager->pageSize;
rc = sqlite3OsRead(pPager->fd, pPg->pData, pPager->pageSize, iOffset);
if( rc==SQLITE_IOERR_SHORT_READ ){
rc = SQLITE_OK;
}
#ifdef SQLITE_SERVER_EDITION
if( pagerIsServer(pPager) ){
sqlite3ServerEndReadPage(pPager->pServer, pPg->pgno);
}
}
#endif
}
if( pPg->pgno==1 ){
@@ -3707,13 +3768,15 @@ void sqlite3PagerSetBusyHandler(
int (*xBusyHandler)(void *), /* Pointer to busy-handler function */
void *pBusyHandlerArg /* Argument to pass to xBusyHandler */
){
void **ap;
pPager->xBusyHandler = xBusyHandler;
pPager->pBusyHandlerArg = pBusyHandlerArg;
ap = (void **)&pPager->xBusyHandler;
assert( ((int(*)(void *))(ap[0]))==xBusyHandler );
assert( ap[1]==pBusyHandlerArg );
sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_BUSYHANDLER, (void *)ap);
if( isOpen(pPager->fd) ){
void **ap = (void **)&pPager->xBusyHandler;
assert( ((int(*)(void *))(ap[0]))==xBusyHandler );
assert( ap[1]==pBusyHandlerArg );
sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_BUSYHANDLER, (void *)ap);
}
}
/*
@@ -4176,15 +4239,31 @@ int sqlite3PagerClose(Pager *pPager, sqlite3 *db){
** rollback before accessing the database file.
*/
if( isOpen(pPager->jfd) ){
#if 0
if( pagerIsServer(pPager) ){
assert( pPager->journalMode==PAGER_JOURNALMODE_PERSIST );
pPager->journalMode = PAGER_JOURNALMODE_DELETE;
/* If necessary, change the pager state so that the journal file
** is deleted by the call to pagerUnlockAndRollback() below. */
if( pPager->eState==PAGER_OPEN ) pPager->eState = PAGER_READER;
}
#endif
pager_error(pPager, pagerSyncHotJournal(pPager));
}
pagerUnlockAndRollback(pPager);
}
#ifdef SQLITE_SERVER_EDITION
if( pagerIsServer(pPager) ){
sqlite3ServerDisconnect(pPager->pServer, pPager->fd);
pPager->pServer = 0;
}else{
sqlite3OsClose(pPager->jfd);
}
#endif
sqlite3EndBenignMalloc();
enable_simulated_io_errors();
PAGERTRACE(("CLOSE %d\n", PAGERID(pPager)));
IOTRACE(("CLOSE %p\n", pPager))
sqlite3OsClose(pPager->jfd);
IOTRACE(("CLOSE %p\n", pPager));
sqlite3OsClose(pPager->fd);
sqlite3PageFree(pTmp);
sqlite3PcacheClose(pPager->pPCache);
@@ -4194,7 +4273,7 @@ int sqlite3PagerClose(Pager *pPager, sqlite3 *db){
#endif
assert( !pPager->aSavepoint && !pPager->pInJournal );
assert( !isOpen(pPager->jfd) && !isOpen(pPager->sjfd) );
assert( !isOpen(pPager->sjfd) );
sqlite3_free(pPager);
return SQLITE_OK;
@@ -4403,6 +4482,14 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
assert( pPager->eLock==EXCLUSIVE_LOCK );
assert( isOpen(pPager->fd) || pList->pDirty==0 );
#ifdef SQLITE_SERVER_EDITION
if( pagerIsProcessServer(pPager) ){
rc = sqlite3ServerPreCommit(pPager->pServer, pPager->pServerPage);
pPager->pServerPage = 0;
if( rc!=SQLITE_OK ) return rc;
}
#endif
/* If the file is a temp-file has not yet been opened, open it now. It
** is not possible for rc to be other than SQLITE_OK if this branch
** is taken, as pager_wait_on_lock() is a no-op for temp-files.
@@ -4585,6 +4672,8 @@ static int pagerStress(void *p, PgHdr *pPg){
Pager *pPager = (Pager *)p;
int rc = SQLITE_OK;
if( pagerIsServer(pPager) ) return SQLITE_OK;
assert( pPg->pPager==pPager );
assert( pPg->flags&PGHDR_DIRTY );
@@ -5034,7 +5123,7 @@ act_like_temp_file:
** to determine whether or not a hot-journal file exists, the IO error
** code is returned and the value of *pExists is undefined.
*/
static int hasHotJournal(Pager *pPager, int *pExists){
static int hasHotJournal(Pager *pPager, int *pExists, int *peServer){
sqlite3_vfs * const pVfs = pPager->pVfs;
int rc = SQLITE_OK; /* Return code */
int exists = 1; /* True if a journal file is present */
@@ -5055,6 +5144,13 @@ static int hasHotJournal(Pager *pPager, int *pExists){
if( rc==SQLITE_OK && exists ){
int locked = 0; /* True if some process holds a RESERVED lock */
#ifdef SQLITE_SERVER_EDITION
rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SERVER_MODE, peServer);
if( rc!=SQLITE_NOTFOUND ){
if( rc!=SQLITE_OK || *peServer ) return rc;
}
#endif
/* Race condition here: Another process might have been holding the
** the RESERVED lock and have a journal open at the sqlite3OsAccess()
** call above, but then delete the journal and drop the lock before
@@ -5127,6 +5223,53 @@ static int hasHotJournal(Pager *pPager, int *pExists){
return rc;
}
#ifdef SQLITE_SERVER_EDITION
static int pagerServerConnect(Pager *pPager, int eServer){
int rc = SQLITE_OK;
if( pPager->tempFile==0 ){
pPager->noLock = 1;
pPager->journalMode = PAGER_JOURNALMODE_PERSIST;
rc = sqlite3ServerConnect(pPager, eServer, &pPager->pServer);
}
return rc;
}
int sqlite3PagerRollbackJournal(Pager *pPager, sqlite3_file *pJfd){
int rc; /* Return Code */
sqlite3_file *saved_jfd = pPager->jfd;
u8 saved_eState = pPager->eState;
u8 saved_eLock = pPager->eLock;
i64 saved_journalOff = pPager->journalOff;
i64 saved_journalHdr = pPager->journalHdr;
assert( pPager->journalMode==PAGER_JOURNALMODE_PERSIST );
pPager->eLock = EXCLUSIVE_LOCK;
pPager->eState = PAGER_WRITER_DBMOD;
pPager->jfd = pJfd;
rc = pagerSyncHotJournal(pPager);
if( rc==SQLITE_OK ) rc = pager_playback(pPager, 1);
assert( isOpen(pPager->jfd) );
pPager->jfd = saved_jfd;
pPager->eState = saved_eState;
pPager->eLock = saved_eLock;
pPager->journalOff = saved_journalOff;
pPager->journalHdr = saved_journalHdr;
return rc;
}
void sqlite3PagerServerJournal(
Pager *pPager,
sqlite3_file *jfd,
const char *zJournal
){
pPager->zJournal = (char*)zJournal;
pPager->jfd = jfd;
}
#endif
/*
** This function is called to obtain a shared lock on the database file.
** It is illegal to call sqlite3PagerGet() until after this function
@@ -5154,8 +5297,11 @@ static int hasHotJournal(Pager *pPager, int *pExists){
** occurs while locking the database, checking for a hot-journal file or
** rolling back a journal file, the IO error code is returned.
*/
int sqlite3PagerSharedLock(Pager *pPager){
int sqlite3PagerSharedLock(Pager *pPager, int bReadonly){
int rc = SQLITE_OK; /* Return code */
#ifdef SQLITE_SERVER_EDITION
int eServer = 0;
#endif
/* This routine is only called from b-tree and only when there are no
** outstanding pages. This implies that the pager state should either
@@ -5166,7 +5312,9 @@ int sqlite3PagerSharedLock(Pager *pPager){
assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER );
assert( pPager->errCode==SQLITE_OK );
if( !pagerUseWal(pPager) && pPager->eState==PAGER_OPEN ){
if( !pagerUseWal(pPager)
&& !pagerIsServer(pPager)
&& pPager->eState==PAGER_OPEN ){
int bHotJournal = 1; /* True if there exists a hot journal-file */
assert( !MEMDB );
@@ -5182,7 +5330,8 @@ int sqlite3PagerSharedLock(Pager *pPager){
** database file, then it either needs to be played back or deleted.
*/
if( pPager->eLock<=SHARED_LOCK ){
rc = hasHotJournal(pPager, &bHotJournal);
rc = hasHotJournal(pPager, &bHotJournal, &eServer);
assert( bHotJournal==0 || eServer==0 );
}
if( rc!=SQLITE_OK ){
goto failed;
@@ -5315,6 +5464,7 @@ int sqlite3PagerSharedLock(Pager *pPager){
if( rc!=SQLITE_IOERR_SHORT_READ ){
goto failed;
}
rc = SQLITE_OK;
memset(dbFileVers, 0, sizeof(dbFileVers));
}
@@ -5333,16 +5483,37 @@ int sqlite3PagerSharedLock(Pager *pPager){
}
}
#ifdef SQLITE_SERVER_EDITION
if( eServer ){
rc = pagerServerConnect(pPager, eServer);
}
#endif
/* If there is a WAL file in the file-system, open this database in WAL
** mode. Otherwise, the following function call is a no-op.
*/
rc = pagerOpenWalIfPresent(pPager);
if( rc==SQLITE_OK ){
rc = pagerOpenWalIfPresent(pPager);
}
#ifndef SQLITE_OMIT_WAL
assert( pPager->pWal==0 || rc==SQLITE_OK );
#endif
}
if( pagerUseWal(pPager) ){
#ifdef SQLITE_SERVER_EDITION
if( pagerIsServer(pPager) ){
assert( rc==SQLITE_OK );
assert( sqlite3PagerRefcount(pPager)==0 );
assert( pagerUseWal(pPager)==0 );
pager_reset(pPager);
rc = sqlite3ServerBegin(pPager->pServer, bReadonly);
if( rc==SQLITE_OK ){
rc = sqlite3ServerLock(pPager->pServer, 1, 0, 0);
}
setGetterMethod(pPager);
}
#endif
if( rc==SQLITE_OK && pagerUseWal(pPager) ){
assert( rc==SQLITE_OK );
rc = pagerBeginReadTransaction(pPager);
}
@@ -5631,6 +5802,12 @@ int sqlite3PagerGet(
DbPage **ppPage, /* Write a pointer to the page here */
int flags /* PAGER_GET_XXX flags */
){
#ifdef SQLITE_SERVER_EDITION
if( pagerIsServer(pPager) ){
int rc = sqlite3ServerLock(pPager->pServer, pgno, 0, 0);
if( rc!=SQLITE_OK ) return rc;
}
#endif
return pPager->xGet(pPager, pgno, ppPage, flags);
}
@@ -5881,6 +6058,24 @@ static SQLITE_NOINLINE int pagerAddPageToRollbackJournal(PgHdr *pPg){
char *pData2;
i64 iOff = pPager->journalOff;
#ifdef SQLITE_SERVER_EDITION
if( pagerIsProcessServer(pPager) ){
ServerPage *p = sqlite3ServerBuffer(pPager->pServer);
if( p==0 ){
int nByte = sizeof(ServerPage) + pPager->pageSize;
p = (ServerPage*)sqlite3_malloc(nByte);
if( !p ) return SQLITE_NOMEM_BKPT;
}
memset(p, 0, sizeof(ServerPage));
p->aData = (u8*)&p[1];
p->nData = pPager->pageSize;
p->pgno = pPg->pgno;
p->pNext = pPager->pServerPage;
pPager->pServerPage = p;
memcpy(p->aData, pPg->pData, pPager->pageSize);
}
#endif
/* We should never write to the journal file the page that
** contains the database locks. The following assert verifies
** that we do not. */
@@ -5948,6 +6143,13 @@ static int pager_write(PgHdr *pPg){
assert( pPager->readOnly==0 );
CHECK_PAGE(pPg);
#ifdef SQLITE_SERVER_EDITION
if( pagerIsServer(pPager) ){
rc = sqlite3ServerLock(pPager->pServer, pPg->pgno, 1, 0);
if( rc!=SQLITE_OK ) return rc;
}
#endif
/* The journal file needs to be opened. Higher level routines have already
** obtained the necessary locks to begin the write-transaction, but the
** rollback journal might not yet be open. Open it now if this is the case.
@@ -6226,7 +6428,10 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
# define DIRECT_MODE isDirectMode
#endif
if( !pPager->changeCountDone && ALWAYS(pPager->dbSize>0) ){
if( 0==pagerIsServer(pPager)
&& !pPager->changeCountDone
&& ALWAYS(pPager->dbSize>0)
){
PgHdr *pPgHdr; /* Reference to page 1 */
assert( !pPager->tempFile && isOpen(pPager->fd) );
@@ -6285,9 +6490,12 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
*/
int sqlite3PagerSync(Pager *pPager, const char *zMaster){
int rc = SQLITE_OK;
void *pArg = (void*)zMaster;
rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC, pArg);
if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
if( isOpen(pPager->fd) ){
void *pArg = (void*)zMaster;
rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC, pArg);
if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
}
if( rc==SQLITE_OK && !pPager->noSync ){
assert( !MEMDB );
rc = sqlite3OsSync(pPager->fd, pPager->syncFlags);
@@ -6382,6 +6590,10 @@ int sqlite3PagerCommitPhaseOne(
** backup in progress needs to be restarted. */
sqlite3BackupRestart(pPager->pBackup);
}else{
/* If this connection is in server mode, ignore any master journal. */
if( pagerIsServer(pPager) ){
zMaster = 0;
}
if( pagerUseWal(pPager) ){
PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache);
PgHdr *pPageOne = 0;
@@ -6969,8 +7181,10 @@ sqlite3_file *sqlite3PagerFile(Pager *pPager){
** Reset the lock timeout for pager.
*/
void sqlite3PagerResetLockTimeout(Pager *pPager){
int x = 0;
sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_LOCK_TIMEOUT, &x);
if( isOpen(pPager->fd) ){
int x = 0;
sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_LOCK_TIMEOUT, &x);
}
}
#endif
@@ -7332,7 +7546,7 @@ int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){
int state = pPager->eState;
assert( state==PAGER_OPEN || state==PAGER_READER );
if( state==PAGER_OPEN ){
rc = sqlite3PagerSharedLock(pPager);
rc = sqlite3PagerSharedLock(pPager, 0);
}
if( pPager->eState==PAGER_READER ){
assert( rc==SQLITE_OK );
@@ -7449,7 +7663,7 @@ int sqlite3PagerWalCallback(Pager *pPager){
*/
int sqlite3PagerWalSupported(Pager *pPager){
const sqlite3_io_methods *pMethods = pPager->fd->pMethods;
if( pPager->noLock ) return 0;
if( pPager->noLock && !pagerIsServer(pPager) ) return 0;
return pPager->exclusiveMode || (pMethods->iVersion>=2 && pMethods->xShmMap);
}
@@ -7657,4 +7871,11 @@ int sqlite3PagerWalFramesize(Pager *pPager){
}
#endif
#ifdef SQLITE_SERVER_EDITION
int sqlite3PagerPagelock(Pager *pPager, Pgno pgno, int bWrite){
if( pagerIsServer(pPager)==0 ) return SQLITE_OK;
return sqlite3ServerLock(pPager->pServer, pgno, bWrite, 0);
}
#endif
#endif /* SQLITE_OMIT_DISKIO */
+7 -1
View File
@@ -171,7 +171,7 @@ int sqlite3PagerCommitPhaseTwo(Pager*);
int sqlite3PagerRollback(Pager*);
int sqlite3PagerOpenSavepoint(Pager *pPager, int n);
int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint);
int sqlite3PagerSharedLock(Pager *pPager);
int sqlite3PagerSharedLock(Pager *pPager, int bReadonly);
#ifndef SQLITE_OMIT_WAL
int sqlite3PagerCheckpoint(Pager *pPager, sqlite3*, int, int*, int*);
@@ -242,4 +242,10 @@ void *sqlite3PagerCodec(DbPage *);
# define enable_simulated_io_errors()
#endif
#ifdef SQLITE_SERVER_EDITION
int sqlite3PagerRollbackJournal(Pager*, sqlite3_file*);
int sqlite3PagerPagelock(Pager *pPager, Pgno, int);
void sqlite3PagerServerJournal(Pager*, sqlite3_file*, const char*);
#endif
#endif /* SQLITE_PAGER_H */
+23 -33
View File
@@ -114,9 +114,9 @@ input ::= cmdlist.
cmdlist ::= cmdlist ecmd.
cmdlist ::= ecmd.
ecmd ::= SEMI.
ecmd ::= cmdx SEMI.
ecmd ::= explain cmdx SEMI.
explain ::= .
%ifndef SQLITE_OMIT_EXPLAIN
ecmd ::= explain cmdx.
explain ::= EXPLAIN. { pParse->explain = 1; }
explain ::= EXPLAIN QUERY PLAN. { pParse->explain = 2; }
%endif SQLITE_OMIT_EXPLAIN
@@ -131,6 +131,7 @@ trans_opt ::= TRANSACTION.
trans_opt ::= TRANSACTION nm.
%type transtype {int}
transtype(A) ::= . {A = TK_DEFERRED;}
transtype(A) ::= READONLY(X). {A = @X; /*A-overwrites-X*/}
transtype(A) ::= DEFERRED(X). {A = @X; /*A-overwrites-X*/}
transtype(A) ::= IMMEDIATE(X). {A = @X; /*A-overwrites-X*/}
transtype(A) ::= EXCLUSIVE(X). {A = @X; /*A-overwrites-X*/}
@@ -464,7 +465,7 @@ cmd ::= select(X). {
}
}
select(A) ::= WITH wqlist(W) selectnowith(X). {
select(A) ::= with(W) selectnowith(X). {
Select *p = X;
if( p ){
p->pWith = W;
@@ -472,24 +473,7 @@ select(A) ::= WITH wqlist(W) selectnowith(X). {
}else{
sqlite3WithDelete(pParse->db, W);
}
A = p;
}
select(A) ::= WITH RECURSIVE wqlist(W) selectnowith(X). {
Select *p = X;
if( p ){
p->pWith = W;
parserDoubleLinkSelect(pParse, p);
}else{
sqlite3WithDelete(pParse->db, W);
}
A = p;
}
select(A) ::= selectnowith(X). {
Select *p = X;
if( p ){
parserDoubleLinkSelect(pParse, p);
}
A = p; /*A-overwrites-X*/
A = p; /*A-overwrites-W*/
}
selectnowith(A) ::= oneselect(A).
@@ -683,9 +667,7 @@ dbnm(A) ::= DOT nm(X). {A = X;}
%type fullname {SrcList*}
%destructor fullname {sqlite3SrcListDelete(pParse->db, $$);}
fullname(A) ::= nm(X).
{A = sqlite3SrcListAppend(pParse->db,0,&X,0); /*A-overwrites-X*/}
fullname(A) ::= nm(X) DOT nm(Y).
fullname(A) ::= nm(X) dbnm(Y).
{A = sqlite3SrcListAppend(pParse->db,0,&X,&Y); /*A-overwrites-X*/}
%type joinop {int}
@@ -781,14 +763,16 @@ limit_opt(A) ::= LIMIT expr(X) COMMA expr(Y).
/////////////////////////// The DELETE statement /////////////////////////////
//
%ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
cmd ::= with DELETE FROM fullname(X) indexed_opt(I) where_opt(W)
cmd ::= with(C) DELETE FROM fullname(X) indexed_opt(I) where_opt(W)
orderby_opt(O) limit_opt(L). {
sqlite3WithPush(pParse, C, 1);
sqlite3SrcListIndexedBy(pParse, X, &I);
sqlite3DeleteFrom(pParse,X,W,O,L);
}
%endif
%ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
cmd ::= with DELETE FROM fullname(X) indexed_opt(I) where_opt(W). {
cmd ::= with(C) DELETE FROM fullname(X) indexed_opt(I) where_opt(W). {
sqlite3WithPush(pParse, C, 1);
sqlite3SrcListIndexedBy(pParse, X, &I);
sqlite3DeleteFrom(pParse,X,W,0,0);
}
@@ -803,16 +787,18 @@ where_opt(A) ::= WHERE expr(X). {A = X;}
////////////////////////// The UPDATE command ////////////////////////////////
//
%ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
cmd ::= with UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y)
cmd ::= with(C) UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y)
where_opt(W) orderby_opt(O) limit_opt(L). {
sqlite3WithPush(pParse, C, 1);
sqlite3SrcListIndexedBy(pParse, X, &I);
sqlite3ExprListCheckLength(pParse,Y,"set list");
sqlite3Update(pParse,X,Y,W,R,O,L);
}
%endif
%ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
cmd ::= with UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y)
cmd ::= with(C) UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y)
where_opt(W). {
sqlite3WithPush(pParse, C, 1);
sqlite3SrcListIndexedBy(pParse, X, &I);
sqlite3ExprListCheckLength(pParse,Y,"set list");
sqlite3Update(pParse,X,Y,W,R,0,0);
@@ -839,11 +825,13 @@ setlist(A) ::= LP idlist(X) RP EQ expr(Y). {
////////////////////////// The INSERT command /////////////////////////////////
//
cmd ::= with insert_cmd(R) INTO fullname(X) idlist_opt(F) select(S). {
cmd ::= with(W) insert_cmd(R) INTO fullname(X) idlist_opt(F) select(S). {
sqlite3WithPush(pParse, W, 1);
sqlite3Insert(pParse, X, S, F, R);
}
cmd ::= with insert_cmd(R) INTO fullname(X) idlist_opt(F) DEFAULT VALUES.
cmd ::= with(W) insert_cmd(R) INTO fullname(X) idlist_opt(F) DEFAULT VALUES.
{
sqlite3WithPush(pParse, W, 1);
sqlite3Insert(pParse, X, 0, F, R);
}
@@ -1501,13 +1489,15 @@ anylist ::= anylist ANY.
//////////////////////// COMMON TABLE EXPRESSIONS ////////////////////////////
%type with {With*}
%type wqlist {With*}
%destructor with {sqlite3WithDelete(pParse->db, $$);}
%destructor wqlist {sqlite3WithDelete(pParse->db, $$);}
with ::= .
with(A) ::= . {A = 0;}
%ifndef SQLITE_OMIT_CTE
with ::= WITH wqlist(W). { sqlite3WithPush(pParse, W, 1); }
with ::= WITH RECURSIVE wqlist(W). { sqlite3WithPush(pParse, W, 1); }
with(A) ::= WITH wqlist(W). { A = W; }
with(A) ::= WITH RECURSIVE wqlist(W). { A = W; }
wqlist(A) ::= nm(X) eidlist_opt(Y) AS LP select(Z) RP. {
A = sqlite3WithAdd(pParse, 0, &X, Y, Z); /*A-overwrites-X*/
+27
View File
@@ -660,6 +660,33 @@ void sqlite3Pragma(
break;
}
#ifdef SQLITE_SERVER_EDITION
/*
** PRAGMA [schema.]freelist_format
** PRAGMA [schema.]freelist_format = (1|2)
*/
case PragTyp_FREELIST_FORMAT: {
sqlite3VdbeUsesBtree(v, iDb);
static const VdbeOpList freelist[] = {
{ OP_Transaction, 0, 0, 0}, /* 0 */
{ OP_FreelistFmt, 0, 1, 0}, /* 1 */
{ OP_ResultRow, 1, 1, 0} /* 2 */
};
VdbeOp *aOp;
sqlite3VdbeVerifyNoMallocRequired(v, ArraySize(freelist));
aOp = sqlite3VdbeAddOpList(v, ArraySize(freelist), freelist,0);
aOp[0].p1 = iDb;
aOp[1].p1 = iDb;
if( zRight && (zRight[0]=='1' || zRight[0]=='2') && zRight[1]=='\0' ){
aOp[0].p2 = 1; /* Open a write transaction */
aOp[1].p3 = (int)(zRight[0] - '0');
}
break;
}
#endif
/*
** PRAGMA [schema.]journal_size_limit
** PRAGMA [schema.]journal_size_limit=N
+41 -33
View File
@@ -20,38 +20,39 @@
#define PragTyp_ENCODING 12
#define PragTyp_FOREIGN_KEY_CHECK 13
#define PragTyp_FOREIGN_KEY_LIST 14
#define PragTyp_FUNCTION_LIST 15
#define PragTyp_INCREMENTAL_VACUUM 16
#define PragTyp_INDEX_INFO 17
#define PragTyp_INDEX_LIST 18
#define PragTyp_INTEGRITY_CHECK 19
#define PragTyp_JOURNAL_MODE 20
#define PragTyp_JOURNAL_SIZE_LIMIT 21
#define PragTyp_LOCK_PROXY_FILE 22
#define PragTyp_LOCKING_MODE 23
#define PragTyp_PAGE_COUNT 24
#define PragTyp_MMAP_SIZE 25
#define PragTyp_MODULE_LIST 26
#define PragTyp_OPTIMIZE 27
#define PragTyp_PAGE_SIZE 28
#define PragTyp_PRAGMA_LIST 29
#define PragTyp_SECURE_DELETE 30
#define PragTyp_SHRINK_MEMORY 31
#define PragTyp_SOFT_HEAP_LIMIT 32
#define PragTyp_SYNCHRONOUS 33
#define PragTyp_TABLE_INFO 34
#define PragTyp_TEMP_STORE 35
#define PragTyp_TEMP_STORE_DIRECTORY 36
#define PragTyp_THREADS 37
#define PragTyp_WAL_AUTOCHECKPOINT 38
#define PragTyp_WAL_CHECKPOINT 39
#define PragTyp_ACTIVATE_EXTENSIONS 40
#define PragTyp_HEXKEY 41
#define PragTyp_KEY 42
#define PragTyp_REKEY 43
#define PragTyp_LOCK_STATUS 44
#define PragTyp_PARSER_TRACE 45
#define PragTyp_STATS 46
#define PragTyp_FREELIST_FORMAT 15
#define PragTyp_FUNCTION_LIST 16
#define PragTyp_INCREMENTAL_VACUUM 17
#define PragTyp_INDEX_INFO 18
#define PragTyp_INDEX_LIST 19
#define PragTyp_INTEGRITY_CHECK 20
#define PragTyp_JOURNAL_MODE 21
#define PragTyp_JOURNAL_SIZE_LIMIT 22
#define PragTyp_LOCK_PROXY_FILE 23
#define PragTyp_LOCKING_MODE 24
#define PragTyp_PAGE_COUNT 25
#define PragTyp_MMAP_SIZE 26
#define PragTyp_MODULE_LIST 27
#define PragTyp_OPTIMIZE 28
#define PragTyp_PAGE_SIZE 29
#define PragTyp_PRAGMA_LIST 30
#define PragTyp_SECURE_DELETE 31
#define PragTyp_SHRINK_MEMORY 32
#define PragTyp_SOFT_HEAP_LIMIT 33
#define PragTyp_SYNCHRONOUS 34
#define PragTyp_TABLE_INFO 35
#define PragTyp_TEMP_STORE 36
#define PragTyp_TEMP_STORE_DIRECTORY 37
#define PragTyp_THREADS 38
#define PragTyp_WAL_AUTOCHECKPOINT 39
#define PragTyp_WAL_CHECKPOINT 40
#define PragTyp_ACTIVATE_EXTENSIONS 41
#define PragTyp_HEXKEY 42
#define PragTyp_KEY 43
#define PragTyp_REKEY 44
#define PragTyp_LOCK_STATUS 45
#define PragTyp_PARSER_TRACE 46
#define PragTyp_STATS 47
/* Property flags associated with various pragma. */
#define PragFlg_NeedSchema 0x01 /* Force schema load before running */
@@ -300,6 +301,13 @@ static const PragmaName aPragmaName[] = {
/* ColNames: */ 0, 0,
/* iArg: */ BTREE_FREE_PAGE_COUNT },
#endif
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && defined(SQLITE_SERVER_EDITION)
{/* zName: */ "freelist_format",
/* ePragTyp: */ PragTyp_FREELIST_FORMAT,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq,
/* ColNames: */ 0, 0,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
{/* zName: */ "full_column_names",
/* ePragTyp: */ PragTyp_FLAG,
@@ -646,4 +654,4 @@ static const PragmaName aPragmaName[] = {
/* iArg: */ SQLITE_WriteSchema },
#endif
};
/* Number of pragmas: 60 on by default, 77 total. */
/* Number of pragmas: 60 on by default, 78 total. */
+19 -199
View File
@@ -44,20 +44,6 @@ struct DistinctCtx {
/*
** An instance of the following object is used to record information about
** the ORDER BY (or GROUP BY) clause of query is being coded.
**
** The aDefer[] array is used by the sorter-references optimization. For
** example, assuming there is no index that can be used for the ORDER BY,
** for the query:
**
** SELECT a, bigblob FROM t1 ORDER BY a LIMIT 10;
**
** it may be more efficient to add just the "a" values to the sorter, and
** retrieve the associated "bigblob" values directly from table t1 as the
** 10 smallest "a" values are extracted from the sorter.
**
** When the sorter-reference optimization is used, there is one entry in the
** aDefer[] array for each database table that may be read as values are
** extracted from the sorter.
*/
typedef struct SortCtx SortCtx;
struct SortCtx {
@@ -70,14 +56,6 @@ struct SortCtx {
int labelDone; /* Jump here when done, ex: LIMIT reached */
u8 sortFlags; /* Zero or more SORTFLAG_* bits */
u8 bOrderedInnerLoop; /* ORDER BY correctly sorts the inner loop */
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
u8 nDefer; /* Number of valid entries in aDefer[] */
struct DeferredCsr {
Table *pTab; /* Table definition */
int iCsr; /* Cursor number for table */
int nKey; /* Number of PK columns for table pTab (>=1) */
} aDefer[4];
#endif
};
#define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */
@@ -700,90 +678,6 @@ static void codeDistinct(
sqlite3ReleaseTempReg(pParse, r1);
}
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
/*
** This function is called as part of inner-loop generation for a SELECT
** statement with an ORDER BY that is not optimized by an index. It
** determines the expressions, if any, that the sorter-reference
** optimization should be used for. The sorter-reference optimization
** is used for SELECT queries like:
**
** SELECT a, bigblob FROM t1 ORDER BY a LIMIT 10
**
** If the optimization is used for expression "bigblob", then instead of
** storing values read from that column in the sorter records, the PK of
** the row from table t1 is stored instead. Then, as records are extracted from
** the sorter to return to the user, the required value of bigblob is
** retrieved directly from table t1. If the values are very large, this
** can be more efficient than storing them directly in the sorter records.
**
** The ExprList_item.bSorterRef flag is set for each expression in pEList
** for which the sorter-reference optimization should be enabled.
** Additionally, the pSort->aDefer[] array is populated with entries
** for all cursors required to evaluate all selected expressions. Finally.
** output variable (*ppExtra) is set to an expression list containing
** expressions for all extra PK values that should be stored in the
** sorter records.
*/
static void selectExprDefer(
Parse *pParse, /* Leave any error here */
SortCtx *pSort, /* Sorter context */
ExprList *pEList, /* Expressions destined for sorter */
ExprList **ppExtra /* Expressions to append to sorter record */
){
int i;
int nDefer = 0;
ExprList *pExtra = 0;
for(i=0; i<pEList->nExpr; i++){
struct ExprList_item *pItem = &pEList->a[i];
if( pItem->u.x.iOrderByCol==0 ){
Expr *pExpr = pItem->pExpr;
Table *pTab = pExpr->pTab;
if( pExpr->op==TK_COLUMN && pTab && !IsVirtual(pTab)
&& (pTab->aCol[pExpr->iColumn].colFlags & COLFLAG_SORTERREF)
#if 0
&& pTab->pSchema && pTab->pSelect==0 && !IsVirtual(pTab)
#endif
){
int j;
for(j=0; j<nDefer; j++){
if( pSort->aDefer[j].iCsr==pExpr->iTable ) break;
}
if( j==nDefer ){
if( nDefer==ArraySize(pSort->aDefer) ){
continue;
}else{
int nKey = 1;
int k;
Index *pPk = 0;
if( !HasRowid(pTab) ){
pPk = sqlite3PrimaryKeyIndex(pTab);
nKey = pPk->nKeyCol;
}
for(k=0; k<nKey; k++){
Expr *pNew = sqlite3PExpr(pParse, TK_COLUMN, 0, 0);
if( pNew ){
pNew->iTable = pExpr->iTable;
pNew->pTab = pExpr->pTab;
pNew->iColumn = pPk ? pPk->aiColumn[k] : -1;
pExtra = sqlite3ExprListAppend(pParse, pExtra, pNew);
}
}
pSort->aDefer[nDefer].pTab = pExpr->pTab;
pSort->aDefer[nDefer].iCsr = pExpr->iTable;
pSort->aDefer[nDefer].nKey = nKey;
nDefer++;
}
}
pItem->bSorterRef = 1;
}
}
}
pSort->nDefer = (u8)nDefer;
*ppExtra = pExtra;
}
#endif
/*
** This routine generates the code for the inside of the inner loop
** of a SELECT.
@@ -856,9 +750,6 @@ static void selectInnerLoop(
VdbeComment((v, "%s", p->pEList->a[i].zName));
}
}else if( eDest!=SRT_Exists ){
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
ExprList *pExtra = 0;
#endif
/* If the destination is an EXISTS(...) expression, the actual
** values returned by the SELECT are not required.
*/
@@ -882,34 +773,12 @@ static void selectInnerLoop(
p->pEList->a[j-1].u.x.iOrderByCol = i+1-pSort->nOBSat;
}
}
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
selectExprDefer(pParse, pSort, p->pEList, &pExtra);
if( pExtra && pParse->db->mallocFailed==0 ){
/* If there are any extra PK columns to add to the sorter records,
** allocate extra memory cells and adjust the OpenEphemeral
** instruction to account for the larger records. This is only
** required if there are one or more WITHOUT ROWID tables with
** composite primary keys in the SortCtx.aDefer[] array. */
VdbeOp *pOp = sqlite3VdbeGetOp(v, pSort->addrSortIndex);
pOp->p2 += (pExtra->nExpr - pSort->nDefer);
pOp->p4.pKeyInfo->nAllField += (pExtra->nExpr - pSort->nDefer);
pParse->nMem += pExtra->nExpr;
}
#endif
regOrig = 0;
assert( eDest==SRT_Set || eDest==SRT_Mem
|| eDest==SRT_Coroutine || eDest==SRT_Output );
}
nResultCol = sqlite3ExprCodeExprList(pParse,p->pEList,regResult,
0,ecelFlags);
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
if( pExtra ){
nResultCol += sqlite3ExprCodeExprList(
pParse, pExtra, regResult + nResultCol, 0, 0
);
sqlite3ExprListDelete(pParse->db, pExtra);
}
#endif
}
/* If the DISTINCT keyword was present on the SELECT statement
@@ -1367,7 +1236,7 @@ static void generateSortTail(
Vdbe *v = pParse->pVdbe; /* The prepared statement */
int addrBreak = pSort->labelDone; /* Jump here to exit loop */
int addrContinue = sqlite3VdbeMakeLabel(v); /* Jump here for next cycle */
int addr; /* Top of output loop. Jump for Next. */
int addr;
int addrOnce = 0;
int iTab;
ExprList *pOrderBy = pSort->pOrderBy;
@@ -1376,11 +1245,11 @@ static void generateSortTail(
int regRow;
int regRowid;
int iCol;
int nKey; /* Number of key columns in sorter record */
int nKey;
int iSortTab; /* Sorter cursor to read from */
int nSortData; /* Trailing values to read from sorter */
int i;
int bSeq; /* True if sorter record includes seq. no. */
int nRefKey = 0;
struct ExprList_item *aOutEx = p->pEList->a;
assert( addrBreak<0 );
@@ -1389,24 +1258,15 @@ static void generateSortTail(
sqlite3VdbeGoto(v, addrBreak);
sqlite3VdbeResolveLabel(v, pSort->labelBkOut);
}
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
/* Open any cursors needed for sorter-reference expressions */
for(i=0; i<pSort->nDefer; i++){
Table *pTab = pSort->aDefer[i].pTab;
int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
sqlite3OpenTable(pParse, pSort->aDefer[i].iCsr, iDb, pTab, OP_OpenRead);
nRefKey = MAX(nRefKey, pSort->aDefer[i].nKey);
}
#endif
iTab = pSort->iECursor;
if( eDest==SRT_Output || eDest==SRT_Coroutine || eDest==SRT_Mem ){
regRowid = 0;
regRow = pDest->iSdst;
nSortData = nColumn;
}else{
regRowid = sqlite3GetTempReg(pParse);
regRow = sqlite3GetTempRange(pParse, nColumn);
nSortData = nColumn;
}
nKey = pOrderBy->nExpr - pSort->nOBSat;
if( pSort->sortFlags & SORTFLAG_UseSorter ){
@@ -1415,8 +1275,7 @@ static void generateSortTail(
if( pSort->labelBkOut ){
addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
}
sqlite3VdbeAddOp3(v, OP_OpenPseudo, iSortTab, regSortOut,
nKey+1+nColumn+nRefKey);
sqlite3VdbeAddOp3(v, OP_OpenPseudo, iSortTab, regSortOut, nKey+1+nSortData);
if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak);
VdbeCoverage(v);
@@ -1429,59 +1288,18 @@ static void generateSortTail(
iSortTab = iTab;
bSeq = 1;
}
for(i=0, iCol=nKey+bSeq-1; i<nColumn; i++){
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
if( aOutEx[i].bSorterRef ) continue;
#endif
for(i=0, iCol=nKey+bSeq-1; i<nSortData; i++){
if( aOutEx[i].u.x.iOrderByCol==0 ) iCol++;
}
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
if( pSort->nDefer ){
int iKey = iCol+1;
int regKey = sqlite3GetTempRange(pParse, nRefKey);
for(i=0; i<pSort->nDefer; i++){
int iCsr = pSort->aDefer[i].iCsr;
Table *pTab = pSort->aDefer[i].pTab;
int nKey = pSort->aDefer[i].nKey;
sqlite3VdbeAddOp1(v, OP_NullRow, iCsr);
if( HasRowid(pTab) ){
sqlite3VdbeAddOp3(v, OP_Column, iSortTab, iKey++, regKey);
sqlite3VdbeAddOp3(v, OP_SeekRowid, iCsr,
sqlite3VdbeCurrentAddr(v)+1, regKey);
}else{
int k;
int iJmp;
assert( sqlite3PrimaryKeyIndex(pTab)->nKeyCol==nKey );
for(k=0; k<nKey; k++){
sqlite3VdbeAddOp3(v, OP_Column, iSortTab, iKey++, regKey+k);
}
iJmp = sqlite3VdbeCurrentAddr(v);
sqlite3VdbeAddOp4Int(v, OP_SeekGE, iCsr, iJmp+2, regKey, nKey);
sqlite3VdbeAddOp4Int(v, OP_IdxLE, iCsr, iJmp+3, regKey, nKey);
sqlite3VdbeAddOp1(v, OP_NullRow, iCsr);
}
}
sqlite3ReleaseTempRange(pParse, regKey, nRefKey);
}
#endif
for(i=nColumn-1; i>=0; i--){
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
if( aOutEx[i].bSorterRef ){
sqlite3ExprCode(pParse, aOutEx[i].pExpr, regRow+i);
}else
#endif
{
int iRead;
if( aOutEx[i].u.x.iOrderByCol ){
iRead = aOutEx[i].u.x.iOrderByCol-1;
}else{
iRead = iCol--;
}
sqlite3VdbeAddOp3(v, OP_Column, iSortTab, iRead, regRow+i);
VdbeComment((v, "%s", aOutEx[i].zName?aOutEx[i].zName : aOutEx[i].zSpan));
for(i=nSortData-1; i>=0; i--){
int iRead;
if( aOutEx[i].u.x.iOrderByCol ){
iRead = aOutEx[i].u.x.iOrderByCol-1;
}else{
iRead = iCol--;
}
sqlite3VdbeAddOp3(v, OP_Column, iSortTab, iRead, regRow+i);
VdbeComment((v, "%s", aOutEx[i].zName ? aOutEx[i].zName : aOutEx[i].zSpan));
}
switch( eDest ){
case SRT_Table:
@@ -3955,6 +3773,7 @@ static int flattenSubquery(
pOrderBy->a[i].u.x.iOrderByCol = 0;
}
assert( pParent->pOrderBy==0 );
assert( pSub->pPrior==0 );
pParent->pOrderBy = pOrderBy;
pSub->pOrderBy = 0;
}
@@ -4560,7 +4379,9 @@ static int selectExpander(Walker *pWalker, Select *p){
}
pTabList = p->pSrc;
pEList = p->pEList;
sqlite3WithPush(pParse, p->pWith, 0);
if( OK_IF_ALWAYS_TRUE(p->pWith) ){
sqlite3WithPush(pParse, p->pWith, 0);
}
/* Make sure cursor numbers have been assigned to all entries in
** the FROM clause of the SELECT statement.
@@ -6257,7 +6078,6 @@ int sqlite3Select(
if( sSort.pOrderBy ){
explainTempTable(pParse,
sSort.nOBSat>0 ? "RIGHT PART OF ORDER BY":"ORDER BY");
assert( p->pEList==pEList );
generateSortTail(pParse, p, &sSort, pEList->nExpr, pDest);
}
+1026
View File
File diff suppressed because it is too large Load Diff
+56
View File
@@ -0,0 +1,56 @@
/*
** 2017 April 24
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
*/
#ifdef SQLITE_SERVER_EDITION
#ifndef SQLITE_SERVER_H
#define SQLITE_SERVER_H
typedef struct Server Server;
typedef struct ServerPage ServerPage;
struct ServerPage {
Pgno pgno; /* Page number for this record */
int nData; /* Size of aData[] in bytes */
u8 *aData;
ServerPage *pNext;
int iCommitId;
ServerPage *pHashNext;
ServerPage *pHashPrev;
};
int sqlite3ServerConnect(Pager *pPager, int eServer, Server **ppOut);
void sqlite3ServerDisconnect(Server *p, sqlite3_file *dbfd);
int sqlite3ServerBegin(Server *p, int bReadonly);
int sqlite3ServerPreCommit(Server*, ServerPage*);
int sqlite3ServerEnd(Server *p);
int sqlite3ServerEndWrite(Server *p);
int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite, int bBlock);
ServerPage *sqlite3ServerBuffer(Server*);
int sqlite3ServerIsSingleProcess(Server*);
/* For "BEGIN READONLY" clients. */
int sqlite3ServerIsReadonly(Server*);
void sqlite3ServerReadPage(Server*, Pgno, u8**);
void sqlite3ServerEndReadPage(Server*, Pgno);
#endif /* SQLITE_SERVER_H */
#endif /* SQLITE_SERVER_EDITION */
+2 -12
View File
@@ -3733,6 +3733,7 @@ static FILE *output_file_open(const char *zFile, int bTextMode){
return f;
}
#if !defined(SQLITE_UNTESTABLE)
#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT)
/*
** A routine for handling output from sqlite3_trace().
@@ -3755,6 +3756,7 @@ static int sql_trace_callback(
return 0;
}
#endif
#endif
/*
** A no-op routine that runs with the ".breakpoint" doc-command. This is
@@ -8076,9 +8078,6 @@ static const char zOptions[] =
" -quote set output mode to 'quote'\n"
" -readonly open the database read-only\n"
" -separator SEP set output column separator. Default: '|'\n"
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
" -sorterref SIZE sorter references threshold size\n"
#endif
" -stats print memory stats before each finalize\n"
" -version show SQLite version\n"
" -vfs NAME use NAME as the default VFS\n"
@@ -8334,11 +8333,6 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
}else if( strcmp(z,"-mmap")==0 ){
sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i));
sqlite3_config(SQLITE_CONFIG_MMAP_SIZE, sz, sz);
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
}else if( strcmp(z,"-sorterref")==0 ){
sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i));
sqlite3_config(SQLITE_CONFIG_SORTERREF_SIZE, (int)sz);
#endif
}else if( strcmp(z,"-vfs")==0 ){
sqlite3_vfs *pVfs = sqlite3_vfs_find(cmdline_option_value(argc,argv,++i));
if( pVfs ){
@@ -8475,10 +8469,6 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
i+=2;
}else if( strcmp(z,"-mmap")==0 ){
i++;
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
}else if( strcmp(z,"-sorterref")==0 ){
i++;
#endif
}else if( strcmp(z,"-vfs")==0 ){
i++;
#ifdef SQLITE_ENABLE_VFSTRACE
+7 -17
View File
@@ -506,6 +506,7 @@ int sqlite3_exec(
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
#define SQLITE_BUSY_DEADLOCK (SQLITE_BUSY | (3<<8))
#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
#define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8))
#define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8))
@@ -1105,6 +1106,12 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_COMMIT_ATOMIC_WRITE 32
#define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33
#define SQLITE_FCNTL_LOCK_TIMEOUT 34
#define SQLITE_FCNTL_FILEID 35
#define SQLITE_FCNTL_SERVER_MODE 36
#define SQLITE_FCNTL_SERVER_SHMOPEN 37
#define SQLITE_FCNTL_SERVER_SHMOPEN2 38
#define SQLITE_FCNTL_SERVER_SHMLOCK 39
#define SQLITE_FCNTL_SERVER_SHMCLOSE 40
/* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
@@ -1930,22 +1937,6 @@ struct sqlite3_mem_methods {
** I/O required to support statement rollback.
** The default value for this setting is controlled by the
** [SQLITE_STMTJRNL_SPILL] compile-time option.
**
** [[SQLITE_CONFIG_SORTERREF_SIZE]]
** <dt>SQLITE_CONFIG_SORTERREF_SIZE
** <dd>The SQLITE_CONFIG_SORTERREF_SIZE option accepts a single parameter
** of type (int) - the new value of the sorter-reference size threshold.
** Usually, when SQLite uses an external sort to order records according
** to an ORDER BY clause, all fields required by the caller are present in the
** sorted records. However, if SQLite determines based on the declared type
** of a table column that its values are likely to be very large - larger
** than the configured sorter-reference size threshold - then a reference
** is stored in each sorted record and the required column values loaded
** from the database as records are returned in sorted order. The default
** value for this option is to never use this optimization. Specifying a
** negative value for this option restores the default behaviour.
** This option is only available if SQLite is compiled with the
** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option.
** </dl>
*/
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
@@ -1975,7 +1966,6 @@ struct sqlite3_mem_methods {
#define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */
#define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */
#define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */
#define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */
/*
** CAPI3REF: Database Connection Configuration Options
+3 -11
View File
@@ -637,13 +637,6 @@
# define SQLITE_DEFAULT_PCACHE_INITSZ 20
#endif
/*
** Default value for the SQLITE_CONFIG_SORTERREF_SIZE option.
*/
#ifndef SQLITE_DEFAULT_SORTERREF_SIZE
# define SQLITE_DEFAULT_SORTERREF_SIZE 0x7fffffff
#endif
/*
** The compile-time options SQLITE_MMAP_READWRITE and
** SQLITE_ENABLE_BATCH_ATOMIC_WRITE are not compatible with one another.
@@ -1127,6 +1120,7 @@ typedef int VList;
#include "pcache.h"
#include "os.h"
#include "mutex.h"
#include "server.h"
/* The SQLITE_EXTRA_DURABLE compile-time option used to set the default
** synchronous setting to EXTRA. It is no longer supported.
@@ -1352,6 +1346,7 @@ struct sqlite3 {
u16 dbOptFlags; /* Flags to enable/disable optimizations */
u8 enc; /* Text encoding */
u8 autoCommit; /* The auto-commit flag. */
u8 readonlyTrans; /* Transaction opened with BEGIN READONLY */
u8 temp_store; /* 1: file 2: memory 0: default */
u8 mallocFailed; /* True if we have seen a malloc failure */
u8 bBenignMalloc; /* Do not require OOMs if true */
@@ -1766,7 +1761,6 @@ struct Column {
#define COLFLAG_HIDDEN 0x0002 /* A hidden column in a virtual table */
#define COLFLAG_HASTYPE 0x0004 /* Type name follows column name */
#define COLFLAG_UNIQUE 0x0008 /* Column def contains "UNIQUE" or "PK" */
#define COLFLAG_SORTERREF 0x0010 /* Use sorter-refs with this column */
/*
** A "Collating Sequence" is defined by an instance of the following
@@ -2507,7 +2501,6 @@ struct ExprList {
unsigned done :1; /* A flag to indicate when processing is finished */
unsigned bSpanIsTab :1; /* zSpan holds DB.TABLE.COLUMN */
unsigned reusable :1; /* Constant expression is reusable */
unsigned bSorterRef :1; /* Defer evaluation until after sorting */
union {
struct {
u16 iOrderByCol; /* For ORDER BY, column number in result set */
@@ -3329,7 +3322,6 @@ struct Sqlite3Config {
#endif
int bLocaltimeFault; /* True to fail localtime() calls */
int iOnceResetThreshold; /* When to reset OP_Once counters */
u32 szSorterRef; /* Min size in bytes to use sorter-refs */
};
/*
@@ -4112,7 +4104,7 @@ void sqlite3ColumnDefault(Vdbe *, Table *, int, int);
void sqlite3AlterFinishAddColumn(Parse *, Token *);
void sqlite3AlterBeginAddColumn(Parse *, SrcList *);
CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*);
char sqlite3AffinityType(const char*, Column*);
char sqlite3AffinityType(const char*, u8*);
void sqlite3Analyze(Parse*, Token*, Token*);
int sqlite3InvokeBusyHandler(BusyHandler*, sqlite3_file*);
int sqlite3FindDb(sqlite3*, Token*);
-22
View File
@@ -2256,27 +2256,6 @@ static int SQLITE_TCLAPI test_config_sqllog(
}
#endif
/*
** Usage: sqlite3_config_sorterref
**
** Set the SQLITE_CONFIG_SORTERREF_SIZE configuration option
*/
static int SQLITE_TCLAPI test_config_sorterref(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int iVal;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "NBYTE");
return TCL_ERROR;
}
if( Tcl_GetIntFromObj(interp, objv[1], &iVal) ) return TCL_ERROR;
sqlite3_config(SQLITE_CONFIG_SORTERREF_SIZE, iVal);
return TCL_OK;
}
/*
** Usage: vfs_current_time_int64
**
@@ -7772,7 +7751,6 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "sqlite3_delete_database", test_delete_database, 0 },
{ "atomic_batch_write", test_atomic_batch_write, 0 },
{ "sqlite3_mmap_warm", test_mmap_warm, 0 },
{ "sqlite3_config_sorterref", test_config_sorterref, 0 },
};
static int bitmask_size = sizeof(Bitmask)*8;
static int longdouble_size = sizeof(LONGDOUBLE_TYPE);
+1 -1
View File
@@ -324,7 +324,7 @@ static int SQLITE_TCLAPI page_get(
}
pPager = sqlite3TestTextToPtr(argv[1]);
if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR;
rc = sqlite3PagerSharedLock(pPager);
rc = sqlite3PagerSharedLock(pPager, 0);
if( rc==SQLITE_OK ){
rc = sqlite3PagerGet(pPager, pgno, &pPage, 0);
}
+6
View File
@@ -762,6 +762,12 @@ Tcl_SetVar2(interp, "sqlite_options", "mergesort", "1", TCL_GLOBAL_ONLY);
Tcl_SetVar2(interp, "sqlite_options", "uri_00_error", "0", TCL_GLOBAL_ONLY);
#endif
#ifdef SQLITE_SERVER_EDITION
Tcl_SetVar2(interp, "sqlite_options", "server", "1", TCL_GLOBAL_ONLY);
#else
Tcl_SetVar2(interp, "sqlite_options", "server", "0", TCL_GLOBAL_ONLY);
#endif
#define LINKVAR(x) { \
static const int cv_ ## x = SQLITE_ ## x; \
Tcl_LinkVar(interp, "SQLITE_" #x, (char *)&(cv_ ## x), \
+1 -1
View File
@@ -396,7 +396,7 @@ void sqlite3Update(
regKey = ++pParse->nMem;
iEph = pParse->nTab++;
sqlite3VdbeAddOp3(v, OP_Null, 0, iPk, iPk+nPk-1);
sqlite3VdbeAddOp2(v, OP_Null, 0, iPk);
addrOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk);
sqlite3VdbeSetP4KeyInfo(pParse, pPk);
}
+30 -1
View File
@@ -3096,7 +3096,7 @@ case OP_Savepoint: {
break;
}
/* Opcode: AutoCommit P1 P2 * * *
/* Opcode: AutoCommit P1 P2 P3 * *
**
** Set the database auto-commit flag to P1 (1 or 0). If P2 is true, roll
** back any currently active btree transactions. If there are any active
@@ -3113,6 +3113,7 @@ case OP_AutoCommit: {
iRollback = pOp->p2;
assert( desiredAutoCommit==1 || desiredAutoCommit==0 );
assert( desiredAutoCommit==1 || iRollback==0 );
assert( desiredAutoCommit==0 || pOp->p3==0 );
assert( db->nVdbeActive>0 ); /* At least this one VM is active */
assert( p->bIsReader );
@@ -3144,6 +3145,7 @@ case OP_AutoCommit: {
sqlite3CloseSavepoints(db);
if( p->rc==SQLITE_OK ){
rc = SQLITE_DONE;
db->readonlyTrans = (pOp->p3==TK_READONLY);
}else{
rc = SQLITE_ERROR;
}
@@ -3351,6 +3353,33 @@ case OP_SetCookie: {
break;
}
#ifdef SQLITE_SERVER_EDITION
/* Opcode: FreelistFmt P1 P2 P3 * *
**
** Parameter P3 must be 0, 1 or 2. If it is not 0, attempt to set the
** freelist format of database P1 to format 1 or format 2. Before
** returning, store the final freelist format (either 1 or 2) of
** database P1 into register P2.
*/
case OP_FreelistFmt: { /* out2 */
Db *pDb;
int iVal;
assert( pOp->p1>=0 && pOp->p1<db->nDb );
assert( DbMaskTest(p->btreeMask, pOp->p1) );
assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) );
pDb = &db->aDb[pOp->p1];
assert( pDb->pBt!=0 );
pOut = out2Prerelease(p, pOp);
rc = sqlite3BtreeFreelistFormat(pDb->pBt, pOp->p3, &iVal);
if( rc ) goto abort_due_to_error;
pOut->u.i = iVal;
break;
}
#endif
/* Opcode: OpenRead P1 P2 P3 P4 P5
** Synopsis: root=P2 iDb=P3
**
+7 -6
View File
@@ -2662,12 +2662,13 @@ int sqlite3VdbeHalt(Vdbe *p){
/* Check for one of the special errors */
mrc = p->rc & 0xff;
isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR
|| mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL;
|| mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL
|| p->rc==SQLITE_BUSY_DEADLOCK;
if( isSpecialError ){
/* If the query was read-only and the error code is SQLITE_INTERRUPT,
** no rollback is necessary. Otherwise, at least a savepoint
** transaction must be rolled back to restore the database to a
** consistent state.
/* If the query was read-only and the error code is SQLITE_INTERRUPT
** or SQLITE_BUSY_SERVER, no rollback is necessary. Otherwise, at
** least a savepoint transaction must be rolled back to restore the
** database to a consistent state.
**
** Even if the statement is read-only, it is important to perform
** a statement or transaction rollback operation. If the error
@@ -2676,7 +2677,7 @@ int sqlite3VdbeHalt(Vdbe *p){
** pagerStress() in pager.c), the rollback is required to restore
** the pager to a consistent state.
*/
if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){
if( !p->readOnly || (mrc!=SQLITE_INTERRUPT && mrc!=SQLITE_BUSY) ){
if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) && p->usesStmtJournal ){
eStatementOp = SAVEPOINT_ROLLBACK;
}else{
+6 -17
View File
@@ -2373,8 +2373,8 @@ static int whereLoopAddBtreeIndex(
pNew = pBuilder->pNew;
if( db->mallocFailed ) return SQLITE_NOMEM_BKPT;
WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d\n",
pProbe->pTable->zName,pProbe->zName, pNew->u.btree.nEq));
WHERETRACE(0x800, ("BEGIN addBtreeIdx(%s), nEq=%d\n",
pProbe->zName, pNew->u.btree.nEq));
assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 );
assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 );
@@ -2660,8 +2660,8 @@ static int whereLoopAddBtreeIndex(
pNew->wsFlags = saved_wsFlags;
}
WHERETRACE(0x800, ("END %s.addBtreeIdx(%s), nEq=%d, rc=%d\n",
pProbe->pTable->zName, pProbe->zName, saved_nEq, rc));
WHERETRACE(0x800, ("END addBtreeIdx(%s), nEq=%d, rc=%d\n",
pProbe->zName, saved_nEq, rc));
return rc;
}
@@ -3099,9 +3099,9 @@ static int whereLoopAddVirtualOne(
|| pNew->aLTerm[iTerm]!=0
|| pIdxCons->usable==0
){
rc = SQLITE_ERROR;
sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pTab->zName);
testcase( pIdxInfo->needToFreeIdxStr );
return SQLITE_ERROR;
return rc;
}
testcase( iTerm==nConstraint-1 );
testcase( j==0 );
@@ -3129,15 +3129,6 @@ static int whereLoopAddVirtualOne(
pNew->u.vtab.omitMask &= ~mNoOmit;
pNew->nLTerm = mxTerm+1;
for(i=0; i<=mxTerm; i++){
if( pNew->aLTerm[i]==0 ){
/* The non-zero argvIdx values must be contiguous. Raise an
** error if they are not */
sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pTab->zName);
testcase( pIdxInfo->needToFreeIdxStr );
return SQLITE_ERROR;
}
}
assert( pNew->nLTerm<=pNew->nLSlot );
pNew->u.vtab.idxNum = pIdxInfo->idxNum;
pNew->u.vtab.needFree = pIdxInfo->needToFreeIdxStr;
@@ -3253,7 +3244,6 @@ static int whereLoopAddVirtual(
}
/* First call xBestIndex() with all constraints usable. */
WHERETRACE(0x800, ("BEGIN %s.addVirtual()\n", pSrc->pTab->zName));
WHERETRACE(0x40, (" VirtualOne: all usable\n"));
rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn);
@@ -3329,7 +3319,6 @@ static int whereLoopAddVirtual(
if( p->needToFreeIdxStr ) sqlite3_free(p->idxStr);
sqlite3DbFreeNN(pParse->db, p);
WHERETRACE(0x800, ("END %s.addVirtual(), rc=%d\n", pSrc->pTab->zName, rc));
return rc;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
+1 -1
View File
@@ -19,7 +19,7 @@
** Trace output macros
*/
#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
/***/ extern int sqlite3WhereTrace;
/***/ int sqlite3WhereTrace;
#endif
#if defined(SQLITE_DEBUG) \
&& (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE))
+3 -4
View File
@@ -2127,7 +2127,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
continue;
}
if( (pTerm->wtFlags & TERM_LIKECOND)!=0 ){
if( pTerm->wtFlags & TERM_LIKECOND ){
/* If the TERM_LIKECOND flag is set, that means that the range search
** is sufficient to guarantee that the LIKE operator is true, so we
** can skip the call to the like(A,B) function. But this only works
@@ -2137,9 +2137,8 @@ Bitmask sqlite3WhereCodeOneLoopStart(
continue;
#else
u32 x = pLevel->iLikeRepCntr;
if( x>0 ){
skipLikeAddr = sqlite3VdbeAddOp1(v, (x&1)?OP_IfNot:OP_If,(int)(x>>1));
}
assert( x>0 );
skipLikeAddr = sqlite3VdbeAddOp1(v, (x&1)?OP_IfNot:OP_If, (int)(x>>1));
VdbeCoverage(v);
#endif
}
-10
View File
@@ -806,16 +806,6 @@ do_execsql_test join-15.105 {
FROM t1 LEFT JOIN t2
WHERE a IN (1,3,x,y);
} {1 2 {} {} x 3 4 {} {} x}
do_execsql_test join-15.106 {
SELECT *, 'x'
FROM t1 LEFT JOIN t2
WHERE NOT ( 'x'='y' AND t2.y=1 );
} {1 2 {} {} x 3 4 {} {} x}
do_execsql_test join-15.107 {
SELECT *, 'x'
FROM t1 LEFT JOIN t2
WHERE t2.y IS NOT 'abc'
} {1 2 {} {} x 3 4 {} {} x}
do_execsql_test join-15.110 {
DROP TABLE t1;
DROP TABLE t2;
+1 -1
View File
@@ -562,7 +562,7 @@ static int exportMain(int argc, char **argv){
nWrote = fwrite(pData, 1, (size_t)nData, out);
fclose(out);
printf("\r%s ", zTail); fflush(stdout);
if( nWrote!=(size_t)nData ){
if( nWrote!=nData ){
fatalError("Wrote only %d of %d bytes to %s\n",
(int)nWrote, nData, zFN);
}
-1
View File
@@ -57,7 +57,6 @@ do_execsql_test 110 {
# What happens when we try to VACUUM a MEMDB database?
#
do_execsql_test 120 {
PRAGMA auto_vacuum = off;
VACUUM;
} {}
do_execsql_test 130 {
-4
View File
@@ -57,10 +57,6 @@ do_catchsql_test misc8-1.7 {
ORDER BY rowid;
} {1 {abort due to ROLLBACK}}
do_catchsql_test misc8-1.8 {
PRAGMA empty_result_callbacks = 1;
SELECT eval('SELECT * FROM t1 WHERE 1 = 0;');
} {0 {{}}}
reset_db
+8 -20
View File
@@ -275,6 +275,14 @@ test_suite "fts5" -prefix "" -description {
All FTS5 tests.
} -files [glob -nocomplain $::testdir/../ext/fts5/test/*.test]
test_suite "server" -prefix "" -description {
All server-edition tests.
} -files [
test_set \
select1.test server2.test server3.test serverfreelist.test \
serverreadonly.test servercrash.test serverlimit.test
]
test_suite "fts5-light" -prefix "" -description {
All FTS5 tests.
} -files [
@@ -1073,26 +1081,6 @@ test_suite "prepare" -description {
stmtvtab1.test index9.test
]
test_suite "sorterref" -prefix "" -description {
Run the "veryquick" test suite with SQLITE_CONFIG_SORTERREF_SIZE set
to 0 so that sorter-references are used whenever possible.
} -files [
test_set $allquicktests -exclude *malloc* *ioerr* *fault* *bigfile* *_err* \
*fts5corrupt* *fts5big* *fts5aj*
] -initialize {
catch {db close}
sqlite3_shutdown
sqlite3_config_sorterref 0
sqlite3_initialize
autoinstall_test_functions
} -shutdown {
catch {db close}
sqlite3_shutdown
sqlite3_config_sorterref -1
sqlite3_initialize
autoinstall_test_functions
}
# End of tests
#############################################################################
-1
View File
@@ -173,7 +173,6 @@ array set ::Configs [strip_comments {
-DSQLITE_OMIT_TRACE=1
-DSQLITE_TEMP_STORE=3
-DSQLITE_THREADSAFE=2
-DSQLITE_ENABLE_DESERIALIZE=1
--enable-json1 --enable-fts5 --enable-session
}
"Locking-Style" {
+175
View File
@@ -0,0 +1,175 @@
# 2017 April 25
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this script is testing the server mode of SQLite.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix server2
source $testdir/server_common.tcl
return_if_no_server
db close
foreach {tn vfs} {1 unix-excl 2 unix} {
server_set_vfs $vfs
foreach f [glob -nocomplain test.db*] {
forcedelete $f
}
#-------------------------------------------------------------------------
# Check that the *-journal* files are deleted correctly.
#
server_reset_db
do_execsql_test 1.0 {
CREATE TABLE t1(a, b);
} {}
do_test $tn.1.1 {
lsort [glob -nocomplain test.db-journal/*-journal]
} {test.db-journal/0-journal}
do_test $tn.1.2 {
db close
lsort [glob -nocomplain test.db-journal/*-journal]
} {}
server_sqlite3 db test.db
do_execsql_test $tn.1.3 {
CREATE TABLE t2(a, b);
} {}
server_sqlite3 db2 test.db
do_test $tn.1.4 {
db eval {
BEGIN;
INSERT INTO t1 VALUES(1, 2);
}
db2 eval {
BEGIN;
INSERT INTO t2 VALUES(3, 4);
}
} {}
do_test $tn.1.5 {
db2 eval COMMIT
db eval COMMIT
lsort [glob -nocomplain test.db-journal/*-journal]
} {test.db-journal/0-journal test.db-journal/1-journal}
do_test $tn.1.6 {
db close
lsort [glob -nocomplain test.db-journal/*-journal]
} {test.db-journal/0-journal test.db-journal/1-journal}
do_test $tn.1.7 {
db2 close
lsort [glob -nocomplain test.db-journal/*-journal]
} {}
#-------------------------------------------------------------------------
#
server_reset_db
server_sqlite3 db2 test.db
do_execsql_test $tn.2.0 {
CREATE TABLE t1(a, b);
CREATE TABLE t2(c, d);
}
# Two concurrent transactions committed.
#
do_test $tn.2.1 {
db eval {
BEGIN;
INSERT INTO t1 VALUES(1, 2);
}
db2 eval {
BEGIN;
INSERT INTO t2 VALUES(3, 4);
}
} {}
do_test $tn.2.2 {
lsort [glob -nocomplain test.db-journal/*-journal]
} {test.db-journal/0-journal test.db-journal/1-journal}
do_test $tn.2.3.1 { db eval COMMIT } {}
do_test $tn.2.3.2 { db2 eval COMMIT } {}
do_execsql_test 2.4 {SELECT * FROM t1, t2} {1 2 3 4}
do_test $tn.2.5 {
lsort [glob -nocomplain test.db-journal/*-journal]
} {test.db-journal/0-journal test.db-journal/1-journal}
do_test $tn.2.6 {
execsql {BEGIN}
execsql {INSERT INTO t1 VALUES(5, 6)}
execsql {BEGIN} db2
catchsql {INSERT INTO t1 VALUES(7, 8)} db2
} {1 {database is locked}}
do_test $tn.2.7 {
# Transaction is automatically rolled back in this case.
sqlite3_get_autocommit db2
} {1}
do_test $tn.2.8 {
execsql COMMIT
execsql { SELECT * FROM t1 } db2
} {1 2 5 6}
db2 close
#-------------------------------------------------------------------------
#
server_reset_db
do_execsql_test $tn.3.0 {
CREATE TABLE t1(a, b);
}
do_test $tn.3.1 {
lsort [glob -nocomplain test.db-journal/*-journal]
} {test.db-journal/0-journal}
do_test $tn.3.2 {
db close
lsort [glob -nocomplain test.db-journal/*-journal]
} {}
#-----------------------------------------------------------------------
# Test that write-locks are downgraded when a transaction is ended,
# even if the connection holds an open read statement.
#
do_test $tn.4.1 {
server_sqlite3 db test.db
server_sqlite3 db2 test.db
db eval {
CREATE TABLE t2(a);
INSERT INTO t2 VALUES('one');
INSERT INTO t2 VALUES('two');
INSERT INTO t2 VALUES('three');
CREATE TABLE t3(k INTEGER PRIMARY KEY, val);
}
set res [list]
db eval { SELECT a FROM t2 ORDER BY rowid } {
db eval { REPLACE INTO t3 VALUES(1, $a) }
lappend res [db2 one { SELECT val FROM t3 }]
}
set res
} {one two three}
catch { db close }
catch { db2 close }
}
finish_test
+57
View File
@@ -0,0 +1,57 @@
# 2017 April 25
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this script is testing the server mode of SQLite.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
set testprefix server3
source $testdir/server_common.tcl
return_if_no_server
foreach {tn vfs} {1 unix-excl 2 unix} {
server_set_vfs $vfs
server_reset_db
server_sqlite3 db2 test.db
do_test 1.1 {
db eval { CREATE TABLE t1(a, b) }
db2 eval { CREATE TABLE t2(a, b) }
} {}
do_test 1.2 {
db eval {
INSERT INTO t2 VALUES(1, 2);
BEGIN;
INSERT INTO t1 VALUES(1, 2);
}
} {}
do_test 1.3 {
list [catch { db2 eval { SELECT * FROM t1 } } msg] $msg
} {1 {database is locked}}
do_test 1.4 {
list [catch { db2 eval { SELECT * FROM t1 } } msg] $msg
} {1 {database is locked}}
do_test 1.4 {
db2 eval { SELECT * FROM t2 }
} {1 2}
}
finish_test
+46
View File
@@ -0,0 +1,46 @@
# 2017 July 25
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
ifcapable !server {
proc return_if_no_server {} {
finish_test
return -code return
}
return
} else {
proc return_if_no_server {} {}
}
proc server_sqlite3 {cmd file} {
sqlite3 $cmd $file -vfs $::server_vfs
}
proc server_reset_db {} {
catch {db close}
forcedelete test.db test.db-journal test.db-wal
file mkdir test.db-journal
server_sqlite3 db test.db
}
set ::server_vfs unix-excl
proc server_set_vfs {vfs} {
if {$vfs=="single"} {
set ::server_vfs unix-excl
} elseif {$vfs=="multi"} {
set ::server_vfs unix
} else {
set ::server_vfs $vfs
}
}
+87
View File
@@ -0,0 +1,87 @@
# 2017 April 27
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix servercrash
ifcapable !crashtest {
finish_test
return
}
do_not_use_codec
source $testdir/server_common.tcl
return_if_no_server
db close
server_set_vfs unix
server_reset_db
do_execsql_test 1.0 {
PRAGMA page_size = 4096;
PRAGMA auto_vacuum = OFF;
CREATE TABLE t1(a, b);
CREATE TABLE t2(c, d);
INSERT INTO t1 VALUES(1, 2), (3, 4);
INSERT INTO t2 VALUES(1, 2), (3, 4);
}
for {set i 0} {$i < 10} {incr i} {
do_test 1.$i.1 {
crashsql -delay 1 -file test.db { INSERT INTO t1 VALUES(5, 6) }
} {1 {child process exited abnormally}}
do_execsql_test 1.$i.2 {
SELECT * FROM t1
} {1 2 3 4}
}
for {set i 0} {$i < 10} {incr i} {
do_test 2.$i.1 {
crashsql -delay 1 -file test.db { INSERT INTO t1 VALUES(5, 6) }
} {1 {child process exited abnormally}}
do_test 2.$i.2 {
sqlite3 dbX test.db
execsql { SELECT * FROM t1 } dbX
} {1 2 3 4}
dbX close
}
db close
for {set i 0} {$i < 10} {incr i} {
do_test 3.$i.1 {
crashsql -delay 1 -file test.db { INSERT INTO t1 VALUES(5, 6) }
} {1 {child process exited abnormally}}
sqlite3 db test.db
do_execsql_test 3.$i.2 { SELECT * FROM t1 } {1 2 3 4}
db close
}
sqlite3 db test.db
db eval {SELECT * FROM t1}
for {set i 0} {$i < 10} {incr i} {
do_test 4.$i.1 {
crashsql -delay 1 -file test.db { INSERT INTO t1 VALUES(5, 6) }
} {1 {child process exited abnormally}}
db close
sqlite3 db test.db
do_execsql_test 4.$i.2 { SELECT * FROM t1 } {1 2 3 4}
}
finish_test
+56
View File
@@ -0,0 +1,56 @@
# 2017 July 09
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
# The focus of this script is testing the server mode of SQLite.
# Specifically, that "PRAGMA freelist_format" works.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix server5
do_execsql_test 1.0 {
PRAGMA freelist_format;
} {1}
do_execsql_test 1.1 {
PRAGMA freelist_format = 2;
} {2}
do_execsql_test 1.2 {
PRAGMA freelist_format;
} {2}
do_execsql_test 1.3 {
PRAGMA freelist_format = 1;
} {1}
do_execsql_test 1.4 {
PRAGMA freelist_format;
} {1}
do_execsql_test 1.5 {
CREATE TABLE t1(x);
PRAGMA freelist_format = 2;
} {2}
do_execsql_test 1.6 {
CREATE TABLE t2(y);
}
do_execsql_test 1.6 {
PRAGMA freelist_format = 1;
} {2}
finish_test
+177
View File
@@ -0,0 +1,177 @@
# 2017 April 25
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this script is testing the server mode of SQLite.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix serverlimit
source $testdir/server_common.tcl
source $testdir/lock_common.tcl
return_if_no_server
#-------------------------------------------------------------------------
# Test plan:
#
# 1.* The concurrent connections limit in multi-process mode. With all
# connections in the local process.
#
# 2.* The concurrent connections limit in multi-process mode. Using
# multiple processes.
#
# 3.* The concurrent transactions limit in single-process mode.
#
server_set_vfs multi
server_reset_db
set MLIMIT 16 ;# maximum number of allowed connections
do_test 1.0 {
server_sqlite3 db test.db
db eval {
CREATE TABLE t1(x);
INSERT INTO t1 VALUES('hello'), ('world');
}
db close
for {set i 0} {$i < $MLIMIT} {incr i} {
server_sqlite3 db.$i test.db
db.$i eval { SELECT * FROM t1 }
}
set {} {}
} {}
# Connection [db] cannot connect - all client slots are occupied.
#
do_test 1.1 {
server_sqlite3 db test.db
list [catch { db eval { SELECT * FROM t1 } } msg] $msg
} {1 {database is locked}}
# But, if one connection disconnects, [db] can then connect and
# query the db.
#
do_test 1.2 {
db.0 close
list [catch { db eval { SELECT * FROM t1 } } msg] $msg
} {0 {hello world}}
do_test 1.3 {
for {set i 0} {$i < $MLIMIT} {incr i} {
catch { db.$i close }
}
set {} {}
} {}
#-------------------------------------------------------------------------
# Connections in different processes.
do_multiclient_test tn {
code1 { db close }
code2 { db2 close }
code3 { db3 close }
set N1 [expr $MLIMIT / 2]
set N2 [expr $MLIMIT - $N1]
do_test 2.$tn.0 {
file mkdir test.db-journal
code1 {
sqlite3 db test.db
db eval {
CREATE TABLE t11(a, b);
INSERT INTO t11 VALUES(1, 2), (3, 4);
}
db close
for {set i 0} {$i < $N1} {incr i} {
sqlite3 db.$i test.db
db.$i eval { SELECT * FROM t11 }
}
}
code2 [string map [list %N2% $N2] {
for {set i 0} {$i < %N2%} {incr i} {
sqlite3 db2.$i test.db
db2.$i eval { SELECT * FROM t11 }
}
}]
code2 { db2.0 eval {SELECT * FROM t11} }
} {1 2 3 4}
do_test 2.$tn.1 {
code3 { sqlite3 db3 test.db }
csql3 { SELECT * FROM t11 }
} {1 {database is locked}}
do_test 2.$tn.2 {
code2 { db2.0 close }
csql3 { SELECT * FROM t11 }
} {0 {1 2 3 4}}
do_test 2.$tn.3 {
code1 { sqlite3 db test.db }
csql1 { SELECT * FROM t11 }
} {1 {database is locked}}
do_test 2.$tn.4 {
code2 { db2.1 close }
csql1 { SELECT * FROM t11 }
} {0 {1 2 3 4}}
do_test 2.$tn.X {
code1 { for {set i 0} {$i < 50} {incr i} { catch {db.$i close} } }
code2 { for {set i 0} {$i < 50} {incr i} { catch {db2.$i close} } }
} {}
}
server_set_vfs single
server_reset_db
set TLIMIT 16
do_test 3.0 {
execsql "CREATE TABLE t1 (o PRIMARY KEY) WITHOUT ROWID"
for {set i 0} {$i < $TLIMIT} {incr i} {
execsql "CREATE TABLE x$i (o PRIMARY KEY) WITHOUT ROWID"
}
set "" ""
} {}
do_test 3.1 {
for {set i 0} {$i < $TLIMIT} {incr i} {
sqlite3 db.$i test.db
db.$i eval "
BEGIN;
INSERT INTO x$i VALUES ('one');
"
}
} {}
do_catchsql_test 3.2 {
INSERT INTO t1 VALUES('two');
} {1 {database is locked}}
do_test 3.3 {
db.0 eval COMMIT
execsql { INSERT INTO t1 VALUES('two'); }
} {}
do_catchsql_test 3.4 { SELECT * FROM x1 } {1 {database is locked}}
do_catchsql_test 3.5 { SELECT * FROM x0 } {0 one}
do_test 3.6 {
for {set i 1} {$i < $TLIMIT} {incr i} {
db.$i eval COMMIT
}
} {}
finish_test
+65
View File
@@ -0,0 +1,65 @@
# 2017 July 09
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
# The focus of this script is testing the server mode of SQLite.
# Specifically, that "BEGIN READONLY" starts a read-only MVCC
# transaction.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
set testprefix server4
source $testdir/server_common.tcl
return_if_no_server
server_reset_db
server_sqlite3 db2 test.db
do_execsql_test 1.0 {
CREATE TABLE t1(x);
INSERT INTO t1 VALUES(1);
CREATE TABLE t2(x);
INSERT INTO t2 VALUES(1);
BEGIN;
INSERT INTO t1 VALUES(2);
INSERT INTO t2 VALUES(2);
}
do_execsql_test -db db2 1.1 {
BEGIN READONLY;
SELECT * FROM t1;
} {1}
do_execsql_test 1.2 {
COMMIT;
INSERT INTO t1 VALUES(3);
SELECT * FROM t1;
} {1 2 3}
do_execsql_test 1.2a {
INSERT INTO t2 VALUES(3);
} {}
do_execsql_test -db db2 1.3 {
SELECT * FROM t2;
} {1}
do_execsql_test -db db2 1.4 {
ROLLBACK;
SELECT * FROM t1;
} {1 2 3}
finish_test
-4
View File
@@ -73,7 +73,6 @@ catch { db close }
forcedelete test.db
sqlite3 db test.db -vfs tvfs
execsql { CREATE TABLE t1(x) }
execsql { PRAGMA temp_store = 1 }
# Each iteration of the following loop attempts to sort 10001 records
# each a bit over 100 bytes in size. In total a little more than 1MiB
@@ -89,9 +88,6 @@ foreach {tn pgsz cachesz bTemp} {
5 4096 -9000 0
6 1024 -9000 0
} {
if {$::TEMP_STORE>2} {
set bTemp 0
}
do_execsql_test 2.$tn.0 "
PRAGMA page_size = $pgsz;
VACUUM;
-50
View File
@@ -1,50 +0,0 @@
# 2018 April 14.
#
# 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 sorterref
do_execsql_test 1.0 {
CREATE TABLE t1(a, b, c);
INSERT INTO t1 VALUES(1, 2, 3);
INSERT INTO t1 VALUES(4, 5, 6);
ALTER TABLE t1 ADD COLUMN d DEFAULT 'string';
INSERT INTO t1 VALUES(7, 8, 9, 'text');
}
do_execsql_test 1.1 {
SELECT * FROM t1 ORDER BY b;
} {
1 2 3 string 4 5 6 string 7 8 9 text
}
do_execsql_test 2.0 {
DROP TABLE IF EXISTS t1;
CREATE TABLE t1(a, b);
CREATE TABLE t2(c, d, PRIMARY KEY(c)) WITHOUT ROWID;
INSERT INTO t1 VALUES(1, 2);
INSERT INTO t1 VALUES(2, 3);
INSERT INTO t1 VALUES(3, 4);
INSERT INTO t2 VALUES(1, 'one');
INSERT INTO t2 VALUES(3, 'three');
}
do_execsql_test 2.1 {
SELECT * FROM t1 LEFT JOIN t2 ON (a=c) ORDER BY b;
} {1 2 1 one 2 3 {} {} 3 4 3 three}
finish_test
+1 -205
View File
@@ -1645,207 +1645,6 @@ void testset_orm(void){
speedtest1_end_test();
}
/*
*/
void testset_trigger(void){
int jj, ii;
char zNum[2000]; /* A number name */
const int NROW = 500*g.szTest;
const int NROW2 = 100*g.szTest;
speedtest1_exec(
"BEGIN;"
"CREATE TABLE t1(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT);"
"CREATE TABLE t2(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT);"
"CREATE TABLE t3(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT);"
"CREATE VIEW v1 AS SELECT rowid, i, t FROM t1;"
"CREATE VIEW v2 AS SELECT rowid, i, t FROM t2;"
"CREATE VIEW v3 AS SELECT rowid, i, t FROM t3;"
);
for(jj=1; jj<=3; jj++){
speedtest1_prepare("INSERT INTO t%d VALUES(NULL,?1,?2)", jj);
for(ii=0; ii<NROW; ii++){
int x1 = speedtest1_random() % NROW;
speedtest1_numbername(x1, zNum, sizeof(zNum));
sqlite3_bind_int(g.pStmt, 1, x1);
sqlite3_bind_text(g.pStmt, 2, zNum, -1, SQLITE_STATIC);
speedtest1_run();
}
}
speedtest1_exec(
"CREATE INDEX i1 ON t1(t);"
"CREATE INDEX i2 ON t2(t);"
"CREATE INDEX i3 ON t3(t);"
"COMMIT;"
);
speedtest1_begin_test(100, "speed4p-join1");
speedtest1_prepare(
"SELECT * FROM t1, t2, t3 WHERE t1.oid = t2.oid AND t2.oid = t3.oid"
);
speedtest1_run();
speedtest1_end_test();
speedtest1_begin_test(110, "speed4p-join2");
speedtest1_prepare(
"SELECT * FROM t1, t2, t3 WHERE t1.t = t2.t AND t2.t = t3.t"
);
speedtest1_run();
speedtest1_end_test();
speedtest1_begin_test(120, "speed4p-view1");
for(jj=1; jj<=3; jj++){
speedtest1_prepare("SELECT * FROM v%d WHERE rowid = ?", jj);
for(ii=0; ii<NROW2; ii+=3){
sqlite3_bind_int(g.pStmt, 1, ii*3);
speedtest1_run();
}
}
speedtest1_end_test();
speedtest1_begin_test(130, "speed4p-table1");
for(jj=1; jj<=3; jj++){
speedtest1_prepare("SELECT * FROM t%d WHERE rowid = ?", jj);
for(ii=0; ii<NROW2; ii+=3){
sqlite3_bind_int(g.pStmt, 1, ii*3);
speedtest1_run();
}
}
speedtest1_end_test();
speedtest1_begin_test(140, "speed4p-table1");
for(jj=1; jj<=3; jj++){
speedtest1_prepare("SELECT * FROM t%d WHERE rowid = ?", jj);
for(ii=0; ii<NROW2; ii+=3){
sqlite3_bind_int(g.pStmt, 1, ii*3);
speedtest1_run();
}
}
speedtest1_end_test();
speedtest1_begin_test(150, "speed4p-subselect1");
speedtest1_prepare("SELECT "
"(SELECT t FROM t1 WHERE rowid = ?1),"
"(SELECT t FROM t2 WHERE rowid = ?1),"
"(SELECT t FROM t3 WHERE rowid = ?1)"
);
for(jj=0; jj<NROW2; jj++){
sqlite3_bind_int(g.pStmt, 1, jj*3);
speedtest1_run();
}
speedtest1_end_test();
speedtest1_begin_test(160, "speed4p-rowid-update");
speedtest1_exec("BEGIN");
speedtest1_prepare("UPDATE t1 SET i=i+1 WHERE rowid=?1");
for(jj=0; jj<NROW2; jj++){
sqlite3_bind_int(g.pStmt, 1, jj);
speedtest1_run();
}
speedtest1_exec("COMMIT");
speedtest1_end_test();
speedtest1_exec("CREATE TABLE t5(t TEXT PRIMARY KEY, i INTEGER);");
speedtest1_begin_test(170, "speed4p-insert-ignore");
speedtest1_exec("INSERT OR IGNORE INTO t5 SELECT t, i FROM t1");
speedtest1_end_test();
speedtest1_exec(
"CREATE TABLE log(op TEXT, r INTEGER, i INTEGER, t TEXT);"
"CREATE TABLE t4(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT);"
"CREATE TRIGGER t4_trigger1 AFTER INSERT ON t4 BEGIN"
" INSERT INTO log VALUES('INSERT INTO t4', new.rowid, new.i, new.t);"
"END;"
"CREATE TRIGGER t4_trigger2 AFTER UPDATE ON t4 BEGIN"
" INSERT INTO log VALUES('UPDATE OF t4', new.rowid, new.i, new.t);"
"END;"
"CREATE TRIGGER t4_trigger3 AFTER DELETE ON t4 BEGIN"
" INSERT INTO log VALUES('DELETE OF t4', old.rowid, old.i, old.t);"
"END;"
"BEGIN;"
);
speedtest1_begin_test(180, "speed4p-trigger1");
speedtest1_prepare("INSERT INTO t4 VALUES(NULL, ?1, ?2)");
for(jj=0; jj<NROW2; jj++){
speedtest1_numbername(jj, zNum, sizeof(zNum));
sqlite3_bind_int(g.pStmt, 1, jj);
sqlite3_bind_text(g.pStmt, 2, zNum, -1, SQLITE_STATIC);
speedtest1_run();
}
speedtest1_end_test();
/*
** Note: Of the queries, only half actually update a row. This property
** was copied over from speed4p.test, where it was probably introduced
** inadvertantly.
*/
speedtest1_begin_test(190, "speed4p-trigger2");
speedtest1_prepare("UPDATE t4 SET i = ?1, t = ?2 WHERE rowid = ?3");
for(jj=1; jj<=NROW2*2; jj+=2){
speedtest1_numbername(jj*2, zNum, sizeof(zNum));
sqlite3_bind_int(g.pStmt, 1, jj*2);
sqlite3_bind_text(g.pStmt, 2, zNum, -1, SQLITE_STATIC);
sqlite3_bind_int(g.pStmt, 3, jj);
speedtest1_run();
}
speedtest1_end_test();
/*
** Note: Same again.
*/
speedtest1_begin_test(200, "speed4p-trigger3");
speedtest1_prepare("DELETE FROM t4 WHERE rowid = ?1");
for(jj=1; jj<=NROW2*2; jj+=2){
sqlite3_bind_int(g.pStmt, 1, jj*2);
speedtest1_run();
}
speedtest1_end_test();
speedtest1_exec("COMMIT");
/*
** The following block contains the same tests as the above block that
** tests triggers, with one crucial difference: no triggers are defined.
** So the difference in speed between these tests and the preceding ones
** is the amount of time taken to compile and execute the trigger programs.
*/
speedtest1_exec(
"DROP TABLE t4;"
"DROP TABLE log;"
"VACUUM;"
"CREATE TABLE t4(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT);"
"BEGIN;"
);
speedtest1_begin_test(210, "speed4p-notrigger1");
speedtest1_prepare("INSERT INTO t4 VALUES(NULL, ?1, ?2)");
for(jj=0; jj<NROW2; jj++){
speedtest1_numbername(jj, zNum, sizeof(zNum));
sqlite3_bind_int(g.pStmt, 1, jj);
sqlite3_bind_text(g.pStmt, 2, zNum, -1, SQLITE_STATIC);
speedtest1_run();
}
speedtest1_end_test();
speedtest1_begin_test(210, "speed4p-notrigger2");
speedtest1_prepare("UPDATE t4 SET i = ?1, t = ?2 WHERE rowid = ?3");
for(jj=1; jj<=NROW2*2; jj+=2){
speedtest1_numbername(jj*2, zNum, sizeof(zNum));
sqlite3_bind_int(g.pStmt, 1, jj*2);
sqlite3_bind_text(g.pStmt, 2, zNum, -1, SQLITE_STATIC);
sqlite3_bind_int(g.pStmt, 3, jj);
speedtest1_run();
}
speedtest1_end_test();
speedtest1_begin_test(220, "speed4p-notrigger3");
speedtest1_prepare("DELETE FROM t4 WHERE rowid = ?1");
for(jj=1; jj<=NROW2*2; jj+=2){
sqlite3_bind_int(g.pStmt, 1, jj*2);
speedtest1_run();
}
speedtest1_end_test();
speedtest1_exec("COMMIT");
}
/*
** A testset used for debugging speedtest1 itself.
*/
@@ -2146,8 +1945,6 @@ int main(int argc, char **argv){
testset_cte();
}else if( strcmp(zTSet,"fp")==0 ){
testset_fp();
}else if( strcmp(zTSet,"trigger")==0 ){
testset_trigger();
}else if( strcmp(zTSet,"rtree")==0 ){
#ifdef SQLITE_ENABLE_RTREE
testset_rtree(6, 147);
@@ -2156,8 +1953,7 @@ int main(int argc, char **argv){
"the R-Tree tests\n");
#endif
}else{
fatal_error("unknown testset: \"%s\"\n"
"Choices: cte debug1 fp main orm rtree trigger\n",
fatal_error("unknown testset: \"%s\"\nChoices: main debug1 cte rtree fp\n",
zTSet);
}
speedtest1_final();
-49
View File
@@ -148,54 +148,5 @@ do_execsql_test 3.2 {
);
} {a 4 b 3 c 2 d 1}
#-------------------------------------------------------------------------
do_execsql_test 4.0 {
CREATE TABLE t6(x);
}
foreach {tn sql} {
1 {
SELECT 'abc' FROM (
SELECT x FROM t6 ORDER BY 1
UNION ALL
SELECT x FROM t6
)
}
2 {
SELECT 'abc' FROM (
SELECT x FROM t6
UNION ALL
SELECT x FROM t6 ORDER BY 1
UNION ALL
SELECT x FROM t6
)
}
3 {
SELECT 'abc' FROM (
SELECT x FROM t6 ORDER BY 1
UNION ALL
SELECT x FROM t6 ORDER BY 1
UNION ALL
SELECT x FROM t6
)
}
4 {
SELECT 'abc' FROM (
SELECT x FROM t6
UNION ALL
SELECT x FROM t6 ORDER BY 1
UNION ALL
SELECT x FROM t6 ORDER BY 1
UNION ALL
SELECT x FROM t6
)
}
} {
do_catchsql_test 4.$tn $sql [list {*}{
1 {ORDER BY clause should come after UNION ALL not before}
}]
}
finish_test
+1 -4
View File
@@ -16,9 +16,6 @@ set testprefix tempdb2
db close
sqlite3 db ""
set unlocked unlocked
if {$::TEMP_STORE>=2} { set unlocked unknown }
proc int2str {i} { string range [string repeat "$i." 450] 0 899 }
db func int2str int2str
@@ -58,7 +55,7 @@ do_execsql_test 1.1 {
COMMIT;
PRAGMA lock_status;
} [list main $unlocked temp closed]
} {main unlocked temp closed}
do_execsql_test 1.2 {
UPDATE t1 SET b=int2str(2);
+1 -1
View File
@@ -344,7 +344,7 @@ do_execsql_test 10.1 {
}
ifcapable mmap {
if {[permutation]!="journaltest" && $::TEMP_STORE<2} {
if {[permutation]!="journaltest"} {
# The journaltest permutation does not support mmap, so this part of
# the test is omitted.
do_execsql_test 10.2 { PRAGMA mmap_size = 512000 } 512000
+4
View File
@@ -586,6 +586,10 @@ proc reset_db {} {
forcedelete test.db
forcedelete test.db-journal
forcedelete test.db-wal
for {set i 0} {$i < 16} {incr i} {
forcedelete test.db-journal$i
}
sqlite3 db ./test.db
set ::DB [sqlite3_connection_pointer db]
if {[info exists ::SETUP_SQL]} {
-1
View File
@@ -57,7 +57,6 @@ foreach {tn defn} {
7 { BEFORE DELETE ON t1 BEGIN SELECT * FROM t2 ORDER BY ?; END; }
8 { BEFORE UPDATE ON t1 BEGIN UPDATE t2 SET c = ?; END; }
9 { BEFORE UPDATE ON t1 BEGIN UPDATE t2 SET c = 1 WHERE d = ?; END; }
10 { AFTER INSERT ON t1 BEGIN SELECT * FROM pragma_stats(?); END; }
} {
catchsql {drop trigger tr1}
do_catchsql_test 1.1.$tn "CREATE TRIGGER tr1 $defn" [list 1 $errmsg]
-13
View File
@@ -341,19 +341,6 @@ do_execsql_test 8.1 {
SELECT type, name, '|' FROM sqlite_master;
} {table t1 | index t1x |}
# 2018-04-05: OSSFuzz found that the following was accessing an
# unintialized memory cell. Which was not actually causing a
# malfunction, but does cause an assert() to fail.
#
do_execsql_test 9.0 {
CREATE TABLE t2(b, c, PRIMARY KEY(b,c)) WITHOUT ROWID;
CREATE UNIQUE INDEX t2b ON t2(b);
UPDATE t2 SET b=1 WHERE b='';
}
do_execsql_test 10.1 {
DELETE FROM t2 WHERE b=1
}
finish_test
+1 -5
View File
@@ -158,9 +158,7 @@ proc strip_slash {in} { regsub {/$} $in {} }
proc do_zip_tests {tn file} {
uplevel do_zipfile_blob_test $tn.1 $file
if {[info exists ::UNZIP]} {
uplevel do_unzip_test $tn.2 $file
}
uplevel do_unzip_test $tn.2 $file
}
forcedelete test.zip
@@ -647,8 +645,6 @@ do_test 8.0.3 {
} {}
execsql COMMIT
catch { forcedelete test_unzip }
catch { file mkdir test_unzip }
do_execsql_test 8.1.1 {
CREATE VIRTUAL TABLE nogood USING zipfile('test_unzip');
}
-14
View File
@@ -3254,7 +3254,6 @@ void ReportOutput(struct lemon *lemp)
struct state *stp;
struct config *cfp;
struct action *ap;
struct rule *rp;
FILE *fp;
fp = file_open(lemp,".out","wb");
@@ -3307,21 +3306,8 @@ void ReportOutput(struct lemon *lemp)
}
}
}
if( sp->prec>=0 ) fprintf(fp," (precedence=%d)", sp->prec);
fprintf(fp, "\n");
}
fprintf(fp, "----------------------------------------------------\n");
fprintf(fp, "Rules:\n");
for(rp=lemp->rule; rp; rp=rp->next){
fprintf(fp, "%4d: ", rp->iRule);
rule_print(fp, rp);
fprintf(fp,".");
if( rp->precsym ){
fprintf(fp," [%s precedence=%d]",
rp->precsym->name, rp->precsym->prec);
}
fprintf(fp,"\n");
}
fclose(fp);
return;
}
+1
View File
@@ -239,6 +239,7 @@ static Keyword aKeywordTable[] = {
{ "PRIMARY", "TK_PRIMARY", ALWAYS },
{ "QUERY", "TK_QUERY", EXPLAIN },
{ "RAISE", "TK_RAISE", TRIGGER },
{ "READONLY", "TK_READONLY", ALWAYS },
{ "RECURSIVE", "TK_RECURSIVE", CTE },
{ "REFERENCES", "TK_REFERENCES", FKEY },
{ "REGEXP", "TK_LIKE_KW", ALWAYS },
+1 -1
View File
@@ -83,7 +83,7 @@ Replace.exe:
sqlite3.def: Replace.exe $(LIBOBJ)
echo EXPORTS > sqlite3.def
dumpbin /all $(LIBOBJ) \\
| .\Replace.exe "^\s+/EXPORT:_?(sqlite3(?:session|changeset|changegroup|rebaser)?_[^@,]*)(?:@\d+|,DATA)?$$" $$1 true \\
| .\Replace.exe "^\s+/EXPORT:_?(sqlite3(?:session|changeset|changegroup)?_[^@,]*)(?:@\d+|,DATA)?$$" $$1 true \\
| sort >> sqlite3.def
}]]
+4
View File
@@ -382,6 +382,10 @@ set pragma_def {
NAME: optimize
FLAG: Result1 NeedSchema
NAME: freelist_format
FLAG: NeedSchema Result0 SchemaReq
IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) && defined(SQLITE_SERVER_EDITION)
}
# Open the output file
+2
View File
@@ -114,6 +114,7 @@ foreach hdr {
pcache.h
pragma.h
rtree.h
server.h
sqlite3session.h
sqlite3.h
sqlite3ext.h
@@ -328,6 +329,7 @@ foreach file {
rowset.c
pager.c
wal.c
server.c
btmutex.c
btree.c
+303
View File
@@ -0,0 +1,303 @@
#!/usr/bin/tclsh
#
# This script is used to run the performance test cases described in
# README-server-edition.html.
#
package require sqlite3
# Default values for command line switches:
set O(-database) ""
set O(-mode) "server"
set O(-rows) [expr 5000000]
set O(-tserver) "./tserver"
set O(-seconds) 20
set O(-writers) 2
set O(-readers) 1
set O(-verbose) 0
proc error_out {err} {
puts stderr $err
exit -1
}
proc usage {} {
puts stderr "Usage: $::argv0 ?OPTIONS?"
puts stderr ""
puts stderr "Where OPTIONS are:"
puts stderr " -database <database file> (default: test.$mode.db)"
puts stderr " -mode server|begin-concurrent (default: server)"
puts stderr " -rows <number of rows> (default: 5000000)"
puts stderr " -tserver <path to tserver executable> (default: ./tserver)"
puts stderr " -seconds <time to run for in seconds> (default: 20)"
puts stderr " -writers <number of writer clients> (default: 2)"
puts stderr " -readers <number of reader clients> (default: 1)"
puts stderr " -verbose 0|1 (default: 0)"
exit -1
}
for {set i 0} {$i < [llength $argv]} {incr i} {
set opt ""
set arg [lindex $argv $i]
set n [expr [string length $arg]-1]
foreach k [array names ::O] {
if {[string range $k 0 $n]==$arg} {
if {$opt==""} {
set opt $k
} else {
error_out "ambiguous option: $arg ($k or $opt)"
}
}
}
if {$opt==""} { usage }
if {$i==[llength $argv]-1} {
error_out "option requires an argument: $opt"
}
incr i
set val [lindex $argv $i]
switch -- $opt {
-mode {
if {$val != "server" && $val != "begin-concurrent"
&& $val != "wal" && $val != "persist"
} {
set xyz "\"server\", \"begin-concurrent\", \"wal\" or \"persist\""
error_out "Found \"$val\" - expected $xyz"
}
}
}
set O($opt) [lindex $argv $i]
}
if {$O(-database)==""} {
set O(-database) "test.$O(-mode).db"
}
set O(-rows) [expr $O(-rows)]
#--------------------------------------------------------------------------
# Create and populate the required test database, if it is not already
# present in the file-system.
#
proc create_test_database {} {
global O
if {[file exists $O(-database)]} {
sqlite3 db $O(-database)
# Check the schema looks Ok.
set s [db one {
SELECT group_concat(name||pk, '.') FROM pragma_table_info('t1');
}]
if {$s != "a1.b0.c0.d0"} {
error_out "Database $O(-database) exists but is not usable (schema)"
}
# Check that the row count matches.
set n [db one { SELECT count(*) FROM t1 }]
if {$n != $O(-rows)} {
error_out "Database $O(-database) exists but is not usable (row-count)"
}
db close
} else {
catch { file delete -force $O(-database)-journal }
catch { file delete -force $O(-database)-wal }
if {$O(-verbose)} {
puts "Building database $O(-database)..."
}
sqlite3 db $O(-database)
db eval {
CREATE TABLE t1(
a INTEGER PRIMARY KEY,
b BLOB(16),
c BLOB(16),
d BLOB(400)
);
CREATE INDEX i1 ON t1(b);
CREATE INDEX i2 ON t1(c);
WITH s(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<$O(-rows))
INSERT INTO t1
SELECT i-1, randomblob(16), randomblob(16), randomblob(400) FROM s;
}
if {$O(-mode)=="server"} {
db eval "PRAGMA freelist_format = 2"
}
db close
switch -- $O(-mode) {
server {
if {![file exists $O(-database)-journal]} {
file mkdir $O(-database)-journal
}
}
wal {
sqlite3 db $O(-database)
db eval {PRAGMA journal_mode = wal}
db close
}
begin-concurrent {
sqlite3 db $O(-database)
db eval {PRAGMA journal_mode = wal}
db close
}
}
}
}
#-------------------------------------------------------------------------
# Functions to start and stop the tserver process:
#
# tserver_start
# tserver_stop
#
set ::tserver {}
proc tserver_start {} {
global O
set ::tserver [open "|$O(-tserver) -vfs unix-excl $O(-database)"]
fconfigure $::tserver -blocking 0
fileevent $::tserver readable tserver_data
}
proc tserver_data {} {
global O
if {[eof $::tserver]} {
error_out "tserver has exited"
}
set line [gets $::tserver]
if {$line != "" && $O(-verbose)} {
puts "tserver: $line"
}
}
proc tserver_stop {} {
close $::tserver
set fd [socket localhost 9999]
puts $fd ".stop"
close $fd
}
#-------------------------------------------------------------------------
set ::nClient 0
set ::client_output [list]
proc client_data {name fd} {
global O
if {[eof $fd]} {
incr ::nClient -1
close $fd
return
}
set str [gets $fd]
if {[string trim $str]!=""} {
if {[string range $str 0 3]=="### "} {
lappend ::client_output [concat [list name $name] [lrange $str 1 end]]
}
if {$O(-verbose)} {
puts "$name: $str"
}
}
}
proc client_launch {name script} {
global O
set fd [socket localhost 9999]
fconfigure $fd -blocking 0
switch -- $O(-mode) {
persist {
puts $fd "PRAGMA journal_mode = PERSIST;"
}
}
puts $fd "PRAGMA synchronous = OFF;"
puts $fd ".repeat 1"
puts $fd ".run"
puts $fd $script
puts $fd ".seconds $O(-seconds)"
puts $fd ".run"
puts $fd ".quit"
flush $fd
incr ::nClient
fileevent $fd readable [list client_data $name $fd]
}
proc client_wait {} {
while {$::nClient>0} {vwait ::nClient}
}
proc script_writer {} {
global O
set commit "COMMIT;"
set begin "BEGIN;"
if {$O(-mode)=="begin-concurrent" || $O(-mode)=="wal"} {
set commit ".mutex_commit"
set begin "BEGIN CONCURRENT;"
}
if {$O(-mode)=="server"} { set beginarg "READONLY" }
set tail "randomblob(16), randomblob(16), randomblob(400));"
return [subst -nocommands {
$begin
REPLACE INTO t1 VALUES(abs(random() % $O(-rows)), $tail
REPLACE INTO t1 VALUES(abs(random() % $O(-rows)), $tail
REPLACE INTO t1 VALUES(abs(random() % $O(-rows)), $tail
REPLACE INTO t1 VALUES(abs(random() % $O(-rows)), $tail
REPLACE INTO t1 VALUES(abs(random() % $O(-rows)), $tail
$commit
}]
}
proc script_reader {} {
global O
set beginarg ""
if {$O(-mode)=="server"} { set beginarg "READONLY" }
return [subst -nocommands {
BEGIN $beginarg;
SELECT * FROM t1 WHERE a>abs((random()%$O(-rows))) LIMIT 10;
SELECT * FROM t1 WHERE a>abs((random()%$O(-rows))) LIMIT 10;
SELECT * FROM t1 WHERE a>abs((random()%$O(-rows))) LIMIT 10;
SELECT * FROM t1 WHERE a>abs((random()%$O(-rows))) LIMIT 10;
SELECT * FROM t1 WHERE a>abs((random()%$O(-rows))) LIMIT 10;
END;
}]
}
create_test_database
tserver_start
for {set i 0} {$i < $O(-writers)} {incr i} {
client_launch w.$i [script_writer]
}
for {set i 0} {$i < $O(-readers)} {incr i} {
client_launch r.$i [script_reader]
}
client_wait
set name(w) "Writers"
set name(r) "Readers"
foreach r $::client_output {
array set a $r
set type [string range $a(name) 0 0]
incr x($type.ok) $a(ok);
incr x($type.busy) $a(busy);
incr x($type.n) 1
set t($type) 1
}
foreach type [array names t] {
set nTPS [expr $x($type.ok) / $O(-seconds)]
set nC [expr $nTPS / $x($type.n)]
set nTotal [expr $x($type.ok) + $x($type.busy)]
set bp [format %.2f [expr $x($type.busy) * 100.0 / $nTotal]]
puts "$name($type): $nTPS transactions/second ($nC per client) ($bp% busy)"
}
tserver_stop
+582
View File
@@ -0,0 +1,582 @@
/*
** 2017 June 7
**
** 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.
**
*************************************************************************
**
** Simple multi-threaded server used for informal testing of concurrency
** between connections in different threads. Listens for tcp/ip connections
** on port 9999 of the 127.0.0.1 interface only. To build:
**
** gcc -g $(TOP)/tool/tserver.c sqlite3.o -lpthread -o tserver
**
** To run using "x.db" as the db file:
**
** ./tserver x.db
**
** To connect, open a client socket on port 9999 and start sending commands.
** Commands are either SQL - which must be terminated by a semi-colon, or
** dot-commands, which must be terminated by a newline. If an SQL statement
** is seen, it is prepared and added to an internal list.
**
** Dot-commands are:
**
** .list Display all SQL statements in the list.
** .quit Disconnect.
** .run Run all SQL statements in the list.
** .repeats N Configure the number of repeats per ".run".
** .seconds N Configure the number of seconds to ".run" for.
** .mutex_commit Add a "COMMIT" protected by a g.commit_mutex
** to the current SQL.
** .stop Stop the tserver process - exit(0).
**
** Example input:
**
** BEGIN;
** INSERT INTO t1 VALUES(randomblob(10), randomblob(100));
** INSERT INTO t1 VALUES(randomblob(10), randomblob(100));
** INSERT INTO t1 VALUES(randomblob(10), randomblob(100));
** COMMIT;
** .repeats 100000
** .run
**
*/
#define TSERVER_PORTNUMBER 9999
#include <arpa/inet.h>
#include <assert.h>
#include <pthread.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include "sqlite3.h"
#define TSERVER_DEFAULT_CHECKPOINT_THRESHOLD 3900
/* Global variables */
struct TserverGlobal {
char *zDatabaseName; /* Database used by this server */
char *zVfs;
sqlite3_mutex *commit_mutex;
sqlite3 *db; /* Global db handle */
/* The following use native pthreads instead of a portable interface. This
** is because a condition variable, as well as a mutex, is required. */
pthread_mutex_t ckpt_mutex;
pthread_cond_t ckpt_cond;
int nThreshold; /* Checkpoint when wal is this large */
int bCkptRequired; /* True if wal checkpoint is required */
int nRun; /* Number of clients in ".run" */
int nWait; /* Number of clients waiting on ckpt_cond */
};
static struct TserverGlobal g = {0};
typedef struct ClientSql ClientSql;
struct ClientSql {
sqlite3_stmt *pStmt;
int bMutex;
};
typedef struct ClientCtx ClientCtx;
struct ClientCtx {
sqlite3 *db; /* Database handle for this client */
int fd; /* Client fd */
int nRepeat; /* Number of times to repeat SQL */
int nSecond; /* Number of seconds to run for */
ClientSql *aPrepare; /* Array of prepared statements */
int nPrepare; /* Valid size of apPrepare[] */
int nAlloc; /* Allocated size of apPrepare[] */
};
static int is_eol(int i){
return (i=='\n' || i=='\r');
}
static int is_whitespace(int i){
return (i==' ' || i=='\t' || is_eol(i));
}
/*
** Implementation of SQL scalar function usleep().
*/
static void usleepFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
int nUs;
sqlite3_vfs *pVfs = (sqlite3_vfs*)sqlite3_user_data(context);
assert( argc==1 );
nUs = sqlite3_value_int64(argv[0]);
pVfs->xSleep(pVfs, nUs);
}
static void trim_string(const char **pzStr, int *pnStr){
const char *zStr = *pzStr;
int nStr = *pnStr;
while( nStr>0 && is_whitespace(zStr[0]) ){
zStr++;
nStr--;
}
while( nStr>0 && is_whitespace(zStr[nStr-1]) ){
nStr--;
}
*pzStr = zStr;
*pnStr = nStr;
}
static int send_message(ClientCtx *p, const char *zFmt, ...){
char *zMsg;
va_list ap; /* Vararg list */
va_start(ap, zFmt);
int res = -1;
zMsg = sqlite3_vmprintf(zFmt, ap);
if( zMsg ){
res = write(p->fd, zMsg, strlen(zMsg));
}
sqlite3_free(zMsg);
va_end(ap);
return (res<0);
}
static int handle_some_sql(ClientCtx *p, const char *zSql, int nSql){
const char *zTail = zSql;
int nTail = nSql;
int rc = SQLITE_OK;
while( rc==SQLITE_OK ){
if( p->nPrepare>=p->nAlloc ){
int nByte = (p->nPrepare+32) * sizeof(ClientSql);
ClientSql *aNew = sqlite3_realloc(p->aPrepare, nByte);
if( aNew ){
memset(&aNew[p->nPrepare], 0, sizeof(ClientSql)*32);
p->aPrepare = aNew;
p->nAlloc = p->nPrepare+32;
}else{
rc = SQLITE_NOMEM;
break;
}
}
rc = sqlite3_prepare_v2(
p->db, zTail, nTail, &p->aPrepare[p->nPrepare].pStmt, &zTail
);
if( rc!=SQLITE_OK ){
send_message(p, "error - %s\n", sqlite3_errmsg(p->db));
rc = 1;
break;
}
if( p->aPrepare[p->nPrepare].pStmt==0 ){
break;
}
p->nPrepare++;
nTail = nSql - (zTail-zSql);
rc = send_message(p, "ok (%d SQL statements)\n", p->nPrepare);
}
return rc;
}
static sqlite3_int64 get_timer(void){
struct timeval t;
gettimeofday(&t, 0);
return ((sqlite3_int64)t.tv_usec / 1000) + ((sqlite3_int64)t.tv_sec * 1000);
}
static void clear_sql(ClientCtx *p){
int j;
for(j=0; j<p->nPrepare; j++){
sqlite3_finalize(p->aPrepare[j].pStmt);
}
p->nPrepare = 0;
}
/*
** The sqlite3_wal_hook() callback used by all client database connections.
*/
static int clientWalHook(void *pArg, sqlite3 *db, const char *zDb, int nFrame){
if( nFrame>=g.nThreshold ){
g.bCkptRequired = 1;
}
return SQLITE_OK;
}
static int handle_run_command(ClientCtx *p){
int i, j;
int nBusy = 0;
sqlite3_int64 t0 = get_timer();
sqlite3_int64 t1 = t0;
int nT1 = 0;
int nTBusy1 = 0;
int rc = SQLITE_OK;
pthread_mutex_lock(&g.ckpt_mutex);
g.nRun++;
pthread_mutex_unlock(&g.ckpt_mutex);
for(j=0; (p->nRepeat<=0 || j<p->nRepeat) && rc==SQLITE_OK; j++){
sqlite3_int64 t2;
for(i=0; i<p->nPrepare && rc==SQLITE_OK; i++){
sqlite3_stmt *pStmt = p->aPrepare[i].pStmt;
/* If the bMutex flag is set, grab g.commit_mutex before executing
** the SQL statement (which is always "COMMIT" in this case). */
if( p->aPrepare[i].bMutex ){
sqlite3_mutex_enter(g.commit_mutex);
}
/* Execute the statement */
while( sqlite3_step(pStmt)==SQLITE_ROW );
rc = sqlite3_reset(pStmt);
/* Relinquish the g.commit_mutex mutex if required. */
if( p->aPrepare[i].bMutex ){
sqlite3_mutex_leave(g.commit_mutex);
}
if( (rc & 0xFF)==SQLITE_BUSY ){
if( sqlite3_get_autocommit(p->db)==0 ){
sqlite3_exec(p->db, "ROLLBACK", 0, 0, 0);
}
nBusy++;
rc = SQLITE_OK;
break;
}
else if( rc!=SQLITE_OK ){
send_message(p, "error - %s\n", sqlite3_errmsg(p->db));
}
}
t2 = get_timer();
if( t2>=(t1+1000) ){
int nMs = (t2 - t1);
int nDone = (j+1 - nBusy - nT1);
rc = send_message(
p, "(%d done @ %d per second, %d busy)\n",
nDone, (1000*nDone + nMs/2) / nMs, nBusy - nTBusy1
);
t1 = t2;
nT1 = j+1 - nBusy;
nTBusy1 = nBusy;
if( p->nSecond>0 && (p->nSecond*1000)<=t1-t0 ) break;
}
/* Checkpoint handling. */
pthread_mutex_lock(&g.ckpt_mutex);
if( rc==SQLITE_OK && g.bCkptRequired ){
if( g.nWait==g.nRun-1 ){
/* All other clients are already waiting on the condition variable.
** Run the checkpoint, signal the condition and move on. */
rc = sqlite3_wal_checkpoint(p->db, "main");
g.bCkptRequired = 0;
pthread_cond_broadcast(&g.ckpt_cond);
}else{
assert( g.nWait<g.nRun-1 );
g.nWait++;
pthread_cond_wait(&g.ckpt_cond, &g.ckpt_mutex);
g.nWait--;
}
}
pthread_mutex_unlock(&g.ckpt_mutex);
}
if( rc==SQLITE_OK ){
int nMs = (int)(get_timer() - t0);
send_message(p, "ok (%d/%d SQLITE_BUSY)\n", nBusy, j);
if( p->nRepeat<=0 ){
send_message(p, "### ok %d busy %d ms %d\n", j-nBusy, nBusy, nMs);
}
}
clear_sql(p);
pthread_mutex_lock(&g.ckpt_mutex);
g.nRun--;
pthread_mutex_unlock(&g.ckpt_mutex);
return rc;
}
static int handle_dot_command(ClientCtx *p, const char *zCmd, int nCmd){
int n;
int rc = 0;
const char *z = &zCmd[1];
const char *zArg;
int nArg;
assert( zCmd[0]=='.' );
for(n=0; n<(nCmd-1); n++){
if( is_whitespace(z[n]) ) break;
}
zArg = &z[n];
nArg = nCmd-n;
trim_string(&zArg, &nArg);
if( n>=1 && n<=4 && 0==strncmp(z, "list", n) ){
int i;
for(i=0; rc==0 && i<p->nPrepare; i++){
const char *zSql = sqlite3_sql(p->aPrepare[i].pStmt);
int nSql = strlen(zSql);
trim_string(&zSql, &nSql);
rc = send_message(p, "%d: %.*s\n", i, nSql, zSql);
}
}
else if( n>=1 && n<=4 && 0==strncmp(z, "quit", n) ){
rc = 1;
}
else if( n>=2 && n<=7 && 0==strncmp(z, "repeats", n) ){
if( nArg ){
p->nRepeat = strtol(zArg, 0, 0);
if( p->nRepeat>0 ) p->nSecond = 0;
}
rc = send_message(p, "ok (repeat=%d)\n", p->nRepeat);
}
else if( n>=2 && n<=3 && 0==strncmp(z, "run", n) ){
rc = handle_run_command(p);
}
else if( n>=2 && n<=7 && 0==strncmp(z, "seconds", n) ){
if( nArg ){
p->nSecond = strtol(zArg, 0, 0);
if( p->nSecond>0 ) p->nRepeat = 0;
}
rc = send_message(p, "ok (repeat=%d)\n", p->nRepeat);
}
else if( n>=1 && n<=12 && 0==strncmp(z, "mutex_commit", n) ){
rc = handle_some_sql(p, "COMMIT;", 7);
if( rc==SQLITE_OK ){
p->aPrepare[p->nPrepare-1].bMutex = 1;
}
}
else if( n>=2 && n<=4 && 0==strncmp(z, "stop", n) ){
sqlite3_close(g.db);
exit(0);
}
else{
send_message(p,
"unrecognized dot command: %.*s\n"
"should be \"list\", \"run\", \"repeats\", \"mutex_commit\" "
"or \"seconds\"\n", n, z
);
rc = 1;
}
return rc;
}
static void *handle_client(void *pArg){
char zCmd[32*1024]; /* Read buffer */
int nCmd = 0; /* Valid bytes in zCmd[] */
int res; /* Result of read() call */
int rc = SQLITE_OK;
ClientCtx ctx;
memset(&ctx, 0, sizeof(ClientCtx));
ctx.fd = (int)(intptr_t)pArg;
ctx.nRepeat = 1;
rc = sqlite3_open_v2(g.zDatabaseName, &ctx.db,
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, g.zVfs
);
if( rc!=SQLITE_OK ){
fprintf(stderr, "sqlite3_open(): %s\n", sqlite3_errmsg(ctx.db));
return 0;
}
sqlite3_create_function(
ctx.db, "usleep", 1, SQLITE_UTF8, (void*)sqlite3_vfs_find(0),
usleepFunc, 0, 0
);
/* Register the wal-hook with the new client connection */
sqlite3_wal_hook(ctx.db, clientWalHook, (void*)&ctx);
while( rc==SQLITE_OK ){
int i;
int iStart;
int nConsume;
res = read(ctx.fd, &zCmd[nCmd], sizeof(zCmd)-nCmd-1);
if( res<=0 ) break;
nCmd += res;
if( nCmd>=sizeof(zCmd)-1 ){
fprintf(stderr, "oversized (>32KiB) message\n");
res = 0;
break;
}
zCmd[nCmd] = '\0';
do {
nConsume = 0;
/* Gobble up any whitespace */
iStart = 0;
while( is_whitespace(zCmd[iStart]) ) iStart++;
if( zCmd[iStart]=='.' ){
/* This is a dot-command. Search for end-of-line. */
for(i=iStart; i<nCmd; i++){
if( is_eol(zCmd[i]) ){
rc = handle_dot_command(&ctx, &zCmd[iStart], i-iStart);
nConsume = i+1;
break;
}
}
}else{
int iSemi;
char c = 0;
for(iSemi=iStart; iSemi<nCmd; iSemi++){
if( zCmd[iSemi]==';' ){
c = zCmd[iSemi+1];
zCmd[iSemi+1] = '\0';
break;
}
}
if( iSemi<nCmd ){
if( sqlite3_complete(zCmd) ){
rc = handle_some_sql(&ctx, zCmd, iSemi+1);
nConsume = iSemi+1;
}
if( c ){
zCmd[iSemi+1] = c;
}
}
}
if( nConsume>0 ){
nCmd = nCmd-nConsume;
if( nCmd>0 ){
memmove(zCmd, &zCmd[nConsume], nCmd);
}
}
}while( rc==SQLITE_OK && nConsume>0 );
}
fprintf(stdout, "Client %d disconnects\n", ctx.fd);
fflush(stdout);
close(ctx.fd);
clear_sql(&ctx);
sqlite3_free(ctx.aPrepare);
sqlite3_close(ctx.db);
return 0;
}
static void usage(const char *zExec){
fprintf(stderr, "Usage: %s ?-vfs VFS? DATABASE\n", zExec);
exit(1);
}
int main(int argc, char *argv[]) {
int sfd;
int rc;
int yes = 1;
struct sockaddr_in server;
/* Ignore SIGPIPE. Otherwise the server exits if a client disconnects
** abruptly. */
signal(SIGPIPE, SIG_IGN);
if( argc!=2 && argc!=4 ){
usage(argv[0]);
}
if( argc==4 ){
int n = strlen(argv[1]);
if( n<2 || n>4 || memcmp("-vfs", argv[1], 4) ) usage(argv[0]);
g.zVfs = argv[2];
}
g.zDatabaseName = argv[argc-1];
g.commit_mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
g.nThreshold = TSERVER_DEFAULT_CHECKPOINT_THRESHOLD;
pthread_mutex_init(&g.ckpt_mutex, 0);
pthread_cond_init(&g.ckpt_cond, 0);
rc = sqlite3_open_v2(g.zDatabaseName, &g.db,
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, g.zVfs
);
if( rc!=SQLITE_OK ){
fprintf(stderr, "sqlite3_open(): %s\n", sqlite3_errmsg(g.db));
return 1;
}
rc = sqlite3_exec(g.db, "SELECT * FROM sqlite_master", 0, 0, 0);
if( rc!=SQLITE_OK ){
fprintf(stderr, "sqlite3_exec(): %s\n", sqlite3_errmsg(g.db));
return 1;
}
sfd = socket(AF_INET, SOCK_STREAM, 0);
if( sfd<0 ){
fprintf(stderr, "socket() failed\n");
return 1;
}
rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
if( rc<0 ){
perror("setsockopt");
return 1;
}
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr("127.0.0.1");
server.sin_port = htons(TSERVER_PORTNUMBER);
rc = bind(sfd, (struct sockaddr *)&server, sizeof(struct sockaddr));
if( rc<0 ){
fprintf(stderr, "bind() failed\n");
return 1;
}
rc = listen(sfd, 8);
if( rc<0 ){
fprintf(stderr, "listen() failed\n");
return 1;
}
while( 1 ){
pthread_t tid;
int cfd = accept(sfd, NULL, NULL);
if( cfd<0 ){
perror("accept()");
return 1;
}
fprintf(stdout, "Client %d connects\n", cfd);
fflush(stdout);
rc = pthread_create(&tid, NULL, handle_client, (void*)(intptr_t)cfd);
if( rc!=0 ){
perror("pthread_create()");
return 1;
}
pthread_detach(tid);
}
return 0;
}