/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
-** version 3.7.6.3. By combining all the individual C code files into this
+** version 3.7.8. By combining all the individual C code files into this
** single large file, the entire code can be compiled as a single translation
** unit. This allows many compilers to do optimizations that would not be
** possible if the files were compiled separately. Performance improvements
** specify which memory allocation subsystem to use.
**
** SQLITE_SYSTEM_MALLOC // Use normal system malloc()
+** SQLITE_WIN32_MALLOC // Use Win32 native heap API
** SQLITE_MEMDEBUG // Debugging version of system malloc()
**
+** On Windows, if the SQLITE_WIN32_MALLOC_VALIDATE macro is defined and the
+** assert() macro is enabled, each call into the Win32 native heap subsystem
+** will cause HeapValidate to be called. If heap validation should fail, an
+** assertion will be triggered.
+**
** (Historical note: There used to be several other options, but we've
** pared it down to just these two.)
**
** If none of the above are defined, then set SQLITE_SYSTEM_MALLOC as
** the default.
*/
-#if defined(SQLITE_SYSTEM_MALLOC)+defined(SQLITE_MEMDEBUG)>1
+#if defined(SQLITE_SYSTEM_MALLOC)+defined(SQLITE_WIN32_MALLOC)+defined(SQLITE_MEMDEBUG)>1
# error "At most one of the following compile-time configuration options\
- is allows: SQLITE_SYSTEM_MALLOC, SQLITE_MEMDEBUG"
+ is allows: SQLITE_SYSTEM_MALLOC, SQLITE_WIN32_MALLOC, SQLITE_MEMDEBUG"
#endif
-#if defined(SQLITE_SYSTEM_MALLOC)+defined(SQLITE_MEMDEBUG)==0
+#if defined(SQLITE_SYSTEM_MALLOC)+defined(SQLITE_WIN32_MALLOC)+defined(SQLITE_MEMDEBUG)==0
# define SQLITE_SYSTEM_MALLOC 1
#endif
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.7.6.3"
-#define SQLITE_VERSION_NUMBER 3007006
-#define SQLITE_SOURCE_ID "2011-05-19 13:26:54 ed1da510a239ea767a01dc332b667119fa3c908e"
+#define SQLITE_VERSION "3.7.8"
+#define SQLITE_VERSION_NUMBER 3007008
+#define SQLITE_SOURCE_ID "2011-09-19 14:49:19 3e0da808d2f5b4d12046e05980ca04578f581177"
/*
** CAPI3REF: Run-Time Library Version Numbers
** argument. ^If the callback function of the 3rd argument to
** sqlite3_exec() is not NULL, then it is invoked for each result row
** coming out of the evaluated SQL statements. ^The 4th argument to
-** to sqlite3_exec() is relayed through to the 1st argument of each
+** sqlite3_exec() is relayed through to the 1st argument of each
** callback invocation. ^If the callback pointer to sqlite3_exec()
** is NULL, then no callback is ever invoked and result rows are
** ignored.
**
** New error codes may be added in future versions of SQLite.
**
-** See also: [SQLITE_IOERR_READ | extended result codes]
+** See also: [SQLITE_IOERR_READ | extended result codes],
+** [sqlite3_vtab_on_conflict()] [SQLITE_ROLLBACK | result codes].
*/
#define SQLITE_OK 0 /* Successful result */
/* beginning-of-error-codes */
#define SQLITE_IOERR_SHMOPEN (SQLITE_IOERR | (18<<8))
#define SQLITE_IOERR_SHMSIZE (SQLITE_IOERR | (19<<8))
#define SQLITE_IOERR_SHMLOCK (SQLITE_IOERR | (20<<8))
+#define SQLITE_IOERR_SHMMAP (SQLITE_IOERR | (21<<8))
+#define SQLITE_IOERR_SEEK (SQLITE_IOERR | (22<<8))
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
+#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8))
+#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8))
+#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8))
/*
** CAPI3REF: Flags For File Open Operations
**
** These bit values are intended for use in the
** 3rd parameter to the [sqlite3_open_v2()] interface and
-** in the 4th parameter to the xOpen method of the
-** [sqlite3_vfs] object.
+** in the 4th parameter to the [sqlite3_vfs.xOpen] method.
*/
#define SQLITE_OPEN_READONLY 0x00000001 /* Ok for sqlite3_open_v2() */
#define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlite3_open_v2() */
#define SQLITE_OPEN_DELETEONCLOSE 0x00000008 /* VFS only */
#define SQLITE_OPEN_EXCLUSIVE 0x00000010 /* VFS only */
#define SQLITE_OPEN_AUTOPROXY 0x00000020 /* VFS only */
+#define SQLITE_OPEN_URI 0x00000040 /* Ok for sqlite3_open_v2() */
#define SQLITE_OPEN_MAIN_DB 0x00000100 /* VFS only */
#define SQLITE_OPEN_TEMP_DB 0x00000200 /* VFS only */
#define SQLITE_OPEN_TRANSIENT_DB 0x00000400 /* VFS only */
/*
** CAPI3REF: OS Interface File Virtual Methods Object
**
-** Every file opened by the [sqlite3_vfs] xOpen method populates an
+** Every file opened by the [sqlite3_vfs.xOpen] method populates an
** [sqlite3_file] object (or, more commonly, a subclass of the
** [sqlite3_file] object) with a pointer to an instance of this object.
** This object defines the methods used to perform various operations
** against the open file represented by the [sqlite3_file] object.
**
-** If the xOpen method sets the sqlite3_file.pMethods element
+** If the [sqlite3_vfs.xOpen] method sets the sqlite3_file.pMethods element
** to a non-NULL pointer, then the sqlite3_io_methods.xClose method
-** may be invoked even if the xOpen reported that it failed. The
-** only way to prevent a call to xClose following a failed xOpen
-** is for the xOpen to set the sqlite3_file.pMethods element to NULL.
+** may be invoked even if the [sqlite3_vfs.xOpen] reported that it failed. The
+** only way to prevent a call to xClose following a failed [sqlite3_vfs.xOpen]
+** is for the [sqlite3_vfs.xOpen] to set the sqlite3_file.pMethods element
+** to NULL.
**
** The flags argument to xSync may be one of [SQLITE_SYNC_NORMAL] or
** [SQLITE_SYNC_FULL]. The first choice is the normal fsync().
** Applications should not call [sqlite3_file_control()] with this
** opcode as doing so may disrupt the operation of the specialized VFSes
** that do require it.
+**
+** ^The [SQLITE_FCNTL_WIN32_AV_RETRY] opcode is used to configure automatic
+** retry counts and intervals for certain disk I/O operations for the
+** windows [VFS] in order to work to provide robustness against
+** anti-virus programs. By default, the windows VFS will retry file read,
+** file write, and file delete opertions up to 10 times, with a delay
+** of 25 milliseconds before the first retry and with the delay increasing
+** by an additional 25 milliseconds with each subsequent retry. This
+** opcode allows those to values (10 retries and 25 milliseconds of delay)
+** to be adjusted. The values are changed for all database connections
+** within the same process. The argument is a pointer to an array of two
+** integers where the first integer i the new retry count and the second
+** integer is the delay. If either integer is negative, then the setting
+** is not changed but instead the prior value of that setting is written
+** into the array entry, allowing the current retry settings to be
+** interrogated. The zDbName parameter is ignored.
+**
+** ^The [SQLITE_FCNTL_PERSIST_WAL] opcode is used to set or query the
+** persistent [WAL | Write AHead Log] setting. By default, the auxiliary
+** write ahead log and shared memory files used for transaction control
+** are automatically deleted when the latest connection to the database
+** closes. Setting persistent WAL mode causes those files to persist after
+** close. Persisting the files is useful when other processes that do not
+** have write permission on the directory containing the database file want
+** to read the database file, as the WAL and shared memory files must exist
+** in order for the database to be readable. The fourth parameter to
+** [sqlite3_file_control()] for this opcode should be a pointer to an integer.
+** That integer is 0 to disable persistent WAL mode or 1 to enable persistent
+** WAL mode. If the integer is -1, then it is overwritten with the current
+** WAL persistence setting.
+**
*/
#define SQLITE_FCNTL_LOCKSTATE 1
#define SQLITE_GET_LOCKPROXYFILE 2
#define SQLITE_FCNTL_CHUNK_SIZE 6
#define SQLITE_FCNTL_FILE_POINTER 7
#define SQLITE_FCNTL_SYNC_OMITTED 8
-
+#define SQLITE_FCNTL_WIN32_AV_RETRY 9
+#define SQLITE_FCNTL_PERSIST_WAL 10
/*
** CAPI3REF: Mutex Handle
**
** An instance of the sqlite3_vfs object defines the interface between
** the SQLite core and the underlying operating system. The "vfs"
-** in the name of the object stands for "virtual file system".
+** in the name of the object stands for "virtual file system". See
+** the [VFS | VFS documentation] for further information.
**
** The value of the iVersion field is initially 1 but may be larger in
** future versions of SQLite. Additional fields may be appended to this
** The zName field holds the name of the VFS module. The name must
** be unique across all VFS modules.
**
+** [[sqlite3_vfs.xOpen]]
** ^SQLite guarantees that the zFilename parameter to xOpen
** is either a NULL pointer or string obtained
** from xFullPathname() with an optional suffix added.
** element will be valid after xOpen returns regardless of the success
** or failure of the xOpen call.
**
+** [[sqlite3_vfs.xAccess]]
** ^The flags argument to xAccess() may be [SQLITE_ACCESS_EXISTS]
** to test for the existence of a file, or [SQLITE_ACCESS_READWRITE] to
** test whether a file is readable and writable, or [SQLITE_ACCESS_READ]
** method returns a Julian Day Number for the current date and time as
** a floating point value.
** ^The xCurrentTimeInt64() method returns, as an integer, the Julian
-** Day Number multipled by 86400000 (the number of milliseconds in
+** Day Number multiplied by 86400000 (the number of milliseconds in
** a 24-hour day).
** ^SQLite will use the xCurrentTimeInt64() method to get the current
** date and time if that method is available (if iVersion is 2 or
** implementation of an application-defined [sqlite3_os_init()].
**
** The first argument to sqlite3_config() is an integer
-** [SQLITE_CONFIG_SINGLETHREAD | configuration option] that determines
+** [configuration option] that determines
** what property of SQLite is to be configured. Subsequent arguments
-** vary depending on the [SQLITE_CONFIG_SINGLETHREAD | configuration option]
+** vary depending on the [configuration option]
** in the first argument.
**
** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK].
** order to verify that SQLite recovers gracefully from such
** conditions.
**
-** The xMalloc and xFree methods must work like the
-** malloc() and free() functions from the standard C library.
-** The xRealloc method must work like realloc() from the standard C library
-** with the exception that if the second argument to xRealloc is zero,
-** xRealloc must be a no-op - it must not perform any allocation or
-** deallocation. ^SQLite guarantees that the second argument to
+** The xMalloc, xRealloc, and xFree methods must work like the
+** malloc(), realloc() and free() functions from the standard C library.
+** ^SQLite guarantees that the second argument to
** xRealloc is always a value returned by a prior call to xRoundup.
-** And so in cases where xRoundup always returns a positive number,
-** xRealloc can perform exactly as the standard library realloc() and
-** still be in compliance with this specification.
**
** xSize should return the allocated size of a memory allocation
** previously obtained from xMalloc or xRealloc. The allocated size
/*
** CAPI3REF: Configuration Options
+** KEYWORDS: {configuration option}
**
** These constants are the available integer configuration options that
** can be passed as the first argument to the [sqlite3_config()] interface.
** is invoked.
**
** <dl>
-** <dt>SQLITE_CONFIG_SINGLETHREAD</dt>
+** [[SQLITE_CONFIG_SINGLETHREAD]] <dt>SQLITE_CONFIG_SINGLETHREAD</dt>
** <dd>There are no arguments to this option. ^This option sets the
** [threading mode] to Single-thread. In other words, it disables
** all mutexing and puts SQLite into a mode where it can only be used
** [SQLITE_ERROR] if called with the SQLITE_CONFIG_SINGLETHREAD
** configuration option.</dd>
**
-** <dt>SQLITE_CONFIG_MULTITHREAD</dt>
+** [[SQLITE_CONFIG_MULTITHREAD]] <dt>SQLITE_CONFIG_MULTITHREAD</dt>
** <dd>There are no arguments to this option. ^This option sets the
** [threading mode] to Multi-thread. In other words, it disables
** mutexing on [database connection] and [prepared statement] objects.
** [sqlite3_config()] will return [SQLITE_ERROR] if called with the
** SQLITE_CONFIG_MULTITHREAD configuration option.</dd>
**
-** <dt>SQLITE_CONFIG_SERIALIZED</dt>
+** [[SQLITE_CONFIG_SERIALIZED]] <dt>SQLITE_CONFIG_SERIALIZED</dt>
** <dd>There are no arguments to this option. ^This option sets the
** [threading mode] to Serialized. In other words, this option enables
** all mutexes including the recursive
** [sqlite3_config()] will return [SQLITE_ERROR] if called with the
** SQLITE_CONFIG_SERIALIZED configuration option.</dd>
**
-** <dt>SQLITE_CONFIG_MALLOC</dt>
+** [[SQLITE_CONFIG_MALLOC]] <dt>SQLITE_CONFIG_MALLOC</dt>
** <dd> ^(This option takes a single argument which is a pointer to an
** instance of the [sqlite3_mem_methods] structure. The argument specifies
** alternative low-level memory allocation routines to be used in place of
** its own private copy of the content of the [sqlite3_mem_methods] structure
** before the [sqlite3_config()] call returns.</dd>
**
-** <dt>SQLITE_CONFIG_GETMALLOC</dt>
+** [[SQLITE_CONFIG_GETMALLOC]] <dt>SQLITE_CONFIG_GETMALLOC</dt>
** <dd> ^(This option takes a single argument which is a pointer to an
** instance of the [sqlite3_mem_methods] structure. The [sqlite3_mem_methods]
** structure is filled with the currently defined memory allocation routines.)^
** routines with a wrapper that simulations memory allocation failure or
** tracks memory usage, for example. </dd>
**
-** <dt>SQLITE_CONFIG_MEMSTATUS</dt>
+** [[SQLITE_CONFIG_MEMSTATUS]] <dt>SQLITE_CONFIG_MEMSTATUS</dt>
** <dd> ^This option takes single argument of type int, interpreted as a
** boolean, which enables or disables the collection of memory allocation
** statistics. ^(When memory allocation statistics are disabled, the
** allocation statistics are disabled by default.
** </dd>
**
-** <dt>SQLITE_CONFIG_SCRATCH</dt>
+** [[SQLITE_CONFIG_SCRATCH]] <dt>SQLITE_CONFIG_SCRATCH</dt>
** <dd> ^This option specifies a static memory buffer that SQLite can use for
** scratch memory. There are three arguments: A pointer an 8-byte
** aligned memory buffer from which the scratch allocations will be
** scratch memory beyond what is provided by this configuration option, then
** [sqlite3_malloc()] will be used to obtain the memory needed.</dd>
**
-** <dt>SQLITE_CONFIG_PAGECACHE</dt>
+** [[SQLITE_CONFIG_PAGECACHE]] <dt>SQLITE_CONFIG_PAGECACHE</dt>
** <dd> ^This option specifies a static memory buffer that SQLite can use for
-** the database page cache with the default page cache implemenation.
+** the database page cache with the default page cache implementation.
** This configuration should not be used if an application-define page
** cache implementation is loaded using the SQLITE_CONFIG_PCACHE option.
** There are three arguments to this option: A pointer to 8-byte aligned
** be aligned to an 8-byte boundary or subsequent behavior of SQLite
** will be undefined.</dd>
**
-** <dt>SQLITE_CONFIG_HEAP</dt>
+** [[SQLITE_CONFIG_HEAP]] <dt>SQLITE_CONFIG_HEAP</dt>
** <dd> ^This option specifies a static memory buffer that SQLite will use
** for all of its dynamic memory allocation needs beyond those provided
** for by [SQLITE_CONFIG_SCRATCH] and [SQLITE_CONFIG_PAGECACHE].
** The minimum allocation size is capped at 2^12. Reasonable values
** for the minimum allocation size are 2^5 through 2^8.</dd>
**
-** <dt>SQLITE_CONFIG_MUTEX</dt>
+** [[SQLITE_CONFIG_MUTEX]] <dt>SQLITE_CONFIG_MUTEX</dt>
** <dd> ^(This option takes a single argument which is a pointer to an
** instance of the [sqlite3_mutex_methods] structure. The argument specifies
** alternative low-level mutex routines to be used in place
** [sqlite3_config()] with the SQLITE_CONFIG_MUTEX configuration option will
** return [SQLITE_ERROR].</dd>
**
-** <dt>SQLITE_CONFIG_GETMUTEX</dt>
+** [[SQLITE_CONFIG_GETMUTEX]] <dt>SQLITE_CONFIG_GETMUTEX</dt>
** <dd> ^(This option takes a single argument which is a pointer to an
** instance of the [sqlite3_mutex_methods] structure. The
** [sqlite3_mutex_methods]
** [sqlite3_config()] with the SQLITE_CONFIG_GETMUTEX configuration option will
** return [SQLITE_ERROR].</dd>
**
-** <dt>SQLITE_CONFIG_LOOKASIDE</dt>
+** [[SQLITE_CONFIG_LOOKASIDE]] <dt>SQLITE_CONFIG_LOOKASIDE</dt>
** <dd> ^(This option takes two arguments that determine the default
** memory allocation for the lookaside memory allocator on each
** [database connection]. The first argument is the
** verb to [sqlite3_db_config()] can be used to change the lookaside
** configuration on individual connections.)^ </dd>
**
-** <dt>SQLITE_CONFIG_PCACHE</dt>
+** [[SQLITE_CONFIG_PCACHE]] <dt>SQLITE_CONFIG_PCACHE</dt>
** <dd> ^(This option takes a single argument which is a pointer to
** an [sqlite3_pcache_methods] object. This object specifies the interface
** to a custom page cache implementation.)^ ^SQLite makes a copy of the
** object and uses it for page cache memory allocations.</dd>
**
-** <dt>SQLITE_CONFIG_GETPCACHE</dt>
+** [[SQLITE_CONFIG_GETPCACHE]] <dt>SQLITE_CONFIG_GETPCACHE</dt>
** <dd> ^(This option takes a single argument which is a pointer to an
** [sqlite3_pcache_methods] object. SQLite copies of the current
** page cache implementation into that object.)^ </dd>
**
-** <dt>SQLITE_CONFIG_LOG</dt>
+** [[SQLITE_CONFIG_LOG]] <dt>SQLITE_CONFIG_LOG</dt>
** <dd> ^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a
** function with a call signature of void(*)(void*,int,const char*),
** and a pointer to void. ^If the function pointer is not NULL, it is
** In a multi-threaded application, the application-defined logger
** function must be threadsafe. </dd>
**
+** [[SQLITE_CONFIG_URI]] <dt>SQLITE_CONFIG_URI
+** <dd> This option takes a single argument of type int. If non-zero, then
+** URI handling is globally enabled. If the parameter is zero, then URI handling
+** is globally disabled. If URI handling is globally enabled, all filenames
+** passed to [sqlite3_open()], [sqlite3_open_v2()], [sqlite3_open16()] or
+** specified as part of [ATTACH] commands are interpreted as URIs, regardless
+** of whether or not the [SQLITE_OPEN_URI] flag is set when the database
+** connection is opened. If it is globally disabled, filenames are
+** only interpreted as URIs if the SQLITE_OPEN_URI flag is set when the
+** database connection is opened. By default, URI handling is globally
+** disabled. The default value may be changed by compiling with the
+** [SQLITE_USE_URI] symbol defined.
** </dl>
*/
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
#define SQLITE_CONFIG_PCACHE 14 /* sqlite3_pcache_methods* */
#define SQLITE_CONFIG_GETPCACHE 15 /* sqlite3_pcache_methods* */
#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */
+#define SQLITE_CONFIG_URI 17 /* int */
/*
** CAPI3REF: Database Connection Configuration Options
**
** ^This routine returns the [rowid] of the most recent
** successful [INSERT] into the database from the [database connection]
-** in the first argument. ^If no successful [INSERT]s
+** in the first argument. ^As of SQLite version 3.7.7, this routines
+** records the last insert rowid of both ordinary tables and [virtual tables].
+** ^If no successful [INSERT]s
** have ever occurred on that database connection, zero is returned.
**
-** ^(If an [INSERT] occurs within a trigger, then the [rowid] of the inserted
-** row is returned by this routine as long as the trigger is running.
-** But once the trigger terminates, the value returned by this routine
-** reverts to the last value inserted before the trigger fired.)^
+** ^(If an [INSERT] occurs within a trigger or within a [virtual table]
+** method, then this routine will return the [rowid] of the inserted
+** row as long as the trigger or virtual table method is running.
+** But once the trigger or virtual table method ends, the value returned
+** by this routine reverts to what it was before the trigger or virtual
+** table method began.)^
**
** ^An [INSERT] that fails due to a constraint violation is not a
** successful [INSERT] and does not change the value returned by this
** to signal SQLite whether or not the action is permitted. See the
** [sqlite3_set_authorizer | authorizer documentation] for additional
** information.
+**
+** Note that SQLITE_IGNORE is also used as a [SQLITE_ROLLBACK | return code]
+** from the [sqlite3_vtab_on_conflict()] interface.
*/
#define SQLITE_DENY 1 /* Abort the SQL statement with an error */
#define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */
/*
** CAPI3REF: Opening A New Database Connection
**
-** ^These routines open an SQLite database file whose name is given by the
+** ^These routines open an SQLite database file as specified by the
** filename argument. ^The filename argument is interpreted as UTF-8 for
** sqlite3_open() and sqlite3_open_v2() and as UTF-16 in the native byte
** order for sqlite3_open16(). ^(A [database connection] handle is usually
** sqlite3_open_v2() can take one of
** the following three values, optionally combined with the
** [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], [SQLITE_OPEN_SHAREDCACHE],
-** and/or [SQLITE_OPEN_PRIVATECACHE] flags:)^
+** [SQLITE_OPEN_PRIVATECACHE], and/or [SQLITE_OPEN_URI] flags:)^
**
** <dl>
** ^(<dt>[SQLITE_OPEN_READONLY]</dt>
** </dl>
**
** If the 3rd parameter to sqlite3_open_v2() is not one of the
-** combinations shown above or one of the combinations shown above combined
-** with the [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX],
-** [SQLITE_OPEN_SHAREDCACHE] and/or [SQLITE_OPEN_PRIVATECACHE] flags,
+** combinations shown above optionally combined with other
+** [SQLITE_OPEN_READONLY | SQLITE_OPEN_* bits]
** then the behavior is undefined.
**
** ^If the [SQLITE_OPEN_NOMUTEX] flag is set, then the database connection
** [SQLITE_OPEN_PRIVATECACHE] flag causes the database connection to not
** participate in [shared cache mode] even if it is enabled.
**
+** ^The fourth parameter to sqlite3_open_v2() is the name of the
+** [sqlite3_vfs] object that defines the operating system interface that
+** the new database connection should use. ^If the fourth parameter is
+** a NULL pointer then the default [sqlite3_vfs] object is used.
+**
** ^If the filename is ":memory:", then a private, temporary in-memory database
** is created for the connection. ^This in-memory database will vanish when
** the database connection is closed. Future versions of SQLite might
** on-disk database will be created. ^This private database will be
** automatically deleted as soon as the database connection is closed.
**
-** ^The fourth parameter to sqlite3_open_v2() is the name of the
-** [sqlite3_vfs] object that defines the operating system interface that
-** the new database connection should use. ^If the fourth parameter is
-** a NULL pointer then the default [sqlite3_vfs] object is used.
+** [[URI filenames in sqlite3_open()]] <h3>URI Filenames</h3>
+**
+** ^If [URI filename] interpretation is enabled, and the filename argument
+** begins with "file:", then the filename is interpreted as a URI. ^URI
+** filename interpretation is enabled if the [SQLITE_OPEN_URI] flag is
+** set in the fourth argument to sqlite3_open_v2(), or if it has
+** been enabled globally using the [SQLITE_CONFIG_URI] option with the
+** [sqlite3_config()] method or by the [SQLITE_USE_URI] compile-time option.
+** As of SQLite version 3.7.7, URI filename interpretation is turned off
+** by default, but future releases of SQLite might enable URI filename
+** interpretation by default. See "[URI filenames]" for additional
+** information.
+**
+** URI filenames are parsed according to RFC 3986. ^If the URI contains an
+** authority, then it must be either an empty string or the string
+** "localhost". ^If the authority is not an empty string or "localhost", an
+** error is returned to the caller. ^The fragment component of a URI, if
+** present, is ignored.
+**
+** ^SQLite uses the path component of the URI as the name of the disk file
+** which contains the database. ^If the path begins with a '/' character,
+** then it is interpreted as an absolute path. ^If the path does not begin
+** with a '/' (meaning that the authority section is omitted from the URI)
+** then the path is interpreted as a relative path.
+** ^On windows, the first component of an absolute path
+** is a drive specification (e.g. "C:").
+**
+** [[core URI query parameters]]
+** The query component of a URI may contain parameters that are interpreted
+** either by SQLite itself, or by a [VFS | custom VFS implementation].
+** SQLite interprets the following three query parameters:
+**
+** <ul>
+** <li> <b>vfs</b>: ^The "vfs" parameter may be used to specify the name of
+** a VFS object that provides the operating system interface that should
+** be used to access the database file on disk. ^If this option is set to
+** an empty string the default VFS object is used. ^Specifying an unknown
+** VFS is an error. ^If sqlite3_open_v2() is used and the vfs option is
+** present, then the VFS specified by the option takes precedence over
+** the value passed as the fourth parameter to sqlite3_open_v2().
+**
+** <li> <b>mode</b>: ^(The mode parameter may be set to either "ro", "rw" or
+** "rwc". Attempting to set it to any other value is an error)^.
+** ^If "ro" is specified, then the database is opened for read-only
+** access, just as if the [SQLITE_OPEN_READONLY] flag had been set in the
+** third argument to sqlite3_prepare_v2(). ^If the mode option is set to
+** "rw", then the database is opened for read-write (but not create)
+** access, as if SQLITE_OPEN_READWRITE (but not SQLITE_OPEN_CREATE) had
+** been set. ^Value "rwc" is equivalent to setting both
+** SQLITE_OPEN_READWRITE and SQLITE_OPEN_CREATE. ^If sqlite3_open_v2() is
+** used, it is an error to specify a value for the mode parameter that is
+** less restrictive than that specified by the flags passed as the third
+** parameter.
+**
+** <li> <b>cache</b>: ^The cache parameter may be set to either "shared" or
+** "private". ^Setting it to "shared" is equivalent to setting the
+** SQLITE_OPEN_SHAREDCACHE bit in the flags argument passed to
+** sqlite3_open_v2(). ^Setting the cache parameter to "private" is
+** equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit.
+** ^If sqlite3_open_v2() is used and the "cache" parameter is present in
+** a URI filename, its value overrides any behaviour requested by setting
+** SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag.
+** </ul>
+**
+** ^Specifying an unknown parameter in the query component of a URI is not an
+** error. Future versions of SQLite might understand additional query
+** parameters. See "[query parameters with special meaning to SQLite]" for
+** additional information.
+**
+** [[URI filename examples]] <h3>URI filename examples</h3>
+**
+** <table border="1" align=center cellpadding=5>
+** <tr><th> URI filenames <th> Results
+** <tr><td> file:data.db <td>
+** Open the file "data.db" in the current directory.
+** <tr><td> file:/home/fred/data.db<br>
+** file:///home/fred/data.db <br>
+** file://localhost/home/fred/data.db <br> <td>
+** Open the database file "/home/fred/data.db".
+** <tr><td> file://darkstar/home/fred/data.db <td>
+** An error. "darkstar" is not a recognized authority.
+** <tr><td style="white-space:nowrap">
+** file:///C:/Documents%20and%20Settings/fred/Desktop/data.db
+** <td> Windows only: Open the file "data.db" on fred's desktop on drive
+** C:. Note that the %20 escaping in this example is not strictly
+** necessary - space characters can be used literally
+** in URI filenames.
+** <tr><td> file:data.db?mode=ro&cache=private <td>
+** Open file "data.db" in the current directory for read-only access.
+** Regardless of whether or not shared-cache mode is enabled by
+** default, use a private cache.
+** <tr><td> file:/home/fred/data.db?vfs=unix-nolock <td>
+** Open file "/home/fred/data.db". Use the special VFS "unix-nolock".
+** <tr><td> file:data.db?mode=readonly <td>
+** An error. "readonly" is not a valid option for the "mode" parameter.
+** </table>
+**
+** ^URI hexadecimal escape sequences (%HH) are supported within the path and
+** query components of a URI. A hexadecimal escape sequence consists of a
+** percent sign - "%" - followed by exactly two hexadecimal digits
+** specifying an octet value. ^Before the path or query components of a
+** URI filename are interpreted, they are encoded using UTF-8 and all
+** hexadecimal escape sequences replaced by a single byte containing the
+** corresponding octet. If this process generates an invalid UTF-8 encoding,
+** the results are undefined.
**
** <b>Note to Windows users:</b> The encoding used for the filename argument
** of sqlite3_open() and sqlite3_open_v2() must be UTF-8, not whatever
);
/*
+** CAPI3REF: Obtain Values For URI Parameters
+**
+** This is a utility routine, useful to VFS implementations, that checks
+** to see if a database file was a URI that contained a specific query
+** parameter, and if so obtains the value of the query parameter.
+**
+** The zFilename argument is the filename pointer passed into the xOpen()
+** method of a VFS implementation. The zParam argument is the name of the
+** query parameter we seek. This routine returns the value of the zParam
+** parameter if it exists. If the parameter does not exist, this routine
+** returns a NULL pointer.
+**
+** If the zFilename argument to this function is not a pointer that SQLite
+** passed into the xOpen VFS method, then the behavior of this routine
+** is undefined and probably undesirable.
+*/
+SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam);
+
+
+/*
** CAPI3REF: Error Codes And Messages
**
** ^The sqlite3_errcode() interface returns the numeric [result code] or
** Additional information is available at [limits | Limits in SQLite].
**
** <dl>
-** ^(<dt>SQLITE_LIMIT_LENGTH</dt>
+** [[SQLITE_LIMIT_LENGTH]] ^(<dt>SQLITE_LIMIT_LENGTH</dt>
** <dd>The maximum size of any string or BLOB or table row, in bytes.<dd>)^
**
-** ^(<dt>SQLITE_LIMIT_SQL_LENGTH</dt>
+** [[SQLITE_LIMIT_SQL_LENGTH]] ^(<dt>SQLITE_LIMIT_SQL_LENGTH</dt>
** <dd>The maximum length of an SQL statement, in bytes.</dd>)^
**
-** ^(<dt>SQLITE_LIMIT_COLUMN</dt>
+** [[SQLITE_LIMIT_COLUMN]] ^(<dt>SQLITE_LIMIT_COLUMN</dt>
** <dd>The maximum number of columns in a table definition or in the
** result set of a [SELECT] or the maximum number of columns in an index
** or in an ORDER BY or GROUP BY clause.</dd>)^
**
-** ^(<dt>SQLITE_LIMIT_EXPR_DEPTH</dt>
+** [[SQLITE_LIMIT_EXPR_DEPTH]] ^(<dt>SQLITE_LIMIT_EXPR_DEPTH</dt>
** <dd>The maximum depth of the parse tree on any expression.</dd>)^
**
-** ^(<dt>SQLITE_LIMIT_COMPOUND_SELECT</dt>
+** [[SQLITE_LIMIT_COMPOUND_SELECT]] ^(<dt>SQLITE_LIMIT_COMPOUND_SELECT</dt>
** <dd>The maximum number of terms in a compound SELECT statement.</dd>)^
**
-** ^(<dt>SQLITE_LIMIT_VDBE_OP</dt>
+** [[SQLITE_LIMIT_VDBE_OP]] ^(<dt>SQLITE_LIMIT_VDBE_OP</dt>
** <dd>The maximum number of instructions in a virtual machine program
** used to implement an SQL statement. This limit is not currently
** enforced, though that might be added in some future release of
** SQLite.</dd>)^
**
-** ^(<dt>SQLITE_LIMIT_FUNCTION_ARG</dt>
+** [[SQLITE_LIMIT_FUNCTION_ARG]] ^(<dt>SQLITE_LIMIT_FUNCTION_ARG</dt>
** <dd>The maximum number of arguments on a function.</dd>)^
**
-** ^(<dt>SQLITE_LIMIT_ATTACHED</dt>
+** [[SQLITE_LIMIT_ATTACHED]] ^(<dt>SQLITE_LIMIT_ATTACHED</dt>
** <dd>The maximum number of [ATTACH | attached databases].)^</dd>
**
+** [[SQLITE_LIMIT_LIKE_PATTERN_LENGTH]]
** ^(<dt>SQLITE_LIMIT_LIKE_PATTERN_LENGTH</dt>
** <dd>The maximum length of the pattern argument to the [LIKE] or
** [GLOB] operators.</dd>)^
**
+** [[SQLITE_LIMIT_VARIABLE_NUMBER]]
** ^(<dt>SQLITE_LIMIT_VARIABLE_NUMBER</dt>
** <dd>The maximum index number of any [parameter] in an SQL statement.)^
**
-** ^(<dt>SQLITE_LIMIT_TRIGGER_DEPTH</dt>
+** [[SQLITE_LIMIT_TRIGGER_DEPTH]] ^(<dt>SQLITE_LIMIT_TRIGGER_DEPTH</dt>
** <dd>The maximum depth of recursion for triggers.</dd>)^
** </dl>
*/
** ^[SQLITE_BUSY] means that the database engine was unable to acquire the
** database locks it needs to do its job. ^If the statement is a [COMMIT]
** or occurs outside of an explicit transaction, then you can retry the
-** statement. If the statement is not a [COMMIT] and occurs within a
+** statement. If the statement is not a [COMMIT] and occurs within an
** explicit transaction then you should rollback the transaction before
** continuing.
**
** CAPI3REF: Destroy A Prepared Statement Object
**
** ^The sqlite3_finalize() function is called to delete a [prepared statement].
-** ^If the most recent evaluation of the statement encountered no errors or
+** ^If the most recent evaluation of the statement encountered no errors
** or if the statement is never been evaluated, then sqlite3_finalize() returns
** SQLITE_OK. ^If the most recent evaluation of statement S failed, then
** sqlite3_finalize(S) returns the appropriate [error code] or
void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
void **ppArg);
int (*xRename)(sqlite3_vtab *pVtab, const char *zNew);
+ /* The methods above are in version 1 of the sqlite_module object. Those
+ ** below are for version 2 and greater. */
+ int (*xSavepoint)(sqlite3_vtab *pVTab, int);
+ int (*xRelease)(sqlite3_vtab *pVTab, int);
+ int (*xRollbackTo)(sqlite3_vtab *pVTab, int);
};
/*
**
** ^If the argument to sqlite3_mutex_held() is a NULL pointer then
** the routine should return 1. This seems counter-intuitive since
-** clearly the mutex cannot be held if it does not exist. But the
+** clearly the mutex cannot be held if it does not exist. But
** the reason the mutex does not exist is because the build is not
** using mutexes. And we do not want the assert() containing the
** call to sqlite3_mutex_held() to fail, so a non-zero return is
#define SQLITE_TESTCTRL_ISKEYWORD 16
#define SQLITE_TESTCTRL_PGHDRSZ 17
#define SQLITE_TESTCTRL_SCRATCHMALLOC 18
-#define SQLITE_TESTCTRL_LAST 18
+#define SQLITE_TESTCTRL_LOCALTIME_FAULT 19
+#define SQLITE_TESTCTRL_LAST 19
/*
** CAPI3REF: SQLite Runtime Status
** about the performance of SQLite, and optionally to reset various
** highwater marks. ^The first argument is an integer code for
** the specific parameter to measure. ^(Recognized integer codes
-** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...].)^
+** are of the form [status parameters | SQLITE_STATUS_...].)^
** ^The current value of the parameter is returned into *pCurrent.
** ^The highest recorded value is returned in *pHighwater. ^If the
** resetFlag is true, then the highest record value is reset after
/*
** CAPI3REF: Status Parameters
+** KEYWORDS: {status parameters}
**
** These integer constants designate various run-time status parameters
** that can be returned by [sqlite3_status()].
**
** <dl>
-** ^(<dt>SQLITE_STATUS_MEMORY_USED</dt>
+** [[SQLITE_STATUS_MEMORY_USED]] ^(<dt>SQLITE_STATUS_MEMORY_USED</dt>
** <dd>This parameter is the current amount of memory checked out
** using [sqlite3_malloc()], either directly or indirectly. The
** figure includes calls made to [sqlite3_malloc()] by the application
** this parameter. The amount returned is the sum of the allocation
** sizes as reported by the xSize method in [sqlite3_mem_methods].</dd>)^
**
-** ^(<dt>SQLITE_STATUS_MALLOC_SIZE</dt>
+** [[SQLITE_STATUS_MALLOC_SIZE]] ^(<dt>SQLITE_STATUS_MALLOC_SIZE</dt>
** <dd>This parameter records the largest memory allocation request
** handed to [sqlite3_malloc()] or [sqlite3_realloc()] (or their
** internal equivalents). Only the value returned in the
** *pHighwater parameter to [sqlite3_status()] is of interest.
** The value written into the *pCurrent parameter is undefined.</dd>)^
**
-** ^(<dt>SQLITE_STATUS_MALLOC_COUNT</dt>
+** [[SQLITE_STATUS_MALLOC_COUNT]] ^(<dt>SQLITE_STATUS_MALLOC_COUNT</dt>
** <dd>This parameter records the number of separate memory allocations
** currently checked out.</dd>)^
**
-** ^(<dt>SQLITE_STATUS_PAGECACHE_USED</dt>
+** [[SQLITE_STATUS_PAGECACHE_USED]] ^(<dt>SQLITE_STATUS_PAGECACHE_USED</dt>
** <dd>This parameter returns the number of pages used out of the
** [pagecache memory allocator] that was configured using
** [SQLITE_CONFIG_PAGECACHE]. The
** value returned is in pages, not in bytes.</dd>)^
**
+** [[SQLITE_STATUS_PAGECACHE_OVERFLOW]]
** ^(<dt>SQLITE_STATUS_PAGECACHE_OVERFLOW</dt>
** <dd>This parameter returns the number of bytes of page cache
** allocation which could not be satisfied by the [SQLITE_CONFIG_PAGECACHE]
** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because
** no space was left in the page cache.</dd>)^
**
-** ^(<dt>SQLITE_STATUS_PAGECACHE_SIZE</dt>
+** [[SQLITE_STATUS_PAGECACHE_SIZE]] ^(<dt>SQLITE_STATUS_PAGECACHE_SIZE</dt>
** <dd>This parameter records the largest memory allocation request
** handed to [pagecache memory allocator]. Only the value returned in the
** *pHighwater parameter to [sqlite3_status()] is of interest.
** The value written into the *pCurrent parameter is undefined.</dd>)^
**
-** ^(<dt>SQLITE_STATUS_SCRATCH_USED</dt>
+** [[SQLITE_STATUS_SCRATCH_USED]] ^(<dt>SQLITE_STATUS_SCRATCH_USED</dt>
** <dd>This parameter returns the number of allocations used out of the
** [scratch memory allocator] configured using
** [SQLITE_CONFIG_SCRATCH]. The value returned is in allocations, not
** outstanding at time, this parameter also reports the number of threads
** using scratch memory at the same time.</dd>)^
**
-** ^(<dt>SQLITE_STATUS_SCRATCH_OVERFLOW</dt>
+** [[SQLITE_STATUS_SCRATCH_OVERFLOW]] ^(<dt>SQLITE_STATUS_SCRATCH_OVERFLOW</dt>
** <dd>This parameter returns the number of bytes of scratch memory
** allocation which could not be satisfied by the [SQLITE_CONFIG_SCRATCH]
** buffer and where forced to overflow to [sqlite3_malloc()]. The values
** slots were available.
** </dd>)^
**
-** ^(<dt>SQLITE_STATUS_SCRATCH_SIZE</dt>
+** [[SQLITE_STATUS_SCRATCH_SIZE]] ^(<dt>SQLITE_STATUS_SCRATCH_SIZE</dt>
** <dd>This parameter records the largest memory allocation request
** handed to [scratch memory allocator]. Only the value returned in the
** *pHighwater parameter to [sqlite3_status()] is of interest.
** The value written into the *pCurrent parameter is undefined.</dd>)^
**
-** ^(<dt>SQLITE_STATUS_PARSER_STACK</dt>
+** [[SQLITE_STATUS_PARSER_STACK]] ^(<dt>SQLITE_STATUS_PARSER_STACK</dt>
** <dd>This parameter records the deepest parser stack. It is only
** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].</dd>)^
** </dl>
** about a single [database connection]. ^The first argument is the
** database connection object to be interrogated. ^The second argument
** is an integer constant, taken from the set of
-** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros, that
+** [SQLITE_DBSTATUS options], that
** determines the parameter to interrogate. The set of
-** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros is likely
+** [SQLITE_DBSTATUS options] is likely
** to grow in future releases of SQLite.
**
** ^The current value of the requested parameter is written into *pCur
/*
** CAPI3REF: Status Parameters for database connections
+** KEYWORDS: {SQLITE_DBSTATUS options}
**
** These constants are the available integer "verbs" that can be passed as
** the second argument to the [sqlite3_db_status()] interface.
** if a discontinued or unsupported verb is invoked.
**
** <dl>
-** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_USED</dt>
+** [[SQLITE_DBSTATUS_LOOKASIDE_USED]] ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_USED</dt>
** <dd>This parameter returns the number of lookaside memory slots currently
** checked out.</dd>)^
**
-** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_HIT</dt>
+** [[SQLITE_DBSTATUS_LOOKASIDE_HIT]] ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_HIT</dt>
** <dd>This parameter returns the number malloc attempts that were
** satisfied using lookaside memory. Only the high-water value is meaningful;
** the current value is always zero.)^
**
+** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE]]
** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE</dt>
** <dd>This parameter returns the number malloc attempts that might have
** been satisfied using lookaside memory but failed due to the amount of
** Only the high-water value is meaningful;
** the current value is always zero.)^
**
+** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL]]
** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL</dt>
** <dd>This parameter returns the number malloc attempts that might have
** been satisfied using lookaside memory but failed due to all lookaside
** Only the high-water value is meaningful;
** the current value is always zero.)^
**
-** ^(<dt>SQLITE_DBSTATUS_CACHE_USED</dt>
+** [[SQLITE_DBSTATUS_CACHE_USED]] ^(<dt>SQLITE_DBSTATUS_CACHE_USED</dt>
** <dd>This parameter returns the approximate number of of bytes of heap
** memory used by all pager caches associated with the database connection.)^
** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0.
**
-** ^(<dt>SQLITE_DBSTATUS_SCHEMA_USED</dt>
+** [[SQLITE_DBSTATUS_SCHEMA_USED]] ^(<dt>SQLITE_DBSTATUS_SCHEMA_USED</dt>
** <dd>This parameter returns the approximate number of of bytes of heap
** memory used to store the schema for all databases associated
** with the connection - main, temp, and any [ATTACH]-ed databases.)^
** [shared cache mode] being enabled.
** ^The highwater mark associated with SQLITE_DBSTATUS_SCHEMA_USED is always 0.
**
-** ^(<dt>SQLITE_DBSTATUS_STMT_USED</dt>
+** [[SQLITE_DBSTATUS_STMT_USED]] ^(<dt>SQLITE_DBSTATUS_STMT_USED</dt>
** <dd>This parameter returns the approximate number of of bytes of heap
** and lookaside memory used by all prepared statements associated with
** the database connection.)^
** CAPI3REF: Prepared Statement Status
**
** ^(Each prepared statement maintains various
-** [SQLITE_STMTSTATUS_SORT | counters] that measure the number
+** [SQLITE_STMTSTATUS counters] that measure the number
** of times it has performed specific operations.)^ These counters can
** be used to monitor the performance characteristics of the prepared
** statements. For example, if the number of table steps greatly exceeds
** ^(This interface is used to retrieve and reset counter values from
** a [prepared statement]. The first argument is the prepared statement
** object to be interrogated. The second argument
-** is an integer code for a specific [SQLITE_STMTSTATUS_SORT | counter]
+** is an integer code for a specific [SQLITE_STMTSTATUS counter]
** to be interrogated.)^
** ^The current value of the requested counter is returned.
** ^If the resetFlg is true, then the counter is reset to zero after this
/*
** CAPI3REF: Status Parameters for prepared statements
+** KEYWORDS: {SQLITE_STMTSTATUS counter} {SQLITE_STMTSTATUS counters}
**
** These preprocessor macros define integer codes that name counter
** values associated with the [sqlite3_stmt_status()] interface.
** The meanings of the various counters are as follows:
**
** <dl>
-** <dt>SQLITE_STMTSTATUS_FULLSCAN_STEP</dt>
+** [[SQLITE_STMTSTATUS_FULLSCAN_STEP]] <dt>SQLITE_STMTSTATUS_FULLSCAN_STEP</dt>
** <dd>^This is the number of times that SQLite has stepped forward in
** a table as part of a full table scan. Large numbers for this counter
** may indicate opportunities for performance improvement through
** careful use of indices.</dd>
**
-** <dt>SQLITE_STMTSTATUS_SORT</dt>
+** [[SQLITE_STMTSTATUS_SORT]] <dt>SQLITE_STMTSTATUS_SORT</dt>
** <dd>^This is the number of sort operations that have occurred.
** A non-zero value in this counter may indicate an opportunity to
** improvement performance through careful use of indices.</dd>
**
-** <dt>SQLITE_STMTSTATUS_AUTOINDEX</dt>
+** [[SQLITE_STMTSTATUS_AUTOINDEX]] <dt>SQLITE_STMTSTATUS_AUTOINDEX</dt>
** <dd>^This is the number of rows inserted into transient indices that
** were created automatically in order to help joins run faster.
** A non-zero value in this counter may indicate an opportunity to
** the application may discard the parameter after the call to
** [sqlite3_config()] returns.)^
**
+** [[the xInit() page cache method]]
** ^(The xInit() method is called once for each effective
** call to [sqlite3_initialize()])^
** (usually only once during the lifetime of the process). ^(The xInit()
** built-in default page cache is used instead of the application defined
** page cache.)^
**
+** [[the xShutdown() page cache method]]
** ^The xShutdown() method is called by [sqlite3_shutdown()].
** It can be used to clean up
** any outstanding resources before process shutdown, if required.
** ^SQLite will never invoke xInit() more than once without an intervening
** call to xShutdown().
**
+** [[the xCreate() page cache methods]]
** ^SQLite invokes the xCreate() method to construct a new cache instance.
** SQLite will typically create one cache instance for each open database file,
** though this is not guaranteed. ^The
** ^Hence, a cache created with bPurgeable false will
** never contain any unpinned pages.
**
+** [[the xCachesize() page cache method]]
** ^(The xCachesize() method may be called at any time by SQLite to set the
** suggested maximum cache-size (number of pages stored by) the cache
** instance passed as the first argument. This is the value configured using
** parameter, the implementation is not required to do anything with this
** value; it is advisory only.
**
+** [[the xPagecount() page cache methods]]
** The xPagecount() method must return the number of pages currently
** stored in the cache, both pinned and unpinned.
**
+** [[the xFetch() page cache methods]]
** The xFetch() method locates a page in the cache and returns a pointer to
** the page, or a NULL pointer.
** A "page", in this context, means a buffer of szPage bytes aligned at an
** 8-byte boundary. The page to be fetched is determined by the key. ^The
-** mimimum key value is 1. After it has been retrieved using xFetch, the page
+** minimum key value is 1. After it has been retrieved using xFetch, the page
** is considered to be "pinned".
**
** If the requested page is already in the page cache, then the page cache
** attempt to unpin one or more cache pages by spilling the content of
** pinned pages to disk and synching the operating system disk cache.
**
+** [[the xUnpin() page cache method]]
** ^xUnpin() is called by SQLite with a pointer to a currently pinned page
** as its second argument. If the third parameter, discard, is non-zero,
** then the page must be evicted from the cache.
** call to xUnpin() unpins the page regardless of the number of prior calls
** to xFetch().
**
+** [[the xRekey() page cache methods]]
** The xRekey() method is used to change the key value associated with the
** page passed as the second argument. If the cache
** previously contains an entry associated with newKey, it must be
** of these pages are pinned, they are implicitly unpinned, meaning that
** they can be safely discarded.
**
+** [[the xDestroy() page cache method]]
** ^The xDestroy() method is used to delete a cache allocated by xCreate().
** All resources associated with the specified cache should be freed. ^After
** calling the xDestroy() method, SQLite considers the [sqlite3_pcache*]
** There should be exactly one call to sqlite3_backup_finish() for each
** successful call to sqlite3_backup_init().
**
-** <b>sqlite3_backup_init()</b>
+** [[sqlite3_backup_init()]] <b>sqlite3_backup_init()</b>
**
** ^The D and N arguments to sqlite3_backup_init(D,N,S,M) are the
** [database connection] associated with the destination database
** sqlite3_backup_finish() functions to perform the specified backup
** operation.
**
-** <b>sqlite3_backup_step()</b>
+** [[sqlite3_backup_step()]] <b>sqlite3_backup_step()</b>
**
** ^Function sqlite3_backup_step(B,N) will copy up to N pages between
** the source and destination databases specified by [sqlite3_backup] object B.
** by the backup operation, then the backup database is automatically
** updated at the same time.
**
-** <b>sqlite3_backup_finish()</b>
+** [[sqlite3_backup_finish()]] <b>sqlite3_backup_finish()</b>
**
** When sqlite3_backup_step() has returned [SQLITE_DONE], or when the
** application wishes to abandon the backup operation, the application
** is not a permanent error and does not affect the return value of
** sqlite3_backup_finish().
**
-** <b>sqlite3_backup_remaining(), sqlite3_backup_pagecount()</b>
+** [[sqlite3_backup__remaining()]] [[sqlite3_backup_pagecount()]]
+** <b>sqlite3_backup_remaining() and sqlite3_backup_pagecount()</b>
**
** ^Each call to sqlite3_backup_step() sets two values inside
** the [sqlite3_backup] object: the number of pages still to be backed
#define SQLITE_CHECKPOINT_FULL 1
#define SQLITE_CHECKPOINT_RESTART 2
+/*
+** CAPI3REF: Virtual Table Interface Configuration
+**
+** This function may be called by either the [xConnect] or [xCreate] method
+** of a [virtual table] implementation to configure
+** various facets of the virtual table interface.
+**
+** If this interface is invoked outside the context of an xConnect or
+** xCreate virtual table method then the behavior is undefined.
+**
+** At present, there is only one option that may be configured using
+** this function. (See [SQLITE_VTAB_CONSTRAINT_SUPPORT].) Further options
+** may be added in the future.
+*/
+SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...);
+
+/*
+** CAPI3REF: Virtual Table Configuration Options
+**
+** These macros define the various options to the
+** [sqlite3_vtab_config()] interface that [virtual table] implementations
+** can use to customize and optimize their behavior.
+**
+** <dl>
+** <dt>SQLITE_VTAB_CONSTRAINT_SUPPORT
+** <dd>Calls of the form
+** [sqlite3_vtab_config](db,SQLITE_VTAB_CONSTRAINT_SUPPORT,X) are supported,
+** where X is an integer. If X is zero, then the [virtual table] whose
+** [xCreate] or [xConnect] method invoked [sqlite3_vtab_config()] does not
+** support constraints. In this configuration (which is the default) if
+** a call to the [xUpdate] method returns [SQLITE_CONSTRAINT], then the entire
+** statement is rolled back as if [ON CONFLICT | OR ABORT] had been
+** specified as part of the users SQL statement, regardless of the actual
+** ON CONFLICT mode specified.
+**
+** If X is non-zero, then the virtual table implementation guarantees
+** that if [xUpdate] returns [SQLITE_CONSTRAINT], it will do so before
+** any modifications to internal or persistent data structures have been made.
+** If the [ON CONFLICT] mode is ABORT, FAIL, IGNORE or ROLLBACK, SQLite
+** is able to roll back a statement or database transaction, and abandon
+** or continue processing the current SQL statement as appropriate.
+** If the ON CONFLICT mode is REPLACE and the [xUpdate] method returns
+** [SQLITE_CONSTRAINT], SQLite handles this as if the ON CONFLICT mode
+** had been ABORT.
+**
+** Virtual table implementations that are required to handle OR REPLACE
+** must do so within the [xUpdate] method. If a call to the
+** [sqlite3_vtab_on_conflict()] function indicates that the current ON
+** CONFLICT policy is REPLACE, the virtual table implementation should
+** silently replace the appropriate rows within the xUpdate callback and
+** return SQLITE_OK. Or, if this is not possible, it may return
+** SQLITE_CONSTRAINT, in which case SQLite falls back to OR ABORT
+** constraint handling.
+** </dl>
+*/
+#define SQLITE_VTAB_CONSTRAINT_SUPPORT 1
+
+/*
+** CAPI3REF: Determine The Virtual Table Conflict Policy
+**
+** This function may only be called from within a call to the [xUpdate] method
+** of a [virtual table] implementation for an INSERT or UPDATE operation. ^The
+** value returned is one of [SQLITE_ROLLBACK], [SQLITE_IGNORE], [SQLITE_FAIL],
+** [SQLITE_ABORT], or [SQLITE_REPLACE], according to the [ON CONFLICT] mode
+** of the SQL statement that triggered the call to the [xUpdate] method of the
+** [virtual table].
+*/
+SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *);
+
+/*
+** CAPI3REF: Conflict resolution modes
+**
+** These constants are returned by [sqlite3_vtab_on_conflict()] to
+** inform a [virtual table] implementation what the [ON CONFLICT] mode
+** is for the SQL statement being evaluated.
+**
+** Note that the [SQLITE_IGNORE] constant is also used as a potential
+** return value from the [sqlite3_set_authorizer()] callback and that
+** [SQLITE_ABORT] is also a [result code].
+*/
+#define SQLITE_ROLLBACK 1
+/* #define SQLITE_IGNORE 2 // Also used by sqlite3_authorizer() callback */
+#define SQLITE_FAIL 3
+/* #define SQLITE_ABORT 4 // Also an error code */
+#define SQLITE_REPLACE 5
+
+
/*
** Undo the hack that converts floating point types to integer for
typedef struct TriggerStep TriggerStep;
typedef struct UnpackedRecord UnpackedRecord;
typedef struct VTable VTable;
+typedef struct VtabCtx VtabCtx;
typedef struct Walker Walker;
typedef struct WherePlan WherePlan;
typedef struct WhereInfo WhereInfo;
SQLITE_PRIVATE int sqlite3BtreeOpen(
+ sqlite3_vfs *pVfs, /* VFS to use with this b-tree */
const char *zFilename, /* Name of database file to open */
sqlite3 *db, /* Associated database connection */
Btree **ppBtree, /* Return open Btree* here */
*/
#ifndef _SQLITE_VDBE_H_
#define _SQLITE_VDBE_H_
+/* #include <stdio.h> */
/*
** A single VDBE is an opaque structure named "Vdbe". Only routines
KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */
int *ai; /* Used when p4type is P4_INTARRAY */
SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */
+ int (*xAdvance)(BtCursor *, int *);
} p4;
#ifdef SQLITE_DEBUG
char *zComment; /* Comment to improve readability */
#define P4_INT32 (-14) /* P4 is a 32-bit signed integer */
#define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */
#define P4_SUBPROGRAM (-18) /* P4 is a pointer to a SubProgram structure */
+#define P4_ADVANCE (-19) /* P4 is a pointer to BtreeNext() or BtreePrev() */
/* When adding a P4 argument using P4_KEYINFO, a copy of the KeyInfo structure
** is made. That copy is freed when the Vdbe is finalized. But if the
#define OP_Or 68 /* same as TK_OR */
#define OP_Not 19 /* same as TK_NOT */
#define OP_BitNot 93 /* same as TK_BITNOT */
-#define OP_If 26
-#define OP_IfNot 27
+#define OP_Once 26
+#define OP_If 27
+#define OP_IfNot 28
#define OP_IsNull 73 /* same as TK_ISNULL */
#define OP_NotNull 74 /* same as TK_NOTNULL */
-#define OP_Column 28
-#define OP_Affinity 29
-#define OP_MakeRecord 30
-#define OP_Count 31
-#define OP_Savepoint 32
-#define OP_AutoCommit 33
-#define OP_Transaction 34
-#define OP_ReadCookie 35
-#define OP_SetCookie 36
-#define OP_VerifyCookie 37
-#define OP_OpenRead 38
-#define OP_OpenWrite 39
-#define OP_OpenAutoindex 40
-#define OP_OpenEphemeral 41
-#define OP_OpenPseudo 42
-#define OP_Close 43
-#define OP_SeekLt 44
-#define OP_SeekLe 45
-#define OP_SeekGe 46
-#define OP_SeekGt 47
-#define OP_Seek 48
-#define OP_NotFound 49
-#define OP_Found 50
-#define OP_IsUnique 51
-#define OP_NotExists 52
-#define OP_Sequence 53
-#define OP_NewRowid 54
-#define OP_Insert 55
-#define OP_InsertInt 56
-#define OP_Delete 57
-#define OP_ResetCount 58
-#define OP_RowKey 59
-#define OP_RowData 60
-#define OP_Rowid 61
-#define OP_NullRow 62
-#define OP_Last 63
-#define OP_Sort 64
-#define OP_Rewind 65
-#define OP_Prev 66
-#define OP_Next 67
-#define OP_IdxInsert 70
-#define OP_IdxDelete 71
-#define OP_IdxRowid 72
-#define OP_IdxLT 81
-#define OP_IdxGE 92
-#define OP_Destroy 95
-#define OP_Clear 96
-#define OP_CreateIndex 97
-#define OP_CreateTable 98
-#define OP_ParseSchema 99
-#define OP_LoadAnalysis 100
-#define OP_DropTable 101
-#define OP_DropIndex 102
-#define OP_DropTrigger 103
-#define OP_IntegrityCk 104
-#define OP_RowSetAdd 105
-#define OP_RowSetRead 106
-#define OP_RowSetTest 107
-#define OP_Program 108
-#define OP_Param 109
-#define OP_FkCounter 110
-#define OP_FkIfZero 111
-#define OP_MemMax 112
-#define OP_IfPos 113
-#define OP_IfNeg 114
-#define OP_IfZero 115
-#define OP_AggStep 116
-#define OP_AggFinal 117
-#define OP_Checkpoint 118
-#define OP_JournalMode 119
-#define OP_Vacuum 120
-#define OP_IncrVacuum 121
-#define OP_Expire 122
-#define OP_TableLock 123
-#define OP_VBegin 124
-#define OP_VCreate 125
-#define OP_VDestroy 126
-#define OP_VOpen 127
-#define OP_VFilter 128
-#define OP_VColumn 129
-#define OP_VNext 131
-#define OP_VRename 132
-#define OP_VUpdate 133
-#define OP_Pagecount 134
-#define OP_MaxPgcnt 135
-#define OP_Trace 136
-#define OP_Noop 137
-#define OP_Explain 138
-
-/* The following opcode values are never used */
-#define OP_NotUsed_139 139
-#define OP_NotUsed_140 140
+#define OP_Column 29
+#define OP_Affinity 30
+#define OP_MakeRecord 31
+#define OP_Count 32
+#define OP_Savepoint 33
+#define OP_AutoCommit 34
+#define OP_Transaction 35
+#define OP_ReadCookie 36
+#define OP_SetCookie 37
+#define OP_VerifyCookie 38
+#define OP_OpenRead 39
+#define OP_OpenWrite 40
+#define OP_OpenAutoindex 41
+#define OP_OpenEphemeral 42
+#define OP_SorterOpen 43
+#define OP_OpenPseudo 44
+#define OP_Close 45
+#define OP_SeekLt 46
+#define OP_SeekLe 47
+#define OP_SeekGe 48
+#define OP_SeekGt 49
+#define OP_Seek 50
+#define OP_NotFound 51
+#define OP_Found 52
+#define OP_IsUnique 53
+#define OP_NotExists 54
+#define OP_Sequence 55
+#define OP_NewRowid 56
+#define OP_Insert 57
+#define OP_InsertInt 58
+#define OP_Delete 59
+#define OP_ResetCount 60
+#define OP_SorterCompare 61
+#define OP_SorterData 62
+#define OP_RowKey 63
+#define OP_RowData 64
+#define OP_Rowid 65
+#define OP_NullRow 66
+#define OP_Last 67
+#define OP_SorterSort 70
+#define OP_Sort 71
+#define OP_Rewind 72
+#define OP_SorterNext 81
+#define OP_Prev 92
+#define OP_Next 95
+#define OP_SorterInsert 96
+#define OP_IdxInsert 97
+#define OP_IdxDelete 98
+#define OP_IdxRowid 99
+#define OP_IdxLT 100
+#define OP_IdxGE 101
+#define OP_Destroy 102
+#define OP_Clear 103
+#define OP_CreateIndex 104
+#define OP_CreateTable 105
+#define OP_ParseSchema 106
+#define OP_LoadAnalysis 107
+#define OP_DropTable 108
+#define OP_DropIndex 109
+#define OP_DropTrigger 110
+#define OP_IntegrityCk 111
+#define OP_RowSetAdd 112
+#define OP_RowSetRead 113
+#define OP_RowSetTest 114
+#define OP_Program 115
+#define OP_Param 116
+#define OP_FkCounter 117
+#define OP_FkIfZero 118
+#define OP_MemMax 119
+#define OP_IfPos 120
+#define OP_IfNeg 121
+#define OP_IfZero 122
+#define OP_AggStep 123
+#define OP_AggFinal 124
+#define OP_Checkpoint 125
+#define OP_JournalMode 126
+#define OP_Vacuum 127
+#define OP_IncrVacuum 128
+#define OP_Expire 129
+#define OP_TableLock 131
+#define OP_VBegin 132
+#define OP_VCreate 133
+#define OP_VDestroy 134
+#define OP_VOpen 135
+#define OP_VFilter 136
+#define OP_VColumn 137
+#define OP_VNext 138
+#define OP_VRename 139
+#define OP_VUpdate 140
+#define OP_Pagecount 146
+#define OP_MaxPgcnt 147
+#define OP_Trace 148
+#define OP_Noop 149
+#define OP_Explain 150
/* Properties such as "out2" or "jump" that are specified in
/* 0 */ 0x00, 0x01, 0x05, 0x04, 0x04, 0x10, 0x00, 0x02,\
/* 8 */ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x24, 0x24,\
/* 16 */ 0x00, 0x00, 0x00, 0x24, 0x04, 0x05, 0x04, 0x00,\
-/* 24 */ 0x00, 0x01, 0x05, 0x05, 0x00, 0x00, 0x00, 0x02,\
-/* 32 */ 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00,\
-/* 40 */ 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11,\
-/* 48 */ 0x08, 0x11, 0x11, 0x11, 0x11, 0x02, 0x02, 0x00,\
-/* 56 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01,\
-/* 64 */ 0x01, 0x01, 0x01, 0x01, 0x4c, 0x4c, 0x08, 0x00,\
-/* 72 */ 0x02, 0x05, 0x05, 0x15, 0x15, 0x15, 0x15, 0x15,\
+/* 24 */ 0x00, 0x01, 0x05, 0x05, 0x05, 0x00, 0x00, 0x00,\
+/* 32 */ 0x02, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00,\
+/* 40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11,\
+/* 48 */ 0x11, 0x11, 0x08, 0x11, 0x11, 0x11, 0x11, 0x02,\
+/* 56 */ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+/* 64 */ 0x00, 0x02, 0x00, 0x01, 0x4c, 0x4c, 0x01, 0x01,\
+/* 72 */ 0x01, 0x05, 0x05, 0x15, 0x15, 0x15, 0x15, 0x15,\
/* 80 */ 0x15, 0x01, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,\
-/* 88 */ 0x4c, 0x4c, 0x4c, 0x4c, 0x01, 0x24, 0x02, 0x02,\
-/* 96 */ 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,\
-/* 104 */ 0x00, 0x0c, 0x45, 0x15, 0x01, 0x02, 0x00, 0x01,\
-/* 112 */ 0x08, 0x05, 0x05, 0x05, 0x00, 0x00, 0x00, 0x02,\
-/* 120 */ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
-/* 128 */ 0x01, 0x00, 0x02, 0x01, 0x00, 0x00, 0x02, 0x02,\
-/* 136 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04,\
-/* 144 */ 0x04, 0x04,}
+/* 88 */ 0x4c, 0x4c, 0x4c, 0x4c, 0x01, 0x24, 0x02, 0x01,\
+/* 96 */ 0x08, 0x08, 0x00, 0x02, 0x01, 0x01, 0x02, 0x00,\
+/* 104 */ 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+/* 112 */ 0x0c, 0x45, 0x15, 0x01, 0x02, 0x00, 0x01, 0x08,\
+/* 120 */ 0x05, 0x05, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00,\
+/* 128 */ 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,\
+/* 136 */ 0x01, 0x00, 0x01, 0x00, 0x00, 0x04, 0x04, 0x04,\
+/* 144 */ 0x04, 0x04, 0x02, 0x02, 0x00, 0x00, 0x00,}
/************** End of opcodes.h *********************************************/
/************** Continuing where we left off in vdbe.h ***********************/
SQLITE_PRIVATE int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int);
SQLITE_PRIVATE int sqlite3VdbeAddOp4Int(Vdbe*,int,int,int,int,int);
SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp);
-SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, int addr, int P1);
-SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2);
-SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, int addr, int P3);
+SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*);
+SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, u32 addr, int P1);
+SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, u32 addr, int P2);
+SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, u32 addr, int P3);
SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe*, u8 P5);
SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe*, int addr);
-SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe*, int addr, int N);
+SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe*, int addr);
SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N);
SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe*, int);
SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe*, int);
SQLITE_PRIVATE void sqlite3VdbeRunOnlyOnce(Vdbe*);
SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe*);
SQLITE_PRIVATE void sqlite3VdbeDeleteObject(sqlite3*,Vdbe*);
-SQLITE_PRIVATE void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int,int,int);
+SQLITE_PRIVATE void sqlite3VdbeMakeReady(Vdbe*,Parse*);
SQLITE_PRIVATE int sqlite3VdbeFinalize(Vdbe*);
SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe*, int);
SQLITE_PRIVATE int sqlite3VdbeCurrentAddr(Vdbe*);
SQLITE_PRIVATE void sqlite3VdbeTrace(Vdbe*,FILE*);
#endif
SQLITE_PRIVATE void sqlite3VdbeResetStepResult(Vdbe*);
+SQLITE_PRIVATE void sqlite3VdbeRewind(Vdbe*);
SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe*);
SQLITE_PRIVATE void sqlite3VdbeSetNumCols(Vdbe*,int);
SQLITE_PRIVATE int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, void(*)(void*));
SQLITE_PRIVATE char *sqlite3VdbeExpandSql(Vdbe*, const char*);
#endif
-SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,char*,int);
-SQLITE_PRIVATE void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord*);
+SQLITE_PRIVATE void sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,UnpackedRecord*);
SQLITE_PRIVATE int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*);
+SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo *, char *, int, char **);
#ifndef SQLITE_OMIT_TRIGGER
SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *);
** A thread must be holding a mutex on the corresponding Btree in order
** to access Schema content. This implies that the thread must also be
** holding a mutex on the sqlite3 connection pointer that owns the Btree.
-** For a TEMP Schema, on the connection mutex is required.
+** For a TEMP Schema, only the connection mutex is required.
*/
struct Schema {
int schema_cookie; /* Database schema version number for this file */
int nDb; /* Number of backends currently in use */
Db *aDb; /* All backends */
int flags; /* Miscellaneous flags. See below */
- int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */
+ unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */
int errCode; /* Most recent error code (SQLITE_*) */
int errMask; /* & result codes with this before returning */
u8 autoCommit; /* The auto-commit flag. */
u8 dfltLockMode; /* Default locking-mode for attached dbs */
signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */
u8 suppressErr; /* Do not issue error messages if true */
+ u8 vtabOnConflict; /* Value to return for s3_vtab_on_conflict() */
int nextPagesize; /* Pagesize after VACUUM if >0 */
int nTable; /* Number of tables in the database */
CollSeq *pDfltColl; /* The default collating sequence (BINARY) */
#endif
#ifndef SQLITE_OMIT_VIRTUALTABLE
Hash aModule; /* populated by sqlite3_create_module() */
- Table *pVTab; /* vtab with active Connect/Create method */
+ VtabCtx *pVtabCtx; /* Context for active vtab connect/create */
VTable **aVTrans; /* Virtual tables with open transactions */
int nVTrans; /* Allocated size of aVTrans */
VTable *pDisconnect; /* Disconnect these in next sqlite3_prepare() */
#define SQLITE_IndexCover 0x10 /* Disable index covering table */
#define SQLITE_GroupByOrder 0x20 /* Disable GROUPBY cover of ORDERBY */
#define SQLITE_FactorOutConst 0x40 /* Disable factoring out constants */
+#define SQLITE_IdxRealAsInt 0x80 /* Store REAL as INT in indices */
+#define SQLITE_DistinctOpt 0x80 /* DISTINCT using indexes */
#define SQLITE_OptMask 0xff /* Mask of all disablable opts */
/*
Module *pMod; /* Pointer to module implementation */
sqlite3_vtab *pVtab; /* Pointer to vtab instance */
int nRef; /* Number of pointers to this structure */
+ u8 bConstraint; /* True if constraints are supported */
+ int iSavepoint; /* Depth of the SAVEPOINT stack */
VTable *pNext; /* Next in linked list (see above) */
};
u8 useSortingIdx; /* In direct mode, reference the sorting index rather
** than the source table */
int sortingIdx; /* Cursor number of the sorting index */
+ int sortingIdxPTab; /* Cursor number of pseudo-table */
ExprList *pGroupBy; /* The group by clause */
int nSortingColumn; /* Number of columns in the sorting index */
struct AggInfo_col { /* For each column used in source tables */
char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */
Table *pTab; /* An SQL table corresponding to zName */
Select *pSelect; /* A SELECT statement used in place of a table name */
- u8 isPopulated; /* Temporary table associated with SELECT is populated */
+ int addrFillSub; /* Address of subroutine to manifest a subquery */
+ int regReturn; /* Register holding return address of addrFillSub */
u8 jointype; /* Type of join between this able and the previous */
u8 notIndexed; /* True if there is a NOT INDEXED clause */
+ u8 isCorrelated; /* True if sub-query is correlated */
#ifndef SQLITE_OMIT_EXPLAIN
u8 iSelectId; /* If pSelect!=0, the id of the sub-select in EQP */
#endif
u16 wctrlFlags; /* Flags originally passed to sqlite3WhereBegin() */
u8 okOnePass; /* Ok to use one-pass algorithm for UPDATE or DELETE */
u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */
+ u8 eDistinct;
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 */
WhereLevel a[1]; /* Information about each nest loop in WHERE */
};
+#define WHERE_DISTINCT_UNIQUE 1
+#define WHERE_DISTINCT_ORDERED 2
+
/*
** A NameContext defines a context in which to resolve table and column
** names. The context consists of a list of tables (the pSrcList) field and
#define SF_UsesEphemeral 0x0008 /* Uses the OpenEphemeral opcode */
#define SF_Expanded 0x0010 /* sqlite3SelectExpand() called on this */
#define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */
+#define SF_UseSorter 0x0040 /* Sort using a sorter */
/*
** each recursion */
int nVar; /* Number of '?' variables seen in the SQL so far */
- int nVarExpr; /* Number of used slots in apVarExpr[] */
- int nVarExprAlloc; /* Number of allocated slots in apVarExpr[] */
- Expr **apVarExpr; /* Pointers to :aaa and $aaaa wildcard expressions */
+ int nzVar; /* Number of available slots in azVar[] */
+ char **azVar; /* Pointers to names of parameters */
Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */
int nAlias; /* Number of aliased result set columns */
int nAliasAlloc; /* Number of allocated slots for aAlias[] */
int bMemstat; /* True to enable memory status */
int bCoreMutex; /* True to enable core mutexing */
int bFullMutex; /* True to enable full mutexing */
+ int bOpenUri; /* True to interpret filenames as URIs */
int mxStrlen; /* Maximum string length */
int szLookaside; /* Default lookaside buffer size */
int nLookaside; /* Default lookaside buffer count */
int nRefInitMutex; /* Number of users of pInitMutex */
void (*xLog)(void*,int,const char*); /* Function for logging */
void *pLogArg; /* First argument to xLog() */
+ int bLocaltimeFault; /* True to fail localtime() calls */
};
/*
SQLITE_PRIVATE void sqlite3AddDefaultValue(Parse*,ExprSpan*);
SQLITE_PRIVATE void sqlite3AddCollateType(Parse*, Token*);
SQLITE_PRIVATE void sqlite3EndTable(Parse*,Token*,Token*,Select*);
+SQLITE_PRIVATE int sqlite3ParseUri(const char*,const char*,unsigned int*,
+ sqlite3_vfs**,char**,char **);
SQLITE_PRIVATE Bitvec *sqlite3BitvecCreate(u32);
SQLITE_PRIVATE int sqlite3BitvecTest(Bitvec*, u32);
#endif
SQLITE_PRIVATE void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
SQLITE_PRIVATE void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
-SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**, u16);
+SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**,ExprList*,u16);
SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo*);
SQLITE_PRIVATE int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int);
SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int);
SQLITE_PRIVATE int sqlite3Atoi(const char*);
SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar);
SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte);
-SQLITE_PRIVATE int sqlite3Utf8Read(const u8*, const u8**);
+SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8*, const u8**);
/*
** Routines to read and write variable-length integers. These used to
SQLITE_PRIVATE int sqlite3Atoi64(const char*, i64*, int, u8);
SQLITE_PRIVATE void sqlite3Error(sqlite3*, int, const char*,...);
SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n);
+SQLITE_PRIVATE u8 sqlite3HexToInt(int h);
SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **);
SQLITE_PRIVATE const char *sqlite3ErrStr(int);
SQLITE_PRIVATE int sqlite3ReadSchema(Parse *pParse);
SQLITE_PRIVATE int sqlite3SubInt64(i64*,i64);
SQLITE_PRIVATE int sqlite3MulInt64(i64*,i64);
SQLITE_PRIVATE int sqlite3AbsInt32(int);
+#ifdef SQLITE_ENABLE_8_3_NAMES
+SQLITE_PRIVATE void sqlite3FileSuffix3(const char*, char*);
+#else
+# define sqlite3FileSuffix3(X,Y)
+#endif
+SQLITE_PRIVATE u8 sqlite3GetBoolean(const char *z);
SQLITE_PRIVATE const void *sqlite3ValueText(sqlite3_value*, u8);
SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value*, u8);
# define sqlite3VtabLock(X)
# define sqlite3VtabUnlock(X)
# define sqlite3VtabUnlockList(X)
+# define sqlite3VtabSavepoint(X, Y, Z) SQLITE_OK
#else
SQLITE_PRIVATE void sqlite3VtabClear(sqlite3 *db, Table*);
SQLITE_PRIVATE int sqlite3VtabSync(sqlite3 *db, char **);
SQLITE_PRIVATE void sqlite3VtabLock(VTable *);
SQLITE_PRIVATE void sqlite3VtabUnlock(VTable *);
SQLITE_PRIVATE void sqlite3VtabUnlockList(sqlite3*);
+SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *, int, int);
# define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0)
#endif
SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse*,Table*);
};
#endif
-
+#ifndef SQLITE_USE_URI
+# define SQLITE_USE_URI 0
+#endif
/*
** The following singleton contains the global configuration for
SQLITE_DEFAULT_MEMSTATUS, /* bMemstat */
1, /* bCoreMutex */
SQLITE_THREADSAFE==1, /* bFullMutex */
+ SQLITE_USE_URI, /* bOpenUri */
0x7ffffffe, /* mxStrlen */
- 100, /* szLookaside */
+ 128, /* szLookaside */
500, /* nLookaside */
{0,0,0,0,0,0,0,0}, /* m */
{0,0,0,0,0,0,0,0,0}, /* mutex */
0, /* nRefInitMutex */
0, /* xLog */
0, /* pLogArg */
+ 0, /* bLocaltimeFault */
};
#ifdef SQLITE_LOCK_TRACE
"LOCK_TRACE",
#endif
+#ifdef SQLITE_MAX_SCHEMA_RETRY
+ "MAX_SCHEMA_RETRY=" CTIMEOPT_VAL(SQLITE_MAX_SCHEMA_RETRY),
+#endif
#ifdef SQLITE_MEMDEBUG
"MEMDEBUG",
#endif
#ifdef SQLITE_OMIT_MEMORYDB
"OMIT_MEMORYDB",
#endif
+#ifdef SQLITE_OMIT_MERGE_SORT
+ "OMIT_MERGE_SORT",
+#endif
#ifdef SQLITE_OMIT_OR_OPTIMIZATION
"OMIT_OR_OPTIMIZATION",
#endif
#ifdef SQLITE_OMIT_XFER_OPT
"OMIT_XFER_OPT",
#endif
+#ifdef SQLITE_PAGECACHE_BLOCKALLOC
+ "PAGECACHE_BLOCKALLOC",
+#endif
#ifdef SQLITE_PERFORMANCE_TRACE
"PERFORMANCE_TRACE",
#endif
*/
typedef unsigned char Bool;
+/* Opaque type used by code in vdbesort.c */
+typedef struct VdbeSorter VdbeSorter;
+
/*
** A cursor is a pointer into a single BTree within a database file.
** The cursor can seek to a BTree entry with a particular key, or
Bool isTable; /* True if a table requiring integer keys */
Bool isIndex; /* True if an index containing keys only - no data */
Bool isOrdered; /* True if the underlying table is BTREE_UNORDERED */
+ Bool isSorter; /* True if a new-style sorter */
sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */
const sqlite3_module *pModule; /* Module for cursor pVtabCursor */
i64 seqCount; /* Sequence counter */
i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */
i64 lastRowid; /* Last rowid from a Next or NextIdx operation */
+ VdbeSorter *pSorter; /* Sorter object for OP_SorterOpen cursors */
/* Result of last sqlite3BtreeMoveto() done by an OP_NotExists or
** OP_IsUnique opcode on this cursor. */
Mem *aVar; /* Values for the OP_Variable opcode. */
char **azVar; /* Name of variables */
ynVar nVar; /* Number of entries in aVar[] */
+ ynVar nzVar; /* Number of entries in azVar[] */
u32 cacheCtr; /* VdbeCursor row cache generation counter */
int pc; /* The program counter */
int rc; /* Value to return */
u8 errorAction; /* Recovery action to do in case of an error */
- u8 okVar; /* True if azVar[] has been initialized */
u8 explain; /* True if EXPLAIN present on SQL command */
u8 changeCntOn; /* True to update the change-counter */
u8 expired; /* True if the VM needs to be recompiled */
SQLITE_PRIVATE int sqlite3VdbeMemFromBtree(BtCursor*,int,int,int,Mem*);
SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p);
SQLITE_PRIVATE void sqlite3VdbeMemReleaseExternal(Mem *p);
+#define MemReleaseExt(X) \
+ if((X)->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame)) \
+ sqlite3VdbeMemReleaseExternal(X);
SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
SQLITE_PRIVATE const char *sqlite3OpcodeName(int);
SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *);
SQLITE_PRIVATE void sqlite3VdbeMemStoreType(Mem *pMem);
+#ifdef SQLITE_OMIT_MERGE_SORT
+# define sqlite3VdbeSorterInit(Y,Z) SQLITE_OK
+# define sqlite3VdbeSorterWrite(X,Y,Z) SQLITE_OK
+# define sqlite3VdbeSorterClose(Y,Z)
+# define sqlite3VdbeSorterRowkey(Y,Z) SQLITE_OK
+# define sqlite3VdbeSorterRewind(X,Y,Z) SQLITE_OK
+# define sqlite3VdbeSorterNext(X,Y,Z) SQLITE_OK
+# define sqlite3VdbeSorterCompare(X,Y,Z) SQLITE_OK
+#else
+SQLITE_PRIVATE int sqlite3VdbeSorterInit(sqlite3 *, VdbeCursor *);
+SQLITE_PRIVATE void sqlite3VdbeSorterClose(sqlite3 *, VdbeCursor *);
+SQLITE_PRIVATE int sqlite3VdbeSorterRowkey(VdbeCursor *, Mem *);
+SQLITE_PRIVATE int sqlite3VdbeSorterNext(sqlite3 *, VdbeCursor *, int *);
+SQLITE_PRIVATE int sqlite3VdbeSorterRewind(sqlite3 *, VdbeCursor *, int *);
+SQLITE_PRIVATE int sqlite3VdbeSorterWrite(sqlite3 *, VdbeCursor *, Mem *);
+SQLITE_PRIVATE int sqlite3VdbeSorterCompare(VdbeCursor *, Mem *, int *);
+#endif
+
#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0
SQLITE_PRIVATE void sqlite3VdbeEnter(Vdbe*);
SQLITE_PRIVATE void sqlite3VdbeLeave(Vdbe*);
** Willmann-Bell, Inc
** Richmond, Virginia (USA)
*/
+/* #include <stdlib.h> */
+/* #include <assert.h> */
#include <time.h>
#ifndef SQLITE_OMIT_DATETIME_FUNCS
-/*
-** On recent Windows platforms, the localtime_s() function is available
-** as part of the "Secure CRT". It is essentially equivalent to
-** localtime_r() available under most POSIX platforms, except that the
-** order of the parameters is reversed.
-**
-** See http://msdn.microsoft.com/en-us/library/a442x3ye(VS.80).aspx.
-**
-** If the user has not indicated to use localtime_r() or localtime_s()
-** already, check for an MSVC build environment that provides
-** localtime_s().
-*/
-#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE_LOCALTIME_S) && \
- defined(_MSC_VER) && defined(_CRT_INSECURE_DEPRECATE)
-#define HAVE_LOCALTIME_S 1
-#endif
/*
** A structure for holding a single date and time.
p->validTZ = 0;
}
+/*
+** On recent Windows platforms, the localtime_s() function is available
+** as part of the "Secure CRT". It is essentially equivalent to
+** localtime_r() available under most POSIX platforms, except that the
+** order of the parameters is reversed.
+**
+** See http://msdn.microsoft.com/en-us/library/a442x3ye(VS.80).aspx.
+**
+** If the user has not indicated to use localtime_r() or localtime_s()
+** already, check for an MSVC build environment that provides
+** localtime_s().
+*/
+#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE_LOCALTIME_S) && \
+ defined(_MSC_VER) && defined(_CRT_INSECURE_DEPRECATE)
+#define HAVE_LOCALTIME_S 1
+#endif
+
#ifndef SQLITE_OMIT_LOCALTIME
/*
-** Compute the difference (in milliseconds)
-** between localtime and UTC (a.k.a. GMT)
-** for the time value p where p is in UTC.
+** The following routine implements the rough equivalent of localtime_r()
+** using whatever operating-system specific localtime facility that
+** is available. This routine returns 0 on success and
+** non-zero on any kind of error.
+**
+** If the sqlite3GlobalConfig.bLocaltimeFault variable is true then this
+** routine will always fail.
*/
-static sqlite3_int64 localtimeOffset(DateTime *p){
+static int osLocaltime(time_t *t, struct tm *pTm){
+ int rc;
+#if (!defined(HAVE_LOCALTIME_R) || !HAVE_LOCALTIME_R) \
+ && (!defined(HAVE_LOCALTIME_S) || !HAVE_LOCALTIME_S)
+ struct tm *pX;
+#if SQLITE_THREADSAFE>0
+ sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+#endif
+ sqlite3_mutex_enter(mutex);
+ pX = localtime(t);
+#ifndef SQLITE_OMIT_BUILTIN_TEST
+ if( sqlite3GlobalConfig.bLocaltimeFault ) pX = 0;
+#endif
+ if( pX ) *pTm = *pX;
+ sqlite3_mutex_leave(mutex);
+ rc = pX==0;
+#else
+#ifndef SQLITE_OMIT_BUILTIN_TEST
+ if( sqlite3GlobalConfig.bLocaltimeFault ) return 1;
+#endif
+#if defined(HAVE_LOCALTIME_R) && HAVE_LOCALTIME_R
+ rc = localtime_r(t, pTm)==0;
+#else
+ rc = localtime_s(pTm, t);
+#endif /* HAVE_LOCALTIME_R */
+#endif /* HAVE_LOCALTIME_R || HAVE_LOCALTIME_S */
+ return rc;
+}
+#endif /* SQLITE_OMIT_LOCALTIME */
+
+
+#ifndef SQLITE_OMIT_LOCALTIME
+/*
+** Compute the difference (in milliseconds) between localtime and UTC
+** (a.k.a. GMT) for the time value p where p is in UTC. If no error occurs,
+** return this value and set *pRc to SQLITE_OK.
+**
+** Or, if an error does occur, set *pRc to SQLITE_ERROR. The returned value
+** is undefined in this case.
+*/
+static sqlite3_int64 localtimeOffset(
+ DateTime *p, /* Date at which to calculate offset */
+ sqlite3_context *pCtx, /* Write error here if one occurs */
+ int *pRc /* OUT: Error code. SQLITE_OK or ERROR */
+){
DateTime x, y;
time_t t;
+ struct tm sLocal;
+
+ /* Initialize the contents of sLocal to avoid a compiler warning. */
+ memset(&sLocal, 0, sizeof(sLocal));
+
x = *p;
computeYMD_HMS(&x);
if( x.Y<1971 || x.Y>=2038 ){
x.validJD = 0;
computeJD(&x);
t = (time_t)(x.iJD/1000 - 21086676*(i64)10000);
-#ifdef HAVE_LOCALTIME_R
- {
- struct tm sLocal;
- localtime_r(&t, &sLocal);
- y.Y = sLocal.tm_year + 1900;
- y.M = sLocal.tm_mon + 1;
- y.D = sLocal.tm_mday;
- y.h = sLocal.tm_hour;
- y.m = sLocal.tm_min;
- y.s = sLocal.tm_sec;
- }
-#elif defined(HAVE_LOCALTIME_S) && HAVE_LOCALTIME_S
- {
- struct tm sLocal;
- localtime_s(&sLocal, &t);
- y.Y = sLocal.tm_year + 1900;
- y.M = sLocal.tm_mon + 1;
- y.D = sLocal.tm_mday;
- y.h = sLocal.tm_hour;
- y.m = sLocal.tm_min;
- y.s = sLocal.tm_sec;
- }
-#else
- {
- struct tm *pTm;
- sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
- pTm = localtime(&t);
- y.Y = pTm->tm_year + 1900;
- y.M = pTm->tm_mon + 1;
- y.D = pTm->tm_mday;
- y.h = pTm->tm_hour;
- y.m = pTm->tm_min;
- y.s = pTm->tm_sec;
- sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+ if( osLocaltime(&t, &sLocal) ){
+ sqlite3_result_error(pCtx, "local time unavailable", -1);
+ *pRc = SQLITE_ERROR;
+ return 0;
}
-#endif
+ y.Y = sLocal.tm_year + 1900;
+ y.M = sLocal.tm_mon + 1;
+ y.D = sLocal.tm_mday;
+ y.h = sLocal.tm_hour;
+ y.m = sLocal.tm_min;
+ y.s = sLocal.tm_sec;
y.validYMD = 1;
y.validHMS = 1;
y.validJD = 0;
y.validTZ = 0;
computeJD(&y);
+ *pRc = SQLITE_OK;
return y.iJD - x.iJD;
}
#endif /* SQLITE_OMIT_LOCALTIME */
** localtime
** utc
**
-** Return 0 on success and 1 if there is any kind of error.
+** Return 0 on success and 1 if there is any kind of error. If the error
+** is in a system call (i.e. localtime()), then an error message is written
+** to context pCtx. If the error is an unrecognized modifier, no error is
+** written to pCtx.
*/
-static int parseModifier(const char *zMod, DateTime *p){
+static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){
int rc = 1;
int n;
double r;
*/
if( strcmp(z, "localtime")==0 ){
computeJD(p);
- p->iJD += localtimeOffset(p);
+ p->iJD += localtimeOffset(p, pCtx, &rc);
clearYMD_HMS_TZ(p);
- rc = 0;
}
break;
}
else if( strcmp(z, "utc")==0 ){
sqlite3_int64 c1;
computeJD(p);
- c1 = localtimeOffset(p);
- p->iJD -= c1;
- clearYMD_HMS_TZ(p);
- p->iJD += c1 - localtimeOffset(p);
- rc = 0;
+ c1 = localtimeOffset(p, pCtx, &rc);
+ if( rc==SQLITE_OK ){
+ p->iJD -= c1;
+ clearYMD_HMS_TZ(p);
+ p->iJD += c1 - localtimeOffset(p, pCtx, &rc);
+ }
}
#endif
break;
}
}
for(i=1; i<argc; i++){
- if( (z = sqlite3_value_text(argv[i]))==0 || parseModifier((char*)z, p) ){
- return 1;
- }
+ z = sqlite3_value_text(argv[i]);
+ if( z==0 || parseModifier(context, (char*)z, p) ) return 1;
}
return 0;
}
** down into the VFS layer. Some SQLITE_OPEN_ flags (for example,
** SQLITE_OPEN_FULLMUTEX or SQLITE_OPEN_SHAREDCACHE) are blocked before
** reaching the VFS. */
- rc = pVfs->xOpen(pVfs, zPath, pFile, flags & 0x87f3f, pFlagsOut);
+ rc = pVfs->xOpen(pVfs, zPath, pFile, flags & 0x87f7f, pFlagsOut);
assert( rc==SQLITE_OK || pFile->pMethods==0 );
return rc;
}
){
int rc = SQLITE_NOMEM;
sqlite3_file *pFile;
- pFile = (sqlite3_file *)sqlite3Malloc(pVfs->szOsFile);
+ pFile = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile);
if( pFile ){
rc = sqlite3OsOpen(pVfs, zFile, pFile, flags, pOutFlags);
if( rc!=SQLITE_OK ){
# define backtrace(A,B) 1
# define backtrace_symbols_fd(A,B,C)
#endif
+/* #include <stdio.h> */
/*
** Each memory allocation looks like this:
** This function assumes that the necessary mutexes, if any, are
** already held by the caller. Hence "Unsafe".
*/
-void memsys3FreeUnsafe(void *pOld){
+static void memsys3FreeUnsafe(void *pOld){
Mem3Block *p = (Mem3Block*)pOld;
int i;
u32 size, x;
/*
** Free memory.
*/
-void memsys3Free(void *pPrior){
+static void memsys3Free(void *pPrior){
assert( pPrior );
memsys3Enter();
memsys3FreeUnsafe(pPrior);
/*
** Change the size of an existing memory allocation
*/
-void *memsys3Realloc(void *pPrior, int nBytes){
+static void *memsys3Realloc(void *pPrior, int nBytes){
int nOld;
void *p;
if( pPrior==0 ){
**
** Memory allocation functions used throughout sqlite.
*/
+/* #include <stdarg.h> */
/*
** Attempt to release up to n bytes of non-essential memory currently
sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, n);
if( mem0.alarmCallback!=0 ){
int nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
- if( nUsed+nFull >= mem0.alarmThreshold ){
+ if( nUsed >= mem0.alarmThreshold - nFull ){
mem0.nearlyFull = 1;
sqlite3MallocAlarm(nFull);
}else{
** Change the size of an existing memory allocation
*/
SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, int nBytes){
- int nOld, nNew;
+ int nOld, nNew, nDiff;
void *pNew;
if( pOld==0 ){
return sqlite3Malloc(nBytes); /* IMP: R-28354-25769 */
}else if( sqlite3GlobalConfig.bMemstat ){
sqlite3_mutex_enter(mem0.mutex);
sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, nBytes);
- if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)+nNew-nOld >=
- mem0.alarmThreshold ){
- sqlite3MallocAlarm(nNew-nOld);
+ nDiff = nNew - nOld;
+ if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >=
+ mem0.alarmThreshold-nDiff ){
+ sqlite3MallocAlarm(nDiff);
}
assert( sqlite3MemdebugHasType(pOld, MEMTYPE_HEAP) );
assert( sqlite3MemdebugNoType(pOld, ~MEMTYPE_HEAP) );
** 0xfe 0xff big-endian utf-16 follows
**
*/
+/* #include <assert.h> */
#ifndef SQLITE_AMALGAMATION
/*
|| (c&0xFFFFF800)==0xD800 \
|| (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \
}
-SQLITE_PRIVATE int sqlite3Utf8Read(
+SQLITE_PRIVATE u32 sqlite3Utf8Read(
const unsigned char *zIn, /* First byte of UTF-8 character */
const unsigned char **pzNext /* Write first byte past UTF-8 char here */
){
** strings, and stuff like that.
**
*/
+/* #include <stdarg.h> */
#ifdef SQLITE_HAVE_ISNAN
# include <math.h>
#endif
-#if !defined(SQLITE_OMIT_BLOB_LITERAL) || defined(SQLITE_HAS_CODEC)
/*
** Translate a single byte of Hex into an integer.
** This routine only works if h really is a valid hexadecimal
** character: 0..9a..fA..F
*/
-static u8 hexToInt(int h){
+SQLITE_PRIVATE u8 sqlite3HexToInt(int h){
assert( (h>='0' && h<='9') || (h>='a' && h<='f') || (h>='A' && h<='F') );
#ifdef SQLITE_ASCII
h += 9*(1&(h>>6));
#endif
return (u8)(h & 0xf);
}
-#endif /* !SQLITE_OMIT_BLOB_LITERAL || SQLITE_HAS_CODEC */
#if !defined(SQLITE_OMIT_BLOB_LITERAL) || defined(SQLITE_HAS_CODEC)
/*
n--;
if( zBlob ){
for(i=0; i<n; i+=2){
- zBlob[i/2] = (hexToInt(z[i])<<4) | hexToInt(z[i+1]);
+ zBlob[i/2] = (sqlite3HexToInt(z[i])<<4) | sqlite3HexToInt(z[i+1]);
}
zBlob[i/2] = 0;
}
return -x;
}
+#ifdef SQLITE_ENABLE_8_3_NAMES
+/*
+** If SQLITE_ENABLE_8_3_NAMES is set at compile-time and if the database
+** filename in zBaseFilename is a URI with the "8_3_names=1" parameter and
+** if filename in z[] has a suffix (a.k.a. "extension") that is longer than
+** three characters, then shorten the suffix on z[] to be the last three
+** characters of the original suffix.
+**
+** If SQLITE_ENABLE_8_3_NAMES is set to 2 at compile-time, then always
+** do the suffix shortening regardless of URI parameter.
+**
+** Examples:
+**
+** test.db-journal => test.nal
+** test.db-wal => test.wal
+** test.db-shm => test.shm
+*/
+SQLITE_PRIVATE void sqlite3FileSuffix3(const char *zBaseFilename, char *z){
+#if SQLITE_ENABLE_8_3_NAMES<2
+ const char *zOk;
+ zOk = sqlite3_uri_parameter(zBaseFilename, "8_3_names");
+ if( zOk && sqlite3GetBoolean(zOk) )
+#endif
+ {
+ int i, sz;
+ sz = sqlite3Strlen30(z);
+ for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){}
+ if( z[i]=='.' && ALWAYS(sz>i+4) ) memcpy(&z[i+1], &z[sz-3], 4);
+ }
+}
+#endif
+
/************** End of util.c ************************************************/
/************** Begin file hash.c ********************************************/
/*
** This is the implementation of generic hash-tables
** used in SQLite.
*/
+/* #include <assert.h> */
/* Turn bulk memory into a hash table object by initializing the
** fields of the Hash structure.
/* 23 */ "Permutation",
/* 24 */ "Compare",
/* 25 */ "Jump",
- /* 26 */ "If",
- /* 27 */ "IfNot",
- /* 28 */ "Column",
- /* 29 */ "Affinity",
- /* 30 */ "MakeRecord",
- /* 31 */ "Count",
- /* 32 */ "Savepoint",
- /* 33 */ "AutoCommit",
- /* 34 */ "Transaction",
- /* 35 */ "ReadCookie",
- /* 36 */ "SetCookie",
- /* 37 */ "VerifyCookie",
- /* 38 */ "OpenRead",
- /* 39 */ "OpenWrite",
- /* 40 */ "OpenAutoindex",
- /* 41 */ "OpenEphemeral",
- /* 42 */ "OpenPseudo",
- /* 43 */ "Close",
- /* 44 */ "SeekLt",
- /* 45 */ "SeekLe",
- /* 46 */ "SeekGe",
- /* 47 */ "SeekGt",
- /* 48 */ "Seek",
- /* 49 */ "NotFound",
- /* 50 */ "Found",
- /* 51 */ "IsUnique",
- /* 52 */ "NotExists",
- /* 53 */ "Sequence",
- /* 54 */ "NewRowid",
- /* 55 */ "Insert",
- /* 56 */ "InsertInt",
- /* 57 */ "Delete",
- /* 58 */ "ResetCount",
- /* 59 */ "RowKey",
- /* 60 */ "RowData",
- /* 61 */ "Rowid",
- /* 62 */ "NullRow",
- /* 63 */ "Last",
- /* 64 */ "Sort",
- /* 65 */ "Rewind",
- /* 66 */ "Prev",
- /* 67 */ "Next",
+ /* 26 */ "Once",
+ /* 27 */ "If",
+ /* 28 */ "IfNot",
+ /* 29 */ "Column",
+ /* 30 */ "Affinity",
+ /* 31 */ "MakeRecord",
+ /* 32 */ "Count",
+ /* 33 */ "Savepoint",
+ /* 34 */ "AutoCommit",
+ /* 35 */ "Transaction",
+ /* 36 */ "ReadCookie",
+ /* 37 */ "SetCookie",
+ /* 38 */ "VerifyCookie",
+ /* 39 */ "OpenRead",
+ /* 40 */ "OpenWrite",
+ /* 41 */ "OpenAutoindex",
+ /* 42 */ "OpenEphemeral",
+ /* 43 */ "SorterOpen",
+ /* 44 */ "OpenPseudo",
+ /* 45 */ "Close",
+ /* 46 */ "SeekLt",
+ /* 47 */ "SeekLe",
+ /* 48 */ "SeekGe",
+ /* 49 */ "SeekGt",
+ /* 50 */ "Seek",
+ /* 51 */ "NotFound",
+ /* 52 */ "Found",
+ /* 53 */ "IsUnique",
+ /* 54 */ "NotExists",
+ /* 55 */ "Sequence",
+ /* 56 */ "NewRowid",
+ /* 57 */ "Insert",
+ /* 58 */ "InsertInt",
+ /* 59 */ "Delete",
+ /* 60 */ "ResetCount",
+ /* 61 */ "SorterCompare",
+ /* 62 */ "SorterData",
+ /* 63 */ "RowKey",
+ /* 64 */ "RowData",
+ /* 65 */ "Rowid",
+ /* 66 */ "NullRow",
+ /* 67 */ "Last",
/* 68 */ "Or",
/* 69 */ "And",
- /* 70 */ "IdxInsert",
- /* 71 */ "IdxDelete",
- /* 72 */ "IdxRowid",
+ /* 70 */ "SorterSort",
+ /* 71 */ "Sort",
+ /* 72 */ "Rewind",
/* 73 */ "IsNull",
/* 74 */ "NotNull",
/* 75 */ "Ne",
/* 78 */ "Le",
/* 79 */ "Lt",
/* 80 */ "Ge",
- /* 81 */ "IdxLT",
+ /* 81 */ "SorterNext",
/* 82 */ "BitAnd",
/* 83 */ "BitOr",
/* 84 */ "ShiftLeft",
/* 89 */ "Divide",
/* 90 */ "Remainder",
/* 91 */ "Concat",
- /* 92 */ "IdxGE",
+ /* 92 */ "Prev",
/* 93 */ "BitNot",
/* 94 */ "String8",
- /* 95 */ "Destroy",
- /* 96 */ "Clear",
- /* 97 */ "CreateIndex",
- /* 98 */ "CreateTable",
- /* 99 */ "ParseSchema",
- /* 100 */ "LoadAnalysis",
- /* 101 */ "DropTable",
- /* 102 */ "DropIndex",
- /* 103 */ "DropTrigger",
- /* 104 */ "IntegrityCk",
- /* 105 */ "RowSetAdd",
- /* 106 */ "RowSetRead",
- /* 107 */ "RowSetTest",
- /* 108 */ "Program",
- /* 109 */ "Param",
- /* 110 */ "FkCounter",
- /* 111 */ "FkIfZero",
- /* 112 */ "MemMax",
- /* 113 */ "IfPos",
- /* 114 */ "IfNeg",
- /* 115 */ "IfZero",
- /* 116 */ "AggStep",
- /* 117 */ "AggFinal",
- /* 118 */ "Checkpoint",
- /* 119 */ "JournalMode",
- /* 120 */ "Vacuum",
- /* 121 */ "IncrVacuum",
- /* 122 */ "Expire",
- /* 123 */ "TableLock",
- /* 124 */ "VBegin",
- /* 125 */ "VCreate",
- /* 126 */ "VDestroy",
- /* 127 */ "VOpen",
- /* 128 */ "VFilter",
- /* 129 */ "VColumn",
+ /* 95 */ "Next",
+ /* 96 */ "SorterInsert",
+ /* 97 */ "IdxInsert",
+ /* 98 */ "IdxDelete",
+ /* 99 */ "IdxRowid",
+ /* 100 */ "IdxLT",
+ /* 101 */ "IdxGE",
+ /* 102 */ "Destroy",
+ /* 103 */ "Clear",
+ /* 104 */ "CreateIndex",
+ /* 105 */ "CreateTable",
+ /* 106 */ "ParseSchema",
+ /* 107 */ "LoadAnalysis",
+ /* 108 */ "DropTable",
+ /* 109 */ "DropIndex",
+ /* 110 */ "DropTrigger",
+ /* 111 */ "IntegrityCk",
+ /* 112 */ "RowSetAdd",
+ /* 113 */ "RowSetRead",
+ /* 114 */ "RowSetTest",
+ /* 115 */ "Program",
+ /* 116 */ "Param",
+ /* 117 */ "FkCounter",
+ /* 118 */ "FkIfZero",
+ /* 119 */ "MemMax",
+ /* 120 */ "IfPos",
+ /* 121 */ "IfNeg",
+ /* 122 */ "IfZero",
+ /* 123 */ "AggStep",
+ /* 124 */ "AggFinal",
+ /* 125 */ "Checkpoint",
+ /* 126 */ "JournalMode",
+ /* 127 */ "Vacuum",
+ /* 128 */ "IncrVacuum",
+ /* 129 */ "Expire",
/* 130 */ "Real",
- /* 131 */ "VNext",
- /* 132 */ "VRename",
- /* 133 */ "VUpdate",
- /* 134 */ "Pagecount",
- /* 135 */ "MaxPgcnt",
- /* 136 */ "Trace",
- /* 137 */ "Noop",
- /* 138 */ "Explain",
- /* 139 */ "NotUsed_139",
- /* 140 */ "NotUsed_140",
+ /* 131 */ "TableLock",
+ /* 132 */ "VBegin",
+ /* 133 */ "VCreate",
+ /* 134 */ "VDestroy",
+ /* 135 */ "VOpen",
+ /* 136 */ "VFilter",
+ /* 137 */ "VColumn",
+ /* 138 */ "VNext",
+ /* 139 */ "VRename",
+ /* 140 */ "VUpdate",
/* 141 */ "ToText",
/* 142 */ "ToBlob",
/* 143 */ "ToNumeric",
/* 144 */ "ToInt",
/* 145 */ "ToReal",
+ /* 146 */ "Pagecount",
+ /* 147 */ "MaxPgcnt",
+ /* 148 */ "Trace",
+ /* 149 */ "Noop",
+ /* 150 */ "Explain",
};
return azName[i];
}
# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
#endif
-#ifdef SQLITE_DEBUG
-SQLITE_PRIVATE int sqlite3OSTrace = 0;
-#define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X
+#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
+# ifndef SQLITE_DEBUG_OS_TRACE
+# define SQLITE_DEBUG_OS_TRACE 0
+# endif
+ int sqlite3OSTrace = SQLITE_DEBUG_OS_TRACE;
+# define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X
#else
-#define OSTRACE(X)
+# define OSTRACE(X)
#endif
/*
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
+/* #include <time.h> */
#include <sys/time.h>
#include <errno.h>
#ifndef SQLITE_OMIT_WAL
# include <sys/mount.h>
#endif
+#ifdef HAVE_UTIME
+# include <utime.h>
+#endif
+
/*
** Allowed values of unixFile.fsFlags
*/
** the SQLITE_UNIX_THREADS macro.
*/
#if SQLITE_THREADSAFE
+/* # include <pthread.h> */
# define SQLITE_UNIX_THREADS 1
#endif
sqlite3_io_methods const *pMethod; /* Always the first entry */
unixInodeInfo *pInode; /* Info about locks on this inode */
int h; /* The file descriptor */
- int dirfd; /* File descriptor for the directory */
unsigned char eFileLock; /* The type of lock held on this fd */
unsigned char ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */
int lastErrno; /* The unix errno from last I/O error */
/*
** Allowed values for the unixFile.ctrlFlags bitmask:
*/
-#define UNIXFILE_EXCL 0x01 /* Connections from one process only */
-#define UNIXFILE_RDONLY 0x02 /* Connection is read only */
+#define UNIXFILE_EXCL 0x01 /* Connections from one process only */
+#define UNIXFILE_RDONLY 0x02 /* Connection is read only */
+#define UNIXFILE_PERSIST_WAL 0x04 /* Persistent WAL mode */
+#ifndef SQLITE_DISABLE_DIRSYNC
+# define UNIXFILE_DIRSYNC 0x08 /* Directory sync needed */
+#else
+# define UNIXFILE_DIRSYNC 0x00
+#endif
/*
** Include code that is common to all os_*.c files
# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
#endif
-#ifdef SQLITE_DEBUG
-SQLITE_PRIVATE int sqlite3OSTrace = 0;
-#define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X
+#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
+# ifndef SQLITE_DEBUG_OS_TRACE
+# define SQLITE_DEBUG_OS_TRACE 0
+# endif
+ int sqlite3OSTrace = SQLITE_DEBUG_OS_TRACE;
+# define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X
#else
-#define OSTRACE(X)
+# define OSTRACE(X)
#endif
/*
#endif
/*
+** Different Unix systems declare open() in different ways. Same use
+** open(const char*,int,mode_t). Others use open(const char*,int,...).
+** The difference is important when using a pointer to the function.
+**
+** The safest way to deal with the problem is to always use this wrapper
+** which always has the same well-defined interface.
+*/
+static int posixOpen(const char *zFile, int flags, int mode){
+ return open(zFile, flags, mode);
+}
+
+/* Forward reference */
+static int openDirectory(const char*, int*);
+
+/*
** Many system calls are accessed through pointer-to-functions so that
** they may be overridden at runtime to facilitate fault injection during
** testing and sandboxing. The following array holds the names and pointers
sqlite3_syscall_ptr pCurrent; /* Current value of the system call */
sqlite3_syscall_ptr pDefault; /* Default value */
} aSyscall[] = {
- { "open", (sqlite3_syscall_ptr)open, 0 },
-#define osOpen ((int(*)(const char*,int,...))aSyscall[0].pCurrent)
+ { "open", (sqlite3_syscall_ptr)posixOpen, 0 },
+#define osOpen ((int(*)(const char*,int,int))aSyscall[0].pCurrent)
{ "close", (sqlite3_syscall_ptr)close, 0 },
#define osClose ((int(*)(int))aSyscall[1].pCurrent)
{ "read", (sqlite3_syscall_ptr)read, 0 },
#define osRead ((ssize_t(*)(int,void*,size_t))aSyscall[8].pCurrent)
-#if defined(USE_PREAD) || defined(SQLITE_ENABLE_LOCKING_STYLE)
+#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE
{ "pread", (sqlite3_syscall_ptr)pread, 0 },
#else
{ "pread", (sqlite3_syscall_ptr)0, 0 },
{ "write", (sqlite3_syscall_ptr)write, 0 },
#define osWrite ((ssize_t(*)(int,const void*,size_t))aSyscall[11].pCurrent)
-#if defined(USE_PREAD) || defined(SQLITE_ENABLE_LOCKING_STYLE)
+#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE
{ "pwrite", (sqlite3_syscall_ptr)pwrite, 0 },
#else
{ "pwrite", (sqlite3_syscall_ptr)0, 0 },
#endif
#define osFallocate ((int(*)(int,off_t,off_t))aSyscall[15].pCurrent)
+ { "unlink", (sqlite3_syscall_ptr)unlink, 0 },
+#define osUnlink ((int(*)(const char*))aSyscall[16].pCurrent)
+
+ { "openDirectory", (sqlite3_syscall_ptr)openDirectory, 0 },
+#define osOpenDirectory ((int(*)(const char*,int*))aSyscall[17].pCurrent)
+
}; /* End of the overrideable system calls */
/*
case ENODEV:
case ENXIO:
case ENOENT:
+#ifdef ESTALE /* ESTALE is not defined on Interix systems */
case ESTALE:
+#endif
case ENOSYS:
/* these should force the client to close the file and reconnect */
UnixUnusedFd *pUnused; /* Unused file descriptors to close */
unixInodeInfo *pNext; /* List of all unixInodeInfo objects */
unixInodeInfo *pPrev; /* .... doubly linked */
-#if defined(SQLITE_ENABLE_LOCKING_STYLE)
+#if SQLITE_ENABLE_LOCKING_STYLE
unsigned long long sharedByte; /* for AFP simulated shared lock */
#endif
#if OS_VXWORKS
*/
static int closeUnixFile(sqlite3_file *id){
unixFile *pFile = (unixFile*)id;
- if( pFile->dirfd>=0 ){
- robust_close(pFile, pFile->dirfd, __LINE__);
- pFile->dirfd=-1;
- }
if( pFile->h>=0 ){
robust_close(pFile, pFile->h, __LINE__);
pFile->h = -1;
#if OS_VXWORKS
if( pFile->pId ){
if( pFile->isDelete ){
- unlink(pFile->pId->zCanonicalName);
+ osUnlink(pFile->pId->zCanonicalName);
}
vxworksReleaseFileId(pFile->pId);
pFile->pId = 0;
*/
if( pFile->eFileLock > NO_LOCK ){
pFile->eFileLock = eFileLock;
-#if !OS_VXWORKS
/* Always update the timestamp on the old file */
+#ifdef HAVE_UTIME
+ utime(zLockFile, NULL);
+#else
utimes(zLockFile, NULL);
#endif
return SQLITE_OK;
/* To fully unlock the database, delete the lock file */
assert( eFileLock==NO_LOCK );
- if( unlink(zLockFile) ){
+ if( osUnlink(zLockFile) ){
int rc = 0;
int tErrno = errno;
if( ENOENT != tErrno ){
int rc = SQLITE_OK;
int reserved = 0;
unixFile *pFile = (unixFile*)id;
+ afpLockingContext *context;
SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
assert( pFile );
- afpLockingContext *context = (afpLockingContext *) pFile->lockingContext;
+ context = (afpLockingContext *) pFile->lockingContext;
if( context->reserved ){
*pResOut = 1;
return SQLITE_OK;
** operating system calls for the specified lock.
*/
if( eFileLock==SHARED_LOCK ){
- int lrc1, lrc2, lrc1Errno;
+ int lrc1, lrc2, lrc1Errno = 0;
long lk, mask;
assert( pInode->nShared==0 );
#elif defined(USE_PREAD64)
do{ got = osPwrite64(id->h, pBuf, cnt, offset);}while( got<0 && errno==EINTR);
#else
- newOffset = lseek(id->h, offset, SEEK_SET);
- SimulateIOError( newOffset-- );
- if( newOffset!=offset ){
- if( newOffset == -1 ){
- ((unixFile*)id)->lastErrno = errno;
- }else{
- ((unixFile*)id)->lastErrno = 0;
+ do{
+ newOffset = lseek(id->h, offset, SEEK_SET);
+ SimulateIOError( newOffset-- );
+ if( newOffset!=offset ){
+ if( newOffset == -1 ){
+ ((unixFile*)id)->lastErrno = errno;
+ }else{
+ ((unixFile*)id)->lastErrno = 0;
+ }
+ return -1;
}
- return -1;
- }
- do{ got = osWrite(id->h, pBuf, cnt); }while( got<0 && errno==EINTR );
+ got = osWrite(id->h, pBuf, cnt);
+ }while( got<0 && errno==EINTR );
#endif
TIMER_END;
if( got<0 ){
SimulateDiskfullError(( wrote=0, amt=1 ));
if( amt>0 ){
- if( wrote<0 ){
+ if( wrote<0 && pFile->lastErrno!=ENOSPC ){
/* lastErrno set by seekAndWrite */
return SQLITE_IOERR_WRITE;
}else{
/*
** We do not trust systems to provide a working fdatasync(). Some do.
-** Others do no. To be safe, we will stick with the (slower) fsync().
-** If you know that your system does support fdatasync() correctly,
+** Others do no. To be safe, we will stick with the (slightly slower)
+** fsync(). If you know that your system does support fdatasync() correctly,
** then simply compile with -Dfdatasync=fdatasync
*/
-#if !defined(fdatasync) && !defined(__linux__)
+#if !defined(fdatasync)
# define fdatasync fsync
#endif
}
/*
+** Open a file descriptor to the directory containing file zFilename.
+** If successful, *pFd is set to the opened file descriptor and
+** SQLITE_OK is returned. If an error occurs, either SQLITE_NOMEM
+** or SQLITE_CANTOPEN is returned and *pFd is set to an undefined
+** value.
+**
+** The directory file descriptor is used for only one thing - to
+** fsync() a directory to make sure file creation and deletion events
+** are flushed to disk. Such fsyncs are not needed on newer
+** journaling filesystems, but are required on older filesystems.
+**
+** This routine can be overridden using the xSetSysCall interface.
+** The ability to override this routine was added in support of the
+** chromium sandbox. Opening a directory is a security risk (we are
+** told) so making it overrideable allows the chromium sandbox to
+** replace this routine with a harmless no-op. To make this routine
+** a no-op, replace it with a stub that returns SQLITE_OK but leaves
+** *pFd set to a negative number.
+**
+** If SQLITE_OK is returned, the caller is responsible for closing
+** the file descriptor *pFd using close().
+*/
+static int openDirectory(const char *zFilename, int *pFd){
+ int ii;
+ int fd = -1;
+ char zDirname[MAX_PATHNAME+1];
+
+ sqlite3_snprintf(MAX_PATHNAME, zDirname, "%s", zFilename);
+ for(ii=(int)strlen(zDirname); ii>1 && zDirname[ii]!='/'; ii--);
+ if( ii>0 ){
+ zDirname[ii] = '\0';
+ fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0);
+ if( fd>=0 ){
+#ifdef FD_CLOEXEC
+ osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
+#endif
+ OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname));
+ }
+ }
+ *pFd = fd;
+ return (fd>=0?SQLITE_OK:unixLogError(SQLITE_CANTOPEN_BKPT, "open", zDirname));
+}
+
+/*
** Make sure all writes to a particular file are committed to disk.
**
** If dataOnly==0 then both the file itself and its metadata (file
pFile->lastErrno = errno;
return unixLogError(SQLITE_IOERR_FSYNC, "full_fsync", pFile->zPath);
}
- if( pFile->dirfd>=0 ){
- OSTRACE(("DIRSYNC %-3d (have_fullfsync=%d fullsync=%d)\n", pFile->dirfd,
+
+ /* Also fsync the directory containing the file if the DIRSYNC flag
+ ** is set. This is a one-time occurrance. Many systems (examples: AIX)
+ ** are unable to fsync a directory, so ignore errors on the fsync.
+ */
+ if( pFile->ctrlFlags & UNIXFILE_DIRSYNC ){
+ int dirfd;
+ OSTRACE(("DIRSYNC %s (have_fullfsync=%d fullsync=%d)\n", pFile->zPath,
HAVE_FULLFSYNC, isFullsync));
-#ifndef SQLITE_DISABLE_DIRSYNC
- /* The directory sync is only attempted if full_fsync is
- ** turned off or unavailable. If a full_fsync occurred above,
- ** then the directory sync is superfluous.
- */
- if( (!HAVE_FULLFSYNC || !isFullsync) && full_fsync(pFile->dirfd,0,0) ){
- /*
- ** We have received multiple reports of fsync() returning
- ** errors when applied to directories on certain file systems.
- ** A failed directory sync is not a big deal. So it seems
- ** better to ignore the error. Ticket #1657
- */
- /* pFile->lastErrno = errno; */
- /* return SQLITE_IOERR; */
+ rc = osOpenDirectory(pFile->zPath, &dirfd);
+ if( rc==SQLITE_OK && dirfd>=0 ){
+ full_fsync(dirfd, 0, 0);
+ robust_close(pFile, dirfd, __LINE__);
+ }else if( rc==SQLITE_CANTOPEN ){
+ rc = SQLITE_OK;
}
-#endif
- /* Only need to sync once, so close the directory when we are done */
- robust_close(pFile, pFile->dirfd, __LINE__);
- pFile->dirfd = -1;
+ pFile->ctrlFlags &= ~UNIXFILE_DIRSYNC;
}
return rc;
}
/*
** This function is called to handle the SQLITE_FCNTL_SIZE_HINT
-** file-control operation.
-**
-** If the user has configured a chunk-size for this file, it could be
-** that the file needs to be extended at this point. Otherwise, the
-** SQLITE_FCNTL_SIZE_HINT operation is a no-op for Unix.
+** file-control operation. Enlarge the database to nBytes in size
+** (rounded up to the next chunk-size). If the database is already
+** nBytes or larger, this routine is a no-op.
*/
static int fcntlSizeHint(unixFile *pFile, i64 nByte){
- if( pFile->szChunk ){
+ if( pFile->szChunk>0 ){
i64 nSize; /* Required file size */
struct stat buf; /* Used to hold return values of fstat() */
** Information and control of an open file handle.
*/
static int unixFileControl(sqlite3_file *id, int op, void *pArg){
+ unixFile *pFile = (unixFile*)id;
switch( op ){
case SQLITE_FCNTL_LOCKSTATE: {
- *(int*)pArg = ((unixFile*)id)->eFileLock;
+ *(int*)pArg = pFile->eFileLock;
return SQLITE_OK;
}
case SQLITE_LAST_ERRNO: {
- *(int*)pArg = ((unixFile*)id)->lastErrno;
+ *(int*)pArg = pFile->lastErrno;
return SQLITE_OK;
}
case SQLITE_FCNTL_CHUNK_SIZE: {
- ((unixFile*)id)->szChunk = *(int *)pArg;
+ pFile->szChunk = *(int *)pArg;
return SQLITE_OK;
}
case SQLITE_FCNTL_SIZE_HINT: {
- return fcntlSizeHint((unixFile *)id, *(i64 *)pArg);
+ int rc;
+ SimulateIOErrorBenign(1);
+ rc = fcntlSizeHint(pFile, *(i64 *)pArg);
+ SimulateIOErrorBenign(0);
+ return rc;
+ }
+ case SQLITE_FCNTL_PERSIST_WAL: {
+ int bPersist = *(int*)pArg;
+ if( bPersist<0 ){
+ *(int*)pArg = (pFile->ctrlFlags & UNIXFILE_PERSIST_WAL)!=0;
+ }else if( bPersist==0 ){
+ pFile->ctrlFlags &= ~UNIXFILE_PERSIST_WAL;
+ }else{
+ pFile->ctrlFlags |= UNIXFILE_PERSIST_WAL;
+ }
+ return SQLITE_OK;
}
#ifndef NDEBUG
/* The pager calls this method to signal that it has done
char *zFilename; /* Name of the mmapped file */
int h; /* Open file descriptor */
int szRegion; /* Size of shared-memory regions */
- int nRegion; /* Size of array apRegion */
+ u16 nRegion; /* Size of array apRegion */
+ u8 isReadonly; /* True if read-only */
char **apRegion; /* Array of mapped shared-memory regions */
int nRef; /* Number of unixShm objects pointing to this */
unixShm *pFirst; /* All unixShm objects pointing to this */
unixShmNode *pShmNode; /* The underlying unixShmNode object */
unixShm *pNext; /* Next unixShm with the same unixShmNode */
u8 hasMutex; /* True if holding the unixShmNode mutex */
+ u8 id; /* Id of this connection within its unixShmNode */
u16 sharedMask; /* Mask of shared locks held */
u16 exclMask; /* Mask of exclusive locks held */
-#ifdef SQLITE_DEBUG
- u8 id; /* Id of this connection within its unixShmNode */
-#endif
};
/*
if( p && p->nRef==0 ){
int i;
assert( p->pInode==pFd->pInode );
- if( p->mutex ) sqlite3_mutex_free(p->mutex);
+ sqlite3_mutex_free(p->mutex);
for(i=0; i<p->nRegion; i++){
if( p->h>=0 ){
munmap(p->apRegion[i], p->szRegion);
(u32)sStat.st_ino, (u32)sStat.st_dev);
#else
sqlite3_snprintf(nShmFilename, zShmFilename, "%s-shm", pDbFd->zPath);
+ sqlite3FileSuffix3(pDbFd->zPath, zShmFilename);
#endif
pShmNode->h = -1;
pDbFd->pInode->pShmNode = pShmNode;
pShmNode->h = robust_open(zShmFilename, O_RDWR|O_CREAT,
(sStat.st_mode & 0777));
if( pShmNode->h<0 ){
- rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename);
- goto shm_open_err;
+ const char *zRO;
+ zRO = sqlite3_uri_parameter(pDbFd->zPath, "readonly_shm");
+ if( zRO && sqlite3GetBoolean(zRO) ){
+ pShmNode->h = robust_open(zShmFilename, O_RDONLY,
+ (sStat.st_mode & 0777));
+ pShmNode->isReadonly = 1;
+ }
+ if( pShmNode->h<0 ){
+ rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename);
+ goto shm_open_err;
+ }
}
/* Check to see if another process is holding the dead-man switch.
while(pShmNode->nRegion<=iRegion){
void *pMem;
if( pShmNode->h>=0 ){
- pMem = mmap(0, szRegion, PROT_READ|PROT_WRITE,
+ pMem = mmap(0, szRegion,
+ pShmNode->isReadonly ? PROT_READ : PROT_READ|PROT_WRITE,
MAP_SHARED, pShmNode->h, pShmNode->nRegion*szRegion
);
if( pMem==MAP_FAILED ){
- rc = SQLITE_IOERR;
+ rc = unixLogError(SQLITE_IOERR_SHMMAP, "mmap", pShmNode->zFilename);
goto shmpage_out;
}
}else{
}else{
*pp = 0;
}
+ if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY;
sqlite3_mutex_leave(pShmNode->mutex);
return rc;
}
assert( pShmNode->nRef>0 );
pShmNode->nRef--;
if( pShmNode->nRef==0 ){
- if( deleteFlag && pShmNode->h>=0 ) unlink(pShmNode->zFilename);
+ if( deleteFlag && pShmNode->h>=0 ) osUnlink(pShmNode->zFilename);
unixShmPurge(pDbFd);
}
unixLeaveMutex();
static int fillInUnixFile(
sqlite3_vfs *pVfs, /* Pointer to vfs object */
int h, /* Open file descriptor of file being opened */
- int dirfd, /* Directory file descriptor */
+ int syncDir, /* True to sync directory on first sync */
sqlite3_file *pId, /* Write to the unixFile structure here */
const char *zFilename, /* Name of the file being opened */
int noLock, /* Omit locking if true */
OSTRACE(("OPEN %-3d %s\n", h, zFilename));
pNew->h = h;
- pNew->dirfd = dirfd;
pNew->zPath = zFilename;
if( memcmp(pVfs->zName,"unix-excl",10)==0 ){
pNew->ctrlFlags = UNIXFILE_EXCL;
if( isReadOnly ){
pNew->ctrlFlags |= UNIXFILE_RDONLY;
}
+ if( syncDir ){
+ pNew->ctrlFlags |= UNIXFILE_DIRSYNC;
+ }
#if OS_VXWORKS
pNew->pId = vxworksFindFileId(zFilename);
if( rc!=SQLITE_OK ){
if( h>=0 ) robust_close(pNew, h, __LINE__);
h = -1;
- unlink(zFilename);
+ osUnlink(zFilename);
isDelete = 0;
}
pNew->isDelete = isDelete;
#endif
if( rc!=SQLITE_OK ){
- if( dirfd>=0 ) robust_close(pNew, dirfd, __LINE__);
if( h>=0 ) robust_close(pNew, h, __LINE__);
}else{
pNew->pMethod = pLockingStyle;
}
/*
-** Open a file descriptor to the directory containing file zFilename.
-** If successful, *pFd is set to the opened file descriptor and
-** SQLITE_OK is returned. If an error occurs, either SQLITE_NOMEM
-** or SQLITE_CANTOPEN is returned and *pFd is set to an undefined
-** value.
-**
-** If SQLITE_OK is returned, the caller is responsible for closing
-** the file descriptor *pFd using close().
-*/
-static int openDirectory(const char *zFilename, int *pFd){
- int ii;
- int fd = -1;
- char zDirname[MAX_PATHNAME+1];
-
- sqlite3_snprintf(MAX_PATHNAME, zDirname, "%s", zFilename);
- for(ii=(int)strlen(zDirname); ii>1 && zDirname[ii]!='/'; ii--);
- if( ii>0 ){
- zDirname[ii] = '\0';
- fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0);
- if( fd>=0 ){
-#ifdef FD_CLOEXEC
- osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
-#endif
- OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname));
- }
- }
- *pFd = fd;
- return (fd>=0?SQLITE_OK:unixLogError(SQLITE_CANTOPEN_BKPT, "open", zDirname));
-}
-
-/*
** Return the name of a directory in which to put temporary files.
** If no suitable temporary file directory can be found, return NULL.
*/
**
** Even if a subsequent open() call does succeed, the consequences of
** not searching for a resusable file descriptor are not dire. */
- if( 0==stat(zPath, &sStat) ){
+ if( 0==osStat(zPath, &sStat) ){
unixInodeInfo *pInode;
unixEnterMutex();
** corresponding database file and sets *pMode to this value. Whenever
** possible, WAL and journal files are created using the same permissions
** as the associated database file.
+**
+** If the SQLITE_ENABLE_8_3_NAMES option is enabled, then the
+** original filename is unavailable. But 8_3_NAMES is only used for
+** FAT filesystems and permissions do not matter there, so just use
+** the default permissions.
*/
static int findCreateFileMode(
const char *zPath, /* Path of file (possibly) being created */
mode_t *pMode /* OUT: Permissions to open file with */
){
int rc = SQLITE_OK; /* Return Code */
+ *pMode = SQLITE_DEFAULT_FILE_PERMISSIONS;
if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){
char zDb[MAX_PATHNAME+1]; /* Database file path */
int nDb; /* Number of valid bytes in zDb */
**
** "<path to db>-journal"
** "<path to db>-wal"
- ** "<path to db>-journal-NNNN"
- ** "<path to db>-wal-NNNN"
+ ** "<path to db>-journalNN"
+ ** "<path to db>-walNN"
**
- ** where NNNN is a 4 digit decimal number. The NNNN naming schemes are
+ ** where NN is a 4 digit decimal number. The NN naming schemes are
** used by the test_multiplex.c module.
*/
nDb = sqlite3Strlen30(zPath) - 1;
- while( nDb>0 && zPath[nDb]!='l' ) nDb--;
- nDb -= ((flags & SQLITE_OPEN_WAL) ? 3 : 7);
+ while( nDb>0 && zPath[nDb]!='-' ) nDb--;
+ if( nDb==0 ) return SQLITE_OK;
memcpy(zDb, zPath, nDb);
zDb[nDb] = '\0';
- if( 0==stat(zDb, &sStat) ){
+ if( 0==osStat(zDb, &sStat) ){
*pMode = sStat.st_mode & 0777;
}else{
rc = SQLITE_IOERR_FSTAT;
}
}else if( flags & SQLITE_OPEN_DELETEONCLOSE ){
*pMode = 0600;
- }else{
- *pMode = SQLITE_DEFAULT_FILE_PERMISSIONS;
}
return rc;
}
){
unixFile *p = (unixFile *)pFile;
int fd = -1; /* File descriptor returned by open() */
- int dirfd = -1; /* Directory file descriptor */
int openFlags = 0; /* Flags to pass to open() */
int eType = flags&0xFFFFFF00; /* Type of file to open */
int noLock; /* True to omit locking primitives */
#if SQLITE_ENABLE_LOCKING_STYLE
int isAutoProxy = (flags & SQLITE_OPEN_AUTOPROXY);
#endif
+#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE
+ struct statfs fsInfo;
+#endif
/* If creating a master or main-file journal, this function will open
** a file-descriptor on the directory too. The first time unixSync()
** is called the directory file descriptor will be fsync()ed and close()d.
*/
- int isOpenDirectory = (isCreate && (
+ int syncDir = (isCreate && (
eType==SQLITE_OPEN_MASTER_JOURNAL
|| eType==SQLITE_OPEN_MAIN_JOURNAL
|| eType==SQLITE_OPEN_WAL
p->pUnused = pUnused;
}else if( !zName ){
/* If zName is NULL, the upper layer is requesting a temp file. */
- assert(isDelete && !isOpenDirectory);
+ assert(isDelete && !syncDir);
rc = unixGetTempname(MAX_PATHNAME+1, zTmpname);
if( rc!=SQLITE_OK ){
return rc;
#if OS_VXWORKS
zPath = zName;
#else
- unlink(zName);
+ osUnlink(zName);
#endif
}
#if SQLITE_ENABLE_LOCKING_STYLE
}
#endif
- if( isOpenDirectory ){
- rc = openDirectory(zPath, &dirfd);
- if( rc!=SQLITE_OK ){
- /* It is safe to close fd at this point, because it is guaranteed not
- ** to be open on a database file. If it were open on a database file,
- ** it would not be safe to close as this would release any locks held
- ** on the file by this process. */
- assert( eType!=SQLITE_OPEN_MAIN_DB );
- robust_close(p, fd, __LINE__);
- goto open_finished;
- }
- }
-
#ifdef FD_CLOEXEC
osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
#endif
#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE
- struct statfs fsInfo;
if( fstatfs(fd, &fsInfo) == -1 ){
((unixFile*)pFile)->lastErrno = errno;
- if( dirfd>=0 ) robust_close(p, dirfd, __LINE__);
robust_close(p, fd, __LINE__);
return SQLITE_IOERR_ACCESS;
}
if( envforce!=NULL ){
useProxy = atoi(envforce)>0;
}else{
- struct statfs fsInfo;
if( statfs(zPath, &fsInfo) == -1 ){
/* In theory, the close(fd) call is sub-optimal. If the file opened
** with fd is a database file, and there are other connections open
** not while other file descriptors opened by the same process on
** the same file are working. */
p->lastErrno = errno;
- if( dirfd>=0 ){
- robust_close(p, dirfd, __LINE__);
- }
robust_close(p, fd, __LINE__);
rc = SQLITE_IOERR_ACCESS;
goto open_finished;
useProxy = !(fsInfo.f_flags&MNT_LOCAL);
}
if( useProxy ){
- rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock,
+ rc = fillInUnixFile(pVfs, fd, syncDir, pFile, zPath, noLock,
isDelete, isReadonly);
if( rc==SQLITE_OK ){
rc = proxyTransformUnixFile((unixFile*)pFile, ":auto:");
}
#endif
- rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock,
+ rc = fillInUnixFile(pVfs, fd, syncDir, pFile, zPath, noLock,
isDelete, isReadonly);
open_finished:
if( rc!=SQLITE_OK ){
int rc = SQLITE_OK;
UNUSED_PARAMETER(NotUsed);
SimulateIOError(return SQLITE_IOERR_DELETE);
- if( unlink(zPath)==(-1) && errno!=ENOENT ){
+ if( osUnlink(zPath)==(-1) && errno!=ENOENT ){
return unixLogError(SQLITE_IOERR_DELETE, "unlink", zPath);
}
#ifndef SQLITE_DISABLE_DIRSYNC
if( dirSync ){
int fd;
- rc = openDirectory(zPath, &fd);
+ rc = osOpenDirectory(zPath, &fd);
if( rc==SQLITE_OK ){
#if OS_VXWORKS
if( fsync(fd)==-1 )
rc = unixLogError(SQLITE_IOERR_DIR_FSYNC, "fsync", zPath);
}
robust_close(0, fd, __LINE__);
+ }else if( rc==SQLITE_CANTOPEN ){
+ rc = SQLITE_OK;
}
}
#endif
*pResOut = (osAccess(zPath, amode)==0);
if( flags==SQLITE_ACCESS_EXISTS && *pResOut ){
struct stat buf;
- if( 0==stat(zPath, &buf) && buf.st_size==0 ){
+ if( 0==osStat(zPath, &buf) && buf.st_size==0 ){
*pResOut = 0;
}
}
int islockfile /* if non zero missing dirs will be created */
) {
int fd = -1;
- int dirfd = -1;
unixFile *pNew;
int rc = SQLITE_OK;
int openFlags = O_RDWR | O_CREAT;
pUnused->flags = openFlags;
pNew->pUnused = pUnused;
- rc = fillInUnixFile(&dummyVfs, fd, dirfd, (sqlite3_file*)pNew, path, 0, 0, 0);
+ rc = fillInUnixFile(&dummyVfs, fd, 0, (sqlite3_file*)pNew, path, 0, 0, 0);
if( rc==SQLITE_OK ){
*ppFile = pNew;
return SQLITE_OK;
return SQLITE_IOERR;
}
}
+#else
+ UNUSED_PARAMETER(pError);
#endif
#ifdef SQLITE_TEST
/* simulate multiple hosts by creating unique hostid file paths */
end_breaklock:
if( rc ){
if( fd>=0 ){
- unlink(tPath);
+ osUnlink(tPath);
robust_close(pFile, fd, __LINE__);
}
fprintf(stderr, "failed to break stale lock on %s, %s\n", cPath, errmsg);
int nTries = 0;
struct timespec conchModTime;
+ memset(&conchModTime, 0, sizeof(conchModTime));
do {
rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, lockType);
nTries ++;
end_takeconch:
OSTRACE(("TRANSPROXY: CLOSE %d\n", pFile->h));
if( rc==SQLITE_OK && pFile->openFlags ){
+ int fd;
if( pFile->h>=0 ){
robust_close(pFile, pFile->h, __LINE__);
}
pFile->h = -1;
- int fd = robust_open(pCtx->dbPath, pFile->openFlags,
+ fd = robust_open(pCtx->dbPath, pFile->openFlags,
SQLITE_DEFAULT_FILE_PERMISSIONS);
OSTRACE(("TRANSPROXY: OPEN %d\n", fd));
if( fd>=0 ){
/* Double-check that the aSyscall[] array has been constructed
** correctly. See ticket [bb3a86e890c8e96ab] */
- assert( ArraySize(aSyscall)==16 );
+ assert( ArraySize(aSyscall)==18 );
/* Register all VFSes defined in the aVfs[] array */
for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){
# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
#endif
-#ifdef SQLITE_DEBUG
-SQLITE_PRIVATE int sqlite3OSTrace = 0;
-#define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X
+#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
+# ifndef SQLITE_DEBUG_OS_TRACE
+# define SQLITE_DEBUG_OS_TRACE 0
+# endif
+ int sqlite3OSTrace = SQLITE_DEBUG_OS_TRACE;
+# define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X
#else
-#define OSTRACE(X)
+# define OSTRACE(X)
#endif
/*
const sqlite3_io_methods *pMethod; /*** Must be first ***/
sqlite3_vfs *pVfs; /* The VFS used to open this file */
HANDLE h; /* Handle for accessing the file */
- unsigned char locktype; /* Type of lock currently held on this file */
+ u8 locktype; /* Type of lock currently held on this file */
short sharedLockByte; /* Randomly chosen byte used as a shared lock */
+ u8 bPersistWal; /* True to persist WAL files */
DWORD lastErrno; /* The Windows errno from the last I/O error */
DWORD sectorSize; /* Sector size of the device file is on */
winShm *pShm; /* Instance of shared memory on this file */
};
/*
+ * If compiled with SQLITE_WIN32_MALLOC on Windows, we will use the
+ * various Win32 API heap functions instead of our own.
+ */
+#ifdef SQLITE_WIN32_MALLOC
+/*
+ * The initial size of the Win32-specific heap. This value may be zero.
+ */
+#ifndef SQLITE_WIN32_HEAP_INIT_SIZE
+# define SQLITE_WIN32_HEAP_INIT_SIZE ((SQLITE_DEFAULT_CACHE_SIZE) * \
+ (SQLITE_DEFAULT_PAGE_SIZE) + 4194304)
+#endif
+
+/*
+ * The maximum size of the Win32-specific heap. This value may be zero.
+ */
+#ifndef SQLITE_WIN32_HEAP_MAX_SIZE
+# define SQLITE_WIN32_HEAP_MAX_SIZE (0)
+#endif
+
+/*
+ * The extra flags to use in calls to the Win32 heap APIs. This value may be
+ * zero for the default behavior.
+ */
+#ifndef SQLITE_WIN32_HEAP_FLAGS
+# define SQLITE_WIN32_HEAP_FLAGS (0)
+#endif
+
+/*
+** The winMemData structure stores information required by the Win32-specific
+** sqlite3_mem_methods implementation.
+*/
+typedef struct winMemData winMemData;
+struct winMemData {
+#ifndef NDEBUG
+ u32 magic; /* Magic number to detect structure corruption. */
+#endif
+ HANDLE hHeap; /* The handle to our heap. */
+ BOOL bOwned; /* Do we own the heap (i.e. destroy it on shutdown)? */
+};
+
+#ifndef NDEBUG
+#define WINMEM_MAGIC 0x42b2830b
+#endif
+
+static struct winMemData win_mem_data = {
+#ifndef NDEBUG
+ WINMEM_MAGIC,
+#endif
+ NULL, FALSE
+};
+
+#ifndef NDEBUG
+#define winMemAssertMagic() assert( win_mem_data.magic==WINMEM_MAGIC )
+#else
+#define winMemAssertMagic()
+#endif
+
+#define winMemGetHeap() win_mem_data.hHeap
+
+static void *winMemMalloc(int nBytes);
+static void winMemFree(void *pPrior);
+static void *winMemRealloc(void *pPrior, int nBytes);
+static int winMemSize(void *p);
+static int winMemRoundup(int n);
+static int winMemInit(void *pAppData);
+static void winMemShutdown(void *pAppData);
+
+SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetWin32(void);
+#endif /* SQLITE_WIN32_MALLOC */
+
+/*
** Forward prototypes.
*/
static int getSectorSize(
}
#endif /* SQLITE_OS_WINCE */
+#ifdef SQLITE_WIN32_MALLOC
+/*
+** Allocate nBytes of memory.
+*/
+static void *winMemMalloc(int nBytes){
+ HANDLE hHeap;
+ void *p;
+
+ winMemAssertMagic();
+ hHeap = winMemGetHeap();
+ assert( hHeap!=0 );
+ assert( hHeap!=INVALID_HANDLE_VALUE );
+#ifdef SQLITE_WIN32_MALLOC_VALIDATE
+ assert ( HeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
+#endif
+ assert( nBytes>=0 );
+ p = HeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes);
+ if( !p ){
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapAlloc %u bytes (%d), heap=%p",
+ nBytes, GetLastError(), (void*)hHeap);
+ }
+ return p;
+}
+
+/*
+** Free memory.
+*/
+static void winMemFree(void *pPrior){
+ HANDLE hHeap;
+
+ winMemAssertMagic();
+ hHeap = winMemGetHeap();
+ assert( hHeap!=0 );
+ assert( hHeap!=INVALID_HANDLE_VALUE );
+#ifdef SQLITE_WIN32_MALLOC_VALIDATE
+ assert ( HeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) );
+#endif
+ if( !pPrior ) return; /* Passing NULL to HeapFree is undefined. */
+ if( !HeapFree(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ){
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapFree block %p (%d), heap=%p",
+ pPrior, GetLastError(), (void*)hHeap);
+ }
+}
+
+/*
+** Change the size of an existing memory allocation
+*/
+static void *winMemRealloc(void *pPrior, int nBytes){
+ HANDLE hHeap;
+ void *p;
+
+ winMemAssertMagic();
+ hHeap = winMemGetHeap();
+ assert( hHeap!=0 );
+ assert( hHeap!=INVALID_HANDLE_VALUE );
+#ifdef SQLITE_WIN32_MALLOC_VALIDATE
+ assert ( HeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) );
+#endif
+ assert( nBytes>=0 );
+ if( !pPrior ){
+ p = HeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes);
+ }else{
+ p = HeapReAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior, (SIZE_T)nBytes);
+ }
+ if( !p ){
+ sqlite3_log(SQLITE_NOMEM, "failed to %s %u bytes (%d), heap=%p",
+ pPrior ? "HeapReAlloc" : "HeapAlloc", nBytes, GetLastError(),
+ (void*)hHeap);
+ }
+ return p;
+}
+
+/*
+** Return the size of an outstanding allocation, in bytes.
+*/
+static int winMemSize(void *p){
+ HANDLE hHeap;
+ SIZE_T n;
+
+ winMemAssertMagic();
+ hHeap = winMemGetHeap();
+ assert( hHeap!=0 );
+ assert( hHeap!=INVALID_HANDLE_VALUE );
+#ifdef SQLITE_WIN32_MALLOC_VALIDATE
+ assert ( HeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
+#endif
+ if( !p ) return 0;
+ n = HeapSize(hHeap, SQLITE_WIN32_HEAP_FLAGS, p);
+ if( n==(SIZE_T)-1 ){
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapSize block %p (%d), heap=%p",
+ p, GetLastError(), (void*)hHeap);
+ return 0;
+ }
+ return (int)n;
+}
+
+/*
+** Round up a request size to the next valid allocation size.
+*/
+static int winMemRoundup(int n){
+ return n;
+}
+
+/*
+** Initialize this module.
+*/
+static int winMemInit(void *pAppData){
+ winMemData *pWinMemData = (winMemData *)pAppData;
+
+ if( !pWinMemData ) return SQLITE_ERROR;
+ assert( pWinMemData->magic==WINMEM_MAGIC );
+ if( !pWinMemData->hHeap ){
+ pWinMemData->hHeap = HeapCreate(SQLITE_WIN32_HEAP_FLAGS,
+ SQLITE_WIN32_HEAP_INIT_SIZE,
+ SQLITE_WIN32_HEAP_MAX_SIZE);
+ if( !pWinMemData->hHeap ){
+ sqlite3_log(SQLITE_NOMEM,
+ "failed to HeapCreate (%d), flags=%u, initSize=%u, maxSize=%u",
+ GetLastError(), SQLITE_WIN32_HEAP_FLAGS, SQLITE_WIN32_HEAP_INIT_SIZE,
+ SQLITE_WIN32_HEAP_MAX_SIZE);
+ return SQLITE_NOMEM;
+ }
+ pWinMemData->bOwned = TRUE;
+ }
+ assert( pWinMemData->hHeap!=0 );
+ assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE );
+#ifdef SQLITE_WIN32_MALLOC_VALIDATE
+ assert( HeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
+#endif
+ return SQLITE_OK;
+}
+
+/*
+** Deinitialize this module.
+*/
+static void winMemShutdown(void *pAppData){
+ winMemData *pWinMemData = (winMemData *)pAppData;
+
+ if( !pWinMemData ) return;
+ if( pWinMemData->hHeap ){
+ assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE );
+#ifdef SQLITE_WIN32_MALLOC_VALIDATE
+ assert( HeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
+#endif
+ if( pWinMemData->bOwned ){
+ if( !HeapDestroy(pWinMemData->hHeap) ){
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapDestroy (%d), heap=%p",
+ GetLastError(), (void*)pWinMemData->hHeap);
+ }
+ pWinMemData->bOwned = FALSE;
+ }
+ pWinMemData->hHeap = NULL;
+ }
+}
+
+/*
+** Populate the low-level memory allocation function pointers in
+** sqlite3GlobalConfig.m with pointers to the routines in this file. The
+** arguments specify the block of memory to manage.
+**
+** This routine is only called by sqlite3_config(), and therefore
+** is not required to be threadsafe (it is not).
+*/
+SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetWin32(void){
+ static const sqlite3_mem_methods winMemMethods = {
+ winMemMalloc,
+ winMemFree,
+ winMemRealloc,
+ winMemSize,
+ winMemRoundup,
+ winMemInit,
+ winMemShutdown,
+ &win_mem_data
+ };
+ return &winMemMethods;
+}
+
+SQLITE_PRIVATE void sqlite3MemSetDefault(void){
+ sqlite3_config(SQLITE_CONFIG_MALLOC, sqlite3MemGetWin32());
+}
+#endif /* SQLITE_WIN32_MALLOC */
+
/*
** Convert a UTF-8 string to microsoft unicode (UTF-16?).
**
** Convert UTF-8 to multibyte character string. Space to hold the
** returned string is obtained from malloc().
*/
-static char *utf8ToMbcs(const char *zFilename){
+SQLITE_API char *sqlite3_win32_utf8_to_mbcs(const char *zFilename){
char *zFilenameMbcs;
WCHAR *zTmpWide;
return zFilenameMbcs;
}
+
+/*
+** The return value of getLastErrorMsg
+** is zero if the error message fits in the buffer, or non-zero
+** otherwise (if the message was truncated).
+*/
+static int getLastErrorMsg(int nBuf, char *zBuf){
+ /* FormatMessage returns 0 on failure. Otherwise it
+ ** returns the number of TCHARs written to the output
+ ** buffer, excluding the terminating null char.
+ */
+ DWORD error = GetLastError();
+ DWORD dwLen = 0;
+ char *zOut = 0;
+
+ if( isNT() ){
+ WCHAR *zTempWide = NULL;
+ dwLen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ error,
+ 0,
+ (LPWSTR) &zTempWide,
+ 0,
+ 0);
+ if( dwLen > 0 ){
+ /* allocate a buffer and convert to UTF8 */
+ zOut = unicodeToUtf8(zTempWide);
+ /* free the system buffer allocated by FormatMessage */
+ LocalFree(zTempWide);
+ }
+/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
+** Since the ASCII version of these Windows API do not exist for WINCE,
+** it's important to not reference them for WINCE builds.
+*/
+#if SQLITE_OS_WINCE==0
+ }else{
+ char *zTemp = NULL;
+ dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ error,
+ 0,
+ (LPSTR) &zTemp,
+ 0,
+ 0);
+ if( dwLen > 0 ){
+ /* allocate a buffer and convert to UTF8 */
+ zOut = sqlite3_win32_mbcs_to_utf8(zTemp);
+ /* free the system buffer allocated by FormatMessage */
+ LocalFree(zTemp);
+ }
+#endif
+ }
+ if( 0 == dwLen ){
+ sqlite3_snprintf(nBuf, zBuf, "OsError 0x%x (%u)", error, error);
+ }else{
+ /* copy a maximum of nBuf chars to output buffer */
+ sqlite3_snprintf(nBuf, zBuf, "%s", zOut);
+ /* free the UTF8 buffer */
+ free(zOut);
+ }
+ return 0;
+}
+
+/*
+**
+** This function - winLogErrorAtLine() - is only ever called via the macro
+** winLogError().
+**
+** This routine is invoked after an error occurs in an OS function.
+** It logs a message using sqlite3_log() containing the current value of
+** error code and, if possible, the human-readable equivalent from
+** FormatMessage.
+**
+** The first argument passed to the macro should be the error code that
+** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN).
+** The two subsequent arguments should be the name of the OS function that
+** failed and the the associated file-system path, if any.
+*/
+#define winLogError(a,b,c) winLogErrorAtLine(a,b,c,__LINE__)
+static int winLogErrorAtLine(
+ int errcode, /* SQLite error code */
+ const char *zFunc, /* Name of OS function that failed */
+ const char *zPath, /* File path associated with error */
+ int iLine /* Source line number where error occurred */
+){
+ char zMsg[500]; /* Human readable error text */
+ int i; /* Loop counter */
+ DWORD iErrno = GetLastError(); /* Error code */
+
+ zMsg[0] = 0;
+ getLastErrorMsg(sizeof(zMsg), zMsg);
+ assert( errcode!=SQLITE_OK );
+ if( zPath==0 ) zPath = "";
+ for(i=0; zMsg[i] && zMsg[i]!='\r' && zMsg[i]!='\n'; i++){}
+ zMsg[i] = 0;
+ sqlite3_log(errcode,
+ "os_win.c:%d: (%d) %s(%s) - %s",
+ iLine, iErrno, zFunc, zPath, zMsg
+ );
+
+ return errcode;
+}
+
+/*
+** The number of times that a ReadFile(), WriteFile(), and DeleteFile()
+** will be retried following a locking error - probably caused by
+** antivirus software. Also the initial delay before the first retry.
+** The delay increases linearly with each retry.
+*/
+#ifndef SQLITE_WIN32_IOERR_RETRY
+# define SQLITE_WIN32_IOERR_RETRY 10
+#endif
+#ifndef SQLITE_WIN32_IOERR_RETRY_DELAY
+# define SQLITE_WIN32_IOERR_RETRY_DELAY 25
+#endif
+static int win32IoerrRetry = SQLITE_WIN32_IOERR_RETRY;
+static int win32IoerrRetryDelay = SQLITE_WIN32_IOERR_RETRY_DELAY;
+
+/*
+** If a ReadFile() or WriteFile() error occurs, invoke this routine
+** to see if it should be retried. Return TRUE to retry. Return FALSE
+** to give up with an error.
+*/
+static int retryIoerr(int *pnRetry){
+ DWORD e;
+ if( *pnRetry>=win32IoerrRetry ){
+ return 0;
+ }
+ e = GetLastError();
+ if( e==ERROR_ACCESS_DENIED ||
+ e==ERROR_LOCK_VIOLATION ||
+ e==ERROR_SHARING_VIOLATION ){
+ Sleep(win32IoerrRetryDelay*(1+*pnRetry));
+ ++*pnRetry;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+** Log a I/O error retry episode.
+*/
+static void logIoerr(int nRetry){
+ if( nRetry ){
+ sqlite3_log(SQLITE_IOERR,
+ "delayed %dms for lock/sharing conflict",
+ win32IoerrRetryDelay*nRetry*(nRetry+1)/2
+ );
+ }
+}
+
#if SQLITE_OS_WINCE
/*************************************************************************
** This section contains code for WinCE only.
** WindowsCE does not have a localtime() function. So create a
** substitute.
*/
+/* #include <time.h> */
struct tm *__cdecl localtime(const time_t *t)
{
static struct tm y;
pFile->hMutex = CreateMutexW(NULL, FALSE, zName);
if (!pFile->hMutex){
pFile->lastErrno = GetLastError();
+ winLogError(SQLITE_ERROR, "winceCreateLock1", zFilename);
free(zName);
return FALSE;
}
/* If mapping failed, close the shared memory handle and erase it */
if (!pFile->shared){
pFile->lastErrno = GetLastError();
+ winLogError(SQLITE_ERROR, "winceCreateLock2", zFilename);
CloseHandle(pFile->hShared);
pFile->hShared = NULL;
}
dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
if( (dwRet==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR) ){
pFile->lastErrno = GetLastError();
+ winLogError(SQLITE_IOERR_SEEK, "seekWinFile", pFile->zPath);
return 1;
}
#endif
OSTRACE(("CLOSE %d %s\n", pFile->h, rc ? "ok" : "failed"));
OpenCounter(-1);
- return rc ? SQLITE_OK : SQLITE_IOERR;
+ return rc ? SQLITE_OK
+ : winLogError(SQLITE_IOERR_CLOSE, "winClose", pFile->zPath);
}
/*
){
winFile *pFile = (winFile*)id; /* file handle */
DWORD nRead; /* Number of bytes actually read from file */
+ int nRetry = 0; /* Number of retrys */
assert( id!=0 );
SimulateIOError(return SQLITE_IOERR_READ);
if( seekWinFile(pFile, offset) ){
return SQLITE_FULL;
}
- if( !ReadFile(pFile->h, pBuf, amt, &nRead, 0) ){
+ while( !ReadFile(pFile->h, pBuf, amt, &nRead, 0) ){
+ if( retryIoerr(&nRetry) ) continue;
pFile->lastErrno = GetLastError();
- return SQLITE_IOERR_READ;
+ return winLogError(SQLITE_IOERR_READ, "winRead", pFile->zPath);
}
+ logIoerr(nRetry);
if( nRead<(DWORD)amt ){
/* Unread parts of the buffer must be zero-filled */
memset(&((char*)pBuf)[nRead], 0, amt-nRead);
){
int rc; /* True if error has occured, else false */
winFile *pFile = (winFile*)id; /* File handle */
+ int nRetry = 0; /* Number of retries */
assert( amt>0 );
assert( pFile );
int nRem = amt; /* Number of bytes yet to be written */
DWORD nWrite; /* Bytes written by each WriteFile() call */
- while( nRem>0 && WriteFile(pFile->h, aRem, nRem, &nWrite, 0) && nWrite>0 ){
+ while( nRem>0 ){
+ if( !WriteFile(pFile->h, aRem, nRem, &nWrite, 0) ){
+ if( retryIoerr(&nRetry) ) continue;
+ break;
+ }
+ if( nWrite<=0 ) break;
aRem += nWrite;
nRem -= nWrite;
}
}
if( rc ){
- if( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ){
+ if( ( pFile->lastErrno==ERROR_HANDLE_DISK_FULL )
+ || ( pFile->lastErrno==ERROR_DISK_FULL )){
return SQLITE_FULL;
}
- return SQLITE_IOERR_WRITE;
+ return winLogError(SQLITE_IOERR_WRITE, "winWrite", pFile->zPath);
+ }else{
+ logIoerr(nRetry);
}
return SQLITE_OK;
}
** actual file size after the operation may be larger than the requested
** size).
*/
- if( pFile->szChunk ){
+ if( pFile->szChunk>0 ){
nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
}
/* SetEndOfFile() returns non-zero when successful, or zero when it fails. */
if( seekWinFile(pFile, nByte) ){
- rc = SQLITE_IOERR_TRUNCATE;
+ rc = winLogError(SQLITE_IOERR_TRUNCATE, "winTruncate1", pFile->zPath);
}else if( 0==SetEndOfFile(pFile->h) ){
pFile->lastErrno = GetLastError();
- rc = SQLITE_IOERR_TRUNCATE;
+ rc = winLogError(SQLITE_IOERR_TRUNCATE, "winTruncate2", pFile->zPath);
}
OSTRACE(("TRUNCATE %d %lld %s\n", pFile->h, nByte, rc ? "failed" : "ok"));
** Make sure all writes to a particular file are committed to disk.
*/
static int winSync(sqlite3_file *id, int flags){
-#if !defined(NDEBUG) || !defined(SQLITE_NO_SYNC) || defined(SQLITE_DEBUG)
+#ifndef SQLITE_NO_SYNC
+ /*
+ ** Used only when SQLITE_NO_SYNC is not defined.
+ */
+ BOOL rc;
+#endif
+#if !defined(NDEBUG) || !defined(SQLITE_NO_SYNC) || \
+ (defined(SQLITE_TEST) && defined(SQLITE_DEBUG))
+ /*
+ ** Used when SQLITE_NO_SYNC is not defined and by the assert() and/or
+ ** OSTRACE() macros.
+ */
winFile *pFile = (winFile*)id;
#else
UNUSED_PARAMETER(id);
OSTRACE(("SYNC %d lock=%d\n", pFile->h, pFile->locktype));
+ /* Unix cannot, but some systems may return SQLITE_FULL from here. This
+ ** line is to test that doing so does not cause any problems.
+ */
+ SimulateDiskfullError( return SQLITE_FULL );
+
#ifndef SQLITE_TEST
UNUSED_PARAMETER(flags);
#else
- if( flags & SQLITE_SYNC_FULL ){
+ if( (flags&0x0F)==SQLITE_SYNC_FULL ){
sqlite3_fullsync_count++;
}
sqlite3_sync_count++;
#endif
- /* Unix cannot, but some systems may return SQLITE_FULL from here. This
- ** line is to test that doing so does not cause any problems.
- */
- SimulateDiskfullError( return SQLITE_FULL );
- SimulateIOError( return SQLITE_IOERR; );
-
/* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a
** no-op
*/
#ifdef SQLITE_NO_SYNC
return SQLITE_OK;
#else
- if( FlushFileBuffers(pFile->h) ){
+ rc = FlushFileBuffers(pFile->h);
+ SimulateIOError( rc=FALSE );
+ if( rc ){
return SQLITE_OK;
}else{
pFile->lastErrno = GetLastError();
- return SQLITE_IOERR;
+ return winLogError(SQLITE_IOERR_FSYNC, "winSync", pFile->zPath);
}
#endif
}
&& ((error = GetLastError()) != NO_ERROR) )
{
pFile->lastErrno = error;
- return SQLITE_IOERR_FSTAT;
+ return winLogError(SQLITE_IOERR_FSTAT, "winFileSize", pFile->zPath);
}
*pSize = (((sqlite3_int64)upperBits)<<32) + lowerBits;
return SQLITE_OK;
}
if( res == 0 ){
pFile->lastErrno = GetLastError();
+ /* No need to log a failure to lock */
}
return res;
}
res = UnlockFile(pFile->h, SHARED_FIRST + pFile->sharedLockByte, 0, 1, 0);
#endif
}
- if( res == 0 ){
+ if( res==0 && GetLastError()!=ERROR_NOT_LOCKED ){
pFile->lastErrno = GetLastError();
+ winLogError(SQLITE_IOERR_UNLOCK, "unlockReadLock", pFile->zPath);
}
return res;
}
if( locktype==SHARED_LOCK && !getReadLock(pFile) ){
/* This should never happen. We should always be able to
** reacquire the read lock */
- rc = SQLITE_IOERR_UNLOCK;
+ rc = winLogError(SQLITE_IOERR_UNLOCK, "winUnlock", pFile->zPath);
}
}
if( type>=RESERVED_LOCK ){
** Control and query of the open file handle.
*/
static int winFileControl(sqlite3_file *id, int op, void *pArg){
+ winFile *pFile = (winFile*)id;
switch( op ){
case SQLITE_FCNTL_LOCKSTATE: {
- *(int*)pArg = ((winFile*)id)->locktype;
+ *(int*)pArg = pFile->locktype;
return SQLITE_OK;
}
case SQLITE_LAST_ERRNO: {
- *(int*)pArg = (int)((winFile*)id)->lastErrno;
+ *(int*)pArg = (int)pFile->lastErrno;
return SQLITE_OK;
}
case SQLITE_FCNTL_CHUNK_SIZE: {
- ((winFile*)id)->szChunk = *(int *)pArg;
+ pFile->szChunk = *(int *)pArg;
return SQLITE_OK;
}
case SQLITE_FCNTL_SIZE_HINT: {
- sqlite3_int64 sz = *(sqlite3_int64*)pArg;
- SimulateIOErrorBenign(1);
- winTruncate(id, sz);
- SimulateIOErrorBenign(0);
+ if( pFile->szChunk>0 ){
+ sqlite3_int64 oldSz;
+ int rc = winFileSize(id, &oldSz);
+ if( rc==SQLITE_OK ){
+ sqlite3_int64 newSz = *(sqlite3_int64*)pArg;
+ if( newSz>oldSz ){
+ SimulateIOErrorBenign(1);
+ rc = winTruncate(id, newSz);
+ SimulateIOErrorBenign(0);
+ }
+ }
+ return rc;
+ }
+ return SQLITE_OK;
+ }
+ case SQLITE_FCNTL_PERSIST_WAL: {
+ int bPersist = *(int*)pArg;
+ if( bPersist<0 ){
+ *(int*)pArg = pFile->bPersistWal;
+ }else{
+ pFile->bPersistWal = bPersist!=0;
+ }
return SQLITE_OK;
}
case SQLITE_FCNTL_SYNC_OMITTED: {
return SQLITE_OK;
}
+ case SQLITE_FCNTL_WIN32_AV_RETRY: {
+ int *a = (int*)pArg;
+ if( a[0]>0 ){
+ win32IoerrRetry = a[0];
+ }else{
+ a[0] = win32IoerrRetry;
+ }
+ if( a[1]>0 ){
+ win32IoerrRetryDelay = a[1];
+ }else{
+ a[1] = win32IoerrRetryDelay;
+ }
+ return SQLITE_OK;
+ }
}
return SQLITE_NOTFOUND;
}
memset(pNew, 0, sizeof(*pNew));
pNew->zFilename = (char*)&pNew[1];
sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath);
+ sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename);
/* Look to see if there is an existing winShmNode that can be used.
** If no matching winShmNode currently exists, create a new one.
if( winShmSystemLock(pShmNode, _SHM_WRLCK, WIN_SHM_DMS, 1)==SQLITE_OK ){
rc = winTruncate((sqlite3_file *)&pShmNode->hFile, 0);
if( rc!=SQLITE_OK ){
- rc = SQLITE_IOERR_SHMOPEN;
+ rc = winLogError(SQLITE_IOERR_SHMOPEN, "winOpenShm", pDbFd->zPath);
}
}
if( rc==SQLITE_OK ){
*/
rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz);
if( rc!=SQLITE_OK ){
- rc = SQLITE_IOERR_SHMSIZE;
+ rc = winLogError(SQLITE_IOERR_SHMSIZE, "winShmMap1", pDbFd->zPath);
goto shmpage_out;
}
if( !isWrite ) goto shmpage_out;
rc = winTruncate((sqlite3_file *)&pShmNode->hFile, nByte);
if( rc!=SQLITE_OK ){
- rc = SQLITE_IOERR_SHMSIZE;
+ rc = winLogError(SQLITE_IOERR_SHMSIZE, "winShmMap2", pDbFd->zPath);
goto shmpage_out;
}
}
}
if( !pMap ){
pShmNode->lastErrno = GetLastError();
- rc = SQLITE_IOERR;
+ rc = winLogError(SQLITE_IOERR_SHMMAP, "winShmMap3", pDbFd->zPath);
if( hMap ) CloseHandle(hMap);
goto shmpage_out;
}
*/
#if SQLITE_OS_WINCE==0
}else{
- zConverted = utf8ToMbcs(zFilename);
+ zConverted = sqlite3_win32_utf8_to_mbcs(zFilename);
#endif
}
/* caller will handle out of memory */
}
/*
-** The return value of getLastErrorMsg
-** is zero if the error message fits in the buffer, or non-zero
-** otherwise (if the message was truncated).
-*/
-static int getLastErrorMsg(int nBuf, char *zBuf){
- /* FormatMessage returns 0 on failure. Otherwise it
- ** returns the number of TCHARs written to the output
- ** buffer, excluding the terminating null char.
- */
- DWORD error = GetLastError();
- DWORD dwLen = 0;
- char *zOut = 0;
-
- if( isNT() ){
- WCHAR *zTempWide = NULL;
- dwLen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL,
- error,
- 0,
- (LPWSTR) &zTempWide,
- 0,
- 0);
- if( dwLen > 0 ){
- /* allocate a buffer and convert to UTF8 */
- zOut = unicodeToUtf8(zTempWide);
- /* free the system buffer allocated by FormatMessage */
- LocalFree(zTempWide);
- }
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-** Since the ASCII version of these Windows API do not exist for WINCE,
-** it's important to not reference them for WINCE builds.
-*/
-#if SQLITE_OS_WINCE==0
- }else{
- char *zTemp = NULL;
- dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL,
- error,
- 0,
- (LPSTR) &zTemp,
- 0,
- 0);
- if( dwLen > 0 ){
- /* allocate a buffer and convert to UTF8 */
- zOut = sqlite3_win32_mbcs_to_utf8(zTemp);
- /* free the system buffer allocated by FormatMessage */
- LocalFree(zTemp);
- }
-#endif
- }
- if( 0 == dwLen ){
- sqlite3_snprintf(nBuf, zBuf, "OsError 0x%x (%u)", error, error);
- }else{
- /* copy a maximum of nBuf chars to output buffer */
- sqlite3_snprintf(nBuf, zBuf, "%s", zOut);
- /* free the UTF8 buffer */
- free(zOut);
- }
- return 0;
-}
-
-/*
** Open a file.
*/
static int winOpen(
winFile *pFile = (winFile*)id;
void *zConverted; /* Filename in OS encoding */
const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
+ int cnt = 0;
/* If argument zPath is a NULL pointer, this function is required to open
** a temporary file. Use this buffer to store the file name in.
#endif
if( isNT() ){
- h = CreateFileW((WCHAR*)zConverted,
- dwDesiredAccess,
- dwShareMode,
- NULL,
- dwCreationDisposition,
- dwFlagsAndAttributes,
- NULL
- );
+ while( (h = CreateFileW((WCHAR*)zConverted,
+ dwDesiredAccess,
+ dwShareMode, NULL,
+ dwCreationDisposition,
+ dwFlagsAndAttributes,
+ NULL))==INVALID_HANDLE_VALUE &&
+ retryIoerr(&cnt) ){}
/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
** Since the ASCII version of these Windows API do not exist for WINCE,
** it's important to not reference them for WINCE builds.
*/
#if SQLITE_OS_WINCE==0
}else{
- h = CreateFileA((char*)zConverted,
- dwDesiredAccess,
- dwShareMode,
- NULL,
- dwCreationDisposition,
- dwFlagsAndAttributes,
- NULL
- );
+ while( (h = CreateFileA((char*)zConverted,
+ dwDesiredAccess,
+ dwShareMode, NULL,
+ dwCreationDisposition,
+ dwFlagsAndAttributes,
+ NULL))==INVALID_HANDLE_VALUE &&
+ retryIoerr(&cnt) ){}
#endif
}
+ logIoerr(cnt);
+
OSTRACE(("OPEN %d %s 0x%lx %s\n",
h, zName, dwDesiredAccess,
h==INVALID_HANDLE_VALUE ? "failed" : "ok"));
if( h==INVALID_HANDLE_VALUE ){
pFile->lastErrno = GetLastError();
+ winLogError(SQLITE_CANTOPEN, "winOpen", zUtf8Name);
free(zConverted);
if( isReadWrite ){
return winOpen(pVfs, zName, id,
** to MX_DELETION_ATTEMPTs deletion attempts are run before giving
** up and returning an error.
*/
-#define MX_DELETION_ATTEMPTS 5
static int winDelete(
sqlite3_vfs *pVfs, /* Not used on win32 */
const char *zFilename, /* Name of file to delete */
int syncDir /* Not used on win32 */
){
int cnt = 0;
- DWORD rc;
- DWORD error = 0;
+ int rc;
void *zConverted;
UNUSED_PARAMETER(pVfs);
UNUSED_PARAMETER(syncDir);
return SQLITE_NOMEM;
}
if( isNT() ){
- do{
- DeleteFileW(zConverted);
- }while( ( ((rc = GetFileAttributesW(zConverted)) != INVALID_FILE_ATTRIBUTES)
- || ((error = GetLastError()) == ERROR_ACCESS_DENIED))
- && (++cnt < MX_DELETION_ATTEMPTS)
- && (Sleep(100), 1) );
+ rc = 1;
+ while( GetFileAttributesW(zConverted)!=INVALID_FILE_ATTRIBUTES &&
+ (rc = DeleteFileW(zConverted))==0 && retryIoerr(&cnt) ){}
+ rc = rc ? SQLITE_OK : SQLITE_ERROR;
/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
** Since the ASCII version of these Windows API do not exist for WINCE,
** it's important to not reference them for WINCE builds.
*/
#if SQLITE_OS_WINCE==0
}else{
- do{
- DeleteFileA(zConverted);
- }while( ( ((rc = GetFileAttributesA(zConverted)) != INVALID_FILE_ATTRIBUTES)
- || ((error = GetLastError()) == ERROR_ACCESS_DENIED))
- && (++cnt < MX_DELETION_ATTEMPTS)
- && (Sleep(100), 1) );
+ rc = 1;
+ while( GetFileAttributesA(zConverted)!=INVALID_FILE_ATTRIBUTES &&
+ (rc = DeleteFileA(zConverted))==0 && retryIoerr(&cnt) ){}
+ rc = rc ? SQLITE_OK : SQLITE_ERROR;
#endif
}
+ if( rc ){
+ rc = winLogError(SQLITE_IOERR_DELETE, "winDelete", zFilename);
+ }else{
+ logIoerr(cnt);
+ }
free(zConverted);
- OSTRACE(("DELETE \"%s\" %s\n", zFilename,
- ( (rc==INVALID_FILE_ATTRIBUTES) && (error==ERROR_FILE_NOT_FOUND)) ?
- "ok" : "failed" ));
-
- return ( (rc == INVALID_FILE_ATTRIBUTES)
- && (error == ERROR_FILE_NOT_FOUND)) ? SQLITE_OK : SQLITE_IOERR_DELETE;
+ OSTRACE(("DELETE \"%s\" %s\n", zFilename, (rc ? "failed" : "ok" )));
+ return rc;
}
/*
return SQLITE_NOMEM;
}
if( isNT() ){
+ int cnt = 0;
WIN32_FILE_ATTRIBUTE_DATA sAttrData;
memset(&sAttrData, 0, sizeof(sAttrData));
- if( GetFileAttributesExW((WCHAR*)zConverted,
+ while( !(rc = GetFileAttributesExW((WCHAR*)zConverted,
GetFileExInfoStandard,
- &sAttrData) ){
+ &sAttrData)) && retryIoerr(&cnt) ){}
+ if( rc ){
/* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file
** as if it does not exist.
*/
attr = sAttrData.dwFileAttributes;
}
}else{
+ logIoerr(cnt);
if( GetLastError()!=ERROR_FILE_NOT_FOUND ){
+ winLogError(SQLITE_IOERR_ACCESS, "winAccess", zFilename);
free(zConverted);
return SQLITE_IOERR_ACCESS;
}else{
rc = attr!=INVALID_FILE_ATTRIBUTES;
break;
case SQLITE_ACCESS_READWRITE:
- rc = (attr & FILE_ATTRIBUTE_READONLY)==0;
+ rc = attr!=INVALID_FILE_ATTRIBUTES &&
+ (attr & FILE_ATTRIBUTE_READONLY)==0;
break;
default:
assert(!"Invalid flags argument");
void *zConverted;
char *zOut;
+ /* If this path name begins with "/X:", where "X" is any alphabetic
+ ** character, discard the initial "/" from the pathname.
+ */
+ if( zRelative[0]=='/' && sqlite3Isalpha(zRelative[1]) && zRelative[2]==':' ){
+ zRelative++;
+ }
+
/* It's odd to simulate an io-error here, but really this is just
** using the io-error infrastructure to test that SQLite handles this
** function failing. This function could fail if, for example, the
}
if( pPg ){
int rc;
+#ifdef SQLITE_LOG_CACHE_SPILL
+ sqlite3_log(SQLITE_FULL,
+ "spill page %d making room for %d - cache used: %d/%d",
+ pPg->pgno, pgno,
+ sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache),
+ pCache->nMax);
+#endif
rc = pCache->xStress(pCache->pStress, pPg);
if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){
return rc;
typedef struct PgFreeslot PgFreeslot;
typedef struct PGroup PGroup;
+typedef struct PGroupBlock PGroupBlock;
+typedef struct PGroupBlockList PGroupBlockList;
+
/* Each page cache (or PCache) belongs to a PGroup. A PGroup is a set
** of one or more PCaches that are able to recycle each others unpinned
** pages when they are under memory pressure. A PGroup is an instance of
int mxPinned; /* nMaxpage + 10 - nMinPage */
int nCurrentPage; /* Number of purgeable pages allocated */
PgHdr1 *pLruHead, *pLruTail; /* LRU list of unpinned pages */
+#ifdef SQLITE_PAGECACHE_BLOCKALLOC
+ int isBusy; /* Do not run ReleaseMemory() if true */
+ PGroupBlockList *pBlockList; /* List of block-lists for this group */
+#endif
+};
+
+/*
+** If SQLITE_PAGECACHE_BLOCKALLOC is defined when the library is built,
+** each PGroup structure has a linked list of the the following starting
+** at PGroup.pBlockList. There is one entry for each distinct page-size
+** currently used by members of the PGroup (i.e. 1024 bytes, 4096 bytes
+** etc.). Variable PGroupBlockList.nByte is set to the actual allocation
+** size requested by each pcache, which is the database page-size plus
+** the various header structures used by the pcache, pager and btree layers.
+** Usually around (pgsz+200) bytes.
+**
+** This size (pgsz+200) bytes is not allocated efficiently by some
+** implementations of malloc. In particular, some implementations are only
+** able to allocate blocks of memory chunks of 2^N bytes, where N is some
+** integer value. Since the page-size is a power of 2, this means we
+** end up wasting (pgsz-200) bytes in each allocation.
+**
+** If SQLITE_PAGECACHE_BLOCKALLOC is defined, the (pgsz+200) byte blocks
+** are not allocated directly. Instead, blocks of roughly M*(pgsz+200) bytes
+** are requested from malloc allocator. After a block is returned,
+** sqlite3MallocSize() is used to determine how many (pgsz+200) byte
+** allocations can fit in the space returned by malloc(). This value may
+** be more than M.
+**
+** The blocks are stored in a doubly-linked list. Variable PGroupBlock.nEntry
+** contains the number of allocations that will fit in the aData[] space.
+** nEntry is limited to the number of bits in bitmask mUsed. If a slot
+** within aData is in use, the corresponding bit in mUsed is set. Thus
+** when (mUsed+1==(1 << nEntry)) the block is completely full.
+**
+** Each time a slot within a block is freed, the block is moved to the start
+** of the linked-list. And if a block becomes completely full, then it is
+** moved to the end of the list. As a result, when searching for a free
+** slot, only the first block in the list need be examined. If it is full,
+** then it is guaranteed that all blocks are full.
+*/
+struct PGroupBlockList {
+ int nByte; /* Size of each allocation in bytes */
+ PGroupBlock *pFirst; /* First PGroupBlock in list */
+ PGroupBlock *pLast; /* Last PGroupBlock in list */
+ PGroupBlockList *pNext; /* Next block-list attached to group */
};
+struct PGroupBlock {
+ Bitmask mUsed; /* Mask of used slots */
+ int nEntry; /* Maximum number of allocations in aData[] */
+ u8 *aData; /* Pointer to data block */
+ PGroupBlock *pNext; /* Next PGroupBlock in list */
+ PGroupBlock *pPrev; /* Previous PGroupBlock in list */
+ PGroupBlockList *pList; /* Owner list */
+};
+
+/* Minimum value for PGroupBlock.nEntry */
+#define PAGECACHE_BLOCKALLOC_MINENTRY 15
+
/* Each page cache is an instance of the following object. Every
** open database file (including each in-memory database and each
** temporary or transient database) has a single page cache which
#define PAGE_TO_PGHDR1(c, p) (PgHdr1*)(((char*)p) + c->szPage)
/*
+** Blocks used by the SQLITE_PAGECACHE_BLOCKALLOC blocks to store/retrieve
+** a PGroupBlock pointer based on a pointer to a page buffer.
+*/
+#define PAGE_SET_BLOCKPTR(pCache, pPg, pBlock) \
+ ( *(PGroupBlock **)&(((u8*)pPg)[sizeof(PgHdr1) + pCache->szPage]) = pBlock )
+
+#define PAGE_GET_BLOCKPTR(pCache, pPg) \
+ ( *(PGroupBlock **)&(((u8*)pPg)[sizeof(PgHdr1) + pCache->szPage]) )
+
+
+/*
** Macros to enter and leave the PCache LRU mutex.
*/
#define pcache1EnterMutex(X) sqlite3_mutex_enter((X)->mutex)
}
#endif /* SQLITE_ENABLE_MEMORY_MANAGEMENT */
+#ifdef SQLITE_PAGECACHE_BLOCKALLOC
+/*
+** The block pBlock belongs to list pList but is not currently linked in.
+** Insert it into the start of the list.
+*/
+static void addBlockToList(PGroupBlockList *pList, PGroupBlock *pBlock){
+ pBlock->pPrev = 0;
+ pBlock->pNext = pList->pFirst;
+ pList->pFirst = pBlock;
+ if( pBlock->pNext ){
+ pBlock->pNext->pPrev = pBlock;
+ }else{
+ assert( pList->pLast==0 );
+ pList->pLast = pBlock;
+ }
+}
+
+/*
+** If there are no blocks in the list headed by pList, remove pList
+** from the pGroup->pBlockList list and free it with sqlite3_free().
+*/
+static void freeListIfEmpty(PGroup *pGroup, PGroupBlockList *pList){
+ assert( sqlite3_mutex_held(pGroup->mutex) );
+ if( pList->pFirst==0 ){
+ PGroupBlockList **pp;
+ for(pp=&pGroup->pBlockList; *pp!=pList; pp=&(*pp)->pNext);
+ *pp = (*pp)->pNext;
+ sqlite3_free(pList);
+ }
+}
+#endif /* SQLITE_PAGECACHE_BLOCKALLOC */
+
/*
** Allocate a new page object initially associated with cache pCache.
*/
static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
int nByte = sizeof(PgHdr1) + pCache->szPage;
- void *pPg = pcache1Alloc(nByte);
+ void *pPg = 0;
PgHdr1 *p;
+
+#ifdef SQLITE_PAGECACHE_BLOCKALLOC
+ PGroup *pGroup = pCache->pGroup;
+ PGroupBlockList *pList;
+ PGroupBlock *pBlock;
+ int i;
+
+ nByte += sizeof(PGroupBlockList *);
+ nByte = ROUND8(nByte);
+
+ for(pList=pGroup->pBlockList; pList; pList=pList->pNext){
+ if( pList->nByte==nByte ) break;
+ }
+ if( pList==0 ){
+ PGroupBlockList *pNew;
+ assert( pGroup->isBusy==0 );
+ assert( sqlite3_mutex_held(pGroup->mutex) );
+ pGroup->isBusy = 1; /* Disable sqlite3PcacheReleaseMemory() */
+ pNew = (PGroupBlockList *)sqlite3MallocZero(sizeof(PGroupBlockList));
+ pGroup->isBusy = 0; /* Reenable sqlite3PcacheReleaseMemory() */
+ if( pNew==0 ){
+ /* malloc() failure. Return early. */
+ return 0;
+ }
+#ifdef SQLITE_DEBUG
+ for(pList=pGroup->pBlockList; pList; pList=pList->pNext){
+ assert( pList->nByte!=nByte );
+ }
+#endif
+ pNew->nByte = nByte;
+ pNew->pNext = pGroup->pBlockList;
+ pGroup->pBlockList = pNew;
+ pList = pNew;
+ }
+
+ pBlock = pList->pFirst;
+ if( pBlock==0 || pBlock->mUsed==(((Bitmask)1<<pBlock->nEntry)-1) ){
+ int sz;
+
+ /* Allocate a new block. Try to allocate enough space for the PGroupBlock
+ ** structure and MINENTRY allocations of nByte bytes each. If the
+ ** allocator returns more memory than requested, then more than MINENTRY
+ ** allocations may fit in it. */
+ assert( sqlite3_mutex_held(pGroup->mutex) );
+ pcache1LeaveMutex(pCache->pGroup);
+ sz = sizeof(PGroupBlock) + PAGECACHE_BLOCKALLOC_MINENTRY * nByte;
+ pBlock = (PGroupBlock *)sqlite3Malloc(sz);
+ pcache1EnterMutex(pCache->pGroup);
+
+ if( !pBlock ){
+ freeListIfEmpty(pGroup, pList);
+ return 0;
+ }
+ pBlock->nEntry = (sqlite3MallocSize(pBlock) - sizeof(PGroupBlock)) / nByte;
+ if( pBlock->nEntry>=BMS ){
+ pBlock->nEntry = BMS-1;
+ }
+ pBlock->pList = pList;
+ pBlock->mUsed = 0;
+ pBlock->aData = (u8 *)&pBlock[1];
+ addBlockToList(pList, pBlock);
+
+ sz = sqlite3MallocSize(pBlock);
+ sqlite3_mutex_enter(pcache1.mutex);
+ sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz);
+ sqlite3_mutex_leave(pcache1.mutex);
+ }
+
+ for(i=0; pPg==0 && ALWAYS(i<pBlock->nEntry); i++){
+ if( 0==(pBlock->mUsed & ((Bitmask)1<<i)) ){
+ pBlock->mUsed |= ((Bitmask)1<<i);
+ pPg = (void *)&pBlock->aData[pList->nByte * i];
+ }
+ }
+ assert( pPg );
+ PAGE_SET_BLOCKPTR(pCache, pPg, pBlock);
+
+ /* If the block is now full, shift it to the end of the list */
+ if( pBlock->mUsed==(((Bitmask)1<<pBlock->nEntry)-1) && pList->pLast!=pBlock ){
+ assert( pList->pFirst==pBlock );
+ assert( pBlock->pPrev==0 );
+ assert( pList->pLast->pNext==0 );
+ pList->pFirst = pBlock->pNext;
+ pList->pFirst->pPrev = 0;
+ pBlock->pPrev = pList->pLast;
+ pBlock->pNext = 0;
+ pList->pLast->pNext = pBlock;
+ pList->pLast = pBlock;
+ }
+ p = PAGE_TO_PGHDR1(pCache, pPg);
+ if( pCache->bPurgeable ){
+ pCache->pGroup->nCurrentPage++;
+ }
+#else
+ /* The group mutex must be released before pcache1Alloc() is called. This
+ ** is because it may call sqlite3_release_memory(), which assumes that
+ ** this mutex is not held. */
+ assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
+ pcache1LeaveMutex(pCache->pGroup);
+ pPg = pcache1Alloc(nByte);
+ pcache1EnterMutex(pCache->pGroup);
if( pPg ){
p = PAGE_TO_PGHDR1(pCache, pPg);
if( pCache->bPurgeable ){
}else{
p = 0;
}
+#endif
return p;
}
static void pcache1FreePage(PgHdr1 *p){
if( ALWAYS(p) ){
PCache1 *pCache = p->pCache;
+ void *pPg = PGHDR1_TO_PAGE(p);
+
+#ifdef SQLITE_PAGECACHE_BLOCKALLOC
+ PGroupBlock *pBlock = PAGE_GET_BLOCKPTR(pCache, pPg);
+ PGroupBlockList *pList = pBlock->pList;
+ int i = ((u8 *)pPg - pBlock->aData) / pList->nByte;
+
+ assert( pPg==(void *)&pBlock->aData[i*pList->nByte] );
+ assert( pBlock->mUsed & ((Bitmask)1<<i) );
+ pBlock->mUsed &= ~((Bitmask)1<<i);
+
+ /* Remove the block from the list. If it is completely empty, free it.
+ ** Or if it is not completely empty, re-insert it at the start of the
+ ** list. */
+ if( pList->pFirst==pBlock ){
+ pList->pFirst = pBlock->pNext;
+ if( pList->pFirst ) pList->pFirst->pPrev = 0;
+ }else{
+ pBlock->pPrev->pNext = pBlock->pNext;
+ }
+ if( pList->pLast==pBlock ){
+ pList->pLast = pBlock->pPrev;
+ if( pList->pLast ) pList->pLast->pNext = 0;
+ }else{
+ pBlock->pNext->pPrev = pBlock->pPrev;
+ }
+
+ if( pBlock->mUsed==0 ){
+ PGroup *pGroup = p->pCache->pGroup;
+
+ int sz = sqlite3MallocSize(pBlock);
+ sqlite3_mutex_enter(pcache1.mutex);
+ sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -sz);
+ sqlite3_mutex_leave(pcache1.mutex);
+ freeListIfEmpty(pGroup, pList);
+ sqlite3_free(pBlock);
+ }else{
+ addBlockToList(pList, pBlock);
+ }
+#else
+ assert( sqlite3_mutex_held(p->pCache->pGroup->mutex) );
+ pcache1Free(pPg);
+#endif
if( pCache->bPurgeable ){
pCache->pGroup->nCurrentPage--;
}
- pcache1Free(PGHDR1_TO_PAGE(p));
}
}
pGroup = (PGroup*)&pCache[1];
pGroup->mxPinned = 10;
}else{
- pGroup = &pcache1_g.grp;
+ pGroup = &pcache1.grp;
}
pCache->pGroup = pGroup;
pCache->szPage = szPage;
*/
if( !pPage ){
if( createFlag==1 ) sqlite3BeginBenignMalloc();
- pcache1LeaveMutex(pGroup);
pPage = pcache1AllocPage(pCache);
- pcache1EnterMutex(pGroup);
if( createFlag==1 ) sqlite3EndBenignMalloc();
}
*/
SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int nReq){
int nFree = 0;
+#ifdef SQLITE_PAGECACHE_BLOCKALLOC
+ if( pcache1.grp.isBusy ) return 0;
+#endif
assert( sqlite3_mutex_notheld(pcache1.grp.mutex) );
assert( sqlite3_mutex_notheld(pcache1.mutex) );
if( pcache1.pStart==0 ){
#ifdef SQLITE_OMIT_WAL
# define sqlite3WalOpen(x,y,z) 0
+# define sqlite3WalLimit(x,y)
# define sqlite3WalClose(w,x,y,z) 0
# define sqlite3WalBeginReadTransaction(y,z) 0
# define sqlite3WalEndReadTransaction(z)
typedef struct Wal Wal;
/* Open and close a connection to a write-ahead log. */
-SQLITE_PRIVATE int sqlite3WalOpen(sqlite3_vfs*, sqlite3_file*, const char *zName, int, Wal**);
+SQLITE_PRIVATE int sqlite3WalOpen(sqlite3_vfs*, sqlite3_file*, const char *, int, i64, Wal**);
SQLITE_PRIVATE int sqlite3WalClose(Wal *pWal, int sync_flags, int, u8 *);
+/* Set the limiting size of a WAL file. */
+SQLITE_PRIVATE void sqlite3WalLimit(Wal*, i64);
+
/* Used by readers to open (lock) and close (unlock) a snapshot. A
** snapshot is like a read-transaction. It is the state of the database
** at an instant in time. sqlite3WalOpenSnapshot gets a read lock and
SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager){
u8 *pTmp = (u8 *)pPager->pTmpSpace;
+ assert( assert_pager_state(pPager) );
disable_simulated_io_errors();
sqlite3BeginBenignMalloc();
/* pPager->errCode = 0; */
int noReadlock = (flags & PAGER_NO_READLOCK)!=0; /* True to omit read-lock */
int pcacheSize = sqlite3PcacheSize(); /* Bytes to allocate for PCache */
u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */
+ const char *zUri = 0; /* URI args to copy */
+ int nUri = 0; /* Number of bytes of URI args at *zUri */
/* Figure out how much space is required for each journal file-handle
** (there are two of them, the main journal and the sub-journal). This
** leave both nPathname and zPathname set to 0.
*/
if( zFilename && zFilename[0] ){
+ const char *z;
nPathname = pVfs->mxPathname+1;
zPathname = sqlite3Malloc(nPathname*2);
if( zPathname==0 ){
zPathname[0] = 0; /* Make sure initialized even if FullPathname() fails */
rc = sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname);
nPathname = sqlite3Strlen30(zPathname);
+ z = zUri = &zFilename[sqlite3Strlen30(zFilename)+1];
+ while( *z ){
+ z += sqlite3Strlen30(z)+1;
+ z += sqlite3Strlen30(z)+1;
+ }
+ nUri = &z[1] - zUri;
if( rc==SQLITE_OK && nPathname+8>pVfs->mxPathname ){
/* This branch is taken when the journal path required by
** the database being opened will be more than pVfs->mxPathname
ROUND8(pcacheSize) + /* PCache object */
ROUND8(pVfs->szOsFile) + /* The main db file */
journalFileSize * 2 + /* The two journal files */
- nPathname + 1 + /* zFilename */
+ nPathname + 1 + nUri + /* zFilename */
nPathname + 8 + 1 /* zJournal */
#ifndef SQLITE_OMIT_WAL
+ nPathname + 4 + 1 /* zWal */
/* Fill in the Pager.zFilename and Pager.zJournal buffers, if required. */
if( zPathname ){
assert( nPathname>0 );
- pPager->zJournal = (char*)(pPtr += nPathname + 1);
+ pPager->zJournal = (char*)(pPtr += nPathname + 1 + nUri);
memcpy(pPager->zFilename, zPathname, nPathname);
+ memcpy(&pPager->zFilename[nPathname+1], zUri, nUri);
memcpy(pPager->zJournal, zPathname, nPathname);
memcpy(&pPager->zJournal[nPathname], "-journal", 8);
+ sqlite3FileSuffix3(pPager->zFilename, pPager->zJournal);
#ifndef SQLITE_OMIT_WAL
pPager->zWal = &pPager->zJournal[nPathname+8+1];
memcpy(pPager->zWal, zPathname, nPathname);
memcpy(&pPager->zWal[nPathname], "-wal", 4);
+ sqlite3FileSuffix3(pPager->zFilename, pPager->zWal);
#endif
sqlite3_free(zPathname);
}
pList = pPageOne;
pList->pDirty = 0;
}
- assert( pList!=0 || rc!=SQLITE_OK );
- if( pList ){
+ assert( rc==SQLITE_OK );
+ if( ALWAYS(pList) ){
rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1,
(pPager->fullSync ? pPager->syncFlags : 0)
);
SQLITE_PRIVATE i64 sqlite3PagerJournalSizeLimit(Pager *pPager, i64 iLimit){
if( iLimit>=-1 ){
pPager->journalSizeLimit = iLimit;
+ sqlite3WalLimit(pPager->pWal, iLimit);
}
return pPager->journalSizeLimit;
}
*/
if( rc==SQLITE_OK ){
rc = sqlite3WalOpen(pPager->pVfs,
- pPager->fd, pPager->zWal, pPager->exclusiveMode, &pPager->pWal
+ pPager->fd, pPager->zWal, pPager->exclusiveMode,
+ pPager->journalSizeLimit, &pPager->pWal
);
}
sqlite3_file *pDbFd; /* File handle for the database file */
sqlite3_file *pWalFd; /* File handle for WAL file */
u32 iCallback; /* Value to pass to log callback (or 0) */
+ i64 mxWalSize; /* Truncate WAL to this size upon reset */
int nWiData; /* Size of array apWiData */
volatile u32 **apWiData; /* Pointer to wal-index content in memory */
u32 szPage; /* Database page size */
u8 exclusiveMode; /* Non-zero if connection is in exclusive mode */
u8 writeLock; /* True if in a write transaction */
u8 ckptLock; /* True if holding a checkpoint lock */
- u8 readOnly; /* True if the WAL file is open read-only */
+ u8 readOnly; /* WAL_RDWR, WAL_RDONLY, or WAL_SHM_RDONLY */
WalIndexHdr hdr; /* Wal-index header for current transaction */
const char *zWalName; /* Name of WAL file */
u32 nCkpt; /* Checkpoint sequence counter in the wal-header */
#define WAL_HEAPMEMORY_MODE 2
/*
+** Possible values for WAL.readOnly
+*/
+#define WAL_RDWR 0 /* Normal read/write connection */
+#define WAL_RDONLY 1 /* The WAL file is readonly */
+#define WAL_SHM_RDONLY 2 /* The SHM file is readonly */
+
+/*
** Each page of the wal-index mapping contains a hash-table made up of
** an array of HASHTABLE_NSLOT elements of the following type.
*/
rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ,
pWal->writeLock, (void volatile **)&pWal->apWiData[iPage]
);
+ if( rc==SQLITE_READONLY ){
+ pWal->readOnly |= WAL_SHM_RDONLY;
+ rc = SQLITE_OK;
+ }
}
}
sqlite3_file *pDbFd, /* The open database file */
const char *zWalName, /* Name of the WAL file */
int bNoShm, /* True to run in heap-memory mode */
+ i64 mxWalSize, /* Truncate WAL to this size on reset */
Wal **ppWal /* OUT: Allocated Wal handle */
){
int rc; /* Return Code */
pRet->pWalFd = (sqlite3_file *)&pRet[1];
pRet->pDbFd = pDbFd;
pRet->readLock = -1;
+ pRet->mxWalSize = mxWalSize;
pRet->zWalName = zWalName;
pRet->exclusiveMode = (bNoShm ? WAL_HEAPMEMORY_MODE: WAL_NORMAL_MODE);
flags = (SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_WAL);
rc = sqlite3OsOpen(pVfs, zWalName, pRet->pWalFd, flags, &flags);
if( rc==SQLITE_OK && flags&SQLITE_OPEN_READONLY ){
- pRet->readOnly = 1;
+ pRet->readOnly = WAL_RDONLY;
}
if( rc!=SQLITE_OK ){
}
/*
+** Change the size to which the WAL file is trucated on each reset.
+*/
+SQLITE_PRIVATE void sqlite3WalLimit(Wal *pWal, i64 iLimit){
+ if( pWal ) pWal->mxWalSize = iLimit;
+}
+
+/*
** Find the smallest page number out of all pages held in the WAL that
** has not been returned by any prior invocation of this method on the
** same WalIterator object. Write into *piFrame the frame index where
*/
rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE);
if( rc==SQLITE_OK ){
+ int bPersistWal = -1;
if( pWal->exclusiveMode==WAL_NORMAL_MODE ){
pWal->exclusiveMode = WAL_EXCLUSIVE_MODE;
}
rc = sqlite3WalCheckpoint(
pWal, SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0
);
- if( rc==SQLITE_OK ){
+ sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_PERSIST_WAL, &bPersistWal);
+ if( rc==SQLITE_OK && bPersistWal!=1 ){
isDelete = 1;
}
}
** with a writer. So get a WRITE lock and try again.
*/
assert( badHdr==0 || pWal->writeLock==0 );
- if( badHdr && SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) ){
- pWal->writeLock = 1;
- if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){
- badHdr = walIndexTryHdr(pWal, pChanged);
- if( badHdr ){
- /* If the wal-index header is still malformed even while holding
- ** a WRITE lock, it can only mean that the header is corrupted and
- ** needs to be reconstructed. So run recovery to do exactly that.
- */
- rc = walIndexRecover(pWal);
- *pChanged = 1;
+ if( badHdr ){
+ if( pWal->readOnly & WAL_SHM_RDONLY ){
+ if( SQLITE_OK==(rc = walLockShared(pWal, WAL_WRITE_LOCK)) ){
+ walUnlockShared(pWal, WAL_WRITE_LOCK);
+ rc = SQLITE_READONLY_RECOVERY;
}
+ }else if( SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) ){
+ pWal->writeLock = 1;
+ if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){
+ badHdr = walIndexTryHdr(pWal, pChanged);
+ if( badHdr ){
+ /* If the wal-index header is still malformed even while holding
+ ** a WRITE lock, it can only mean that the header is corrupted and
+ ** needs to be reconstructed. So run recovery to do exactly that.
+ */
+ rc = walIndexRecover(pWal);
+ *pChanged = 1;
+ }
+ }
+ pWal->writeLock = 0;
+ walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
}
- pWal->writeLock = 0;
- walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
}
/* If the header is read successfully, check the version number to make
}
/* There was once an "if" here. The extra "{" is to preserve indentation. */
{
- if( mxReadMark < pWal->hdr.mxFrame || mxI==0 ){
+ if( (pWal->readOnly & WAL_SHM_RDONLY)==0
+ && (mxReadMark<pWal->hdr.mxFrame || mxI==0)
+ ){
for(i=1; i<WAL_NREADER; i++){
rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
if( rc==SQLITE_OK ){
}
}
if( mxI==0 ){
- assert( rc==SQLITE_BUSY );
- return WAL_RETRY;
+ assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 );
+ return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTLOCK;
}
rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
*/
int i; /* Loop counter */
u32 *aSalt = pWal->hdr.aSalt; /* Big-endian salt values */
+
+ /* Limit the size of WAL file if the journal_size_limit PRAGMA is
+ ** set to a non-negative value. Log errors encountered
+ ** during the truncation attempt. */
+ if( pWal->mxWalSize>=0 ){
+ i64 sz;
+ int rx;
+ sqlite3BeginBenignMalloc();
+ rx = sqlite3OsFileSize(pWal->pWalFd, &sz);
+ if( rx==SQLITE_OK && (sz > pWal->mxWalSize) ){
+ rx = sqlite3OsTruncate(pWal->pWalFd, pWal->mxWalSize);
+ }
+ sqlite3EndBenignMalloc();
+ if( rx ){
+ sqlite3_log(rx, "cannot limit WAL size: %s", pWal->zWalName);
+ }
+ }
+
pWal->nCkpt++;
pWal->hdr.mxFrame = 0;
sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0]));
assert( pWal->ckptLock==0 );
assert( pWal->writeLock==0 );
+ if( pWal->readOnly ) return SQLITE_READONLY;
WALTRACE(("WAL%p: checkpoint begins\n", pWal));
rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
if( rc ){
int rc; /* Status code */
UnpackedRecord *pIdxKey; /* Unpacked index key */
char aSpace[150]; /* Temp space for pIdxKey - to avoid a malloc */
+ char *pFree = 0;
if( pKey ){
assert( nKey==(i64)(int)nKey );
- pIdxKey = sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey,
- aSpace, sizeof(aSpace));
+ pIdxKey = sqlite3VdbeAllocUnpackedRecord(
+ pCur->pKeyInfo, aSpace, sizeof(aSpace), &pFree
+ );
if( pIdxKey==0 ) return SQLITE_NOMEM;
+ sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey, pIdxKey);
}else{
pIdxKey = 0;
}
rc = sqlite3BtreeMovetoUnpacked(pCur, pIdxKey, nKey, bias, pRes);
- if( pKey ){
- sqlite3VdbeDeleteUnpackedRecord(pIdxKey);
+ if( pFree ){
+ sqlite3DbFree(pCur->pKeyInfo->db, pFree);
}
return rc;
}
*pRC = SQLITE_CORRUPT_BKPT;
goto ptrmap_exit;
}
+ assert( offset <= (int)pBt->usableSize-5 );
pPtrmap = (u8 *)sqlite3PagerGetData(pDbPage);
if( eType!=pPtrmap[offset] || get4byte(&pPtrmap[offset+1])!=parent ){
pPtrmap = (u8 *)sqlite3PagerGetData(pDbPage);
offset = PTRMAP_PTROFFSET(iPtrmap, key);
+ if( offset<0 ){
+ sqlite3PagerUnref(pDbPage);
+ return SQLITE_CORRUPT_BKPT;
+ }
+ assert( offset <= (int)pBt->usableSize-5 );
assert( pEType!=0 );
*pEType = pPtrmap[offset];
if( pPgno ) *pPgno = get4byte(&pPtrmap[offset+1]);
*/
#define findCell(P,I) \
((P)->aData + ((P)->maskPage & get2byte(&(P)->aData[(P)->cellOffset+2*(I)])))
+#define findCellv2(D,M,O,I) (D+(M&get2byte(D+(O+2*(I)))))
+
/*
** This a more complex version of findCell() that works for
** to problems with locking.
*/
SQLITE_PRIVATE int sqlite3BtreeOpen(
+ sqlite3_vfs *pVfs, /* VFS to use for this b-tree */
const char *zFilename, /* Name of the file containing the BTree database */
sqlite3 *db, /* Associated database handle */
Btree **ppBtree, /* Pointer to new Btree object written here */
int flags, /* Options */
int vfsFlags /* Flags passed through to sqlite3_vfs.xOpen() */
){
- sqlite3_vfs *pVfs; /* The VFS to use for this btree */
BtShared *pBt = 0; /* Shared part of btree structure */
Btree *p; /* Handle to return */
sqlite3_mutex *mutexOpen = 0; /* Prevents a race condition. Ticket #3537 */
#endif
assert( db!=0 );
+ assert( pVfs!=0 );
assert( sqlite3_mutex_held(db->mutex) );
assert( (flags&0xff)==flags ); /* flags fit in 8 bits */
if( (vfsFlags & SQLITE_OPEN_MAIN_DB)!=0 && (isMemdb || isTempDb) ){
vfsFlags = (vfsFlags & ~SQLITE_OPEN_MAIN_DB) | SQLITE_OPEN_TEMP_DB;
}
- pVfs = db->pVfs;
p = sqlite3MallocZero(sizeof(Btree));
if( !p ){
return SQLITE_NOMEM;
if( eType==PTRMAP_OVERFLOW1 ){
CellInfo info;
btreeParseCellPtr(pPage, pCell, &info);
- if( info.iOverflow ){
- if( iFrom==get4byte(&pCell[info.iOverflow]) ){
- put4byte(&pCell[info.iOverflow], iTo);
- break;
- }
+ if( info.iOverflow
+ && pCell+info.iOverflow+3<=pPage->aData+pPage->maskPage
+ && iFrom==get4byte(&pCell[info.iOverflow])
+ ){
+ put4byte(&pCell[info.iOverflow], iTo);
+ break;
}
}else{
if( get4byte(pCell)==iFrom ){
return SQLITE_READONLY;
}
if( iTable==1 && btreePagecount(pBt)==0 ){
- return SQLITE_EMPTY;
+ assert( wrFlag==0 );
+ iTable = 0;
}
/* Now that no other errors can occur, finish filling in the BtCursor
releasePage(pCur->apPage[i]);
}
pCur->iPage = 0;
+ }else if( pCur->pgnoRoot==0 ){
+ pCur->eState = CURSOR_INVALID;
+ return SQLITE_OK;
}else{
rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->apPage[0]);
if( rc!=SQLITE_OK ){
rc = moveToRoot(pCur);
if( rc==SQLITE_OK ){
if( pCur->eState==CURSOR_INVALID ){
- assert( pCur->apPage[pCur->iPage]->nCell==0 );
+ assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->nCell==0 );
*pRes = 1;
}else{
assert( pCur->apPage[pCur->iPage]->nCell>0 );
rc = moveToRoot(pCur);
if( rc==SQLITE_OK ){
if( CURSOR_INVALID==pCur->eState ){
- assert( pCur->apPage[pCur->iPage]->nCell==0 );
+ assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->nCell==0 );
*pRes = 1;
}else{
assert( pCur->eState==CURSOR_VALID );
if( rc ){
return rc;
}
- assert( pCur->apPage[pCur->iPage] );
- assert( pCur->apPage[pCur->iPage]->isInit );
- assert( pCur->apPage[pCur->iPage]->nCell>0 || pCur->eState==CURSOR_INVALID );
+ assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage] );
+ assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->isInit );
+ assert( pCur->eState==CURSOR_INVALID || pCur->apPage[pCur->iPage]->nCell>0 );
if( pCur->eState==CURSOR_INVALID ){
*pRes = -1;
- assert( pCur->apPage[pCur->iPage]->nCell==0 );
+ assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->nCell==0 );
return SQLITE_OK;
}
assert( pCur->apPage[0]->intKey || pIdxKey );
for(;;){
- int lwr, upr;
+ int lwr, upr, idx;
Pgno chldPg;
MemPage *pPage = pCur->apPage[pCur->iPage];
int c;
lwr = 0;
upr = pPage->nCell-1;
if( biasRight ){
- pCur->aiIdx[pCur->iPage] = (u16)upr;
+ pCur->aiIdx[pCur->iPage] = (u16)(idx = upr);
}else{
- pCur->aiIdx[pCur->iPage] = (u16)((upr+lwr)/2);
+ pCur->aiIdx[pCur->iPage] = (u16)(idx = (upr+lwr)/2);
}
for(;;){
- int idx = pCur->aiIdx[pCur->iPage]; /* Index of current cell in pPage */
u8 *pCell; /* Pointer to current cell in pPage */
+ assert( idx==pCur->aiIdx[pCur->iPage] );
pCur->info.nSize = 0;
pCell = findCell(pPage, idx) + pPage->childPtrSize;
if( pPage->intKey ){
if( lwr>upr ){
break;
}
- pCur->aiIdx[pCur->iPage] = (u16)((lwr+upr)/2);
+ pCur->aiIdx[pCur->iPage] = (u16)(idx = (lwr+upr)/2);
}
assert( lwr==upr+1 );
assert( pPage->isInit );
if( info.iOverflow==0 ){
return SQLITE_OK; /* No overflow pages. Return without doing anything */
}
+ if( pCell+info.iOverflow+3 > pPage->aData+pPage->maskPage ){
+ return SQLITE_CORRUPT; /* Cell extends past end of page */
+ }
ovflPgno = get4byte(&pCell[info.iOverflow]);
assert( pBt->usableSize > 4 );
ovflPageSize = pBt->usableSize - 4;
** "sz" must be the number of bytes in the cell.
*/
static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
- int i; /* Loop counter */
u32 pc; /* Offset to cell content of cell being deleted */
u8 *data; /* pPage->aData */
u8 *ptr; /* Used to move bytes around within data[] */
+ u8 *endPtr; /* End of loop */
int rc; /* The return code */
int hdr; /* Beginning of the header. 0 most pages. 100 page 1 */
*pRC = rc;
return;
}
- for(i=idx+1; i<pPage->nCell; i++, ptr+=2){
- ptr[0] = ptr[2];
- ptr[1] = ptr[3];
+ endPtr = &data[pPage->cellOffset + 2*pPage->nCell - 2];
+ assert( (SQLITE_PTR_TO_INT(ptr)&1)==0 ); /* ptr is always 2-byte aligned */
+ while( ptr<endPtr ){
+ *(u16*)ptr = *(u16*)&ptr[2];
+ ptr += 2;
}
pPage->nCell--;
put2byte(&data[hdr+3], pPage->nCell);
int cellOffset; /* Address of first cell pointer in data[] */
u8 *data; /* The content of the whole page */
u8 *ptr; /* Used for moving information around in data[] */
+ u8 *endPtr; /* End of the loop */
int nSkip = (iChild ? 4 : 0);
if( iChild ){
put4byte(&data[idx], iChild);
}
- for(j=end, ptr=&data[j]; j>ins; j-=2, ptr-=2){
- ptr[0] = ptr[-2];
- ptr[1] = ptr[-1];
+ ptr = &data[end];
+ endPtr = &data[ins];
+ assert( (SQLITE_PTR_TO_INT(ptr)&1)==0 ); /* ptr is always 2-byte aligned */
+ while( ptr>endPtr ){
+ *(u16*)ptr = *(u16*)&ptr[-2];
+ ptr -= 2;
}
put2byte(&data[ins], idx);
put2byte(&data[pPage->hdrOffset+3], pPage->nCell);
pCellptr = &data[pPage->cellOffset + nCell*2];
cellbody = nUsable;
for(i=nCell-1; i>=0; i--){
+ u16 sz = aSize[i];
pCellptr -= 2;
- cellbody -= aSize[i];
+ cellbody -= sz;
put2byte(pCellptr, cellbody);
- memcpy(&data[cellbody], apCell[i], aSize[i]);
+ memcpy(&data[cellbody], apCell[i], sz);
}
put2byte(&data[hdr+3], nCell);
put2byte(&data[hdr+5], cellbody);
memcpy(pOld->aData, apOld[i]->aData, pBt->pageSize);
limit = pOld->nCell+pOld->nOverflow;
- for(j=0; j<limit; j++){
- assert( nCell<nMaxCells );
- apCell[nCell] = findOverflowCell(pOld, j);
- szCell[nCell] = cellSizePtr(pOld, apCell[nCell]);
- nCell++;
- }
+ if( pOld->nOverflow>0 ){
+ for(j=0; j<limit; j++){
+ assert( nCell<nMaxCells );
+ apCell[nCell] = findOverflowCell(pOld, j);
+ szCell[nCell] = cellSizePtr(pOld, apCell[nCell]);
+ nCell++;
+ }
+ }else{
+ u8 *aData = pOld->aData;
+ u16 maskPage = pOld->maskPage;
+ u16 cellOffset = pOld->cellOffset;
+ for(j=0; j<limit; j++){
+ assert( nCell<nMaxCells );
+ apCell[nCell] = findCellv2(aData, maskPage, cellOffset, j);
+ szCell[nCell] = cellSizePtr(pOld, apCell[nCell]);
+ nCell++;
+ }
+ }
if( i<nOld-1 && !leafData){
u16 sz = (u16)szNew[i];
u8 *pTemp;
SQLITE_PRIVATE int sqlite3BtreeCount(BtCursor *pCur, i64 *pnEntry){
i64 nEntry = 0; /* Value to return in *pnEntry */
int rc; /* Return code */
+
+ if( pCur->pgnoRoot==0 ){
+ *pnEntry = 0;
+ return SQLITE_OK;
+ }
rc = moveToRoot(pCur);
/* Unless an error occurs, the following loop runs one iteration for each
BtShared *pBt = pBtree->pBt;
int rc; /* Return code */
- assert( pBtree->inTrans==TRANS_NONE );
assert( iVersion==1 || iVersion==2 );
/* If setting the version fields to 1, do not automatically open the
** the case where the source and destination databases have the
** same schema version.
*/
- if( rc==SQLITE_DONE
- && (rc = sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1))==SQLITE_OK
- ){
- int nDestTruncate;
-
- if( p->pDestDb ){
- sqlite3ResetInternalSchema(p->pDestDb, -1);
- }
-
- /* Set nDestTruncate to the final number of pages in the destination
- ** database. The complication here is that the destination page
- ** size may be different to the source page size.
- **
- ** If the source page size is smaller than the destination page size,
- ** round up. In this case the call to sqlite3OsTruncate() below will
- ** fix the size of the file. However it is important to call
- ** sqlite3PagerTruncateImage() here so that any pages in the
- ** destination file that lie beyond the nDestTruncate page mark are
- ** journalled by PagerCommitPhaseOne() before they are destroyed
- ** by the file truncation.
- */
- assert( pgszSrc==sqlite3BtreeGetPageSize(p->pSrc) );
- assert( pgszDest==sqlite3BtreeGetPageSize(p->pDest) );
- if( pgszSrc<pgszDest ){
- int ratio = pgszDest/pgszSrc;
- nDestTruncate = (nSrcPage+ratio-1)/ratio;
- if( nDestTruncate==(int)PENDING_BYTE_PAGE(p->pDest->pBt) ){
- nDestTruncate--;
+ if( rc==SQLITE_DONE ){
+ rc = sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1);
+ if( rc==SQLITE_OK ){
+ if( p->pDestDb ){
+ sqlite3ResetInternalSchema(p->pDestDb, -1);
+ }
+ if( destMode==PAGER_JOURNALMODE_WAL ){
+ rc = sqlite3BtreeSetVersion(p->pDest, 2);
}
- }else{
- nDestTruncate = nSrcPage * (pgszSrc/pgszDest);
}
- sqlite3PagerTruncateImage(pDestPager, nDestTruncate);
-
- if( pgszSrc<pgszDest ){
- /* If the source page-size is smaller than the destination page-size,
- ** two extra things may need to happen:
- **
- ** * The destination may need to be truncated, and
+ if( rc==SQLITE_OK ){
+ int nDestTruncate;
+ /* Set nDestTruncate to the final number of pages in the destination
+ ** database. The complication here is that the destination page
+ ** size may be different to the source page size.
**
- ** * Data stored on the pages immediately following the
- ** pending-byte page in the source database may need to be
- ** copied into the destination database.
+ ** If the source page size is smaller than the destination page size,
+ ** round up. In this case the call to sqlite3OsTruncate() below will
+ ** fix the size of the file. However it is important to call
+ ** sqlite3PagerTruncateImage() here so that any pages in the
+ ** destination file that lie beyond the nDestTruncate page mark are
+ ** journalled by PagerCommitPhaseOne() before they are destroyed
+ ** by the file truncation.
*/
- const i64 iSize = (i64)pgszSrc * (i64)nSrcPage;
- sqlite3_file * const pFile = sqlite3PagerFile(pDestPager);
- i64 iOff;
- i64 iEnd;
-
- assert( pFile );
- assert( (i64)nDestTruncate*(i64)pgszDest >= iSize || (
- nDestTruncate==(int)(PENDING_BYTE_PAGE(p->pDest->pBt)-1)
- && iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest
- ));
+ assert( pgszSrc==sqlite3BtreeGetPageSize(p->pSrc) );
+ assert( pgszDest==sqlite3BtreeGetPageSize(p->pDest) );
+ if( pgszSrc<pgszDest ){
+ int ratio = pgszDest/pgszSrc;
+ nDestTruncate = (nSrcPage+ratio-1)/ratio;
+ if( nDestTruncate==(int)PENDING_BYTE_PAGE(p->pDest->pBt) ){
+ nDestTruncate--;
+ }
+ }else{
+ nDestTruncate = nSrcPage * (pgszSrc/pgszDest);
+ }
+ sqlite3PagerTruncateImage(pDestPager, nDestTruncate);
- /* This call ensures that all data required to recreate the original
- ** database has been stored in the journal for pDestPager and the
- ** journal synced to disk. So at this point we may safely modify
- ** the database file in any way, knowing that if a power failure
- ** occurs, the original database will be reconstructed from the
- ** journal file. */
- rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1);
-
- /* Write the extra pages and truncate the database file as required. */
- iEnd = MIN(PENDING_BYTE + pgszDest, iSize);
- for(
- iOff=PENDING_BYTE+pgszSrc;
- rc==SQLITE_OK && iOff<iEnd;
- iOff+=pgszSrc
- ){
- PgHdr *pSrcPg = 0;
- const Pgno iSrcPg = (Pgno)((iOff/pgszSrc)+1);
- rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg);
+ if( pgszSrc<pgszDest ){
+ /* If the source page-size is smaller than the destination page-size,
+ ** two extra things may need to happen:
+ **
+ ** * The destination may need to be truncated, and
+ **
+ ** * Data stored on the pages immediately following the
+ ** pending-byte page in the source database may need to be
+ ** copied into the destination database.
+ */
+ const i64 iSize = (i64)pgszSrc * (i64)nSrcPage;
+ sqlite3_file * const pFile = sqlite3PagerFile(pDestPager);
+ i64 iOff;
+ i64 iEnd;
+
+ assert( pFile );
+ assert( (i64)nDestTruncate*(i64)pgszDest >= iSize || (
+ nDestTruncate==(int)(PENDING_BYTE_PAGE(p->pDest->pBt)-1)
+ && iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest
+ ));
+
+ /* This call ensures that all data required to recreate the original
+ ** database has been stored in the journal for pDestPager and the
+ ** journal synced to disk. So at this point we may safely modify
+ ** the database file in any way, knowing that if a power failure
+ ** occurs, the original database will be reconstructed from the
+ ** journal file. */
+ rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1);
+
+ /* Write the extra pages and truncate the database file as required */
+ iEnd = MIN(PENDING_BYTE + pgszDest, iSize);
+ for(
+ iOff=PENDING_BYTE+pgszSrc;
+ rc==SQLITE_OK && iOff<iEnd;
+ iOff+=pgszSrc
+ ){
+ PgHdr *pSrcPg = 0;
+ const Pgno iSrcPg = (Pgno)((iOff/pgszSrc)+1);
+ rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg);
+ if( rc==SQLITE_OK ){
+ u8 *zData = sqlite3PagerGetData(pSrcPg);
+ rc = sqlite3OsWrite(pFile, zData, pgszSrc, iOff);
+ }
+ sqlite3PagerUnref(pSrcPg);
+ }
if( rc==SQLITE_OK ){
- u8 *zData = sqlite3PagerGetData(pSrcPg);
- rc = sqlite3OsWrite(pFile, zData, pgszSrc, iOff);
+ rc = backupTruncateFile(pFile, iSize);
}
- sqlite3PagerUnref(pSrcPg);
- }
- if( rc==SQLITE_OK ){
- rc = backupTruncateFile(pFile, iSize);
- }
- /* Sync the database file to disk. */
- if( rc==SQLITE_OK ){
- rc = sqlite3PagerSync(pDestPager);
+ /* Sync the database file to disk. */
+ if( rc==SQLITE_OK ){
+ rc = sqlite3PagerSync(pDestPager);
+ }
+ }else{
+ rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0);
+ }
+
+ /* Finish committing the transaction to the destination database. */
+ if( SQLITE_OK==rc
+ && SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest, 0))
+ ){
+ rc = SQLITE_DONE;
}
- }else{
- rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0);
- }
-
- /* Finish committing the transaction to the destination database. */
- if( SQLITE_OK==rc
- && SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest, 0))
- ){
- rc = SQLITE_DONE;
}
}
*/
SQLITE_PRIVATE void sqlite3VdbeMemReleaseExternal(Mem *p){
assert( p->db==0 || sqlite3_mutex_held(p->db->mutex) );
- testcase( p->flags & MEM_Agg );
- testcase( p->flags & MEM_Dyn );
- testcase( p->flags & MEM_RowSet );
- testcase( p->flags & MEM_Frame );
- if( p->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame) ){
- 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);
- }else if( p->flags&MEM_Frame ){
- sqlite3VdbeMemSetNull(p);
- }
+ 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);
+ }else if( p->flags&MEM_Frame ){
+ sqlite3VdbeMemSetNull(p);
}
}
** (Mem.type==SQLITE_TEXT).
*/
SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p){
- sqlite3VdbeMemReleaseExternal(p);
+ MemReleaseExt(p);
sqlite3DbFree(p->db, p->zMalloc);
p->z = 0;
p->zMalloc = 0;
*/
SQLITE_PRIVATE void sqlite3VdbeMemShallowCopy(Mem *pTo, const Mem *pFrom, int srcType){
assert( (pFrom->flags & MEM_RowSet)==0 );
- sqlite3VdbeMemReleaseExternal(pTo);
+ MemReleaseExt(pTo);
memcpy(pTo, pFrom, MEMCELLSIZE);
pTo->xDel = 0;
if( (pFrom->flags&MEM_Static)==0 ){
int rc = SQLITE_OK;
assert( (pFrom->flags & MEM_RowSet)==0 );
- sqlite3VdbeMemReleaseExternal(pTo);
+ MemReleaseExt(pTo);
memcpy(pTo, pFrom, MEMCELLSIZE);
pTo->flags &= ~MEM_Dyn;
pOp->p3 = p3;
pOp->p4.p = 0;
pOp->p4type = P4_NOTUSED;
- p->expired = 0;
- if( op==OP_ParseSchema ){
- /* Any program that uses the OP_ParseSchema opcode needs to lock
- ** all btrees. */
- int j;
- for(j=0; j<p->db->nDb; j++) sqlite3VdbeUsesBtree(p, j);
- }
#ifdef SQLITE_DEBUG
pOp->zComment = 0;
if( sqlite3VdbeAddopTrace ) sqlite3VdbePrintOp(0, i, &p->aOp[i]);
}
/*
+** Add an OP_ParseSchema opcode. This routine is broken out from
+** sqlite3VdbeAddOp4() since it needs to also local all btrees.
+**
+** The zWhere string must have been obtained from sqlite3_malloc().
+** This routine will take ownership of the allocated memory.
+*/
+SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere){
+ int j;
+ int addr = sqlite3VdbeAddOp3(p, OP_ParseSchema, iDb, 0, 0);
+ sqlite3VdbeChangeP4(p, addr, zWhere, P4_DYNAMIC);
+ for(j=0; j<p->db->nDb; j++) sqlite3VdbeUsesBtree(p, j);
+}
+
+/*
** Add an opcode that includes the p4 value as an integer.
*/
SQLITE_PRIVATE int sqlite3VdbeAddOp4Int(
n = pOp[-1].p1;
if( n>nMaxArgs ) nMaxArgs = n;
#endif
+ }else if( opcode==OP_Next || opcode==OP_SorterNext ){
+ pOp->p4.xAdvance = sqlite3BtreeNext;
+ pOp->p4type = P4_ADVANCE;
+ }else if( opcode==OP_Prev ){
+ pOp->p4.xAdvance = sqlite3BtreePrevious;
+ pOp->p4type = P4_ADVANCE;
}
if( (pOp->opflags & OPFLG_JUMP)!=0 && pOp->p2<0 ){
** static array using sqlite3VdbeAddOpList but we want to make a
** few minor changes to the program.
*/
-SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe *p, int addr, int val){
+SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe *p, u32 addr, int val){
assert( p!=0 );
- assert( addr>=0 );
- if( p->nOp>addr ){
+ if( ((u32)p->nOp)>addr ){
p->aOp[addr].p1 = val;
}
}
** Change the value of the P2 operand for a specific instruction.
** This routine is useful for setting a jump destination.
*/
-SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe *p, int addr, int val){
+SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe *p, u32 addr, int val){
assert( p!=0 );
- assert( addr>=0 );
- if( p->nOp>addr ){
+ if( ((u32)p->nOp)>addr ){
p->aOp[addr].p2 = val;
}
}
/*
** Change the value of the P3 operand for a specific instruction.
*/
-SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe *p, int addr, int val){
+SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe *p, u32 addr, int val){
assert( p!=0 );
- assert( addr>=0 );
- if( p->nOp>addr ){
+ if( ((u32)p->nOp)>addr ){
p->aOp[addr].p3 = val;
}
}
}
/*
-** Change N opcodes starting at addr to No-ops.
+** Change the opcode at addr into OP_Noop
*/
-SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe *p, int addr, int N){
+SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe *p, int addr){
if( p->aOp ){
VdbeOp *pOp = &p->aOp[addr];
sqlite3 *db = p->db;
- while( N-- ){
- freeP4(db, pOp->p4type, pOp->p4.p);
- memset(pOp, 0, sizeof(pOp[0]));
- pOp->opcode = OP_Noop;
- pOp++;
- }
+ freeP4(db, pOp->p4type, pOp->p4.p);
+ memset(pOp, 0, sizeof(pOp[0]));
+ pOp->opcode = OP_Noop;
}
}
SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){
/* C89 specifies that the constant "dummy" will be initialized to all
** zeros, which is correct. MSVC generates a warning, nevertheless. */
- static const VdbeOp dummy; /* Ignore the MSVC warning about no initializer */
+ static VdbeOp dummy; /* Ignore the MSVC warning about no initializer */
assert( p->magic==VDBE_MAGIC_INIT );
if( addr<0 ){
#ifdef SQLITE_OMIT_TRACE
sqlite3_snprintf(nTemp, zTemp, "program");
break;
}
+ case P4_ADVANCE: {
+ zTemp[0] = 0;
+ break;
+ }
default: {
zP4 = pOp->p4.z;
if( zP4==0 ){
}
/*
-** Prepare a virtual machine for execution. This involves things such
+** Rewind the VDBE back to the beginning in preparation for
+** running it.
+*/
+SQLITE_PRIVATE void sqlite3VdbeRewind(Vdbe *p){
+#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
+ int i;
+#endif
+ assert( p!=0 );
+ assert( p->magic==VDBE_MAGIC_INIT );
+
+ /* There should be at least one opcode.
+ */
+ assert( p->nOp>0 );
+
+ /* Set the magic to VDBE_MAGIC_RUN sooner rather than later. */
+ p->magic = VDBE_MAGIC_RUN;
+
+#ifdef SQLITE_DEBUG
+ for(i=1; i<p->nMem; i++){
+ assert( p->aMem[i].db==p->db );
+ }
+#endif
+ p->pc = -1;
+ p->rc = SQLITE_OK;
+ p->errorAction = OE_Abort;
+ p->magic = VDBE_MAGIC_RUN;
+ p->nChange = 0;
+ p->cacheCtr = 1;
+ p->minWriteFileFormat = 255;
+ p->iStatement = 0;
+ p->nFkConstraint = 0;
+#ifdef VDBE_PROFILE
+ for(i=0; i<p->nOp; i++){
+ p->aOp[i].cnt = 0;
+ p->aOp[i].cycles = 0;
+ }
+#endif
+}
+
+/*
+** Prepare a virtual machine for execution for the first time after
+** creating the virtual machine. This involves things such
** as allocating stack space and initializing the program counter.
** After the VDBE has be prepped, it can be executed by one or more
** calls to sqlite3VdbeExec().
**
-** This is the only way to move a VDBE from VDBE_MAGIC_INIT to
-** VDBE_MAGIC_RUN.
+** This function may be called exact once on a each virtual machine.
+** After this routine is called the VM has been "packaged" and is ready
+** to run. After this routine is called, futher calls to
+** sqlite3VdbeAddOp() functions are prohibited. This routine disconnects
+** the Vdbe from the Parse object that helped generate it so that the
+** the Vdbe becomes an independent entity and the Parse object can be
+** destroyed.
**
-** This function may be called more than once on a single virtual machine.
-** The first call is made while compiling the SQL statement. Subsequent
-** calls are made as part of the process of resetting a statement to be
-** re-executed (from a call to sqlite3_reset()). The nVar, nMem, nCursor
-** and isExplain parameters are only passed correct values the first time
-** the function is called. On subsequent calls, from sqlite3_reset(), nVar
-** is passed -1 and nMem, nCursor and isExplain are all passed zero.
+** Use the sqlite3VdbeRewind() procedure to restore a virtual machine back
+** to its initial state after it has been run.
*/
SQLITE_PRIVATE void sqlite3VdbeMakeReady(
Vdbe *p, /* The VDBE */
- int nVar, /* Number of '?' see in the SQL statement */
- int nMem, /* Number of memory cells to allocate */
- int nCursor, /* Number of cursors to allocate */
- int nArg, /* Maximum number of args in SubPrograms */
- int isExplain, /* True if the EXPLAIN keywords is present */
- int usesStmtJournal /* True to set Vdbe.usesStmtJournal */
+ Parse *pParse /* Parsing context */
){
- int n;
- sqlite3 *db = p->db;
+ sqlite3 *db; /* The database connection */
+ int nVar; /* Number of parameters */
+ int nMem; /* Number of VM memory registers */
+ int nCursor; /* Number of cursors required */
+ int nArg; /* Number of arguments in subprograms */
+ int n; /* Loop counter */
+ u8 *zCsr; /* Memory available for allocation */
+ u8 *zEnd; /* First byte past allocated memory */
+ int nByte; /* How much extra memory is needed */
assert( p!=0 );
- assert( p->magic==VDBE_MAGIC_INIT );
-
- /* There should be at least one opcode.
- */
assert( p->nOp>0 );
-
- /* Set the magic to VDBE_MAGIC_RUN sooner rather than later. */
- p->magic = VDBE_MAGIC_RUN;
-
+ assert( pParse!=0 );
+ assert( p->magic==VDBE_MAGIC_INIT );
+ db = p->db;
+ assert( db->mallocFailed==0 );
+ nVar = pParse->nVar;
+ nMem = pParse->nMem;
+ nCursor = pParse->nTab;
+ nArg = pParse->nMaxArg;
+
/* For each cursor required, also allocate a memory cell. Memory
** cells (nMem+1-nCursor)..nMem, inclusive, will never be used by
** the vdbe program. Instead they are used to allocate space for
nMem += nCursor;
/* Allocate space for memory registers, SQL variables, VDBE cursors and
- ** an array to marshal SQL function arguments in. This is only done the
- ** first time this function is called for a given VDBE, not when it is
- ** being called from sqlite3_reset() to reset the virtual machine.
- */
- if( nVar>=0 && ALWAYS(db->mallocFailed==0) ){
- u8 *zCsr = (u8 *)&p->aOp[p->nOp]; /* Memory avaliable for alloation */
- u8 *zEnd = (u8 *)&p->aOp[p->nOpAlloc]; /* First byte past available mem */
- int nByte; /* How much extra memory needed */
-
- resolveP2Values(p, &nArg);
- p->usesStmtJournal = (u8)usesStmtJournal;
- if( isExplain && nMem<10 ){
- nMem = 10;
- }
- memset(zCsr, 0, zEnd-zCsr);
- zCsr += (zCsr - (u8*)0)&7;
- assert( EIGHT_BYTE_ALIGNMENT(zCsr) );
-
- /* Memory for registers, parameters, cursor, etc, is allocated in two
- ** passes. On the first pass, we try to reuse unused space at the
- ** end of the opcode array. If we are unable to satisfy all memory
- ** requirements by reusing the opcode array tail, then the second
- ** pass will fill in the rest using a fresh allocation.
- **
- ** This two-pass approach that reuses as much memory as possible from
- ** the leftover space at the end of the opcode array can significantly
- ** reduce the amount of memory held by a prepared statement.
- */
- do {
- nByte = 0;
- p->aMem = allocSpace(p->aMem, nMem*sizeof(Mem), &zCsr, zEnd, &nByte);
- p->aVar = allocSpace(p->aVar, nVar*sizeof(Mem), &zCsr, zEnd, &nByte);
- p->apArg = allocSpace(p->apArg, nArg*sizeof(Mem*), &zCsr, zEnd, &nByte);
- p->azVar = allocSpace(p->azVar, nVar*sizeof(char*), &zCsr, zEnd, &nByte);
- p->apCsr = allocSpace(p->apCsr, nCursor*sizeof(VdbeCursor*),
- &zCsr, zEnd, &nByte);
- if( nByte ){
- p->pFree = sqlite3DbMallocZero(db, nByte);
- }
- zCsr = p->pFree;
- zEnd = &zCsr[nByte];
- }while( nByte && !db->mallocFailed );
+ ** an array to marshal SQL function arguments in.
+ */
+ zCsr = (u8*)&p->aOp[p->nOp]; /* Memory avaliable for allocation */
+ zEnd = (u8*)&p->aOp[p->nOpAlloc]; /* First byte past end of zCsr[] */
- p->nCursor = (u16)nCursor;
- if( p->aVar ){
- p->nVar = (ynVar)nVar;
- for(n=0; n<nVar; n++){
- p->aVar[n].flags = MEM_Null;
- p->aVar[n].db = db;
- }
+ resolveP2Values(p, &nArg);
+ p->usesStmtJournal = (u8)(pParse->isMultiWrite && pParse->mayAbort);
+ if( pParse->explain && nMem<10 ){
+ nMem = 10;
+ }
+ memset(zCsr, 0, zEnd-zCsr);
+ zCsr += (zCsr - (u8*)0)&7;
+ assert( EIGHT_BYTE_ALIGNMENT(zCsr) );
+ p->expired = 0;
+
+ /* Memory for registers, parameters, cursor, etc, is allocated in two
+ ** passes. On the first pass, we try to reuse unused space at the
+ ** end of the opcode array. If we are unable to satisfy all memory
+ ** requirements by reusing the opcode array tail, then the second
+ ** pass will fill in the rest using a fresh allocation.
+ **
+ ** This two-pass approach that reuses as much memory as possible from
+ ** the leftover space at the end of the opcode array can significantly
+ ** reduce the amount of memory held by a prepared statement.
+ */
+ do {
+ nByte = 0;
+ p->aMem = allocSpace(p->aMem, nMem*sizeof(Mem), &zCsr, zEnd, &nByte);
+ p->aVar = allocSpace(p->aVar, nVar*sizeof(Mem), &zCsr, zEnd, &nByte);
+ p->apArg = allocSpace(p->apArg, nArg*sizeof(Mem*), &zCsr, zEnd, &nByte);
+ p->azVar = allocSpace(p->azVar, nVar*sizeof(char*), &zCsr, zEnd, &nByte);
+ p->apCsr = allocSpace(p->apCsr, nCursor*sizeof(VdbeCursor*),
+ &zCsr, zEnd, &nByte);
+ if( nByte ){
+ p->pFree = sqlite3DbMallocZero(db, nByte);
}
- if( p->aMem ){
- p->aMem--; /* aMem[] goes from 1..nMem */
- p->nMem = nMem; /* not from 0..nMem-1 */
- for(n=1; n<=nMem; n++){
- p->aMem[n].flags = MEM_Null;
- p->aMem[n].db = db;
- }
+ zCsr = p->pFree;
+ zEnd = &zCsr[nByte];
+ }while( nByte && !db->mallocFailed );
+
+ p->nCursor = (u16)nCursor;
+ if( p->aVar ){
+ p->nVar = (ynVar)nVar;
+ for(n=0; n<nVar; n++){
+ p->aVar[n].flags = MEM_Null;
+ p->aVar[n].db = db;
}
}
-#ifdef SQLITE_DEBUG
- for(n=1; n<p->nMem; n++){
- assert( p->aMem[n].db==db );
+ if( p->azVar ){
+ p->nzVar = pParse->nzVar;
+ memcpy(p->azVar, pParse->azVar, p->nzVar*sizeof(p->azVar[0]));
+ memset(pParse->azVar, 0, pParse->nzVar*sizeof(pParse->azVar[0]));
}
-#endif
-
- p->pc = -1;
- p->rc = SQLITE_OK;
- p->errorAction = OE_Abort;
- p->explain |= isExplain;
- p->magic = VDBE_MAGIC_RUN;
- p->nChange = 0;
- p->cacheCtr = 1;
- p->minWriteFileFormat = 255;
- p->iStatement = 0;
- p->nFkConstraint = 0;
-#ifdef VDBE_PROFILE
- {
- int i;
- for(i=0; i<p->nOp; i++){
- p->aOp[i].cnt = 0;
- p->aOp[i].cycles = 0;
+ if( p->aMem ){
+ p->aMem--; /* aMem[] goes from 1..nMem */
+ p->nMem = nMem; /* not from 0..nMem-1 */
+ for(n=1; n<=nMem; n++){
+ p->aMem[n].flags = MEM_Null;
+ p->aMem[n].db = db;
}
}
-#endif
+ p->explain = pParse->explain;
+ sqlite3VdbeRewind(p);
}
/*
if( pCx==0 ){
return;
}
+ sqlite3VdbeSorterClose(p->db, pCx);
if( pCx->pBt ){
sqlite3BtreeClose(pCx->pBt);
/* The pCx->pCursor will be close automatically, if it exists, by
if( !zMaster ){
return SQLITE_NOMEM;
}
+ sqlite3FileSuffix3(zMainFile, zMaster);
rc = sqlite3OsAccess(pVfs, zMaster, SQLITE_ACCESS_EXISTS, &res);
}while( rc==SQLITE_OK && res );
if( rc==SQLITE_OK ){
db->nStatement--;
p->iStatement = 0;
+ if( rc==SQLITE_OK ){
+ if( eOp==SAVEPOINT_ROLLBACK ){
+ rc = sqlite3VtabSavepoint(db, SAVEPOINT_ROLLBACK, iSavepoint);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3VtabSavepoint(db, SAVEPOINT_RELEASE, iSavepoint);
+ }
+ }
+
/* If the statement transaction is being rolled back, also restore the
** database handles deferred constraint counter to the value it had when
** the statement transaction was opened. */
** do so. If this operation returns an error, and the current statement
** error code is SQLITE_OK or SQLITE_CONSTRAINT, then promote the
** current statement error code.
- **
- ** Note that sqlite3VdbeCloseStatement() can only fail if eStatementOp
- ** is SAVEPOINT_ROLLBACK. But if p->rc==SQLITE_OK then eStatementOp
- ** must be SAVEPOINT_RELEASE. Hence the NEVER(p->rc==SQLITE_OK) in
- ** the following code.
*/
if( eStatementOp ){
rc = sqlite3VdbeCloseStatement(p, eStatementOp);
if( rc ){
- assert( eStatementOp==SAVEPOINT_ROLLBACK );
- if( NEVER(p->rc==SQLITE_OK) || p->rc==SQLITE_CONSTRAINT ){
+ if( p->rc==SQLITE_OK || p->rc==SQLITE_CONSTRAINT ){
p->rc = rc;
sqlite3DbFree(db, p->zErrMsg);
p->zErrMsg = 0;
*/
SQLITE_PRIVATE void sqlite3VdbeDeleteObject(sqlite3 *db, Vdbe *p){
SubProgram *pSub, *pNext;
+ int i;
assert( p->db==0 || p->db==db );
releaseMemArray(p->aVar, p->nVar);
releaseMemArray(p->aColName, p->nResColumn*COLNAME_N);
vdbeFreeOpArray(db, pSub->aOp, pSub->nOp);
sqlite3DbFree(db, pSub);
}
+ for(i=p->nzVar-1; i>=0; i--) sqlite3DbFree(db, p->azVar[i]);
vdbeFreeOpArray(db, p->aOp, p->nOp);
sqlite3DbFree(db, p->aLabel);
sqlite3DbFree(db, p->aColName);
return 0;
}
-
/*
-** Given the nKey-byte encoding of a record in pKey[], parse the
-** record into a UnpackedRecord structure. Return a pointer to
-** that structure.
+** This routine is used to allocate sufficient space for an UnpackedRecord
+** structure large enough to be used with sqlite3VdbeRecordUnpack() if
+** the first argument is a pointer to KeyInfo structure pKeyInfo.
**
-** The calling function might provide szSpace bytes of memory
-** space at pSpace. This space can be used to hold the returned
-** VDbeParsedRecord structure if it is large enough. If it is
-** not big enough, space is obtained from sqlite3_malloc().
+** The space is either allocated using sqlite3DbMallocRaw() or from within
+** the unaligned buffer passed via the second and third arguments (presumably
+** stack space). If the former, then *ppFree is set to a pointer that should
+** be eventually freed by the caller using sqlite3DbFree(). Or, if the
+** allocation comes from the pSpace/szSpace buffer, *ppFree is set to NULL
+** before returning.
**
-** The returned structure should be closed by a call to
-** sqlite3VdbeDeleteUnpackedRecord().
-*/
-SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeRecordUnpack(
- KeyInfo *pKeyInfo, /* Information about the record format */
- int nKey, /* Size of the binary record */
- const void *pKey, /* The binary record */
- char *pSpace, /* Unaligned space available to hold the object */
- int szSpace /* Size of pSpace[] in bytes */
+** If an OOM error occurs, NULL is returned.
+*/
+SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(
+ KeyInfo *pKeyInfo, /* Description of the record */
+ char *pSpace, /* Unaligned space available */
+ int szSpace, /* Size of pSpace[] in bytes */
+ char **ppFree /* OUT: Caller should free this pointer */
){
- const unsigned char *aKey = (const unsigned char *)pKey;
- UnpackedRecord *p; /* The unpacked record that we will return */
- int nByte; /* Memory space needed to hold p, in bytes */
- int d;
- u32 idx;
- u16 u; /* Unsigned loop counter */
- u32 szHdr;
- Mem *pMem;
- int nOff; /* Increase pSpace by this much to 8-byte align it */
-
- /*
- ** We want to shift the pointer pSpace up such that it is 8-byte aligned.
+ UnpackedRecord *p; /* Unpacked record to return */
+ int nOff; /* Increment pSpace by nOff to align it */
+ int nByte; /* Number of bytes required for *p */
+
+ /* We want to shift the pointer pSpace up such that it is 8-byte aligned.
** Thus, we need to calculate a value, nOff, between 0 and 7, to shift
** it by. If pSpace is already 8-byte aligned, nOff should be zero.
*/
nOff = (8 - (SQLITE_PTR_TO_INT(pSpace) & 7)) & 7;
- pSpace += nOff;
- szSpace -= nOff;
nByte = ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nField+1);
- if( nByte>szSpace ){
- p = sqlite3DbMallocRaw(pKeyInfo->db, nByte);
- if( p==0 ) return 0;
- p->flags = UNPACKED_NEED_FREE | UNPACKED_NEED_DESTROY;
+ if( nByte>szSpace+nOff ){
+ p = (UnpackedRecord *)sqlite3DbMallocRaw(pKeyInfo->db, nByte);
+ *ppFree = (char *)p;
+ if( !p ) return 0;
}else{
- p = (UnpackedRecord*)pSpace;
- p->flags = UNPACKED_NEED_DESTROY;
+ p = (UnpackedRecord*)&pSpace[nOff];
+ *ppFree = 0;
}
+
+ p->aMem = (Mem*)&((char*)p)[ROUND8(sizeof(UnpackedRecord))];
p->pKeyInfo = pKeyInfo;
p->nField = pKeyInfo->nField + 1;
- p->aMem = pMem = (Mem*)&((char*)p)[ROUND8(sizeof(UnpackedRecord))];
+ return p;
+}
+
+/*
+** Given the nKey-byte encoding of a record in pKey[], populate the
+** UnpackedRecord structure indicated by the fourth argument with the
+** contents of the decoded record.
+*/
+SQLITE_PRIVATE void sqlite3VdbeRecordUnpack(
+ KeyInfo *pKeyInfo, /* Information about the record format */
+ int nKey, /* Size of the binary record */
+ const void *pKey, /* The binary record */
+ UnpackedRecord *p /* Populate this structure before returning. */
+){
+ const unsigned char *aKey = (const unsigned char *)pKey;
+ int d;
+ u32 idx; /* Offset in aKey[] to read from */
+ u16 u; /* Unsigned loop counter */
+ u32 szHdr;
+ Mem *pMem = p->aMem;
+
+ p->flags = 0;
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
idx = getVarint32(aKey, szHdr);
d = szHdr;
idx += getVarint32(&aKey[idx], serial_type);
pMem->enc = pKeyInfo->enc;
pMem->db = pKeyInfo->db;
- pMem->flags = 0;
+ /* pMem->flags = 0; // sqlite3VdbeSerialGet() will set this for us */
pMem->zMalloc = 0;
d += sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem);
pMem++;
}
assert( u<=pKeyInfo->nField + 1 );
p->nField = u;
- return (void*)p;
-}
-
-/*
-** This routine destroys a UnpackedRecord object.
-*/
-SQLITE_PRIVATE void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord *p){
- int i;
- Mem *pMem;
-
- assert( p!=0 );
- assert( p->flags & UNPACKED_NEED_DESTROY );
- for(i=0, pMem=p->aMem; i<p->nField; i++, pMem++){
- /* The unpacked record is always constructed by the
- ** sqlite3VdbeUnpackRecord() function above, which makes all
- ** strings and blobs static. And none of the elements are
- ** ever transformed, so there is never anything to delete.
- */
- if( NEVER(pMem->zMalloc) ) sqlite3VdbeMemRelease(pMem);
- }
- if( p->flags & UNPACKED_NEED_FREE ){
- sqlite3DbFree(p->pKeyInfo->db, p);
- }
}
/*
/* Compilers may complain that mem1.u.i is potentially uninitialized.
** We could initialize it, as shown here, to silence those complaints.
- ** But in fact, mem1.u.i will never actually be used initialized, and doing
+ ** But in fact, mem1.u.i will never actually be used uninitialized, and doing
** the unnecessary initialization has a measurable negative performance
** impact, since this routine is a very high runner. And so, we choose
** to ignore the compiler warnings and leave this variable uninitialized.
Vdbe *v = (Vdbe*)pStmt;
sqlite3_mutex_enter(v->db->mutex);
rc = sqlite3VdbeReset(v);
- sqlite3VdbeMakeReady(v, -1, 0, 0, 0, 0, 0);
+ sqlite3VdbeRewind(v);
assert( (rc & (v->db->errMask))==rc );
rc = sqlite3ApiExit(v->db, rc);
sqlite3_mutex_leave(v->db->mutex);
}
/*
+** The maximum number of times that a statement will try to reparse
+** itself before giving up and returning SQLITE_SCHEMA.
+*/
+#ifndef SQLITE_MAX_SCHEMA_RETRY
+# define SQLITE_MAX_SCHEMA_RETRY 5
+#endif
+
+/*
** This is the top-level implementation of sqlite3_step(). Call
** sqlite3Step() to do most of the work. If a schema error occurs,
** call sqlite3Reprepare() and try again.
db = v->db;
sqlite3_mutex_enter(db->mutex);
while( (rc = sqlite3Step(v))==SQLITE_SCHEMA
- && cnt++ < 5
+ && cnt++ < SQLITE_MAX_SCHEMA_RETRY
&& (rc2 = rc = sqlite3Reprepare(v))==SQLITE_OK ){
sqlite3_reset(pStmt);
- v->expired = 0;
+ assert( v->expired==0 );
}
if( rc2!=SQLITE_OK && ALWAYS(v->isPrepareV2) && ALWAYS(db->pErr) ){
/* This case occurs after failing to recompile an sql statement.
}
/*
-** Create a mapping from variable numbers to variable names
-** in the Vdbe.azVar[] array, if such a mapping does not already
-** exist.
-*/
-static void createVarMap(Vdbe *p){
- if( !p->okVar ){
- int j;
- Op *pOp;
- sqlite3_mutex_enter(p->db->mutex);
- /* The race condition here is harmless. If two threads call this
- ** routine on the same Vdbe at the same time, they both might end
- ** up initializing the Vdbe.azVar[] array. That is a little extra
- ** work but it results in the same answer.
- */
- for(j=0, pOp=p->aOp; j<p->nOp; j++, pOp++){
- if( pOp->opcode==OP_Variable ){
- assert( pOp->p1>0 && pOp->p1<=p->nVar );
- p->azVar[pOp->p1-1] = pOp->p4.z;
- }
- }
- p->okVar = 1;
- sqlite3_mutex_leave(p->db->mutex);
- }
-}
-
-/*
** Return the name of a wildcard parameter. Return NULL if the index
** is out of range or if the wildcard is unnamed.
**
*/
SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt *pStmt, int i){
Vdbe *p = (Vdbe*)pStmt;
- if( p==0 || i<1 || i>p->nVar ){
+ if( p==0 || i<1 || i>p->nzVar ){
return 0;
}
- createVarMap(p);
return p->azVar[i-1];
}
if( p==0 ){
return 0;
}
- createVarMap(p);
if( zName ){
- for(i=0; i<p->nVar; i++){
+ for(i=0; i<p->nzVar; i++){
const char *z = p->azVar[i];
if( z && memcmp(z,zName,nName)==0 && z[nName]==0 ){
return i+1;
*/
#define ExpandBlob(P) (((P)->flags&MEM_Zero)?sqlite3VdbeMemExpandBlob(P):0)
+/* Return true if the cursor was opened using the OP_OpenSorter opcode. */
+#ifdef SQLITE_OMIT_MERGE_SORT
+# define isSorter(x) 0
+#else
+# define isSorter(x) ((x)->pSorter!=0)
+#endif
+
/*
** Argument pMem points at a register that will be passed to a
** user-defined function or returned to the user as the result of a query.
Mem *pOut = 0; /* Output operand */
int iCompare = 0; /* Result of last OP_Compare operation */
int *aPermute = 0; /* Permutation of columns for OP_Compare */
+ i64 lastRowid = db->lastRowid; /* Saved value of the last insert ROWID */
#ifdef VDBE_PROFILE
u64 start; /* CPU clock count at start of opcode */
int origPc; /* Program counter at start of opcode */
u32 szField; /* Number of bytes in the content of a field */
int szHdr; /* Size of the header size field at start of record */
int avail; /* Number of bytes of available data */
+ u32 t; /* A type code from the record header */
Mem *pReg; /* PseudoTable input register */
} am;
struct OP_Affinity_stack_vars {
struct OP_OpenEphemeral_stack_vars {
VdbeCursor *pCx;
} ax;
- struct OP_OpenPseudo_stack_vars {
+ struct OP_SorterOpen_stack_vars {
VdbeCursor *pCx;
} ay;
+ struct OP_OpenPseudo_stack_vars {
+ VdbeCursor *pCx;
+ } az;
struct OP_SeekGt_stack_vars {
int res;
int oc;
UnpackedRecord r;
int nField;
i64 iKey; /* The rowid we are to seek to */
- } az;
+ } ba;
struct OP_Seek_stack_vars {
VdbeCursor *pC;
- } ba;
+ } bb;
struct OP_Found_stack_vars {
int alreadyExists;
VdbeCursor *pC;
int res;
+ char *pFree;
UnpackedRecord *pIdxKey;
UnpackedRecord r;
char aTempRec[ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*3 + 7];
- } bb;
+ } bc;
struct OP_IsUnique_stack_vars {
u16 ii;
VdbeCursor *pCx;
Mem *aMx;
UnpackedRecord r; /* B-Tree index search key */
i64 R; /* Rowid stored in register P3 */
- } bc;
+ } bd;
struct OP_NotExists_stack_vars {
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
u64 iKey;
- } bd;
+ } be;
struct OP_NewRowid_stack_vars {
i64 v; /* The new rowid */
VdbeCursor *pC; /* Cursor of table to get the new rowid */
int cnt; /* Counter to limit the number of searches */
Mem *pMem; /* Register holding largest rowid for AUTOINCREMENT */
VdbeFrame *pFrame; /* Root frame of VDBE */
- } be;
+ } bf;
struct OP_InsertInt_stack_vars {
Mem *pData; /* MEM cell holding data for the record to be inserted */
Mem *pKey; /* MEM cell holding key for the record */
const char *zDb; /* database name - used by the update hook */
const char *zTbl; /* Table name - used by the opdate hook */
int op; /* Opcode for update hook: SQLITE_UPDATE or SQLITE_INSERT */
- } bf;
+ } bg;
struct OP_Delete_stack_vars {
i64 iKey;
VdbeCursor *pC;
- } bg;
+ } bh;
+ struct OP_SorterCompare_stack_vars {
+ VdbeCursor *pC;
+ int res;
+ } bi;
+ struct OP_SorterData_stack_vars {
+ VdbeCursor *pC;
+ } bj;
struct OP_RowData_stack_vars {
VdbeCursor *pC;
BtCursor *pCrsr;
u32 n;
i64 n64;
- } bh;
+ } bk;
struct OP_Rowid_stack_vars {
VdbeCursor *pC;
i64 v;
sqlite3_vtab *pVtab;
const sqlite3_module *pModule;
- } bi;
+ } bl;
struct OP_NullRow_stack_vars {
VdbeCursor *pC;
- } bj;
+ } bm;
struct OP_Last_stack_vars {
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
- } bk;
+ } bn;
struct OP_Rewind_stack_vars {
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
- } bl;
+ } bo;
struct OP_Next_stack_vars {
VdbeCursor *pC;
- BtCursor *pCrsr;
int res;
- } bm;
+ } bp;
struct OP_IdxInsert_stack_vars {
VdbeCursor *pC;
BtCursor *pCrsr;
int nKey;
const char *zKey;
- } bn;
+ } bq;
struct OP_IdxDelete_stack_vars {
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
UnpackedRecord r;
- } bo;
+ } br;
struct OP_IdxRowid_stack_vars {
BtCursor *pCrsr;
VdbeCursor *pC;
i64 rowid;
- } bp;
+ } bs;
struct OP_IdxGE_stack_vars {
VdbeCursor *pC;
int res;
UnpackedRecord r;
- } bq;
+ } bt;
struct OP_Destroy_stack_vars {
int iMoved;
int iCnt;
Vdbe *pVdbe;
int iDb;
- } br;
+ } bu;
struct OP_Clear_stack_vars {
int nChange;
- } bs;
+ } bv;
struct OP_CreateTable_stack_vars {
int pgno;
int flags;
Db *pDb;
- } bt;
+ } bw;
struct OP_ParseSchema_stack_vars {
int iDb;
const char *zMaster;
char *zSql;
InitData initData;
- } bu;
+ } bx;
struct OP_IntegrityCk_stack_vars {
int nRoot; /* Number of tables to check. (Number of root pages.) */
int *aRoot; /* Array of rootpage numbers for tables to be checked */
int nErr; /* Number of errors reported */
char *z; /* Text of the error report */
Mem *pnErr; /* Register keeping track of errors remaining */
- } bv;
+ } by;
struct OP_RowSetRead_stack_vars {
i64 val;
- } bw;
+ } bz;
struct OP_RowSetTest_stack_vars {
int iSet;
int exists;
- } bx;
+ } ca;
struct OP_Program_stack_vars {
int nMem; /* Number of memory registers for sub-program */
int nByte; /* Bytes of runtime space required for sub-program */
VdbeFrame *pFrame; /* New vdbe frame to execute in */
SubProgram *pProgram; /* Sub-program to execute */
void *t; /* Token identifying trigger */
- } by;
+ } cb;
struct OP_Param_stack_vars {
VdbeFrame *pFrame;
Mem *pIn;
- } bz;
+ } cc;
struct OP_MemMax_stack_vars {
Mem *pIn1;
VdbeFrame *pFrame;
- } ca;
+ } cd;
struct OP_AggStep_stack_vars {
int n;
int i;
Mem *pRec;
sqlite3_context ctx;
sqlite3_value **apVal;
- } cb;
+ } ce;
struct OP_AggFinal_stack_vars {
Mem *pMem;
- } cc;
+ } cf;
struct OP_Checkpoint_stack_vars {
int i; /* Loop counter */
int aRes[3]; /* Results */
Mem *pMem; /* Write results here */
- } cd;
+ } cg;
struct OP_JournalMode_stack_vars {
Btree *pBt; /* Btree to change journal mode of */
Pager *pPager; /* Pager associated with pBt */
int eNew; /* New journal mode */
int eOld; /* The old journal mode */
const char *zFilename; /* Name of database file for pPager */
- } ce;
+ } ch;
struct OP_IncrVacuum_stack_vars {
Btree *pBt;
- } cf;
+ } ci;
struct OP_VBegin_stack_vars {
VTable *pVTab;
- } cg;
+ } cj;
struct OP_VOpen_stack_vars {
VdbeCursor *pCur;
sqlite3_vtab_cursor *pVtabCursor;
sqlite3_vtab *pVtab;
sqlite3_module *pModule;
- } ch;
+ } ck;
struct OP_VFilter_stack_vars {
int nArg;
int iQuery;
int res;
int i;
Mem **apArg;
- } ci;
+ } cl;
struct OP_VColumn_stack_vars {
sqlite3_vtab *pVtab;
const sqlite3_module *pModule;
Mem *pDest;
sqlite3_context sContext;
- } cj;
+ } cm;
struct OP_VNext_stack_vars {
sqlite3_vtab *pVtab;
const sqlite3_module *pModule;
int res;
VdbeCursor *pCur;
- } ck;
+ } cn;
struct OP_VRename_stack_vars {
sqlite3_vtab *pVtab;
Mem *pName;
- } cl;
+ } co;
struct OP_VUpdate_stack_vars {
sqlite3_vtab *pVtab;
sqlite3_module *pModule;
sqlite_int64 rowid;
Mem **apArg;
Mem *pX;
- } cm;
+ } cp;
struct OP_Trace_stack_vars {
char *zTrace;
- } cn;
+ char *z;
+ } cq;
} u;
/* End automatically generated code
********************************************************************/
assert( pOp->p2<=p->nMem );
pOut = &aMem[pOp->p2];
memAboutToChange(p, pOut);
- sqlite3VdbeMemReleaseExternal(pOut);
+ MemReleaseExt(pOut);
pOut->flags = MEM_Int;
}
/* Opcode: HaltIfNull P1 P2 P3 P4 *
**
-** Check the value in register P3. If is is NULL then Halt using
+** Check the value in register P3. If it is NULL then Halt using
** parameter P1, P2, and P4 as if this were a Halt instruction. If the
** value in register P3 is not NULL, then this routine is a no-op.
*/
p->nFrame--;
sqlite3VdbeSetChanges(db, p->nChange);
pc = sqlite3VdbeFrameRestore(pFrame);
+ lastRowid = db->lastRowid;
if( pOp->p2==OE_Ignore ){
/* Instruction pc is the OP_Program that invoked the sub-program
** currently being halted. If the p2 instruction of this OP_Halt
#endif /* local variables moved into u.ab */
assert( pOp->p1>0 && pOp->p1<=p->nVar );
+ assert( pOp->p4.z==0 || pOp->p4.z==p->azVar[pOp->p1-1] );
u.ab.pVar = &p->aVar[pOp->p1 - 1];
if( sqlite3VdbeMemTooBig(u.ab.pVar) ){
goto too_big;
u.ac.zMalloc = pOut->zMalloc;
pOut->zMalloc = 0;
sqlite3VdbeMemMove(pOut, pIn1);
+#ifdef SQLITE_DEBUG
+ if( pOut->pScopyFrom>=&aMem[u.ac.p1] && pOut->pScopyFrom<&aMem[u.ac.p1+pOp->p3] ){
+ pOut->pScopyFrom += u.ac.p1 - pOp->p2;
+ }
+#endif
pIn1->zMalloc = u.ac.zMalloc;
REGISTER_TRACE(u.ac.p2++, pOut);
pIn1++;
assert( pOp[-1].opcode==OP_CollSeq );
u.ag.ctx.pColl = pOp[-1].p4.pColl;
}
+ db->lastRowid = lastRowid;
(*u.ag.ctx.pFunc->xFunc)(&u.ag.ctx, u.ag.n, u.ag.apVal); /* IMP: R-24505-23230 */
- if( db->mallocFailed ){
- /* Even though a malloc() has failed, the implementation of the
- ** user function may have called an sqlite3_result_XXX() function
- ** to return a value. The following call releases any resources
- ** associated with such a value.
- */
- sqlite3VdbeMemRelease(&u.ag.ctx.s);
- goto no_mem;
- }
+ lastRowid = db->lastRowid;
/* If any auxiliary data functions have been called by this user function,
** immediately call the destructor for any non-static values.
pOp->p4type = P4_VDBEFUNC;
}
+ if( db->mallocFailed ){
+ /* Even though a malloc() has failed, the implementation of the
+ ** user function may have called an sqlite3_result_XXX() function
+ ** to return a value. The following call releases any resources
+ ** associated with such a value.
+ */
+ sqlite3VdbeMemRelease(&u.ag.ctx.s);
+ goto no_mem;
+ }
+
/* If the function returned an error, throw an exception */
if( u.ag.ctx.isError ){
sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&u.ag.ctx.s));
** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either
** true or false and is never NULL. If both operands are NULL then the result
** of comparison is false. If either operand is NULL then the result is true.
-** If neither operand is NULL the the result is the same as it would be if
+** If neither operand is NULL the result is the same as it would be if
** the SQLITE_NULLEQ flag were omitted from P5.
*/
/* Opcode: Eq P1 P2 P3 P4 P5
** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either
** true or false and is never NULL. If both operands are NULL then the result
** of comparison is true. If either operand is NULL then the result is false.
-** If neither operand is NULL the the result is the same as it would be if
+** If neither operand is NULL the result is the same as it would be if
** the SQLITE_NULLEQ flag were omitted from P5.
*/
/* Opcode: Le P1 P2 P3 P4 P5
pIn3 = &aMem[pOp->p3];
u.ai.flags1 = pIn1->flags;
u.ai.flags3 = pIn3->flags;
- if( (pIn1->flags | pIn3->flags)&MEM_Null ){
+ if( (u.ai.flags1 | u.ai.flags3)&MEM_Null ){
/* One or both operands are NULL */
if( pOp->p5 & SQLITE_NULLEQ ){
/* If SQLITE_NULLEQ is set (which will only happen if the operator is
** or not both operands are null.
*/
assert( pOp->opcode==OP_Eq || pOp->opcode==OP_Ne );
- u.ai.res = (pIn1->flags & pIn3->flags & MEM_Null)==0;
+ u.ai.res = (u.ai.flags1 & u.ai.flags3 & MEM_Null)==0;
}else{
/* SQLITE_NULLEQ is clear and at least one operand is NULL,
** then the result is always NULL.
break;
}
+/* Opcode: Once P1 P2 * * *
+**
+** Jump to P2 if the value in register P1 is a not null or zero. If
+** the value is NULL or zero, fall through and change the P1 register
+** to an integer 1.
+**
+** When P1 is not used otherwise in a program, this opcode falls through
+** once and jumps on all subsequent invocations. It is the equivalent
+** of "OP_If P1 P2", followed by "OP_Integer 1 P1".
+*/
/* Opcode: If P1 P2 P3 * *
**
-** Jump to P2 if the value in register P1 is true. The value is
+** Jump to P2 if the value in register P1 is true. The value
** is considered true if it is numeric and non-zero. If the value
** in P1 is NULL then take the jump if P3 is true.
*/
/* Opcode: IfNot P1 P2 P3 * *
**
-** Jump to P2 if the value in register P1 is False. The value is
+** Jump to P2 if the value in register P1 is False. The value
** is considered true if it has a numeric value of zero. If the value
** in P1 is NULL then take the jump if P3 is true.
*/
+case OP_Once: /* jump, in1 */
case OP_If: /* jump, in1 */
case OP_IfNot: { /* jump, in1 */
#if 0 /* local variables moved into u.al */
}
if( u.al.c ){
pc = pOp->p2-1;
+ }else if( pOp->opcode==OP_Once ){
+ assert( (pIn1->flags & (MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame))==0 );
+ memAboutToChange(p, pIn1);
+ pIn1->flags = MEM_Int;
+ pIn1->u.i = 1;
+ REGISTER_TRACE(pOp->p1, pIn1);
}
break;
}
u32 szField; /* Number of bytes in the content of a field */
int szHdr; /* Size of the header size field at start of record */
int avail; /* Number of bytes of available data */
+ u32 t; /* A type code from the record header */
Mem *pReg; /* PseudoTable input register */
#endif /* local variables moved into u.am */
assert( pOp->p3>0 && pOp->p3<=p->nMem );
u.am.pDest = &aMem[pOp->p3];
memAboutToChange(p, u.am.pDest);
- MemSetTypeFlag(u.am.pDest, MEM_Null);
u.am.zRec = 0;
/* This block sets the variable u.am.payloadSize to be the total number of
rc = sqlite3BtreeDataSize(u.am.pCrsr, &u.am.payloadSize);
assert( rc==SQLITE_OK ); /* DataSize() cannot fail */
}
- }else if( u.am.pC->pseudoTableReg>0 ){
+ }else if( ALWAYS(u.am.pC->pseudoTableReg>0) ){
u.am.pReg = &aMem[u.am.pC->pseudoTableReg];
assert( u.am.pReg->flags & MEM_Blob );
assert( memIsValid(u.am.pReg) );
u.am.payloadSize = 0;
}
- /* If u.am.payloadSize is 0, then just store a NULL */
+ /* If u.am.payloadSize is 0, then just store a NULL. This can happen because of
+ ** nullRow or because of a corrupt database. */
if( u.am.payloadSize==0 ){
- assert( u.am.pDest->flags&MEM_Null );
+ MemSetTypeFlag(u.am.pDest, MEM_Null);
goto op_column_out;
}
assert( db->aLimit[SQLITE_LIMIT_LENGTH]>=0 );
for(u.am.i=0; u.am.i<u.am.nField; u.am.i++){
if( u.am.zIdx<u.am.zEndHdr ){
u.am.aOffset[u.am.i] = u.am.offset;
- u.am.zIdx += getVarint32(u.am.zIdx, u.am.aType[u.am.i]);
- u.am.szField = sqlite3VdbeSerialTypeLen(u.am.aType[u.am.i]);
+ if( u.am.zIdx[0]<0x80 ){
+ u.am.t = u.am.zIdx[0];
+ u.am.zIdx++;
+ }else{
+ u.am.zIdx += sqlite3GetVarint32(u.am.zIdx, &u.am.t);
+ }
+ u.am.aType[u.am.i] = u.am.t;
+ u.am.szField = sqlite3VdbeSerialTypeLen(u.am.t);
u.am.offset += u.am.szField;
if( u.am.offset<u.am.szField ){ /* True if u.am.offset overflows */
u.am.zIdx = &u.am.zEndHdr[1]; /* Forces SQLITE_CORRUPT return below */
if( u.am.aOffset[u.am.p2] ){
assert( rc==SQLITE_OK );
if( u.am.zRec ){
- sqlite3VdbeMemReleaseExternal(u.am.pDest);
+ MemReleaseExt(u.am.pDest);
sqlite3VdbeSerialGet((u8 *)&u.am.zRec[u.am.aOffset[u.am.p2]], u.am.aType[u.am.p2], u.am.pDest);
}else{
u.am.len = sqlite3VdbeSerialTypeLen(u.am.aType[u.am.p2]);
if( pOp->p4type==P4_MEM ){
sqlite3VdbeMemShallowCopy(u.am.pDest, pOp->p4.pMem, MEM_Static);
}else{
- assert( u.am.pDest->flags&MEM_Null );
+ MemSetTypeFlag(u.am.pDest, MEM_Null);
}
}
#endif /* local variables moved into u.ap */
u.ap.pCrsr = p->apCsr[pOp->p1]->pCursor;
- if( u.ap.pCrsr ){
+ if( ALWAYS(u.ap.pCrsr) ){
rc = sqlite3BtreeCount(u.ap.pCrsr, &u.ap.nEntry);
}else{
u.ap.nEntry = 0;
}else{
u.aq.nName = sqlite3Strlen30(u.aq.zName);
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ /* This call is Ok even if this savepoint is actually a transaction
+ ** savepoint (and therefore should not prompt xSavepoint()) callbacks.
+ ** If this is a transaction savepoint being opened, it is guaranteed
+ ** that the db->aVTrans[] array is empty. */
+ assert( db->autoCommit==0 || db->nVTrans==0 );
+ rc = sqlite3VtabSavepoint(db, SAVEPOINT_BEGIN,
+ db->nStatement+db->nSavepoint);
+ if( rc!=SQLITE_OK ) goto abort_due_to_error;
+#endif
+
/* Create a new savepoint structure. */
u.aq.pNew = sqlite3DbMallocRaw(db, sizeof(Savepoint)+u.aq.nName+1);
if( u.aq.pNew ){
}else{
db->nDeferredCons = u.aq.pSavepoint->nDeferredCons;
}
+
+ if( !isTransaction ){
+ rc = sqlite3VtabSavepoint(db, u.aq.p1, u.aq.iSavepoint);
+ if( rc!=SQLITE_OK ) goto abort_due_to_error;
+ }
}
}
db->nStatement++;
p->iStatement = db->nSavepoint + db->nStatement;
}
- rc = sqlite3BtreeBeginStmt(u.as.pBt, p->iStatement);
+
+ rc = sqlite3VtabSavepoint(db, SAVEPOINT_BEGIN, p->iStatement-1);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3BtreeBeginStmt(u.as.pBt, p->iStatement);
+ }
/* Store the current value of the database handles deferred constraint
** counter. If the statement transaction needs to be rolled back,
rc = sqlite3BtreeCursor(u.aw.pX, u.aw.p2, u.aw.wrFlag, u.aw.pKeyInfo, u.aw.pCur->pCursor);
u.aw.pCur->pKeyInfo = u.aw.pKeyInfo;
- /* Since it performs no memory allocation or IO, the only values that
- ** sqlite3BtreeCursor() may return are SQLITE_EMPTY and SQLITE_OK.
- ** SQLITE_EMPTY is only returned when attempting to open the table
- ** rooted at page 1 of a zero-byte database. */
- assert( rc==SQLITE_EMPTY || rc==SQLITE_OK );
- if( rc==SQLITE_EMPTY ){
- u.aw.pCur->pCursor = 0;
- rc = SQLITE_OK;
- }
+ /* Since it performs no memory allocation or IO, the only value that
+ ** sqlite3BtreeCursor() may return is SQLITE_OK. */
+ assert( rc==SQLITE_OK );
/* Set the VdbeCursor.isTable and isIndex variables. Previous versions of
** SQLite used to check if the root-page flags were sane at this point
break;
}
-/* Opcode: OpenEphemeral P1 P2 * P4 *
+/* Opcode: OpenEphemeral P1 P2 * P4 P5
**
** Open a new cursor P1 to a transient table.
** The cursor is always opened read/write even if
** to a TEMP table at the SQL level, or to a table opened by
** this opcode. Then this opcode was call OpenVirtual. But
** that created confusion with the whole virtual-table idea.
+**
+** The P5 parameter can be a mask of the BTREE_* flags defined
+** in btree.h. These flags control aspects of the operation of
+** the btree. The BTREE_OMIT_JOURNAL and BTREE_SINGLE flags are
+** added automatically.
*/
/* Opcode: OpenAutoindex P1 P2 * P4 *
**
u.ax.pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1);
if( u.ax.pCx==0 ) goto no_mem;
u.ax.pCx->nullRow = 1;
- rc = sqlite3BtreeOpen(0, db, &u.ax.pCx->pBt,
+ rc = sqlite3BtreeOpen(db->pVfs, 0, db, &u.ax.pCx->pBt,
BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags);
if( rc==SQLITE_OK ){
rc = sqlite3BtreeBeginTrans(u.ax.pCx->pBt, 1);
if( pOp->p4.pKeyInfo ){
int pgno;
assert( pOp->p4type==P4_KEYINFO );
- rc = sqlite3BtreeCreateTable(u.ax.pCx->pBt, &pgno, BTREE_BLOBKEY);
+ rc = sqlite3BtreeCreateTable(u.ax.pCx->pBt, &pgno, BTREE_BLOBKEY | pOp->p5);
if( rc==SQLITE_OK ){
assert( pgno==MASTER_ROOT+1 );
rc = sqlite3BtreeCursor(u.ax.pCx->pBt, pgno, 1,
break;
}
+/* Opcode: OpenSorter P1 P2 * P4 *
+**
+** This opcode works like OP_OpenEphemeral except that it opens
+** a transient index that is specifically designed to sort large
+** tables using an external merge-sort algorithm.
+*/
+case OP_SorterOpen: {
+#if 0 /* local variables moved into u.ay */
+ VdbeCursor *pCx;
+#endif /* local variables moved into u.ay */
+#ifndef SQLITE_OMIT_MERGE_SORT
+ u.ay.pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1);
+ if( u.ay.pCx==0 ) goto no_mem;
+ u.ay.pCx->pKeyInfo = pOp->p4.pKeyInfo;
+ u.ay.pCx->pKeyInfo->enc = ENC(p->db);
+ u.ay.pCx->isSorter = 1;
+ rc = sqlite3VdbeSorterInit(db, u.ay.pCx);
+#else
+ pOp->opcode = OP_OpenEphemeral;
+ pc--;
+#endif
+ break;
+}
+
/* Opcode: OpenPseudo P1 P2 P3 * *
**
** Open a new cursor that points to a fake table that contains a single
** the pseudo-table.
*/
case OP_OpenPseudo: {
-#if 0 /* local variables moved into u.ay */
+#if 0 /* local variables moved into u.az */
VdbeCursor *pCx;
-#endif /* local variables moved into u.ay */
+#endif /* local variables moved into u.az */
assert( pOp->p1>=0 );
- u.ay.pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, 0);
- if( u.ay.pCx==0 ) goto no_mem;
- u.ay.pCx->nullRow = 1;
- u.ay.pCx->pseudoTableReg = pOp->p2;
- u.ay.pCx->isTable = 1;
- u.ay.pCx->isIndex = 0;
+ u.az.pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, 0);
+ if( u.az.pCx==0 ) goto no_mem;
+ u.az.pCx->nullRow = 1;
+ u.az.pCx->pseudoTableReg = pOp->p2;
+ u.az.pCx->isTable = 1;
+ u.az.pCx->isIndex = 0;
break;
}
case OP_SeekLe: /* jump, in3 */
case OP_SeekGe: /* jump, in3 */
case OP_SeekGt: { /* jump, in3 */
-#if 0 /* local variables moved into u.az */
+#if 0 /* local variables moved into u.ba */
int res;
int oc;
VdbeCursor *pC;
UnpackedRecord r;
int nField;
i64 iKey; /* The rowid we are to seek to */
-#endif /* local variables moved into u.az */
+#endif /* local variables moved into u.ba */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
assert( pOp->p2!=0 );
- u.az.pC = p->apCsr[pOp->p1];
- assert( u.az.pC!=0 );
- assert( u.az.pC->pseudoTableReg==0 );
+ u.ba.pC = p->apCsr[pOp->p1];
+ assert( u.ba.pC!=0 );
+ assert( u.ba.pC->pseudoTableReg==0 );
assert( OP_SeekLe == OP_SeekLt+1 );
assert( OP_SeekGe == OP_SeekLt+2 );
assert( OP_SeekGt == OP_SeekLt+3 );
- assert( u.az.pC->isOrdered );
- if( u.az.pC->pCursor!=0 ){
- u.az.oc = pOp->opcode;
- u.az.pC->nullRow = 0;
- if( u.az.pC->isTable ){
+ assert( u.ba.pC->isOrdered );
+ if( ALWAYS(u.ba.pC->pCursor!=0) ){
+ u.ba.oc = pOp->opcode;
+ u.ba.pC->nullRow = 0;
+ if( u.ba.pC->isTable ){
/* The input value in P3 might be of any type: integer, real, string,
** blob, or NULL. But it needs to be an integer before we can do
** the seek, so covert it. */
pIn3 = &aMem[pOp->p3];
applyNumericAffinity(pIn3);
- u.az.iKey = sqlite3VdbeIntValue(pIn3);
- u.az.pC->rowidIsValid = 0;
+ u.ba.iKey = sqlite3VdbeIntValue(pIn3);
+ u.ba.pC->rowidIsValid = 0;
/* If the P3 value could not be converted into an integer without
** loss of information, then special processing is required... */
** point number. */
assert( (pIn3->flags & MEM_Real)!=0 );
- if( u.az.iKey==SMALLEST_INT64 && (pIn3->r<(double)u.az.iKey || pIn3->r>0) ){
+ if( u.ba.iKey==SMALLEST_INT64 && (pIn3->r<(double)u.ba.iKey || pIn3->r>0) ){
/* The P3 value is too large in magnitude to be expressed as an
** integer. */
- u.az.res = 1;
+ u.ba.res = 1;
if( pIn3->r<0 ){
- if( u.az.oc>=OP_SeekGe ){ assert( u.az.oc==OP_SeekGe || u.az.oc==OP_SeekGt );
- rc = sqlite3BtreeFirst(u.az.pC->pCursor, &u.az.res);
+ if( u.ba.oc>=OP_SeekGe ){ assert( u.ba.oc==OP_SeekGe || u.ba.oc==OP_SeekGt );
+ rc = sqlite3BtreeFirst(u.ba.pC->pCursor, &u.ba.res);
if( rc!=SQLITE_OK ) goto abort_due_to_error;
}
}else{
- if( u.az.oc<=OP_SeekLe ){ assert( u.az.oc==OP_SeekLt || u.az.oc==OP_SeekLe );
- rc = sqlite3BtreeLast(u.az.pC->pCursor, &u.az.res);
+ if( u.ba.oc<=OP_SeekLe ){ assert( u.ba.oc==OP_SeekLt || u.ba.oc==OP_SeekLe );
+ rc = sqlite3BtreeLast(u.ba.pC->pCursor, &u.ba.res);
if( rc!=SQLITE_OK ) goto abort_due_to_error;
}
}
- if( u.az.res ){
+ if( u.ba.res ){
pc = pOp->p2 - 1;
}
break;
- }else if( u.az.oc==OP_SeekLt || u.az.oc==OP_SeekGe ){
+ }else if( u.ba.oc==OP_SeekLt || u.ba.oc==OP_SeekGe ){
/* Use the ceiling() function to convert real->int */
- if( pIn3->r > (double)u.az.iKey ) u.az.iKey++;
+ if( pIn3->r > (double)u.ba.iKey ) u.ba.iKey++;
}else{
/* Use the floor() function to convert real->int */
- assert( u.az.oc==OP_SeekLe || u.az.oc==OP_SeekGt );
- if( pIn3->r < (double)u.az.iKey ) u.az.iKey--;
+ assert( u.ba.oc==OP_SeekLe || u.ba.oc==OP_SeekGt );
+ if( pIn3->r < (double)u.ba.iKey ) u.ba.iKey--;
}
}
- rc = sqlite3BtreeMovetoUnpacked(u.az.pC->pCursor, 0, (u64)u.az.iKey, 0, &u.az.res);
+ rc = sqlite3BtreeMovetoUnpacked(u.ba.pC->pCursor, 0, (u64)u.ba.iKey, 0, &u.ba.res);
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
- if( u.az.res==0 ){
- u.az.pC->rowidIsValid = 1;
- u.az.pC->lastRowid = u.az.iKey;
+ if( u.ba.res==0 ){
+ u.ba.pC->rowidIsValid = 1;
+ u.ba.pC->lastRowid = u.ba.iKey;
}
}else{
- u.az.nField = pOp->p4.i;
+ u.ba.nField = pOp->p4.i;
assert( pOp->p4type==P4_INT32 );
- assert( u.az.nField>0 );
- u.az.r.pKeyInfo = u.az.pC->pKeyInfo;
- u.az.r.nField = (u16)u.az.nField;
+ assert( u.ba.nField>0 );
+ u.ba.r.pKeyInfo = u.ba.pC->pKeyInfo;
+ u.ba.r.nField = (u16)u.ba.nField;
/* The next line of code computes as follows, only faster:
- ** if( u.az.oc==OP_SeekGt || u.az.oc==OP_SeekLe ){
- ** u.az.r.flags = UNPACKED_INCRKEY;
+ ** if( u.ba.oc==OP_SeekGt || u.ba.oc==OP_SeekLe ){
+ ** u.ba.r.flags = UNPACKED_INCRKEY;
** }else{
- ** u.az.r.flags = 0;
+ ** u.ba.r.flags = 0;
** }
*/
- u.az.r.flags = (u16)(UNPACKED_INCRKEY * (1 & (u.az.oc - OP_SeekLt)));
- assert( u.az.oc!=OP_SeekGt || u.az.r.flags==UNPACKED_INCRKEY );
- assert( u.az.oc!=OP_SeekLe || u.az.r.flags==UNPACKED_INCRKEY );
- assert( u.az.oc!=OP_SeekGe || u.az.r.flags==0 );
- assert( u.az.oc!=OP_SeekLt || u.az.r.flags==0 );
+ u.ba.r.flags = (u16)(UNPACKED_INCRKEY * (1 & (u.ba.oc - OP_SeekLt)));
+ assert( u.ba.oc!=OP_SeekGt || u.ba.r.flags==UNPACKED_INCRKEY );
+ assert( u.ba.oc!=OP_SeekLe || u.ba.r.flags==UNPACKED_INCRKEY );
+ assert( u.ba.oc!=OP_SeekGe || u.ba.r.flags==0 );
+ assert( u.ba.oc!=OP_SeekLt || u.ba.r.flags==0 );
- u.az.r.aMem = &aMem[pOp->p3];
+ u.ba.r.aMem = &aMem[pOp->p3];
#ifdef SQLITE_DEBUG
- { int i; for(i=0; i<u.az.r.nField; i++) assert( memIsValid(&u.az.r.aMem[i]) ); }
+ { int i; for(i=0; i<u.ba.r.nField; i++) assert( memIsValid(&u.ba.r.aMem[i]) ); }
#endif
- ExpandBlob(u.az.r.aMem);
- rc = sqlite3BtreeMovetoUnpacked(u.az.pC->pCursor, &u.az.r, 0, 0, &u.az.res);
+ ExpandBlob(u.ba.r.aMem);
+ rc = sqlite3BtreeMovetoUnpacked(u.ba.pC->pCursor, &u.ba.r, 0, 0, &u.ba.res);
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
- u.az.pC->rowidIsValid = 0;
+ u.ba.pC->rowidIsValid = 0;
}
- u.az.pC->deferredMoveto = 0;
- u.az.pC->cacheStatus = CACHE_STALE;
+ u.ba.pC->deferredMoveto = 0;
+ u.ba.pC->cacheStatus = CACHE_STALE;
#ifdef SQLITE_TEST
sqlite3_search_count++;
#endif
- if( u.az.oc>=OP_SeekGe ){ assert( u.az.oc==OP_SeekGe || u.az.oc==OP_SeekGt );
- if( u.az.res<0 || (u.az.res==0 && u.az.oc==OP_SeekGt) ){
- rc = sqlite3BtreeNext(u.az.pC->pCursor, &u.az.res);
+ if( u.ba.oc>=OP_SeekGe ){ assert( u.ba.oc==OP_SeekGe || u.ba.oc==OP_SeekGt );
+ if( u.ba.res<0 || (u.ba.res==0 && u.ba.oc==OP_SeekGt) ){
+ rc = sqlite3BtreeNext(u.ba.pC->pCursor, &u.ba.res);
if( rc!=SQLITE_OK ) goto abort_due_to_error;
- u.az.pC->rowidIsValid = 0;
+ u.ba.pC->rowidIsValid = 0;
}else{
- u.az.res = 0;
+ u.ba.res = 0;
}
}else{
- assert( u.az.oc==OP_SeekLt || u.az.oc==OP_SeekLe );
- if( u.az.res>0 || (u.az.res==0 && u.az.oc==OP_SeekLt) ){
- rc = sqlite3BtreePrevious(u.az.pC->pCursor, &u.az.res);
+ assert( u.ba.oc==OP_SeekLt || u.ba.oc==OP_SeekLe );
+ if( u.ba.res>0 || (u.ba.res==0 && u.ba.oc==OP_SeekLt) ){
+ rc = sqlite3BtreePrevious(u.ba.pC->pCursor, &u.ba.res);
if( rc!=SQLITE_OK ) goto abort_due_to_error;
- u.az.pC->rowidIsValid = 0;
+ u.ba.pC->rowidIsValid = 0;
}else{
- /* u.az.res might be negative because the table is empty. Check to
+ /* u.ba.res might be negative because the table is empty. Check to
** see if this is the case.
*/
- u.az.res = sqlite3BtreeEof(u.az.pC->pCursor);
+ u.ba.res = sqlite3BtreeEof(u.ba.pC->pCursor);
}
}
assert( pOp->p2>0 );
- if( u.az.res ){
+ if( u.ba.res ){
pc = pOp->p2 - 1;
}
}else{
** occur, no unnecessary I/O happens.
*/
case OP_Seek: { /* in2 */
-#if 0 /* local variables moved into u.ba */
+#if 0 /* local variables moved into u.bb */
VdbeCursor *pC;
-#endif /* local variables moved into u.ba */
+#endif /* local variables moved into u.bb */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- u.ba.pC = p->apCsr[pOp->p1];
- assert( u.ba.pC!=0 );
- if( ALWAYS(u.ba.pC->pCursor!=0) ){
- assert( u.ba.pC->isTable );
- u.ba.pC->nullRow = 0;
+ u.bb.pC = p->apCsr[pOp->p1];
+ assert( u.bb.pC!=0 );
+ if( ALWAYS(u.bb.pC->pCursor!=0) ){
+ assert( u.bb.pC->isTable );
+ u.bb.pC->nullRow = 0;
pIn2 = &aMem[pOp->p2];
- u.ba.pC->movetoTarget = sqlite3VdbeIntValue(pIn2);
- u.ba.pC->rowidIsValid = 0;
- u.ba.pC->deferredMoveto = 1;
+ u.bb.pC->movetoTarget = sqlite3VdbeIntValue(pIn2);
+ u.bb.pC->rowidIsValid = 0;
+ u.bb.pC->deferredMoveto = 1;
}
break;
}
*/
case OP_NotFound: /* jump, in3 */
case OP_Found: { /* jump, in3 */
-#if 0 /* local variables moved into u.bb */
+#if 0 /* local variables moved into u.bc */
int alreadyExists;
VdbeCursor *pC;
int res;
+ char *pFree;
UnpackedRecord *pIdxKey;
UnpackedRecord r;
char aTempRec[ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*3 + 7];
-#endif /* local variables moved into u.bb */
+#endif /* local variables moved into u.bc */
#ifdef SQLITE_TEST
sqlite3_found_count++;
#endif
- u.bb.alreadyExists = 0;
+ u.bc.alreadyExists = 0;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
assert( pOp->p4type==P4_INT32 );
- u.bb.pC = p->apCsr[pOp->p1];
- assert( u.bb.pC!=0 );
+ u.bc.pC = p->apCsr[pOp->p1];
+ assert( u.bc.pC!=0 );
pIn3 = &aMem[pOp->p3];
- if( ALWAYS(u.bb.pC->pCursor!=0) ){
+ if( ALWAYS(u.bc.pC->pCursor!=0) ){
- assert( u.bb.pC->isTable==0 );
+ assert( u.bc.pC->isTable==0 );
if( pOp->p4.i>0 ){
- u.bb.r.pKeyInfo = u.bb.pC->pKeyInfo;
- u.bb.r.nField = (u16)pOp->p4.i;
- u.bb.r.aMem = pIn3;
+ u.bc.r.pKeyInfo = u.bc.pC->pKeyInfo;
+ u.bc.r.nField = (u16)pOp->p4.i;
+ u.bc.r.aMem = pIn3;
#ifdef SQLITE_DEBUG
- { int i; for(i=0; i<u.bb.r.nField; i++) assert( memIsValid(&u.bb.r.aMem[i]) ); }
+ { int i; for(i=0; i<u.bc.r.nField; i++) assert( memIsValid(&u.bc.r.aMem[i]) ); }
#endif
- u.bb.r.flags = UNPACKED_PREFIX_MATCH;
- u.bb.pIdxKey = &u.bb.r;
+ u.bc.r.flags = UNPACKED_PREFIX_MATCH;
+ u.bc.pIdxKey = &u.bc.r;
}else{
+ u.bc.pIdxKey = sqlite3VdbeAllocUnpackedRecord(
+ u.bc.pC->pKeyInfo, u.bc.aTempRec, sizeof(u.bc.aTempRec), &u.bc.pFree
+ );
+ if( u.bc.pIdxKey==0 ) goto no_mem;
assert( pIn3->flags & MEM_Blob );
assert( (pIn3->flags & MEM_Zero)==0 ); /* zeroblobs already expanded */
- u.bb.pIdxKey = sqlite3VdbeRecordUnpack(u.bb.pC->pKeyInfo, pIn3->n, pIn3->z,
- u.bb.aTempRec, sizeof(u.bb.aTempRec));
- if( u.bb.pIdxKey==0 ){
- goto no_mem;
- }
- u.bb.pIdxKey->flags |= UNPACKED_PREFIX_MATCH;
+ sqlite3VdbeRecordUnpack(u.bc.pC->pKeyInfo, pIn3->n, pIn3->z, u.bc.pIdxKey);
+ u.bc.pIdxKey->flags |= UNPACKED_PREFIX_MATCH;
}
- rc = sqlite3BtreeMovetoUnpacked(u.bb.pC->pCursor, u.bb.pIdxKey, 0, 0, &u.bb.res);
+ rc = sqlite3BtreeMovetoUnpacked(u.bc.pC->pCursor, u.bc.pIdxKey, 0, 0, &u.bc.res);
if( pOp->p4.i==0 ){
- sqlite3VdbeDeleteUnpackedRecord(u.bb.pIdxKey);
+ sqlite3DbFree(db, u.bc.pFree);
}
if( rc!=SQLITE_OK ){
break;
}
- u.bb.alreadyExists = (u.bb.res==0);
- u.bb.pC->deferredMoveto = 0;
- u.bb.pC->cacheStatus = CACHE_STALE;
+ u.bc.alreadyExists = (u.bc.res==0);
+ u.bc.pC->deferredMoveto = 0;
+ u.bc.pC->cacheStatus = CACHE_STALE;
}
if( pOp->opcode==OP_Found ){
- if( u.bb.alreadyExists ) pc = pOp->p2 - 1;
+ if( u.bc.alreadyExists ) pc = pOp->p2 - 1;
}else{
- if( !u.bb.alreadyExists ) pc = pOp->p2 - 1;
+ if( !u.bc.alreadyExists ) pc = pOp->p2 - 1;
}
break;
}
** See also: NotFound, NotExists, Found
*/
case OP_IsUnique: { /* jump, in3 */
-#if 0 /* local variables moved into u.bc */
+#if 0 /* local variables moved into u.bd */
u16 ii;
VdbeCursor *pCx;
BtCursor *pCrsr;
Mem *aMx;
UnpackedRecord r; /* B-Tree index search key */
i64 R; /* Rowid stored in register P3 */
-#endif /* local variables moved into u.bc */
+#endif /* local variables moved into u.bd */
pIn3 = &aMem[pOp->p3];
- u.bc.aMx = &aMem[pOp->p4.i];
+ u.bd.aMx = &aMem[pOp->p4.i];
/* 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 );
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
/* Find the index cursor. */
- u.bc.pCx = p->apCsr[pOp->p1];
- assert( u.bc.pCx->deferredMoveto==0 );
- u.bc.pCx->seekResult = 0;
- u.bc.pCx->cacheStatus = CACHE_STALE;
- u.bc.pCrsr = u.bc.pCx->pCursor;
+ u.bd.pCx = p->apCsr[pOp->p1];
+ assert( u.bd.pCx->deferredMoveto==0 );
+ u.bd.pCx->seekResult = 0;
+ u.bd.pCx->cacheStatus = CACHE_STALE;
+ u.bd.pCrsr = u.bd.pCx->pCursor;
/* If any of the values are NULL, take the jump. */
- u.bc.nField = u.bc.pCx->pKeyInfo->nField;
- for(u.bc.ii=0; u.bc.ii<u.bc.nField; u.bc.ii++){
- if( u.bc.aMx[u.bc.ii].flags & MEM_Null ){
+ u.bd.nField = u.bd.pCx->pKeyInfo->nField;
+ for(u.bd.ii=0; u.bd.ii<u.bd.nField; u.bd.ii++){
+ if( u.bd.aMx[u.bd.ii].flags & MEM_Null ){
pc = pOp->p2 - 1;
- u.bc.pCrsr = 0;
+ u.bd.pCrsr = 0;
break;
}
}
- assert( (u.bc.aMx[u.bc.nField].flags & MEM_Null)==0 );
+ assert( (u.bd.aMx[u.bd.nField].flags & MEM_Null)==0 );
- if( u.bc.pCrsr!=0 ){
+ if( u.bd.pCrsr!=0 ){
/* Populate the index search key. */
- u.bc.r.pKeyInfo = u.bc.pCx->pKeyInfo;
- u.bc.r.nField = u.bc.nField + 1;
- u.bc.r.flags = UNPACKED_PREFIX_SEARCH;
- u.bc.r.aMem = u.bc.aMx;
+ u.bd.r.pKeyInfo = u.bd.pCx->pKeyInfo;
+ u.bd.r.nField = u.bd.nField + 1;
+ u.bd.r.flags = UNPACKED_PREFIX_SEARCH;
+ u.bd.r.aMem = u.bd.aMx;
#ifdef SQLITE_DEBUG
- { int i; for(i=0; i<u.bc.r.nField; i++) assert( memIsValid(&u.bc.r.aMem[i]) ); }
+ { int i; for(i=0; i<u.bd.r.nField; i++) assert( memIsValid(&u.bd.r.aMem[i]) ); }
#endif
- /* Extract the value of u.bc.R from register P3. */
+ /* Extract the value of u.bd.R from register P3. */
sqlite3VdbeMemIntegerify(pIn3);
- u.bc.R = pIn3->u.i;
+ u.bd.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(u.bc.pCrsr, &u.bc.r, 0, 0, &u.bc.pCx->seekResult);
- if( (u.bc.r.flags & UNPACKED_PREFIX_SEARCH) || u.bc.r.rowid==u.bc.R ){
+ rc = sqlite3BtreeMovetoUnpacked(u.bd.pCrsr, &u.bd.r, 0, 0, &u.bd.pCx->seekResult);
+ if( (u.bd.r.flags & UNPACKED_PREFIX_SEARCH) || u.bd.r.rowid==u.bd.R ){
pc = pOp->p2 - 1;
}else{
- pIn3->u.i = u.bc.r.rowid;
+ pIn3->u.i = u.bd.r.rowid;
}
}
break;
/* Opcode: NotExists P1 P2 P3 * *
**
-** Use the content of register P3 as a integer key. If a record
+** Use the content of register P3 as an integer key. If a record
** with that key does not exist in table of P1, then jump to P2.
** If the record does exist, then fall through. The cursor is left
** pointing to the record if it exists.
** See also: Found, NotFound, IsUnique
*/
case OP_NotExists: { /* jump, in3 */
-#if 0 /* local variables moved into u.bd */
+#if 0 /* local variables moved into u.be */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
u64 iKey;
-#endif /* local variables moved into u.bd */
+#endif /* local variables moved into u.be */
pIn3 = &aMem[pOp->p3];
assert( pIn3->flags & MEM_Int );
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- u.bd.pC = p->apCsr[pOp->p1];
- assert( u.bd.pC!=0 );
- assert( u.bd.pC->isTable );
- assert( u.bd.pC->pseudoTableReg==0 );
- u.bd.pCrsr = u.bd.pC->pCursor;
- if( u.bd.pCrsr!=0 ){
- u.bd.res = 0;
- u.bd.iKey = pIn3->u.i;
- rc = sqlite3BtreeMovetoUnpacked(u.bd.pCrsr, 0, u.bd.iKey, 0, &u.bd.res);
- u.bd.pC->lastRowid = pIn3->u.i;
- u.bd.pC->rowidIsValid = u.bd.res==0 ?1:0;
- u.bd.pC->nullRow = 0;
- u.bd.pC->cacheStatus = CACHE_STALE;
- u.bd.pC->deferredMoveto = 0;
- if( u.bd.res!=0 ){
+ u.be.pC = p->apCsr[pOp->p1];
+ assert( u.be.pC!=0 );
+ assert( u.be.pC->isTable );
+ assert( u.be.pC->pseudoTableReg==0 );
+ u.be.pCrsr = u.be.pC->pCursor;
+ if( ALWAYS(u.be.pCrsr!=0) ){
+ u.be.res = 0;
+ u.be.iKey = pIn3->u.i;
+ rc = sqlite3BtreeMovetoUnpacked(u.be.pCrsr, 0, u.be.iKey, 0, &u.be.res);
+ u.be.pC->lastRowid = pIn3->u.i;
+ u.be.pC->rowidIsValid = u.be.res==0 ?1:0;
+ u.be.pC->nullRow = 0;
+ u.be.pC->cacheStatus = CACHE_STALE;
+ u.be.pC->deferredMoveto = 0;
+ if( u.be.res!=0 ){
pc = pOp->p2 - 1;
- assert( u.bd.pC->rowidIsValid==0 );
+ assert( u.be.pC->rowidIsValid==0 );
}
- u.bd.pC->seekResult = u.bd.res;
+ u.be.pC->seekResult = u.be.res;
}else{
/* This happens when an attempt to open a read cursor on the
** sqlite_master table returns SQLITE_EMPTY.
*/
pc = pOp->p2 - 1;
- assert( u.bd.pC->rowidIsValid==0 );
- u.bd.pC->seekResult = 0;
+ assert( u.be.pC->rowidIsValid==0 );
+ u.be.pC->seekResult = 0;
}
break;
}
** If P3>0 then P3 is a register in the root frame of this VDBE that holds
** the largest previously generated record number. No new record numbers are
** allowed to be less than this value. When this value reaches its maximum,
-** a SQLITE_FULL error is generated. The P3 register is updated with the '
+** an SQLITE_FULL error is generated. The P3 register is updated with the '
** generated record number. This P3 mechanism is used to help implement the
** AUTOINCREMENT feature.
*/
case OP_NewRowid: { /* out2-prerelease */
-#if 0 /* local variables moved into u.be */
+#if 0 /* local variables moved into u.bf */
i64 v; /* The new rowid */
VdbeCursor *pC; /* Cursor of table to get the new rowid */
int res; /* Result of an sqlite3BtreeLast() */
int cnt; /* Counter to limit the number of searches */
Mem *pMem; /* Register holding largest rowid for AUTOINCREMENT */
VdbeFrame *pFrame; /* Root frame of VDBE */
-#endif /* local variables moved into u.be */
+#endif /* local variables moved into u.bf */
- u.be.v = 0;
- u.be.res = 0;
+ u.bf.v = 0;
+ u.bf.res = 0;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- u.be.pC = p->apCsr[pOp->p1];
- assert( u.be.pC!=0 );
- if( NEVER(u.be.pC->pCursor==0) ){
+ u.bf.pC = p->apCsr[pOp->p1];
+ assert( u.bf.pC!=0 );
+ if( NEVER(u.bf.pC->pCursor==0) ){
/* The zero initialization above is all that is needed */
}else{
/* The next rowid or record number (different terms for the same
** succeeded. If the random rowid does exist, we select a new one
** and try again, up to 100 times.
*/
- assert( u.be.pC->isTable );
+ assert( u.bf.pC->isTable );
#ifdef SQLITE_32BIT_ROWID
# define MAX_ROWID 0x7fffffff
# define MAX_ROWID (i64)( (((u64)0x7fffffff)<<32) | (u64)0xffffffff )
#endif
- if( !u.be.pC->useRandomRowid ){
- u.be.v = sqlite3BtreeGetCachedRowid(u.be.pC->pCursor);
- if( u.be.v==0 ){
- rc = sqlite3BtreeLast(u.be.pC->pCursor, &u.be.res);
+ if( !u.bf.pC->useRandomRowid ){
+ u.bf.v = sqlite3BtreeGetCachedRowid(u.bf.pC->pCursor);
+ if( u.bf.v==0 ){
+ rc = sqlite3BtreeLast(u.bf.pC->pCursor, &u.bf.res);
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
- if( u.be.res ){
- u.be.v = 1; /* IMP: R-61914-48074 */
+ if( u.bf.res ){
+ u.bf.v = 1; /* IMP: R-61914-48074 */
}else{
- assert( sqlite3BtreeCursorIsValid(u.be.pC->pCursor) );
- rc = sqlite3BtreeKeySize(u.be.pC->pCursor, &u.be.v);
+ assert( sqlite3BtreeCursorIsValid(u.bf.pC->pCursor) );
+ rc = sqlite3BtreeKeySize(u.bf.pC->pCursor, &u.bf.v);
assert( rc==SQLITE_OK ); /* Cannot fail following BtreeLast() */
- if( u.be.v==MAX_ROWID ){
- u.be.pC->useRandomRowid = 1;
+ if( u.bf.v==MAX_ROWID ){
+ u.bf.pC->useRandomRowid = 1;
}else{
- u.be.v++; /* IMP: R-29538-34987 */
+ u.bf.v++; /* IMP: R-29538-34987 */
}
}
}
/* Assert that P3 is a valid memory cell. */
assert( pOp->p3>0 );
if( p->pFrame ){
- for(u.be.pFrame=p->pFrame; u.be.pFrame->pParent; u.be.pFrame=u.be.pFrame->pParent);
+ for(u.bf.pFrame=p->pFrame; u.bf.pFrame->pParent; u.bf.pFrame=u.bf.pFrame->pParent);
/* Assert that P3 is a valid memory cell. */
- assert( pOp->p3<=u.be.pFrame->nMem );
- u.be.pMem = &u.be.pFrame->aMem[pOp->p3];
+ assert( pOp->p3<=u.bf.pFrame->nMem );
+ u.bf.pMem = &u.bf.pFrame->aMem[pOp->p3];
}else{
/* Assert that P3 is a valid memory cell. */
assert( pOp->p3<=p->nMem );
- u.be.pMem = &aMem[pOp->p3];
- memAboutToChange(p, u.be.pMem);
+ u.bf.pMem = &aMem[pOp->p3];
+ memAboutToChange(p, u.bf.pMem);
}
- assert( memIsValid(u.be.pMem) );
+ assert( memIsValid(u.bf.pMem) );
- REGISTER_TRACE(pOp->p3, u.be.pMem);
- sqlite3VdbeMemIntegerify(u.be.pMem);
- assert( (u.be.pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */
- if( u.be.pMem->u.i==MAX_ROWID || u.be.pC->useRandomRowid ){
+ REGISTER_TRACE(pOp->p3, u.bf.pMem);
+ sqlite3VdbeMemIntegerify(u.bf.pMem);
+ assert( (u.bf.pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */
+ if( u.bf.pMem->u.i==MAX_ROWID || u.bf.pC->useRandomRowid ){
rc = SQLITE_FULL; /* IMP: R-12275-61338 */
goto abort_due_to_error;
}
- if( u.be.v<u.be.pMem->u.i+1 ){
- u.be.v = u.be.pMem->u.i + 1;
+ if( u.bf.v<u.bf.pMem->u.i+1 ){
+ u.bf.v = u.bf.pMem->u.i + 1;
}
- u.be.pMem->u.i = u.be.v;
+ u.bf.pMem->u.i = u.bf.v;
}
#endif
- sqlite3BtreeSetCachedRowid(u.be.pC->pCursor, u.be.v<MAX_ROWID ? u.be.v+1 : 0);
+ sqlite3BtreeSetCachedRowid(u.bf.pC->pCursor, u.bf.v<MAX_ROWID ? u.bf.v+1 : 0);
}
- if( u.be.pC->useRandomRowid ){
+ if( u.bf.pC->useRandomRowid ){
/* IMPLEMENTATION-OF: R-07677-41881 If the largest ROWID is equal to the
** largest possible integer (9223372036854775807) then the database
** engine starts picking positive candidate ROWIDs at random until
assert( pOp->p3==0 ); /* We cannot be in random rowid mode if this is
** an AUTOINCREMENT table. */
/* on the first attempt, simply do one more than previous */
- u.be.v = db->lastRowid;
- u.be.v &= (MAX_ROWID>>1); /* ensure doesn't go negative */
- u.be.v++; /* ensure non-zero */
- u.be.cnt = 0;
- while( ((rc = sqlite3BtreeMovetoUnpacked(u.be.pC->pCursor, 0, (u64)u.be.v,
- 0, &u.be.res))==SQLITE_OK)
- && (u.be.res==0)
- && (++u.be.cnt<100)){
+ u.bf.v = lastRowid;
+ u.bf.v &= (MAX_ROWID>>1); /* ensure doesn't go negative */
+ u.bf.v++; /* ensure non-zero */
+ u.bf.cnt = 0;
+ while( ((rc = sqlite3BtreeMovetoUnpacked(u.bf.pC->pCursor, 0, (u64)u.bf.v,
+ 0, &u.bf.res))==SQLITE_OK)
+ && (u.bf.res==0)
+ && (++u.bf.cnt<100)){
/* collision - try another random rowid */
- sqlite3_randomness(sizeof(u.be.v), &u.be.v);
- if( u.be.cnt<5 ){
+ sqlite3_randomness(sizeof(u.bf.v), &u.bf.v);
+ if( u.bf.cnt<5 ){
/* try "small" random rowids for the initial attempts */
- u.be.v &= 0xffffff;
+ u.bf.v &= 0xffffff;
}else{
- u.be.v &= (MAX_ROWID>>1); /* ensure doesn't go negative */
+ u.bf.v &= (MAX_ROWID>>1); /* ensure doesn't go negative */
}
- u.be.v++; /* ensure non-zero */
+ u.bf.v++; /* ensure non-zero */
}
- if( rc==SQLITE_OK && u.be.res==0 ){
+ if( rc==SQLITE_OK && u.bf.res==0 ){
rc = SQLITE_FULL; /* IMP: R-38219-53002 */
goto abort_due_to_error;
}
- assert( u.be.v>0 ); /* EV: R-40812-03570 */
+ assert( u.bf.v>0 ); /* EV: R-40812-03570 */
}
- u.be.pC->rowidIsValid = 0;
- u.be.pC->deferredMoveto = 0;
- u.be.pC->cacheStatus = CACHE_STALE;
+ u.bf.pC->rowidIsValid = 0;
+ u.bf.pC->deferredMoveto = 0;
+ u.bf.pC->cacheStatus = CACHE_STALE;
}
- pOut->u.i = u.be.v;
+ pOut->u.i = u.bf.v;
break;
}
*/
case OP_Insert:
case OP_InsertInt: {
-#if 0 /* local variables moved into u.bf */
+#if 0 /* local variables moved into u.bg */
Mem *pData; /* MEM cell holding data for the record to be inserted */
Mem *pKey; /* MEM cell holding key for the record */
i64 iKey; /* The integer ROWID or key for the record to be inserted */
const char *zDb; /* database name - used by the update hook */
const char *zTbl; /* Table name - used by the opdate hook */
int op; /* Opcode for update hook: SQLITE_UPDATE or SQLITE_INSERT */
-#endif /* local variables moved into u.bf */
+#endif /* local variables moved into u.bg */
- u.bf.pData = &aMem[pOp->p2];
+ u.bg.pData = &aMem[pOp->p2];
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- assert( memIsValid(u.bf.pData) );
- u.bf.pC = p->apCsr[pOp->p1];
- assert( u.bf.pC!=0 );
- assert( u.bf.pC->pCursor!=0 );
- assert( u.bf.pC->pseudoTableReg==0 );
- assert( u.bf.pC->isTable );
- REGISTER_TRACE(pOp->p2, u.bf.pData);
+ assert( memIsValid(u.bg.pData) );
+ u.bg.pC = p->apCsr[pOp->p1];
+ assert( u.bg.pC!=0 );
+ assert( u.bg.pC->pCursor!=0 );
+ assert( u.bg.pC->pseudoTableReg==0 );
+ assert( u.bg.pC->isTable );
+ REGISTER_TRACE(pOp->p2, u.bg.pData);
if( pOp->opcode==OP_Insert ){
- u.bf.pKey = &aMem[pOp->p3];
- assert( u.bf.pKey->flags & MEM_Int );
- assert( memIsValid(u.bf.pKey) );
- REGISTER_TRACE(pOp->p3, u.bf.pKey);
- u.bf.iKey = u.bf.pKey->u.i;
+ u.bg.pKey = &aMem[pOp->p3];
+ assert( u.bg.pKey->flags & MEM_Int );
+ assert( memIsValid(u.bg.pKey) );
+ REGISTER_TRACE(pOp->p3, u.bg.pKey);
+ u.bg.iKey = u.bg.pKey->u.i;
}else{
assert( pOp->opcode==OP_InsertInt );
- u.bf.iKey = pOp->p3;
+ u.bg.iKey = pOp->p3;
}
if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
- if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = u.bf.iKey;
- if( u.bf.pData->flags & MEM_Null ){
- u.bf.pData->z = 0;
- u.bf.pData->n = 0;
+ if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = lastRowid = u.bg.iKey;
+ if( u.bg.pData->flags & MEM_Null ){
+ u.bg.pData->z = 0;
+ u.bg.pData->n = 0;
}else{
- assert( u.bf.pData->flags & (MEM_Blob|MEM_Str) );
+ assert( u.bg.pData->flags & (MEM_Blob|MEM_Str) );
}
- u.bf.seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? u.bf.pC->seekResult : 0);
- if( u.bf.pData->flags & MEM_Zero ){
- u.bf.nZero = u.bf.pData->u.nZero;
+ u.bg.seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? u.bg.pC->seekResult : 0);
+ if( u.bg.pData->flags & MEM_Zero ){
+ u.bg.nZero = u.bg.pData->u.nZero;
}else{
- u.bf.nZero = 0;
+ u.bg.nZero = 0;
}
- sqlite3BtreeSetCachedRowid(u.bf.pC->pCursor, 0);
- rc = sqlite3BtreeInsert(u.bf.pC->pCursor, 0, u.bf.iKey,
- u.bf.pData->z, u.bf.pData->n, u.bf.nZero,
- pOp->p5 & OPFLAG_APPEND, u.bf.seekResult
+ sqlite3BtreeSetCachedRowid(u.bg.pC->pCursor, 0);
+ rc = sqlite3BtreeInsert(u.bg.pC->pCursor, 0, u.bg.iKey,
+ u.bg.pData->z, u.bg.pData->n, u.bg.nZero,
+ pOp->p5 & OPFLAG_APPEND, u.bg.seekResult
);
- u.bf.pC->rowidIsValid = 0;
- u.bf.pC->deferredMoveto = 0;
- u.bf.pC->cacheStatus = CACHE_STALE;
+ u.bg.pC->rowidIsValid = 0;
+ u.bg.pC->deferredMoveto = 0;
+ u.bg.pC->cacheStatus = CACHE_STALE;
/* Invoke the update-hook if required. */
if( rc==SQLITE_OK && db->xUpdateCallback && pOp->p4.z ){
- u.bf.zDb = db->aDb[u.bf.pC->iDb].zName;
- u.bf.zTbl = pOp->p4.z;
- u.bf.op = ((pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT);
- assert( u.bf.pC->isTable );
- db->xUpdateCallback(db->pUpdateArg, u.bf.op, u.bf.zDb, u.bf.zTbl, u.bf.iKey);
- assert( u.bf.pC->iDb>=0 );
+ u.bg.zDb = db->aDb[u.bg.pC->iDb].zName;
+ u.bg.zTbl = pOp->p4.z;
+ u.bg.op = ((pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT);
+ assert( u.bg.pC->isTable );
+ db->xUpdateCallback(db->pUpdateArg, u.bg.op, u.bg.zDb, u.bg.zTbl, u.bg.iKey);
+ assert( u.bg.pC->iDb>=0 );
}
break;
}
** using OP_NotFound prior to invoking this opcode.
*/
case OP_Delete: {
-#if 0 /* local variables moved into u.bg */
+#if 0 /* local variables moved into u.bh */
i64 iKey;
VdbeCursor *pC;
-#endif /* local variables moved into u.bg */
+#endif /* local variables moved into u.bh */
- u.bg.iKey = 0;
+ u.bh.iKey = 0;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- u.bg.pC = p->apCsr[pOp->p1];
- assert( u.bg.pC!=0 );
- assert( u.bg.pC->pCursor!=0 ); /* Only valid for real tables, no pseudotables */
+ u.bh.pC = p->apCsr[pOp->p1];
+ assert( u.bh.pC!=0 );
+ assert( u.bh.pC->pCursor!=0 ); /* Only valid for real tables, no pseudotables */
- /* If the update-hook will be invoked, set u.bg.iKey to the rowid of the
+ /* If the update-hook will be invoked, set u.bh.iKey to the rowid of the
** row being deleted.
*/
if( db->xUpdateCallback && pOp->p4.z ){
- assert( u.bg.pC->isTable );
- assert( u.bg.pC->rowidIsValid ); /* lastRowid set by previous OP_NotFound */
- u.bg.iKey = u.bg.pC->lastRowid;
+ assert( u.bh.pC->isTable );
+ assert( u.bh.pC->rowidIsValid ); /* lastRowid set by previous OP_NotFound */
+ u.bh.iKey = u.bh.pC->lastRowid;
}
/* The OP_Delete opcode always follows an OP_NotExists or OP_Last or
** OP_Column on the same table without any intervening operations that
- ** might move or invalidate the cursor. Hence cursor u.bg.pC is always pointing
+ ** might move or invalidate the cursor. Hence cursor u.bh.pC is always pointing
** to the row to be deleted and the sqlite3VdbeCursorMoveto() operation
** below is always a no-op and cannot fail. We will run it anyhow, though,
** to guard against future changes to the code generator.
**/
- assert( u.bg.pC->deferredMoveto==0 );
- rc = sqlite3VdbeCursorMoveto(u.bg.pC);
+ assert( u.bh.pC->deferredMoveto==0 );
+ rc = sqlite3VdbeCursorMoveto(u.bh.pC);
if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error;
- sqlite3BtreeSetCachedRowid(u.bg.pC->pCursor, 0);
- rc = sqlite3BtreeDelete(u.bg.pC->pCursor);
- u.bg.pC->cacheStatus = CACHE_STALE;
+ sqlite3BtreeSetCachedRowid(u.bh.pC->pCursor, 0);
+ rc = sqlite3BtreeDelete(u.bh.pC->pCursor);
+ u.bh.pC->cacheStatus = CACHE_STALE;
/* Invoke the update-hook if required. */
if( rc==SQLITE_OK && db->xUpdateCallback && pOp->p4.z ){
- const char *zDb = db->aDb[u.bg.pC->iDb].zName;
+ const char *zDb = db->aDb[u.bh.pC->iDb].zName;
const char *zTbl = pOp->p4.z;
- db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, zTbl, u.bg.iKey);
- assert( u.bg.pC->iDb>=0 );
+ db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, zTbl, u.bh.iKey);
+ assert( u.bh.pC->iDb>=0 );
}
if( pOp->p2 & OPFLAG_NCHANGE ) p->nChange++;
break;
break;
}
+/* Opcode: SorterCompare P1 P2 P3
+**
+** P1 is a sorter cursor. This instruction compares the record blob in
+** register P3 with the entry that the sorter cursor currently points to.
+** If, excluding the rowid fields at the end, the two records are a match,
+** fall through to the next instruction. Otherwise, jump to instruction P2.
+*/
+case OP_SorterCompare: {
+#if 0 /* local variables moved into u.bi */
+ VdbeCursor *pC;
+ int res;
+#endif /* local variables moved into u.bi */
+
+ u.bi.pC = p->apCsr[pOp->p1];
+ assert( isSorter(u.bi.pC) );
+ pIn3 = &aMem[pOp->p3];
+ rc = sqlite3VdbeSorterCompare(u.bi.pC, pIn3, &u.bi.res);
+ if( u.bi.res ){
+ pc = pOp->p2-1;
+ }
+ break;
+};
+
+/* Opcode: SorterData P1 P2 * * *
+**
+** Write into register P2 the current sorter data for sorter cursor P1.
+*/
+case OP_SorterData: {
+#if 0 /* local variables moved into u.bj */
+ VdbeCursor *pC;
+#endif /* local variables moved into u.bj */
+#ifndef SQLITE_OMIT_MERGE_SORT
+ pOut = &aMem[pOp->p2];
+ u.bj.pC = p->apCsr[pOp->p1];
+ assert( u.bj.pC->isSorter );
+ rc = sqlite3VdbeSorterRowkey(u.bj.pC, pOut);
+#else
+ pOp->opcode = OP_RowKey;
+ pc--;
+#endif
+ break;
+}
+
/* Opcode: RowData P1 P2 * * *
**
** Write into register P2 the complete row data for cursor P1.
*/
case OP_RowKey:
case OP_RowData: {
-#if 0 /* local variables moved into u.bh */
+#if 0 /* local variables moved into u.bk */
VdbeCursor *pC;
BtCursor *pCrsr;
u32 n;
i64 n64;
-#endif /* local variables moved into u.bh */
+#endif /* local variables moved into u.bk */
pOut = &aMem[pOp->p2];
memAboutToChange(p, pOut);
/* Note that RowKey and RowData are really exactly the same instruction */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- u.bh.pC = p->apCsr[pOp->p1];
- assert( u.bh.pC->isTable || pOp->opcode==OP_RowKey );
- assert( u.bh.pC->isIndex || pOp->opcode==OP_RowData );
- assert( u.bh.pC!=0 );
- assert( u.bh.pC->nullRow==0 );
- assert( u.bh.pC->pseudoTableReg==0 );
- assert( u.bh.pC->pCursor!=0 );
- u.bh.pCrsr = u.bh.pC->pCursor;
- assert( sqlite3BtreeCursorIsValid(u.bh.pCrsr) );
+ u.bk.pC = p->apCsr[pOp->p1];
+ assert( u.bk.pC->isSorter==0 );
+ assert( u.bk.pC->isTable || pOp->opcode!=OP_RowData );
+ assert( u.bk.pC->isIndex || pOp->opcode==OP_RowData );
+ assert( u.bk.pC!=0 );
+ assert( u.bk.pC->nullRow==0 );
+ assert( u.bk.pC->pseudoTableReg==0 );
+ assert( !u.bk.pC->isSorter );
+ assert( u.bk.pC->pCursor!=0 );
+ u.bk.pCrsr = u.bk.pC->pCursor;
+ assert( sqlite3BtreeCursorIsValid(u.bk.pCrsr) );
/* The OP_RowKey and OP_RowData opcodes always follow OP_NotExists or
** OP_Rewind/Op_Next with no intervening instructions that might invalidate
** the cursor. Hence the following sqlite3VdbeCursorMoveto() call is always
** a no-op and can never fail. But we leave it in place as a safety.
*/
- assert( u.bh.pC->deferredMoveto==0 );
- rc = sqlite3VdbeCursorMoveto(u.bh.pC);
+ assert( u.bk.pC->deferredMoveto==0 );
+ rc = sqlite3VdbeCursorMoveto(u.bk.pC);
if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error;
- if( u.bh.pC->isIndex ){
- assert( !u.bh.pC->isTable );
- rc = sqlite3BtreeKeySize(u.bh.pCrsr, &u.bh.n64);
+ if( u.bk.pC->isIndex ){
+ assert( !u.bk.pC->isTable );
+ rc = sqlite3BtreeKeySize(u.bk.pCrsr, &u.bk.n64);
assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */
- if( u.bh.n64>db->aLimit[SQLITE_LIMIT_LENGTH] ){
+ if( u.bk.n64>db->aLimit[SQLITE_LIMIT_LENGTH] ){
goto too_big;
}
- u.bh.n = (u32)u.bh.n64;
+ u.bk.n = (u32)u.bk.n64;
}else{
- rc = sqlite3BtreeDataSize(u.bh.pCrsr, &u.bh.n);
+ rc = sqlite3BtreeDataSize(u.bk.pCrsr, &u.bk.n);
assert( rc==SQLITE_OK ); /* DataSize() cannot fail */
- if( u.bh.n>(u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){
+ if( u.bk.n>(u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){
goto too_big;
}
}
- if( sqlite3VdbeMemGrow(pOut, u.bh.n, 0) ){
+ if( sqlite3VdbeMemGrow(pOut, u.bk.n, 0) ){
goto no_mem;
}
- pOut->n = u.bh.n;
+ pOut->n = u.bk.n;
MemSetTypeFlag(pOut, MEM_Blob);
- if( u.bh.pC->isIndex ){
- rc = sqlite3BtreeKey(u.bh.pCrsr, 0, u.bh.n, pOut->z);
+ if( u.bk.pC->isIndex ){
+ rc = sqlite3BtreeKey(u.bk.pCrsr, 0, u.bk.n, pOut->z);
}else{
- rc = sqlite3BtreeData(u.bh.pCrsr, 0, u.bh.n, pOut->z);
+ rc = sqlite3BtreeData(u.bk.pCrsr, 0, u.bk.n, pOut->z);
}
pOut->enc = SQLITE_UTF8; /* In case the blob is ever cast to text */
UPDATE_MAX_BLOBSIZE(pOut);
** one opcode now works for both table types.
*/
case OP_Rowid: { /* out2-prerelease */
-#if 0 /* local variables moved into u.bi */
+#if 0 /* local variables moved into u.bl */
VdbeCursor *pC;
i64 v;
sqlite3_vtab *pVtab;
const sqlite3_module *pModule;
-#endif /* local variables moved into u.bi */
+#endif /* local variables moved into u.bl */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- u.bi.pC = p->apCsr[pOp->p1];
- assert( u.bi.pC!=0 );
- assert( u.bi.pC->pseudoTableReg==0 );
- if( u.bi.pC->nullRow ){
+ u.bl.pC = p->apCsr[pOp->p1];
+ assert( u.bl.pC!=0 );
+ assert( u.bl.pC->pseudoTableReg==0 );
+ if( u.bl.pC->nullRow ){
pOut->flags = MEM_Null;
break;
- }else if( u.bi.pC->deferredMoveto ){
- u.bi.v = u.bi.pC->movetoTarget;
+ }else if( u.bl.pC->deferredMoveto ){
+ u.bl.v = u.bl.pC->movetoTarget;
#ifndef SQLITE_OMIT_VIRTUALTABLE
- }else if( u.bi.pC->pVtabCursor ){
- u.bi.pVtab = u.bi.pC->pVtabCursor->pVtab;
- u.bi.pModule = u.bi.pVtab->pModule;
- assert( u.bi.pModule->xRowid );
- rc = u.bi.pModule->xRowid(u.bi.pC->pVtabCursor, &u.bi.v);
- importVtabErrMsg(p, u.bi.pVtab);
+ }else if( u.bl.pC->pVtabCursor ){
+ u.bl.pVtab = u.bl.pC->pVtabCursor->pVtab;
+ u.bl.pModule = u.bl.pVtab->pModule;
+ assert( u.bl.pModule->xRowid );
+ rc = u.bl.pModule->xRowid(u.bl.pC->pVtabCursor, &u.bl.v);
+ importVtabErrMsg(p, u.bl.pVtab);
#endif /* SQLITE_OMIT_VIRTUALTABLE */
}else{
- assert( u.bi.pC->pCursor!=0 );
- rc = sqlite3VdbeCursorMoveto(u.bi.pC);
+ assert( u.bl.pC->pCursor!=0 );
+ rc = sqlite3VdbeCursorMoveto(u.bl.pC);
if( rc ) goto abort_due_to_error;
- if( u.bi.pC->rowidIsValid ){
- u.bi.v = u.bi.pC->lastRowid;
+ if( u.bl.pC->rowidIsValid ){
+ u.bl.v = u.bl.pC->lastRowid;
}else{
- rc = sqlite3BtreeKeySize(u.bi.pC->pCursor, &u.bi.v);
+ rc = sqlite3BtreeKeySize(u.bl.pC->pCursor, &u.bl.v);
assert( rc==SQLITE_OK ); /* Always so because of CursorMoveto() above */
}
}
- pOut->u.i = u.bi.v;
+ pOut->u.i = u.bl.v;
break;
}
** write a NULL.
*/
case OP_NullRow: {
-#if 0 /* local variables moved into u.bj */
+#if 0 /* local variables moved into u.bm */
VdbeCursor *pC;
-#endif /* local variables moved into u.bj */
+#endif /* local variables moved into u.bm */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- u.bj.pC = p->apCsr[pOp->p1];
- assert( u.bj.pC!=0 );
- u.bj.pC->nullRow = 1;
- u.bj.pC->rowidIsValid = 0;
- if( u.bj.pC->pCursor ){
- sqlite3BtreeClearCursor(u.bj.pC->pCursor);
+ u.bm.pC = p->apCsr[pOp->p1];
+ assert( u.bm.pC!=0 );
+ u.bm.pC->nullRow = 1;
+ u.bm.pC->rowidIsValid = 0;
+ assert( u.bm.pC->pCursor || u.bm.pC->pVtabCursor );
+ if( u.bm.pC->pCursor ){
+ sqlite3BtreeClearCursor(u.bm.pC->pCursor);
}
break;
}
** to the following instruction.
*/
case OP_Last: { /* jump */
-#if 0 /* local variables moved into u.bk */
+#if 0 /* local variables moved into u.bn */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
-#endif /* local variables moved into u.bk */
+#endif /* local variables moved into u.bn */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- u.bk.pC = p->apCsr[pOp->p1];
- assert( u.bk.pC!=0 );
- u.bk.pCrsr = u.bk.pC->pCursor;
- if( u.bk.pCrsr==0 ){
- u.bk.res = 1;
+ u.bn.pC = p->apCsr[pOp->p1];
+ assert( u.bn.pC!=0 );
+ u.bn.pCrsr = u.bn.pC->pCursor;
+ if( NEVER(u.bn.pCrsr==0) ){
+ u.bn.res = 1;
}else{
- rc = sqlite3BtreeLast(u.bk.pCrsr, &u.bk.res);
+ rc = sqlite3BtreeLast(u.bn.pCrsr, &u.bn.res);
}
- u.bk.pC->nullRow = (u8)u.bk.res;
- u.bk.pC->deferredMoveto = 0;
- u.bk.pC->rowidIsValid = 0;
- u.bk.pC->cacheStatus = CACHE_STALE;
- if( pOp->p2>0 && u.bk.res ){
+ u.bn.pC->nullRow = (u8)u.bn.res;
+ u.bn.pC->deferredMoveto = 0;
+ u.bn.pC->rowidIsValid = 0;
+ u.bn.pC->cacheStatus = CACHE_STALE;
+ if( pOp->p2>0 && u.bn.res ){
pc = pOp->p2 - 1;
}
break;
** regression tests can determine whether or not the optimizer is
** correctly optimizing out sorts.
*/
+case OP_SorterSort: /* jump */
+#ifdef SQLITE_OMIT_MERGE_SORT
+ pOp->opcode = OP_Sort;
+#endif
case OP_Sort: { /* jump */
#ifdef SQLITE_TEST
sqlite3_sort_count++;
** to the following instruction.
*/
case OP_Rewind: { /* jump */
-#if 0 /* local variables moved into u.bl */
+#if 0 /* local variables moved into u.bo */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
-#endif /* local variables moved into u.bl */
+#endif /* local variables moved into u.bo */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- u.bl.pC = p->apCsr[pOp->p1];
- assert( u.bl.pC!=0 );
- u.bl.res = 1;
- if( (u.bl.pCrsr = u.bl.pC->pCursor)!=0 ){
- rc = sqlite3BtreeFirst(u.bl.pCrsr, &u.bl.res);
- u.bl.pC->atFirst = u.bl.res==0 ?1:0;
- u.bl.pC->deferredMoveto = 0;
- u.bl.pC->cacheStatus = CACHE_STALE;
- u.bl.pC->rowidIsValid = 0;
- }
- u.bl.pC->nullRow = (u8)u.bl.res;
+ u.bo.pC = p->apCsr[pOp->p1];
+ assert( u.bo.pC!=0 );
+ assert( u.bo.pC->isSorter==(pOp->opcode==OP_SorterSort) );
+ u.bo.res = 1;
+ if( isSorter(u.bo.pC) ){
+ rc = sqlite3VdbeSorterRewind(db, u.bo.pC, &u.bo.res);
+ }else{
+ u.bo.pCrsr = u.bo.pC->pCursor;
+ assert( u.bo.pCrsr );
+ rc = sqlite3BtreeFirst(u.bo.pCrsr, &u.bo.res);
+ u.bo.pC->atFirst = u.bo.res==0 ?1:0;
+ u.bo.pC->deferredMoveto = 0;
+ u.bo.pC->cacheStatus = CACHE_STALE;
+ u.bo.pC->rowidIsValid = 0;
+ }
+ u.bo.pC->nullRow = (u8)u.bo.res;
assert( pOp->p2>0 && pOp->p2<p->nOp );
- if( u.bl.res ){
+ if( u.bo.res ){
pc = pOp->p2 - 1;
}
break;
}
-/* Opcode: Next P1 P2 * * P5
+/* Opcode: Next P1 P2 * P4 P5
**
** Advance cursor P1 so that it points to the next key/data pair in its
** table or index. If there are no more key/value pairs then fall through
**
** The P1 cursor must be for a real table, not a pseudo-table.
**
+** P4 is always of type P4_ADVANCE. The function pointer points to
+** sqlite3BtreeNext().
+**
** If P5 is positive and the jump is taken, then event counter
** number P5-1 in the prepared statement is incremented.
**
**
** The P1 cursor must be for a real table, not a pseudo-table.
**
+** P4 is always of type P4_ADVANCE. The function pointer points to
+** sqlite3BtreePrevious().
+**
** If P5 is positive and the jump is taken, then event counter
** number P5-1 in the prepared statement is incremented.
*/
+case OP_SorterNext: /* jump */
+#ifdef SQLITE_OMIT_MERGE_SORT
+ pOp->opcode = OP_Next;
+#endif
case OP_Prev: /* jump */
case OP_Next: { /* jump */
-#if 0 /* local variables moved into u.bm */
+#if 0 /* local variables moved into u.bp */
VdbeCursor *pC;
- BtCursor *pCrsr;
int res;
-#endif /* local variables moved into u.bm */
+#endif /* local variables moved into u.bp */
CHECK_FOR_INTERRUPT;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
assert( pOp->p5<=ArraySize(p->aCounter) );
- u.bm.pC = p->apCsr[pOp->p1];
- if( u.bm.pC==0 ){
+ u.bp.pC = p->apCsr[pOp->p1];
+ if( u.bp.pC==0 ){
break; /* See ticket #2273 */
}
- u.bm.pCrsr = u.bm.pC->pCursor;
- if( u.bm.pCrsr==0 ){
- u.bm.pC->nullRow = 1;
- break;
- }
- u.bm.res = 1;
- assert( u.bm.pC->deferredMoveto==0 );
- rc = pOp->opcode==OP_Next ? sqlite3BtreeNext(u.bm.pCrsr, &u.bm.res) :
- sqlite3BtreePrevious(u.bm.pCrsr, &u.bm.res);
- u.bm.pC->nullRow = (u8)u.bm.res;
- u.bm.pC->cacheStatus = CACHE_STALE;
- if( u.bm.res==0 ){
+ assert( u.bp.pC->isSorter==(pOp->opcode==OP_SorterNext) );
+ if( isSorter(u.bp.pC) ){
+ assert( pOp->opcode==OP_SorterNext );
+ rc = sqlite3VdbeSorterNext(db, u.bp.pC, &u.bp.res);
+ }else{
+ u.bp.res = 1;
+ assert( u.bp.pC->deferredMoveto==0 );
+ assert( u.bp.pC->pCursor );
+ assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite3BtreeNext );
+ assert( pOp->opcode!=OP_Prev || pOp->p4.xAdvance==sqlite3BtreePrevious );
+ rc = pOp->p4.xAdvance(u.bp.pC->pCursor, &u.bp.res);
+ }
+ u.bp.pC->nullRow = (u8)u.bp.res;
+ u.bp.pC->cacheStatus = CACHE_STALE;
+ if( u.bp.res==0 ){
pc = pOp->p2 - 1;
if( pOp->p5 ) p->aCounter[pOp->p5-1]++;
#ifdef SQLITE_TEST
sqlite3_search_count++;
#endif
}
- u.bm.pC->rowidIsValid = 0;
+ u.bp.pC->rowidIsValid = 0;
break;
}
/* Opcode: IdxInsert P1 P2 P3 * P5
**
-** Register P2 holds a SQL index key made using the
+** Register P2 holds an SQL index key made using the
** MakeRecord instructions. This opcode writes that key
** into the index P1. Data for the entry is nil.
**
** This instruction only works for indices. The equivalent instruction
** for tables is OP_Insert.
*/
+case OP_SorterInsert: /* in2 */
+#ifdef SQLITE_OMIT_MERGE_SORT
+ pOp->opcode = OP_IdxInsert;
+#endif
case OP_IdxInsert: { /* in2 */
-#if 0 /* local variables moved into u.bn */
+#if 0 /* local variables moved into u.bq */
VdbeCursor *pC;
BtCursor *pCrsr;
int nKey;
const char *zKey;
-#endif /* local variables moved into u.bn */
+#endif /* local variables moved into u.bq */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- u.bn.pC = p->apCsr[pOp->p1];
- assert( u.bn.pC!=0 );
+ u.bq.pC = p->apCsr[pOp->p1];
+ assert( u.bq.pC!=0 );
+ assert( u.bq.pC->isSorter==(pOp->opcode==OP_SorterInsert) );
pIn2 = &aMem[pOp->p2];
assert( pIn2->flags & MEM_Blob );
- u.bn.pCrsr = u.bn.pC->pCursor;
- if( ALWAYS(u.bn.pCrsr!=0) ){
- assert( u.bn.pC->isTable==0 );
+ u.bq.pCrsr = u.bq.pC->pCursor;
+ if( ALWAYS(u.bq.pCrsr!=0) ){
+ assert( u.bq.pC->isTable==0 );
rc = ExpandBlob(pIn2);
if( rc==SQLITE_OK ){
- u.bn.nKey = pIn2->n;
- u.bn.zKey = pIn2->z;
- rc = sqlite3BtreeInsert(u.bn.pCrsr, u.bn.zKey, u.bn.nKey, "", 0, 0, pOp->p3,
- ((pOp->p5 & OPFLAG_USESEEKRESULT) ? u.bn.pC->seekResult : 0)
- );
- assert( u.bn.pC->deferredMoveto==0 );
- u.bn.pC->cacheStatus = CACHE_STALE;
+ if( isSorter(u.bq.pC) ){
+ rc = sqlite3VdbeSorterWrite(db, u.bq.pC, pIn2);
+ }else{
+ u.bq.nKey = pIn2->n;
+ u.bq.zKey = pIn2->z;
+ rc = sqlite3BtreeInsert(u.bq.pCrsr, u.bq.zKey, u.bq.nKey, "", 0, 0, pOp->p3,
+ ((pOp->p5 & OPFLAG_USESEEKRESULT) ? u.bq.pC->seekResult : 0)
+ );
+ assert( u.bq.pC->deferredMoveto==0 );
+ u.bq.pC->cacheStatus = CACHE_STALE;
+ }
}
}
break;
** index opened by cursor P1.
*/
case OP_IdxDelete: {
-#if 0 /* local variables moved into u.bo */
+#if 0 /* local variables moved into u.br */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
UnpackedRecord r;
-#endif /* local variables moved into u.bo */
+#endif /* local variables moved into u.br */
assert( pOp->p3>0 );
assert( pOp->p2>0 && pOp->p2+pOp->p3<=p->nMem+1 );
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- u.bo.pC = p->apCsr[pOp->p1];
- assert( u.bo.pC!=0 );
- u.bo.pCrsr = u.bo.pC->pCursor;
- if( ALWAYS(u.bo.pCrsr!=0) ){
- u.bo.r.pKeyInfo = u.bo.pC->pKeyInfo;
- u.bo.r.nField = (u16)pOp->p3;
- u.bo.r.flags = 0;
- u.bo.r.aMem = &aMem[pOp->p2];
+ u.br.pC = p->apCsr[pOp->p1];
+ assert( u.br.pC!=0 );
+ u.br.pCrsr = u.br.pC->pCursor;
+ if( ALWAYS(u.br.pCrsr!=0) ){
+ u.br.r.pKeyInfo = u.br.pC->pKeyInfo;
+ u.br.r.nField = (u16)pOp->p3;
+ u.br.r.flags = 0;
+ u.br.r.aMem = &aMem[pOp->p2];
#ifdef SQLITE_DEBUG
- { int i; for(i=0; i<u.bo.r.nField; i++) assert( memIsValid(&u.bo.r.aMem[i]) ); }
+ { int i; for(i=0; i<u.br.r.nField; i++) assert( memIsValid(&u.br.r.aMem[i]) ); }
#endif
- rc = sqlite3BtreeMovetoUnpacked(u.bo.pCrsr, &u.bo.r, 0, 0, &u.bo.res);
- if( rc==SQLITE_OK && u.bo.res==0 ){
- rc = sqlite3BtreeDelete(u.bo.pCrsr);
+ rc = sqlite3BtreeMovetoUnpacked(u.br.pCrsr, &u.br.r, 0, 0, &u.br.res);
+ if( rc==SQLITE_OK && u.br.res==0 ){
+ rc = sqlite3BtreeDelete(u.br.pCrsr);
}
- assert( u.bo.pC->deferredMoveto==0 );
- u.bo.pC->cacheStatus = CACHE_STALE;
+ assert( u.br.pC->deferredMoveto==0 );
+ u.br.pC->cacheStatus = CACHE_STALE;
}
break;
}
** See also: Rowid, MakeRecord.
*/
case OP_IdxRowid: { /* out2-prerelease */
-#if 0 /* local variables moved into u.bp */
+#if 0 /* local variables moved into u.bs */
BtCursor *pCrsr;
VdbeCursor *pC;
i64 rowid;
-#endif /* local variables moved into u.bp */
+#endif /* local variables moved into u.bs */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- u.bp.pC = p->apCsr[pOp->p1];
- assert( u.bp.pC!=0 );
- u.bp.pCrsr = u.bp.pC->pCursor;
+ u.bs.pC = p->apCsr[pOp->p1];
+ assert( u.bs.pC!=0 );
+ u.bs.pCrsr = u.bs.pC->pCursor;
pOut->flags = MEM_Null;
- if( ALWAYS(u.bp.pCrsr!=0) ){
- rc = sqlite3VdbeCursorMoveto(u.bp.pC);
+ if( ALWAYS(u.bs.pCrsr!=0) ){
+ rc = sqlite3VdbeCursorMoveto(u.bs.pC);
if( NEVER(rc) ) goto abort_due_to_error;
- assert( u.bp.pC->deferredMoveto==0 );
- assert( u.bp.pC->isTable==0 );
- if( !u.bp.pC->nullRow ){
- rc = sqlite3VdbeIdxRowid(db, u.bp.pCrsr, &u.bp.rowid);
+ assert( u.bs.pC->deferredMoveto==0 );
+ assert( u.bs.pC->isTable==0 );
+ if( !u.bs.pC->nullRow ){
+ rc = sqlite3VdbeIdxRowid(db, u.bs.pCrsr, &u.bs.rowid);
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
- pOut->u.i = u.bp.rowid;
+ pOut->u.i = u.bs.rowid;
pOut->flags = MEM_Int;
}
}
*/
case OP_IdxLT: /* jump */
case OP_IdxGE: { /* jump */
-#if 0 /* local variables moved into u.bq */
+#if 0 /* local variables moved into u.bt */
VdbeCursor *pC;
int res;
UnpackedRecord r;
-#endif /* local variables moved into u.bq */
+#endif /* local variables moved into u.bt */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- u.bq.pC = p->apCsr[pOp->p1];
- assert( u.bq.pC!=0 );
- assert( u.bq.pC->isOrdered );
- if( ALWAYS(u.bq.pC->pCursor!=0) ){
- assert( u.bq.pC->deferredMoveto==0 );
+ u.bt.pC = p->apCsr[pOp->p1];
+ assert( u.bt.pC!=0 );
+ assert( u.bt.pC->isOrdered );
+ if( ALWAYS(u.bt.pC->pCursor!=0) ){
+ assert( u.bt.pC->deferredMoveto==0 );
assert( pOp->p5==0 || pOp->p5==1 );
assert( pOp->p4type==P4_INT32 );
- u.bq.r.pKeyInfo = u.bq.pC->pKeyInfo;
- u.bq.r.nField = (u16)pOp->p4.i;
+ u.bt.r.pKeyInfo = u.bt.pC->pKeyInfo;
+ u.bt.r.nField = (u16)pOp->p4.i;
if( pOp->p5 ){
- u.bq.r.flags = UNPACKED_INCRKEY | UNPACKED_IGNORE_ROWID;
+ u.bt.r.flags = UNPACKED_INCRKEY | UNPACKED_IGNORE_ROWID;
}else{
- u.bq.r.flags = UNPACKED_IGNORE_ROWID;
+ u.bt.r.flags = UNPACKED_IGNORE_ROWID;
}
- u.bq.r.aMem = &aMem[pOp->p3];
+ u.bt.r.aMem = &aMem[pOp->p3];
#ifdef SQLITE_DEBUG
- { int i; for(i=0; i<u.bq.r.nField; i++) assert( memIsValid(&u.bq.r.aMem[i]) ); }
+ { int i; for(i=0; i<u.bt.r.nField; i++) assert( memIsValid(&u.bt.r.aMem[i]) ); }
#endif
- rc = sqlite3VdbeIdxKeyCompare(u.bq.pC, &u.bq.r, &u.bq.res);
+ rc = sqlite3VdbeIdxKeyCompare(u.bt.pC, &u.bt.r, &u.bt.res);
if( pOp->opcode==OP_IdxLT ){
- u.bq.res = -u.bq.res;
+ u.bt.res = -u.bt.res;
}else{
assert( pOp->opcode==OP_IdxGE );
- u.bq.res++;
+ u.bt.res++;
}
- if( u.bq.res>0 ){
+ if( u.bt.res>0 ){
pc = pOp->p2 - 1 ;
}
}
** See also: Clear
*/
case OP_Destroy: { /* out2-prerelease */
-#if 0 /* local variables moved into u.br */
+#if 0 /* local variables moved into u.bu */
int iMoved;
int iCnt;
Vdbe *pVdbe;
int iDb;
-#endif /* local variables moved into u.br */
+#endif /* local variables moved into u.bu */
#ifndef SQLITE_OMIT_VIRTUALTABLE
- u.br.iCnt = 0;
- for(u.br.pVdbe=db->pVdbe; u.br.pVdbe; u.br.pVdbe = u.br.pVdbe->pNext){
- if( u.br.pVdbe->magic==VDBE_MAGIC_RUN && u.br.pVdbe->inVtabMethod<2 && u.br.pVdbe->pc>=0 ){
- u.br.iCnt++;
+ u.bu.iCnt = 0;
+ for(u.bu.pVdbe=db->pVdbe; u.bu.pVdbe; u.bu.pVdbe = u.bu.pVdbe->pNext){
+ if( u.bu.pVdbe->magic==VDBE_MAGIC_RUN && u.bu.pVdbe->inVtabMethod<2 && u.bu.pVdbe->pc>=0 ){
+ u.bu.iCnt++;
}
}
#else
- u.br.iCnt = db->activeVdbeCnt;
+ u.bu.iCnt = db->activeVdbeCnt;
#endif
pOut->flags = MEM_Null;
- if( u.br.iCnt>1 ){
+ if( u.bu.iCnt>1 ){
rc = SQLITE_LOCKED;
p->errorAction = OE_Abort;
}else{
- u.br.iDb = pOp->p3;
- assert( u.br.iCnt==1 );
- assert( (p->btreeMask & (((yDbMask)1)<<u.br.iDb))!=0 );
- rc = sqlite3BtreeDropTable(db->aDb[u.br.iDb].pBt, pOp->p1, &u.br.iMoved);
+ u.bu.iDb = pOp->p3;
+ assert( u.bu.iCnt==1 );
+ assert( (p->btreeMask & (((yDbMask)1)<<u.bu.iDb))!=0 );
+ rc = sqlite3BtreeDropTable(db->aDb[u.bu.iDb].pBt, pOp->p1, &u.bu.iMoved);
pOut->flags = MEM_Int;
- pOut->u.i = u.br.iMoved;
+ pOut->u.i = u.bu.iMoved;
#ifndef SQLITE_OMIT_AUTOVACUUM
- if( rc==SQLITE_OK && u.br.iMoved!=0 ){
- sqlite3RootPageMoved(db, u.br.iDb, u.br.iMoved, pOp->p1);
+ if( rc==SQLITE_OK && u.bu.iMoved!=0 ){
+ sqlite3RootPageMoved(db, u.bu.iDb, u.bu.iMoved, pOp->p1);
/* All OP_Destroy operations occur on the same btree */
- assert( resetSchemaOnFault==0 || resetSchemaOnFault==u.br.iDb+1 );
- resetSchemaOnFault = u.br.iDb+1;
+ assert( resetSchemaOnFault==0 || resetSchemaOnFault==u.bu.iDb+1 );
+ resetSchemaOnFault = u.bu.iDb+1;
}
#endif
}
** See also: Destroy
*/
case OP_Clear: {
-#if 0 /* local variables moved into u.bs */
+#if 0 /* local variables moved into u.bv */
int nChange;
-#endif /* local variables moved into u.bs */
+#endif /* local variables moved into u.bv */
- u.bs.nChange = 0;
+ u.bv.nChange = 0;
assert( (p->btreeMask & (((yDbMask)1)<<pOp->p2))!=0 );
rc = sqlite3BtreeClearTable(
- db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &u.bs.nChange : 0)
+ db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &u.bv.nChange : 0)
);
if( pOp->p3 ){
- p->nChange += u.bs.nChange;
+ p->nChange += u.bv.nChange;
if( pOp->p3>0 ){
assert( memIsValid(&aMem[pOp->p3]) );
memAboutToChange(p, &aMem[pOp->p3]);
- aMem[pOp->p3].u.i += u.bs.nChange;
+ aMem[pOp->p3].u.i += u.bv.nChange;
}
}
break;
*/
case OP_CreateIndex: /* out2-prerelease */
case OP_CreateTable: { /* out2-prerelease */
-#if 0 /* local variables moved into u.bt */
+#if 0 /* local variables moved into u.bw */
int pgno;
int flags;
Db *pDb;
-#endif /* local variables moved into u.bt */
+#endif /* local variables moved into u.bw */
- u.bt.pgno = 0;
+ u.bw.pgno = 0;
assert( pOp->p1>=0 && pOp->p1<db->nDb );
assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
- u.bt.pDb = &db->aDb[pOp->p1];
- assert( u.bt.pDb->pBt!=0 );
+ u.bw.pDb = &db->aDb[pOp->p1];
+ assert( u.bw.pDb->pBt!=0 );
if( pOp->opcode==OP_CreateTable ){
- /* u.bt.flags = BTREE_INTKEY; */
- u.bt.flags = BTREE_INTKEY;
+ /* u.bw.flags = BTREE_INTKEY; */
+ u.bw.flags = BTREE_INTKEY;
}else{
- u.bt.flags = BTREE_BLOBKEY;
+ u.bw.flags = BTREE_BLOBKEY;
}
- rc = sqlite3BtreeCreateTable(u.bt.pDb->pBt, &u.bt.pgno, u.bt.flags);
- pOut->u.i = u.bt.pgno;
+ rc = sqlite3BtreeCreateTable(u.bw.pDb->pBt, &u.bw.pgno, u.bw.flags);
+ pOut->u.i = u.bw.pgno;
break;
}
** then runs the new virtual machine. It is thus a re-entrant opcode.
*/
case OP_ParseSchema: {
-#if 0 /* local variables moved into u.bu */
+#if 0 /* local variables moved into u.bx */
int iDb;
const char *zMaster;
char *zSql;
InitData initData;
-#endif /* local variables moved into u.bu */
+#endif /* local variables moved into u.bx */
/* Any prepared statement that invokes this opcode will hold mutexes
** on every btree. This is a prerequisite for invoking
** sqlite3InitCallback().
*/
#ifdef SQLITE_DEBUG
- for(u.bu.iDb=0; u.bu.iDb<db->nDb; u.bu.iDb++){
- assert( u.bu.iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[u.bu.iDb].pBt) );
+ for(u.bx.iDb=0; u.bx.iDb<db->nDb; u.bx.iDb++){
+ assert( u.bx.iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[u.bx.iDb].pBt) );
}
#endif
- u.bu.iDb = pOp->p1;
- assert( u.bu.iDb>=0 && u.bu.iDb<db->nDb );
- assert( DbHasProperty(db, u.bu.iDb, DB_SchemaLoaded) );
+ u.bx.iDb = pOp->p1;
+ assert( u.bx.iDb>=0 && u.bx.iDb<db->nDb );
+ assert( DbHasProperty(db, u.bx.iDb, DB_SchemaLoaded) );
/* Used to be a conditional */ {
- u.bu.zMaster = SCHEMA_TABLE(u.bu.iDb);
- u.bu.initData.db = db;
- u.bu.initData.iDb = pOp->p1;
- u.bu.initData.pzErrMsg = &p->zErrMsg;
- u.bu.zSql = sqlite3MPrintf(db,
+ u.bx.zMaster = SCHEMA_TABLE(u.bx.iDb);
+ u.bx.initData.db = db;
+ u.bx.initData.iDb = pOp->p1;
+ u.bx.initData.pzErrMsg = &p->zErrMsg;
+ u.bx.zSql = sqlite3MPrintf(db,
"SELECT name, rootpage, sql FROM '%q'.%s WHERE %s ORDER BY rowid",
- db->aDb[u.bu.iDb].zName, u.bu.zMaster, pOp->p4.z);
- if( u.bu.zSql==0 ){
+ db->aDb[u.bx.iDb].zName, u.bx.zMaster, pOp->p4.z);
+ if( u.bx.zSql==0 ){
rc = SQLITE_NOMEM;
}else{
assert( db->init.busy==0 );
db->init.busy = 1;
- u.bu.initData.rc = SQLITE_OK;
+ u.bx.initData.rc = SQLITE_OK;
assert( !db->mallocFailed );
- rc = sqlite3_exec(db, u.bu.zSql, sqlite3InitCallback, &u.bu.initData, 0);
- if( rc==SQLITE_OK ) rc = u.bu.initData.rc;
- sqlite3DbFree(db, u.bu.zSql);
+ rc = sqlite3_exec(db, u.bx.zSql, sqlite3InitCallback, &u.bx.initData, 0);
+ if( rc==SQLITE_OK ) rc = u.bx.initData.rc;
+ sqlite3DbFree(db, u.bx.zSql);
db->init.busy = 0;
}
}
** This opcode is used to implement the integrity_check pragma.
*/
case OP_IntegrityCk: {
-#if 0 /* local variables moved into u.bv */
+#if 0 /* local variables moved into u.by */
int nRoot; /* Number of tables to check. (Number of root pages.) */
int *aRoot; /* Array of rootpage numbers for tables to be checked */
int j; /* Loop counter */
int nErr; /* Number of errors reported */
char *z; /* Text of the error report */
Mem *pnErr; /* Register keeping track of errors remaining */
-#endif /* local variables moved into u.bv */
+#endif /* local variables moved into u.by */
- u.bv.nRoot = pOp->p2;
- assert( u.bv.nRoot>0 );
- u.bv.aRoot = sqlite3DbMallocRaw(db, sizeof(int)*(u.bv.nRoot+1) );
- if( u.bv.aRoot==0 ) goto no_mem;
+ u.by.nRoot = pOp->p2;
+ assert( u.by.nRoot>0 );
+ u.by.aRoot = sqlite3DbMallocRaw(db, sizeof(int)*(u.by.nRoot+1) );
+ if( u.by.aRoot==0 ) goto no_mem;
assert( pOp->p3>0 && pOp->p3<=p->nMem );
- u.bv.pnErr = &aMem[pOp->p3];
- assert( (u.bv.pnErr->flags & MEM_Int)!=0 );
- assert( (u.bv.pnErr->flags & (MEM_Str|MEM_Blob))==0 );
+ u.by.pnErr = &aMem[pOp->p3];
+ assert( (u.by.pnErr->flags & MEM_Int)!=0 );
+ assert( (u.by.pnErr->flags & (MEM_Str|MEM_Blob))==0 );
pIn1 = &aMem[pOp->p1];
- for(u.bv.j=0; u.bv.j<u.bv.nRoot; u.bv.j++){
- u.bv.aRoot[u.bv.j] = (int)sqlite3VdbeIntValue(&pIn1[u.bv.j]);
+ for(u.by.j=0; u.by.j<u.by.nRoot; u.by.j++){
+ u.by.aRoot[u.by.j] = (int)sqlite3VdbeIntValue(&pIn1[u.by.j]);
}
- u.bv.aRoot[u.bv.j] = 0;
+ u.by.aRoot[u.by.j] = 0;
assert( pOp->p5<db->nDb );
assert( (p->btreeMask & (((yDbMask)1)<<pOp->p5))!=0 );
- u.bv.z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p5].pBt, u.bv.aRoot, u.bv.nRoot,
- (int)u.bv.pnErr->u.i, &u.bv.nErr);
- sqlite3DbFree(db, u.bv.aRoot);
- u.bv.pnErr->u.i -= u.bv.nErr;
+ u.by.z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p5].pBt, u.by.aRoot, u.by.nRoot,
+ (int)u.by.pnErr->u.i, &u.by.nErr);
+ sqlite3DbFree(db, u.by.aRoot);
+ u.by.pnErr->u.i -= u.by.nErr;
sqlite3VdbeMemSetNull(pIn1);
- if( u.bv.nErr==0 ){
- assert( u.bv.z==0 );
- }else if( u.bv.z==0 ){
+ if( u.by.nErr==0 ){
+ assert( u.by.z==0 );
+ }else if( u.by.z==0 ){
goto no_mem;
}else{
- sqlite3VdbeMemSetStr(pIn1, u.bv.z, -1, SQLITE_UTF8, sqlite3_free);
+ sqlite3VdbeMemSetStr(pIn1, u.by.z, -1, SQLITE_UTF8, sqlite3_free);
}
UPDATE_MAX_BLOBSIZE(pIn1);
sqlite3VdbeChangeEncoding(pIn1, encoding);
** unchanged and jump to instruction P2.
*/
case OP_RowSetRead: { /* jump, in1, out3 */
-#if 0 /* local variables moved into u.bw */
+#if 0 /* local variables moved into u.bz */
i64 val;
-#endif /* local variables moved into u.bw */
+#endif /* local variables moved into u.bz */
CHECK_FOR_INTERRUPT;
pIn1 = &aMem[pOp->p1];
if( (pIn1->flags & MEM_RowSet)==0
- || sqlite3RowSetNext(pIn1->u.pRowSet, &u.bw.val)==0
+ || sqlite3RowSetNext(pIn1->u.pRowSet, &u.bz.val)==0
){
/* The boolean index is empty */
sqlite3VdbeMemSetNull(pIn1);
pc = pOp->p2 - 1;
}else{
/* A value was pulled from the index */
- sqlite3VdbeMemSetInt64(&aMem[pOp->p3], u.bw.val);
+ sqlite3VdbeMemSetInt64(&aMem[pOp->p3], u.bz.val);
}
break;
}
** inserted as part of some other set).
*/
case OP_RowSetTest: { /* jump, in1, in3 */
-#if 0 /* local variables moved into u.bx */
+#if 0 /* local variables moved into u.ca */
int iSet;
int exists;
-#endif /* local variables moved into u.bx */
+#endif /* local variables moved into u.ca */
pIn1 = &aMem[pOp->p1];
pIn3 = &aMem[pOp->p3];
- u.bx.iSet = pOp->p4.i;
+ u.ca.iSet = pOp->p4.i;
assert( pIn3->flags&MEM_Int );
/* If there is anything other than a rowset object in memory cell P1,
}
assert( pOp->p4type==P4_INT32 );
- assert( u.bx.iSet==-1 || u.bx.iSet>=0 );
- if( u.bx.iSet ){
- u.bx.exists = sqlite3RowSetTest(pIn1->u.pRowSet,
- (u8)(u.bx.iSet>=0 ? u.bx.iSet & 0xf : 0xff),
+ assert( u.ca.iSet==-1 || u.ca.iSet>=0 );
+ if( u.ca.iSet ){
+ u.ca.exists = sqlite3RowSetTest(pIn1->u.pRowSet,
+ (u8)(u.ca.iSet>=0 ? u.ca.iSet & 0xf : 0xff),
pIn3->u.i);
- if( u.bx.exists ){
+ if( u.ca.exists ){
pc = pOp->p2 - 1;
break;
}
}
- if( u.bx.iSet>=0 ){
+ if( u.ca.iSet>=0 ){
sqlite3RowSetInsert(pIn1->u.pRowSet, pIn3->u.i);
}
break;
** P4 is a pointer to the VM containing the trigger program.
*/
case OP_Program: { /* jump */
-#if 0 /* local variables moved into u.by */
+#if 0 /* local variables moved into u.cb */
int nMem; /* Number of memory registers for sub-program */
int nByte; /* Bytes of runtime space required for sub-program */
Mem *pRt; /* Register to allocate runtime space */
VdbeFrame *pFrame; /* New vdbe frame to execute in */
SubProgram *pProgram; /* Sub-program to execute */
void *t; /* Token identifying trigger */
-#endif /* local variables moved into u.by */
+#endif /* local variables moved into u.cb */
- u.by.pProgram = pOp->p4.pProgram;
- u.by.pRt = &aMem[pOp->p3];
- assert( memIsValid(u.by.pRt) );
- assert( u.by.pProgram->nOp>0 );
+ u.cb.pProgram = pOp->p4.pProgram;
+ u.cb.pRt = &aMem[pOp->p3];
+ assert( memIsValid(u.cb.pRt) );
+ assert( u.cb.pProgram->nOp>0 );
/* If the p5 flag is clear, then recursive invocation of triggers is
** disabled for backwards compatibility (p5 is set if this sub-program
** single trigger all have the same value for the SubProgram.token
** variable. */
if( pOp->p5 ){
- u.by.t = u.by.pProgram->token;
- for(u.by.pFrame=p->pFrame; u.by.pFrame && u.by.pFrame->token!=u.by.t; u.by.pFrame=u.by.pFrame->pParent);
- if( u.by.pFrame ) break;
+ u.cb.t = u.cb.pProgram->token;
+ for(u.cb.pFrame=p->pFrame; u.cb.pFrame && u.cb.pFrame->token!=u.cb.t; u.cb.pFrame=u.cb.pFrame->pParent);
+ if( u.cb.pFrame ) break;
}
if( p->nFrame>=db->aLimit[SQLITE_LIMIT_TRIGGER_DEPTH] ){
break;
}
- /* Register u.by.pRt is used to store the memory required to save the state
+ /* Register u.cb.pRt is used to store the memory required to save the state
** of the current program, and the memory required at runtime to execute
- ** the trigger program. If this trigger has been fired before, then u.by.pRt
+ ** the trigger program. If this trigger has been fired before, then u.cb.pRt
** is already allocated. Otherwise, it must be initialized. */
- if( (u.by.pRt->flags&MEM_Frame)==0 ){
+ if( (u.cb.pRt->flags&MEM_Frame)==0 ){
/* SubProgram.nMem is set to the number of memory cells used by the
** program stored in SubProgram.aOp. As well as these, one memory
** cell is required for each cursor used by the program. Set local
- ** variable u.by.nMem (and later, VdbeFrame.nChildMem) to this value.
+ ** variable u.cb.nMem (and later, VdbeFrame.nChildMem) to this value.
*/
- u.by.nMem = u.by.pProgram->nMem + u.by.pProgram->nCsr;
- u.by.nByte = ROUND8(sizeof(VdbeFrame))
- + u.by.nMem * sizeof(Mem)
- + u.by.pProgram->nCsr * sizeof(VdbeCursor *);
- u.by.pFrame = sqlite3DbMallocZero(db, u.by.nByte);
- if( !u.by.pFrame ){
+ u.cb.nMem = u.cb.pProgram->nMem + u.cb.pProgram->nCsr;
+ u.cb.nByte = ROUND8(sizeof(VdbeFrame))
+ + u.cb.nMem * sizeof(Mem)
+ + u.cb.pProgram->nCsr * sizeof(VdbeCursor *);
+ u.cb.pFrame = sqlite3DbMallocZero(db, u.cb.nByte);
+ if( !u.cb.pFrame ){
goto no_mem;
}
- sqlite3VdbeMemRelease(u.by.pRt);
- u.by.pRt->flags = MEM_Frame;
- u.by.pRt->u.pFrame = u.by.pFrame;
-
- u.by.pFrame->v = p;
- u.by.pFrame->nChildMem = u.by.nMem;
- u.by.pFrame->nChildCsr = u.by.pProgram->nCsr;
- u.by.pFrame->pc = pc;
- u.by.pFrame->aMem = p->aMem;
- u.by.pFrame->nMem = p->nMem;
- u.by.pFrame->apCsr = p->apCsr;
- u.by.pFrame->nCursor = p->nCursor;
- u.by.pFrame->aOp = p->aOp;
- u.by.pFrame->nOp = p->nOp;
- u.by.pFrame->token = u.by.pProgram->token;
-
- u.by.pEnd = &VdbeFrameMem(u.by.pFrame)[u.by.pFrame->nChildMem];
- for(u.by.pMem=VdbeFrameMem(u.by.pFrame); u.by.pMem!=u.by.pEnd; u.by.pMem++){
- u.by.pMem->flags = MEM_Null;
- u.by.pMem->db = db;
+ sqlite3VdbeMemRelease(u.cb.pRt);
+ u.cb.pRt->flags = MEM_Frame;
+ u.cb.pRt->u.pFrame = u.cb.pFrame;
+
+ u.cb.pFrame->v = p;
+ u.cb.pFrame->nChildMem = u.cb.nMem;
+ u.cb.pFrame->nChildCsr = u.cb.pProgram->nCsr;
+ u.cb.pFrame->pc = pc;
+ u.cb.pFrame->aMem = p->aMem;
+ u.cb.pFrame->nMem = p->nMem;
+ u.cb.pFrame->apCsr = p->apCsr;
+ u.cb.pFrame->nCursor = p->nCursor;
+ u.cb.pFrame->aOp = p->aOp;
+ u.cb.pFrame->nOp = p->nOp;
+ u.cb.pFrame->token = u.cb.pProgram->token;
+
+ u.cb.pEnd = &VdbeFrameMem(u.cb.pFrame)[u.cb.pFrame->nChildMem];
+ for(u.cb.pMem=VdbeFrameMem(u.cb.pFrame); u.cb.pMem!=u.cb.pEnd; u.cb.pMem++){
+ u.cb.pMem->flags = MEM_Null;
+ u.cb.pMem->db = db;
}
}else{
- u.by.pFrame = u.by.pRt->u.pFrame;
- assert( u.by.pProgram->nMem+u.by.pProgram->nCsr==u.by.pFrame->nChildMem );
- assert( u.by.pProgram->nCsr==u.by.pFrame->nChildCsr );
- assert( pc==u.by.pFrame->pc );
+ u.cb.pFrame = u.cb.pRt->u.pFrame;
+ assert( u.cb.pProgram->nMem+u.cb.pProgram->nCsr==u.cb.pFrame->nChildMem );
+ assert( u.cb.pProgram->nCsr==u.cb.pFrame->nChildCsr );
+ assert( pc==u.cb.pFrame->pc );
}
p->nFrame++;
- u.by.pFrame->pParent = p->pFrame;
- u.by.pFrame->lastRowid = db->lastRowid;
- u.by.pFrame->nChange = p->nChange;
+ u.cb.pFrame->pParent = p->pFrame;
+ u.cb.pFrame->lastRowid = lastRowid;
+ u.cb.pFrame->nChange = p->nChange;
p->nChange = 0;
- p->pFrame = u.by.pFrame;
- p->aMem = aMem = &VdbeFrameMem(u.by.pFrame)[-1];
- p->nMem = u.by.pFrame->nChildMem;
- p->nCursor = (u16)u.by.pFrame->nChildCsr;
+ p->pFrame = u.cb.pFrame;
+ p->aMem = aMem = &VdbeFrameMem(u.cb.pFrame)[-1];
+ p->nMem = u.cb.pFrame->nChildMem;
+ p->nCursor = (u16)u.cb.pFrame->nChildCsr;
p->apCsr = (VdbeCursor **)&aMem[p->nMem+1];
- p->aOp = aOp = u.by.pProgram->aOp;
- p->nOp = u.by.pProgram->nOp;
+ p->aOp = aOp = u.cb.pProgram->aOp;
+ p->nOp = u.cb.pProgram->nOp;
pc = -1;
break;
** calling OP_Program instruction.
*/
case OP_Param: { /* out2-prerelease */
-#if 0 /* local variables moved into u.bz */
+#if 0 /* local variables moved into u.cc */
VdbeFrame *pFrame;
Mem *pIn;
-#endif /* local variables moved into u.bz */
- u.bz.pFrame = p->pFrame;
- u.bz.pIn = &u.bz.pFrame->aMem[pOp->p1 + u.bz.pFrame->aOp[u.bz.pFrame->pc].p1];
- sqlite3VdbeMemShallowCopy(pOut, u.bz.pIn, MEM_Ephem);
+#endif /* local variables moved into u.cc */
+ u.cc.pFrame = p->pFrame;
+ u.cc.pIn = &u.cc.pFrame->aMem[pOp->p1 + u.cc.pFrame->aOp[u.cc.pFrame->pc].p1];
+ sqlite3VdbeMemShallowCopy(pOut, u.cc.pIn, MEM_Ephem);
break;
}
** an integer.
*/
case OP_MemMax: { /* in2 */
-#if 0 /* local variables moved into u.ca */
+#if 0 /* local variables moved into u.cd */
Mem *pIn1;
VdbeFrame *pFrame;
-#endif /* local variables moved into u.ca */
+#endif /* local variables moved into u.cd */
if( p->pFrame ){
- for(u.ca.pFrame=p->pFrame; u.ca.pFrame->pParent; u.ca.pFrame=u.ca.pFrame->pParent);
- u.ca.pIn1 = &u.ca.pFrame->aMem[pOp->p1];
+ for(u.cd.pFrame=p->pFrame; u.cd.pFrame->pParent; u.cd.pFrame=u.cd.pFrame->pParent);
+ u.cd.pIn1 = &u.cd.pFrame->aMem[pOp->p1];
}else{
- u.ca.pIn1 = &aMem[pOp->p1];
+ u.cd.pIn1 = &aMem[pOp->p1];
}
- assert( memIsValid(u.ca.pIn1) );
- sqlite3VdbeMemIntegerify(u.ca.pIn1);
+ assert( memIsValid(u.cd.pIn1) );
+ sqlite3VdbeMemIntegerify(u.cd.pIn1);
pIn2 = &aMem[pOp->p2];
sqlite3VdbeMemIntegerify(pIn2);
- if( u.ca.pIn1->u.i<pIn2->u.i){
- u.ca.pIn1->u.i = pIn2->u.i;
+ if( u.cd.pIn1->u.i<pIn2->u.i){
+ u.cd.pIn1->u.i = pIn2->u.i;
}
break;
}
** successors.
*/
case OP_AggStep: {
-#if 0 /* local variables moved into u.cb */
+#if 0 /* local variables moved into u.ce */
int n;
int i;
Mem *pMem;
Mem *pRec;
sqlite3_context ctx;
sqlite3_value **apVal;
-#endif /* local variables moved into u.cb */
+#endif /* local variables moved into u.ce */
- u.cb.n = pOp->p5;
- assert( u.cb.n>=0 );
- u.cb.pRec = &aMem[pOp->p2];
- u.cb.apVal = p->apArg;
- assert( u.cb.apVal || u.cb.n==0 );
- for(u.cb.i=0; u.cb.i<u.cb.n; u.cb.i++, u.cb.pRec++){
- assert( memIsValid(u.cb.pRec) );
- u.cb.apVal[u.cb.i] = u.cb.pRec;
- memAboutToChange(p, u.cb.pRec);
- sqlite3VdbeMemStoreType(u.cb.pRec);
- }
- u.cb.ctx.pFunc = pOp->p4.pFunc;
+ u.ce.n = pOp->p5;
+ assert( u.ce.n>=0 );
+ u.ce.pRec = &aMem[pOp->p2];
+ u.ce.apVal = p->apArg;
+ assert( u.ce.apVal || u.ce.n==0 );
+ for(u.ce.i=0; u.ce.i<u.ce.n; u.ce.i++, u.ce.pRec++){
+ assert( memIsValid(u.ce.pRec) );
+ u.ce.apVal[u.ce.i] = u.ce.pRec;
+ memAboutToChange(p, u.ce.pRec);
+ sqlite3VdbeMemStoreType(u.ce.pRec);
+ }
+ u.ce.ctx.pFunc = pOp->p4.pFunc;
assert( pOp->p3>0 && pOp->p3<=p->nMem );
- u.cb.ctx.pMem = u.cb.pMem = &aMem[pOp->p3];
- u.cb.pMem->n++;
- u.cb.ctx.s.flags = MEM_Null;
- u.cb.ctx.s.z = 0;
- u.cb.ctx.s.zMalloc = 0;
- u.cb.ctx.s.xDel = 0;
- u.cb.ctx.s.db = db;
- u.cb.ctx.isError = 0;
- u.cb.ctx.pColl = 0;
- if( u.cb.ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){
+ u.ce.ctx.pMem = u.ce.pMem = &aMem[pOp->p3];
+ u.ce.pMem->n++;
+ u.ce.ctx.s.flags = MEM_Null;
+ u.ce.ctx.s.z = 0;
+ u.ce.ctx.s.zMalloc = 0;
+ u.ce.ctx.s.xDel = 0;
+ u.ce.ctx.s.db = db;
+ u.ce.ctx.isError = 0;
+ u.ce.ctx.pColl = 0;
+ if( u.ce.ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){
assert( pOp>p->aOp );
assert( pOp[-1].p4type==P4_COLLSEQ );
assert( pOp[-1].opcode==OP_CollSeq );
- u.cb.ctx.pColl = pOp[-1].p4.pColl;
+ u.ce.ctx.pColl = pOp[-1].p4.pColl;
}
- (u.cb.ctx.pFunc->xStep)(&u.cb.ctx, u.cb.n, u.cb.apVal); /* IMP: R-24505-23230 */
- if( u.cb.ctx.isError ){
- sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&u.cb.ctx.s));
- rc = u.cb.ctx.isError;
+ (u.ce.ctx.pFunc->xStep)(&u.ce.ctx, u.ce.n, u.ce.apVal); /* IMP: R-24505-23230 */
+ if( u.ce.ctx.isError ){
+ sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&u.ce.ctx.s));
+ rc = u.ce.ctx.isError;
}
- sqlite3VdbeMemRelease(&u.cb.ctx.s);
+ sqlite3VdbeMemRelease(&u.ce.ctx.s);
break;
}
** the step function was not previously called.
*/
case OP_AggFinal: {
-#if 0 /* local variables moved into u.cc */
+#if 0 /* local variables moved into u.cf */
Mem *pMem;
-#endif /* local variables moved into u.cc */
+#endif /* local variables moved into u.cf */
assert( pOp->p1>0 && pOp->p1<=p->nMem );
- u.cc.pMem = &aMem[pOp->p1];
- assert( (u.cc.pMem->flags & ~(MEM_Null|MEM_Agg))==0 );
- rc = sqlite3VdbeMemFinalize(u.cc.pMem, pOp->p4.pFunc);
+ u.cf.pMem = &aMem[pOp->p1];
+ assert( (u.cf.pMem->flags & ~(MEM_Null|MEM_Agg))==0 );
+ rc = sqlite3VdbeMemFinalize(u.cf.pMem, pOp->p4.pFunc);
if( rc ){
- sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(u.cc.pMem));
+ sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(u.cf.pMem));
}
- sqlite3VdbeChangeEncoding(u.cc.pMem, encoding);
- UPDATE_MAX_BLOBSIZE(u.cc.pMem);
- if( sqlite3VdbeMemTooBig(u.cc.pMem) ){
+ sqlite3VdbeChangeEncoding(u.cf.pMem, encoding);
+ UPDATE_MAX_BLOBSIZE(u.cf.pMem);
+ if( sqlite3VdbeMemTooBig(u.cf.pMem) ){
goto too_big;
}
break;
** mem[P3+2] are initialized to -1.
*/
case OP_Checkpoint: {
-#if 0 /* local variables moved into u.cd */
+#if 0 /* local variables moved into u.cg */
int i; /* Loop counter */
int aRes[3]; /* Results */
Mem *pMem; /* Write results here */
-#endif /* local variables moved into u.cd */
+#endif /* local variables moved into u.cg */
- u.cd.aRes[0] = 0;
- u.cd.aRes[1] = u.cd.aRes[2] = -1;
+ u.cg.aRes[0] = 0;
+ u.cg.aRes[1] = u.cg.aRes[2] = -1;
assert( pOp->p2==SQLITE_CHECKPOINT_PASSIVE
|| pOp->p2==SQLITE_CHECKPOINT_FULL
|| pOp->p2==SQLITE_CHECKPOINT_RESTART
);
- rc = sqlite3Checkpoint(db, pOp->p1, pOp->p2, &u.cd.aRes[1], &u.cd.aRes[2]);
+ rc = sqlite3Checkpoint(db, pOp->p1, pOp->p2, &u.cg.aRes[1], &u.cg.aRes[2]);
if( rc==SQLITE_BUSY ){
rc = SQLITE_OK;
- u.cd.aRes[0] = 1;
+ u.cg.aRes[0] = 1;
}
- for(u.cd.i=0, u.cd.pMem = &aMem[pOp->p3]; u.cd.i<3; u.cd.i++, u.cd.pMem++){
- sqlite3VdbeMemSetInt64(u.cd.pMem, (i64)u.cd.aRes[u.cd.i]);
+ for(u.cg.i=0, u.cg.pMem = &aMem[pOp->p3]; u.cg.i<3; u.cg.i++, u.cg.pMem++){
+ sqlite3VdbeMemSetInt64(u.cg.pMem, (i64)u.cg.aRes[u.cg.i]);
}
break;
};
** Write a string containing the final journal-mode to register P2.
*/
case OP_JournalMode: { /* out2-prerelease */
-#if 0 /* local variables moved into u.ce */
+#if 0 /* local variables moved into u.ch */
Btree *pBt; /* Btree to change journal mode of */
Pager *pPager; /* Pager associated with pBt */
int eNew; /* New journal mode */
int eOld; /* The old journal mode */
const char *zFilename; /* Name of database file for pPager */
-#endif /* local variables moved into u.ce */
+#endif /* local variables moved into u.ch */
- u.ce.eNew = pOp->p3;
- assert( u.ce.eNew==PAGER_JOURNALMODE_DELETE
- || u.ce.eNew==PAGER_JOURNALMODE_TRUNCATE
- || u.ce.eNew==PAGER_JOURNALMODE_PERSIST
- || u.ce.eNew==PAGER_JOURNALMODE_OFF
- || u.ce.eNew==PAGER_JOURNALMODE_MEMORY
- || u.ce.eNew==PAGER_JOURNALMODE_WAL
- || u.ce.eNew==PAGER_JOURNALMODE_QUERY
+ u.ch.eNew = pOp->p3;
+ assert( u.ch.eNew==PAGER_JOURNALMODE_DELETE
+ || u.ch.eNew==PAGER_JOURNALMODE_TRUNCATE
+ || u.ch.eNew==PAGER_JOURNALMODE_PERSIST
+ || u.ch.eNew==PAGER_JOURNALMODE_OFF
+ || u.ch.eNew==PAGER_JOURNALMODE_MEMORY
+ || u.ch.eNew==PAGER_JOURNALMODE_WAL
+ || u.ch.eNew==PAGER_JOURNALMODE_QUERY
);
assert( pOp->p1>=0 && pOp->p1<db->nDb );
- u.ce.pBt = db->aDb[pOp->p1].pBt;
- u.ce.pPager = sqlite3BtreePager(u.ce.pBt);
- u.ce.eOld = sqlite3PagerGetJournalMode(u.ce.pPager);
- if( u.ce.eNew==PAGER_JOURNALMODE_QUERY ) u.ce.eNew = u.ce.eOld;
- if( !sqlite3PagerOkToChangeJournalMode(u.ce.pPager) ) u.ce.eNew = u.ce.eOld;
+ u.ch.pBt = db->aDb[pOp->p1].pBt;
+ u.ch.pPager = sqlite3BtreePager(u.ch.pBt);
+ u.ch.eOld = sqlite3PagerGetJournalMode(u.ch.pPager);
+ if( u.ch.eNew==PAGER_JOURNALMODE_QUERY ) u.ch.eNew = u.ch.eOld;
+ if( !sqlite3PagerOkToChangeJournalMode(u.ch.pPager) ) u.ch.eNew = u.ch.eOld;
#ifndef SQLITE_OMIT_WAL
- u.ce.zFilename = sqlite3PagerFilename(u.ce.pPager);
+ u.ch.zFilename = sqlite3PagerFilename(u.ch.pPager);
/* Do not allow a transition to journal_mode=WAL for a database
** in temporary storage or if the VFS does not support shared memory
*/
- if( u.ce.eNew==PAGER_JOURNALMODE_WAL
- && (u.ce.zFilename[0]==0 /* Temp file */
- || !sqlite3PagerWalSupported(u.ce.pPager)) /* No shared-memory support */
+ if( u.ch.eNew==PAGER_JOURNALMODE_WAL
+ && (u.ch.zFilename[0]==0 /* Temp file */
+ || !sqlite3PagerWalSupported(u.ch.pPager)) /* No shared-memory support */
){
- u.ce.eNew = u.ce.eOld;
+ u.ch.eNew = u.ch.eOld;
}
- if( (u.ce.eNew!=u.ce.eOld)
- && (u.ce.eOld==PAGER_JOURNALMODE_WAL || u.ce.eNew==PAGER_JOURNALMODE_WAL)
+ if( (u.ch.eNew!=u.ch.eOld)
+ && (u.ch.eOld==PAGER_JOURNALMODE_WAL || u.ch.eNew==PAGER_JOURNALMODE_WAL)
){
if( !db->autoCommit || db->activeVdbeCnt>1 ){
rc = SQLITE_ERROR;
sqlite3SetString(&p->zErrMsg, db,
"cannot change %s wal mode from within a transaction",
- (u.ce.eNew==PAGER_JOURNALMODE_WAL ? "into" : "out of")
+ (u.ch.eNew==PAGER_JOURNALMODE_WAL ? "into" : "out of")
);
break;
}else{
- if( u.ce.eOld==PAGER_JOURNALMODE_WAL ){
+ if( u.ch.eOld==PAGER_JOURNALMODE_WAL ){
/* If leaving WAL mode, close the log file. If successful, the call
** to PagerCloseWal() checkpoints and deletes the write-ahead-log
** file. An EXCLUSIVE lock may still be held on the database file
** after a successful return.
*/
- rc = sqlite3PagerCloseWal(u.ce.pPager);
+ rc = sqlite3PagerCloseWal(u.ch.pPager);
if( rc==SQLITE_OK ){
- sqlite3PagerSetJournalMode(u.ce.pPager, u.ce.eNew);
+ sqlite3PagerSetJournalMode(u.ch.pPager, u.ch.eNew);
}
- }else if( u.ce.eOld==PAGER_JOURNALMODE_MEMORY ){
+ }else if( u.ch.eOld==PAGER_JOURNALMODE_MEMORY ){
/* Cannot transition directly from MEMORY to WAL. Use mode OFF
** as an intermediate */
- sqlite3PagerSetJournalMode(u.ce.pPager, PAGER_JOURNALMODE_OFF);
+ sqlite3PagerSetJournalMode(u.ch.pPager, PAGER_JOURNALMODE_OFF);
}
/* Open a transaction on the database file. Regardless of the journal
** mode, this transaction always uses a rollback journal.
*/
- assert( sqlite3BtreeIsInTrans(u.ce.pBt)==0 );
+ assert( sqlite3BtreeIsInTrans(u.ch.pBt)==0 );
if( rc==SQLITE_OK ){
- rc = sqlite3BtreeSetVersion(u.ce.pBt, (u.ce.eNew==PAGER_JOURNALMODE_WAL ? 2 : 1));
+ rc = sqlite3BtreeSetVersion(u.ch.pBt, (u.ch.eNew==PAGER_JOURNALMODE_WAL ? 2 : 1));
}
}
}
#endif /* ifndef SQLITE_OMIT_WAL */
if( rc ){
- u.ce.eNew = u.ce.eOld;
+ u.ch.eNew = u.ch.eOld;
}
- u.ce.eNew = sqlite3PagerSetJournalMode(u.ce.pPager, u.ce.eNew);
+ u.ch.eNew = sqlite3PagerSetJournalMode(u.ch.pPager, u.ch.eNew);
pOut = &aMem[pOp->p2];
pOut->flags = MEM_Str|MEM_Static|MEM_Term;
- pOut->z = (char *)sqlite3JournalModename(u.ce.eNew);
+ pOut->z = (char *)sqlite3JournalModename(u.ch.eNew);
pOut->n = sqlite3Strlen30(pOut->z);
pOut->enc = SQLITE_UTF8;
sqlite3VdbeChangeEncoding(pOut, encoding);
** P2. Otherwise, fall through to the next instruction.
*/
case OP_IncrVacuum: { /* jump */
-#if 0 /* local variables moved into u.cf */
+#if 0 /* local variables moved into u.ci */
Btree *pBt;
-#endif /* local variables moved into u.cf */
+#endif /* local variables moved into u.ci */
assert( pOp->p1>=0 && pOp->p1<db->nDb );
assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
- u.cf.pBt = db->aDb[pOp->p1].pBt;
- rc = sqlite3BtreeIncrVacuum(u.cf.pBt);
+ u.ci.pBt = db->aDb[pOp->p1].pBt;
+ rc = sqlite3BtreeIncrVacuum(u.ci.pBt);
if( rc==SQLITE_DONE ){
pc = pOp->p2 - 1;
rc = SQLITE_OK;
** code will be set to SQLITE_LOCKED.
*/
case OP_VBegin: {
-#if 0 /* local variables moved into u.cg */
+#if 0 /* local variables moved into u.cj */
VTable *pVTab;
-#endif /* local variables moved into u.cg */
- u.cg.pVTab = pOp->p4.pVtab;
- rc = sqlite3VtabBegin(db, u.cg.pVTab);
- if( u.cg.pVTab ) importVtabErrMsg(p, u.cg.pVTab->pVtab);
+#endif /* local variables moved into u.cj */
+ u.cj.pVTab = pOp->p4.pVtab;
+ rc = sqlite3VtabBegin(db, u.cj.pVTab);
+ if( u.cj.pVTab ) importVtabErrMsg(p, u.cj.pVTab->pVtab);
break;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
** table and stores that cursor in P1.
*/
case OP_VOpen: {
-#if 0 /* local variables moved into u.ch */
+#if 0 /* local variables moved into u.ck */
VdbeCursor *pCur;
sqlite3_vtab_cursor *pVtabCursor;
sqlite3_vtab *pVtab;
sqlite3_module *pModule;
-#endif /* local variables moved into u.ch */
+#endif /* local variables moved into u.ck */
- u.ch.pCur = 0;
- u.ch.pVtabCursor = 0;
- u.ch.pVtab = pOp->p4.pVtab->pVtab;
- u.ch.pModule = (sqlite3_module *)u.ch.pVtab->pModule;
- assert(u.ch.pVtab && u.ch.pModule);
- rc = u.ch.pModule->xOpen(u.ch.pVtab, &u.ch.pVtabCursor);
- importVtabErrMsg(p, u.ch.pVtab);
+ u.ck.pCur = 0;
+ u.ck.pVtabCursor = 0;
+ u.ck.pVtab = pOp->p4.pVtab->pVtab;
+ u.ck.pModule = (sqlite3_module *)u.ck.pVtab->pModule;
+ assert(u.ck.pVtab && u.ck.pModule);
+ rc = u.ck.pModule->xOpen(u.ck.pVtab, &u.ck.pVtabCursor);
+ importVtabErrMsg(p, u.ck.pVtab);
if( SQLITE_OK==rc ){
/* Initialize sqlite3_vtab_cursor base class */
- u.ch.pVtabCursor->pVtab = u.ch.pVtab;
+ u.ck.pVtabCursor->pVtab = u.ck.pVtab;
/* Initialise vdbe cursor object */
- u.ch.pCur = allocateCursor(p, pOp->p1, 0, -1, 0);
- if( u.ch.pCur ){
- u.ch.pCur->pVtabCursor = u.ch.pVtabCursor;
- u.ch.pCur->pModule = u.ch.pVtabCursor->pVtab->pModule;
+ u.ck.pCur = allocateCursor(p, pOp->p1, 0, -1, 0);
+ if( u.ck.pCur ){
+ u.ck.pCur->pVtabCursor = u.ck.pVtabCursor;
+ u.ck.pCur->pModule = u.ck.pVtabCursor->pVtab->pModule;
}else{
db->mallocFailed = 1;
- u.ch.pModule->xClose(u.ch.pVtabCursor);
+ u.ck.pModule->xClose(u.ck.pVtabCursor);
}
}
break;
** A jump is made to P2 if the result set after filtering would be empty.
*/
case OP_VFilter: { /* jump */
-#if 0 /* local variables moved into u.ci */
+#if 0 /* local variables moved into u.cl */
int nArg;
int iQuery;
const sqlite3_module *pModule;
int res;
int i;
Mem **apArg;
-#endif /* local variables moved into u.ci */
+#endif /* local variables moved into u.cl */
- u.ci.pQuery = &aMem[pOp->p3];
- u.ci.pArgc = &u.ci.pQuery[1];
- u.ci.pCur = p->apCsr[pOp->p1];
- assert( memIsValid(u.ci.pQuery) );
- REGISTER_TRACE(pOp->p3, u.ci.pQuery);
- assert( u.ci.pCur->pVtabCursor );
- u.ci.pVtabCursor = u.ci.pCur->pVtabCursor;
- u.ci.pVtab = u.ci.pVtabCursor->pVtab;
- u.ci.pModule = u.ci.pVtab->pModule;
+ u.cl.pQuery = &aMem[pOp->p3];
+ u.cl.pArgc = &u.cl.pQuery[1];
+ u.cl.pCur = p->apCsr[pOp->p1];
+ assert( memIsValid(u.cl.pQuery) );
+ REGISTER_TRACE(pOp->p3, u.cl.pQuery);
+ assert( u.cl.pCur->pVtabCursor );
+ u.cl.pVtabCursor = u.cl.pCur->pVtabCursor;
+ u.cl.pVtab = u.cl.pVtabCursor->pVtab;
+ u.cl.pModule = u.cl.pVtab->pModule;
/* Grab the index number and argc parameters */
- assert( (u.ci.pQuery->flags&MEM_Int)!=0 && u.ci.pArgc->flags==MEM_Int );
- u.ci.nArg = (int)u.ci.pArgc->u.i;
- u.ci.iQuery = (int)u.ci.pQuery->u.i;
+ assert( (u.cl.pQuery->flags&MEM_Int)!=0 && u.cl.pArgc->flags==MEM_Int );
+ u.cl.nArg = (int)u.cl.pArgc->u.i;
+ u.cl.iQuery = (int)u.cl.pQuery->u.i;
/* Invoke the xFilter method */
{
- u.ci.res = 0;
- u.ci.apArg = p->apArg;
- for(u.ci.i = 0; u.ci.i<u.ci.nArg; u.ci.i++){
- u.ci.apArg[u.ci.i] = &u.ci.pArgc[u.ci.i+1];
- sqlite3VdbeMemStoreType(u.ci.apArg[u.ci.i]);
+ u.cl.res = 0;
+ u.cl.apArg = p->apArg;
+ for(u.cl.i = 0; u.cl.i<u.cl.nArg; u.cl.i++){
+ u.cl.apArg[u.cl.i] = &u.cl.pArgc[u.cl.i+1];
+ sqlite3VdbeMemStoreType(u.cl.apArg[u.cl.i]);
}
p->inVtabMethod = 1;
- rc = u.ci.pModule->xFilter(u.ci.pVtabCursor, u.ci.iQuery, pOp->p4.z, u.ci.nArg, u.ci.apArg);
+ rc = u.cl.pModule->xFilter(u.cl.pVtabCursor, u.cl.iQuery, pOp->p4.z, u.cl.nArg, u.cl.apArg);
p->inVtabMethod = 0;
- importVtabErrMsg(p, u.ci.pVtab);
+ importVtabErrMsg(p, u.cl.pVtab);
if( rc==SQLITE_OK ){
- u.ci.res = u.ci.pModule->xEof(u.ci.pVtabCursor);
+ u.cl.res = u.cl.pModule->xEof(u.cl.pVtabCursor);
}
- if( u.ci.res ){
+ if( u.cl.res ){
pc = pOp->p2 - 1;
}
}
- u.ci.pCur->nullRow = 0;
+ u.cl.pCur->nullRow = 0;
break;
}
** P1 cursor is pointing to into register P3.
*/
case OP_VColumn: {
-#if 0 /* local variables moved into u.cj */
+#if 0 /* local variables moved into u.cm */
sqlite3_vtab *pVtab;
const sqlite3_module *pModule;
Mem *pDest;
sqlite3_context sContext;
-#endif /* local variables moved into u.cj */
+#endif /* local variables moved into u.cm */
VdbeCursor *pCur = p->apCsr[pOp->p1];
assert( pCur->pVtabCursor );
assert( pOp->p3>0 && pOp->p3<=p->nMem );
- u.cj.pDest = &aMem[pOp->p3];
- memAboutToChange(p, u.cj.pDest);
+ u.cm.pDest = &aMem[pOp->p3];
+ memAboutToChange(p, u.cm.pDest);
if( pCur->nullRow ){
- sqlite3VdbeMemSetNull(u.cj.pDest);
+ sqlite3VdbeMemSetNull(u.cm.pDest);
break;
}
- u.cj.pVtab = pCur->pVtabCursor->pVtab;
- u.cj.pModule = u.cj.pVtab->pModule;
- assert( u.cj.pModule->xColumn );
- memset(&u.cj.sContext, 0, sizeof(u.cj.sContext));
+ u.cm.pVtab = pCur->pVtabCursor->pVtab;
+ u.cm.pModule = u.cm.pVtab->pModule;
+ assert( u.cm.pModule->xColumn );
+ memset(&u.cm.sContext, 0, sizeof(u.cm.sContext));
/* The output cell may already have a buffer allocated. Move
- ** the current contents to u.cj.sContext.s so in case the user-function
+ ** the current contents to u.cm.sContext.s so in case the user-function
** can use the already allocated buffer instead of allocating a
** new one.
*/
- sqlite3VdbeMemMove(&u.cj.sContext.s, u.cj.pDest);
- MemSetTypeFlag(&u.cj.sContext.s, MEM_Null);
+ sqlite3VdbeMemMove(&u.cm.sContext.s, u.cm.pDest);
+ MemSetTypeFlag(&u.cm.sContext.s, MEM_Null);
- rc = u.cj.pModule->xColumn(pCur->pVtabCursor, &u.cj.sContext, pOp->p2);
- importVtabErrMsg(p, u.cj.pVtab);
- if( u.cj.sContext.isError ){
- rc = u.cj.sContext.isError;
+ rc = u.cm.pModule->xColumn(pCur->pVtabCursor, &u.cm.sContext, pOp->p2);
+ importVtabErrMsg(p, u.cm.pVtab);
+ if( u.cm.sContext.isError ){
+ rc = u.cm.sContext.isError;
}
/* Copy the result of the function to the P3 register. We
** do this regardless of whether or not an error occurred to ensure any
- ** dynamic allocation in u.cj.sContext.s (a Mem struct) is released.
+ ** dynamic allocation in u.cm.sContext.s (a Mem struct) is released.
*/
- sqlite3VdbeChangeEncoding(&u.cj.sContext.s, encoding);
- sqlite3VdbeMemMove(u.cj.pDest, &u.cj.sContext.s);
- REGISTER_TRACE(pOp->p3, u.cj.pDest);
- UPDATE_MAX_BLOBSIZE(u.cj.pDest);
+ sqlite3VdbeChangeEncoding(&u.cm.sContext.s, encoding);
+ sqlite3VdbeMemMove(u.cm.pDest, &u.cm.sContext.s);
+ REGISTER_TRACE(pOp->p3, u.cm.pDest);
+ UPDATE_MAX_BLOBSIZE(u.cm.pDest);
- if( sqlite3VdbeMemTooBig(u.cj.pDest) ){
+ if( sqlite3VdbeMemTooBig(u.cm.pDest) ){
goto too_big;
}
break;
** the end of its result set, then fall through to the next instruction.
*/
case OP_VNext: { /* jump */
-#if 0 /* local variables moved into u.ck */
+#if 0 /* local variables moved into u.cn */
sqlite3_vtab *pVtab;
const sqlite3_module *pModule;
int res;
VdbeCursor *pCur;
-#endif /* local variables moved into u.ck */
+#endif /* local variables moved into u.cn */
- u.ck.res = 0;
- u.ck.pCur = p->apCsr[pOp->p1];
- assert( u.ck.pCur->pVtabCursor );
- if( u.ck.pCur->nullRow ){
+ u.cn.res = 0;
+ u.cn.pCur = p->apCsr[pOp->p1];
+ assert( u.cn.pCur->pVtabCursor );
+ if( u.cn.pCur->nullRow ){
break;
}
- u.ck.pVtab = u.ck.pCur->pVtabCursor->pVtab;
- u.ck.pModule = u.ck.pVtab->pModule;
- assert( u.ck.pModule->xNext );
+ u.cn.pVtab = u.cn.pCur->pVtabCursor->pVtab;
+ u.cn.pModule = u.cn.pVtab->pModule;
+ assert( u.cn.pModule->xNext );
/* Invoke the xNext() method of the module. There is no way for the
** underlying implementation to return an error if one occurs during
** some other method is next invoked on the save virtual table cursor.
*/
p->inVtabMethod = 1;
- rc = u.ck.pModule->xNext(u.ck.pCur->pVtabCursor);
+ rc = u.cn.pModule->xNext(u.cn.pCur->pVtabCursor);
p->inVtabMethod = 0;
- importVtabErrMsg(p, u.ck.pVtab);
+ importVtabErrMsg(p, u.cn.pVtab);
if( rc==SQLITE_OK ){
- u.ck.res = u.ck.pModule->xEof(u.ck.pCur->pVtabCursor);
+ u.cn.res = u.cn.pModule->xEof(u.cn.pCur->pVtabCursor);
}
- if( !u.ck.res ){
+ if( !u.cn.res ){
/* If there is data, jump to P2 */
pc = pOp->p2 - 1;
}
** in register P1 is passed as the zName argument to the xRename method.
*/
case OP_VRename: {
-#if 0 /* local variables moved into u.cl */
+#if 0 /* local variables moved into u.co */
sqlite3_vtab *pVtab;
Mem *pName;
-#endif /* local variables moved into u.cl */
-
- u.cl.pVtab = pOp->p4.pVtab->pVtab;
- u.cl.pName = &aMem[pOp->p1];
- assert( u.cl.pVtab->pModule->xRename );
- assert( memIsValid(u.cl.pName) );
- REGISTER_TRACE(pOp->p1, u.cl.pName);
- assert( u.cl.pName->flags & MEM_Str );
- rc = u.cl.pVtab->pModule->xRename(u.cl.pVtab, u.cl.pName->z);
- importVtabErrMsg(p, u.cl.pVtab);
+#endif /* local variables moved into u.co */
+
+ u.co.pVtab = pOp->p4.pVtab->pVtab;
+ u.co.pName = &aMem[pOp->p1];
+ assert( u.co.pVtab->pModule->xRename );
+ assert( memIsValid(u.co.pName) );
+ REGISTER_TRACE(pOp->p1, u.co.pName);
+ assert( u.co.pName->flags & MEM_Str );
+ rc = u.co.pVtab->pModule->xRename(u.co.pVtab, u.co.pName->z);
+ importVtabErrMsg(p, u.co.pVtab);
p->expired = 0;
break;
** is set to the value of the rowid for the row just inserted.
*/
case OP_VUpdate: {
-#if 0 /* local variables moved into u.cm */
+#if 0 /* local variables moved into u.cp */
sqlite3_vtab *pVtab;
sqlite3_module *pModule;
int nArg;
sqlite_int64 rowid;
Mem **apArg;
Mem *pX;
-#endif /* local variables moved into u.cm */
+#endif /* local variables moved into u.cp */
- u.cm.pVtab = pOp->p4.pVtab->pVtab;
- u.cm.pModule = (sqlite3_module *)u.cm.pVtab->pModule;
- u.cm.nArg = pOp->p2;
+ assert( pOp->p2==1 || pOp->p5==OE_Fail || pOp->p5==OE_Rollback
+ || pOp->p5==OE_Abort || pOp->p5==OE_Ignore || pOp->p5==OE_Replace
+ );
+ u.cp.pVtab = pOp->p4.pVtab->pVtab;
+ u.cp.pModule = (sqlite3_module *)u.cp.pVtab->pModule;
+ u.cp.nArg = pOp->p2;
assert( pOp->p4type==P4_VTAB );
- if( ALWAYS(u.cm.pModule->xUpdate) ){
- u.cm.apArg = p->apArg;
- u.cm.pX = &aMem[pOp->p3];
- for(u.cm.i=0; u.cm.i<u.cm.nArg; u.cm.i++){
- assert( memIsValid(u.cm.pX) );
- memAboutToChange(p, u.cm.pX);
- sqlite3VdbeMemStoreType(u.cm.pX);
- u.cm.apArg[u.cm.i] = u.cm.pX;
- u.cm.pX++;
- }
- rc = u.cm.pModule->xUpdate(u.cm.pVtab, u.cm.nArg, u.cm.apArg, &u.cm.rowid);
- importVtabErrMsg(p, u.cm.pVtab);
+ if( ALWAYS(u.cp.pModule->xUpdate) ){
+ u8 vtabOnConflict = db->vtabOnConflict;
+ u.cp.apArg = p->apArg;
+ u.cp.pX = &aMem[pOp->p3];
+ for(u.cp.i=0; u.cp.i<u.cp.nArg; u.cp.i++){
+ assert( memIsValid(u.cp.pX) );
+ memAboutToChange(p, u.cp.pX);
+ sqlite3VdbeMemStoreType(u.cp.pX);
+ u.cp.apArg[u.cp.i] = u.cp.pX;
+ u.cp.pX++;
+ }
+ db->vtabOnConflict = pOp->p5;
+ rc = u.cp.pModule->xUpdate(u.cp.pVtab, u.cp.nArg, u.cp.apArg, &u.cp.rowid);
+ db->vtabOnConflict = vtabOnConflict;
+ importVtabErrMsg(p, u.cp.pVtab);
if( rc==SQLITE_OK && pOp->p1 ){
- assert( u.cm.nArg>1 && u.cm.apArg[0] && (u.cm.apArg[0]->flags&MEM_Null) );
- db->lastRowid = u.cm.rowid;
+ assert( u.cp.nArg>1 && u.cp.apArg[0] && (u.cp.apArg[0]->flags&MEM_Null) );
+ db->lastRowid = lastRowid = u.cp.rowid;
+ }
+ if( rc==SQLITE_CONSTRAINT && pOp->p4.pVtab->bConstraint ){
+ if( pOp->p5==OE_Ignore ){
+ rc = SQLITE_OK;
+ }else{
+ p->errorAction = ((pOp->p5==OE_Replace) ? OE_Abort : pOp->p5);
+ }
+ }else{
+ p->nChange++;
}
- p->nChange++;
}
break;
}
** the UTF-8 string contained in P4 is emitted on the trace callback.
*/
case OP_Trace: {
-#if 0 /* local variables moved into u.cn */
+#if 0 /* local variables moved into u.cq */
char *zTrace;
-#endif /* local variables moved into u.cn */
+ char *z;
+#endif /* local variables moved into u.cq */
- u.cn.zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql);
- if( u.cn.zTrace ){
- if( db->xTrace ){
- char *z = sqlite3VdbeExpandSql(p, u.cn.zTrace);
- db->xTrace(db->pTraceArg, z);
- sqlite3DbFree(db, z);
- }
+ if( db->xTrace && (u.cq.zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 ){
+ u.cq.z = sqlite3VdbeExpandSql(p, u.cq.zTrace);
+ db->xTrace(db->pTraceArg, u.cq.z);
+ sqlite3DbFree(db, u.cq.z);
+ }
#ifdef SQLITE_DEBUG
- if( (db->flags & SQLITE_SqlTrace)!=0 ){
- sqlite3DebugPrintf("SQL-trace: %s\n", u.cn.zTrace);
- }
-#endif /* SQLITE_DEBUG */
+ if( (db->flags & SQLITE_SqlTrace)!=0
+ && (u.cq.zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0
+ ){
+ sqlite3DebugPrintf("SQL-trace: %s\n", u.cq.zTrace);
}
+#endif /* SQLITE_DEBUG */
break;
}
#endif
** release the mutexes on btrees that were acquired at the
** top. */
vdbe_return:
+ db->lastRowid = lastRowid;
sqlite3VdbeLeave(p);
return rc;
/* Configure the OP_TableLock instruction */
#ifdef SQLITE_OMIT_SHARED_CACHE
- sqlite3VdbeChangeToNoop(v, 2, 1);
+ sqlite3VdbeChangeToNoop(v, 2);
#else
sqlite3VdbeChangeP1(v, 2, iDb);
sqlite3VdbeChangeP2(v, 2, pTab->tnum);
/* Remove either the OP_OpenWrite or OpenRead. Set the P2
** parameter of the other to pTab->tnum. */
- sqlite3VdbeChangeToNoop(v, 4 - flags, 1);
+ sqlite3VdbeChangeToNoop(v, 4 - flags);
sqlite3VdbeChangeP2(v, 3 + flags, pTab->tnum);
sqlite3VdbeChangeP3(v, 3 + flags, iDb);
sqlite3VdbeChangeP4(v, 3+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32);
sqlite3VdbeChangeP2(v, 7, pTab->nCol);
if( !db->mallocFailed ){
- sqlite3VdbeMakeReady(v, 1, 1, 1, 0, 0, 0);
+ pParse->nVar = 1;
+ pParse->nMem = 1;
+ pParse->nTab = 1;
+ sqlite3VdbeMakeReady(v, pParse);
}
}
#endif /* #ifndef SQLITE_OMIT_INCRBLOB */
/************** End of vdbeblob.c ********************************************/
+/************** Begin file vdbesort.c ****************************************/
+/*
+** 2011 July 9
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains code for the VdbeSorter object, used in concert with
+** a VdbeCursor to sort large numbers of keys (as may be required, for
+** example, by CREATE INDEX statements on tables too large to fit in main
+** memory).
+*/
+
+
+#ifndef SQLITE_OMIT_MERGE_SORT
+
+typedef struct VdbeSorterIter VdbeSorterIter;
+typedef struct SorterRecord SorterRecord;
+
+/*
+** NOTES ON DATA STRUCTURE USED FOR N-WAY MERGES:
+**
+** As keys are added to the sorter, they are written to disk in a series
+** of sorted packed-memory-arrays (PMAs). The size of each PMA is roughly
+** the same as the cache-size allowed for temporary databases. In order
+** to allow the caller to extract keys from the sorter in sorted order,
+** all PMAs currently stored on disk must be merged together. This comment
+** describes the data structure used to do so. The structure supports
+** merging any number of arrays in a single pass with no redundant comparison
+** operations.
+**
+** The aIter[] array contains an iterator for each of the PMAs being merged.
+** An aIter[] iterator either points to a valid key or else is at EOF. For
+** the purposes of the paragraphs below, we assume that the array is actually
+** N elements in size, where N is the smallest power of 2 greater to or equal
+** to the number of iterators being merged. The extra aIter[] elements are
+** treated as if they are empty (always at EOF).
+**
+** The aTree[] array is also N elements in size. The value of N is stored in
+** the VdbeSorter.nTree variable.
+**
+** The final (N/2) elements of aTree[] contain the results of comparing
+** pairs of iterator keys together. Element i contains the result of
+** comparing aIter[2*i-N] and aIter[2*i-N+1]. Whichever key is smaller, the
+** aTree element is set to the index of it.
+**
+** For the purposes of this comparison, EOF is considered greater than any
+** other key value. If the keys are equal (only possible with two EOF
+** values), it doesn't matter which index is stored.
+**
+** The (N/4) elements of aTree[] that preceed the final (N/2) described
+** above contains the index of the smallest of each block of 4 iterators.
+** And so on. So that aTree[1] contains the index of the iterator that
+** currently points to the smallest key value. aTree[0] is unused.
+**
+** Example:
+**
+** aIter[0] -> Banana
+** aIter[1] -> Feijoa
+** aIter[2] -> Elderberry
+** aIter[3] -> Currant
+** aIter[4] -> Grapefruit
+** aIter[5] -> Apple
+** aIter[6] -> Durian
+** aIter[7] -> EOF
+**
+** aTree[] = { X, 5 0, 5 0, 3, 5, 6 }
+**
+** The current element is "Apple" (the value of the key indicated by
+** iterator 5). When the Next() operation is invoked, iterator 5 will
+** be advanced to the next key in its segment. Say the next key is
+** "Eggplant":
+**
+** aIter[5] -> Eggplant
+**
+** The contents of aTree[] are updated first by comparing the new iterator
+** 5 key to the current key of iterator 4 (still "Grapefruit"). The iterator
+** 5 value is still smaller, so aTree[6] is set to 5. And so on up the tree.
+** The value of iterator 6 - "Durian" - is now smaller than that of iterator
+** 5, so aTree[3] is set to 6. Key 0 is smaller than key 6 (Banana<Durian),
+** so the value written into element 1 of the array is 0. As follows:
+**
+** aTree[] = { X, 0 0, 6 0, 3, 5, 6 }
+**
+** In other words, each time we advance to the next sorter element, log2(N)
+** key comparison operations are required, where N is the number of segments
+** being merged (rounded up to the next power of 2).
+*/
+struct VdbeSorter {
+ int nInMemory; /* Current size of pRecord list as PMA */
+ int nTree; /* Used size of aTree/aIter (power of 2) */
+ VdbeSorterIter *aIter; /* Array of iterators to merge */
+ int *aTree; /* Current state of incremental merge */
+ i64 iWriteOff; /* Current write offset within file pTemp1 */
+ i64 iReadOff; /* Current read offset within file pTemp1 */
+ sqlite3_file *pTemp1; /* PMA file 1 */
+ int nPMA; /* Number of PMAs stored in pTemp1 */
+ SorterRecord *pRecord; /* Head of in-memory record list */
+ int mnPmaSize; /* Minimum PMA size, in bytes */
+ int mxPmaSize; /* Maximum PMA size, in bytes. 0==no limit */
+ UnpackedRecord *pUnpacked; /* Used to unpack keys */
+};
+
+/*
+** The following type is an iterator for a PMA. It caches the current key in
+** variables nKey/aKey. If the iterator is at EOF, pFile==0.
+*/
+struct VdbeSorterIter {
+ i64 iReadOff; /* Current read offset */
+ i64 iEof; /* 1 byte past EOF for this iterator */
+ sqlite3_file *pFile; /* File iterator is reading from */
+ int nAlloc; /* Bytes of space at aAlloc */
+ u8 *aAlloc; /* Allocated space */
+ int nKey; /* Number of bytes in key */
+ u8 *aKey; /* Pointer to current key */
+};
+
+/*
+** A structure to store a single record. All in-memory records are connected
+** together into a linked list headed at VdbeSorter.pRecord using the
+** SorterRecord.pNext pointer.
+*/
+struct SorterRecord {
+ void *pVal;
+ int nVal;
+ SorterRecord *pNext;
+};
+
+/* Minimum allowable value for the VdbeSorter.nWorking variable */
+#define SORTER_MIN_WORKING 10
+
+/* Maximum number of segments to merge in a single pass. */
+#define SORTER_MAX_MERGE_COUNT 16
+
+/*
+** Free all memory belonging to the VdbeSorterIter object passed as the second
+** argument. All structure fields are set to zero before returning.
+*/
+static void vdbeSorterIterZero(sqlite3 *db, VdbeSorterIter *pIter){
+ sqlite3DbFree(db, pIter->aAlloc);
+ memset(pIter, 0, sizeof(VdbeSorterIter));
+}
+
+/*
+** Advance iterator pIter to the next key in its PMA. Return SQLITE_OK if
+** no error occurs, or an SQLite error code if one does.
+*/
+static int vdbeSorterIterNext(
+ sqlite3 *db, /* Database handle (for sqlite3DbMalloc() ) */
+ VdbeSorterIter *pIter /* Iterator to advance */
+){
+ int rc; /* Return Code */
+ int nRead; /* Number of bytes read */
+ int nRec = 0; /* Size of record in bytes */
+ int iOff = 0; /* Size of serialized size varint in bytes */
+
+ assert( pIter->iEof>=pIter->iReadOff );
+ if( pIter->iEof-pIter->iReadOff>5 ){
+ nRead = 5;
+ }else{
+ nRead = (int)(pIter->iEof - pIter->iReadOff);
+ }
+ if( nRead<=0 ){
+ /* This is an EOF condition */
+ vdbeSorterIterZero(db, pIter);
+ return SQLITE_OK;
+ }
+
+ rc = sqlite3OsRead(pIter->pFile, pIter->aAlloc, nRead, pIter->iReadOff);
+ if( rc==SQLITE_OK ){
+ iOff = getVarint32(pIter->aAlloc, nRec);
+ if( (iOff+nRec)>nRead ){
+ int nRead2; /* Number of extra bytes to read */
+ if( (iOff+nRec)>pIter->nAlloc ){
+ int nNew = pIter->nAlloc*2;
+ while( (iOff+nRec)>nNew ) nNew = nNew*2;
+ pIter->aAlloc = sqlite3DbReallocOrFree(db, pIter->aAlloc, nNew);
+ if( !pIter->aAlloc ) return SQLITE_NOMEM;
+ pIter->nAlloc = nNew;
+ }
+
+ nRead2 = iOff + nRec - nRead;
+ rc = sqlite3OsRead(
+ pIter->pFile, &pIter->aAlloc[nRead], nRead2, pIter->iReadOff+nRead
+ );
+ }
+ }
+
+ assert( rc!=SQLITE_OK || nRec>0 );
+ pIter->iReadOff += iOff+nRec;
+ pIter->nKey = nRec;
+ pIter->aKey = &pIter->aAlloc[iOff];
+ return rc;
+}
+
+/*
+** Write a single varint, value iVal, to file-descriptor pFile. Return
+** SQLITE_OK if successful, or an SQLite error code if some error occurs.
+**
+** The value of *piOffset when this function is called is used as the byte
+** offset in file pFile to write to. Before returning, *piOffset is
+** incremented by the number of bytes written.
+*/
+static int vdbeSorterWriteVarint(
+ sqlite3_file *pFile, /* File to write to */
+ i64 iVal, /* Value to write as a varint */
+ i64 *piOffset /* IN/OUT: Write offset in file pFile */
+){
+ u8 aVarint[9]; /* Buffer large enough for a varint */
+ int nVarint; /* Number of used bytes in varint */
+ int rc; /* Result of write() call */
+
+ nVarint = sqlite3PutVarint(aVarint, iVal);
+ rc = sqlite3OsWrite(pFile, aVarint, nVarint, *piOffset);
+ *piOffset += nVarint;
+
+ return rc;
+}
+
+/*
+** Read a single varint from file-descriptor pFile. Return SQLITE_OK if
+** successful, or an SQLite error code if some error occurs.
+**
+** The value of *piOffset when this function is called is used as the
+** byte offset in file pFile from whence to read the varint. If successful
+** (i.e. if no IO error occurs), then *piOffset is set to the offset of
+** the first byte past the end of the varint before returning. *piVal is
+** set to the integer value read. If an error occurs, the final values of
+** both *piOffset and *piVal are undefined.
+*/
+static int vdbeSorterReadVarint(
+ sqlite3_file *pFile, /* File to read from */
+ i64 *piOffset, /* IN/OUT: Read offset in pFile */
+ i64 *piVal /* OUT: Value read from file */
+){
+ u8 aVarint[9]; /* Buffer large enough for a varint */
+ i64 iOff = *piOffset; /* Offset in file to read from */
+ int rc; /* Return code */
+
+ rc = sqlite3OsRead(pFile, aVarint, 9, iOff);
+ if( rc==SQLITE_OK ){
+ *piOffset += getVarint(aVarint, (u64 *)piVal);
+ }
+
+ return rc;
+}
+
+/*
+** Initialize iterator pIter to scan through the PMA stored in file pFile
+** starting at offset iStart and ending at offset iEof-1. This function
+** leaves the iterator pointing to the first key in the PMA (or EOF if the
+** PMA is empty).
+*/
+static int vdbeSorterIterInit(
+ sqlite3 *db, /* Database handle */
+ VdbeSorter *pSorter, /* Sorter object */
+ i64 iStart, /* Start offset in pFile */
+ VdbeSorterIter *pIter, /* Iterator to populate */
+ i64 *pnByte /* IN/OUT: Increment this value by PMA size */
+){
+ int rc;
+
+ assert( pSorter->iWriteOff>iStart );
+ assert( pIter->aAlloc==0 );
+ pIter->pFile = pSorter->pTemp1;
+ pIter->iReadOff = iStart;
+ pIter->nAlloc = 128;
+ pIter->aAlloc = (u8 *)sqlite3DbMallocRaw(db, pIter->nAlloc);
+ if( !pIter->aAlloc ){
+ rc = SQLITE_NOMEM;
+ }else{
+ i64 nByte; /* Total size of PMA in bytes */
+ rc = vdbeSorterReadVarint(pSorter->pTemp1, &pIter->iReadOff, &nByte);
+ *pnByte += nByte;
+ pIter->iEof = pIter->iReadOff + nByte;
+ }
+ if( rc==SQLITE_OK ){
+ rc = vdbeSorterIterNext(db, pIter);
+ }
+ return rc;
+}
+
+
+/*
+** Compare key1 (buffer pKey1, size nKey1 bytes) with key2 (buffer pKey2,
+** size nKey2 bytes). Argument pKeyInfo supplies the collation functions
+** used by the comparison. If an error occurs, return an SQLite error code.
+** Otherwise, return SQLITE_OK and set *pRes to a negative, zero or positive
+** value, depending on whether key1 is smaller, equal to or larger than key2.
+**
+** If the bOmitRowid argument is non-zero, assume both keys end in a rowid
+** field. For the purposes of the comparison, ignore it. Also, if bOmitRowid
+** is true and key1 contains even a single NULL value, it is considered to
+** be less than key2. Even if key2 also contains NULL values.
+**
+** If pKey2 is passed a NULL pointer, then it is assumed that the pCsr->aSpace
+** has been allocated and contains an unpacked record that is used as key2.
+*/
+static void vdbeSorterCompare(
+ VdbeCursor *pCsr, /* Cursor object (for pKeyInfo) */
+ int bOmitRowid, /* Ignore rowid field at end of keys */
+ void *pKey1, int nKey1, /* Left side of comparison */
+ void *pKey2, int nKey2, /* Right side of comparison */
+ int *pRes /* OUT: Result of comparison */
+){
+ KeyInfo *pKeyInfo = pCsr->pKeyInfo;
+ VdbeSorter *pSorter = pCsr->pSorter;
+ UnpackedRecord *r2 = pSorter->pUnpacked;
+ int i;
+
+ if( pKey2 ){
+ sqlite3VdbeRecordUnpack(pKeyInfo, nKey2, pKey2, r2);
+ }
+
+ if( bOmitRowid ){
+ r2->nField = pKeyInfo->nField;
+ assert( r2->nField>0 );
+ for(i=0; i<r2->nField; i++){
+ if( r2->aMem[i].flags & MEM_Null ){
+ *pRes = -1;
+ return;
+ }
+ }
+ r2->flags |= UNPACKED_PREFIX_MATCH;
+ }
+
+ *pRes = sqlite3VdbeRecordCompare(nKey1, pKey1, r2);
+}
+
+/*
+** This function is called to compare two iterator keys when merging
+** multiple b-tree segments. Parameter iOut is the index of the aTree[]
+** value to recalculate.
+*/
+static int vdbeSorterDoCompare(VdbeCursor *pCsr, int iOut){
+ VdbeSorter *pSorter = pCsr->pSorter;
+ int i1;
+ int i2;
+ int iRes;
+ VdbeSorterIter *p1;
+ VdbeSorterIter *p2;
+
+ assert( iOut<pSorter->nTree && iOut>0 );
+
+ if( iOut>=(pSorter->nTree/2) ){
+ i1 = (iOut - pSorter->nTree/2) * 2;
+ i2 = i1 + 1;
+ }else{
+ i1 = pSorter->aTree[iOut*2];
+ i2 = pSorter->aTree[iOut*2+1];
+ }
+
+ p1 = &pSorter->aIter[i1];
+ p2 = &pSorter->aIter[i2];
+
+ if( p1->pFile==0 ){
+ iRes = i2;
+ }else if( p2->pFile==0 ){
+ iRes = i1;
+ }else{
+ int res;
+ assert( pCsr->pSorter->pUnpacked!=0 ); /* allocated in vdbeSorterMerge() */
+ vdbeSorterCompare(
+ pCsr, 0, p1->aKey, p1->nKey, p2->aKey, p2->nKey, &res
+ );
+ if( res<=0 ){
+ iRes = i1;
+ }else{
+ iRes = i2;
+ }
+ }
+
+ pSorter->aTree[iOut] = iRes;
+ return SQLITE_OK;
+}
+
+/*
+** Initialize the temporary index cursor just opened as a sorter cursor.
+*/
+SQLITE_PRIVATE int sqlite3VdbeSorterInit(sqlite3 *db, VdbeCursor *pCsr){
+ int pgsz; /* Page size of main database */
+ int mxCache; /* Cache size */
+ VdbeSorter *pSorter; /* The new sorter */
+ char *d; /* Dummy */
+
+ assert( pCsr->pKeyInfo && pCsr->pBt==0 );
+ pCsr->pSorter = pSorter = sqlite3DbMallocZero(db, sizeof(VdbeSorter));
+ if( pSorter==0 ){
+ return SQLITE_NOMEM;
+ }
+
+ pSorter->pUnpacked = sqlite3VdbeAllocUnpackedRecord(pCsr->pKeyInfo, 0, 0, &d);
+ if( pSorter->pUnpacked==0 ) return SQLITE_NOMEM;
+ assert( pSorter->pUnpacked==(UnpackedRecord *)d );
+
+ if( !sqlite3TempInMemory(db) ){
+ pgsz = sqlite3BtreeGetPageSize(db->aDb[0].pBt);
+ pSorter->mnPmaSize = SORTER_MIN_WORKING * pgsz;
+ mxCache = db->aDb[0].pSchema->cache_size;
+ if( mxCache<SORTER_MIN_WORKING ) mxCache = SORTER_MIN_WORKING;
+ pSorter->mxPmaSize = mxCache * pgsz;
+ }
+
+ return SQLITE_OK;
+}
+
+/*
+** Free the list of sorted records starting at pRecord.
+*/
+static void vdbeSorterRecordFree(sqlite3 *db, SorterRecord *pRecord){
+ SorterRecord *p;
+ SorterRecord *pNext;
+ for(p=pRecord; p; p=pNext){
+ pNext = p->pNext;
+ sqlite3DbFree(db, p);
+ }
+}
+
+/*
+** Free any cursor components allocated by sqlite3VdbeSorterXXX routines.
+*/
+SQLITE_PRIVATE void sqlite3VdbeSorterClose(sqlite3 *db, VdbeCursor *pCsr){
+ VdbeSorter *pSorter = pCsr->pSorter;
+ if( pSorter ){
+ if( pSorter->aIter ){
+ int i;
+ for(i=0; i<pSorter->nTree; i++){
+ vdbeSorterIterZero(db, &pSorter->aIter[i]);
+ }
+ sqlite3DbFree(db, pSorter->aIter);
+ }
+ if( pSorter->pTemp1 ){
+ sqlite3OsCloseFree(pSorter->pTemp1);
+ }
+ vdbeSorterRecordFree(db, pSorter->pRecord);
+ sqlite3DbFree(db, pSorter->pUnpacked);
+ sqlite3DbFree(db, pSorter);
+ pCsr->pSorter = 0;
+ }
+}
+
+/*
+** Allocate space for a file-handle and open a temporary file. If successful,
+** set *ppFile to point to the malloc'd file-handle and return SQLITE_OK.
+** Otherwise, set *ppFile to 0 and return an SQLite error code.
+*/
+static int vdbeSorterOpenTempFile(sqlite3 *db, sqlite3_file **ppFile){
+ int dummy;
+ return sqlite3OsOpenMalloc(db->pVfs, 0, ppFile,
+ SQLITE_OPEN_TEMP_JOURNAL |
+ SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
+ SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE, &dummy
+ );
+}
+
+/*
+** Merge the two sorted lists p1 and p2 into a single list.
+** Set *ppOut to the head of the new list.
+*/
+static void vdbeSorterMerge(
+ VdbeCursor *pCsr, /* For pKeyInfo */
+ SorterRecord *p1, /* First list to merge */
+ SorterRecord *p2, /* Second list to merge */
+ SorterRecord **ppOut /* OUT: Head of merged list */
+){
+ SorterRecord *pFinal = 0;
+ SorterRecord **pp = &pFinal;
+ void *pVal2 = p2 ? p2->pVal : 0;
+
+ while( p1 && p2 ){
+ int res;
+ vdbeSorterCompare(pCsr, 0, p1->pVal, p1->nVal, pVal2, p2->nVal, &res);
+ if( res<=0 ){
+ *pp = p1;
+ pp = &p1->pNext;
+ p1 = p1->pNext;
+ pVal2 = 0;
+ }else{
+ *pp = p2;
+ pp = &p2->pNext;
+ p2 = p2->pNext;
+ if( p2==0 ) break;
+ pVal2 = p2->pVal;
+ }
+ }
+ *pp = p1 ? p1 : p2;
+ *ppOut = pFinal;
+}
+
+/*
+** Sort the linked list of records headed at pCsr->pRecord. Return SQLITE_OK
+** if successful, or an SQLite error code (i.e. SQLITE_NOMEM) if an error
+** occurs.
+*/
+static int vdbeSorterSort(VdbeCursor *pCsr){
+ int i;
+ SorterRecord **aSlot;
+ SorterRecord *p;
+ VdbeSorter *pSorter = pCsr->pSorter;
+
+ aSlot = (SorterRecord **)sqlite3MallocZero(64 * sizeof(SorterRecord *));
+ if( !aSlot ){
+ return SQLITE_NOMEM;
+ }
+
+ p = pSorter->pRecord;
+ while( p ){
+ SorterRecord *pNext = p->pNext;
+ p->pNext = 0;
+ for(i=0; aSlot[i]; i++){
+ vdbeSorterMerge(pCsr, p, aSlot[i], &p);
+ aSlot[i] = 0;
+ }
+ aSlot[i] = p;
+ p = pNext;
+ }
+
+ p = 0;
+ for(i=0; i<64; i++){
+ vdbeSorterMerge(pCsr, p, aSlot[i], &p);
+ }
+ pSorter->pRecord = p;
+
+ sqlite3_free(aSlot);
+ return SQLITE_OK;
+}
+
+
+/*
+** Write the current contents of the in-memory linked-list to a PMA. Return
+** SQLITE_OK if successful, or an SQLite error code otherwise.
+**
+** The format of a PMA is:
+**
+** * A varint. This varint contains the total number of bytes of content
+** in the PMA (not including the varint itself).
+**
+** * One or more records packed end-to-end in order of ascending keys.
+** Each record consists of a varint followed by a blob of data (the
+** key). The varint is the number of bytes in the blob of data.
+*/
+static int vdbeSorterListToPMA(sqlite3 *db, VdbeCursor *pCsr){
+ int rc = SQLITE_OK; /* Return code */
+ VdbeSorter *pSorter = pCsr->pSorter;
+
+ if( pSorter->nInMemory==0 ){
+ assert( pSorter->pRecord==0 );
+ return rc;
+ }
+
+ rc = vdbeSorterSort(pCsr);
+
+ /* If the first temporary PMA file has not been opened, open it now. */
+ if( rc==SQLITE_OK && pSorter->pTemp1==0 ){
+ rc = vdbeSorterOpenTempFile(db, &pSorter->pTemp1);
+ assert( rc!=SQLITE_OK || pSorter->pTemp1 );
+ assert( pSorter->iWriteOff==0 );
+ assert( pSorter->nPMA==0 );
+ }
+
+ if( rc==SQLITE_OK ){
+ i64 iOff = pSorter->iWriteOff;
+ SorterRecord *p;
+ SorterRecord *pNext = 0;
+ static const char eightZeros[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ pSorter->nPMA++;
+ rc = vdbeSorterWriteVarint(pSorter->pTemp1, pSorter->nInMemory, &iOff);
+ for(p=pSorter->pRecord; rc==SQLITE_OK && p; p=pNext){
+ pNext = p->pNext;
+ rc = vdbeSorterWriteVarint(pSorter->pTemp1, p->nVal, &iOff);
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3OsWrite(pSorter->pTemp1, p->pVal, p->nVal, iOff);
+ iOff += p->nVal;
+ }
+
+ sqlite3DbFree(db, p);
+ }
+
+ /* This assert verifies that unless an error has occurred, the size of
+ ** the PMA on disk is the same as the expected size stored in
+ ** pSorter->nInMemory. */
+ assert( rc!=SQLITE_OK || pSorter->nInMemory==(
+ iOff-pSorter->iWriteOff-sqlite3VarintLen(pSorter->nInMemory)
+ ));
+
+ pSorter->iWriteOff = iOff;
+ if( rc==SQLITE_OK ){
+ /* Terminate each file with 8 extra bytes so that from any offset
+ ** in the file we can always read 9 bytes without a SHORT_READ error */
+ rc = sqlite3OsWrite(pSorter->pTemp1, eightZeros, 8, iOff);
+ }
+ pSorter->pRecord = p;
+ }
+
+ return rc;
+}
+
+/*
+** Add a record to the sorter.
+*/
+SQLITE_PRIVATE int sqlite3VdbeSorterWrite(
+ sqlite3 *db, /* Database handle */
+ VdbeCursor *pCsr, /* Sorter cursor */
+ Mem *pVal /* Memory cell containing record */
+){
+ VdbeSorter *pSorter = pCsr->pSorter;
+ int rc = SQLITE_OK; /* Return Code */
+ SorterRecord *pNew; /* New list element */
+
+ assert( pSorter );
+ pSorter->nInMemory += sqlite3VarintLen(pVal->n) + pVal->n;
+
+ pNew = (SorterRecord *)sqlite3DbMallocRaw(db, pVal->n + sizeof(SorterRecord));
+ if( pNew==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ pNew->pVal = (void *)&pNew[1];
+ memcpy(pNew->pVal, pVal->z, pVal->n);
+ pNew->nVal = pVal->n;
+ pNew->pNext = pSorter->pRecord;
+ pSorter->pRecord = pNew;
+ }
+
+ /* See if the contents of the sorter should now be written out. They
+ ** are written out when either of the following are true:
+ **
+ ** * The total memory allocated for the in-memory list is greater
+ ** than (page-size * cache-size), or
+ **
+ ** * The total memory allocated for the in-memory list is greater
+ ** than (page-size * 10) and sqlite3HeapNearlyFull() returns true.
+ */
+ if( rc==SQLITE_OK && pSorter->mxPmaSize>0 && (
+ (pSorter->nInMemory>pSorter->mxPmaSize)
+ || (pSorter->nInMemory>pSorter->mnPmaSize && sqlite3HeapNearlyFull())
+ )){
+ rc = vdbeSorterListToPMA(db, pCsr);
+ pSorter->nInMemory = 0;
+ }
+
+ return rc;
+}
+
+/*
+** Helper function for sqlite3VdbeSorterRewind().
+*/
+static int vdbeSorterInitMerge(
+ sqlite3 *db, /* Database handle */
+ VdbeCursor *pCsr, /* Cursor handle for this sorter */
+ i64 *pnByte /* Sum of bytes in all opened PMAs */
+){
+ VdbeSorter *pSorter = pCsr->pSorter;
+ int rc = SQLITE_OK; /* Return code */
+ int i; /* Used to iterator through aIter[] */
+ i64 nByte = 0; /* Total bytes in all opened PMAs */
+
+ /* Initialize the iterators. */
+ for(i=0; i<SORTER_MAX_MERGE_COUNT; i++){
+ VdbeSorterIter *pIter = &pSorter->aIter[i];
+ rc = vdbeSorterIterInit(db, pSorter, pSorter->iReadOff, pIter, &nByte);
+ pSorter->iReadOff = pIter->iEof;
+ assert( rc!=SQLITE_OK || pSorter->iReadOff<=pSorter->iWriteOff );
+ if( rc!=SQLITE_OK || pSorter->iReadOff>=pSorter->iWriteOff ) break;
+ }
+
+ /* Initialize the aTree[] array. */
+ for(i=pSorter->nTree-1; rc==SQLITE_OK && i>0; i--){
+ rc = vdbeSorterDoCompare(pCsr, i);
+ }
+
+ *pnByte = nByte;
+ return rc;
+}
+
+/*
+** Once the sorter has been populated, this function is called to prepare
+** for iterating through its contents in sorted order.
+*/
+SQLITE_PRIVATE int sqlite3VdbeSorterRewind(sqlite3 *db, VdbeCursor *pCsr, int *pbEof){
+ VdbeSorter *pSorter = pCsr->pSorter;
+ int rc; /* Return code */
+ sqlite3_file *pTemp2 = 0; /* Second temp file to use */
+ i64 iWrite2 = 0; /* Write offset for pTemp2 */
+ int nIter; /* Number of iterators used */
+ int nByte; /* Bytes of space required for aIter/aTree */
+ int N = 2; /* Power of 2 >= nIter */
+
+ assert( pSorter );
+
+ /* If no data has been written to disk, then do not do so now. Instead,
+ ** sort the VdbeSorter.pRecord list. The vdbe layer will read data directly
+ ** from the in-memory list. */
+ if( pSorter->nPMA==0 ){
+ *pbEof = !pSorter->pRecord;
+ assert( pSorter->aTree==0 );
+ return vdbeSorterSort(pCsr);
+ }
+
+ /* Write the current b-tree to a PMA. Close the b-tree cursor. */
+ rc = vdbeSorterListToPMA(db, pCsr);
+ if( rc!=SQLITE_OK ) return rc;
+
+ /* Allocate space for aIter[] and aTree[]. */
+ nIter = pSorter->nPMA;
+ if( nIter>SORTER_MAX_MERGE_COUNT ) nIter = SORTER_MAX_MERGE_COUNT;
+ assert( nIter>0 );
+ while( N<nIter ) N += N;
+ nByte = N * (sizeof(int) + sizeof(VdbeSorterIter));
+ pSorter->aIter = (VdbeSorterIter *)sqlite3DbMallocZero(db, nByte);
+ if( !pSorter->aIter ) return SQLITE_NOMEM;
+ pSorter->aTree = (int *)&pSorter->aIter[N];
+ pSorter->nTree = N;
+
+ do {
+ int iNew; /* Index of new, merged, PMA */
+
+ for(iNew=0;
+ rc==SQLITE_OK && iNew*SORTER_MAX_MERGE_COUNT<pSorter->nPMA;
+ iNew++
+ ){
+ i64 nWrite; /* Number of bytes in new PMA */
+
+ /* If there are SORTER_MAX_MERGE_COUNT or less PMAs in file pTemp1,
+ ** initialize an iterator for each of them and break out of the loop.
+ ** These iterators will be incrementally merged as the VDBE layer calls
+ ** sqlite3VdbeSorterNext().
+ **
+ ** Otherwise, if pTemp1 contains more than SORTER_MAX_MERGE_COUNT PMAs,
+ ** initialize interators for SORTER_MAX_MERGE_COUNT of them. These PMAs
+ ** are merged into a single PMA that is written to file pTemp2.
+ */
+ rc = vdbeSorterInitMerge(db, pCsr, &nWrite);
+ assert( rc!=SQLITE_OK || pSorter->aIter[ pSorter->aTree[1] ].pFile );
+ if( rc!=SQLITE_OK || pSorter->nPMA<=SORTER_MAX_MERGE_COUNT ){
+ break;
+ }
+
+ /* Open the second temp file, if it is not already open. */
+ if( pTemp2==0 ){
+ assert( iWrite2==0 );
+ rc = vdbeSorterOpenTempFile(db, &pTemp2);
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = vdbeSorterWriteVarint(pTemp2, nWrite, &iWrite2);
+ }
+
+ if( rc==SQLITE_OK ){
+ int bEof = 0;
+ while( rc==SQLITE_OK && bEof==0 ){
+ int nToWrite;
+ VdbeSorterIter *pIter = &pSorter->aIter[ pSorter->aTree[1] ];
+ assert( pIter->pFile );
+ nToWrite = pIter->nKey + sqlite3VarintLen(pIter->nKey);
+ rc = sqlite3OsWrite(pTemp2, pIter->aAlloc, nToWrite, iWrite2);
+ iWrite2 += nToWrite;
+ if( rc==SQLITE_OK ){
+ rc = sqlite3VdbeSorterNext(db, pCsr, &bEof);
+ }
+ }
+ }
+ }
+
+ if( pSorter->nPMA<=SORTER_MAX_MERGE_COUNT ){
+ break;
+ }else{
+ sqlite3_file *pTmp = pSorter->pTemp1;
+ pSorter->nPMA = iNew;
+ pSorter->pTemp1 = pTemp2;
+ pTemp2 = pTmp;
+ pSorter->iWriteOff = iWrite2;
+ pSorter->iReadOff = 0;
+ iWrite2 = 0;
+ }
+ }while( rc==SQLITE_OK );
+
+ if( pTemp2 ){
+ sqlite3OsCloseFree(pTemp2);
+ }
+ *pbEof = (pSorter->aIter[pSorter->aTree[1]].pFile==0);
+ return rc;
+}
+
+/*
+** Advance to the next element in the sorter.
+*/
+SQLITE_PRIVATE int sqlite3VdbeSorterNext(sqlite3 *db, VdbeCursor *pCsr, int *pbEof){
+ VdbeSorter *pSorter = pCsr->pSorter;
+ int rc; /* Return code */
+
+ if( pSorter->aTree ){
+ int iPrev = pSorter->aTree[1];/* Index of iterator to advance */
+ int i; /* Index of aTree[] to recalculate */
+
+ rc = vdbeSorterIterNext(db, &pSorter->aIter[iPrev]);
+ for(i=(pSorter->nTree+iPrev)/2; rc==SQLITE_OK && i>0; i=i/2){
+ rc = vdbeSorterDoCompare(pCsr, i);
+ }
+
+ *pbEof = (pSorter->aIter[pSorter->aTree[1]].pFile==0);
+ }else{
+ SorterRecord *pFree = pSorter->pRecord;
+ pSorter->pRecord = pFree->pNext;
+ pFree->pNext = 0;
+ vdbeSorterRecordFree(db, pFree);
+ *pbEof = !pSorter->pRecord;
+ rc = SQLITE_OK;
+ }
+ return rc;
+}
+
+/*
+** Return a pointer to a buffer owned by the sorter that contains the
+** current key.
+*/
+static void *vdbeSorterRowkey(
+ VdbeSorter *pSorter, /* Sorter object */
+ int *pnKey /* OUT: Size of current key in bytes */
+){
+ void *pKey;
+ if( pSorter->aTree ){
+ VdbeSorterIter *pIter;
+ pIter = &pSorter->aIter[ pSorter->aTree[1] ];
+ *pnKey = pIter->nKey;
+ pKey = pIter->aKey;
+ }else{
+ *pnKey = pSorter->pRecord->nVal;
+ pKey = pSorter->pRecord->pVal;
+ }
+ return pKey;
+}
+
+/*
+** Copy the current sorter key into the memory cell pOut.
+*/
+SQLITE_PRIVATE int sqlite3VdbeSorterRowkey(VdbeCursor *pCsr, Mem *pOut){
+ VdbeSorter *pSorter = pCsr->pSorter;
+ void *pKey; int nKey; /* Sorter key to copy into pOut */
+
+ pKey = vdbeSorterRowkey(pSorter, &nKey);
+ if( sqlite3VdbeMemGrow(pOut, nKey, 0) ){
+ return SQLITE_NOMEM;
+ }
+ pOut->n = nKey;
+ MemSetTypeFlag(pOut, MEM_Blob);
+ memcpy(pOut->z, pKey, nKey);
+
+ return SQLITE_OK;
+}
+
+/*
+** Compare the key in memory cell pVal with the key that the sorter cursor
+** passed as the first argument currently points to. For the purposes of
+** the comparison, ignore the rowid field at the end of each record.
+**
+** If an error occurs, return an SQLite error code (i.e. SQLITE_NOMEM).
+** Otherwise, set *pRes to a negative, zero or positive value if the
+** key in pVal is smaller than, equal to or larger than the current sorter
+** key.
+*/
+SQLITE_PRIVATE int sqlite3VdbeSorterCompare(
+ VdbeCursor *pCsr, /* Sorter cursor */
+ Mem *pVal, /* Value to compare to current sorter key */
+ int *pRes /* OUT: Result of comparison */
+){
+ VdbeSorter *pSorter = pCsr->pSorter;
+ void *pKey; int nKey; /* Sorter key to compare pVal with */
+
+ pKey = vdbeSorterRowkey(pSorter, &nKey);
+ vdbeSorterCompare(pCsr, 1, pVal->z, pVal->n, pKey, nKey, pRes);
+ return SQLITE_OK;
+}
+
+#endif /* #ifndef SQLITE_OMIT_MERGE_SORT */
+
+/************** End of vdbesort.c ********************************************/
/************** Begin file journal.c *****************************************/
/*
** 2007 August 22
** This file contains routines used for walking the parser tree for
** an SQL statement.
*/
+/* #include <stdlib.h> */
+/* #include <string.h> */
/*
** resolve all identifiers by associating them with a particular
** table and column.
*/
+/* #include <stdlib.h> */
+/* #include <string.h> */
/*
** Turn the pExpr expression into an alias for the iCol-th column of the
for(i=0; i<p->pSrc->nSrc; i++){
struct SrcList_item *pItem = &p->pSrc->a[i];
if( pItem->pSelect ){
+ NameContext *pNC; /* Used to iterate name contexts */
+ int nRef = 0; /* Refcount for pOuterNC and outer contexts */
const char *zSavedContext = pParse->zAuthContext;
+
+ /* Count the total number of references to pOuterNC and all of its
+ ** parent contexts. After resolving references to expressions in
+ ** pItem->pSelect, check if this value has changed. If so, then
+ ** SELECT statement pItem->pSelect must be correlated. Set the
+ ** pItem->isCorrelated flag if this is the case. */
+ for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef += pNC->nRef;
+
if( pItem->zName ) pParse->zAuthContext = pItem->zName;
sqlite3ResolveSelectNames(pParse, pItem->pSelect, pOuterNC);
pParse->zAuthContext = zSavedContext;
if( pParse->nErr || db->mallocFailed ) return WRC_Abort;
+
+ for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef -= pNC->nRef;
+ assert( pItem->isCorrelated==0 && nRef<=0 );
+ pItem->isCorrelated = (nRef!=0);
}
}
/* Wildcard of the form "?". Assign the next variable number */
assert( z[0]=='?' );
pExpr->iColumn = (ynVar)(++pParse->nVar);
- }else if( z[0]=='?' ){
- /* Wildcard of the form "?nnn". Convert "nnn" to an integer and
- ** use it as the variable number */
- i64 i;
- int bOk = 0==sqlite3Atoi64(&z[1], &i, sqlite3Strlen30(&z[1]), SQLITE_UTF8);
- pExpr->iColumn = (ynVar)i;
- testcase( i==0 );
- testcase( i==1 );
- testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]-1 );
- testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] );
- if( bOk==0 || i<1 || i>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){
- sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d",
- db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]);
- }
- if( i>pParse->nVar ){
- pParse->nVar = (int)i;
- }
}else{
- /* Wildcards like ":aaa", "$aaa" or "@aaa". Reuse the same variable
- ** number as the prior appearance of the same name, or if the name
- ** has never appeared before, reuse the same variable number
- */
- int i;
- u32 n;
- n = sqlite3Strlen30(z);
- for(i=0; i<pParse->nVarExpr; i++){
- Expr *pE = pParse->apVarExpr[i];
- assert( pE!=0 );
- if( memcmp(pE->u.zToken, z, n)==0 && pE->u.zToken[n]==0 ){
- pExpr->iColumn = pE->iColumn;
- break;
+ ynVar x = 0;
+ u32 n = sqlite3Strlen30(z);
+ if( z[0]=='?' ){
+ /* Wildcard of the form "?nnn". Convert "nnn" to an integer and
+ ** use it as the variable number */
+ i64 i;
+ int bOk = 0==sqlite3Atoi64(&z[1], &i, n-1, SQLITE_UTF8);
+ pExpr->iColumn = x = (ynVar)i;
+ testcase( i==0 );
+ testcase( i==1 );
+ testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]-1 );
+ testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] );
+ if( bOk==0 || i<1 || i>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){
+ sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d",
+ db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]);
+ x = 0;
+ }
+ if( i>pParse->nVar ){
+ pParse->nVar = (int)i;
}
+ }else{
+ /* Wildcards like ":aaa", "$aaa" or "@aaa". Reuse the same variable
+ ** number as the prior appearance of the same name, or if the name
+ ** has never appeared before, reuse the same variable number
+ */
+ ynVar i;
+ for(i=0; i<pParse->nzVar; i++){
+ if( pParse->azVar[i] && memcmp(pParse->azVar[i],z,n+1)==0 ){
+ pExpr->iColumn = x = (ynVar)i+1;
+ break;
+ }
+ }
+ if( x==0 ) x = pExpr->iColumn = (ynVar)(++pParse->nVar);
}
- if( i>=pParse->nVarExpr ){
- pExpr->iColumn = (ynVar)(++pParse->nVar);
- if( pParse->nVarExpr>=pParse->nVarExprAlloc-1 ){
- pParse->nVarExprAlloc += pParse->nVarExprAlloc + 10;
- pParse->apVarExpr =
- sqlite3DbReallocOrFree(
- db,
- pParse->apVarExpr,
- pParse->nVarExprAlloc*sizeof(pParse->apVarExpr[0])
- );
+ if( x>0 ){
+ if( x>pParse->nzVar ){
+ char **a;
+ a = sqlite3DbRealloc(db, pParse->azVar, x*sizeof(a[0]));
+ if( a==0 ) return; /* Error reported through db->mallocFailed */
+ pParse->azVar = a;
+ memset(&a[pParse->nzVar], 0, (x-pParse->nzVar)*sizeof(a[0]));
+ pParse->nzVar = x;
}
- if( !db->mallocFailed ){
- assert( pParse->apVarExpr!=0 );
- pParse->apVarExpr[pParse->nVarExpr++] = pExpr;
+ if( z[0]!='?' || pParse->azVar[x-1]==0 ){
+ sqlite3DbFree(db, pParse->azVar[x-1]);
+ pParse->azVar[x-1] = sqlite3DbStrNDup(db, z, n);
}
}
}
pNewItem->zAlias = sqlite3DbStrDup(db, pOldItem->zAlias);
pNewItem->jointype = pOldItem->jointype;
pNewItem->iCursor = pOldItem->iCursor;
- pNewItem->isPopulated = pOldItem->isPopulated;
+ pNewItem->addrFillSub = pOldItem->addrFillSub;
+ pNewItem->regReturn = pOldItem->regReturn;
+ pNewItem->isCorrelated = pOldItem->isCorrelated;
pNewItem->zIndex = sqlite3DbStrDup(db, pOldItem->zIndex);
pNewItem->notIndexed = pOldItem->notIndexed;
pNewItem->pIndex = pOldItem->pIndex;
int iMem = ++pParse->nMem;
int iAddr;
- iAddr = sqlite3VdbeAddOp1(v, OP_If, iMem);
- sqlite3VdbeAddOp2(v, OP_Integer, 1, iMem);
+ iAddr = sqlite3VdbeAddOp1(v, OP_Once, iMem);
sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead);
eType = IN_INDEX_ROWID;
char *pKey;
pKey = (char *)sqlite3IndexKeyinfo(pParse, pIdx);
- iAddr = sqlite3VdbeAddOp1(v, OP_If, iMem);
- sqlite3VdbeAddOp2(v, OP_Integer, 1, iMem);
+ iAddr = sqlite3VdbeAddOp1(v, OP_Once, iMem);
sqlite3VdbeAddOp4(v, OP_OpenRead, iTab, pIdx->tnum, iDb,
pKey,P4_KEYINFO_HANDOFF);
int rMayHaveNull, /* Register that records whether NULLs exist in RHS */
int isRowid /* If true, LHS of IN operator is a rowid */
){
- int testAddr = 0; /* One-time test address */
+ int testAddr = -1; /* One-time test address */
int rReg = 0; /* Register storing resulting */
Vdbe *v = sqlite3GetVdbe(pParse);
if( NEVER(v==0) ) return 0;
*/
if( !ExprHasAnyProperty(pExpr, EP_VarSelect) && !pParse->pTriggerTab ){
int mem = ++pParse->nMem;
- sqlite3VdbeAddOp1(v, OP_If, mem);
- testAddr = sqlite3VdbeAddOp2(v, OP_Integer, 1, mem);
- assert( testAddr>0 || pParse->db->mallocFailed );
+ testAddr = sqlite3VdbeAddOp1(v, OP_Once, mem);
}
#ifndef SQLITE_OMIT_EXPLAIN
if( pParse->explain==2 ){
char *zMsg = sqlite3MPrintf(
- pParse->db, "EXECUTE %s%s SUBQUERY %d", testAddr?"":"CORRELATED ",
+ pParse->db, "EXECUTE %s%s SUBQUERY %d", testAddr>=0?"":"CORRELATED ",
pExpr->op==TK_IN?"LIST":"SCALAR", pParse->iNextSelectId
);
sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC);
** this code only executes once. Because for a non-constant
** expression we need to rerun this code each time.
*/
- if( testAddr && !sqlite3ExprIsConstant(pE2) ){
- sqlite3VdbeChangeToNoop(v, testAddr-1, 2);
- testAddr = 0;
+ if( testAddr>=0 && !sqlite3ExprIsConstant(pE2) ){
+ sqlite3VdbeChangeToNoop(v, testAddr);
+ testAddr = -1;
}
/* Evaluate the expression and insert it into the temp table */
}
}
- if( testAddr ){
- sqlite3VdbeJumpHere(v, testAddr-1);
+ if( testAddr>=0 ){
+ sqlite3VdbeJumpHere(v, testAddr);
}
sqlite3ExprCachePop(pParse, 1);
inReg = pCol->iMem;
break;
}else if( pAggInfo->useSortingIdx ){
- sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdx,
+ sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdxPTab,
pCol->iSorterColumn, target);
break;
}
assert( pExpr->u.zToken[0]!=0 );
sqlite3VdbeAddOp2(v, OP_Variable, pExpr->iColumn, target);
if( pExpr->u.zToken[1]!=0 ){
- sqlite3VdbeChangeP4(v, -1, pExpr->u.zToken, P4_TRANSIENT);
+ assert( pExpr->u.zToken[0]=='?'
+ || strcmp(pExpr->u.zToken, pParse->azVar[pExpr->iColumn-1])==0 );
+ sqlite3VdbeChangeP4(v, -1, pParse->azVar[pExpr->iColumn-1], P4_STATIC);
}
break;
}
/* Reload the table, index and permanent trigger schemas. */
zWhere = sqlite3MPrintf(pParse->db, "tbl_name=%Q", zName);
if( !zWhere ) return;
- sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0, zWhere, P4_DYNAMIC);
+ sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere);
#ifndef SQLITE_OMIT_TRIGGER
/* Now, if the table is not stored in the temp database, reload any temp
** triggers. Don't use IN(...) in case SQLITE_OMIT_SUBQUERY is defined.
*/
if( (zWhere=whereTempTriggers(pParse, pTab))!=0 ){
- sqlite3VdbeAddOp4(v, OP_ParseSchema, 1, 0, 0, zWhere, P4_DYNAMIC);
+ sqlite3VdbeAddParseSchemaOp(v, 1, zWhere);
}
#endif
}
sqlite3 *db = sqlite3_context_db_handle(context);
const char *zName;
const char *zFile;
+ char *zPath = 0;
+ char *zErr = 0;
+ unsigned int flags;
Db *aNew;
char *zErrDyn = 0;
+ sqlite3_vfs *pVfs;
UNUSED_PARAMETER(NotUsed);
** it to obtain the database schema. At this point the schema may
** or may not be initialised.
*/
- rc = sqlite3BtreeOpen(zFile, db, &aNew->pBt, 0,
- db->openFlags | SQLITE_OPEN_MAIN_DB);
+ flags = db->openFlags;
+ rc = sqlite3ParseUri(db->pVfs->zName, zFile, &flags, &pVfs, &zPath, &zErr);
+ if( rc!=SQLITE_OK ){
+ if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;
+ sqlite3_result_error(context, zErr, -1);
+ sqlite3_free(zErr);
+ return;
+ }
+ assert( pVfs );
+ flags |= SQLITE_OPEN_MAIN_DB;
+ rc = sqlite3BtreeOpen(pVfs, zPath, db, &aNew->pBt, 0, flags);
+ sqlite3_free( zPath );
db->nDb++;
if( rc==SQLITE_CONSTRAINT ){
rc = SQLITE_ERROR;
/* A minimum of one cursor is required if autoincrement is used
* See ticket [a696379c1f08866] */
if( pParse->pAinc!=0 && pParse->nTab==0 ) pParse->nTab = 1;
- sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem,
- pParse->nTab, pParse->nMaxArg, pParse->explain,
- pParse->isMultiWrite && pParse->mayAbort);
+ sqlite3VdbeMakeReady(v, pParse);
pParse->rc = SQLITE_DONE;
pParse->colNamesSet = 0;
}else{
#endif
/* Reparse everything to update our internal data structures */
- sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0,
- sqlite3MPrintf(db, "tbl_name='%q'",p->zName), P4_DYNAMIC);
+ sqlite3VdbeAddParseSchemaOp(v, iDb,
+ sqlite3MPrintf(db, "tbl_name='%q'", p->zName));
}
const char *z;
Token sEnd;
DbFixer sFix;
- Token *pName;
+ Token *pName = 0;
int iDb;
sqlite3 *db = pParse->db;
}
/*
+** Remove entries from the sqlite_stat1 and sqlite_stat2 tables
+** after a DROP INDEX or DROP TABLE command.
+*/
+static void sqlite3ClearStatTables(
+ Parse *pParse, /* The parsing context */
+ int iDb, /* The database number */
+ const char *zType, /* "idx" or "tbl" */
+ const char *zName /* Name of index or table */
+){
+ static const char *azStatTab[] = { "sqlite_stat1", "sqlite_stat2" };
+ int i;
+ const char *zDbName = pParse->db->aDb[iDb].zName;
+ for(i=0; i<ArraySize(azStatTab); i++){
+ if( sqlite3FindTable(pParse->db, azStatTab[i], zDbName) ){
+ sqlite3NestedParse(pParse,
+ "DELETE FROM %Q.%s WHERE %s=%Q",
+ zDbName, azStatTab[i], zType, zName
+ );
+ }
+ }
+}
+
+/*
** This routine is called to do the work of a DROP TABLE statement.
** pName is the name of the table to be dropped.
*/
sqlite3NestedParse(pParse,
"DELETE FROM %Q.%s WHERE tbl_name=%Q and type!='trigger'",
pDb->zName, SCHEMA_TABLE(iDb), pTab->zName);
-
- /* Drop any statistics from the sqlite_stat1 table, if it exists */
- if( sqlite3FindTable(db, "sqlite_stat1", db->aDb[iDb].zName) ){
- sqlite3NestedParse(pParse,
- "DELETE FROM %Q.sqlite_stat1 WHERE tbl=%Q", pDb->zName, pTab->zName
- );
- }
-
+ sqlite3ClearStatTables(pParse, iDb, "tbl", pTab->zName);
if( !isView && !IsVirtual(pTab) ){
destroyTable(pParse, pTab);
}
Table *pTab = pIndex->pTable; /* The table that is indexed */
int iTab = pParse->nTab++; /* Btree cursor used for pTab */
int iIdx = pParse->nTab++; /* Btree cursor used for pIndex */
+ int iSorter = iTab; /* Cursor opened by OpenSorter (if in use) */
int addr1; /* Address of top of loop */
+ int addr2; /* Address to jump to for next iteration */
int tnum; /* Root page of index */
Vdbe *v; /* Generate code into this virtual machine */
KeyInfo *pKey; /* KeyInfo for index */
if( memRootPage>=0 ){
sqlite3VdbeChangeP5(v, 1);
}
+
+#ifndef SQLITE_OMIT_MERGE_SORT
+ /* Open the sorter cursor if we are to use one. */
+ iSorter = pParse->nTab++;
+ sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0, 0, (char*)pKey, P4_KEYINFO);
+#endif
+
+ /* Open the table. Loop through all rows of the table, inserting index
+ ** records into the sorter. */
sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead);
addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0);
+ addr2 = addr1 + 1;
regRecord = sqlite3GetTempReg(pParse);
regIdxKey = sqlite3GenerateIndexKey(pParse, pIndex, iTab, regRecord, 1);
+
+#ifndef SQLITE_OMIT_MERGE_SORT
+ sqlite3VdbeAddOp2(v, OP_SorterInsert, iSorter, regRecord);
+ sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1);
+ sqlite3VdbeJumpHere(v, addr1);
+ addr1 = sqlite3VdbeAddOp2(v, OP_SorterSort, iSorter, 0);
+ if( pIndex->onError!=OE_None ){
+ int j2 = sqlite3VdbeCurrentAddr(v) + 3;
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, j2);
+ addr2 = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp3(v, OP_SorterCompare, iSorter, j2, regRecord);
+ sqlite3HaltConstraint(
+ pParse, OE_Abort, "indexed columns are not unique", P4_STATIC
+ );
+ }else{
+ addr2 = sqlite3VdbeCurrentAddr(v);
+ }
+ sqlite3VdbeAddOp2(v, OP_SorterData, iSorter, regRecord);
+ sqlite3VdbeAddOp3(v, OP_IdxInsert, iIdx, regRecord, 1);
+ sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
+#else
if( pIndex->onError!=OE_None ){
const int regRowid = regIdxKey + pIndex->nColumn;
const int j2 = sqlite3VdbeCurrentAddr(v) + 2;
sqlite3HaltConstraint(
pParse, OE_Abort, "indexed columns are not unique", P4_STATIC);
}
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdx, regRecord);
+ sqlite3VdbeAddOp3(v, OP_IdxInsert, iIdx, regRecord, 0);
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
+#endif
sqlite3ReleaseTempReg(pParse, regRecord);
- sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1);
+ sqlite3VdbeAddOp2(v, OP_SorterNext, iSorter, addr2);
sqlite3VdbeJumpHere(v, addr1);
+
sqlite3VdbeAddOp1(v, OP_Close, iTab);
sqlite3VdbeAddOp1(v, OP_Close, iIdx);
+ sqlite3VdbeAddOp1(v, OP_Close, iSorter);
}
/*
/* A named index with an explicit CREATE INDEX statement */
zStmt = sqlite3MPrintf(db, "CREATE%s INDEX %.*s",
onError==OE_None ? "" : " UNIQUE",
- pEnd->z - pName->z + 1,
+ (int)(pEnd->z - pName->z) + 1,
pName->z);
}else{
/* An automatic index created by a PRIMARY KEY or UNIQUE constraint */
if( pTblName ){
sqlite3RefillIndex(pParse, pIndex, iMem);
sqlite3ChangeCookie(pParse, iDb);
- sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0,
- sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName),
- P4_DYNAMIC);
+ sqlite3VdbeAddParseSchemaOp(v, iDb,
+ sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName));
sqlite3VdbeAddOp1(v, OP_Expire, 0);
}
}
sqlite3BeginWriteOperation(pParse, 1, iDb);
sqlite3NestedParse(pParse,
"DELETE FROM %Q.%s WHERE name=%Q AND type='index'",
- db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
- pIndex->zName
+ db->aDb[iDb].zName, SCHEMA_TABLE(iDb), pIndex->zName
);
- if( sqlite3FindTable(db, "sqlite_stat1", db->aDb[iDb].zName) ){
- sqlite3NestedParse(pParse,
- "DELETE FROM %Q.sqlite_stat1 WHERE idx=%Q",
- db->aDb[iDb].zName, pIndex->zName
- );
- }
+ sqlite3ClearStatTables(pParse, iDb, "idx", pIndex->zName);
sqlite3ChangeCookie(pParse, iDb);
destroyRootPage(pParse, pIndex->tnum, iDb);
sqlite3VdbeAddOp4(v, OP_DropIndex, iDb, 0, 0, pIndex->zName, 0);
** operator with A. This routine shifts that operator over to B.
*/
SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(SrcList *p){
- if( p && p->a ){
+ if( p ){
int i;
+ assert( p->a || p->nSrc==0 );
for(i=p->nSrc-1; i>0; i--){
p->a[i].jointype = p->a[i-1].jointype;
}
SQLITE_OPEN_DELETEONCLOSE |
SQLITE_OPEN_TEMP_DB;
- rc = sqlite3BtreeOpen(0, db, &pBt, 0, flags);
+ rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pBt, 0, flags);
if( rc!=SQLITE_OK ){
sqlite3ErrorMsg(pParse, "unable to open a temporary database "
"file for storing temporary tables");
/* Collect rowids of every row to be deleted.
*/
sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet);
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere,0,WHERE_DUPLICATES_OK);
+ pWInfo = sqlite3WhereBegin(
+ pParse, pTabList, pWhere, 0, 0, WHERE_DUPLICATES_OK
+ );
if( pWInfo==0 ) goto delete_from_cleanup;
regRowid = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, iRowid);
sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, regRowid);
const char *pVTab = (const char *)sqlite3GetVTable(db, pTab);
sqlite3VtabMakeWritable(pParse, pTab);
sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iRowid, pVTab, P4_VTAB);
+ sqlite3VdbeChangeP5(v, OE_Abort);
sqlite3MayAbort(pParse);
}else
#endif
}
}
if( doMakeRec ){
+ const char *zAff;
+ if( pTab->pSelect || (pParse->db->flags & SQLITE_IdxRealAsInt)!=0 ){
+ zAff = 0;
+ }else{
+ zAff = sqlite3IndexAffinityStr(v, pIdx);
+ }
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol+1, regOut);
- sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), P4_TRANSIENT);
+ sqlite3VdbeChangeP4(v, -1, zAff, P4_TRANSIENT);
}
sqlite3ReleaseTempRange(pParse, regBase, nCol+1);
return regBase;
** sqliteRegisterBuildinFunctions() found at the bottom of the file.
** All other code has file scope.
*/
+/* #include <stdlib.h> */
+/* #include <assert.h> */
/*
** Return the collating function associated with a function.
** whereas only characters less than 0x80 do in ASCII.
*/
#if defined(SQLITE_EBCDIC)
-# define sqlite3Utf8Read(A,C) (*(A++))
-# define GlogUpperToLower(A) A = sqlite3UpperToLower[A]
+# define sqlite3Utf8Read(A,C) (*(A++))
+# define GlogUpperToLower(A) A = sqlite3UpperToLower[A]
#else
-# define GlogUpperToLower(A) if( A<0x80 ){ A = sqlite3UpperToLower[A]; }
+# define GlogUpperToLower(A) if( !((A)&~0x7f) ){ A = sqlite3UpperToLower[A]; }
#endif
static const struct compareInfo globInfo = { '*', '?', '[', 0 };
const u8 *zPattern, /* The glob pattern */
const u8 *zString, /* The string to compare against the glob */
const struct compareInfo *pInfo, /* Information about how to do the compare */
- const int esc /* The escape character */
+ u32 esc /* The escape character */
){
- int c, c2;
+ u32 c, c2;
int invert;
int seen;
u8 matchOne = pInfo->matchOne;
return 0;
}
}else if( c==matchSet ){
- int prior_c = 0;
+ u32 prior_c = 0;
assert( esc==0 ); /* This only occurs for GLOB, not LIKE */
seen = 0;
invert = 0;
sqlite3_value **argv
){
const unsigned char *zA, *zB;
- int escape = 0;
+ u32 escape = 0;
int nPat;
sqlite3 *db = sqlite3_context_db_handle(context);
}
/*
+** Implementation of the sqlite_log() function. This is a wrapper around
+** sqlite3_log(). The return value is NULL. The function exists purely for
+** its side-effects.
+*/
+static void errlogFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ UNUSED_PARAMETER(argc);
+ UNUSED_PARAMETER(context);
+ sqlite3_log(sqlite3_value_int(argv[0]), "%s", sqlite3_value_text(argv[1]));
+}
+
+/*
** Implementation of the sqlite_compileoption_used() function.
** The result is an integer that identifies if the compiler option
** was used to build SQLite.
FUNCTION(nullif, 2, 0, 1, nullifFunc ),
FUNCTION(sqlite_version, 0, 0, 0, versionFunc ),
FUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ),
+ FUNCTION(sqlite_log, 2, 0, 0, errlogFunc ),
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
FUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ),
FUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ),
/* If the parent table is the same as the child table, and we are about
** to increment the constraint-counter (i.e. this is an INSERT operation),
** then check if the row being inserted matches itself. If so, do not
- ** increment the constraint-counter. */
+ ** increment the constraint-counter.
+ **
+ ** If any of the parent-key values are NULL, then the row cannot match
+ ** itself. So set JUMPIFNULL to make sure we do the OP_Found if any
+ ** of the parent-key values are NULL (at this point it is known that
+ ** none of the child key values are).
+ */
if( pTab==pFKey->pFrom && nIncr==1 ){
int iJump = sqlite3VdbeCurrentAddr(v) + nCol + 1;
for(i=0; i<nCol; i++){
int iChild = aiCol[i]+1+regData;
int iParent = pIdx->aiColumn[i]+1+regData;
+ assert( aiCol[i]!=pTab->iPKey );
+ if( pIdx->aiColumn[i]==pTab->iPKey ){
+ /* The parent key is a composite key that includes the IPK column */
+ iParent = regData;
+ }
sqlite3VdbeAddOp3(v, OP_Ne, iChild, iJump, iParent);
+ sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL);
}
sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk);
}
** clause. If the constraint is not deferred, throw an exception for
** each row found. Otherwise, for deferred constraints, increment the
** deferred constraint counter by nIncr for each row selected. */
- pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0);
+ pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0);
if( nIncr>0 && pFKey->isDeferred==0 ){
sqlite3ParseToplevel(pParse)->mayAbort = 1;
}
pTo = sqlite3LocateTable(pParse, 0, pFKey->zTo, zDb);
}
if( !pTo || locateFkeyIndex(pParse, pTo, pFKey, &pIdx, &aiFree) ){
+ assert( isIgnoreErrors==0 || (regOld!=0 && regNew==0) );
if( !isIgnoreErrors || db->mallocFailed ) return;
+ if( pTo==0 ){
+ /* If isIgnoreErrors is true, then a table is being dropped. In this
+ ** case SQLite runs a "DELETE FROM xxx" on the table being dropped
+ ** before actually dropping it in order to check FK constraints.
+ ** If the parent table of an FK constraint on the current table is
+ ** missing, behave as if it is empty. i.e. decrement the relevant
+ ** FK counter for each row of the current table with non-NULL keys.
+ */
+ Vdbe *v = sqlite3GetVdbe(pParse);
+ int iJump = sqlite3VdbeCurrentAddr(v) + pFKey->nCol + 1;
+ for(i=0; i<pFKey->nCol; i++){
+ int iReg = pFKey->aCol[i].iFrom + regOld + 1;
+ sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iJump);
+ }
+ sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, -1);
+ }
continue;
}
assert( pFKey->nCol==1 || (aiFree && pIdx) );
const char *pVTab = (const char *)sqlite3GetVTable(db, pTab);
sqlite3VtabMakeWritable(pParse, pTab);
sqlite3VdbeAddOp4(v, OP_VUpdate, 1, pTab->nCol+2, regIns, pVTab, P4_VTAB);
+ sqlite3VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError);
sqlite3MayAbort(pParse);
}else
#endif
return 0; /* Tables have different CHECK constraints. Ticket #2252 */
}
#endif
+#ifndef SQLITE_OMIT_FOREIGN_KEY
+ /* Disallow the transfer optimization if the destination table constains
+ ** any foreign key constraints. This is more restrictive than necessary.
+ ** But the main beneficiary of the transfer optimization is the VACUUM
+ ** command, and the VACUUM command disables foreign key constraints. So
+ ** the extra complication to make this rule less restrictive is probably
+ ** not worth the effort. Ticket [6284df89debdfa61db8073e062908af0c9b6118e]
+ */
+ if( (pParse->db->flags & SQLITE_ForeignKeys)!=0 && pDest->pFKey!=0 ){
+ return 0;
+ }
+#endif
/* If we get this far, it means either:
**
int (*wal_autocheckpoint)(sqlite3*,int);
int (*wal_checkpoint)(sqlite3*,const char*);
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
+ int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
+ int (*vtab_config)(sqlite3*,int op,...);
+ int (*vtab_on_conflict)(sqlite3*);
};
/*
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
#define sqlite3_wal_hook sqlite3_api->wal_hook
+#define sqlite3_blob_reopen sqlite3_api->blob_reopen
+#define sqlite3_vtab_config sqlite3_api->vtab_config
+#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
#endif /* SQLITE_CORE */
#define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api = 0;
/************** End of sqlite3ext.h ******************************************/
/************** Continuing where we left off in loadext.c ********************/
+/* #include <string.h> */
#ifndef SQLITE_OMIT_LOAD_EXTENSION
# define sqlite3_create_module 0
# define sqlite3_create_module_v2 0
# define sqlite3_declare_vtab 0
+# define sqlite3_vtab_config 0
+# define sqlite3_vtab_on_conflict 0
#endif
#ifdef SQLITE_OMIT_SHARED_CACHE
#define sqlite3_blob_open 0
#define sqlite3_blob_read 0
#define sqlite3_blob_write 0
+#define sqlite3_blob_reopen 0
#endif
/*
0,
0,
#endif
+ sqlite3_blob_reopen,
+ sqlite3_vtab_config,
+ sqlite3_vtab_on_conflict,
};
/*
** This file contains code used to implement the PRAGMA command.
*/
-/* Ignore this whole file if pragmas are disabled
-*/
-#if !defined(SQLITE_OMIT_PRAGMA)
-
/*
** Interpret the given string as a safety level. Return 0 for OFF,
** 1 for ON or NORMAL and 2 for FULL. Return 1 for an empty or
/*
** Interpret the given string as a boolean value.
*/
-static u8 getBoolean(const char *z){
+SQLITE_PRIVATE u8 sqlite3GetBoolean(const char *z){
return getSafetyLevel(z)&1;
}
+/* The sqlite3GetBoolean() function is used by other modules but the
+** remainder of this file is specific to PRAGMA processing. So omit
+** the rest of the file if PRAGMAs are omitted from the build.
+*/
+#if !defined(SQLITE_OMIT_PRAGMA)
+
/*
** Interpret the given string as a locking mode value.
*/
mask &= ~(SQLITE_ForeignKeys);
}
- if( getBoolean(zRight) ){
+ if( sqlite3GetBoolean(zRight) ){
db->flags |= mask;
}else{
db->flags &= ~mask;
int b = -1;
assert( pBt!=0 );
if( zRight ){
- b = getBoolean(zRight);
+ b = sqlite3GetBoolean(zRight);
}
if( pId2->n==0 && b>=0 ){
int ii;
#ifndef NDEBUG
if( sqlite3StrICmp(zLeft, "parser_trace")==0 ){
if( zRight ){
- if( getBoolean(zRight) ){
+ if( sqlite3GetBoolean(zRight) ){
sqlite3ParserTrace(stderr, "parser: ");
}else{
sqlite3ParserTrace(0, 0);
*/
if( sqlite3StrICmp(zLeft, "case_sensitive_like")==0 ){
if( zRight ){
- sqlite3RegisterLikeFunctions(db, getBoolean(zRight));
+ sqlite3RegisterLikeFunctions(db, sqlite3GetBoolean(zRight));
}
}else
clearSelect(db, pNew);
if( pNew!=&standin ) sqlite3DbFree(db, pNew);
pNew = 0;
+ }else{
+ assert( pNew->pSrc!=0 || pParse->nErr>0 );
}
return pNew;
}
int nExpr = pOrderBy->nExpr;
int regBase = sqlite3GetTempRange(pParse, nExpr+2);
int regRecord = sqlite3GetTempReg(pParse);
+ int op;
sqlite3ExprCacheClear(pParse);
sqlite3ExprCodeExprList(pParse, pOrderBy, regBase, 0);
sqlite3VdbeAddOp2(v, OP_Sequence, pOrderBy->iECursor, regBase+nExpr);
sqlite3ExprCodeMove(pParse, regData, regBase+nExpr+1, 1);
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nExpr + 2, regRecord);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, pOrderBy->iECursor, regRecord);
+ if( pSelect->selFlags & SF_UseSorter ){
+ op = OP_SorterInsert;
+ }else{
+ op = OP_IdxInsert;
+ }
+ sqlite3VdbeAddOp2(v, op, pOrderBy->iECursor, regRecord);
sqlite3ReleaseTempReg(pParse, regRecord);
sqlite3ReleaseTempRange(pParse, regBase, nExpr+2);
if( pSelect->iLimit ){
}else{
regRowid = sqlite3GetTempReg(pParse);
}
- addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak);
- codeOffset(v, p, addrContinue);
- sqlite3VdbeAddOp3(v, OP_Column, iTab, pOrderBy->nExpr + 1, regRow);
+ if( p->selFlags & SF_UseSorter ){
+ int regSortOut = ++pParse->nMem;
+ int ptab2 = pParse->nTab++;
+ sqlite3VdbeAddOp3(v, OP_OpenPseudo, ptab2, regSortOut, pOrderBy->nExpr+2);
+ addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak);
+ codeOffset(v, p, addrContinue);
+ sqlite3VdbeAddOp2(v, OP_SorterData, iTab, regSortOut);
+ sqlite3VdbeAddOp3(v, OP_Column, ptab2, pOrderBy->nExpr+1, regRow);
+ sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE);
+ }else{
+ addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak);
+ codeOffset(v, p, addrContinue);
+ sqlite3VdbeAddOp3(v, OP_Column, iTab, pOrderBy->nExpr+1, regRow);
+ }
switch( eDest ){
case SRT_Table:
case SRT_EphemTab: {
/* The bottom of the loop
*/
sqlite3VdbeResolveLabel(v, addrContinue);
- sqlite3VdbeAddOp2(v, OP_Next, iTab, addr);
+ if( p->selFlags & SF_UseSorter ){
+ sqlite3VdbeAddOp2(v, OP_SorterNext, iTab, addr);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Next, iTab, addr);
+ }
sqlite3VdbeResolveLabel(v, addrBreak);
if( eDest==SRT_Output || eDest==SRT_Coroutine ){
sqlite3VdbeAddOp2(v, OP_Close, pseudoTab, 0);
int distinct; /* Table to use for the distinct set */
int rc = 1; /* Value to return from this function */
int addrSortIndex; /* Address of an OP_OpenEphemeral instruction */
+ int addrDistinctIndex; /* Address of an OP_OpenEphemeral instruction */
AggInfo sAggInfo; /* Information used by aggregate queries */
int iEnd; /* Address of the end of the query */
sqlite3 *db; /* The database connection */
Select *pSub = pItem->pSelect;
int isAggSub;
- if( pSub==0 || pItem->isPopulated ) continue;
+ if( pSub==0 ) continue;
+ if( pItem->addrFillSub ){
+ sqlite3VdbeAddOp2(v, OP_Gosub, pItem->regReturn, pItem->addrFillSub);
+ continue;
+ }
/* Increment Parse.nHeight by the height of the largest expression
** tree refered to by this, the parent select. The child select
*/
pParse->nHeight += sqlite3SelectExprHeight(p);
- /* Check to see if the subquery can be absorbed into the parent. */
isAggSub = (pSub->selFlags & SF_Aggregate)!=0;
if( flattenSubquery(pParse, p, i, isAgg, isAggSub) ){
+ /* This subquery can be absorbed into its parent. */
if( isAggSub ){
isAgg = 1;
p->selFlags |= SF_Aggregate;
}
i = -1;
}else{
+ /* Generate a subroutine that will fill an ephemeral table with
+ ** the content of this subquery. pItem->addrFillSub will point
+ ** to the address of the generated subroutine. pItem->regReturn
+ ** is a register allocated to hold the subroutine return address
+ */
+ int topAddr;
+ int onceAddr = 0;
+ int retAddr;
+ assert( pItem->addrFillSub==0 );
+ pItem->regReturn = ++pParse->nMem;
+ topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn);
+ pItem->addrFillSub = topAddr+1;
+ VdbeNoopComment((v, "materialize %s", pItem->pTab->zName));
+ if( pItem->isCorrelated==0 && pParse->pTriggerTab==0 ){
+ /* If the subquery is no correlated and if we are not inside of
+ ** a trigger, then we only need to compute the value of the subquery
+ ** once. */
+ int regOnce = ++pParse->nMem;
+ onceAddr = sqlite3VdbeAddOp1(v, OP_Once, regOnce);
+ }
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
- assert( pItem->isPopulated==0 );
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
sqlite3Select(pParse, pSub, &dest);
- pItem->isPopulated = 1;
pItem->pTab->nRowEst = (unsigned)pSub->nSelectRow;
+ if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr);
+ retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn);
+ VdbeComment((v, "end %s", pItem->pTab->zName));
+ sqlite3VdbeChangeP1(v, topAddr, retAddr);
+
}
if( /*pParse->nErr ||*/ db->mallocFailed ){
goto select_end;
}
#endif
- /* If possible, rewrite the query to use GROUP BY instead of DISTINCT.
- ** GROUP BY might use an index, DISTINCT never does.
- */
- assert( p->pGroupBy==0 || (p->selFlags & SF_Aggregate)!=0 );
- if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct ){
- p->pGroupBy = sqlite3ExprListDup(db, p->pEList, 0);
- pGroupBy = p->pGroupBy;
- p->selFlags &= ~SF_Distinct;
- }
-
/* If there is both a GROUP BY and an ORDER BY clause and they are
** identical, then disable the ORDER BY clause since the GROUP BY
** will cause elements to come out in the correct order. This is
pOrderBy = 0;
}
+ /* If the query is DISTINCT with an ORDER BY but is not an aggregate, and
+ ** if the select-list is the same as the ORDER BY list, then this query
+ ** can be rewritten as a GROUP BY. In other words, this:
+ **
+ ** SELECT DISTINCT xyz FROM ... ORDER BY xyz
+ **
+ ** is transformed to:
+ **
+ ** SELECT xyz FROM ... GROUP BY xyz
+ **
+ ** The second form is preferred as a single index (or temp-table) may be
+ ** used for both the ORDER BY and DISTINCT processing. As originally
+ ** written the query must use a temp-table for at least one of the ORDER
+ ** BY and DISTINCT, and an index or separate temp-table for the other.
+ */
+ if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct
+ && sqlite3ExprListCompare(pOrderBy, p->pEList)==0
+ ){
+ p->selFlags &= ~SF_Distinct;
+ p->pGroupBy = sqlite3ExprListDup(db, p->pEList, 0);
+ pGroupBy = p->pGroupBy;
+ pOrderBy = 0;
+ }
+
/* If there is an ORDER BY clause, then this sorting
** index might end up being unused if the data can be
** extracted in pre-sorted order. If that is the case, then the
iEnd = sqlite3VdbeMakeLabel(v);
p->nSelectRow = (double)LARGEST_INT64;
computeLimitRegisters(pParse, p, iEnd);
+ if( p->iLimit==0 && addrSortIndex>=0 ){
+ sqlite3VdbeGetOp(v, addrSortIndex)->opcode = OP_SorterOpen;
+ p->selFlags |= SF_UseSorter;
+ }
/* Open a virtual index to use for the distinct set.
*/
if( p->selFlags & SF_Distinct ){
KeyInfo *pKeyInfo;
- assert( isAgg || pGroupBy );
distinct = pParse->nTab++;
pKeyInfo = keyInfoFromExprList(pParse, p->pEList);
- sqlite3VdbeAddOp4(v, OP_OpenEphemeral, distinct, 0, 0,
- (char*)pKeyInfo, P4_KEYINFO_HANDOFF);
+ addrDistinctIndex = sqlite3VdbeAddOp4(v, OP_OpenEphemeral, distinct, 0, 0,
+ (char*)pKeyInfo, P4_KEYINFO_HANDOFF);
sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
}else{
- distinct = -1;
+ distinct = addrDistinctIndex = -1;
}
/* Aggregate and non-aggregate queries are handled differently */
if( !isAgg && pGroupBy==0 ){
- /* This case is for non-aggregate queries
- ** Begin the database scan
- */
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy, 0);
+ ExprList *pDist = (isDistinct ? p->pEList : 0);
+
+ /* Begin the database scan. */
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy, pDist, 0);
if( pWInfo==0 ) goto select_end;
if( pWInfo->nRowOut < p->nSelectRow ) p->nSelectRow = pWInfo->nRowOut;
** into an OP_Noop.
*/
if( addrSortIndex>=0 && pOrderBy==0 ){
- sqlite3VdbeChangeToNoop(v, addrSortIndex, 1);
+ sqlite3VdbeChangeToNoop(v, addrSortIndex);
p->addrOpenEphm[2] = -1;
}
- /* Use the standard inner loop
- */
- assert(!isDistinct);
- selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, -1, pDest,
+ if( pWInfo->eDistinct ){
+ VdbeOp *pOp; /* No longer required OpenEphemeral instr. */
+
+ assert( addrDistinctIndex>=0 );
+ pOp = sqlite3VdbeGetOp(v, addrDistinctIndex);
+
+ assert( isDistinct );
+ assert( pWInfo->eDistinct==WHERE_DISTINCT_ORDERED
+ || pWInfo->eDistinct==WHERE_DISTINCT_UNIQUE
+ );
+ distinct = -1;
+ if( pWInfo->eDistinct==WHERE_DISTINCT_ORDERED ){
+ int iJump;
+ int iExpr;
+ int iFlag = ++pParse->nMem;
+ int iBase = pParse->nMem+1;
+ int iBase2 = iBase + pEList->nExpr;
+ pParse->nMem += (pEList->nExpr*2);
+
+ /* Change the OP_OpenEphemeral coded earlier to an OP_Integer. The
+ ** OP_Integer initializes the "first row" flag. */
+ pOp->opcode = OP_Integer;
+ pOp->p1 = 1;
+ pOp->p2 = iFlag;
+
+ sqlite3ExprCodeExprList(pParse, pEList, iBase, 1);
+ iJump = sqlite3VdbeCurrentAddr(v) + 1 + pEList->nExpr + 1 + 1;
+ sqlite3VdbeAddOp2(v, OP_If, iFlag, iJump-1);
+ for(iExpr=0; iExpr<pEList->nExpr; iExpr++){
+ CollSeq *pColl = sqlite3ExprCollSeq(pParse, pEList->a[iExpr].pExpr);
+ sqlite3VdbeAddOp3(v, OP_Ne, iBase+iExpr, iJump, iBase2+iExpr);
+ sqlite3VdbeChangeP4(v, -1, (const char *)pColl, P4_COLLSEQ);
+ sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
+ }
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, pWInfo->iContinue);
+
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, iFlag);
+ assert( sqlite3VdbeCurrentAddr(v)==iJump );
+ sqlite3VdbeAddOp3(v, OP_Move, iBase, iBase2, pEList->nExpr);
+ }else{
+ pOp->opcode = OP_Noop;
+ }
+ }
+
+ /* Use the standard inner loop. */
+ selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, distinct, pDest,
pWInfo->iContinue, pWInfo->iBreak);
/* End the database scan loop.
int iAbortFlag; /* Mem address which causes query abort if positive */
int groupBySort; /* Rows come from source in GROUP BY order */
int addrEnd; /* End of processing for this SELECT */
+ int sortPTab = 0; /* Pseudotable used to decode sorting results */
+ int sortOut = 0; /* Output register from the sorter */
/* Remove any and all aliases between the result set and the
** GROUP BY clause.
/* If there is a GROUP BY clause we might need a sorting index to
** implement it. Allocate that sorting index now. If it turns out
- ** that we do not need it after all, the OpenEphemeral instruction
+ ** that we do not need it after all, the OP_SorterOpen instruction
** will be converted into a Noop.
*/
sAggInfo.sortingIdx = pParse->nTab++;
pKeyInfo = keyInfoFromExprList(pParse, pGroupBy);
- addrSortingIdx = sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
+ addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen,
sAggInfo.sortingIdx, sAggInfo.nSortingColumn,
0, (char*)pKeyInfo, P4_KEYINFO_HANDOFF);
** in the right order to begin with.
*/
sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pGroupBy, 0);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pGroupBy, 0, 0);
if( pWInfo==0 ) goto select_end;
if( pGroupBy==0 ){
/* The optimizer is able to deliver rows in group by order so
}
regRecord = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regRecord);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, sAggInfo.sortingIdx, regRecord);
+ sqlite3VdbeAddOp2(v, OP_SorterInsert, sAggInfo.sortingIdx, regRecord);
sqlite3ReleaseTempReg(pParse, regRecord);
sqlite3ReleaseTempRange(pParse, regBase, nCol);
sqlite3WhereEnd(pWInfo);
- sqlite3VdbeAddOp2(v, OP_Sort, sAggInfo.sortingIdx, addrEnd);
+ sAggInfo.sortingIdxPTab = sortPTab = pParse->nTab++;
+ sortOut = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp3(v, OP_OpenPseudo, sortPTab, sortOut, nCol);
+ sqlite3VdbeAddOp2(v, OP_SorterSort, sAggInfo.sortingIdx, addrEnd);
VdbeComment((v, "GROUP BY sort"));
sAggInfo.useSortingIdx = 1;
sqlite3ExprCacheClear(pParse);
*/
addrTopOfLoop = sqlite3VdbeCurrentAddr(v);
sqlite3ExprCacheClear(pParse);
+ if( groupBySort ){
+ sqlite3VdbeAddOp2(v, OP_SorterData, sAggInfo.sortingIdx, sortOut);
+ }
for(j=0; j<pGroupBy->nExpr; j++){
if( groupBySort ){
- sqlite3VdbeAddOp3(v, OP_Column, sAggInfo.sortingIdx, j, iBMem+j);
+ sqlite3VdbeAddOp3(v, OP_Column, sortPTab, j, iBMem+j);
+ if( j==0 ) sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE);
}else{
sAggInfo.directMode = 1;
sqlite3ExprCode(pParse, pGroupBy->a[j].pExpr, iBMem+j);
/* End of the loop
*/
if( groupBySort ){
- sqlite3VdbeAddOp2(v, OP_Next, sAggInfo.sortingIdx, addrTopOfLoop);
+ sqlite3VdbeAddOp2(v, OP_SorterNext, sAggInfo.sortingIdx, addrTopOfLoop);
}else{
sqlite3WhereEnd(pWInfo);
- sqlite3VdbeChangeToNoop(v, addrSortingIdx, 1);
+ sqlite3VdbeChangeToNoop(v, addrSortingIdx);
}
/* Output the final row of result
** and pKeyInfo to the KeyInfo structure required to navigate the
** index.
**
+ ** (2011-04-15) Do not do a full scan of an unordered index.
+ **
** In practice the KeyInfo structure will not be used. It is only
** passed to keep OP_OpenRead happy.
*/
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
- if( !pBest || pIdx->nColumn<pBest->nColumn ){
+ if( pIdx->bUnordered==0 && (!pBest || pIdx->nColumn<pBest->nColumn) ){
pBest = pIdx;
}
}
** of output.
*/
resetAccumulator(pParse, &sAggInfo);
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax, flag);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax, 0, flag);
if( pWInfo==0 ){
sqlite3ExprListDelete(db, pDel);
goto select_end;
** These routines are in a separate files so that they will not be linked
** if they are not used.
*/
+/* #include <stdlib.h> */
+/* #include <string.h> */
#ifndef SQLITE_OMIT_GET_TABLE
goto trigger_cleanup;
}
}
+ if( !pTableName || db->mallocFailed ){
+ goto trigger_cleanup;
+ }
+
+ /* A long-standing parser bug is that this syntax was allowed:
+ **
+ ** CREATE TRIGGER attached.demo AFTER INSERT ON attached.tab ....
+ ** ^^^^^^^^
+ **
+ ** To maintain backwards compatibility, ignore the database
+ ** name on pTableName if we are reparsing our of SQLITE_MASTER.
+ */
+ if( db->init.busy && iDb!=1 ){
+ sqlite3DbFree(db, pTableName->a[0].zDatabase);
+ pTableName->a[0].zDatabase = 0;
+ }
/* If the trigger name was unqualified, and the table is a temp table,
** then set iDb to 1 to create the trigger in the temporary database.
** If sqlite3SrcListLookup() returns 0, indicating the table does not
** exist, the error is caught by the block below.
*/
- if( !pTableName || db->mallocFailed ){
- goto trigger_cleanup;
- }
pTab = sqlite3SrcListLookup(pParse, pTableName);
if( db->init.busy==0 && pName2->n==0 && pTab
&& pTab->pSchema==db->aDb[1].pSchema ){
pTrig->table, z);
sqlite3DbFree(db, z);
sqlite3ChangeCookie(pParse, iDb);
- sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0, sqlite3MPrintf(
- db, "type='trigger' AND name='%q'", zName), P4_DYNAMIC
- );
+ sqlite3VdbeAddParseSchemaOp(v, iDb,
+ sqlite3MPrintf(db, "type='trigger' AND name='%q'", zName));
}
if( db->init.busy ){
ExprList *pChanges, /* The columns to change in the UPDATE statement */
Expr *pRowidExpr, /* Expression used to recompute the rowid */
int *aXRef, /* Mapping from columns of pTab to entries in pChanges */
- Expr *pWhere /* WHERE clause of the UPDATE statement */
+ Expr *pWhere, /* WHERE clause of the UPDATE statement */
+ int onError /* ON CONFLICT strategy */
);
#endif /* SQLITE_OMIT_VIRTUALTABLE */
}
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
int reg;
- if( chngRowid ){
+ if( hasFK || chngRowid ){
reg = ++pParse->nMem;
}else{
reg = 0;
/* Virtual tables must be handled separately */
if( IsVirtual(pTab) ){
updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef,
- pWhere);
+ pWhere, onError);
pWhere = 0;
pTabList = 0;
goto update_cleanup;
/* Begin the database scan
*/
sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid);
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere,0, WHERE_ONEPASS_DESIRED);
+ pWInfo = sqlite3WhereBegin(
+ pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED
+ );
if( pWInfo==0 ) goto update_cleanup;
okOnePass = pWInfo->okOnePass;
ExprList *pChanges, /* The columns to change in the UPDATE statement */
Expr *pRowid, /* Expression used to recompute the rowid */
int *aXRef, /* Mapping from columns of pTab to entries in pChanges */
- Expr *pWhere /* WHERE clause of the UPDATE statement */
+ Expr *pWhere, /* WHERE clause of the UPDATE statement */
+ int onError /* ON CONFLICT strategy */
){
Vdbe *v = pParse->pVdbe; /* Virtual machine under construction */
ExprList *pEList = 0; /* The result set of the SELECT statement */
}
sqlite3VtabMakeWritable(pParse, pTab);
sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab->nCol+2, iReg, pVTab, P4_VTAB);
+ sqlite3VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError);
sqlite3MayAbort(pParse);
sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1);
sqlite3VdbeJumpHere(v, addr);
#ifndef SQLITE_OMIT_VIRTUALTABLE
/*
+** Before a virtual table xCreate() or xConnect() method is invoked, the
+** sqlite3.pVtabCtx member variable is set to point to an instance of
+** this struct allocated on the stack. It is used by the implementation of
+** the sqlite3_declare_vtab() and sqlite3_vtab_config() APIs, both of which
+** are invoked only from within xCreate and xConnect methods.
+*/
+struct VtabCtx {
+ Table *pTab;
+ VTable *pVTable;
+};
+
+/*
** The actual function that does the work of creating a new module.
** This function implements the sqlite3_create_module() and
** sqlite3_create_module_v2() interfaces.
pMod->xDestroy = xDestroy;
pDel = (Module *)sqlite3HashInsert(&db->aModule, zCopy, nName, (void*)pMod);
if( pDel && pDel->xDestroy ){
+ sqlite3ResetInternalSchema(db, -1);
pDel->xDestroy(pDel->pAux);
}
sqlite3DbFree(db, pDel);
if( pDel==pMod ){
db->mallocFailed = 1;
}
- sqlite3ResetInternalSchema(db, -1);
}else if( xDestroy ){
xDestroy(pAux);
}
sqlite3VdbeAddOp2(v, OP_Expire, 0, 0);
zWhere = sqlite3MPrintf(db, "name='%q' AND type='table'", pTab->zName);
- sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0, zWhere, P4_DYNAMIC);
+ sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere);
sqlite3VdbeAddOp4(v, OP_VCreate, iDb, 0, 0,
pTab->zName, sqlite3Strlen30(pTab->zName) + 1);
}
int (*xConstruct)(sqlite3*,void*,int,const char*const*,sqlite3_vtab**,char**),
char **pzErr
){
+ VtabCtx sCtx;
VTable *pVTable;
int rc;
const char *const*azArg = (const char *const*)pTab->azModuleArg;
pVTable->db = db;
pVTable->pMod = pMod;
- assert( !db->pVTab );
- assert( xConstruct );
- db->pVTab = pTab;
-
/* Invoke the virtual table constructor */
+ assert( &db->pVtabCtx );
+ assert( xConstruct );
+ sCtx.pTab = pTab;
+ sCtx.pVTable = pVTable;
+ db->pVtabCtx = &sCtx;
rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr);
+ db->pVtabCtx = 0;
if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;
if( SQLITE_OK!=rc ){
** the sqlite3_vtab object if successful. */
pVTable->pVtab->pModule = pMod->pModule;
pVTable->nRef = 1;
- if( db->pVTab ){
+ if( sCtx.pTab ){
const char *zFormat = "vtable constructor did not declare schema: %s";
*pzErr = sqlite3MPrintf(db, zFormat, pTab->zName);
sqlite3VtabUnlock(pVTable);
}
sqlite3DbFree(db, zModuleName);
- db->pVTab = 0;
return rc;
}
return rc;
}
-
/*
-** Add the virtual table pVTab to the array sqlite3.aVTrans[].
+** Grow the db->aVTrans[] array so that there is room for at least one
+** more v-table. Return SQLITE_NOMEM if a malloc fails, or SQLITE_OK otherwise.
*/
-static int addToVTrans(sqlite3 *db, VTable *pVTab){
+static int growVTrans(sqlite3 *db){
const int ARRAY_INCR = 5;
/* Grow the sqlite3.aVTrans array if required */
db->aVTrans = aVTrans;
}
+ return SQLITE_OK;
+}
+
+/*
+** Add the virtual table pVTab to the array sqlite3.aVTrans[]. Space should
+** have already been reserved using growVTrans().
+*/
+static void addToVTrans(sqlite3 *db, VTable *pVTab){
/* Add pVtab to the end of sqlite3.aVTrans */
db->aVTrans[db->nVTrans++] = pVTab;
sqlite3VtabLock(pVTab);
- return SQLITE_OK;
}
/*
/* Justification of ALWAYS(): The xConstructor method is required to
** create a valid sqlite3_vtab if it returns SQLITE_OK. */
if( rc==SQLITE_OK && ALWAYS(sqlite3GetVTable(db, pTab)) ){
- rc = addToVTrans(db, sqlite3GetVTable(db, pTab));
+ rc = growVTrans(db);
+ if( rc==SQLITE_OK ){
+ addToVTrans(db, sqlite3GetVTable(db, pTab));
+ }
}
return rc;
char *zErr = 0;
sqlite3_mutex_enter(db->mutex);
- pTab = db->pVTab;
- if( !pTab ){
+ if( !db->pVtabCtx || !(pTab = db->pVtabCtx->pTab) ){
sqlite3Error(db, SQLITE_MISUSE, 0);
sqlite3_mutex_leave(db->mutex);
return SQLITE_MISUSE_BKPT;
pParse->pNewTable->nCol = 0;
pParse->pNewTable->aCol = 0;
}
- db->pVTab = 0;
+ db->pVtabCtx->pTab = 0;
}else{
sqlite3Error(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr);
sqlite3DbFree(db, zErr);
x = *(int (**)(sqlite3_vtab *))((char *)p->pModule + offset);
if( x ) x(p);
}
+ pVTab->iSavepoint = 0;
sqlite3VtabUnlock(pVTab);
}
sqlite3DbFree(db, db->aVTrans);
if( pModule->xBegin ){
int i;
-
/* If pVtab is already in the aVTrans array, return early */
for(i=0; i<db->nVTrans; i++){
if( db->aVTrans[i]==pVTab ){
}
}
- /* Invoke the xBegin method */
- rc = pModule->xBegin(pVTab->pVtab);
+ /* Invoke the xBegin method. If successful, add the vtab to the
+ ** sqlite3.aVTrans[] array. */
+ rc = growVTrans(db);
if( rc==SQLITE_OK ){
- rc = addToVTrans(db, pVTab);
+ rc = pModule->xBegin(pVTab->pVtab);
+ if( rc==SQLITE_OK ){
+ addToVTrans(db, pVTab);
+ }
+ }
+ }
+ return rc;
+}
+
+/*
+** Invoke either the xSavepoint, xRollbackTo or xRelease method of all
+** virtual tables that currently have an open transaction. Pass iSavepoint
+** as the second argument to the virtual table method invoked.
+**
+** If op is SAVEPOINT_BEGIN, the xSavepoint method is invoked. If it is
+** SAVEPOINT_ROLLBACK, the xRollbackTo method. Otherwise, if op is
+** SAVEPOINT_RELEASE, then the xRelease method of each virtual table with
+** an open transaction is invoked.
+**
+** If any virtual table method returns an error code other than SQLITE_OK,
+** processing is abandoned and the error returned to the caller of this
+** function immediately. If all calls to virtual table methods are successful,
+** SQLITE_OK is returned.
+*/
+SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){
+ int rc = SQLITE_OK;
+
+ assert( op==SAVEPOINT_RELEASE||op==SAVEPOINT_ROLLBACK||op==SAVEPOINT_BEGIN );
+ assert( iSavepoint>=0 );
+ if( db->aVTrans ){
+ int i;
+ for(i=0; rc==SQLITE_OK && i<db->nVTrans; i++){
+ VTable *pVTab = db->aVTrans[i];
+ const sqlite3_module *pMod = pVTab->pMod->pModule;
+ if( pMod->iVersion>=2 ){
+ int (*xMethod)(sqlite3_vtab *, int);
+ switch( op ){
+ case SAVEPOINT_BEGIN:
+ xMethod = pMod->xSavepoint;
+ pVTab->iSavepoint = iSavepoint+1;
+ break;
+ case SAVEPOINT_ROLLBACK:
+ xMethod = pMod->xRollbackTo;
+ break;
+ default:
+ xMethod = pMod->xRelease;
+ break;
+ }
+ if( xMethod && pVTab->iSavepoint>iSavepoint ){
+ rc = xMethod(db->aVTrans[i]->pVtab, iSavepoint);
+ }
+ }
}
}
return rc;
}
}
+/*
+** Return the ON CONFLICT resolution mode in effect for the virtual
+** table update operation currently in progress.
+**
+** The results of this routine are undefined unless it is called from
+** within an xUpdate method.
+*/
+SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *db){
+ static const unsigned char aMap[] = {
+ SQLITE_ROLLBACK, SQLITE_ABORT, SQLITE_FAIL, SQLITE_IGNORE, SQLITE_REPLACE
+ };
+ assert( OE_Rollback==1 && OE_Abort==2 && OE_Fail==3 );
+ assert( OE_Ignore==4 && OE_Replace==5 );
+ assert( db->vtabOnConflict>=1 && db->vtabOnConflict<=5 );
+ return (int)aMap[db->vtabOnConflict-1];
+}
+
+/*
+** Call from within the xCreate() or xConnect() methods to provide
+** the SQLite core with additional information about the behavior
+** of the virtual table being implemented.
+*/
+SQLITE_API int sqlite3_vtab_config(sqlite3 *db, int op, ...){
+ va_list ap;
+ int rc = SQLITE_OK;
+
+ sqlite3_mutex_enter(db->mutex);
+
+ va_start(ap, op);
+ switch( op ){
+ case SQLITE_VTAB_CONSTRAINT_SUPPORT: {
+ VtabCtx *p = db->pVtabCtx;
+ if( !p ){
+ rc = SQLITE_MISUSE_BKPT;
+ }else{
+ assert( p->pTab==0 || (p->pTab->tabFlags & TF_Virtual)!=0 );
+ p->pVTable->bConstraint = (u8)va_arg(ap, int);
+ }
+ break;
+ }
+ default:
+ rc = SQLITE_MISUSE_BKPT;
+ break;
+ }
+ va_end(ap);
+
+ if( rc!=SQLITE_OK ) sqlite3Error(db, rc, 0);
+ sqlite3_mutex_leave(db->mutex);
+ return rc;
+}
+
#endif /* SQLITE_OMIT_VIRTUALTABLE */
/************** End of vtab.c ************************************************/
#define WHERE_VIRTUALTABLE 0x08000000 /* Use virtual-table processing */
#define WHERE_MULTI_OR 0x10000000 /* OR using multiple indices */
#define WHERE_TEMP_INDEX 0x20000000 /* Uses an ephemeral index */
+#define WHERE_DISTINCT 0x40000000 /* Correct order for DISTINCT */
/*
** Initialize a preallocated WhereClause structure.
static Bitmask exprSelectTableUsage(WhereMaskSet *pMaskSet, Select *pS){
Bitmask mask = 0;
while( pS ){
+ SrcList *pSrc = pS->pSrc;
mask |= exprListTableUsage(pMaskSet, pS->pEList);
mask |= exprListTableUsage(pMaskSet, pS->pGroupBy);
mask |= exprListTableUsage(pMaskSet, pS->pOrderBy);
mask |= exprTableUsage(pMaskSet, pS->pWhere);
mask |= exprTableUsage(pMaskSet, pS->pHaving);
+ if( ALWAYS(pSrc!=0) ){
+ int i;
+ for(i=0; i<pSrc->nSrc; i++){
+ mask |= exprSelectTableUsage(pMaskSet, pSrc->a[i].pSelect);
+ mask |= exprTableUsage(pMaskSet, pSrc->a[i].pOn);
+ }
+ }
pS = pS->pPrior;
}
return mask;
return 0;
}
+/*
+** This function searches the expression list passed as the second argument
+** for an expression of type TK_COLUMN that refers to the same column and
+** uses the same collation sequence as the iCol'th column of index pIdx.
+** Argument iBase is the cursor number used for the table that pIdx refers
+** to.
+**
+** If such an expression is found, its index in pList->a[] is returned. If
+** no expression is found, -1 is returned.
+*/
+static int findIndexCol(
+ Parse *pParse, /* Parse context */
+ ExprList *pList, /* Expression list to search */
+ int iBase, /* Cursor for table associated with pIdx */
+ Index *pIdx, /* Index to match column of */
+ int iCol /* Column of index to match */
+){
+ int i;
+ const char *zColl = pIdx->azColl[iCol];
+
+ for(i=0; i<pList->nExpr; i++){
+ Expr *p = pList->a[i].pExpr;
+ if( p->op==TK_COLUMN
+ && p->iColumn==pIdx->aiColumn[iCol]
+ && p->iTable==iBase
+ ){
+ CollSeq *pColl = sqlite3ExprCollSeq(pParse, p);
+ if( ALWAYS(pColl) && 0==sqlite3StrICmp(pColl->zName, zColl) ){
+ return i;
+ }
+ }
+ }
+
+ return -1;
+}
+
+/*
+** This routine determines if pIdx can be used to assist in processing a
+** DISTINCT qualifier. In other words, it tests whether or not using this
+** index for the outer loop guarantees that rows with equal values for
+** all expressions in the pDistinct list are delivered grouped together.
+**
+** For example, the query
+**
+** SELECT DISTINCT a, b, c FROM tbl WHERE a = ?
+**
+** can benefit from any index on columns "b" and "c".
+*/
+static int isDistinctIndex(
+ Parse *pParse, /* Parsing context */
+ WhereClause *pWC, /* The WHERE clause */
+ Index *pIdx, /* The index being considered */
+ int base, /* Cursor number for the table pIdx is on */
+ ExprList *pDistinct, /* The DISTINCT expressions */
+ int nEqCol /* Number of index columns with == */
+){
+ Bitmask mask = 0; /* Mask of unaccounted for pDistinct exprs */
+ int i; /* Iterator variable */
+
+ if( pIdx->zName==0 || pDistinct==0 || pDistinct->nExpr>=BMS ) return 0;
+ testcase( pDistinct->nExpr==BMS-1 );
+
+ /* Loop through all the expressions in the distinct list. If any of them
+ ** are not simple column references, return early. Otherwise, test if the
+ ** WHERE clause contains a "col=X" clause. If it does, the expression
+ ** can be ignored. If it does not, and the column does not belong to the
+ ** same table as index pIdx, return early. Finally, if there is no
+ ** matching "col=X" expression and the column is on the same table as pIdx,
+ ** set the corresponding bit in variable mask.
+ */
+ for(i=0; i<pDistinct->nExpr; i++){
+ WhereTerm *pTerm;
+ Expr *p = pDistinct->a[i].pExpr;
+ if( p->op!=TK_COLUMN ) return 0;
+ pTerm = findTerm(pWC, p->iTable, p->iColumn, ~(Bitmask)0, WO_EQ, 0);
+ if( pTerm ){
+ Expr *pX = pTerm->pExpr;
+ CollSeq *p1 = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight);
+ CollSeq *p2 = sqlite3ExprCollSeq(pParse, p);
+ if( p1==p2 ) continue;
+ }
+ if( p->iTable!=base ) return 0;
+ mask |= (((Bitmask)1) << i);
+ }
+
+ for(i=nEqCol; mask && i<pIdx->nColumn; i++){
+ int iExpr = findIndexCol(pParse, pDistinct, base, pIdx, i);
+ if( iExpr<0 ) break;
+ mask &= ~(((Bitmask)1) << iExpr);
+ }
+
+ return (mask==0);
+}
+
+
+/*
+** Return true if the DISTINCT expression-list passed as the third argument
+** is redundant. A DISTINCT list is redundant if the database contains a
+** UNIQUE index that guarantees that the result of the query will be distinct
+** anyway.
+*/
+static int isDistinctRedundant(
+ Parse *pParse,
+ SrcList *pTabList,
+ WhereClause *pWC,
+ ExprList *pDistinct
+){
+ Table *pTab;
+ Index *pIdx;
+ int i;
+ int iBase;
+
+ /* If there is more than one table or sub-select in the FROM clause of
+ ** this query, then it will not be possible to show that the DISTINCT
+ ** clause is redundant. */
+ if( pTabList->nSrc!=1 ) return 0;
+ iBase = pTabList->a[0].iCursor;
+ pTab = pTabList->a[0].pTab;
+
+ /* If any of the expressions is an IPK column on table iBase, then return
+ ** true. Note: The (p->iTable==iBase) part of this test may be false if the
+ ** current SELECT is a correlated sub-query.
+ */
+ for(i=0; i<pDistinct->nExpr; i++){
+ Expr *p = pDistinct->a[i].pExpr;
+ if( p->op==TK_COLUMN && p->iTable==iBase && p->iColumn<0 ) return 1;
+ }
+
+ /* Loop through all indices on the table, checking each to see if it makes
+ ** the DISTINCT qualifier redundant. It does so if:
+ **
+ ** 1. The index is itself UNIQUE, and
+ **
+ ** 2. All of the columns in the index are either part of the pDistinct
+ ** list, or else the WHERE clause contains a term of the form "col=X",
+ ** where X is a constant value. The collation sequences of the
+ ** comparison and select-list expressions must match those of the index.
+ */
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ if( pIdx->onError==OE_None ) continue;
+ for(i=0; i<pIdx->nColumn; i++){
+ int iCol = pIdx->aiColumn[i];
+ if( 0==findTerm(pWC, iBase, iCol, ~(Bitmask)0, WO_EQ, pIdx)
+ && 0>findIndexCol(pParse, pDistinct, iBase, pIdx, i)
+ ){
+ break;
+ }
+ }
+ if( i==pIdx->nColumn ){
+ /* This index implies that the DISTINCT qualifier is redundant. */
+ return 1;
+ }
+ }
+
+ return 0;
+}
/*
** This routine decides if pIdx can be used to satisfy the ORDER BY
struct ExprList_item *pTerm; /* A term of the ORDER BY clause */
sqlite3 *db = pParse->db;
- assert( pOrderBy!=0 );
+ if( !pOrderBy ) return 0;
+ if( wsFlags & WHERE_COLUMN_IN ) return 0;
+ if( pIdx->bUnordered ) return 0;
+
nTerm = pOrderBy->nExpr;
assert( nTerm>0 );
WhereTerm *pWCEnd; /* End of pWC->a[] */
Table *pTable; /* Table tht might be indexed */
+ if( pParse->nQueryLoop<=(double)1 ){
+ /* There is no point in building an automatic index for a single scan */
+ return;
+ }
if( (pParse->db->flags & SQLITE_AutoIndex)==0 ){
/* Automatic indices are disabled at run-time */
return;
/* The NOT INDEXED clause appears in the SQL. */
return;
}
+ if( pSrc->isCorrelated ){
+ /* The source is a correlated sub-query. No point in indexing it. */
+ return;
+ }
assert( pParse->nQueryLoop >= (double)1 );
pTable = pSrc->pTab;
v = pParse->pVdbe;
assert( v!=0 );
regIsInit = ++pParse->nMem;
- addrInit = sqlite3VdbeAddOp1(v, OP_If, regIsInit);
- sqlite3VdbeAddOp2(v, OP_Integer, 1, regIsInit);
+ addrInit = sqlite3VdbeAddOp1(v, OP_Once, regIsInit);
/* Count the number of columns that will be added to the index
** and used to match WHERE clause constraints */
testcase( pTerm->eOperator==WO_IN );
testcase( pTerm->eOperator==WO_ISNULL );
if( pTerm->eOperator & (WO_IN|WO_ISNULL) ) continue;
+ if( pTerm->wtFlags & TERM_VNULL ) continue;
nTerm++;
}
testcase( pTerm->eOperator==WO_IN );
testcase( pTerm->eOperator==WO_ISNULL );
if( pTerm->eOperator & (WO_IN|WO_ISNULL) ) continue;
+ if( pTerm->wtFlags & TERM_VNULL ) continue;
pIdxCons[j].iColumn = pTerm->u.leftColumn;
pIdxCons[j].iTermOffset = i;
pIdxCons[j].op = (u8)pTerm->eOperator;
Bitmask notReady, /* Mask of cursors not available for indexing */
Bitmask notValid, /* Cursors not available for any purpose */
ExprList *pOrderBy, /* The ORDER BY clause */
+ ExprList *pDistinct, /* The select-list if query is DISTINCT */
WhereCost *pCost /* Lowest cost query plan */
){
int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */
int nInMul = 1; /* Number of distinct equalities to lookup */
int estBound = 100; /* Estimated reduction in search space */
int nBound = 0; /* Number of range constraints seen */
- int bSort = 0; /* True if external sort required */
+ int bSort = !!pOrderBy; /* True if external sort required */
+ int bDist = !!pDistinct; /* True if index cannot help with DISTINCT */
int bLookup = 0; /* True if not a covering index */
WhereTerm *pTerm; /* A single term of the WHERE clause */
#ifdef SQLITE_ENABLE_STAT2
** naturally scan rows in the required order, set the appropriate flags
** in wsFlags. Otherwise, if there is an ORDER BY clause but the index
** will scan rows in a different order, set the bSort variable. */
- if( pOrderBy ){
- if( (wsFlags & WHERE_COLUMN_IN)==0
- && pProbe->bUnordered==0
- && isSortingIndex(pParse, pWC->pMaskSet, pProbe, iCur, pOrderBy,
- nEq, wsFlags, &rev)
- ){
- wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_ORDERBY;
- wsFlags |= (rev ? WHERE_REVERSE : 0);
- }else{
- bSort = 1;
- }
+ if( isSortingIndex(
+ pParse, pWC->pMaskSet, pProbe, iCur, pOrderBy, nEq, wsFlags, &rev)
+ ){
+ bSort = 0;
+ wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_ORDERBY;
+ wsFlags |= (rev ? WHERE_REVERSE : 0);
+ }
+
+ /* If there is a DISTINCT qualifier and this index will scan rows in
+ ** order of the DISTINCT expressions, clear bDist and set the appropriate
+ ** flags in wsFlags. */
+ if( isDistinctIndex(pParse, pWC, pProbe, iCur, pDistinct, nEq) ){
+ bDist = 0;
+ wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_DISTINCT;
}
/* If currently calculating the cost of using an index (not the IPK
}
#ifdef SQLITE_ENABLE_STAT2
- /* If the constraint is of the form x=VALUE and histogram
+ /* If the constraint is of the form x=VALUE or x IN (E1,E2,...)
+ ** and we do not think that values of x are unique and if histogram
** data is available for column x, then it might be possible
** to get a better estimate on the number of rows based on
** VALUE and how common that value is according to the histogram.
*/
- if( nRow>(double)1 && nEq==1 && pFirstTerm!=0 ){
+ if( nRow>(double)1 && nEq==1 && pFirstTerm!=0 && aiRowEst[1]>1 ){
if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){
testcase( pFirstTerm->eOperator==WO_EQ );
testcase( pFirstTerm->eOperator==WO_ISNULL );
if( bSort ){
cost += nRow*estLog(nRow)*3;
}
+ if( bDist ){
+ cost += nRow*estLog(nRow)*3;
+ }
/**** Cost of using this index has now been computed ****/
}else
#endif
{
- bestBtreeIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost);
+ bestBtreeIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, 0, pCost);
}
}
if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){
WhereInfo *pSubWInfo; /* Info for single OR-term scan */
/* Loop through table entries that match term pOrTerm. */
- pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrTerm->pExpr, 0,
+ pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrTerm->pExpr, 0, 0,
WHERE_OMIT_OPEN | WHERE_OMIT_CLOSE |
WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY);
if( pSubWInfo ){
SrcList *pTabList, /* A list of all tables to be scanned */
Expr *pWhere, /* The WHERE clause */
ExprList **ppOrderBy, /* An ORDER BY clause, or NULL */
+ ExprList *pDistinct, /* The select-list for DISTINCT queries - or NULL */
u16 wctrlFlags /* One of the WHERE_* flags defined in sqliteInt.h */
){
int i; /* Loop counter */
pWInfo->savedNQueryLoop = pParse->nQueryLoop;
pMaskSet = (WhereMaskSet*)&pWC[1];
+ /* Disable the DISTINCT optimization if SQLITE_DistinctOpt is set via
+ ** sqlite3_test_ctrl(SQLITE_TESTCTRL_OPTIMIZATIONS,...) */
+ if( db->flags & SQLITE_DistinctOpt ) pDistinct = 0;
+
/* Split the WHERE clause into separate subexpressions where each
** subexpression is separated by an AND operator.
*/
goto whereBeginError;
}
+ /* Check if the DISTINCT qualifier, if there is one, is redundant.
+ ** If it is, then set pDistinct to NULL and WhereInfo.eDistinct to
+ ** WHERE_DISTINCT_UNIQUE to tell the caller to ignore the DISTINCT.
+ */
+ if( pDistinct && isDistinctRedundant(pParse, pTabList, pWC, pDistinct) ){
+ pDistinct = 0;
+ pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
+ }
+
/* Chose the best index to use for each table in the FROM clause.
**
** This loop fills in the following fields:
int doNotReorder; /* True if this table should not be reordered */
WhereCost sCost; /* Cost information from best[Virtual]Index() */
ExprList *pOrderBy; /* ORDER BY clause for index to optimize */
+ ExprList *pDist; /* DISTINCT clause for index to optimize */
doNotReorder = (pTabItem->jointype & (JT_LEFT|JT_CROSS))!=0;
if( j!=iFrom && doNotReorder ) break;
}
mask = (isOptimal ? m : notReady);
pOrderBy = ((i==0 && ppOrderBy )?*ppOrderBy:0);
+ pDist = (i==0 ? pDistinct : 0);
if( pTabItem->pIndex==0 ) nUnconstrained++;
WHERETRACE(("=== trying table %d with isOptimal=%d ===\n",
#endif
{
bestBtreeIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy,
- &sCost);
+ pDist, &sCost);
}
assert( isOptimal || (sCost.used¬Ready)==0 );
if( (bestPlan.plan.wsFlags & WHERE_ORDERBY)!=0 ){
*ppOrderBy = 0;
}
+ if( (bestPlan.plan.wsFlags & WHERE_DISTINCT)!=0 ){
+ assert( pWInfo->eDistinct==0 );
+ pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
+ }
andFlags &= bestPlan.plan.wsFlags;
pLevel->plan = bestPlan.plan;
testcase( bestPlan.plan.wsFlags & WHERE_INDEXED );
*/
/* First off, code is included that follows the "include" declaration
** in the input grammar file. */
+/* #include <stdio.h> */
/*
typedef struct yyParser yyParser;
#ifndef NDEBUG
+/* #include <stdio.h> */
static FILE *yyTraceFILE = 0;
static char *yyTracePrompt = 0;
#endif /* NDEBUG */
** individual tokens and sends those tokens one-by-one over to the
** parser for analysis.
*/
+/* #include <stdlib.h> */
/*
** The charMap() macro maps alphabetic characters into their
testcase( z[0]=='x' ); testcase( z[0]=='X' );
if( z[1]=='\'' ){
*tokenType = TK_BLOB;
- for(i=2; (c=z[i])!=0 && c!='\''; i++){
- if( !sqlite3Isxdigit(c) ){
- *tokenType = TK_ILLEGAL;
- }
+ for(i=2; sqlite3Isxdigit(z[i]); i++){}
+ if( z[i]!='\'' || i%2 ){
+ *tokenType = TK_ILLEGAL;
+ while( z[i] && z[i]!='\'' ){ i++; }
}
- if( i%2 || !c ) *tokenType = TK_ILLEGAL;
- if( c ) i++;
+ if( z[i] ) i++;
return i;
}
/* Otherwise fall through to the next case */
assert( pParse->pNewTable==0 );
assert( pParse->pNewTrigger==0 );
assert( pParse->nVar==0 );
- assert( pParse->nVarExpr==0 );
- assert( pParse->nVarExprAlloc==0 );
- assert( pParse->apVarExpr==0 );
+ assert( pParse->nzVar==0 );
+ assert( pParse->azVar==0 );
enableLookaside = db->lookaside.bEnabled;
if( db->lookaside.pStart ) db->lookaside.bEnabled = 1;
while( !db->mallocFailed && zSql[i]!=0 ){
}
sqlite3DeleteTrigger(db, pParse->pNewTrigger);
- sqlite3DbFree(db, pParse->apVarExpr);
+ for(i=pParse->nzVar-1; i>=0; i--) sqlite3DbFree(db, pParse->azVar[i]);
+ sqlite3DbFree(db, pParse->azVar);
sqlite3DbFree(db, pParse->aAlias);
while( pParse->pAinc ){
AutoincInfo *p = pParse->pAinc;
#endif
#endif
+ /* Do extra initialization steps requested by the SQLITE_EXTRA_INIT
+ ** compile-time option.
+ */
+#ifdef SQLITE_EXTRA_INIT
+ if( rc==SQLITE_OK && sqlite3GlobalConfig.isInit ){
+ int SQLITE_EXTRA_INIT(void);
+ rc = SQLITE_EXTRA_INIT();
+ }
+#endif
+
return rc;
}
break;
}
+ case SQLITE_CONFIG_URI: {
+ sqlite3GlobalConfig.bOpenUri = va_arg(ap, int);
+ break;
+ }
+
default: {
rc = SQLITE_ERROR;
break;
}
/*
+** This function is used to parse both URIs and non-URI filenames passed by the
+** user to API functions sqlite3_open() or sqlite3_open_v2(), and for database
+** URIs specified as part of ATTACH statements.
+**
+** The first argument to this function is the name of the VFS to use (or
+** a NULL to signify the default VFS) if the URI does not contain a "vfs=xxx"
+** query parameter. The second argument contains the URI (or non-URI filename)
+** itself. When this function is called the *pFlags variable should contain
+** the default flags to open the database handle with. The value stored in
+** *pFlags may be updated before returning if the URI filename contains
+** "cache=xxx" or "mode=xxx" query parameters.
+**
+** If successful, SQLITE_OK is returned. In this case *ppVfs is set to point to
+** the VFS that should be used to open the database file. *pzFile is set to
+** point to a buffer containing the name of the file to open. It is the
+** responsibility of the caller to eventually call sqlite3_free() to release
+** this buffer.
+**
+** If an error occurs, then an SQLite error code is returned and *pzErrMsg
+** may be set to point to a buffer containing an English language error
+** message. It is the responsibility of the caller to eventually release
+** this buffer by calling sqlite3_free().
+*/
+SQLITE_PRIVATE int sqlite3ParseUri(
+ const char *zDefaultVfs, /* VFS to use if no "vfs=xxx" query option */
+ const char *zUri, /* Nul-terminated URI to parse */
+ unsigned int *pFlags, /* IN/OUT: SQLITE_OPEN_XXX flags */
+ sqlite3_vfs **ppVfs, /* OUT: VFS to use */
+ char **pzFile, /* OUT: Filename component of URI */
+ char **pzErrMsg /* OUT: Error message (if rc!=SQLITE_OK) */
+){
+ int rc = SQLITE_OK;
+ unsigned int flags = *pFlags;
+ const char *zVfs = zDefaultVfs;
+ char *zFile;
+ char c;
+ int nUri = sqlite3Strlen30(zUri);
+
+ assert( *pzErrMsg==0 );
+
+ if( ((flags & SQLITE_OPEN_URI) || sqlite3GlobalConfig.bOpenUri)
+ && nUri>=5 && memcmp(zUri, "file:", 5)==0
+ ){
+ char *zOpt;
+ int eState; /* Parser state when parsing URI */
+ int iIn; /* Input character index */
+ int iOut = 0; /* Output character index */
+ int nByte = nUri+2; /* Bytes of space to allocate */
+
+ /* Make sure the SQLITE_OPEN_URI flag is set to indicate to the VFS xOpen
+ ** method that there may be extra parameters following the file-name. */
+ flags |= SQLITE_OPEN_URI;
+
+ for(iIn=0; iIn<nUri; iIn++) nByte += (zUri[iIn]=='&');
+ zFile = sqlite3_malloc(nByte);
+ if( !zFile ) return SQLITE_NOMEM;
+
+ /* Discard the scheme and authority segments of the URI. */
+ if( zUri[5]=='/' && zUri[6]=='/' ){
+ iIn = 7;
+ while( zUri[iIn] && zUri[iIn]!='/' ) iIn++;
+
+ if( iIn!=7 && (iIn!=16 || memcmp("localhost", &zUri[7], 9)) ){
+ *pzErrMsg = sqlite3_mprintf("invalid uri authority: %.*s",
+ iIn-7, &zUri[7]);
+ rc = SQLITE_ERROR;
+ goto parse_uri_out;
+ }
+ }else{
+ iIn = 5;
+ }
+
+ /* Copy the filename and any query parameters into the zFile buffer.
+ ** Decode %HH escape codes along the way.
+ **
+ ** Within this loop, variable eState may be set to 0, 1 or 2, depending
+ ** on the parsing context. As follows:
+ **
+ ** 0: Parsing file-name.
+ ** 1: Parsing name section of a name=value query parameter.
+ ** 2: Parsing value section of a name=value query parameter.
+ */
+ eState = 0;
+ while( (c = zUri[iIn])!=0 && c!='#' ){
+ iIn++;
+ if( c=='%'
+ && sqlite3Isxdigit(zUri[iIn])
+ && sqlite3Isxdigit(zUri[iIn+1])
+ ){
+ int octet = (sqlite3HexToInt(zUri[iIn++]) << 4);
+ octet += sqlite3HexToInt(zUri[iIn++]);
+
+ assert( octet>=0 && octet<256 );
+ if( octet==0 ){
+ /* This branch is taken when "%00" appears within the URI. In this
+ ** case we ignore all text in the remainder of the path, name or
+ ** value currently being parsed. So ignore the current character
+ ** and skip to the next "?", "=" or "&", as appropriate. */
+ while( (c = zUri[iIn])!=0 && c!='#'
+ && (eState!=0 || c!='?')
+ && (eState!=1 || (c!='=' && c!='&'))
+ && (eState!=2 || c!='&')
+ ){
+ iIn++;
+ }
+ continue;
+ }
+ c = octet;
+ }else if( eState==1 && (c=='&' || c=='=') ){
+ if( zFile[iOut-1]==0 ){
+ /* An empty option name. Ignore this option altogether. */
+ while( zUri[iIn] && zUri[iIn]!='#' && zUri[iIn-1]!='&' ) iIn++;
+ continue;
+ }
+ if( c=='&' ){
+ zFile[iOut++] = '\0';
+ }else{
+ eState = 2;
+ }
+ c = 0;
+ }else if( (eState==0 && c=='?') || (eState==2 && c=='&') ){
+ c = 0;
+ eState = 1;
+ }
+ zFile[iOut++] = c;
+ }
+ if( eState==1 ) zFile[iOut++] = '\0';
+ zFile[iOut++] = '\0';
+ zFile[iOut++] = '\0';
+
+ /* Check if there were any options specified that should be interpreted
+ ** here. Options that are interpreted here include "vfs" and those that
+ ** correspond to flags that may be passed to the sqlite3_open_v2()
+ ** method. */
+ zOpt = &zFile[sqlite3Strlen30(zFile)+1];
+ while( zOpt[0] ){
+ int nOpt = sqlite3Strlen30(zOpt);
+ char *zVal = &zOpt[nOpt+1];
+ int nVal = sqlite3Strlen30(zVal);
+
+ if( nOpt==3 && memcmp("vfs", zOpt, 3)==0 ){
+ zVfs = zVal;
+ }else{
+ struct OpenMode {
+ const char *z;
+ int mode;
+ } *aMode = 0;
+ char *zModeType = 0;
+ int mask = 0;
+ int limit = 0;
+
+ if( nOpt==5 && memcmp("cache", zOpt, 5)==0 ){
+ static struct OpenMode aCacheMode[] = {
+ { "shared", SQLITE_OPEN_SHAREDCACHE },
+ { "private", SQLITE_OPEN_PRIVATECACHE },
+ { 0, 0 }
+ };
+
+ mask = SQLITE_OPEN_SHAREDCACHE|SQLITE_OPEN_PRIVATECACHE;
+ aMode = aCacheMode;
+ limit = mask;
+ zModeType = "cache";
+ }
+ if( nOpt==4 && memcmp("mode", zOpt, 4)==0 ){
+ static struct OpenMode aOpenMode[] = {
+ { "ro", SQLITE_OPEN_READONLY },
+ { "rw", SQLITE_OPEN_READWRITE },
+ { "rwc", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE },
+ { 0, 0 }
+ };
+
+ mask = SQLITE_OPEN_READONLY|SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
+ aMode = aOpenMode;
+ limit = mask & flags;
+ zModeType = "access";
+ }
+
+ if( aMode ){
+ int i;
+ int mode = 0;
+ for(i=0; aMode[i].z; i++){
+ const char *z = aMode[i].z;
+ if( nVal==sqlite3Strlen30(z) && 0==memcmp(zVal, z, nVal) ){
+ mode = aMode[i].mode;
+ break;
+ }
+ }
+ if( mode==0 ){
+ *pzErrMsg = sqlite3_mprintf("no such %s mode: %s", zModeType, zVal);
+ rc = SQLITE_ERROR;
+ goto parse_uri_out;
+ }
+ if( mode>limit ){
+ *pzErrMsg = sqlite3_mprintf("%s mode not allowed: %s",
+ zModeType, zVal);
+ rc = SQLITE_PERM;
+ goto parse_uri_out;
+ }
+ flags = (flags & ~mask) | mode;
+ }
+ }
+
+ zOpt = &zVal[nVal+1];
+ }
+
+ }else{
+ zFile = sqlite3_malloc(nUri+2);
+ if( !zFile ) return SQLITE_NOMEM;
+ memcpy(zFile, zUri, nUri);
+ zFile[nUri] = '\0';
+ zFile[nUri+1] = '\0';
+ }
+
+ *ppVfs = sqlite3_vfs_find(zVfs);
+ if( *ppVfs==0 ){
+ *pzErrMsg = sqlite3_mprintf("no such vfs: %s", zVfs);
+ rc = SQLITE_ERROR;
+ }
+ parse_uri_out:
+ if( rc!=SQLITE_OK ){
+ sqlite3_free(zFile);
+ zFile = 0;
+ }
+ *pFlags = flags;
+ *pzFile = zFile;
+ return rc;
+}
+
+
+/*
** This routine does the work of opening a database on behalf of
** sqlite3_open() and sqlite3_open16(). The database filename "zFilename"
** is UTF-8 encoded.
static int openDatabase(
const char *zFilename, /* Database filename UTF-8 encoded */
sqlite3 **ppDb, /* OUT: Returned database handle */
- unsigned flags, /* Operational flags */
+ unsigned int flags, /* Operational flags */
const char *zVfs /* Name of the VFS to use */
){
- sqlite3 *db;
- int rc;
- int isThreadsafe;
+ sqlite3 *db; /* Store allocated handle here */
+ int rc; /* Return code */
+ int isThreadsafe; /* True for threadsafe connections */
+ char *zOpen = 0; /* Filename argument to pass to BtreeOpen() */
+ char *zErrMsg = 0; /* Error message from sqlite3ParseUri() */
*ppDb = 0;
#ifndef SQLITE_OMIT_AUTOINIT
testcase( (1<<(flags&7))==0x02 ); /* READONLY */
testcase( (1<<(flags&7))==0x04 ); /* READWRITE */
testcase( (1<<(flags&7))==0x40 ); /* READWRITE | CREATE */
- if( ((1<<(flags&7)) & 0x46)==0 ) return SQLITE_MISUSE;
+ if( ((1<<(flags&7)) & 0x46)==0 ) return SQLITE_MISUSE_BKPT;
if( sqlite3GlobalConfig.bCoreMutex==0 ){
isThreadsafe = 0;
sqlite3HashInit(&db->aModule);
#endif
- db->pVfs = sqlite3_vfs_find(zVfs);
- if( !db->pVfs ){
- rc = SQLITE_ERROR;
- sqlite3Error(db, rc, "no such vfs: %s", zVfs);
- goto opendb_out;
- }
-
/* Add the default collation sequence BINARY. BINARY works for both UTF-8
** and UTF-16, so add a version for each to avoid any unnecessary
** conversions. The only error that can occur here is a malloc() failure.
createCollation(db, "NOCASE", SQLITE_UTF8, SQLITE_COLL_NOCASE, 0,
nocaseCollatingFunc, 0);
- /* Open the backend database driver */
+ /* Parse the filename/URI argument. */
db->openFlags = flags;
- rc = sqlite3BtreeOpen(zFilename, db, &db->aDb[0].pBt, 0,
+ rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg);
+ if( rc!=SQLITE_OK ){
+ if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;
+ sqlite3Error(db, rc, zErrMsg ? "%s" : 0, zErrMsg);
+ sqlite3_free(zErrMsg);
+ goto opendb_out;
+ }
+
+ /* Open the backend database driver */
+ rc = sqlite3BtreeOpen(db->pVfs, zOpen, db, &db->aDb[0].pBt, 0,
flags | SQLITE_OPEN_MAIN_DB);
if( rc!=SQLITE_OK ){
if( rc==SQLITE_IOERR_NOMEM ){
sqlite3_wal_autocheckpoint(db, SQLITE_DEFAULT_WAL_AUTOCHECKPOINT);
opendb_out:
+ sqlite3_free(zOpen);
if( db ){
assert( db->mutex!=0 || isThreadsafe==0 || sqlite3GlobalConfig.bFullMutex==0 );
sqlite3_mutex_leave(db->mutex);
int flags, /* Flags */
const char *zVfs /* Name of VFS module to use */
){
- return openDatabase(filename, ppDb, flags, zVfs);
+ return openDatabase(filename, ppDb, (unsigned int)flags, zVfs);
}
#ifndef SQLITE_OMIT_UTF16
break;
}
+ /* sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, int onoff);
+ **
+ ** If parameter onoff is non-zero, configure the wrappers so that all
+ ** subsequent calls to localtime() and variants fail. If onoff is zero,
+ ** undo this setting.
+ */
+ case SQLITE_TESTCTRL_LOCALTIME_FAULT: {
+ sqlite3GlobalConfig.bLocaltimeFault = va_arg(ap, int);
+ break;
+ }
+
}
va_end(ap);
#endif /* SQLITE_OMIT_BUILTIN_TEST */
return rc;
}
+/*
+** This is a utility routine, useful to VFS implementations, that checks
+** to see if a database file was a URI that contained a specific query
+** parameter, and if so obtains the value of the query parameter.
+**
+** The zFilename argument is the filename pointer passed into the xOpen()
+** method of a VFS implementation. The zParam argument is the name of the
+** query parameter we seek. This routine returns the value of the zParam
+** parameter if it exists. If the parameter does not exist, this routine
+** returns a NULL pointer.
+*/
+SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam){
+ zFilename += sqlite3Strlen30(zFilename) + 1;
+ while( zFilename[0] ){
+ int x = strcmp(zFilename, zParam);
+ zFilename += sqlite3Strlen30(zFilename) + 1;
+ if( x==0 ) return zFilename;
+ zFilename += sqlite3Strlen30(zFilename) + 1;
+ }
+ return 0;
+}
+
/************** End of main.c ************************************************/
/************** Begin file notify.c ******************************************/
/*
** into a single segment.
*/
-#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
-
-#if defined(SQLITE_ENABLE_FTS3) && !defined(SQLITE_CORE)
-# define SQLITE_CORE 1
-#endif
-
/************** Include fts3Int.h in the middle of fts3.c ********************/
/************** Begin file fts3Int.h *****************************************/
/*
******************************************************************************
**
*/
-
#ifndef _FTSINT_H
#define _FTSINT_H
# define NDEBUG 1
#endif
+/*
+** FTS4 is really an extension for FTS3. It is enabled using the
+** SQLITE_ENABLE_FTS3 macro. But to avoid confusion we also all
+** the SQLITE_ENABLE_FTS4 macro to serve as an alisse for SQLITE_ENABLE_FTS3.
+*/
+#if defined(SQLITE_ENABLE_FTS4) && !defined(SQLITE_ENABLE_FTS3)
+# define SQLITE_ENABLE_FTS3
+#endif
+
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
+
+/* If not building as part of the core, include sqlite3ext.h. */
+#ifndef SQLITE_CORE
+SQLITE_API extern const sqlite3_api_routines *sqlite3_api;
+#endif
+
/************** Include fts3_tokenizer.h in the middle of fts3Int.h **********/
/************** Begin file fts3_tokenizer.h **********************************/
/*
*/
#define SizeofArray(X) ((int)(sizeof(X)/sizeof(X[0])))
+
+#ifndef MIN
+# define MIN(x,y) ((x)<(y)?(x):(y))
+#endif
+
/*
** Maximum length of a varint encoded integer. The varint format is different
** from that used by SQLite, so the maximum length is 10, not 9.
#define FTS3_VARINT_MAX 10
/*
+** FTS4 virtual tables may maintain multiple indexes - one index of all terms
+** in the document set and zero or more prefix indexes. All indexes are stored
+** as one or more b+-trees in the %_segments and %_segdir tables.
+**
+** It is possible to determine which index a b+-tree belongs to based on the
+** value stored in the "%_segdir.level" column. Given this value L, the index
+** that the b+-tree belongs to is (L<<10). In other words, all b+-trees with
+** level values between 0 and 1023 (inclusive) belong to index 0, all levels
+** between 1024 and 2047 to index 1, and so on.
+**
+** It is considered impossible for an index to use more than 1024 levels. In
+** theory though this may happen, but only after at least
+** (FTS3_MERGE_COUNT^1024) separate flushes of the pending-terms tables.
+*/
+#define FTS3_SEGDIR_MAXLEVEL 1024
+#define FTS3_SEGDIR_MAXLEVEL_STR "1024"
+
+/*
** The testcase() macro is only used by the amalgamation. If undefined,
** make it a no-op.
*/
typedef short int i16; /* 2-byte (or larger) signed integer */
typedef unsigned int u32; /* 4-byte unsigned integer */
typedef sqlite3_uint64 u64; /* 8-byte unsigned integer */
+
/*
** Macro used to suppress compiler warnings for unused parameters.
*/
#define UNUSED_PARAMETER(x) (void)(x)
+
+/*
+** Activate assert() only if SQLITE_TEST is enabled.
+*/
+#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
+# define NDEBUG 1
#endif
+/*
+** The TESTONLY macro is used to enclose variable declarations or
+** other bits of code that are needed to support the arguments
+** within testcase() and assert() macros.
+*/
+#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
+# define TESTONLY(X) X
+#else
+# define TESTONLY(X)
+#endif
+
+#endif /* SQLITE_AMALGAMATION */
+
typedef struct Fts3Table Fts3Table;
typedef struct Fts3Cursor Fts3Cursor;
typedef struct Fts3Expr Fts3Expr;
typedef struct Fts3Phrase Fts3Phrase;
typedef struct Fts3PhraseToken Fts3PhraseToken;
+typedef struct Fts3Doclist Fts3Doclist;
typedef struct Fts3SegFilter Fts3SegFilter;
typedef struct Fts3DeferredToken Fts3DeferredToken;
typedef struct Fts3SegReader Fts3SegReader;
-typedef struct Fts3SegReaderCursor Fts3SegReaderCursor;
+typedef struct Fts3MultiSegReader Fts3MultiSegReader;
/*
** A connection to a fulltext index is an instance of the following
/* Precompiled statements used by the implementation. Each of these
** statements is run and reset within a single virtual table API call.
*/
- sqlite3_stmt *aStmt[24];
+ sqlite3_stmt *aStmt[27];
char *zReadExprlist;
char *zWriteExprlist;
int nNodeSize; /* Soft limit for node size */
u8 bHasStat; /* True if %_stat table exists */
u8 bHasDocsize; /* True if %_docsize table exists */
+ u8 bDescIdx; /* True if doclists are in reverse order */
int nPgsz; /* Page size for host database */
char *zSegmentsTbl; /* Name of %_segments table */
sqlite3_blob *pSegments; /* Blob handle open on %_segments table */
- /* The following hash table is used to buffer pending index updates during
+ /* TODO: Fix the first paragraph of this comment.
+ **
+ ** The following hash table is used to buffer pending index updates during
** transactions. Variable nPendingData estimates the memory size of the
** pending data, including hash table overhead, but not malloc overhead.
** When nPendingData exceeds nMaxPendingData, the buffer is flushed
** automatically. Variable iPrevDocid is the docid of the most recently
** inserted record.
+ **
+ ** A single FTS4 table may have multiple full-text indexes. For each index
+ ** there is an entry in the aIndex[] array. Index 0 is an index of all the
+ ** terms that appear in the document set. Each subsequent index in aIndex[]
+ ** is an index of prefixes of a specific length.
+ */
+ int nIndex; /* Size of aIndex[] */
+ struct Fts3Index {
+ int nPrefix; /* Prefix length (0 for main terms index) */
+ Fts3Hash hPending; /* Pending terms table for this index */
+ } *aIndex;
+ int nMaxPendingData; /* Max pending data before flush to disk */
+ int nPendingData; /* Current bytes of pending data */
+ sqlite_int64 iPrevDocid; /* Docid of most recently inserted document */
+
+#if defined(SQLITE_DEBUG)
+ /* State variables used for validating that the transaction control
+ ** methods of the virtual table are called at appropriate times. These
+ ** values do not contribution to the FTS computation; they are used for
+ ** verifying the SQLite core.
*/
- int nMaxPendingData;
- int nPendingData;
- sqlite_int64 iPrevDocid;
- Fts3Hash pendingTerms;
+ int inTransaction; /* True after xBegin but before xCommit/xRollback */
+ int mxSavepoint; /* Largest valid xSavepoint integer */
+#endif
};
/*
char *pNextId; /* Pointer into the body of aDoclist */
char *aDoclist; /* List of docids for full-text queries */
int nDoclist; /* Size of buffer at aDoclist */
+ u8 bDesc; /* True to sort in descending order */
int eEvalmode; /* An FTS3_EVAL_XX constant */
int nRowAvg; /* Average size of database rows, in pages */
+ sqlite3_int64 nDoc; /* Documents in table */
int isMatchinfoNeeded; /* True when aMatchinfo[] needs filling in */
u32 *aMatchinfo; /* Information about most recent match */
#define FTS3_DOCID_SEARCH 1 /* Lookup by rowid on %_content table */
#define FTS3_FULLTEXT_SEARCH 2 /* Full-text index search */
+
+struct Fts3Doclist {
+ char *aAll; /* Array containing doclist (or NULL) */
+ int nAll; /* Size of a[] in bytes */
+ char *pNextDocid; /* Pointer to next docid */
+
+ sqlite3_int64 iDocid; /* Current docid (if pList!=0) */
+ int bFreeList; /* True if pList should be sqlite3_free()d */
+ char *pList; /* Pointer to position list following iDocid */
+ int nList; /* Length of position list */
+};
+
/*
** A "phrase" is a sequence of one or more tokens that must match in
** sequence. A single token is the base case and the most common case.
** For a sequence of tokens contained in double-quotes (i.e. "one two three")
** nToken will be the number of tokens in the string.
-**
-** The nDocMatch and nMatch variables contain data that may be used by the
-** matchinfo() function. They are populated when the full-text index is
-** queried for hits on the phrase. If one or more tokens in the phrase
-** are deferred, the nDocMatch and nMatch variables are populated based
-** on the assumption that the
*/
struct Fts3PhraseToken {
char *z; /* Text of the token */
int n; /* Number of bytes in buffer z */
int isPrefix; /* True if token ends with a "*" character */
- int bFulltext; /* True if full-text index was used */
- Fts3SegReaderCursor *pSegcsr; /* Segment-reader for this token */
+
+ /* Variables above this point are populated when the expression is
+ ** parsed (by code in fts3_expr.c). Below this point the variables are
+ ** used when evaluating the expression. */
Fts3DeferredToken *pDeferred; /* Deferred token object for this token */
+ Fts3MultiSegReader *pSegcsr; /* Segment-reader for this token */
};
struct Fts3Phrase {
- /* Variables populated by fts3_expr.c when parsing a MATCH expression */
+ /* Cache of doclist for this phrase. */
+ Fts3Doclist doclist;
+ int bIncr; /* True if doclist is loaded incrementally */
+ int iDoclistToken;
+
+ /* Variables below this point are populated by fts3_expr.c when parsing
+ ** a MATCH expression. Everything above is part of the evaluation phase.
+ */
int nToken; /* Number of tokens in the phrase */
int iColumn; /* Index of column this phrase must match */
- int isNot; /* Phrase prefixed by unary not (-) operator */
Fts3PhraseToken aToken[1]; /* One entry for each token in the phrase */
};
/*
** A tree of these objects forms the RHS of a MATCH operator.
**
-** If Fts3Expr.eType is either FTSQUERY_NEAR or FTSQUERY_PHRASE and isLoaded
-** is true, then aDoclist points to a malloced buffer, size nDoclist bytes,
-** containing the results of the NEAR or phrase query in FTS3 doclist
-** format. As usual, the initial "Length" field found in doclists stored
-** on disk is omitted from this buffer.
+** If Fts3Expr.eType is FTSQUERY_PHRASE and isLoaded is true, then aDoclist
+** points to a malloced buffer, size nDoclist bytes, containing the results
+** of this phrase query in FTS3 doclist format. As usual, the initial
+** "Length" field found in doclists stored on disk is omitted from this
+** buffer.
+**
+** Variable aMI is used only for FTSQUERY_NEAR nodes to store the global
+** matchinfo data. If it is not NULL, it points to an array of size nCol*3,
+** where nCol is the number of columns in the queried FTS table. The array
+** is populated as follows:
+**
+** aMI[iCol*3 + 0] = Undefined
+** aMI[iCol*3 + 1] = Number of occurrences
+** aMI[iCol*3 + 2] = Number of rows containing at least one instance
**
-** Variable pCurrent always points to the start of a docid field within
-** aDoclist. Since the doclist is usually scanned in docid order, this can
-** be used to accelerate seeking to the required docid within the doclist.
+** The aMI array is allocated using sqlite3_malloc(). It should be freed
+** when the expression node is.
*/
struct Fts3Expr {
int eType; /* One of the FTSQUERY_XXX values defined below */
Fts3Expr *pRight; /* Right operand */
Fts3Phrase *pPhrase; /* Valid if eType==FTSQUERY_PHRASE */
- int isLoaded; /* True if aDoclist/nDoclist are initialized. */
- char *aDoclist; /* Buffer containing doclist */
- int nDoclist; /* Size of aDoclist in bytes */
+ /* The following are used by the fts3_eval.c module. */
+ sqlite3_int64 iDocid; /* Current docid */
+ u8 bEof; /* True this expression is at EOF already */
+ u8 bStart; /* True if iDocid is valid */
+ u8 bDeferred; /* True if this expression is entirely deferred */
- sqlite3_int64 iCurrent;
- char *pCurrent;
+ u32 *aMI;
};
/*
SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *);
SQLITE_PRIVATE int sqlite3Fts3SegReaderNew(int, sqlite3_int64,
sqlite3_int64, sqlite3_int64, const char *, int, Fts3SegReader**);
-SQLITE_PRIVATE int sqlite3Fts3SegReaderPending(Fts3Table*,const char*,int,int,Fts3SegReader**);
+SQLITE_PRIVATE int sqlite3Fts3SegReaderPending(
+ Fts3Table*,int,const char*,int,int,Fts3SegReader**);
SQLITE_PRIVATE void sqlite3Fts3SegReaderFree(Fts3SegReader *);
-SQLITE_PRIVATE int sqlite3Fts3SegReaderCost(Fts3Cursor *, Fts3SegReader *, int *);
-SQLITE_PRIVATE int sqlite3Fts3AllSegdirs(Fts3Table*, int, sqlite3_stmt **);
+SQLITE_PRIVATE int sqlite3Fts3AllSegdirs(Fts3Table*, int, int, sqlite3_stmt **);
SQLITE_PRIVATE int sqlite3Fts3ReadLock(Fts3Table *);
-SQLITE_PRIVATE int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char **, int*);
+SQLITE_PRIVATE int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char **, int*, int*);
SQLITE_PRIVATE int sqlite3Fts3SelectDoctotal(Fts3Table *, sqlite3_stmt **);
SQLITE_PRIVATE int sqlite3Fts3SelectDocsize(Fts3Table *, sqlite3_int64, sqlite3_stmt **);
SQLITE_PRIVATE int sqlite3Fts3DeferToken(Fts3Cursor *, Fts3PhraseToken *, int);
SQLITE_PRIVATE int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *);
SQLITE_PRIVATE void sqlite3Fts3FreeDeferredDoclists(Fts3Cursor *);
-SQLITE_PRIVATE char *sqlite3Fts3DeferredDoclist(Fts3DeferredToken *, int *);
SQLITE_PRIVATE void sqlite3Fts3SegmentsClose(Fts3Table *);
-#define FTS3_SEGCURSOR_PENDING -1
-#define FTS3_SEGCURSOR_ALL -2
+/* Special values interpreted by sqlite3SegReaderCursor() */
+#define FTS3_SEGCURSOR_PENDING -1
+#define FTS3_SEGCURSOR_ALL -2
+
+SQLITE_PRIVATE int sqlite3Fts3SegReaderStart(Fts3Table*, Fts3MultiSegReader*, Fts3SegFilter*);
+SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(Fts3Table *, Fts3MultiSegReader *);
+SQLITE_PRIVATE void sqlite3Fts3SegReaderFinish(Fts3MultiSegReader *);
-SQLITE_PRIVATE int sqlite3Fts3SegReaderStart(Fts3Table*, Fts3SegReaderCursor*, Fts3SegFilter*);
-SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(Fts3Table *, Fts3SegReaderCursor *);
-SQLITE_PRIVATE void sqlite3Fts3SegReaderFinish(Fts3SegReaderCursor *);
SQLITE_PRIVATE int sqlite3Fts3SegReaderCursor(
- Fts3Table *, int, const char *, int, int, int, Fts3SegReaderCursor *);
+ Fts3Table *, int, int, const char *, int, int, int, Fts3MultiSegReader *);
/* Flags allowed as part of the 4th argument to SegmentReaderIterate() */
#define FTS3_SEGMENT_REQUIRE_POS 0x00000001
int flags;
};
-struct Fts3SegReaderCursor {
+struct Fts3MultiSegReader {
/* Used internally by sqlite3Fts3SegReaderXXX() calls */
Fts3SegReader **apSegment; /* Array of Fts3SegReader objects */
int nSegment; /* Size of apSegment array */
char *aBuffer; /* Buffer to merge doclists in */
int nBuffer; /* Allocated size of aBuffer[] in bytes */
- /* Cost of running this iterator. Used by fts3.c only. */
- int nCost;
+ int iColFilter; /* If >=0, filter for this column */
+ int bRestart;
+
+ /* Used by fts3.c only. */
+ int nCost; /* Cost of running iterator */
+ int bLookup; /* True if a lookup of a single entry. */
/* Output values. Valid only after Fts3SegReaderStep() returns SQLITE_ROW. */
char *zTerm; /* Pointer to term buffer */
SQLITE_PRIVATE int sqlite3Fts3GetVarint32(const char *, int *);
SQLITE_PRIVATE int sqlite3Fts3VarintLen(sqlite3_uint64);
SQLITE_PRIVATE void sqlite3Fts3Dequote(char *);
+SQLITE_PRIVATE void sqlite3Fts3DoclistPrev(int,char*,int,char**,sqlite3_int64*,int*,u8*);
-SQLITE_PRIVATE char *sqlite3Fts3FindPositions(Fts3Expr *, sqlite3_int64, int);
-SQLITE_PRIVATE int sqlite3Fts3ExprLoadDoclist(Fts3Cursor *, Fts3Expr *);
-SQLITE_PRIVATE int sqlite3Fts3ExprLoadFtDoclist(Fts3Cursor *, Fts3Expr *, char **, int *);
-SQLITE_PRIVATE int sqlite3Fts3ExprNearTrim(Fts3Expr *, Fts3Expr *, int);
+SQLITE_PRIVATE int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *);
/* fts3_tokenizer.c */
SQLITE_PRIVATE const char *sqlite3Fts3NextToken(const char *, int *);
SQLITE_PRIVATE void sqlite3Fts3ExprFree(Fts3Expr *);
#ifdef SQLITE_TEST
SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3 *db);
+SQLITE_PRIVATE int sqlite3Fts3InitTerm(sqlite3 *db);
#endif
/* fts3_aux.c */
SQLITE_PRIVATE int sqlite3Fts3InitAux(sqlite3 *db);
+SQLITE_PRIVATE void sqlite3Fts3EvalPhraseCleanup(Fts3Phrase *);
+
+SQLITE_PRIVATE int sqlite3Fts3MsrIncrStart(
+ Fts3Table*, Fts3MultiSegReader*, int, const char*, int);
+SQLITE_PRIVATE int sqlite3Fts3MsrIncrNext(
+ Fts3Table *, Fts3MultiSegReader *, sqlite3_int64 *, char **, int *);
+SQLITE_PRIVATE char *sqlite3Fts3EvalPhrasePoslist(Fts3Cursor *, Fts3Expr *, int iCol);
+SQLITE_PRIVATE int sqlite3Fts3MsrOvfl(Fts3Cursor *, Fts3MultiSegReader *, int *);
+SQLITE_PRIVATE int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr);
+
+SQLITE_PRIVATE int sqlite3Fts3DeferredTokenList(Fts3DeferredToken *, char **, int *);
+
+#endif /* !SQLITE_CORE || SQLITE_ENABLE_FTS3 */
#endif /* _FTSINT_H */
/************** End of fts3Int.h *********************************************/
/************** Continuing where we left off in fts3.c ***********************/
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
+
+#if defined(SQLITE_ENABLE_FTS3) && !defined(SQLITE_CORE)
+# define SQLITE_CORE 1
+#endif
+/* #include <assert.h> */
+/* #include <stdlib.h> */
+/* #include <stddef.h> */
+/* #include <stdio.h> */
+/* #include <string.h> */
+/* #include <stdarg.h> */
#ifndef SQLITE_CORE
SQLITE_EXTENSION_INIT1
#endif
+static int fts3EvalNext(Fts3Cursor *pCsr);
+static int fts3EvalStart(Fts3Cursor *pCsr);
+static int fts3TermSegReaderCursor(
+ Fts3Cursor *, const char *, int, int, Fts3MultiSegReader **);
+
/*
** Write a 64-bit variable-length integer to memory starting at p[0].
** The length of data written will be between 1 and FTS3_VARINT_MAX bytes.
}
/*
-** As long as *pp has not reached its end (pEnd), then do the same
-** as fts3GetDeltaVarint(): read a single varint and add it to *pVal.
-** But if we have reached the end of the varint, just set *pp=0 and
-** leave *pVal unchanged.
+** When this function is called, *pp points to the first byte following a
+** varint that is part of a doclist (or position-list, or any other list
+** of varints). This function moves *pp to point to the start of that varint,
+** and sets *pVal by the varint value.
+**
+** Argument pStart points to the first byte of the doclist that the
+** varint is part of.
*/
-static void fts3GetDeltaVarint2(char **pp, char *pEnd, sqlite3_int64 *pVal){
- if( *pp>=pEnd ){
- *pp = 0;
- }else{
- fts3GetDeltaVarint(pp, pVal);
- }
+static void fts3GetReverseVarint(
+ char **pp,
+ char *pStart,
+ sqlite3_int64 *pVal
+){
+ sqlite3_int64 iVal;
+ char *p = *pp;
+
+ /* Pointer p now points at the first byte past the varint we are
+ ** interested in. So, unless the doclist is corrupt, the 0x80 bit is
+ ** clear on character p[-1]. */
+ for(p = (*pp)-2; p>=pStart && *p&0x80; p--);
+ p++;
+ *pp = p;
+
+ sqlite3Fts3GetVarint(p, &iVal);
+ *pVal = iVal;
}
/*
char *zSql; /* SQL statement passed to declare_vtab() */
char *zCols; /* List of user defined columns */
+ sqlite3_vtab_config(p->db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
+
/* Create a list of user columns for the virtual table */
zCols = sqlite3_mprintf("%Q, ", p->azColumn[0]);
for(i=1; zCols && i<p->nColumn; i++){
sqlite3_step(pStmt);
p->nPgsz = sqlite3_column_int(pStmt, 0);
rc = sqlite3_finalize(pStmt);
+ }else if( rc==SQLITE_AUTH ){
+ p->nPgsz = 1024;
+ rc = SQLITE_OK;
}
}
assert( p->nPgsz>0 || rc!=SQLITE_OK );
}
/*
+** This function interprets the string at (*pp) as a non-negative integer
+** value. It reads the integer and sets *pnOut to the value read, then
+** sets *pp to point to the byte immediately following the last byte of
+** the integer value.
+**
+** Only decimal digits ('0'..'9') may be part of an integer value.
+**
+** If *pp does not being with a decimal digit SQLITE_ERROR is returned and
+** the output value undefined. Otherwise SQLITE_OK is returned.
+**
+** This function is used when parsing the "prefix=" FTS4 parameter.
+*/
+static int fts3GobbleInt(const char **pp, int *pnOut){
+ const char *p = *pp; /* Iterator pointer */
+ int nInt = 0; /* Output value */
+
+ for(p=*pp; p[0]>='0' && p[0]<='9'; p++){
+ nInt = nInt * 10 + (p[0] - '0');
+ }
+ if( p==*pp ) return SQLITE_ERROR;
+ *pnOut = nInt;
+ *pp = p;
+ return SQLITE_OK;
+}
+
+/*
+** This function is called to allocate an array of Fts3Index structures
+** representing the indexes maintained by the current FTS table. FTS tables
+** always maintain the main "terms" index, but may also maintain one or
+** more "prefix" indexes, depending on the value of the "prefix=" parameter
+** (if any) specified as part of the CREATE VIRTUAL TABLE statement.
+**
+** Argument zParam is passed the value of the "prefix=" option if one was
+** specified, or NULL otherwise.
+**
+** If no error occurs, SQLITE_OK is returned and *apIndex set to point to
+** the allocated array. *pnIndex is set to the number of elements in the
+** array. If an error does occur, an SQLite error code is returned.
+**
+** Regardless of whether or not an error is returned, it is the responsibility
+** of the caller to call sqlite3_free() on the output array to free it.
+*/
+static int fts3PrefixParameter(
+ const char *zParam, /* ABC in prefix=ABC parameter to parse */
+ int *pnIndex, /* OUT: size of *apIndex[] array */
+ struct Fts3Index **apIndex /* OUT: Array of indexes for this table */
+){
+ struct Fts3Index *aIndex; /* Allocated array */
+ int nIndex = 1; /* Number of entries in array */
+
+ if( zParam && zParam[0] ){
+ const char *p;
+ nIndex++;
+ for(p=zParam; *p; p++){
+ if( *p==',' ) nIndex++;
+ }
+ }
+
+ aIndex = sqlite3_malloc(sizeof(struct Fts3Index) * nIndex);
+ *apIndex = aIndex;
+ *pnIndex = nIndex;
+ if( !aIndex ){
+ return SQLITE_NOMEM;
+ }
+
+ memset(aIndex, 0, sizeof(struct Fts3Index) * nIndex);
+ if( zParam ){
+ const char *p = zParam;
+ int i;
+ for(i=1; i<nIndex; i++){
+ int nPrefix;
+ if( fts3GobbleInt(&p, &nPrefix) ) return SQLITE_ERROR;
+ aIndex[i].nPrefix = nPrefix;
+ p++;
+ }
+ }
+
+ return SQLITE_OK;
+}
+
+/*
** This function is the implementation of both the xConnect and xCreate
** methods of the FTS3 virtual table.
**
int nDb; /* Bytes required to hold database name */
int nName; /* Bytes required to hold table name */
int isFts4 = (argv[0][3]=='4'); /* True for FTS4, false for FTS3 */
- int bNoDocsize = 0; /* True to omit %_docsize table */
const char **aCol; /* Array of column names */
sqlite3_tokenizer *pTokenizer = 0; /* Tokenizer for this table */
- char *zCompress = 0;
- char *zUncompress = 0;
+ int nIndex; /* Size of aIndex[] array */
+ struct Fts3Index *aIndex = 0; /* Array of indexes for this table */
+
+ /* The results of parsing supported FTS4 key=value options: */
+ int bNoDocsize = 0; /* True to omit %_docsize table */
+ int bDescIdx = 0; /* True to store descending indexes */
+ char *zPrefix = 0; /* Prefix parameter value (or NULL) */
+ char *zCompress = 0; /* compress=? parameter (or NULL) */
+ char *zUncompress = 0; /* uncompress=? parameter (or NULL) */
assert( strlen(argv[0])==4 );
assert( (sqlite3_strnicmp(argv[0], "fts4", 4)==0 && isFts4)
/* Check if it is an FTS4 special argument. */
else if( isFts4 && fts3IsSpecialColumn(z, &nKey, &zVal) ){
+ struct Fts4Option {
+ const char *zOpt;
+ int nOpt;
+ char **pzVar;
+ } aFts4Opt[] = {
+ { "matchinfo", 9, 0 }, /* 0 -> MATCHINFO */
+ { "prefix", 6, 0 }, /* 1 -> PREFIX */
+ { "compress", 8, 0 }, /* 2 -> COMPRESS */
+ { "uncompress", 10, 0 }, /* 3 -> UNCOMPRESS */
+ { "order", 5, 0 } /* 4 -> ORDER */
+ };
+
+ int iOpt;
if( !zVal ){
rc = SQLITE_NOMEM;
- goto fts3_init_out;
- }
- if( nKey==9 && 0==sqlite3_strnicmp(z, "matchinfo", 9) ){
- if( strlen(zVal)==4 && 0==sqlite3_strnicmp(zVal, "fts3", 4) ){
- bNoDocsize = 1;
- }else{
- *pzErr = sqlite3_mprintf("unrecognized matchinfo: %s", zVal);
+ }else{
+ for(iOpt=0; iOpt<SizeofArray(aFts4Opt); iOpt++){
+ struct Fts4Option *pOp = &aFts4Opt[iOpt];
+ if( nKey==pOp->nOpt && !sqlite3_strnicmp(z, pOp->zOpt, pOp->nOpt) ){
+ break;
+ }
+ }
+ if( iOpt==SizeofArray(aFts4Opt) ){
+ *pzErr = sqlite3_mprintf("unrecognized parameter: %s", z);
rc = SQLITE_ERROR;
+ }else{
+ switch( iOpt ){
+ case 0: /* MATCHINFO */
+ if( strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "fts3", 4) ){
+ *pzErr = sqlite3_mprintf("unrecognized matchinfo: %s", zVal);
+ rc = SQLITE_ERROR;
+ }
+ bNoDocsize = 1;
+ break;
+
+ case 1: /* PREFIX */
+ sqlite3_free(zPrefix);
+ zPrefix = zVal;
+ zVal = 0;
+ break;
+
+ case 2: /* COMPRESS */
+ sqlite3_free(zCompress);
+ zCompress = zVal;
+ zVal = 0;
+ break;
+
+ case 3: /* UNCOMPRESS */
+ sqlite3_free(zUncompress);
+ zUncompress = zVal;
+ zVal = 0;
+ break;
+
+ case 4: /* ORDER */
+ if( (strlen(zVal)!=3 || sqlite3_strnicmp(zVal, "asc", 3))
+ && (strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "desc", 3))
+ ){
+ *pzErr = sqlite3_mprintf("unrecognized order: %s", zVal);
+ rc = SQLITE_ERROR;
+ }
+ bDescIdx = (zVal[0]=='d' || zVal[0]=='D');
+ break;
+ }
}
- }else if( nKey==8 && 0==sqlite3_strnicmp(z, "compress", 8) ){
- zCompress = zVal;
- zVal = 0;
- }else if( nKey==10 && 0==sqlite3_strnicmp(z, "uncompress", 10) ){
- zUncompress = zVal;
- zVal = 0;
- }else{
- *pzErr = sqlite3_mprintf("unrecognized parameter: %s", z);
- rc = SQLITE_ERROR;
+ sqlite3_free(zVal);
}
- sqlite3_free(zVal);
}
/* Otherwise, the argument is a column name. */
}
assert( pTokenizer );
+ rc = fts3PrefixParameter(zPrefix, &nIndex, &aIndex);
+ if( rc==SQLITE_ERROR ){
+ assert( zPrefix );
+ *pzErr = sqlite3_mprintf("error parsing prefix parameter: %s", zPrefix);
+ }
+ if( rc!=SQLITE_OK ) goto fts3_init_out;
/* Allocate and populate the Fts3Table structure. */
- nByte = sizeof(Fts3Table) + /* Fts3Table */
+ nByte = sizeof(Fts3Table) + /* Fts3Table */
nCol * sizeof(char *) + /* azColumn */
+ nIndex * sizeof(struct Fts3Index) + /* aIndex */
nName + /* zName */
nDb + /* zDb */
nString; /* Space for azColumn strings */
p->nPendingData = 0;
p->azColumn = (char **)&p[1];
p->pTokenizer = pTokenizer;
- p->nNodeSize = 1000;
p->nMaxPendingData = FTS3_MAX_PENDING_DATA;
p->bHasDocsize = (isFts4 && bNoDocsize==0);
p->bHasStat = isFts4;
- fts3HashInit(&p->pendingTerms, FTS3_HASH_STRING, 1);
+ p->bDescIdx = bDescIdx;
+ TESTONLY( p->inTransaction = -1 );
+ TESTONLY( p->mxSavepoint = -1 );
+
+ p->aIndex = (struct Fts3Index *)&p->azColumn[nCol];
+ memcpy(p->aIndex, aIndex, sizeof(struct Fts3Index) * nIndex);
+ p->nIndex = nIndex;
+ for(i=0; i<nIndex; i++){
+ fts3HashInit(&p->aIndex[i].hPending, FTS3_HASH_STRING, 1);
+ }
/* Fill in the zName and zDb fields of the vtab structure. */
- zCsr = (char *)&p->azColumn[nCol];
+ zCsr = (char *)&p->aIndex[nIndex];
p->zName = zCsr;
memcpy(zCsr, argv[2], nName);
zCsr += nName;
/* Fill in the azColumn array */
for(iCol=0; iCol<nCol; iCol++){
char *z;
- int n;
+ int n = 0;
z = (char *)sqlite3Fts3NextToken(aCol[iCol], &n);
memcpy(zCsr, z, n);
zCsr[n] = '\0';
}
/* Figure out the page-size for the database. This is required in order to
- ** estimate the cost of loading large doclists from the database (see
- ** function sqlite3Fts3SegReaderCost() for details).
- */
+ ** estimate the cost of loading large doclists from the database. */
fts3DatabasePageSize(&rc, p);
+ p->nNodeSize = p->nPgsz-35;
/* Declare the table schema to SQLite. */
fts3DeclareVtab(&rc, p);
fts3_init_out:
+ sqlite3_free(zPrefix);
+ sqlite3_free(aIndex);
sqlite3_free(zCompress);
sqlite3_free(zUncompress);
sqlite3_free((void *)aCol);
pTokenizer->pModule->xDestroy(pTokenizer);
}
}else{
+ assert( p->pSegments==0 );
*ppVTab = &p->base;
}
return rc;
pInfo->aConstraintUsage[iCons].argvIndex = 1;
pInfo->aConstraintUsage[iCons].omit = 1;
}
+
+ /* Regardless of the strategy selected, FTS can deliver rows in rowid (or
+ ** docid) order. Both ascending and descending are possible.
+ */
+ if( pInfo->nOrderBy==1 ){
+ struct sqlite3_index_orderby *pOrder = &pInfo->aOrderBy[0];
+ if( pOrder->iColumn<0 || pOrder->iColumn==p->nColumn+1 ){
+ if( pOrder->desc ){
+ pInfo->idxStr = "DESC";
+ }else{
+ pInfo->idxStr = "ASC";
+ }
+ pInfo->orderByConsumed = 1;
+ }
+ }
+
+ assert( p->pSegments==0 );
return SQLITE_OK;
}
sqlite3Fts3FreeDeferredTokens(pCsr);
sqlite3_free(pCsr->aDoclist);
sqlite3_free(pCsr->aMatchinfo);
+ assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 );
sqlite3_free(pCsr);
return SQLITE_OK;
}
*/
static int fts3CursorSeek(sqlite3_context *pContext, Fts3Cursor *pCsr){
if( pCsr->isRequireSeek ){
- pCsr->isRequireSeek = 0;
sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iPrevId);
+ pCsr->isRequireSeek = 0;
if( SQLITE_ROW==sqlite3_step(pCsr->pStmt) ){
return SQLITE_OK;
}else{
** table is missing a row that is present in the full-text index.
** The data structures are corrupt.
*/
- rc = SQLITE_CORRUPT;
+ rc = SQLITE_CORRUPT_VTAB;
}
pCsr->isEof = 1;
if( pContext ){
zCsr += sqlite3Fts3GetVarint(zCsr, &iChild);
zCsr += sqlite3Fts3GetVarint(zCsr, &iChild);
if( zCsr>zEnd ){
- return SQLITE_CORRUPT;
+ return SQLITE_CORRUPT_VTAB;
}
while( zCsr<zEnd && (piFirst || piLast) ){
zCsr += sqlite3Fts3GetVarint32(zCsr, &nSuffix);
if( nPrefix<0 || nSuffix<0 || &zCsr[nSuffix]>zEnd ){
- rc = SQLITE_CORRUPT;
+ rc = SQLITE_CORRUPT_VTAB;
goto finish_scan;
}
if( nPrefix+nSuffix>nAlloc ){
int nBlob; /* Size of zBlob in bytes */
if( piLeaf && piLeaf2 && (*piLeaf!=*piLeaf2) ){
- rc = sqlite3Fts3ReadBlock(p, *piLeaf, &zBlob, &nBlob);
+ rc = sqlite3Fts3ReadBlock(p, *piLeaf, &zBlob, &nBlob, 0);
if( rc==SQLITE_OK ){
rc = fts3SelectLeaf(p, zTerm, nTerm, zBlob, nBlob, piLeaf, 0);
}
}
if( rc==SQLITE_OK ){
- rc = sqlite3Fts3ReadBlock(p, piLeaf ? *piLeaf : *piLeaf2, &zBlob, &nBlob);
+ rc = sqlite3Fts3ReadBlock(p, piLeaf?*piLeaf:*piLeaf2, &zBlob, &nBlob, 0);
}
if( rc==SQLITE_OK ){
rc = fts3SelectLeaf(p, zTerm, nTerm, zBlob, nBlob, piLeaf, piLeaf2);
}
/*
-** nToken==1 searches for adjacent positions.
-**
** This function is used to merge two position lists into one. When it is
** called, *pp1 and *pp2 must both point to position lists. A position-list is
** the part of a doclist that follows each document id. For example, if a row
** *pp1 so that (pos(*pp2)>pos(*pp1) && pos(*pp2)-pos(*pp1)<=nToken). i.e.
** when the *pp1 token appears before the *pp2 token, but not more than nToken
** slots before it.
+**
+** e.g. nToken==1 searches for adjacent positions.
*/
static int fts3PoslistPhraseMerge(
char **pp, /* IN/OUT: Preallocated output buffer */
}
/*
-** Merge two position-lists as required by the NEAR operator.
+** Merge two position-lists as required by the NEAR operator. The argument
+** position lists correspond to the left and right phrases of an expression
+** like:
+**
+** "phrase 1" NEAR "phrase number 2"
+**
+** Position list *pp1 corresponds to the left-hand side of the NEAR
+** expression and *pp2 to the right. As usual, the indexes in the position
+** lists are the offsets of the last token in each phrase (tokens "1" and "2"
+** in the example above).
+**
+** The output position list - written to *pp - is a copy of *pp2 with those
+** entries that are not sufficiently NEAR entries in *pp1 removed.
*/
static int fts3PoslistNearMerge(
char **pp, /* Output buffer */
char *p1 = *pp1;
char *p2 = *pp2;
- if( !pp ){
- if( fts3PoslistPhraseMerge(0, nRight, 0, 0, pp1, pp2) ) return 1;
- *pp1 = p1;
- *pp2 = p2;
- return fts3PoslistPhraseMerge(0, nLeft, 0, 0, pp2, pp1);
+ char *pTmp1 = aTmp;
+ char *pTmp2;
+ char *aTmp2;
+ int res = 1;
+
+ fts3PoslistPhraseMerge(&pTmp1, nRight, 0, 0, pp1, pp2);
+ aTmp2 = pTmp2 = pTmp1;
+ *pp1 = p1;
+ *pp2 = p2;
+ fts3PoslistPhraseMerge(&pTmp2, nLeft, 1, 0, pp2, pp1);
+ if( pTmp1!=aTmp && pTmp2!=aTmp2 ){
+ fts3PoslistMerge(pp, &aTmp, &aTmp2);
+ }else if( pTmp1!=aTmp ){
+ fts3PoslistCopy(pp, &aTmp);
+ }else if( pTmp2!=aTmp2 ){
+ fts3PoslistCopy(pp, &aTmp2);
+ }else{
+ res = 0;
+ }
+
+ return res;
+}
+
+/*
+** An instance of this function is used to merge together the (potentially
+** large number of) doclists for each term that matches a prefix query.
+** See function fts3TermSelectMerge() for details.
+*/
+typedef struct TermSelect TermSelect;
+struct TermSelect {
+ char *aaOutput[16]; /* Malloc'd output buffers */
+ int anOutput[16]; /* Size each output buffer in bytes */
+};
+
+/*
+** This function is used to read a single varint from a buffer. Parameter
+** pEnd points 1 byte past the end of the buffer. When this function is
+** called, if *pp points to pEnd or greater, then the end of the buffer
+** has been reached. In this case *pp is set to 0 and the function returns.
+**
+** If *pp does not point to or past pEnd, then a single varint is read
+** from *pp. *pp is then set to point 1 byte past the end of the read varint.
+**
+** If bDescIdx is false, the value read is added to *pVal before returning.
+** If it is true, the value read is subtracted from *pVal before this
+** function returns.
+*/
+static void fts3GetDeltaVarint3(
+ char **pp, /* IN/OUT: Point to read varint from */
+ char *pEnd, /* End of buffer */
+ int bDescIdx, /* True if docids are descending */
+ sqlite3_int64 *pVal /* IN/OUT: Integer value */
+){
+ if( *pp>=pEnd ){
+ *pp = 0;
}else{
- char *pTmp1 = aTmp;
- char *pTmp2;
- char *aTmp2;
- int res = 1;
-
- fts3PoslistPhraseMerge(&pTmp1, nRight, 0, 0, pp1, pp2);
- aTmp2 = pTmp2 = pTmp1;
- *pp1 = p1;
- *pp2 = p2;
- fts3PoslistPhraseMerge(&pTmp2, nLeft, 1, 0, pp2, pp1);
- if( pTmp1!=aTmp && pTmp2!=aTmp2 ){
- fts3PoslistMerge(pp, &aTmp, &aTmp2);
- }else if( pTmp1!=aTmp ){
- fts3PoslistCopy(pp, &aTmp);
- }else if( pTmp2!=aTmp2 ){
- fts3PoslistCopy(pp, &aTmp2);
+ sqlite3_int64 iVal;
+ *pp += sqlite3Fts3GetVarint(*pp, &iVal);
+ if( bDescIdx ){
+ *pVal -= iVal;
}else{
- res = 0;
+ *pVal += iVal;
}
+ }
+}
- return res;
+/*
+** This function is used to write a single varint to a buffer. The varint
+** is written to *pp. Before returning, *pp is set to point 1 byte past the
+** end of the value written.
+**
+** If *pbFirst is zero when this function is called, the value written to
+** the buffer is that of parameter iVal.
+**
+** If *pbFirst is non-zero when this function is called, then the value
+** written is either (iVal-*piPrev) (if bDescIdx is zero) or (*piPrev-iVal)
+** (if bDescIdx is non-zero).
+**
+** Before returning, this function always sets *pbFirst to 1 and *piPrev
+** to the value of parameter iVal.
+*/
+static void fts3PutDeltaVarint3(
+ char **pp, /* IN/OUT: Output pointer */
+ int bDescIdx, /* True for descending docids */
+ sqlite3_int64 *piPrev, /* IN/OUT: Previous value written to list */
+ int *pbFirst, /* IN/OUT: True after first int written */
+ sqlite3_int64 iVal /* Write this value to the list */
+){
+ sqlite3_int64 iWrite;
+ if( bDescIdx==0 || *pbFirst==0 ){
+ iWrite = iVal - *piPrev;
+ }else{
+ iWrite = *piPrev - iVal;
}
+ assert( *pbFirst || *piPrev==0 );
+ assert( *pbFirst==0 || iWrite>0 );
+ *pp += sqlite3Fts3PutVarint(*pp, iWrite);
+ *piPrev = iVal;
+ *pbFirst = 1;
}
+
/*
-** Values that may be used as the first parameter to fts3DoclistMerge().
+** This macro is used by various functions that merge doclists. The two
+** arguments are 64-bit docid values. If the value of the stack variable
+** bDescDoclist is 0 when this macro is invoked, then it returns (i1-i2).
+** Otherwise, (i2-i1).
+**
+** Using this makes it easier to write code that can merge doclists that are
+** sorted in either ascending or descending order.
*/
-#define MERGE_NOT 2 /* D + D -> D */
-#define MERGE_AND 3 /* D + D -> D */
-#define MERGE_OR 4 /* D + D -> D */
-#define MERGE_POS_OR 5 /* P + P -> P */
-#define MERGE_PHRASE 6 /* P + P -> D */
-#define MERGE_POS_PHRASE 7 /* P + P -> P */
-#define MERGE_NEAR 8 /* P + P -> D */
-#define MERGE_POS_NEAR 9 /* P + P -> P */
+#define DOCID_CMP(i1, i2) ((bDescDoclist?-1:1) * (i1-i2))
/*
-** Merge the two doclists passed in buffer a1 (size n1 bytes) and a2
-** (size n2 bytes). The output is written to pre-allocated buffer aBuffer,
-** which is guaranteed to be large enough to hold the results. The number
-** of bytes written to aBuffer is stored in *pnBuffer before returning.
+** This function does an "OR" merge of two doclists (output contains all
+** positions contained in either argument doclist). If the docids in the
+** input doclists are sorted in ascending order, parameter bDescDoclist
+** should be false. If they are sorted in ascending order, it should be
+** passed a non-zero value.
**
-** If successful, SQLITE_OK is returned. Otherwise, if a malloc error
-** occurs while allocating a temporary buffer as part of the merge operation,
-** SQLITE_NOMEM is returned.
+** If no error occurs, *paOut is set to point at an sqlite3_malloc'd buffer
+** containing the output doclist and SQLITE_OK is returned. In this case
+** *pnOut is set to the number of bytes in the output doclist.
+**
+** If an error occurs, an SQLite error code is returned. The output values
+** are undefined in this case.
*/
-static int fts3DoclistMerge(
- int mergetype, /* One of the MERGE_XXX constants */
- int nParam1, /* Used by MERGE_NEAR and MERGE_POS_NEAR */
- int nParam2, /* Used by MERGE_NEAR and MERGE_POS_NEAR */
- char *aBuffer, /* Pre-allocated output buffer */
- int *pnBuffer, /* OUT: Bytes written to aBuffer */
- char *a1, /* Buffer containing first doclist */
- int n1, /* Size of buffer a1 */
- char *a2, /* Buffer containing second doclist */
- int n2, /* Size of buffer a2 */
- int *pnDoc /* OUT: Number of docids in output */
+static int fts3DoclistOrMerge(
+ int bDescDoclist, /* True if arguments are desc */
+ char *a1, int n1, /* First doclist */
+ char *a2, int n2, /* Second doclist */
+ char **paOut, int *pnOut /* OUT: Malloc'd doclist */
){
sqlite3_int64 i1 = 0;
sqlite3_int64 i2 = 0;
sqlite3_int64 iPrev = 0;
-
- char *p = aBuffer;
- char *p1 = a1;
- char *p2 = a2;
char *pEnd1 = &a1[n1];
char *pEnd2 = &a2[n2];
- int nDoc = 0;
-
- assert( mergetype==MERGE_OR || mergetype==MERGE_POS_OR
- || mergetype==MERGE_AND || mergetype==MERGE_NOT
- || mergetype==MERGE_PHRASE || mergetype==MERGE_POS_PHRASE
- || mergetype==MERGE_NEAR || mergetype==MERGE_POS_NEAR
- );
-
- if( !aBuffer ){
- *pnBuffer = 0;
- return SQLITE_NOMEM;
- }
-
- /* Read the first docid from each doclist */
- fts3GetDeltaVarint2(&p1, pEnd1, &i1);
- fts3GetDeltaVarint2(&p2, pEnd2, &i2);
-
- switch( mergetype ){
- case MERGE_OR:
- case MERGE_POS_OR:
- while( p1 || p2 ){
- if( p2 && p1 && i1==i2 ){
- fts3PutDeltaVarint(&p, &iPrev, i1);
- if( mergetype==MERGE_POS_OR ) fts3PoslistMerge(&p, &p1, &p2);
- fts3GetDeltaVarint2(&p1, pEnd1, &i1);
- fts3GetDeltaVarint2(&p2, pEnd2, &i2);
- }else if( !p2 || (p1 && i1<i2) ){
- fts3PutDeltaVarint(&p, &iPrev, i1);
- if( mergetype==MERGE_POS_OR ) fts3PoslistCopy(&p, &p1);
- fts3GetDeltaVarint2(&p1, pEnd1, &i1);
- }else{
- fts3PutDeltaVarint(&p, &iPrev, i2);
- if( mergetype==MERGE_POS_OR ) fts3PoslistCopy(&p, &p2);
- fts3GetDeltaVarint2(&p2, pEnd2, &i2);
- }
- }
- break;
-
- case MERGE_AND:
- while( p1 && p2 ){
- if( i1==i2 ){
- fts3PutDeltaVarint(&p, &iPrev, i1);
- fts3GetDeltaVarint2(&p1, pEnd1, &i1);
- fts3GetDeltaVarint2(&p2, pEnd2, &i2);
- nDoc++;
- }else if( i1<i2 ){
- fts3GetDeltaVarint2(&p1, pEnd1, &i1);
- }else{
- fts3GetDeltaVarint2(&p2, pEnd2, &i2);
- }
- }
- break;
+ char *p1 = a1;
+ char *p2 = a2;
+ char *p;
+ char *aOut;
+ int bFirstOut = 0;
- case MERGE_NOT:
- while( p1 ){
- if( p2 && i1==i2 ){
- fts3GetDeltaVarint2(&p1, pEnd1, &i1);
- fts3GetDeltaVarint2(&p2, pEnd2, &i2);
- }else if( !p2 || i1<i2 ){
- fts3PutDeltaVarint(&p, &iPrev, i1);
- fts3GetDeltaVarint2(&p1, pEnd1, &i1);
- }else{
- fts3GetDeltaVarint2(&p2, pEnd2, &i2);
- }
- }
- break;
+ *paOut = 0;
+ *pnOut = 0;
- case MERGE_POS_PHRASE:
- case MERGE_PHRASE: {
- char **ppPos = (mergetype==MERGE_PHRASE ? 0 : &p);
- while( p1 && p2 ){
- if( i1==i2 ){
- char *pSave = p;
- sqlite3_int64 iPrevSave = iPrev;
- fts3PutDeltaVarint(&p, &iPrev, i1);
- if( 0==fts3PoslistPhraseMerge(ppPos, nParam1, 0, 1, &p1, &p2) ){
- p = pSave;
- iPrev = iPrevSave;
- }else{
- nDoc++;
- }
- fts3GetDeltaVarint2(&p1, pEnd1, &i1);
- fts3GetDeltaVarint2(&p2, pEnd2, &i2);
- }else if( i1<i2 ){
- fts3PoslistCopy(0, &p1);
- fts3GetDeltaVarint2(&p1, pEnd1, &i1);
- }else{
- fts3PoslistCopy(0, &p2);
- fts3GetDeltaVarint2(&p2, pEnd2, &i2);
- }
- }
- break;
+ /* Allocate space for the output. Both the input and output doclists
+ ** are delta encoded. If they are in ascending order (bDescDoclist==0),
+ ** then the first docid in each list is simply encoded as a varint. For
+ ** each subsequent docid, the varint stored is the difference between the
+ ** current and previous docid (a positive number - since the list is in
+ ** ascending order).
+ **
+ ** The first docid written to the output is therefore encoded using the
+ ** same number of bytes as it is in whichever of the input lists it is
+ ** read from. And each subsequent docid read from the same input list
+ ** consumes either the same or less bytes as it did in the input (since
+ ** the difference between it and the previous value in the output must
+ ** be a positive value less than or equal to the delta value read from
+ ** the input list). The same argument applies to all but the first docid
+ ** read from the 'other' list. And to the contents of all position lists
+ ** that will be copied and merged from the input to the output.
+ **
+ ** However, if the first docid copied to the output is a negative number,
+ ** then the encoding of the first docid from the 'other' input list may
+ ** be larger in the output than it was in the input (since the delta value
+ ** may be a larger positive integer than the actual docid).
+ **
+ ** The space required to store the output is therefore the sum of the
+ ** sizes of the two inputs, plus enough space for exactly one of the input
+ ** docids to grow.
+ **
+ ** A symetric argument may be made if the doclists are in descending
+ ** order.
+ */
+ aOut = sqlite3_malloc(n1+n2+FTS3_VARINT_MAX-1);
+ if( !aOut ) return SQLITE_NOMEM;
+
+ p = aOut;
+ fts3GetDeltaVarint3(&p1, pEnd1, 0, &i1);
+ fts3GetDeltaVarint3(&p2, pEnd2, 0, &i2);
+ while( p1 || p2 ){
+ sqlite3_int64 iDiff = DOCID_CMP(i1, i2);
+
+ if( p2 && p1 && iDiff==0 ){
+ fts3PutDeltaVarint3(&p, bDescDoclist, &iPrev, &bFirstOut, i1);
+ fts3PoslistMerge(&p, &p1, &p2);
+ fts3GetDeltaVarint3(&p1, pEnd1, bDescDoclist, &i1);
+ fts3GetDeltaVarint3(&p2, pEnd2, bDescDoclist, &i2);
+ }else if( !p2 || (p1 && iDiff<0) ){
+ fts3PutDeltaVarint3(&p, bDescDoclist, &iPrev, &bFirstOut, i1);
+ fts3PoslistCopy(&p, &p1);
+ fts3GetDeltaVarint3(&p1, pEnd1, bDescDoclist, &i1);
+ }else{
+ fts3PutDeltaVarint3(&p, bDescDoclist, &iPrev, &bFirstOut, i2);
+ fts3PoslistCopy(&p, &p2);
+ fts3GetDeltaVarint3(&p2, pEnd2, bDescDoclist, &i2);
}
+ }
- default: assert( mergetype==MERGE_POS_NEAR || mergetype==MERGE_NEAR ); {
- char *aTmp = 0;
- char **ppPos = 0;
-
- if( mergetype==MERGE_POS_NEAR ){
- ppPos = &p;
- aTmp = sqlite3_malloc(2*(n1+n2+1));
- if( !aTmp ){
- return SQLITE_NOMEM;
- }
- }
-
- while( p1 && p2 ){
- if( i1==i2 ){
- char *pSave = p;
- sqlite3_int64 iPrevSave = iPrev;
- fts3PutDeltaVarint(&p, &iPrev, i1);
+ *paOut = aOut;
+ *pnOut = (p-aOut);
+ assert( *pnOut<=n1+n2+FTS3_VARINT_MAX-1 );
+ return SQLITE_OK;
+}
- if( !fts3PoslistNearMerge(ppPos, aTmp, nParam1, nParam2, &p1, &p2) ){
- iPrev = iPrevSave;
- p = pSave;
- }
+/*
+** This function does a "phrase" merge of two doclists. In a phrase merge,
+** the output contains a copy of each position from the right-hand input
+** doclist for which there is a position in the left-hand input doclist
+** exactly nDist tokens before it.
+**
+** If the docids in the input doclists are sorted in ascending order,
+** parameter bDescDoclist should be false. If they are sorted in ascending
+** order, it should be passed a non-zero value.
+**
+** The right-hand input doclist is overwritten by this function.
+*/
+static void fts3DoclistPhraseMerge(
+ int bDescDoclist, /* True if arguments are desc */
+ int nDist, /* Distance from left to right (1=adjacent) */
+ char *aLeft, int nLeft, /* Left doclist */
+ char *aRight, int *pnRight /* IN/OUT: Right/output doclist */
+){
+ sqlite3_int64 i1 = 0;
+ sqlite3_int64 i2 = 0;
+ sqlite3_int64 iPrev = 0;
+ char *pEnd1 = &aLeft[nLeft];
+ char *pEnd2 = &aRight[*pnRight];
+ char *p1 = aLeft;
+ char *p2 = aRight;
+ char *p;
+ int bFirstOut = 0;
+ char *aOut = aRight;
+
+ assert( nDist>0 );
+
+ p = aOut;
+ fts3GetDeltaVarint3(&p1, pEnd1, 0, &i1);
+ fts3GetDeltaVarint3(&p2, pEnd2, 0, &i2);
+
+ while( p1 && p2 ){
+ sqlite3_int64 iDiff = DOCID_CMP(i1, i2);
+ if( iDiff==0 ){
+ char *pSave = p;
+ sqlite3_int64 iPrevSave = iPrev;
+ int bFirstOutSave = bFirstOut;
- fts3GetDeltaVarint2(&p1, pEnd1, &i1);
- fts3GetDeltaVarint2(&p2, pEnd2, &i2);
- }else if( i1<i2 ){
- fts3PoslistCopy(0, &p1);
- fts3GetDeltaVarint2(&p1, pEnd1, &i1);
- }else{
- fts3PoslistCopy(0, &p2);
- fts3GetDeltaVarint2(&p2, pEnd2, &i2);
- }
- }
- sqlite3_free(aTmp);
- break;
+ fts3PutDeltaVarint3(&p, bDescDoclist, &iPrev, &bFirstOut, i1);
+ if( 0==fts3PoslistPhraseMerge(&p, nDist, 0, 1, &p1, &p2) ){
+ p = pSave;
+ iPrev = iPrevSave;
+ bFirstOut = bFirstOutSave;
+ }
+ fts3GetDeltaVarint3(&p1, pEnd1, bDescDoclist, &i1);
+ fts3GetDeltaVarint3(&p2, pEnd2, bDescDoclist, &i2);
+ }else if( iDiff<0 ){
+ fts3PoslistCopy(0, &p1);
+ fts3GetDeltaVarint3(&p1, pEnd1, bDescDoclist, &i1);
+ }else{
+ fts3PoslistCopy(0, &p2);
+ fts3GetDeltaVarint3(&p2, pEnd2, bDescDoclist, &i2);
}
}
- if( pnDoc ) *pnDoc = nDoc;
- *pnBuffer = (int)(p-aBuffer);
- return SQLITE_OK;
+ *pnRight = p - aOut;
}
-/*
-** A pointer to an instance of this structure is used as the context
-** argument to sqlite3Fts3SegReaderIterate()
-*/
-typedef struct TermSelect TermSelect;
-struct TermSelect {
- int isReqPos;
- char *aaOutput[16]; /* Malloc'd output buffer */
- int anOutput[16]; /* Size of output in bytes */
-};
/*
** Merge all doclists in the TermSelect.aaOutput[] array into a single
** the responsibility of the caller to free any doclists left in the
** TermSelect.aaOutput[] array.
*/
-static int fts3TermSelectMerge(TermSelect *pTS){
- int mergetype = (pTS->isReqPos ? MERGE_POS_OR : MERGE_OR);
+static int fts3TermSelectFinishMerge(Fts3Table *p, TermSelect *pTS){
char *aOut = 0;
int nOut = 0;
int i;
nOut = pTS->anOutput[i];
pTS->aaOutput[i] = 0;
}else{
- int nNew = nOut + pTS->anOutput[i];
- char *aNew = sqlite3_malloc(nNew);
- if( !aNew ){
+ int nNew;
+ char *aNew;
+
+ int rc = fts3DoclistOrMerge(p->bDescIdx,
+ pTS->aaOutput[i], pTS->anOutput[i], aOut, nOut, &aNew, &nNew
+ );
+ if( rc!=SQLITE_OK ){
sqlite3_free(aOut);
- return SQLITE_NOMEM;
+ return rc;
}
- fts3DoclistMerge(mergetype, 0, 0,
- aNew, &nNew, pTS->aaOutput[i], pTS->anOutput[i], aOut, nOut, 0
- );
+
sqlite3_free(pTS->aaOutput[i]);
sqlite3_free(aOut);
pTS->aaOutput[i] = 0;
}
/*
-** This function is used as the sqlite3Fts3SegReaderIterate() callback when
-** querying the full-text index for a doclist associated with a term or
-** term-prefix.
+** Merge the doclist aDoclist/nDoclist into the TermSelect object passed
+** as the first argument. The merge is an "OR" merge (see function
+** fts3DoclistOrMerge() for details).
+**
+** This function is called with the doclist for each term that matches
+** a queried prefix. It merges all these doclists into one, the doclist
+** for the specified prefix. Since there can be a very large number of
+** doclists to merge, the merging is done pair-wise using the TermSelect
+** object.
+**
+** This function returns SQLITE_OK if the merge is successful, or an
+** SQLite error code (SQLITE_NOMEM) if an error occurs.
*/
-static int fts3TermSelectCb(
- Fts3Table *p, /* Virtual table object */
- void *pContext, /* Pointer to TermSelect structure */
- char *zTerm,
- int nTerm,
- char *aDoclist,
- int nDoclist
+static int fts3TermSelectMerge(
+ Fts3Table *p, /* FTS table handle */
+ TermSelect *pTS, /* TermSelect object to merge into */
+ char *aDoclist, /* Pointer to doclist */
+ int nDoclist /* Size of aDoclist in bytes */
){
- TermSelect *pTS = (TermSelect *)pContext;
-
- UNUSED_PARAMETER(p);
- UNUSED_PARAMETER(zTerm);
- UNUSED_PARAMETER(nTerm);
-
if( pTS->aaOutput[0]==0 ){
/* If this is the first term selected, copy the doclist to the output
- ** buffer using memcpy(). TODO: Add a way to transfer control of the
- ** aDoclist buffer from the caller so as to avoid the memcpy().
- */
+ ** buffer using memcpy(). */
pTS->aaOutput[0] = sqlite3_malloc(nDoclist);
pTS->anOutput[0] = nDoclist;
if( pTS->aaOutput[0] ){
return SQLITE_NOMEM;
}
}else{
- int mergetype = (pTS->isReqPos ? MERGE_POS_OR : MERGE_OR);
char *aMerge = aDoclist;
int nMerge = nDoclist;
int iOut;
for(iOut=0; iOut<SizeofArray(pTS->aaOutput); iOut++){
- char *aNew;
- int nNew;
if( pTS->aaOutput[iOut]==0 ){
assert( iOut>0 );
pTS->aaOutput[iOut] = aMerge;
pTS->anOutput[iOut] = nMerge;
break;
- }
+ }else{
+ char *aNew;
+ int nNew;
- nNew = nMerge + pTS->anOutput[iOut];
- aNew = sqlite3_malloc(nNew);
- if( !aNew ){
- if( aMerge!=aDoclist ){
- sqlite3_free(aMerge);
+ int rc = fts3DoclistOrMerge(p->bDescIdx, aMerge, nMerge,
+ pTS->aaOutput[iOut], pTS->anOutput[iOut], &aNew, &nNew
+ );
+ if( rc!=SQLITE_OK ){
+ if( aMerge!=aDoclist ) sqlite3_free(aMerge);
+ return rc;
}
- return SQLITE_NOMEM;
- }
- fts3DoclistMerge(mergetype, 0, 0, aNew, &nNew,
- pTS->aaOutput[iOut], pTS->anOutput[iOut], aMerge, nMerge, 0
- );
-
- if( iOut>0 ) sqlite3_free(aMerge);
- sqlite3_free(pTS->aaOutput[iOut]);
- pTS->aaOutput[iOut] = 0;
- aMerge = aNew;
- nMerge = nNew;
- if( (iOut+1)==SizeofArray(pTS->aaOutput) ){
- pTS->aaOutput[iOut] = aMerge;
- pTS->anOutput[iOut] = nMerge;
+ if( aMerge!=aDoclist ) sqlite3_free(aMerge);
+ sqlite3_free(pTS->aaOutput[iOut]);
+ pTS->aaOutput[iOut] = 0;
+
+ aMerge = aNew;
+ nMerge = nNew;
+ if( (iOut+1)==SizeofArray(pTS->aaOutput) ){
+ pTS->aaOutput[iOut] = aMerge;
+ pTS->anOutput[iOut] = nMerge;
+ }
}
}
}
return SQLITE_OK;
}
-static int fts3DeferredTermSelect(
- Fts3DeferredToken *pToken, /* Phrase token */
- int isTermPos, /* True to include positions */
- int *pnOut, /* OUT: Size of list */
- char **ppOut /* OUT: Body of list */
-){
- char *aSource;
- int nSource;
-
- aSource = sqlite3Fts3DeferredDoclist(pToken, &nSource);
- if( !aSource ){
- *pnOut = 0;
- *ppOut = 0;
- }else if( isTermPos ){
- *ppOut = sqlite3_malloc(nSource);
- if( !*ppOut ) return SQLITE_NOMEM;
- memcpy(*ppOut, aSource, nSource);
- *pnOut = nSource;
- }else{
- sqlite3_int64 docid;
- *pnOut = sqlite3Fts3GetVarint(aSource, &docid);
- *ppOut = sqlite3_malloc(*pnOut);
- if( !*ppOut ) return SQLITE_NOMEM;
- sqlite3Fts3PutVarint(*ppOut, docid);
+/*
+** Append SegReader object pNew to the end of the pCsr->apSegment[] array.
+*/
+static int fts3SegReaderCursorAppend(
+ Fts3MultiSegReader *pCsr,
+ Fts3SegReader *pNew
+){
+ if( (pCsr->nSegment%16)==0 ){
+ Fts3SegReader **apNew;
+ int nByte = (pCsr->nSegment + 16)*sizeof(Fts3SegReader*);
+ apNew = (Fts3SegReader **)sqlite3_realloc(pCsr->apSegment, nByte);
+ if( !apNew ){
+ sqlite3Fts3SegReaderFree(pNew);
+ return SQLITE_NOMEM;
+ }
+ pCsr->apSegment = apNew;
}
-
+ pCsr->apSegment[pCsr->nSegment++] = pNew;
return SQLITE_OK;
}
-SQLITE_PRIVATE int sqlite3Fts3SegReaderCursor(
+/*
+** Add seg-reader objects to the Fts3MultiSegReader object passed as the
+** 8th argument.
+**
+** This function returns SQLITE_OK if successful, or an SQLite error code
+** otherwise.
+*/
+static int fts3SegReaderCursor(
Fts3Table *p, /* FTS3 table handle */
+ int iIndex, /* Index to search (from 0 to p->nIndex-1) */
int iLevel, /* Level of segments to scan */
const char *zTerm, /* Term to query for */
int nTerm, /* Size of zTerm in bytes */
int isPrefix, /* True for a prefix search */
int isScan, /* True to scan from zTerm to EOF */
- Fts3SegReaderCursor *pCsr /* Cursor object to populate */
+ Fts3MultiSegReader *pCsr /* Cursor object to populate */
){
- int rc = SQLITE_OK;
- int rc2;
- int iAge = 0;
- sqlite3_stmt *pStmt = 0;
- Fts3SegReader *pPending = 0;
-
- assert( iLevel==FTS3_SEGCURSOR_ALL
- || iLevel==FTS3_SEGCURSOR_PENDING
- || iLevel>=0
- );
- assert( FTS3_SEGCURSOR_PENDING<0 );
- assert( FTS3_SEGCURSOR_ALL<0 );
- assert( iLevel==FTS3_SEGCURSOR_ALL || (zTerm==0 && isPrefix==1) );
- assert( isPrefix==0 || isScan==0 );
-
+ int rc = SQLITE_OK; /* Error code */
+ sqlite3_stmt *pStmt = 0; /* Statement to iterate through segments */
+ int rc2; /* Result of sqlite3_reset() */
- memset(pCsr, 0, sizeof(Fts3SegReaderCursor));
-
- /* If iLevel is less than 0, include a seg-reader for the pending-terms. */
- assert( isScan==0 || fts3HashCount(&p->pendingTerms)==0 );
- if( iLevel<0 && isScan==0 ){
- rc = sqlite3Fts3SegReaderPending(p, zTerm, nTerm, isPrefix, &pPending);
- if( rc==SQLITE_OK && pPending ){
- int nByte = (sizeof(Fts3SegReader *) * 16);
- pCsr->apSegment = (Fts3SegReader **)sqlite3_malloc(nByte);
- if( pCsr->apSegment==0 ){
- rc = SQLITE_NOMEM;
- }else{
- pCsr->apSegment[0] = pPending;
- pCsr->nSegment = 1;
- pPending = 0;
- }
+ /* If iLevel is less than 0 and this is not a scan, include a seg-reader
+ ** for the pending-terms. If this is a scan, then this call must be being
+ ** made by an fts4aux module, not an FTS table. In this case calling
+ ** Fts3SegReaderPending might segfault, as the data structures used by
+ ** fts4aux are not completely populated. So it's easiest to filter these
+ ** calls out here. */
+ if( iLevel<0 && p->aIndex ){
+ Fts3SegReader *pSeg = 0;
+ rc = sqlite3Fts3SegReaderPending(p, iIndex, zTerm, nTerm, isPrefix, &pSeg);
+ if( rc==SQLITE_OK && pSeg ){
+ rc = fts3SegReaderCursorAppend(pCsr, pSeg);
}
}
if( iLevel!=FTS3_SEGCURSOR_PENDING ){
if( rc==SQLITE_OK ){
- rc = sqlite3Fts3AllSegdirs(p, iLevel, &pStmt);
+ rc = sqlite3Fts3AllSegdirs(p, iIndex, iLevel, &pStmt);
}
+
while( rc==SQLITE_OK && SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){
+ Fts3SegReader *pSeg = 0;
/* Read the values returned by the SELECT into local variables. */
sqlite3_int64 iStartBlock = sqlite3_column_int64(pStmt, 1);
int nRoot = sqlite3_column_bytes(pStmt, 4);
char const *zRoot = sqlite3_column_blob(pStmt, 4);
- /* If nSegment is a multiple of 16 the array needs to be extended. */
- if( (pCsr->nSegment%16)==0 ){
- Fts3SegReader **apNew;
- int nByte = (pCsr->nSegment + 16)*sizeof(Fts3SegReader*);
- apNew = (Fts3SegReader **)sqlite3_realloc(pCsr->apSegment, nByte);
- if( !apNew ){
- rc = SQLITE_NOMEM;
- goto finished;
- }
- pCsr->apSegment = apNew;
- }
-
/* If zTerm is not NULL, and this segment is not stored entirely on its
** root node, the range of leaves scanned can be reduced. Do this. */
if( iStartBlock && zTerm ){
if( isPrefix==0 && isScan==0 ) iLeavesEndBlock = iStartBlock;
}
- rc = sqlite3Fts3SegReaderNew(iAge, iStartBlock, iLeavesEndBlock,
- iEndBlock, zRoot, nRoot, &pCsr->apSegment[pCsr->nSegment]
+ rc = sqlite3Fts3SegReaderNew(pCsr->nSegment+1,
+ iStartBlock, iLeavesEndBlock, iEndBlock, zRoot, nRoot, &pSeg
);
if( rc!=SQLITE_OK ) goto finished;
- pCsr->nSegment++;
- iAge++;
+ rc = fts3SegReaderCursorAppend(pCsr, pSeg);
}
}
finished:
rc2 = sqlite3_reset(pStmt);
if( rc==SQLITE_DONE ) rc = rc2;
- sqlite3Fts3SegReaderFree(pPending);
return rc;
}
+/*
+** Set up a cursor object for iterating through a full-text index or a
+** single level therein.
+*/
+SQLITE_PRIVATE int sqlite3Fts3SegReaderCursor(
+ Fts3Table *p, /* FTS3 table handle */
+ int iIndex, /* Index to search (from 0 to p->nIndex-1) */
+ int iLevel, /* Level of segments to scan */
+ const char *zTerm, /* Term to query for */
+ int nTerm, /* Size of zTerm in bytes */
+ int isPrefix, /* True for a prefix search */
+ int isScan, /* True to scan from zTerm to EOF */
+ Fts3MultiSegReader *pCsr /* Cursor object to populate */
+){
+ assert( iIndex>=0 && iIndex<p->nIndex );
+ assert( iLevel==FTS3_SEGCURSOR_ALL
+ || iLevel==FTS3_SEGCURSOR_PENDING
+ || iLevel>=0
+ );
+ assert( iLevel<FTS3_SEGDIR_MAXLEVEL );
+ assert( FTS3_SEGCURSOR_ALL<0 && FTS3_SEGCURSOR_PENDING<0 );
+ assert( isPrefix==0 || isScan==0 );
+
+ /* "isScan" is only set to true by the ft4aux module, an ordinary
+ ** full-text tables. */
+ assert( isScan==0 || p->aIndex==0 );
+
+ memset(pCsr, 0, sizeof(Fts3MultiSegReader));
+
+ return fts3SegReaderCursor(
+ p, iIndex, iLevel, zTerm, nTerm, isPrefix, isScan, pCsr
+ );
+}
+
+/*
+** In addition to its current configuration, have the Fts3MultiSegReader
+** passed as the 4th argument also scan the doclist for term zTerm/nTerm.
+**
+** SQLITE_OK is returned if no error occurs, otherwise an SQLite error code.
+*/
+static int fts3SegReaderCursorAddZero(
+ Fts3Table *p, /* FTS virtual table handle */
+ const char *zTerm, /* Term to scan doclist of */
+ int nTerm, /* Number of bytes in zTerm */
+ Fts3MultiSegReader *pCsr /* Fts3MultiSegReader to modify */
+){
+ return fts3SegReaderCursor(p, 0, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 0, 0,pCsr);
+}
+/*
+** Open an Fts3MultiSegReader to scan the doclist for term zTerm/nTerm. Or,
+** if isPrefix is true, to scan the doclist for all terms for which
+** zTerm/nTerm is a prefix. If successful, return SQLITE_OK and write
+** a pointer to the new Fts3MultiSegReader to *ppSegcsr. Otherwise, return
+** an SQLite error code.
+**
+** It is the responsibility of the caller to free this object by eventually
+** passing it to fts3SegReaderCursorFree()
+**
+** SQLITE_OK is returned if no error occurs, otherwise an SQLite error code.
+** Output parameter *ppSegcsr is set to 0 if an error occurs.
+*/
static int fts3TermSegReaderCursor(
Fts3Cursor *pCsr, /* Virtual table cursor handle */
const char *zTerm, /* Term to query for */
int nTerm, /* Size of zTerm in bytes */
int isPrefix, /* True for a prefix search */
- Fts3SegReaderCursor **ppSegcsr /* OUT: Allocated seg-reader cursor */
+ Fts3MultiSegReader **ppSegcsr /* OUT: Allocated seg-reader cursor */
){
- Fts3SegReaderCursor *pSegcsr; /* Object to allocate and return */
+ Fts3MultiSegReader *pSegcsr; /* Object to allocate and return */
int rc = SQLITE_NOMEM; /* Return code */
- pSegcsr = sqlite3_malloc(sizeof(Fts3SegReaderCursor));
+ pSegcsr = sqlite3_malloc(sizeof(Fts3MultiSegReader));
if( pSegcsr ){
- Fts3Table *p = (Fts3Table *)pCsr->base.pVtab;
int i;
- int nCost = 0;
- rc = sqlite3Fts3SegReaderCursor(
- p, FTS3_SEGCURSOR_ALL, zTerm, nTerm, isPrefix, 0, pSegcsr);
-
- for(i=0; rc==SQLITE_OK && i<pSegcsr->nSegment; i++){
- rc = sqlite3Fts3SegReaderCost(pCsr, pSegcsr->apSegment[i], &nCost);
+ int bFound = 0; /* True once an index has been found */
+ Fts3Table *p = (Fts3Table *)pCsr->base.pVtab;
+
+ if( isPrefix ){
+ for(i=1; bFound==0 && i<p->nIndex; i++){
+ if( p->aIndex[i].nPrefix==nTerm ){
+ bFound = 1;
+ rc = sqlite3Fts3SegReaderCursor(
+ p, i, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 0, 0, pSegcsr);
+ pSegcsr->bLookup = 1;
+ }
+ }
+
+ for(i=1; bFound==0 && i<p->nIndex; i++){
+ if( p->aIndex[i].nPrefix==nTerm+1 ){
+ bFound = 1;
+ rc = sqlite3Fts3SegReaderCursor(
+ p, i, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 1, 0, pSegcsr
+ );
+ if( rc==SQLITE_OK ){
+ rc = fts3SegReaderCursorAddZero(p, zTerm, nTerm, pSegcsr);
+ }
+ }
+ }
+ }
+
+ if( bFound==0 ){
+ rc = sqlite3Fts3SegReaderCursor(
+ p, 0, FTS3_SEGCURSOR_ALL, zTerm, nTerm, isPrefix, 0, pSegcsr
+ );
+ pSegcsr->bLookup = !isPrefix;
}
- pSegcsr->nCost = nCost;
}
*ppSegcsr = pSegcsr;
return rc;
}
-static void fts3SegReaderCursorFree(Fts3SegReaderCursor *pSegcsr){
+/*
+** Free an Fts3MultiSegReader allocated by fts3TermSegReaderCursor().
+*/
+static void fts3SegReaderCursorFree(Fts3MultiSegReader *pSegcsr){
sqlite3Fts3SegReaderFinish(pSegcsr);
sqlite3_free(pSegcsr);
}
/*
** This function retreives the doclist for the specified term (or term
-** prefix) from the database.
-**
-** The returned doclist may be in one of two formats, depending on the
-** value of parameter isReqPos. If isReqPos is zero, then the doclist is
-** a sorted list of delta-compressed docids (a bare doclist). If isReqPos
-** is non-zero, then the returned list is in the same format as is stored
-** in the database without the found length specifier at the start of on-disk
-** doclists.
+** prefix) from the database.
*/
static int fts3TermSelect(
Fts3Table *p, /* Virtual table handle */
Fts3PhraseToken *pTok, /* Token to query for */
int iColumn, /* Column to query (or -ve for all columns) */
- int isReqPos, /* True to include position lists in output */
int *pnOut, /* OUT: Size of buffer at *ppOut */
char **ppOut /* OUT: Malloced result buffer */
){
int rc; /* Return code */
- Fts3SegReaderCursor *pSegcsr; /* Seg-reader cursor for this term */
- TermSelect tsc; /* Context object for fts3TermSelectCb() */
+ Fts3MultiSegReader *pSegcsr; /* Seg-reader cursor for this term */
+ TermSelect tsc; /* Object for pair-wise doclist merging */
Fts3SegFilter filter; /* Segment term filter configuration */
pSegcsr = pTok->pSegcsr;
memset(&tsc, 0, sizeof(TermSelect));
- tsc.isReqPos = isReqPos;
- filter.flags = FTS3_SEGMENT_IGNORE_EMPTY
+ filter.flags = FTS3_SEGMENT_IGNORE_EMPTY | FTS3_SEGMENT_REQUIRE_POS
| (pTok->isPrefix ? FTS3_SEGMENT_PREFIX : 0)
- | (isReqPos ? FTS3_SEGMENT_REQUIRE_POS : 0)
| (iColumn<p->nColumn ? FTS3_SEGMENT_COLUMN_FILTER : 0);
filter.iCol = iColumn;
filter.zTerm = pTok->z;
while( SQLITE_OK==rc
&& SQLITE_ROW==(rc = sqlite3Fts3SegReaderStep(p, pSegcsr))
){
- rc = fts3TermSelectCb(p, (void *)&tsc,
- pSegcsr->zTerm, pSegcsr->nTerm, pSegcsr->aDoclist, pSegcsr->nDoclist
- );
+ rc = fts3TermSelectMerge(p, &tsc, pSegcsr->aDoclist, pSegcsr->nDoclist);
}
if( rc==SQLITE_OK ){
- rc = fts3TermSelectMerge(&tsc);
+ rc = fts3TermSelectFinishMerge(p, &tsc);
}
if( rc==SQLITE_OK ){
*ppOut = tsc.aaOutput[0];
** that the doclist is simply a list of docids stored as delta encoded
** varints.
*/
-static int fts3DoclistCountDocids(int isPoslist, char *aList, int nList){
+static int fts3DoclistCountDocids(char *aList, int nList){
int nDoc = 0; /* Return value */
if( aList ){
char *aEnd = &aList[nList]; /* Pointer to one byte after EOF */
char *p = aList; /* Cursor */
- if( !isPoslist ){
- /* The number of docids in the list is the same as the number of
- ** varints. In FTS3 a varint consists of a single byte with the 0x80
- ** bit cleared and zero or more bytes with the 0x80 bit set. So to
- ** count the varints in the buffer, just count the number of bytes
- ** with the 0x80 bit clear. */
- while( p<aEnd ) nDoc += (((*p++)&0x80)==0);
- }else{
- while( p<aEnd ){
- nDoc++;
- while( (*p++)&0x80 ); /* Skip docid varint */
- fts3PoslistCopy(0, &p); /* Skip over position list */
- }
+ while( p<aEnd ){
+ nDoc++;
+ while( (*p++)&0x80 ); /* Skip docid varint */
+ fts3PoslistCopy(0, &p); /* Skip over position list */
}
}
}
/*
-** Call sqlite3Fts3DeferToken() for each token in the expression pExpr.
+** Advance the cursor to the next row in the %_content table that
+** matches the search criteria. For a MATCH search, this will be
+** the next row that matches. For a full-table scan, this will be
+** simply the next row in the %_content table. For a docid lookup,
+** this routine simply sets the EOF flag.
+**
+** Return SQLITE_OK if nothing goes wrong. SQLITE_OK is returned
+** even if we reach end-of-file. The fts3EofMethod() will be called
+** subsequently to determine whether or not an EOF was hit.
*/
-static int fts3DeferExpression(Fts3Cursor *pCsr, Fts3Expr *pExpr){
- int rc = SQLITE_OK;
- if( pExpr ){
- rc = fts3DeferExpression(pCsr, pExpr->pLeft);
- if( rc==SQLITE_OK ){
- rc = fts3DeferExpression(pCsr, pExpr->pRight);
- }
- if( pExpr->eType==FTSQUERY_PHRASE ){
- int iCol = pExpr->pPhrase->iColumn;
- int i;
- for(i=0; rc==SQLITE_OK && i<pExpr->pPhrase->nToken; i++){
- Fts3PhraseToken *pToken = &pExpr->pPhrase->aToken[i];
- if( pToken->pDeferred==0 ){
- rc = sqlite3Fts3DeferToken(pCsr, pToken, iCol);
- }
- }
+static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){
+ int rc;
+ Fts3Cursor *pCsr = (Fts3Cursor *)pCursor;
+ if( pCsr->eSearch==FTS3_DOCID_SEARCH || pCsr->eSearch==FTS3_FULLSCAN_SEARCH ){
+ if( SQLITE_ROW!=sqlite3_step(pCsr->pStmt) ){
+ pCsr->isEof = 1;
+ rc = sqlite3_reset(pCsr->pStmt);
+ }else{
+ pCsr->iPrevId = sqlite3_column_int64(pCsr->pStmt, 0);
+ rc = SQLITE_OK;
}
+ }else{
+ rc = fts3EvalNext((Fts3Cursor *)pCursor);
}
+ assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 );
return rc;
}
/*
-** This function removes the position information from a doclist. When
-** called, buffer aList (size *pnList bytes) contains a doclist that includes
-** position information. This function removes the position information so
-** that aList contains only docids, and adjusts *pnList to reflect the new
-** (possibly reduced) size of the doclist.
-*/
-static void fts3DoclistStripPositions(
- char *aList, /* IN/OUT: Buffer containing doclist */
- int *pnList /* IN/OUT: Size of doclist in bytes */
-){
- if( aList ){
- char *aEnd = &aList[*pnList]; /* Pointer to one byte after EOF */
- char *p = aList; /* Input cursor */
- char *pOut = aList; /* Output cursor */
-
- while( p<aEnd ){
- sqlite3_int64 delta;
- p += sqlite3Fts3GetVarint(p, &delta);
- fts3PoslistCopy(0, &p);
- pOut += sqlite3Fts3PutVarint(pOut, delta);
- }
-
- *pnList = (int)(pOut - aList);
- }
-}
-
-/*
-** Return a DocList corresponding to the phrase *pPhrase.
+** This is the xFilter interface for the virtual table. See
+** the virtual table xFilter method documentation for additional
+** information.
+**
+** If idxNum==FTS3_FULLSCAN_SEARCH then do a full table scan against
+** the %_content table.
+**
+** If idxNum==FTS3_DOCID_SEARCH then do a docid lookup for a single entry
+** in the %_content table.
**
-** If this function returns SQLITE_OK, but *pnOut is set to a negative value,
-** then no tokens in the phrase were looked up in the full-text index. This
-** is only possible when this function is called from within xFilter(). The
-** caller should assume that all documents match the phrase. The actual
-** filtering will take place in xNext().
+** If idxNum>=FTS3_FULLTEXT_SEARCH then use the full text index. The
+** column on the left-hand side of the MATCH operator is column
+** number idxNum-FTS3_FULLTEXT_SEARCH, 0 indexed. argv[0] is the right-hand
+** side of the MATCH operator.
*/
-static int fts3PhraseSelect(
- Fts3Cursor *pCsr, /* Virtual table cursor handle */
- Fts3Phrase *pPhrase, /* Phrase to return a doclist for */
- int isReqPos, /* True if output should contain positions */
- char **paOut, /* OUT: Pointer to malloc'd result buffer */
- int *pnOut /* OUT: Size of buffer at *paOut */
+static int fts3FilterMethod(
+ sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */
+ int idxNum, /* Strategy index */
+ const char *idxStr, /* Unused */
+ int nVal, /* Number of elements in apVal */
+ sqlite3_value **apVal /* Arguments for the indexing scheme */
){
- char *pOut = 0;
- int nOut = 0;
- int rc = SQLITE_OK;
- int ii;
- int iCol = pPhrase->iColumn;
- int isTermPos = (pPhrase->nToken>1 || isReqPos);
- Fts3Table *p = (Fts3Table *)pCsr->base.pVtab;
- int isFirst = 1;
-
- int iPrevTok = 0;
- int nDoc = 0;
-
- /* If this is an xFilter() evaluation, create a segment-reader for each
- ** phrase token. Or, if this is an xNext() or snippet/offsets/matchinfo
- ** evaluation, only create segment-readers if there are no Fts3DeferredToken
- ** objects attached to the phrase-tokens.
- */
- for(ii=0; ii<pPhrase->nToken; ii++){
- Fts3PhraseToken *pTok = &pPhrase->aToken[ii];
- if( pTok->pSegcsr==0 ){
- if( (pCsr->eEvalmode==FTS3_EVAL_FILTER)
- || (pCsr->eEvalmode==FTS3_EVAL_NEXT && pCsr->pDeferred==0)
- || (pCsr->eEvalmode==FTS3_EVAL_MATCHINFO && pTok->bFulltext)
- ){
- rc = fts3TermSegReaderCursor(
- pCsr, pTok->z, pTok->n, pTok->isPrefix, &pTok->pSegcsr
- );
- if( rc!=SQLITE_OK ) return rc;
- }
- }
- }
-
- for(ii=0; ii<pPhrase->nToken; ii++){
- Fts3PhraseToken *pTok; /* Token to find doclist for */
- int iTok = 0; /* The token being queried this iteration */
- char *pList = 0; /* Pointer to token doclist */
- int nList = 0; /* Size of buffer at pList */
-
- /* Select a token to process. If this is an xFilter() call, then tokens
- ** are processed in order from least to most costly. Otherwise, tokens
- ** are processed in the order in which they occur in the phrase.
- */
- if( pCsr->eEvalmode==FTS3_EVAL_MATCHINFO ){
- assert( isReqPos );
- iTok = ii;
- pTok = &pPhrase->aToken[iTok];
- if( pTok->bFulltext==0 ) continue;
- }else if( pCsr->eEvalmode==FTS3_EVAL_NEXT || isReqPos ){
- iTok = ii;
- pTok = &pPhrase->aToken[iTok];
- }else{
- int nMinCost = 0x7FFFFFFF;
- int jj;
-
- /* Find the remaining token with the lowest cost. */
- for(jj=0; jj<pPhrase->nToken; jj++){
- Fts3SegReaderCursor *pSegcsr = pPhrase->aToken[jj].pSegcsr;
- if( pSegcsr && pSegcsr->nCost<nMinCost ){
- iTok = jj;
- nMinCost = pSegcsr->nCost;
- }
- }
- pTok = &pPhrase->aToken[iTok];
+ int rc;
+ char *zSql; /* SQL statement used to access %_content */
+ Fts3Table *p = (Fts3Table *)pCursor->pVtab;
+ Fts3Cursor *pCsr = (Fts3Cursor *)pCursor;
- /* This branch is taken if it is determined that loading the doclist
- ** for the next token would require more IO than loading all documents
- ** currently identified by doclist pOut/nOut. No further doclists will
- ** be loaded from the full-text index for this phrase.
- */
- if( nMinCost>nDoc && ii>0 ){
- rc = fts3DeferExpression(pCsr, pCsr->pExpr);
- break;
- }
- }
+ UNUSED_PARAMETER(idxStr);
+ UNUSED_PARAMETER(nVal);
- if( pCsr->eEvalmode==FTS3_EVAL_NEXT && pTok->pDeferred ){
- rc = fts3DeferredTermSelect(pTok->pDeferred, isTermPos, &nList, &pList);
- }else{
- if( pTok->pSegcsr ){
- rc = fts3TermSelect(p, pTok, iCol, isTermPos, &nList, &pList);
- }
- pTok->bFulltext = 1;
- }
- assert( rc!=SQLITE_OK || pCsr->eEvalmode || pTok->pSegcsr==0 );
- if( rc!=SQLITE_OK ) break;
+ assert( idxNum>=0 && idxNum<=(FTS3_FULLTEXT_SEARCH+p->nColumn) );
+ assert( nVal==0 || nVal==1 );
+ assert( (nVal==0)==(idxNum==FTS3_FULLSCAN_SEARCH) );
+ assert( p->pSegments==0 );
- if( isFirst ){
- pOut = pList;
- nOut = nList;
- if( pCsr->eEvalmode==FTS3_EVAL_FILTER && pPhrase->nToken>1 ){
- nDoc = fts3DoclistCountDocids(1, pOut, nOut);
- }
- isFirst = 0;
- iPrevTok = iTok;
- }else{
- /* Merge the new term list and the current output. */
- char *aLeft, *aRight;
- int nLeft, nRight;
- int nDist;
- int mt;
-
- /* If this is the final token of the phrase, and positions were not
- ** requested by the caller, use MERGE_PHRASE instead of POS_PHRASE.
- ** This drops the position information from the output list.
- */
- mt = MERGE_POS_PHRASE;
- if( ii==pPhrase->nToken-1 && !isReqPos ) mt = MERGE_PHRASE;
-
- assert( iPrevTok!=iTok );
- if( iPrevTok<iTok ){
- aLeft = pOut;
- nLeft = nOut;
- aRight = pList;
- nRight = nList;
- nDist = iTok-iPrevTok;
- iPrevTok = iTok;
- }else{
- aRight = pOut;
- nRight = nOut;
- aLeft = pList;
- nLeft = nList;
- nDist = iPrevTok-iTok;
- }
- pOut = aRight;
- fts3DoclistMerge(
- mt, nDist, 0, pOut, &nOut, aLeft, nLeft, aRight, nRight, &nDoc
- );
- sqlite3_free(aLeft);
- }
- assert( nOut==0 || pOut!=0 );
- }
+ /* In case the cursor has been used before, clear it now. */
+ sqlite3_finalize(pCsr->pStmt);
+ sqlite3_free(pCsr->aDoclist);
+ sqlite3Fts3ExprFree(pCsr->pExpr);
+ memset(&pCursor[1], 0, sizeof(Fts3Cursor)-sizeof(sqlite3_vtab_cursor));
- if( rc==SQLITE_OK ){
- if( ii!=pPhrase->nToken ){
- assert( pCsr->eEvalmode==FTS3_EVAL_FILTER && isReqPos==0 );
- fts3DoclistStripPositions(pOut, &nOut);
- }
- *paOut = pOut;
- *pnOut = nOut;
+ if( idxStr ){
+ pCsr->bDesc = (idxStr[0]=='D');
}else{
- sqlite3_free(pOut);
+ pCsr->bDesc = p->bDescIdx;
}
- return rc;
-}
+ pCsr->eSearch = (i16)idxNum;
-/*
-** This function merges two doclists according to the requirements of a
-** NEAR operator.
-**
-** Both input doclists must include position information. The output doclist
-** includes position information if the first argument to this function
-** is MERGE_POS_NEAR, or does not if it is MERGE_NEAR.
-*/
-static int fts3NearMerge(
- int mergetype, /* MERGE_POS_NEAR or MERGE_NEAR */
- int nNear, /* Parameter to NEAR operator */
- int nTokenLeft, /* Number of tokens in LHS phrase arg */
- char *aLeft, /* Doclist for LHS (incl. positions) */
- int nLeft, /* Size of LHS doclist in bytes */
- int nTokenRight, /* As nTokenLeft */
- char *aRight, /* As aLeft */
- int nRight, /* As nRight */
- char **paOut, /* OUT: Results of merge (malloced) */
- int *pnOut /* OUT: Sized of output buffer */
-){
- char *aOut; /* Buffer to write output doclist to */
- int rc; /* Return code */
+ if( idxNum!=FTS3_DOCID_SEARCH && idxNum!=FTS3_FULLSCAN_SEARCH ){
+ int iCol = idxNum-FTS3_FULLTEXT_SEARCH;
+ const char *zQuery = (const char *)sqlite3_value_text(apVal[0]);
- assert( mergetype==MERGE_POS_NEAR || MERGE_NEAR );
+ if( zQuery==0 && sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
+ return SQLITE_NOMEM;
+ }
- aOut = sqlite3_malloc(nLeft+nRight+1);
- if( aOut==0 ){
- rc = SQLITE_NOMEM;
- }else{
- rc = fts3DoclistMerge(mergetype, nNear+nTokenRight, nNear+nTokenLeft,
- aOut, pnOut, aLeft, nLeft, aRight, nRight, 0
+ rc = sqlite3Fts3ExprParse(p->pTokenizer, p->azColumn, p->nColumn,
+ iCol, zQuery, -1, &pCsr->pExpr
);
if( rc!=SQLITE_OK ){
- sqlite3_free(aOut);
- aOut = 0;
+ if( rc==SQLITE_ERROR ){
+ static const char *zErr = "malformed MATCH expression: [%s]";
+ p->base.zErrMsg = sqlite3_mprintf(zErr, zQuery);
+ }
+ return rc;
}
- }
- *paOut = aOut;
- return rc;
-}
+ rc = sqlite3Fts3ReadLock(p);
+ if( rc!=SQLITE_OK ) return rc;
-/*
-** This function is used as part of the processing for the snippet() and
-** offsets() functions.
-**
-** Both pLeft and pRight are expression nodes of type FTSQUERY_PHRASE. Both
-** have their respective doclists (including position information) loaded
-** in Fts3Expr.aDoclist/nDoclist. This function removes all entries from
-** each doclist that are not within nNear tokens of a corresponding entry
-** in the other doclist.
-*/
-SQLITE_PRIVATE int sqlite3Fts3ExprNearTrim(Fts3Expr *pLeft, Fts3Expr *pRight, int nNear){
- int rc; /* Return code */
+ rc = fts3EvalStart(pCsr);
- assert( pLeft->eType==FTSQUERY_PHRASE );
- assert( pRight->eType==FTSQUERY_PHRASE );
- assert( pLeft->isLoaded && pRight->isLoaded );
+ sqlite3Fts3SegmentsClose(p);
+ if( rc!=SQLITE_OK ) return rc;
+ pCsr->pNextId = pCsr->aDoclist;
+ pCsr->iPrevId = 0;
+ }
- if( pLeft->aDoclist==0 || pRight->aDoclist==0 ){
- sqlite3_free(pLeft->aDoclist);
- sqlite3_free(pRight->aDoclist);
- pRight->aDoclist = 0;
- pLeft->aDoclist = 0;
- rc = SQLITE_OK;
+ /* Compile a SELECT statement for this cursor. For a full-table-scan, the
+ ** statement loops through all rows of the %_content table. For a
+ ** full-text query or docid lookup, the statement retrieves a single
+ ** row by docid.
+ */
+ if( idxNum==FTS3_FULLSCAN_SEARCH ){
+ const char *zSort = (pCsr->bDesc ? "DESC" : "ASC");
+ const char *zTmpl = "SELECT %s FROM %Q.'%q_content' AS x ORDER BY docid %s";
+ zSql = sqlite3_mprintf(zTmpl, p->zReadExprlist, p->zDb, p->zName, zSort);
}else{
- char *aOut; /* Buffer in which to assemble new doclist */
- int nOut; /* Size of buffer aOut in bytes */
+ const char *zTmpl = "SELECT %s FROM %Q.'%q_content' AS x WHERE docid = ?";
+ zSql = sqlite3_mprintf(zTmpl, p->zReadExprlist, p->zDb, p->zName);
+ }
+ if( !zSql ) return SQLITE_NOMEM;
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0);
+ sqlite3_free(zSql);
+ if( rc!=SQLITE_OK ) return rc;
- rc = fts3NearMerge(MERGE_POS_NEAR, nNear,
- pLeft->pPhrase->nToken, pLeft->aDoclist, pLeft->nDoclist,
- pRight->pPhrase->nToken, pRight->aDoclist, pRight->nDoclist,
- &aOut, &nOut
- );
+ if( idxNum==FTS3_DOCID_SEARCH ){
+ rc = sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]);
if( rc!=SQLITE_OK ) return rc;
- sqlite3_free(pRight->aDoclist);
- pRight->aDoclist = aOut;
- pRight->nDoclist = nOut;
-
- rc = fts3NearMerge(MERGE_POS_NEAR, nNear,
- pRight->pPhrase->nToken, pRight->aDoclist, pRight->nDoclist,
- pLeft->pPhrase->nToken, pLeft->aDoclist, pLeft->nDoclist,
- &aOut, &nOut
- );
- sqlite3_free(pLeft->aDoclist);
- pLeft->aDoclist = aOut;
- pLeft->nDoclist = nOut;
}
- return rc;
+
+ return fts3NextMethod(pCursor);
}
+/*
+** This is the xEof method of the virtual table. SQLite calls this
+** routine to find out if it has reached the end of a result set.
+*/
+static int fts3EofMethod(sqlite3_vtab_cursor *pCursor){
+ return ((Fts3Cursor *)pCursor)->isEof;
+}
-/*
-** Allocate an Fts3SegReaderArray for each token in the expression pExpr.
-** The allocated objects are stored in the Fts3PhraseToken.pArray member
-** variables of each token structure.
+/*
+** This is the xRowid method. The SQLite core calls this routine to
+** retrieve the rowid for the current row of the result set. fts3
+** exposes %_content.docid as the rowid for the virtual table. The
+** rowid should be written to *pRowid.
*/
-static int fts3ExprAllocateSegReaders(
- Fts3Cursor *pCsr, /* FTS3 table */
- Fts3Expr *pExpr, /* Expression to create seg-readers for */
- int *pnExpr /* OUT: Number of AND'd expressions */
-){
- int rc = SQLITE_OK; /* Return code */
+static int fts3RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
+ Fts3Cursor *pCsr = (Fts3Cursor *) pCursor;
+ *pRowid = pCsr->iPrevId;
+ return SQLITE_OK;
+}
- assert( pCsr->eEvalmode==FTS3_EVAL_FILTER );
- if( pnExpr && pExpr->eType!=FTSQUERY_AND ){
- (*pnExpr)++;
- pnExpr = 0;
- }
+/*
+** This is the xColumn method, called by SQLite to request a value from
+** the row that the supplied cursor currently points to.
+*/
+static int fts3ColumnMethod(
+ sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
+ sqlite3_context *pContext, /* Context for sqlite3_result_xxx() calls */
+ int iCol /* Index of column to read value from */
+){
+ int rc = SQLITE_OK; /* Return Code */
+ Fts3Cursor *pCsr = (Fts3Cursor *) pCursor;
+ Fts3Table *p = (Fts3Table *)pCursor->pVtab;
- if( pExpr->eType==FTSQUERY_PHRASE ){
- Fts3Phrase *pPhrase = pExpr->pPhrase;
- int ii;
+ /* The column value supplied by SQLite must be in range. */
+ assert( iCol>=0 && iCol<=p->nColumn+1 );
- for(ii=0; rc==SQLITE_OK && ii<pPhrase->nToken; ii++){
- Fts3PhraseToken *pTok = &pPhrase->aToken[ii];
- if( pTok->pSegcsr==0 ){
- rc = fts3TermSegReaderCursor(
- pCsr, pTok->z, pTok->n, pTok->isPrefix, &pTok->pSegcsr
- );
- }
- }
- }else{
- rc = fts3ExprAllocateSegReaders(pCsr, pExpr->pLeft, pnExpr);
+ if( iCol==p->nColumn+1 ){
+ /* This call is a request for the "docid" column. Since "docid" is an
+ ** alias for "rowid", use the xRowid() method to obtain the value.
+ */
+ sqlite3_result_int64(pContext, pCsr->iPrevId);
+ }else if( iCol==p->nColumn ){
+ /* The extra column whose name is the same as the table.
+ ** Return a blob which is a pointer to the cursor.
+ */
+ sqlite3_result_blob(pContext, &pCsr, sizeof(pCsr), SQLITE_TRANSIENT);
+ }else{
+ rc = fts3CursorSeek(0, pCsr);
if( rc==SQLITE_OK ){
- rc = fts3ExprAllocateSegReaders(pCsr, pExpr->pRight, pnExpr);
+ sqlite3_result_value(pContext, sqlite3_column_value(pCsr->pStmt, iCol+1));
}
}
+
+ assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 );
return rc;
}
-/*
-** Free the Fts3SegReaderArray objects associated with each token in the
-** expression pExpr. In other words, this function frees the resources
-** allocated by fts3ExprAllocateSegReaders().
+/*
+** This function is the implementation of the xUpdate callback used by
+** FTS3 virtual tables. It is invoked by SQLite each time a row is to be
+** inserted, updated or deleted.
*/
-static void fts3ExprFreeSegReaders(Fts3Expr *pExpr){
- if( pExpr ){
- Fts3Phrase *pPhrase = pExpr->pPhrase;
- if( pPhrase ){
- int kk;
- for(kk=0; kk<pPhrase->nToken; kk++){
- fts3SegReaderCursorFree(pPhrase->aToken[kk].pSegcsr);
- pPhrase->aToken[kk].pSegcsr = 0;
- }
- }
- fts3ExprFreeSegReaders(pExpr->pLeft);
- fts3ExprFreeSegReaders(pExpr->pRight);
- }
+static int fts3UpdateMethod(
+ sqlite3_vtab *pVtab, /* Virtual table handle */
+ int nArg, /* Size of argument array */
+ sqlite3_value **apVal, /* Array of arguments */
+ sqlite_int64 *pRowid /* OUT: The affected (or effected) rowid */
+){
+ return sqlite3Fts3UpdateMethod(pVtab, nArg, apVal, pRowid);
}
/*
-** Return the sum of the costs of all tokens in the expression pExpr. This
-** function must be called after Fts3SegReaderArrays have been allocated
-** for all tokens using fts3ExprAllocateSegReaders().
+** Implementation of xSync() method. Flush the contents of the pending-terms
+** hash-table to the database.
*/
-static int fts3ExprCost(Fts3Expr *pExpr){
- int nCost; /* Return value */
- if( pExpr->eType==FTSQUERY_PHRASE ){
- Fts3Phrase *pPhrase = pExpr->pPhrase;
- int ii;
- nCost = 0;
- for(ii=0; ii<pPhrase->nToken; ii++){
- Fts3SegReaderCursor *pSegcsr = pPhrase->aToken[ii].pSegcsr;
- if( pSegcsr ) nCost += pSegcsr->nCost;
- }
- }else{
- nCost = fts3ExprCost(pExpr->pLeft) + fts3ExprCost(pExpr->pRight);
- }
- return nCost;
+static int fts3SyncMethod(sqlite3_vtab *pVtab){
+ int rc = sqlite3Fts3PendingTermsFlush((Fts3Table *)pVtab);
+ sqlite3Fts3SegmentsClose((Fts3Table *)pVtab);
+ return rc;
}
/*
-** The following is a helper function (and type) for fts3EvalExpr(). It
-** must be called after Fts3SegReaders have been allocated for every token
-** in the expression. See the context it is called from in fts3EvalExpr()
-** for further explanation.
+** Implementation of xBegin() method. This is a no-op.
*/
-typedef struct ExprAndCost ExprAndCost;
-struct ExprAndCost {
- Fts3Expr *pExpr;
- int nCost;
-};
-static void fts3ExprAssignCosts(
- Fts3Expr *pExpr, /* Expression to create seg-readers for */
- ExprAndCost **ppExprCost /* OUT: Write to *ppExprCost */
-){
- if( pExpr->eType==FTSQUERY_AND ){
- fts3ExprAssignCosts(pExpr->pLeft, ppExprCost);
- fts3ExprAssignCosts(pExpr->pRight, ppExprCost);
- }else{
- (*ppExprCost)->pExpr = pExpr;
- (*ppExprCost)->nCost = fts3ExprCost(pExpr);
- (*ppExprCost)++;
- }
+static int fts3BeginMethod(sqlite3_vtab *pVtab){
+ TESTONLY( Fts3Table *p = (Fts3Table*)pVtab );
+ UNUSED_PARAMETER(pVtab);
+ assert( p->pSegments==0 );
+ assert( p->nPendingData==0 );
+ assert( p->inTransaction!=1 );
+ TESTONLY( p->inTransaction = 1 );
+ TESTONLY( p->mxSavepoint = -1; );
+ return SQLITE_OK;
}
/*
-** Evaluate the full-text expression pExpr against FTS3 table pTab. Store
-** the resulting doclist in *paOut and *pnOut. This routine mallocs for
-** the space needed to store the output. The caller is responsible for
-** freeing the space when it has finished.
-**
-** This function is called in two distinct contexts:
-**
-** * From within the virtual table xFilter() method. In this case, the
-** output doclist contains entries for all rows in the table, based on
-** data read from the full-text index.
-**
-** In this case, if the query expression contains one or more tokens that
-** are very common, then the returned doclist may contain a superset of
-** the documents that actually match the expression.
-**
-** * From within the virtual table xNext() method. This call is only made
-** if the call from within xFilter() found that there were very common
-** tokens in the query expression and did return a superset of the
-** matching documents. In this case the returned doclist contains only
-** entries that correspond to the current row of the table. Instead of
-** reading the data for each token from the full-text index, the data is
-** already available in-memory in the Fts3PhraseToken.pDeferred structures.
-** See fts3EvalDeferred() for how it gets there.
-**
-** In the first case above, Fts3Cursor.doDeferred==0. In the second (if it is
-** required) Fts3Cursor.doDeferred==1.
-**
-** If the SQLite invokes the snippet(), offsets() or matchinfo() function
-** as part of a SELECT on an FTS3 table, this function is called on each
-** individual phrase expression in the query. If there were very common tokens
-** found in the xFilter() call, then this function is called once for phrase
-** for each row visited, and the returned doclist contains entries for the
-** current row only. Otherwise, if there were no very common tokens, then this
-** function is called once only for each phrase in the query and the returned
-** doclist contains entries for all rows of the table.
-**
-** Fts3Cursor.doDeferred==1 when this function is called on phrases as a
-** result of a snippet(), offsets() or matchinfo() invocation.
+** Implementation of xCommit() method. This is a no-op. The contents of
+** the pending-terms hash-table have already been flushed into the database
+** by fts3SyncMethod().
*/
-static int fts3EvalExpr(
- Fts3Cursor *p, /* Virtual table cursor handle */
- Fts3Expr *pExpr, /* Parsed fts3 expression */
- char **paOut, /* OUT: Pointer to malloc'd result buffer */
- int *pnOut, /* OUT: Size of buffer at *paOut */
- int isReqPos /* Require positions in output buffer */
-){
- int rc = SQLITE_OK; /* Return code */
+static int fts3CommitMethod(sqlite3_vtab *pVtab){
+ TESTONLY( Fts3Table *p = (Fts3Table*)pVtab );
+ UNUSED_PARAMETER(pVtab);
+ assert( p->nPendingData==0 );
+ assert( p->inTransaction!=0 );
+ assert( p->pSegments==0 );
+ TESTONLY( p->inTransaction = 0 );
+ TESTONLY( p->mxSavepoint = -1; );
+ return SQLITE_OK;
+}
- /* Zero the output parameters. */
- *paOut = 0;
- *pnOut = 0;
+/*
+** Implementation of xRollback(). Discard the contents of the pending-terms
+** hash-table. Any changes made to the database are reverted by SQLite.
+*/
+static int fts3RollbackMethod(sqlite3_vtab *pVtab){
+ Fts3Table *p = (Fts3Table*)pVtab;
+ sqlite3Fts3PendingTermsClear(p);
+ assert( p->inTransaction!=0 );
+ TESTONLY( p->inTransaction = 0 );
+ TESTONLY( p->mxSavepoint = -1; );
+ return SQLITE_OK;
+}
- if( pExpr ){
- assert( pExpr->eType==FTSQUERY_NEAR || pExpr->eType==FTSQUERY_OR
- || pExpr->eType==FTSQUERY_AND || pExpr->eType==FTSQUERY_NOT
- || pExpr->eType==FTSQUERY_PHRASE
- );
- assert( pExpr->eType==FTSQUERY_PHRASE || isReqPos==0 );
+/*
+** When called, *ppPoslist must point to the byte immediately following the
+** end of a position-list. i.e. ( (*ppPoslist)[-1]==POS_END ). This function
+** moves *ppPoslist so that it instead points to the first byte of the
+** same position list.
+*/
+static void fts3ReversePoslist(char *pStart, char **ppPoslist){
+ char *p = &(*ppPoslist)[-2];
+ char c;
- if( pExpr->eType==FTSQUERY_PHRASE ){
- rc = fts3PhraseSelect(p, pExpr->pPhrase,
- isReqPos || (pExpr->pParent && pExpr->pParent->eType==FTSQUERY_NEAR),
- paOut, pnOut
- );
- fts3ExprFreeSegReaders(pExpr);
- }else if( p->eEvalmode==FTS3_EVAL_FILTER && pExpr->eType==FTSQUERY_AND ){
- ExprAndCost *aExpr = 0; /* Array of AND'd expressions and costs */
- int nExpr = 0; /* Size of aExpr[] */
- char *aRet = 0; /* Doclist to return to caller */
- int nRet = 0; /* Length of aRet[] in bytes */
- int nDoc = 0x7FFFFFFF;
+ while( p>pStart && (c=*p--)==0 );
+ while( p>pStart && (*p & 0x80) | c ){
+ c = *p--;
+ }
+ if( p>pStart ){ p = &p[2]; }
+ while( *p++&0x80 );
+ *ppPoslist = p;
+}
- assert( !isReqPos );
+/*
+** Helper function used by the implementation of the overloaded snippet(),
+** offsets() and optimize() SQL functions.
+**
+** If the value passed as the third argument is a blob of size
+** sizeof(Fts3Cursor*), then the blob contents are copied to the
+** output variable *ppCsr and SQLITE_OK is returned. Otherwise, an error
+** message is written to context pContext and SQLITE_ERROR returned. The
+** string passed via zFunc is used as part of the error message.
+*/
+static int fts3FunctionArg(
+ sqlite3_context *pContext, /* SQL function call context */
+ const char *zFunc, /* Function name */
+ sqlite3_value *pVal, /* argv[0] passed to function */
+ Fts3Cursor **ppCsr /* OUT: Store cursor handle here */
+){
+ Fts3Cursor *pRet;
+ if( sqlite3_value_type(pVal)!=SQLITE_BLOB
+ || sqlite3_value_bytes(pVal)!=sizeof(Fts3Cursor *)
+ ){
+ char *zErr = sqlite3_mprintf("illegal first argument to %s", zFunc);
+ sqlite3_result_error(pContext, zErr, -1);
+ sqlite3_free(zErr);
+ return SQLITE_ERROR;
+ }
+ memcpy(&pRet, sqlite3_value_blob(pVal), sizeof(Fts3Cursor *));
+ *ppCsr = pRet;
+ return SQLITE_OK;
+}
- rc = fts3ExprAllocateSegReaders(p, pExpr, &nExpr);
- if( rc==SQLITE_OK ){
- assert( nExpr>1 );
- aExpr = sqlite3_malloc(sizeof(ExprAndCost) * nExpr);
- if( !aExpr ) rc = SQLITE_NOMEM;
- }
- if( rc==SQLITE_OK ){
- int ii; /* Used to iterate through expressions */
-
- fts3ExprAssignCosts(pExpr, &aExpr);
- aExpr -= nExpr;
- for(ii=0; ii<nExpr; ii++){
- char *aNew;
- int nNew;
- int jj;
- ExprAndCost *pBest = 0;
-
- for(jj=0; jj<nExpr; jj++){
- ExprAndCost *pCand = &aExpr[jj];
- if( pCand->pExpr && (pBest==0 || pCand->nCost<pBest->nCost) ){
- pBest = pCand;
- }
- }
-
- if( pBest->nCost>nDoc ){
- rc = fts3DeferExpression(p, p->pExpr);
- break;
- }else{
- rc = fts3EvalExpr(p, pBest->pExpr, &aNew, &nNew, 0);
- if( rc!=SQLITE_OK ) break;
- pBest->pExpr = 0;
- if( ii==0 ){
- aRet = aNew;
- nRet = nNew;
- nDoc = fts3DoclistCountDocids(0, aRet, nRet);
- }else{
- fts3DoclistMerge(
- MERGE_AND, 0, 0, aRet, &nRet, aRet, nRet, aNew, nNew, &nDoc
- );
- sqlite3_free(aNew);
- }
- }
- }
- }
+/*
+** Implementation of the snippet() function for FTS3
+*/
+static void fts3SnippetFunc(
+ sqlite3_context *pContext, /* SQLite function call context */
+ int nVal, /* Size of apVal[] array */
+ sqlite3_value **apVal /* Array of arguments */
+){
+ Fts3Cursor *pCsr; /* Cursor handle passed through apVal[0] */
+ const char *zStart = "<b>";
+ const char *zEnd = "</b>";
+ const char *zEllipsis = "<b>...</b>";
+ int iCol = -1;
+ int nToken = 15; /* Default number of tokens in snippet */
- if( rc==SQLITE_OK ){
- *paOut = aRet;
- *pnOut = nRet;
- }else{
- assert( *paOut==0 );
- sqlite3_free(aRet);
- }
- sqlite3_free(aExpr);
- fts3ExprFreeSegReaders(pExpr);
+ /* There must be at least one argument passed to this function (otherwise
+ ** the non-overloaded version would have been called instead of this one).
+ */
+ assert( nVal>=1 );
- }else{
- char *aLeft;
- char *aRight;
- int nLeft;
- int nRight;
-
- assert( pExpr->eType==FTSQUERY_NEAR
- || pExpr->eType==FTSQUERY_OR
- || pExpr->eType==FTSQUERY_NOT
- || (pExpr->eType==FTSQUERY_AND && p->eEvalmode==FTS3_EVAL_NEXT)
- );
+ if( nVal>6 ){
+ sqlite3_result_error(pContext,
+ "wrong number of arguments to function snippet()", -1);
+ return;
+ }
+ if( fts3FunctionArg(pContext, "snippet", apVal[0], &pCsr) ) return;
- if( 0==(rc = fts3EvalExpr(p, pExpr->pRight, &aRight, &nRight, isReqPos))
- && 0==(rc = fts3EvalExpr(p, pExpr->pLeft, &aLeft, &nLeft, isReqPos))
- ){
- switch( pExpr->eType ){
- case FTSQUERY_NEAR: {
- Fts3Expr *pLeft;
- Fts3Expr *pRight;
- int mergetype = MERGE_NEAR;
- if( pExpr->pParent && pExpr->pParent->eType==FTSQUERY_NEAR ){
- mergetype = MERGE_POS_NEAR;
- }
- pLeft = pExpr->pLeft;
- while( pLeft->eType==FTSQUERY_NEAR ){
- pLeft=pLeft->pRight;
- }
- pRight = pExpr->pRight;
- assert( pRight->eType==FTSQUERY_PHRASE );
- assert( pLeft->eType==FTSQUERY_PHRASE );
-
- rc = fts3NearMerge(mergetype, pExpr->nNear,
- pLeft->pPhrase->nToken, aLeft, nLeft,
- pRight->pPhrase->nToken, aRight, nRight,
- paOut, pnOut
- );
- sqlite3_free(aLeft);
- break;
- }
+ switch( nVal ){
+ case 6: nToken = sqlite3_value_int(apVal[5]);
+ case 5: iCol = sqlite3_value_int(apVal[4]);
+ case 4: zEllipsis = (const char*)sqlite3_value_text(apVal[3]);
+ case 3: zEnd = (const char*)sqlite3_value_text(apVal[2]);
+ case 2: zStart = (const char*)sqlite3_value_text(apVal[1]);
+ }
+ if( !zEllipsis || !zEnd || !zStart ){
+ sqlite3_result_error_nomem(pContext);
+ }else if( SQLITE_OK==fts3CursorSeek(pContext, pCsr) ){
+ sqlite3Fts3Snippet(pContext, pCsr, zStart, zEnd, zEllipsis, iCol, nToken);
+ }
+}
- case FTSQUERY_OR: {
- /* Allocate a buffer for the output. The maximum size is the
- ** sum of the sizes of the two input buffers. The +1 term is
- ** so that a buffer of zero bytes is never allocated - this can
- ** cause fts3DoclistMerge() to incorrectly return SQLITE_NOMEM.
- */
- char *aBuffer = sqlite3_malloc(nRight+nLeft+1);
- rc = fts3DoclistMerge(MERGE_OR, 0, 0, aBuffer, pnOut,
- aLeft, nLeft, aRight, nRight, 0
- );
- *paOut = aBuffer;
- sqlite3_free(aLeft);
- break;
- }
+/*
+** Implementation of the offsets() function for FTS3
+*/
+static void fts3OffsetsFunc(
+ sqlite3_context *pContext, /* SQLite function call context */
+ int nVal, /* Size of argument array */
+ sqlite3_value **apVal /* Array of arguments */
+){
+ Fts3Cursor *pCsr; /* Cursor handle passed through apVal[0] */
- default: {
- assert( FTSQUERY_NOT==MERGE_NOT && FTSQUERY_AND==MERGE_AND );
- fts3DoclistMerge(pExpr->eType, 0, 0, aLeft, pnOut,
- aLeft, nLeft, aRight, nRight, 0
- );
- *paOut = aLeft;
- break;
- }
- }
- }
- sqlite3_free(aRight);
+ UNUSED_PARAMETER(nVal);
+
+ assert( nVal==1 );
+ if( fts3FunctionArg(pContext, "offsets", apVal[0], &pCsr) ) return;
+ assert( pCsr );
+ if( SQLITE_OK==fts3CursorSeek(pContext, pCsr) ){
+ sqlite3Fts3Offsets(pContext, pCsr);
+ }
+}
+
+/*
+** Implementation of the special optimize() function for FTS3. This
+** function merges all segments in the database to a single segment.
+** Example usage is:
+**
+** SELECT optimize(t) FROM t LIMIT 1;
+**
+** where 't' is the name of an FTS3 table.
+*/
+static void fts3OptimizeFunc(
+ sqlite3_context *pContext, /* SQLite function call context */
+ int nVal, /* Size of argument array */
+ sqlite3_value **apVal /* Array of arguments */
+){
+ int rc; /* Return code */
+ Fts3Table *p; /* Virtual table handle */
+ Fts3Cursor *pCursor; /* Cursor handle passed through apVal[0] */
+
+ UNUSED_PARAMETER(nVal);
+
+ assert( nVal==1 );
+ if( fts3FunctionArg(pContext, "optimize", apVal[0], &pCursor) ) return;
+ p = (Fts3Table *)pCursor->base.pVtab;
+ assert( p );
+
+ rc = sqlite3Fts3Optimize(p);
+
+ switch( rc ){
+ case SQLITE_OK:
+ sqlite3_result_text(pContext, "Index optimized", -1, SQLITE_STATIC);
+ break;
+ case SQLITE_DONE:
+ sqlite3_result_text(pContext, "Index already optimal", -1, SQLITE_STATIC);
+ break;
+ default:
+ sqlite3_result_error_code(pContext, rc);
+ break;
+ }
+}
+
+/*
+** Implementation of the matchinfo() function for FTS3
+*/
+static void fts3MatchinfoFunc(
+ sqlite3_context *pContext, /* SQLite function call context */
+ int nVal, /* Size of argument array */
+ sqlite3_value **apVal /* Array of arguments */
+){
+ Fts3Cursor *pCsr; /* Cursor handle passed through apVal[0] */
+ assert( nVal==1 || nVal==2 );
+ if( SQLITE_OK==fts3FunctionArg(pContext, "matchinfo", apVal[0], &pCsr) ){
+ const char *zArg = 0;
+ if( nVal>1 ){
+ zArg = (const char *)sqlite3_value_text(apVal[1]);
}
+ sqlite3Fts3Matchinfo(pContext, pCsr, zArg);
}
+}
- assert( rc==SQLITE_OK || *paOut==0 );
+/*
+** This routine implements the xFindFunction method for the FTS3
+** virtual table.
+*/
+static int fts3FindFunctionMethod(
+ sqlite3_vtab *pVtab, /* Virtual table handle */
+ int nArg, /* Number of SQL function arguments */
+ const char *zName, /* Name of SQL function */
+ void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), /* OUT: Result */
+ void **ppArg /* Unused */
+){
+ struct Overloaded {
+ const char *zName;
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
+ } aOverload[] = {
+ { "snippet", fts3SnippetFunc },
+ { "offsets", fts3OffsetsFunc },
+ { "optimize", fts3OptimizeFunc },
+ { "matchinfo", fts3MatchinfoFunc },
+ };
+ int i; /* Iterator variable */
+
+ UNUSED_PARAMETER(pVtab);
+ UNUSED_PARAMETER(nArg);
+ UNUSED_PARAMETER(ppArg);
+
+ for(i=0; i<SizeofArray(aOverload); i++){
+ if( strcmp(zName, aOverload[i].zName)==0 ){
+ *pxFunc = aOverload[i].xFunc;
+ return 1;
+ }
+ }
+
+ /* No function of the specified name was found. Return 0. */
+ return 0;
+}
+
+/*
+** Implementation of FTS3 xRename method. Rename an fts3 table.
+*/
+static int fts3RenameMethod(
+ sqlite3_vtab *pVtab, /* Virtual table handle */
+ const char *zName /* New name of table */
+){
+ Fts3Table *p = (Fts3Table *)pVtab;
+ sqlite3 *db = p->db; /* Database connection */
+ int rc; /* Return Code */
+
+ rc = sqlite3Fts3PendingTermsFlush(p);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+
+ fts3DbExec(&rc, db,
+ "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';",
+ p->zDb, p->zName, zName
+ );
+ if( p->bHasDocsize ){
+ fts3DbExec(&rc, db,
+ "ALTER TABLE %Q.'%q_docsize' RENAME TO '%q_docsize';",
+ p->zDb, p->zName, zName
+ );
+ }
+ if( p->bHasStat ){
+ fts3DbExec(&rc, db,
+ "ALTER TABLE %Q.'%q_stat' RENAME TO '%q_stat';",
+ p->zDb, p->zName, zName
+ );
+ }
+ fts3DbExec(&rc, db,
+ "ALTER TABLE %Q.'%q_segments' RENAME TO '%q_segments';",
+ p->zDb, p->zName, zName
+ );
+ fts3DbExec(&rc, db,
+ "ALTER TABLE %Q.'%q_segdir' RENAME TO '%q_segdir';",
+ p->zDb, p->zName, zName
+ );
return rc;
}
/*
-** This function is called from within xNext() for each row visited by
-** an FTS3 query. If evaluating the FTS3 query expression within xFilter()
-** was able to determine the exact set of matching rows, this function sets
-** *pbRes to true and returns SQLITE_IO immediately.
+** The xSavepoint() method.
**
-** Otherwise, if evaluating the query expression within xFilter() returned a
-** superset of the matching documents instead of an exact set (this happens
-** when the query includes very common tokens and it is deemed too expensive to
-** load their doclists from disk), this function tests if the current row
-** really does match the FTS3 query.
+** Flush the contents of the pending-terms table to disk.
+*/
+static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
+ UNUSED_PARAMETER(iSavepoint);
+ assert( ((Fts3Table *)pVtab)->inTransaction );
+ assert( ((Fts3Table *)pVtab)->mxSavepoint < iSavepoint );
+ TESTONLY( ((Fts3Table *)pVtab)->mxSavepoint = iSavepoint );
+ return fts3SyncMethod(pVtab);
+}
+
+/*
+** The xRelease() method.
**
-** If an error occurs, an SQLite error code is returned. Otherwise, SQLITE_OK
-** is returned and *pbRes is set to true if the current row matches the
-** FTS3 query (and should be included in the results returned to SQLite), or
-** false otherwise.
+** This is a no-op.
*/
-static int fts3EvalDeferred(
- Fts3Cursor *pCsr, /* FTS3 cursor pointing at row to test */
- int *pbRes /* OUT: Set to true if row is a match */
-){
+static int fts3ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
+ TESTONLY( Fts3Table *p = (Fts3Table*)pVtab );
+ UNUSED_PARAMETER(iSavepoint);
+ UNUSED_PARAMETER(pVtab);
+ assert( p->inTransaction );
+ assert( p->mxSavepoint >= iSavepoint );
+ TESTONLY( p->mxSavepoint = iSavepoint-1 );
+ return SQLITE_OK;
+}
+
+/*
+** The xRollbackTo() method.
+**
+** Discard the contents of the pending terms table.
+*/
+static int fts3RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
+ Fts3Table *p = (Fts3Table*)pVtab;
+ UNUSED_PARAMETER(iSavepoint);
+ assert( p->inTransaction );
+ assert( p->mxSavepoint >= iSavepoint );
+ TESTONLY( p->mxSavepoint = iSavepoint );
+ sqlite3Fts3PendingTermsClear(p);
+ return SQLITE_OK;
+}
+
+static const sqlite3_module fts3Module = {
+ /* iVersion */ 2,
+ /* xCreate */ fts3CreateMethod,
+ /* xConnect */ fts3ConnectMethod,
+ /* xBestIndex */ fts3BestIndexMethod,
+ /* xDisconnect */ fts3DisconnectMethod,
+ /* xDestroy */ fts3DestroyMethod,
+ /* xOpen */ fts3OpenMethod,
+ /* xClose */ fts3CloseMethod,
+ /* xFilter */ fts3FilterMethod,
+ /* xNext */ fts3NextMethod,
+ /* xEof */ fts3EofMethod,
+ /* xColumn */ fts3ColumnMethod,
+ /* xRowid */ fts3RowidMethod,
+ /* xUpdate */ fts3UpdateMethod,
+ /* xBegin */ fts3BeginMethod,
+ /* xSync */ fts3SyncMethod,
+ /* xCommit */ fts3CommitMethod,
+ /* xRollback */ fts3RollbackMethod,
+ /* xFindFunction */ fts3FindFunctionMethod,
+ /* xRename */ fts3RenameMethod,
+ /* xSavepoint */ fts3SavepointMethod,
+ /* xRelease */ fts3ReleaseMethod,
+ /* xRollbackTo */ fts3RollbackToMethod,
+};
+
+/*
+** This function is registered as the module destructor (called when an
+** FTS3 enabled database connection is closed). It frees the memory
+** allocated for the tokenizer hash table.
+*/
+static void hashDestroy(void *p){
+ Fts3Hash *pHash = (Fts3Hash *)p;
+ sqlite3Fts3HashClear(pHash);
+ sqlite3_free(pHash);
+}
+
+/*
+** The fts3 built-in tokenizers - "simple", "porter" and "icu"- are
+** implemented in files fts3_tokenizer1.c, fts3_porter.c and fts3_icu.c
+** respectively. The following three forward declarations are for functions
+** declared in these files used to retrieve the respective implementations.
+**
+** Calling sqlite3Fts3SimpleTokenizerModule() sets the value pointed
+** to by the argument to point to the "simple" tokenizer implementation.
+** And so on.
+*/
+SQLITE_PRIVATE void sqlite3Fts3SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule);
+SQLITE_PRIVATE void sqlite3Fts3PorterTokenizerModule(sqlite3_tokenizer_module const**ppModule);
+#ifdef SQLITE_ENABLE_ICU
+SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(sqlite3_tokenizer_module const**ppModule);
+#endif
+
+/*
+** Initialise the fts3 extension. If this extension is built as part
+** of the sqlite library, then this function is called directly by
+** SQLite. If fts3 is built as a dynamically loadable extension, this
+** function is called by the sqlite3_extension_init() entry point.
+*/
+SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){
int rc = SQLITE_OK;
- if( pCsr->pDeferred==0 ){
- *pbRes = 1;
+ Fts3Hash *pHash = 0;
+ const sqlite3_tokenizer_module *pSimple = 0;
+ const sqlite3_tokenizer_module *pPorter = 0;
+
+#ifdef SQLITE_ENABLE_ICU
+ const sqlite3_tokenizer_module *pIcu = 0;
+ sqlite3Fts3IcuTokenizerModule(&pIcu);
+#endif
+
+#ifdef SQLITE_TEST
+ rc = sqlite3Fts3InitTerm(db);
+ if( rc!=SQLITE_OK ) return rc;
+#endif
+
+ rc = sqlite3Fts3InitAux(db);
+ if( rc!=SQLITE_OK ) return rc;
+
+ sqlite3Fts3SimpleTokenizerModule(&pSimple);
+ sqlite3Fts3PorterTokenizerModule(&pPorter);
+
+ /* Allocate and initialise the hash-table used to store tokenizers. */
+ pHash = sqlite3_malloc(sizeof(Fts3Hash));
+ if( !pHash ){
+ rc = SQLITE_NOMEM;
}else{
- rc = fts3CursorSeek(0, pCsr);
- if( rc==SQLITE_OK ){
- sqlite3Fts3FreeDeferredDoclists(pCsr);
- rc = sqlite3Fts3CacheDeferredDoclists(pCsr);
+ sqlite3Fts3HashInit(pHash, FTS3_HASH_STRING, 1);
+ }
+
+ /* Load the built-in tokenizers into the hash table */
+ if( rc==SQLITE_OK ){
+ if( sqlite3Fts3HashInsert(pHash, "simple", 7, (void *)pSimple)
+ || sqlite3Fts3HashInsert(pHash, "porter", 7, (void *)pPorter)
+#ifdef SQLITE_ENABLE_ICU
+ || (pIcu && sqlite3Fts3HashInsert(pHash, "icu", 4, (void *)pIcu))
+#endif
+ ){
+ rc = SQLITE_NOMEM;
}
+ }
+
+#ifdef SQLITE_TEST
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts3ExprInitTestInterface(db);
+ }
+#endif
+
+ /* Create the virtual table wrapper around the hash-table and overload
+ ** the two scalar functions. If this is successful, register the
+ ** module with sqlite.
+ */
+ if( SQLITE_OK==rc
+ && SQLITE_OK==(rc = sqlite3Fts3InitHashTable(db, pHash, "fts3_tokenizer"))
+ && SQLITE_OK==(rc = sqlite3_overload_function(db, "snippet", -1))
+ && SQLITE_OK==(rc = sqlite3_overload_function(db, "offsets", 1))
+ && SQLITE_OK==(rc = sqlite3_overload_function(db, "matchinfo", 1))
+ && SQLITE_OK==(rc = sqlite3_overload_function(db, "matchinfo", 2))
+ && SQLITE_OK==(rc = sqlite3_overload_function(db, "optimize", 1))
+ ){
+ rc = sqlite3_create_module_v2(
+ db, "fts3", &fts3Module, (void *)pHash, hashDestroy
+ );
if( rc==SQLITE_OK ){
- char *a = 0;
- int n = 0;
- rc = fts3EvalExpr(pCsr, pCsr->pExpr, &a, &n, 0);
- assert( n>=0 );
- *pbRes = (n>0);
- sqlite3_free(a);
+ rc = sqlite3_create_module_v2(
+ db, "fts4", &fts3Module, (void *)pHash, 0
+ );
}
+ return rc;
+ }
+
+ /* An error has occurred. Delete the hash table and return the error code. */
+ assert( rc!=SQLITE_OK );
+ if( pHash ){
+ sqlite3Fts3HashClear(pHash);
+ sqlite3_free(pHash);
}
return rc;
}
/*
-** Advance the cursor to the next row in the %_content table that
-** matches the search criteria. For a MATCH search, this will be
-** the next row that matches. For a full-table scan, this will be
-** simply the next row in the %_content table. For a docid lookup,
-** this routine simply sets the EOF flag.
+** Allocate an Fts3MultiSegReader for each token in the expression headed
+** by pExpr.
**
-** Return SQLITE_OK if nothing goes wrong. SQLITE_OK is returned
-** even if we reach end-of-file. The fts3EofMethod() will be called
-** subsequently to determine whether or not an EOF was hit.
+** An Fts3SegReader object is a cursor that can seek or scan a range of
+** entries within a single segment b-tree. An Fts3MultiSegReader uses multiple
+** Fts3SegReader objects internally to provide an interface to seek or scan
+** within the union of all segments of a b-tree. Hence the name.
+**
+** If the allocated Fts3MultiSegReader just seeks to a single entry in a
+** segment b-tree (if the term is not a prefix or it is a prefix for which
+** there exists prefix b-tree of the right length) then it may be traversed
+** and merged incrementally. Otherwise, it has to be merged into an in-memory
+** doclist and then traversed.
*/
-static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){
- int res;
- int rc = SQLITE_OK; /* Return code */
- Fts3Cursor *pCsr = (Fts3Cursor *)pCursor;
-
- pCsr->eEvalmode = FTS3_EVAL_NEXT;
- do {
- if( pCsr->aDoclist==0 ){
- if( SQLITE_ROW!=sqlite3_step(pCsr->pStmt) ){
- pCsr->isEof = 1;
- rc = sqlite3_reset(pCsr->pStmt);
- break;
+static void fts3EvalAllocateReaders(
+ Fts3Cursor *pCsr, /* FTS cursor handle */
+ Fts3Expr *pExpr, /* Allocate readers for this expression */
+ int *pnToken, /* OUT: Total number of tokens in phrase. */
+ int *pnOr, /* OUT: Total number of OR nodes in expr. */
+ int *pRc /* IN/OUT: Error code */
+){
+ if( pExpr && SQLITE_OK==*pRc ){
+ if( pExpr->eType==FTSQUERY_PHRASE ){
+ int i;
+ int nToken = pExpr->pPhrase->nToken;
+ *pnToken += nToken;
+ for(i=0; i<nToken; i++){
+ Fts3PhraseToken *pToken = &pExpr->pPhrase->aToken[i];
+ int rc = fts3TermSegReaderCursor(pCsr,
+ pToken->z, pToken->n, pToken->isPrefix, &pToken->pSegcsr
+ );
+ if( rc!=SQLITE_OK ){
+ *pRc = rc;
+ return;
+ }
}
- pCsr->iPrevId = sqlite3_column_int64(pCsr->pStmt, 0);
+ assert( pExpr->pPhrase->iDoclistToken==0 );
+ pExpr->pPhrase->iDoclistToken = -1;
}else{
- if( pCsr->pNextId>=&pCsr->aDoclist[pCsr->nDoclist] ){
- pCsr->isEof = 1;
- break;
+ *pnOr += (pExpr->eType==FTSQUERY_OR);
+ fts3EvalAllocateReaders(pCsr, pExpr->pLeft, pnToken, pnOr, pRc);
+ fts3EvalAllocateReaders(pCsr, pExpr->pRight, pnToken, pnOr, pRc);
+ }
+ }
+}
+
+/*
+** Arguments pList/nList contain the doclist for token iToken of phrase p.
+** It is merged into the main doclist stored in p->doclist.aAll/nAll.
+**
+** This function assumes that pList points to a buffer allocated using
+** sqlite3_malloc(). This function takes responsibility for eventually
+** freeing the buffer.
+*/
+static void fts3EvalPhraseMergeToken(
+ Fts3Table *pTab, /* FTS Table pointer */
+ Fts3Phrase *p, /* Phrase to merge pList/nList into */
+ int iToken, /* Token pList/nList corresponds to */
+ char *pList, /* Pointer to doclist */
+ int nList /* Number of bytes in pList */
+){
+ assert( iToken!=p->iDoclistToken );
+
+ if( pList==0 ){
+ sqlite3_free(p->doclist.aAll);
+ p->doclist.aAll = 0;
+ p->doclist.nAll = 0;
+ }
+
+ else if( p->iDoclistToken<0 ){
+ p->doclist.aAll = pList;
+ p->doclist.nAll = nList;
+ }
+
+ else if( p->doclist.aAll==0 ){
+ sqlite3_free(pList);
+ }
+
+ else {
+ char *pLeft;
+ char *pRight;
+ int nLeft;
+ int nRight;
+ int nDiff;
+
+ if( p->iDoclistToken<iToken ){
+ pLeft = p->doclist.aAll;
+ nLeft = p->doclist.nAll;
+ pRight = pList;
+ nRight = nList;
+ nDiff = iToken - p->iDoclistToken;
+ }else{
+ pRight = p->doclist.aAll;
+ nRight = p->doclist.nAll;
+ pLeft = pList;
+ nLeft = nList;
+ nDiff = p->iDoclistToken - iToken;
+ }
+
+ fts3DoclistPhraseMerge(pTab->bDescIdx, nDiff, pLeft, nLeft, pRight,&nRight);
+ sqlite3_free(pLeft);
+ p->doclist.aAll = pRight;
+ p->doclist.nAll = nRight;
+ }
+
+ if( iToken>p->iDoclistToken ) p->iDoclistToken = iToken;
+}
+
+/*
+** Load the doclist for phrase p into p->doclist.aAll/nAll. The loaded doclist
+** does not take deferred tokens into account.
+**
+** SQLITE_OK is returned if no error occurs, otherwise an SQLite error code.
+*/
+static int fts3EvalPhraseLoad(
+ Fts3Cursor *pCsr, /* FTS Cursor handle */
+ Fts3Phrase *p /* Phrase object */
+){
+ Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
+ int iToken;
+ int rc = SQLITE_OK;
+
+ for(iToken=0; rc==SQLITE_OK && iToken<p->nToken; iToken++){
+ Fts3PhraseToken *pToken = &p->aToken[iToken];
+ assert( pToken->pDeferred==0 || pToken->pSegcsr==0 );
+
+ if( pToken->pSegcsr ){
+ int nThis = 0;
+ char *pThis = 0;
+ rc = fts3TermSelect(pTab, pToken, p->iColumn, &nThis, &pThis);
+ if( rc==SQLITE_OK ){
+ fts3EvalPhraseMergeToken(pTab, p, iToken, pThis, nThis);
}
- sqlite3_reset(pCsr->pStmt);
- fts3GetDeltaVarint(&pCsr->pNextId, &pCsr->iPrevId);
- pCsr->isRequireSeek = 1;
- pCsr->isMatchinfoNeeded = 1;
}
- }while( SQLITE_OK==(rc = fts3EvalDeferred(pCsr, &res)) && res==0 );
+ assert( pToken->pSegcsr==0 );
+ }
return rc;
}
/*
-** This is the xFilter interface for the virtual table. See
-** the virtual table xFilter method documentation for additional
-** information.
+** This function is called on each phrase after the position lists for
+** any deferred tokens have been loaded into memory. It updates the phrases
+** current position list to include only those positions that are really
+** instances of the phrase (after considering deferred tokens). If this
+** means that the phrase does not appear in the current row, doclist.pList
+** and doclist.nList are both zeroed.
**
-** If idxNum==FTS3_FULLSCAN_SEARCH then do a full table scan against
-** the %_content table.
-**
-** If idxNum==FTS3_DOCID_SEARCH then do a docid lookup for a single entry
-** in the %_content table.
-**
-** If idxNum>=FTS3_FULLTEXT_SEARCH then use the full text index. The
-** column on the left-hand side of the MATCH operator is column
-** number idxNum-FTS3_FULLTEXT_SEARCH, 0 indexed. argv[0] is the right-hand
-** side of the MATCH operator.
+** SQLITE_OK is returned if no error occurs, otherwise an SQLite error code.
*/
-static int fts3FilterMethod(
- sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */
- int idxNum, /* Strategy index */
- const char *idxStr, /* Unused */
- int nVal, /* Number of elements in apVal */
- sqlite3_value **apVal /* Arguments for the indexing scheme */
-){
- const char *azSql[] = {
- "SELECT %s FROM %Q.'%q_content' AS x WHERE docid = ?", /* non-full-scan */
- "SELECT %s FROM %Q.'%q_content' AS x ", /* full-scan */
- };
- int rc; /* Return code */
- char *zSql; /* SQL statement used to access %_content */
- Fts3Table *p = (Fts3Table *)pCursor->pVtab;
- Fts3Cursor *pCsr = (Fts3Cursor *)pCursor;
+static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
+ int iToken; /* Used to iterate through phrase tokens */
+ int rc = SQLITE_OK; /* Return code */
+ char *aPoslist = 0; /* Position list for deferred tokens */
+ int nPoslist = 0; /* Number of bytes in aPoslist */
+ int iPrev = -1; /* Token number of previous deferred token */
- UNUSED_PARAMETER(idxStr);
- UNUSED_PARAMETER(nVal);
+ assert( pPhrase->doclist.bFreeList==0 );
- assert( idxNum>=0 && idxNum<=(FTS3_FULLTEXT_SEARCH+p->nColumn) );
- assert( nVal==0 || nVal==1 );
- assert( (nVal==0)==(idxNum==FTS3_FULLSCAN_SEARCH) );
- assert( p->pSegments==0 );
+ for(iToken=0; rc==SQLITE_OK && iToken<pPhrase->nToken; iToken++){
+ Fts3PhraseToken *pToken = &pPhrase->aToken[iToken];
+ Fts3DeferredToken *pDeferred = pToken->pDeferred;
- /* In case the cursor has been used before, clear it now. */
- sqlite3_finalize(pCsr->pStmt);
- sqlite3_free(pCsr->aDoclist);
- sqlite3Fts3ExprFree(pCsr->pExpr);
- memset(&pCursor[1], 0, sizeof(Fts3Cursor)-sizeof(sqlite3_vtab_cursor));
+ if( pDeferred ){
+ char *pList;
+ int nList;
+ rc = sqlite3Fts3DeferredTokenList(pDeferred, &pList, &nList);
+ if( rc!=SQLITE_OK ) return rc;
- if( idxNum!=FTS3_DOCID_SEARCH && idxNum!=FTS3_FULLSCAN_SEARCH ){
- int iCol = idxNum-FTS3_FULLTEXT_SEARCH;
- const char *zQuery = (const char *)sqlite3_value_text(apVal[0]);
+ if( pList==0 ){
+ sqlite3_free(aPoslist);
+ pPhrase->doclist.pList = 0;
+ pPhrase->doclist.nList = 0;
+ return SQLITE_OK;
- if( zQuery==0 && sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
- return SQLITE_NOMEM;
- }
+ }else if( aPoslist==0 ){
+ aPoslist = pList;
+ nPoslist = nList;
- rc = sqlite3Fts3ExprParse(p->pTokenizer, p->azColumn, p->nColumn,
- iCol, zQuery, -1, &pCsr->pExpr
- );
- if( rc!=SQLITE_OK ){
- if( rc==SQLITE_ERROR ){
- p->base.zErrMsg = sqlite3_mprintf("malformed MATCH expression: [%s]",
- zQuery);
+ }else{
+ char *aOut = pList;
+ char *p1 = aPoslist;
+ char *p2 = aOut;
+
+ assert( iPrev>=0 );
+ fts3PoslistPhraseMerge(&aOut, iToken-iPrev, 0, 1, &p1, &p2);
+ sqlite3_free(aPoslist);
+ aPoslist = pList;
+ nPoslist = aOut - aPoslist;
+ if( nPoslist==0 ){
+ sqlite3_free(aPoslist);
+ pPhrase->doclist.pList = 0;
+ pPhrase->doclist.nList = 0;
+ return SQLITE_OK;
+ }
}
- return rc;
+ iPrev = iToken;
}
+ }
- rc = sqlite3Fts3ReadLock(p);
- if( rc!=SQLITE_OK ) return rc;
+ if( iPrev>=0 ){
+ int nMaxUndeferred = pPhrase->iDoclistToken;
+ if( nMaxUndeferred<0 ){
+ pPhrase->doclist.pList = aPoslist;
+ pPhrase->doclist.nList = nPoslist;
+ pPhrase->doclist.iDocid = pCsr->iPrevId;
+ pPhrase->doclist.bFreeList = 1;
+ }else{
+ int nDistance;
+ char *p1;
+ char *p2;
+ char *aOut;
+
+ if( nMaxUndeferred>iPrev ){
+ p1 = aPoslist;
+ p2 = pPhrase->doclist.pList;
+ nDistance = nMaxUndeferred - iPrev;
+ }else{
+ p1 = pPhrase->doclist.pList;
+ p2 = aPoslist;
+ nDistance = iPrev - nMaxUndeferred;
+ }
- rc = fts3EvalExpr(pCsr, pCsr->pExpr, &pCsr->aDoclist, &pCsr->nDoclist, 0);
- sqlite3Fts3SegmentsClose(p);
- if( rc!=SQLITE_OK ) return rc;
- pCsr->pNextId = pCsr->aDoclist;
- pCsr->iPrevId = 0;
+ aOut = (char *)sqlite3_malloc(nPoslist+8);
+ if( !aOut ){
+ sqlite3_free(aPoslist);
+ return SQLITE_NOMEM;
+ }
+
+ pPhrase->doclist.pList = aOut;
+ if( fts3PoslistPhraseMerge(&aOut, nDistance, 0, 1, &p1, &p2) ){
+ pPhrase->doclist.bFreeList = 1;
+ pPhrase->doclist.nList = (aOut - pPhrase->doclist.pList);
+ }else{
+ sqlite3_free(aOut);
+ pPhrase->doclist.pList = 0;
+ pPhrase->doclist.nList = 0;
+ }
+ sqlite3_free(aPoslist);
+ }
}
- /* Compile a SELECT statement for this cursor. For a full-table-scan, the
- ** statement loops through all rows of the %_content table. For a
- ** full-text query or docid lookup, the statement retrieves a single
- ** row by docid.
- */
- zSql = (char *)azSql[idxNum==FTS3_FULLSCAN_SEARCH];
- zSql = sqlite3_mprintf(zSql, p->zReadExprlist, p->zDb, p->zName);
- if( !zSql ){
- rc = SQLITE_NOMEM;
+ return SQLITE_OK;
+}
+
+/*
+** This function is called for each Fts3Phrase in a full-text query
+** expression to initialize the mechanism for returning rows. Once this
+** function has been called successfully on an Fts3Phrase, it may be
+** used with fts3EvalPhraseNext() to iterate through the matching docids.
+**
+** If parameter bOptOk is true, then the phrase may (or may not) use the
+** incremental loading strategy. Otherwise, the entire doclist is loaded into
+** memory within this call.
+**
+** SQLITE_OK is returned if no error occurs, otherwise an SQLite error code.
+*/
+static int fts3EvalPhraseStart(Fts3Cursor *pCsr, int bOptOk, Fts3Phrase *p){
+ int rc; /* Error code */
+ Fts3PhraseToken *pFirst = &p->aToken[0];
+ Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
+
+ if( pCsr->bDesc==pTab->bDescIdx
+ && bOptOk==1
+ && p->nToken==1
+ && pFirst->pSegcsr
+ && pFirst->pSegcsr->bLookup
+ ){
+ /* Use the incremental approach. */
+ int iCol = (p->iColumn >= pTab->nColumn ? -1 : p->iColumn);
+ rc = sqlite3Fts3MsrIncrStart(
+ pTab, pFirst->pSegcsr, iCol, pFirst->z, pFirst->n);
+ p->bIncr = 1;
+
}else{
- rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0);
- sqlite3_free(zSql);
- }
- if( rc==SQLITE_OK && idxNum==FTS3_DOCID_SEARCH ){
- rc = sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]);
+ /* Load the full doclist for the phrase into memory. */
+ rc = fts3EvalPhraseLoad(pCsr, p);
+ p->bIncr = 0;
}
- pCsr->eSearch = (i16)idxNum;
- if( rc!=SQLITE_OK ) return rc;
- return fts3NextMethod(pCursor);
+ assert( rc!=SQLITE_OK || p->nToken<1 || p->aToken[0].pSegcsr==0 || p->bIncr );
+ return rc;
}
-/*
-** This is the xEof method of the virtual table. SQLite calls this
-** routine to find out if it has reached the end of a result set.
+/*
+** This function is used to iterate backwards (from the end to start)
+** through doclists. It is used by this module to iterate through phrase
+** doclists in reverse and by the fts3_write.c module to iterate through
+** pending-terms lists when writing to databases with "order=desc".
+**
+** The doclist may be sorted in ascending (parameter bDescIdx==0) or
+** descending (parameter bDescIdx==1) order of docid. Regardless, this
+** function iterates from the end of the doclist to the beginning.
*/
-static int fts3EofMethod(sqlite3_vtab_cursor *pCursor){
- return ((Fts3Cursor *)pCursor)->isEof;
+SQLITE_PRIVATE void sqlite3Fts3DoclistPrev(
+ int bDescIdx, /* True if the doclist is desc */
+ char *aDoclist, /* Pointer to entire doclist */
+ int nDoclist, /* Length of aDoclist in bytes */
+ char **ppIter, /* IN/OUT: Iterator pointer */
+ sqlite3_int64 *piDocid, /* IN/OUT: Docid pointer */
+ int *pnList, /* IN/OUT: List length pointer */
+ u8 *pbEof /* OUT: End-of-file flag */
+){
+ char *p = *ppIter;
+
+ assert( nDoclist>0 );
+ assert( *pbEof==0 );
+ assert( p || *piDocid==0 );
+ assert( !p || (p>aDoclist && p<&aDoclist[nDoclist]) );
+
+ if( p==0 ){
+ sqlite3_int64 iDocid = 0;
+ char *pNext = 0;
+ char *pDocid = aDoclist;
+ char *pEnd = &aDoclist[nDoclist];
+ int iMul = 1;
+
+ while( pDocid<pEnd ){
+ sqlite3_int64 iDelta;
+ pDocid += sqlite3Fts3GetVarint(pDocid, &iDelta);
+ iDocid += (iMul * iDelta);
+ pNext = pDocid;
+ fts3PoslistCopy(0, &pDocid);
+ while( pDocid<pEnd && *pDocid==0 ) pDocid++;
+ iMul = (bDescIdx ? -1 : 1);
+ }
+
+ *pnList = pEnd - pNext;
+ *ppIter = pNext;
+ *piDocid = iDocid;
+ }else{
+ int iMul = (bDescIdx ? -1 : 1);
+ sqlite3_int64 iDelta;
+ fts3GetReverseVarint(&p, aDoclist, &iDelta);
+ *piDocid -= (iMul * iDelta);
+
+ if( p==aDoclist ){
+ *pbEof = 1;
+ }else{
+ char *pSave = p;
+ fts3ReversePoslist(aDoclist, &p);
+ *pnList = (pSave - p);
+ }
+ *ppIter = p;
+ }
}
-/*
-** This is the xRowid method. The SQLite core calls this routine to
-** retrieve the rowid for the current row of the result set. fts3
-** exposes %_content.docid as the rowid for the virtual table. The
-** rowid should be written to *pRowid.
+/*
+** Attempt to move the phrase iterator to point to the next matching docid.
+** If an error occurs, return an SQLite error code. Otherwise, return
+** SQLITE_OK.
+**
+** If there is no "next" entry and no error occurs, then *pbEof is set to
+** 1 before returning. Otherwise, if no error occurs and the iterator is
+** successfully advanced, *pbEof is set to 0.
*/
-static int fts3RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
- Fts3Cursor *pCsr = (Fts3Cursor *) pCursor;
- if( pCsr->aDoclist ){
- *pRowid = pCsr->iPrevId;
+static int fts3EvalPhraseNext(
+ Fts3Cursor *pCsr, /* FTS Cursor handle */
+ Fts3Phrase *p, /* Phrase object to advance to next docid */
+ u8 *pbEof /* OUT: Set to 1 if EOF */
+){
+ int rc = SQLITE_OK;
+ Fts3Doclist *pDL = &p->doclist;
+ Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
+
+ if( p->bIncr ){
+ assert( p->nToken==1 );
+ assert( pDL->pNextDocid==0 );
+ rc = sqlite3Fts3MsrIncrNext(pTab, p->aToken[0].pSegcsr,
+ &pDL->iDocid, &pDL->pList, &pDL->nList
+ );
+ if( rc==SQLITE_OK && !pDL->pList ){
+ *pbEof = 1;
+ }
+ }else if( pCsr->bDesc!=pTab->bDescIdx && pDL->nAll ){
+ sqlite3Fts3DoclistPrev(pTab->bDescIdx, pDL->aAll, pDL->nAll,
+ &pDL->pNextDocid, &pDL->iDocid, &pDL->nList, pbEof
+ );
+ pDL->pList = pDL->pNextDocid;
}else{
- /* This branch runs if the query is implemented using a full-table scan
- ** (not using the full-text index). In this case grab the rowid from the
- ** SELECT statement.
- */
- assert( pCsr->isRequireSeek==0 );
- *pRowid = sqlite3_column_int64(pCsr->pStmt, 0);
+ char *pIter; /* Used to iterate through aAll */
+ char *pEnd = &pDL->aAll[pDL->nAll]; /* 1 byte past end of aAll */
+ if( pDL->pNextDocid ){
+ pIter = pDL->pNextDocid;
+ }else{
+ pIter = pDL->aAll;
+ }
+
+ if( pIter>=pEnd ){
+ /* We have already reached the end of this doclist. EOF. */
+ *pbEof = 1;
+ }else{
+ sqlite3_int64 iDelta;
+ pIter += sqlite3Fts3GetVarint(pIter, &iDelta);
+ if( pTab->bDescIdx==0 || pDL->pNextDocid==0 ){
+ pDL->iDocid += iDelta;
+ }else{
+ pDL->iDocid -= iDelta;
+ }
+ pDL->pList = pIter;
+ fts3PoslistCopy(0, &pIter);
+ pDL->nList = (pIter - pDL->pList);
+
+ /* pIter now points just past the 0x00 that terminates the position-
+ ** list for document pDL->iDocid. However, if this position-list was
+ ** edited in place by fts3EvalNearTrim(), then pIter may not actually
+ ** point to the start of the next docid value. The following line deals
+ ** with this case by advancing pIter past the zero-padding added by
+ ** fts3EvalNearTrim(). */
+ while( pIter<pEnd && *pIter==0 ) pIter++;
+
+ pDL->pNextDocid = pIter;
+ assert( pIter>=&pDL->aAll[pDL->nAll] || *pIter );
+ *pbEof = 0;
+ }
}
- return SQLITE_OK;
+
+ return rc;
}
-/*
-** This is the xColumn method, called by SQLite to request a value from
-** the row that the supplied cursor currently points to.
+/*
+**
+** If *pRc is not SQLITE_OK when this function is called, it is a no-op.
+** Otherwise, fts3EvalPhraseStart() is called on all phrases within the
+** expression. Also the Fts3Expr.bDeferred variable is set to true for any
+** expressions for which all descendent tokens are deferred.
+**
+** If parameter bOptOk is zero, then it is guaranteed that the
+** Fts3Phrase.doclist.aAll/nAll variables contain the entire doclist for
+** each phrase in the expression (subject to deferred token processing).
+** Or, if bOptOk is non-zero, then one or more tokens within the expression
+** may be loaded incrementally, meaning doclist.aAll/nAll is not available.
+**
+** If an error occurs within this function, *pRc is set to an SQLite error
+** code before returning.
*/
-static int fts3ColumnMethod(
- sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
- sqlite3_context *pContext, /* Context for sqlite3_result_xxx() calls */
- int iCol /* Index of column to read value from */
+static void fts3EvalStartReaders(
+ Fts3Cursor *pCsr, /* FTS Cursor handle */
+ Fts3Expr *pExpr, /* Expression to initialize phrases in */
+ int bOptOk, /* True to enable incremental loading */
+ int *pRc /* IN/OUT: Error code */
){
- int rc; /* Return Code */
- Fts3Cursor *pCsr = (Fts3Cursor *) pCursor;
- Fts3Table *p = (Fts3Table *)pCursor->pVtab;
+ if( pExpr && SQLITE_OK==*pRc ){
+ if( pExpr->eType==FTSQUERY_PHRASE ){
+ int i;
+ int nToken = pExpr->pPhrase->nToken;
+ for(i=0; i<nToken; i++){
+ if( pExpr->pPhrase->aToken[i].pDeferred==0 ) break;
+ }
+ pExpr->bDeferred = (i==nToken);
+ *pRc = fts3EvalPhraseStart(pCsr, bOptOk, pExpr->pPhrase);
+ }else{
+ fts3EvalStartReaders(pCsr, pExpr->pLeft, bOptOk, pRc);
+ fts3EvalStartReaders(pCsr, pExpr->pRight, bOptOk, pRc);
+ pExpr->bDeferred = (pExpr->pLeft->bDeferred && pExpr->pRight->bDeferred);
+ }
+ }
+}
- /* The column value supplied by SQLite must be in range. */
- assert( iCol>=0 && iCol<=p->nColumn+1 );
+/*
+** An array of the following structures is assembled as part of the process
+** of selecting tokens to defer before the query starts executing (as part
+** of the xFilter() method). There is one element in the array for each
+** token in the FTS expression.
+**
+** Tokens are divided into AND/NEAR clusters. All tokens in a cluster belong
+** to phrases that are connected only by AND and NEAR operators (not OR or
+** NOT). When determining tokens to defer, each AND/NEAR cluster is considered
+** separately. The root of a tokens AND/NEAR cluster is stored in
+** Fts3TokenAndCost.pRoot.
+*/
+typedef struct Fts3TokenAndCost Fts3TokenAndCost;
+struct Fts3TokenAndCost {
+ Fts3Phrase *pPhrase; /* The phrase the token belongs to */
+ int iToken; /* Position of token in phrase */
+ Fts3PhraseToken *pToken; /* The token itself */
+ Fts3Expr *pRoot; /* Root of NEAR/AND cluster */
+ int nOvfl; /* Number of overflow pages to load doclist */
+ int iCol; /* The column the token must match */
+};
- if( iCol==p->nColumn+1 ){
- /* This call is a request for the "docid" column. Since "docid" is an
- ** alias for "rowid", use the xRowid() method to obtain the value.
- */
- sqlite3_int64 iRowid;
- rc = fts3RowidMethod(pCursor, &iRowid);
- sqlite3_result_int64(pContext, iRowid);
- }else if( iCol==p->nColumn ){
- /* The extra column whose name is the same as the table.
- ** Return a blob which is a pointer to the cursor.
+/*
+** This function is used to populate an allocated Fts3TokenAndCost array.
+**
+** If *pRc is not SQLITE_OK when this function is called, it is a no-op.
+** Otherwise, if an error occurs during execution, *pRc is set to an
+** SQLite error code.
+*/
+static void fts3EvalTokenCosts(
+ Fts3Cursor *pCsr, /* FTS Cursor handle */
+ Fts3Expr *pRoot, /* Root of current AND/NEAR cluster */
+ Fts3Expr *pExpr, /* Expression to consider */
+ Fts3TokenAndCost **ppTC, /* Write new entries to *(*ppTC)++ */
+ Fts3Expr ***ppOr, /* Write new OR root to *(*ppOr)++ */
+ int *pRc /* IN/OUT: Error code */
+){
+ if( *pRc==SQLITE_OK && pExpr ){
+ if( pExpr->eType==FTSQUERY_PHRASE ){
+ Fts3Phrase *pPhrase = pExpr->pPhrase;
+ int i;
+ for(i=0; *pRc==SQLITE_OK && i<pPhrase->nToken; i++){
+ Fts3TokenAndCost *pTC = (*ppTC)++;
+ pTC->pPhrase = pPhrase;
+ pTC->iToken = i;
+ pTC->pRoot = pRoot;
+ pTC->pToken = &pPhrase->aToken[i];
+ pTC->iCol = pPhrase->iColumn;
+ *pRc = sqlite3Fts3MsrOvfl(pCsr, pTC->pToken->pSegcsr, &pTC->nOvfl);
+ }
+ }else if( pExpr->eType!=FTSQUERY_NOT ){
+ if( pExpr->eType==FTSQUERY_OR ){
+ pRoot = pExpr->pLeft;
+ **ppOr = pRoot;
+ (*ppOr)++;
+ }
+ fts3EvalTokenCosts(pCsr, pRoot, pExpr->pLeft, ppTC, ppOr, pRc);
+ if( pExpr->eType==FTSQUERY_OR ){
+ pRoot = pExpr->pRight;
+ **ppOr = pRoot;
+ (*ppOr)++;
+ }
+ fts3EvalTokenCosts(pCsr, pRoot, pExpr->pRight, ppTC, ppOr, pRc);
+ }
+ }
+}
+
+/*
+** Determine the average document (row) size in pages. If successful,
+** write this value to *pnPage and return SQLITE_OK. Otherwise, return
+** an SQLite error code.
+**
+** The average document size in pages is calculated by first calculating
+** determining the average size in bytes, B. If B is less than the amount
+** of data that will fit on a single leaf page of an intkey table in
+** this database, then the average docsize is 1. Otherwise, it is 1 plus
+** the number of overflow pages consumed by a record B bytes in size.
+*/
+static int fts3EvalAverageDocsize(Fts3Cursor *pCsr, int *pnPage){
+ if( pCsr->nRowAvg==0 ){
+ /* The average document size, which is required to calculate the cost
+ ** of each doclist, has not yet been determined. Read the required
+ ** data from the %_stat table to calculate it.
+ **
+ ** Entry 0 of the %_stat table is a blob containing (nCol+1) FTS3
+ ** varints, where nCol is the number of columns in the FTS3 table.
+ ** The first varint is the number of documents currently stored in
+ ** the table. The following nCol varints contain the total amount of
+ ** data stored in all rows of each column of the table, from left
+ ** to right.
*/
- sqlite3_result_blob(pContext, &pCsr, sizeof(pCsr), SQLITE_TRANSIENT);
- rc = SQLITE_OK;
- }else{
- rc = fts3CursorSeek(0, pCsr);
- if( rc==SQLITE_OK ){
- sqlite3_result_value(pContext, sqlite3_column_value(pCsr->pStmt, iCol+1));
+ int rc;
+ Fts3Table *p = (Fts3Table*)pCsr->base.pVtab;
+ sqlite3_stmt *pStmt;
+ sqlite3_int64 nDoc = 0;
+ sqlite3_int64 nByte = 0;
+ const char *pEnd;
+ const char *a;
+
+ rc = sqlite3Fts3SelectDoctotal(p, &pStmt);
+ if( rc!=SQLITE_OK ) return rc;
+ a = sqlite3_column_blob(pStmt, 0);
+ assert( a );
+
+ pEnd = &a[sqlite3_column_bytes(pStmt, 0)];
+ a += sqlite3Fts3GetVarint(a, &nDoc);
+ while( a<pEnd ){
+ a += sqlite3Fts3GetVarint(a, &nByte);
}
+ if( nDoc==0 || nByte==0 ){
+ sqlite3_reset(pStmt);
+ return SQLITE_CORRUPT_VTAB;
+ }
+
+ pCsr->nDoc = nDoc;
+ pCsr->nRowAvg = (int)(((nByte / nDoc) + p->nPgsz) / p->nPgsz);
+ assert( pCsr->nRowAvg>0 );
+ rc = sqlite3_reset(pStmt);
+ if( rc!=SQLITE_OK ) return rc;
}
- return rc;
+
+ *pnPage = pCsr->nRowAvg;
+ return SQLITE_OK;
}
-/*
-** This function is the implementation of the xUpdate callback used by
-** FTS3 virtual tables. It is invoked by SQLite each time a row is to be
-** inserted, updated or deleted.
+/*
+** This function is called to select the tokens (if any) that will be
+** deferred. The array aTC[] has already been populated when this is
+** called.
+**
+** This function is called once for each AND/NEAR cluster in the
+** expression. Each invocation determines which tokens to defer within
+** the cluster with root node pRoot. See comments above the definition
+** of struct Fts3TokenAndCost for more details.
+**
+** If no error occurs, SQLITE_OK is returned and sqlite3Fts3DeferToken()
+** called on each token to defer. Otherwise, an SQLite error code is
+** returned.
*/
-static int fts3UpdateMethod(
- sqlite3_vtab *pVtab, /* Virtual table handle */
- int nArg, /* Size of argument array */
- sqlite3_value **apVal, /* Array of arguments */
- sqlite_int64 *pRowid /* OUT: The affected (or effected) rowid */
+static int fts3EvalSelectDeferred(
+ Fts3Cursor *pCsr, /* FTS Cursor handle */
+ Fts3Expr *pRoot, /* Consider tokens with this root node */
+ Fts3TokenAndCost *aTC, /* Array of expression tokens and costs */
+ int nTC /* Number of entries in aTC[] */
){
- return sqlite3Fts3UpdateMethod(pVtab, nArg, apVal, pRowid);
+ Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
+ int nDocSize = 0; /* Number of pages per doc loaded */
+ int rc = SQLITE_OK; /* Return code */
+ int ii; /* Iterator variable for various purposes */
+ int nOvfl = 0; /* Total overflow pages used by doclists */
+ int nToken = 0; /* Total number of tokens in cluster */
+
+ int nMinEst = 0; /* The minimum count for any phrase so far. */
+ int nLoad4 = 1; /* (Phrases that will be loaded)^4. */
+
+ /* Count the tokens in this AND/NEAR cluster. If none of the doclists
+ ** associated with the tokens spill onto overflow pages, or if there is
+ ** only 1 token, exit early. No tokens to defer in this case. */
+ for(ii=0; ii<nTC; ii++){
+ if( aTC[ii].pRoot==pRoot ){
+ nOvfl += aTC[ii].nOvfl;
+ nToken++;
+ }
+ }
+ if( nOvfl==0 || nToken<2 ) return SQLITE_OK;
+
+ /* Obtain the average docsize (in pages). */
+ rc = fts3EvalAverageDocsize(pCsr, &nDocSize);
+ assert( rc!=SQLITE_OK || nDocSize>0 );
+
+
+ /* Iterate through all tokens in this AND/NEAR cluster, in ascending order
+ ** of the number of overflow pages that will be loaded by the pager layer
+ ** to retrieve the entire doclist for the token from the full-text index.
+ ** Load the doclists for tokens that are either:
+ **
+ ** a. The cheapest token in the entire query (i.e. the one visited by the
+ ** first iteration of this loop), or
+ **
+ ** b. Part of a multi-token phrase.
+ **
+ ** After each token doclist is loaded, merge it with the others from the
+ ** same phrase and count the number of documents that the merged doclist
+ ** contains. Set variable "nMinEst" to the smallest number of documents in
+ ** any phrase doclist for which 1 or more token doclists have been loaded.
+ ** Let nOther be the number of other phrases for which it is certain that
+ ** one or more tokens will not be deferred.
+ **
+ ** Then, for each token, defer it if loading the doclist would result in
+ ** loading N or more overflow pages into memory, where N is computed as:
+ **
+ ** (nMinEst + 4^nOther - 1) / (4^nOther)
+ */
+ for(ii=0; ii<nToken && rc==SQLITE_OK; ii++){
+ int iTC; /* Used to iterate through aTC[] array. */
+ Fts3TokenAndCost *pTC = 0; /* Set to cheapest remaining token. */
+
+ /* Set pTC to point to the cheapest remaining token. */
+ for(iTC=0; iTC<nTC; iTC++){
+ if( aTC[iTC].pToken && aTC[iTC].pRoot==pRoot
+ && (!pTC || aTC[iTC].nOvfl<pTC->nOvfl)
+ ){
+ pTC = &aTC[iTC];
+ }
+ }
+ assert( pTC );
+
+ if( ii && pTC->nOvfl>=((nMinEst+(nLoad4/4)-1)/(nLoad4/4))*nDocSize ){
+ /* The number of overflow pages to load for this (and therefore all
+ ** subsequent) tokens is greater than the estimated number of pages
+ ** that will be loaded if all subsequent tokens are deferred.
+ */
+ Fts3PhraseToken *pToken = pTC->pToken;
+ rc = sqlite3Fts3DeferToken(pCsr, pToken, pTC->iCol);
+ fts3SegReaderCursorFree(pToken->pSegcsr);
+ pToken->pSegcsr = 0;
+ }else{
+ nLoad4 = nLoad4*4;
+ if( ii==0 || pTC->pPhrase->nToken>1 ){
+ /* Either this is the cheapest token in the entire query, or it is
+ ** part of a multi-token phrase. Either way, the entire doclist will
+ ** (eventually) be loaded into memory. It may as well be now. */
+ Fts3PhraseToken *pToken = pTC->pToken;
+ int nList = 0;
+ char *pList = 0;
+ rc = fts3TermSelect(pTab, pToken, pTC->iCol, &nList, &pList);
+ assert( rc==SQLITE_OK || pList==0 );
+ if( rc==SQLITE_OK ){
+ int nCount;
+ fts3EvalPhraseMergeToken(pTab, pTC->pPhrase, pTC->iToken,pList,nList);
+ nCount = fts3DoclistCountDocids(
+ pTC->pPhrase->doclist.aAll, pTC->pPhrase->doclist.nAll
+ );
+ if( ii==0 || nCount<nMinEst ) nMinEst = nCount;
+ }
+ }
+ }
+ pTC->pToken = 0;
+ }
+
+ return rc;
}
/*
-** Implementation of xSync() method. Flush the contents of the pending-terms
-** hash-table to the database.
+** This function is called from within the xFilter method. It initializes
+** the full-text query currently stored in pCsr->pExpr. To iterate through
+** the results of a query, the caller does:
+**
+** fts3EvalStart(pCsr);
+** while( 1 ){
+** fts3EvalNext(pCsr);
+** if( pCsr->bEof ) break;
+** ... return row pCsr->iPrevId to the caller ...
+** }
*/
-static int fts3SyncMethod(sqlite3_vtab *pVtab){
- int rc = sqlite3Fts3PendingTermsFlush((Fts3Table *)pVtab);
- sqlite3Fts3SegmentsClose((Fts3Table *)pVtab);
+static int fts3EvalStart(Fts3Cursor *pCsr){
+ Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
+ int rc = SQLITE_OK;
+ int nToken = 0;
+ int nOr = 0;
+
+ /* Allocate a MultiSegReader for each token in the expression. */
+ fts3EvalAllocateReaders(pCsr, pCsr->pExpr, &nToken, &nOr, &rc);
+
+ /* Determine which, if any, tokens in the expression should be deferred. */
+ if( rc==SQLITE_OK && nToken>1 && pTab->bHasStat ){
+ Fts3TokenAndCost *aTC;
+ Fts3Expr **apOr;
+ aTC = (Fts3TokenAndCost *)sqlite3_malloc(
+ sizeof(Fts3TokenAndCost) * nToken
+ + sizeof(Fts3Expr *) * nOr * 2
+ );
+ apOr = (Fts3Expr **)&aTC[nToken];
+
+ if( !aTC ){
+ rc = SQLITE_NOMEM;
+ }else{
+ int ii;
+ Fts3TokenAndCost *pTC = aTC;
+ Fts3Expr **ppOr = apOr;
+
+ fts3EvalTokenCosts(pCsr, 0, pCsr->pExpr, &pTC, &ppOr, &rc);
+ nToken = pTC-aTC;
+ nOr = ppOr-apOr;
+
+ if( rc==SQLITE_OK ){
+ rc = fts3EvalSelectDeferred(pCsr, 0, aTC, nToken);
+ for(ii=0; rc==SQLITE_OK && ii<nOr; ii++){
+ rc = fts3EvalSelectDeferred(pCsr, apOr[ii], aTC, nToken);
+ }
+ }
+
+ sqlite3_free(aTC);
+ }
+ }
+
+ fts3EvalStartReaders(pCsr, pCsr->pExpr, 1, &rc);
return rc;
}
/*
-** Implementation of xBegin() method. This is a no-op.
+** Invalidate the current position list for phrase pPhrase.
*/
-static int fts3BeginMethod(sqlite3_vtab *pVtab){
- UNUSED_PARAMETER(pVtab);
- assert( ((Fts3Table *)pVtab)->nPendingData==0 );
- return SQLITE_OK;
+static void fts3EvalInvalidatePoslist(Fts3Phrase *pPhrase){
+ if( pPhrase->doclist.bFreeList ){
+ sqlite3_free(pPhrase->doclist.pList);
+ }
+ pPhrase->doclist.pList = 0;
+ pPhrase->doclist.nList = 0;
+ pPhrase->doclist.bFreeList = 0;
}
/*
-** Implementation of xCommit() method. This is a no-op. The contents of
-** the pending-terms hash-table have already been flushed into the database
-** by fts3SyncMethod().
+** This function is called to edit the position list associated with
+** the phrase object passed as the fifth argument according to a NEAR
+** condition. For example:
+**
+** abc NEAR/5 "def ghi"
+**
+** Parameter nNear is passed the NEAR distance of the expression (5 in
+** the example above). When this function is called, *paPoslist points to
+** the position list, and *pnToken is the number of phrase tokens in, the
+** phrase on the other side of the NEAR operator to pPhrase. For example,
+** if pPhrase refers to the "def ghi" phrase, then *paPoslist points to
+** the position list associated with phrase "abc".
+**
+** All positions in the pPhrase position list that are not sufficiently
+** close to a position in the *paPoslist position list are removed. If this
+** leaves 0 positions, zero is returned. Otherwise, non-zero.
+**
+** Before returning, *paPoslist is set to point to the position lsit
+** associated with pPhrase. And *pnToken is set to the number of tokens in
+** pPhrase.
*/
-static int fts3CommitMethod(sqlite3_vtab *pVtab){
- UNUSED_PARAMETER(pVtab);
- assert( ((Fts3Table *)pVtab)->nPendingData==0 );
- return SQLITE_OK;
+static int fts3EvalNearTrim(
+ int nNear, /* NEAR distance. As in "NEAR/nNear". */
+ char *aTmp, /* Temporary space to use */
+ char **paPoslist, /* IN/OUT: Position list */
+ int *pnToken, /* IN/OUT: Tokens in phrase of *paPoslist */
+ Fts3Phrase *pPhrase /* The phrase object to trim the doclist of */
+){
+ int nParam1 = nNear + pPhrase->nToken;
+ int nParam2 = nNear + *pnToken;
+ int nNew;
+ char *p2;
+ char *pOut;
+ int res;
+
+ assert( pPhrase->doclist.pList );
+
+ p2 = pOut = pPhrase->doclist.pList;
+ res = fts3PoslistNearMerge(
+ &pOut, aTmp, nParam1, nParam2, paPoslist, &p2
+ );
+ if( res ){
+ nNew = (pOut - pPhrase->doclist.pList) - 1;
+ assert( pPhrase->doclist.pList[nNew]=='\0' );
+ assert( nNew<=pPhrase->doclist.nList && nNew>0 );
+ memset(&pPhrase->doclist.pList[nNew], 0, pPhrase->doclist.nList - nNew);
+ pPhrase->doclist.nList = nNew;
+ *paPoslist = pPhrase->doclist.pList;
+ *pnToken = pPhrase->nToken;
+ }
+
+ return res;
}
/*
-** Implementation of xRollback(). Discard the contents of the pending-terms
-** hash-table. Any changes made to the database are reverted by SQLite.
+** This function is a no-op if *pRc is other than SQLITE_OK when it is called.
+** Otherwise, it advances the expression passed as the second argument to
+** point to the next matching row in the database. Expressions iterate through
+** matching rows in docid order. Ascending order if Fts3Cursor.bDesc is zero,
+** or descending if it is non-zero.
+**
+** If an error occurs, *pRc is set to an SQLite error code. Otherwise, if
+** successful, the following variables in pExpr are set:
+**
+** Fts3Expr.bEof (non-zero if EOF - there is no next row)
+** Fts3Expr.iDocid (valid if bEof==0. The docid of the next row)
+**
+** If the expression is of type FTSQUERY_PHRASE, and the expression is not
+** at EOF, then the following variables are populated with the position list
+** for the phrase for the visited row:
+**
+** FTs3Expr.pPhrase->doclist.nList (length of pList in bytes)
+** FTs3Expr.pPhrase->doclist.pList (pointer to position list)
+**
+** It says above that this function advances the expression to the next
+** matching row. This is usually true, but there are the following exceptions:
+**
+** 1. Deferred tokens are not taken into account. If a phrase consists
+** entirely of deferred tokens, it is assumed to match every row in
+** the db. In this case the position-list is not populated at all.
+**
+** Or, if a phrase contains one or more deferred tokens and one or
+** more non-deferred tokens, then the expression is advanced to the
+** next possible match, considering only non-deferred tokens. In other
+** words, if the phrase is "A B C", and "B" is deferred, the expression
+** is advanced to the next row that contains an instance of "A * C",
+** where "*" may match any single token. The position list in this case
+** is populated as for "A * C" before returning.
+**
+** 2. NEAR is treated as AND. If the expression is "x NEAR y", it is
+** advanced to point to the next row that matches "x AND y".
+**
+** See fts3EvalTestDeferredAndNear() for details on testing if a row is
+** really a match, taking into account deferred tokens and NEAR operators.
*/
-static int fts3RollbackMethod(sqlite3_vtab *pVtab){
- sqlite3Fts3PendingTermsClear((Fts3Table *)pVtab);
- return SQLITE_OK;
+static void fts3EvalNextRow(
+ Fts3Cursor *pCsr, /* FTS Cursor handle */
+ Fts3Expr *pExpr, /* Expr. to advance to next matching row */
+ int *pRc /* IN/OUT: Error code */
+){
+ if( *pRc==SQLITE_OK ){
+ int bDescDoclist = pCsr->bDesc; /* Used by DOCID_CMP() macro */
+ assert( pExpr->bEof==0 );
+ pExpr->bStart = 1;
+
+ switch( pExpr->eType ){
+ case FTSQUERY_NEAR:
+ case FTSQUERY_AND: {
+ Fts3Expr *pLeft = pExpr->pLeft;
+ Fts3Expr *pRight = pExpr->pRight;
+ assert( !pLeft->bDeferred || !pRight->bDeferred );
+
+ if( pLeft->bDeferred ){
+ /* LHS is entirely deferred. So we assume it matches every row.
+ ** Advance the RHS iterator to find the next row visited. */
+ fts3EvalNextRow(pCsr, pRight, pRc);
+ pExpr->iDocid = pRight->iDocid;
+ pExpr->bEof = pRight->bEof;
+ }else if( pRight->bDeferred ){
+ /* RHS is entirely deferred. So we assume it matches every row.
+ ** Advance the LHS iterator to find the next row visited. */
+ fts3EvalNextRow(pCsr, pLeft, pRc);
+ pExpr->iDocid = pLeft->iDocid;
+ pExpr->bEof = pLeft->bEof;
+ }else{
+ /* Neither the RHS or LHS are deferred. */
+ fts3EvalNextRow(pCsr, pLeft, pRc);
+ fts3EvalNextRow(pCsr, pRight, pRc);
+ while( !pLeft->bEof && !pRight->bEof && *pRc==SQLITE_OK ){
+ sqlite3_int64 iDiff = DOCID_CMP(pLeft->iDocid, pRight->iDocid);
+ if( iDiff==0 ) break;
+ if( iDiff<0 ){
+ fts3EvalNextRow(pCsr, pLeft, pRc);
+ }else{
+ fts3EvalNextRow(pCsr, pRight, pRc);
+ }
+ }
+ pExpr->iDocid = pLeft->iDocid;
+ pExpr->bEof = (pLeft->bEof || pRight->bEof);
+ }
+ break;
+ }
+
+ case FTSQUERY_OR: {
+ Fts3Expr *pLeft = pExpr->pLeft;
+ Fts3Expr *pRight = pExpr->pRight;
+ sqlite3_int64 iCmp = DOCID_CMP(pLeft->iDocid, pRight->iDocid);
+
+ assert( pLeft->bStart || pLeft->iDocid==pRight->iDocid );
+ assert( pRight->bStart || pLeft->iDocid==pRight->iDocid );
+
+ if( pRight->bEof || (pLeft->bEof==0 && iCmp<0) ){
+ fts3EvalNextRow(pCsr, pLeft, pRc);
+ }else if( pLeft->bEof || (pRight->bEof==0 && iCmp>0) ){
+ fts3EvalNextRow(pCsr, pRight, pRc);
+ }else{
+ fts3EvalNextRow(pCsr, pLeft, pRc);
+ fts3EvalNextRow(pCsr, pRight, pRc);
+ }
+
+ pExpr->bEof = (pLeft->bEof && pRight->bEof);
+ iCmp = DOCID_CMP(pLeft->iDocid, pRight->iDocid);
+ if( pRight->bEof || (pLeft->bEof==0 && iCmp<0) ){
+ pExpr->iDocid = pLeft->iDocid;
+ }else{
+ pExpr->iDocid = pRight->iDocid;
+ }
+
+ break;
+ }
+
+ case FTSQUERY_NOT: {
+ Fts3Expr *pLeft = pExpr->pLeft;
+ Fts3Expr *pRight = pExpr->pRight;
+
+ if( pRight->bStart==0 ){
+ fts3EvalNextRow(pCsr, pRight, pRc);
+ assert( *pRc!=SQLITE_OK || pRight->bStart );
+ }
+
+ fts3EvalNextRow(pCsr, pLeft, pRc);
+ if( pLeft->bEof==0 ){
+ while( !*pRc
+ && !pRight->bEof
+ && DOCID_CMP(pLeft->iDocid, pRight->iDocid)>0
+ ){
+ fts3EvalNextRow(pCsr, pRight, pRc);
+ }
+ }
+ pExpr->iDocid = pLeft->iDocid;
+ pExpr->bEof = pLeft->bEof;
+ break;
+ }
+
+ default: {
+ Fts3Phrase *pPhrase = pExpr->pPhrase;
+ fts3EvalInvalidatePoslist(pPhrase);
+ *pRc = fts3EvalPhraseNext(pCsr, pPhrase, &pExpr->bEof);
+ pExpr->iDocid = pPhrase->doclist.iDocid;
+ break;
+ }
+ }
+ }
}
/*
-** Load the doclist associated with expression pExpr to pExpr->aDoclist.
-** The loaded doclist contains positions as well as the document ids.
-** This is used by the matchinfo(), snippet() and offsets() auxillary
-** functions.
+** If *pRc is not SQLITE_OK, or if pExpr is not the root node of a NEAR
+** cluster, then this function returns 1 immediately.
+**
+** Otherwise, it checks if the current row really does match the NEAR
+** expression, using the data currently stored in the position lists
+** (Fts3Expr->pPhrase.doclist.pList/nList) for each phrase in the expression.
+**
+** If the current row is a match, the position list associated with each
+** phrase in the NEAR expression is edited in place to contain only those
+** phrase instances sufficiently close to their peers to satisfy all NEAR
+** constraints. In this case it returns 1. If the NEAR expression does not
+** match the current row, 0 is returned. The position lists may or may not
+** be edited if 0 is returned.
*/
-SQLITE_PRIVATE int sqlite3Fts3ExprLoadDoclist(Fts3Cursor *pCsr, Fts3Expr *pExpr){
- int rc;
- assert( pExpr->eType==FTSQUERY_PHRASE && pExpr->pPhrase );
- assert( pCsr->eEvalmode==FTS3_EVAL_NEXT );
- rc = fts3EvalExpr(pCsr, pExpr, &pExpr->aDoclist, &pExpr->nDoclist, 1);
- return rc;
-}
+static int fts3EvalNearTest(Fts3Expr *pExpr, int *pRc){
+ int res = 1;
-SQLITE_PRIVATE int sqlite3Fts3ExprLoadFtDoclist(
- Fts3Cursor *pCsr,
- Fts3Expr *pExpr,
- char **paDoclist,
- int *pnDoclist
-){
- int rc;
- assert( pCsr->eEvalmode==FTS3_EVAL_NEXT );
- assert( pExpr->eType==FTSQUERY_PHRASE && pExpr->pPhrase );
- pCsr->eEvalmode = FTS3_EVAL_MATCHINFO;
- rc = fts3EvalExpr(pCsr, pExpr, paDoclist, pnDoclist, 1);
- pCsr->eEvalmode = FTS3_EVAL_NEXT;
- return rc;
+ /* The following block runs if pExpr is the root of a NEAR query.
+ ** For example, the query:
+ **
+ ** "w" NEAR "x" NEAR "y" NEAR "z"
+ **
+ ** which is represented in tree form as:
+ **
+ ** |
+ ** +--NEAR--+ <-- root of NEAR query
+ ** | |
+ ** +--NEAR--+ "z"
+ ** | |
+ ** +--NEAR--+ "y"
+ ** | |
+ ** "w" "x"
+ **
+ ** The right-hand child of a NEAR node is always a phrase. The
+ ** left-hand child may be either a phrase or a NEAR node. There are
+ ** no exceptions to this - it's the way the parser in fts3_expr.c works.
+ */
+ if( *pRc==SQLITE_OK
+ && pExpr->eType==FTSQUERY_NEAR
+ && pExpr->bEof==0
+ && (pExpr->pParent==0 || pExpr->pParent->eType!=FTSQUERY_NEAR)
+ ){
+ Fts3Expr *p;
+ int nTmp = 0; /* Bytes of temp space */
+ char *aTmp; /* Temp space for PoslistNearMerge() */
+
+ /* Allocate temporary working space. */
+ for(p=pExpr; p->pLeft; p=p->pLeft){
+ nTmp += p->pRight->pPhrase->doclist.nList;
+ }
+ nTmp += p->pPhrase->doclist.nList;
+ aTmp = sqlite3_malloc(nTmp*2);
+ if( !aTmp ){
+ *pRc = SQLITE_NOMEM;
+ res = 0;
+ }else{
+ char *aPoslist = p->pPhrase->doclist.pList;
+ int nToken = p->pPhrase->nToken;
+
+ for(p=p->pParent;res && p && p->eType==FTSQUERY_NEAR; p=p->pParent){
+ Fts3Phrase *pPhrase = p->pRight->pPhrase;
+ int nNear = p->nNear;
+ res = fts3EvalNearTrim(nNear, aTmp, &aPoslist, &nToken, pPhrase);
+ }
+
+ aPoslist = pExpr->pRight->pPhrase->doclist.pList;
+ nToken = pExpr->pRight->pPhrase->nToken;
+ for(p=pExpr->pLeft; p && res; p=p->pLeft){
+ int nNear = p->pParent->nNear;
+ Fts3Phrase *pPhrase = (
+ p->eType==FTSQUERY_NEAR ? p->pRight->pPhrase : p->pPhrase
+ );
+ res = fts3EvalNearTrim(nNear, aTmp, &aPoslist, &nToken, pPhrase);
+ }
+ }
+
+ sqlite3_free(aTmp);
+ }
+
+ return res;
}
/*
-** After ExprLoadDoclist() (see above) has been called, this function is
-** used to iterate/search through the position lists that make up the doclist
-** stored in pExpr->aDoclist.
+** This function is a helper function for fts3EvalTestDeferredAndNear().
+** Assuming no error occurs or has occurred, It returns non-zero if the
+** expression passed as the second argument matches the row that pCsr
+** currently points to, or zero if it does not.
+**
+** If *pRc is not SQLITE_OK when this function is called, it is a no-op.
+** If an error occurs during execution of this function, *pRc is set to
+** the appropriate SQLite error code. In this case the returned value is
+** undefined.
*/
-SQLITE_PRIVATE char *sqlite3Fts3FindPositions(
- Fts3Expr *pExpr, /* Access this expressions doclist */
- sqlite3_int64 iDocid, /* Docid associated with requested pos-list */
- int iCol /* Column of requested pos-list */
+static int fts3EvalTestExpr(
+ Fts3Cursor *pCsr, /* FTS cursor handle */
+ Fts3Expr *pExpr, /* Expr to test. May or may not be root. */
+ int *pRc /* IN/OUT: Error code */
){
- assert( pExpr->isLoaded );
- if( pExpr->aDoclist ){
- char *pEnd = &pExpr->aDoclist[pExpr->nDoclist];
- char *pCsr;
-
- if( pExpr->pCurrent==0 ){
- pExpr->pCurrent = pExpr->aDoclist;
- pExpr->iCurrent = 0;
- pExpr->pCurrent += sqlite3Fts3GetVarint(pExpr->pCurrent,&pExpr->iCurrent);
- }
- pCsr = pExpr->pCurrent;
- assert( pCsr );
+ int bHit = 1; /* Return value */
+ if( *pRc==SQLITE_OK ){
+ switch( pExpr->eType ){
+ case FTSQUERY_NEAR:
+ case FTSQUERY_AND:
+ bHit = (
+ fts3EvalTestExpr(pCsr, pExpr->pLeft, pRc)
+ && fts3EvalTestExpr(pCsr, pExpr->pRight, pRc)
+ && fts3EvalNearTest(pExpr, pRc)
+ );
- while( pCsr<pEnd ){
- if( pExpr->iCurrent<iDocid ){
- fts3PoslistCopy(0, &pCsr);
- if( pCsr<pEnd ){
- fts3GetDeltaVarint(&pCsr, &pExpr->iCurrent);
- }
- pExpr->pCurrent = pCsr;
- }else{
- if( pExpr->iCurrent==iDocid ){
- int iThis = 0;
- if( iCol<0 ){
- /* If iCol is negative, return a pointer to the start of the
- ** position-list (instead of a pointer to the start of a list
- ** of offsets associated with a specific column).
- */
- return pCsr;
+ /* If the NEAR expression does not match any rows, zero the doclist for
+ ** all phrases involved in the NEAR. This is because the snippet(),
+ ** offsets() and matchinfo() functions are not supposed to recognize
+ ** any instances of phrases that are part of unmatched NEAR queries.
+ ** For example if this expression:
+ **
+ ** ... MATCH 'a OR (b NEAR c)'
+ **
+ ** is matched against a row containing:
+ **
+ ** 'a b d e'
+ **
+ ** then any snippet() should ony highlight the "a" term, not the "b"
+ ** (as "b" is part of a non-matching NEAR clause).
+ */
+ if( bHit==0
+ && pExpr->eType==FTSQUERY_NEAR
+ && (pExpr->pParent==0 || pExpr->pParent->eType!=FTSQUERY_NEAR)
+ ){
+ Fts3Expr *p;
+ for(p=pExpr; p->pPhrase==0; p=p->pLeft){
+ if( p->pRight->iDocid==pCsr->iPrevId ){
+ fts3EvalInvalidatePoslist(p->pRight->pPhrase);
+ }
}
- while( iThis<iCol ){
- fts3ColumnlistCopy(0, &pCsr);
- if( *pCsr==0x00 ) return 0;
- pCsr++;
- pCsr += sqlite3Fts3GetVarint32(pCsr, &iThis);
+ if( p->iDocid==pCsr->iPrevId ){
+ fts3EvalInvalidatePoslist(p->pPhrase);
}
- if( iCol==iThis && (*pCsr&0xFE) ) return pCsr;
}
- return 0;
+
+ break;
+
+ case FTSQUERY_OR: {
+ int bHit1 = fts3EvalTestExpr(pCsr, pExpr->pLeft, pRc);
+ int bHit2 = fts3EvalTestExpr(pCsr, pExpr->pRight, pRc);
+ bHit = bHit1 || bHit2;
+ break;
+ }
+
+ case FTSQUERY_NOT:
+ bHit = (
+ fts3EvalTestExpr(pCsr, pExpr->pLeft, pRc)
+ && !fts3EvalTestExpr(pCsr, pExpr->pRight, pRc)
+ );
+ break;
+
+ default: {
+ if( pCsr->pDeferred
+ && (pExpr->iDocid==pCsr->iPrevId || pExpr->bDeferred)
+ ){
+ Fts3Phrase *pPhrase = pExpr->pPhrase;
+ assert( pExpr->bDeferred || pPhrase->doclist.bFreeList==0 );
+ if( pExpr->bDeferred ){
+ fts3EvalInvalidatePoslist(pPhrase);
+ }
+ *pRc = fts3EvalDeferredPhrase(pCsr, pPhrase);
+ bHit = (pPhrase->doclist.pList!=0);
+ pExpr->iDocid = pCsr->iPrevId;
+ }else{
+ bHit = (pExpr->bEof==0 && pExpr->iDocid==pCsr->iPrevId);
+ }
+ break;
}
}
}
-
- return 0;
+ return bHit;
}
/*
-** Helper function used by the implementation of the overloaded snippet(),
-** offsets() and optimize() SQL functions.
+** This function is called as the second part of each xNext operation when
+** iterating through the results of a full-text query. At this point the
+** cursor points to a row that matches the query expression, with the
+** following caveats:
**
-** If the value passed as the third argument is a blob of size
-** sizeof(Fts3Cursor*), then the blob contents are copied to the
-** output variable *ppCsr and SQLITE_OK is returned. Otherwise, an error
-** message is written to context pContext and SQLITE_ERROR returned. The
-** string passed via zFunc is used as part of the error message.
+** * Up until this point, "NEAR" operators in the expression have been
+** treated as "AND".
+**
+** * Deferred tokens have not yet been considered.
+**
+** If *pRc is not SQLITE_OK when this function is called, it immediately
+** returns 0. Otherwise, it tests whether or not after considering NEAR
+** operators and deferred tokens the current row is still a match for the
+** expression. It returns 1 if both of the following are true:
+**
+** 1. *pRc is SQLITE_OK when this function returns, and
+**
+** 2. After scanning the current FTS table row for the deferred tokens,
+** it is determined that the row does *not* match the query.
+**
+** Or, if no error occurs and it seems the current row does match the FTS
+** query, return 0.
*/
-static int fts3FunctionArg(
- sqlite3_context *pContext, /* SQL function call context */
- const char *zFunc, /* Function name */
- sqlite3_value *pVal, /* argv[0] passed to function */
- Fts3Cursor **ppCsr /* OUT: Store cursor handle here */
-){
- Fts3Cursor *pRet;
- if( sqlite3_value_type(pVal)!=SQLITE_BLOB
- || sqlite3_value_bytes(pVal)!=sizeof(Fts3Cursor *)
- ){
- char *zErr = sqlite3_mprintf("illegal first argument to %s", zFunc);
- sqlite3_result_error(pContext, zErr, -1);
- sqlite3_free(zErr);
- return SQLITE_ERROR;
+static int fts3EvalTestDeferredAndNear(Fts3Cursor *pCsr, int *pRc){
+ int rc = *pRc;
+ int bMiss = 0;
+ if( rc==SQLITE_OK ){
+
+ /* If there are one or more deferred tokens, load the current row into
+ ** memory and scan it to determine the position list for each deferred
+ ** token. Then, see if this row is really a match, considering deferred
+ ** tokens and NEAR operators (neither of which were taken into account
+ ** earlier, by fts3EvalNextRow()).
+ */
+ if( pCsr->pDeferred ){
+ rc = fts3CursorSeek(0, pCsr);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts3CacheDeferredDoclists(pCsr);
+ }
+ }
+ bMiss = (0==fts3EvalTestExpr(pCsr, pCsr->pExpr, &rc));
+
+ /* Free the position-lists accumulated for each deferred token above. */
+ sqlite3Fts3FreeDeferredDoclists(pCsr);
+ *pRc = rc;
}
- memcpy(&pRet, sqlite3_value_blob(pVal), sizeof(Fts3Cursor *));
- *ppCsr = pRet;
- return SQLITE_OK;
+ return (rc==SQLITE_OK && bMiss);
}
/*
-** Implementation of the snippet() function for FTS3
+** Advance to the next document that matches the FTS expression in
+** Fts3Cursor.pExpr.
*/
-static void fts3SnippetFunc(
- sqlite3_context *pContext, /* SQLite function call context */
- int nVal, /* Size of apVal[] array */
- sqlite3_value **apVal /* Array of arguments */
+static int fts3EvalNext(Fts3Cursor *pCsr){
+ int rc = SQLITE_OK; /* Return Code */
+ Fts3Expr *pExpr = pCsr->pExpr;
+ assert( pCsr->isEof==0 );
+ if( pExpr==0 ){
+ pCsr->isEof = 1;
+ }else{
+ do {
+ if( pCsr->isRequireSeek==0 ){
+ sqlite3_reset(pCsr->pStmt);
+ }
+ assert( sqlite3_data_count(pCsr->pStmt)==0 );
+ fts3EvalNextRow(pCsr, pExpr, &rc);
+ pCsr->isEof = pExpr->bEof;
+ pCsr->isRequireSeek = 1;
+ pCsr->isMatchinfoNeeded = 1;
+ pCsr->iPrevId = pExpr->iDocid;
+ }while( pCsr->isEof==0 && fts3EvalTestDeferredAndNear(pCsr, &rc) );
+ }
+ return rc;
+}
+
+/*
+** Restart interation for expression pExpr so that the next call to
+** fts3EvalNext() visits the first row. Do not allow incremental
+** loading or merging of phrase doclists for this iteration.
+**
+** If *pRc is other than SQLITE_OK when this function is called, it is
+** a no-op. If an error occurs within this function, *pRc is set to an
+** SQLite error code before returning.
+*/
+static void fts3EvalRestart(
+ Fts3Cursor *pCsr,
+ Fts3Expr *pExpr,
+ int *pRc
){
- Fts3Cursor *pCsr; /* Cursor handle passed through apVal[0] */
- const char *zStart = "<b>";
- const char *zEnd = "</b>";
- const char *zEllipsis = "<b>...</b>";
- int iCol = -1;
- int nToken = 15; /* Default number of tokens in snippet */
+ if( pExpr && *pRc==SQLITE_OK ){
+ Fts3Phrase *pPhrase = pExpr->pPhrase;
- /* There must be at least one argument passed to this function (otherwise
- ** the non-overloaded version would have been called instead of this one).
- */
- assert( nVal>=1 );
+ if( pPhrase ){
+ fts3EvalInvalidatePoslist(pPhrase);
+ if( pPhrase->bIncr ){
+ assert( pPhrase->nToken==1 );
+ assert( pPhrase->aToken[0].pSegcsr );
+ sqlite3Fts3MsrIncrRestart(pPhrase->aToken[0].pSegcsr);
+ *pRc = fts3EvalPhraseStart(pCsr, 0, pPhrase);
+ }
- if( nVal>6 ){
- sqlite3_result_error(pContext,
- "wrong number of arguments to function snippet()", -1);
- return;
- }
- if( fts3FunctionArg(pContext, "snippet", apVal[0], &pCsr) ) return;
+ pPhrase->doclist.pNextDocid = 0;
+ pPhrase->doclist.iDocid = 0;
+ }
- switch( nVal ){
- case 6: nToken = sqlite3_value_int(apVal[5]);
- case 5: iCol = sqlite3_value_int(apVal[4]);
- case 4: zEllipsis = (const char*)sqlite3_value_text(apVal[3]);
- case 3: zEnd = (const char*)sqlite3_value_text(apVal[2]);
- case 2: zStart = (const char*)sqlite3_value_text(apVal[1]);
- }
- if( !zEllipsis || !zEnd || !zStart ){
- sqlite3_result_error_nomem(pContext);
- }else if( SQLITE_OK==fts3CursorSeek(pContext, pCsr) ){
- sqlite3Fts3Snippet(pContext, pCsr, zStart, zEnd, zEllipsis, iCol, nToken);
+ pExpr->iDocid = 0;
+ pExpr->bEof = 0;
+ pExpr->bStart = 0;
+
+ fts3EvalRestart(pCsr, pExpr->pLeft, pRc);
+ fts3EvalRestart(pCsr, pExpr->pRight, pRc);
}
}
/*
-** Implementation of the offsets() function for FTS3
+** After allocating the Fts3Expr.aMI[] array for each phrase in the
+** expression rooted at pExpr, the cursor iterates through all rows matched
+** by pExpr, calling this function for each row. This function increments
+** the values in Fts3Expr.aMI[] according to the position-list currently
+** found in Fts3Expr.pPhrase->doclist.pList for each of the phrase
+** expression nodes.
*/
-static void fts3OffsetsFunc(
- sqlite3_context *pContext, /* SQLite function call context */
- int nVal, /* Size of argument array */
- sqlite3_value **apVal /* Array of arguments */
-){
- Fts3Cursor *pCsr; /* Cursor handle passed through apVal[0] */
+static void fts3EvalUpdateCounts(Fts3Expr *pExpr){
+ if( pExpr ){
+ Fts3Phrase *pPhrase = pExpr->pPhrase;
+ if( pPhrase && pPhrase->doclist.pList ){
+ int iCol = 0;
+ char *p = pPhrase->doclist.pList;
- UNUSED_PARAMETER(nVal);
+ assert( *p );
+ while( 1 ){
+ u8 c = 0;
+ int iCnt = 0;
+ while( 0xFE & (*p | c) ){
+ if( (c&0x80)==0 ) iCnt++;
+ c = *p++ & 0x80;
+ }
- assert( nVal==1 );
- if( fts3FunctionArg(pContext, "offsets", apVal[0], &pCsr) ) return;
- assert( pCsr );
- if( SQLITE_OK==fts3CursorSeek(pContext, pCsr) ){
- sqlite3Fts3Offsets(pContext, pCsr);
+ /* aMI[iCol*3 + 1] = Number of occurrences
+ ** aMI[iCol*3 + 2] = Number of rows containing at least one instance
+ */
+ pExpr->aMI[iCol*3 + 1] += iCnt;
+ pExpr->aMI[iCol*3 + 2] += (iCnt>0);
+ if( *p==0x00 ) break;
+ p++;
+ p += sqlite3Fts3GetVarint32(p, &iCol);
+ }
+ }
+
+ fts3EvalUpdateCounts(pExpr->pLeft);
+ fts3EvalUpdateCounts(pExpr->pRight);
}
}
-/*
-** Implementation of the special optimize() function for FTS3. This
-** function merges all segments in the database to a single segment.
-** Example usage is:
+/*
+** Expression pExpr must be of type FTSQUERY_PHRASE.
**
-** SELECT optimize(t) FROM t LIMIT 1;
+** If it is not already allocated and populated, this function allocates and
+** populates the Fts3Expr.aMI[] array for expression pExpr. If pExpr is part
+** of a NEAR expression, then it also allocates and populates the same array
+** for all other phrases that are part of the NEAR expression.
**
-** where 't' is the name of an FTS3 table.
+** SQLITE_OK is returned if the aMI[] array is successfully allocated and
+** populated. Otherwise, if an error occurs, an SQLite error code is returned.
*/
-static void fts3OptimizeFunc(
- sqlite3_context *pContext, /* SQLite function call context */
- int nVal, /* Size of argument array */
- sqlite3_value **apVal /* Array of arguments */
+static int fts3EvalGatherStats(
+ Fts3Cursor *pCsr, /* Cursor object */
+ Fts3Expr *pExpr /* FTSQUERY_PHRASE expression */
){
- int rc; /* Return code */
- Fts3Table *p; /* Virtual table handle */
- Fts3Cursor *pCursor; /* Cursor handle passed through apVal[0] */
+ int rc = SQLITE_OK; /* Return code */
- UNUSED_PARAMETER(nVal);
+ assert( pExpr->eType==FTSQUERY_PHRASE );
+ if( pExpr->aMI==0 ){
+ Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
+ Fts3Expr *pRoot; /* Root of NEAR expression */
+ Fts3Expr *p; /* Iterator used for several purposes */
- assert( nVal==1 );
- if( fts3FunctionArg(pContext, "optimize", apVal[0], &pCursor) ) return;
- p = (Fts3Table *)pCursor->base.pVtab;
- assert( p );
+ sqlite3_int64 iPrevId = pCsr->iPrevId;
+ sqlite3_int64 iDocid;
+ u8 bEof;
- rc = sqlite3Fts3Optimize(p);
+ /* Find the root of the NEAR expression */
+ pRoot = pExpr;
+ while( pRoot->pParent && pRoot->pParent->eType==FTSQUERY_NEAR ){
+ pRoot = pRoot->pParent;
+ }
+ iDocid = pRoot->iDocid;
+ bEof = pRoot->bEof;
+ assert( pRoot->bStart );
- switch( rc ){
- case SQLITE_OK:
- sqlite3_result_text(pContext, "Index optimized", -1, SQLITE_STATIC);
- break;
- case SQLITE_DONE:
- sqlite3_result_text(pContext, "Index already optimal", -1, SQLITE_STATIC);
- break;
- default:
- sqlite3_result_error_code(pContext, rc);
- break;
- }
-}
+ /* Allocate space for the aMSI[] array of each FTSQUERY_PHRASE node */
+ for(p=pRoot; p; p=p->pLeft){
+ Fts3Expr *pE = (p->eType==FTSQUERY_PHRASE?p:p->pRight);
+ assert( pE->aMI==0 );
+ pE->aMI = (u32 *)sqlite3_malloc(pTab->nColumn * 3 * sizeof(u32));
+ if( !pE->aMI ) return SQLITE_NOMEM;
+ memset(pE->aMI, 0, pTab->nColumn * 3 * sizeof(u32));
+ }
-/*
-** Implementation of the matchinfo() function for FTS3
-*/
-static void fts3MatchinfoFunc(
- sqlite3_context *pContext, /* SQLite function call context */
- int nVal, /* Size of argument array */
- sqlite3_value **apVal /* Array of arguments */
-){
- Fts3Cursor *pCsr; /* Cursor handle passed through apVal[0] */
- assert( nVal==1 || nVal==2 );
- if( SQLITE_OK==fts3FunctionArg(pContext, "matchinfo", apVal[0], &pCsr) ){
- const char *zArg = 0;
- if( nVal>1 ){
- zArg = (const char *)sqlite3_value_text(apVal[1]);
+ fts3EvalRestart(pCsr, pRoot, &rc);
+
+ while( pCsr->isEof==0 && rc==SQLITE_OK ){
+
+ do {
+ /* Ensure the %_content statement is reset. */
+ if( pCsr->isRequireSeek==0 ) sqlite3_reset(pCsr->pStmt);
+ assert( sqlite3_data_count(pCsr->pStmt)==0 );
+
+ /* Advance to the next document */
+ fts3EvalNextRow(pCsr, pRoot, &rc);
+ pCsr->isEof = pRoot->bEof;
+ pCsr->isRequireSeek = 1;
+ pCsr->isMatchinfoNeeded = 1;
+ pCsr->iPrevId = pRoot->iDocid;
+ }while( pCsr->isEof==0
+ && pRoot->eType==FTSQUERY_NEAR
+ && fts3EvalTestDeferredAndNear(pCsr, &rc)
+ );
+
+ if( rc==SQLITE_OK && pCsr->isEof==0 ){
+ fts3EvalUpdateCounts(pRoot);
+ }
+ }
+
+ pCsr->isEof = 0;
+ pCsr->iPrevId = iPrevId;
+
+ if( bEof ){
+ pRoot->bEof = bEof;
+ }else{
+ /* Caution: pRoot may iterate through docids in ascending or descending
+ ** order. For this reason, even though it seems more defensive, the
+ ** do loop can not be written:
+ **
+ ** do {...} while( pRoot->iDocid<iDocid && rc==SQLITE_OK );
+ */
+ fts3EvalRestart(pCsr, pRoot, &rc);
+ do {
+ fts3EvalNextRow(pCsr, pRoot, &rc);
+ assert( pRoot->bEof==0 );
+ }while( pRoot->iDocid!=iDocid && rc==SQLITE_OK );
+ fts3EvalTestDeferredAndNear(pCsr, &rc);
}
- sqlite3Fts3Matchinfo(pContext, pCsr, zArg);
}
+ return rc;
}
/*
-** This routine implements the xFindFunction method for the FTS3
-** virtual table.
+** This function is used by the matchinfo() module to query a phrase
+** expression node for the following information:
+**
+** 1. The total number of occurrences of the phrase in each column of
+** the FTS table (considering all rows), and
+**
+** 2. For each column, the number of rows in the table for which the
+** column contains at least one instance of the phrase.
+**
+** If no error occurs, SQLITE_OK is returned and the values for each column
+** written into the array aiOut as follows:
+**
+** aiOut[iCol*3 + 1] = Number of occurrences
+** aiOut[iCol*3 + 2] = Number of rows containing at least one instance
+**
+** Caveats:
+**
+** * If a phrase consists entirely of deferred tokens, then all output
+** values are set to the number of documents in the table. In other
+** words we assume that very common tokens occur exactly once in each
+** column of each row of the table.
+**
+** * If a phrase contains some deferred tokens (and some non-deferred
+** tokens), count the potential occurrence identified by considering
+** the non-deferred tokens instead of actual phrase occurrences.
+**
+** * If the phrase is part of a NEAR expression, then only phrase instances
+** that meet the NEAR constraint are included in the counts.
*/
-static int fts3FindFunctionMethod(
- sqlite3_vtab *pVtab, /* Virtual table handle */
- int nArg, /* Number of SQL function arguments */
- const char *zName, /* Name of SQL function */
- void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), /* OUT: Result */
- void **ppArg /* Unused */
+SQLITE_PRIVATE int sqlite3Fts3EvalPhraseStats(
+ Fts3Cursor *pCsr, /* FTS cursor handle */
+ Fts3Expr *pExpr, /* Phrase expression */
+ u32 *aiOut /* Array to write results into (see above) */
){
- struct Overloaded {
- const char *zName;
- void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
- } aOverload[] = {
- { "snippet", fts3SnippetFunc },
- { "offsets", fts3OffsetsFunc },
- { "optimize", fts3OptimizeFunc },
- { "matchinfo", fts3MatchinfoFunc },
- };
- int i; /* Iterator variable */
-
- UNUSED_PARAMETER(pVtab);
- UNUSED_PARAMETER(nArg);
- UNUSED_PARAMETER(ppArg);
+ Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
+ int rc = SQLITE_OK;
+ int iCol;
- for(i=0; i<SizeofArray(aOverload); i++){
- if( strcmp(zName, aOverload[i].zName)==0 ){
- *pxFunc = aOverload[i].xFunc;
- return 1;
+ if( pExpr->bDeferred && pExpr->pParent->eType!=FTSQUERY_NEAR ){
+ assert( pCsr->nDoc>0 );
+ for(iCol=0; iCol<pTab->nColumn; iCol++){
+ aiOut[iCol*3 + 1] = (u32)pCsr->nDoc;
+ aiOut[iCol*3 + 2] = (u32)pCsr->nDoc;
+ }
+ }else{
+ rc = fts3EvalGatherStats(pCsr, pExpr);
+ if( rc==SQLITE_OK ){
+ assert( pExpr->aMI );
+ for(iCol=0; iCol<pTab->nColumn; iCol++){
+ aiOut[iCol*3 + 1] = pExpr->aMI[iCol*3 + 1];
+ aiOut[iCol*3 + 2] = pExpr->aMI[iCol*3 + 2];
+ }
}
}
- /* No function of the specified name was found. Return 0. */
- return 0;
+ return rc;
}
/*
-** Implementation of FTS3 xRename method. Rename an fts3 table.
+** The expression pExpr passed as the second argument to this function
+** must be of type FTSQUERY_PHRASE.
+**
+** The returned value is either NULL or a pointer to a buffer containing
+** a position-list indicating the occurrences of the phrase in column iCol
+** of the current row.
+**
+** More specifically, the returned buffer contains 1 varint for each
+** occurence of the phrase in the column, stored using the normal (delta+2)
+** compression and is terminated by either an 0x01 or 0x00 byte. For example,
+** if the requested column contains "a b X c d X X" and the position-list
+** for 'X' is requested, the buffer returned may contain:
+**
+** 0x04 0x05 0x03 0x01 or 0x04 0x05 0x03 0x00
+**
+** This function works regardless of whether or not the phrase is deferred,
+** incremental, or neither.
*/
-static int fts3RenameMethod(
- sqlite3_vtab *pVtab, /* Virtual table handle */
- const char *zName /* New name of table */
+SQLITE_PRIVATE char *sqlite3Fts3EvalPhrasePoslist(
+ Fts3Cursor *pCsr, /* FTS3 cursor object */
+ Fts3Expr *pExpr, /* Phrase to return doclist for */
+ int iCol /* Column to return position list for */
){
- Fts3Table *p = (Fts3Table *)pVtab;
- sqlite3 *db = p->db; /* Database connection */
- int rc; /* Return Code */
-
- rc = sqlite3Fts3PendingTermsFlush(p);
- if( rc!=SQLITE_OK ){
- return rc;
+ Fts3Phrase *pPhrase = pExpr->pPhrase;
+ Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
+ char *pIter = pPhrase->doclist.pList;
+ int iThis;
+
+ assert( iCol>=0 && iCol<pTab->nColumn );
+ if( !pIter
+ || pExpr->bEof
+ || pExpr->iDocid!=pCsr->iPrevId
+ || (pPhrase->iColumn<pTab->nColumn && pPhrase->iColumn!=iCol)
+ ){
+ return 0;
}
- fts3DbExec(&rc, db,
- "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';",
- p->zDb, p->zName, zName
- );
- if( p->bHasDocsize ){
- fts3DbExec(&rc, db,
- "ALTER TABLE %Q.'%q_docsize' RENAME TO '%q_docsize';",
- p->zDb, p->zName, zName
- );
+ assert( pPhrase->doclist.nList>0 );
+ if( *pIter==0x01 ){
+ pIter++;
+ pIter += sqlite3Fts3GetVarint32(pIter, &iThis);
+ }else{
+ iThis = 0;
}
- if( p->bHasStat ){
- fts3DbExec(&rc, db,
- "ALTER TABLE %Q.'%q_stat' RENAME TO '%q_stat';",
- p->zDb, p->zName, zName
- );
+ while( iThis<iCol ){
+ fts3ColumnlistCopy(0, &pIter);
+ if( *pIter==0x00 ) return 0;
+ pIter++;
+ pIter += sqlite3Fts3GetVarint32(pIter, &iThis);
}
- fts3DbExec(&rc, db,
- "ALTER TABLE %Q.'%q_segments' RENAME TO '%q_segments';",
- p->zDb, p->zName, zName
- );
- fts3DbExec(&rc, db,
- "ALTER TABLE %Q.'%q_segdir' RENAME TO '%q_segdir';",
- p->zDb, p->zName, zName
- );
- return rc;
-}
-
-static const sqlite3_module fts3Module = {
- /* iVersion */ 0,
- /* xCreate */ fts3CreateMethod,
- /* xConnect */ fts3ConnectMethod,
- /* xBestIndex */ fts3BestIndexMethod,
- /* xDisconnect */ fts3DisconnectMethod,
- /* xDestroy */ fts3DestroyMethod,
- /* xOpen */ fts3OpenMethod,
- /* xClose */ fts3CloseMethod,
- /* xFilter */ fts3FilterMethod,
- /* xNext */ fts3NextMethod,
- /* xEof */ fts3EofMethod,
- /* xColumn */ fts3ColumnMethod,
- /* xRowid */ fts3RowidMethod,
- /* xUpdate */ fts3UpdateMethod,
- /* xBegin */ fts3BeginMethod,
- /* xSync */ fts3SyncMethod,
- /* xCommit */ fts3CommitMethod,
- /* xRollback */ fts3RollbackMethod,
- /* xFindFunction */ fts3FindFunctionMethod,
- /* xRename */ fts3RenameMethod,
-};
-/*
-** This function is registered as the module destructor (called when an
-** FTS3 enabled database connection is closed). It frees the memory
-** allocated for the tokenizer hash table.
-*/
-static void hashDestroy(void *p){
- Fts3Hash *pHash = (Fts3Hash *)p;
- sqlite3Fts3HashClear(pHash);
- sqlite3_free(pHash);
+ return ((iCol==iThis)?pIter:0);
}
/*
-** The fts3 built-in tokenizers - "simple", "porter" and "icu"- are
-** implemented in files fts3_tokenizer1.c, fts3_porter.c and fts3_icu.c
-** respectively. The following three forward declarations are for functions
-** declared in these files used to retrieve the respective implementations.
+** Free all components of the Fts3Phrase structure that were allocated by
+** the eval module. Specifically, this means to free:
**
-** Calling sqlite3Fts3SimpleTokenizerModule() sets the value pointed
-** to by the argument to point to the "simple" tokenizer implementation.
-** And so on.
-*/
-SQLITE_PRIVATE void sqlite3Fts3SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule);
-SQLITE_PRIVATE void sqlite3Fts3PorterTokenizerModule(sqlite3_tokenizer_module const**ppModule);
-#ifdef SQLITE_ENABLE_ICU
-SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(sqlite3_tokenizer_module const**ppModule);
-#endif
-
-/*
-** Initialise the fts3 extension. If this extension is built as part
-** of the sqlite library, then this function is called directly by
-** SQLite. If fts3 is built as a dynamically loadable extension, this
-** function is called by the sqlite3_extension_init() entry point.
+** * the contents of pPhrase->doclist, and
+** * any Fts3MultiSegReader objects held by phrase tokens.
*/
-SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){
- int rc = SQLITE_OK;
- Fts3Hash *pHash = 0;
- const sqlite3_tokenizer_module *pSimple = 0;
- const sqlite3_tokenizer_module *pPorter = 0;
-
-#ifdef SQLITE_ENABLE_ICU
- const sqlite3_tokenizer_module *pIcu = 0;
- sqlite3Fts3IcuTokenizerModule(&pIcu);
-#endif
-
- rc = sqlite3Fts3InitAux(db);
- if( rc!=SQLITE_OK ) return rc;
-
- sqlite3Fts3SimpleTokenizerModule(&pSimple);
- sqlite3Fts3PorterTokenizerModule(&pPorter);
-
- /* Allocate and initialise the hash-table used to store tokenizers. */
- pHash = sqlite3_malloc(sizeof(Fts3Hash));
- if( !pHash ){
- rc = SQLITE_NOMEM;
- }else{
- sqlite3Fts3HashInit(pHash, FTS3_HASH_STRING, 1);
- }
-
- /* Load the built-in tokenizers into the hash table */
- if( rc==SQLITE_OK ){
- if( sqlite3Fts3HashInsert(pHash, "simple", 7, (void *)pSimple)
- || sqlite3Fts3HashInsert(pHash, "porter", 7, (void *)pPorter)
-#ifdef SQLITE_ENABLE_ICU
- || (pIcu && sqlite3Fts3HashInsert(pHash, "icu", 4, (void *)pIcu))
-#endif
- ){
- rc = SQLITE_NOMEM;
- }
- }
-
-#ifdef SQLITE_TEST
- if( rc==SQLITE_OK ){
- rc = sqlite3Fts3ExprInitTestInterface(db);
- }
-#endif
-
- /* Create the virtual table wrapper around the hash-table and overload
- ** the two scalar functions. If this is successful, register the
- ** module with sqlite.
- */
- if( SQLITE_OK==rc
- && SQLITE_OK==(rc = sqlite3Fts3InitHashTable(db, pHash, "fts3_tokenizer"))
- && SQLITE_OK==(rc = sqlite3_overload_function(db, "snippet", -1))
- && SQLITE_OK==(rc = sqlite3_overload_function(db, "offsets", 1))
- && SQLITE_OK==(rc = sqlite3_overload_function(db, "matchinfo", 1))
- && SQLITE_OK==(rc = sqlite3_overload_function(db, "matchinfo", 2))
- && SQLITE_OK==(rc = sqlite3_overload_function(db, "optimize", 1))
- ){
- rc = sqlite3_create_module_v2(
- db, "fts3", &fts3Module, (void *)pHash, hashDestroy
- );
- if( rc==SQLITE_OK ){
- rc = sqlite3_create_module_v2(
- db, "fts4", &fts3Module, (void *)pHash, 0
- );
+SQLITE_PRIVATE void sqlite3Fts3EvalPhraseCleanup(Fts3Phrase *pPhrase){
+ if( pPhrase ){
+ int i;
+ sqlite3_free(pPhrase->doclist.aAll);
+ fts3EvalInvalidatePoslist(pPhrase);
+ memset(&pPhrase->doclist, 0, sizeof(Fts3Doclist));
+ for(i=0; i<pPhrase->nToken; i++){
+ fts3SegReaderCursorFree(pPhrase->aToken[i].pSegcsr);
+ pPhrase->aToken[i].pSegcsr = 0;
}
- return rc;
- }
-
- /* An error has occurred. Delete the hash table and return the error code. */
- assert( rc!=SQLITE_OK );
- if( pHash ){
- sqlite3Fts3HashClear(pHash);
- sqlite3_free(pHash);
}
- return rc;
}
#if !SQLITE_CORE
+/*
+** Initialize API pointer table, if required.
+*/
SQLITE_API int sqlite3_extension_init(
sqlite3 *db,
char **pzErrMsg,
******************************************************************************
**
*/
-
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
+/* #include <string.h> */
+/* #include <assert.h> */
typedef struct Fts3auxTable Fts3auxTable;
typedef struct Fts3auxCursor Fts3auxCursor;
struct Fts3auxCursor {
sqlite3_vtab_cursor base; /* Base class used by SQLite core */
- Fts3SegReaderCursor csr; /* Must be right after "base" */
+ Fts3MultiSegReader csr; /* Must be right after "base" */
Fts3SegFilter filter;
char *zStop;
int nStop; /* Byte-length of string zStop */
p->pFts3Tab->zDb = (char *)&p->pFts3Tab[1];
p->pFts3Tab->zName = &p->pFts3Tab->zDb[nDb+1];
p->pFts3Tab->db = db;
+ p->pFts3Tab->nIndex = 1;
memcpy((char *)p->pFts3Tab->zDb, zDb, nDb);
memcpy((char *)p->pFts3Tab->zName, zFts3, nFts3);
int isScan;
UNUSED_PARAMETER(nVal);
+ UNUSED_PARAMETER(idxStr);
assert( idxStr==0 );
assert( idxNum==FTS4AUX_EQ_CONSTRAINT || idxNum==0
if( pCsr->zStop==0 ) return SQLITE_NOMEM;
}
- rc = sqlite3Fts3SegReaderCursor(pFts3, FTS3_SEGCURSOR_ALL,
+ rc = sqlite3Fts3SegReaderCursor(pFts3, 0, FTS3_SEGCURSOR_ALL,
pCsr->filter.zTerm, pCsr->filter.nTerm, 0, isScan, &pCsr->csr
);
if( rc==SQLITE_OK ){
0, /* xCommit */
0, /* xRollback */
0, /* xFindFunction */
- 0 /* xRename */
+ 0, /* xRename */
+ 0, /* xSavepoint */
+ 0, /* xRelease */
+ 0 /* xRollbackTo */
};
int rc; /* Return code */
*/
#define SQLITE_FTS3_DEFAULT_NEAR_PARAM 10
+/* #include <string.h> */
+/* #include <assert.h> */
+/*
+** isNot:
+** This variable is used by function getNextNode(). When getNextNode() is
+** called, it sets ParseContext.isNot to true if the 'next node' is a
+** FTSQUERY_PHRASE with a unary "-" attached to it. i.e. "mysql" in the
+** FTS3 query "sqlite -mysql". Otherwise, ParseContext.isNot is set to
+** zero.
+*/
typedef struct ParseContext ParseContext;
struct ParseContext {
sqlite3_tokenizer *pTokenizer; /* Tokenizer module */
const char **azCol; /* Array of column names for fts3 table */
int nCol; /* Number of entries in azCol[] */
int iDefaultCol; /* Default column to query */
+ int isNot; /* True if getNextNode() sees a unary - */
sqlite3_context *pCtx; /* Write error message here */
int nNest; /* Number of nested brackets */
};
iEnd++;
}
if( !sqlite3_fts3_enable_parentheses && iStart>0 && z[iStart-1]=='-' ){
- pRet->pPhrase->isNot = 1;
+ pParse->isNot = 1;
}
}
nConsumed = iEnd;
char *zTemp = 0;
int nTemp = 0;
+ const int nSpace = sizeof(Fts3Expr) + sizeof(Fts3Phrase);
+ int nToken = 0;
+
+ /* The final Fts3Expr data structure, including the Fts3Phrase,
+ ** Fts3PhraseToken structures token buffers are all stored as a single
+ ** allocation so that the expression can be freed with a single call to
+ ** sqlite3_free(). Setting this up requires a two pass approach.
+ **
+ ** The first pass, in the block below, uses a tokenizer cursor to iterate
+ ** through the tokens in the expression. This pass uses fts3ReallocOrFree()
+ ** to assemble data in two dynamic buffers:
+ **
+ ** Buffer p: Points to the Fts3Expr structure, followed by the Fts3Phrase
+ ** structure, followed by the array of Fts3PhraseToken
+ ** structures. This pass only populates the Fts3PhraseToken array.
+ **
+ ** Buffer zTemp: Contains copies of all tokens.
+ **
+ ** The second pass, in the block that begins "if( rc==SQLITE_DONE )" below,
+ ** appends buffer zTemp to buffer p, and fills in the Fts3Expr and Fts3Phrase
+ ** structures.
+ */
rc = pModule->xOpen(pTokenizer, zInput, nInput, &pCursor);
if( rc==SQLITE_OK ){
int ii;
pCursor->pTokenizer = pTokenizer;
for(ii=0; rc==SQLITE_OK; ii++){
- const char *zToken;
- int nToken, iBegin, iEnd, iPos;
- rc = pModule->xNext(pCursor, &zToken, &nToken, &iBegin, &iEnd, &iPos);
+ const char *zByte;
+ int nByte, iBegin, iEnd, iPos;
+ rc = pModule->xNext(pCursor, &zByte, &nByte, &iBegin, &iEnd, &iPos);
if( rc==SQLITE_OK ){
- int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase);
- p = fts3ReallocOrFree(p, nByte+ii*sizeof(Fts3PhraseToken));
- zTemp = fts3ReallocOrFree(zTemp, nTemp + nToken);
- if( !p || !zTemp ){
- goto no_mem;
- }
- if( ii==0 ){
- memset(p, 0, nByte);
- p->pPhrase = (Fts3Phrase *)&p[1];
- }
- p->pPhrase = (Fts3Phrase *)&p[1];
- memset(&p->pPhrase->aToken[ii], 0, sizeof(Fts3PhraseToken));
- p->pPhrase->nToken = ii+1;
- p->pPhrase->aToken[ii].n = nToken;
- memcpy(&zTemp[nTemp], zToken, nToken);
- nTemp += nToken;
- if( iEnd<nInput && zInput[iEnd]=='*' ){
- p->pPhrase->aToken[ii].isPrefix = 1;
- }else{
- p->pPhrase->aToken[ii].isPrefix = 0;
- }
+ Fts3PhraseToken *pToken;
+
+ p = fts3ReallocOrFree(p, nSpace + ii*sizeof(Fts3PhraseToken));
+ if( !p ) goto no_mem;
+
+ zTemp = fts3ReallocOrFree(zTemp, nTemp + nByte);
+ if( !zTemp ) goto no_mem;
+
+ assert( nToken==ii );
+ pToken = &((Fts3Phrase *)(&p[1]))->aToken[ii];
+ memset(pToken, 0, sizeof(Fts3PhraseToken));
+
+ memcpy(&zTemp[nTemp], zByte, nByte);
+ nTemp += nByte;
+
+ pToken->n = nByte;
+ pToken->isPrefix = (iEnd<nInput && zInput[iEnd]=='*');
+ nToken = ii+1;
}
}
if( rc==SQLITE_DONE ){
int jj;
- char *zNew = NULL;
- int nNew = 0;
- int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase);
- nByte += (p?(p->pPhrase->nToken-1):0) * sizeof(Fts3PhraseToken);
- p = fts3ReallocOrFree(p, nByte + nTemp);
- if( !p ){
- goto no_mem;
- }
- if( zTemp ){
- zNew = &(((char *)p)[nByte]);
- memcpy(zNew, zTemp, nTemp);
- }else{
- memset(p, 0, nByte+nTemp);
- }
+ char *zBuf = 0;
+
+ p = fts3ReallocOrFree(p, nSpace + nToken*sizeof(Fts3PhraseToken) + nTemp);
+ if( !p ) goto no_mem;
+ memset(p, 0, (char *)&(((Fts3Phrase *)&p[1])->aToken[0])-(char *)p);
+ p->eType = FTSQUERY_PHRASE;
p->pPhrase = (Fts3Phrase *)&p[1];
+ p->pPhrase->iColumn = pParse->iDefaultCol;
+ p->pPhrase->nToken = nToken;
+
+ zBuf = (char *)&p->pPhrase->aToken[nToken];
+ memcpy(zBuf, zTemp, nTemp);
+ sqlite3_free(zTemp);
+
for(jj=0; jj<p->pPhrase->nToken; jj++){
- p->pPhrase->aToken[jj].z = &zNew[nNew];
- nNew += p->pPhrase->aToken[jj].n;
+ p->pPhrase->aToken[jj].z = zBuf;
+ zBuf += p->pPhrase->aToken[jj].n;
}
- sqlite3_free(zTemp);
- p->eType = FTSQUERY_PHRASE;
- p->pPhrase->iColumn = pParse->iDefaultCol;
rc = SQLITE_OK;
}
const char *zInput = z;
int nInput = n;
+ pParse->isNot = 0;
+
/* Skip over any whitespace before checking for a keyword, an open or
** close bracket, or a quoted string.
*/
int isPhrase;
if( !sqlite3_fts3_enable_parentheses
- && p->eType==FTSQUERY_PHRASE && p->pPhrase->isNot
+ && p->eType==FTSQUERY_PHRASE && pParse->isNot
){
/* Create an implicit NOT operator. */
Fts3Expr *pNot = fts3MallocZero(sizeof(Fts3Expr));
p = pPrev;
}else{
int eType = p->eType;
- assert( eType!=FTSQUERY_PHRASE || !p->pPhrase->isNot );
isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft);
/* The isRequirePhrase variable is set to true if a phrase or
*/
SQLITE_PRIVATE void sqlite3Fts3ExprFree(Fts3Expr *p){
if( p ){
+ assert( p->eType==FTSQUERY_PHRASE || p->pPhrase==0 );
sqlite3Fts3ExprFree(p->pLeft);
sqlite3Fts3ExprFree(p->pRight);
- sqlite3_free(p->aDoclist);
+ sqlite3Fts3EvalPhraseCleanup(p->pPhrase);
+ sqlite3_free(p->aMI);
sqlite3_free(p);
}
}
#ifdef SQLITE_TEST
+/* #include <stdio.h> */
/*
** Function to query the hash-table of tokenizers (see README.tokenizers).
Fts3Phrase *pPhrase = pExpr->pPhrase;
int i;
zBuf = sqlite3_mprintf(
- "%zPHRASE %d %d", zBuf, pPhrase->iColumn, pPhrase->isNot);
+ "%zPHRASE %d 0", zBuf, pPhrase->iColumn);
for(i=0; zBuf && i<pPhrase->nToken; i++){
zBuf = sqlite3_mprintf("%z %.*s%s", zBuf,
pPhrase->aToken[i].n, pPhrase->aToken[i].z,
*/
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
+/* #include <assert.h> */
+/* #include <stdlib.h> */
+/* #include <string.h> */
/*
*/
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
-
+/* #include <assert.h> */
+/* #include <stdlib.h> */
+/* #include <stdio.h> */
+/* #include <string.h> */
/*
*/
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
-#ifndef SQLITE_CORE
- SQLITE_EXTENSION_INIT1
-#endif
-
+/* #include <assert.h> */
+/* #include <string.h> */
/*
** Implementation of the SQL scalar function for accessing the underlying
){
int rc;
char *z = (char *)zArg;
- int n;
+ int n = 0;
char *zCopy;
char *zEnd; /* Pointer to nul-term of zCopy */
sqlite3_tokenizer_module *m;
#ifdef SQLITE_TEST
+/* #include <tcl.h> */
+/* #include <string.h> */
/*
** Implementation of a special SQL scalar function for testing tokenizers
*/
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
-
+/* #include <assert.h> */
+/* #include <stdlib.h> */
+/* #include <stdio.h> */
+/* #include <string.h> */
typedef struct simple_tokenizer {
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
+/* #include <string.h> */
+/* #include <assert.h> */
+/* #include <stdlib.h> */
/*
** When full-text index nodes are loaded from disk, the buffer that they
*/
#define FTS3_NODE_PADDING (FTS3_VARINT_MAX*2)
+/*
+** Under certain circumstances, b-tree nodes (doclists) can be loaded into
+** memory incrementally instead of all at once. This can be a big performance
+** win (reduced IO and CPU) if SQLite stops calling the virtual table xNext()
+** method before retrieving all query results (as may happen, for example,
+** if a query has a LIMIT clause).
+**
+** Incremental loading is used for b-tree nodes FTS3_NODE_CHUNK_THRESHOLD
+** bytes and larger. Nodes are loaded in chunks of FTS3_NODE_CHUNKSIZE bytes.
+** The code is written so that the hard lower-limit for each of these values
+** is 1. Clearly such small values would be inefficient, but can be useful
+** for testing purposes.
+**
+** If this module is built with SQLITE_TEST defined, these constants may
+** be overridden at runtime for testing purposes. File fts3_test.c contains
+** a Tcl interface to read and write the values.
+*/
+#ifdef SQLITE_TEST
+int test_fts3_node_chunksize = (4*1024);
+int test_fts3_node_chunk_threshold = (4*1024)*4;
+# define FTS3_NODE_CHUNKSIZE test_fts3_node_chunksize
+# define FTS3_NODE_CHUNK_THRESHOLD test_fts3_node_chunk_threshold
+#else
+# define FTS3_NODE_CHUNKSIZE (4*1024)
+# define FTS3_NODE_CHUNK_THRESHOLD (FTS3_NODE_CHUNKSIZE*4)
+#endif
+
typedef struct PendingList PendingList;
typedef struct SegmentNode SegmentNode;
typedef struct SegmentWriter SegmentWriter;
/*
-** Data structure used while accumulating terms in the pending-terms hash
-** table. The hash table entry maps from term (a string) to a malloc'd
-** instance of this structure.
+** An instance of the following data structure is used to build doclists
+** incrementally. See function fts3PendingListAppend() for details.
*/
struct PendingList {
int nData;
**
** sqlite3Fts3SegReaderNew()
** sqlite3Fts3SegReaderFree()
-** sqlite3Fts3SegReaderCost()
** sqlite3Fts3SegReaderIterate()
**
** Methods used to manipulate Fts3SegReader structures:
char *aNode; /* Pointer to node data (or NULL) */
int nNode; /* Size of buffer at aNode (or 0) */
+ int nPopulate; /* If >0, bytes of buffer aNode[] loaded */
+ sqlite3_blob *pBlob; /* If not NULL, blob handle to read node */
+
Fts3HashElem **ppNextElem;
/* Variables set by fts3SegReaderNext(). These may be read directly
char *aDoclist; /* Pointer to doclist of current entry */
int nDoclist; /* Size of doclist in current entry */
- /* The following variables are used to iterate through the current doclist */
+ /* The following variables are used by fts3SegReaderNextDocid() to iterate
+ ** through the current doclist (aDoclist/nDoclist).
+ */
char *pOffsetList;
+ int nOffsetList; /* For descending pending seg-readers only */
sqlite3_int64 iDocid;
};
** fts3NodeAddTerm()
** fts3NodeWrite()
** fts3NodeFree()
+**
+** When a b+tree is written to the database (either as a result of a merge
+** or the pending-terms table being flushed), leaves are written into the
+** database file as soon as they are completely populated. The interior of
+** the tree is assembled in memory and written out only once all leaves have
+** been populated and stored. This is Ok, as the b+-tree fanout is usually
+** very large, meaning that the interior of the tree consumes relatively
+** little memory.
*/
struct SegmentNode {
SegmentNode *pParent; /* Parent node (or NULL for root node) */
#define SQL_NEXT_SEGMENTS_ID 10
#define SQL_INSERT_SEGDIR 11
#define SQL_SELECT_LEVEL 12
-#define SQL_SELECT_ALL_LEVEL 13
+#define SQL_SELECT_LEVEL_RANGE 13
#define SQL_SELECT_LEVEL_COUNT 14
-#define SQL_SELECT_SEGDIR_COUNT_MAX 15
-#define SQL_DELETE_SEGDIR_BY_LEVEL 16
+#define SQL_SELECT_SEGDIR_MAX_LEVEL 15
+#define SQL_DELETE_SEGDIR_LEVEL 16
#define SQL_DELETE_SEGMENTS_RANGE 17
#define SQL_CONTENT_INSERT 18
#define SQL_DELETE_DOCSIZE 19
#define SQL_SELECT_DOCTOTAL 22
#define SQL_REPLACE_DOCTOTAL 23
+#define SQL_SELECT_ALL_PREFIX_LEVEL 24
+#define SQL_DELETE_ALL_TERMS_SEGDIR 25
+
+#define SQL_DELETE_SEGDIR_RANGE 26
+
/*
** This function is used to obtain an SQLite prepared statement handle
** for the statement identified by the second argument. If successful,
/* 12 */ "SELECT idx, start_block, leaves_end_block, end_block, root "
"FROM %Q.'%q_segdir' WHERE level = ? ORDER BY idx ASC",
/* 13 */ "SELECT idx, start_block, leaves_end_block, end_block, root "
- "FROM %Q.'%q_segdir' ORDER BY level DESC, idx ASC",
+ "FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?"
+ "ORDER BY level DESC, idx ASC",
/* 14 */ "SELECT count(*) FROM %Q.'%q_segdir' WHERE level = ?",
-/* 15 */ "SELECT count(*), max(level) FROM %Q.'%q_segdir'",
+/* 15 */ "SELECT max(level) FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?",
/* 16 */ "DELETE FROM %Q.'%q_segdir' WHERE level = ?",
/* 17 */ "DELETE FROM %Q.'%q_segments' WHERE blockid BETWEEN ? AND ?",
/* 21 */ "SELECT size FROM %Q.'%q_docsize' WHERE docid=?",
/* 22 */ "SELECT value FROM %Q.'%q_stat' WHERE id=0",
/* 23 */ "REPLACE INTO %Q.'%q_stat' VALUES(0,?)",
+/* 24 */ "",
+/* 25 */ "",
+
+/* 26 */ "DELETE FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?",
+
};
int rc = SQLITE_OK;
sqlite3_stmt *pStmt;
rc = sqlite3_step(pStmt);
if( rc!=SQLITE_ROW || sqlite3_column_type(pStmt, 0)!=SQLITE_BLOB ){
rc = sqlite3_reset(pStmt);
- if( rc==SQLITE_OK ) rc = SQLITE_CORRUPT;
+ if( rc==SQLITE_OK ) rc = SQLITE_CORRUPT_VTAB;
pStmt = 0;
}else{
rc = SQLITE_OK;
** 3: end_block
** 4: root
*/
-SQLITE_PRIVATE int sqlite3Fts3AllSegdirs(Fts3Table *p, int iLevel, sqlite3_stmt **ppStmt){
+SQLITE_PRIVATE int sqlite3Fts3AllSegdirs(
+ Fts3Table *p, /* FTS3 table */
+ int iIndex, /* Index for p->aIndex[] */
+ int iLevel, /* Level to select */
+ sqlite3_stmt **ppStmt /* OUT: Compiled statement */
+){
int rc;
sqlite3_stmt *pStmt = 0;
+
+ assert( iLevel==FTS3_SEGCURSOR_ALL || iLevel>=0 );
+ assert( iLevel<FTS3_SEGDIR_MAXLEVEL );
+ assert( iIndex>=0 && iIndex<p->nIndex );
+
if( iLevel<0 ){
- rc = fts3SqlStmt(p, SQL_SELECT_ALL_LEVEL, &pStmt, 0);
+ /* "SELECT * FROM %_segdir WHERE level BETWEEN ? AND ? ORDER BY ..." */
+ rc = fts3SqlStmt(p, SQL_SELECT_LEVEL_RANGE, &pStmt, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int(pStmt, 1, iIndex*FTS3_SEGDIR_MAXLEVEL);
+ sqlite3_bind_int(pStmt, 2, (iIndex+1)*FTS3_SEGDIR_MAXLEVEL-1);
+ }
}else{
+ /* "SELECT * FROM %_segdir WHERE level = ? ORDER BY ..." */
rc = fts3SqlStmt(p, SQL_SELECT_LEVEL, &pStmt, 0);
- if( rc==SQLITE_OK ) sqlite3_bind_int(pStmt, 1, iLevel);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int(pStmt, 1, iLevel+iIndex*FTS3_SEGDIR_MAXLEVEL);
+ }
}
*ppStmt = pStmt;
return rc;
}
/*
+** Free a PendingList object allocated by fts3PendingListAppend().
+*/
+static void fts3PendingListDelete(PendingList *pList){
+ sqlite3_free(pList);
+}
+
+/*
+** Add an entry to one of the pending-terms hash tables.
+*/
+static int fts3PendingTermsAddOne(
+ Fts3Table *p,
+ int iCol,
+ int iPos,
+ Fts3Hash *pHash, /* Pending terms hash table to add entry to */
+ const char *zToken,
+ int nToken
+){
+ PendingList *pList;
+ int rc = SQLITE_OK;
+
+ pList = (PendingList *)fts3HashFind(pHash, zToken, nToken);
+ if( pList ){
+ p->nPendingData -= (pList->nData + nToken + sizeof(Fts3HashElem));
+ }
+ if( fts3PendingListAppend(&pList, p->iPrevDocid, iCol, iPos, &rc) ){
+ if( pList==fts3HashInsert(pHash, zToken, nToken, pList) ){
+ /* Malloc failed while inserting the new entry. This can only
+ ** happen if there was no previous entry for this token.
+ */
+ assert( 0==fts3HashFind(pHash, zToken, nToken) );
+ sqlite3_free(pList);
+ rc = SQLITE_NOMEM;
+ }
+ }
+ if( rc==SQLITE_OK ){
+ p->nPendingData += (pList->nData + nToken + sizeof(Fts3HashElem));
+ }
+ return rc;
+}
+
+/*
** Tokenize the nul-terminated string zText and add all tokens to the
** pending-terms hash-table. The docid used is that currently stored in
** p->iPrevDocid, and the column is specified by argument iCol.
assert( pTokenizer && pModule );
+ /* If the user has inserted a NULL value, this function may be called with
+ ** zText==0. In this case, add zero token entries to the hash table and
+ ** return early. */
+ if( zText==0 ){
+ *pnWord = 0;
+ return SQLITE_OK;
+ }
+
rc = pModule->xOpen(pTokenizer, zText, -1, &pCsr);
if( rc!=SQLITE_OK ){
return rc;
while( SQLITE_OK==rc
&& SQLITE_OK==(rc = xNext(pCsr, &zToken, &nToken, &iStart, &iEnd, &iPos))
){
- PendingList *pList;
-
+ int i;
if( iPos>=nWord ) nWord = iPos+1;
/* Positions cannot be negative; we use -1 as a terminator internally.
break;
}
- pList = (PendingList *)fts3HashFind(&p->pendingTerms, zToken, nToken);
- if( pList ){
- p->nPendingData -= (pList->nData + nToken + sizeof(Fts3HashElem));
- }
- if( fts3PendingListAppend(&pList, p->iPrevDocid, iCol, iPos, &rc) ){
- if( pList==fts3HashInsert(&p->pendingTerms, zToken, nToken, pList) ){
- /* Malloc failed while inserting the new entry. This can only
- ** happen if there was no previous entry for this token.
- */
- assert( 0==fts3HashFind(&p->pendingTerms, zToken, nToken) );
- sqlite3_free(pList);
- rc = SQLITE_NOMEM;
- }
- }
- if( rc==SQLITE_OK ){
- p->nPendingData += (pList->nData + nToken + sizeof(Fts3HashElem));
+ /* Add the term to the terms index */
+ rc = fts3PendingTermsAddOne(
+ p, iCol, iPos, &p->aIndex[0].hPending, zToken, nToken
+ );
+
+ /* Add the term to each of the prefix indexes that it is not too
+ ** short for. */
+ for(i=1; rc==SQLITE_OK && i<p->nIndex; i++){
+ struct Fts3Index *pIndex = &p->aIndex[i];
+ if( nToken<pIndex->nPrefix ) continue;
+ rc = fts3PendingTermsAddOne(
+ p, iCol, iPos, &pIndex->hPending, zToken, pIndex->nPrefix
+ );
}
}
}
/*
-** Discard the contents of the pending-terms hash table.
+** Discard the contents of the pending-terms hash tables.
*/
SQLITE_PRIVATE void sqlite3Fts3PendingTermsClear(Fts3Table *p){
- Fts3HashElem *pElem;
- for(pElem=fts3HashFirst(&p->pendingTerms); pElem; pElem=fts3HashNext(pElem)){
- sqlite3_free(fts3HashData(pElem));
+ int i;
+ for(i=0; i<p->nIndex; i++){
+ Fts3HashElem *pElem;
+ Fts3Hash *pHash = &p->aIndex[i].hPending;
+ for(pElem=fts3HashFirst(pHash); pElem; pElem=fts3HashNext(pElem)){
+ PendingList *pList = (PendingList *)fts3HashData(pElem);
+ fts3PendingListDelete(pList);
+ }
+ fts3HashClear(pHash);
}
- fts3HashClear(&p->pendingTerms);
p->nPendingData = 0;
}
int i; /* Iterator variable */
for(i=2; i<p->nColumn+2; i++){
const char *zText = (const char *)sqlite3_value_text(apVal[i]);
- if( zText ){
- int rc = fts3PendingTermsAdd(p, zText, i-2, &aSz[i-2]);
- if( rc!=SQLITE_OK ){
- return rc;
- }
+ int rc = fts3PendingTermsAdd(p, zText, i-2, &aSz[i-2]);
+ if( rc!=SQLITE_OK ){
+ return rc;
}
aSz[p->nColumn] += sqlite3_value_bytes(apVal[i]);
}
static void fts3DeleteTerms(
int *pRC, /* Result code */
Fts3Table *p, /* The FTS table to delete from */
- sqlite3_value **apVal, /* apVal[] contains the docid to be deleted */
+ sqlite3_value *pRowid, /* The docid to be deleted */
u32 *aSz /* Sizes of deleted document written here */
){
int rc;
sqlite3_stmt *pSelect;
if( *pRC ) return;
- rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pSelect, apVal);
+ rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pSelect, &pRowid);
if( rc==SQLITE_OK ){
if( SQLITE_ROW==sqlite3_step(pSelect) ){
int i;
** Forward declaration to account for the circular dependency between
** functions fts3SegmentMerge() and fts3AllocateSegdirIdx().
*/
-static int fts3SegmentMerge(Fts3Table *, int);
+static int fts3SegmentMerge(Fts3Table *, int, int);
/*
** This function allocates a new level iLevel index in the segdir table.
** If successful, *piIdx is set to the allocated index slot and SQLITE_OK
** returned. Otherwise, an SQLite error code is returned.
*/
-static int fts3AllocateSegdirIdx(Fts3Table *p, int iLevel, int *piIdx){
+static int fts3AllocateSegdirIdx(
+ Fts3Table *p,
+ int iIndex, /* Index for p->aIndex */
+ int iLevel,
+ int *piIdx
+){
int rc; /* Return Code */
sqlite3_stmt *pNextIdx; /* Query for next idx at level iLevel */
int iNext = 0; /* Result of query pNextIdx */
/* Set variable iNext to the next available segdir index at level iLevel. */
rc = fts3SqlStmt(p, SQL_NEXT_SEGMENT_INDEX, &pNextIdx, 0);
if( rc==SQLITE_OK ){
- sqlite3_bind_int(pNextIdx, 1, iLevel);
+ sqlite3_bind_int(pNextIdx, 1, iIndex*FTS3_SEGDIR_MAXLEVEL + iLevel);
if( SQLITE_ROW==sqlite3_step(pNextIdx) ){
iNext = sqlite3_column_int(pNextIdx, 0);
}
** if iNext is less than FTS3_MERGE_COUNT, allocate index iNext.
*/
if( iNext>=FTS3_MERGE_COUNT ){
- rc = fts3SegmentMerge(p, iLevel);
+ rc = fts3SegmentMerge(p, iIndex, iLevel);
*piIdx = 0;
}else{
*piIdx = iNext;
Fts3Table *p, /* FTS3 table handle */
sqlite3_int64 iBlockid, /* Access the row with blockid=$iBlockid */
char **paBlob, /* OUT: Blob data in malloc'd buffer */
- int *pnBlob /* OUT: Size of blob data */
+ int *pnBlob, /* OUT: Size of blob data */
+ int *pnLoad /* OUT: Bytes actually loaded */
){
int rc; /* Return code */
if( rc==SQLITE_OK ){
int nByte = sqlite3_blob_bytes(p->pSegments);
+ *pnBlob = nByte;
if( paBlob ){
char *aByte = sqlite3_malloc(nByte + FTS3_NODE_PADDING);
if( !aByte ){
rc = SQLITE_NOMEM;
}else{
+ if( pnLoad && nByte>(FTS3_NODE_CHUNK_THRESHOLD) ){
+ nByte = FTS3_NODE_CHUNKSIZE;
+ *pnLoad = nByte;
+ }
rc = sqlite3_blob_read(p->pSegments, aByte, nByte, 0);
memset(&aByte[nByte], 0, FTS3_NODE_PADDING);
if( rc!=SQLITE_OK ){
}
*paBlob = aByte;
}
- *pnBlob = nByte;
}
return rc;
sqlite3_blob_close(p->pSegments);
p->pSegments = 0;
}
+
+static int fts3SegReaderIncrRead(Fts3SegReader *pReader){
+ int nRead; /* Number of bytes to read */
+ int rc; /* Return code */
+
+ nRead = MIN(pReader->nNode - pReader->nPopulate, FTS3_NODE_CHUNKSIZE);
+ rc = sqlite3_blob_read(
+ pReader->pBlob,
+ &pReader->aNode[pReader->nPopulate],
+ nRead,
+ pReader->nPopulate
+ );
+
+ if( rc==SQLITE_OK ){
+ pReader->nPopulate += nRead;
+ memset(&pReader->aNode[pReader->nPopulate], 0, FTS3_NODE_PADDING);
+ if( pReader->nPopulate==pReader->nNode ){
+ sqlite3_blob_close(pReader->pBlob);
+ pReader->pBlob = 0;
+ pReader->nPopulate = 0;
+ }
+ }
+ return rc;
+}
+
+static int fts3SegReaderRequire(Fts3SegReader *pReader, char *pFrom, int nByte){
+ int rc = SQLITE_OK;
+ assert( !pReader->pBlob
+ || (pFrom>=pReader->aNode && pFrom<&pReader->aNode[pReader->nNode])
+ );
+ while( pReader->pBlob && rc==SQLITE_OK
+ && (pFrom - pReader->aNode + nByte)>pReader->nPopulate
+ ){
+ rc = fts3SegReaderIncrRead(pReader);
+ }
+ return rc;
+}
/*
** Move the iterator passed as the first argument to the next term in the
** segment. If successful, SQLITE_OK is returned. If there is no next term,
** SQLITE_DONE. Otherwise, an SQLite error code.
*/
-static int fts3SegReaderNext(Fts3Table *p, Fts3SegReader *pReader){
+static int fts3SegReaderNext(
+ Fts3Table *p,
+ Fts3SegReader *pReader,
+ int bIncr
+){
+ int rc; /* Return code of various sub-routines */
char *pNext; /* Cursor variable */
int nPrefix; /* Number of bytes in term prefix */
int nSuffix; /* Number of bytes in term suffix */
}
if( !pNext || pNext>=&pReader->aNode[pReader->nNode] ){
- int rc; /* Return code from Fts3ReadBlock() */
if( fts3SegReaderIsPending(pReader) ){
Fts3HashElem *pElem = *(pReader->ppNextElem);
if( !fts3SegReaderIsRootOnly(pReader) ){
sqlite3_free(pReader->aNode);
+ sqlite3_blob_close(pReader->pBlob);
+ pReader->pBlob = 0;
}
pReader->aNode = 0;
}
rc = sqlite3Fts3ReadBlock(
- p, ++pReader->iCurrentBlock, &pReader->aNode, &pReader->nNode
+ p, ++pReader->iCurrentBlock, &pReader->aNode, &pReader->nNode,
+ (bIncr ? &pReader->nPopulate : 0)
);
if( rc!=SQLITE_OK ) return rc;
+ assert( pReader->pBlob==0 );
+ if( bIncr && pReader->nPopulate<pReader->nNode ){
+ pReader->pBlob = p->pSegments;
+ p->pSegments = 0;
+ }
pNext = pReader->aNode;
}
+
+ assert( !fts3SegReaderIsPending(pReader) );
+
+ rc = fts3SegReaderRequire(pReader, pNext, FTS3_VARINT_MAX*2);
+ if( rc!=SQLITE_OK ) return rc;
/* Because of the FTS3_NODE_PADDING bytes of padding, the following is
- ** safe (no risk of overread) even if the node data is corrupted.
- */
+ ** safe (no risk of overread) even if the node data is corrupted. */
pNext += sqlite3Fts3GetVarint32(pNext, &nPrefix);
pNext += sqlite3Fts3GetVarint32(pNext, &nSuffix);
if( nPrefix<0 || nSuffix<=0
|| &pNext[nSuffix]>&pReader->aNode[pReader->nNode]
){
- return SQLITE_CORRUPT;
+ return SQLITE_CORRUPT_VTAB;
}
if( nPrefix+nSuffix>pReader->nTermAlloc ){
pReader->zTerm = zNew;
pReader->nTermAlloc = nNew;
}
+
+ rc = fts3SegReaderRequire(pReader, pNext, nSuffix+FTS3_VARINT_MAX);
+ if( rc!=SQLITE_OK ) return rc;
+
memcpy(&pReader->zTerm[nPrefix], pNext, nSuffix);
pReader->nTerm = nPrefix+nSuffix;
pNext += nSuffix;
** of these statements is untrue, then the data structure is corrupt.
*/
if( &pReader->aDoclist[pReader->nDoclist]>&pReader->aNode[pReader->nNode]
- || pReader->aDoclist[pReader->nDoclist-1]
+ || (pReader->nPopulate==0 && pReader->aDoclist[pReader->nDoclist-1])
){
- return SQLITE_CORRUPT;
+ return SQLITE_CORRUPT_VTAB;
}
return SQLITE_OK;
}
** Set the SegReader to point to the first docid in the doclist associated
** with the current term.
*/
-static void fts3SegReaderFirstDocid(Fts3SegReader *pReader){
- int n;
+static int fts3SegReaderFirstDocid(Fts3Table *pTab, Fts3SegReader *pReader){
+ int rc = SQLITE_OK;
assert( pReader->aDoclist );
assert( !pReader->pOffsetList );
- n = sqlite3Fts3GetVarint(pReader->aDoclist, &pReader->iDocid);
- pReader->pOffsetList = &pReader->aDoclist[n];
+ if( pTab->bDescIdx && fts3SegReaderIsPending(pReader) ){
+ u8 bEof = 0;
+ pReader->iDocid = 0;
+ pReader->nOffsetList = 0;
+ sqlite3Fts3DoclistPrev(0,
+ pReader->aDoclist, pReader->nDoclist, &pReader->pOffsetList,
+ &pReader->iDocid, &pReader->nOffsetList, &bEof
+ );
+ }else{
+ rc = fts3SegReaderRequire(pReader, pReader->aDoclist, FTS3_VARINT_MAX);
+ if( rc==SQLITE_OK ){
+ int n = sqlite3Fts3GetVarint(pReader->aDoclist, &pReader->iDocid);
+ pReader->pOffsetList = &pReader->aDoclist[n];
+ }
+ }
+ return rc;
}
/*
** *pnOffsetList is set to the length of the set of column-offset
** lists, not including the nul-terminator byte. For example:
*/
-static void fts3SegReaderNextDocid(
- Fts3SegReader *pReader,
- char **ppOffsetList,
- int *pnOffsetList
+static int fts3SegReaderNextDocid(
+ Fts3Table *pTab,
+ Fts3SegReader *pReader, /* Reader to advance to next docid */
+ char **ppOffsetList, /* OUT: Pointer to current position-list */
+ int *pnOffsetList /* OUT: Length of *ppOffsetList in bytes */
){
+ int rc = SQLITE_OK;
char *p = pReader->pOffsetList;
char c = 0;
- /* Pointer p currently points at the first byte of an offset list. The
- ** following two lines advance it to point one byte past the end of
- ** the same offset list.
- */
- while( *p | c ) c = *p++ & 0x80;
- p++;
-
- /* If required, populate the output variables with a pointer to and the
- ** size of the previous offset-list.
- */
- if( ppOffsetList ){
- *ppOffsetList = pReader->pOffsetList;
- *pnOffsetList = (int)(p - pReader->pOffsetList - 1);
- }
+ assert( p );
- /* If there are no more entries in the doclist, set pOffsetList to
- ** NULL. Otherwise, set Fts3SegReader.iDocid to the next docid and
- ** Fts3SegReader.pOffsetList to point to the next offset list before
- ** returning.
- */
- if( p>=&pReader->aDoclist[pReader->nDoclist] ){
- pReader->pOffsetList = 0;
+ if( pTab->bDescIdx && fts3SegReaderIsPending(pReader) ){
+ /* A pending-terms seg-reader for an FTS4 table that uses order=desc.
+ ** Pending-terms doclists are always built up in ascending order, so
+ ** we have to iterate through them backwards here. */
+ u8 bEof = 0;
+ if( ppOffsetList ){
+ *ppOffsetList = pReader->pOffsetList;
+ *pnOffsetList = pReader->nOffsetList - 1;
+ }
+ sqlite3Fts3DoclistPrev(0,
+ pReader->aDoclist, pReader->nDoclist, &p, &pReader->iDocid,
+ &pReader->nOffsetList, &bEof
+ );
+ if( bEof ){
+ pReader->pOffsetList = 0;
+ }else{
+ pReader->pOffsetList = p;
+ }
}else{
- sqlite3_int64 iDelta;
- pReader->pOffsetList = p + sqlite3Fts3GetVarint(p, &iDelta);
- pReader->iDocid += iDelta;
- }
-}
+ char *pEnd = &pReader->aDoclist[pReader->nDoclist];
-/*
-** This function is called to estimate the amount of data that will be
-** loaded from the disk If SegReaderIterate() is called on this seg-reader,
-** in units of average document size.
-**
-** This can be used as follows: If the caller has a small doclist that
-** contains references to N documents, and is considering merging it with
-** a large doclist (size X "average documents"), it may opt not to load
-** the large doclist if X>N.
-*/
-SQLITE_PRIVATE int sqlite3Fts3SegReaderCost(
- Fts3Cursor *pCsr, /* FTS3 cursor handle */
- Fts3SegReader *pReader, /* Segment-reader handle */
- int *pnCost /* IN/OUT: Number of bytes read */
-){
- Fts3Table *p = (Fts3Table*)pCsr->base.pVtab;
- int rc = SQLITE_OK; /* Return code */
- int nCost = 0; /* Cost in bytes to return */
- int pgsz = p->nPgsz; /* Database page size */
-
- /* If this seg-reader is reading the pending-terms table, or if all data
- ** for the segment is stored on the root page of the b-tree, then the cost
- ** is zero. In this case all required data is already in main memory.
- */
- if( p->bHasStat
- && !fts3SegReaderIsPending(pReader)
- && !fts3SegReaderIsRootOnly(pReader)
- ){
- int nBlob = 0;
- sqlite3_int64 iBlock;
-
- if( pCsr->nRowAvg==0 ){
- /* The average document size, which is required to calculate the cost
- ** of each doclist, has not yet been determined. Read the required
- ** data from the %_stat table to calculate it.
- **
- ** Entry 0 of the %_stat table is a blob containing (nCol+1) FTS3
- ** varints, where nCol is the number of columns in the FTS3 table.
- ** The first varint is the number of documents currently stored in
- ** the table. The following nCol varints contain the total amount of
- ** data stored in all rows of each column of the table, from left
- ** to right.
+ /* Pointer p currently points at the first byte of an offset list. The
+ ** following block advances it to point one byte past the end of
+ ** the same offset list. */
+ while( 1 ){
+
+ /* The following line of code (and the "p++" below the while() loop) is
+ ** normally all that is required to move pointer p to the desired
+ ** position. The exception is if this node is being loaded from disk
+ ** incrementally and pointer "p" now points to the first byte passed
+ ** the populated part of pReader->aNode[].
*/
- sqlite3_stmt *pStmt;
- sqlite3_int64 nDoc = 0;
- sqlite3_int64 nByte = 0;
- const char *pEnd;
- const char *a;
-
- rc = sqlite3Fts3SelectDoctotal(p, &pStmt);
- if( rc!=SQLITE_OK ) return rc;
- a = sqlite3_column_blob(pStmt, 0);
- assert( a );
-
- pEnd = &a[sqlite3_column_bytes(pStmt, 0)];
- a += sqlite3Fts3GetVarint(a, &nDoc);
- while( a<pEnd ){
- a += sqlite3Fts3GetVarint(a, &nByte);
- }
- if( nDoc==0 || nByte==0 ){
- sqlite3_reset(pStmt);
- return SQLITE_CORRUPT;
- }
-
- pCsr->nRowAvg = (int)(((nByte / nDoc) + pgsz) / pgsz);
- assert( pCsr->nRowAvg>0 );
- rc = sqlite3_reset(pStmt);
+ while( *p | c ) c = *p++ & 0x80;
+ assert( *p==0 );
+
+ if( pReader->pBlob==0 || p<&pReader->aNode[pReader->nPopulate] ) break;
+ rc = fts3SegReaderIncrRead(pReader);
if( rc!=SQLITE_OK ) return rc;
}
+ p++;
+
+ /* If required, populate the output variables with a pointer to and the
+ ** size of the previous offset-list.
+ */
+ if( ppOffsetList ){
+ *ppOffsetList = pReader->pOffsetList;
+ *pnOffsetList = (int)(p - pReader->pOffsetList - 1);
+ }
- /* Assume that a blob flows over onto overflow pages if it is larger
- ** than (pgsz-35) bytes in size (the file-format documentation
- ** confirms this).
+ while( p<pEnd && *p==0 ) p++;
+
+ /* If there are no more entries in the doclist, set pOffsetList to
+ ** NULL. Otherwise, set Fts3SegReader.iDocid to the next docid and
+ ** Fts3SegReader.pOffsetList to point to the next offset list before
+ ** returning.
*/
- for(iBlock=pReader->iStartBlock; iBlock<=pReader->iLeafEndBlock; iBlock++){
- rc = sqlite3Fts3ReadBlock(p, iBlock, 0, &nBlob);
- if( rc!=SQLITE_OK ) break;
- if( (nBlob+35)>pgsz ){
- int nOvfl = (nBlob + 34)/pgsz;
- nCost += ((nOvfl + pCsr->nRowAvg - 1)/pCsr->nRowAvg);
+ if( p>=pEnd ){
+ pReader->pOffsetList = 0;
+ }else{
+ rc = fts3SegReaderRequire(pReader, p, FTS3_VARINT_MAX);
+ if( rc==SQLITE_OK ){
+ sqlite3_int64 iDelta;
+ pReader->pOffsetList = p + sqlite3Fts3GetVarint(p, &iDelta);
+ if( pTab->bDescIdx ){
+ pReader->iDocid -= iDelta;
+ }else{
+ pReader->iDocid += iDelta;
+ }
}
}
}
- *pnCost += nCost;
+ return SQLITE_OK;
+}
+
+
+SQLITE_PRIVATE int sqlite3Fts3MsrOvfl(
+ Fts3Cursor *pCsr,
+ Fts3MultiSegReader *pMsr,
+ int *pnOvfl
+){
+ Fts3Table *p = (Fts3Table*)pCsr->base.pVtab;
+ int nOvfl = 0;
+ int ii;
+ int rc = SQLITE_OK;
+ int pgsz = p->nPgsz;
+
+ assert( p->bHasStat );
+ assert( pgsz>0 );
+
+ for(ii=0; rc==SQLITE_OK && ii<pMsr->nSegment; ii++){
+ Fts3SegReader *pReader = pMsr->apSegment[ii];
+ if( !fts3SegReaderIsPending(pReader)
+ && !fts3SegReaderIsRootOnly(pReader)
+ ){
+ sqlite3_int64 jj;
+ for(jj=pReader->iStartBlock; jj<=pReader->iLeafEndBlock; jj++){
+ int nBlob;
+ rc = sqlite3Fts3ReadBlock(p, jj, 0, &nBlob, 0);
+ if( rc!=SQLITE_OK ) break;
+ if( (nBlob+35)>pgsz ){
+ nOvfl += (nBlob + 34)/pgsz;
+ }
+ }
+ }
+ }
+ *pnOvfl = nOvfl;
return rc;
}
sqlite3_free(pReader->zTerm);
if( !fts3SegReaderIsRootOnly(pReader) ){
sqlite3_free(pReader->aNode);
+ sqlite3_blob_close(pReader->pBlob);
}
}
sqlite3_free(pReader);
/*
** This function is used to allocate an Fts3SegReader that iterates through
** a subset of the terms stored in the Fts3Table.pendingTerms array.
+**
+** If the isPrefixIter parameter is zero, then the returned SegReader iterates
+** through each term in the pending-terms table. Or, if isPrefixIter is
+** non-zero, it iterates through each term and its prefixes. For example, if
+** the pending terms hash table contains the terms "sqlite", "mysql" and
+** "firebird", then the iterator visits the following 'terms' (in the order
+** shown):
+**
+** f fi fir fire fireb firebi firebir firebird
+** m my mys mysq mysql
+** s sq sql sqli sqlit sqlite
+**
+** Whereas if isPrefixIter is zero, the terms visited are:
+**
+** firebird mysql sqlite
*/
SQLITE_PRIVATE int sqlite3Fts3SegReaderPending(
Fts3Table *p, /* Virtual table handle */
+ int iIndex, /* Index for p->aIndex */
const char *zTerm, /* Term to search for */
int nTerm, /* Size of buffer zTerm */
- int isPrefix, /* True for a term-prefix query */
+ int bPrefix, /* True for a prefix iterator */
Fts3SegReader **ppReader /* OUT: SegReader for pending-terms */
){
Fts3SegReader *pReader = 0; /* Fts3SegReader object to return */
Fts3HashElem **aElem = 0; /* Array of term hash entries to scan */
int nElem = 0; /* Size of array at aElem */
int rc = SQLITE_OK; /* Return Code */
+ Fts3Hash *pHash;
- if( isPrefix ){
+ pHash = &p->aIndex[iIndex].hPending;
+ if( bPrefix ){
int nAlloc = 0; /* Size of allocated array at aElem */
Fts3HashElem *pE = 0; /* Iterator variable */
- for(pE=fts3HashFirst(&p->pendingTerms); pE; pE=fts3HashNext(pE)){
+ for(pE=fts3HashFirst(pHash); pE; pE=fts3HashNext(pE)){
char *zKey = (char *)fts3HashKey(pE);
int nKey = fts3HashKeysize(pE);
if( nTerm==0 || (nKey>=nTerm && 0==memcmp(zKey, zTerm, nTerm)) ){
}
aElem = aElem2;
}
+
aElem[nElem++] = pE;
}
}
}
}else{
- Fts3HashElem *pE = fts3HashFindElem(&p->pendingTerms, zTerm, nTerm);
+ /* The query is a simple term lookup that matches at most one term in
+ ** the index. All that is required is a straight hash-lookup. */
+ Fts3HashElem *pE = fts3HashFindElem(pHash, zTerm, nTerm);
if( pE ){
aElem = &pE;
nElem = 1;
}
}
- if( isPrefix ){
+ if( bPrefix ){
sqlite3_free(aElem);
}
*ppReader = pReader;
assert( pLhs->aNode && pRhs->aNode );
return rc;
}
+static int fts3SegReaderDoclistCmpRev(Fts3SegReader *pLhs, Fts3SegReader *pRhs){
+ int rc = (pLhs->pOffsetList==0)-(pRhs->pOffsetList==0);
+ if( rc==0 ){
+ if( pLhs->iDocid==pRhs->iDocid ){
+ rc = pRhs->iIdx - pLhs->iIdx;
+ }else{
+ rc = (pLhs->iDocid < pRhs->iDocid) ? 1 : -1;
+ }
+ }
+ assert( pLhs->aNode && pRhs->aNode );
+ return rc;
+}
/*
** Compare the term that the Fts3SegReader object passed as the first argument
** The first value in the apVal[] array is assumed to contain an integer.
** This function tests if there exist any documents with docid values that
** are different from that integer. i.e. if deleting the document with docid
-** apVal[0] would mean the FTS3 table were empty.
+** pRowid would mean the FTS3 table were empty.
**
** If successful, *pisEmpty is set to true if the table is empty except for
-** document apVal[0], or false otherwise, and SQLITE_OK is returned. If an
+** document pRowid, or false otherwise, and SQLITE_OK is returned. If an
** error occurs, an SQLite error code is returned.
*/
-static int fts3IsEmpty(Fts3Table *p, sqlite3_value **apVal, int *pisEmpty){
+static int fts3IsEmpty(Fts3Table *p, sqlite3_value *pRowid, int *pisEmpty){
sqlite3_stmt *pStmt;
int rc;
- rc = fts3SqlStmt(p, SQL_IS_EMPTY, &pStmt, apVal);
+ rc = fts3SqlStmt(p, SQL_IS_EMPTY, &pStmt, &pRowid);
if( rc==SQLITE_OK ){
if( SQLITE_ROW==sqlite3_step(pStmt) ){
*pisEmpty = sqlite3_column_int(pStmt, 0);
}
/*
-** Set *pnSegment to the total number of segments in the database. Set
-** *pnMax to the largest segment level in the database (segment levels
-** are stored in the 'level' column of the %_segdir table).
+** Set *pnMax to the largest segment level in the database for the index
+** iIndex.
+**
+** Segment levels are stored in the 'level' column of the %_segdir table.
**
** Return SQLITE_OK if successful, or an SQLite error code if not.
*/
-static int fts3SegmentCountMax(Fts3Table *p, int *pnSegment, int *pnMax){
+static int fts3SegmentMaxLevel(Fts3Table *p, int iIndex, int *pnMax){
sqlite3_stmt *pStmt;
int rc;
+ assert( iIndex>=0 && iIndex<p->nIndex );
- rc = fts3SqlStmt(p, SQL_SELECT_SEGDIR_COUNT_MAX, &pStmt, 0);
+ /* Set pStmt to the compiled version of:
+ **
+ ** SELECT max(level) FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?
+ **
+ ** (1024 is actually the value of macro FTS3_SEGDIR_PREFIXLEVEL_STR).
+ */
+ rc = fts3SqlStmt(p, SQL_SELECT_SEGDIR_MAX_LEVEL, &pStmt, 0);
if( rc!=SQLITE_OK ) return rc;
+ sqlite3_bind_int(pStmt, 1, iIndex*FTS3_SEGDIR_MAXLEVEL);
+ sqlite3_bind_int(pStmt, 2, (iIndex+1)*FTS3_SEGDIR_MAXLEVEL - 1);
if( SQLITE_ROW==sqlite3_step(pStmt) ){
- *pnSegment = sqlite3_column_int(pStmt, 0);
- *pnMax = sqlite3_column_int(pStmt, 1);
+ *pnMax = sqlite3_column_int(pStmt, 0);
}
return sqlite3_reset(pStmt);
}
*/
static int fts3DeleteSegdir(
Fts3Table *p, /* Virtual table handle */
+ int iIndex, /* Index for p->aIndex */
int iLevel, /* Level of %_segdir entries to delete */
Fts3SegReader **apSegment, /* Array of SegReader objects */
int nReader /* Size of array apSegment */
return rc;
}
+ assert( iLevel>=0 || iLevel==FTS3_SEGCURSOR_ALL );
if( iLevel==FTS3_SEGCURSOR_ALL ){
- fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGDIR, 0);
- }else if( iLevel==FTS3_SEGCURSOR_PENDING ){
- sqlite3Fts3PendingTermsClear(p);
+ rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_RANGE, &pDelete, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int(pDelete, 1, iIndex*FTS3_SEGDIR_MAXLEVEL);
+ sqlite3_bind_int(pDelete, 2, (iIndex+1) * FTS3_SEGDIR_MAXLEVEL - 1);
+ }
}else{
- assert( iLevel>=0 );
- rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_BY_LEVEL, &pDelete, 0);
+ rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_LEVEL, &pDelete, 0);
if( rc==SQLITE_OK ){
- sqlite3_bind_int(pDelete, 1, iLevel);
- sqlite3_step(pDelete);
- rc = sqlite3_reset(pDelete);
+ sqlite3_bind_int(pDelete, 1, iIndex*FTS3_SEGDIR_MAXLEVEL + iLevel);
}
}
+ if( rc==SQLITE_OK ){
+ sqlite3_step(pDelete);
+ rc = sqlite3_reset(pDelete);
+ }
+
return rc;
}
*pnList = nList;
}
-SQLITE_PRIVATE int sqlite3Fts3SegReaderStart(
+/*
+** Cache data in the Fts3MultiSegReader.aBuffer[] buffer (overwriting any
+** existing data). Grow the buffer if required.
+**
+** If successful, return SQLITE_OK. Otherwise, if an OOM error is encountered
+** trying to resize the buffer, return SQLITE_NOMEM.
+*/
+static int fts3MsrBufferData(
+ Fts3MultiSegReader *pMsr, /* Multi-segment-reader handle */
+ char *pList,
+ int nList
+){
+ if( nList>pMsr->nBuffer ){
+ char *pNew;
+ pMsr->nBuffer = nList*2;
+ pNew = (char *)sqlite3_realloc(pMsr->aBuffer, pMsr->nBuffer);
+ if( !pNew ) return SQLITE_NOMEM;
+ pMsr->aBuffer = pNew;
+ }
+
+ memcpy(pMsr->aBuffer, pList, nList);
+ return SQLITE_OK;
+}
+
+SQLITE_PRIVATE int sqlite3Fts3MsrIncrNext(
Fts3Table *p, /* Virtual table handle */
- Fts3SegReaderCursor *pCsr, /* Cursor object */
- Fts3SegFilter *pFilter /* Restrictions on range of iteration */
+ Fts3MultiSegReader *pMsr, /* Multi-segment-reader handle */
+ sqlite3_int64 *piDocid, /* OUT: Docid value */
+ char **paPoslist, /* OUT: Pointer to position list */
+ int *pnPoslist /* OUT: Size of position list in bytes */
+){
+ int nMerge = pMsr->nAdvance;
+ Fts3SegReader **apSegment = pMsr->apSegment;
+ int (*xCmp)(Fts3SegReader *, Fts3SegReader *) = (
+ p->bDescIdx ? fts3SegReaderDoclistCmpRev : fts3SegReaderDoclistCmp
+ );
+
+ if( nMerge==0 ){
+ *paPoslist = 0;
+ return SQLITE_OK;
+ }
+
+ while( 1 ){
+ Fts3SegReader *pSeg;
+ pSeg = pMsr->apSegment[0];
+
+ if( pSeg->pOffsetList==0 ){
+ *paPoslist = 0;
+ break;
+ }else{
+ int rc;
+ char *pList;
+ int nList;
+ int j;
+ sqlite3_int64 iDocid = apSegment[0]->iDocid;
+
+ rc = fts3SegReaderNextDocid(p, apSegment[0], &pList, &nList);
+ j = 1;
+ while( rc==SQLITE_OK
+ && j<nMerge
+ && apSegment[j]->pOffsetList
+ && apSegment[j]->iDocid==iDocid
+ ){
+ rc = fts3SegReaderNextDocid(p, apSegment[j], 0, 0);
+ j++;
+ }
+ if( rc!=SQLITE_OK ) return rc;
+ fts3SegReaderSort(pMsr->apSegment, nMerge, j, xCmp);
+
+ if( pMsr->iColFilter>=0 ){
+ fts3ColumnFilter(pMsr->iColFilter, &pList, &nList);
+ }
+
+ if( nList>0 ){
+ if( fts3SegReaderIsPending(apSegment[0]) ){
+ rc = fts3MsrBufferData(pMsr, pList, nList+1);
+ if( rc!=SQLITE_OK ) return rc;
+ *paPoslist = pMsr->aBuffer;
+ assert( (pMsr->aBuffer[nList] & 0xFE)==0x00 );
+ }else{
+ *paPoslist = pList;
+ }
+ *piDocid = iDocid;
+ *pnPoslist = nList;
+ break;
+ }
+ }
+ }
+
+ return SQLITE_OK;
+}
+
+static int fts3SegReaderStart(
+ Fts3Table *p, /* Virtual table handle */
+ Fts3MultiSegReader *pCsr, /* Cursor object */
+ const char *zTerm, /* Term searched for (or NULL) */
+ int nTerm /* Length of zTerm in bytes */
){
int i;
-
- /* Initialize the cursor object */
- pCsr->pFilter = pFilter;
+ int nSeg = pCsr->nSegment;
/* If the Fts3SegFilter defines a specific term (or term prefix) to search
** for, then advance each segment iterator until it points to a term of
** unnecessary merge/sort operations for the case where single segment
** b-tree leaf nodes contain more than one term.
*/
- for(i=0; i<pCsr->nSegment; i++){
- int nTerm = pFilter->nTerm;
- const char *zTerm = pFilter->zTerm;
+ for(i=0; pCsr->bRestart==0 && i<pCsr->nSegment; i++){
Fts3SegReader *pSeg = pCsr->apSegment[i];
do {
- int rc = fts3SegReaderNext(p, pSeg);
+ int rc = fts3SegReaderNext(p, pSeg, 0);
if( rc!=SQLITE_OK ) return rc;
}while( zTerm && fts3SegReaderTermCmp(pSeg, zTerm, nTerm)<0 );
}
- fts3SegReaderSort(
- pCsr->apSegment, pCsr->nSegment, pCsr->nSegment, fts3SegReaderCmp);
+ fts3SegReaderSort(pCsr->apSegment, nSeg, nSeg, fts3SegReaderCmp);
return SQLITE_OK;
}
+SQLITE_PRIVATE int sqlite3Fts3SegReaderStart(
+ Fts3Table *p, /* Virtual table handle */
+ Fts3MultiSegReader *pCsr, /* Cursor object */
+ Fts3SegFilter *pFilter /* Restrictions on range of iteration */
+){
+ pCsr->pFilter = pFilter;
+ return fts3SegReaderStart(p, pCsr, pFilter->zTerm, pFilter->nTerm);
+}
+
+SQLITE_PRIVATE int sqlite3Fts3MsrIncrStart(
+ Fts3Table *p, /* Virtual table handle */
+ Fts3MultiSegReader *pCsr, /* Cursor object */
+ int iCol, /* Column to match on. */
+ const char *zTerm, /* Term to iterate through a doclist for */
+ int nTerm /* Number of bytes in zTerm */
+){
+ int i;
+ int rc;
+ int nSegment = pCsr->nSegment;
+ int (*xCmp)(Fts3SegReader *, Fts3SegReader *) = (
+ p->bDescIdx ? fts3SegReaderDoclistCmpRev : fts3SegReaderDoclistCmp
+ );
+
+ assert( pCsr->pFilter==0 );
+ assert( zTerm && nTerm>0 );
+
+ /* Advance each segment iterator until it points to the term zTerm/nTerm. */
+ rc = fts3SegReaderStart(p, pCsr, zTerm, nTerm);
+ if( rc!=SQLITE_OK ) return rc;
+
+ /* Determine how many of the segments actually point to zTerm/nTerm. */
+ for(i=0; i<nSegment; i++){
+ Fts3SegReader *pSeg = pCsr->apSegment[i];
+ if( !pSeg->aNode || fts3SegReaderTermCmp(pSeg, zTerm, nTerm) ){
+ break;
+ }
+ }
+ pCsr->nAdvance = i;
+
+ /* Advance each of the segments to point to the first docid. */
+ for(i=0; i<pCsr->nAdvance; i++){
+ rc = fts3SegReaderFirstDocid(p, pCsr->apSegment[i]);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+ fts3SegReaderSort(pCsr->apSegment, i, i, xCmp);
+
+ assert( iCol<0 || iCol<p->nColumn );
+ pCsr->iColFilter = iCol;
+
+ return SQLITE_OK;
+}
+
+/*
+** This function is called on a MultiSegReader that has been started using
+** sqlite3Fts3MsrIncrStart(). One or more calls to MsrIncrNext() may also
+** have been made. Calling this function puts the MultiSegReader in such
+** a state that if the next two calls are:
+**
+** sqlite3Fts3SegReaderStart()
+** sqlite3Fts3SegReaderStep()
+**
+** then the entire doclist for the term is available in
+** MultiSegReader.aDoclist/nDoclist.
+*/
+SQLITE_PRIVATE int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr){
+ int i; /* Used to iterate through segment-readers */
+
+ assert( pCsr->zTerm==0 );
+ assert( pCsr->nTerm==0 );
+ assert( pCsr->aDoclist==0 );
+ assert( pCsr->nDoclist==0 );
+
+ pCsr->nAdvance = 0;
+ pCsr->bRestart = 1;
+ for(i=0; i<pCsr->nSegment; i++){
+ pCsr->apSegment[i]->pOffsetList = 0;
+ pCsr->apSegment[i]->nOffsetList = 0;
+ pCsr->apSegment[i]->iDocid = 0;
+ }
+
+ return SQLITE_OK;
+}
+
+
SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(
Fts3Table *p, /* Virtual table handle */
- Fts3SegReaderCursor *pCsr /* Cursor object */
+ Fts3MultiSegReader *pCsr /* Cursor object */
){
int rc = SQLITE_OK;
Fts3SegReader **apSegment = pCsr->apSegment;
int nSegment = pCsr->nSegment;
Fts3SegFilter *pFilter = pCsr->pFilter;
+ int (*xCmp)(Fts3SegReader *, Fts3SegReader *) = (
+ p->bDescIdx ? fts3SegReaderDoclistCmpRev : fts3SegReaderDoclistCmp
+ );
if( pCsr->nSegment==0 ) return SQLITE_OK;
** forward. Then sort the list in order of current term again.
*/
for(i=0; i<pCsr->nAdvance; i++){
- rc = fts3SegReaderNext(p, apSegment[i]);
+ rc = fts3SegReaderNext(p, apSegment[i], 0);
if( rc!=SQLITE_OK ) return rc;
}
fts3SegReaderSort(apSegment, nSegment, pCsr->nAdvance, fts3SegReaderCmp);
}
assert( isIgnoreEmpty || (isRequirePos && !isColFilter) );
- if( nMerge==1 && !isIgnoreEmpty ){
- pCsr->aDoclist = apSegment[0]->aDoclist;
+ if( nMerge==1
+ && !isIgnoreEmpty
+ && (p->bDescIdx==0 || fts3SegReaderIsPending(apSegment[0])==0)
+ ){
pCsr->nDoclist = apSegment[0]->nDoclist;
- rc = SQLITE_ROW;
+ if( fts3SegReaderIsPending(apSegment[0]) ){
+ rc = fts3MsrBufferData(pCsr, apSegment[0]->aDoclist, pCsr->nDoclist);
+ pCsr->aDoclist = pCsr->aBuffer;
+ }else{
+ pCsr->aDoclist = apSegment[0]->aDoclist;
+ }
+ if( rc==SQLITE_OK ) rc = SQLITE_ROW;
}else{
int nDoclist = 0; /* Size of doclist */
sqlite3_int64 iPrev = 0; /* Previous docid stored in doclist */
** and a single term returned with the merged doclist.
*/
for(i=0; i<nMerge; i++){
- fts3SegReaderFirstDocid(apSegment[i]);
+ fts3SegReaderFirstDocid(p, apSegment[i]);
}
- fts3SegReaderSort(apSegment, nMerge, nMerge, fts3SegReaderDoclistCmp);
+ fts3SegReaderSort(apSegment, nMerge, nMerge, xCmp);
while( apSegment[0]->pOffsetList ){
int j; /* Number of segments that share a docid */
char *pList;
int nList;
int nByte;
sqlite3_int64 iDocid = apSegment[0]->iDocid;
- fts3SegReaderNextDocid(apSegment[0], &pList, &nList);
+ fts3SegReaderNextDocid(p, apSegment[0], &pList, &nList);
j = 1;
while( j<nMerge
&& apSegment[j]->pOffsetList
&& apSegment[j]->iDocid==iDocid
){
- fts3SegReaderNextDocid(apSegment[j], 0, 0);
+ fts3SegReaderNextDocid(p, apSegment[j], 0, 0);
j++;
}
}
if( !isIgnoreEmpty || nList>0 ){
- nByte = sqlite3Fts3VarintLen(iDocid-iPrev) + (isRequirePos?nList+1:0);
+
+ /* Calculate the 'docid' delta value to write into the merged
+ ** doclist. */
+ sqlite3_int64 iDelta;
+ if( p->bDescIdx && nDoclist>0 ){
+ iDelta = iPrev - iDocid;
+ }else{
+ iDelta = iDocid - iPrev;
+ }
+ assert( iDelta>0 || (nDoclist==0 && iDelta==iDocid) );
+ assert( nDoclist>0 || iDelta==iDocid );
+
+ nByte = sqlite3Fts3VarintLen(iDelta) + (isRequirePos?nList+1:0);
if( nDoclist+nByte>pCsr->nBuffer ){
char *aNew;
pCsr->nBuffer = (nDoclist+nByte)*2;
}
pCsr->aBuffer = aNew;
}
- nDoclist += sqlite3Fts3PutVarint(
- &pCsr->aBuffer[nDoclist], iDocid-iPrev
- );
+ nDoclist += sqlite3Fts3PutVarint(&pCsr->aBuffer[nDoclist], iDelta);
iPrev = iDocid;
if( isRequirePos ){
memcpy(&pCsr->aBuffer[nDoclist], pList, nList);
}
}
- fts3SegReaderSort(apSegment, nMerge, j, fts3SegReaderDoclistCmp);
+ fts3SegReaderSort(apSegment, nMerge, j, xCmp);
}
if( nDoclist>0 ){
pCsr->aDoclist = pCsr->aBuffer;
return rc;
}
+
SQLITE_PRIVATE void sqlite3Fts3SegReaderFinish(
- Fts3SegReaderCursor *pCsr /* Cursor object */
+ Fts3MultiSegReader *pCsr /* Cursor object */
){
if( pCsr ){
int i;
** Otherwise, if successful, SQLITE_OK is returned. If an error occurs,
** an SQLite error code is returned.
*/
-static int fts3SegmentMerge(Fts3Table *p, int iLevel){
+static int fts3SegmentMerge(Fts3Table *p, int iIndex, int iLevel){
int rc; /* Return code */
int iIdx = 0; /* Index of new segment */
- int iNewLevel = 0; /* Level to create new segment at */
+ int iNewLevel = 0; /* Level/index to create new segment at */
SegmentWriter *pWriter = 0; /* Used to write the new, merged, segment */
Fts3SegFilter filter; /* Segment term filter condition */
- Fts3SegReaderCursor csr; /* Cursor to iterate through level(s) */
+ Fts3MultiSegReader csr; /* Cursor to iterate through level(s) */
+ int bIgnoreEmpty = 0; /* True to ignore empty segments */
- rc = sqlite3Fts3SegReaderCursor(p, iLevel, 0, 0, 1, 0, &csr);
+ assert( iLevel==FTS3_SEGCURSOR_ALL
+ || iLevel==FTS3_SEGCURSOR_PENDING
+ || iLevel>=0
+ );
+ assert( iLevel<FTS3_SEGDIR_MAXLEVEL );
+ assert( iIndex>=0 && iIndex<p->nIndex );
+
+ rc = sqlite3Fts3SegReaderCursor(p, iIndex, iLevel, 0, 0, 1, 0, &csr);
if( rc!=SQLITE_OK || csr.nSegment==0 ) goto finished;
if( iLevel==FTS3_SEGCURSOR_ALL ){
/* This call is to merge all segments in the database to a single
** segment. The level of the new segment is equal to the the numerically
- ** greatest segment level currently present in the database. The index
- ** of the new segment is always 0. */
- int nDummy; /* TODO: Remove this */
+ ** greatest segment level currently present in the database for this
+ ** index. The idx of the new segment is always 0. */
if( csr.nSegment==1 ){
rc = SQLITE_DONE;
goto finished;
}
- rc = fts3SegmentCountMax(p, &nDummy, &iNewLevel);
+ rc = fts3SegmentMaxLevel(p, iIndex, &iNewLevel);
+ bIgnoreEmpty = 1;
+
+ }else if( iLevel==FTS3_SEGCURSOR_PENDING ){
+ iNewLevel = iIndex * FTS3_SEGDIR_MAXLEVEL;
+ rc = fts3AllocateSegdirIdx(p, iIndex, 0, &iIdx);
}else{
- /* This call is to merge all segments at level iLevel. Find the next
+ /* This call is to merge all segments at level iLevel. find the next
** available segment index at level iLevel+1. The call to
** fts3AllocateSegdirIdx() will merge the segments at level iLevel+1 to
** a single iLevel+2 segment if necessary. */
- iNewLevel = iLevel+1;
- rc = fts3AllocateSegdirIdx(p, iNewLevel, &iIdx);
+ rc = fts3AllocateSegdirIdx(p, iIndex, iLevel+1, &iIdx);
+ iNewLevel = iIndex * FTS3_SEGDIR_MAXLEVEL + iLevel+1;
}
if( rc!=SQLITE_OK ) goto finished;
assert( csr.nSegment>0 );
- assert( iNewLevel>=0 );
+ assert( iNewLevel>=(iIndex*FTS3_SEGDIR_MAXLEVEL) );
+ assert( iNewLevel<((iIndex+1)*FTS3_SEGDIR_MAXLEVEL) );
memset(&filter, 0, sizeof(Fts3SegFilter));
filter.flags = FTS3_SEGMENT_REQUIRE_POS;
- filter.flags |= (iLevel==FTS3_SEGCURSOR_ALL ? FTS3_SEGMENT_IGNORE_EMPTY : 0);
+ filter.flags |= (bIgnoreEmpty ? FTS3_SEGMENT_IGNORE_EMPTY : 0);
rc = sqlite3Fts3SegReaderStart(p, &csr, &filter);
while( SQLITE_OK==rc ){
if( rc!=SQLITE_OK ) goto finished;
assert( pWriter );
- rc = fts3DeleteSegdir(p, iLevel, csr.apSegment, csr.nSegment);
- if( rc!=SQLITE_OK ) goto finished;
+ if( iLevel!=FTS3_SEGCURSOR_PENDING ){
+ rc = fts3DeleteSegdir(p, iIndex, iLevel, csr.apSegment, csr.nSegment);
+ if( rc!=SQLITE_OK ) goto finished;
+ }
rc = fts3SegWriterFlush(p, pWriter, iNewLevel, iIdx);
finished:
/*
-** Flush the contents of pendingTerms to a level 0 segment.
+** Flush the contents of pendingTerms to level 0 segments.
*/
SQLITE_PRIVATE int sqlite3Fts3PendingTermsFlush(Fts3Table *p){
- return fts3SegmentMerge(p, FTS3_SEGCURSOR_PENDING);
+ int rc = SQLITE_OK;
+ int i;
+ for(i=0; rc==SQLITE_OK && i<p->nIndex; i++){
+ rc = fts3SegmentMerge(p, i, FTS3_SEGCURSOR_PENDING);
+ if( rc==SQLITE_DONE ) rc = SQLITE_OK;
+ }
+ sqlite3Fts3PendingTermsClear(p);
+ return rc;
}
/*
sqlite3_free(a);
}
+static int fts3DoOptimize(Fts3Table *p, int bReturnDone){
+ int i;
+ int bSeenDone = 0;
+ int rc = SQLITE_OK;
+ for(i=0; rc==SQLITE_OK && i<p->nIndex; i++){
+ rc = fts3SegmentMerge(p, i, FTS3_SEGCURSOR_ALL);
+ if( rc==SQLITE_DONE ){
+ bSeenDone = 1;
+ rc = SQLITE_OK;
+ }
+ }
+ sqlite3Fts3SegmentsClose(p);
+ sqlite3Fts3PendingTermsClear(p);
+
+ return (rc==SQLITE_OK && bReturnDone && bSeenDone) ? SQLITE_DONE : rc;
+}
+
/*
** Handle a 'special' INSERT of the form:
**
if( !zVal ){
return SQLITE_NOMEM;
}else if( nVal==8 && 0==sqlite3_strnicmp(zVal, "optimize", 8) ){
- rc = fts3SegmentMerge(p, FTS3_SEGCURSOR_ALL);
- if( rc==SQLITE_DONE ){
- rc = SQLITE_OK;
- }else{
- sqlite3Fts3PendingTermsClear(p);
- }
+ rc = fts3DoOptimize(p, 0);
#ifdef SQLITE_TEST
}else if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){
p->nNodeSize = atoi(&zVal[9]);
rc = SQLITE_ERROR;
}
- sqlite3Fts3SegmentsClose(p);
return rc;
}
/*
-** Return the deferred doclist associated with deferred token pDeferred.
-** This function assumes that sqlite3Fts3CacheDeferredDoclists() has already
-** been called to allocate and populate the doclist.
-*/
-SQLITE_PRIVATE char *sqlite3Fts3DeferredDoclist(Fts3DeferredToken *pDeferred, int *pnByte){
- if( pDeferred->pList ){
- *pnByte = pDeferred->pList->nData;
- return pDeferred->pList->aData;
- }
- *pnByte = 0;
- return 0;
-}
-
-/*
-** Helper fucntion for FreeDeferredDoclists(). This function removes all
-** references to deferred doclists from within the tree of Fts3Expr
-** structures headed by
-*/
-static void fts3DeferredDoclistClear(Fts3Expr *pExpr){
- if( pExpr ){
- fts3DeferredDoclistClear(pExpr->pLeft);
- fts3DeferredDoclistClear(pExpr->pRight);
- if( pExpr->isLoaded ){
- sqlite3_free(pExpr->aDoclist);
- pExpr->isLoaded = 0;
- pExpr->aDoclist = 0;
- pExpr->nDoclist = 0;
- pExpr->pCurrent = 0;
- pExpr->iCurrent = 0;
- }
- }
-}
-
-/*
** Delete all cached deferred doclists. Deferred doclists are cached
** (allocated) by the sqlite3Fts3CacheDeferredDoclists() function.
*/
SQLITE_PRIVATE void sqlite3Fts3FreeDeferredDoclists(Fts3Cursor *pCsr){
Fts3DeferredToken *pDef;
for(pDef=pCsr->pDeferred; pDef; pDef=pDef->pNext){
- sqlite3_free(pDef->pList);
+ fts3PendingListDelete(pDef->pList);
pDef->pList = 0;
}
- if( pCsr->pDeferred ){
- fts3DeferredDoclistClear(pCsr->pExpr);
- }
}
/*
Fts3DeferredToken *pNext;
for(pDef=pCsr->pDeferred; pDef; pDef=pNext){
pNext = pDef->pNext;
- sqlite3_free(pDef->pList);
+ fts3PendingListDelete(pDef->pList);
sqlite3_free(pDef);
}
pCsr->pDeferred = 0;
return rc;
}
+SQLITE_PRIVATE int sqlite3Fts3DeferredTokenList(
+ Fts3DeferredToken *p,
+ char **ppData,
+ int *pnData
+){
+ char *pRet;
+ int nSkip;
+ sqlite3_int64 dummy;
+
+ *ppData = 0;
+ *pnData = 0;
+
+ if( p->pList==0 ){
+ return SQLITE_OK;
+ }
+
+ pRet = (char *)sqlite3_malloc(p->pList->nData);
+ if( !pRet ) return SQLITE_NOMEM;
+
+ nSkip = sqlite3Fts3GetVarint(p->pList->aData, &dummy);
+ *pnData = p->pList->nData - nSkip;
+ *ppData = pRet;
+
+ memcpy(pRet, &p->pList->aData[nSkip], *pnData);
+ return SQLITE_OK;
+}
+
/*
** Add an entry for token pToken to the pCsr->pDeferred list.
*/
return SQLITE_OK;
}
+/*
+** SQLite value pRowid contains the rowid of a row that may or may not be
+** present in the FTS3 table. If it is, delete it and adjust the contents
+** of subsiduary data structures accordingly.
+*/
+static int fts3DeleteByRowid(
+ Fts3Table *p,
+ sqlite3_value *pRowid,
+ int *pnDoc,
+ u32 *aSzDel
+){
+ int isEmpty = 0;
+ int rc = fts3IsEmpty(p, pRowid, &isEmpty);
+ if( rc==SQLITE_OK ){
+ if( isEmpty ){
+ /* Deleting this row means the whole table is empty. In this case
+ ** delete the contents of all three tables and throw away any
+ ** data in the pendingTerms hash table. */
+ rc = fts3DeleteAll(p);
+ *pnDoc = *pnDoc - 1;
+ }else{
+ sqlite3_int64 iRemove = sqlite3_value_int64(pRowid);
+ rc = fts3PendingTermsDocid(p, iRemove);
+ fts3DeleteTerms(&rc, p, pRowid, aSzDel);
+ fts3SqlExec(&rc, p, SQL_DELETE_CONTENT, &pRowid);
+ if( sqlite3_changes(p->db) ) *pnDoc = *pnDoc - 1;
+ if( p->bHasDocsize ){
+ fts3SqlExec(&rc, p, SQL_DELETE_DOCSIZE, &pRowid);
+ }
+ }
+ }
+
+ return rc;
+}
/*
** This function does the work for the xUpdate method of FTS3 virtual
int rc = SQLITE_OK; /* Return Code */
int isRemove = 0; /* True for an UPDATE or DELETE */
sqlite3_int64 iRemove = 0; /* Rowid removed by UPDATE or DELETE */
- u32 *aSzIns; /* Sizes of inserted documents */
+ u32 *aSzIns = 0; /* Sizes of inserted documents */
u32 *aSzDel; /* Sizes of deleted documents */
int nChng = 0; /* Net change in number of documents */
+ int bInsertDone = 0;
assert( p->pSegments==0 );
+ /* Check for a "special" INSERT operation. One of the form:
+ **
+ ** INSERT INTO xyz(xyz) VALUES('command');
+ */
+ if( nArg>1
+ && sqlite3_value_type(apVal[0])==SQLITE_NULL
+ && sqlite3_value_type(apVal[p->nColumn+2])!=SQLITE_NULL
+ ){
+ rc = fts3SpecialInsert(p, apVal[p->nColumn+2]);
+ goto update_out;
+ }
+
/* Allocate space to hold the change in document sizes */
aSzIns = sqlite3_malloc( sizeof(aSzIns[0])*(p->nColumn+1)*2 );
- if( aSzIns==0 ) return SQLITE_NOMEM;
+ if( aSzIns==0 ){
+ rc = SQLITE_NOMEM;
+ goto update_out;
+ }
aSzDel = &aSzIns[p->nColumn+1];
memset(aSzIns, 0, sizeof(aSzIns[0])*(p->nColumn+1)*2);
- /* If this is a DELETE or UPDATE operation, remove the old record. */
- if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
- int isEmpty = 0;
- rc = fts3IsEmpty(p, apVal, &isEmpty);
- if( rc==SQLITE_OK ){
- if( isEmpty ){
- /* Deleting this row means the whole table is empty. In this case
- ** delete the contents of all three tables and throw away any
- ** data in the pendingTerms hash table.
- */
- rc = fts3DeleteAll(p);
+ /* If this is an INSERT operation, or an UPDATE that modifies the rowid
+ ** value, then this operation requires constraint handling.
+ **
+ ** If the on-conflict mode is REPLACE, this means that the existing row
+ ** should be deleted from the database before inserting the new row. Or,
+ ** if the on-conflict mode is other than REPLACE, then this method must
+ ** detect the conflict and return SQLITE_CONSTRAINT before beginning to
+ ** modify the database file.
+ */
+ if( nArg>1 ){
+ /* Find the value object that holds the new rowid value. */
+ sqlite3_value *pNewRowid = apVal[3+p->nColumn];
+ if( sqlite3_value_type(pNewRowid)==SQLITE_NULL ){
+ pNewRowid = apVal[1];
+ }
+
+ if( sqlite3_value_type(pNewRowid)!=SQLITE_NULL && (
+ sqlite3_value_type(apVal[0])==SQLITE_NULL
+ || sqlite3_value_int64(apVal[0])!=sqlite3_value_int64(pNewRowid)
+ )){
+ /* The new rowid is not NULL (in this case the rowid will be
+ ** automatically assigned and there is no chance of a conflict), and
+ ** the statement is either an INSERT or an UPDATE that modifies the
+ ** rowid column. So if the conflict mode is REPLACE, then delete any
+ ** existing row with rowid=pNewRowid.
+ **
+ ** Or, if the conflict mode is not REPLACE, insert the new record into
+ ** the %_content table. If we hit the duplicate rowid constraint (or any
+ ** other error) while doing so, return immediately.
+ **
+ ** This branch may also run if pNewRowid contains a value that cannot
+ ** be losslessly converted to an integer. In this case, the eventual
+ ** call to fts3InsertData() (either just below or further on in this
+ ** function) will return SQLITE_MISMATCH. If fts3DeleteByRowid is
+ ** invoked, it will delete zero rows (since no row will have
+ ** docid=$pNewRowid if $pNewRowid is not an integer value).
+ */
+ if( sqlite3_vtab_on_conflict(p->db)==SQLITE_REPLACE ){
+ rc = fts3DeleteByRowid(p, pNewRowid, &nChng, aSzDel);
}else{
- isRemove = 1;
- iRemove = sqlite3_value_int64(apVal[0]);
- rc = fts3PendingTermsDocid(p, iRemove);
- fts3DeleteTerms(&rc, p, apVal, aSzDel);
- fts3SqlExec(&rc, p, SQL_DELETE_CONTENT, apVal);
- if( p->bHasDocsize ){
- fts3SqlExec(&rc, p, SQL_DELETE_DOCSIZE, apVal);
- }
- nChng--;
+ rc = fts3InsertData(p, apVal, pRowid);
+ bInsertDone = 1;
}
}
- }else if( sqlite3_value_type(apVal[p->nColumn+2])!=SQLITE_NULL ){
- sqlite3_free(aSzIns);
- return fts3SpecialInsert(p, apVal[p->nColumn+2]);
+ }
+ if( rc!=SQLITE_OK ){
+ goto update_out;
+ }
+
+ /* If this is a DELETE or UPDATE operation, remove the old record. */
+ if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
+ assert( sqlite3_value_type(apVal[0])==SQLITE_INTEGER );
+ rc = fts3DeleteByRowid(p, apVal[0], &nChng, aSzDel);
+ isRemove = 1;
+ iRemove = sqlite3_value_int64(apVal[0]);
}
/* If this is an INSERT or UPDATE operation, insert the new record. */
if( nArg>1 && rc==SQLITE_OK ){
- rc = fts3InsertData(p, apVal, pRowid);
+ if( bInsertDone==0 ){
+ rc = fts3InsertData(p, apVal, pRowid);
+ if( rc==SQLITE_CONSTRAINT ) rc = SQLITE_CORRUPT_VTAB;
+ }
if( rc==SQLITE_OK && (!isRemove || *pRowid!=iRemove) ){
rc = fts3PendingTermsDocid(p, *pRowid);
}
fts3UpdateDocTotals(&rc, p, aSzIns, aSzDel, nChng);
}
+ update_out:
sqlite3_free(aSzIns);
sqlite3Fts3SegmentsClose(p);
return rc;
int rc;
rc = sqlite3_exec(p->db, "SAVEPOINT fts3", 0, 0, 0);
if( rc==SQLITE_OK ){
- rc = fts3SegmentMerge(p, FTS3_SEGCURSOR_ALL);
- if( rc==SQLITE_OK ){
- rc = sqlite3_exec(p->db, "RELEASE fts3", 0, 0, 0);
- if( rc==SQLITE_OK ){
- sqlite3Fts3PendingTermsClear(p);
- }
+ rc = fts3DoOptimize(p, 1);
+ if( rc==SQLITE_OK || rc==SQLITE_DONE ){
+ int rc2 = sqlite3_exec(p->db, "RELEASE fts3", 0, 0, 0);
+ if( rc2!=SQLITE_OK ) rc = rc2;
}else{
sqlite3_exec(p->db, "ROLLBACK TO fts3", 0, 0, 0);
sqlite3_exec(p->db, "RELEASE fts3", 0, 0, 0);
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
+/* #include <string.h> */
+/* #include <assert.h> */
/*
** Characters that may appear in the second argument to matchinfo().
}
/*
-** The argument to this function is always a phrase node. Its doclist
-** (Fts3Expr.aDoclist[]) and the doclists associated with all phrase nodes
-** to the left of this one in the query tree have already been loaded.
-**
-** If this phrase node is part of a series of phrase nodes joined by
-** NEAR operators (and is not the left-most of said series), then elements are
-** removed from the phrases doclist consistent with the NEAR restriction. If
-** required, elements may be removed from the doclists of phrases to the
-** left of this one that are part of the same series of NEAR operator
-** connected phrases.
-**
-** If an OOM error occurs, SQLITE_NOMEM is returned. Otherwise, SQLITE_OK.
-*/
-static int fts3ExprNearTrim(Fts3Expr *pExpr){
- int rc = SQLITE_OK;
- Fts3Expr *pParent = pExpr->pParent;
-
- assert( pExpr->eType==FTSQUERY_PHRASE );
- while( rc==SQLITE_OK
- && pParent
- && pParent->eType==FTSQUERY_NEAR
- && pParent->pRight==pExpr
- ){
- /* This expression (pExpr) is the right-hand-side of a NEAR operator.
- ** Find the expression to the left of the same operator.
- */
- int nNear = pParent->nNear;
- Fts3Expr *pLeft = pParent->pLeft;
-
- if( pLeft->eType!=FTSQUERY_PHRASE ){
- assert( pLeft->eType==FTSQUERY_NEAR );
- assert( pLeft->pRight->eType==FTSQUERY_PHRASE );
- pLeft = pLeft->pRight;
- }
-
- rc = sqlite3Fts3ExprNearTrim(pLeft, pExpr, nNear);
-
- pExpr = pLeft;
- pParent = pExpr->pParent;
- }
-
- return rc;
-}
-
-/*
** This is an fts3ExprIterate() callback used while loading the doclists
** for each phrase into Fts3Expr.aDoclist[]/nDoclist. See also
** fts3ExprLoadDoclists().
*/
static int fts3ExprLoadDoclistsCb(Fts3Expr *pExpr, int iPhrase, void *ctx){
int rc = SQLITE_OK;
+ Fts3Phrase *pPhrase = pExpr->pPhrase;
LoadDoclistCtx *p = (LoadDoclistCtx *)ctx;
UNUSED_PARAMETER(iPhrase);
p->nPhrase++;
- p->nToken += pExpr->pPhrase->nToken;
-
- if( pExpr->isLoaded==0 ){
- rc = sqlite3Fts3ExprLoadDoclist(p->pCsr, pExpr);
- pExpr->isLoaded = 1;
- if( rc==SQLITE_OK ){
- rc = fts3ExprNearTrim(pExpr);
- }
- }
+ p->nToken += pPhrase->nToken;
return rc;
}
pPhrase->nToken = pExpr->pPhrase->nToken;
- pCsr = sqlite3Fts3FindPositions(pExpr, p->pCsr->iPrevId, p->iCol);
+ pCsr = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol);
if( pCsr ){
int iFirst = 0;
pPhrase->pList = pCsr;
return nEntry;
}
-static void fts3LoadColumnlistCounts(char **pp, u32 *aOut, int isGlobal){
- char *pCsr = *pp;
- while( *pCsr ){
- int nHit;
- sqlite3_int64 iCol = 0;
- if( *pCsr==0x01 ){
- pCsr++;
- pCsr += sqlite3Fts3GetVarint(pCsr, &iCol);
- }
- nHit = fts3ColumnlistCount(&pCsr);
- assert( nHit>0 );
- if( isGlobal ){
- aOut[iCol*3+1]++;
- }
- aOut[iCol*3] += nHit;
- }
- pCsr++;
- *pp = pCsr;
-}
-
/*
** fts3ExprIterate() callback used to collect the "global" matchinfo stats
** for a single query.
void *pCtx /* Pointer to MatchInfo structure */
){
MatchInfo *p = (MatchInfo *)pCtx;
- Fts3Cursor *pCsr = p->pCursor;
- char *pIter;
- char *pEnd;
- char *pFree = 0;
- u32 *aOut = &p->aMatchinfo[3*iPhrase*p->nCol];
-
- assert( pExpr->isLoaded );
- assert( pExpr->eType==FTSQUERY_PHRASE );
-
- if( pCsr->pDeferred ){
- Fts3Phrase *pPhrase = pExpr->pPhrase;
- int ii;
- for(ii=0; ii<pPhrase->nToken; ii++){
- if( pPhrase->aToken[ii].bFulltext ) break;
- }
- if( ii<pPhrase->nToken ){
- int nFree = 0;
- int rc = sqlite3Fts3ExprLoadFtDoclist(pCsr, pExpr, &pFree, &nFree);
- if( rc!=SQLITE_OK ) return rc;
- pIter = pFree;
- pEnd = &pFree[nFree];
- }else{
- int iCol; /* Column index */
- for(iCol=0; iCol<p->nCol; iCol++){
- aOut[iCol*3 + 1] = (u32)p->nDoc;
- aOut[iCol*3 + 2] = (u32)p->nDoc;
- }
- return SQLITE_OK;
- }
- }else{
- pIter = pExpr->aDoclist;
- pEnd = &pExpr->aDoclist[pExpr->nDoclist];
- }
-
- /* Fill in the global hit count matrix row for this phrase. */
- while( pIter<pEnd ){
- while( *pIter++ & 0x80 ); /* Skip past docid. */
- fts3LoadColumnlistCounts(&pIter, &aOut[1], 1);
- }
-
- sqlite3_free(pFree);
- return SQLITE_OK;
+ return sqlite3Fts3EvalPhraseStats(
+ p->pCursor, pExpr, &p->aMatchinfo[3*iPhrase*p->nCol]
+ );
}
/*
int iStart = iPhrase * p->nCol * 3;
int i;
- for(i=0; i<p->nCol; i++) p->aMatchinfo[iStart+i*3] = 0;
-
- if( pExpr->aDoclist ){
+ for(i=0; i<p->nCol; i++){
char *pCsr;
-
- pCsr = sqlite3Fts3FindPositions(pExpr, p->pCursor->iPrevId, -1);
+ pCsr = sqlite3Fts3EvalPhrasePoslist(p->pCursor, pExpr, i);
if( pCsr ){
- fts3LoadColumnlistCounts(&pCsr, &p->aMatchinfo[iStart], 0);
+ p->aMatchinfo[iStart+i*3] = fts3ColumnlistCount(&pCsr);
+ }else{
+ p->aMatchinfo[iStart+i*3] = 0;
}
}
a = sqlite3_column_blob(pStmt, 0);
a += sqlite3Fts3GetVarint(a, &nDoc);
- if( nDoc==0 ) return SQLITE_CORRUPT;
+ if( nDoc==0 ) return SQLITE_CORRUPT_VTAB;
*pnDoc = (u32)nDoc;
if( paLen ) *paLen = a;
typedef struct LcsIterator LcsIterator;
struct LcsIterator {
Fts3Expr *pExpr; /* Pointer to phrase expression */
- char *pRead; /* Cursor used to iterate through aDoclist */
int iPosOffset; /* Tokens count up to end of this phrase */
- int iCol; /* Current column number */
+ char *pRead; /* Cursor used to iterate through aDoclist */
int iPos; /* Current position */
};
int rc = 0;
pRead += sqlite3Fts3GetVarint(pRead, &iRead);
- if( iRead==0 ){
- pIter->iCol = LCS_ITERATOR_FINISHED;
+ if( iRead==0 || iRead==1 ){
+ pRead = 0;
rc = 1;
}else{
- if( iRead==1 ){
- pRead += sqlite3Fts3GetVarint(pRead, &iRead);
- pIter->iCol = (int)iRead;
- pIter->iPos = pIter->iPosOffset;
- pRead += sqlite3Fts3GetVarint(pRead, &iRead);
- rc = 1;
- }
pIter->iPos += (int)(iRead-2);
}
if( !aIter ) return SQLITE_NOMEM;
memset(aIter, 0, sizeof(LcsIterator) * pCsr->nPhrase);
(void)fts3ExprIterate(pCsr->pExpr, fts3MatchinfoLcsCb, (void*)aIter);
+
for(i=0; i<pInfo->nPhrase; i++){
LcsIterator *pIter = &aIter[i];
nToken -= pIter->pExpr->pPhrase->nToken;
pIter->iPosOffset = nToken;
- pIter->pRead = sqlite3Fts3FindPositions(pIter->pExpr, pCsr->iPrevId, -1);
- if( pIter->pRead ){
- pIter->iPos = pIter->iPosOffset;
- fts3LcsIteratorAdvance(&aIter[i]);
- }else{
- pIter->iCol = LCS_ITERATOR_FINISHED;
- }
}
for(iCol=0; iCol<pInfo->nCol; iCol++){
int nLcs = 0; /* LCS value for this column */
int nLive = 0; /* Number of iterators in aIter not at EOF */
- /* Loop through the iterators in aIter[]. Set nLive to the number of
- ** iterators that point to a position-list corresponding to column iCol.
- */
for(i=0; i<pInfo->nPhrase; i++){
- assert( aIter[i].iCol>=iCol );
- if( aIter[i].iCol==iCol ) nLive++;
+ LcsIterator *pIt = &aIter[i];
+ pIt->pRead = sqlite3Fts3EvalPhrasePoslist(pCsr, pIt->pExpr, iCol);
+ if( pIt->pRead ){
+ pIt->iPos = pIt->iPosOffset;
+ fts3LcsIteratorAdvance(&aIter[i]);
+ nLive++;
+ }
}
- /* The following loop runs until all iterators in aIter[] have finished
- ** iterating through positions in column iCol. Exactly one of the
- ** iterators is advanced each time the body of the loop is run.
- */
while( nLive>0 ){
LcsIterator *pAdv = 0; /* The iterator to advance by one position */
int nThisLcs = 0; /* LCS for the current iterator positions */
for(i=0; i<pInfo->nPhrase; i++){
LcsIterator *pIter = &aIter[i];
- if( iCol!=pIter->iCol ){
+ if( pIter->pRead==0 ){
/* This iterator is already at EOF for this column. */
nThisLcs = 0;
}else{
case FTS3_MATCHINFO_NDOC:
if( bGlobal ){
- sqlite3_int64 nDoc;
+ sqlite3_int64 nDoc = 0;
rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &nDoc, 0);
pInfo->aMatchinfo[0] = (u32)nDoc;
}
};
struct TermOffsetCtx {
+ Fts3Cursor *pCsr;
int iCol; /* Column of table to populate aTerm for */
int iTerm;
sqlite3_int64 iDocid;
int iPos = 0; /* First position in position-list */
UNUSED_PARAMETER(iPhrase);
- pList = sqlite3Fts3FindPositions(pExpr, p->iDocid, p->iCol);
+ pList = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol);
nTerm = pExpr->pPhrase->nToken;
if( pList ){
fts3GetDeltaPosition(&pList, &iPos);
goto offsets_out;
}
sCtx.iDocid = pCsr->iPrevId;
+ sCtx.pCsr = pCsr;
/* Loop through the table columns, appending offset information to
** string-buffer res for each column.
);
rc = fts3StringAppend(&res, aBuffer, -1);
}else if( rc==SQLITE_DONE ){
- rc = SQLITE_CORRUPT;
+ rc = SQLITE_CORRUPT_VTAB;
}
}
}
#else
#endif
+/* #include <string.h> */
+/* #include <assert.h> */
#ifndef SQLITE_AMALGAMATION
#include "sqlite3rtree.h"
if( pNode && iNode==1 ){
pRtree->iDepth = readInt16(pNode->zData);
if( pRtree->iDepth>RTREE_MAX_DEPTH ){
- rc = SQLITE_CORRUPT;
+ rc = SQLITE_CORRUPT_VTAB;
}
}
/* If no error has occurred so far, check if the "number of entries"
** field on the node is too large. If so, set the return code to
- ** SQLITE_CORRUPT.
+ ** SQLITE_CORRUPT_VTAB.
*/
if( pNode && rc==SQLITE_OK ){
if( NCELL(pNode)>((pRtree->iNodeSize-4)/pRtree->nBytesPerCell) ){
- rc = SQLITE_CORRUPT;
+ rc = SQLITE_CORRUPT_VTAB;
}
}
if( pNode!=0 ){
nodeHashInsert(pRtree, pNode);
}else{
- rc = SQLITE_CORRUPT;
+ rc = SQLITE_CORRUPT_VTAB;
}
*ppNode = pNode;
}else{
return SQLITE_OK;
}
}
- return SQLITE_CORRUPT;
+ return SQLITE_CORRUPT_VTAB;
}
/*
float area = 1.0;
int ii;
for(ii=0; ii<(pRtree->nDim*2); ii+=2){
- area = area * (DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii]));
+ area = (float)(area * (DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii])));
}
return area;
}
float margin = 0.0;
int ii;
for(ii=0; ii<(pRtree->nDim*2); ii+=2){
- margin += (DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii]));
+ margin += (float)(DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii]));
}
return margin;
}
o = 0.0;
break;
}else{
- o = o * (x2-x1);
+ o = o * (float)(x2-x1);
}
}
overlap += o;
int nCell,
int iExclude
){
- float before;
- float after;
+ double before;
+ double after;
before = cellOverlap(pRtree, p, aCell, nCell, iExclude);
cellUnion(pRtree, p, pInsert);
after = cellOverlap(pRtree, p, aCell, nCell, iExclude);
- return after-before;
+ return (float)(after-before);
}
#endif
for(ii=0; rc==SQLITE_OK && ii<(pRtree->iDepth-iHeight); ii++){
int iCell;
- sqlite3_int64 iBest;
+ sqlite3_int64 iBest = 0;
- float fMinGrowth;
- float fMinArea;
- float fMinOverlap;
+ float fMinGrowth = 0.0;
+ float fMinArea = 0.0;
+ float fMinOverlap = 0.0;
int nCell = NCELL(pNode);
RtreeCell cell;
int iCell;
if( nodeParentIndex(pRtree, p, &iCell) ){
- return SQLITE_CORRUPT;
+ return SQLITE_CORRUPT_VTAB;
}
nodeGetCell(pRtree, pParent, iCell, &cell);
int *aSpare;
int ii;
- int iBestDim;
- int iBestSplit;
- float fBestMargin;
+ int iBestDim = 0;
+ int iBestSplit = 0;
+ float fBestMargin = 0.0;
int nByte = (pRtree->nDim+1)*(sizeof(int*)+nCell*sizeof(int));
for(ii=0; ii<pRtree->nDim; ii++){
float margin = 0.0;
- float fBestOverlap;
- float fBestArea;
- int iBestLeft;
+ float fBestOverlap = 0.0;
+ float fBestArea = 0.0;
+ int iBestLeft = 0;
int nLeft;
for(
}
rc = sqlite3_reset(pRtree->pReadParent);
if( rc==SQLITE_OK ) rc = rc2;
- if( rc==SQLITE_OK && !pChild->pParent ) rc = SQLITE_CORRUPT;
+ if( rc==SQLITE_OK && !pChild->pParent ) rc = SQLITE_CORRUPT_VTAB;
pChild = pChild->pParent;
}
return rc;
static int removeNode(Rtree *pRtree, RtreeNode *pNode, int iHeight){
int rc;
int rc2;
- RtreeNode *pParent;
+ RtreeNode *pParent = 0;
int iCell;
assert( pNode->nRef==1 );
}
aOrder[ii] = ii;
for(iDim=0; iDim<pRtree->nDim; iDim++){
- aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2]);
- aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2+1]);
+ aCenterCoord[iDim] += (float)DCOORD(aCell[ii].aCoord[iDim*2]);
+ aCenterCoord[iDim] += (float)DCOORD(aCell[ii].aCoord[iDim*2+1]);
}
}
for(iDim=0; iDim<pRtree->nDim; iDim++){
- aCenterCoord[iDim] = aCenterCoord[iDim]/((float)nCell*2.0);
+ aCenterCoord[iDim] = (float)(aCenterCoord[iDim]/((float)nCell*2.0));
}
for(ii=0; ii<nCell; ii++){
aDistance[ii] = 0.0;
for(iDim=0; iDim<pRtree->nDim; iDim++){
- float coord = DCOORD(aCell[ii].aCoord[iDim*2+1]) -
- DCOORD(aCell[ii].aCoord[iDim*2]);
+ float coord = (float)(DCOORD(aCell[ii].aCoord[iDim*2+1]) -
+ DCOORD(aCell[ii].aCoord[iDim*2]));
aDistance[ii] += (coord-aCenterCoord[iDim])*(coord-aCenterCoord[iDim]);
}
}
/* Find a node to store this cell in. pNode->iNode currently contains
** the height of the sub-tree headed by the cell.
*/
- rc = ChooseLeaf(pRtree, &cell, pNode->iNode, &pInsert);
+ rc = ChooseLeaf(pRtree, &cell, (int)pNode->iNode, &pInsert);
if( rc==SQLITE_OK ){
int rc2;
- rc = rtreeInsertCell(pRtree, pInsert, &cell, pNode->iNode);
+ rc = rtreeInsertCell(pRtree, pInsert, &cell, (int)pNode->iNode);
rc2 = nodeRelease(pRtree, pInsert);
if( rc==SQLITE_OK ){
rc = rc2;
}
/*
-** The xUpdate method for rtree module virtual tables.
+** Remove the entry with rowid=iDelete from the r-tree structure.
*/
-static int rtreeUpdate(
- sqlite3_vtab *pVtab,
- int nData,
- sqlite3_value **azData,
- sqlite_int64 *pRowid
-){
- Rtree *pRtree = (Rtree *)pVtab;
- int rc = SQLITE_OK;
+static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){
+ int rc; /* Return code */
+ RtreeNode *pLeaf; /* Leaf node containing record iDelete */
+ int iCell; /* Index of iDelete cell in pLeaf */
+ RtreeNode *pRoot; /* Root node of rtree structure */
- rtreeReference(pRtree);
- assert(nData>=1);
+ /* Obtain a reference to the root node to initialise Rtree.iDepth */
+ rc = nodeAcquire(pRtree, 1, 0, &pRoot);
- /* If azData[0] is not an SQL NULL value, it is the rowid of a
- ** record to delete from the r-tree table. The following block does
- ** just that.
+ /* Obtain a reference to the leaf node that contains the entry
+ ** about to be deleted.
*/
- if( sqlite3_value_type(azData[0])!=SQLITE_NULL ){
- i64 iDelete; /* The rowid to delete */
- RtreeNode *pLeaf; /* Leaf node containing record iDelete */
- int iCell; /* Index of iDelete cell in pLeaf */
- RtreeNode *pRoot;
-
- /* Obtain a reference to the root node to initialise Rtree.iDepth */
- rc = nodeAcquire(pRtree, 1, 0, &pRoot);
+ if( rc==SQLITE_OK ){
+ rc = findLeafNode(pRtree, iDelete, &pLeaf);
+ }
- /* Obtain a reference to the leaf node that contains the entry
- ** about to be deleted.
- */
+ /* Delete the cell in question from the leaf node. */
+ if( rc==SQLITE_OK ){
+ int rc2;
+ rc = nodeRowidIndex(pRtree, pLeaf, iDelete, &iCell);
if( rc==SQLITE_OK ){
- iDelete = sqlite3_value_int64(azData[0]);
- rc = findLeafNode(pRtree, iDelete, &pLeaf);
+ rc = deleteCell(pRtree, pLeaf, iCell, 0);
}
-
- /* Delete the cell in question from the leaf node. */
+ rc2 = nodeRelease(pRtree, pLeaf);
if( rc==SQLITE_OK ){
- int rc2;
- rc = nodeRowidIndex(pRtree, pLeaf, iDelete, &iCell);
- if( rc==SQLITE_OK ){
- rc = deleteCell(pRtree, pLeaf, iCell, 0);
- }
- rc2 = nodeRelease(pRtree, pLeaf);
- if( rc==SQLITE_OK ){
- rc = rc2;
- }
+ rc = rc2;
}
+ }
- /* Delete the corresponding entry in the <rtree>_rowid table. */
- if( rc==SQLITE_OK ){
- sqlite3_bind_int64(pRtree->pDeleteRowid, 1, iDelete);
- sqlite3_step(pRtree->pDeleteRowid);
- rc = sqlite3_reset(pRtree->pDeleteRowid);
- }
+ /* Delete the corresponding entry in the <rtree>_rowid table. */
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int64(pRtree->pDeleteRowid, 1, iDelete);
+ sqlite3_step(pRtree->pDeleteRowid);
+ rc = sqlite3_reset(pRtree->pDeleteRowid);
+ }
- /* Check if the root node now has exactly one child. If so, remove
- ** it, schedule the contents of the child for reinsertion and
- ** reduce the tree height by one.
- **
- ** This is equivalent to copying the contents of the child into
- ** the root node (the operation that Gutman's paper says to perform
- ** in this scenario).
- */
- if( rc==SQLITE_OK && pRtree->iDepth>0 && NCELL(pRoot)==1 ){
- int rc2;
- RtreeNode *pChild;
- i64 iChild = nodeGetRowid(pRtree, pRoot, 0);
- rc = nodeAcquire(pRtree, iChild, pRoot, &pChild);
- if( rc==SQLITE_OK ){
- rc = removeNode(pRtree, pChild, pRtree->iDepth-1);
- }
- rc2 = nodeRelease(pRtree, pChild);
- if( rc==SQLITE_OK ) rc = rc2;
- if( rc==SQLITE_OK ){
- pRtree->iDepth--;
- writeInt16(pRoot->zData, pRtree->iDepth);
- pRoot->isDirty = 1;
- }
+ /* Check if the root node now has exactly one child. If so, remove
+ ** it, schedule the contents of the child for reinsertion and
+ ** reduce the tree height by one.
+ **
+ ** This is equivalent to copying the contents of the child into
+ ** the root node (the operation that Gutman's paper says to perform
+ ** in this scenario).
+ */
+ if( rc==SQLITE_OK && pRtree->iDepth>0 && NCELL(pRoot)==1 ){
+ int rc2;
+ RtreeNode *pChild;
+ i64 iChild = nodeGetRowid(pRtree, pRoot, 0);
+ rc = nodeAcquire(pRtree, iChild, pRoot, &pChild);
+ if( rc==SQLITE_OK ){
+ rc = removeNode(pRtree, pChild, pRtree->iDepth-1);
}
-
- /* Re-insert the contents of any underfull nodes removed from the tree. */
- for(pLeaf=pRtree->pDeleted; pLeaf; pLeaf=pRtree->pDeleted){
- if( rc==SQLITE_OK ){
- rc = reinsertNodeContent(pRtree, pLeaf);
- }
- pRtree->pDeleted = pLeaf->pNext;
- sqlite3_free(pLeaf);
+ rc2 = nodeRelease(pRtree, pChild);
+ if( rc==SQLITE_OK ) rc = rc2;
+ if( rc==SQLITE_OK ){
+ pRtree->iDepth--;
+ writeInt16(pRoot->zData, pRtree->iDepth);
+ pRoot->isDirty = 1;
}
+ }
- /* Release the reference to the root node. */
+ /* Re-insert the contents of any underfull nodes removed from the tree. */
+ for(pLeaf=pRtree->pDeleted; pLeaf; pLeaf=pRtree->pDeleted){
if( rc==SQLITE_OK ){
- rc = nodeRelease(pRtree, pRoot);
- }else{
- nodeRelease(pRtree, pRoot);
+ rc = reinsertNodeContent(pRtree, pLeaf);
}
+ pRtree->pDeleted = pLeaf->pNext;
+ sqlite3_free(pLeaf);
}
- /* If the azData[] array contains more than one element, elements
- ** (azData[2]..azData[argc-1]) contain a new record to insert into
- ** the r-tree structure.
+ /* Release the reference to the root node. */
+ if( rc==SQLITE_OK ){
+ rc = nodeRelease(pRtree, pRoot);
+ }else{
+ nodeRelease(pRtree, pRoot);
+ }
+
+ return rc;
+}
+
+/*
+** The xUpdate method for rtree module virtual tables.
+*/
+static int rtreeUpdate(
+ sqlite3_vtab *pVtab,
+ int nData,
+ sqlite3_value **azData,
+ sqlite_int64 *pRowid
+){
+ Rtree *pRtree = (Rtree *)pVtab;
+ int rc = SQLITE_OK;
+ RtreeCell cell; /* New cell to insert if nData>1 */
+ int bHaveRowid = 0; /* Set to 1 after new rowid is determined */
+
+ rtreeReference(pRtree);
+ assert(nData>=1);
+
+ /* Constraint handling. A write operation on an r-tree table may return
+ ** SQLITE_CONSTRAINT for two reasons:
+ **
+ ** 1. A duplicate rowid value, or
+ ** 2. The supplied data violates the "x2>=x1" constraint.
+ **
+ ** In the first case, if the conflict-handling mode is REPLACE, then
+ ** the conflicting row can be removed before proceeding. In the second
+ ** case, SQLITE_CONSTRAINT must be returned regardless of the
+ ** conflict-handling mode specified by the user.
*/
- if( rc==SQLITE_OK && nData>1 ){
- /* Insert a new record into the r-tree */
- RtreeCell cell;
+ if( nData>1 ){
int ii;
- RtreeNode *pLeaf;
/* Populate the cell.aCoord[] array. The first coordinate is azData[3]. */
assert( nData==(pRtree->nDim*2 + 3) );
}
}
- /* Figure out the rowid of the new row. */
- if( sqlite3_value_type(azData[2])==SQLITE_NULL ){
- rc = newRowid(pRtree, &cell.iRowid);
- }else{
+ /* If a rowid value was supplied, check if it is already present in
+ ** the table. If so, the constraint has failed. */
+ if( sqlite3_value_type(azData[2])!=SQLITE_NULL ){
cell.iRowid = sqlite3_value_int64(azData[2]);
- sqlite3_bind_int64(pRtree->pReadRowid, 1, cell.iRowid);
- if( SQLITE_ROW==sqlite3_step(pRtree->pReadRowid) ){
- sqlite3_reset(pRtree->pReadRowid);
- rc = SQLITE_CONSTRAINT;
- goto constraint;
+ if( sqlite3_value_type(azData[0])==SQLITE_NULL
+ || sqlite3_value_int64(azData[0])!=cell.iRowid
+ ){
+ int steprc;
+ sqlite3_bind_int64(pRtree->pReadRowid, 1, cell.iRowid);
+ steprc = sqlite3_step(pRtree->pReadRowid);
+ rc = sqlite3_reset(pRtree->pReadRowid);
+ if( SQLITE_ROW==steprc ){
+ if( sqlite3_vtab_on_conflict(pRtree->db)==SQLITE_REPLACE ){
+ rc = rtreeDeleteRowid(pRtree, cell.iRowid);
+ }else{
+ rc = SQLITE_CONSTRAINT;
+ goto constraint;
+ }
+ }
}
- rc = sqlite3_reset(pRtree->pReadRowid);
+ bHaveRowid = 1;
+ }
+ }
+
+ /* If azData[0] is not an SQL NULL value, it is the rowid of a
+ ** record to delete from the r-tree table. The following block does
+ ** just that.
+ */
+ if( sqlite3_value_type(azData[0])!=SQLITE_NULL ){
+ rc = rtreeDeleteRowid(pRtree, sqlite3_value_int64(azData[0]));
+ }
+
+ /* If the azData[] array contains more than one element, elements
+ ** (azData[2]..azData[argc-1]) contain a new record to insert into
+ ** the r-tree structure.
+ */
+ if( rc==SQLITE_OK && nData>1 ){
+ /* Insert the new record into the r-tree */
+ RtreeNode *pLeaf;
+
+ /* Figure out the rowid of the new row. */
+ if( bHaveRowid==0 ){
+ rc = newRowid(pRtree, &cell.iRowid);
}
*pRowid = cell.iRowid;
}
static sqlite3_module rtreeModule = {
- 0, /* iVersion */
+ 0, /* iVersion */
rtreeCreate, /* xCreate - create a table */
rtreeConnect, /* xConnect - connect to an existing table */
rtreeBestIndex, /* xBestIndex - Determine search strategy */
0, /* xCommit - commit transaction */
0, /* xRollback - rollback transaction */
0, /* xFindFunction - function overloading */
- rtreeRename /* xRename - rename the table */
+ rtreeRename, /* xRename - rename the table */
+ 0, /* xSavepoint */
+ 0, /* xRelease */
+ 0 /* xRollbackTo */
};
static int rtreeSqlInit(
int rc;
char *zSql;
if( isCreate ){
- int iPageSize;
+ int iPageSize = 0;
zSql = sqlite3_mprintf("PRAGMA %Q.page_size", pRtree->zDb);
rc = getIntFromStmt(db, zSql, &iPageSize);
if( rc==SQLITE_OK ){
return SQLITE_ERROR;
}
+ sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
+
/* Allocate the sqlite3_vtab structure */
nDb = strlen(argv[1]);
nName = strlen(argv[2]);
#include <unicode/ustring.h>
#include <unicode/ucol.h>
+/* #include <assert.h> */
#ifndef SQLITE_CORE
SQLITE_EXTENSION_INIT1
**
*************************************************************************
** This file implements a tokenizer for fts3 based on the ICU library.
-**
-** $Id: fts3_icu.c,v 1.3 2008/09/01 18:34:20 danielk1977 Exp $
*/
-
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
#ifdef SQLITE_ENABLE_ICU
+/* #include <assert.h> */
+/* #include <string.h> */
#include <unicode/ubrk.h>
+/* #include <unicode/ucol.h> */
+/* #include <unicode/ustring.h> */
#include <unicode/utf16.h>
typedef struct IcuTokenizer IcuTokenizer;