mirror of
https://github.com/status-im/sqlcipher.git
synced 2026-08-31 06:21:07 +00:00
update to upstream sqlite 3.6.14
This commit is contained in:
+35
-14
@@ -188,19 +188,24 @@ NAWK = awk
|
||||
|
||||
# This is how we compile
|
||||
#
|
||||
TCCX = $(TCC) $(OPTS) -I. -I$(TOP)/src -I$(TOP) -I$(TOP)/ext/rtree
|
||||
TCCX_SHARED = $(TCC_SHARED) $(OPTS) -I. -I$(TOP)/src -I$(TOP) -I$(TOP)/ext/rtree
|
||||
TCCX = $(TCC) $(OPTS) -I. -I$(TOP)/src -I$(TOP)
|
||||
TCCX_SHARED = $(TCC_SHARED) $(OPTS) -I. -I$(TOP)/src -I$(TOP) \
|
||||
-I$(TOP)/ext/rtree -I$(TOP)/ext/icu -I$(TOP)/ext/fts3 \
|
||||
-I$(TOP)/ext/async
|
||||
|
||||
# Object files for the SQLite library.
|
||||
#
|
||||
LIBOBJ+= alter.o analyze.o attach.o auth.o \
|
||||
backup.o bitvec.o btmutex.o btree.o build.o \
|
||||
callback.o complete.o date.o delete.o \
|
||||
expr.o fault.o func.o global.o hash.o \
|
||||
callback.o complete.o date.o delete.o expr.o fault.o \
|
||||
fts3.o fts3_expr.o fts3_hash.o fts3_icu.o fts3_porter.o \
|
||||
fts3_tokenizer.o fts3_tokenizer1.o \
|
||||
func.o global.o hash.o \
|
||||
icu.o insert.o journal.o legacy.o loadext.o \
|
||||
main.o malloc.o mem0.o mem1.o mem2.o mem3.o mem5.o memjournal.o \
|
||||
mutex.o mutex_os2.o mutex_unix.o mutex_w32.o mutex_noop.o \
|
||||
opcodes.o os.o os_os2.o os_unix.o os_win.o \
|
||||
main.o malloc.o mem0.o mem1.o mem2.o mem3.o mem5.o \
|
||||
memjournal.o \
|
||||
mutex.o mutex_noop.o mutex_os2.o mutex_unix.o mutex_w32.o \
|
||||
notify.o opcodes.o os.o os_os2.o os_unix.o os_win.o \
|
||||
pager.o parse.o pcache.o pcache1.o pragma.o prepare.o printf.o \
|
||||
random.o resolve.o rowset.o rtree.o select.o status.o \
|
||||
table.o tokenize.o trigger.o \
|
||||
@@ -209,6 +214,7 @@ LIBOBJ+= alter.o analyze.o attach.o auth.o \
|
||||
walker.o where.o utf.o vtab.o
|
||||
|
||||
|
||||
|
||||
# All of the source code files.
|
||||
#
|
||||
SRC = \
|
||||
@@ -248,10 +254,11 @@ SRC = \
|
||||
$(TOP)/src/memjournal.c \
|
||||
$(TOP)/src/mutex.c \
|
||||
$(TOP)/src/mutex.h \
|
||||
$(TOP)/src/mutex_noop.c \
|
||||
$(TOP)/src/mutex_os2.c \
|
||||
$(TOP)/src/mutex_unix.c \
|
||||
$(TOP)/src/mutex_w32.c \
|
||||
$(TOP)/src/mutex_noop.c \
|
||||
$(TOP)/src/notify.c \
|
||||
$(TOP)/src/os.c \
|
||||
$(TOP)/src/os.h \
|
||||
$(TOP)/src/os_common.h \
|
||||
@@ -262,8 +269,8 @@ SRC = \
|
||||
$(TOP)/src/pager.h \
|
||||
$(TOP)/src/parse.y \
|
||||
$(TOP)/src/pcache.c \
|
||||
$(TOP)/src/pcache1.c \
|
||||
$(TOP)/src/pcache.h \
|
||||
$(TOP)/src/pcache1.c \
|
||||
$(TOP)/src/pragma.c \
|
||||
$(TOP)/src/prepare.c \
|
||||
$(TOP)/src/printf.c \
|
||||
@@ -329,8 +336,8 @@ SRC += \
|
||||
$(TOP)/ext/fts3/fts3_tokenizer.c \
|
||||
$(TOP)/ext/fts3/fts3_tokenizer1.c
|
||||
SRC += \
|
||||
$(TOP)/ext/icu/icu.c \
|
||||
$(TOP)/ext/icu/sqliteicu.h
|
||||
$(TOP)/ext/icu/sqliteicu.h \
|
||||
$(TOP)/ext/icu/icu.c
|
||||
SRC += \
|
||||
$(TOP)/ext/rtree/rtree.h \
|
||||
$(TOP)/ext/rtree/rtree.c
|
||||
@@ -361,33 +368,41 @@ TESTSRC = \
|
||||
$(TOP)/src/test9.c \
|
||||
$(TOP)/src/test_autoext.c \
|
||||
$(TOP)/src/test_async.c \
|
||||
$(TOP)/src/test_backup.c \
|
||||
$(TOP)/src/test_btree.c \
|
||||
$(TOP)/src/test_config.c \
|
||||
$(TOP)/src/test_devsym.c \
|
||||
$(TOP)/src/test_func.c \
|
||||
$(TOP)/src/test_hexio.c \
|
||||
$(TOP)/src/test_journal.c \
|
||||
$(TOP)/src/test_malloc.c \
|
||||
$(TOP)/src/test_md5.c \
|
||||
$(TOP)/src/test_mutex.c \
|
||||
$(TOP)/src/test_onefile.c \
|
||||
$(TOP)/src/test_osinst.c \
|
||||
$(TOP)/src/test_pcache.c \
|
||||
$(TOP)/src/test_schema.c \
|
||||
$(TOP)/src/test_server.c \
|
||||
$(TOP)/src/test_tclvar.c \
|
||||
$(TOP)/src/test_thread.c \
|
||||
$(TOP)/src/test_wsd.c \
|
||||
|
||||
#TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c
|
||||
#TESTSRC += $(TOP)/ext/fts3/fts3_tokenizer.c
|
||||
|
||||
TESTSRC2 = \
|
||||
$(TOP)/src/attach.c $(TOP)/src/btree.c $(TOP)/src/build.c $(TOP)/src/date.c \
|
||||
$(TOP)/src/attach.c $(TOP)/src/backup.c $(TOP)/src/btree.c \
|
||||
$(TOP)/src/build.c $(TOP)/src/date.c \
|
||||
$(TOP)/src/expr.c $(TOP)/src/func.c $(TOP)/src/insert.c $(TOP)/src/os.c \
|
||||
$(TOP)/src/os_os2.c $(TOP)/src/os_unix.c $(TOP)/src/os_win.c \
|
||||
$(TOP)/src/pager.c $(TOP)/src/pragma.c $(TOP)/src/prepare.c \
|
||||
$(TOP)/src/printf.c $(TOP)/src/random.c $(TOP)/src/pcache.c \
|
||||
$(TOP)/src/select.c $(TOP)/src/tokenize.c \
|
||||
$(TOP)/src/pcache1.c $(TOP)/src/select.c $(TOP)/src/tokenize.c \
|
||||
$(TOP)/src/utf.c $(TOP)/src/util.c $(TOP)/src/vdbeapi.c $(TOP)/src/vdbeaux.c \
|
||||
$(TOP)/src/vdbe.c $(TOP)/src/vdbemem.c $(TOP)/src/where.c parse.c
|
||||
$(TOP)/src/vdbe.c $(TOP)/src/vdbemem.c $(TOP)/src/where.c parse.c \
|
||||
$(TOP)/ext/fts3/fts3.c $(TOP)/ext/fts3/fts3_expr.c \
|
||||
$(TOP)/ext/fts3/fts3_tokenizer.c \
|
||||
$(TOP)/ext/async/sqlite3async.c
|
||||
|
||||
# Header files used by all library source files.
|
||||
#
|
||||
@@ -423,10 +438,13 @@ EXTHDR += \
|
||||
$(TOP)/ext/fts2/fts2_tokenizer.h
|
||||
EXTHDR += \
|
||||
$(TOP)/ext/fts3/fts3.h \
|
||||
$(TOP)/ext/fts3/fts3_expr.h \
|
||||
$(TOP)/ext/fts3/fts3_hash.h \
|
||||
$(TOP)/ext/fts3/fts3_tokenizer.h
|
||||
EXTHDR += \
|
||||
$(TOP)/ext/rtree/rtree.h
|
||||
EXTHDR += \
|
||||
$(TOP)/ext/icu/sqliteicu.h
|
||||
|
||||
# This is the default Makefile target. The objects listed here
|
||||
# are what get build when you type just "make" with no arguments.
|
||||
@@ -551,6 +569,9 @@ fts2_tokenizer1.o: $(TOP)/ext/fts2/fts2_tokenizer1.c $(HDR) $(EXTHDR)
|
||||
fts3.o: $(TOP)/ext/fts3/fts3.c $(HDR) $(EXTHDR)
|
||||
$(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3.c
|
||||
|
||||
fts3_expr.o: $(TOP)/ext/fts3/fts3_expr.c $(HDR) $(EXTHDR)
|
||||
$(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_expr.c
|
||||
|
||||
fts3_hash.o: $(TOP)/ext/fts3/fts3_hash.c $(HDR) $(EXTHDR)
|
||||
$(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_hash.c
|
||||
|
||||
|
||||
+2
-2
@@ -90,7 +90,7 @@
|
||||
AC_INIT(sqlite, m4_esyscmd([cat VERSION | tr -d '\n']))
|
||||
|
||||
dnl Make sure the local VERSION file matches this configure script
|
||||
sqlite_version_sanity_check=`cat VERSION | tr -d '\n'`
|
||||
sqlite_version_sanity_check=`cat $srcdir/VERSION | tr -d '\n'`
|
||||
if test "$PACKAGE_VERSION" != "$sqlite_version_sanity_check" ; then
|
||||
AC_MSG_ERROR([configure script is out of date:
|
||||
configure \$PACKAGE_VERSION = $PACKAGE_VERSION
|
||||
@@ -101,7 +101,7 @@ fi
|
||||
dnl Put the RCS revision string after AC_INIT so that it will also
|
||||
dnl show in in configure.
|
||||
# The following RCS revision string applies to configure.in
|
||||
# $Revision: 1.55 $
|
||||
# $Revision: 1.56 $
|
||||
|
||||
#########
|
||||
# Programs needed
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
|
||||
Normally, when SQLite writes to a database file, it waits until the write
|
||||
operation is finished before returning control to the calling application.
|
||||
Since writing to the file-system is usually very slow compared with CPU
|
||||
bound operations, this can be a performance bottleneck. This directory
|
||||
contains an extension that causes SQLite to perform all write requests
|
||||
using a separate thread running in the background. Although this does not
|
||||
reduce the overall system resources (CPU, disk bandwidth etc.) at all, it
|
||||
allows SQLite to return control to the caller quickly even when writing to
|
||||
the database, eliminating the bottleneck.
|
||||
|
||||
1. Functionality
|
||||
|
||||
1.1 How it Works
|
||||
1.2 Limitations
|
||||
1.3 Locking and Concurrency
|
||||
|
||||
2. Compilation and Usage
|
||||
|
||||
3. Porting
|
||||
|
||||
|
||||
|
||||
1. FUNCTIONALITY
|
||||
|
||||
With asynchronous I/O, write requests are handled by a separate thread
|
||||
running in the background. This means that the thread that initiates
|
||||
a database write does not have to wait for (sometimes slow) disk I/O
|
||||
to occur. The write seems to happen very quickly, though in reality
|
||||
it is happening at its usual slow pace in the background.
|
||||
|
||||
Asynchronous I/O appears to give better responsiveness, but at a price.
|
||||
You lose the Durable property. With the default I/O backend of SQLite,
|
||||
once a write completes, you know that the information you wrote is
|
||||
safely on disk. With the asynchronous I/O, this is not the case. If
|
||||
your program crashes or if a power loss occurs after the database
|
||||
write but before the asynchronous write thread has completed, then the
|
||||
database change might never make it to disk and the next user of the
|
||||
database might not see your change.
|
||||
|
||||
You lose Durability with asynchronous I/O, but you still retain the
|
||||
other parts of ACID: Atomic, Consistent, and Isolated. Many
|
||||
appliations get along fine without the Durablity.
|
||||
|
||||
1.1 How it Works
|
||||
|
||||
Asynchronous I/O works by creating a special SQLite "vfs" structure
|
||||
and registering it with sqlite3_vfs_register(). When files opened via
|
||||
this vfs are written to (using the vfs xWrite() method), the data is not
|
||||
written directly to disk, but is placed in the "write-queue" to be
|
||||
handled by the background thread.
|
||||
|
||||
When files opened with the asynchronous vfs are read from
|
||||
(using the vfs xRead() method), the data is read from the file on
|
||||
disk and the write-queue, so that from the point of view of
|
||||
the vfs reader the xWrite() appears to have already completed.
|
||||
|
||||
The special vfs is registered (and unregistered) by calls to the
|
||||
API functions sqlite3async_initialize() and sqlite3async_shutdown().
|
||||
See section "Compilation and Usage" below for details.
|
||||
|
||||
1.2 Limitations
|
||||
|
||||
In order to gain experience with the main ideas surrounding asynchronous
|
||||
IO, this implementation is deliberately kept simple. Additional
|
||||
capabilities may be added in the future.
|
||||
|
||||
For example, as currently implemented, if writes are happening at a
|
||||
steady stream that exceeds the I/O capability of the background writer
|
||||
thread, the queue of pending write operations will grow without bound.
|
||||
If this goes on for long enough, the host system could run out of memory.
|
||||
A more sophisticated module could to keep track of the quantity of
|
||||
pending writes and stop accepting new write requests when the queue of
|
||||
pending writes grows too large.
|
||||
|
||||
1.3 Locking and Concurrency
|
||||
|
||||
Multiple connections from within a single process that use this
|
||||
implementation of asynchronous IO may access a single database
|
||||
file concurrently. From the point of view of the user, if all
|
||||
connections are from within a single process, there is no difference
|
||||
between the concurrency offered by "normal" SQLite and SQLite
|
||||
using the asynchronous backend.
|
||||
|
||||
If file-locking is enabled (it is enabled by default), then connections
|
||||
from multiple processes may also read and write the database file.
|
||||
However concurrency is reduced as follows:
|
||||
|
||||
* When a connection using asynchronous IO begins a database
|
||||
transaction, the database is locked immediately. However the
|
||||
lock is not released until after all relevant operations
|
||||
in the write-queue have been flushed to disk. This means
|
||||
(for example) that the database may remain locked for some
|
||||
time after a "COMMIT" or "ROLLBACK" is issued.
|
||||
|
||||
* If an application using asynchronous IO executes transactions
|
||||
in quick succession, other database users may be effectively
|
||||
locked out of the database. This is because when a BEGIN
|
||||
is executed, a database lock is established immediately. But
|
||||
when the corresponding COMMIT or ROLLBACK occurs, the lock
|
||||
is not released until the relevant part of the write-queue
|
||||
has been flushed through. As a result, if a COMMIT is followed
|
||||
by a BEGIN before the write-queue is flushed through, the database
|
||||
is never unlocked,preventing other processes from accessing
|
||||
the database.
|
||||
|
||||
File-locking may be disabled at runtime using the sqlite3async_control()
|
||||
API (see below). This may improve performance when an NFS or other
|
||||
network file-system, as the synchronous round-trips to the server be
|
||||
required to establish file locks are avoided. However, if multiple
|
||||
connections attempt to access the same database file when file-locking
|
||||
is disabled, application crashes and database corruption is a likely
|
||||
outcome.
|
||||
|
||||
|
||||
2. COMPILATION AND USAGE
|
||||
|
||||
The asynchronous IO extension consists of a single file of C code
|
||||
(sqlite3async.c), and a header file (sqlite3async.h) that defines the
|
||||
C API used by applications to activate and control the modules
|
||||
functionality.
|
||||
|
||||
To use the asynchronous IO extension, compile sqlite3async.c as
|
||||
part of the application that uses SQLite. Then use the API defined
|
||||
in sqlite3async.h to initialize and configure the module.
|
||||
|
||||
The asynchronous IO VFS API is described in detail in comments in
|
||||
sqlite3async.h. Using the API usually consists of the following steps:
|
||||
|
||||
1. Register the asynchronous IO VFS with SQLite by calling the
|
||||
sqlite3async_initialize() function.
|
||||
|
||||
2. Create a background thread to perform write operations and call
|
||||
sqlite3async_run().
|
||||
|
||||
3. Use the normal SQLite API to read and write to databases via
|
||||
the asynchronous IO VFS.
|
||||
|
||||
Refer to sqlite3async.h for details.
|
||||
|
||||
|
||||
3. PORTING
|
||||
|
||||
Currently the asynchronous IO extension is compatible with win32 systems
|
||||
and systems that support the pthreads interface, including Mac OSX, Linux,
|
||||
and other varieties of Unix.
|
||||
|
||||
To port the asynchronous IO extension to another platform, the user must
|
||||
implement mutex and condition variable primitives for the new platform.
|
||||
Currently there is no externally available interface to allow this, but
|
||||
modifying the code within sqlite3async.c to include the new platforms
|
||||
concurrency primitives is relatively easy. Search within sqlite3async.c
|
||||
for the comment string "PORTING FUNCTIONS" for details. Then implement
|
||||
new versions of each of the following:
|
||||
|
||||
static void async_mutex_enter(int eMutex);
|
||||
static void async_mutex_leave(int eMutex);
|
||||
static void async_cond_wait(int eCond, int eMutex);
|
||||
static void async_cond_signal(int eCond);
|
||||
static void async_sched_yield(void);
|
||||
|
||||
The functionality required of each of the above functions is described
|
||||
in comments in sqlite3async.c.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,213 @@
|
||||
|
||||
#ifndef __SQLITEASYNC_H_
|
||||
#define __SQLITEASYNC_H_ 1
|
||||
|
||||
#define SQLITEASYNC_VFSNAME "sqlite3async"
|
||||
|
||||
/*
|
||||
** THREAD SAFETY NOTES:
|
||||
**
|
||||
** Of the four API functions in this file, the following are not threadsafe:
|
||||
**
|
||||
** sqlite3async_initialize()
|
||||
** sqlite3async_shutdown()
|
||||
**
|
||||
** Care must be taken that neither of these functions is called while
|
||||
** another thread may be calling either any sqlite3async_XXX() function
|
||||
** or an sqlite3_XXX() API function related to a database handle that
|
||||
** is using the asynchronous IO VFS.
|
||||
**
|
||||
** These functions:
|
||||
**
|
||||
** sqlite3async_run()
|
||||
** sqlite3async_control()
|
||||
**
|
||||
** are threadsafe. It is quite safe to call either of these functions even
|
||||
** if another thread may also be calling one of them or an sqlite3_XXX()
|
||||
** function related to a database handle that uses the asynchronous IO VFS.
|
||||
*/
|
||||
|
||||
/*
|
||||
** Initialize the asynchronous IO VFS and register it with SQLite using
|
||||
** sqlite3_vfs_register(). If the asynchronous VFS is already initialized
|
||||
** and registered, this function is a no-op. The asynchronous IO VFS
|
||||
** is registered as "sqlite3async".
|
||||
**
|
||||
** The asynchronous IO VFS does not make operating system IO requests
|
||||
** directly. Instead, it uses an existing VFS implementation for all
|
||||
** required file-system operations. If the first parameter to this function
|
||||
** is NULL, then the current default VFS is used for IO. If it is not
|
||||
** NULL, then it must be the name of an existing VFS. In other words, the
|
||||
** first argument to this function is passed to sqlite3_vfs_find() to
|
||||
** locate the VFS to use for all real IO operations. This VFS is known
|
||||
** as the "parent VFS".
|
||||
**
|
||||
** If the second parameter to this function is non-zero, then the
|
||||
** asynchronous IO VFS is registered as the default VFS for all SQLite
|
||||
** database connections within the process. Otherwise, the asynchronous IO
|
||||
** VFS is only used by connections opened using sqlite3_open_v2() that
|
||||
** specifically request VFS "sqlite3async".
|
||||
**
|
||||
** If a parent VFS cannot be located, then SQLITE_ERROR is returned.
|
||||
** In the unlikely event that operating system specific initialization
|
||||
** fails (win32 systems create the required critical section and event
|
||||
** objects within this function), then SQLITE_ERROR is also returned.
|
||||
** Finally, if the call to sqlite3_vfs_register() returns an error, then
|
||||
** the error code is returned to the user by this function. In all three
|
||||
** of these cases, intialization has failed and the asynchronous IO VFS
|
||||
** is not registered with SQLite.
|
||||
**
|
||||
** Otherwise, if no error occurs, SQLITE_OK is returned.
|
||||
*/
|
||||
int sqlite3async_initialize(const char *zParent, int isDefault);
|
||||
|
||||
/*
|
||||
** This function unregisters the asynchronous IO VFS using
|
||||
** sqlite3_vfs_unregister().
|
||||
**
|
||||
** On win32 platforms, this function also releases the small number of
|
||||
** critical section and event objects created by sqlite3async_initialize().
|
||||
*/
|
||||
void sqlite3async_shutdown();
|
||||
|
||||
/*
|
||||
** This function may only be called when the asynchronous IO VFS is
|
||||
** installed (after a call to sqlite3async_initialize()). It processes
|
||||
** zero or more queued write operations before returning. It is expected
|
||||
** (but not required) that this function will be called by a different
|
||||
** thread than those threads that use SQLite. The "background thread"
|
||||
** that performs IO.
|
||||
**
|
||||
** How many queued write operations are performed before returning
|
||||
** depends on the global setting configured by passing the SQLITEASYNC_HALT
|
||||
** verb to sqlite3async_control() (see below for details). By default
|
||||
** this function never returns - it processes all pending operations and
|
||||
** then blocks waiting for new ones.
|
||||
**
|
||||
** If multiple simultaneous calls are made to sqlite3async_run() from two
|
||||
** or more threads, then the calls are serialized internally.
|
||||
*/
|
||||
void sqlite3async_run();
|
||||
|
||||
/*
|
||||
** This function may only be called when the asynchronous IO VFS is
|
||||
** installed (after a call to sqlite3async_initialize()). It is used
|
||||
** to query or configure various parameters that affect the operation
|
||||
** of the asynchronous IO VFS. At present there are three parameters
|
||||
** supported:
|
||||
**
|
||||
** * The "halt" parameter, which configures the circumstances under
|
||||
** which the sqlite3async_run() parameter is configured.
|
||||
**
|
||||
** * The "delay" parameter. Setting the delay parameter to a non-zero
|
||||
** value causes the sqlite3async_run() function to sleep for the
|
||||
** configured number of milliseconds between each queued write
|
||||
** operation.
|
||||
**
|
||||
** * The "lockfiles" parameter. This parameter determines whether or
|
||||
** not the asynchronous IO VFS locks the database files it operates
|
||||
** on. Disabling file locking can improve throughput.
|
||||
**
|
||||
** This function is always passed two arguments. When setting the value
|
||||
** of a parameter, the first argument must be one of SQLITEASYNC_HALT,
|
||||
** SQLITEASYNC_DELAY or SQLITEASYNC_LOCKFILES. The second argument must
|
||||
** be passed the new value for the parameter as type "int".
|
||||
**
|
||||
** When querying the current value of a paramter, the first argument must
|
||||
** be one of SQLITEASYNC_GET_HALT, GET_DELAY or GET_LOCKFILES. The second
|
||||
** argument to this function must be of type (int *). The current value
|
||||
** of the queried parameter is copied to the memory pointed to by the
|
||||
** second argument. For example:
|
||||
**
|
||||
** int eCurrentHalt;
|
||||
** int eNewHalt = SQLITEASYNC_HALT_IDLE;
|
||||
**
|
||||
** sqlite3async_control(SQLITEASYNC_HALT, eNewHalt);
|
||||
** sqlite3async_control(SQLITEASYNC_GET_HALT, &eCurrentHalt);
|
||||
** assert( eNewHalt==eCurrentHalt );
|
||||
**
|
||||
** See below for more detail on each configuration parameter.
|
||||
**
|
||||
** SQLITEASYNC_HALT:
|
||||
**
|
||||
** This is used to set the value of the "halt" parameter. The second
|
||||
** argument must be one of the SQLITEASYNC_HALT_XXX symbols defined
|
||||
** below (either NEVER, IDLE and NOW).
|
||||
**
|
||||
** If the parameter is set to NEVER, then calls to sqlite3async_run()
|
||||
** never return. This is the default setting. If the parameter is set
|
||||
** to IDLE, then calls to sqlite3async_run() return as soon as the
|
||||
** queue of pending write operations is empty. If the parameter is set
|
||||
** to NOW, then calls to sqlite3async_run() return as quickly as
|
||||
** possible, without processing any pending write requests.
|
||||
**
|
||||
** If an attempt is made to set this parameter to an integer value other
|
||||
** than SQLITEASYNC_HALT_NEVER, IDLE or NOW, then sqlite3async_control()
|
||||
** returns SQLITE_MISUSE and the current value of the parameter is not
|
||||
** modified.
|
||||
**
|
||||
** Modifying the "halt" parameter affects calls to sqlite3async_run()
|
||||
** made by other threads that are currently in progress.
|
||||
**
|
||||
** SQLITEASYNC_DELAY:
|
||||
**
|
||||
** This is used to set the value of the "delay" parameter. If set to
|
||||
** a non-zero value, then after completing a pending write request, the
|
||||
** sqlite3async_run() function sleeps for the configured number of
|
||||
** milliseconds.
|
||||
**
|
||||
** If an attempt is made to set this parameter to a negative value,
|
||||
** sqlite3async_control() returns SQLITE_MISUSE and the current value
|
||||
** of the parameter is not modified.
|
||||
**
|
||||
** Modifying the "delay" parameter affects calls to sqlite3async_run()
|
||||
** made by other threads that are currently in progress.
|
||||
**
|
||||
** SQLITEASYNC_LOCKFILES:
|
||||
**
|
||||
** This is used to set the value of the "lockfiles" parameter. This
|
||||
** parameter must be set to either 0 or 1. If set to 1, then the
|
||||
** asynchronous IO VFS uses the xLock() and xUnlock() methods of the
|
||||
** parent VFS to lock database files being read and/or written. If
|
||||
** the parameter is set to 0, then these locks are omitted.
|
||||
**
|
||||
** This parameter may only be set when there are no open database
|
||||
** connections using the VFS and the queue of pending write requests
|
||||
** is empty. Attempting to set it when this is not true, or to set it
|
||||
** to a value other than 0 or 1 causes sqlite3async_control() to return
|
||||
** SQLITE_MISUSE and the value of the parameter to remain unchanged.
|
||||
**
|
||||
** If this parameter is set to zero, then it is only safe to access the
|
||||
** database via the asynchronous IO VFS from within a single process. If
|
||||
** while writing to the database via the asynchronous IO VFS the database
|
||||
** is also read or written from within another process, or via another
|
||||
** connection that does not use the asynchronous IO VFS within the same
|
||||
** process, the results are undefined (and may include crashes or database
|
||||
** corruption).
|
||||
**
|
||||
** Alternatively, if this parameter is set to 1, then it is safe to access
|
||||
** the database from multiple connections within multiple processes using
|
||||
** either the asynchronous IO VFS or the parent VFS directly.
|
||||
*/
|
||||
int sqlite3async_control(int op, ...);
|
||||
|
||||
/*
|
||||
** Values that can be used as the first argument to sqlite3async_control().
|
||||
*/
|
||||
#define SQLITEASYNC_HALT 1
|
||||
#define SQLITEASYNC_GET_HALT 2
|
||||
#define SQLITEASYNC_DELAY 3
|
||||
#define SQLITEASYNC_GET_DELAY 4
|
||||
#define SQLITEASYNC_LOCKFILES 5
|
||||
#define SQLITEASYNC_GET_LOCKFILES 6
|
||||
|
||||
/*
|
||||
** If the first argument to sqlite3async_control() is SQLITEASYNC_HALT,
|
||||
** the second argument should be one of the following.
|
||||
*/
|
||||
#define SQLITEASYNC_HALT_NEVER 0 /* Never halt (default value) */
|
||||
#define SQLITEASYNC_HALT_NOW 1 /* Halt as soon as possible */
|
||||
#define SQLITEASYNC_HALT_IDLE 2 /* Halt when write-queue is empty */
|
||||
|
||||
#endif /* ifndef __SQLITEASYNC_H_ */
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#
|
||||
TCCX = $(TCC) $(OPTS) -I. -I$(TOP)/src -I$(TOP)
|
||||
TCCX += -I$(TOP)/ext/rtree -I$(TOP)/ext/icu -I$(TOP)/ext/fts3
|
||||
TCCX += -I$(TOP)/ext/async
|
||||
|
||||
# Object files for the SQLite library.
|
||||
#
|
||||
@@ -255,7 +256,8 @@ TESTSRC2 = \
|
||||
$(TOP)/src/utf.c $(TOP)/src/util.c $(TOP)/src/vdbeapi.c $(TOP)/src/vdbeaux.c \
|
||||
$(TOP)/src/vdbe.c $(TOP)/src/vdbemem.c $(TOP)/src/where.c parse.c \
|
||||
$(TOP)/ext/fts3/fts3.c $(TOP)/ext/fts3/fts3_expr.c \
|
||||
$(TOP)/ext/fts3/fts3_tokenizer.c
|
||||
$(TOP)/ext/fts3/fts3_tokenizer.c \
|
||||
$(TOP)/ext/async/sqlite3async.c
|
||||
|
||||
# Header files used by all library source files.
|
||||
#
|
||||
|
||||
+3392
File diff suppressed because it is too large
Load Diff
+2200
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,387 @@
|
||||
/*
|
||||
** 2009 April 15
|
||||
**
|
||||
** 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 contains the implementation of the RowHash data structure.
|
||||
** A RowHash has the following properties:
|
||||
**
|
||||
** * A RowHash stores an unordered "bag" of 64-bit integer rowids.
|
||||
** There is no other content.
|
||||
**
|
||||
** * Primative operations are CREATE, INSERT, TEST, and DESTROY.
|
||||
** There is no way to remove individual elements from the RowHash
|
||||
** once they are inserted.
|
||||
**
|
||||
** * INSERT operations are batched. TEST operation will ignore
|
||||
** elements in the current INSERT batch. Only elements inserted
|
||||
** in prior batches will be seen by a TEST.
|
||||
**
|
||||
** The insert batch number is a parameter to the TEST primitive. The
|
||||
** hash table is rebuilt whenever the batch number increases. TEST
|
||||
** operations only look for INSERTs that occurred in prior batches.
|
||||
**
|
||||
** The caller is responsible for insuring that there are no duplicate
|
||||
** INSERTs.
|
||||
**
|
||||
** $Id: rowhash.c,v 1.1 2009/04/21 09:02:47 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** An upper bound on the size of heap allocations made by this module.
|
||||
** Limiting the size of allocations helps to avoid memory fragmentation.
|
||||
*/
|
||||
#define ROWHASH_ALLOCATION 1024
|
||||
|
||||
/*
|
||||
** If there are less than this number of elements in the RowHash, do not
|
||||
** bother building a hash-table. Just do a linear search.
|
||||
*/
|
||||
#define ROWHASH_LINEAR_SEARCH_LIMIT 10
|
||||
|
||||
/*
|
||||
** This value is what we want the average length of the collision hash
|
||||
** chain to be.
|
||||
*/
|
||||
#define ROWHASH_COLLISION_LENGTH 3
|
||||
|
||||
|
||||
/* Forward references to data structures. */
|
||||
typedef struct RowHashElem RowHashElem;
|
||||
typedef struct RowHashBlock RowHashBlock;
|
||||
typedef union RowHashPtr RowHashPtr;
|
||||
typedef struct RowHashPage RowHashPage;
|
||||
|
||||
/*
|
||||
** Number of elements in the RowHashBlock.aElem[] array. This array is
|
||||
** sized to make RowHashBlock very close to (without exceeding)
|
||||
** ROWHASH_ALLOCATION bytes in size.
|
||||
*/
|
||||
#define ROWHASH_ELEM_PER_BLOCK ( \
|
||||
(ROWHASH_ALLOCATION - ROUND8(sizeof(struct RowHashBlockData))) / \
|
||||
sizeof(RowHashElem) \
|
||||
)
|
||||
|
||||
/*
|
||||
** Number of pointers that fit into a single allocation of
|
||||
** ROWHASH_ALLOCATION bytes.
|
||||
*/
|
||||
#define ROWHASH_POINTER_PER_PAGE (ROWHASH_ALLOCATION/sizeof(RowHashPtr))
|
||||
|
||||
/*
|
||||
** A page of pointers used to construct a hash table.
|
||||
**
|
||||
** The hash table is actually a tree composed of instances of this
|
||||
** object. Leaves of the tree use the a[].pElem pointer to point
|
||||
** to RowHashElem entries. Interior nodes of the tree use the
|
||||
** a[].pPage element to point to subpages.
|
||||
**
|
||||
** The hash table is split into a tree in order to avoid having
|
||||
** to make large memory allocations, since large allocations can
|
||||
** result in unwanted memory fragmentation.
|
||||
*/
|
||||
struct RowHashPage {
|
||||
union RowHashPtr {
|
||||
RowHashPage *pPage; /* Used by interior nodes. Pointer to subtree. */
|
||||
RowHashElem *pElem; /* Used by leaves. Pointer to hash entry. */
|
||||
} a[ROWHASH_POINTER_PER_PAGE];
|
||||
};
|
||||
|
||||
/*
|
||||
** Each 64-bit integer in a RowHash is stored as an instance of
|
||||
** this object.
|
||||
**
|
||||
** Instances of this object are not allocated separately. They are
|
||||
** allocated in large blocks using the RowHashBlock object as a container.
|
||||
*/
|
||||
struct RowHashElem {
|
||||
i64 iVal; /* The value being stored. A rowid. */
|
||||
RowHashElem *pNext; /* Next element with the same hash */
|
||||
};
|
||||
|
||||
/*
|
||||
** In order to avoid many small allocations of RowHashElem objects,
|
||||
** multiple RowHashElem objects are allocated at once, as an instance
|
||||
** of this object, and then used as needed.
|
||||
**
|
||||
** A single RowHash object will allocate one or more of these RowHashBlock
|
||||
** objects. As many are allocated as are needed to store all of the
|
||||
** content. All RowHashBlocks are kept on a linked list formed using
|
||||
** RowHashBlock.data.pNext so that they can be freed when the RowHash
|
||||
** is destroyed.
|
||||
**
|
||||
** The linked list of RowHashBlock objects also provides a way to sequentially
|
||||
** scan all elements in the RowHash. This sequential scan is used when
|
||||
** rebuilding the hash table. The hash table is rebuilt after every
|
||||
** batch of inserts.
|
||||
*/
|
||||
struct RowHashBlock {
|
||||
struct RowHashBlockData {
|
||||
int nUsed; /* Num of aElem[] currently used in this block */
|
||||
RowHashBlock *pNext; /* Next RowHashBlock object in list of them all */
|
||||
} data;
|
||||
RowHashElem aElem[ROWHASH_ELEM_PER_BLOCK]; /* Available RowHashElem objects */
|
||||
};
|
||||
|
||||
/*
|
||||
** RowHash structure. References to a structure of this type are passed
|
||||
** around and used as opaque handles by code in other modules.
|
||||
*/
|
||||
struct RowHash {
|
||||
unsigned int nEntry; /* Number of used entries over all RowHashBlocks */
|
||||
int iBatch; /* The current insert batch number */
|
||||
u8 nHeight; /* Height of tree of hash pages */
|
||||
u8 nLinearLimit; /* Linear search limit (used if pHash==0) */
|
||||
int nBucket; /* Number of buckets in hash table */
|
||||
RowHashPage *pHash; /* Pointer to root of hash table tree */
|
||||
RowHashBlock *pBlock; /* Linked list of RowHashBlocks */
|
||||
sqlite3 *db; /* Associated database connection */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** Allocate a hash table tree of height nHeight with *pnLeaf leaf pages.
|
||||
** Set *pp to point to the root of the tree. If the maximum number of leaf
|
||||
** pages in a tree of height nHeight is less than *pnLeaf, allocate only
|
||||
** that part of the tree that is necessary to account for all leaves.
|
||||
**
|
||||
** Before returning, subtract the number of leaves in the tree allocated
|
||||
** from *pnLeaf.
|
||||
**
|
||||
** This routine returns SQLITE_NOMEM if a malloc() fails, or SQLITE_OK
|
||||
** otherwise.
|
||||
*/
|
||||
static int allocHashTable(RowHashPage **pp, int nHeight, int *pnLeaf){
|
||||
RowHashPage *p = (RowHashPage *)sqlite3MallocZero(sizeof(*p));
|
||||
if( !p ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
*pp = p;
|
||||
if( nHeight==0 ){
|
||||
(*pnLeaf)--;
|
||||
}else{
|
||||
int ii;
|
||||
for(ii=0; ii<ROWHASH_POINTER_PER_PAGE && *pnLeaf>0; ii++){
|
||||
if( allocHashTable(&p->a[ii].pPage, nHeight-1, pnLeaf) ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Delete the hash table tree of height nHeight passed as the first argument.
|
||||
*/
|
||||
static void deleteHashTable(RowHashPage *p, int nHeight){
|
||||
if( p ){
|
||||
if( nHeight>0 ){
|
||||
int ii;
|
||||
for(ii=0; ii<ROWHASH_POINTER_PER_PAGE; ii++){
|
||||
deleteHashTable(p->a[ii].pPage, nHeight-1);
|
||||
}
|
||||
}
|
||||
sqlite3_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Find the hash-bucket associated with value iVal. Return a pointer to it.
|
||||
**
|
||||
** By "hash-bucket", we mean the RowHashPage.a[].pElem pointer that
|
||||
** corresponds to a particular hash entry.
|
||||
*/
|
||||
static RowHashElem **findHashBucket(RowHash *pRowHash, i64 iVal){
|
||||
int aOffset[16];
|
||||
int n;
|
||||
RowHashPage *pPage = pRowHash->pHash;
|
||||
int h = (((u64)iVal) % pRowHash->nBucket);
|
||||
|
||||
assert( pRowHash->nHeight < sizeof(aOffset)/sizeof(aOffset[0]) );
|
||||
for(n=0; n<pRowHash->nHeight; n++){
|
||||
int h1 = h / ROWHASH_POINTER_PER_PAGE;
|
||||
aOffset[n] = h - (h1 * ROWHASH_POINTER_PER_PAGE);
|
||||
h = h1;
|
||||
}
|
||||
aOffset[n] = h;
|
||||
for(n=pRowHash->nHeight; n>0; n--){
|
||||
pPage = pPage->a[aOffset[n]].pPage;
|
||||
}
|
||||
return &pPage->a[aOffset[0]].pElem;
|
||||
}
|
||||
|
||||
/*
|
||||
** Build a new hash table tree in p->pHash. The new hash table should
|
||||
** contain all p->nEntry entries in the p->pBlock list. If there
|
||||
** existed a prior tree, delete the old tree first before constructing
|
||||
** the new one.
|
||||
**
|
||||
** If the number of entries (p->nEntry) is less than
|
||||
** ROWHASH_LINEAR_SEARCH_LIMIT, then we are guessing that a linear
|
||||
** search is going to be faster than a lookup, so do not bother
|
||||
** building the hash table.
|
||||
*/
|
||||
static int makeHashTable(RowHash *p, int iBatch){
|
||||
RowHashBlock *pBlock;
|
||||
int nBucket;
|
||||
int nLeaf, n;
|
||||
|
||||
/* Delete the old hash table. */
|
||||
deleteHashTable(p->pHash, p->nHeight);
|
||||
assert( p->iBatch!=iBatch );
|
||||
p->iBatch = iBatch;
|
||||
|
||||
/* Skip building the hash table if the number of elements is small */
|
||||
if( p->nEntry<ROWHASH_LINEAR_SEARCH_LIMIT ){
|
||||
p->nLinearLimit = p->nEntry;
|
||||
p->pHash = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Determine how many leaves the hash-table will comprise. */
|
||||
nLeaf = 1 + (p->nEntry / (ROWHASH_POINTER_PER_PAGE*ROWHASH_COLLISION_LENGTH));
|
||||
p->nBucket = nBucket = nLeaf*ROWHASH_POINTER_PER_PAGE;
|
||||
|
||||
/* Set nHeight to the height of the tree that contains the leaf pages. If
|
||||
** RowHash.nHeight is zero, then the whole hash-table fits on a single
|
||||
** leaf. If RowHash.nHeight is 1, then RowHash.pHash points to an array
|
||||
** of pointers to leaf pages. If 2, pHash points to an array of pointers
|
||||
** to arrays of pointers to leaf pages. And so on.
|
||||
*/
|
||||
p->nHeight = 0;
|
||||
n = nLeaf;
|
||||
while( n>1 ){
|
||||
n = (n+ROWHASH_POINTER_PER_PAGE-1) / ROWHASH_POINTER_PER_PAGE;
|
||||
p->nHeight++;
|
||||
}
|
||||
|
||||
/* Allocate the hash-table. */
|
||||
if( allocHashTable(&p->pHash, p->nHeight, &nLeaf) ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
/* Insert all values into the hash-table. */
|
||||
for(pBlock=p->pBlock; pBlock; pBlock=pBlock->data.pNext){
|
||||
RowHashElem * const pEnd = &pBlock->aElem[pBlock->data.nUsed];
|
||||
RowHashElem *pIter;
|
||||
for(pIter=pBlock->aElem; pIter<pEnd; pIter++){
|
||||
RowHashElem **ppElem = findHashBucket(p, pIter->iVal);
|
||||
pIter->pNext = *ppElem;
|
||||
*ppElem = pIter;
|
||||
}
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Check to see if iVal has been inserted into the hash table "p"
|
||||
** in some batch prior to iBatch. If so, set *pExists to 1.
|
||||
** If not, set *pExists to 0.
|
||||
**
|
||||
** The hash table is rebuilt whenever iBatch changes. A hash table
|
||||
** rebuild might encounter an out-of-memory condition. If that happens,
|
||||
** return SQLITE_NOMEM. If there are no problems, return SQLITE_OK.
|
||||
**
|
||||
** The initial "batch" is 0. So, if there were prior calls to
|
||||
** sqlite3RowhashInsert() and then this routine is invoked with iBatch==0,
|
||||
** because all prior inserts where in the same batch, none of the prior
|
||||
** inserts will be visible and this routine will indicate not found.
|
||||
** Hence, the first invocation of this routine should probably use
|
||||
** a batch number of 1.
|
||||
*/
|
||||
int sqlite3RowhashTest(
|
||||
RowHash *p, /* The RowHash to search in */
|
||||
int iBatch, /* Look for values inserted in batches prior to this batch */
|
||||
i64 iVal, /* The rowid value we are looking for */
|
||||
int *pExists /* Store 0 or 1 hear to indicate not-found or found */
|
||||
){
|
||||
*pExists = 0;
|
||||
if( p ){
|
||||
assert( p->pBlock );
|
||||
if( iBatch!=p->iBatch && makeHashTable(p, iBatch) ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
if( p->pHash ){
|
||||
RowHashElem *pElem = *findHashBucket(p, iVal);
|
||||
for(; pElem; pElem=pElem->pNext){
|
||||
if( pElem->iVal==iVal ){
|
||||
*pExists = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
int ii;
|
||||
RowHashElem *aElem = p->pBlock->aElem;
|
||||
for(ii=0; ii<p->nLinearLimit; ii++){
|
||||
if( aElem[ii].iVal==iVal ){
|
||||
*pExists = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Insert value iVal into the RowHash object. Allocate a new RowHash
|
||||
** object if necessary.
|
||||
**
|
||||
** Return SQLITE_OK if all goes as planned. If a malloc() fails, return
|
||||
** SQLITE_NOMEM.
|
||||
*/
|
||||
int sqlite3RowhashInsert(sqlite3 *db, RowHash **pp, i64 iVal){
|
||||
RowHash *p = *pp;
|
||||
|
||||
/* If the RowHash structure has not been allocated, allocate it now. */
|
||||
if( !p ){
|
||||
p = (RowHash*)sqlite3DbMallocZero(db, sizeof(RowHash));
|
||||
if( !p ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
p->db = db;
|
||||
*pp = p;
|
||||
}
|
||||
|
||||
/* If the current RowHashBlock is full, or if the first RowHashBlock has
|
||||
** not yet been allocated, allocate one now. */
|
||||
if( !p->pBlock || p->pBlock->data.nUsed==ROWHASH_ELEM_PER_BLOCK ){
|
||||
RowHashBlock *pBlock = (RowHashBlock*)sqlite3Malloc(sizeof(RowHashBlock));
|
||||
if( !pBlock ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
pBlock->data.nUsed = 0;
|
||||
pBlock->data.pNext = p->pBlock;
|
||||
p->pBlock = pBlock;
|
||||
}
|
||||
|
||||
/* Add iVal to the current RowHashBlock. */
|
||||
p->pBlock->aElem[p->pBlock->data.nUsed].iVal = iVal;
|
||||
p->pBlock->data.nUsed++;
|
||||
p->nEntry++;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Destroy the RowHash object passed as the first argument.
|
||||
*/
|
||||
void sqlite3RowhashDestroy(RowHash *p){
|
||||
if( p ){
|
||||
RowHashBlock *pBlock, *pNext;
|
||||
deleteHashTable(p->pHash, p->nHeight);
|
||||
for(pBlock=p->pBlock; pBlock; pBlock=pNext){
|
||||
pNext = pBlock->data.pNext;
|
||||
sqlite3_free(pBlock);
|
||||
}
|
||||
sqlite3DbFree(p->db, p);
|
||||
}
|
||||
}
|
||||
+1006
File diff suppressed because it is too large
Load Diff
+3578
File diff suppressed because it is too large
Load Diff
+4
-4
@@ -12,7 +12,7 @@
|
||||
** This file contains C code routines that used to generate VDBE code
|
||||
** that implements the ALTER TABLE command.
|
||||
**
|
||||
** $Id: alter.c,v 1.55 2009/03/24 15:08:10 drh Exp $
|
||||
** $Id: alter.c,v 1.57 2009/04/16 16:30:18 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -225,7 +225,7 @@ static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){
|
||||
#endif
|
||||
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
if( !v ) return;
|
||||
if( NEVER(v==0) ) return;
|
||||
assert( sqlite3BtreeHoldsAllMutexes(pParse->db) );
|
||||
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
|
||||
assert( iDb>=0 );
|
||||
@@ -279,7 +279,7 @@ void sqlite3AlterRenameTable(
|
||||
#endif
|
||||
int isVirtualRename = 0; /* True if this is a v-table with an xRename() */
|
||||
|
||||
if( db->mallocFailed ) goto exit_rename_table;
|
||||
if( NEVER(db->mallocFailed) ) goto exit_rename_table;
|
||||
assert( pSrc->nSrc==1 );
|
||||
assert( sqlite3BtreeHoldsAllMutexes(pParse->db) );
|
||||
|
||||
@@ -512,7 +512,7 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
|
||||
zCol = sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n);
|
||||
if( zCol ){
|
||||
char *zEnd = &zCol[pColDef->n-1];
|
||||
while( (zEnd>zCol && *zEnd==';') || sqlite3Isspace(*zEnd) ){
|
||||
while( zEnd>zCol && (*zEnd==';' || sqlite3Isspace(*zEnd)) ){
|
||||
*zEnd-- = '\0';
|
||||
}
|
||||
sqlite3NestedParse(pParse,
|
||||
|
||||
+4
-3
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file contains code associated with the ANALYZE command.
|
||||
**
|
||||
** @(#) $Id: analyze.c,v 1.51 2009/02/28 10:47:42 danielk1977 Exp $
|
||||
** @(#) $Id: analyze.c,v 1.52 2009/04/16 17:45:48 drh Exp $
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_ANALYZE
|
||||
#include "sqliteInt.h"
|
||||
@@ -100,7 +100,7 @@ static void analyzeOneTable(
|
||||
int iDb; /* Index of database containing pTab */
|
||||
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
if( v==0 || pTab==0 || pTab->pIndex==0 ){
|
||||
if( v==0 || NEVER(pTab==0) || pTab->pIndex==0 ){
|
||||
/* Do no analysis for tables that have no indices */
|
||||
return;
|
||||
}
|
||||
@@ -300,13 +300,14 @@ void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){
|
||||
return;
|
||||
}
|
||||
|
||||
assert( pName2!=0 || pName1==0 );
|
||||
if( pName1==0 ){
|
||||
/* Form 1: Analyze everything */
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( i==1 ) continue; /* Do not analyze the TEMP database */
|
||||
analyzeDatabase(pParse, i);
|
||||
}
|
||||
}else if( pName2==0 || pName2->n==0 ){
|
||||
}else if( pName2->n==0 ){
|
||||
/* Form 2: Analyze the database or table named */
|
||||
iDb = sqlite3FindDb(db, pName1);
|
||||
if( iDb>=0 ){
|
||||
|
||||
+20
-21
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file contains code used to implement the ATTACH and DETACH commands.
|
||||
**
|
||||
** $Id: attach.c,v 1.84 2009/04/08 13:51:51 drh Exp $
|
||||
** $Id: attach.c,v 1.90 2009/05/01 06:19:21 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -74,7 +74,6 @@ static void attachFunc(
|
||||
const char *zFile;
|
||||
Db *aNew;
|
||||
char *zErrDyn = 0;
|
||||
char zErr[128];
|
||||
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
|
||||
@@ -90,22 +89,20 @@ static void attachFunc(
|
||||
** * Specified database name already being used.
|
||||
*/
|
||||
if( db->nDb>=db->aLimit[SQLITE_LIMIT_ATTACHED]+2 ){
|
||||
sqlite3_snprintf(
|
||||
sizeof(zErr), zErr, "too many attached databases - max %d",
|
||||
zErrDyn = sqlite3MPrintf(db, "too many attached databases - max %d",
|
||||
db->aLimit[SQLITE_LIMIT_ATTACHED]
|
||||
);
|
||||
goto attach_error;
|
||||
}
|
||||
if( !db->autoCommit ){
|
||||
sqlite3_snprintf(sizeof(zErr), zErr,
|
||||
"cannot ATTACH database within transaction");
|
||||
zErrDyn = sqlite3MPrintf(db, "cannot ATTACH database within transaction");
|
||||
goto attach_error;
|
||||
}
|
||||
for(i=0; i<db->nDb; i++){
|
||||
char *z = db->aDb[i].zName;
|
||||
if( z && zName && sqlite3StrICmp(z, zName)==0 ){
|
||||
sqlite3_snprintf(sizeof(zErr), zErr,
|
||||
"database %s is already in use", zName);
|
||||
assert( z && zName );
|
||||
if( sqlite3StrICmp(z, zName)==0 ){
|
||||
zErrDyn = sqlite3MPrintf(db, "database %s is already in use", zName);
|
||||
goto attach_error;
|
||||
}
|
||||
}
|
||||
@@ -122,7 +119,7 @@ static void attachFunc(
|
||||
if( aNew==0 ) return;
|
||||
}
|
||||
db->aDb = aNew;
|
||||
aNew = &db->aDb[db->nDb++];
|
||||
aNew = &db->aDb[db->nDb];
|
||||
memset(aNew, 0, sizeof(*aNew));
|
||||
|
||||
/* Open the database file. If the btree is successfully opened, use
|
||||
@@ -132,15 +129,19 @@ static void attachFunc(
|
||||
rc = sqlite3BtreeFactory(db, zFile, 0, SQLITE_DEFAULT_CACHE_SIZE,
|
||||
db->openFlags | SQLITE_OPEN_MAIN_DB,
|
||||
&aNew->pBt);
|
||||
if( rc==SQLITE_OK ){
|
||||
db->nDb++;
|
||||
if( rc==SQLITE_CONSTRAINT ){
|
||||
rc = SQLITE_ERROR;
|
||||
zErrDyn = sqlite3MPrintf(db, "database is already attached");
|
||||
}else if( rc==SQLITE_OK ){
|
||||
Pager *pPager;
|
||||
aNew->pSchema = sqlite3SchemaGet(db, aNew->pBt);
|
||||
if( !aNew->pSchema ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else if( aNew->pSchema->file_format && aNew->pSchema->enc!=ENC(db) ){
|
||||
sqlite3_snprintf(sizeof(zErr), zErr,
|
||||
zErrDyn = sqlite3MPrintf(db,
|
||||
"attached databases must use the same text encoding as main database");
|
||||
goto attach_error;
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
pPager = sqlite3BtreePager(aNew->pBt);
|
||||
sqlite3PagerLockingMode(pPager, db->dfltLockMode);
|
||||
@@ -203,9 +204,10 @@ static void attachFunc(
|
||||
db->nDb = iDb;
|
||||
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
|
||||
db->mallocFailed = 1;
|
||||
sqlite3_snprintf(sizeof(zErr),zErr, "out of memory");
|
||||
}else{
|
||||
sqlite3_snprintf(sizeof(zErr),zErr, "unable to open database: %s", zFile);
|
||||
sqlite3DbFree(db, zErrDyn);
|
||||
zErrDyn = sqlite3MPrintf(db, "out of memory");
|
||||
}else if( zErrDyn==0 ){
|
||||
zErrDyn = sqlite3MPrintf(db, "unable to open database: %s", zFile);
|
||||
}
|
||||
goto attach_error;
|
||||
}
|
||||
@@ -217,9 +219,6 @@ attach_error:
|
||||
if( zErrDyn ){
|
||||
sqlite3_result_error(context, zErrDyn, -1);
|
||||
sqlite3DbFree(db, zErrDyn);
|
||||
}else{
|
||||
zErr[sizeof(zErr)-1] = 0;
|
||||
sqlite3_result_error(context, zErr, -1);
|
||||
}
|
||||
if( rc ) sqlite3_result_error_code(context, rc);
|
||||
}
|
||||
@@ -411,7 +410,7 @@ int sqlite3FixInit(
|
||||
){
|
||||
sqlite3 *db;
|
||||
|
||||
if( iDb<0 || iDb==1 ) return 0;
|
||||
if( NEVER(iDb<0) || iDb==1 ) return 0;
|
||||
db = pParse->db;
|
||||
assert( db->nDb>iDb );
|
||||
pFix->pParse = pParse;
|
||||
@@ -443,7 +442,7 @@ int sqlite3FixSrcList(
|
||||
const char *zDb;
|
||||
struct SrcList_item *pItem;
|
||||
|
||||
if( pList==0 ) return 0;
|
||||
if( NEVER(pList==0) ) return 0;
|
||||
zDb = pFix->zDb;
|
||||
for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){
|
||||
if( pItem->zDatabase==0 ){
|
||||
|
||||
+24
-23
@@ -14,7 +14,7 @@
|
||||
** systems that do not need this facility may omit it by recompiling
|
||||
** the library with -DSQLITE_OMIT_AUTHORIZATION=1
|
||||
**
|
||||
** $Id: auth.c,v 1.29 2007/09/18 15:55:07 drh Exp $
|
||||
** $Id: auth.c,v 1.31 2009/05/04 18:01:40 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -86,10 +86,8 @@ int sqlite3_set_authorizer(
|
||||
** Write an error message into pParse->zErrMsg that explains that the
|
||||
** user-supplied authorization function returned an illegal value.
|
||||
*/
|
||||
static void sqliteAuthBadReturnCode(Parse *pParse, int rc){
|
||||
sqlite3ErrorMsg(pParse, "illegal return value (%d) from the "
|
||||
"authorization function - should be SQLITE_OK, SQLITE_IGNORE, "
|
||||
"or SQLITE_DENY", rc);
|
||||
static void sqliteAuthBadReturnCode(Parse *pParse){
|
||||
sqlite3ErrorMsg(pParse, "authorizer malfunction");
|
||||
pParse->rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
@@ -118,26 +116,30 @@ void sqlite3AuthRead(
|
||||
int iDb; /* The index of the database the expression refers to */
|
||||
|
||||
if( db->xAuth==0 ) return;
|
||||
if( pExpr->op!=TK_COLUMN ) return;
|
||||
assert( pExpr->op==TK_COLUMN );
|
||||
iDb = sqlite3SchemaToIndex(pParse->db, pSchema);
|
||||
if( iDb<0 ){
|
||||
/* An attempt to read a column out of a subquery or other
|
||||
** temporary table. */
|
||||
return;
|
||||
}
|
||||
for(iSrc=0; pTabList && iSrc<pTabList->nSrc; iSrc++){
|
||||
if( pExpr->iTable==pTabList->a[iSrc].iCursor ) break;
|
||||
}
|
||||
if( iSrc>=0 && pTabList && iSrc<pTabList->nSrc ){
|
||||
if( pTabList ){
|
||||
for(iSrc=0; ALWAYS(iSrc<pTabList->nSrc); iSrc++){
|
||||
if( pExpr->iTable==pTabList->a[iSrc].iCursor ) break;
|
||||
}
|
||||
assert( iSrc<pTabList->nSrc );
|
||||
pTab = pTabList->a[iSrc].pTab;
|
||||
}else if( (pStack = pParse->trigStack)!=0 ){
|
||||
/* This must be an attempt to read the NEW or OLD pseudo-tables
|
||||
** of a trigger.
|
||||
*/
|
||||
assert( pExpr->iTable==pStack->newIdx || pExpr->iTable==pStack->oldIdx );
|
||||
pTab = pStack->pTab;
|
||||
}else{
|
||||
pStack = pParse->trigStack;
|
||||
if( ALWAYS(pStack) ){
|
||||
/* This must be an attempt to read the NEW or OLD pseudo-tables
|
||||
** of a trigger.
|
||||
*/
|
||||
assert( pExpr->iTable==pStack->newIdx || pExpr->iTable==pStack->oldIdx );
|
||||
pTab = pStack->pTab;
|
||||
}
|
||||
}
|
||||
if( pTab==0 ) return;
|
||||
if( NEVER(pTab==0) ) return;
|
||||
if( pExpr->iColumn>=0 ){
|
||||
assert( pExpr->iColumn<pTab->nCol );
|
||||
zCol = pTab->aCol[pExpr->iColumn].zName;
|
||||
@@ -162,7 +164,7 @@ void sqlite3AuthRead(
|
||||
}
|
||||
pParse->rc = SQLITE_AUTH;
|
||||
}else if( rc!=SQLITE_OK ){
|
||||
sqliteAuthBadReturnCode(pParse, rc);
|
||||
sqliteAuthBadReturnCode(pParse);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,7 +200,7 @@ int sqlite3AuthCheck(
|
||||
pParse->rc = SQLITE_AUTH;
|
||||
}else if( rc!=SQLITE_OK && rc!=SQLITE_IGNORE ){
|
||||
rc = SQLITE_DENY;
|
||||
sqliteAuthBadReturnCode(pParse, rc);
|
||||
sqliteAuthBadReturnCode(pParse);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -213,11 +215,10 @@ void sqlite3AuthContextPush(
|
||||
AuthContext *pContext,
|
||||
const char *zContext
|
||||
){
|
||||
assert( pParse );
|
||||
pContext->pParse = pParse;
|
||||
if( pParse ){
|
||||
pContext->zAuthContext = pParse->zAuthContext;
|
||||
pParse->zAuthContext = zContext;
|
||||
}
|
||||
pContext->zAuthContext = pParse->zAuthContext;
|
||||
pParse->zAuthContext = zContext;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+219
-91
@@ -9,7 +9,7 @@
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** $Id: btree.c,v 1.595 2009/04/11 16:06:15 danielk1977 Exp $
|
||||
** $Id: btree.c,v 1.608 2009/05/06 18:57:10 shane Exp $
|
||||
**
|
||||
** This file implements a external (disk-based) database using BTrees.
|
||||
** See the header comment on "btreeInt.h" for additional information.
|
||||
@@ -762,18 +762,59 @@ void sqlite3BtreeParseCell(
|
||||
** data header and the local payload, but not any overflow page or
|
||||
** the space used by the cell pointer.
|
||||
*/
|
||||
static u16 cellSizePtr(MemPage *pPage, u8 *pCell){
|
||||
u8 *pIter = &pCell[pPage->childPtrSize];
|
||||
u32 nSize;
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* The value returned by this function should always be the same as
|
||||
** the (CellInfo.nSize) value found by doing a full parse of the
|
||||
** cell. If SQLITE_DEBUG is defined, an assert() at the bottom of
|
||||
** this function verifies that this invariant is not violated. */
|
||||
CellInfo debuginfo;
|
||||
sqlite3BtreeParseCellPtr(pPage, pCell, &debuginfo);
|
||||
#endif
|
||||
|
||||
if( pPage->intKey ){
|
||||
u8 *pEnd;
|
||||
if( pPage->hasData ){
|
||||
pIter += getVarint32(pIter, nSize);
|
||||
}else{
|
||||
nSize = 0;
|
||||
}
|
||||
|
||||
/* pIter now points at the 64-bit integer key value, a variable length
|
||||
** integer. The following block moves pIter to point at the first byte
|
||||
** past the end of the key value. */
|
||||
pEnd = &pIter[9];
|
||||
while( (*pIter++)&0x80 && pIter<pEnd );
|
||||
}else{
|
||||
pIter += getVarint32(pIter, nSize);
|
||||
}
|
||||
|
||||
if( nSize>pPage->maxLocal ){
|
||||
int minLocal = pPage->minLocal;
|
||||
nSize = minLocal + (nSize - minLocal) % (pPage->pBt->usableSize - 4);
|
||||
if( nSize>pPage->maxLocal ){
|
||||
nSize = minLocal;
|
||||
}
|
||||
nSize += 4;
|
||||
}
|
||||
nSize += (pIter - pCell);
|
||||
|
||||
/* The minimum size of any cell is 4 bytes. */
|
||||
if( nSize<4 ){
|
||||
nSize = 4;
|
||||
}
|
||||
|
||||
assert( nSize==debuginfo.nSize );
|
||||
return (u16)nSize;
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
static u16 cellSize(MemPage *pPage, int iCell){
|
||||
CellInfo info;
|
||||
sqlite3BtreeParseCell(pPage, iCell, &info);
|
||||
return info.nSize;
|
||||
return cellSizePtr(pPage, findCell(pPage, iCell));
|
||||
}
|
||||
#endif
|
||||
static u16 cellSizePtr(MemPage *pPage, u8 *pCell){
|
||||
CellInfo info;
|
||||
sqlite3BtreeParseCellPtr(pPage, pCell, &info);
|
||||
return info.nSize;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
/*
|
||||
@@ -920,13 +961,13 @@ static int allocateSpace(MemPage *pPage, int nByte){
|
||||
if( size>=nByte ){
|
||||
int x = size - nByte;
|
||||
if( x<4 ){
|
||||
/* Remove the slot from the free-list. Update the number of
|
||||
** fragmented bytes within the page. */
|
||||
/* Remove the slot from the free-list. Update the number of
|
||||
** fragmented bytes within the page. */
|
||||
memcpy(&data[addr], &data[pc], 2);
|
||||
data[hdr+7] = (u8)(nFrag + x);
|
||||
}else{
|
||||
/* The slot remains on the free-list. Reduce its size to account
|
||||
** for the portion used by the new allocation. */
|
||||
/* The slot remains on the free-list. Reduce its size to account
|
||||
** for the portion used by the new allocation. */
|
||||
put2byte(&data[pc+2], x);
|
||||
}
|
||||
return pc + x;
|
||||
@@ -1360,6 +1401,12 @@ static int btreeInvokeBusyHandler(void *pArg){
|
||||
** database file will be deleted when sqlite3BtreeClose() is called.
|
||||
** If zFilename is ":memory:" then an in-memory database is created
|
||||
** that is automatically destroyed when it is closed.
|
||||
**
|
||||
** If the database is already opened in the same database connection
|
||||
** and we are in shared cache mode, then the open will fail with an
|
||||
** SQLITE_CONSTRAINT error. We cannot allow two or more BtShared
|
||||
** objects in the same database connection since doing so will lead
|
||||
** to problems with locking.
|
||||
*/
|
||||
int sqlite3BtreeOpen(
|
||||
const char *zFilename, /* Name of the file containing the BTree database */
|
||||
@@ -1425,6 +1472,17 @@ int sqlite3BtreeOpen(
|
||||
assert( pBt->nRef>0 );
|
||||
if( 0==strcmp(zFullPathname, sqlite3PagerFilename(pBt->pPager))
|
||||
&& sqlite3PagerVfs(pBt->pPager)==pVfs ){
|
||||
int iDb;
|
||||
for(iDb=db->nDb-1; iDb>=0; iDb--){
|
||||
Btree *pExisting = db->aDb[iDb].pBt;
|
||||
if( pExisting && pExisting->pBt==pBt ){
|
||||
sqlite3_mutex_leave(mutexShared);
|
||||
sqlite3_mutex_leave(mutexOpen);
|
||||
sqlite3_free(zFullPathname);
|
||||
sqlite3_free(p);
|
||||
return SQLITE_CONSTRAINT;
|
||||
}
|
||||
}
|
||||
p->pBt = pBt;
|
||||
pBt->nRef++;
|
||||
break;
|
||||
@@ -1482,7 +1540,6 @@ int sqlite3BtreeOpen(
|
||||
if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE
|
||||
|| ((pBt->pageSize-1)&pBt->pageSize)!=0 ){
|
||||
pBt->pageSize = 0;
|
||||
sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize);
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
/* If the magic name ":memory:" will create an in-memory database, then
|
||||
** leave the autoVacuum mode at 0 (do not auto-vacuum), even if
|
||||
@@ -1504,9 +1561,10 @@ int sqlite3BtreeOpen(
|
||||
pBt->incrVacuum = (get4byte(&zDbHeader[36 + 7*4])?1:0);
|
||||
#endif
|
||||
}
|
||||
rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize);
|
||||
if( rc ) goto btree_open_out;
|
||||
pBt->usableSize = pBt->pageSize - nReserve;
|
||||
assert( (pBt->pageSize & 7)==0 ); /* 8-byte alignment of pageSize */
|
||||
sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize);
|
||||
|
||||
#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO)
|
||||
/* Add the new BtShared object to the linked list sharable BtShareds.
|
||||
@@ -1950,7 +2008,8 @@ static int lockBtree(BtShared *pBt){
|
||||
pBt->usableSize = (u16)usableSize;
|
||||
pBt->pageSize = (u16)pageSize;
|
||||
freeTempSpace(pBt);
|
||||
sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize);
|
||||
rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize);
|
||||
if( rc ) goto page1_init_failed;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
if( usableSize<500 ){
|
||||
@@ -2029,14 +2088,6 @@ static void unlockBtreeIfUnused(BtShared *pBt){
|
||||
if( pBt->inTransaction==TRANS_NONE && pBt->pCursor==0 && pBt->pPage1!=0 ){
|
||||
if( sqlite3PagerRefcount(pBt->pPager)>=1 ){
|
||||
assert( pBt->pPage1->aData );
|
||||
#if 0
|
||||
if( pBt->pPage1->aData==0 ){
|
||||
MemPage *pPage = pBt->pPage1;
|
||||
pPage->aData = sqlite3PagerGetData(pPage->pDbPage);
|
||||
pPage->pBt = pBt;
|
||||
pPage->pgno = 1;
|
||||
}
|
||||
#endif
|
||||
releasePage(pBt->pPage1);
|
||||
}
|
||||
pBt->pPage1 = 0;
|
||||
@@ -2179,7 +2230,7 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
|
||||
if( pBt->readOnly ){
|
||||
rc = SQLITE_READONLY;
|
||||
}else{
|
||||
rc = sqlite3PagerBegin(pBt->pPager, wrflag>1);
|
||||
rc = sqlite3PagerBegin(pBt->pPager,wrflag>1,sqlite3TempInMemory(p->db));
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = newDatabase(pBt);
|
||||
}
|
||||
@@ -2629,7 +2680,7 @@ static int autoVacuumCommit(BtShared *pBt){
|
||||
** database are written into the database file and flushed to oxide.
|
||||
** At the end of this call, the rollback journal still exists on the
|
||||
** disk and we are still holding all locks, so the transaction has not
|
||||
** committed. See sqlite3BtreeCommit() for the second phase of the
|
||||
** committed. See sqlite3BtreeCommitPhaseTwo() for the second phase of the
|
||||
** commit process.
|
||||
**
|
||||
** This call is a no-op if no write-transaction is currently active on pBt.
|
||||
@@ -2669,12 +2720,13 @@ int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
|
||||
** Commit the transaction currently in progress.
|
||||
**
|
||||
** This routine implements the second phase of a 2-phase commit. The
|
||||
** sqlite3BtreeSync() routine does the first phase and should be invoked
|
||||
** prior to calling this routine. The sqlite3BtreeSync() routine did
|
||||
** all the work of writing information out to disk and flushing the
|
||||
** sqlite3BtreeCommitPhaseOne() routine does the first phase and should
|
||||
** be invoked prior to calling this routine. The sqlite3BtreeCommitPhaseOne()
|
||||
** routine did all the work of writing information out to disk and flushing the
|
||||
** contents so that they are written onto the disk platter. All this
|
||||
** routine has to do is delete or truncate the rollback journal
|
||||
** (which causes the transaction to commit) and drop locks.
|
||||
** routine has to do is delete or truncate or zero the header in the
|
||||
** the rollback journal (which causes the transaction to commit) and
|
||||
** drop locks.
|
||||
**
|
||||
** This will release the write lock on the database file. If there
|
||||
** are no active cursors, it also releases the read lock.
|
||||
@@ -2713,7 +2765,7 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p){
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the handles current transaction state to TRANS_NONE and unlock
|
||||
/* Set the current transaction state to TRANS_NONE and unlock
|
||||
** the pager if this call closed the only read or write transaction.
|
||||
*/
|
||||
btreeClearHasContent(pBt);
|
||||
@@ -3869,6 +3921,22 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
|
||||
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
|
||||
|
||||
/* If the cursor already points to the last entry, this is a no-op. */
|
||||
if( CURSOR_VALID==pCur->eState && pCur->atLast ){
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* This block serves to assert() that the cursor really does point
|
||||
** to the last entry in the b-tree. */
|
||||
int ii;
|
||||
for(ii=0; ii<pCur->iPage; ii++){
|
||||
assert( pCur->aiIdx[ii]==pCur->apPage[ii]->nCell );
|
||||
}
|
||||
assert( pCur->aiIdx[pCur->iPage]==pCur->apPage[pCur->iPage]->nCell-1 );
|
||||
assert( pCur->apPage[pCur->iPage]->leaf );
|
||||
#endif
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
rc = moveToRoot(pCur);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( CURSOR_INVALID==pCur->eState ){
|
||||
@@ -3878,7 +3946,6 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
|
||||
assert( pCur->eState==CURSOR_VALID );
|
||||
*pRes = 0;
|
||||
rc = moveToRightmost(pCur);
|
||||
getCellInfo(pCur);
|
||||
pCur->atLast = rc==SQLITE_OK ?1:0;
|
||||
}
|
||||
}
|
||||
@@ -3969,14 +4036,13 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
pCur->aiIdx[pCur->iPage] = (u16)((upr+lwr)/2);
|
||||
}
|
||||
for(;;){
|
||||
void *pCellKey;
|
||||
i64 nCellKey;
|
||||
int idx = pCur->aiIdx[pCur->iPage];
|
||||
int idx = pCur->aiIdx[pCur->iPage]; /* Index of current cell in pPage */
|
||||
u8 *pCell; /* Pointer to current cell in pPage */
|
||||
|
||||
pCur->info.nSize = 0;
|
||||
pCur->validNKey = 1;
|
||||
pCell = findCell(pPage, idx) + pPage->childPtrSize;
|
||||
if( pPage->intKey ){
|
||||
u8 *pCell;
|
||||
pCell = findCell(pPage, idx) + pPage->childPtrSize;
|
||||
i64 nCellKey;
|
||||
if( pPage->hasData ){
|
||||
u32 dummy;
|
||||
pCell += getVarint32(pCell, dummy);
|
||||
@@ -3990,26 +4056,50 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
assert( nCellKey>intKey );
|
||||
c = +1;
|
||||
}
|
||||
pCur->validNKey = 1;
|
||||
pCur->info.nKey = nCellKey;
|
||||
}else{
|
||||
int available;
|
||||
pCellKey = (void *)fetchPayload(pCur, &available, 0);
|
||||
nCellKey = pCur->info.nKey;
|
||||
if( available>=nCellKey ){
|
||||
c = sqlite3VdbeRecordCompare((int)nCellKey, pCellKey, pIdxKey);
|
||||
/* The maximum supported page-size is 32768 bytes. This means that
|
||||
** the maximum number of record bytes stored on an index B-Tree
|
||||
** page is at most 8198 bytes, which may be stored as a 2-byte
|
||||
** varint. This information is used to attempt to avoid parsing
|
||||
** the entire cell by checking for the cases where the record is
|
||||
** stored entirely within the b-tree page by inspecting the first
|
||||
** 2 bytes of the cell.
|
||||
*/
|
||||
int nCell = pCell[0];
|
||||
if( !(nCell & 0x80) && nCell<=pPage->maxLocal ){
|
||||
/* This branch runs if the record-size field of the cell is a
|
||||
** single byte varint and the record fits entirely on the main
|
||||
** b-tree page. */
|
||||
c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[1], pIdxKey);
|
||||
}else if( !(pCell[1] & 0x80)
|
||||
&& (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal
|
||||
){
|
||||
/* The record-size field is a 2 byte varint and the record
|
||||
** fits entirely on the main b-tree page. */
|
||||
c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[2], pIdxKey);
|
||||
}else{
|
||||
pCellKey = sqlite3Malloc( (int)nCellKey );
|
||||
/* The record flows over onto one or more overflow pages. In
|
||||
** this case the whole cell needs to be parsed, a buffer allocated
|
||||
** and accessPayload() used to retrieve the record into the
|
||||
** buffer before VdbeRecordCompare() can be called. */
|
||||
void *pCellKey;
|
||||
u8 * const pCellBody = pCell - pPage->childPtrSize;
|
||||
sqlite3BtreeParseCellPtr(pPage, pCellBody, &pCur->info);
|
||||
nCell = (int)pCur->info.nKey;
|
||||
pCellKey = sqlite3Malloc( nCell );
|
||||
if( pCellKey==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
goto moveto_finish;
|
||||
}
|
||||
rc = sqlite3BtreeKey(pCur, 0, (int)nCellKey, (void*)pCellKey);
|
||||
c = sqlite3VdbeRecordCompare((int)nCellKey, pCellKey, pIdxKey);
|
||||
rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0, 0);
|
||||
c = sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey);
|
||||
sqlite3_free(pCellKey);
|
||||
if( rc ) goto moveto_finish;
|
||||
}
|
||||
}
|
||||
if( c==0 ){
|
||||
pCur->info.nKey = nCellKey;
|
||||
if( pPage->intKey && !pPage->leaf ){
|
||||
lwr = idx;
|
||||
upr = lwr - 1;
|
||||
@@ -4026,7 +4116,6 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
upr = idx-1;
|
||||
}
|
||||
if( lwr>upr ){
|
||||
pCur->info.nKey = nCellKey;
|
||||
break;
|
||||
}
|
||||
pCur->aiIdx[pCur->iPage] = (u16)((lwr+upr)/2);
|
||||
@@ -4987,7 +5076,7 @@ static int insertCell(
|
||||
CellInfo info;
|
||||
sqlite3BtreeParseCellPtr(pPage, pCell, &info);
|
||||
assert( (info.nData+(pPage->intKey?0:info.nKey))==info.nPayload );
|
||||
if( (info.nData+(pPage->intKey?0:info.nKey))>info.nLocal ){
|
||||
if( info.iOverflow ){
|
||||
Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]);
|
||||
rc = ptrmapPut(pPage->pBt, pgnoOvfl, PTRMAP_OVERFLOW1, pPage->pgno);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
@@ -5010,39 +5099,32 @@ static void assemblePage(
|
||||
u16 *aSize /* Sizes of the cells */
|
||||
){
|
||||
int i; /* Loop counter */
|
||||
int totalSize; /* Total size of all cells */
|
||||
int hdr; /* Index of page header */
|
||||
int cellptr; /* Address of next cell pointer */
|
||||
u8 *pCellptr; /* Address of next cell pointer */
|
||||
int cellbody; /* Address of next cell body */
|
||||
u8 *data; /* Data for the page */
|
||||
u8 * const data = pPage->aData; /* Pointer to data for pPage */
|
||||
const int hdr = pPage->hdrOffset; /* Offset of header on pPage */
|
||||
const int nUsable = pPage->pBt->usableSize; /* Usable size of page */
|
||||
|
||||
assert( pPage->nOverflow==0 );
|
||||
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
|
||||
assert( nCell>=0 && nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=5460 );
|
||||
totalSize = 0;
|
||||
for(i=0; i<nCell; i++){
|
||||
totalSize += aSize[i];
|
||||
}
|
||||
assert( totalSize+2*nCell<=pPage->nFree );
|
||||
assert( pPage->nCell==0 );
|
||||
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
|
||||
cellptr = pPage->cellOffset;
|
||||
data = pPage->aData;
|
||||
hdr = pPage->hdrOffset;
|
||||
put2byte(&data[hdr+3], nCell);
|
||||
if( nCell ){
|
||||
cellbody = allocateSpace(pPage, totalSize);
|
||||
assert( cellbody>0 );
|
||||
assert( pPage->nFree >= 2*nCell );
|
||||
pPage->nFree -= 2*nCell;
|
||||
for(i=0; i<nCell; i++){
|
||||
put2byte(&data[cellptr], cellbody);
|
||||
memcpy(&data[cellbody], apCell[i], aSize[i]);
|
||||
cellptr += 2;
|
||||
cellbody += aSize[i];
|
||||
}
|
||||
assert( cellbody==pPage->pBt->usableSize );
|
||||
|
||||
/* Check that the page has just been zeroed by zeroPage() */
|
||||
assert( pPage->nCell==0 );
|
||||
assert( get2byte(&data[hdr+5])==nUsable );
|
||||
|
||||
pCellptr = &data[pPage->cellOffset + nCell*2];
|
||||
cellbody = nUsable;
|
||||
for(i=nCell-1; i>=0; i--){
|
||||
pCellptr -= 2;
|
||||
cellbody -= aSize[i];
|
||||
put2byte(pCellptr, cellbody);
|
||||
memcpy(&data[cellbody], apCell[i], aSize[i]);
|
||||
}
|
||||
put2byte(&data[hdr+3], nCell);
|
||||
put2byte(&data[hdr+5], cellbody);
|
||||
pPage->nFree -= (nCell*2 + nUsable - cellbody);
|
||||
pPage->nCell = (u16)nCell;
|
||||
}
|
||||
|
||||
@@ -6074,16 +6156,28 @@ static int checkForReadConflicts(
|
||||
**
|
||||
** For an INTKEY table, only the nKey value of the key is used. pKey is
|
||||
** ignored. For a ZERODATA table, the pData and nData are both ignored.
|
||||
**
|
||||
** If the seekResult parameter is non-zero, then a successful call to
|
||||
** sqlite3BtreeMoveto() to seek cursor pCur to (pKey, nKey) has already
|
||||
** been performed. seekResult is the search result returned (a negative
|
||||
** number if pCur points at an entry that is smaller than (pKey, nKey), or
|
||||
** a positive value if pCur points at an etry that is larger than
|
||||
** (pKey, nKey)).
|
||||
**
|
||||
** If the seekResult parameter is 0, then cursor pCur may point to any
|
||||
** entry or to no entry at all. In this case this function has to seek
|
||||
** the cursor before the new key can be inserted.
|
||||
*/
|
||||
int sqlite3BtreeInsert(
|
||||
BtCursor *pCur, /* Insert data into the table of this cursor */
|
||||
const void *pKey, i64 nKey, /* The key of the new record */
|
||||
const void *pData, int nData, /* The data of the new record */
|
||||
int nZero, /* Number of extra 0 bytes to append to data */
|
||||
int appendBias /* True if this is likely an append */
|
||||
int appendBias, /* True if this is likely an append */
|
||||
int seekResult /* Result of prior sqlite3BtreeMoveto() call */
|
||||
){
|
||||
int rc;
|
||||
int loc;
|
||||
int loc = seekResult;
|
||||
int szNew;
|
||||
int idx;
|
||||
MemPage *pPage;
|
||||
@@ -6106,12 +6200,21 @@ int sqlite3BtreeInsert(
|
||||
return pCur->skip;
|
||||
}
|
||||
|
||||
/* Save the positions of any other cursors open on this table */
|
||||
sqlite3BtreeClearCursor(pCur);
|
||||
if(
|
||||
SQLITE_OK!=(rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur)) ||
|
||||
/* Save the positions of any other cursors open on this table.
|
||||
**
|
||||
** In some cases, the call to sqlite3BtreeMoveto() below is a no-op. For
|
||||
** example, when inserting data into a table with auto-generated integer
|
||||
** keys, the VDBE layer invokes sqlite3BtreeLast() to figure out the
|
||||
** integer key to use. It then calls this function to actually insert the
|
||||
** data into the intkey B-Tree. In this case sqlite3BtreeMoveto() recognizes
|
||||
** that the cursor is already where it needs to be and returns without
|
||||
** doing any work. To avoid thwarting these optimizations, it is important
|
||||
** not to clear the cursor here.
|
||||
*/
|
||||
if(
|
||||
SQLITE_OK!=(rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur)) || (!loc &&
|
||||
SQLITE_OK!=(rc = sqlite3BtreeMoveto(pCur, pKey, nKey, appendBias, &loc))
|
||||
){
|
||||
)){
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -6157,17 +6260,42 @@ int sqlite3BtreeInsert(
|
||||
assert( pPage->leaf );
|
||||
}
|
||||
rc = insertCell(pPage, idx, newCell, szNew, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = balance(pCur, 1);
|
||||
}
|
||||
assert( rc!=SQLITE_OK || pPage->nCell>0 || pPage->nOverflow>0 );
|
||||
|
||||
/* If no error has occured, call balance() to deal with any overflow and
|
||||
** move the cursor to point at the root of the table (since balance may
|
||||
** have rearranged the table in such a way as to invalidate BtCursor.apPage[]
|
||||
** or BtCursor.aiIdx[]).
|
||||
**
|
||||
** Except, if all of the following are true, do nothing:
|
||||
**
|
||||
** * Inserting the new cell did not cause overflow,
|
||||
**
|
||||
** * Before inserting the new cell the cursor was pointing at the
|
||||
** largest key in an intkey B-Tree, and
|
||||
**
|
||||
** * The key value associated with the new cell is now the largest
|
||||
** in the B-Tree.
|
||||
**
|
||||
** In this case the cursor can be safely left pointing at the (new)
|
||||
** largest key value in the B-Tree. Doing so speeds up inserting a set
|
||||
** of entries with increasing integer key values via a single cursor
|
||||
** (comes up with "INSERT INTO ... SELECT ..." statements), as
|
||||
** the next insert operation is not required to seek the cursor.
|
||||
*/
|
||||
if( rc==SQLITE_OK
|
||||
&& (pPage->nOverflow || !pCur->atLast || loc>=0 || !pCur->apPage[0]->intKey)
|
||||
){
|
||||
rc = balance(pCur, 1);
|
||||
if( rc==SQLITE_OK ){
|
||||
moveToRoot(pCur);
|
||||
}
|
||||
}
|
||||
|
||||
/* Must make sure nOverflow is reset to zero even if the balance()
|
||||
** fails. Internal data structure corruption will result otherwise. */
|
||||
pCur->apPage[pCur->iPage]->nOverflow = 0;
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
moveToRoot(pCur);
|
||||
}
|
||||
end_insert:
|
||||
return rc;
|
||||
}
|
||||
@@ -6999,7 +7127,7 @@ static void checkPtrmap(
|
||||
|
||||
rc = ptrmapGet(pCheck->pBt, iChild, &ePtrmapType, &iPtrmapParent);
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( rc==SQLITE_NOMEM ) pCheck->mallocFailed = 1;
|
||||
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) pCheck->mallocFailed = 1;
|
||||
checkAppendMsg(pCheck, zContext, "Failed to read ptrmap key=%d", iChild);
|
||||
return;
|
||||
}
|
||||
@@ -7126,7 +7254,7 @@ static int checkTreePage(
|
||||
if( iPage==0 ) return 0;
|
||||
if( checkRef(pCheck, iPage, zParentContext) ) return 0;
|
||||
if( (rc = sqlite3BtreeGetPage(pBt, (Pgno)iPage, &pPage, 0))!=0 ){
|
||||
if( rc==SQLITE_NOMEM ) pCheck->mallocFailed = 1;
|
||||
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) pCheck->mallocFailed = 1;
|
||||
checkAppendMsg(pCheck, zContext,
|
||||
"unable to get the page. error code=%d", rc);
|
||||
return 0;
|
||||
|
||||
+2
-2
@@ -13,7 +13,7 @@
|
||||
** subsystem. See comments in the source code for a detailed description
|
||||
** of what each interface routine does.
|
||||
**
|
||||
** @(#) $Id: btree.h,v 1.113 2009/04/10 12:55:17 danielk1977 Exp $
|
||||
** @(#) $Id: btree.h,v 1.114 2009/05/04 11:42:30 danielk1977 Exp $
|
||||
*/
|
||||
#ifndef _BTREE_H_
|
||||
#define _BTREE_H_
|
||||
@@ -148,7 +148,7 @@ int sqlite3BtreeCursorHasMoved(BtCursor*, int*);
|
||||
int sqlite3BtreeDelete(BtCursor*);
|
||||
int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey,
|
||||
const void *pData, int nData,
|
||||
int nZero, int bias);
|
||||
int nZero, int bias, int seekResult);
|
||||
int sqlite3BtreeFirst(BtCursor*, int *pRes);
|
||||
int sqlite3BtreeLast(BtCursor*, int *pRes);
|
||||
int sqlite3BtreeNext(BtCursor*, int *pRes);
|
||||
|
||||
+41
-67
@@ -22,7 +22,7 @@
|
||||
** COMMIT
|
||||
** ROLLBACK
|
||||
**
|
||||
** $Id: build.c,v 1.528 2009/04/08 13:51:51 drh Exp $
|
||||
** $Id: build.c,v 1.537 2009/05/06 18:42:21 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -158,7 +158,9 @@ void sqlite3FinishCoding(Parse *pParse){
|
||||
if( (mask & pParse->cookieMask)==0 ) continue;
|
||||
sqlite3VdbeUsesBtree(v, iDb);
|
||||
sqlite3VdbeAddOp2(v,OP_Transaction, iDb, (mask & pParse->writeMask)!=0);
|
||||
sqlite3VdbeAddOp2(v,OP_VerifyCookie, iDb, pParse->cookieValue[iDb]);
|
||||
if( db->init.busy==0 ){
|
||||
sqlite3VdbeAddOp2(v,OP_VerifyCookie, iDb, pParse->cookieValue[iDb]);
|
||||
}
|
||||
}
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
{
|
||||
@@ -188,7 +190,7 @@ void sqlite3FinishCoding(Parse *pParse){
|
||||
FILE *trace = (db->flags & SQLITE_VdbeTrace)!=0 ? stdout : 0;
|
||||
sqlite3VdbeTrace(v, trace);
|
||||
#endif
|
||||
assert( pParse->disableColCache==0 ); /* Disables and re-enables match */
|
||||
assert( pParse->iCacheLevel==0 ); /* Disables and re-enables match */
|
||||
sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem,
|
||||
pParse->nTab, pParse->explain);
|
||||
pParse->rc = SQLITE_DONE;
|
||||
@@ -259,7 +261,7 @@ Table *sqlite3FindTable(sqlite3 *db, const char *zName, const char *zDatabase){
|
||||
int i;
|
||||
int nName;
|
||||
assert( zName!=0 );
|
||||
nName = sqlite3Strlen(db, zName) + 1;
|
||||
nName = sqlite3Strlen30(zName);
|
||||
for(i=OMIT_TEMPDB; i<db->nDb; i++){
|
||||
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
|
||||
if( zDatabase!=0 && sqlite3StrICmp(zDatabase, db->aDb[j].zName) ) continue;
|
||||
@@ -321,7 +323,7 @@ Table *sqlite3LocateTable(
|
||||
Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const char *zDb){
|
||||
Index *p = 0;
|
||||
int i;
|
||||
int nName = sqlite3Strlen(db, zName)+1;
|
||||
int nName = sqlite3Strlen30(zName);
|
||||
for(i=OMIT_TEMPDB; i<db->nDb; i++){
|
||||
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
|
||||
Schema *pSchema = db->aDb[j].pSchema;
|
||||
@@ -357,7 +359,7 @@ static void sqlite3DeleteIndex(Index *p){
|
||||
const char *zName = p->zName;
|
||||
|
||||
pOld = sqlite3HashInsert(&p->pSchema->idxHash, zName,
|
||||
sqlite3Strlen30(zName)+1, 0);
|
||||
sqlite3Strlen30(zName), 0);
|
||||
assert( pOld==0 || pOld==p );
|
||||
freeIndex(p);
|
||||
}
|
||||
@@ -373,8 +375,8 @@ void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){
|
||||
int len;
|
||||
Hash *pHash = &db->aDb[iDb].pSchema->idxHash;
|
||||
|
||||
len = sqlite3Strlen(db, zIdxName);
|
||||
pIndex = sqlite3HashInsert(pHash, zIdxName, len+1, 0);
|
||||
len = sqlite3Strlen30(zIdxName);
|
||||
pIndex = sqlite3HashInsert(pHash, zIdxName, len, 0);
|
||||
if( pIndex ){
|
||||
if( pIndex->pTable->pIndex==pIndex ){
|
||||
pIndex->pTable->pIndex = pIndex->pNext;
|
||||
@@ -487,8 +489,7 @@ static void sqliteResetColumnNames(Table *pTable){
|
||||
** Table. No changes are made to disk by this routine.
|
||||
**
|
||||
** This routine just deletes the data structure. It does not unlink
|
||||
** the table data structure from the hash table. Nor does it remove
|
||||
** foreign keys from the sqlite.aFKey hash table. But it does destroy
|
||||
** the table data structure from the hash table. But it does destroy
|
||||
** memory structures of the indices and foreign keys associated with
|
||||
** the table.
|
||||
*/
|
||||
@@ -516,13 +517,9 @@ void sqlite3DeleteTable(Table *pTable){
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_FOREIGN_KEY
|
||||
/* Delete all foreign keys associated with this table. The keys
|
||||
** should have already been unlinked from the pSchema->aFKey hash table
|
||||
*/
|
||||
/* Delete all foreign keys associated with this table. */
|
||||
for(pFKey=pTable->pFKey; pFKey; pFKey=pNextFKey){
|
||||
pNextFKey = pFKey->pNextFrom;
|
||||
assert( sqlite3HashFind(&pTable->pSchema->aFKey,
|
||||
pFKey->zTo, sqlite3Strlen30(pFKey->zTo)+1)!=pFKey );
|
||||
sqlite3DbFree(db, pFKey);
|
||||
}
|
||||
#endif
|
||||
@@ -546,7 +543,6 @@ void sqlite3DeleteTable(Table *pTable){
|
||||
*/
|
||||
void sqlite3UnlinkAndDeleteTable(sqlite3 *db, int iDb, const char *zTabName){
|
||||
Table *p;
|
||||
FKey *pF1, *pF2;
|
||||
Db *pDb;
|
||||
|
||||
assert( db!=0 );
|
||||
@@ -554,33 +550,20 @@ void sqlite3UnlinkAndDeleteTable(sqlite3 *db, int iDb, const char *zTabName){
|
||||
assert( zTabName && zTabName[0] );
|
||||
pDb = &db->aDb[iDb];
|
||||
p = sqlite3HashInsert(&pDb->pSchema->tblHash, zTabName,
|
||||
sqlite3Strlen30(zTabName)+1,0);
|
||||
if( p ){
|
||||
#ifndef SQLITE_OMIT_FOREIGN_KEY
|
||||
for(pF1=p->pFKey; pF1; pF1=pF1->pNextFrom){
|
||||
int nTo = sqlite3Strlen30(pF1->zTo) + 1;
|
||||
pF2 = sqlite3HashFind(&pDb->pSchema->aFKey, pF1->zTo, nTo);
|
||||
if( pF2==pF1 ){
|
||||
sqlite3HashInsert(&pDb->pSchema->aFKey, pF1->zTo, nTo, pF1->pNextTo);
|
||||
}else{
|
||||
while( pF2 && pF2->pNextTo!=pF1 ){ pF2=pF2->pNextTo; }
|
||||
if( pF2 ){
|
||||
pF2->pNextTo = pF1->pNextTo;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
sqlite3DeleteTable(p);
|
||||
}
|
||||
sqlite3Strlen30(zTabName),0);
|
||||
sqlite3DeleteTable(p);
|
||||
db->flags |= SQLITE_InternChanges;
|
||||
}
|
||||
|
||||
/*
|
||||
** Given a token, return a string that consists of the text of that
|
||||
** token with any quotations removed. Space to hold the returned string
|
||||
** token. Space to hold the returned string
|
||||
** is obtained from sqliteMalloc() and must be freed by the calling
|
||||
** function.
|
||||
**
|
||||
** Any quotation marks (ex: "name", 'name', [name], or `name`) that
|
||||
** surround the body of the token are removed.
|
||||
**
|
||||
** Tokens are often just pointers into the original SQL text and so
|
||||
** are not \000 terminated and are not persistent. The returned string
|
||||
** is \000 terminated and is persistent.
|
||||
@@ -589,7 +572,7 @@ char *sqlite3NameFromToken(sqlite3 *db, Token *pName){
|
||||
char *zName;
|
||||
if( pName ){
|
||||
zName = sqlite3DbStrNDup(db, (char*)pName->z, pName->n);
|
||||
sqlite3Dequote(zName);
|
||||
if( pName->quoted ) sqlite3Dequote(zName);
|
||||
}else{
|
||||
zName = 0;
|
||||
}
|
||||
@@ -1278,7 +1261,7 @@ CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName, int nName){
|
||||
pColl = sqlite3GetCollSeq(db, pColl, zName, nName);
|
||||
if( !pColl ){
|
||||
if( nName<0 ){
|
||||
nName = sqlite3Strlen(db, zName);
|
||||
nName = sqlite3Strlen30(zName);
|
||||
}
|
||||
sqlite3ErrorMsg(pParse, "no such collation sequence: %.*s", nName, zName);
|
||||
pColl = 0;
|
||||
@@ -1683,26 +1666,14 @@ void sqlite3EndTable(
|
||||
*/
|
||||
if( db->init.busy && pParse->nErr==0 ){
|
||||
Table *pOld;
|
||||
FKey *pFKey;
|
||||
Schema *pSchema = p->pSchema;
|
||||
pOld = sqlite3HashInsert(&pSchema->tblHash, p->zName,
|
||||
sqlite3Strlen30(p->zName)+1,p);
|
||||
sqlite3Strlen30(p->zName),p);
|
||||
if( pOld ){
|
||||
assert( p==pOld ); /* Malloc must have failed inside HashInsert() */
|
||||
db->mallocFailed = 1;
|
||||
return;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_FOREIGN_KEY
|
||||
for(pFKey=p->pFKey; pFKey; pFKey=pFKey->pNextFrom){
|
||||
void *data;
|
||||
int nTo = sqlite3Strlen30(pFKey->zTo) + 1;
|
||||
pFKey->pNextTo = sqlite3HashFind(&pSchema->aFKey, pFKey->zTo, nTo);
|
||||
data = sqlite3HashInsert(&pSchema->aFKey, pFKey->zTo, nTo, pFKey);
|
||||
if( data==(void *)pFKey ){
|
||||
db->mallocFailed = 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
pParse->pNewTable = 0;
|
||||
db->nTable++;
|
||||
db->flags |= SQLITE_InternChanges;
|
||||
@@ -2209,9 +2180,7 @@ exit_drop_table:
|
||||
** in the ON DELETE, ON UPDATE and ON INSERT clauses.
|
||||
**
|
||||
** An FKey structure is created and added to the table currently
|
||||
** under construction in the pParse->pNewTable field. The new FKey
|
||||
** is not linked into db->aFKey at this point - that does not happen
|
||||
** until sqlite3EndTable().
|
||||
** under construction in the pParse->pNewTable field.
|
||||
**
|
||||
** The foreign key is set for IMMEDIATE processing. A subsequent call
|
||||
** to sqlite3DeferForeignKey() might change this to DEFERRED.
|
||||
@@ -2252,7 +2221,7 @@ void sqlite3CreateForeignKey(
|
||||
}else{
|
||||
nCol = pFromCol->nExpr;
|
||||
}
|
||||
nByte = sizeof(*pFKey) + nCol*sizeof(pFKey->aCol[0]) + pTo->n + 1;
|
||||
nByte = sizeof(*pFKey) + (nCol-1)*sizeof(pFKey->aCol[0]) + pTo->n + 1;
|
||||
if( pToCol ){
|
||||
for(i=0; i<pToCol->nExpr; i++){
|
||||
nByte += sqlite3Strlen30(pToCol->a[i].zName) + 1;
|
||||
@@ -2264,14 +2233,12 @@ void sqlite3CreateForeignKey(
|
||||
}
|
||||
pFKey->pFrom = p;
|
||||
pFKey->pNextFrom = p->pFKey;
|
||||
z = (char*)&pFKey[1];
|
||||
pFKey->aCol = (struct sColMap*)z;
|
||||
z += sizeof(struct sColMap)*nCol;
|
||||
z = (char*)&pFKey->aCol[nCol];
|
||||
pFKey->zTo = z;
|
||||
memcpy(z, pTo->z, pTo->n);
|
||||
z[pTo->n] = 0;
|
||||
sqlite3Dequote(z);
|
||||
z += pTo->n+1;
|
||||
pFKey->pNextTo = 0;
|
||||
pFKey->nCol = nCol;
|
||||
if( pFromCol==0 ){
|
||||
pFKey->aCol[0].iFrom = p->nCol-1;
|
||||
@@ -2388,19 +2355,25 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
|
||||
regRecord = sqlite3GetTempReg(pParse);
|
||||
regIdxKey = sqlite3GenerateIndexKey(pParse, pIndex, iTab, regRecord, 1);
|
||||
if( pIndex->onError!=OE_None ){
|
||||
int j1, j2;
|
||||
int regRowid;
|
||||
const int regRowid = regIdxKey + pIndex->nColumn;
|
||||
const int j2 = sqlite3VdbeCurrentAddr(v) + 2;
|
||||
void * const pRegKey = SQLITE_INT_TO_PTR(regIdxKey);
|
||||
|
||||
regRowid = regIdxKey + pIndex->nColumn;
|
||||
j1 = sqlite3VdbeAddOp3(v, OP_IsNull, regIdxKey, 0, pIndex->nColumn);
|
||||
j2 = sqlite3VdbeAddOp4(v, OP_IsUnique, iIdx,
|
||||
0, regRowid, SQLITE_INT_TO_PTR(regRecord), P4_INT32);
|
||||
/* The registers accessed by the OP_IsUnique opcode were allocated
|
||||
** using sqlite3GetTempRange() inside of the sqlite3GenerateIndexKey()
|
||||
** call above. Just before that function was freed they were released
|
||||
** (made available to the compiler for reuse) using
|
||||
** sqlite3ReleaseTempRange(). So in some ways having the OP_IsUnique
|
||||
** opcode use the values stored within seems dangerous. However, since
|
||||
** we can be sure that no other temp registers have been allocated
|
||||
** since sqlite3ReleaseTempRange() was called, it is safe to do so.
|
||||
*/
|
||||
sqlite3VdbeAddOp4(v, OP_IsUnique, iIdx, j2, regRowid, pRegKey, P4_INT32);
|
||||
sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, OE_Abort, 0,
|
||||
"indexed columns are not unique", P4_STATIC);
|
||||
sqlite3VdbeJumpHere(v, j1);
|
||||
sqlite3VdbeJumpHere(v, j2);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdx, regRecord);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
|
||||
sqlite3ReleaseTempReg(pParse, regRecord);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1);
|
||||
sqlite3VdbeJumpHere(v, addr1);
|
||||
@@ -2583,6 +2556,7 @@ void sqlite3CreateIndex(
|
||||
if( pList==0 ){
|
||||
nullId.z = (u8*)pTab->aCol[pTab->nCol-1].zName;
|
||||
nullId.n = sqlite3Strlen30((char*)nullId.z);
|
||||
nullId.quoted = 0;
|
||||
pList = sqlite3ExprListAppend(pParse, 0, 0, &nullId);
|
||||
if( pList==0 ) goto exit_create_index;
|
||||
pList->a[0].sortOrder = (u8)sortOrder;
|
||||
@@ -2739,7 +2713,7 @@ void sqlite3CreateIndex(
|
||||
if( db->init.busy ){
|
||||
Index *p;
|
||||
p = sqlite3HashInsert(&pIndex->pSchema->idxHash,
|
||||
pIndex->zName, sqlite3Strlen30(pIndex->zName)+1,
|
||||
pIndex->zName, sqlite3Strlen30(pIndex->zName),
|
||||
pIndex);
|
||||
if( p ){
|
||||
assert( p==pIndex ); /* Malloc must have failed */
|
||||
|
||||
+8
-10
@@ -13,7 +13,7 @@
|
||||
** This file contains functions used to access the internal hash tables
|
||||
** of user defined functions and collation sequences.
|
||||
**
|
||||
** $Id: callback.c,v 1.37 2009/03/24 15:08:10 drh Exp $
|
||||
** $Id: callback.c,v 1.39 2009/05/03 20:23:53 drh Exp $
|
||||
*/
|
||||
|
||||
#include "sqliteInt.h"
|
||||
@@ -25,7 +25,7 @@
|
||||
*/
|
||||
static void callCollNeeded(sqlite3 *db, const char *zName, int nName){
|
||||
assert( !db->xCollNeeded || !db->xCollNeeded16 );
|
||||
if( nName<0 ) nName = sqlite3Strlen(db, zName);
|
||||
if( nName<0 ) nName = sqlite3Strlen30(zName);
|
||||
if( db->xCollNeeded ){
|
||||
char *zExternal = sqlite3DbStrNDup(db, zName, nName);
|
||||
if( !zExternal ) return;
|
||||
@@ -158,7 +158,7 @@ static CollSeq *findCollSeqEntry(
|
||||
int create
|
||||
){
|
||||
CollSeq *pColl;
|
||||
if( nName<0 ) nName = sqlite3Strlen(db, zName);
|
||||
if( nName<0 ) nName = sqlite3Strlen30(zName);
|
||||
pColl = sqlite3HashFind(&db->aCollSeq, zName, nName);
|
||||
|
||||
if( 0==pColl && create ){
|
||||
@@ -413,14 +413,13 @@ void sqlite3SchemaFree(void *p){
|
||||
|
||||
temp1 = pSchema->tblHash;
|
||||
temp2 = pSchema->trigHash;
|
||||
sqlite3HashInit(&pSchema->trigHash, 0);
|
||||
sqlite3HashClear(&pSchema->aFKey);
|
||||
sqlite3HashInit(&pSchema->trigHash);
|
||||
sqlite3HashClear(&pSchema->idxHash);
|
||||
for(pElem=sqliteHashFirst(&temp2); pElem; pElem=sqliteHashNext(pElem)){
|
||||
sqlite3DeleteTrigger(0, (Trigger*)sqliteHashData(pElem));
|
||||
}
|
||||
sqlite3HashClear(&temp2);
|
||||
sqlite3HashInit(&pSchema->tblHash, 0);
|
||||
sqlite3HashInit(&pSchema->tblHash);
|
||||
for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){
|
||||
Table *pTab = sqliteHashData(pElem);
|
||||
assert( pTab->dbMem==0 );
|
||||
@@ -445,10 +444,9 @@ Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){
|
||||
if( !p ){
|
||||
db->mallocFailed = 1;
|
||||
}else if ( 0==p->file_format ){
|
||||
sqlite3HashInit(&p->tblHash, 0);
|
||||
sqlite3HashInit(&p->idxHash, 0);
|
||||
sqlite3HashInit(&p->trigHash, 0);
|
||||
sqlite3HashInit(&p->aFKey, 1);
|
||||
sqlite3HashInit(&p->tblHash);
|
||||
sqlite3HashInit(&p->idxHash);
|
||||
sqlite3HashInit(&p->trigHash);
|
||||
p->enc = SQLITE_UTF8;
|
||||
}
|
||||
return p;
|
||||
|
||||
+2
-2
@@ -16,7 +16,7 @@
|
||||
** separating it out, the code will be automatically omitted from
|
||||
** static links that do not use it.
|
||||
**
|
||||
** $Id: complete.c,v 1.7 2008/06/13 18:24:27 drh Exp $
|
||||
** $Id: complete.c,v 1.8 2009/04/28 04:46:42 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#ifndef SQLITE_OMIT_COMPLETE
|
||||
@@ -112,7 +112,7 @@ int sqlite3_complete(const char *zSql){
|
||||
/* State: ** SEMI WS OTHER EXPLAIN CREATE TEMP TRIGGER END */
|
||||
/* 0 START: */ { 0, 0, 1, 2, 3, 1, 1, 1, },
|
||||
/* 1 NORMAL: */ { 0, 1, 1, 1, 1, 1, 1, 1, },
|
||||
/* 2 EXPLAIN: */ { 0, 2, 1, 1, 3, 1, 1, 1, },
|
||||
/* 2 EXPLAIN: */ { 0, 2, 2, 1, 3, 1, 1, 1, },
|
||||
/* 3 CREATE: */ { 0, 3, 1, 1, 1, 3, 4, 1, },
|
||||
/* 4 TRIGGER: */ { 5, 4, 4, 4, 4, 4, 4, 4, },
|
||||
/* 5 SEMI: */ { 5, 5, 4, 4, 4, 4, 4, 6, },
|
||||
|
||||
+4
-3
@@ -16,7 +16,7 @@
|
||||
** sqlite3RegisterDateTimeFunctions() found at the bottom of the file.
|
||||
** All other code has file scope.
|
||||
**
|
||||
** $Id: date.c,v 1.105 2009/04/03 12:04:37 drh Exp $
|
||||
** $Id: date.c,v 1.107 2009/05/03 20:23:53 drh Exp $
|
||||
**
|
||||
** SQLite processes all times and dates as Julian Day numbers. The
|
||||
** dates and times are stored as the number of days since noon
|
||||
@@ -344,6 +344,7 @@ static int parseDateOrTime(
|
||||
const char *zDate,
|
||||
DateTime *p
|
||||
){
|
||||
int isRealNum; /* Return from sqlite3IsNumber(). Not used */
|
||||
if( parseYyyyMmDd(zDate,p)==0 ){
|
||||
return 0;
|
||||
}else if( parseHhMmSs(zDate, p)==0 ){
|
||||
@@ -351,7 +352,7 @@ static int parseDateOrTime(
|
||||
}else if( sqlite3StrICmp(zDate,"now")==0){
|
||||
setDateTimeToCurrent(context, p);
|
||||
return 0;
|
||||
}else if( sqlite3IsNumber(zDate, 0, SQLITE_UTF8) ){
|
||||
}else if( sqlite3IsNumber(zDate, &isRealNum, SQLITE_UTF8) ){
|
||||
double r;
|
||||
getValue(zDate, &r);
|
||||
p->iJD = (sqlite3_int64)(r*86400000.0 + 0.5);
|
||||
@@ -549,7 +550,7 @@ static int parseModifier(const char *zMod, DateTime *p){
|
||||
** seconds since 1970. Convert to a real julian day number.
|
||||
*/
|
||||
if( strcmp(z, "unixepoch")==0 && p->validJD ){
|
||||
p->iJD = p->iJD/86400 + 21086676*(i64)10000000;
|
||||
p->iJD = (p->iJD + 43200)/86400 + 21086676*(i64)10000000;
|
||||
clearYMD_HMS_TZ(p);
|
||||
rc = 0;
|
||||
}
|
||||
|
||||
+8
-27
@@ -12,7 +12,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** in order to generate code for DELETE FROM statements.
|
||||
**
|
||||
** $Id: delete.c,v 1.198 2009/03/05 03:48:07 shane Exp $
|
||||
** $Id: delete.c,v 1.201 2009/05/01 21:13:37 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -62,26 +62,6 @@ int sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code that will open a table for reading.
|
||||
*/
|
||||
void sqlite3OpenTable(
|
||||
Parse *p, /* Generate code into this VDBE */
|
||||
int iCur, /* The cursor number of the table */
|
||||
int iDb, /* The database index in sqlite3.aDb[] */
|
||||
Table *pTab, /* The table to be opened */
|
||||
int opcode /* OP_OpenRead or OP_OpenWrite */
|
||||
){
|
||||
Vdbe *v;
|
||||
if( IsVirtual(pTab) ) return;
|
||||
v = sqlite3GetVdbe(p);
|
||||
assert( opcode==OP_OpenWrite || opcode==OP_OpenRead );
|
||||
sqlite3TableLock(p, iDb, pTab->tnum, (opcode==OP_OpenWrite)?1:0, pTab->zName);
|
||||
sqlite3VdbeAddOp3(v, opcode, iCur, pTab->tnum, iDb);
|
||||
sqlite3VdbeChangeP4(v, -1, SQLITE_INT_TO_PTR(pTab->nCol), P4_INT32);
|
||||
VdbeComment((v, "%s", pTab->zName));
|
||||
}
|
||||
|
||||
|
||||
#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
|
||||
/*
|
||||
@@ -107,6 +87,7 @@ void sqlite3MaterializeView(
|
||||
pWhere = sqlite3ExprDup(db, pWhere, 0);
|
||||
viewName.z = (u8*)pView->zName;
|
||||
viewName.n = (unsigned int)sqlite3Strlen30((const char*)viewName.z);
|
||||
viewName.quoted = 0;
|
||||
pFrom = sqlite3SrcListAppendFromTerm(pParse, 0, 0, 0, &viewName, pDup, 0,0);
|
||||
pDup = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
@@ -377,10 +358,8 @@ void sqlite3DeleteFrom(
|
||||
*/
|
||||
if( rcauth==SQLITE_OK && pWhere==0 && !pTrigger && !IsVirtual(pTab) ){
|
||||
assert( !isView );
|
||||
sqlite3VdbeAddOp3(v, OP_Clear, pTab->tnum, iDb, memCnt);
|
||||
if( !pParse->nested ){
|
||||
sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_STATIC);
|
||||
}
|
||||
sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt,
|
||||
pTab->zName, P4_STATIC);
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
assert( pIdx->pSchema==pTab->pSchema );
|
||||
sqlite3VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb);
|
||||
@@ -393,13 +372,15 @@ void sqlite3DeleteFrom(
|
||||
{
|
||||
int iRowid = ++pParse->nMem; /* Used for storing rowid values. */
|
||||
int iRowSet = ++pParse->nMem; /* Register for rowset of rows to delete */
|
||||
int regRowid; /* Actual register containing rowids */
|
||||
|
||||
/* Collect rowids of every row to be deleted.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0,
|
||||
WHERE_FILL_ROWSET, iRowSet);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere,0,WHERE_DUPLICATES_OK);
|
||||
if( pWInfo==0 ) goto delete_from_cleanup;
|
||||
regRowid = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, iRowid, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, regRowid);
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1);
|
||||
}
|
||||
|
||||
+188
-112
@@ -12,7 +12,7 @@
|
||||
** This file contains routines used for analyzing expressions and
|
||||
** for generating VDBE code that evaluates expressions in SQLite.
|
||||
**
|
||||
** $Id: expr.c,v 1.426 2009/04/08 13:51:51 drh Exp $
|
||||
** $Id: expr.c,v 1.432 2009/05/06 18:57:10 shane Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -407,21 +407,18 @@ Expr *sqlite3Expr(
|
||||
int c;
|
||||
assert( pToken->dyn==0 );
|
||||
pNew->span = *pToken;
|
||||
|
||||
/* The pToken->z value is read-only. But the new expression
|
||||
** node created here might be passed to sqlite3DequoteExpr() which
|
||||
** will attempt to modify pNew->token.z. Hence, if the token
|
||||
** is quoted, make a copy now so that DequoteExpr() will change
|
||||
** the copy rather than the original text.
|
||||
*/
|
||||
if( pToken->n>=2
|
||||
&& ((c = pToken->z[0])=='\'' || c=='"' || c=='[' || c=='`') ){
|
||||
sqlite3TokenCopy(db, &pNew->token, pToken);
|
||||
if( pNew->token.z ){
|
||||
pNew->token.n = sqlite3Dequote((char*)pNew->token.z);
|
||||
assert( pNew->token.n==(unsigned)sqlite3Strlen30((char*)pNew->token.z) );
|
||||
}
|
||||
if( c=='"' ) pNew->flags |= EP_DblQuoted;
|
||||
}else{
|
||||
pNew->token = *pToken;
|
||||
pNew->flags |= EP_Dequoted;
|
||||
VVA_ONLY( pNew->vvaFlags |= EVVA_ReadOnlyToken; )
|
||||
}
|
||||
pNew->token.quoted = 0;
|
||||
}else if( pLeft ){
|
||||
if( pRight ){
|
||||
if( pRight->span.dyn==0 && pLeft->span.dyn==0 ){
|
||||
@@ -658,18 +655,6 @@ void sqlite3ExprDelete(sqlite3 *db, Expr *p){
|
||||
sqlite3DbFree(db, p);
|
||||
}
|
||||
|
||||
/*
|
||||
** The Expr.token field might be a string literal that is quoted.
|
||||
** If so, remove the quotation marks.
|
||||
*/
|
||||
void sqlite3DequoteExpr(Expr *p){
|
||||
if( !ExprHasAnyProperty(p, EP_Dequoted) ){
|
||||
ExprSetProperty(p, EP_Dequoted);
|
||||
assert( (p->vvaFlags & EVVA_ReadOnlyToken)==0 );
|
||||
sqlite3Dequote((char*)p->token.z);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of bytes allocated for the expression structure
|
||||
** passed as the first argument. This is always one of EXPR_FULLSIZE,
|
||||
@@ -1463,7 +1448,7 @@ void sqlite3CodeSubselect(
|
||||
int testAddr = 0; /* One-time test address */
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
if( v==0 ) return;
|
||||
|
||||
sqlite3ExprCachePush(pParse);
|
||||
|
||||
/* This code must be run in its entirety every time it is encountered
|
||||
** if any of the following is true:
|
||||
@@ -1570,11 +1555,7 @@ void sqlite3CodeSubselect(
|
||||
}
|
||||
|
||||
/* Evaluate the expression and insert it into the temp table */
|
||||
pParse->disableColCache++;
|
||||
r3 = sqlite3ExprCodeTarget(pParse, pE2, r1);
|
||||
assert( pParse->disableColCache>0 );
|
||||
pParse->disableColCache--;
|
||||
|
||||
if( isRowid ){
|
||||
sqlite3VdbeAddOp2(v, OP_MustBeInt, r3, sqlite3VdbeCurrentAddr(v)+2);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, pExpr->iTable, r2, r3);
|
||||
@@ -1599,7 +1580,7 @@ void sqlite3CodeSubselect(
|
||||
** value of this select in a memory cell and record the number
|
||||
** of the memory cell in iColumn.
|
||||
*/
|
||||
static const Token one = { (u8*)"1", 0, 1 };
|
||||
static const Token one = { (u8*)"1", 0, 0, 1 };
|
||||
Select *pSel;
|
||||
SelectDest dest;
|
||||
|
||||
@@ -1628,6 +1609,7 @@ void sqlite3CodeSubselect(
|
||||
if( testAddr ){
|
||||
sqlite3VdbeJumpHere(v, testAddr-1);
|
||||
}
|
||||
sqlite3ExprCachePop(pParse, 1);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -1705,6 +1687,120 @@ static void codeInteger(Vdbe *v, Expr *pExpr, int negFlag, int iMem){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Clear a cache entry.
|
||||
*/
|
||||
static void cacheEntryClear(Parse *pParse, struct yColCache *p){
|
||||
if( p->tempReg ){
|
||||
if( pParse->nTempReg<ArraySize(pParse->aTempReg) ){
|
||||
pParse->aTempReg[pParse->nTempReg++] = p->iReg;
|
||||
}
|
||||
p->tempReg = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Record in the column cache that a particular column from a
|
||||
** particular table is stored in a particular register.
|
||||
*/
|
||||
void sqlite3ExprCacheStore(Parse *pParse, int iTab, int iCol, int iReg){
|
||||
int i;
|
||||
int minLru;
|
||||
int idxLru;
|
||||
struct yColCache *p;
|
||||
|
||||
/* First replace any existing entry */
|
||||
for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
|
||||
if( p->iReg && p->iTable==iTab && p->iColumn==iCol ){
|
||||
cacheEntryClear(pParse, p);
|
||||
p->iLevel = pParse->iCacheLevel;
|
||||
p->iReg = iReg;
|
||||
p->affChange = 0;
|
||||
p->lru = pParse->iCacheCnt++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if( iReg<=0 ) return;
|
||||
|
||||
/* Find an empty slot and replace it */
|
||||
for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
|
||||
if( p->iReg==0 ){
|
||||
p->iLevel = pParse->iCacheLevel;
|
||||
p->iTable = iTab;
|
||||
p->iColumn = iCol;
|
||||
p->iReg = iReg;
|
||||
p->affChange = 0;
|
||||
p->tempReg = 0;
|
||||
p->lru = pParse->iCacheCnt++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Replace the last recently used */
|
||||
minLru = 0x7fffffff;
|
||||
idxLru = -1;
|
||||
for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
|
||||
if( p->lru<minLru ){
|
||||
idxLru = i;
|
||||
minLru = p->lru;
|
||||
}
|
||||
}
|
||||
if( idxLru>=0 ){
|
||||
p = &pParse->aColCache[idxLru];
|
||||
p->iLevel = pParse->iCacheLevel;
|
||||
p->iTable = iTab;
|
||||
p->iColumn = iCol;
|
||||
p->iReg = iReg;
|
||||
p->affChange = 0;
|
||||
p->tempReg = 0;
|
||||
p->lru = pParse->iCacheCnt++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Indicate that a register is being overwritten. Purge the register
|
||||
** from the column cache.
|
||||
*/
|
||||
void sqlite3ExprCacheRemove(Parse *pParse, int iReg){
|
||||
int i;
|
||||
struct yColCache *p;
|
||||
for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
|
||||
if( p->iReg==iReg ){
|
||||
cacheEntryClear(pParse, p);
|
||||
p->iReg = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Remember the current column cache context. Any new entries added
|
||||
** added to the column cache after this call are removed when the
|
||||
** corresponding pop occurs.
|
||||
*/
|
||||
void sqlite3ExprCachePush(Parse *pParse){
|
||||
pParse->iCacheLevel++;
|
||||
}
|
||||
|
||||
/*
|
||||
** Remove from the column cache any entries that were added since the
|
||||
** the previous N Push operations. In other words, restore the cache
|
||||
** to the state it was in N Pushes ago.
|
||||
*/
|
||||
void sqlite3ExprCachePop(Parse *pParse, int N){
|
||||
int i;
|
||||
struct yColCache *p;
|
||||
assert( N>0 );
|
||||
assert( pParse->iCacheLevel>=N );
|
||||
pParse->iCacheLevel -= N;
|
||||
for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
|
||||
if( p->iReg && p->iLevel>pParse->iCacheLevel ){
|
||||
cacheEntryClear(pParse, p);
|
||||
p->iReg = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code that will extract the iColumn-th column from
|
||||
@@ -1733,20 +1829,21 @@ int sqlite3ExprCodeGetColumn(
|
||||
int i;
|
||||
struct yColCache *p;
|
||||
|
||||
for(i=0, p=pParse->aColCache; i<pParse->nColCache; i++, p++){
|
||||
if( p->iTable==iTable && p->iColumn==iColumn
|
||||
for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
|
||||
if( p->iReg>0 && p->iTable==iTable && p->iColumn==iColumn
|
||||
&& (!p->affChange || allowAffChng) ){
|
||||
#if 0
|
||||
sqlite3VdbeAddOp0(v, OP_Noop);
|
||||
VdbeComment((v, "OPT: tab%d.col%d -> r%d", iTable, iColumn, p->iReg));
|
||||
#endif
|
||||
p->lru = pParse->iCacheCnt++;
|
||||
p->tempReg = 0; /* This pins the register, but also leaks it */
|
||||
return p->iReg;
|
||||
}
|
||||
}
|
||||
assert( v!=0 );
|
||||
if( iColumn<0 ){
|
||||
int op = (pTab && IsVirtual(pTab)) ? OP_VRowid : OP_Rowid;
|
||||
sqlite3VdbeAddOp2(v, op, iTable, iReg);
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iTable, iReg);
|
||||
}else if( pTab==0 ){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iTable, iColumn, iReg);
|
||||
}else{
|
||||
@@ -1759,37 +1856,21 @@ int sqlite3ExprCodeGetColumn(
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if( pParse->disableColCache==0 ){
|
||||
i = pParse->iColCache;
|
||||
p = &pParse->aColCache[i];
|
||||
p->iTable = iTable;
|
||||
p->iColumn = iColumn;
|
||||
p->iReg = iReg;
|
||||
p->affChange = 0;
|
||||
i++;
|
||||
if( i>=ArraySize(pParse->aColCache) ) i = 0;
|
||||
if( i>pParse->nColCache ) pParse->nColCache = i;
|
||||
pParse->iColCache = i;
|
||||
}
|
||||
sqlite3ExprCacheStore(pParse, iTable, iColumn, iReg);
|
||||
return iReg;
|
||||
}
|
||||
|
||||
/*
|
||||
** Clear all column cache entries associated with the vdbe
|
||||
** cursor with cursor number iTable.
|
||||
** Clear all column cache entries.
|
||||
*/
|
||||
void sqlite3ExprClearColumnCache(Parse *pParse, int iTable){
|
||||
if( iTable<0 ){
|
||||
pParse->nColCache = 0;
|
||||
pParse->iColCache = 0;
|
||||
}else{
|
||||
int i;
|
||||
for(i=0; i<pParse->nColCache; i++){
|
||||
if( pParse->aColCache[i].iTable==iTable ){
|
||||
testcase( i==pParse->nColCache-1 );
|
||||
pParse->aColCache[i] = pParse->aColCache[--pParse->nColCache];
|
||||
pParse->iColCache = pParse->nColCache;
|
||||
}
|
||||
void sqlite3ExprCacheClear(Parse *pParse){
|
||||
int i;
|
||||
struct yColCache *p;
|
||||
|
||||
for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
|
||||
if( p->iReg ){
|
||||
cacheEntryClear(pParse, p);
|
||||
p->iReg = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1801,10 +1882,11 @@ void sqlite3ExprClearColumnCache(Parse *pParse, int iTable){
|
||||
void sqlite3ExprCacheAffinityChange(Parse *pParse, int iStart, int iCount){
|
||||
int iEnd = iStart + iCount - 1;
|
||||
int i;
|
||||
for(i=0; i<pParse->nColCache; i++){
|
||||
int r = pParse->aColCache[i].iReg;
|
||||
struct yColCache *p;
|
||||
for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
|
||||
int r = p->iReg;
|
||||
if( r>=iStart && r<=iEnd ){
|
||||
pParse->aColCache[i].affChange = 1;
|
||||
p->affChange = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1815,12 +1897,13 @@ void sqlite3ExprCacheAffinityChange(Parse *pParse, int iStart, int iCount){
|
||||
*/
|
||||
void sqlite3ExprCodeMove(Parse *pParse, int iFrom, int iTo, int nReg){
|
||||
int i;
|
||||
struct yColCache *p;
|
||||
if( iFrom==iTo ) return;
|
||||
sqlite3VdbeAddOp3(pParse->pVdbe, OP_Move, iFrom, iTo, nReg);
|
||||
for(i=0; i<pParse->nColCache; i++){
|
||||
int x = pParse->aColCache[i].iReg;
|
||||
for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
|
||||
int x = p->iReg;
|
||||
if( x>=iFrom && x<iFrom+nReg ){
|
||||
pParse->aColCache[i].iReg += iTo-iFrom;
|
||||
p->iReg += iTo-iFrom;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1843,32 +1926,14 @@ void sqlite3ExprCodeCopy(Parse *pParse, int iFrom, int iTo, int nReg){
|
||||
*/
|
||||
static int usedAsColumnCache(Parse *pParse, int iFrom, int iTo){
|
||||
int i;
|
||||
for(i=0; i<pParse->nColCache; i++){
|
||||
int r = pParse->aColCache[i].iReg;
|
||||
struct yColCache *p;
|
||||
for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
|
||||
int r = p->iReg;
|
||||
if( r>=iFrom && r<=iTo ) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** There is a value in register iReg.
|
||||
**
|
||||
** We are going to modify the value, so we need to make sure it
|
||||
** is not a cached register. If iReg is a cached register,
|
||||
** then clear the corresponding cache line.
|
||||
*/
|
||||
void sqlite3ExprWritableRegister(Parse *pParse, int iReg){
|
||||
int i;
|
||||
if( usedAsColumnCache(pParse, iReg, iReg) ){
|
||||
for(i=0; i<pParse->nColCache; i++){
|
||||
if( pParse->aColCache[i].iReg==iReg ){
|
||||
pParse->aColCache[i] = pParse->aColCache[--pParse->nColCache];
|
||||
pParse->iColCache = pParse->nColCache;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** If the last instruction coded is an ephemeral copy of any of
|
||||
** the registers in the nReg registers beginning with iReg, then
|
||||
@@ -1906,6 +1971,7 @@ void sqlite3ExprHardCopy(Parse *pParse, int iReg, int nReg){
|
||||
** alias has not yet been computed.
|
||||
*/
|
||||
static int codeAlias(Parse *pParse, int iAlias, Expr *pExpr, int target){
|
||||
#if 0
|
||||
sqlite3 *db = pParse->db;
|
||||
int iReg;
|
||||
if( pParse->nAliasAlloc<pParse->nAlias ){
|
||||
@@ -1920,7 +1986,7 @@ static int codeAlias(Parse *pParse, int iAlias, Expr *pExpr, int target){
|
||||
assert( iAlias>0 && iAlias<=pParse->nAlias );
|
||||
iReg = pParse->aAlias[iAlias-1];
|
||||
if( iReg==0 ){
|
||||
if( pParse->disableColCache ){
|
||||
if( pParse->iCacheLevel>0 ){
|
||||
iReg = sqlite3ExprCodeTarget(pParse, pExpr, target);
|
||||
}else{
|
||||
iReg = ++pParse->nMem;
|
||||
@@ -1929,6 +1995,10 @@ static int codeAlias(Parse *pParse, int iAlias, Expr *pExpr, int target){
|
||||
}
|
||||
}
|
||||
return iReg;
|
||||
#else
|
||||
UNUSED_PARAMETER(iAlias);
|
||||
return sqlite3ExprCodeTarget(pParse, pExpr, target);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1998,8 +2068,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
break;
|
||||
}
|
||||
case TK_STRING: {
|
||||
sqlite3DequoteExpr(pExpr);
|
||||
sqlite3VdbeAddOp4(v,OP_String8, 0, target, 0,
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, target, 0,
|
||||
(char*)pExpr->token.z, pExpr->token.n);
|
||||
break;
|
||||
}
|
||||
@@ -2308,9 +2377,8 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
/* Code the <expr> from "<expr> IN (...)". The temporary table
|
||||
** pExpr->iTable contains the values that make up the (...) set.
|
||||
*/
|
||||
pParse->disableColCache++;
|
||||
sqlite3ExprCachePush(pParse);
|
||||
sqlite3ExprCode(pParse, pExpr->pLeft, target);
|
||||
pParse->disableColCache--;
|
||||
j2 = sqlite3VdbeAddOp1(v, OP_IsNull, target);
|
||||
if( eType==IN_INDEX_ROWID ){
|
||||
j3 = sqlite3VdbeAddOp1(v, OP_MustBeInt, target);
|
||||
@@ -2371,6 +2439,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
}
|
||||
sqlite3VdbeJumpHere(v, j2);
|
||||
sqlite3VdbeJumpHere(v, j5);
|
||||
sqlite3ExprCachePop(pParse, 1);
|
||||
VdbeComment((v, "end IN expr r%d", target));
|
||||
break;
|
||||
}
|
||||
@@ -2447,6 +2516,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
Expr cacheX; /* Cached expression X */
|
||||
Expr *pX; /* The X expression */
|
||||
Expr *pTest = 0; /* X==Ei (form A) or just Ei (form B) */
|
||||
VVA_ONLY( int iCacheLevel = pParse->iCacheLevel; )
|
||||
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) && pExpr->x.pList );
|
||||
assert((pExpr->x.pList->nExpr % 2) == 0);
|
||||
@@ -2465,8 +2535,8 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
opCompare.pLeft = &cacheX;
|
||||
pTest = &opCompare;
|
||||
}
|
||||
pParse->disableColCache++;
|
||||
for(i=0; i<nExpr; i=i+2){
|
||||
sqlite3ExprCachePush(pParse);
|
||||
if( pX ){
|
||||
assert( pTest!=0 );
|
||||
opCompare.pRight = aListelem[i].pExpr;
|
||||
@@ -2480,16 +2550,19 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
testcase( aListelem[i+1].pExpr->op==TK_REGISTER );
|
||||
sqlite3ExprCode(pParse, aListelem[i+1].pExpr, target);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, endLabel);
|
||||
sqlite3ExprCachePop(pParse, 1);
|
||||
sqlite3VdbeResolveLabel(v, nextCase);
|
||||
}
|
||||
if( pExpr->pRight ){
|
||||
sqlite3ExprCachePush(pParse);
|
||||
sqlite3ExprCode(pParse, pExpr->pRight, target);
|
||||
sqlite3ExprCachePop(pParse, 1);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, target);
|
||||
}
|
||||
assert( db->mallocFailed || pParse->nErr>0
|
||||
|| pParse->iCacheLevel==iCacheLevel );
|
||||
sqlite3VdbeResolveLabel(v, endLabel);
|
||||
assert( pParse->disableColCache>0 );
|
||||
pParse->disableColCache--;
|
||||
break;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
@@ -2503,7 +2576,6 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
assert( pExpr->affinity==OE_Rollback ||
|
||||
pExpr->affinity == OE_Abort ||
|
||||
pExpr->affinity == OE_Fail );
|
||||
sqlite3DequoteExpr(pExpr);
|
||||
sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->affinity, 0,
|
||||
(char*)pExpr->token.z, pExpr->token.n);
|
||||
} else {
|
||||
@@ -2764,23 +2836,17 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
||||
case TK_AND: {
|
||||
int d2 = sqlite3VdbeMakeLabel(v);
|
||||
testcase( jumpIfNull==0 );
|
||||
testcase( pParse->disableColCache==0 );
|
||||
sqlite3ExprCachePush(pParse);
|
||||
sqlite3ExprIfFalse(pParse, pExpr->pLeft, d2,jumpIfNull^SQLITE_JUMPIFNULL);
|
||||
pParse->disableColCache++;
|
||||
sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
|
||||
assert( pParse->disableColCache>0 );
|
||||
pParse->disableColCache--;
|
||||
sqlite3VdbeResolveLabel(v, d2);
|
||||
sqlite3ExprCachePop(pParse, 1);
|
||||
break;
|
||||
}
|
||||
case TK_OR: {
|
||||
testcase( jumpIfNull==0 );
|
||||
testcase( pParse->disableColCache==0 );
|
||||
sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull);
|
||||
pParse->disableColCache++;
|
||||
sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
|
||||
assert( pParse->disableColCache>0 );
|
||||
pParse->disableColCache--;
|
||||
break;
|
||||
}
|
||||
case TK_NOT: {
|
||||
@@ -2924,24 +2990,18 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
||||
switch( pExpr->op ){
|
||||
case TK_AND: {
|
||||
testcase( jumpIfNull==0 );
|
||||
testcase( pParse->disableColCache==0 );
|
||||
sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
|
||||
pParse->disableColCache++;
|
||||
sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
|
||||
assert( pParse->disableColCache>0 );
|
||||
pParse->disableColCache--;
|
||||
break;
|
||||
}
|
||||
case TK_OR: {
|
||||
int d2 = sqlite3VdbeMakeLabel(v);
|
||||
testcase( jumpIfNull==0 );
|
||||
testcase( pParse->disableColCache==0 );
|
||||
sqlite3ExprCachePush(pParse);
|
||||
sqlite3ExprIfTrue(pParse, pExpr->pLeft, d2, jumpIfNull^SQLITE_JUMPIFNULL);
|
||||
pParse->disableColCache++;
|
||||
sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
|
||||
assert( pParse->disableColCache>0 );
|
||||
pParse->disableColCache--;
|
||||
sqlite3VdbeResolveLabel(v, d2);
|
||||
sqlite3ExprCachePop(pParse, 1);
|
||||
break;
|
||||
}
|
||||
case TK_NOT: {
|
||||
@@ -3277,7 +3337,7 @@ void sqlite3ExprAnalyzeAggList(NameContext *pNC, ExprList *pList){
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate or deallocate temporary use registers during code generation.
|
||||
** Allocate a single new register for use to hold some intermediate result.
|
||||
*/
|
||||
int sqlite3GetTempReg(Parse *pParse){
|
||||
if( pParse->nTempReg==0 ){
|
||||
@@ -3285,9 +3345,25 @@ int sqlite3GetTempReg(Parse *pParse){
|
||||
}
|
||||
return pParse->aTempReg[--pParse->nTempReg];
|
||||
}
|
||||
|
||||
/*
|
||||
** Deallocate a register, making available for reuse for some other
|
||||
** purpose.
|
||||
**
|
||||
** If a register is currently being used by the column cache, then
|
||||
** the dallocation is deferred until the column cache line that uses
|
||||
** the register becomes stale.
|
||||
*/
|
||||
void sqlite3ReleaseTempReg(Parse *pParse, int iReg){
|
||||
if( iReg && pParse->nTempReg<ArraySize(pParse->aTempReg) ){
|
||||
sqlite3ExprWritableRegister(pParse, iReg);
|
||||
int i;
|
||||
struct yColCache *p;
|
||||
for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
|
||||
if( p->iReg==iReg ){
|
||||
p->tempReg = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
pParse->aTempReg[pParse->nTempReg++] = iReg;
|
||||
}
|
||||
}
|
||||
|
||||
+10
-2
@@ -16,7 +16,7 @@
|
||||
** sqliteRegisterBuildinFunctions() found at the bottom of the file.
|
||||
** All other code has file scope.
|
||||
**
|
||||
** $Id: func.c,v 1.231 2009/04/08 23:04:14 drh Exp $
|
||||
** $Id: func.c,v 1.234 2009/04/20 12:07:37 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <stdlib.h>
|
||||
@@ -1179,12 +1179,14 @@ static void countStep(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
p->n++;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
/* The sqlite3_aggregate_count() function is deprecated. But just to make
|
||||
** sure it still operates correctly, verify that its count agrees with our
|
||||
** internal count when using count(*) and when the total count can be
|
||||
** expressed as a 32-bit integer. */
|
||||
assert( argc==1 || p==0 || p->n>0x7fffffff
|
||||
|| p->n==sqlite3_aggregate_count(context) );
|
||||
#endif
|
||||
}
|
||||
static void countFinalize(sqlite3_context *context){
|
||||
CountCtx *p;
|
||||
@@ -1258,9 +1260,15 @@ static void groupConcatStep(
|
||||
|
||||
if( pAccum ){
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
int n;
|
||||
pAccum->useMalloc = 1;
|
||||
pAccum->mxAlloc = db->aLimit[SQLITE_LIMIT_LENGTH];
|
||||
if( pAccum->nChar ){
|
||||
#ifdef SQLITE_OMIT_DEPRECATED
|
||||
n = context->pMem->n;
|
||||
#else
|
||||
n = sqlite3_aggregate_count(context);
|
||||
#endif
|
||||
if( n>1 ){
|
||||
if( argc==2 ){
|
||||
zSep = (char*)sqlite3_value_text(argv[1]);
|
||||
nSep = sqlite3_value_bytes(argv[1]);
|
||||
|
||||
+92
-117
@@ -12,7 +12,7 @@
|
||||
** This is the implementation of generic hash-tables
|
||||
** used in SQLite.
|
||||
**
|
||||
** $Id: hash.c,v 1.33 2009/01/09 01:12:28 drh Exp $
|
||||
** $Id: hash.c,v 1.37 2009/05/02 13:29:38 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <assert.h>
|
||||
@@ -21,12 +21,9 @@
|
||||
** fields of the Hash structure.
|
||||
**
|
||||
** "pNew" is a pointer to the hash table that is to be initialized.
|
||||
** "copyKey" is true if the hash table should make its own private
|
||||
** copy of keys and false if it should just use the supplied pointer.
|
||||
*/
|
||||
void sqlite3HashInit(Hash *pNew, int copyKey){
|
||||
void sqlite3HashInit(Hash *pNew){
|
||||
assert( pNew!=0 );
|
||||
pNew->copyKey = copyKey!=0;
|
||||
pNew->first = 0;
|
||||
pNew->count = 0;
|
||||
pNew->htsize = 0;
|
||||
@@ -48,9 +45,6 @@ void sqlite3HashClear(Hash *pH){
|
||||
pH->htsize = 0;
|
||||
while( elem ){
|
||||
HashElem *next_elem = elem->next;
|
||||
if( pH->copyKey ){
|
||||
sqlite3_free(elem->pKey);
|
||||
}
|
||||
sqlite3_free(elem);
|
||||
elem = next_elem;
|
||||
}
|
||||
@@ -58,25 +52,21 @@ void sqlite3HashClear(Hash *pH){
|
||||
}
|
||||
|
||||
/*
|
||||
** Hash and comparison functions when the mode is SQLITE_HASH_STRING
|
||||
** The hashing function.
|
||||
*/
|
||||
static int strHash(const void *pKey, int nKey){
|
||||
const char *z = (const char *)pKey;
|
||||
static unsigned int strHash(const char *z, int nKey){
|
||||
int h = 0;
|
||||
if( nKey<=0 ) nKey = sqlite3Strlen30(z);
|
||||
assert( nKey>=0 );
|
||||
while( nKey > 0 ){
|
||||
h = (h<<3) ^ h ^ sqlite3UpperToLower[(unsigned char)*z++];
|
||||
nKey--;
|
||||
}
|
||||
return h & 0x7fffffff;
|
||||
}
|
||||
static int strCompare(const void *pKey1, int n1, const void *pKey2, int n2){
|
||||
if( n1!=n2 ) return 1;
|
||||
return sqlite3StrNICmp((const char*)pKey1,(const char*)pKey2,n1);
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
/* Link an element into the hash table
|
||||
/* Link pNew element into the hash table pH. If pEntry!=0 then also
|
||||
** insert pNew into the pEntry hash bucket.
|
||||
*/
|
||||
static void insertElement(
|
||||
Hash *pH, /* The complete hash table */
|
||||
@@ -84,7 +74,13 @@ static void insertElement(
|
||||
HashElem *pNew /* The element to be inserted */
|
||||
){
|
||||
HashElem *pHead; /* First element already in pEntry */
|
||||
pHead = pEntry->chain;
|
||||
if( pEntry ){
|
||||
pHead = pEntry->count ? pEntry->chain : 0;
|
||||
pEntry->count++;
|
||||
pEntry->chain = pNew;
|
||||
}else{
|
||||
pHead = 0;
|
||||
}
|
||||
if( pHead ){
|
||||
pNew->next = pHead;
|
||||
pNew->prev = pHead->prev;
|
||||
@@ -97,44 +93,45 @@ static void insertElement(
|
||||
pNew->prev = 0;
|
||||
pH->first = pNew;
|
||||
}
|
||||
pEntry->count++;
|
||||
pEntry->chain = pNew;
|
||||
}
|
||||
|
||||
|
||||
/* Resize the hash table so that it cantains "new_size" buckets.
|
||||
** "new_size" must be a power of 2. The hash table might fail
|
||||
** to resize if sqlite3_malloc() fails.
|
||||
**
|
||||
** The hash table might fail to resize if sqlite3_malloc() fails or
|
||||
** if the new size is the same as the prior size.
|
||||
** Return TRUE if the resize occurs and false if not.
|
||||
*/
|
||||
static void rehash(Hash *pH, int new_size){
|
||||
static int rehash(Hash *pH, unsigned int new_size){
|
||||
struct _ht *new_ht; /* The new hash table */
|
||||
HashElem *elem, *next_elem; /* For looping over existing elements */
|
||||
|
||||
#ifdef SQLITE_MALLOC_SOFT_LIMIT
|
||||
#if SQLITE_MALLOC_SOFT_LIMIT>0
|
||||
if( new_size*sizeof(struct _ht)>SQLITE_MALLOC_SOFT_LIMIT ){
|
||||
new_size = SQLITE_MALLOC_SOFT_LIMIT/sizeof(struct _ht);
|
||||
}
|
||||
if( new_size==pH->htsize ) return;
|
||||
if( new_size==pH->htsize ) return 0;
|
||||
#endif
|
||||
|
||||
/* There is a call to sqlite3_malloc() inside rehash(). If there is
|
||||
** already an allocation at pH->ht, then if this malloc() fails it
|
||||
** is benign (since failing to resize a hash table is a performance
|
||||
** hit only, not a fatal error).
|
||||
/* The inability to allocates space for a larger hash table is
|
||||
** a performance hit but it is not a fatal error. So mark the
|
||||
** allocation as a benign.
|
||||
*/
|
||||
if( pH->htsize>0 ) sqlite3BeginBenignMalloc();
|
||||
new_ht = (struct _ht *)sqlite3MallocZero( new_size*sizeof(struct _ht) );
|
||||
if( pH->htsize>0 ) sqlite3EndBenignMalloc();
|
||||
sqlite3BeginBenignMalloc();
|
||||
new_ht = (struct _ht *)sqlite3Malloc( new_size*sizeof(struct _ht) );
|
||||
sqlite3EndBenignMalloc();
|
||||
|
||||
if( new_ht==0 ) return;
|
||||
if( new_ht==0 ) return 0;
|
||||
sqlite3_free(pH->ht);
|
||||
pH->ht = new_ht;
|
||||
pH->htsize = new_size;
|
||||
pH->htsize = new_size = sqlite3MallocSize(new_ht)/sizeof(struct _ht);
|
||||
memset(new_ht, 0, new_size*sizeof(struct _ht));
|
||||
for(elem=pH->first, pH->first=0; elem; elem = next_elem){
|
||||
int h = strHash(elem->pKey, elem->nKey) & (new_size-1);
|
||||
unsigned int h = strHash(elem->pKey, elem->nKey) % new_size;
|
||||
next_elem = elem->next;
|
||||
insertElement(pH, &new_ht[h], elem);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* This function (for internal use only) locates an element in an
|
||||
@@ -143,9 +140,9 @@ static void rehash(Hash *pH, int new_size){
|
||||
*/
|
||||
static HashElem *findElementGivenHash(
|
||||
const Hash *pH, /* The pH to be searched */
|
||||
const void *pKey, /* The key we are searching for */
|
||||
int nKey,
|
||||
int h /* The hash for this key. */
|
||||
const char *pKey, /* The key we are searching for */
|
||||
int nKey, /* Bytes in key (not counting zero terminator) */
|
||||
unsigned int h /* The hash for this key. */
|
||||
){
|
||||
HashElem *elem; /* Used to loop thru the element list */
|
||||
int count; /* Number of elements left to test */
|
||||
@@ -154,12 +151,15 @@ static HashElem *findElementGivenHash(
|
||||
struct _ht *pEntry = &pH->ht[h];
|
||||
elem = pEntry->chain;
|
||||
count = pEntry->count;
|
||||
while( count-- && elem ){
|
||||
if( strCompare(elem->pKey,elem->nKey,pKey,nKey)==0 ){
|
||||
return elem;
|
||||
}
|
||||
elem = elem->next;
|
||||
}else{
|
||||
elem = pH->first;
|
||||
count = pH->count;
|
||||
}
|
||||
while( count-- && ALWAYS(elem) ){
|
||||
if( elem->nKey==nKey && sqlite3StrNICmp(elem->pKey,pKey,nKey)==0 ){
|
||||
return elem;
|
||||
}
|
||||
elem = elem->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -170,7 +170,7 @@ static HashElem *findElementGivenHash(
|
||||
static void removeElementGivenHash(
|
||||
Hash *pH, /* The pH containing "elem" */
|
||||
HashElem* elem, /* The element to be removed from the pH */
|
||||
int h /* Hash value for the element */
|
||||
unsigned int h /* Hash value for the element */
|
||||
){
|
||||
struct _ht *pEntry;
|
||||
if( elem->prev ){
|
||||
@@ -181,16 +181,13 @@ static void removeElementGivenHash(
|
||||
if( elem->next ){
|
||||
elem->next->prev = elem->prev;
|
||||
}
|
||||
pEntry = &pH->ht[h];
|
||||
if( pEntry->chain==elem ){
|
||||
pEntry->chain = elem->next;
|
||||
}
|
||||
pEntry->count--;
|
||||
if( pEntry->count<=0 ){
|
||||
pEntry->chain = 0;
|
||||
}
|
||||
if( pH->copyKey ){
|
||||
sqlite3_free(elem->pKey);
|
||||
if( pH->ht ){
|
||||
pEntry = &pH->ht[h];
|
||||
if( pEntry->chain==elem ){
|
||||
pEntry->chain = elem->next;
|
||||
}
|
||||
pEntry->count--;
|
||||
assert( pEntry->count>=0 );
|
||||
}
|
||||
sqlite3_free( elem );
|
||||
pH->count--;
|
||||
@@ -201,28 +198,23 @@ static void removeElementGivenHash(
|
||||
}
|
||||
}
|
||||
|
||||
/* Attempt to locate an element of the hash table pH with a key
|
||||
** that matches pKey,nKey. Return a pointer to the corresponding
|
||||
** HashElem structure for this element if it is found, or NULL
|
||||
** otherwise.
|
||||
*/
|
||||
HashElem *sqlite3HashFindElem(const Hash *pH, const void *pKey, int nKey){
|
||||
int h; /* A hash on key */
|
||||
HashElem *elem; /* The element that matches key */
|
||||
|
||||
if( pH==0 || pH->ht==0 ) return 0;
|
||||
h = strHash(pKey,nKey);
|
||||
elem = findElementGivenHash(pH,pKey,nKey, h % pH->htsize);
|
||||
return elem;
|
||||
}
|
||||
|
||||
/* Attempt to locate an element of the hash table pH with a key
|
||||
** that matches pKey,nKey. Return the data for this element if it is
|
||||
** found, or NULL if there is no match.
|
||||
*/
|
||||
void *sqlite3HashFind(const Hash *pH, const void *pKey, int nKey){
|
||||
void *sqlite3HashFind(const Hash *pH, const char *pKey, int nKey){
|
||||
HashElem *elem; /* The element that matches key */
|
||||
elem = sqlite3HashFindElem(pH, pKey, nKey);
|
||||
unsigned int h; /* A hash on key */
|
||||
|
||||
assert( pH!=0 );
|
||||
assert( pKey!=0 );
|
||||
assert( nKey>=0 );
|
||||
if( pH->ht ){
|
||||
h = strHash(pKey, nKey) % pH->htsize;
|
||||
}else{
|
||||
h = 0;
|
||||
}
|
||||
elem = findElementGivenHash(pH, pKey, nKey, h);
|
||||
return elem ? elem->data : 0;
|
||||
}
|
||||
|
||||
@@ -230,8 +222,7 @@ void *sqlite3HashFind(const Hash *pH, const void *pKey, int nKey){
|
||||
** and the data is "data".
|
||||
**
|
||||
** If no element exists with a matching key, then a new
|
||||
** element is created. A copy of the key is made if the copyKey
|
||||
** flag is set. NULL is returned.
|
||||
** element is created and NULL is returned.
|
||||
**
|
||||
** If another element already exists with the same key, then the
|
||||
** new data replaces the old data and the old data is returned.
|
||||
@@ -241,63 +232,47 @@ void *sqlite3HashFind(const Hash *pH, const void *pKey, int nKey){
|
||||
** If the "data" parameter to this function is NULL, then the
|
||||
** element corresponding to "key" is removed from the hash table.
|
||||
*/
|
||||
void *sqlite3HashInsert(Hash *pH, const void *pKey, int nKey, void *data){
|
||||
int hraw; /* Raw hash value of the key */
|
||||
int h; /* the hash of the key modulo hash table size */
|
||||
void *sqlite3HashInsert(Hash *pH, const char *pKey, int nKey, void *data){
|
||||
unsigned int h; /* the hash of the key modulo hash table size */
|
||||
HashElem *elem; /* Used to loop thru the element list */
|
||||
HashElem *new_elem; /* New element added to the pH */
|
||||
|
||||
assert( pH!=0 );
|
||||
hraw = strHash(pKey, nKey);
|
||||
assert( pKey!=0 );
|
||||
assert( nKey>=0 );
|
||||
if( pH->htsize ){
|
||||
h = hraw % pH->htsize;
|
||||
elem = findElementGivenHash(pH,pKey,nKey,h);
|
||||
if( elem ){
|
||||
void *old_data = elem->data;
|
||||
if( data==0 ){
|
||||
removeElementGivenHash(pH,elem,h);
|
||||
}else{
|
||||
elem->data = data;
|
||||
if( !pH->copyKey ){
|
||||
elem->pKey = (void *)pKey;
|
||||
}
|
||||
assert(nKey==elem->nKey);
|
||||
}
|
||||
return old_data;
|
||||
h = strHash(pKey, nKey) % pH->htsize;
|
||||
}else{
|
||||
h = 0;
|
||||
}
|
||||
elem = findElementGivenHash(pH,pKey,nKey,h);
|
||||
if( elem ){
|
||||
void *old_data = elem->data;
|
||||
if( data==0 ){
|
||||
removeElementGivenHash(pH,elem,h);
|
||||
}else{
|
||||
elem->data = data;
|
||||
elem->pKey = pKey;
|
||||
assert(nKey==elem->nKey);
|
||||
}
|
||||
return old_data;
|
||||
}
|
||||
if( data==0 ) return 0;
|
||||
new_elem = (HashElem*)sqlite3Malloc( sizeof(HashElem) );
|
||||
if( new_elem==0 ) return data;
|
||||
if( pH->copyKey && pKey!=0 ){
|
||||
new_elem->pKey = sqlite3Malloc( nKey );
|
||||
if( new_elem->pKey==0 ){
|
||||
sqlite3_free(new_elem);
|
||||
return data;
|
||||
}
|
||||
memcpy((void*)new_elem->pKey, pKey, nKey);
|
||||
}else{
|
||||
new_elem->pKey = (void*)pKey;
|
||||
}
|
||||
new_elem->pKey = pKey;
|
||||
new_elem->nKey = nKey;
|
||||
new_elem->data = data;
|
||||
pH->count++;
|
||||
if( pH->htsize==0 ){
|
||||
rehash(pH, 128/sizeof(pH->ht[0]));
|
||||
if( pH->htsize==0 ){
|
||||
pH->count = 0;
|
||||
if( pH->copyKey ){
|
||||
sqlite3_free(new_elem->pKey);
|
||||
}
|
||||
sqlite3_free(new_elem);
|
||||
return data;
|
||||
if( pH->count>=10 && pH->count > 2*pH->htsize ){
|
||||
if( rehash(pH, pH->count*2) && pH->htsize ){
|
||||
h = strHash(pKey, nKey) % pH->htsize;
|
||||
}
|
||||
}
|
||||
if( pH->count > pH->htsize ){
|
||||
rehash(pH,pH->htsize*2);
|
||||
if( pH->ht ){
|
||||
insertElement(pH, &pH->ht[h], new_elem);
|
||||
}else{
|
||||
insertElement(pH, 0, new_elem);
|
||||
}
|
||||
assert( pH->htsize>0 );
|
||||
h = hraw % pH->htsize;
|
||||
insertElement(pH, &pH->ht[h], new_elem);
|
||||
new_elem->data = data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
+25
-14
@@ -12,7 +12,7 @@
|
||||
** This is the header file for the generic hash-table implemenation
|
||||
** used in SQLite.
|
||||
**
|
||||
** $Id: hash.h,v 1.12 2008/10/10 17:41:29 drh Exp $
|
||||
** $Id: hash.h,v 1.15 2009/05/02 13:29:38 drh Exp $
|
||||
*/
|
||||
#ifndef _SQLITE_HASH_H_
|
||||
#define _SQLITE_HASH_H_
|
||||
@@ -25,13 +25,25 @@ typedef struct HashElem HashElem;
|
||||
** The internals of this structure are intended to be opaque -- client
|
||||
** code should not attempt to access or modify the fields of this structure
|
||||
** directly. Change this structure only by using the routines below.
|
||||
** However, many of the "procedures" and "functions" for modifying and
|
||||
** However, some of the "procedures" and "functions" for modifying and
|
||||
** accessing this structure are really macros, so we can't really make
|
||||
** this structure opaque.
|
||||
**
|
||||
** All elements of the hash table are on a single doubly-linked list.
|
||||
** Hash.first points to the head of this list.
|
||||
**
|
||||
** There are Hash.htsize buckets. Each bucket points to a spot in
|
||||
** the global doubly-linked list. The contents of the bucket are the
|
||||
** element pointed to plus the next _ht.count-1 elements in the list.
|
||||
**
|
||||
** Hash.htsize and Hash.ht may be zero. In that case lookup is done
|
||||
** by a linear search of the global list. For small tables, the
|
||||
** Hash.ht table is never allocated because if there are few elements
|
||||
** in the table, it is faster to do a linear search than to manage
|
||||
** the hash table.
|
||||
*/
|
||||
struct Hash {
|
||||
unsigned int copyKey: 1; /* True if copy of key made on insert */
|
||||
unsigned int htsize : 31; /* Number of buckets in the hash table */
|
||||
unsigned int htsize; /* Number of buckets in the hash table */
|
||||
unsigned int count; /* Number of entries in this table */
|
||||
HashElem *first; /* The first element of the array */
|
||||
struct _ht { /* the hash table */
|
||||
@@ -47,18 +59,17 @@ struct Hash {
|
||||
** be opaque because it is used by macros.
|
||||
*/
|
||||
struct HashElem {
|
||||
HashElem *next, *prev; /* Next and previous elements in the table */
|
||||
void *data; /* Data associated with this element */
|
||||
void *pKey; int nKey; /* Key associated with this element */
|
||||
HashElem *next, *prev; /* Next and previous elements in the table */
|
||||
void *data; /* Data associated with this element */
|
||||
const char *pKey; int nKey; /* Key associated with this element */
|
||||
};
|
||||
|
||||
/*
|
||||
** Access routines. To delete, insert a NULL pointer.
|
||||
*/
|
||||
void sqlite3HashInit(Hash*, int copyKey);
|
||||
void *sqlite3HashInsert(Hash*, const void *pKey, int nKey, void *pData);
|
||||
void *sqlite3HashFind(const Hash*, const void *pKey, int nKey);
|
||||
HashElem *sqlite3HashFindElem(const Hash*, const void *pKey, int nKey);
|
||||
void sqlite3HashInit(Hash*);
|
||||
void *sqlite3HashInsert(Hash*, const char *pKey, int nKey, void *pData);
|
||||
void *sqlite3HashFind(const Hash*, const char *pKey, int nKey);
|
||||
void sqlite3HashClear(Hash*);
|
||||
|
||||
/*
|
||||
@@ -76,12 +87,12 @@ void sqlite3HashClear(Hash*);
|
||||
#define sqliteHashFirst(H) ((H)->first)
|
||||
#define sqliteHashNext(E) ((E)->next)
|
||||
#define sqliteHashData(E) ((E)->data)
|
||||
#define sqliteHashKey(E) ((E)->pKey)
|
||||
#define sqliteHashKeysize(E) ((E)->nKey)
|
||||
/* #define sqliteHashKey(E) ((E)->pKey) // NOT USED */
|
||||
/* #define sqliteHashKeysize(E) ((E)->nKey) // NOT USED */
|
||||
|
||||
/*
|
||||
** Number of entries in a hash table
|
||||
*/
|
||||
#define sqliteHashCount(H) ((H)->count)
|
||||
/* #define sqliteHashCount(H) ((H)->count) // NOT USED */
|
||||
|
||||
#endif /* _SQLITE_HASH_H_ */
|
||||
|
||||
+89
-69
@@ -12,10 +12,30 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle INSERT statements in SQLite.
|
||||
**
|
||||
** $Id: insert.c,v 1.260 2009/02/28 10:47:42 danielk1977 Exp $
|
||||
** $Id: insert.c,v 1.267 2009/05/04 11:42:30 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** Generate code that will open a table for reading.
|
||||
*/
|
||||
void sqlite3OpenTable(
|
||||
Parse *p, /* Generate code into this VDBE */
|
||||
int iCur, /* The cursor number of the table */
|
||||
int iDb, /* The database index in sqlite3.aDb[] */
|
||||
Table *pTab, /* The table to be opened */
|
||||
int opcode /* OP_OpenRead or OP_OpenWrite */
|
||||
){
|
||||
Vdbe *v;
|
||||
if( IsVirtual(pTab) ) return;
|
||||
v = sqlite3GetVdbe(p);
|
||||
assert( opcode==OP_OpenWrite || opcode==OP_OpenRead );
|
||||
sqlite3TableLock(p, iDb, pTab->tnum, (opcode==OP_OpenWrite)?1:0, pTab->zName);
|
||||
sqlite3VdbeAddOp3(v, opcode, iCur, pTab->tnum, iDb);
|
||||
sqlite3VdbeChangeP4(v, -1, SQLITE_INT_TO_PTR(pTab->nCol), P4_INT32);
|
||||
VdbeComment((v, "%s", pTab->zName));
|
||||
}
|
||||
|
||||
/*
|
||||
** Set P4 of the most recently inserted opcode to a column affinity
|
||||
** string for index pIdx. A column affinity string has one character
|
||||
@@ -415,7 +435,7 @@ void sqlite3Insert(
|
||||
*/
|
||||
assert( pTabList->nSrc==1 );
|
||||
zTab = pTabList->a[0].zName;
|
||||
if( zTab==0 ) goto insert_cleanup;
|
||||
if( NEVER(zTab==0) ) goto insert_cleanup;
|
||||
pTab = sqlite3SrcListLookup(pParse, pTabList);
|
||||
if( pTab==0 ){
|
||||
goto insert_cleanup;
|
||||
@@ -537,7 +557,8 @@ void sqlite3Insert(
|
||||
|
||||
/* Resolve the expressions in the SELECT statement and execute it. */
|
||||
rc = sqlite3Select(pParse, pSelect, &dest);
|
||||
if( rc || pParse->nErr || db->mallocFailed ){
|
||||
assert( pParse->nErr==0 || rc );
|
||||
if( rc || NEVER(pParse->nErr) || db->mallocFailed ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 1, regEof); /* EOF <- 1 */
|
||||
@@ -623,7 +644,7 @@ void sqlite3Insert(
|
||||
if( pColumn==0 && nColumn && nColumn!=(pTab->nCol-nHidden) ){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"table %S has %d columns but %d values were supplied",
|
||||
pTabList, 0, pTab->nCol, nColumn);
|
||||
pTabList, 0, pTab->nCol-nHidden, nColumn);
|
||||
goto insert_cleanup;
|
||||
}
|
||||
if( pColumn!=0 && nColumn!=pColumn->nId ){
|
||||
@@ -761,12 +782,14 @@ void sqlite3Insert(
|
||||
regTrigRowid = sqlite3GetTempReg(pParse);
|
||||
if( keyColumn<0 ){
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, -1, regTrigRowid);
|
||||
}else if( useTempTable ){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, srcTab, keyColumn, regTrigRowid);
|
||||
}else{
|
||||
int j1;
|
||||
assert( pSelect==0 ); /* Otherwise useTempTable is true */
|
||||
sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr, regTrigRowid);
|
||||
if( useTempTable ){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, srcTab, keyColumn, regTrigRowid);
|
||||
}else{
|
||||
assert( pSelect==0 ); /* Otherwise useTempTable is true */
|
||||
sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr, regTrigRowid);
|
||||
}
|
||||
j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regTrigRowid);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, -1, regTrigRowid);
|
||||
sqlite3VdbeJumpHere(v, j1);
|
||||
@@ -840,7 +863,7 @@ void sqlite3Insert(
|
||||
VdbeOp *pOp;
|
||||
sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr, regRowid);
|
||||
pOp = sqlite3VdbeGetOp(v, sqlite3VdbeCurrentAddr(v) - 1);
|
||||
if( pOp && pOp->opcode==OP_Null && !IsVirtual(pTab) ){
|
||||
if( ALWAYS(pOp) && pOp->opcode==OP_Null && !IsVirtual(pTab) ){
|
||||
appendFlag = 1;
|
||||
pOp->opcode = OP_NewRowid;
|
||||
pOp->p1 = baseCur;
|
||||
@@ -920,27 +943,14 @@ void sqlite3Insert(
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
sqlite3GenerateConstraintChecks(
|
||||
pParse,
|
||||
pTab,
|
||||
baseCur,
|
||||
regIns,
|
||||
aRegIdx,
|
||||
keyColumn>=0,
|
||||
0,
|
||||
onError,
|
||||
endOfLoop
|
||||
int isReplace; /* Set to true if constraints may cause a replace */
|
||||
sqlite3GenerateConstraintChecks(pParse, pTab, baseCur, regIns, aRegIdx,
|
||||
keyColumn>=0, 0, onError, endOfLoop, &isReplace
|
||||
);
|
||||
sqlite3CompleteInsertion(
|
||||
pParse,
|
||||
pTab,
|
||||
baseCur,
|
||||
regIns,
|
||||
aRegIdx,
|
||||
0,
|
||||
(tmask&TRIGGER_AFTER) ? newIdx : -1,
|
||||
appendFlag
|
||||
);
|
||||
pParse, pTab, baseCur, regIns, aRegIdx, 0,
|
||||
(tmask&TRIGGER_AFTER) ? newIdx : -1, appendFlag, isReplace==0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1090,18 +1100,19 @@ void sqlite3GenerateConstraintChecks(
|
||||
int rowidChng, /* True if the rowid might collide with existing entry */
|
||||
int isUpdate, /* True for UPDATE, False for INSERT */
|
||||
int overrideError, /* Override onError to this if not OE_Default */
|
||||
int ignoreDest /* Jump to this label on an OE_Ignore resolution */
|
||||
int ignoreDest, /* Jump to this label on an OE_Ignore resolution */
|
||||
int *pbMayReplace /* OUT: Set to true if constraint may cause a replace */
|
||||
){
|
||||
int i;
|
||||
Vdbe *v;
|
||||
int nCol;
|
||||
int onError;
|
||||
int i; /* loop counter */
|
||||
Vdbe *v; /* VDBE under constrution */
|
||||
int nCol; /* Number of columns */
|
||||
int onError; /* Conflict resolution strategy */
|
||||
int j1; /* Addresss of jump instruction */
|
||||
int j2 = 0, j3; /* Addresses of jump instructions */
|
||||
int regData; /* Register containing first data column */
|
||||
int iCur;
|
||||
Index *pIdx;
|
||||
int seenReplace = 0;
|
||||
int iCur; /* Table cursor number */
|
||||
Index *pIdx; /* Pointer to one of the indices */
|
||||
int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */
|
||||
int hasTwoRowids = (isUpdate && rowidChng);
|
||||
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
@@ -1145,7 +1156,8 @@ void sqlite3GenerateConstraintChecks(
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, regData+i, ignoreDest);
|
||||
break;
|
||||
}
|
||||
case OE_Replace: {
|
||||
default: {
|
||||
assert( onError==OE_Replace );
|
||||
j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regData+i);
|
||||
sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regData+i);
|
||||
sqlite3VdbeJumpHere(v, j1);
|
||||
@@ -1242,11 +1254,13 @@ void sqlite3GenerateConstraintChecks(
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn+1, aRegIdx[iCur]);
|
||||
sqlite3IndexAffinityStr(v, pIdx);
|
||||
sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn+1);
|
||||
sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1);
|
||||
|
||||
/* Find out what action to take in case there is an indexing conflict */
|
||||
onError = pIdx->onError;
|
||||
if( onError==OE_None ) continue; /* pIdx is not a UNIQUE index */
|
||||
if( onError==OE_None ){
|
||||
sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1);
|
||||
continue; /* pIdx is not a UNIQUE index */
|
||||
}
|
||||
if( overrideError!=OE_Default ){
|
||||
onError = overrideError;
|
||||
}else if( onError==OE_Default ){
|
||||
@@ -1259,12 +1273,12 @@ void sqlite3GenerateConstraintChecks(
|
||||
|
||||
|
||||
/* Check to see if the new index entry will be unique */
|
||||
j2 = sqlite3VdbeAddOp3(v, OP_IsNull, regIdx, 0, pIdx->nColumn);
|
||||
regR = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, regRowid-hasTwoRowids, regR);
|
||||
j3 = sqlite3VdbeAddOp4(v, OP_IsUnique, baseCur+iCur+1, 0,
|
||||
regR, SQLITE_INT_TO_PTR(aRegIdx[iCur]),
|
||||
regR, SQLITE_INT_TO_PTR(regIdx),
|
||||
P4_INT32);
|
||||
sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1);
|
||||
|
||||
/* Generate code that executes if the new index entry is not unique */
|
||||
assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail
|
||||
@@ -1273,30 +1287,25 @@ void sqlite3GenerateConstraintChecks(
|
||||
case OE_Rollback:
|
||||
case OE_Abort:
|
||||
case OE_Fail: {
|
||||
int j, n1, n2;
|
||||
char zErrMsg[200];
|
||||
sqlite3_snprintf(ArraySize(zErrMsg), zErrMsg,
|
||||
pIdx->nColumn>1 ? "columns " : "column ");
|
||||
n1 = sqlite3Strlen30(zErrMsg);
|
||||
for(j=0; j<pIdx->nColumn && n1<ArraySize(zErrMsg)-30; j++){
|
||||
int j;
|
||||
StrAccum errMsg;
|
||||
const char *zSep;
|
||||
char *zErr;
|
||||
|
||||
sqlite3StrAccumInit(&errMsg, 0, 0, 200);
|
||||
errMsg.db = pParse->db;
|
||||
zSep = pIdx->nColumn>1 ? "columns " : "column ";
|
||||
for(j=0; j<pIdx->nColumn; j++){
|
||||
char *zCol = pTab->aCol[pIdx->aiColumn[j]].zName;
|
||||
n2 = sqlite3Strlen30(zCol);
|
||||
if( j>0 ){
|
||||
sqlite3_snprintf(ArraySize(zErrMsg)-n1, &zErrMsg[n1], ", ");
|
||||
n1 += 2;
|
||||
}
|
||||
if( n1+n2>ArraySize(zErrMsg)-30 ){
|
||||
sqlite3_snprintf(ArraySize(zErrMsg)-n1, &zErrMsg[n1], "...");
|
||||
n1 += 3;
|
||||
break;
|
||||
}else{
|
||||
sqlite3_snprintf(ArraySize(zErrMsg)-n1, &zErrMsg[n1], "%s", zCol);
|
||||
n1 += n2;
|
||||
}
|
||||
sqlite3StrAccumAppend(&errMsg, zSep, -1);
|
||||
zSep = ", ";
|
||||
sqlite3StrAccumAppend(&errMsg, zCol, -1);
|
||||
}
|
||||
sqlite3_snprintf(ArraySize(zErrMsg)-n1, &zErrMsg[n1],
|
||||
pIdx->nColumn>1 ? " are not unique" : " is not unique");
|
||||
sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, onError, 0, zErrMsg,0);
|
||||
sqlite3StrAccumAppend(&errMsg,
|
||||
pIdx->nColumn>1 ? " are not unique" : " is not unique", -1);
|
||||
zErr = sqlite3StrAccumFinish(&errMsg);
|
||||
sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, onError, 0, zErr, 0);
|
||||
sqlite3DbFree(errMsg.db, zErr);
|
||||
break;
|
||||
}
|
||||
case OE_Ignore: {
|
||||
@@ -1304,16 +1313,20 @@ void sqlite3GenerateConstraintChecks(
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
|
||||
break;
|
||||
}
|
||||
case OE_Replace: {
|
||||
default: {
|
||||
assert( onError==OE_Replace );
|
||||
sqlite3GenerateRowDelete(pParse, pTab, baseCur, regR, 0);
|
||||
seenReplace = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
sqlite3VdbeJumpHere(v, j2);
|
||||
sqlite3VdbeJumpHere(v, j3);
|
||||
sqlite3ReleaseTempReg(pParse, regR);
|
||||
}
|
||||
|
||||
if( pbMayReplace ){
|
||||
*pbMayReplace = seenReplace;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1333,7 +1346,8 @@ void sqlite3CompleteInsertion(
|
||||
int *aRegIdx, /* Register used by each index. 0 for unused indices */
|
||||
int isUpdate, /* True for UPDATE, False for INSERT */
|
||||
int newIdx, /* Index of NEW table for triggers. -1 if none */
|
||||
int appendBias /* True if this is likely to be an append */
|
||||
int appendBias, /* True if this is likely to be an append */
|
||||
int useSeekResult /* True to set the USESEEKRESULT flag on OP_[Idx]Insert */
|
||||
){
|
||||
int i;
|
||||
Vdbe *v;
|
||||
@@ -1350,6 +1364,9 @@ void sqlite3CompleteInsertion(
|
||||
for(i=nIdx-1; i>=0; i--){
|
||||
if( aRegIdx[i]==0 ) continue;
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, baseCur+i+1, aRegIdx[i]);
|
||||
if( useSeekResult ){
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
|
||||
}
|
||||
}
|
||||
regData = regRowid + 1;
|
||||
regRec = sqlite3GetTempReg(pParse);
|
||||
@@ -1370,6 +1387,9 @@ void sqlite3CompleteInsertion(
|
||||
if( appendBias ){
|
||||
pik_flags |= OPFLAG_APPEND;
|
||||
}
|
||||
if( useSeekResult ){
|
||||
pik_flags |= OPFLAG_USESEEKRESULT;
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, baseCur, regRec, regRowid);
|
||||
if( !pParse->nested ){
|
||||
sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_STATIC);
|
||||
@@ -1407,7 +1427,7 @@ int sqlite3OpenTableAndIndices(
|
||||
(char*)pKey, P4_KEYINFO_HANDOFF);
|
||||
VdbeComment((v, "%s", pIdx->zName));
|
||||
}
|
||||
if( pParse->nTab<=baseCur+i ){
|
||||
if( pParse->nTab<baseCur+i ){
|
||||
pParse->nTab = baseCur+i;
|
||||
}
|
||||
return i-1;
|
||||
@@ -1467,7 +1487,7 @@ static int xferCompatibleIndex(Index *pDest, Index *pSrc){
|
||||
if( pSrc->aSortOrder[i]!=pDest->aSortOrder[i] ){
|
||||
return 0; /* Different sort orders */
|
||||
}
|
||||
if( pSrc->azColl[i]!=pDest->azColl[i] ){
|
||||
if( !xferCompatibleCollation(pSrc->azColl[i],pDest->azColl[i]) ){
|
||||
return 0; /* Different collating sequences */
|
||||
}
|
||||
}
|
||||
@@ -1698,7 +1718,7 @@ static int xferOptimization(
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1);
|
||||
autoIncEnd(pParse, iDbDest, pDest, regAutoinc);
|
||||
for(pDestIdx=pDest->pIndex; pDestIdx; pDestIdx=pDestIdx->pNext){
|
||||
for(pSrcIdx=pSrc->pIndex; pSrcIdx; pSrcIdx=pSrcIdx->pNext){
|
||||
for(pSrcIdx=pSrc->pIndex; ALWAYS(pSrcIdx); pSrcIdx=pSrcIdx->pNext){
|
||||
if( xferCompatibleIndex(pDestIdx, pSrcIdx) ) break;
|
||||
}
|
||||
assert( pSrcIdx );
|
||||
|
||||
+15
-17
@@ -14,7 +14,7 @@
|
||||
** other files are for internal use by SQLite and should not be
|
||||
** accessed by users of the library.
|
||||
**
|
||||
** $Id: legacy.c,v 1.32 2009/03/19 18:51:07 danielk1977 Exp $
|
||||
** $Id: legacy.c,v 1.33 2009/05/05 20:02:48 drh Exp $
|
||||
*/
|
||||
|
||||
#include "sqliteInt.h"
|
||||
@@ -36,13 +36,12 @@ int sqlite3_exec(
|
||||
void *pArg, /* First argument to xCallback() */
|
||||
char **pzErrMsg /* Write error messages here */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
const char *zLeftover;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
char **azCols = 0;
|
||||
|
||||
int nRetry = 0;
|
||||
int nCallback;
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
const char *zLeftover; /* Tail of unprocessed SQL */
|
||||
sqlite3_stmt *pStmt = 0; /* The current SQL statement */
|
||||
char **azCols = 0; /* Names of result columns */
|
||||
int nRetry = 0; /* Number of retry attempts */
|
||||
int callbackIsInit; /* True if callback data is initialized */
|
||||
|
||||
if( zSql==0 ) zSql = "";
|
||||
|
||||
@@ -64,7 +63,7 @@ int sqlite3_exec(
|
||||
continue;
|
||||
}
|
||||
|
||||
nCallback = 0;
|
||||
callbackIsInit = 0;
|
||||
nCol = sqlite3_column_count(pStmt);
|
||||
|
||||
while( 1 ){
|
||||
@@ -73,13 +72,12 @@ int sqlite3_exec(
|
||||
|
||||
/* Invoke the callback function if required */
|
||||
if( xCallback && (SQLITE_ROW==rc ||
|
||||
(SQLITE_DONE==rc && !nCallback && db->flags&SQLITE_NullCallback)) ){
|
||||
if( 0==nCallback ){
|
||||
(SQLITE_DONE==rc && !callbackIsInit
|
||||
&& db->flags&SQLITE_NullCallback)) ){
|
||||
if( !callbackIsInit ){
|
||||
azCols = sqlite3DbMallocZero(db, 2*nCol*sizeof(const char*) + 1);
|
||||
if( azCols==0 ){
|
||||
azCols = sqlite3DbMallocZero(db, 2*nCol*sizeof(const char*) + 1);
|
||||
if( azCols==0 ){
|
||||
goto exec_out;
|
||||
}
|
||||
goto exec_out;
|
||||
}
|
||||
for(i=0; i<nCol; i++){
|
||||
azCols[i] = (char *)sqlite3_column_name(pStmt, i);
|
||||
@@ -87,7 +85,7 @@ int sqlite3_exec(
|
||||
** strings so there is no way for sqlite3_column_name() to fail. */
|
||||
assert( azCols[i]!=0 );
|
||||
}
|
||||
nCallback++;
|
||||
callbackIsInit = 1;
|
||||
}
|
||||
if( rc==SQLITE_ROW ){
|
||||
azVals = &azCols[nCol];
|
||||
@@ -129,7 +127,7 @@ exec_out:
|
||||
sqlite3DbFree(db, azCols);
|
||||
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
if( rc!=SQLITE_OK && rc==sqlite3_errcode(db) && pzErrMsg ){
|
||||
if( rc!=SQLITE_OK && ALWAYS(rc==sqlite3_errcode(db)) && pzErrMsg ){
|
||||
int nErrMsg = 1 + sqlite3Strlen30(sqlite3_errmsg(db));
|
||||
*pzErrMsg = sqlite3Malloc(nErrMsg);
|
||||
if( *pzErrMsg ){
|
||||
|
||||
+97
-76
@@ -14,7 +14,7 @@
|
||||
** other files are for internal use by SQLite and should not be
|
||||
** accessed by users of the library.
|
||||
**
|
||||
** $Id: main.c,v 1.536 2009/04/09 01:23:49 drh Exp $
|
||||
** $Id: main.c,v 1.548 2009/05/06 19:03:14 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -162,14 +162,16 @@ int sqlite3_initialize(void){
|
||||
sqlite3GlobalConfig.inProgress = 1;
|
||||
memset(pHash, 0, sizeof(sqlite3GlobalFunctions));
|
||||
sqlite3RegisterGlobalFunctions();
|
||||
rc = sqlite3_os_init();
|
||||
rc = sqlite3PcacheInitialize();
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_os_init();
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3PcacheInitialize();
|
||||
sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage,
|
||||
sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage);
|
||||
sqlite3GlobalConfig.isInit = 1;
|
||||
}
|
||||
sqlite3GlobalConfig.inProgress = 0;
|
||||
sqlite3GlobalConfig.isInit = (rc==SQLITE_OK ? 1 : 0);
|
||||
}
|
||||
sqlite3_mutex_leave(sqlite3GlobalConfig.pInitMutex);
|
||||
|
||||
@@ -211,18 +213,20 @@ int sqlite3_initialize(void){
|
||||
** Undo the effects of sqlite3_initialize(). Must not be called while
|
||||
** there are outstanding database connections or memory allocations or
|
||||
** while any part of SQLite is otherwise in use in any thread. This
|
||||
** routine is not threadsafe. Not by a long shot.
|
||||
** routine is not threadsafe. But it is safe to invoke this routine
|
||||
** on when SQLite is already shut down. If SQLite is already shut down
|
||||
** when this routine is invoked, then this routine is a harmless no-op.
|
||||
*/
|
||||
int sqlite3_shutdown(void){
|
||||
sqlite3GlobalConfig.isMallocInit = 0;
|
||||
sqlite3PcacheShutdown();
|
||||
if( sqlite3GlobalConfig.isInit ){
|
||||
sqlite3GlobalConfig.isMallocInit = 0;
|
||||
sqlite3PcacheShutdown();
|
||||
sqlite3_os_end();
|
||||
sqlite3_reset_auto_extension();
|
||||
sqlite3MallocEnd();
|
||||
sqlite3MutexEnd();
|
||||
sqlite3GlobalConfig.isInit = 0;
|
||||
}
|
||||
sqlite3_reset_auto_extension();
|
||||
sqlite3MallocEnd();
|
||||
sqlite3MutexEnd();
|
||||
sqlite3GlobalConfig.isInit = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -734,37 +738,41 @@ void sqlite3RollbackAll(sqlite3 *db){
|
||||
** argument.
|
||||
*/
|
||||
const char *sqlite3ErrStr(int rc){
|
||||
const char *z;
|
||||
switch( rc & 0xff ){
|
||||
case SQLITE_ROW:
|
||||
case SQLITE_DONE:
|
||||
case SQLITE_OK: z = "not an error"; break;
|
||||
case SQLITE_ERROR: z = "SQL logic error or missing database"; break;
|
||||
case SQLITE_PERM: z = "access permission denied"; break;
|
||||
case SQLITE_ABORT: z = "callback requested query abort"; break;
|
||||
case SQLITE_BUSY: z = "database is locked"; break;
|
||||
case SQLITE_LOCKED: z = "database table is locked"; break;
|
||||
case SQLITE_NOMEM: z = "out of memory"; break;
|
||||
case SQLITE_READONLY: z = "attempt to write a readonly database"; break;
|
||||
case SQLITE_INTERRUPT: z = "interrupted"; break;
|
||||
case SQLITE_IOERR: z = "disk I/O error"; break;
|
||||
case SQLITE_CORRUPT: z = "database disk image is malformed"; break;
|
||||
case SQLITE_FULL: z = "database or disk is full"; break;
|
||||
case SQLITE_CANTOPEN: z = "unable to open database file"; break;
|
||||
case SQLITE_EMPTY: z = "table contains no data"; break;
|
||||
case SQLITE_SCHEMA: z = "database schema has changed"; break;
|
||||
case SQLITE_TOOBIG: z = "String or BLOB exceeded size limit"; break;
|
||||
case SQLITE_CONSTRAINT: z = "constraint failed"; break;
|
||||
case SQLITE_MISMATCH: z = "datatype mismatch"; break;
|
||||
case SQLITE_MISUSE: z = "library routine called out of sequence";break;
|
||||
case SQLITE_NOLFS: z = "large file support is disabled"; break;
|
||||
case SQLITE_AUTH: z = "authorization denied"; break;
|
||||
case SQLITE_FORMAT: z = "auxiliary database format error"; break;
|
||||
case SQLITE_RANGE: z = "bind or column index out of range"; break;
|
||||
case SQLITE_NOTADB: z = "file is encrypted or is not a database";break;
|
||||
default: z = "unknown error"; break;
|
||||
static const char* const aMsg[] = {
|
||||
/* SQLITE_OK */ "not an error",
|
||||
/* SQLITE_ERROR */ "SQL logic error or missing database",
|
||||
/* SQLITE_INTERNAL */ 0,
|
||||
/* SQLITE_PERM */ "access permission denied",
|
||||
/* SQLITE_ABORT */ "callback requested query abort",
|
||||
/* SQLITE_BUSY */ "database is locked",
|
||||
/* SQLITE_LOCKED */ "database table is locked",
|
||||
/* SQLITE_NOMEM */ "out of memory",
|
||||
/* SQLITE_READONLY */ "attempt to write a readonly database",
|
||||
/* SQLITE_INTERRUPT */ "interrupted",
|
||||
/* SQLITE_IOERR */ "disk I/O error",
|
||||
/* SQLITE_CORRUPT */ "database disk image is malformed",
|
||||
/* SQLITE_NOTFOUND */ 0,
|
||||
/* SQLITE_FULL */ "database or disk is full",
|
||||
/* SQLITE_CANTOPEN */ "unable to open database file",
|
||||
/* SQLITE_PROTOCOL */ 0,
|
||||
/* SQLITE_EMPTY */ "table contains no data",
|
||||
/* SQLITE_SCHEMA */ "database schema has changed",
|
||||
/* SQLITE_TOOBIG */ "String or BLOB exceeded size limit",
|
||||
/* SQLITE_CONSTRAINT */ "constraint failed",
|
||||
/* SQLITE_MISMATCH */ "datatype mismatch",
|
||||
/* SQLITE_MISUSE */ "library routine called out of sequence",
|
||||
/* SQLITE_NOLFS */ "large file support is disabled",
|
||||
/* SQLITE_AUTH */ "authorization denied",
|
||||
/* SQLITE_FORMAT */ "auxiliary database format error",
|
||||
/* SQLITE_RANGE */ "bind or column index out of range",
|
||||
/* SQLITE_NOTADB */ "file is encrypted or is not a database",
|
||||
};
|
||||
rc &= 0xff;
|
||||
if( ALWAYS(rc>=0) && rc<(int)(sizeof(aMsg)/sizeof(aMsg[0])) && aMsg[rc]!=0 ){
|
||||
return aMsg[rc];
|
||||
}else{
|
||||
return "unknown error";
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -922,7 +930,7 @@ int sqlite3CreateFunc(
|
||||
(!xFunc && (xFinal && !xStep)) ||
|
||||
(!xFunc && (!xFinal && xStep)) ||
|
||||
(nArg<-1 || nArg>SQLITE_MAX_FUNCTION_ARG) ||
|
||||
(255<(nName = sqlite3Strlen(db, zFunctionName))) ){
|
||||
(255<(nName = sqlite3Strlen30( zFunctionName))) ){
|
||||
sqlite3Error(db, SQLITE_ERROR, "bad parameters");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
@@ -1048,7 +1056,7 @@ int sqlite3_overload_function(
|
||||
const char *zName,
|
||||
int nArg
|
||||
){
|
||||
int nName = sqlite3Strlen(db, zName);
|
||||
int nName = sqlite3Strlen30(zName);
|
||||
int rc;
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
if( sqlite3FindFunction(db, zName, nName, nArg, SQLITE_UTF8, 0)==0 ){
|
||||
@@ -1157,6 +1165,39 @@ void *sqlite3_rollback_hook(
|
||||
return pRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function returns true if main-memory should be used instead of
|
||||
** a temporary file for transient pager files and statement journals.
|
||||
** The value returned depends on the value of db->temp_store (runtime
|
||||
** parameter) and the compile time value of SQLITE_TEMP_STORE. The
|
||||
** following table describes the relationship between these two values
|
||||
** and this functions return value.
|
||||
**
|
||||
** SQLITE_TEMP_STORE db->temp_store Location of temporary database
|
||||
** ----------------- -------------- ------------------------------
|
||||
** 0 any file (return 0)
|
||||
** 1 1 file (return 0)
|
||||
** 1 2 memory (return 1)
|
||||
** 1 0 file (return 0)
|
||||
** 2 1 file (return 0)
|
||||
** 2 2 memory (return 1)
|
||||
** 2 0 memory (return 1)
|
||||
** 3 any memory (return 1)
|
||||
*/
|
||||
int sqlite3TempInMemory(const sqlite3 *db){
|
||||
#if SQLITE_TEMP_STORE==1
|
||||
return ( db->temp_store==2 );
|
||||
#endif
|
||||
#if SQLITE_TEMP_STORE==2
|
||||
return ( db->temp_store!=1 );
|
||||
#endif
|
||||
#if SQLITE_TEMP_STORE==3
|
||||
return 1;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is called to create a connection to a database BTree
|
||||
** driver. If zFilename is the name of a file, then that file is
|
||||
@@ -1167,20 +1208,8 @@ void *sqlite3_rollback_hook(
|
||||
** soon as the connection is closed.
|
||||
**
|
||||
** A virtual database can be either a disk file (that is automatically
|
||||
** deleted when the file is closed) or it an be held entirely in memory,
|
||||
** depending on the values of the SQLITE_TEMP_STORE compile-time macro and the
|
||||
** db->temp_store variable, according to the following chart:
|
||||
**
|
||||
** SQLITE_TEMP_STORE db->temp_store Location of temporary database
|
||||
** ----------------- -------------- ------------------------------
|
||||
** 0 any file
|
||||
** 1 1 file
|
||||
** 1 2 memory
|
||||
** 1 0 file
|
||||
** 2 1 file
|
||||
** 2 2 memory
|
||||
** 2 0 memory
|
||||
** 3 any memory
|
||||
** deleted when the file is closed) or it an be held entirely in memory.
|
||||
** The sqlite3TempInMemory() function is used to determine which.
|
||||
*/
|
||||
int sqlite3BtreeFactory(
|
||||
const sqlite3 *db, /* Main database when opening aux otherwise 0 */
|
||||
@@ -1201,22 +1230,11 @@ int sqlite3BtreeFactory(
|
||||
if( db->flags & SQLITE_NoReadlock ){
|
||||
btFlags |= BTREE_NO_READLOCK;
|
||||
}
|
||||
if( zFilename==0 ){
|
||||
#if SQLITE_TEMP_STORE==0
|
||||
/* Do nothing */
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_MEMORYDB
|
||||
#if SQLITE_TEMP_STORE==1
|
||||
if( db->temp_store==2 ) zFilename = ":memory:";
|
||||
#endif
|
||||
#if SQLITE_TEMP_STORE==2
|
||||
if( db->temp_store!=1 ) zFilename = ":memory:";
|
||||
#endif
|
||||
#if SQLITE_TEMP_STORE==3
|
||||
if( zFilename==0 && sqlite3TempInMemory(db) ){
|
||||
zFilename = ":memory:";
|
||||
#endif
|
||||
#endif /* SQLITE_OMIT_MEMORYDB */
|
||||
}
|
||||
#endif
|
||||
|
||||
if( (vfsFlags & SQLITE_OPEN_MAIN_DB)!=0 && (zFilename==0 || *zFilename==0) ){
|
||||
vfsFlags = (vfsFlags & ~SQLITE_OPEN_MAIN_DB) | SQLITE_OPEN_TEMP_DB;
|
||||
@@ -1352,11 +1370,13 @@ static int createCollation(
|
||||
** to one of SQLITE_UTF16LE or SQLITE_UTF16BE using the
|
||||
** SQLITE_UTF16NATIVE macro. SQLITE_UTF16 is not used internally.
|
||||
*/
|
||||
enc2 = enc & ~SQLITE_UTF16_ALIGNED;
|
||||
if( enc2==SQLITE_UTF16 ){
|
||||
enc2 = enc;
|
||||
testcase( enc2==SQLITE_UTF16 );
|
||||
testcase( enc2==SQLITE_UTF16_ALIGNED );
|
||||
if( enc2==SQLITE_UTF16 || enc2==SQLITE_UTF16_ALIGNED ){
|
||||
enc2 = SQLITE_UTF16NATIVE;
|
||||
}
|
||||
if( (enc2&~3)!=0 ){
|
||||
if( enc2<SQLITE_UTF8 || enc2>SQLITE_UTF16BE ){
|
||||
return SQLITE_MISUSE;
|
||||
}
|
||||
|
||||
@@ -1364,7 +1384,7 @@ static int createCollation(
|
||||
** sequence. If so, and there are active VMs, return busy. If there
|
||||
** are no active VMs, invalidate any pre-compiled statements.
|
||||
*/
|
||||
nName = sqlite3Strlen(db, zName);
|
||||
nName = sqlite3Strlen30(zName);
|
||||
pColl = sqlite3FindCollSeq(db, (u8)enc2, zName, nName, 0);
|
||||
if( pColl && pColl->xCmp ){
|
||||
if( db->activeVdbeCnt ){
|
||||
@@ -1501,6 +1521,7 @@ static int openDatabase(
|
||||
CollSeq *pColl;
|
||||
int isThreadsafe;
|
||||
|
||||
*ppDb = 0;
|
||||
#ifndef SQLITE_OMIT_AUTOINIT
|
||||
rc = sqlite3_initialize();
|
||||
if( rc ) return rc;
|
||||
@@ -1560,9 +1581,9 @@ static int openDatabase(
|
||||
| SQLITE_LoadExtension
|
||||
#endif
|
||||
;
|
||||
sqlite3HashInit(&db->aCollSeq, 0);
|
||||
sqlite3HashInit(&db->aCollSeq);
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
sqlite3HashInit(&db->aModule, 0);
|
||||
sqlite3HashInit(&db->aModule);
|
||||
#endif
|
||||
|
||||
db->pVfs = sqlite3_vfs_find(zVfs);
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@
|
||||
**
|
||||
** Memory allocation functions used throughout sqlite.
|
||||
**
|
||||
** $Id: malloc.c,v 1.61 2009/03/24 15:08:10 drh Exp $
|
||||
** $Id: malloc.c,v 1.62 2009/05/03 20:23:54 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <stdarg.h>
|
||||
@@ -651,7 +651,7 @@ char *sqlite3DbStrDup(sqlite3 *db, const char *z){
|
||||
if( z==0 ){
|
||||
return 0;
|
||||
}
|
||||
n = (db ? sqlite3Strlen(db, z) : sqlite3Strlen30(z))+1;
|
||||
n = sqlite3Strlen30(z) + 1;
|
||||
assert( (n&0x7fffffff)==n );
|
||||
zNew = sqlite3DbMallocRaw(db, (int)n);
|
||||
if( zNew ){
|
||||
|
||||
+2
-2
@@ -14,7 +14,7 @@
|
||||
** The in-memory rollback journal is used to journal transactions for
|
||||
** ":memory:" databases and when the journal_mode=MEMORY pragma is used.
|
||||
**
|
||||
** @(#) $Id: memjournal.c,v 1.11 2009/04/05 12:22:09 drh Exp $
|
||||
** @(#) $Id: memjournal.c,v 1.12 2009/05/04 11:42:30 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -129,7 +129,7 @@ static int memjrnlWrite(
|
||||
/* An in-memory journal file should only ever be appended to. Random
|
||||
** access writes are not required by sqlite.
|
||||
*/
|
||||
assert(iOfst==p->endpoint.iOffset);
|
||||
assert( iOfst==p->endpoint.iOffset );
|
||||
UNUSED_PARAMETER(iOfst);
|
||||
|
||||
while( nWrite>0 ){
|
||||
|
||||
+20
-17
@@ -12,7 +12,7 @@
|
||||
**
|
||||
** This file contains code that is specific to windows.
|
||||
**
|
||||
** $Id: os_win.c,v 1.154 2009/04/09 14:27:07 chw Exp $
|
||||
** $Id: os_win.c,v 1.156 2009/04/23 19:08:33 shane Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#if SQLITE_OS_WIN /* This file is used for windows only */
|
||||
@@ -1312,16 +1312,23 @@ static int winOpen(
|
||||
}else{
|
||||
dwDesiredAccess = GENERIC_READ;
|
||||
}
|
||||
if( flags & SQLITE_OPEN_CREATE ){
|
||||
/* SQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is
|
||||
** created. SQLite doesn't use it to indicate "exclusive access"
|
||||
** as it is usually understood.
|
||||
*/
|
||||
assert(!(flags & SQLITE_OPEN_EXCLUSIVE) || (flags & SQLITE_OPEN_CREATE));
|
||||
if( flags & SQLITE_OPEN_EXCLUSIVE ){
|
||||
/* Creates a new file, only if it does not already exist. */
|
||||
/* If the file exists, it fails. */
|
||||
dwCreationDisposition = CREATE_NEW;
|
||||
}else if( flags & SQLITE_OPEN_CREATE ){
|
||||
/* Open existing file, or create if it doesn't exist */
|
||||
dwCreationDisposition = OPEN_ALWAYS;
|
||||
}else{
|
||||
/* Opens a file, only if it exists. */
|
||||
dwCreationDisposition = OPEN_EXISTING;
|
||||
}
|
||||
if( flags & SQLITE_OPEN_MAIN_DB ){
|
||||
dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||||
}else{
|
||||
dwShareMode = 0;
|
||||
}
|
||||
dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||||
if( flags & SQLITE_OPEN_DELETEONCLOSE ){
|
||||
#if SQLITE_OS_WINCE
|
||||
dwFlagsAndAttributes = FILE_ATTRIBUTE_HIDDEN;
|
||||
@@ -1600,13 +1607,11 @@ static int getSectorSize(
|
||||
void *zConverted = convertUtf8Filename(zFullpath);
|
||||
if( zConverted ){
|
||||
if( isNT() ){
|
||||
int i;
|
||||
/* trim path to just drive reference */
|
||||
WCHAR *p = zConverted;
|
||||
for(i=0;i<MAX_PATH;i++){
|
||||
if( p[i] == '\\' ){
|
||||
i++;
|
||||
p[i] = '\0';
|
||||
for(;*p;p++){
|
||||
if( *p == '\\' ){
|
||||
*p = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1617,13 +1622,11 @@ static int getSectorSize(
|
||||
&dwDummy);
|
||||
#if SQLITE_OS_WINCE==0
|
||||
}else{
|
||||
int i;
|
||||
/* trim path to just drive reference */
|
||||
CHAR *p = (CHAR *)zConverted;
|
||||
for(i=0;i<MAX_PATH;i++){
|
||||
if( p[i] == '\\' ){
|
||||
i++;
|
||||
p[i] = '\0';
|
||||
for(;*p;p++){
|
||||
if( *p == '\\' ){
|
||||
*p = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+52
-15
@@ -18,7 +18,7 @@
|
||||
** file simultaneously, or one process from reading the database while
|
||||
** another is writing.
|
||||
**
|
||||
** @(#) $Id: pager.c,v 1.580 2009/04/11 16:27:50 drh Exp $
|
||||
** @(#) $Id: pager.c,v 1.586 2009/05/06 18:57:10 shane Exp $
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_DISKIO
|
||||
#include "sqliteInt.h"
|
||||
@@ -229,6 +229,12 @@ struct PagerSavepoint {
|
||||
** TODO: It might be easier to set this variable in writeJournalHdr()
|
||||
** and writeMasterJournal() only. Change its meaning to "unsynced data
|
||||
** has been written to the journal".
|
||||
**
|
||||
** subjInMemory
|
||||
**
|
||||
** This is a boolean variable. If true, then any required sub-journal
|
||||
** is opened as an in-memory journal file. If false, then in-memory
|
||||
** sub-journals are only used for in-memory pager files.
|
||||
*/
|
||||
struct Pager {
|
||||
sqlite3_vfs *pVfs; /* OS functions to use for IO */
|
||||
@@ -262,6 +268,7 @@ struct Pager {
|
||||
u8 setMaster; /* True if a m-j name has been written to jrnl */
|
||||
u8 doNotSync; /* Boolean. While true, do not spill the cache */
|
||||
u8 dbSizeValid; /* Set when dbSize is correct */
|
||||
u8 subjInMemory; /* True to use in-memory sub-journals */
|
||||
Pgno dbSize; /* Number of pages in the database */
|
||||
Pgno dbOrigSize; /* dbSize before the current transaction */
|
||||
Pgno dbFileSize; /* Number of pages in the database file */
|
||||
@@ -1041,6 +1048,7 @@ static void pager_reset(Pager *pPager){
|
||||
if( SQLITE_OK==pPager->errCode ){
|
||||
sqlite3BackupRestart(pPager->pBackup);
|
||||
sqlite3PcacheClear(pPager->pPCache);
|
||||
pPager->dbSizeValid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1054,7 +1062,7 @@ static void releaseAllSavepoints(Pager *pPager){
|
||||
for(ii=0; ii<pPager->nSavepoint; ii++){
|
||||
sqlite3BitvecDestroy(pPager->aSavepoint[ii].pInSavepoint);
|
||||
}
|
||||
if( !pPager->exclusiveMode ){
|
||||
if( !pPager->exclusiveMode || sqlite3IsMemJournal(pPager->sjfd) ){
|
||||
sqlite3OsClose(pPager->sjfd);
|
||||
}
|
||||
sqlite3_free(pPager->aSavepoint);
|
||||
@@ -1290,7 +1298,11 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){
|
||||
rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
|
||||
}
|
||||
}else if( pPager->journalMode==PAGER_JOURNALMODE_TRUNCATE ){
|
||||
rc = sqlite3OsTruncate(pPager->jfd, 0);
|
||||
if( pPager->journalOff==0 ){
|
||||
rc = SQLITE_OK;
|
||||
}else{
|
||||
rc = sqlite3OsTruncate(pPager->jfd, 0);
|
||||
}
|
||||
pPager->journalOff = 0;
|
||||
pPager->journalStarted = 0;
|
||||
}else if( pPager->exclusiveMode
|
||||
@@ -3179,7 +3191,7 @@ int sqlite3PagerOpen(
|
||||
nPathname + 1 + /* zFilename */
|
||||
nPathname + 8 + 1 /* zJournal */
|
||||
);
|
||||
assert( EIGHT_BYTE_ALIGNMENT(journalFileSize) );
|
||||
assert( EIGHT_BYTE_ALIGNMENT(SQLITE_INT_TO_PTR(journalFileSize)) );
|
||||
if( !pPtr ){
|
||||
sqlite3_free(zPathname);
|
||||
return SQLITE_NOMEM;
|
||||
@@ -3664,9 +3676,15 @@ static int pagerSharedLock(Pager *pPager){
|
||||
/*
|
||||
** If the reference count has reached zero, rollback any active
|
||||
** transaction and unlock the pager.
|
||||
**
|
||||
** Except, in locking_mode=EXCLUSIVE when there is nothing to in
|
||||
** the rollback journal, the unlock is not performed and there is
|
||||
** nothing to rollback, so this routine is a no-op.
|
||||
*/
|
||||
static void pagerUnlockIfUnused(Pager *pPager){
|
||||
if( sqlite3PcacheRefCount(pPager->pPCache)==0 ){
|
||||
if( (sqlite3PcacheRefCount(pPager->pPCache)==0)
|
||||
&& (!pPager->exclusiveMode || pPager->journalOff>0)
|
||||
){
|
||||
pagerUnlockAndRollback(pPager);
|
||||
}
|
||||
}
|
||||
@@ -3894,7 +3912,7 @@ void sqlite3PagerUnref(DbPage *pPg){
|
||||
static int openSubJournal(Pager *pPager){
|
||||
int rc = SQLITE_OK;
|
||||
if( isOpen(pPager->jfd) && !isOpen(pPager->sjfd) ){
|
||||
if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){
|
||||
if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY || pPager->subjInMemory ){
|
||||
sqlite3MemJournalOpen(pPager->sjfd);
|
||||
}else{
|
||||
rc = pagerOpentemp(pPager, pPager->sjfd, SQLITE_OPEN_SUBJOURNAL);
|
||||
@@ -4013,10 +4031,19 @@ static int pager_open_journal(Pager *pPager){
|
||||
**
|
||||
** If the journal file is opened (or if it is already open), then a
|
||||
** journal-header is written to the start of it.
|
||||
**
|
||||
** If the subjInMemory argument is non-zero, then any sub-journal opened
|
||||
** within this transaction will be opened as an in-memory file. This
|
||||
** has no effect if the sub-journal is already opened (as it may be when
|
||||
** running in exclusive mode) or if the transaction does not require a
|
||||
** sub-journal. If the subjInMemory argument is zero, then any required
|
||||
** sub-journal is implemented in-memory if pPager is an in-memory database,
|
||||
** or using a temporary file otherwise.
|
||||
*/
|
||||
int sqlite3PagerBegin(Pager *pPager, int exFlag){
|
||||
int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){
|
||||
int rc = SQLITE_OK;
|
||||
assert( pPager->state!=PAGER_UNLOCK );
|
||||
pPager->subjInMemory = (u8)subjInMemory;
|
||||
if( pPager->state==PAGER_SHARED ){
|
||||
assert( pPager->pInJournal==0 );
|
||||
assert( !MEMDB && !pPager->tempFile );
|
||||
@@ -4101,7 +4128,7 @@ static int pager_write(PgHdr *pPg){
|
||||
** create it if it does not.
|
||||
*/
|
||||
assert( pPager->state!=PAGER_UNLOCK );
|
||||
rc = sqlite3PagerBegin(pPager, 0);
|
||||
rc = sqlite3PagerBegin(pPager, 0, pPager->subjInMemory);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
@@ -5184,11 +5211,14 @@ int sqlite3PagerLockingMode(Pager *pPager, int eMode){
|
||||
** PAGER_JOURNALMODE_OFF
|
||||
** PAGER_JOURNALMODE_MEMORY
|
||||
**
|
||||
** If the parameter is not _QUERY, then the journal-mode is set to the
|
||||
** value specified. Except, an in-memory database can only have its
|
||||
** journal mode set to _OFF or _MEMORY. Attempts to change the journal
|
||||
** mode of an in-memory database to something other than _OFF or _MEMORY
|
||||
** are silently ignored.
|
||||
** If the parameter is not _QUERY, then the journal_mode is set to the
|
||||
** value specified if the change is allowed. The change is disallowed
|
||||
** for the following reasons:
|
||||
**
|
||||
** * An in-memory database can only have its journal_mode set to _OFF
|
||||
** or _MEMORY.
|
||||
**
|
||||
** * The journal mode may not be changed while a transaction is active.
|
||||
**
|
||||
** The returned indicate the current (possibly updated) journal-mode.
|
||||
*/
|
||||
@@ -5200,8 +5230,15 @@ int sqlite3PagerJournalMode(Pager *pPager, int eMode){
|
||||
|| eMode==PAGER_JOURNALMODE_OFF
|
||||
|| eMode==PAGER_JOURNALMODE_MEMORY );
|
||||
assert( PAGER_JOURNALMODE_QUERY<0 );
|
||||
if( eMode>=0 && (!MEMDB || eMode==PAGER_JOURNALMODE_MEMORY
|
||||
|| eMode==PAGER_JOURNALMODE_OFF) ){
|
||||
if( eMode>=0
|
||||
&& (!MEMDB || eMode==PAGER_JOURNALMODE_MEMORY
|
||||
|| eMode==PAGER_JOURNALMODE_OFF)
|
||||
&& !pPager->dbModified
|
||||
&& (!isOpen(pPager->jfd) || 0==pPager->journalOff)
|
||||
){
|
||||
if( isOpen(pPager->jfd) ){
|
||||
sqlite3OsClose(pPager->jfd);
|
||||
}
|
||||
pPager->journalMode = (u8)eMode;
|
||||
}
|
||||
return (int)pPager->journalMode;
|
||||
|
||||
+2
-2
@@ -13,7 +13,7 @@
|
||||
** subsystem. The page cache subsystem reads and writes a file a page
|
||||
** at a time and provides a journal for rollback.
|
||||
**
|
||||
** @(#) $Id: pager.h,v 1.100 2009/02/03 16:51:25 danielk1977 Exp $
|
||||
** @(#) $Id: pager.h,v 1.101 2009/04/30 09:10:38 danielk1977 Exp $
|
||||
*/
|
||||
|
||||
#ifndef _PAGER_H_
|
||||
@@ -119,7 +119,7 @@ void *sqlite3PagerGetExtra(DbPage *);
|
||||
|
||||
/* Functions used to manage pager transactions and savepoints. */
|
||||
int sqlite3PagerPagecount(Pager*, int*);
|
||||
int sqlite3PagerBegin(Pager*, int exFlag);
|
||||
int sqlite3PagerBegin(Pager*, int exFlag, int);
|
||||
int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int);
|
||||
int sqlite3PagerSync(Pager *pPager);
|
||||
int sqlite3PagerCommitPhaseTwo(Pager*);
|
||||
|
||||
+3
-1
@@ -14,7 +14,7 @@
|
||||
** the parser. Lemon will also generate a header file containing
|
||||
** numeric codes for all of the tokens.
|
||||
**
|
||||
** @(#) $Id: parse.y,v 1.274 2009/04/06 14:16:43 drh Exp $
|
||||
** @(#) $Id: parse.y,v 1.275 2009/05/01 21:13:37 drh Exp $
|
||||
*/
|
||||
|
||||
// All token codes are small integers with #defines that begin with "TK_"
|
||||
@@ -166,6 +166,8 @@ columnlist ::= column.
|
||||
column(A) ::= columnid(X) type carglist. {
|
||||
A.z = X.z;
|
||||
A.n = (int)(pParse->sLastToken.z-X.z) + pParse->sLastToken.n;
|
||||
A.quoted = 0;
|
||||
A.dyn = 0;
|
||||
}
|
||||
columnid(A) ::= nm(X). {
|
||||
sqlite3AddColumn(pParse,&X);
|
||||
|
||||
+2
-2
@@ -16,7 +16,7 @@
|
||||
** If the default page cache implementation is overriden, then neither of
|
||||
** these two features are available.
|
||||
**
|
||||
** @(#) $Id: pcache1.c,v 1.10 2009/03/23 04:33:33 danielk1977 Exp $
|
||||
** @(#) $Id: pcache1.c,v 1.11 2009/04/14 18:44:39 aswift Exp $
|
||||
*/
|
||||
|
||||
#include "sqliteInt.h"
|
||||
@@ -527,7 +527,7 @@ static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){
|
||||
|
||||
/* Step 4. Try to recycle a page buffer if appropriate. */
|
||||
if( pCache->bPurgeable && pcache1.pLruTail && (
|
||||
pCache->nPage>=pCache->nMax-1 || pcache1.nCurrentPage>=pcache1.nMaxPage
|
||||
(pCache->nPage+1>=pCache->nMax) || pcache1.nCurrentPage>=pcache1.nMaxPage
|
||||
)){
|
||||
pPage = pcache1.pLruTail;
|
||||
pcache1RemoveFromHash(pPage);
|
||||
|
||||
+9
-12
@@ -13,7 +13,7 @@
|
||||
** interface, and routines that contribute to loading the database schema
|
||||
** from disk.
|
||||
**
|
||||
** $Id: prepare.c,v 1.116 2009/04/02 18:32:27 drh Exp $
|
||||
** $Id: prepare.c,v 1.117 2009/04/20 17:43:03 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -127,6 +127,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
||||
*/
|
||||
static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
int rc;
|
||||
int i;
|
||||
BtCursor *curMain;
|
||||
int size;
|
||||
Table *pTab;
|
||||
@@ -216,7 +217,8 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
}
|
||||
sqlite3BtreeEnter(pDb->pBt);
|
||||
rc = sqlite3BtreeCursor(pDb->pBt, MASTER_ROOT, 0, 0, curMain);
|
||||
if( rc!=SQLITE_OK && rc!=SQLITE_EMPTY ){
|
||||
if( rc==SQLITE_EMPTY ) rc = SQLITE_OK;
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3SetString(pzErrMsg, db, "%s", sqlite3ErrStr(rc));
|
||||
goto initone_error_out;
|
||||
}
|
||||
@@ -238,17 +240,12 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
** Note: The #defined SQLITE_UTF* symbols in sqliteInt.h correspond to
|
||||
** the possible values of meta[4].
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
int i;
|
||||
for(i=0; i<ArraySize(meta); i++){
|
||||
rc = sqlite3BtreeGetMeta(pDb->pBt, i+1, (u32 *)&meta[i]);
|
||||
if( rc ){
|
||||
sqlite3SetString(pzErrMsg, db, "%s", sqlite3ErrStr(rc));
|
||||
goto initone_error_out;
|
||||
}
|
||||
for(i=0; i<ArraySize(meta); i++){
|
||||
rc = sqlite3BtreeGetMeta(pDb->pBt, i+1, (u32 *)&meta[i]);
|
||||
if( rc ){
|
||||
sqlite3SetString(pzErrMsg, db, "%s", sqlite3ErrStr(rc));
|
||||
goto initone_error_out;
|
||||
}
|
||||
}else{
|
||||
memset(meta, 0, sizeof(meta));
|
||||
}
|
||||
pDb->pSchema->schema_cookie = meta[0];
|
||||
|
||||
|
||||
+15
-7
@@ -5,7 +5,7 @@
|
||||
** an historical reference. Most of the "enhancements" have been backed
|
||||
** out so that the functionality is now the same as standard printf().
|
||||
**
|
||||
** $Id: printf.c,v 1.102 2009/04/08 16:10:04 drh Exp $
|
||||
** $Id: printf.c,v 1.103 2009/05/04 20:20:16 drh Exp $
|
||||
**
|
||||
**************************************************************************
|
||||
**
|
||||
@@ -391,9 +391,13 @@ void sqlite3VXPrintf(
|
||||
case etRADIX:
|
||||
if( infop->flags & FLAG_SIGNED ){
|
||||
i64 v;
|
||||
if( flag_longlong ) v = va_arg(ap,i64);
|
||||
else if( flag_long ) v = va_arg(ap,long int);
|
||||
else v = va_arg(ap,int);
|
||||
if( flag_longlong ){
|
||||
v = va_arg(ap,i64);
|
||||
}else if( flag_long ){
|
||||
v = va_arg(ap,long int);
|
||||
}else{
|
||||
v = va_arg(ap,int);
|
||||
}
|
||||
if( v<0 ){
|
||||
longvalue = -v;
|
||||
prefix = '-';
|
||||
@@ -404,9 +408,13 @@ void sqlite3VXPrintf(
|
||||
else prefix = 0;
|
||||
}
|
||||
}else{
|
||||
if( flag_longlong ) longvalue = va_arg(ap,u64);
|
||||
else if( flag_long ) longvalue = va_arg(ap,unsigned long int);
|
||||
else longvalue = va_arg(ap,unsigned int);
|
||||
if( flag_longlong ){
|
||||
longvalue = va_arg(ap,u64);
|
||||
}else if( flag_long ){
|
||||
longvalue = va_arg(ap,unsigned long int);
|
||||
}else{
|
||||
longvalue = va_arg(ap,unsigned int);
|
||||
}
|
||||
prefix = 0;
|
||||
}
|
||||
if( longvalue==0 ) flag_alternateform = 0;
|
||||
|
||||
+7
-3
@@ -14,7 +14,7 @@
|
||||
** resolve all identifiers by associating them with a particular
|
||||
** table and column.
|
||||
**
|
||||
** $Id: resolve.c,v 1.20 2009/03/05 04:23:47 shane Exp $
|
||||
** $Id: resolve.c,v 1.22 2009/05/05 15:46:43 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <stdlib.h>
|
||||
@@ -248,7 +248,11 @@ static int lookupName(
|
||||
if( iCol>=0 ){
|
||||
testcase( iCol==31 );
|
||||
testcase( iCol==32 );
|
||||
*piColMask |= ((u32)1<<iCol) | (iCol>=32?0xffffffff:0);
|
||||
if( iCol>=32 ){
|
||||
*piColMask = 0xffffffff;
|
||||
}else{
|
||||
*piColMask |= ((u32)1)<<iCol;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -319,7 +323,7 @@ static int lookupName(
|
||||
** Because no reference was made to outer contexts, the pNC->nRef
|
||||
** fields are not changed in any context.
|
||||
*/
|
||||
if( cnt==0 && zTab==0 && pColumnToken->z[0]=='"' ){
|
||||
if( cnt==0 && zTab==0 && ExprHasProperty(pExpr,EP_DblQuoted) ){
|
||||
sqlite3DbFree(db, zCol);
|
||||
pExpr->op = TK_STRING;
|
||||
pExpr->pTab = 0;
|
||||
|
||||
+242
-56
@@ -10,47 +10,89 @@
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This module implements an object we call a "Row Set".
|
||||
** This module implements an object we call a "RowSet".
|
||||
**
|
||||
** The RowSet object is a bag of rowids. Rowids
|
||||
** are inserted into the bag in an arbitrary order. Then they are
|
||||
** pulled from the bag in sorted order. Rowids only appear in the
|
||||
** bag once. If the same rowid is inserted multiple times, the
|
||||
** second and subsequent inserts make no difference on the output.
|
||||
** The RowSet object is a collection of rowids. Rowids
|
||||
** are inserted into the RowSet in an arbitrary order. Inserts
|
||||
** can be intermixed with tests to see if a given rowid has been
|
||||
** previously inserted into the RowSet.
|
||||
**
|
||||
** This implementation accumulates rowids in a linked list. For
|
||||
** output, it first sorts the linked list (removing duplicates during
|
||||
** the sort) then returns elements one by one by walking the list.
|
||||
** After all inserts are finished, it is possible to extract the
|
||||
** elements of the RowSet in sorted order. Once this extraction
|
||||
** process has started, no new elements may be inserted.
|
||||
**
|
||||
** Big chunks of rowid/next-ptr pairs are allocated at a time, to
|
||||
** reduce the malloc overhead.
|
||||
** Hence, the primitive operations for a RowSet are:
|
||||
**
|
||||
** $Id: rowset.c,v 1.4 2009/04/01 19:35:55 drh Exp $
|
||||
** CREATE
|
||||
** INSERT
|
||||
** TEST
|
||||
** SMALLEST
|
||||
** DESTROY
|
||||
**
|
||||
** The CREATE and DESTROY primitives are the constructor and destructor,
|
||||
** obviously. The INSERT primitive adds a new element to the RowSet.
|
||||
** TEST checks to see if an element is already in the RowSet. SMALLEST
|
||||
** extracts the least value from the RowSet.
|
||||
**
|
||||
** The INSERT primitive might allocate additional memory. Memory is
|
||||
** allocated in chunks so most INSERTs do no allocation. There is an
|
||||
** upper bound on the size of allocated memory. No memory is freed
|
||||
** until DESTROY.
|
||||
**
|
||||
** The TEST primitive includes a "batch" number. The TEST primitive
|
||||
** will only see elements that were inserted before the last change
|
||||
** in the batch number. In other words, if an INSERT occurs between
|
||||
** two TESTs where the TESTs have the same batch nubmer, then the
|
||||
** value added by the INSERT will not be visible to the second TEST.
|
||||
** The initial batch number is zero, so if the very first TEST contains
|
||||
** a non-zero batch number, it will see all prior INSERTs.
|
||||
**
|
||||
** No INSERTs may occurs after a SMALLEST. An assertion will fail if
|
||||
** that is attempted.
|
||||
**
|
||||
** The cost of an INSERT is roughly constant. (Sometime new memory
|
||||
** has to be allocated on an INSERT.) The cost of a TEST with a new
|
||||
** batch number is O(NlogN) where N is the number of elements in the RowSet.
|
||||
** The cost of a TEST using the same batch number is O(logN). The cost
|
||||
** of the first SMALLEST is O(NlogN). Second and subsequent SMALLEST
|
||||
** primitives are constant time. The cost of DESTROY is O(N).
|
||||
**
|
||||
** There is an added cost of O(N) when switching between TEST and
|
||||
** SMALLEST primitives.
|
||||
**
|
||||
** $Id: rowset.c,v 1.6 2009/04/22 15:32:59 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
|
||||
/*
|
||||
** Target size for allocation chunks.
|
||||
*/
|
||||
#define ROWSET_ALLOCATION_SIZE 1024
|
||||
|
||||
/*
|
||||
** The number of rowset entries per allocation chunk.
|
||||
*/
|
||||
#define ROWSET_ENTRY_PER_CHUNK 63
|
||||
#define ROWSET_ENTRY_PER_CHUNK \
|
||||
((ROWSET_ALLOCATION_SIZE-8)/sizeof(struct RowSetEntry))
|
||||
|
||||
/*
|
||||
** Each entry in a RowSet is an instance of the following
|
||||
** structure:
|
||||
** Each entry in a RowSet is an instance of the following object.
|
||||
*/
|
||||
struct RowSetEntry {
|
||||
i64 v; /* ROWID value for this entry */
|
||||
struct RowSetEntry *pNext; /* Next entry on a list of all entries */
|
||||
struct RowSetEntry *pRight; /* Right subtree (larger entries) or list */
|
||||
struct RowSetEntry *pLeft; /* Left subtree (smaller entries) */
|
||||
};
|
||||
|
||||
/*
|
||||
** Index entries are allocated in large chunks (instances of the
|
||||
** RowSetEntry objects are allocated in large chunks (instances of the
|
||||
** following structure) to reduce memory allocation overhead. The
|
||||
** chunks are kept on a linked list so that they can be deallocated
|
||||
** when the RowSet is destroyed.
|
||||
*/
|
||||
struct RowSetChunk {
|
||||
struct RowSetChunk *pNext; /* Next chunk on list of them all */
|
||||
struct RowSetChunk *pNextChunk; /* Next chunk on list of them all */
|
||||
struct RowSetEntry aEntry[ROWSET_ENTRY_PER_CHUNK]; /* Allocated entries */
|
||||
};
|
||||
|
||||
@@ -62,11 +104,13 @@ struct RowSetChunk {
|
||||
struct RowSet {
|
||||
struct RowSetChunk *pChunk; /* List of all chunk allocations */
|
||||
sqlite3 *db; /* The database connection */
|
||||
struct RowSetEntry *pEntry; /* List of entries in the rowset */
|
||||
struct RowSetEntry *pEntry; /* List of entries using pRight */
|
||||
struct RowSetEntry *pLast; /* Last entry on the pEntry list */
|
||||
struct RowSetEntry *pFresh; /* Source of new entry objects */
|
||||
struct RowSetEntry *pTree; /* Binary tree of entries */
|
||||
u16 nFresh; /* Number of objects on pFresh */
|
||||
u8 isSorted; /* True if content is sorted */
|
||||
u8 isSorted; /* True if pEntry is sorted */
|
||||
u8 iBatch; /* Current insert batch */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -89,25 +133,30 @@ RowSet *sqlite3RowSetInit(sqlite3 *db, void *pSpace, unsigned int N){
|
||||
p->db = db;
|
||||
p->pEntry = 0;
|
||||
p->pLast = 0;
|
||||
p->pTree = 0;
|
||||
p->pFresh = (struct RowSetEntry*)&p[1];
|
||||
p->nFresh = (u16)((N - sizeof(*p))/sizeof(struct RowSetEntry));
|
||||
p->isSorted = 1;
|
||||
p->iBatch = 0;
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Deallocate all chunks from a RowSet.
|
||||
** Deallocate all chunks from a RowSet. This frees all memory that
|
||||
** the RowSet has allocated over its lifetime. This routine is
|
||||
** the destructor for the RowSet.
|
||||
*/
|
||||
void sqlite3RowSetClear(RowSet *p){
|
||||
struct RowSetChunk *pChunk, *pNextChunk;
|
||||
for(pChunk=p->pChunk; pChunk; pChunk = pNextChunk){
|
||||
pNextChunk = pChunk->pNext;
|
||||
pNextChunk = pChunk->pNextChunk;
|
||||
sqlite3DbFree(p->db, pChunk);
|
||||
}
|
||||
p->pChunk = 0;
|
||||
p->nFresh = 0;
|
||||
p->pEntry = 0;
|
||||
p->pLast = 0;
|
||||
p->pTree = 0;
|
||||
p->isSorted = 1;
|
||||
}
|
||||
|
||||
@@ -118,8 +167,8 @@ void sqlite3RowSetClear(RowSet *p){
|
||||
** memory allocation fails.
|
||||
*/
|
||||
void sqlite3RowSetInsert(RowSet *p, i64 rowid){
|
||||
struct RowSetEntry *pEntry;
|
||||
struct RowSetEntry *pLast;
|
||||
struct RowSetEntry *pEntry; /* The new entry */
|
||||
struct RowSetEntry *pLast; /* The last prior entry */
|
||||
assert( p!=0 );
|
||||
if( p->nFresh==0 ){
|
||||
struct RowSetChunk *pNew;
|
||||
@@ -127,7 +176,7 @@ void sqlite3RowSetInsert(RowSet *p, i64 rowid){
|
||||
if( pNew==0 ){
|
||||
return;
|
||||
}
|
||||
pNew->pNext = p->pChunk;
|
||||
pNew->pNextChunk = p->pChunk;
|
||||
p->pChunk = pNew;
|
||||
p->pFresh = pNew->aEntry;
|
||||
p->nFresh = ROWSET_ENTRY_PER_CHUNK;
|
||||
@@ -135,26 +184,27 @@ void sqlite3RowSetInsert(RowSet *p, i64 rowid){
|
||||
pEntry = p->pFresh++;
|
||||
p->nFresh--;
|
||||
pEntry->v = rowid;
|
||||
pEntry->pNext = 0;
|
||||
pEntry->pRight = 0;
|
||||
pLast = p->pLast;
|
||||
if( pLast ){
|
||||
if( p->isSorted && rowid<=pLast->v ){
|
||||
p->isSorted = 0;
|
||||
}
|
||||
pLast->pNext = pEntry;
|
||||
pLast->pRight = pEntry;
|
||||
}else{
|
||||
assert( p->pEntry==0 );
|
||||
assert( p->pEntry==0 ); /* Fires if INSERT after SMALLEST */
|
||||
p->pEntry = pEntry;
|
||||
}
|
||||
p->pLast = pEntry;
|
||||
}
|
||||
|
||||
/*
|
||||
** Merge two lists of RowSet entries. Remove duplicates.
|
||||
** Merge two lists of RowSetEntry objects. Remove duplicates.
|
||||
**
|
||||
** The input lists are assumed to be in sorted order.
|
||||
** The input lists are connected via pRight pointers and are
|
||||
** assumed to each already be in sorted order.
|
||||
*/
|
||||
static struct RowSetEntry *boolidxMerge(
|
||||
static struct RowSetEntry *rowSetMerge(
|
||||
struct RowSetEntry *pA, /* First sorted list to be merged */
|
||||
struct RowSetEntry *pB /* Second sorted list to be merged */
|
||||
){
|
||||
@@ -163,34 +213,34 @@ static struct RowSetEntry *boolidxMerge(
|
||||
|
||||
pTail = &head;
|
||||
while( pA && pB ){
|
||||
assert( pA->pNext==0 || pA->v<=pA->pNext->v );
|
||||
assert( pB->pNext==0 || pB->v<=pB->pNext->v );
|
||||
assert( pA->pRight==0 || pA->v<=pA->pRight->v );
|
||||
assert( pB->pRight==0 || pB->v<=pB->pRight->v );
|
||||
if( pA->v<pB->v ){
|
||||
pTail->pNext = pA;
|
||||
pA = pA->pNext;
|
||||
pTail = pTail->pNext;
|
||||
pTail->pRight = pA;
|
||||
pA = pA->pRight;
|
||||
pTail = pTail->pRight;
|
||||
}else if( pB->v<pA->v ){
|
||||
pTail->pNext = pB;
|
||||
pB = pB->pNext;
|
||||
pTail = pTail->pNext;
|
||||
pTail->pRight = pB;
|
||||
pB = pB->pRight;
|
||||
pTail = pTail->pRight;
|
||||
}else{
|
||||
pA = pA->pNext;
|
||||
pA = pA->pRight;
|
||||
}
|
||||
}
|
||||
if( pA ){
|
||||
assert( pA->pNext==0 || pA->v<=pA->pNext->v );
|
||||
pTail->pNext = pA;
|
||||
assert( pA->pRight==0 || pA->v<=pA->pRight->v );
|
||||
pTail->pRight = pA;
|
||||
}else{
|
||||
assert( pB==0 || pB->pNext==0 || pB->v<=pB->pNext->v );
|
||||
pTail->pNext = pB;
|
||||
assert( pB==0 || pB->pRight==0 || pB->v<=pB->pRight->v );
|
||||
pTail->pRight = pB;
|
||||
}
|
||||
return head.pNext;
|
||||
return head.pRight;
|
||||
}
|
||||
|
||||
/*
|
||||
** Sort all elements of the RowSet into ascending order.
|
||||
** Sort all elements on the pEntry list of the RowSet into ascending order.
|
||||
*/
|
||||
static void sqlite3RowSetSort(RowSet *p){
|
||||
static void rowSetSort(RowSet *p){
|
||||
unsigned int i;
|
||||
struct RowSetEntry *pEntry;
|
||||
struct RowSetEntry *aBucket[40];
|
||||
@@ -199,35 +249,143 @@ static void sqlite3RowSetSort(RowSet *p){
|
||||
memset(aBucket, 0, sizeof(aBucket));
|
||||
while( p->pEntry ){
|
||||
pEntry = p->pEntry;
|
||||
p->pEntry = pEntry->pNext;
|
||||
pEntry->pNext = 0;
|
||||
p->pEntry = pEntry->pRight;
|
||||
pEntry->pRight = 0;
|
||||
for(i=0; aBucket[i]; i++){
|
||||
pEntry = boolidxMerge(aBucket[i],pEntry);
|
||||
pEntry = rowSetMerge(aBucket[i], pEntry);
|
||||
aBucket[i] = 0;
|
||||
}
|
||||
aBucket[i] = pEntry;
|
||||
}
|
||||
pEntry = 0;
|
||||
for(i=0; i<sizeof(aBucket)/sizeof(aBucket[0]); i++){
|
||||
pEntry = boolidxMerge(pEntry,aBucket[i]);
|
||||
pEntry = rowSetMerge(pEntry, aBucket[i]);
|
||||
}
|
||||
p->pEntry = pEntry;
|
||||
p->pLast = 0;
|
||||
p->isSorted = 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Extract the next (smallest) element from the RowSet.
|
||||
** The input, pIn, is a binary tree (or subtree) of RowSetEntry objects.
|
||||
** Convert this tree into a linked list connected by the pRight pointers
|
||||
** and return pointers to the first and last elements of the new list.
|
||||
*/
|
||||
static void rowSetTreeToList(
|
||||
struct RowSetEntry *pIn, /* Root of the input tree */
|
||||
struct RowSetEntry **ppFirst, /* Write head of the output list here */
|
||||
struct RowSetEntry **ppLast /* Write tail of the output list here */
|
||||
){
|
||||
assert( pIn!=0 );
|
||||
if( pIn->pLeft ){
|
||||
struct RowSetEntry *p;
|
||||
rowSetTreeToList(pIn->pLeft, ppFirst, &p);
|
||||
p->pRight = pIn;
|
||||
}else{
|
||||
*ppFirst = pIn;
|
||||
}
|
||||
if( pIn->pRight ){
|
||||
rowSetTreeToList(pIn->pRight, &pIn->pRight, ppLast);
|
||||
}else{
|
||||
*ppLast = pIn;
|
||||
}
|
||||
assert( (*ppLast)->pRight==0 );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Convert a sorted list of elements (connected by pRight) into a binary
|
||||
** tree with depth of iDepth. A depth of 1 means the tree contains a single
|
||||
** node taken from the head of *ppList. A depth of 2 means a tree with
|
||||
** three nodes. And so forth.
|
||||
**
|
||||
** Use as many entries from the input list as required and update the
|
||||
** *ppList to point to the unused elements of the list. If the input
|
||||
** list contains too few elements, then construct an incomplete tree
|
||||
** and leave *ppList set to NULL.
|
||||
**
|
||||
** Return a pointer to the root of the constructed binary tree.
|
||||
*/
|
||||
static struct RowSetEntry *rowSetNDeepTree(
|
||||
struct RowSetEntry **ppList,
|
||||
int iDepth
|
||||
){
|
||||
struct RowSetEntry *p; /* Root of the new tree */
|
||||
struct RowSetEntry *pLeft; /* Left subtree */
|
||||
if( *ppList==0 ){
|
||||
return 0;
|
||||
}
|
||||
if( iDepth==1 ){
|
||||
p = *ppList;
|
||||
*ppList = p->pRight;
|
||||
p->pLeft = p->pRight = 0;
|
||||
return p;
|
||||
}
|
||||
pLeft = rowSetNDeepTree(ppList, iDepth-1);
|
||||
p = *ppList;
|
||||
if( p==0 ){
|
||||
return pLeft;
|
||||
}
|
||||
p->pLeft = pLeft;
|
||||
*ppList = p->pRight;
|
||||
p->pRight = rowSetNDeepTree(ppList, iDepth-1);
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert a sorted list of elements into a binary tree. Make the tree
|
||||
** as deep as it needs to be in order to contain the entire list.
|
||||
*/
|
||||
static struct RowSetEntry *rowSetListToTree(struct RowSetEntry *pList){
|
||||
int iDepth; /* Depth of the tree so far */
|
||||
struct RowSetEntry *p; /* Current tree root */
|
||||
struct RowSetEntry *pLeft; /* Left subtree */
|
||||
|
||||
assert( pList!=0 );
|
||||
p = pList;
|
||||
pList = p->pRight;
|
||||
p->pLeft = p->pRight = 0;
|
||||
for(iDepth=1; pList; iDepth++){
|
||||
pLeft = p;
|
||||
p = pList;
|
||||
pList = p->pRight;
|
||||
p->pLeft = pLeft;
|
||||
p->pRight = rowSetNDeepTree(&pList, iDepth);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert the list in p->pEntry into a sorted list if it is not
|
||||
** sorted already. If there is a binary tree on p->pTree, then
|
||||
** convert it into a list too and merge it into the p->pEntry list.
|
||||
*/
|
||||
static void rowSetToList(RowSet *p){
|
||||
if( !p->isSorted ){
|
||||
rowSetSort(p);
|
||||
}
|
||||
if( p->pTree ){
|
||||
struct RowSetEntry *pHead, *pTail;
|
||||
rowSetTreeToList(p->pTree, &pHead, &pTail);
|
||||
p->pTree = 0;
|
||||
p->pEntry = rowSetMerge(p->pEntry, pHead);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Extract the smallest element from the RowSet.
|
||||
** Write the element into *pRowid. Return 1 on success. Return
|
||||
** 0 if the RowSet is already empty.
|
||||
**
|
||||
** After this routine has been called, the sqlite3RowSetInsert()
|
||||
** routine may not be called again.
|
||||
*/
|
||||
int sqlite3RowSetNext(RowSet *p, i64 *pRowid){
|
||||
if( !p->isSorted ){
|
||||
sqlite3RowSetSort(p);
|
||||
}
|
||||
rowSetToList(p);
|
||||
if( p->pEntry ){
|
||||
*pRowid = p->pEntry->v;
|
||||
p->pEntry = p->pEntry->pNext;
|
||||
p->pEntry = p->pEntry->pRight;
|
||||
if( p->pEntry==0 ){
|
||||
sqlite3RowSetClear(p);
|
||||
}
|
||||
@@ -236,3 +394,31 @@ int sqlite3RowSetNext(RowSet *p, i64 *pRowid){
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Check to see if element iRowid was inserted into the the rowset as
|
||||
** part of any insert batch prior to iBatch. Return 1 or 0.
|
||||
*/
|
||||
int sqlite3RowSetTest(RowSet *pRowSet, u8 iBatch, sqlite3_int64 iRowid){
|
||||
struct RowSetEntry *p;
|
||||
if( iBatch!=pRowSet->iBatch ){
|
||||
if( pRowSet->pEntry ){
|
||||
rowSetToList(pRowSet);
|
||||
pRowSet->pTree = rowSetListToTree(pRowSet->pEntry);
|
||||
pRowSet->pEntry = 0;
|
||||
pRowSet->pLast = 0;
|
||||
}
|
||||
pRowSet->iBatch = iBatch;
|
||||
}
|
||||
p = pRowSet->pTree;
|
||||
while( p ){
|
||||
if( p->v<iRowid ){
|
||||
p = p->pRight;
|
||||
}else if( p->v>iRowid ){
|
||||
p = p->pLeft;
|
||||
}else{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
+19
-45
@@ -12,7 +12,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle SELECT statements in SQLite.
|
||||
**
|
||||
** $Id: select.c,v 1.507 2009/04/02 16:59:47 drh Exp $
|
||||
** $Id: select.c,v 1.512 2009/05/03 20:23:54 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -193,42 +193,7 @@ static void setToken(Token *p, const char *z){
|
||||
p->z = (u8*)z;
|
||||
p->n = z ? sqlite3Strlen30(z) : 0;
|
||||
p->dyn = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the token to the double-quoted and escaped version of the string pointed
|
||||
** to by z. For example;
|
||||
**
|
||||
** {a"bc} -> {"a""bc"}
|
||||
*/
|
||||
static void setQuotedToken(Parse *pParse, Token *p, const char *z){
|
||||
|
||||
/* Check if the string appears to be quoted using "..." or `...`
|
||||
** or [...] or '...' or if the string contains any " characters.
|
||||
** If it does, then record a version of the string with the special
|
||||
** characters escaped.
|
||||
*/
|
||||
const char *z2 = z;
|
||||
if( *z2!='[' && *z2!='`' && *z2!='\'' ){
|
||||
while( *z2 ){
|
||||
if( *z2=='"' ) break;
|
||||
z2++;
|
||||
}
|
||||
}
|
||||
|
||||
if( *z2 ){
|
||||
/* String contains " characters - copy and quote the string. */
|
||||
p->z = (u8 *)sqlite3MPrintf(pParse->db, "\"%w\"", z);
|
||||
if( p->z ){
|
||||
p->n = sqlite3Strlen30((char *)p->z);
|
||||
p->dyn = 1;
|
||||
}
|
||||
}else{
|
||||
/* String contains no " characters - copy the pointer. */
|
||||
p->z = (u8*)z;
|
||||
p->n = (int)(z2 - z);
|
||||
p->dyn = 0;
|
||||
}
|
||||
p->quoted = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -421,6 +386,7 @@ static void pushOntoSorter(
|
||||
int nExpr = pOrderBy->nExpr;
|
||||
int regBase = sqlite3GetTempRange(pParse, nExpr+2);
|
||||
int regRecord = sqlite3GetTempReg(pParse);
|
||||
sqlite3ExprCacheClear(pParse);
|
||||
sqlite3ExprCodeExprList(pParse, pOrderBy, regBase, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Sequence, pOrderBy->iECursor, regBase+nExpr);
|
||||
sqlite3ExprCodeMove(pParse, regData, regBase+nExpr+1, 1);
|
||||
@@ -573,6 +539,7 @@ static void selectInnerLoop(
|
||||
/* If the destination is an EXISTS(...) expression, the actual
|
||||
** values returned by the SELECT are not required.
|
||||
*/
|
||||
sqlite3ExprCacheClear(pParse);
|
||||
sqlite3ExprCodeExprList(pParse, pEList, regResult, eDest==SRT_Output);
|
||||
}
|
||||
nColumn = nResultCol;
|
||||
@@ -1190,7 +1157,6 @@ static int selectColumnsFromExprList(
|
||||
sqlite3DbFree(db, zName);
|
||||
break;
|
||||
}
|
||||
sqlite3Dequote(zName);
|
||||
|
||||
/* Make sure the column name is unique. If the name is not unique,
|
||||
** append a integer to the name so that it becomes unique.
|
||||
@@ -1346,6 +1312,7 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){
|
||||
** The current implementation interprets "LIMIT 0" to mean
|
||||
** no rows.
|
||||
*/
|
||||
sqlite3ExprCacheClear(pParse);
|
||||
if( p->pLimit ){
|
||||
p->iLimit = iLimit = ++pParse->nMem;
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
@@ -1394,7 +1361,8 @@ static CollSeq *multiSelectCollSeq(Parse *pParse, Select *p, int iCol){
|
||||
}else{
|
||||
pRet = 0;
|
||||
}
|
||||
if( pRet==0 ){
|
||||
assert( iCol>=0 );
|
||||
if( pRet==0 && iCol<p->pEList->nExpr ){
|
||||
pRet = sqlite3ExprCollSeq(pParse, p->pEList->a[iCol].pExpr);
|
||||
}
|
||||
return pRet;
|
||||
@@ -2101,7 +2069,7 @@ static int multiSelectOrderBy(
|
||||
}
|
||||
|
||||
/* Compute the comparison permutation and keyinfo that is used with
|
||||
** the permutation in order to comparisons to determine if the next
|
||||
** the permutation used to determine if the next
|
||||
** row of results comes from selectA or selectB. Also add explicit
|
||||
** collations to the ORDER BY clause terms so that when the subqueries
|
||||
** to the right and the left are evaluated, they use the correct
|
||||
@@ -3224,12 +3192,12 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
}
|
||||
pRight = sqlite3PExpr(pParse, TK_ID, 0, 0, 0);
|
||||
if( pRight==0 ) break;
|
||||
setQuotedToken(pParse, &pRight->token, zName);
|
||||
setToken(&pRight->token, zName);
|
||||
if( longNames || pTabList->nSrc>1 ){
|
||||
Expr *pLeft = sqlite3PExpr(pParse, TK_ID, 0, 0, 0);
|
||||
pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0);
|
||||
if( pExpr==0 ) break;
|
||||
setQuotedToken(pParse, &pLeft->token, zTabName);
|
||||
setToken(&pLeft->token, zTabName);
|
||||
setToken(&pExpr->span,
|
||||
sqlite3MPrintf(db, "%s.%s", zTabName, zName));
|
||||
pExpr->span.dyn = 1;
|
||||
@@ -3455,6 +3423,7 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
|
||||
struct AggInfo_col *pC;
|
||||
|
||||
pAggInfo->directMode = 1;
|
||||
sqlite3ExprCacheClear(pParse);
|
||||
for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){
|
||||
int nArg;
|
||||
int addrNext = 0;
|
||||
@@ -3494,12 +3463,14 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
|
||||
sqlite3ExprCacheAffinityChange(pParse, regAgg, nArg);
|
||||
if( addrNext ){
|
||||
sqlite3VdbeResolveLabel(v, addrNext);
|
||||
sqlite3ExprCacheClear(pParse);
|
||||
}
|
||||
}
|
||||
for(i=0, pC=pAggInfo->aCol; i<pAggInfo->nAccumulator; i++, pC++){
|
||||
sqlite3ExprCode(pParse, pC->pExpr, pC->iMem);
|
||||
}
|
||||
pAggInfo->directMode = 0;
|
||||
sqlite3ExprCacheClear(pParse);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3764,7 +3735,7 @@ int sqlite3Select(
|
||||
/* This case is for non-aggregate queries
|
||||
** Begin the database scan
|
||||
*/
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy, 0, 0);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy, 0);
|
||||
if( pWInfo==0 ) goto select_end;
|
||||
|
||||
/* If sorting index that was created by a prior OP_OpenEphemeral
|
||||
@@ -3886,7 +3857,7 @@ int sqlite3Select(
|
||||
** in the right order to begin with.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pGroupBy, 0, 0);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pGroupBy, 0);
|
||||
if( pWInfo==0 ) goto select_end;
|
||||
if( pGroupBy==0 ){
|
||||
/* The optimizer is able to deliver rows in group by order so
|
||||
@@ -3917,6 +3888,7 @@ int sqlite3Select(
|
||||
}
|
||||
}
|
||||
regBase = sqlite3GetTempRange(pParse, nCol);
|
||||
sqlite3ExprCacheClear(pParse);
|
||||
sqlite3ExprCodeExprList(pParse, pGroupBy, regBase, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Sequence, sAggInfo.sortingIdx,regBase+nGroupBy);
|
||||
j = nGroupBy+1;
|
||||
@@ -3943,6 +3915,7 @@ int sqlite3Select(
|
||||
sqlite3VdbeAddOp2(v, OP_Sort, sAggInfo.sortingIdx, addrEnd);
|
||||
VdbeComment((v, "GROUP BY sort"));
|
||||
sAggInfo.useSortingIdx = 1;
|
||||
sqlite3ExprCacheClear(pParse);
|
||||
}
|
||||
|
||||
/* Evaluate the current GROUP BY terms and store in b0, b1, b2...
|
||||
@@ -3951,6 +3924,7 @@ int sqlite3Select(
|
||||
** from the previous row currently stored in a0, a1, a2...
|
||||
*/
|
||||
addrTopOfLoop = sqlite3VdbeCurrentAddr(v);
|
||||
sqlite3ExprCacheClear(pParse);
|
||||
for(j=0; j<pGroupBy->nExpr; j++){
|
||||
if( groupBySort ){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, sAggInfo.sortingIdx, j, iBMem+j);
|
||||
@@ -4141,7 +4115,7 @@ int sqlite3Select(
|
||||
** of output.
|
||||
*/
|
||||
resetAccumulator(pParse, &sAggInfo);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax, flag, 0);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax, flag);
|
||||
if( pWInfo==0 ){
|
||||
sqlite3ExprListDelete(db, pDel);
|
||||
goto select_end;
|
||||
|
||||
+134
-104
@@ -30,7 +30,7 @@
|
||||
** the version number) and changes its name to "sqlite3.h" as
|
||||
** part of the build process.
|
||||
**
|
||||
** @(#) $Id: sqlite.h.in,v 1.440 2009/04/06 15:55:04 drh Exp $
|
||||
** @(#) $Id: sqlite.h.in,v 1.447 2009/04/30 15:59:56 drh Exp $
|
||||
*/
|
||||
#ifndef _SQLITE3_H_
|
||||
#define _SQLITE3_H_
|
||||
@@ -791,6 +791,11 @@ struct sqlite3_vfs {
|
||||
** of sqlite3_initialize() does any initialization. All other calls
|
||||
** are harmless no-ops.
|
||||
**
|
||||
** A call to sqlite3_shutdown() is an "effective" call if it is the first
|
||||
** call to sqlite3_shutdown() since the last sqlite3_initialize(). Only
|
||||
** an effective call to sqlite3_shutdown() does any deinitialization.
|
||||
** All other calls to sqlite3_shutdown() are harmless no-ops.
|
||||
**
|
||||
** Among other things, sqlite3_initialize() shall invoke
|
||||
** sqlite3_os_init(). Similarly, sqlite3_shutdown()
|
||||
** shall invoke sqlite3_os_end().
|
||||
@@ -1218,14 +1223,18 @@ sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*);
|
||||
** triggers are not counted. Use the [sqlite3_total_changes()] function
|
||||
** to find the total number of changes including changes caused by triggers.
|
||||
**
|
||||
** Changes to a view that are simulated by an [INSTEAD OF trigger]
|
||||
** are not counted. Only real table changes are counted.
|
||||
**
|
||||
** A "row change" is a change to a single row of a single table
|
||||
** caused by an INSERT, DELETE, or UPDATE statement. Rows that
|
||||
** are changed as side effects of REPLACE constraint resolution,
|
||||
** rollback, ABORT processing, DROP TABLE, or by any other
|
||||
** are changed as side effects of [REPLACE] constraint resolution,
|
||||
** rollback, ABORT processing, [DROP TABLE], or by any other
|
||||
** mechanisms do not count as direct row changes.
|
||||
**
|
||||
** A "trigger context" is a scope of execution that begins and
|
||||
** ends with the script of a trigger. Most SQL statements are
|
||||
** ends with the script of a [CREATE TRIGGER | trigger].
|
||||
** Most SQL statements are
|
||||
** evaluated outside of any trigger. This is the "top level"
|
||||
** trigger context. If a trigger fires from the top level, a
|
||||
** new trigger context is entered for the duration of that one
|
||||
@@ -1247,16 +1256,8 @@ sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*);
|
||||
** However, the number returned does not include changes
|
||||
** caused by subtriggers since those have their own context.
|
||||
**
|
||||
** SQLite implements the command "DELETE FROM table" without a WHERE clause
|
||||
** by dropping and recreating the table. Doing so is much faster than going
|
||||
** through and deleting individual elements from the table. Because of this
|
||||
** optimization, the deletions in "DELETE FROM table" are not row changes and
|
||||
** will not be counted by the sqlite3_changes() or [sqlite3_total_changes()]
|
||||
** functions, regardless of the number of elements that were originally
|
||||
** in the table. To get an accurate count of the number of rows deleted, use
|
||||
** "DELETE FROM table WHERE 1" instead. Or recompile using the
|
||||
** [SQLITE_OMIT_TRUNCATE_OPTIMIZATION] compile-time option to disable the
|
||||
** optimization on all queries.
|
||||
** See also the [sqlite3_total_changes()] interface and the
|
||||
** [count_changes pragma].
|
||||
**
|
||||
** Requirements:
|
||||
** [H12241] [H12243]
|
||||
@@ -1270,27 +1271,21 @@ int sqlite3_changes(sqlite3*);
|
||||
/*
|
||||
** CAPI3REF: Total Number Of Rows Modified {H12260} <S10600>
|
||||
**
|
||||
** This function returns the number of row changes caused by INSERT,
|
||||
** UPDATE or DELETE statements since the [database connection] was opened.
|
||||
** The count includes all changes from all trigger contexts. However,
|
||||
** the count does not include changes used to implement REPLACE constraints,
|
||||
** do rollbacks or ABORT processing, or DROP table processing.
|
||||
** This function returns the number of row changes caused by [INSERT],
|
||||
** [UPDATE] or [DELETE] statements since the [database connection] was opened.
|
||||
** The count includes all changes from all
|
||||
** [CREATE TRIGGER | trigger] contexts. However,
|
||||
** the count does not include changes used to implement [REPLACE] constraints,
|
||||
** do rollbacks or ABORT processing, or [DROP TABLE] processing. The
|
||||
** count does not rows of views that fire an [INSTEAD OF trigger], though if
|
||||
** the INSTEAD OF trigger makes changes of its own, those changes are
|
||||
** counted.
|
||||
** The changes are counted as soon as the statement that makes them is
|
||||
** completed (when the statement handle is passed to [sqlite3_reset()] or
|
||||
** [sqlite3_finalize()]).
|
||||
**
|
||||
** SQLite implements the command "DELETE FROM table" without a WHERE clause
|
||||
** by dropping and recreating the table. (This is much faster than going
|
||||
** through and deleting individual elements from the table.) Because of this
|
||||
** optimization, the deletions in "DELETE FROM table" are not row changes and
|
||||
** will not be counted by the sqlite3_changes() or [sqlite3_total_changes()]
|
||||
** functions, regardless of the number of elements that were originally
|
||||
** in the table. To get an accurate count of the number of rows deleted, use
|
||||
** "DELETE FROM table WHERE 1" instead. Or recompile using the
|
||||
** [SQLITE_OMIT_TRUNCATE_OPTIMIZATION] compile-time option to disable the
|
||||
** optimization on all queries.
|
||||
**
|
||||
** See also the [sqlite3_changes()] interface.
|
||||
** See also the [sqlite3_changes()] interface and the
|
||||
** [count_changes pragma].
|
||||
**
|
||||
** Requirements:
|
||||
** [H12261] [H12263]
|
||||
@@ -1324,8 +1319,16 @@ int sqlite3_total_changes(sqlite3*);
|
||||
** that is inside an explicit transaction, then the entire transaction
|
||||
** will be rolled back automatically.
|
||||
**
|
||||
** A call to sqlite3_interrupt() has no effect on SQL statements
|
||||
** that are started after sqlite3_interrupt() returns.
|
||||
** The sqlite3_interrupt(D) call is in effect until all currently running
|
||||
** SQL statements on [database connection] D complete. Any new SQL statements
|
||||
** that are started after the sqlite3_interrupt() call and before the
|
||||
** running statements reaches zero are interrupted as if they had been
|
||||
** running prior to the sqlite3_interrupt() call. New SQL statements
|
||||
** that are started after the running statement count reaches zero are
|
||||
** not effected by the sqlite3_interrupt().
|
||||
** A call to sqlite3_interrupt(D) that occurs when there are no running
|
||||
** SQL statements is a no-op and has no effect on SQL statements
|
||||
** that are started after the sqlite3_interrupt() call returns.
|
||||
**
|
||||
** Requirements:
|
||||
** [H12271] [H12272]
|
||||
@@ -1338,20 +1341,30 @@ void sqlite3_interrupt(sqlite3*);
|
||||
/*
|
||||
** CAPI3REF: Determine If An SQL Statement Is Complete {H10510} <S70200>
|
||||
**
|
||||
** These routines are useful for command-line input to determine if the
|
||||
** currently entered text seems to form complete a SQL statement or
|
||||
** These routines are useful during command-line input to determine if the
|
||||
** currently entered text seems to form a complete SQL statement or
|
||||
** if additional input is needed before sending the text into
|
||||
** SQLite for parsing. These routines return true if the input string
|
||||
** SQLite for parsing. These routines return 1 if the input string
|
||||
** appears to be a complete SQL statement. A statement is judged to be
|
||||
** complete if it ends with a semicolon token and is not a fragment of a
|
||||
** CREATE TRIGGER statement. Semicolons that are embedded within
|
||||
** complete if it ends with a semicolon token and is not a prefix of a
|
||||
** well-formed CREATE TRIGGER statement. Semicolons that are embedded within
|
||||
** string literals or quoted identifier names or comments are not
|
||||
** independent tokens (they are part of the token in which they are
|
||||
** embedded) and thus do not count as a statement terminator.
|
||||
** embedded) and thus do not count as a statement terminator. Whitespace
|
||||
** and comments that follow the final semicolon are ignored.
|
||||
**
|
||||
** These routines return 0 if the statement is incomplete. If a
|
||||
** memory allocation fails, then SQLITE_NOMEM is returned.
|
||||
**
|
||||
** These routines do not parse the SQL statements thus
|
||||
** will not detect syntactically incorrect SQL.
|
||||
**
|
||||
** If SQLite has not been initialized using [sqlite3_initialize()] prior
|
||||
** to invoking sqlite3_complete16() then sqlite3_initialize() is invoked
|
||||
** automatically by sqlite3_complete16(). If that initialization fails,
|
||||
** then the return value from sqlite3_complete16() will be non-zero
|
||||
** regardless of whether or not the input SQL is complete.
|
||||
**
|
||||
** Requirements: [H10511] [H10512]
|
||||
**
|
||||
** The input to [sqlite3_complete()] must be a zero-terminated
|
||||
@@ -1779,13 +1792,7 @@ void sqlite3_randomness(int N, void *P);
|
||||
** requested is ok. When the callback returns [SQLITE_DENY], the
|
||||
** [sqlite3_prepare_v2()] or equivalent call that triggered the
|
||||
** authorizer will fail with an error message explaining that
|
||||
** access is denied. If the authorizer code is [SQLITE_READ]
|
||||
** and the callback returns [SQLITE_IGNORE] then the
|
||||
** [prepared statement] statement is constructed to substitute
|
||||
** a NULL value in place of the table column that would have
|
||||
** been read if [SQLITE_OK] had been returned. The [SQLITE_IGNORE]
|
||||
** return can be used to deny an untrusted user access to individual
|
||||
** columns of a table.
|
||||
** access is denied.
|
||||
**
|
||||
** The first parameter to the authorizer callback is a copy of the third
|
||||
** parameter to the sqlite3_set_authorizer() interface. The second parameter
|
||||
@@ -1794,6 +1801,17 @@ void sqlite3_randomness(int N, void *P);
|
||||
** to the callback are zero-terminated strings that contain additional
|
||||
** details about the action to be authorized.
|
||||
**
|
||||
** If the action code is [SQLITE_READ]
|
||||
** and the callback returns [SQLITE_IGNORE] then the
|
||||
** [prepared statement] statement is constructed to substitute
|
||||
** a NULL value in place of the table column that would have
|
||||
** been read if [SQLITE_OK] had been returned. The [SQLITE_IGNORE]
|
||||
** return can be used to deny an untrusted user access to individual
|
||||
** columns of a table.
|
||||
** If the action code is [SQLITE_DELETE] and the callback returns
|
||||
** [SQLITE_IGNORE] then the [DELETE] operation proceeds but the
|
||||
** [truncate optimization] is disabled and all rows are deleted individually.
|
||||
**
|
||||
** An authorizer is used when [sqlite3_prepare | preparing]
|
||||
** SQL statements from an untrusted source, to ensure that the SQL statements
|
||||
** do not try to access data they are not allowed to see, or that they do not
|
||||
@@ -1827,7 +1845,9 @@ void sqlite3_randomness(int N, void *P);
|
||||
**
|
||||
** Note that the authorizer callback is invoked only during
|
||||
** [sqlite3_prepare()] or its variants. Authorization is not
|
||||
** performed during statement evaluation in [sqlite3_step()].
|
||||
** performed during statement evaluation in [sqlite3_step()], unless
|
||||
** as stated in the previous paragraph, sqlite3_step() invokes
|
||||
** sqlite3_prepare_v2() to reprepare a statement after a schema change.
|
||||
**
|
||||
** Requirements:
|
||||
** [H12501] [H12502] [H12503] [H12504] [H12505] [H12506] [H12507] [H12510]
|
||||
@@ -3483,12 +3503,14 @@ void sqlite3_result_zeroblob(sqlite3_context*, int n);
|
||||
** the name is passed as the second function argument.
|
||||
**
|
||||
** The third argument may be one of the constants [SQLITE_UTF8],
|
||||
** [SQLITE_UTF16LE] or [SQLITE_UTF16BE], indicating that the user-supplied
|
||||
** [SQLITE_UTF16LE], or [SQLITE_UTF16BE], indicating that the user-supplied
|
||||
** routine expects to be passed pointers to strings encoded using UTF-8,
|
||||
** UTF-16 little-endian, or UTF-16 big-endian, respectively. The
|
||||
** third argument might also be [SQLITE_UTF16_ALIGNED] to indicate that
|
||||
** third argument might also be [SQLITE_UTF16] to indicate that the routine
|
||||
** expects pointers to be UTF-16 strings in the native byte order, or the
|
||||
** argument can be [SQLITE_UTF16_ALIGNED] if the
|
||||
** the routine expects pointers to 16-bit word aligned strings
|
||||
** of UTF-16 in the native byte order of the host computer.
|
||||
** of UTF-16 in the native byte order.
|
||||
**
|
||||
** A pointer to the user supplied routine must be passed as the fifth
|
||||
** argument. If it is NULL, this is the same as deleting the collation
|
||||
@@ -3513,6 +3535,8 @@ void sqlite3_result_zeroblob(sqlite3_context*, int n);
|
||||
** collation creation functions or when the [database connection] is closed
|
||||
** using [sqlite3_close()].
|
||||
**
|
||||
** See also: [sqlite3_collation_needed()] and [sqlite3_collation_needed16()].
|
||||
**
|
||||
** Requirements:
|
||||
** [H16603] [H16604] [H16606] [H16609] [H16612] [H16615] [H16618] [H16621]
|
||||
** [H16624] [H16627] [H16630]
|
||||
@@ -4066,15 +4090,20 @@ typedef struct sqlite3_module sqlite3_module;
|
||||
|
||||
/*
|
||||
** CAPI3REF: Virtual Table Object {H18000} <S20400>
|
||||
** KEYWORDS: sqlite3_module
|
||||
** KEYWORDS: sqlite3_module {virtual table module}
|
||||
** EXPERIMENTAL
|
||||
**
|
||||
** A module is a class of virtual tables. Each module is defined
|
||||
** by an instance of the following structure. This structure consists
|
||||
** mostly of methods for the module.
|
||||
** This structure, sometimes called a a "virtual table module",
|
||||
** defines the implementation of a [virtual tables].
|
||||
** This structure consists mostly of methods for the module.
|
||||
**
|
||||
** This interface is experimental and is subject to change or
|
||||
** removal in future releases of SQLite.
|
||||
** A virtual table module is created by filling in a persistent
|
||||
** instance of this structure and passing a pointer to that instance
|
||||
** to [sqlite3_create_module()] or [sqlite3_create_module_v2()].
|
||||
** The registration remains valid until it is replaced by a different
|
||||
** module or until the [database connection] closes. The content
|
||||
** of this structure must not change while it is registered with
|
||||
** any database connection.
|
||||
*/
|
||||
struct sqlite3_module {
|
||||
int iVersion;
|
||||
@@ -4112,8 +4141,8 @@ struct sqlite3_module {
|
||||
** EXPERIMENTAL
|
||||
**
|
||||
** The sqlite3_index_info structure and its substructures is used to
|
||||
** pass information into and receive the reply from the xBestIndex
|
||||
** method of an sqlite3_module. The fields under **Inputs** are the
|
||||
** pass information into and receive the reply from the [xBestIndex]
|
||||
** method of a [virtual table module]. The fields under **Inputs** are the
|
||||
** inputs to xBestIndex and are read-only. xBestIndex inserts its
|
||||
** results into the **Outputs** fields.
|
||||
**
|
||||
@@ -4136,17 +4165,19 @@ struct sqlite3_module {
|
||||
** Information about the ORDER BY clause is stored in aOrderBy[].
|
||||
** Each term of aOrderBy records a column of the ORDER BY clause.
|
||||
**
|
||||
** The xBestIndex method must fill aConstraintUsage[] with information
|
||||
** The [xBestIndex] method must fill aConstraintUsage[] with information
|
||||
** about what parameters to pass to xFilter. If argvIndex>0 then
|
||||
** the right-hand side of the corresponding aConstraint[] is evaluated
|
||||
** and becomes the argvIndex-th entry in argv. If aConstraintUsage[].omit
|
||||
** is true, then the constraint is assumed to be fully handled by the
|
||||
** virtual table and is not checked again by SQLite.
|
||||
**
|
||||
** The idxNum and idxPtr values are recorded and passed into xFilter.
|
||||
** sqlite3_free() is used to free idxPtr if needToFreeIdxPtr is true.
|
||||
** The idxNum and idxPtr values are recorded and passed into the
|
||||
** [xFilter] method.
|
||||
** [sqlite3_free()] is used to free idxPtr if and only iff
|
||||
** needToFreeIdxPtr is true.
|
||||
**
|
||||
** The orderByConsumed means that output from xFilter will occur in
|
||||
** The orderByConsumed means that output from [xFilter]/[xNext] will occur in
|
||||
** the correct order to satisfy the ORDER BY clause so that no separate
|
||||
** sorting step is required.
|
||||
**
|
||||
@@ -4154,9 +4185,6 @@ struct sqlite3_module {
|
||||
** particular lookup. A full scan of a table with N entries should have
|
||||
** a cost of N. A binary search of a table of N entries should have a
|
||||
** cost of approximately log(N).
|
||||
**
|
||||
** This interface is experimental and is subject to change or
|
||||
** removal in future releases of SQLite.
|
||||
*/
|
||||
struct sqlite3_index_info {
|
||||
/* Inputs */
|
||||
@@ -4194,34 +4222,44 @@ struct sqlite3_index_info {
|
||||
** CAPI3REF: Register A Virtual Table Implementation {H18200} <S20400>
|
||||
** EXPERIMENTAL
|
||||
**
|
||||
** This routine is used to register a new module name with a
|
||||
** [database connection]. Module names must be registered before
|
||||
** creating new virtual tables on the module, or before using
|
||||
** preexisting virtual tables of the module.
|
||||
** This routine is used to register a new [virtual table module] name.
|
||||
** Module names must be registered before
|
||||
** creating a new [virtual table] using the module, or before using a
|
||||
** preexisting [virtual table] for the module.
|
||||
**
|
||||
** This interface is experimental and is subject to change or
|
||||
** removal in future releases of SQLite.
|
||||
** The module name is registered on the [database connection] specified
|
||||
** by the first parameter. The name of the module is given by the
|
||||
** second parameter. The third parameter is a pointer to
|
||||
** the implementation of the [virtual table module]. The fourth
|
||||
** parameter is an arbitrary client data pointer that is passed through
|
||||
** into the [xCreate] and [xConnect] methods of the virtual table module
|
||||
** when a new virtual table is be being created or reinitialized.
|
||||
**
|
||||
** This interface has exactly the same effect as calling
|
||||
** [sqlite3_create_module_v2()] with a NULL client data destructor.
|
||||
*/
|
||||
SQLITE_EXPERIMENTAL int sqlite3_create_module(
|
||||
sqlite3 *db, /* SQLite connection to register module with */
|
||||
const char *zName, /* Name of the module */
|
||||
const sqlite3_module *, /* Methods for the module */
|
||||
void * /* Client data for xCreate/xConnect */
|
||||
const sqlite3_module *p, /* Methods for the module */
|
||||
void *pClientData /* Client data for xCreate/xConnect */
|
||||
);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Register A Virtual Table Implementation {H18210} <S20400>
|
||||
** EXPERIMENTAL
|
||||
**
|
||||
** This routine is identical to the [sqlite3_create_module()] method above,
|
||||
** except that it allows a destructor function to be specified. It is
|
||||
** even more experimental than the rest of the virtual tables API.
|
||||
** This routine is identical to the [sqlite3_create_module()] method,
|
||||
** except that it has an extra parameter to specify
|
||||
** a destructor function for the client data pointer. SQLite will
|
||||
** invoke the destructor function (if it is not NULL) when SQLite
|
||||
** no longer needs the pClientData pointer.
|
||||
*/
|
||||
SQLITE_EXPERIMENTAL int sqlite3_create_module_v2(
|
||||
sqlite3 *db, /* SQLite connection to register module with */
|
||||
const char *zName, /* Name of the module */
|
||||
const sqlite3_module *, /* Methods for the module */
|
||||
void *, /* Client data for xCreate/xConnect */
|
||||
const sqlite3_module *p, /* Methods for the module */
|
||||
void *pClientData, /* Client data for xCreate/xConnect */
|
||||
void(*xDestroy)(void*) /* Module destructor function */
|
||||
);
|
||||
|
||||
@@ -4230,8 +4268,9 @@ SQLITE_EXPERIMENTAL int sqlite3_create_module_v2(
|
||||
** KEYWORDS: sqlite3_vtab
|
||||
** EXPERIMENTAL
|
||||
**
|
||||
** Every module implementation uses a subclass of the following structure
|
||||
** to describe a particular instance of the module. Each subclass will
|
||||
** Every [virtual table module] implementation uses a subclass
|
||||
** of the following structure to describe a particular instance
|
||||
** of the [virtual table]. Each subclass will
|
||||
** be tailored to the specific needs of the module implementation.
|
||||
** The purpose of this superclass is to define certain fields that are
|
||||
** common to all module implementations.
|
||||
@@ -4241,13 +4280,7 @@ SQLITE_EXPERIMENTAL int sqlite3_create_module_v2(
|
||||
** take care that any prior string is freed by a call to [sqlite3_free()]
|
||||
** prior to assigning a new string to zErrMsg. After the error message
|
||||
** is delivered up to the client application, the string will be automatically
|
||||
** freed by sqlite3_free() and the zErrMsg field will be zeroed. Note
|
||||
** that sqlite3_mprintf() and sqlite3_free() are used on the zErrMsg field
|
||||
** since virtual tables are commonly implemented in loadable extensions which
|
||||
** do not have access to sqlite3MPrintf() or sqlite3Free().
|
||||
**
|
||||
** This interface is experimental and is subject to change or
|
||||
** removal in future releases of SQLite.
|
||||
** freed by sqlite3_free() and the zErrMsg field will be zeroed.
|
||||
*/
|
||||
struct sqlite3_vtab {
|
||||
const sqlite3_module *pModule; /* The module for this virtual table */
|
||||
@@ -4258,20 +4291,21 @@ struct sqlite3_vtab {
|
||||
|
||||
/*
|
||||
** CAPI3REF: Virtual Table Cursor Object {H18020} <S20400>
|
||||
** KEYWORDS: sqlite3_vtab_cursor
|
||||
** KEYWORDS: sqlite3_vtab_cursor {virtual table cursor}
|
||||
** EXPERIMENTAL
|
||||
**
|
||||
** Every module implementation uses a subclass of the following structure
|
||||
** to describe cursors that point into the virtual table and are used
|
||||
** Every [virtual table module] implementation uses a subclass of the
|
||||
** following structure to describe cursors that point into the
|
||||
** [virtual table] and are used
|
||||
** to loop through the virtual table. Cursors are created using the
|
||||
** xOpen method of the module. Each module implementation will define
|
||||
** [sqlite3_module.xOpen | xOpen] method of the module and are destroyed
|
||||
** by the [sqlite3_module.xClose | xClose] method. Cussors are used
|
||||
** by the [xFilter], [xNext], [xEof], [xColumn], and [xRowid] methods
|
||||
** of the module. Each module implementation will define
|
||||
** the content of a cursor structure to suit its own needs.
|
||||
**
|
||||
** This superclass exists in order to define fields of the cursor that
|
||||
** are common to all implementations.
|
||||
**
|
||||
** This interface is experimental and is subject to change or
|
||||
** removal in future releases of SQLite.
|
||||
*/
|
||||
struct sqlite3_vtab_cursor {
|
||||
sqlite3_vtab *pVtab; /* Virtual table of this cursor */
|
||||
@@ -4282,21 +4316,20 @@ struct sqlite3_vtab_cursor {
|
||||
** CAPI3REF: Declare The Schema Of A Virtual Table {H18280} <S20400>
|
||||
** EXPERIMENTAL
|
||||
**
|
||||
** The xCreate and xConnect methods of a module use the following API
|
||||
** The [xCreate] and [xConnect] methods of a
|
||||
** [virtual table module] call this interface
|
||||
** to declare the format (the names and datatypes of the columns) of
|
||||
** the virtual tables they implement.
|
||||
**
|
||||
** This interface is experimental and is subject to change or
|
||||
** removal in future releases of SQLite.
|
||||
*/
|
||||
SQLITE_EXPERIMENTAL int sqlite3_declare_vtab(sqlite3*, const char *zCreateTable);
|
||||
SQLITE_EXPERIMENTAL int sqlite3_declare_vtab(sqlite3*, const char *zSQL);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Overload A Function For A Virtual Table {H18300} <S20400>
|
||||
** EXPERIMENTAL
|
||||
**
|
||||
** Virtual tables can provide alternative implementations of functions
|
||||
** using the xFindFunction method. But global versions of those functions
|
||||
** using the [xFindFunction] method of the [virtual table module].
|
||||
** But global versions of those functions
|
||||
** must exist in order to be overloaded.
|
||||
**
|
||||
** This API makes sure a global version of a function with a particular
|
||||
@@ -4305,10 +4338,7 @@ SQLITE_EXPERIMENTAL int sqlite3_declare_vtab(sqlite3*, const char *zCreateTable)
|
||||
** of the new function always causes an exception to be thrown. So
|
||||
** the new function is not good for anything by itself. Its only
|
||||
** purpose is to be a placeholder function that can be overloaded
|
||||
** by virtual tables.
|
||||
**
|
||||
** This API should be considered part of the virtual table interface,
|
||||
** which is experimental and subject to change.
|
||||
** by a [virtual table].
|
||||
*/
|
||||
SQLITE_EXPERIMENTAL int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg);
|
||||
|
||||
|
||||
+69
-57
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** Internal interface definitions for SQLite.
|
||||
**
|
||||
** @(#) $Id: sqliteInt.h,v 1.854 2009/04/08 13:51:51 drh Exp $
|
||||
** @(#) $Id: sqliteInt.h,v 1.868 2009/05/04 11:42:30 danielk1977 Exp $
|
||||
*/
|
||||
#ifndef _SQLITEINT_H_
|
||||
#define _SQLITEINT_H_
|
||||
@@ -145,10 +145,10 @@
|
||||
#endif
|
||||
|
||||
/*
|
||||
** If SQLITE_MALLOC_SOFT_LIMIT is defined, then try to keep the
|
||||
** If SQLITE_MALLOC_SOFT_LIMIT is not zero, then try to keep the
|
||||
** sizes of memory allocations below this value where possible.
|
||||
*/
|
||||
#if defined(SQLITE_POW2_MEMORY_SIZE) && !defined(SQLITE_MALLOC_SOFT_LIMIT)
|
||||
#if !defined(SQLITE_MALLOC_SOFT_LIMIT)
|
||||
# define SQLITE_MALLOC_SOFT_LIMIT 1024
|
||||
#endif
|
||||
|
||||
@@ -219,6 +219,20 @@
|
||||
# define TESTONLY(X)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Sometimes we need a small amount of code such as a variable initialization
|
||||
** to setup for a later assert() statement. We do not want this code to
|
||||
** appear when assert() is disabled. The following macro is therefore
|
||||
** used to contain that setup code. The "VVA" acronym stands for
|
||||
** "Verification, Validation, and Accreditation". In other words, the
|
||||
** code within VVA_ONLY() will only run during verification processes.
|
||||
*/
|
||||
#ifndef NDEBUG
|
||||
# define VVA_ONLY(X) X
|
||||
#else
|
||||
# define VVA_ONLY(X)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The ALWAYS and NEVER macros surround boolean expressions which
|
||||
** are intended to always be true or false, respectively. Such
|
||||
@@ -260,20 +274,6 @@
|
||||
# define unlikely(X) !!(X)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Sometimes we need a small amount of code such as a variable initialization
|
||||
** to setup for a later assert() statement. We do not want this code to
|
||||
** appear when assert() is disabled. The following macro is therefore
|
||||
** used to contain that setup code. The "VVA" acronym stands for
|
||||
** "Verification, Validation, and Accreditation". In other words, the
|
||||
** code within VVA_ONLY() will only run during verification processes.
|
||||
*/
|
||||
#ifndef NDEBUG
|
||||
# define VVA_ONLY(X) X
|
||||
#else
|
||||
# define VVA_ONLY(X)
|
||||
#endif
|
||||
|
||||
#include "sqlite3.h"
|
||||
#include "hash.h"
|
||||
#include "parse.h"
|
||||
@@ -639,7 +639,6 @@ struct Schema {
|
||||
Hash tblHash; /* All tables indexed by name */
|
||||
Hash idxHash; /* All (named) indices indexed by name */
|
||||
Hash trigHash; /* All triggers indexed by name */
|
||||
Hash aFKey; /* Foreign keys indexed by to-table */
|
||||
Table *pSeqTab; /* The sqlite_sequence table used by AUTOINCREMENT */
|
||||
u8 file_format; /* Schema format version for this file */
|
||||
u8 enc; /* Text encoding used by this database */
|
||||
@@ -1195,28 +1194,21 @@ struct Table {
|
||||
**
|
||||
** Each REFERENCES clause generates an instance of the following structure
|
||||
** which is attached to the from-table. The to-table need not exist when
|
||||
** the from-table is created. The existence of the to-table is not checked
|
||||
** until an attempt is made to insert data into the from-table.
|
||||
**
|
||||
** The sqlite.aFKey hash table stores pointers to this structure
|
||||
** given the name of a to-table. For each to-table, all foreign keys
|
||||
** associated with that table are on a linked list using the FKey.pNextTo
|
||||
** field.
|
||||
** the from-table is created. The existence of the to-table is not checked.
|
||||
*/
|
||||
struct FKey {
|
||||
Table *pFrom; /* The table that contains the REFERENCES clause */
|
||||
FKey *pNextFrom; /* Next foreign key in pFrom */
|
||||
char *zTo; /* Name of table that the key points to */
|
||||
FKey *pNextTo; /* Next foreign key that points to zTo */
|
||||
int nCol; /* Number of columns in this key */
|
||||
struct sColMap { /* Mapping of columns in pFrom to columns in zTo */
|
||||
int iFrom; /* Index of column in pFrom */
|
||||
char *zCol; /* Name of column in zTo. If 0 use PRIMARY KEY */
|
||||
} *aCol; /* One entry for each of nCol column s */
|
||||
u8 isDeferred; /* True if constraint checking is deferred till COMMIT */
|
||||
u8 updateConf; /* How to resolve conflicts that occur on UPDATE */
|
||||
u8 deleteConf; /* How to resolve conflicts that occur on DELETE */
|
||||
u8 insertConf; /* How to resolve conflicts that occur on INSERT */
|
||||
struct sColMap { /* Mapping of columns in pFrom to columns in zTo */
|
||||
int iFrom; /* Index of column in pFrom */
|
||||
char *zCol; /* Name of column in zTo. If 0 use PRIMARY KEY */
|
||||
} aCol[1]; /* One entry for each of nCol column s */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -1290,6 +1282,7 @@ struct UnpackedRecord {
|
||||
KeyInfo *pKeyInfo; /* Collation and sort-order information */
|
||||
u16 nField; /* Number of entries in apMem[] */
|
||||
u16 flags; /* Boolean settings. UNPACKED_... below */
|
||||
i64 rowid; /* Used by UNPACKED_PREFIX_SEARCH */
|
||||
Mem *aMem; /* Values */
|
||||
};
|
||||
|
||||
@@ -1301,6 +1294,7 @@ struct UnpackedRecord {
|
||||
#define UNPACKED_IGNORE_ROWID 0x0004 /* Ignore trailing rowid on key1 */
|
||||
#define UNPACKED_INCRKEY 0x0008 /* Make this key an epsilon larger */
|
||||
#define UNPACKED_PREFIX_MATCH 0x0010 /* A prefix match is considered OK */
|
||||
#define UNPACKED_PREFIX_SEARCH 0x0020 /* A prefix match is considered OK */
|
||||
|
||||
/*
|
||||
** Each SQL index is represented in memory by an
|
||||
@@ -1354,8 +1348,9 @@ struct Index {
|
||||
*/
|
||||
struct Token {
|
||||
const unsigned char *z; /* Text of the token. Not NULL-terminated! */
|
||||
unsigned dyn : 1; /* True for malloced memory, false for static */
|
||||
unsigned n : 31; /* Number of characters in this token */
|
||||
unsigned dyn : 1; /* True for malloced memory, false for static */
|
||||
unsigned quoted : 1; /* True if token still has its quotes */
|
||||
unsigned n : 30; /* Number of characters in this token */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -1517,7 +1512,7 @@ struct Expr {
|
||||
#define EP_Error 0x0008 /* Expression contains one or more errors */
|
||||
#define EP_Distinct 0x0010 /* Aggregate function with DISTINCT keyword */
|
||||
#define EP_VarSelect 0x0020 /* pSelect is correlated, not constant */
|
||||
#define EP_Dequoted 0x0040 /* True if the string has been dequoted */
|
||||
#define EP_DblQuoted 0x0040 /* token.z was originally in "..." */
|
||||
#define EP_InfixFunc 0x0080 /* True for an infix function: LIKE, GLOB, etc */
|
||||
#define EP_ExpCollate 0x0100 /* Collating sequence specified explicitly */
|
||||
#define EP_AnyAff 0x0200 /* Can take a cached column of any affinity */
|
||||
@@ -1742,15 +1737,17 @@ struct WhereLevel {
|
||||
};
|
||||
|
||||
/*
|
||||
** Flags appropriate for the wctrlFlags parameter of sqlite3WhereBegin().
|
||||
** Flags appropriate for the wctrlFlags parameter of sqlite3WhereBegin()
|
||||
** and the WhereInfo.wctrlFlags member.
|
||||
*/
|
||||
#define WHERE_ORDERBY_NORMAL 0x0000 /* No-op */
|
||||
#define WHERE_ORDERBY_MIN 0x0001 /* ORDER BY processing for min() func */
|
||||
#define WHERE_ORDERBY_MAX 0x0002 /* ORDER BY processing for max() func */
|
||||
#define WHERE_ONEPASS_DESIRED 0x0004 /* Want to do one-pass UPDATE/DELETE */
|
||||
#define WHERE_FILL_ROWSET 0x0008 /* Save results in a RowSet object */
|
||||
#define WHERE_OMIT_OPEN 0x0010 /* Table cursor are already open */
|
||||
#define WHERE_OMIT_CLOSE 0x0020 /* Omit close of table & index cursors */
|
||||
#define WHERE_DUPLICATES_OK 0x0008 /* Ok to return a row more than once */
|
||||
#define WHERE_OMIT_OPEN 0x0010 /* Table cursor are already open */
|
||||
#define WHERE_OMIT_CLOSE 0x0020 /* Omit close of table & index cursors */
|
||||
#define WHERE_FORCE_TABLE 0x0040 /* Do not use an index-only search */
|
||||
|
||||
/*
|
||||
** The WHERE clause processing routine has two halves. The
|
||||
@@ -1763,7 +1760,6 @@ struct WhereInfo {
|
||||
Parse *pParse; /* Parsing and code generating context */
|
||||
u16 wctrlFlags; /* Flags originally passed to sqlite3WhereBegin() */
|
||||
u8 okOnePass; /* Ok to use one-pass algorithm for UPDATE or DELETE */
|
||||
int regRowSet; /* Store rowids in this rowset if >=0 */
|
||||
SrcList *pTabList; /* List of tables in the join */
|
||||
int iTop; /* The very beginning of the WHERE loop */
|
||||
int iContinue; /* Jump here to continue with next record */
|
||||
@@ -1891,6 +1887,13 @@ struct SelectDest {
|
||||
int nMem; /* Number of registers allocated */
|
||||
};
|
||||
|
||||
/*
|
||||
** Size of the column cache
|
||||
*/
|
||||
#ifndef SQLITE_N_COLCACHE
|
||||
# define SQLITE_N_COLCACHE 10
|
||||
#endif
|
||||
|
||||
/*
|
||||
** An SQL parser context. A copy of this structure is passed through
|
||||
** the parser and down into all the parser action routine in order to
|
||||
@@ -1927,15 +1930,19 @@ struct Parse {
|
||||
int nMem; /* Number of memory cells used so far */
|
||||
int nSet; /* Number of sets used so far */
|
||||
int ckBase; /* Base register of data during check constraints */
|
||||
int disableColCache; /* True to disable adding to column cache */
|
||||
int nColCache; /* Number of entries in the column cache */
|
||||
int iColCache; /* Next entry of the cache to replace */
|
||||
int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */
|
||||
int iCacheCnt; /* Counter used to generate aColCache[].lru values */
|
||||
u8 nColCache; /* Number of entries in the column cache */
|
||||
u8 iColCache; /* Next entry of the cache to replace */
|
||||
struct yColCache {
|
||||
int iTable; /* Table cursor number */
|
||||
int iColumn; /* Table column number */
|
||||
char affChange; /* True if this register has had an affinity change */
|
||||
int iReg; /* Register holding value of this column */
|
||||
} aColCache[10]; /* One for each valid column cache entry */
|
||||
u8 affChange; /* True if this register has had an affinity change */
|
||||
u8 tempReg; /* iReg is a temp register that needs to be freed */
|
||||
int iLevel; /* Nesting level */
|
||||
int iReg; /* Reg with value of this column. 0 means none. */
|
||||
int lru; /* Least recently used entry has the smallest value */
|
||||
} aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */
|
||||
u32 writeMask; /* Start a write transaction on these databases */
|
||||
u32 cookieMask; /* Bitmask of schema verified databases */
|
||||
int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */
|
||||
@@ -1993,12 +2000,13 @@ struct AuthContext {
|
||||
};
|
||||
|
||||
/*
|
||||
** Bitfield flags for P2 value in OP_Insert and OP_Delete
|
||||
** Bitfield flags for P5 value in OP_Insert and OP_Delete
|
||||
*/
|
||||
#define OPFLAG_NCHANGE 1 /* Set to update db->nChange */
|
||||
#define OPFLAG_LASTROWID 2 /* Set to update db->lastRowid */
|
||||
#define OPFLAG_ISUPDATE 4 /* This OP_Insert is an sql UPDATE */
|
||||
#define OPFLAG_APPEND 8 /* This is likely to be an append */
|
||||
#define OPFLAG_NCHANGE 1 /* Set to update db->nChange */
|
||||
#define OPFLAG_LASTROWID 2 /* Set to update db->lastRowid */
|
||||
#define OPFLAG_ISUPDATE 4 /* This OP_Insert is an sql UPDATE */
|
||||
#define OPFLAG_APPEND 8 /* This is likely to be an append */
|
||||
#define OPFLAG_USESEEKRESULT 16 /* Try to avoid a seek in BtreeInsert() */
|
||||
|
||||
/*
|
||||
* Each trigger present in the database schema is stored as an instance of
|
||||
@@ -2289,7 +2297,6 @@ int sqlite3WalkSelectFrom(Walker*, Select*);
|
||||
int sqlite3StrICmp(const char *, const char *);
|
||||
int sqlite3StrNICmp(const char *, const char *, int);
|
||||
int sqlite3IsNumber(const char*, int*, u8);
|
||||
int sqlite3Strlen(sqlite3*, const char*);
|
||||
int sqlite3Strlen30(const char*);
|
||||
|
||||
int sqlite3MallocInit(void);
|
||||
@@ -2348,8 +2355,7 @@ char *sqlite3MAppendf(sqlite3*,char*,const char*,...);
|
||||
void sqlite3SetString(char **, sqlite3*, const char*, ...);
|
||||
void sqlite3ErrorMsg(Parse*, const char*, ...);
|
||||
void sqlite3ErrorClear(Parse*);
|
||||
void sqlite3Dequote(char*);
|
||||
void sqlite3DequoteExpr(Expr*);
|
||||
int sqlite3Dequote(char*);
|
||||
int sqlite3KeywordCode(const unsigned char*, int);
|
||||
int sqlite3RunParser(Parse*, const char*, char **);
|
||||
void sqlite3FinishCoding(Parse*);
|
||||
@@ -2397,6 +2403,7 @@ int sqlite3BitvecBuiltinTest(int,int*);
|
||||
RowSet *sqlite3RowSetInit(sqlite3*, void*, unsigned int);
|
||||
void sqlite3RowSetClear(RowSet*);
|
||||
void sqlite3RowSetInsert(RowSet*, i64);
|
||||
int sqlite3RowSetTest(RowSet*, u8 iBatch, i64);
|
||||
int sqlite3RowSetNext(RowSet*, i64*);
|
||||
|
||||
void sqlite3CreateView(Parse*,Token*,Token*,Token*,Select*,int,int);
|
||||
@@ -2438,14 +2445,17 @@ Expr *sqlite3LimitWhere(Parse *, SrcList *, Expr *, ExprList *, Expr *, Expr *,
|
||||
#endif
|
||||
void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
|
||||
void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
|
||||
WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**, u8, int);
|
||||
WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**, u16);
|
||||
void sqlite3WhereEnd(WhereInfo*);
|
||||
int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, int);
|
||||
void sqlite3ExprCodeMove(Parse*, int, int, int);
|
||||
void sqlite3ExprCodeCopy(Parse*, int, int, int);
|
||||
void sqlite3ExprClearColumnCache(Parse*, int);
|
||||
void sqlite3ExprCacheStore(Parse*, int, int, int);
|
||||
void sqlite3ExprCachePush(Parse*);
|
||||
void sqlite3ExprCachePop(Parse*, int);
|
||||
void sqlite3ExprCacheRemove(Parse*, int);
|
||||
void sqlite3ExprCacheClear(Parse*);
|
||||
void sqlite3ExprCacheAffinityChange(Parse*, int, int);
|
||||
void sqlite3ExprWritableRegister(Parse*,int);
|
||||
void sqlite3ExprHardCopy(Parse*,int,int);
|
||||
int sqlite3ExprCode(Parse*, Expr*, int);
|
||||
int sqlite3ExprCodeTemp(Parse*, Expr*, int*);
|
||||
@@ -2487,8 +2497,8 @@ void sqlite3GenerateRowDelete(Parse*, Table*, int, int, int);
|
||||
void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int*);
|
||||
int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int);
|
||||
void sqlite3GenerateConstraintChecks(Parse*,Table*,int,int,
|
||||
int*,int,int,int,int);
|
||||
void sqlite3CompleteInsertion(Parse*, Table*, int, int, int*, int, int, int);
|
||||
int*,int,int,int,int,int*);
|
||||
void sqlite3CompleteInsertion(Parse*, Table*, int, int, int*, int, int,int,int);
|
||||
int sqlite3OpenTableAndIndices(Parse*, Table*, int, int);
|
||||
void sqlite3BeginWriteOperation(Parse*, int, int);
|
||||
Expr *sqlite3ExprDup(sqlite3*,Expr*,int);
|
||||
@@ -2749,6 +2759,8 @@ int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *);
|
||||
int sqlite3Reprepare(Vdbe*);
|
||||
void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*);
|
||||
CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *);
|
||||
int sqlite3TempInMemory(const sqlite3*);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
+4
-18
@@ -13,7 +13,7 @@
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
**
|
||||
** $Id: test1.c,v 1.351 2009/04/08 15:45:32 drh Exp $
|
||||
** $Id: test1.c,v 1.353 2009/05/03 20:23:54 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
@@ -1217,18 +1217,6 @@ static int sqlite3_mprintf_int(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** If zNum represents an integer that will fit in 64-bits, then set
|
||||
** *pValue to that integer and return true. Otherwise return false.
|
||||
*/
|
||||
static int sqlite3GetInt64(const char *zNum, i64 *pValue){
|
||||
if( sqlite3FitsIn64Bits(zNum, 0) ){
|
||||
sqlite3Atoi64(zNum, pValue);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_mprintf_int64 FORMAT INTEGER INTEGER INTEGER
|
||||
**
|
||||
@@ -1249,7 +1237,7 @@ static int sqlite3_mprintf_int64(
|
||||
return TCL_ERROR;
|
||||
}
|
||||
for(i=2; i<5; i++){
|
||||
if( !sqlite3GetInt64(argv[i], &a[i-2]) ){
|
||||
if( !sqlite3Atoi64(argv[i], &a[i-2]) ){
|
||||
Tcl_AppendResult(interp, "argument is not a valid 64-bit integer", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
@@ -2488,11 +2476,9 @@ static int add_alignment_test_collations(
|
||||
sqlite3 *db;
|
||||
if( objc>=2 ){
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
sqlite3_create_collation(db, "utf16_unaligned",
|
||||
SQLITE_UTF16,
|
||||
sqlite3_create_collation(db, "utf16_unaligned", SQLITE_UTF16,
|
||||
0, alignmentCollFunc);
|
||||
sqlite3_create_collation(db, "utf16_aligned",
|
||||
SQLITE_UTF16 | SQLITE_UTF16_ALIGNED,
|
||||
sqlite3_create_collation(db, "utf16_aligned", SQLITE_UTF16_ALIGNED,
|
||||
0, alignmentCollFunc);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
|
||||
+3
-3
@@ -13,7 +13,7 @@
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
**
|
||||
** $Id: test3.c,v 1.103 2009/03/18 10:33:02 danielk1977 Exp $
|
||||
** $Id: test3.c,v 1.104 2009/05/04 11:42:30 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "btreeInt.h"
|
||||
@@ -782,7 +782,7 @@ static int btree_insert(
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pBuf = Tcl_GetByteArrayFromObj(objv[3], &len);
|
||||
rc = sqlite3BtreeInsert(pCur, 0, iKey, pBuf, len, nZero, 0);
|
||||
rc = sqlite3BtreeInsert(pCur, 0, iKey, pBuf, len, nZero, 0, 0);
|
||||
}else{
|
||||
int keylen;
|
||||
int dlen;
|
||||
@@ -790,7 +790,7 @@ static int btree_insert(
|
||||
unsigned char *pDBuf;
|
||||
pKBuf = Tcl_GetByteArrayFromObj(objv[2], &keylen);
|
||||
pDBuf = Tcl_GetByteArrayFromObj(objv[3], &dlen);
|
||||
rc = sqlite3BtreeInsert(pCur, pKBuf, keylen, pDBuf, dlen, nZero, 0);
|
||||
rc = sqlite3BtreeInsert(pCur, pKBuf, keylen, pDBuf, dlen, nZero, 0, 0);
|
||||
}
|
||||
sqlite3BtreeLeave(pCur->pBtree);
|
||||
if( rc ){
|
||||
|
||||
+5
-5
@@ -13,7 +13,7 @@
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
**
|
||||
** $Id: test8.c,v 1.76 2009/04/08 15:45:32 drh Exp $
|
||||
** $Id: test8.c,v 1.78 2009/04/29 11:50:54 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
@@ -695,7 +695,7 @@ static int echoFilter(
|
||||
rc = sqlite3_prepare(db, idxStr, -1, &pCur->pStmt, 0);
|
||||
assert( pCur->pStmt || rc!=SQLITE_OK );
|
||||
for(i=0; rc==SQLITE_OK && i<argc; i++){
|
||||
sqlite3_bind_value(pCur->pStmt, i+1, argv[i]);
|
||||
rc = sqlite3_bind_value(pCur->pStmt, i+1, argv[i]);
|
||||
}
|
||||
|
||||
/* If everything was successful, advance to the first row of the scan */
|
||||
@@ -893,16 +893,16 @@ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
pIdxInfo->idxNum = hashString(zQuery);
|
||||
pIdxInfo->idxStr = zQuery;
|
||||
pIdxInfo->needToFreeIdxStr = 1;
|
||||
if (useCost) {
|
||||
if( useCost ){
|
||||
pIdxInfo->estimatedCost = cost;
|
||||
} else if( useIdx ){
|
||||
}else if( useIdx ){
|
||||
/* Approximation of log2(nRow). */
|
||||
for( ii=0; ii<(sizeof(int)*8); ii++ ){
|
||||
if( nRow & (1<<ii) ){
|
||||
pIdxInfo->estimatedCost = (double)ii;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}else{
|
||||
pIdxInfo->estimatedCost = (double)nRow;
|
||||
}
|
||||
return rc;
|
||||
|
||||
+151
-1760
File diff suppressed because it is too large
Load Diff
+3
-1
@@ -15,7 +15,7 @@
|
||||
** individual tokens and sends those tokens one-by-one over to the
|
||||
** parser for analysis.
|
||||
**
|
||||
** $Id: tokenize.c,v 1.155 2009/03/31 03:41:57 shane Exp $
|
||||
** $Id: tokenize.c,v 1.156 2009/05/01 21:13:37 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <stdlib.h>
|
||||
@@ -413,10 +413,12 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
|
||||
assert( pParse->apVarExpr==0 );
|
||||
enableLookaside = db->lookaside.bEnabled;
|
||||
if( db->lookaside.pStart ) db->lookaside.bEnabled = 1;
|
||||
pParse->sLastToken.quoted = 1;
|
||||
while( !db->mallocFailed && zSql[i]!=0 ){
|
||||
assert( i>=0 );
|
||||
pParse->sLastToken.z = (u8*)&zSql[i];
|
||||
assert( pParse->sLastToken.dyn==0 );
|
||||
assert( pParse->sLastToken.quoted );
|
||||
pParse->sLastToken.n = sqlite3GetToken((unsigned char*)&zSql[i],&tokenType);
|
||||
i += pParse->sLastToken.n;
|
||||
if( i>mxSqlLen ){
|
||||
|
||||
+5
-4
@@ -10,7 +10,7 @@
|
||||
*************************************************************************
|
||||
**
|
||||
**
|
||||
** $Id: trigger.c,v 1.135 2009/02/28 10:47:42 danielk1977 Exp $
|
||||
** $Id: trigger.c,v 1.138 2009/05/06 18:42:21 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -285,7 +285,7 @@ void sqlite3FinishTrigger(
|
||||
db->mallocFailed = 1;
|
||||
}else if( pLink->pSchema==pLink->pTabSchema ){
|
||||
Table *pTab;
|
||||
int n = sqlite3Strlen30(pLink->table) + 1;
|
||||
int n = sqlite3Strlen30(pLink->table);
|
||||
pTab = sqlite3HashFind(&pLink->pTabSchema->tblHash, pLink->table, n);
|
||||
assert( pTab!=0 );
|
||||
pLink->pNext = pTab->pTrigger;
|
||||
@@ -512,7 +512,7 @@ drop_trigger_cleanup:
|
||||
** is set on.
|
||||
*/
|
||||
static Table *tableOfTrigger(Trigger *pTrigger){
|
||||
int n = sqlite3Strlen30(pTrigger->table) + 1;
|
||||
int n = sqlite3Strlen30(pTrigger->table);
|
||||
return sqlite3HashFind(&pTrigger->pTabSchema->tblHash, pTrigger->table, n);
|
||||
}
|
||||
|
||||
@@ -663,6 +663,7 @@ static SrcList *targetSrcList(
|
||||
assert( iDb<pParse->db->nDb );
|
||||
sDb.z = (u8*)pParse->db->aDb[iDb].zName;
|
||||
sDb.n = sqlite3Strlen30((char*)sDb.z);
|
||||
sDb.quoted = 0;
|
||||
pSrc = sqlite3SrcListAppend(pParse->db, 0, &sDb, &pStep->target);
|
||||
} else {
|
||||
pSrc = sqlite3SrcListAppend(pParse->db, 0, &pStep->target, 0);
|
||||
@@ -689,7 +690,7 @@ static int codeTriggerProgram(
|
||||
sqlite3VdbeAddOp2(v, OP_ContextPush, 0, 0);
|
||||
VdbeComment((v, "begin trigger %s", pStepList->pTrig->name));
|
||||
while( pTriggerStep ){
|
||||
sqlite3ExprClearColumnCache(pParse, -1);
|
||||
sqlite3ExprCacheClear(pParse);
|
||||
orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin;
|
||||
pParse->trigStack->orconf = orconf;
|
||||
switch( pTriggerStep->op ){
|
||||
|
||||
+10
-10
@@ -12,7 +12,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle UPDATE statements.
|
||||
**
|
||||
** $Id: update.c,v 1.196 2009/02/28 10:47:42 danielk1977 Exp $
|
||||
** $Id: update.c,v 1.200 2009/05/05 15:46:10 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -55,7 +55,8 @@ static void updateVirtualTable(
|
||||
** sqlite3_value objects.
|
||||
*/
|
||||
void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i){
|
||||
if( pTab && !pTab->pSelect ){
|
||||
assert( pTab!=0 );
|
||||
if( !pTab->pSelect ){
|
||||
sqlite3_value *pValue;
|
||||
u8 enc = ENC(sqlite3VdbeDb(v));
|
||||
Column *pCol = &pTab->aCol[i];
|
||||
@@ -345,14 +346,13 @@ void sqlite3Update(
|
||||
/* Begin the database scan
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0,
|
||||
WHERE_ONEPASS_DESIRED, 0);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere,0, WHERE_ONEPASS_DESIRED);
|
||||
if( pWInfo==0 ) goto update_cleanup;
|
||||
okOnePass = pWInfo->okOnePass;
|
||||
|
||||
/* Remember the rowid of every item to be updated.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, IsVirtual(pTab)?OP_VRowid:OP_Rowid, iCur, regOldRowid);
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regOldRowid);
|
||||
if( !okOnePass ){
|
||||
regRowSet = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
|
||||
@@ -369,7 +369,7 @@ void sqlite3Update(
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
|
||||
}
|
||||
|
||||
if( !isView && !IsVirtual(pTab) ){
|
||||
if( !isView ){
|
||||
/*
|
||||
** Open every index that needs updating. Note that if any
|
||||
** index could potentially invoke a REPLACE conflict resolution
|
||||
@@ -448,7 +448,7 @@ void sqlite3Update(
|
||||
continue;
|
||||
}
|
||||
j = aXRef[i];
|
||||
if( new_col_mask&((u32)1<<i) || new_col_mask==0xffffffff ){
|
||||
if( (i<32 && (new_col_mask&((u32)1<<i))!=0) || new_col_mask==0xffffffff ){
|
||||
if( j<0 ){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regCols+i);
|
||||
sqlite3ColumnDefault(v, pTab, i);
|
||||
@@ -474,7 +474,7 @@ void sqlite3Update(
|
||||
sqlite3VdbeJumpHere(v, iEndBeforeTrigger);
|
||||
}
|
||||
|
||||
if( !isView && !IsVirtual(pTab) ){
|
||||
if( !isView ){
|
||||
/* Loop over every record that needs updating. We have to load
|
||||
** the old data for each record to be updated because some columns
|
||||
** might not change and we will need to copy the old value.
|
||||
@@ -512,7 +512,7 @@ void sqlite3Update(
|
||||
*/
|
||||
sqlite3GenerateConstraintChecks(pParse, pTab, iCur, regNewRowid,
|
||||
aRegIdx, chngRowid, 1,
|
||||
onError, addr);
|
||||
onError, addr, 0);
|
||||
|
||||
/* Delete the old indices for the current record.
|
||||
*/
|
||||
@@ -529,7 +529,7 @@ void sqlite3Update(
|
||||
/* Create the new index entries and the new record.
|
||||
*/
|
||||
sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid,
|
||||
aRegIdx, 1, -1, 0);
|
||||
aRegIdx, 1, -1, 0, 0);
|
||||
}
|
||||
|
||||
/* Increment the row counter
|
||||
|
||||
+109
-49
@@ -14,10 +14,13 @@
|
||||
** This file contains functions for allocating memory, comparing
|
||||
** strings, and stuff like that.
|
||||
**
|
||||
** $Id: util.c,v 1.249 2009/03/01 22:29:20 drh Exp $
|
||||
** $Id: util.c,v 1.254 2009/05/06 19:03:14 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <stdarg.h>
|
||||
#ifdef SQLITE_HAVE_ISNAN
|
||||
# include <math.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Routine needed to support the testcase() macro.
|
||||
@@ -52,9 +55,20 @@ int sqlite3Assert(void){
|
||||
|
||||
/*
|
||||
** Return true if the floating point value is Not a Number (NaN).
|
||||
**
|
||||
** Use the math library isnan() function if compiled with SQLITE_HAVE_ISNAN.
|
||||
** Otherwise, we have our own implementation that works on most systems.
|
||||
*/
|
||||
int sqlite3IsNaN(double x){
|
||||
/* This NaN test sometimes fails if compiled on GCC with -ffast-math.
|
||||
int rc; /* The value return */
|
||||
#if !defined(SQLITE_HAVE_ISNAN)
|
||||
/*
|
||||
** Systems that support the isnan() library function should probably
|
||||
** make use of it by compiling with -DSQLITE_HAVE_ISNAN. But we have
|
||||
** found that many systems do not have a working isnan() function so
|
||||
** this implementation is provided as an alternative.
|
||||
**
|
||||
** This NaN test sometimes fails if compiled on GCC with -ffast-math.
|
||||
** On the other hand, the use of -ffast-math comes with the following
|
||||
** warning:
|
||||
**
|
||||
@@ -76,12 +90,21 @@ int sqlite3IsNaN(double x){
|
||||
#endif
|
||||
volatile double y = x;
|
||||
volatile double z = y;
|
||||
return y!=z;
|
||||
rc = (y!=z);
|
||||
#else /* if defined(SQLITE_HAVE_ISNAN) */
|
||||
rc = isnan(x);
|
||||
#endif /* SQLITE_HAVE_ISNAN */
|
||||
testcase( rc );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Compute a string length that is limited to what can be stored in
|
||||
** lower 30 bits of a 32-bit signed integer.
|
||||
**
|
||||
** The value returned will never be negative. Nor will it ever be greater
|
||||
** than the actual length of the string. For very long strings (greater
|
||||
** than 1GiB) the value returned might be less than the true string length.
|
||||
*/
|
||||
int sqlite3Strlen30(const char *z){
|
||||
const char *z2 = z;
|
||||
@@ -89,24 +112,6 @@ int sqlite3Strlen30(const char *z){
|
||||
return 0x3fffffff & (int)(z2 - z);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the length of a string, except do not allow the string length
|
||||
** to exceed the SQLITE_LIMIT_LENGTH setting.
|
||||
*/
|
||||
int sqlite3Strlen(sqlite3 *db, const char *z){
|
||||
const char *z2 = z;
|
||||
int len;
|
||||
int x;
|
||||
while( *z2 ){ z2++; }
|
||||
x = (int)(z2 - z);
|
||||
len = 0x7fffffff & x;
|
||||
if( len!=x || len > db->aLimit[SQLITE_LIMIT_LENGTH] ){
|
||||
return db->aLimit[SQLITE_LIMIT_LENGTH];
|
||||
}else{
|
||||
return len;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the most recent error code and error string for the sqlite
|
||||
** handle "db". The error code is set to "err_code".
|
||||
@@ -165,6 +170,7 @@ void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){
|
||||
va_list ap;
|
||||
sqlite3 *db = pParse->db;
|
||||
pParse->nErr++;
|
||||
testcase( pParse->zErrMsg!=0 );
|
||||
sqlite3DbFree(db, pParse->zErrMsg);
|
||||
va_start(ap, zFormat);
|
||||
pParse->zErrMsg = sqlite3VMPrintf(db, zFormat, ap);
|
||||
@@ -189,35 +195,43 @@ void sqlite3ErrorClear(Parse *pParse){
|
||||
** input does not begin with a quote character, then this routine
|
||||
** is a no-op.
|
||||
**
|
||||
** The input string must be zero-terminated. A new zero-terminator
|
||||
** is added to the dequoted string.
|
||||
**
|
||||
** The return value is -1 if no dequoting occurs or the length of the
|
||||
** dequoted string, exclusive of the zero terminator, if dequoting does
|
||||
** occur.
|
||||
**
|
||||
** 2002-Feb-14: This routine is extended to remove MS-Access style
|
||||
** brackets from around identifers. For example: "[a-b-c]" becomes
|
||||
** "a-b-c".
|
||||
*/
|
||||
void sqlite3Dequote(char *z){
|
||||
int sqlite3Dequote(char *z){
|
||||
char quote;
|
||||
int i, j;
|
||||
if( z==0 ) return;
|
||||
if( z==0 ) return -1;
|
||||
quote = z[0];
|
||||
switch( quote ){
|
||||
case '\'': break;
|
||||
case '"': break;
|
||||
case '`': break; /* For MySQL compatibility */
|
||||
case '[': quote = ']'; break; /* For MS SqlServer compatibility */
|
||||
default: return;
|
||||
default: return -1;
|
||||
}
|
||||
for(i=1, j=0; z[i]; i++){
|
||||
for(i=1, j=0; ALWAYS(z[i]); i++){
|
||||
if( z[i]==quote ){
|
||||
if( z[i+1]==quote ){
|
||||
z[j++] = quote;
|
||||
i++;
|
||||
}else{
|
||||
z[j++] = 0;
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
z[j++] = z[i];
|
||||
}
|
||||
}
|
||||
z[j] = 0;
|
||||
return j;
|
||||
}
|
||||
|
||||
/* Convenient short-hand */
|
||||
@@ -243,10 +257,15 @@ int sqlite3StrNICmp(const char *zLeft, const char *zRight, int N){
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if z is a pure numeric string. Return FALSE if the
|
||||
** string contains any character which is not part of a number. If
|
||||
** the string is numeric and contains the '.' character, set *realnum
|
||||
** to TRUE (otherwise FALSE).
|
||||
** Return TRUE if z is a pure numeric string. Return FALSE and leave
|
||||
** *realnum unchanged if the string contains any character which is not
|
||||
** part of a number.
|
||||
**
|
||||
** If the string is pure numeric, set *realnum to TRUE if the string
|
||||
** contains the '.' character or an "E+000" style exponentiation suffix.
|
||||
** Otherwise set *realnum to FALSE. Note that just becaue *realnum is
|
||||
** false does not mean that the number can be successfully converted into
|
||||
** an integer - it might be too big.
|
||||
**
|
||||
** An empty string is considered non-numeric.
|
||||
*/
|
||||
@@ -258,20 +277,20 @@ int sqlite3IsNumber(const char *z, int *realnum, u8 enc){
|
||||
return 0;
|
||||
}
|
||||
z += incr;
|
||||
if( realnum ) *realnum = 0;
|
||||
*realnum = 0;
|
||||
while( sqlite3Isdigit(*z) ){ z += incr; }
|
||||
if( *z=='.' ){
|
||||
z += incr;
|
||||
if( !sqlite3Isdigit(*z) ) return 0;
|
||||
while( sqlite3Isdigit(*z) ){ z += incr; }
|
||||
if( realnum ) *realnum = 1;
|
||||
*realnum = 1;
|
||||
}
|
||||
if( *z=='e' || *z=='E' ){
|
||||
z += incr;
|
||||
if( *z=='+' || *z=='-' ) z += incr;
|
||||
if( !sqlite3Isdigit(*z) ) return 0;
|
||||
while( sqlite3Isdigit(*z) ){ z += incr; }
|
||||
if( realnum ) *realnum = 1;
|
||||
*realnum = 1;
|
||||
}
|
||||
return *z==0;
|
||||
}
|
||||
@@ -430,25 +449,25 @@ int sqlite3Atoi64(const char *zNum, i64 *pNum){
|
||||
}
|
||||
|
||||
/*
|
||||
** The string zNum represents an integer. There might be some other
|
||||
** The string zNum represents an unsigned integer. There might be some other
|
||||
** information following the integer too, but that part is ignored.
|
||||
** If the integer that the prefix of zNum represents will fit in a
|
||||
** 64-bit signed integer, return TRUE. Otherwise return FALSE.
|
||||
**
|
||||
** This routine returns FALSE for the string -9223372036854775808 even that
|
||||
** that number will, in theory fit in a 64-bit integer. Positive
|
||||
** 9223373036854775808 will not fit in 64 bits. So it seems safer to return
|
||||
** false.
|
||||
** If the negFlag parameter is true, that means that zNum really represents
|
||||
** a negative number. (The leading "-" is omitted from zNum.) This
|
||||
** parameter is needed to determine a boundary case. A string
|
||||
** of "9223373036854775808" returns false if negFlag is false or true
|
||||
** if negFlag is true.
|
||||
**
|
||||
** Leading zeros are ignored.
|
||||
*/
|
||||
int sqlite3FitsIn64Bits(const char *zNum, int negFlag){
|
||||
int i, c;
|
||||
int neg = 0;
|
||||
if( *zNum=='-' ){
|
||||
neg = 1;
|
||||
zNum++;
|
||||
}else if( *zNum=='+' ){
|
||||
zNum++;
|
||||
}
|
||||
|
||||
assert( zNum[0]>='0' && zNum[0]<='9' ); /* zNum is an unsigned number */
|
||||
|
||||
if( negFlag ) neg = 1-neg;
|
||||
while( *zNum=='0' ){
|
||||
zNum++; /* Skip leading zeros. Ticket #2454 */
|
||||
@@ -753,33 +772,40 @@ u8 sqlite3GetVarint(const unsigned char *p, u64 *v){
|
||||
u8 sqlite3GetVarint32(const unsigned char *p, u32 *v){
|
||||
u32 a,b;
|
||||
|
||||
/* The 1-byte case. Overwhelmingly the most common. Handled inline
|
||||
** by the getVarin32() macro */
|
||||
a = *p;
|
||||
/* a: p0 (unmasked) */
|
||||
#ifndef getVarint32
|
||||
if (!(a&0x80))
|
||||
{
|
||||
/* Values between 0 and 127 */
|
||||
*v = a;
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The 2-byte case */
|
||||
p++;
|
||||
b = *p;
|
||||
/* b: p1 (unmasked) */
|
||||
if (!(b&0x80))
|
||||
{
|
||||
/* Values between 128 and 16383 */
|
||||
a &= 0x7f;
|
||||
a = a<<7;
|
||||
*v = a | b;
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* The 3-byte case */
|
||||
p++;
|
||||
a = a<<14;
|
||||
a |= *p;
|
||||
/* a: p0<<14 | p2 (unmasked) */
|
||||
if (!(a&0x80))
|
||||
{
|
||||
/* Values between 16384 and 2097151 */
|
||||
a &= (0x7f<<14)|(0x7f);
|
||||
b &= 0x7f;
|
||||
b = b<<7;
|
||||
@@ -787,12 +813,39 @@ u8 sqlite3GetVarint32(const unsigned char *p, u32 *v){
|
||||
return 3;
|
||||
}
|
||||
|
||||
/* A 32-bit varint is used to store size information in btrees.
|
||||
** Objects are rarely larger than 2MiB limit of a 3-byte varint.
|
||||
** A 3-byte varint is sufficient, for example, to record the size
|
||||
** of a 1048569-byte BLOB or string.
|
||||
**
|
||||
** We only unroll the first 1-, 2-, and 3- byte cases. The very
|
||||
** rare larger cases can be handled by the slower 64-bit varint
|
||||
** routine.
|
||||
*/
|
||||
#if 1
|
||||
{
|
||||
u64 v64;
|
||||
u8 n;
|
||||
|
||||
p -= 2;
|
||||
n = sqlite3GetVarint(p, &v64);
|
||||
assert( n>3 && n<=9 );
|
||||
*v = (u32)v64;
|
||||
return n;
|
||||
}
|
||||
|
||||
#else
|
||||
/* For following code (kept for historical record only) shows an
|
||||
** unrolling for the 3- and 4-byte varint cases. This code is
|
||||
** slightly faster, but it is also larger and much harder to test.
|
||||
*/
|
||||
p++;
|
||||
b = b<<14;
|
||||
b |= *p;
|
||||
/* b: p1<<14 | p3 (unmasked) */
|
||||
if (!(b&0x80))
|
||||
{
|
||||
/* Values between 2097152 and 268435455 */
|
||||
b &= (0x7f<<14)|(0x7f);
|
||||
a &= (0x7f<<14)|(0x7f);
|
||||
a = a<<7;
|
||||
@@ -806,6 +859,7 @@ u8 sqlite3GetVarint32(const unsigned char *p, u32 *v){
|
||||
/* a: p0<<28 | p2<<14 | p4 (unmasked) */
|
||||
if (!(a&0x80))
|
||||
{
|
||||
/* Walues between 268435456 and 34359738367 */
|
||||
a &= (0x1f<<28)|(0x7f<<14)|(0x7f);
|
||||
b &= (0x1f<<28)|(0x7f<<14)|(0x7f);
|
||||
b = b<<7;
|
||||
@@ -827,6 +881,7 @@ u8 sqlite3GetVarint32(const unsigned char *p, u32 *v){
|
||||
*v = (u32)v64;
|
||||
return n;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -838,7 +893,7 @@ int sqlite3VarintLen(u64 v){
|
||||
do{
|
||||
i++;
|
||||
v >>= 7;
|
||||
}while( v!=0 && i<9 );
|
||||
}while( v!=0 && ALWAYS(i<9) );
|
||||
return i;
|
||||
}
|
||||
|
||||
@@ -976,13 +1031,18 @@ int sqlite3SafetyCheckOk(sqlite3 *db){
|
||||
u32 magic;
|
||||
if( db==0 ) return 0;
|
||||
magic = db->magic;
|
||||
if( magic!=SQLITE_MAGIC_OPEN &&
|
||||
magic!=SQLITE_MAGIC_BUSY ) return 0;
|
||||
return 1;
|
||||
if( magic!=SQLITE_MAGIC_OPEN
|
||||
#ifdef SQLITE_DEBUG
|
||||
&& magic!=SQLITE_MAGIC_BUSY
|
||||
#endif
|
||||
){
|
||||
return 0;
|
||||
}else{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
int sqlite3SafetyCheckSickOrOk(sqlite3 *db){
|
||||
u32 magic;
|
||||
if( db==0 ) return 0;
|
||||
magic = db->magic;
|
||||
if( magic!=SQLITE_MAGIC_SICK &&
|
||||
magic!=SQLITE_MAGIC_OPEN &&
|
||||
|
||||
+12
-9
@@ -14,7 +14,7 @@
|
||||
** Most of the code in this file may be omitted by defining the
|
||||
** SQLITE_OMIT_VACUUM macro.
|
||||
**
|
||||
** $Id: vacuum.c,v 1.87 2009/04/02 20:16:59 drh Exp $
|
||||
** $Id: vacuum.c,v 1.88 2009/05/05 17:37:23 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "vdbeInt.h"
|
||||
@@ -25,13 +25,15 @@
|
||||
*/
|
||||
static int execSql(sqlite3 *db, const char *zSql){
|
||||
sqlite3_stmt *pStmt;
|
||||
VVA_ONLY( int rc; )
|
||||
if( !zSql ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
if( SQLITE_OK!=sqlite3_prepare(db, zSql, -1, &pStmt, 0) ){
|
||||
return sqlite3_errcode(db);
|
||||
}
|
||||
while( SQLITE_ROW==sqlite3_step(pStmt) ){}
|
||||
VVA_ONLY( rc = ) sqlite3_step(pStmt);
|
||||
assert( rc!=SQLITE_ROW );
|
||||
return sqlite3_finalize(pStmt);
|
||||
}
|
||||
|
||||
@@ -142,7 +144,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
||||
|
||||
if( sqlite3BtreeSetPageSize(pTemp, sqlite3BtreeGetPageSize(pMain), nRes, 0)
|
||||
|| (!isMemDb && sqlite3BtreeSetPageSize(pTemp, db->nextPagesize, nRes, 0))
|
||||
|| db->mallocFailed
|
||||
|| NEVER(db->mallocFailed)
|
||||
){
|
||||
rc = SQLITE_NOMEM;
|
||||
goto end_of_vacuum;
|
||||
@@ -230,7 +232,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
||||
** opened for writing. This way, the SQL transaction used to create the
|
||||
** temporary database never needs to be committed.
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
{
|
||||
u32 meta;
|
||||
int i;
|
||||
|
||||
@@ -252,10 +254,12 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
||||
|
||||
/* Copy Btree meta values */
|
||||
for(i=0; i<ArraySize(aCopy); i+=2){
|
||||
/* GetMeta() and UpdateMeta() cannot fail in this context because
|
||||
** we already have page 1 loaded into cache and marked dirty. */
|
||||
rc = sqlite3BtreeGetMeta(pMain, aCopy[i], &meta);
|
||||
if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
||||
if( NEVER(rc!=SQLITE_OK) ) goto end_of_vacuum;
|
||||
rc = sqlite3BtreeUpdateMeta(pTemp, aCopy[i], meta+aCopy[i+1]);
|
||||
if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
||||
if( NEVER(rc!=SQLITE_OK) ) goto end_of_vacuum;
|
||||
}
|
||||
|
||||
rc = sqlite3BtreeCopyFile(pMain, pTemp);
|
||||
@@ -267,9 +271,8 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
||||
#endif
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3BtreeSetPageSize(pMain, sqlite3BtreeGetPageSize(pTemp), nRes,1);
|
||||
}
|
||||
assert( rc==SQLITE_OK );
|
||||
rc = sqlite3BtreeSetPageSize(pMain, sqlite3BtreeGetPageSize(pTemp), nRes,1);
|
||||
|
||||
end_of_vacuum:
|
||||
/* Restore the original value of db->flags */
|
||||
|
||||
+160
-128
@@ -43,7 +43,7 @@
|
||||
** in this file for details. If in doubt, do not deviate from existing
|
||||
** commenting and indentation practices when changing or adding code.
|
||||
**
|
||||
** $Id: vdbe.c,v 1.832 2009/04/10 12:55:17 danielk1977 Exp $
|
||||
** $Id: vdbe.c,v 1.842 2009/05/06 18:57:10 shane Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "vdbeInt.h"
|
||||
@@ -428,6 +428,8 @@ static void memTracePrint(FILE *out, Mem *p){
|
||||
fprintf(out, " i:%lld", p->u.i);
|
||||
}else if( p->flags & MEM_Real ){
|
||||
fprintf(out, " r:%g", p->r);
|
||||
}else if( p->flags & MEM_RowSet ){
|
||||
fprintf(out, " (rowset)");
|
||||
}else{
|
||||
char zBuf[200];
|
||||
sqlite3VdbeMemPrettyPrint(p, zBuf);
|
||||
@@ -2462,7 +2464,7 @@ case OP_Savepoint: {
|
||||
db->isTransactionSavepoint = 1;
|
||||
}else{
|
||||
db->nSavepoint++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Link the new savepoint into the database handle's list. */
|
||||
pNew->pNext = db->pSavepoint;
|
||||
@@ -3336,103 +3338,82 @@ case OP_Found: { /* jump, in3 */
|
||||
|
||||
/* Opcode: IsUnique P1 P2 P3 P4 *
|
||||
**
|
||||
** The P3 register contains an integer record number. Call this
|
||||
** record number R. The P4 register contains an index key created
|
||||
** using MakeRecord. Call it K.
|
||||
**
|
||||
** P1 is an index. So it has no data and its key consists of a
|
||||
** record generated by OP_MakeRecord where the last field is the
|
||||
** Cursor P1 is open on an index. So it has no data and its key consists
|
||||
** of a record generated by OP_MakeRecord where the last field is the
|
||||
** rowid of the entry that the index refers to.
|
||||
**
|
||||
** This instruction asks if there is an entry in P1 where the
|
||||
** fields matches K but the rowid is different from R.
|
||||
** If there is no such entry, then there is an immediate
|
||||
** jump to P2. If any entry does exist where the index string
|
||||
** matches K but the record number is not R, then the record
|
||||
** number for that entry is written into P3 and control
|
||||
** falls through to the next instruction.
|
||||
**
|
||||
** The P3 register contains an integer record number. Call this record
|
||||
** number R. Register P4 is the first in a set of N contiguous registers
|
||||
** that make up an unpacked index key that can be used with cursor P1.
|
||||
** The value of N can be inferred from the cursor. N includes the rowid
|
||||
** value appended to the end of the index record. This rowid value may
|
||||
** or may not be the same as R.
|
||||
**
|
||||
** If any of the N registers beginning with register P4 contains a NULL
|
||||
** value, jump immediately to P2.
|
||||
**
|
||||
** Otherwise, this instruction checks if cursor P1 contains an entry
|
||||
** where the first (N-1) fields match but the rowid value at the end
|
||||
** of the index entry is not R. If there is no such entry, control jumps
|
||||
** to instruction P2. Otherwise, the rowid of the conflicting index
|
||||
** entry is copied to register P3 and control falls through to the next
|
||||
** instruction.
|
||||
**
|
||||
** See also: NotFound, NotExists, Found
|
||||
*/
|
||||
case OP_IsUnique: { /* jump, in3 */
|
||||
int i = pOp->p1;
|
||||
u16 ii;
|
||||
VdbeCursor *pCx;
|
||||
BtCursor *pCrsr;
|
||||
Mem *pK;
|
||||
i64 R;
|
||||
u16 nField;
|
||||
Mem *aMem = &p->aMem[pOp->p4.i];
|
||||
|
||||
/* Pop the value R off the top of the stack
|
||||
*/
|
||||
/* Assert that the values of parameters P1 and P4 are in range. */
|
||||
assert( pOp->p4type==P4_INT32 );
|
||||
assert( pOp->p4.i>0 && pOp->p4.i<=p->nMem );
|
||||
pK = &p->aMem[pOp->p4.i];
|
||||
sqlite3VdbeMemIntegerify(pIn3);
|
||||
R = pIn3->u.i;
|
||||
assert( i>=0 && i<p->nCursor );
|
||||
pCx = p->apCsr[i];
|
||||
assert( pCx!=0 );
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||
|
||||
/* Find the index cursor. */
|
||||
pCx = p->apCsr[pOp->p1];
|
||||
assert( pCx->deferredMoveto==0 );
|
||||
pCx->seekResult = 0;
|
||||
pCx->cacheStatus = CACHE_STALE;
|
||||
pCrsr = pCx->pCursor;
|
||||
|
||||
/* If any of the values are NULL, take the jump. */
|
||||
nField = pCx->pKeyInfo->nField;
|
||||
for(ii=0; ii<nField; ii++){
|
||||
if( aMem[ii].flags & MEM_Null ){
|
||||
pc = pOp->p2 - 1;
|
||||
pCrsr = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert( (aMem[nField].flags & MEM_Null)==0 );
|
||||
|
||||
if( pCrsr!=0 ){
|
||||
int res;
|
||||
i64 v; /* The record number that matches K */
|
||||
UnpackedRecord *pIdxKey; /* Unpacked version of P4 */
|
||||
UnpackedRecord r; /* B-Tree index search key */
|
||||
i64 R; /* Rowid stored in register P3 */
|
||||
|
||||
/* Make sure K is a string and make zKey point to K
|
||||
*/
|
||||
assert( pK->flags & MEM_Blob );
|
||||
pIdxKey = sqlite3VdbeRecordUnpack(pCx->pKeyInfo, pK->n, pK->z,
|
||||
aTempRec, sizeof(aTempRec));
|
||||
if( pIdxKey==0 ){
|
||||
goto no_mem;
|
||||
}
|
||||
pIdxKey->flags |= UNPACKED_IGNORE_ROWID;
|
||||
/* Populate the index search key. */
|
||||
r.pKeyInfo = pCx->pKeyInfo;
|
||||
r.nField = nField + 1;
|
||||
r.flags = UNPACKED_PREFIX_SEARCH;
|
||||
r.aMem = aMem;
|
||||
|
||||
/* Search for an entry in P1 where all but the last rowid match K
|
||||
** If there is no such entry, jump immediately to P2.
|
||||
*/
|
||||
assert( pCx->deferredMoveto==0 );
|
||||
pCx->cacheStatus = CACHE_STALE;
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCrsr, pIdxKey, 0, 0, &res);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3VdbeDeleteUnpackedRecord(pIdxKey);
|
||||
goto abort_due_to_error;
|
||||
}
|
||||
if( res<0 ){
|
||||
rc = sqlite3BtreeNext(pCrsr, &res);
|
||||
if( res ){
|
||||
pc = pOp->p2 - 1;
|
||||
sqlite3VdbeDeleteUnpackedRecord(pIdxKey);
|
||||
break;
|
||||
}
|
||||
}
|
||||
rc = sqlite3VdbeIdxKeyCompare(pCx, pIdxKey, &res);
|
||||
sqlite3VdbeDeleteUnpackedRecord(pIdxKey);
|
||||
if( rc!=SQLITE_OK ) goto abort_due_to_error;
|
||||
if( res>0 ){
|
||||
/* Extract the value of R from register P3. */
|
||||
sqlite3VdbeMemIntegerify(pIn3);
|
||||
R = pIn3->u.i;
|
||||
|
||||
/* Search the B-Tree index. If no conflicting record is found, jump
|
||||
** to P2. Otherwise, copy the rowid of the conflicting record to
|
||||
** register P3 and fall through to the next instruction. */
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &pCx->seekResult);
|
||||
if( (r.flags & UNPACKED_PREFIX_SEARCH) || r.rowid==R ){
|
||||
pc = pOp->p2 - 1;
|
||||
break;
|
||||
}else{
|
||||
pIn3->u.i = r.rowid;
|
||||
}
|
||||
|
||||
/* At this point, pCrsr is pointing to an entry in P1 where all but
|
||||
** the final entry (the rowid) matches K. Check to see if the
|
||||
** final rowid column is different from R. If it equals R then jump
|
||||
** immediately to P2.
|
||||
*/
|
||||
rc = sqlite3VdbeIdxRowid(pCrsr, &v);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto abort_due_to_error;
|
||||
}
|
||||
if( v==R ){
|
||||
pc = pOp->p2 - 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* The final varint of the key is different from R. Store it back
|
||||
** into register R3. (The record number of an entry that violates
|
||||
** a UNIQUE constraint.)
|
||||
*/
|
||||
pIn3->u.i = v;
|
||||
assert( pIn3->flags&MEM_Int );
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -3463,15 +3444,17 @@ case OP_NotExists: { /* jump, in3 */
|
||||
assert( pIn3->flags & MEM_Int );
|
||||
assert( p->apCsr[i]->isTable );
|
||||
iKey = intToKey(pIn3->u.i);
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0,&res);
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res);
|
||||
pC->lastRowid = pIn3->u.i;
|
||||
pC->rowidIsValid = res==0 ?1:0;
|
||||
pC->nullRow = 0;
|
||||
pC->cacheStatus = CACHE_STALE;
|
||||
pC->deferredMoveto = 0;
|
||||
if( res!=0 ){
|
||||
pc = pOp->p2 - 1;
|
||||
assert( pC->rowidIsValid==0 );
|
||||
}
|
||||
pC->seekResult = res;
|
||||
}else if( !pC->pseudoTable ){
|
||||
/* This happens when an attempt to open a read cursor on the
|
||||
** sqlite_master table returns SQLITE_EMPTY.
|
||||
@@ -3479,6 +3462,7 @@ case OP_NotExists: { /* jump, in3 */
|
||||
assert( pC->isTable );
|
||||
pc = pOp->p2 - 1;
|
||||
assert( pC->rowidIsValid==0 );
|
||||
pC->seekResult = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -3721,6 +3705,7 @@ case OP_Insert: {
|
||||
pC->nullRow = 0;
|
||||
}else{
|
||||
int nZero;
|
||||
int seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0);
|
||||
if( pData->flags & MEM_Zero ){
|
||||
nZero = pData->u.nZero;
|
||||
}else{
|
||||
@@ -3729,7 +3714,8 @@ case OP_Insert: {
|
||||
sqlite3BtreeSetCachedRowid(pC->pCursor, 0);
|
||||
rc = sqlite3BtreeInsert(pC->pCursor, 0, iKey,
|
||||
pData->z, pData->n, nZero,
|
||||
pOp->p5 & OPFLAG_APPEND);
|
||||
pOp->p5 & OPFLAG_APPEND, seekResult
|
||||
);
|
||||
}
|
||||
|
||||
pC->rowidIsValid = 0;
|
||||
@@ -3893,6 +3879,10 @@ case OP_RowData: {
|
||||
**
|
||||
** Store in register P2 an integer which is the key of the table entry that
|
||||
** P1 is currently point to.
|
||||
**
|
||||
** P1 can be either an ordinary table or a virtual table. There used to
|
||||
** be a separate OP_VRowid opcode for use with virtual tables, but this
|
||||
** one opcode now works for both table types.
|
||||
*/
|
||||
case OP_Rowid: { /* out2-prerelease */
|
||||
int i = pOp->p1;
|
||||
@@ -3902,19 +3892,37 @@ case OP_Rowid: { /* out2-prerelease */
|
||||
assert( i>=0 && i<p->nCursor );
|
||||
pC = p->apCsr[i];
|
||||
assert( pC!=0 );
|
||||
rc = sqlite3VdbeCursorMoveto(pC);
|
||||
if( rc ) goto abort_due_to_error;
|
||||
if( pC->rowidIsValid ){
|
||||
v = pC->lastRowid;
|
||||
if( pC->nullRow ){
|
||||
/* Do nothing so that reg[P2] remains NULL */
|
||||
break;
|
||||
}else if( pC->deferredMoveto ){
|
||||
v = pC->movetoTarget;
|
||||
}else if( pC->pseudoTable ){
|
||||
v = keyToInt(pC->iKey);
|
||||
}else if( pC->nullRow ){
|
||||
/* Leave the rowid set to a NULL */
|
||||
break;
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
}else if( pC->pVtabCursor ){
|
||||
sqlite3_vtab *pVtab;
|
||||
const sqlite3_module *pModule;
|
||||
pVtab = pC->pVtabCursor->pVtab;
|
||||
pModule = pVtab->pModule;
|
||||
assert( pModule->xRowid );
|
||||
if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse;
|
||||
rc = pModule->xRowid(pC->pVtabCursor, &v);
|
||||
sqlite3DbFree(db, p->zErrMsg);
|
||||
p->zErrMsg = pVtab->zErrMsg;
|
||||
pVtab->zErrMsg = 0;
|
||||
if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse;
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
}else{
|
||||
assert( pC->pCursor!=0 );
|
||||
sqlite3BtreeKeySize(pC->pCursor, &v);
|
||||
v = keyToInt(v);
|
||||
rc = sqlite3VdbeCursorMoveto(pC);
|
||||
if( rc ) goto abort_due_to_error;
|
||||
if( pC->rowidIsValid ){
|
||||
v = pC->lastRowid;
|
||||
}else{
|
||||
assert( pC->pCursor!=0 );
|
||||
sqlite3BtreeKeySize(pC->pCursor, &v);
|
||||
v = keyToInt(v);
|
||||
}
|
||||
}
|
||||
pOut->u.i = v;
|
||||
MemSetTypeFlag(pOut, MEM_Int);
|
||||
@@ -4078,7 +4086,7 @@ case OP_Next: { /* jump */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: IdxInsert P1 P2 P3 * *
|
||||
/* Opcode: IdxInsert P1 P2 P3 * P5
|
||||
**
|
||||
** Register P2 holds a SQL index key made using the
|
||||
** MakeRecord instructions. This opcode writes that key
|
||||
@@ -4103,7 +4111,9 @@ case OP_IdxInsert: { /* in2 */
|
||||
if( rc==SQLITE_OK ){
|
||||
int nKey = pIn2->n;
|
||||
const char *zKey = pIn2->z;
|
||||
rc = sqlite3BtreeInsert(pCrsr, zKey, nKey, "", 0, 0, pOp->p3);
|
||||
rc = sqlite3BtreeInsert(pCrsr, zKey, nKey, "", 0, 0, pOp->p3,
|
||||
((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0)
|
||||
);
|
||||
assert( pC->deferredMoveto==0 );
|
||||
pC->cacheStatus = CACHE_STALE;
|
||||
}
|
||||
@@ -4601,6 +4611,59 @@ case OP_RowSetRead: { /* jump, out3 */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: RowSetTest P1 P2 P3 P4
|
||||
**
|
||||
** Register P3 is assumed to hold a 64-bit integer value. If register P1
|
||||
** contains a RowSet object and that RowSet object contains
|
||||
** the value held in P3, jump to register P2. Otherwise, insert the
|
||||
** integer in P3 into the RowSet and continue on to the
|
||||
** next opcode.
|
||||
**
|
||||
** The RowSet object is optimized for the case where successive sets
|
||||
** of integers, where each set contains no duplicates. Each set
|
||||
** of values is identified by a unique P4 value. The first set
|
||||
** must have P4==0, the final set P4=-1. P4 must be either -1 or
|
||||
** non-negative. For non-negative values of P4 only the lower 4
|
||||
** bits are significant.
|
||||
**
|
||||
** This allows optimizations: (a) when P4==0 there is no need to test
|
||||
** the rowset object for P3, as it is guaranteed not to contain it,
|
||||
** (b) when P4==-1 there is no need to insert the value, as it will
|
||||
** never be tested for, and (c) when a value that is part of set X is
|
||||
** inserted, there is no need to search to see if the same value was
|
||||
** previously inserted as part of set X (only if it was previously
|
||||
** inserted as part of some other set).
|
||||
*/
|
||||
case OP_RowSetTest: { /* jump, in1, in3 */
|
||||
int iSet = pOp->p4.i;
|
||||
assert( pIn3->flags&MEM_Int );
|
||||
|
||||
/* If there is anything other than a rowset object in memory cell P1,
|
||||
** delete it now and initialize P1 with an empty rowset
|
||||
*/
|
||||
if( (pIn1->flags & MEM_RowSet)==0 ){
|
||||
sqlite3VdbeMemSetRowSet(pIn1);
|
||||
if( (pIn1->flags & MEM_RowSet)==0 ) goto no_mem;
|
||||
}
|
||||
|
||||
assert( pOp->p4type==P4_INT32 );
|
||||
assert( iSet==-1 || iSet>=0 );
|
||||
if( iSet ){
|
||||
int exists;
|
||||
exists = sqlite3RowSetTest(pIn1->u.pRowSet,
|
||||
(u8)(iSet>=0 ? iSet & 0xf : 0xff),
|
||||
pIn3->u.i);
|
||||
if( exists ){
|
||||
pc = pOp->p2 - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( iSet>=0 ){
|
||||
sqlite3RowSetInsert(pIn1->u.pRowSet, pIn3->u.i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
/* Opcode: ContextPush * * *
|
||||
@@ -5034,37 +5097,6 @@ case OP_VFilter: { /* jump */
|
||||
}
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
/* Opcode: VRowid P1 P2 * * *
|
||||
**
|
||||
** Store into register P2 the rowid of
|
||||
** the virtual-table that the P1 cursor is pointing to.
|
||||
*/
|
||||
case OP_VRowid: { /* out2-prerelease */
|
||||
sqlite3_vtab *pVtab;
|
||||
const sqlite3_module *pModule;
|
||||
sqlite_int64 iRow;
|
||||
VdbeCursor *pCur = p->apCsr[pOp->p1];
|
||||
|
||||
assert( pCur->pVtabCursor );
|
||||
if( pCur->nullRow ){
|
||||
break;
|
||||
}
|
||||
pVtab = pCur->pVtabCursor->pVtab;
|
||||
pModule = pVtab->pModule;
|
||||
assert( pModule->xRowid );
|
||||
if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse;
|
||||
rc = pModule->xRowid(pCur->pVtabCursor, &iRow);
|
||||
sqlite3DbFree(db, p->zErrMsg);
|
||||
p->zErrMsg = pVtab->zErrMsg;
|
||||
pVtab->zErrMsg = 0;
|
||||
if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse;
|
||||
MemSetTypeFlag(pOut, MEM_Int);
|
||||
pOut->u.i = iRow;
|
||||
break;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
/* Opcode: VColumn P1 P2 P3 * *
|
||||
**
|
||||
|
||||
+5
-1
@@ -15,7 +15,7 @@
|
||||
** 6000 lines long) it was split up into several smaller files and
|
||||
** this header information was factored out.
|
||||
**
|
||||
** $Id: vdbeInt.h,v 1.167 2009/04/10 12:55:17 danielk1977 Exp $
|
||||
** $Id: vdbeInt.h,v 1.170 2009/05/04 11:42:30 danielk1977 Exp $
|
||||
*/
|
||||
#ifndef _VDBEINT_H_
|
||||
#define _VDBEINT_H_
|
||||
@@ -80,6 +80,10 @@ struct VdbeCursor {
|
||||
sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */
|
||||
const sqlite3_module *pModule; /* Module for cursor pVtabCursor */
|
||||
|
||||
/* Result of last sqlite3BtreeMoveto() done by an OP_NotExists or
|
||||
** OP_IsUnique opcode on this cursor. */
|
||||
int seekResult;
|
||||
|
||||
/* Cached information about the header for the data record that the
|
||||
** cursor is currently pointing to. Only valid if cacheValid is true.
|
||||
** aRow might point to (ephemeral) data for the current row, or it might
|
||||
|
||||
+30
-9
@@ -13,7 +13,7 @@
|
||||
** This file contains code use to implement APIs that are part of the
|
||||
** VDBE.
|
||||
**
|
||||
** $Id: vdbeapi.c,v 1.161 2009/04/10 23:11:31 drh Exp $
|
||||
** $Id: vdbeapi.c,v 1.164 2009/04/27 18:46:06 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "vdbeInt.h"
|
||||
@@ -400,6 +400,10 @@ void sqlite3_result_zeroblob(sqlite3_context *pCtx, int n){
|
||||
}
|
||||
void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){
|
||||
pCtx->isError = errCode;
|
||||
if( pCtx->s.flags & MEM_Null ){
|
||||
sqlite3VdbeMemSetStr(&pCtx->s, sqlite3ErrStr(errCode), -1,
|
||||
SQLITE_UTF8, SQLITE_STATIC);
|
||||
}
|
||||
}
|
||||
|
||||
/* Force an SQLITE_TOOBIG error. */
|
||||
@@ -1152,15 +1156,32 @@ int sqlite3_bind_text16(
|
||||
#endif /* SQLITE_OMIT_UTF16 */
|
||||
int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_value *pValue){
|
||||
int rc;
|
||||
Vdbe *p = (Vdbe *)pStmt;
|
||||
rc = vdbeUnbind(p, i);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3VdbeMemCopy(&p->aVar[i-1], pValue);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3VdbeChangeEncoding(&p->aVar[i-1], ENC(p->db));
|
||||
switch( pValue->type ){
|
||||
case SQLITE_INTEGER: {
|
||||
rc = sqlite3_bind_int64(pStmt, i, pValue->u.i);
|
||||
break;
|
||||
}
|
||||
case SQLITE_FLOAT: {
|
||||
rc = sqlite3_bind_double(pStmt, i, pValue->r);
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
if( pValue->flags & MEM_Zero ){
|
||||
rc = sqlite3_bind_zeroblob(pStmt, i, pValue->u.nZero);
|
||||
}else{
|
||||
rc = sqlite3_bind_blob(pStmt, i, pValue->z, pValue->n,SQLITE_TRANSIENT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SQLITE_TEXT: {
|
||||
rc = bindText(pStmt,i, pValue->z, pValue->n, SQLITE_TRANSIENT,
|
||||
pValue->enc);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
rc = sqlite3_bind_null(pStmt, i);
|
||||
break;
|
||||
}
|
||||
sqlite3_mutex_leave(p->db->mutex);
|
||||
rc = sqlite3ApiExit(p->db, rc);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
+19
-6
@@ -14,7 +14,7 @@
|
||||
** to version 2.8.7, all this code was combined into the vdbe.c source file.
|
||||
** But that file was getting too big so this subroutines were split out.
|
||||
**
|
||||
** $Id: vdbeaux.c,v 1.451 2009/04/10 15:42:36 shane Exp $
|
||||
** $Id: vdbeaux.c,v 1.457 2009/05/06 18:57:10 shane Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "vdbeInt.h"
|
||||
@@ -730,9 +730,9 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
|
||||
*/
|
||||
void sqlite3VdbeUsesBtree(Vdbe *p, int i){
|
||||
int mask;
|
||||
assert( i>=0 && i<p->db->nDb );
|
||||
assert( i>=0 && i<p->db->nDb && i<sizeof(u32)*8 );
|
||||
assert( i<(int)sizeof(p->btreeMask)*8 );
|
||||
mask = 1<<i;
|
||||
mask = ((u32)1)<<i;
|
||||
if( (p->btreeMask & mask)==0 ){
|
||||
p->btreeMask |= mask;
|
||||
sqlite3BtreeMutexArrayInsert(&p->aMutex, p->db->aDb[i].pBt);
|
||||
@@ -1940,7 +1940,7 @@ void sqlite3VdbeDeleteAuxData(VdbeFunc *pVdbeFunc, int mask){
|
||||
int i;
|
||||
for(i=0; i<pVdbeFunc->nAux; i++){
|
||||
struct AuxData *pAux = &pVdbeFunc->apAux[i];
|
||||
if( (i>31 || !(mask&(1<<i))) && pAux->pAux ){
|
||||
if( (i>31 || !(mask&(((u32)1)<<i))) && pAux->pAux ){
|
||||
if( pAux->xDelete ){
|
||||
pAux->xDelete(pAux->pAux);
|
||||
}
|
||||
@@ -2002,8 +2002,8 @@ int sqlite3VdbeCursorMoveto(VdbeCursor *p){
|
||||
rc = sqlite3BtreeMovetoUnpacked(p->pCursor, 0, p->movetoTarget, 0, &res);
|
||||
if( rc ) return rc;
|
||||
p->lastRowid = keyToInt(p->movetoTarget);
|
||||
p->rowidIsValid = res==0 ?1:0;
|
||||
if( res<0 ){
|
||||
p->rowidIsValid = ALWAYS(res==0) ?1:0;
|
||||
if( NEVER(res<0) ){
|
||||
rc = sqlite3BtreeNext(p->pCursor, &res);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
@@ -2466,6 +2466,7 @@ int sqlite3VdbeRecordCompare(
|
||||
mem1.enc = pKeyInfo->enc;
|
||||
mem1.db = pKeyInfo->db;
|
||||
mem1.flags = 0;
|
||||
mem1.u.i = 0; /* not needed, here to silence compiler warning */
|
||||
mem1.zMalloc = 0;
|
||||
|
||||
idx1 = getVarint32(aKey1, szHdr1);
|
||||
@@ -2496,6 +2497,18 @@ int sqlite3VdbeRecordCompare(
|
||||
}
|
||||
if( mem1.zMalloc ) sqlite3VdbeMemRelease(&mem1);
|
||||
|
||||
/* If the PREFIX_SEARCH flag is set and all fields except the final
|
||||
** rowid field were equal, then clear the PREFIX_SEARCH flag and set
|
||||
** pPKey2->rowid to the value of the rowid field in (pKey1, nKey1).
|
||||
** This is used by the OP_IsUnique opcode.
|
||||
*/
|
||||
if( (pPKey2->flags & UNPACKED_PREFIX_SEARCH) && i==(pPKey2->nField-1) ){
|
||||
assert( idx1==szHdr1 && rc );
|
||||
assert( mem1.flags & MEM_Int );
|
||||
pPKey2->flags &= ~UNPACKED_PREFIX_SEARCH;
|
||||
pPKey2->rowid = mem1.u.i;
|
||||
}
|
||||
|
||||
if( rc==0 ){
|
||||
/* rc==0 here means that one of the keys ran out of fields and
|
||||
** all the fields up to that point were equal. If the UNPACKED_INCRKEY
|
||||
|
||||
+18
-14
@@ -15,7 +15,7 @@
|
||||
** only within the VDBE. Interface routines refer to a Mem using the
|
||||
** name sqlite_value
|
||||
**
|
||||
** $Id: vdbemem.c,v 1.140 2009/04/05 12:22:09 drh Exp $
|
||||
** $Id: vdbemem.c,v 1.144 2009/05/05 12:54:50 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "vdbeInt.h"
|
||||
@@ -270,16 +270,18 @@ int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){
|
||||
*/
|
||||
void sqlite3VdbeMemReleaseExternal(Mem *p){
|
||||
assert( p->db==0 || sqlite3_mutex_held(p->db->mutex) );
|
||||
if( p->flags&MEM_Agg ){
|
||||
sqlite3VdbeMemFinalize(p, p->u.pDef);
|
||||
assert( (p->flags & MEM_Agg)==0 );
|
||||
sqlite3VdbeMemRelease(p);
|
||||
}else if( p->flags&MEM_Dyn && p->xDel ){
|
||||
assert( (p->flags&MEM_RowSet)==0 );
|
||||
p->xDel((void *)p->z);
|
||||
p->xDel = 0;
|
||||
}else if( p->flags&MEM_RowSet ){
|
||||
sqlite3RowSetClear(p->u.pRowSet);
|
||||
if( p->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet) ){
|
||||
if( p->flags&MEM_Agg ){
|
||||
sqlite3VdbeMemFinalize(p, p->u.pDef);
|
||||
assert( (p->flags & MEM_Agg)==0 );
|
||||
sqlite3VdbeMemRelease(p);
|
||||
}else if( p->flags&MEM_Dyn && p->xDel ){
|
||||
assert( (p->flags&MEM_RowSet)==0 );
|
||||
p->xDel((void *)p->z);
|
||||
p->xDel = 0;
|
||||
}else if( p->flags&MEM_RowSet ){
|
||||
sqlite3RowSetClear(p->u.pRowSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -975,12 +977,14 @@ int sqlite3ValueFromExpr(
|
||||
zVal = sqlite3DbStrNDup(db, (char*)pExpr->token.z, pExpr->token.n);
|
||||
pVal = sqlite3ValueNew(db);
|
||||
if( !zVal || !pVal ) goto no_mem;
|
||||
sqlite3Dequote(zVal);
|
||||
sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC);
|
||||
if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_NONE ){
|
||||
sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, enc);
|
||||
sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8);
|
||||
}else{
|
||||
sqlite3ValueApplyAffinity(pVal, affinity, enc);
|
||||
sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8);
|
||||
}
|
||||
if( enc!=SQLITE_UTF8 ){
|
||||
sqlite3VdbeChangeEncoding(pVal, enc);
|
||||
}
|
||||
}else if( op==TK_UMINUS ) {
|
||||
if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal) ){
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file contains code used to help implement virtual tables.
|
||||
**
|
||||
** $Id: vtab.c,v 1.85 2009/04/11 16:27:20 drh Exp $
|
||||
** $Id: vtab.c,v 1.86 2009/04/28 13:01:09 drh Exp $
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
#include "sqliteInt.h"
|
||||
@@ -308,7 +308,7 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
|
||||
Table *pOld;
|
||||
Schema *pSchema = pTab->pSchema;
|
||||
const char *zName = pTab->zName;
|
||||
int nName = sqlite3Strlen30(zName) + 1;
|
||||
int nName = sqlite3Strlen30(zName);
|
||||
pOld = sqlite3HashInsert(&pSchema->tblHash, zName, nName, pTab);
|
||||
if( pOld ){
|
||||
db->mallocFailed = 1;
|
||||
|
||||
+468
-383
File diff suppressed because it is too large
Load Diff
+5
-1
@@ -13,11 +13,15 @@
|
||||
# focus of this script is correct code generation of aliased result-set
|
||||
# values. See ticket #3343.
|
||||
#
|
||||
# $Id: alias.test,v 1.2 2008/10/25 15:03:21 drh Exp $
|
||||
# $Id: alias.test,v 1.3 2009/04/23 13:22:44 drh Exp $
|
||||
#
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# Aliases are currently evaluated twice. We might try to change this
|
||||
# in the future. But not now.
|
||||
return
|
||||
|
||||
# A procedure to return a sequence of increasing integers.
|
||||
#
|
||||
namespace eval ::seq {
|
||||
|
||||
+10
-10
@@ -6,12 +6,12 @@
|
||||
#***********************************************************************
|
||||
# This file runs all tests.
|
||||
#
|
||||
# $Id: async.test,v 1.19 2009/04/11 10:25:04 danielk1977 Exp $
|
||||
# $Id: async.test,v 1.20 2009/04/25 08:39:15 danielk1977 Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
if {[catch {sqlite3async_enable}]} {
|
||||
if {[info commands sqlite3async_initialize] eq ""} {
|
||||
# The async logic is not built into this system
|
||||
finish_test
|
||||
return
|
||||
@@ -41,15 +41,15 @@ set ASYNC_INCLUDE {
|
||||
}
|
||||
|
||||
# Enable asynchronous IO.
|
||||
sqlite3async_enable 1
|
||||
sqlite3async_initialize "" 1
|
||||
|
||||
rename do_test async_really_do_test
|
||||
proc do_test {name args} {
|
||||
uplevel async_really_do_test async_io-$name $args
|
||||
sqlite3async_start
|
||||
sqlite3async_halt idle
|
||||
sqlite3async_control halt idle
|
||||
sqlite3async_wait
|
||||
sqlite3async_halt never
|
||||
sqlite3async_control halt never
|
||||
}
|
||||
|
||||
foreach testfile [lsort -dictionary [glob $testdir/*.test]] {
|
||||
@@ -62,20 +62,20 @@ foreach testfile [lsort -dictionary [glob $testdir/*.test]] {
|
||||
# [file delete]). If the asynchronous backend still has the file
|
||||
# open, it will become confused.
|
||||
#
|
||||
sqlite3async_halt idle
|
||||
sqlite3async_control halt idle
|
||||
sqlite3async_start
|
||||
sqlite3async_wait
|
||||
sqlite3async_halt never
|
||||
sqlite3async_control halt never
|
||||
}
|
||||
|
||||
# Flush the write-queue and disable asynchronous IO. This should ensure
|
||||
# all allocated memory is cleaned up.
|
||||
set sqlite3async_trace 1
|
||||
sqlite3async_halt idle
|
||||
sqlite3async_control halt idle
|
||||
sqlite3async_start
|
||||
sqlite3async_wait
|
||||
sqlite3async_halt never
|
||||
sqlite3async_enable 0
|
||||
sqlite3async_control halt never
|
||||
sqlite3async_shutdown
|
||||
set sqlite3async_trace 0
|
||||
|
||||
rename do_test {}
|
||||
|
||||
+6
-10
@@ -5,14 +5,14 @@
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# $Id: async2.test,v 1.10 2009/03/24 17:43:57 drh Exp $
|
||||
# $Id: async2.test,v 1.12 2009/04/25 08:39:15 danielk1977 Exp $
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
if {
|
||||
[info commands sqlite3async_enable]=="" ||
|
||||
[info commands sqlite3async_initialize]=="" ||
|
||||
[info command sqlite3_memdebug_fail]==""
|
||||
} {
|
||||
# The async logic is not built into this system
|
||||
@@ -56,7 +56,7 @@ foreach err [list ioerr malloc-transient malloc-persistent] {
|
||||
execsql $::setup_script
|
||||
db close
|
||||
|
||||
sqlite3async_enable 1
|
||||
sqlite3async_initialize "" 1
|
||||
sqlite3 db test.db
|
||||
sqlite3_db_config_lookaside db 0 0 0
|
||||
|
||||
@@ -69,11 +69,11 @@ foreach err [list ioerr malloc-transient malloc-persistent] {
|
||||
catchsql $::sql_script
|
||||
db close
|
||||
|
||||
sqlite3async_halt idle
|
||||
sqlite3async_control halt idle
|
||||
sqlite3async_start
|
||||
sqlite3async_wait
|
||||
sqlite3async_halt never
|
||||
sqlite3async_enable 0
|
||||
sqlite3async_control halt never
|
||||
sqlite3async_shutdown
|
||||
|
||||
set ::sqlite_io_error_pending 0
|
||||
sqlite3_memdebug_fail -1
|
||||
@@ -122,9 +122,5 @@ foreach err [list ioerr malloc-transient malloc-persistent] {
|
||||
}
|
||||
|
||||
catch {db close}
|
||||
sqlite3async_halt idle
|
||||
sqlite3async_start
|
||||
sqlite3async_wait
|
||||
sqlite3async_halt never
|
||||
|
||||
finish_test
|
||||
|
||||
+6
-6
@@ -13,12 +13,12 @@
|
||||
# Specifically, it tests that the xFullPathname() method of
|
||||
# of the asynchronous vfs works correctly.
|
||||
#
|
||||
# $Id: async3.test,v 1.4 2009/03/28 18:56:14 drh Exp $
|
||||
# $Id: async3.test,v 1.5 2009/04/25 08:39:15 danielk1977 Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
if { [info commands sqlite3async_enable]=="" } {
|
||||
if { [info commands sqlite3async_initialize]=="" } {
|
||||
# The async logic is not built into this system
|
||||
puts "Skipping async3 tests: not compiled with required features"
|
||||
finish_test
|
||||
@@ -26,7 +26,7 @@ if { [info commands sqlite3async_enable]=="" } {
|
||||
}
|
||||
|
||||
db close
|
||||
sqlite3async_enable 1
|
||||
sqlite3async_initialize "" 1
|
||||
#set sqlite3async_trace 1
|
||||
sqlite3async_start
|
||||
|
||||
@@ -69,8 +69,8 @@ foreach p $paths {
|
||||
|
||||
db close
|
||||
|
||||
sqlite3async_halt idle
|
||||
sqlite3async_control halt idle
|
||||
sqlite3async_wait
|
||||
sqlite3async_halt never
|
||||
sqlite3async_enable 0
|
||||
sqlite3async_control halt never
|
||||
sqlite3async_shutdown
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
# 2009 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# $Id: async4.test,v 1.1 2009/04/25 08:39:15 danielk1977 Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
if {[info commands sqlite3async_initialize] eq ""} {
|
||||
# The async logic is not built into this system
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
db close
|
||||
|
||||
# Test layout:
|
||||
#
|
||||
# async4.1.*: Test the lockfiles parameter.
|
||||
# async4.2.*: Test the delay parameter.
|
||||
|
||||
do_test async4.1.1 {
|
||||
sqlite3async_initialize {} 0
|
||||
sqlite3async_control lockfiles
|
||||
} {1}
|
||||
do_test async4.1.2 {
|
||||
sqlite3async_control lockfiles false
|
||||
} {0}
|
||||
do_test async4.1.3 {
|
||||
sqlite3async_control lockfiles
|
||||
} {0}
|
||||
do_test async4.1.4 {
|
||||
sqlite3async_control lockfiles true
|
||||
} {1}
|
||||
|
||||
do_test async4.1.5 {
|
||||
sqlite3 db test.db -vfs sqlite3async
|
||||
execsql { CREATE TABLE t1(a, b, c) }
|
||||
} {}
|
||||
do_test async4.1.6 {
|
||||
list [file exists test.db] [file size test.db]
|
||||
} {1 0}
|
||||
do_test async4.1.7 {
|
||||
sqlite3 db2 test.db
|
||||
catchsql { CREATE TABLE t2(a, b, c) } db2
|
||||
} {1 {database is locked}}
|
||||
do_test async4.1.8 {
|
||||
sqlite3async_control halt idle
|
||||
sqlite3async_start
|
||||
sqlite3async_wait
|
||||
} {}
|
||||
do_test async4.1.9 {
|
||||
catchsql { CREATE TABLE t2(a, b, c) } db2
|
||||
} {0 {}}
|
||||
do_test async4.1.10 {
|
||||
list [catch {sqlite3async_control lockfiles false} msg] $msg
|
||||
} {1 SQLITE_MISUSE}
|
||||
do_test async4.1.11 {
|
||||
db close
|
||||
list [catch {sqlite3async_control lockfiles false} msg] $msg
|
||||
} {1 SQLITE_MISUSE}
|
||||
do_test async4.1.12 {
|
||||
sqlite3async_start
|
||||
sqlite3async_wait
|
||||
sqlite3async_control lockfiles false
|
||||
} {0}
|
||||
do_test async4.1.13 {
|
||||
sqlite3 db test.db -vfs sqlite3async
|
||||
execsql { CREATE TABLE t3(a, b, c) } db
|
||||
} {}
|
||||
do_test async4.1.14 {
|
||||
execsql {
|
||||
CREATE INDEX i1 ON t2(a);
|
||||
CREATE INDEX i2 ON t1(a);
|
||||
} db2
|
||||
} {}
|
||||
do_test async4.1.15 {
|
||||
sqlite3async_start
|
||||
sqlite3async_wait
|
||||
execsql { pragma integrity_check } db2
|
||||
} {{*** in database main ***
|
||||
Page 5 is never used}}
|
||||
do_test async4.1.16 {
|
||||
db close
|
||||
db2 close
|
||||
sqlite3async_start
|
||||
sqlite3async_wait
|
||||
} {}
|
||||
|
||||
do_test async4.2.1 {
|
||||
sqlite3async_control delay
|
||||
} {0}
|
||||
do_test async4.2.2 {
|
||||
sqlite3async_control delay 23
|
||||
} {23}
|
||||
do_test async4.2.3 {
|
||||
sqlite3async_control delay
|
||||
} {23}
|
||||
do_test async4.2.4 {
|
||||
sqlite3async_control delay 0
|
||||
} {0}
|
||||
do_test async4.2.5 {
|
||||
sqlite3 db test.db -vfs sqlite3async
|
||||
|
||||
execsql { CREATE TABLE t4(a, b) }
|
||||
set T1 [lindex [time {
|
||||
sqlite3async_start
|
||||
sqlite3async_wait
|
||||
}] 0]
|
||||
|
||||
sqlite3async_control delay 100
|
||||
execsql { CREATE TABLE t5(a, b) }
|
||||
set T2 [lindex [time {
|
||||
sqlite3async_start
|
||||
sqlite3async_wait
|
||||
}] 0]
|
||||
|
||||
expr {($T1+1000000) < $T2}
|
||||
} {1}
|
||||
|
||||
do_test async4.2.6 {
|
||||
sqlite3async_control delay 0
|
||||
execsql { CREATE TABLE t6(a, b) }
|
||||
set T1 [lindex [time {
|
||||
sqlite3async_start
|
||||
sqlite3async_wait
|
||||
}] 0]
|
||||
|
||||
expr {($T1+1000000) < $T2}
|
||||
} {1}
|
||||
|
||||
do_test async4.2.7 {
|
||||
list [catch { sqlite3async_control delay -1 } msg] $msg
|
||||
} {1 SQLITE_MISUSE}
|
||||
|
||||
do_test async4.2.8 {
|
||||
db close
|
||||
sqlite3async_start
|
||||
sqlite3async_wait
|
||||
} {}
|
||||
|
||||
finish_test
|
||||
|
||||
+3
-3
@@ -12,7 +12,7 @@
|
||||
# focus of this script is testing the sqlite3_set_authorizer() API
|
||||
# and related functionality.
|
||||
#
|
||||
# $Id: auth.test,v 1.44 2008/10/27 15:34:33 danielk1977 Exp $
|
||||
# $Id: auth.test,v 1.45 2009/05/04 01:58:31 drh Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
@@ -2141,7 +2141,7 @@ do_test auth-2.9.1 {
|
||||
return SQLITE_OK
|
||||
}
|
||||
catchsql {SELECT ROWID,b,c FROM t2}
|
||||
} {1 {illegal return value (999) from the authorization function - should be SQLITE_OK, SQLITE_IGNORE, or SQLITE_DENY}}
|
||||
} {1 {authorizer malfunction}}
|
||||
do_test auth-2.9.2 {
|
||||
db errorcode
|
||||
} {1}
|
||||
@@ -2153,7 +2153,7 @@ do_test auth-2.10 {
|
||||
return SQLITE_OK
|
||||
}
|
||||
catchsql {SELECT ROWID,b,c FROM t2}
|
||||
} {1 {illegal return value (1) from the authorization function - should be SQLITE_OK, SQLITE_IGNORE, or SQLITE_DENY}}
|
||||
} {1 {authorizer malfunction}}
|
||||
do_test auth-2.11.1 {
|
||||
proc auth {code arg1 arg2 arg3 arg4} {
|
||||
if {$code=="SQLITE_READ" && $arg2=="a"} {
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@
|
||||
# Test that the truncate optimization is disabled if the SQLITE_DELETE
|
||||
# authorization callback returns SQLITE_IGNORE.
|
||||
#
|
||||
# $Id: auth3.test,v 1.1 2008/10/27 15:34:33 danielk1977 Exp $
|
||||
# $Id: auth3.test,v 1.2 2009/05/04 01:58:31 drh Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
@@ -57,7 +57,7 @@ do_test auth3.1.2 {
|
||||
do_test auth3.1.3 {
|
||||
set ::authcode SQLITE_INVALID
|
||||
catchsql { DELETE FROM t1 }
|
||||
} {1 {illegal return value (1) from the authorization function - should be SQLITE_OK, SQLITE_IGNORE, or SQLITE_DENY}}
|
||||
} {1 {authorizer malfunction}}
|
||||
do_test auth3.1.4 {
|
||||
execsql { SELECT * FROM t1 }
|
||||
} {1 2 3 4 5 6}
|
||||
|
||||
+1
-8
@@ -11,7 +11,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script testing the sqlite_transfer_bindings() API.
|
||||
#
|
||||
# $Id: bindxfer.test,v 1.7 2009/04/09 14:02:44 drh Exp $
|
||||
# $Id: bindxfer.test,v 1.9 2009/04/17 11:56:28 drh Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
@@ -68,13 +68,6 @@ ifcapable deprecated {
|
||||
do_test bindxfer-1.8 {
|
||||
set VALUES
|
||||
} {one two {}}
|
||||
catch {sqlite3_finalize $VM1}
|
||||
catch {sqlite3_finalize $VM2}
|
||||
do_test bindxfer-1.10 {
|
||||
set VM1 [sqlite3_prepare $DB {SELECT ?, ?, ?} -1 TAIL]
|
||||
set VM2 [sqlite3_prepare $DB {SELECT ?, ?, ?, ?} -1 TAIL]
|
||||
sqlite3_transfer_bindings $VM1 $VM2
|
||||
} 1 ;# SQLITE_ERROR
|
||||
}
|
||||
catch {sqlite3_finalize $VM1}
|
||||
catch {sqlite3_finalize $VM2}
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library.
|
||||
#
|
||||
# $Id: blob.test,v 1.7 2008/04/04 15:12:22 drh Exp $
|
||||
# $Id: blob.test,v 1.8 2009/04/28 18:00:27 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -137,7 +137,7 @@ do_test blob-3.1 {
|
||||
sqlite3_finalize $STMT
|
||||
db2 close
|
||||
} {}
|
||||
do_test blob-2.3 {
|
||||
do_test blob-3.2 {
|
||||
set blobs [execsql {SELECT * FROM t1}]
|
||||
set blobs2 [list]
|
||||
foreach b $blobs {lappend blobs2 [bin_to_hex $b]}
|
||||
|
||||
+2
-2
@@ -13,7 +13,7 @@
|
||||
# This file implements tests for the conflict resolution extension
|
||||
# to SQLite.
|
||||
#
|
||||
# $Id: conflict.test,v 1.31 2008/01/21 16:22:46 drh Exp $
|
||||
# $Id: conflict.test,v 1.32 2009/04/30 09:10:38 danielk1977 Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -308,7 +308,7 @@ foreach {i conf1 cmd t0 t1 t2 t3 t4} {
|
||||
} {
|
||||
if {$t0} {set t1 {column a is not unique}}
|
||||
if {[info exists TEMP_STORE] && $TEMP_STORE>=2} {
|
||||
set t3 $t4
|
||||
set t3 0
|
||||
} else {
|
||||
set t3 [expr {$t3+$t4}]
|
||||
}
|
||||
|
||||
+2
-1
@@ -11,7 +11,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing "SELECT count(*)" statements.
|
||||
#
|
||||
# $Id: count.test,v 1.4 2009/04/02 20:27:28 drh Exp $
|
||||
# $Id: count.test,v 1.5 2009/05/01 21:13:37 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -155,6 +155,7 @@ ifcapable vtab {
|
||||
}
|
||||
|
||||
do_test count-3.1 {
|
||||
breakpoint
|
||||
execsql {
|
||||
CREATE TABLE t3(a, b);
|
||||
SELECT a FROM (SELECT count(*) AS a FROM t3) WHERE a==0;
|
||||
|
||||
+6
-1
@@ -11,7 +11,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing date and time functions.
|
||||
#
|
||||
# $Id: date.test,v 1.33 2009/04/01 20:44:14 drh Exp $
|
||||
# $Id: date.test,v 1.34 2009/04/16 12:58:03 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -71,6 +71,11 @@ datetest 2.1c datetime(0,'unixepochx') NULL
|
||||
datetest 2.1d datetime('2003-10-22','unixepoch') NULL
|
||||
datetest 2.2 datetime(946684800,'unixepoch') {2000-01-01 00:00:00}
|
||||
datetest 2.2b datetime('946684800','unixepoch') {2000-01-01 00:00:00}
|
||||
for {set i 0} {$i<1000} {incr i} {
|
||||
set sql [format {strftime('%%H:%%M:%%f',1237962480.%03d,'unixepoch')} $i]
|
||||
set res [format {06:28:00.%03d} $i]
|
||||
datetest 2.2c-$i $sql $res
|
||||
}
|
||||
datetest 2.3 {date('2003-10-22','weekday 0')} 2003-10-26
|
||||
datetest 2.4 {date('2003-10-22','weekday 1')} 2003-10-27
|
||||
datetest 2.4a {date('2003-10-22','weekday 1')} 2003-10-27
|
||||
|
||||
+4
-4
@@ -12,7 +12,7 @@
|
||||
# of these tests is exclusive access mode (i.e. the thing activated by
|
||||
# "PRAGMA locking_mode = EXCLUSIVE").
|
||||
#
|
||||
# $Id: exclusive.test,v 1.11 2008/12/22 11:43:36 danielk1977 Exp $
|
||||
# $Id: exclusive.test,v 1.14 2009/04/30 16:41:00 danielk1977 Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -417,7 +417,7 @@ do_test exclusive-5.1 {
|
||||
# Three files are open: The db, journal and statement-journal.
|
||||
set sqlite_open_file_count
|
||||
expr $sqlite_open_file_count-$extrafds
|
||||
} {3}
|
||||
} [expr 3 - ($TEMP_STORE>=2)]
|
||||
do_test exclusive-5.2 {
|
||||
execsql {
|
||||
COMMIT;
|
||||
@@ -443,7 +443,7 @@ do_test exclusive-5.4 {
|
||||
# Three files are open: The db, journal and statement-journal.
|
||||
set sqlite_open_file_count
|
||||
expr $sqlite_open_file_count-$extrafds
|
||||
} {3}
|
||||
} [expr 3 - ($TEMP_STORE>=2)]
|
||||
do_test exclusive-5.5 {
|
||||
execsql {
|
||||
COMMIT;
|
||||
@@ -451,7 +451,7 @@ do_test exclusive-5.5 {
|
||||
# Three files are still open: The db, journal and statement-journal.
|
||||
set sqlite_open_file_count
|
||||
expr $sqlite_open_file_count-$extrafds
|
||||
} {3}
|
||||
} [expr 3 - ($TEMP_STORE>=2)]
|
||||
do_test exclusive-5.6 {
|
||||
execsql {
|
||||
PRAGMA locking_mode = normal;
|
||||
|
||||
+15
-1
@@ -11,7 +11,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing built-in functions.
|
||||
#
|
||||
# $Id: func.test,v 1.91 2009/02/04 03:59:25 shane Exp $
|
||||
# $Id: func.test,v 1.92 2009/04/15 15:16:53 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -1056,6 +1056,20 @@ for {set i 1} {$i<[sqlite3_limit db SQLITE_LIMIT_FUNCTION_ARG -1]} {incr i} {
|
||||
} $result
|
||||
}
|
||||
|
||||
# Ticket #3806. If the initial string in a group_concat is an empty
|
||||
# string, the separate that follows should still be present.
|
||||
#
|
||||
do_test func-24.8 {
|
||||
execsql {
|
||||
SELECT group_concat(CASE t1 WHEN 'this' THEN '' ELSE t1 END) FROM tbl1
|
||||
}
|
||||
} {,program,is,free,software}
|
||||
do_test func-24.9 {
|
||||
execsql {
|
||||
SELECT group_concat(CASE WHEN t1!='software' THEN '' ELSE t1 END) FROM tbl1
|
||||
}
|
||||
} {,,,,software}
|
||||
|
||||
# Use the test_isolation function to make sure that type conversions
|
||||
# on function arguments do not effect subsequent arguments.
|
||||
#
|
||||
|
||||
+15
-1
@@ -19,7 +19,7 @@
|
||||
#
|
||||
# The most complicated trees are for SELECT statements.
|
||||
#
|
||||
# $Id: fuzz.test,v 1.18 2009/01/06 00:11:26 drh Exp $
|
||||
# $Id: fuzz.test,v 1.19 2009/04/28 11:10:39 danielk1977 Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -287,6 +287,20 @@ do_test fuzz-1.18 {
|
||||
}
|
||||
} {0 -4294967298}
|
||||
|
||||
# At one point the following INSERT statement caused an assert() to fail.
|
||||
#
|
||||
do_test fuzz-1.19 {
|
||||
execsql { CREATE TABLE t1(a) }
|
||||
catchsql {
|
||||
INSERT INTO t1 VALUES(
|
||||
CASE WHEN NULL THEN NULL ELSE ( SELECT 0 ORDER BY 456 ) END
|
||||
)
|
||||
}
|
||||
} {1 {1st ORDER BY term out of range - should be between 1 and 1}}
|
||||
do_test fuzz-1.20 {
|
||||
execsql { DROP TABLE t1 }
|
||||
} {}
|
||||
|
||||
#----------------------------------------------------------------
|
||||
# Test some fuzzily generated expressions.
|
||||
#
|
||||
|
||||
+5
-3
@@ -11,7 +11,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing corner cases of the INSERT statement.
|
||||
#
|
||||
# $Id: insert3.test,v 1.8 2008/12/23 10:37:47 danielk1977 Exp $
|
||||
# $Id: insert3.test,v 1.9 2009/04/23 14:58:40 danielk1977 Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -166,9 +166,11 @@ ifcapable bloblit {
|
||||
}
|
||||
} {{} 4.3 hi}
|
||||
}
|
||||
db close
|
||||
|
||||
file delete -force test.db
|
||||
foreach tab [db eval {SELECT name FROM sqlite_master WHERE type = 'table'}] {
|
||||
db eval "DROP TABLE $tab"
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
+13
-4
@@ -11,7 +11,7 @@
|
||||
# This file implements regression tests for SQLite library. The focus
|
||||
# of these tests is the journal mode pragma.
|
||||
#
|
||||
# $Id: jrnlmode.test,v 1.14 2009/04/10 15:02:44 danielk1977 Exp $
|
||||
# $Id: jrnlmode.test,v 1.15 2009/04/20 17:43:03 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -282,6 +282,7 @@ ifcapable autovacuum&&pragma {
|
||||
} {3}
|
||||
|
||||
do_test jrnlmode-4.2 {
|
||||
breakpoint
|
||||
execsql { PRAGMA journal_mode = off }
|
||||
} {off}
|
||||
|
||||
@@ -464,7 +465,7 @@ ifcapable pragma {
|
||||
INSERT INTO t4 VALUES(1, 2);
|
||||
PRAGMA journal_mode = memory;
|
||||
}
|
||||
} {truncate memory}
|
||||
} {truncate truncate}
|
||||
do_test jrnlmode-6.2 {
|
||||
file exists test.db-journal
|
||||
} {1}
|
||||
@@ -476,14 +477,15 @@ ifcapable pragma {
|
||||
} {1 2}
|
||||
do_test jrnlmode-6.4 {
|
||||
file exists test.db-journal
|
||||
} {0}
|
||||
} {1}
|
||||
do_test jrnlmode-6.5 {
|
||||
execsql {
|
||||
PRAGMA journal_mode = MEMORY;
|
||||
BEGIN;
|
||||
INSERT INTO t4 VALUES(3, 4);
|
||||
}
|
||||
file exists test.db-journal
|
||||
} {0}
|
||||
} {1}
|
||||
do_test jrnlmode-6.7 {
|
||||
execsql {
|
||||
COMMIT;
|
||||
@@ -492,6 +494,13 @@ ifcapable pragma {
|
||||
} {1 2 3 4}
|
||||
do_test jrnlmode-6.8 {
|
||||
file exists test.db-journal
|
||||
} {1}
|
||||
do_test jrnlmode-6.9 {
|
||||
execsql {
|
||||
PRAGMA journal_mode = DELETE;
|
||||
BEGIN IMMEDIATE; COMMIT;
|
||||
}
|
||||
file exists test.db-journal
|
||||
} {0}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
# 2009 April 20
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Test cases inspired by ticket #3811. Tests to make sure that
|
||||
# the journal_mode can only be changed at appropriate times and that
|
||||
# all reported changes are effective.
|
||||
#
|
||||
# $Id: jrnlmode3.test,v 1.5 2009/04/20 17:43:03 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable {!pager_pragmas} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
#
|
||||
# Verify that journal_mode=OFF works as long as it occurs before the first
|
||||
# transaction, even if locking_mode=EXCLUSIVE is enabled. The behavior if
|
||||
# journal_mode is changed after the first transaction is undefined and hence
|
||||
# untested.
|
||||
#
|
||||
do_test jrnlmode3-1.1 {
|
||||
db eval {
|
||||
PRAGMA journal_mode=OFF;
|
||||
PRAGMA locking_mode=EXCLUSIVE;
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(1);
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {off exclusive 1}
|
||||
do_test jrnlmode3-1.2 {
|
||||
db eval {
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES(2);
|
||||
ROLLBACK;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 2}
|
||||
|
||||
db close
|
||||
file delete -force test.db test.db-journal
|
||||
sqlite3 db test.db
|
||||
|
||||
do_test jrnlmode3-2.1 {
|
||||
db eval {
|
||||
PRAGMA locking_mode=EXCLUSIVE;
|
||||
PRAGMA journal_mode=OFF;
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(1);
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {exclusive off 1}
|
||||
do_test jrnlmode3-2.2 {
|
||||
db eval {
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES(2);
|
||||
ROLLBACK;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 2}
|
||||
|
||||
# Test cases to verify that we can move from any journal_mode
|
||||
# to any other, as long as we are not in a transaction. Verify
|
||||
# that we cannot change journal_mode while a transaction is active.
|
||||
#
|
||||
set all_journal_modes {delete persist truncate memory off}
|
||||
set cnt 0
|
||||
foreach fromjmode $all_journal_modes {
|
||||
foreach tojmode $all_journal_modes {
|
||||
|
||||
# Skip the no-change cases
|
||||
if {$fromjmode==$tojmode} continue
|
||||
incr cnt
|
||||
|
||||
# Start with a fresh database connection an empty database file.
|
||||
#
|
||||
db close
|
||||
file delete -force test.db test.db-journal
|
||||
sqlite3 db test.db
|
||||
|
||||
# Initialize the journal mode.
|
||||
#
|
||||
do_test jrnlmode3-3.$cnt.1-($fromjmode-to-$tojmode) {
|
||||
db eval "PRAGMA journal_mode = $fromjmode;"
|
||||
} $fromjmode
|
||||
|
||||
# Verify that the initial journal mode takes.
|
||||
#
|
||||
do_test jrnlmode3-3.$cnt.2 {
|
||||
db eval {PRAGMA main.journal_mode}
|
||||
} $fromjmode
|
||||
|
||||
# Start a transaction and try to change the journal mode within
|
||||
# the transaction. This should fail.
|
||||
#
|
||||
do_test jrnlmode3-3.$cnt.3 {
|
||||
db eval {
|
||||
CREATE TABLE t1(x);
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES($cnt);
|
||||
}
|
||||
db eval "PRAGMA journal_mode=$tojmode"
|
||||
} $fromjmode
|
||||
|
||||
# Rollback the transaction. Verify that the rollback occurred
|
||||
# if journal_mode!=OFF.
|
||||
#
|
||||
do_test jrnlmode3-3.$cnt.4 {
|
||||
db eval {
|
||||
ROLLBACK;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} [expr {$fromjmode=="off"?$cnt:""}]
|
||||
|
||||
# Now change the journal mode again. This time the new mode
|
||||
# should take.
|
||||
#
|
||||
do_test jrnlmode3-3.$cnt.5 {
|
||||
db eval "PRAGMA journal_mode=$tojmode"
|
||||
} $tojmode
|
||||
|
||||
# Do a the transaction. Verify that the rollback occurred
|
||||
# if journal_mode!=OFF.
|
||||
#
|
||||
do_test jrnlmode3-3.$cnt.6 {
|
||||
db eval {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE TABLE t1(x);
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES(1);
|
||||
}
|
||||
db eval ROLLBACK
|
||||
db eval {
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} [expr {$tojmode=="off"?"1":""}]
|
||||
}
|
||||
}
|
||||
|
||||
finish_test
|
||||
+4
-1
@@ -11,7 +11,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is database locks between competing processes.
|
||||
#
|
||||
# $Id: lock2.test,v 1.10 2009/02/05 16:31:46 drh Exp $
|
||||
# $Id: lock2.test,v 1.11 2009/05/01 10:55:34 danielk1977 Exp $
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
@@ -42,6 +42,9 @@ proc testfixture {chan cmd} {
|
||||
if { $line == "OVER" } {
|
||||
return $r
|
||||
}
|
||||
if {[eof $chan]} {
|
||||
return "ERROR: Child process hung up"
|
||||
}
|
||||
append r $line
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -11,7 +11,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is database locks.
|
||||
#
|
||||
# $Id: lock4.test,v 1.9 2009/02/05 16:31:46 drh Exp $
|
||||
# $Id: lock4.test,v 1.10 2009/05/06 00:52:41 drh Exp $
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
@@ -102,6 +102,7 @@ do_test lock4-1.3 {
|
||||
# deleting the journal file. But it might retain the lock for a
|
||||
# fraction longer
|
||||
#
|
||||
after 25
|
||||
db2 eval {
|
||||
SELECT * FROM t2
|
||||
}
|
||||
|
||||
+22
-12
@@ -11,7 +11,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is exercising the code in main.c.
|
||||
#
|
||||
# $Id: main.test,v 1.30 2008/09/18 18:18:29 drh Exp $
|
||||
# $Id: main.test,v 1.32 2009/04/28 04:51:29 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -85,6 +85,16 @@ ifcapable {trigger} {
|
||||
CREATE TRIGGER xyz AFTER DELETE abc BEGIN UPDATE pqr;
|
||||
}
|
||||
} {0}
|
||||
do_test main-1.17.2 {
|
||||
db complete {
|
||||
EXPLAIN CREATE TRIGGER xyz AFTER DELETE abc BEGIN UPDATE pqr;
|
||||
}
|
||||
} {0}
|
||||
do_test main-1.17.3 {
|
||||
db complete {
|
||||
EXPLAIN QUERY PLAN CREATE TRIGGER xyz AFTER DELETE abc BEGIN UPDATE pqr;
|
||||
}
|
||||
} {0}
|
||||
do_test main-1.18 {
|
||||
db complete {
|
||||
CREATE TRIGGER xyz AFTER DELETE abc BEGIN UPDATE pqr; END;
|
||||
@@ -444,33 +454,33 @@ do_test main-3.6 {
|
||||
# The following test-case tests the linked list code used to manage
|
||||
# sqlite3_vfs structures.
|
||||
if {$::tcl_platform(platform)=="unix"
|
||||
&& [info command sqlite3async_enable]!=""} {
|
||||
&& [info command sqlite3async_initialize]!=""} {
|
||||
ifcapable threadsafe {
|
||||
do_test main-4.1 {
|
||||
sqlite3_crash_enable 1
|
||||
sqlite3_crash_enable 0
|
||||
|
||||
sqlite3async_enable 1
|
||||
sqlite3async_enable 0
|
||||
sqlite3async_initialize "" 1
|
||||
sqlite3async_shutdown
|
||||
|
||||
sqlite3_crash_enable 1
|
||||
sqlite3async_enable 1
|
||||
sqlite3async_initialize "" 1
|
||||
sqlite3_crash_enable 0
|
||||
sqlite3async_enable 0
|
||||
sqlite3async_shutdown
|
||||
|
||||
sqlite3_crash_enable 1
|
||||
sqlite3async_enable 1
|
||||
sqlite3async_enable 0
|
||||
sqlite3async_initialize "" 1
|
||||
sqlite3async_shutdown
|
||||
sqlite3_crash_enable 0
|
||||
|
||||
sqlite3async_enable 1
|
||||
sqlite3async_initialize "" 1
|
||||
sqlite3_crash_enable 1
|
||||
sqlite3_crash_enable 0
|
||||
sqlite3async_enable 0
|
||||
sqlite3async_shutdown
|
||||
|
||||
sqlite3async_enable 1
|
||||
sqlite3async_initialize "" 1
|
||||
sqlite3_crash_enable 1
|
||||
sqlite3async_enable 0
|
||||
sqlite3async_shutdown
|
||||
sqlite3_crash_enable 0
|
||||
} {}
|
||||
do_test main-4.2 {
|
||||
|
||||
@@ -103,19 +103,17 @@ for {set i 2} {$i<=$NT+1} {incr i} {
|
||||
set seen($w) 1
|
||||
set result [lsort -int [array names r]]
|
||||
puts "do_test where7-2.$i.1 \173"
|
||||
puts " count_steps \173"
|
||||
puts " count_steps_sort \173"
|
||||
puts " SELECT a FROM t2"
|
||||
set wc [join $w "\n OR "]
|
||||
puts " WHERE $wc"
|
||||
puts " ORDER BY a"
|
||||
puts " \175"
|
||||
puts "\175 {$result scan 0 sort 0}"
|
||||
puts "do_test where7-2.$i.2 \173"
|
||||
puts " count_steps \173"
|
||||
puts " count_steps_sort \173"
|
||||
puts " SELECT a FROM t3"
|
||||
set wc [join $w "\n OR "]
|
||||
puts " WHERE $wc"
|
||||
puts " ORDER BY a"
|
||||
puts " \175"
|
||||
puts "\175 {$result scan 0 sort 0}"
|
||||
}
|
||||
|
||||
+21
-11
@@ -13,7 +13,7 @@
|
||||
# This file implements tests for miscellanous features that were
|
||||
# left out of other test files.
|
||||
#
|
||||
# $Id: misc3.test,v 1.19 2008/06/25 02:47:57 drh Exp $
|
||||
# $Id: misc3.test,v 1.20 2009/05/06 00:49:01 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -272,16 +272,26 @@ ifcapable {explain} {
|
||||
}]
|
||||
regexp { IsUnique \d+ \d+ \d+ \d+ } $x
|
||||
} {1}
|
||||
do_test misc3-6.11 {
|
||||
set x [execsql {
|
||||
EXPLAIN SELECT a+123456789012, b*4.5678, c FROM ex1 ORDER BY +a, b DESC
|
||||
}]
|
||||
set y [regexp { 123456789012 } $x]
|
||||
lappend y [regexp { 4.5678 } $x]
|
||||
lappend y [regexp { hello } $x]
|
||||
lappend y [regexp {,-BINARY} $x]
|
||||
} {1 1 1 1}
|
||||
|
||||
if {[regexp {16} [db one {PRAGMA encoding}]]} {
|
||||
do_test misc3-6.11-utf16 {
|
||||
set x [execsql {
|
||||
EXPLAIN SELECT a+123456789012, b*4.5678, c FROM ex1 ORDER BY +a, b DESC
|
||||
}]
|
||||
set y [regexp { 123456789012 } $x]
|
||||
lappend y [regexp { 4.5678 } $x]
|
||||
lappend y [regexp {,-BINARY} $x]
|
||||
} {1 1 1}
|
||||
} else {
|
||||
do_test misc3-6.11-utf8 {
|
||||
set x [execsql {
|
||||
EXPLAIN SELECT a+123456789012, b*4.5678, c FROM ex1 ORDER BY +a, b DESC
|
||||
}]
|
||||
set y [regexp { 123456789012 } $x]
|
||||
lappend y [regexp { 4.5678 } $x]
|
||||
lappend y [regexp { hello } $x]
|
||||
lappend y [regexp {,-BINARY} $x]
|
||||
} {1 1 1 1}
|
||||
}
|
||||
}
|
||||
|
||||
ifcapable {trigger} {
|
||||
|
||||
+6
-8
@@ -9,7 +9,7 @@
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# $Id: mutex1.test,v 1.19 2009/03/24 15:08:10 drh Exp $
|
||||
# $Id: mutex1.test,v 1.20 2009/04/23 14:58:40 danielk1977 Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -97,8 +97,8 @@ do_test mutex1-1.9 {
|
||||
# * Multi-threaded mode,
|
||||
# * Single-threaded mode.
|
||||
#
|
||||
set enable_shared_cache [sqlite3_enable_shared_cache 1]
|
||||
ifcapable threadsafe {
|
||||
ifcapable threadsafe&&shared_cache {
|
||||
set enable_shared_cache [sqlite3_enable_shared_cache 1]
|
||||
foreach {mode mutexes} {
|
||||
singlethread {}
|
||||
multithread {fast static_lru static_master static_mem static_open static_prng }
|
||||
@@ -133,12 +133,10 @@ ifcapable threadsafe {
|
||||
lsort $res
|
||||
} [lsort $mutexes]
|
||||
}
|
||||
}
|
||||
sqlite3_enable_shared_cache $enable_shared_cache
|
||||
sqlite3_enable_shared_cache $enable_shared_cache
|
||||
|
||||
# Open and use a connection in "nomutex" mode. Test that no recursive
|
||||
# mutexes are obtained.
|
||||
ifcapable threadsafe {
|
||||
# Open and use a connection in "nomutex" mode. Test that no recursive
|
||||
# mutexes are obtained.
|
||||
do_test mutex1.3.1 {
|
||||
catch {db close}
|
||||
clear_mutex_counters
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# 2009 April 17
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is the code in rowhash.c.
|
||||
#
|
||||
# $Id: rowhash.test,v 1.5 2009/05/02 12:02:02 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
do_test rowhash-1.1 {
|
||||
execsql {
|
||||
CREATE TABLE t1(id INTEGER PRIMARY KEY, a, b, c);
|
||||
CREATE INDEX i1 ON t1(a);
|
||||
CREATE INDEX i2 ON t1(b);
|
||||
CREATE INDEX i3 ON t1(c);
|
||||
}
|
||||
} {}
|
||||
|
||||
proc do_keyset_test {name lKey} {
|
||||
db transaction {
|
||||
execsql { DELETE FROM t1 }
|
||||
foreach key $lKey {
|
||||
execsql { INSERT OR IGNORE INTO t1 VALUES($key, 'a', 'b', 'c') }
|
||||
}
|
||||
}
|
||||
do_test $name {
|
||||
lsort -integer [execsql {
|
||||
SELECT id FROM t1 WHERE a = 'a' OR b = 'b' OR c = 'c';
|
||||
}]
|
||||
} [lsort -integer -unique $lKey]
|
||||
}
|
||||
|
||||
do_keyset_test rowhash-2.1 {1 2 3}
|
||||
do_keyset_test rowhash-2.2 {0 1 2 3}
|
||||
do_keyset_test rowhash-2.3 {62 125 188}
|
||||
if {[working_64bit_int]} {
|
||||
expr srand(1)
|
||||
for {set i 4} {$i < 10} {incr i} {
|
||||
for {set j 0} {$j < 5000} {incr j} {
|
||||
lappend L [expr int(rand()*1000000000)]
|
||||
}
|
||||
do_keyset_test rowhash-2.$i $L
|
||||
}
|
||||
}
|
||||
|
||||
finish_test
|
||||
@@ -9,11 +9,15 @@
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# $Id: savepoint4.test,v 1.4 2009/02/10 14:45:13 danielk1977 Exp $
|
||||
# $Id: savepoint4.test,v 1.5 2009/04/23 18:42:05 shane Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !crashtest {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
proc signature {} {
|
||||
return [db eval {SELECT count(*), md5sum(x) FROM t1}]
|
||||
|
||||
+5
-2
@@ -11,7 +11,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing the SELECT statement.
|
||||
#
|
||||
# $Id: select1.test,v 1.67 2009/04/10 15:38:43 drh Exp $
|
||||
# $Id: select1.test,v 1.68 2009/04/23 14:58:40 danielk1977 Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -1022,9 +1022,12 @@ ifcapable subquery {
|
||||
} {0}
|
||||
}
|
||||
|
||||
foreach tab [db eval {SELECT name FROM sqlite_master WHERE type = 'table'}] {
|
||||
db eval "DROP TABLE $tab"
|
||||
}
|
||||
db close
|
||||
file delete -force test.db
|
||||
sqlite3 db test.db
|
||||
|
||||
do_test select1-14.1 {
|
||||
execsql {
|
||||
SELECT * FROM sqlite_master WHERE rowid>10;
|
||||
|
||||
+13
-1
@@ -12,7 +12,7 @@
|
||||
# focus of this file is testing UNION, INTERSECT and EXCEPT operators
|
||||
# in SELECT statements.
|
||||
#
|
||||
# $Id: select4.test,v 1.29 2008/08/04 03:51:24 danielk1977 Exp $
|
||||
# $Id: select4.test,v 1.30 2009/04/16 00:24:24 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -374,6 +374,11 @@ do_test select4-5.3 {
|
||||
}} msg]
|
||||
lappend v $msg
|
||||
} {1 {SELECTs to the left and right of UNION ALL do not have the same number of result columns}}
|
||||
do_test select4-5.3-3807-1 {
|
||||
catchsql {
|
||||
SELECT 1 UNION SELECT 2, 3 UNION SELECT 4, 5 ORDER BY 1;
|
||||
}
|
||||
} {1 {SELECTs to the left and right of UNION do not have the same number of result columns}}
|
||||
do_test select4-5.4 {
|
||||
set v [catch {execsql {
|
||||
SELECT log FROM t1 WHERE n=2
|
||||
@@ -791,6 +796,13 @@ do_test select4-11.15 {
|
||||
}
|
||||
} {1 {SELECTs to the left and right of UNION do not have the same number of result columns}}
|
||||
|
||||
do_test select4-12.1 {
|
||||
sqlite3 db2 :memory:
|
||||
catchsql {
|
||||
SELECT 1 UNION SELECT 2,3 UNION SELECT 4,5 ORDER BY 1;
|
||||
} db2
|
||||
} {1 {SELECTs to the left and right of UNION do not have the same number of result columns}}
|
||||
|
||||
} ;# ifcapable compound
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
# 2009 April 30
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Make sure that attaching the same database multiple times in
|
||||
# shared cache mode fails.
|
||||
#
|
||||
# $Id: shared7.test,v 1.1 2009/04/30 13:30:33 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !shared_cache { finish_test ; return }
|
||||
|
||||
do_test shared7-1.1 {
|
||||
set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
|
||||
sqlite3_enable_shared_cache
|
||||
} {1}
|
||||
|
||||
do_test shared7-1.2 {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
db eval {
|
||||
CREATE TABLE t1(x);
|
||||
}
|
||||
catchsql {
|
||||
ATTACH 'test.db' AS err1;
|
||||
}
|
||||
} {1 {database is already attached}}
|
||||
|
||||
do_test shared7-1.3 {
|
||||
file delete -force test2.db test2.db-journal
|
||||
db eval {
|
||||
ATTACH 'test2.db' AS test2;
|
||||
CREATE TABLE test2.t2(y);
|
||||
}
|
||||
catchsql {
|
||||
ATTACH 'test2.db' AS err2;
|
||||
}
|
||||
} {1 {database is already attached}}
|
||||
do_test shared7-1.4 {
|
||||
catchsql {
|
||||
ATTACH 'test.db' AS err1;
|
||||
}
|
||||
} {1 {database is already attached}}
|
||||
|
||||
|
||||
sqlite3_enable_shared_cache $::enable_shared_cache
|
||||
finish_test
|
||||
+5
-4
@@ -11,7 +11,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing the CREATE TABLE statement.
|
||||
#
|
||||
# $Id: table.test,v 1.50 2009/03/14 08:37:24 danielk1977 Exp $
|
||||
# $Id: table.test,v 1.51 2009/04/28 15:43:45 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -112,15 +112,16 @@ do_test table-2.1e {
|
||||
catchsql {CREATE TABLE IF NOT EXISTS test2(x UNIQUE, y TEXT PRIMARY KEY)}
|
||||
} {0 {}}
|
||||
do_test table-2.1f {
|
||||
breakpoint
|
||||
execsql {DROP TABLE test2; SELECT name FROM sqlite_master WHERE type!='meta'}
|
||||
} {}
|
||||
|
||||
# Verify that we cannot make a table with the same name as an index
|
||||
#
|
||||
do_test table-2.2a {
|
||||
execsql {CREATE TABLE test2(one text); CREATE INDEX test3 ON test2(one)}
|
||||
set v [catch {execsql {CREATE TABLE test3(two text)}} msg]
|
||||
lappend v $msg
|
||||
execsql {CREATE TABLE test2(one text)}
|
||||
execsql {CREATE INDEX test3 ON test2(one)}
|
||||
catchsql {CREATE TABLE test3(two text)}
|
||||
} {1 {there is already an index named test3}}
|
||||
do_test table-2.2b {
|
||||
db close
|
||||
|
||||
+42
-1
@@ -12,7 +12,7 @@
|
||||
# The focus of this file is in making sure that rolling back
|
||||
# a statement journal works correctly.
|
||||
#
|
||||
# $Id: tempdb.test,v 1.1 2008/04/15 00:02:00 drh Exp $
|
||||
# $Id: tempdb.test,v 1.3 2009/05/01 05:23:18 danielk1977 Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -52,4 +52,45 @@ do_test tempdb-1.2 {
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test tempdb-2.1 {
|
||||
# Set $::jrnl_in_memory if the journal file is expected to be in-memory.
|
||||
# Similarly, set $::subj_in_memory if the sub-journal file is expected
|
||||
# to be in memory. These variables are used to calculate the expected
|
||||
# number of open files in the test cases below.
|
||||
#
|
||||
set jrnl_in_memory [expr {
|
||||
[info exists ::permutations_test_prefix] &&
|
||||
$::permutations_test_prefix eq "inmemory_journal"
|
||||
}]
|
||||
set subj_in_memory [expr {$jrnl_in_memory || $TEMP_STORE == 3}]
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
} {}
|
||||
do_test tempdb-2.2 {
|
||||
execsql {
|
||||
CREATE TABLE t1 (a PRIMARY KEY, b, c);
|
||||
CREATE TABLE t2 (a, b, c);
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(4, 5, 6);
|
||||
INSERT INTO t2 SELECT * FROM t1;
|
||||
}
|
||||
catchsql { INSERT INTO t1 SELECT * FROM t2 }
|
||||
set sqlite_open_file_count
|
||||
} [expr 1 + (0==$jrnl_in_memory) + (0==$subj_in_memory)]
|
||||
do_test tempdb-2.3 {
|
||||
execsql {
|
||||
PRAGMA temp_store = 'memory';
|
||||
ROLLBACK;
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(4, 5, 6);
|
||||
INSERT INTO t2 SELECT * FROM t1;
|
||||
}
|
||||
catchsql { INSERT INTO t1 SELECT * FROM t2 }
|
||||
set sqlite_open_file_count
|
||||
} [expr 1 + (0==$jrnl_in_memory)]
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# $Id: temptrigger.test,v 1.2 2009/04/07 14:14:23 danielk1977 Exp $
|
||||
# $Id: temptrigger.test,v 1.3 2009/04/15 13:07:19 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !trigger { finish_test ; return }
|
||||
ifcapable {!trigger || !shared_cache} { finish_test ; return }
|
||||
|
||||
# Test cases:
|
||||
#
|
||||
|
||||
+2
-1
@@ -13,7 +13,7 @@
|
||||
# This file implements tests to verify that ticket #3461 has been
|
||||
# fixed.
|
||||
#
|
||||
# $Id: tkt3461.test,v 1.2 2008/10/25 15:03:21 drh Exp $
|
||||
# $Id: tkt3461.test,v 1.3 2009/04/23 13:22:44 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -57,6 +57,7 @@ do_test tkt3461-3.1 {
|
||||
INSERT INTO t2 VALUES(3, 4);
|
||||
}
|
||||
# execsql { PRAGMA vdbe_trace = 1; PRAGMA vdbe_listing=1 }
|
||||
breakpoint
|
||||
execsql {
|
||||
SELECT a, b+1 AS b_plus_one, c, d
|
||||
FROM t1 LEFT JOIN t2
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
# 2009 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Ticket #3824
|
||||
#
|
||||
# When you use an "IS NULL" constraint on a UNIQUE index, the result
|
||||
# is not necessarily UNIQUE. Make sure the optimizer does not assume
|
||||
# uniqueness.
|
||||
#
|
||||
# $Id: tkt3824.test,v 1.2 2009/04/24 20:32:31 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
proc execsql_status {sql {db db}} {
|
||||
set result [uplevel $db eval [list $sql]]
|
||||
if {[db status sort]} {
|
||||
concat $result sort
|
||||
} else {
|
||||
concat $result nosort
|
||||
}
|
||||
}
|
||||
|
||||
do_test tkt3824-1.1 {
|
||||
db eval {
|
||||
CREATE TABLE t1(a,b);
|
||||
INSERT INTO t1 VALUES(1,NULL);
|
||||
INSERT INTO t1 VALUES(9,NULL);
|
||||
INSERT INTO t1 VALUES(5,NULL);
|
||||
INSERT INTO t1 VALUES(123,NULL);
|
||||
INSERT INTO t1 VALUES(-10,NULL);
|
||||
CREATE UNIQUE INDEX t1b ON t1(b);
|
||||
}
|
||||
execsql_status {
|
||||
SELECT a FROM t1 WHERE b IS NULL ORDER BY a;
|
||||
}
|
||||
} {-10 1 5 9 123 sort}
|
||||
do_test tkt3824-1.2 {
|
||||
execsql_status {
|
||||
SELECT a FROM t1 WHERE b IS NULL ORDER BY b, a;
|
||||
}
|
||||
} {-10 1 5 9 123 sort}
|
||||
|
||||
do_test tkt3824-2.1 {
|
||||
db eval {
|
||||
CREATE TABLE t2(a,b,c);
|
||||
INSERT INTO t2 VALUES(1,1,NULL);
|
||||
INSERT INTO t2 VALUES(9,2,NULL);
|
||||
INSERT INTO t2 VALUES(5,2,NULL);
|
||||
INSERT INTO t2 VALUES(123,3,NULL);
|
||||
INSERT INTO t2 VALUES(-10,3,NULL);
|
||||
CREATE UNIQUE INDEX t2bc ON t2(b,c);
|
||||
}
|
||||
execsql_status {
|
||||
SELECT a FROM t2 WHERE b=2 AND c IS NULL ORDER BY a;
|
||||
}
|
||||
} {5 9 sort}
|
||||
do_test tkt3824-2.2 {
|
||||
execsql_status {
|
||||
SELECT a FROM t2 WHERE b=2 AND c IS NULL ORDER BY b, a;
|
||||
}
|
||||
} {5 9 sort}
|
||||
do_test tkt3824-2.3 {
|
||||
lsort [execsql_status {
|
||||
SELECT a FROM t2 WHERE b=2 AND c IS NULL ORDER BY b;
|
||||
}]
|
||||
} {5 9 sort}
|
||||
|
||||
do_test tkt3824-3.1 {
|
||||
db eval {
|
||||
CREATE TABLE t3(x,y);
|
||||
INSERT INTO t3 SELECT a, b FROM t1;
|
||||
INSERT INTO t3 VALUES(234,567);
|
||||
CREATE UNIQUE INDEX t3y ON t3(y);
|
||||
DELETE FROM t3 WHERE y IS NULL;
|
||||
SELECT * FROM t3;
|
||||
}
|
||||
} {234 567}
|
||||
|
||||
do_test tkt3824-4.1 {
|
||||
db eval {
|
||||
CREATE TABLE t4(x,y);
|
||||
INSERT INTO t4 SELECT a, b FROM t1;
|
||||
INSERT INTO t4 VALUES(234,567);
|
||||
CREATE UNIQUE INDEX t4y ON t4(y);
|
||||
UPDATE t4 SET rowid=rowid+100 WHERE y IS NULL;
|
||||
SELECT rowid, x FROM t4 ORDER BY rowid;
|
||||
}
|
||||
} {6 234 101 1 102 9 103 5 104 123 105 -10}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,37 @@
|
||||
# 2009 April 30
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Ticket #3832
|
||||
#
|
||||
# A segfault when using a BEFORE trigger on an INSERT and inserting
|
||||
# a NULL into the INTEGER PRIMARY KEY.
|
||||
#
|
||||
# $Id: tkt3832.test,v 1.1 2009/05/01 02:08:04 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
|
||||
do_test tkt3832-1.1 {
|
||||
db eval {
|
||||
CREATE TABLE t1(a INT, b INTEGER PRIMARY KEY);
|
||||
CREATE TABLE log(x);
|
||||
CREATE TRIGGER t1r1 BEFORE INSERT ON t1 BEGIN
|
||||
INSERT INTO log VALUES(new.b);
|
||||
END;
|
||||
INSERT INTO t1 VALUES(NULL,5);
|
||||
INSERT INTO t1 SELECT b, a FROM t1 ORDER BY b;
|
||||
SELECT rowid, * FROM t1;
|
||||
SELECT rowid, * FROM log;
|
||||
}
|
||||
} {5 {} 5 6 5 6 1 5 2 -1}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,37 @@
|
||||
# 2009 May 5
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Ticket #3838
|
||||
#
|
||||
# The ticket reports that the encoding is UTF8 on the DEFAULT VALUE of
|
||||
# a column added using ALTER TABLE even when the database is UTF16.
|
||||
# Verify that this has been fixed.
|
||||
#
|
||||
# $Id: tkt3838.test,v 1.1 2009/05/05 12:54:50 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
|
||||
do_test tkt3838-1.1 {
|
||||
db eval {
|
||||
PRAGMA encoding=UTF16;
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(1);
|
||||
ALTER TABLE t1 ADD COLUMN b INTEGER DEFAULT '999';
|
||||
ALTER TABLE t1 ADD COLUMN c REAL DEFAULT '9e99';
|
||||
ALTER TABLE t1 ADD COLUMN d TEXT DEFAULT 'xyzzy';
|
||||
UPDATE t1 SET x=x+1;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {2 999 9e+99 xyzzy}
|
||||
|
||||
finish_test
|
||||
+9
-2
@@ -11,7 +11,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is database locks.
|
||||
#
|
||||
# $Id: trans.test,v 1.40 2009/03/27 09:10:12 danielk1977 Exp $
|
||||
# $Id: trans.test,v 1.41 2009/04/28 16:37:59 danielk1977 Exp $
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
@@ -871,7 +871,14 @@ proc signature {} {
|
||||
# t3 a little larger, and thus takes a little longer, so doing 40 tests
|
||||
# is more than 2.0 times slower than doing 20 tests. Considerably more.
|
||||
#
|
||||
if {[info exists ISQUICK]} {
|
||||
# Also, if temporary tables are stored in memory and the test pcache
|
||||
# is in use, only 20 iterations. Otherwise the test pcache runs out
|
||||
# of page slots and SQLite reports "out of memory".
|
||||
#
|
||||
if {[info exists ISQUICK] || (
|
||||
$TEMP_STORE==3 && [catch {set ::permutations_test_prefix} val]==0 &&
|
||||
[regexp {^pcache[[:digit:]]*$} $val]
|
||||
) } {
|
||||
set limit 20
|
||||
} elseif {[info exists SOAKTEST]} {
|
||||
set limit 100
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@
|
||||
# focus of this file is testing the CREATE UNIQUE INDEX statement,
|
||||
# and primary keys, and the UNIQUE constraint on table columns
|
||||
#
|
||||
# $Id: unique.test,v 1.8 2005/06/24 03:53:06 drh Exp $
|
||||
# $Id: unique.test,v 1.9 2009/05/02 15:46:47 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -248,6 +248,6 @@ do_test unique-5.2 {
|
||||
catchsql {
|
||||
INSERT INTO t5 VALUES(1,2,3,4,5,6);
|
||||
}
|
||||
} {1 {columns first_column_with_long_name, second_column_with_long_name, third_column_with_long_name, fourth_column_with_long_name, fifth_column_with_long_name, ... are not unique}}
|
||||
} {1 {columns first_column_with_long_name, second_column_with_long_name, third_column_with_long_name, fourth_column_with_long_name, fifth_column_with_long_name, sixth_column_with_long_name are not unique}}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
# 2009 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is creating and dropping virtual tables.
|
||||
#
|
||||
# $Id: vtabD.test,v 1.2 2009/04/23 18:42:05 shane Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !vtab||!schema_pragmas { finish_test ; return }
|
||||
|
||||
# Register the echo module
|
||||
register_echo_module [sqlite3_connection_pointer db]
|
||||
|
||||
do_test vtabD-1.1 {
|
||||
execsql {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(a);
|
||||
CREATE INDEX i2 ON t1(b);
|
||||
CREATE VIRTUAL TABLE tv1 USING echo(t1);
|
||||
}
|
||||
} {}
|
||||
do_test vtabD-1.2 {
|
||||
execsql BEGIN
|
||||
for {set i 0} {$i < 100000} {incr i} {
|
||||
execsql { INSERT INTO t1 VALUES($i, $i*$i) }
|
||||
}
|
||||
execsql COMMIT
|
||||
} {}
|
||||
do_test vtabD-1.3 {
|
||||
execsql { SELECT * FROM tv1 WHERE a = 1 OR b = 4 }
|
||||
} {1 1 2 4}
|
||||
do_test vtabD-1.4 {
|
||||
execsql { SELECT * FROM tv1 WHERE a = 1 OR b = 1 }
|
||||
} {1 1}
|
||||
do_test vtabD-1.5 {
|
||||
execsql { SELECT * FROM tv1 WHERE (a > 0 AND a < 5) OR (b > 15 AND b < 65) }
|
||||
} {1 1 2 4 3 9 4 16 5 25 6 36 7 49 8 64}
|
||||
|
||||
do_test vtabD-1.6 {
|
||||
execsql { SELECT * FROM tv1 WHERE a < 500 OR b = 810000 }
|
||||
} [execsql {
|
||||
SELECT * FROM t1 WHERE a < 500
|
||||
UNION ALL
|
||||
SELECT * FROM t1 WHERE b = 810000 AND NOT (a < 500)
|
||||
}]
|
||||
|
||||
do_test vtabD-1.7 {
|
||||
execsql { SELECT * FROM tv1 WHERE a < 90000 OR b = 8100000000 }
|
||||
} [execsql {
|
||||
SELECT * FROM t1 WHERE a < 90000
|
||||
UNION ALL
|
||||
SELECT * FROM t1 WHERE b = 8100000000 AND NOT (a < 90000)
|
||||
}]
|
||||
|
||||
if {[working_64bit_int]} {
|
||||
do_test vtabD-1.8 {
|
||||
execsql { SELECT * FROM tv1 WHERE a = 90001 OR b = 810000 }
|
||||
} {90001 8100180001 900 810000}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
+22900
-24587
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user