Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f0c6bc3a5 | |||
| bddcb53614 | |||
| fd971c8b83 | |||
| 332faddd99 | |||
| 7c4805fa5c | |||
| 852841cc14 | |||
| 528e4e4bbf | |||
| 335ce3575f | |||
| 4c454ff90b | |||
| 7ba5f0e768 | |||
| e278a9bacd | |||
| b585dbf8db | |||
| cfcd9d34b9 | |||
| 44bbccc05a | |||
| 0a1c5dc414 | |||
| 0ee56139e9 | |||
| af12f8e2c9 | |||
| aa90aad86b | |||
| b4b69facc0 | |||
| a12baadd15 | |||
| 11584e6779 | |||
| 6af090cc05 | |||
| e1e878687d | |||
| e83dba9e4d | |||
| 26a8ddbcdb | |||
| 087794b136 | |||
| ad9b696471 | |||
| f42e5d8917 | |||
| 411c20ad62 | |||
| 6b0cfe5343 | |||
| 6f3283a427 | |||
| bab38cbc9a | |||
| 538ed8e376 | |||
| b4c4601adf | |||
| 60dd40a62c | |||
| 2ac63b9812 | |||
| bf431367f3 | |||
| 730859568d | |||
| c9bcad12ea | |||
| e376cac1d1 | |||
| b7bcf5c16c | |||
| 31349393ba | |||
| 8df9974169 | |||
| f427296520 | |||
| 920a129de3 | |||
| 34d02325be | |||
| aa189cf00f | |||
| 2dab053e22 | |||
| 0561247d07 | |||
| 1381bbc262 |
+1
-1
@@ -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>>
|
||||
|
||||
|
||||
@@ -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 "<database>-journal" in the file system alongside
|
||||
the database file "<database>" 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;
|
||||
<create directory>
|
||||
END;
|
||||
</pre>
|
||||
|
||||
<p> As well as signalling new clients that they should enter server-mode,
|
||||
creating a directory named "<database>-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 <database>-journal, server-mode
|
||||
clients use <database>-journal/<client-id>-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 <client-id> 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 <client-id> 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 <client-id>. 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
|
||||
<database>-journal and that no other process holds a RESERVED lock).
|
||||
If the <database>-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>abs((random()%5000000)) LIMIT 10;
|
||||
SELECT * FROM t1 WHERE a>abs((random()%5000000)) LIMIT 10;
|
||||
SELECT * FROM t1 WHERE a>abs((random()%5000000)) LIMIT 10;
|
||||
SELECT * FROM t1 WHERE a>abs((random()%5000000)) LIMIT 10;
|
||||
SELECT * FROM t1 WHERE a>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>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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\\"
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
}]
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
@@ -1 +1 @@
|
||||
413015c029d850d4ce7e66be1f59b57f291254240a958856378a62f5ac4a5092
|
||||
754ad35cd26da361e2ed736b0e400497714a0db9b7fd05fd24e7803b6f478263
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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*/
|
||||
|
||||
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
|
||||
#############################################################################
|
||||
|
||||
|
||||
@@ -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" {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]} {
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
@@ -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');
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
}]]
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user