[libgda] Upgraded SQLite to 3.7.14.1 and SqlCipher to 2.1.1
- From: Vivien Malerba <vivien src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgda] Upgraded SQLite to 3.7.14.1 and SqlCipher to 2.1.1
- Date: Sat, 29 Dec 2012 22:06:05 +0000 (UTC)
commit 23c186535232bade4acd8b0f51f8ec3eea491bb4
Author: Vivien Malerba <malerba gnome-db org>
Date: Sat Dec 22 18:22:25 2012 +0100
Upgraded SQLite to 3.7.14.1 and SqlCipher to 2.1.1
libgda/sqlite/sqlite-src/PragmasPatch | 6 +-
libgda/sqlite/sqlite-src/sqlite3.c | 6859 ++++++++++++++++-----------------
libgda/sqlite/sqlite-src/sqlite3.h | 157 +-
providers/sqlcipher/sqlcipher.patch | 3241 ++++++++++------
4 files changed, 5342 insertions(+), 4921 deletions(-)
---
diff --git a/libgda/sqlite/sqlite-src/PragmasPatch b/libgda/sqlite/sqlite-src/PragmasPatch
index b622a53..b0b53da 100644
--- a/libgda/sqlite/sqlite-src/PragmasPatch
+++ b/libgda/sqlite/sqlite-src/PragmasPatch
@@ -1,6 +1,6 @@
---- sqlite3.c.orig 2012-05-22 13:03:50.000000000 +0200
-+++ sqlite3.c 2012-08-27 14:52:58.000000000 +0200
-@@ -93389,6 +93389,60 @@
+--- sqlite3.c.orig 2012-10-04 21:49:28.000000000 +0200
++++ sqlite3.c 2012-12-22 18:10:51.472282229 +0100
+@@ -92000,6 +92000,60 @@
#ifndef SQLITE_OMIT_SCHEMA_PRAGMAS
/*
diff --git a/libgda/sqlite/sqlite-src/sqlite3.c b/libgda/sqlite/sqlite-src/sqlite3.c
index f9a6648..712a8bc 100644
--- a/libgda/sqlite/sqlite-src/sqlite3.c
+++ b/libgda/sqlite/sqlite-src/sqlite3.c
@@ -1,6 +1,6 @@
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
-** version 3.7.12.1. By combining all the individual C code files into this
+** version 3.7.14.1. 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
@@ -389,6 +389,7 @@
**
** SQLITE_SYSTEM_MALLOC // Use normal system malloc()
** SQLITE_WIN32_MALLOC // Use Win32 native heap API
+** SQLITE_ZERO_MALLOC // Use a stub allocator that always fails
** SQLITE_MEMDEBUG // Debugging version of system malloc()
**
** On Windows, if the SQLITE_WIN32_MALLOC_VALIDATE macro is defined and the
@@ -402,11 +403,19 @@
** If none of the above are defined, then set SQLITE_SYSTEM_MALLOC as
** the default.
*/
-#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_WIN32_MALLOC, SQLITE_MEMDEBUG"
-#endif
-#if defined(SQLITE_SYSTEM_MALLOC)+defined(SQLITE_WIN32_MALLOC)+defined(SQLITE_MEMDEBUG)==0
+#if defined(SQLITE_SYSTEM_MALLOC) \
+ + defined(SQLITE_WIN32_MALLOC) \
+ + defined(SQLITE_ZERO_MALLOC) \
+ + defined(SQLITE_MEMDEBUG)>1
+# error "Two or more of the following compile-time configuration options\
+ are defined but at most one is allowed:\
+ SQLITE_SYSTEM_MALLOC, SQLITE_WIN32_MALLOC, SQLITE_MEMDEBUG,\
+ SQLITE_ZERO_MALLOC"
+#endif
+#if defined(SQLITE_SYSTEM_MALLOC) \
+ + defined(SQLITE_WIN32_MALLOC) \
+ + defined(SQLITE_ZERO_MALLOC) \
+ + defined(SQLITE_MEMDEBUG)==0
# define SQLITE_SYSTEM_MALLOC 1
#endif
@@ -443,15 +452,22 @@
#endif
/*
-** Many people are failing to set -DNDEBUG=1 when compiling SQLite.
-** Setting NDEBUG makes the code smaller and run faster. So the following
-** lines are added to automatically set NDEBUG unless the -DSQLITE_DEBUG=1
-** option is set. Thus NDEBUG becomes an opt-in rather than an opt-out
+** NDEBUG and SQLITE_DEBUG are opposites. It should always be true that
+** defined(NDEBUG)==!defined(SQLITE_DEBUG). If this is not currently true,
+** make it true by defining or undefining NDEBUG.
+**
+** Setting NDEBUG makes the code smaller and run faster by disabling the
+** number assert() statements in the code. So we want the default action
+** to be for NDEBUG to be set and NDEBUG to be undefined only if SQLITE_DEBUG
+** is set. Thus NDEBUG becomes an opt-in rather than an opt-out
** feature.
*/
#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
# define NDEBUG 1
#endif
+#if defined(NDEBUG) && defined(SQLITE_DEBUG)
+# undef NDEBUG
+#endif
/*
** The testcase() macro is used to aid in coverage testing. When
@@ -657,9 +673,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.7.12.1"
-#define SQLITE_VERSION_NUMBER 3007012
-#define SQLITE_SOURCE_ID "2012-05-22 02:45:53 6d326d44fd1d626aae0e8456e5fa2049f1ce0789"
+#define SQLITE_VERSION "3.7.14.1"
+#define SQLITE_VERSION_NUMBER 3007014
+#define SQLITE_SOURCE_ID "2012-10-04 19:37:12 091570e46d04e84b67228e0bdbcd6e1fb60c6bdb"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -769,7 +785,8 @@ SQLITE_API int sqlite3_threadsafe(void);
** the opaque structure named "sqlite3". It is useful to think of an sqlite3
** pointer as an object. The [sqlite3_open()], [sqlite3_open16()], and
** [sqlite3_open_v2()] interfaces are its constructors, and [sqlite3_close()]
-** is its destructor. There are many other interfaces (such as
+** and [sqlite3_close_v2()] are its destructors. There are many other
+** interfaces (such as
** [sqlite3_prepare_v2()], [sqlite3_create_function()], and
** [sqlite3_busy_timeout()] to name but three) that are methods on an
** sqlite3 object.
@@ -816,28 +833,46 @@ typedef sqlite_uint64 sqlite3_uint64;
/*
** CAPI3REF: Closing A Database Connection
**
-** ^The sqlite3_close() routine is the destructor for the [sqlite3] object.
-** ^Calls to sqlite3_close() return SQLITE_OK if the [sqlite3] object is
-** successfully destroyed and all associated resources are deallocated.
-**
-** Applications must [sqlite3_finalize | finalize] all [prepared statements]
-** and [sqlite3_blob_close | close] all [BLOB handles] associated with
-** the [sqlite3] object prior to attempting to close the object. ^If
+** ^The sqlite3_close() and sqlite3_close_v2() routines are destructors
+** for the [sqlite3] object.
+** ^Calls to sqlite3_close() and sqlite3_close_v2() return SQLITE_OK if
+** the [sqlite3] object is successfully destroyed and all associated
+** resources are deallocated.
+**
+** ^If the database connection is associated with unfinalized prepared
+** statements or unfinished sqlite3_backup objects then sqlite3_close()
+** will leave the database connection open and return [SQLITE_BUSY].
+** ^If sqlite3_close_v2() is called with unfinalized prepared statements
+** and unfinished sqlite3_backups, then the database connection becomes
+** an unusable "zombie" which will automatically be deallocated when the
+** last prepared statement is finalized or the last sqlite3_backup is
+** finished. The sqlite3_close_v2() interface is intended for use with
+** host languages that are garbage collected, and where the order in which
+** destructors are called is arbitrary.
+**
+** Applications should [sqlite3_finalize | finalize] all [prepared statements],
+** [sqlite3_blob_close | close] all [BLOB handles], and
+** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated
+** with the [sqlite3] object prior to attempting to close the object. ^If
** sqlite3_close() is called on a [database connection] that still has
-** outstanding [prepared statements] or [BLOB handles], then it returns
-** SQLITE_BUSY.
+** outstanding [prepared statements], [BLOB handles], and/or
+** [sqlite3_backup] objects then it returns SQLITE_OK but the deallocation
+** of resources is deferred until all [prepared statements], [BLOB handles],
+** and [sqlite3_backup] objects are also destroyed.
**
-** ^If [sqlite3_close()] is invoked while a transaction is open,
+** ^If an [sqlite3] object is destroyed while a transaction is open,
** the transaction is automatically rolled back.
**
-** The C parameter to [sqlite3_close(C)] must be either a NULL
+** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)]
+** must be either a NULL
** pointer or an [sqlite3] object pointer obtained
** from [sqlite3_open()], [sqlite3_open16()], or
** [sqlite3_open_v2()], and not previously closed.
-** ^Calling sqlite3_close() with a NULL pointer argument is a
-** harmless no-op.
+** ^Calling sqlite3_close() or sqlite3_close_v2() with a NULL pointer
+** argument is a harmless no-op.
*/
-SQLITE_API int sqlite3_close(sqlite3 *);
+SQLITE_API int sqlite3_close(sqlite3*);
+SQLITE_API int sqlite3_close_v2(sqlite3*);
/*
** The type for a callback function.
@@ -1028,6 +1063,7 @@ SQLITE_API int sqlite3_exec(
#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_MEMORY 0x00000080 /* 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 */
@@ -1047,7 +1083,7 @@ SQLITE_API int sqlite3_exec(
** CAPI3REF: Device Characteristics
**
** The xDeviceCharacteristics method of the [sqlite3_io_methods]
-** object returns an integer which is a vector of the these
+** object returns an integer which is a vector of these
** bit values expressing I/O characteristics of the mass storage
** device that holds the file that the [sqlite3_io_methods]
** refers to.
@@ -2719,12 +2755,12 @@ SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list);
** implementation of these routines to be omitted. That capability
** is no longer provided. Only built-in memory allocators can be used.
**
-** The Windows OS interface layer calls
+** Prior to SQLite version 3.7.10, the Windows OS interface layer called
** the system malloc() and free() directly when converting
** filenames between the UTF-8 encoding used by SQLite
** and whatever filename encoding is used by the particular Windows
-** installation. Memory allocation errors are detected, but
-** they are reported back as [SQLITE_CANTOPEN] or
+** installation. Memory allocation errors were detected, but
+** they were reported back as [SQLITE_CANTOPEN] or
** [SQLITE_IOERR] rather than [SQLITE_NOMEM].
**
** The pointer arguments to [sqlite3_free()] and [sqlite3_realloc()]
@@ -3125,18 +3161,20 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** 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)^.
+** <li> <b>mode</b>: ^(The mode parameter may be set to either "ro", "rw",
+** "rwc", or "memory". 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.
+** SQLITE_OPEN_READWRITE and SQLITE_OPEN_CREATE. ^If the mode option is
+** set to "memory" then a pure [in-memory database] that never reads
+** or writes from disk 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 in the third parameter to sqlite3_open_v2().
**
** <li> <b>cache</b>: ^The cache parameter may be set to either "shared" or
** "private". ^Setting it to "shared" is equivalent to setting the
@@ -3195,6 +3233,12 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** codepage is currently defined. Filenames containing international
** characters must be converted to UTF-8 prior to passing them into
** sqlite3_open() or sqlite3_open_v2().
+**
+** <b>Note to Windows Runtime users:</b> The temporary directory must be set
+** prior to calling sqlite3_open() or sqlite3_open_v2(). Otherwise, various
+** features that require the use of temporary files may fail.
+**
+** See also: [sqlite3_temp_directory]
*/
SQLITE_API int sqlite3_open(
const char *filename, /* Database filename (UTF-8) */
@@ -3687,8 +3731,11 @@ typedef struct sqlite3_context sqlite3_context;
** ^(In those routines that have a fourth argument, its value is the
** number of bytes in the parameter. To be clear: the value is the
** number of <u>bytes</u> in the value, not the number of characters.)^
-** ^If the fourth parameter is negative, the length of the string is
+** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16()
+** is negative, then the length of the string is
** the number of bytes up to the first zero terminator.
+** If the fourth parameter to sqlite3_bind_blob() is negative, then
+** the behavior is undefined.
** If a non-negative fourth parameter is provided to sqlite3_bind_text()
** or sqlite3_bind_text16() then that parameter must be the byte offset
** where the NUL terminator would occur assuming the string were NUL
@@ -4685,11 +4732,11 @@ typedef void (*sqlite3_destructor_type)(void*);
** the error code is SQLITE_ERROR. ^A subsequent call to sqlite3_result_error()
** or sqlite3_result_error16() resets the error code to SQLITE_ERROR.
**
-** ^The sqlite3_result_toobig() interface causes SQLite to throw an error
-** indicating that a string or BLOB is too long to represent.
+** ^The sqlite3_result_error_toobig() interface causes SQLite to throw an
+** error indicating that a string or BLOB is too long to represent.
**
-** ^The sqlite3_result_nomem() interface causes SQLite to throw an error
-** indicating that a memory allocation failed.
+** ^The sqlite3_result_error_nomem() interface causes SQLite to throw an
+** error indicating that a memory allocation failed.
**
** ^The sqlite3_result_int() interface sets the return value
** of the application-defined function to be the 32-bit signed integer
@@ -4996,10 +5043,62 @@ SQLITE_API int sqlite3_sleep(int);
** Hence, if this variable is modified directly, either it should be
** made NULL or made to point to memory obtained from [sqlite3_malloc]
** or else the use of the [temp_store_directory pragma] should be avoided.
+**
+** <b>Note to Windows Runtime users:</b> The temporary directory must be set
+** prior to calling [sqlite3_open] or [sqlite3_open_v2]. Otherwise, various
+** features that require the use of temporary files may fail. Here is an
+** example of how to do this using C++ with the Windows Runtime:
+**
+** <blockquote><pre>
+** LPCWSTR zPath = Windows::Storage::ApplicationData::Current->
+** TemporaryFolder->Path->Data();
+** char zPathBuf[MAX_PATH + 1];
+** memset(zPathBuf, 0, sizeof(zPathBuf));
+** WideCharToMultiByte(CP_UTF8, 0, zPath, -1, zPathBuf, sizeof(zPathBuf),
+** NULL, NULL);
+** sqlite3_temp_directory = sqlite3_mprintf("%s", zPathBuf);
+** </pre></blockquote>
*/
SQLITE_API char *sqlite3_temp_directory;
/*
+** CAPI3REF: Name Of The Folder Holding Database Files
+**
+** ^(If this global variable is made to point to a string which is
+** the name of a folder (a.k.a. directory), then all database files
+** specified with a relative pathname and created or accessed by
+** SQLite when using a built-in windows [sqlite3_vfs | VFS] will be assumed
+** to be relative to that directory.)^ ^If this variable is a NULL
+** pointer, then SQLite assumes that all database files specified
+** with a relative pathname are relative to the current directory
+** for the process. Only the windows VFS makes use of this global
+** variable; it is ignored by the unix VFS.
+**
+** Changing the value of this variable while a database connection is
+** open can result in a corrupt database.
+**
+** It is not safe to read or modify this variable in more than one
+** thread at a time. It is not safe to read or modify this variable
+** if a [database connection] is being used at the same time in a separate
+** thread.
+** It is intended that this variable be set once
+** as part of process initialization and before any SQLite interface
+** routines have been called and that this variable remain unchanged
+** thereafter.
+**
+** ^The [data_store_directory pragma] may modify this variable and cause
+** it to point to memory obtained from [sqlite3_malloc]. ^Furthermore,
+** the [data_store_directory pragma] always assumes that any string
+** that this variable points to is held in memory obtained from
+** [sqlite3_malloc] and the pragma may attempt to free that memory
+** using [sqlite3_free].
+** Hence, if this variable is modified directly, either it should be
+** made NULL or made to point to memory obtained from [sqlite3_malloc]
+** or else the use of the [data_store_directory pragma] should be avoided.
+*/
+SQLITE_API char *sqlite3_data_directory;
+
+/*
** CAPI3REF: Test For Auto-Commit Mode
** KEYWORDS: {autocommit mode}
**
@@ -5177,7 +5276,6 @@ SQLITE_API void *sqlite3_update_hook(
/*
** CAPI3REF: Enable Or Disable Shared Pager Cache
-** KEYWORDS: {shared cache}
**
** ^(This routine enables or disables the sharing of the database cache
** and schema data structures between [database connection | connections]
@@ -6005,7 +6103,6 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);
** implementations are available in the SQLite core:
**
** <ul>
-** <li> SQLITE_MUTEX_OS2
** <li> SQLITE_MUTEX_PTHREADS
** <li> SQLITE_MUTEX_W32
** <li> SQLITE_MUTEX_NOOP
@@ -6013,9 +6110,9 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);
**
** ^The SQLITE_MUTEX_NOOP implementation is a set of routines
** that does no real locking and is appropriate for use in
-** a single-threaded application. ^The SQLITE_MUTEX_OS2,
-** SQLITE_MUTEX_PTHREADS, and SQLITE_MUTEX_W32 implementations
-** are appropriate for use on OS/2, Unix, and Windows.
+** a single-threaded application. ^The SQLITE_MUTEX_PTHREADS and
+** SQLITE_MUTEX_W32 implementations are appropriate for use on Unix
+** and Windows.
**
** ^(If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor
** macro defined (with "-DSQLITE_MUTEX_APPDEF=1"), then no mutex
@@ -8326,6 +8423,12 @@ SQLITE_PRIVATE int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value);
#define BTREE_USER_VERSION 6
#define BTREE_INCR_VACUUM 7
+/*
+** Values that may be OR'd together to form the second argument of an
+** sqlite3BtreeCursorHints() call.
+*/
+#define BTREE_BULKLOAD 0x00000001
+
SQLITE_PRIVATE int sqlite3BtreeCursor(
Btree*, /* BTree containing table to open */
int iTable, /* Index of root page */
@@ -8369,8 +8472,8 @@ SQLITE_PRIVATE struct Pager *sqlite3BtreePager(Btree*);
SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*);
SQLITE_PRIVATE void sqlite3BtreeCacheOverflow(BtCursor *);
SQLITE_PRIVATE void sqlite3BtreeClearCursor(BtCursor *);
-
SQLITE_PRIVATE int sqlite3BtreeSetVersion(Btree *pBt, int iVersion);
+SQLITE_PRIVATE void sqlite3BtreeCursorHints(BtCursor *, unsigned int mask);
#ifndef NDEBUG
SQLITE_PRIVATE int sqlite3BtreeCursorIsValid(BtCursor*);
@@ -9016,7 +9119,7 @@ SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager);
SQLITE_PRIVATE u8 sqlite3PagerIsreadonly(Pager*);
SQLITE_PRIVATE int sqlite3PagerRefcount(Pager*);
SQLITE_PRIVATE int sqlite3PagerMemUsed(Pager*);
-SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager*);
+SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager*, int);
SQLITE_PRIVATE const sqlite3_vfs *sqlite3PagerVfs(Pager*);
SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*);
SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*);
@@ -9244,7 +9347,7 @@ SQLITE_PRIVATE void sqlite3PCacheSetDefault(void);
/*
** Figure out if we are dealing with Unix, Windows, or some other
** operating system. After the following block of preprocess macros,
-** all of SQLITE_OS_UNIX, SQLITE_OS_WIN, SQLITE_OS_OS2, and SQLITE_OS_OTHER
+** all of SQLITE_OS_UNIX, SQLITE_OS_WIN, and SQLITE_OS_OTHER
** will defined to either 1 or 0. One of the four will be 1. The other
** three will be 0.
*/
@@ -9254,8 +9357,6 @@ SQLITE_PRIVATE void sqlite3PCacheSetDefault(void);
# define SQLITE_OS_UNIX 0
# undef SQLITE_OS_WIN
# define SQLITE_OS_WIN 0
-# undef SQLITE_OS_OS2
-# define SQLITE_OS_OS2 0
# else
# undef SQLITE_OS_OTHER
# endif
@@ -9266,19 +9367,12 @@ SQLITE_PRIVATE void sqlite3PCacheSetDefault(void);
# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__)
# define SQLITE_OS_WIN 1
# define SQLITE_OS_UNIX 0
-# define SQLITE_OS_OS2 0
-# elif defined(__EMX__) || defined(_OS2) || defined(OS2) || defined(_OS2_) || defined(__OS2__)
-# define SQLITE_OS_WIN 0
-# define SQLITE_OS_UNIX 0
-# define SQLITE_OS_OS2 1
# else
# define SQLITE_OS_WIN 0
# define SQLITE_OS_UNIX 1
-# define SQLITE_OS_OS2 0
# endif
# else
# define SQLITE_OS_UNIX 0
-# define SQLITE_OS_OS2 0
# endif
#else
# ifndef SQLITE_OS_WIN
@@ -9286,28 +9380,8 @@ SQLITE_PRIVATE void sqlite3PCacheSetDefault(void);
# endif
#endif
-/*
-** Define the maximum size of a temporary filename
-*/
#if SQLITE_OS_WIN
# include <windows.h>
-# define SQLITE_TEMPNAME_SIZE (MAX_PATH+50)
-#elif SQLITE_OS_OS2
-# if (__GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ >= 3) && defined(OS2_HIGH_MEMORY)
-# include <os2safe.h> /* has to be included before os2.h for linking to work */
-# endif
-# define INCL_DOSDATETIME
-# define INCL_DOSFILEMGR
-# define INCL_DOSERRORS
-# define INCL_DOSMISC
-# define INCL_DOSPROCESS
-# define INCL_DOSMODULEMGR
-# define INCL_DOSSEMAPHORES
-# include <os2.h>
-# include <uconv.h>
-# define SQLITE_TEMPNAME_SIZE (CCHMAXPATHCOMP)
-#else
-# define SQLITE_TEMPNAME_SIZE 200
#endif
/*
@@ -9341,6 +9415,22 @@ SQLITE_PRIVATE void sqlite3PCacheSetDefault(void);
# define SQLITE_OS_WINCE 0
#endif
+/*
+** Determine if we are dealing with WinRT, which provides only a subset of
+** the full Win32 API.
+*/
+#if !defined(SQLITE_OS_WINRT)
+# define SQLITE_OS_WINRT 0
+#endif
+
+/*
+** When compiled for WinCE or WinRT, there is no concept of the current
+** directory.
+ */
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
+# define SQLITE_CURDIR 1
+#endif
+
/* If the SET_FULLSYNC macro is not defined above, then make it
** a no-op
*/
@@ -9563,8 +9653,6 @@ SQLITE_PRIVATE int sqlite3OsCloseFree(sqlite3_file *);
** SQLITE_MUTEX_PTHREADS For multi-threaded applications on Unix.
**
** SQLITE_MUTEX_W32 For multi-threaded applications on Win32.
-**
-** SQLITE_MUTEX_OS2 For multi-threaded applications on OS/2.
*/
#if !SQLITE_THREADSAFE
# define SQLITE_MUTEX_OMIT
@@ -9574,8 +9662,6 @@ SQLITE_PRIVATE int sqlite3OsCloseFree(sqlite3_file *);
# define SQLITE_MUTEX_PTHREADS
# elif SQLITE_OS_WIN
# define SQLITE_MUTEX_W32
-# elif SQLITE_OS_OS2
-# define SQLITE_MUTEX_OS2
# else
# define SQLITE_MUTEX_NOOP
# endif
@@ -9896,6 +9982,7 @@ struct sqlite3 {
#define SQLITE_MAGIC_SICK 0x4b771290 /* Error and awaiting close */
#define SQLITE_MAGIC_BUSY 0xf03b7906 /* Database currently in use */
#define SQLITE_MAGIC_ERROR 0xb5357930 /* An SQLITE_MISUSE error occurred */
+#define SQLITE_MAGIC_ZOMBIE 0x64cffc7f /* Close with last statement close */
/*
** Each SQL function is defined by an instance of the following
@@ -10602,8 +10689,9 @@ struct Expr {
i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */
i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */
u8 flags2; /* Second set of flags. EP2_... */
- u8 op2; /* If a TK_REGISTER, the original value of Expr.op */
- /* If TK_COLUMN, the value of p5 for OP_Column */
+ u8 op2; /* TK_REGISTER: original value of Expr.op
+ ** TK_COLUMN: the value of p5 for OP_Column
+ ** TK_AGG_FUNCTION: nesting depth */
AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
Table *pTab; /* Table for TK_COLUMN expressions. */
#if SQLITE_MAX_EXPR_DEPTH>0
@@ -10828,7 +10916,7 @@ struct WherePlan {
/*
** For each nested loop in a WHERE clause implementation, the WhereInfo
** structure contains a single instance of this structure. This structure
-** is intended to be private the the where.c module and should not be
+** is intended to be private to the where.c module and should not be
** access or modified by other modules.
**
** The pIdxInfo field is used to help pick the best index on a
@@ -10858,6 +10946,7 @@ struct WhereLevel {
int addrInTop; /* Top of the IN loop */
} *aInLoop; /* Information about each nested IN operator */
} in; /* Used when plan.wsFlags&WHERE_IN_ABLE */
+ Index *pCovidx; /* Possible covering index for WHERE_MULTI_OR */
} u;
/* The following field is really not part of the current level. But
@@ -11030,10 +11119,10 @@ struct Select {
typedef struct SelectDest SelectDest;
struct SelectDest {
u8 eDest; /* How to dispose of the results */
- u8 affinity; /* Affinity used when eDest==SRT_Set */
- int iParm; /* A parameter used by the eDest disposal method */
- int iMem; /* Base register where results are written */
- int nMem; /* Number of registers allocated */
+ u8 affSdst; /* Affinity used when eDest==SRT_Set */
+ int iSDParm; /* A parameter used by the eDest disposal method */
+ int iSdst; /* Base register where results are written */
+ int nSdst; /* Number of registers allocated */
};
/*
@@ -11229,6 +11318,8 @@ struct AuthContext {
#define OPFLAG_CLEARCACHE 0x20 /* Clear pseudo-table cache in OP_Column */
#define OPFLAG_LENGTHARG 0x40 /* OP_Column only used for length() */
#define OPFLAG_TYPEOFARG 0x80 /* OP_Column only used for typeof() */
+#define OPFLAG_BULKCSR 0x01 /* OP_Open** used to open bulk cursor */
+#define OPFLAG_P2ISREG 0x02 /* P2 to OP_Open** is a register number */
/*
* Each trigger present in the database schema is stored as an instance of
@@ -11408,10 +11499,12 @@ struct Walker {
int (*xExprCallback)(Walker*, Expr*); /* Callback for expressions */
int (*xSelectCallback)(Walker*,Select*); /* Callback for SELECTs */
Parse *pParse; /* Parser context. */
+ int walkerDepth; /* Number of subqueries */
union { /* Extra data for callback */
NameContext *pNC; /* Naming context */
int i; /* Integer value */
SrcList *pSrcList; /* FROM clause */
+ struct SrcCount *pSrcCount; /* Counting column references */
} u;
};
@@ -11631,7 +11724,9 @@ SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3*, ExprList*);
SQLITE_PRIVATE int sqlite3Init(sqlite3*, char**);
SQLITE_PRIVATE int sqlite3InitCallback(void*, int, char**, char**);
SQLITE_PRIVATE void sqlite3Pragma(Parse*,Token*,Token*,Token*,int);
-SQLITE_PRIVATE void sqlite3ResetInternalSchema(sqlite3*, int);
+SQLITE_PRIVATE void sqlite3ResetAllSchemasOfConnection(sqlite3*);
+SQLITE_PRIVATE void sqlite3ResetOneSchema(sqlite3*,int);
+SQLITE_PRIVATE void sqlite3CollapseDatabaseArray(sqlite3*);
SQLITE_PRIVATE void sqlite3BeginParse(Parse*,int);
SQLITE_PRIVATE void sqlite3CommitInternalChanges(sqlite3*);
SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*);
@@ -11711,7 +11806,8 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere(Parse *, SrcList *, Expr *, ExprList *, E
#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**,ExprList*,u16);
+SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
+ Parse*,SrcList*,Expr*,ExprList**,ExprList*,u16,int);
SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo*);
SQLITE_PRIVATE int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, u8);
SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int);
@@ -11743,6 +11839,7 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Expr*, Expr*);
SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList*, ExprList*);
SQLITE_PRIVATE void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*);
SQLITE_PRIVATE void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*);
+SQLITE_PRIVATE int sqlite3FunctionUsesThisSrc(Expr*, SrcList*);
SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse*);
SQLITE_PRIVATE void sqlite3PrngSaveState(void);
SQLITE_PRIVATE void sqlite3PrngRestoreState(void);
@@ -11755,6 +11852,7 @@ SQLITE_PRIVATE void sqlite3CommitTransaction(Parse*);
SQLITE_PRIVATE void sqlite3RollbackTransaction(Parse*);
SQLITE_PRIVATE void sqlite3Savepoint(Parse*, int, Token*);
SQLITE_PRIVATE void sqlite3CloseSavepoints(sqlite3 *);
+SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3*);
SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr*);
SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*);
SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*);
@@ -12036,6 +12134,7 @@ SQLITE_PRIVATE int sqlite3Utf8To8(unsigned char*);
# define sqlite3GetVTable(X,Y) ((VTable*)0)
#else
SQLITE_PRIVATE void sqlite3VtabClear(sqlite3 *db, Table*);
+SQLITE_PRIVATE void sqlite3VtabDisconnect(sqlite3 *db, Table *p);
SQLITE_PRIVATE int sqlite3VtabSync(sqlite3 *db, char **);
SQLITE_PRIVATE int sqlite3VtabRollback(sqlite3 *db);
SQLITE_PRIVATE int sqlite3VtabCommit(sqlite3 *db);
@@ -12490,6 +12589,9 @@ static const char * const azCompileOpt[] = {
#ifdef SQLITE_COVERAGE_TEST
"COVERAGE_TEST",
#endif
+#ifdef SQLITE_CURDIR
+ "CURDIR",
+#endif
#ifdef SQLITE_DEBUG
"DEBUG",
#endif
@@ -13292,11 +13394,11 @@ SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p);
#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 *);
+SQLITE_PRIVATE int sqlite3VdbeSorterRowkey(const VdbeCursor *, Mem *);
+SQLITE_PRIVATE int sqlite3VdbeSorterNext(sqlite3 *, const VdbeCursor *, int *);
+SQLITE_PRIVATE int sqlite3VdbeSorterRewind(sqlite3 *, const VdbeCursor *, int *);
+SQLITE_PRIVATE int sqlite3VdbeSorterWrite(sqlite3 *, const VdbeCursor *, Mem *);
+SQLITE_PRIVATE int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int *);
#endif
#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0
@@ -15443,14 +15545,14 @@ static int sqlite3MemInit(void *NotUsed){
}else{
/* only 1 core, use our own zone to contention over global locks,
** e.g. we have our own dedicated locks */
- bool success;
+ bool success;
malloc_zone_t* newzone = malloc_create_zone(4096, 0);
malloc_set_zone_name(newzone, "Sqlite_Heap");
do{
success = OSAtomicCompareAndSwapPtrBarrier(NULL, newzone,
(void * volatile *)&_sqliteZone_);
}while(!_sqliteZone_);
- if( !success ){
+ if( !success ){
/* somebody registered a zone first */
malloc_destroy_zone(newzone);
}
@@ -17656,7 +17758,7 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
#endif /* !defined(SQLITE_MUTEX_OMIT) */
/************** End of mutex_noop.c ******************************************/
-/************** Begin file mutex_os2.c ***************************************/
+/************** Begin file mutex_unix.c **************************************/
/*
** 2007 August 28
**
@@ -17668,49 +17770,85 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
** May you share freely, never taking more than you give.
**
*************************************************************************
-** This file contains the C functions that implement mutexes for OS/2
+** This file contains the C functions that implement mutexes for pthreads
*/
/*
-** The code in this file is only used if SQLITE_MUTEX_OS2 is defined.
-** See the mutex.h file for details.
+** The code in this file is only used if we are compiling threadsafe
+** under unix with pthreads.
+**
+** Note that this implementation requires a version of pthreads that
+** supports recursive mutexes.
*/
-#ifdef SQLITE_MUTEX_OS2
+#ifdef SQLITE_MUTEX_PTHREADS
-/********************** OS/2 Mutex Implementation **********************
-**
-** This implementation of mutexes is built using the OS/2 API.
+#include <pthread.h>
+
+/*
+** The sqlite3_mutex.id, sqlite3_mutex.nRef, and sqlite3_mutex.owner fields
+** are necessary under two condidtions: (1) Debug builds and (2) using
+** home-grown mutexes. Encapsulate these conditions into a single #define.
*/
+#if defined(SQLITE_DEBUG) || defined(SQLITE_HOMEGROWN_RECURSIVE_MUTEX)
+# define SQLITE_MUTEX_NREF 1
+#else
+# define SQLITE_MUTEX_NREF 0
+#endif
/*
-** The mutex object
** Each recursive mutex is an instance of the following structure.
*/
struct sqlite3_mutex {
- HMTX mutex; /* Mutex controlling the lock */
- int id; /* Mutex type */
-#ifdef SQLITE_DEBUG
- int trace; /* True to trace changes */
+ pthread_mutex_t mutex; /* Mutex controlling the lock */
+#if SQLITE_MUTEX_NREF
+ int id; /* Mutex type */
+ volatile int nRef; /* Number of entrances */
+ volatile pthread_t owner; /* Thread that is within this mutex */
+ int trace; /* True to trace changes */
#endif
};
-
-#ifdef SQLITE_DEBUG
-#define SQLITE3_MUTEX_INITIALIZER { 0, 0, 0 }
+#if SQLITE_MUTEX_NREF
+#define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0, 0, (pthread_t)0, 0 }
#else
-#define SQLITE3_MUTEX_INITIALIZER { 0, 0 }
+#define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER }
+#endif
+
+/*
+** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
+** intended for use only inside assert() statements. On some platforms,
+** there might be race conditions that can cause these routines to
+** deliver incorrect results. In particular, if pthread_equal() is
+** not an atomic operation, then these routines might delivery
+** incorrect results. On most platforms, pthread_equal() is a
+** comparison of two integers and is therefore atomic. But we are
+** told that HPUX is not such a platform. If so, then these routines
+** will not always work correctly on HPUX.
+**
+** On those platforms where pthread_equal() is not atomic, SQLite
+** should be compiled without -DSQLITE_DEBUG and with -DNDEBUG to
+** make sure no assert() statements are evaluated and hence these
+** routines are never called.
+*/
+#if !defined(NDEBUG) || defined(SQLITE_DEBUG)
+static int pthreadMutexHeld(sqlite3_mutex *p){
+ return (p->nRef!=0 && pthread_equal(p->owner, pthread_self()));
+}
+static int pthreadMutexNotheld(sqlite3_mutex *p){
+ return p->nRef==0 || pthread_equal(p->owner, pthread_self())==0;
+}
#endif
/*
** Initialize and deinitialize the mutex subsystem.
*/
-static int os2MutexInit(void){ return SQLITE_OK; }
-static int os2MutexEnd(void){ return SQLITE_OK; }
+static int pthreadMutexInit(void){ return SQLITE_OK; }
+static int pthreadMutexEnd(void){ return SQLITE_OK; }
/*
** The sqlite3_mutex_alloc() routine allocates a new
** mutex and returns a pointer to it. If it returns NULL
-** that means that a mutex could not be allocated.
-** SQLite will unwind its stack and return an error. The argument
+** that means that a mutex could not be allocated. SQLite
+** will unwind its stack and return an error. The argument
** to sqlite3_mutex_alloc() is one of these integer constants:
**
** <ul>
@@ -17721,7 +17859,7 @@ static int os2MutexEnd(void){ return SQLITE_OK; }
** <li> SQLITE_MUTEX_STATIC_MEM2
** <li> SQLITE_MUTEX_STATIC_PRNG
** <li> SQLITE_MUTEX_STATIC_LRU
-** <li> SQLITE_MUTEX_STATIC_LRU2
+** <li> SQLITE_MUTEX_STATIC_PMEM
** </ul>
**
** The first two constants cause sqlite3_mutex_alloc() to create
@@ -17744,67 +17882,59 @@ static int os2MutexEnd(void){ return SQLITE_OK; }
**
** Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST
** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc()
-** returns a different mutex on every call. But for the static
+** returns a different mutex on every call. But for the static
** mutex types, the same mutex is returned on every call that has
** the same type number.
*/
-static sqlite3_mutex *os2MutexAlloc(int iType){
- sqlite3_mutex *p = NULL;
+static sqlite3_mutex *pthreadMutexAlloc(int iType){
+ static sqlite3_mutex staticMutexes[] = {
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER
+ };
+ sqlite3_mutex *p;
switch( iType ){
- case SQLITE_MUTEX_FAST:
case SQLITE_MUTEX_RECURSIVE: {
p = sqlite3MallocZero( sizeof(*p) );
if( p ){
+#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
+ /* If recursive mutexes are not available, we will have to
+ ** build our own. See below. */
+ pthread_mutex_init(&p->mutex, 0);
+#else
+ /* Use a recursive mutex if it is available */
+ pthread_mutexattr_t recursiveAttr;
+ pthread_mutexattr_init(&recursiveAttr);
+ pthread_mutexattr_settype(&recursiveAttr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&p->mutex, &recursiveAttr);
+ pthread_mutexattr_destroy(&recursiveAttr);
+#endif
+#if SQLITE_MUTEX_NREF
+ p->id = iType;
+#endif
+ }
+ break;
+ }
+ case SQLITE_MUTEX_FAST: {
+ p = sqlite3MallocZero( sizeof(*p) );
+ if( p ){
+#if SQLITE_MUTEX_NREF
p->id = iType;
- if( DosCreateMutexSem( 0, &p->mutex, 0, FALSE ) != NO_ERROR ){
- sqlite3_free( p );
- p = NULL;
- }
+#endif
+ pthread_mutex_init(&p->mutex, 0);
}
break;
}
default: {
- static volatile int isInit = 0;
- static sqlite3_mutex staticMutexes[6] = {
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- };
- if ( !isInit ){
- APIRET rc;
- PTIB ptib;
- PPIB ppib;
- HMTX mutex;
- char name[32];
- DosGetInfoBlocks( &ptib, &ppib );
- sqlite3_snprintf( sizeof(name), name, "\\SEM32\\SQLITE%04x",
- ppib->pib_ulpid );
- while( !isInit ){
- mutex = 0;
- rc = DosCreateMutexSem( name, &mutex, 0, FALSE);
- if( rc == NO_ERROR ){
- unsigned int i;
- if( !isInit ){
- for( i = 0; i < sizeof(staticMutexes)/sizeof(staticMutexes[0]); i++ ){
- DosCreateMutexSem( 0, &staticMutexes[i].mutex, 0, FALSE );
- }
- isInit = 1;
- }
- DosCloseMutexSem( mutex );
- }else if( rc == ERROR_DUPLICATE_NAME ){
- DosSleep( 1 );
- }else{
- return p;
- }
- }
- }
assert( iType-2 >= 0 );
- assert( iType-2 < sizeof(staticMutexes)/sizeof(staticMutexes[0]) );
+ assert( iType-2 < ArraySize(staticMutexes) );
p = &staticMutexes[iType-2];
+#if SQLITE_MUTEX_NREF
p->id = iType;
+#endif
break;
}
}
@@ -17813,57 +17943,16 @@ static sqlite3_mutex *os2MutexAlloc(int iType){
/*
-** This routine deallocates a previously allocated mutex.
-** SQLite is careful to deallocate every mutex that it allocates.
+** This routine deallocates a previously
+** allocated mutex. SQLite is careful to deallocate every
+** mutex that it allocates.
*/
-static void os2MutexFree(sqlite3_mutex *p){
-#ifdef SQLITE_DEBUG
- TID tid;
- PID pid;
- ULONG ulCount;
- DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
- assert( ulCount==0 );
+static void pthreadMutexFree(sqlite3_mutex *p){
+ assert( p->nRef==0 );
assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
-#endif
- DosCloseMutexSem( p->mutex );
- sqlite3_free( p );
-}
-
-#ifdef SQLITE_DEBUG
-/*
-** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
-** intended for use inside assert() statements.
-*/
-static int os2MutexHeld(sqlite3_mutex *p){
- TID tid;
- PID pid;
- ULONG ulCount;
- PTIB ptib;
- DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
- if( ulCount==0 || ( ulCount>1 && p->id!=SQLITE_MUTEX_RECURSIVE ) )
- return 0;
- DosGetInfoBlocks(&ptib, NULL);
- return tid==ptib->tib_ptib2->tib2_ultid;
-}
-static int os2MutexNotheld(sqlite3_mutex *p){
- TID tid;
- PID pid;
- ULONG ulCount;
- PTIB ptib;
- DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
- if( ulCount==0 )
- return 1;
- DosGetInfoBlocks(&ptib, NULL);
- return tid!=ptib->tib_ptib2->tib2_ultid;
-}
-static void os2MutexTrace(sqlite3_mutex *p, char *pAction){
- TID tid;
- PID pid;
- ULONG ulCount;
- DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
- printf("%s mutex %p (%d) with nRef=%ld\n", pAction, (void*)p, p->trace, ulCount);
+ pthread_mutex_destroy(&p->mutex);
+ sqlite3_free(p);
}
-#endif
/*
** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt
@@ -17876,22 +17965,96 @@ static void os2MutexTrace(sqlite3_mutex *p, char *pAction){
** can enter. If the same thread tries to enter any other kind of mutex
** more than once, the behavior is undefined.
*/
-static void os2MutexEnter(sqlite3_mutex *p){
- assert( p->id==SQLITE_MUTEX_RECURSIVE || os2MutexNotheld(p) );
- DosRequestMutexSem(p->mutex, SEM_INDEFINITE_WAIT);
+static void pthreadMutexEnter(sqlite3_mutex *p){
+ assert( p->id==SQLITE_MUTEX_RECURSIVE || pthreadMutexNotheld(p) );
+
+#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
+ /* If recursive mutexes are not available, then we have to grow
+ ** our own. This implementation assumes that pthread_equal()
+ ** is atomic - that it cannot be deceived into thinking self
+ ** and p->owner are equal if p->owner changes between two values
+ ** that are not equal to self while the comparison is taking place.
+ ** This implementation also assumes a coherent cache - that
+ ** separate processes cannot read different values from the same
+ ** address at the same time. If either of these two conditions
+ ** are not met, then the mutexes will fail and problems will result.
+ */
+ {
+ pthread_t self = pthread_self();
+ if( p->nRef>0 && pthread_equal(p->owner, self) ){
+ p->nRef++;
+ }else{
+ pthread_mutex_lock(&p->mutex);
+ assert( p->nRef==0 );
+ p->owner = self;
+ p->nRef = 1;
+ }
+ }
+#else
+ /* Use the built-in recursive mutexes if they are available.
+ */
+ pthread_mutex_lock(&p->mutex);
+#if SQLITE_MUTEX_NREF
+ assert( p->nRef>0 || p->owner==0 );
+ p->owner = pthread_self();
+ p->nRef++;
+#endif
+#endif
+
#ifdef SQLITE_DEBUG
- if( p->trace ) os2MutexTrace(p, "enter");
+ if( p->trace ){
+ printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
+ }
#endif
}
-static int os2MutexTry(sqlite3_mutex *p){
- int rc = SQLITE_BUSY;
- assert( p->id==SQLITE_MUTEX_RECURSIVE || os2MutexNotheld(p) );
- if( DosRequestMutexSem(p->mutex, SEM_IMMEDIATE_RETURN) == NO_ERROR ) {
+static int pthreadMutexTry(sqlite3_mutex *p){
+ int rc;
+ assert( p->id==SQLITE_MUTEX_RECURSIVE || pthreadMutexNotheld(p) );
+
+#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
+ /* If recursive mutexes are not available, then we have to grow
+ ** our own. This implementation assumes that pthread_equal()
+ ** is atomic - that it cannot be deceived into thinking self
+ ** and p->owner are equal if p->owner changes between two values
+ ** that are not equal to self while the comparison is taking place.
+ ** This implementation also assumes a coherent cache - that
+ ** separate processes cannot read different values from the same
+ ** address at the same time. If either of these two conditions
+ ** are not met, then the mutexes will fail and problems will result.
+ */
+ {
+ pthread_t self = pthread_self();
+ if( p->nRef>0 && pthread_equal(p->owner, self) ){
+ p->nRef++;
+ rc = SQLITE_OK;
+ }else if( pthread_mutex_trylock(&p->mutex)==0 ){
+ assert( p->nRef==0 );
+ p->owner = self;
+ p->nRef = 1;
+ rc = SQLITE_OK;
+ }else{
+ rc = SQLITE_BUSY;
+ }
+ }
+#else
+ /* Use the built-in recursive mutexes if they are available.
+ */
+ if( pthread_mutex_trylock(&p->mutex)==0 ){
+#if SQLITE_MUTEX_NREF
+ p->owner = pthread_self();
+ p->nRef++;
+#endif
rc = SQLITE_OK;
-#ifdef SQLITE_DEBUG
- if( p->trace ) os2MutexTrace(p, "try");
+ }else{
+ rc = SQLITE_BUSY;
+ }
#endif
+
+#ifdef SQLITE_DEBUG
+ if( rc==SQLITE_OK && p->trace ){
+ printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
}
+#endif
return rc;
}
@@ -17901,26 +18064,41 @@ static int os2MutexTry(sqlite3_mutex *p){
** is undefined if the mutex is not currently entered or
** is not currently allocated. SQLite will never do either.
*/
-static void os2MutexLeave(sqlite3_mutex *p){
- assert( os2MutexHeld(p) );
- DosReleaseMutexSem(p->mutex);
+static void pthreadMutexLeave(sqlite3_mutex *p){
+ assert( pthreadMutexHeld(p) );
+#if SQLITE_MUTEX_NREF
+ p->nRef--;
+ if( p->nRef==0 ) p->owner = 0;
+#endif
+ assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
+
+#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
+ if( p->nRef==0 ){
+ pthread_mutex_unlock(&p->mutex);
+ }
+#else
+ pthread_mutex_unlock(&p->mutex);
+#endif
+
#ifdef SQLITE_DEBUG
- if( p->trace ) os2MutexTrace(p, "leave");
+ if( p->trace ){
+ printf("leave mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
+ }
#endif
}
SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
static const sqlite3_mutex_methods sMutex = {
- os2MutexInit,
- os2MutexEnd,
- os2MutexAlloc,
- os2MutexFree,
- os2MutexEnter,
- os2MutexTry,
- os2MutexLeave,
+ pthreadMutexInit,
+ pthreadMutexEnd,
+ pthreadMutexAlloc,
+ pthreadMutexFree,
+ pthreadMutexEnter,
+ pthreadMutexTry,
+ pthreadMutexLeave,
#ifdef SQLITE_DEBUG
- os2MutexHeld,
- os2MutexNotheld
+ pthreadMutexHeld,
+ pthreadMutexNotheld
#else
0,
0
@@ -17929,12 +18107,13 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
return &sMutex;
}
-#endif /* SQLITE_MUTEX_OS2 */
-/************** End of mutex_os2.c *******************************************/
-/************** Begin file mutex_unix.c **************************************/
+#endif /* SQLITE_MUTEX_PTHREADS */
+
+/************** End of mutex_unix.c ******************************************/
+/************** Begin file mutex_w32.c ***************************************/
/*
-** 2007 August 28
+** 2007 August 14
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
@@ -17944,489 +18123,142 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
** May you share freely, never taking more than you give.
**
*************************************************************************
-** This file contains the C functions that implement mutexes for pthreads
-*/
-
-/*
-** The code in this file is only used if we are compiling threadsafe
-** under unix with pthreads.
-**
-** Note that this implementation requires a version of pthreads that
-** supports recursive mutexes.
+** This file contains the C functions that implement mutexes for win32
*/
-#ifdef SQLITE_MUTEX_PTHREADS
-
-#include <pthread.h>
/*
-** The sqlite3_mutex.id, sqlite3_mutex.nRef, and sqlite3_mutex.owner fields
-** are necessary under two condidtions: (1) Debug builds and (2) using
-** home-grown mutexes. Encapsulate these conditions into a single #define.
+** The code in this file is only used if we are compiling multithreaded
+** on a win32 system.
*/
-#if defined(SQLITE_DEBUG) || defined(SQLITE_HOMEGROWN_RECURSIVE_MUTEX)
-# define SQLITE_MUTEX_NREF 1
-#else
-# define SQLITE_MUTEX_NREF 0
-#endif
+#ifdef SQLITE_MUTEX_W32
/*
** Each recursive mutex is an instance of the following structure.
*/
struct sqlite3_mutex {
- pthread_mutex_t mutex; /* Mutex controlling the lock */
-#if SQLITE_MUTEX_NREF
+ CRITICAL_SECTION mutex; /* Mutex controlling the lock */
int id; /* Mutex type */
- volatile int nRef; /* Number of entrances */
- volatile pthread_t owner; /* Thread that is within this mutex */
+#ifdef SQLITE_DEBUG
+ volatile int nRef; /* Number of enterances */
+ volatile DWORD owner; /* Thread holding this mutex */
int trace; /* True to trace changes */
#endif
};
-#if SQLITE_MUTEX_NREF
-#define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0, 0, (pthread_t)0, 0 }
+#define SQLITE_W32_MUTEX_INITIALIZER { 0 }
+#ifdef SQLITE_DEBUG
+#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0, 0L, (DWORD)0, 0 }
#else
-#define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER }
+#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0 }
#endif
/*
-** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
-** intended for use only inside assert() statements. On some platforms,
-** there might be race conditions that can cause these routines to
-** deliver incorrect results. In particular, if pthread_equal() is
-** not an atomic operation, then these routines might delivery
-** incorrect results. On most platforms, pthread_equal() is a
-** comparison of two integers and is therefore atomic. But we are
-** told that HPUX is not such a platform. If so, then these routines
-** will not always work correctly on HPUX.
+** Return true (non-zero) if we are running under WinNT, Win2K, WinXP,
+** or WinCE. Return false (zero) for Win95, Win98, or WinME.
**
-** On those platforms where pthread_equal() is not atomic, SQLite
-** should be compiled without -DSQLITE_DEBUG and with -DNDEBUG to
-** make sure no assert() statements are evaluated and hence these
-** routines are never called.
+** Here is an interesting observation: Win95, Win98, and WinME lack
+** the LockFileEx() API. But we can still statically link against that
+** API as long as we don't call it win running Win95/98/ME. A call to
+** this routine is used to determine if the host is Win95/98/ME or
+** WinNT/2K/XP so that we will know whether or not we can safely call
+** the LockFileEx() API.
+**
+** mutexIsNT() is only used for the TryEnterCriticalSection() API call,
+** which is only available if your application was compiled with
+** _WIN32_WINNT defined to a value >= 0x0400. Currently, the only
+** call to TryEnterCriticalSection() is #ifdef'ed out, so #ifdef
+** this out as well.
*/
-#if !defined(NDEBUG) || defined(SQLITE_DEBUG)
-static int pthreadMutexHeld(sqlite3_mutex *p){
- return (p->nRef!=0 && pthread_equal(p->owner, pthread_self()));
+#if 0
+#if SQLITE_OS_WINCE || SQLITE_OS_WINRT
+# define mutexIsNT() (1)
+#else
+ static int mutexIsNT(void){
+ static int osType = 0;
+ if( osType==0 ){
+ OSVERSIONINFO sInfo;
+ sInfo.dwOSVersionInfoSize = sizeof(sInfo);
+ GetVersionEx(&sInfo);
+ osType = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1;
+ }
+ return osType==2;
+ }
+#endif /* SQLITE_OS_WINCE */
+#endif
+
+#ifdef SQLITE_DEBUG
+/*
+** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
+** intended for use only inside assert() statements.
+*/
+static int winMutexHeld(sqlite3_mutex *p){
+ return p->nRef!=0 && p->owner==GetCurrentThreadId();
}
-static int pthreadMutexNotheld(sqlite3_mutex *p){
- return p->nRef==0 || pthread_equal(p->owner, pthread_self())==0;
+static int winMutexNotheld2(sqlite3_mutex *p, DWORD tid){
+ return p->nRef==0 || p->owner!=tid;
+}
+static int winMutexNotheld(sqlite3_mutex *p){
+ DWORD tid = GetCurrentThreadId();
+ return winMutexNotheld2(p, tid);
}
#endif
+
/*
** Initialize and deinitialize the mutex subsystem.
*/
-static int pthreadMutexInit(void){ return SQLITE_OK; }
-static int pthreadMutexEnd(void){ return SQLITE_OK; }
-
-/*
-** The sqlite3_mutex_alloc() routine allocates a new
-** mutex and returns a pointer to it. If it returns NULL
-** that means that a mutex could not be allocated. SQLite
-** will unwind its stack and return an error. The argument
-** to sqlite3_mutex_alloc() is one of these integer constants:
-**
-** <ul>
-** <li> SQLITE_MUTEX_FAST
-** <li> SQLITE_MUTEX_RECURSIVE
-** <li> SQLITE_MUTEX_STATIC_MASTER
-** <li> SQLITE_MUTEX_STATIC_MEM
-** <li> SQLITE_MUTEX_STATIC_MEM2
-** <li> SQLITE_MUTEX_STATIC_PRNG
-** <li> SQLITE_MUTEX_STATIC_LRU
-** <li> SQLITE_MUTEX_STATIC_PMEM
-** </ul>
-**
-** The first two constants cause sqlite3_mutex_alloc() to create
-** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE
-** is used but not necessarily so when SQLITE_MUTEX_FAST is used.
-** The mutex implementation does not need to make a distinction
-** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does
-** not want to. But SQLite will only request a recursive mutex in
-** cases where it really needs one. If a faster non-recursive mutex
-** implementation is available on the host platform, the mutex subsystem
-** might return such a mutex in response to SQLITE_MUTEX_FAST.
-**
-** The other allowed parameters to sqlite3_mutex_alloc() each return
-** a pointer to a static preexisting mutex. Six static mutexes are
-** used by the current version of SQLite. Future versions of SQLite
-** may add additional static mutexes. Static mutexes are for internal
-** use by SQLite only. Applications that use SQLite mutexes should
-** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or
-** SQLITE_MUTEX_RECURSIVE.
-**
-** Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST
-** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc()
-** returns a different mutex on every call. But for the static
-** mutex types, the same mutex is returned on every call that has
-** the same type number.
-*/
-static sqlite3_mutex *pthreadMutexAlloc(int iType){
- static sqlite3_mutex staticMutexes[] = {
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER
- };
- sqlite3_mutex *p;
- switch( iType ){
- case SQLITE_MUTEX_RECURSIVE: {
- p = sqlite3MallocZero( sizeof(*p) );
- if( p ){
-#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
- /* If recursive mutexes are not available, we will have to
- ** build our own. See below. */
- pthread_mutex_init(&p->mutex, 0);
-#else
- /* Use a recursive mutex if it is available */
- pthread_mutexattr_t recursiveAttr;
- pthread_mutexattr_init(&recursiveAttr);
- pthread_mutexattr_settype(&recursiveAttr, PTHREAD_MUTEX_RECURSIVE);
- pthread_mutex_init(&p->mutex, &recursiveAttr);
- pthread_mutexattr_destroy(&recursiveAttr);
-#endif
-#if SQLITE_MUTEX_NREF
- p->id = iType;
-#endif
- }
- break;
- }
- case SQLITE_MUTEX_FAST: {
- p = sqlite3MallocZero( sizeof(*p) );
- if( p ){
-#if SQLITE_MUTEX_NREF
- p->id = iType;
-#endif
- pthread_mutex_init(&p->mutex, 0);
- }
- break;
- }
- default: {
- assert( iType-2 >= 0 );
- assert( iType-2 < ArraySize(staticMutexes) );
- p = &staticMutexes[iType-2];
-#if SQLITE_MUTEX_NREF
- p->id = iType;
-#endif
- break;
- }
- }
- return p;
-}
-
-
-/*
-** This routine deallocates a previously
-** allocated mutex. SQLite is careful to deallocate every
-** mutex that it allocates.
-*/
-static void pthreadMutexFree(sqlite3_mutex *p){
- assert( p->nRef==0 );
- assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
- pthread_mutex_destroy(&p->mutex);
- sqlite3_free(p);
-}
-
-/*
-** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt
-** to enter a mutex. If another thread is already within the mutex,
-** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return
-** SQLITE_BUSY. The sqlite3_mutex_try() interface returns SQLITE_OK
-** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can
-** be entered multiple times by the same thread. In such cases the,
-** mutex must be exited an equal number of times before another thread
-** can enter. If the same thread tries to enter any other kind of mutex
-** more than once, the behavior is undefined.
-*/
-static void pthreadMutexEnter(sqlite3_mutex *p){
- assert( p->id==SQLITE_MUTEX_RECURSIVE || pthreadMutexNotheld(p) );
-
-#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
- /* If recursive mutexes are not available, then we have to grow
- ** our own. This implementation assumes that pthread_equal()
- ** is atomic - that it cannot be deceived into thinking self
- ** and p->owner are equal if p->owner changes between two values
- ** that are not equal to self while the comparison is taking place.
- ** This implementation also assumes a coherent cache - that
- ** separate processes cannot read different values from the same
- ** address at the same time. If either of these two conditions
- ** are not met, then the mutexes will fail and problems will result.
- */
- {
- pthread_t self = pthread_self();
- if( p->nRef>0 && pthread_equal(p->owner, self) ){
- p->nRef++;
- }else{
- pthread_mutex_lock(&p->mutex);
- assert( p->nRef==0 );
- p->owner = self;
- p->nRef = 1;
- }
- }
-#else
- /* Use the built-in recursive mutexes if they are available.
- */
- pthread_mutex_lock(&p->mutex);
-#if SQLITE_MUTEX_NREF
- assert( p->nRef>0 || p->owner==0 );
- p->owner = pthread_self();
- p->nRef++;
-#endif
-#endif
-
-#ifdef SQLITE_DEBUG
- if( p->trace ){
- printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
- }
-#endif
-}
-static int pthreadMutexTry(sqlite3_mutex *p){
- int rc;
- assert( p->id==SQLITE_MUTEX_RECURSIVE || pthreadMutexNotheld(p) );
-
-#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
- /* If recursive mutexes are not available, then we have to grow
- ** our own. This implementation assumes that pthread_equal()
- ** is atomic - that it cannot be deceived into thinking self
- ** and p->owner are equal if p->owner changes between two values
- ** that are not equal to self while the comparison is taking place.
- ** This implementation also assumes a coherent cache - that
- ** separate processes cannot read different values from the same
- ** address at the same time. If either of these two conditions
- ** are not met, then the mutexes will fail and problems will result.
- */
- {
- pthread_t self = pthread_self();
- if( p->nRef>0 && pthread_equal(p->owner, self) ){
- p->nRef++;
- rc = SQLITE_OK;
- }else if( pthread_mutex_trylock(&p->mutex)==0 ){
- assert( p->nRef==0 );
- p->owner = self;
- p->nRef = 1;
- rc = SQLITE_OK;
- }else{
- rc = SQLITE_BUSY;
- }
- }
-#else
- /* Use the built-in recursive mutexes if they are available.
- */
- if( pthread_mutex_trylock(&p->mutex)==0 ){
-#if SQLITE_MUTEX_NREF
- p->owner = pthread_self();
- p->nRef++;
-#endif
- rc = SQLITE_OK;
- }else{
- rc = SQLITE_BUSY;
- }
-#endif
-
-#ifdef SQLITE_DEBUG
- if( rc==SQLITE_OK && p->trace ){
- printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
- }
-#endif
- return rc;
-}
-
-/*
-** The sqlite3_mutex_leave() routine exits a mutex that was
-** previously entered by the same thread. The behavior
-** is undefined if the mutex is not currently entered or
-** is not currently allocated. SQLite will never do either.
-*/
-static void pthreadMutexLeave(sqlite3_mutex *p){
- assert( pthreadMutexHeld(p) );
-#if SQLITE_MUTEX_NREF
- p->nRef--;
- if( p->nRef==0 ) p->owner = 0;
-#endif
- assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
-
-#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
- if( p->nRef==0 ){
- pthread_mutex_unlock(&p->mutex);
- }
-#else
- pthread_mutex_unlock(&p->mutex);
-#endif
-
-#ifdef SQLITE_DEBUG
- if( p->trace ){
- printf("leave mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
- }
-#endif
-}
-
-SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
- static const sqlite3_mutex_methods sMutex = {
- pthreadMutexInit,
- pthreadMutexEnd,
- pthreadMutexAlloc,
- pthreadMutexFree,
- pthreadMutexEnter,
- pthreadMutexTry,
- pthreadMutexLeave,
-#ifdef SQLITE_DEBUG
- pthreadMutexHeld,
- pthreadMutexNotheld
-#else
- 0,
- 0
-#endif
- };
-
- return &sMutex;
-}
-
-#endif /* SQLITE_MUTEX_PTHREADS */
-
-/************** End of mutex_unix.c ******************************************/
-/************** Begin file mutex_w32.c ***************************************/
-/*
-** 2007 August 14
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-*************************************************************************
-** This file contains the C functions that implement mutexes for win32
-*/
-
-/*
-** The code in this file is only used if we are compiling multithreaded
-** on a win32 system.
-*/
-#ifdef SQLITE_MUTEX_W32
-
-/*
-** Each recursive mutex is an instance of the following structure.
-*/
-struct sqlite3_mutex {
- CRITICAL_SECTION mutex; /* Mutex controlling the lock */
- int id; /* Mutex type */
-#ifdef SQLITE_DEBUG
- volatile int nRef; /* Number of enterances */
- volatile DWORD owner; /* Thread holding this mutex */
- int trace; /* True to trace changes */
-#endif
-};
-#define SQLITE_W32_MUTEX_INITIALIZER { 0 }
-#ifdef SQLITE_DEBUG
-#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0, 0L, (DWORD)0, 0 }
-#else
-#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0 }
-#endif
-
-/*
-** Return true (non-zero) if we are running under WinNT, Win2K, WinXP,
-** or WinCE. Return false (zero) for Win95, Win98, or WinME.
-**
-** Here is an interesting observation: Win95, Win98, and WinME lack
-** the LockFileEx() API. But we can still statically link against that
-** API as long as we don't call it win running Win95/98/ME. A call to
-** this routine is used to determine if the host is Win95/98/ME or
-** WinNT/2K/XP so that we will know whether or not we can safely call
-** the LockFileEx() API.
-**
-** mutexIsNT() is only used for the TryEnterCriticalSection() API call,
-** which is only available if your application was compiled with
-** _WIN32_WINNT defined to a value >= 0x0400. Currently, the only
-** call to TryEnterCriticalSection() is #ifdef'ed out, so #ifdef
-** this out as well.
-*/
-#if 0
-#if SQLITE_OS_WINCE
-# define mutexIsNT() (1)
-#else
- static int mutexIsNT(void){
- static int osType = 0;
- if( osType==0 ){
- OSVERSIONINFO sInfo;
- sInfo.dwOSVersionInfoSize = sizeof(sInfo);
- GetVersionEx(&sInfo);
- osType = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1;
- }
- return osType==2;
- }
-#endif /* SQLITE_OS_WINCE */
-#endif
-
-#ifdef SQLITE_DEBUG
-/*
-** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
-** intended for use only inside assert() statements.
-*/
-static int winMutexHeld(sqlite3_mutex *p){
- return p->nRef!=0 && p->owner==GetCurrentThreadId();
-}
-static int winMutexNotheld2(sqlite3_mutex *p, DWORD tid){
- return p->nRef==0 || p->owner!=tid;
-}
-static int winMutexNotheld(sqlite3_mutex *p){
- DWORD tid = GetCurrentThreadId();
- return winMutexNotheld2(p, tid);
-}
-#endif
-
-
-/*
-** Initialize and deinitialize the mutex subsystem.
-*/
-static sqlite3_mutex winMutex_staticMutexes[6] = {
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER
-};
-static int winMutex_isInit = 0;
-/* As winMutexInit() and winMutexEnd() are called as part
-** of the sqlite3_initialize and sqlite3_shutdown()
-** processing, the "interlocked" magic is probably not
-** strictly necessary.
-*/
-static long winMutex_lock = 0;
-
-static int winMutexInit(void){
- /* The first to increment to 1 does actual initialization */
- if( InterlockedCompareExchange(&winMutex_lock, 1, 0)==0 ){
- int i;
- for(i=0; i<ArraySize(winMutex_staticMutexes); i++){
- InitializeCriticalSection(&winMutex_staticMutexes[i].mutex);
- }
- winMutex_isInit = 1;
- }else{
- /* Someone else is in the process of initing the static mutexes */
- while( !winMutex_isInit ){
- Sleep(1);
- }
- }
- return SQLITE_OK;
-}
-
-static int winMutexEnd(void){
- /* The first to decrement to 0 does actual shutdown
- ** (which should be the last to shutdown.) */
- if( InterlockedCompareExchange(&winMutex_lock, 0, 1)==1 ){
- if( winMutex_isInit==1 ){
- int i;
- for(i=0; i<ArraySize(winMutex_staticMutexes); i++){
- DeleteCriticalSection(&winMutex_staticMutexes[i].mutex);
- }
- winMutex_isInit = 0;
- }
- }
- return SQLITE_OK;
-}
+static sqlite3_mutex winMutex_staticMutexes[6] = {
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER
+};
+static int winMutex_isInit = 0;
+/* As winMutexInit() and winMutexEnd() are called as part
+** of the sqlite3_initialize and sqlite3_shutdown()
+** processing, the "interlocked" magic is probably not
+** strictly necessary.
+*/
+static long winMutex_lock = 0;
+
+SQLITE_API void sqlite3_win32_sleep(DWORD milliseconds); /* os_win.c */
+
+static int winMutexInit(void){
+ /* The first to increment to 1 does actual initialization */
+ if( InterlockedCompareExchange(&winMutex_lock, 1, 0)==0 ){
+ int i;
+ for(i=0; i<ArraySize(winMutex_staticMutexes); i++){
+#if SQLITE_OS_WINRT
+ InitializeCriticalSectionEx(&winMutex_staticMutexes[i].mutex, 0, 0);
+#else
+ InitializeCriticalSection(&winMutex_staticMutexes[i].mutex);
+#endif
+ }
+ winMutex_isInit = 1;
+ }else{
+ /* Someone else is in the process of initing the static mutexes */
+ while( !winMutex_isInit ){
+ sqlite3_win32_sleep(1);
+ }
+ }
+ return SQLITE_OK;
+}
+
+static int winMutexEnd(void){
+ /* The first to decrement to 0 does actual shutdown
+ ** (which should be the last to shutdown.) */
+ if( InterlockedCompareExchange(&winMutex_lock, 0, 1)==1 ){
+ if( winMutex_isInit==1 ){
+ int i;
+ for(i=0; i<ArraySize(winMutex_staticMutexes); i++){
+ DeleteCriticalSection(&winMutex_staticMutexes[i].mutex);
+ }
+ winMutex_isInit = 0;
+ }
+ }
+ return SQLITE_OK;
+}
/*
** The sqlite3_mutex_alloc() routine allocates a new
@@ -18481,7 +18313,11 @@ static sqlite3_mutex *winMutexAlloc(int iType){
#ifdef SQLITE_DEBUG
p->id = iType;
#endif
+#if SQLITE_OS_WINRT
+ InitializeCriticalSectionEx(&p->mutex, 0, 0);
+#else
InitializeCriticalSection(&p->mutex);
+#endif
}
break;
}
@@ -19529,7 +19365,8 @@ static const et_info fmtinfo[] = {
static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
int digit;
LONGDOUBLE_TYPE d;
- if( (*cnt)++ >= 16 ) return '0';
+ if( (*cnt)<=0 ) return '0';
+ (*cnt)--;
digit = (int)*val;
d = digit;
digit += '0';
@@ -19833,9 +19670,12 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
break;
}
if( realvalue>0.0 ){
- while( realvalue>=1e32 && exp<=350 ){ realvalue *= 1e-32; exp+=32; }
- while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; }
- while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; }
+ LONGDOUBLE_TYPE scale = 1.0;
+ while( realvalue>=1e100*scale && exp<=350 ){ scale *= 1e100;exp+=100;}
+ while( realvalue>=1e64*scale && exp<=350 ){ scale *= 1e64; exp+=64; }
+ while( realvalue>=1e8*scale && exp<=350 ){ scale *= 1e8; exp+=8; }
+ while( realvalue>=10.0*scale && exp<=350 ){ scale *= 10.0; exp++; }
+ realvalue /= scale;
while( realvalue<1e-8 ){ realvalue *= 1e8; exp-=8; }
while( realvalue<1.0 ){ realvalue *= 10.0; exp--; }
if( exp>350 ){
@@ -19868,7 +19708,7 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
xtype = etFLOAT;
}
}else{
- flag_rtz = 0;
+ flag_rtz = flag_altform2;
}
if( xtype==etEXP ){
e2 = 0;
@@ -19883,7 +19723,7 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
}
}
zOut = bufpt;
- nsd = 0;
+ nsd = 16 + flag_altform2*10;
flag_dp = (precision>0 ?1:0) | flag_alternateform | flag_altform2;
/* The sign in front of the number */
if( prefix ){
@@ -21456,7 +21296,7 @@ do_atof_calc:
/* if exponent, scale significand as appropriate
** and store in result. */
if( e ){
- double scale = 1.0;
+ LONGDOUBLE_TYPE scale = 1.0;
/* attempt to handle extremely small/large numbers better */
if( e>307 && e<342 ){
while( e%308 ) { scale *= 1.0e+1; e -= 1; }
@@ -22385,7 +22225,11 @@ static int rehash(Hash *pH, unsigned int new_size){
/* The inability to allocates space for a larger hash table is
** a performance hit but it is not a fatal error. So mark the
- ** allocation as a benign.
+ ** allocation as a benign. Use sqlite3Malloc()/memset(0) instead of
+ ** sqlite3MallocZero() to make the allocation, as sqlite3MallocZero()
+ ** only zeroes the requested number of bytes whereas this module will
+ ** use the actual amount of space allocated for the hash table (which
+ ** may be larger than the requested amount).
*/
sqlite3BeginBenignMalloc();
new_ht = (struct _ht *)sqlite3Malloc( new_size*sizeof(struct _ht) );
@@ -22711,2140 +22555,6 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
#endif
/************** End of opcodes.c *********************************************/
-/************** Begin file os_os2.c ******************************************/
-/*
-** 2006 Feb 14
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-******************************************************************************
-**
-** This file contains code that is specific to OS/2.
-*/
-
-
-#if SQLITE_OS_OS2
-
-/*
-** A Note About Memory Allocation:
-**
-** This driver uses malloc()/free() directly rather than going through
-** the SQLite-wrappers sqlite3_malloc()/sqlite3_free(). Those wrappers
-** are designed for use on embedded systems where memory is scarce and
-** malloc failures happen frequently. OS/2 does not typically run on
-** embedded systems, and when it does the developers normally have bigger
-** problems to worry about than running out of memory. So there is not
-** a compelling need to use the wrappers.
-**
-** But there is a good reason to not use the wrappers. If we use the
-** wrappers then we will get simulated malloc() failures within this
-** driver. And that causes all kinds of problems for our tests. We
-** could enhance SQLite to deal with simulated malloc failures within
-** the OS driver, but the code to deal with those failure would not
-** be exercised on Linux (which does not need to malloc() in the driver)
-** and so we would have difficulty writing coverage tests for that
-** code. Better to leave the code out, we think.
-**
-** The point of this discussion is as follows: When creating a new
-** OS layer for an embedded system, if you use this file as an example,
-** avoid the use of malloc()/free(). Those routines work ok on OS/2
-** desktops but not so well in embedded systems.
-*/
-
-/*
-** Macros used to determine whether or not to use threads.
-*/
-#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE
-# define SQLITE_OS2_THREADS 1
-#endif
-
-/*
-** Include code that is common to all os_*.c files
-*/
-/************** Include os_common.h in the middle of os_os2.c ****************/
-/************** Begin file os_common.h ***************************************/
-/*
-** 2004 May 22
-**
-** 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 macros and a little bit of code that is common to
-** all of the platform-specific files (os_*.c) and is #included into those
-** files.
-**
-** This file should be #included by the os_*.c files only. It is not a
-** general purpose header file.
-*/
-#ifndef _OS_COMMON_H_
-#define _OS_COMMON_H_
-
-/*
-** At least two bugs have slipped in because we changed the MEMORY_DEBUG
-** macro to SQLITE_DEBUG and some older makefiles have not yet made the
-** switch. The following code should catch this problem at compile-time.
-*/
-#ifdef MEMORY_DEBUG
-# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
-#endif
-
-#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)
-#endif
-
-/*
-** Macros for performance tracing. Normally turned off. Only works
-** on i486 hardware.
-*/
-#ifdef SQLITE_PERFORMANCE_TRACE
-
-/*
-** hwtime.h contains inline assembler code for implementing
-** high-performance timing routines.
-*/
-/************** Include hwtime.h in the middle of os_common.h ****************/
-/************** Begin file hwtime.h ******************************************/
-/*
-** 2008 May 27
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-******************************************************************************
-**
-** This file contains inline asm code for retrieving "high-performance"
-** counters for x86 class CPUs.
-*/
-#ifndef _HWTIME_H_
-#define _HWTIME_H_
-
-/*
-** The following routine only works on pentium-class (or newer) processors.
-** It uses the RDTSC opcode to read the cycle count value out of the
-** processor and returns that value. This can be used for high-res
-** profiling.
-*/
-#if (defined(__GNUC__) || defined(_MSC_VER)) && \
- (defined(i386) || defined(__i386__) || defined(_M_IX86))
-
- #if defined(__GNUC__)
-
- __inline__ sqlite_uint64 sqlite3Hwtime(void){
- unsigned int lo, hi;
- __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
- return (sqlite_uint64)hi << 32 | lo;
- }
-
- #elif defined(_MSC_VER)
-
- __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){
- __asm {
- rdtsc
- ret ; return value at EDX:EAX
- }
- }
-
- #endif
-
-#elif (defined(__GNUC__) && defined(__x86_64__))
-
- __inline__ sqlite_uint64 sqlite3Hwtime(void){
- unsigned long val;
- __asm__ __volatile__ ("rdtsc" : "=A" (val));
- return val;
- }
-
-#elif (defined(__GNUC__) && defined(__ppc__))
-
- __inline__ sqlite_uint64 sqlite3Hwtime(void){
- unsigned long long retval;
- unsigned long junk;
- __asm__ __volatile__ ("\n\
- 1: mftbu %1\n\
- mftb %L0\n\
- mftbu %0\n\
- cmpw %0,%1\n\
- bne 1b"
- : "=r" (retval), "=r" (junk));
- return retval;
- }
-
-#else
-
- #error Need implementation of sqlite3Hwtime() for your platform.
-
- /*
- ** To compile without implementing sqlite3Hwtime() for your platform,
- ** you can remove the above #error and use the following
- ** stub function. You will lose timing support for many
- ** of the debugging and testing utilities, but it should at
- ** least compile and run.
- */
-SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); }
-
-#endif
-
-#endif /* !defined(_HWTIME_H_) */
-
-/************** End of hwtime.h **********************************************/
-/************** Continuing where we left off in os_common.h ******************/
-
-static sqlite_uint64 g_start;
-static sqlite_uint64 g_elapsed;
-#define TIMER_START g_start=sqlite3Hwtime()
-#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start
-#define TIMER_ELAPSED g_elapsed
-#else
-#define TIMER_START
-#define TIMER_END
-#define TIMER_ELAPSED ((sqlite_uint64)0)
-#endif
-
-/*
-** If we compile with the SQLITE_TEST macro set, then the following block
-** of code will give us the ability to simulate a disk I/O error. This
-** is used for testing the I/O recovery logic.
-*/
-#ifdef SQLITE_TEST
-SQLITE_API int sqlite3_io_error_hit = 0; /* Total number of I/O Errors */
-SQLITE_API int sqlite3_io_error_hardhit = 0; /* Number of non-benign errors */
-SQLITE_API int sqlite3_io_error_pending = 0; /* Count down to first I/O error */
-SQLITE_API int sqlite3_io_error_persist = 0; /* True if I/O errors persist */
-SQLITE_API int sqlite3_io_error_benign = 0; /* True if errors are benign */
-SQLITE_API int sqlite3_diskfull_pending = 0;
-SQLITE_API int sqlite3_diskfull = 0;
-#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X)
-#define SimulateIOError(CODE) \
- if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \
- || sqlite3_io_error_pending-- == 1 ) \
- { local_ioerr(); CODE; }
-static void local_ioerr(){
- IOTRACE(("IOERR\n"));
- sqlite3_io_error_hit++;
- if( !sqlite3_io_error_benign ) sqlite3_io_error_hardhit++;
-}
-#define SimulateDiskfullError(CODE) \
- if( sqlite3_diskfull_pending ){ \
- if( sqlite3_diskfull_pending == 1 ){ \
- local_ioerr(); \
- sqlite3_diskfull = 1; \
- sqlite3_io_error_hit = 1; \
- CODE; \
- }else{ \
- sqlite3_diskfull_pending--; \
- } \
- }
-#else
-#define SimulateIOErrorBenign(X)
-#define SimulateIOError(A)
-#define SimulateDiskfullError(A)
-#endif
-
-/*
-** When testing, keep a count of the number of open files.
-*/
-#ifdef SQLITE_TEST
-SQLITE_API int sqlite3_open_file_count = 0;
-#define OpenCounter(X) sqlite3_open_file_count+=(X)
-#else
-#define OpenCounter(X)
-#endif
-
-#endif /* !defined(_OS_COMMON_H_) */
-
-/************** End of os_common.h *******************************************/
-/************** Continuing where we left off in os_os2.c *********************/
-
-/* Forward references */
-typedef struct os2File os2File; /* The file structure */
-typedef struct os2ShmNode os2ShmNode; /* A shared descritive memory node */
-typedef struct os2ShmLink os2ShmLink; /* A connection to shared-memory */
-
-/*
-** The os2File structure is subclass of sqlite3_file specific for the OS/2
-** protability layer.
-*/
-struct os2File {
- const sqlite3_io_methods *pMethod; /* Always the first entry */
- HFILE h; /* Handle for accessing the file */
- int flags; /* Flags provided to os2Open() */
- int locktype; /* Type of lock currently held on this file */
- int szChunk; /* Chunk size configured by FCNTL_CHUNK_SIZE */
- char *zFullPathCp; /* Full path name of this file */
- os2ShmLink *pShmLink; /* Instance of shared memory on this file */
-};
-
-#define LOCK_TIMEOUT 10L /* the default locking timeout */
-
-/*
-** Missing from some versions of the OS/2 toolkit -
-** used to allocate from high memory if possible
-*/
-#ifndef OBJ_ANY
-# define OBJ_ANY 0x00000400
-#endif
-
-/*****************************************************************************
-** The next group of routines implement the I/O methods specified
-** by the sqlite3_io_methods object.
-******************************************************************************/
-
-/*
-** Close a file.
-*/
-static int os2Close( sqlite3_file *id ){
- APIRET rc;
- os2File *pFile = (os2File*)id;
-
- assert( id!=0 );
- OSTRACE(( "CLOSE %d (%s)\n", pFile->h, pFile->zFullPathCp ));
-
- rc = DosClose( pFile->h );
-
- if( pFile->flags & SQLITE_OPEN_DELETEONCLOSE )
- DosForceDelete( (PSZ)pFile->zFullPathCp );
-
- free( pFile->zFullPathCp );
- pFile->zFullPathCp = NULL;
- pFile->locktype = NO_LOCK;
- pFile->h = (HFILE)-1;
- pFile->flags = 0;
-
- OpenCounter( -1 );
- return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
-}
-
-/*
-** Read data from a file into a buffer. Return SQLITE_OK if all
-** bytes were read successfully and SQLITE_IOERR if anything goes
-** wrong.
-*/
-static int os2Read(
- sqlite3_file *id, /* File to read from */
- void *pBuf, /* Write content into this buffer */
- int amt, /* Number of bytes to read */
- sqlite3_int64 offset /* Begin reading at this offset */
-){
- ULONG fileLocation = 0L;
- ULONG got;
- os2File *pFile = (os2File*)id;
- assert( id!=0 );
- SimulateIOError( return SQLITE_IOERR_READ );
- OSTRACE(( "READ %d lock=%d\n", pFile->h, pFile->locktype ));
- if( DosSetFilePtr(pFile->h, offset, FILE_BEGIN, &fileLocation) != NO_ERROR ){
- return SQLITE_IOERR;
- }
- if( DosRead( pFile->h, pBuf, amt, &got ) != NO_ERROR ){
- return SQLITE_IOERR_READ;
- }
- if( got == (ULONG)amt )
- return SQLITE_OK;
- else {
- /* Unread portions of the input buffer must be zero-filled */
- memset(&((char*)pBuf)[got], 0, amt-got);
- return SQLITE_IOERR_SHORT_READ;
- }
-}
-
-/*
-** Write data from a buffer into a file. Return SQLITE_OK on success
-** or some other error code on failure.
-*/
-static int os2Write(
- sqlite3_file *id, /* File to write into */
- const void *pBuf, /* The bytes to be written */
- int amt, /* Number of bytes to write */
- sqlite3_int64 offset /* Offset into the file to begin writing at */
-){
- ULONG fileLocation = 0L;
- APIRET rc = NO_ERROR;
- ULONG wrote;
- os2File *pFile = (os2File*)id;
- assert( id!=0 );
- SimulateIOError( return SQLITE_IOERR_WRITE );
- SimulateDiskfullError( return SQLITE_FULL );
- OSTRACE(( "WRITE %d lock=%d\n", pFile->h, pFile->locktype ));
- if( DosSetFilePtr(pFile->h, offset, FILE_BEGIN, &fileLocation) != NO_ERROR ){
- return SQLITE_IOERR;
- }
- assert( amt>0 );
- while( amt > 0 &&
- ( rc = DosWrite( pFile->h, (PVOID)pBuf, amt, &wrote ) ) == NO_ERROR &&
- wrote > 0
- ){
- amt -= wrote;
- pBuf = &((char*)pBuf)[wrote];
- }
-
- return ( rc != NO_ERROR || amt > (int)wrote ) ? SQLITE_FULL : SQLITE_OK;
-}
-
-/*
-** Truncate an open file to a specified size
-*/
-static int os2Truncate( sqlite3_file *id, i64 nByte ){
- APIRET rc;
- os2File *pFile = (os2File*)id;
- assert( id!=0 );
- OSTRACE(( "TRUNCATE %d %lld\n", pFile->h, nByte ));
- SimulateIOError( return SQLITE_IOERR_TRUNCATE );
-
- /* If the user has configured a chunk-size for this file, truncate the
- ** file so that it consists of an integer number of chunks (i.e. the
- ** actual file size after the operation may be larger than the requested
- ** size).
- */
- if( pFile->szChunk ){
- nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
- }
-
- rc = DosSetFileSize( pFile->h, nByte );
- return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR_TRUNCATE;
-}
-
-#ifdef SQLITE_TEST
-/*
-** Count the number of fullsyncs and normal syncs. This is used to test
-** that syncs and fullsyncs are occuring at the right times.
-*/
-SQLITE_API int sqlite3_sync_count = 0;
-SQLITE_API int sqlite3_fullsync_count = 0;
-#endif
-
-/*
-** Make sure all writes to a particular file are committed to disk.
-*/
-static int os2Sync( sqlite3_file *id, int flags ){
- os2File *pFile = (os2File*)id;
- OSTRACE(( "SYNC %d lock=%d\n", pFile->h, pFile->locktype ));
-#ifdef SQLITE_TEST
- if( flags & SQLITE_SYNC_FULL){
- sqlite3_fullsync_count++;
- }
- sqlite3_sync_count++;
-#endif
- /* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a
- ** no-op
- */
-#ifdef SQLITE_NO_SYNC
- UNUSED_PARAMETER(pFile);
- return SQLITE_OK;
-#else
- return DosResetBuffer( pFile->h ) == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
-#endif
-}
-
-/*
-** Determine the current size of a file in bytes
-*/
-static int os2FileSize( sqlite3_file *id, sqlite3_int64 *pSize ){
- APIRET rc = NO_ERROR;
- FILESTATUS3 fsts3FileInfo;
- memset(&fsts3FileInfo, 0, sizeof(fsts3FileInfo));
- assert( id!=0 );
- SimulateIOError( return SQLITE_IOERR_FSTAT );
- rc = DosQueryFileInfo( ((os2File*)id)->h, FIL_STANDARD, &fsts3FileInfo, sizeof(FILESTATUS3) );
- if( rc == NO_ERROR ){
- *pSize = fsts3FileInfo.cbFile;
- return SQLITE_OK;
- }else{
- return SQLITE_IOERR_FSTAT;
- }
-}
-
-/*
-** Acquire a reader lock.
-*/
-static int getReadLock( os2File *pFile ){
- FILELOCK LockArea,
- UnlockArea;
- APIRET res;
- memset(&LockArea, 0, sizeof(LockArea));
- memset(&UnlockArea, 0, sizeof(UnlockArea));
- LockArea.lOffset = SHARED_FIRST;
- LockArea.lRange = SHARED_SIZE;
- UnlockArea.lOffset = 0L;
- UnlockArea.lRange = 0L;
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 1L );
- OSTRACE(( "GETREADLOCK %d res=%d\n", pFile->h, res ));
- return res;
-}
-
-/*
-** Undo a readlock
-*/
-static int unlockReadLock( os2File *id ){
- FILELOCK LockArea,
- UnlockArea;
- APIRET res;
- memset(&LockArea, 0, sizeof(LockArea));
- memset(&UnlockArea, 0, sizeof(UnlockArea));
- LockArea.lOffset = 0L;
- LockArea.lRange = 0L;
- UnlockArea.lOffset = SHARED_FIRST;
- UnlockArea.lRange = SHARED_SIZE;
- res = DosSetFileLocks( id->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 1L );
- OSTRACE(( "UNLOCK-READLOCK file handle=%d res=%d?\n", id->h, res ));
- return res;
-}
-
-/*
-** Lock the file with the lock specified by parameter locktype - one
-** of the following:
-**
-** (1) SHARED_LOCK
-** (2) RESERVED_LOCK
-** (3) PENDING_LOCK
-** (4) EXCLUSIVE_LOCK
-**
-** Sometimes when requesting one lock state, additional lock states
-** are inserted in between. The locking might fail on one of the later
-** transitions leaving the lock state different from what it started but
-** still short of its goal. The following chart shows the allowed
-** transitions and the inserted intermediate states:
-**
-** UNLOCKED -> SHARED
-** SHARED -> RESERVED
-** SHARED -> (PENDING) -> EXCLUSIVE
-** RESERVED -> (PENDING) -> EXCLUSIVE
-** PENDING -> EXCLUSIVE
-**
-** This routine will only increase a lock. The os2Unlock() routine
-** erases all locks at once and returns us immediately to locking level 0.
-** It is not possible to lower the locking level one step at a time. You
-** must go straight to locking level 0.
-*/
-static int os2Lock( sqlite3_file *id, int locktype ){
- int rc = SQLITE_OK; /* Return code from subroutines */
- APIRET res = NO_ERROR; /* Result of an OS/2 lock call */
- int newLocktype; /* Set pFile->locktype to this value before exiting */
- int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */
- FILELOCK LockArea,
- UnlockArea;
- os2File *pFile = (os2File*)id;
- memset(&LockArea, 0, sizeof(LockArea));
- memset(&UnlockArea, 0, sizeof(UnlockArea));
- assert( pFile!=0 );
- OSTRACE(( "LOCK %d %d was %d\n", pFile->h, locktype, pFile->locktype ));
-
- /* If there is already a lock of this type or more restrictive on the
- ** os2File, do nothing. Don't use the end_lock: exit path, as
- ** sqlite3_mutex_enter() hasn't been called yet.
- */
- if( pFile->locktype>=locktype ){
- OSTRACE(( "LOCK %d %d ok (already held)\n", pFile->h, locktype ));
- return SQLITE_OK;
- }
-
- /* Make sure the locking sequence is correct
- */
- assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK );
- assert( locktype!=PENDING_LOCK );
- assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK );
-
- /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or
- ** a SHARED lock. If we are acquiring a SHARED lock, the acquisition of
- ** the PENDING_LOCK byte is temporary.
- */
- newLocktype = pFile->locktype;
- if( pFile->locktype==NO_LOCK
- || (locktype==EXCLUSIVE_LOCK && pFile->locktype==RESERVED_LOCK)
- ){
- LockArea.lOffset = PENDING_BYTE;
- LockArea.lRange = 1L;
- UnlockArea.lOffset = 0L;
- UnlockArea.lRange = 0L;
-
- /* wait longer than LOCK_TIMEOUT here not to have to try multiple times */
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, 100L, 0L );
- if( res == NO_ERROR ){
- gotPendingLock = 1;
- OSTRACE(( "LOCK %d pending lock boolean set. res=%d\n", pFile->h, res ));
- }
- }
-
- /* Acquire a shared lock
- */
- if( locktype==SHARED_LOCK && res == NO_ERROR ){
- assert( pFile->locktype==NO_LOCK );
- res = getReadLock(pFile);
- if( res == NO_ERROR ){
- newLocktype = SHARED_LOCK;
- }
- OSTRACE(( "LOCK %d acquire shared lock. res=%d\n", pFile->h, res ));
- }
-
- /* Acquire a RESERVED lock
- */
- if( locktype==RESERVED_LOCK && res == NO_ERROR ){
- assert( pFile->locktype==SHARED_LOCK );
- LockArea.lOffset = RESERVED_BYTE;
- LockArea.lRange = 1L;
- UnlockArea.lOffset = 0L;
- UnlockArea.lRange = 0L;
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- if( res == NO_ERROR ){
- newLocktype = RESERVED_LOCK;
- }
- OSTRACE(( "LOCK %d acquire reserved lock. res=%d\n", pFile->h, res ));
- }
-
- /* Acquire a PENDING lock
- */
- if( locktype==EXCLUSIVE_LOCK && res == NO_ERROR ){
- newLocktype = PENDING_LOCK;
- gotPendingLock = 0;
- OSTRACE(( "LOCK %d acquire pending lock. pending lock boolean unset.\n",
- pFile->h ));
- }
-
- /* Acquire an EXCLUSIVE lock
- */
- if( locktype==EXCLUSIVE_LOCK && res == NO_ERROR ){
- assert( pFile->locktype>=SHARED_LOCK );
- res = unlockReadLock(pFile);
- OSTRACE(( "unreadlock = %d\n", res ));
- LockArea.lOffset = SHARED_FIRST;
- LockArea.lRange = SHARED_SIZE;
- UnlockArea.lOffset = 0L;
- UnlockArea.lRange = 0L;
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- if( res == NO_ERROR ){
- newLocktype = EXCLUSIVE_LOCK;
- }else{
- OSTRACE(( "OS/2 error-code = %d\n", res ));
- getReadLock(pFile);
- }
- OSTRACE(( "LOCK %d acquire exclusive lock. res=%d\n", pFile->h, res ));
- }
-
- /* If we are holding a PENDING lock that ought to be released, then
- ** release it now.
- */
- if( gotPendingLock && locktype==SHARED_LOCK ){
- int r;
- LockArea.lOffset = 0L;
- LockArea.lRange = 0L;
- UnlockArea.lOffset = PENDING_BYTE;
- UnlockArea.lRange = 1L;
- r = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- OSTRACE(( "LOCK %d unlocking pending/is shared. r=%d\n", pFile->h, r ));
- }
-
- /* Update the state of the lock has held in the file descriptor then
- ** return the appropriate result code.
- */
- if( res == NO_ERROR ){
- rc = SQLITE_OK;
- }else{
- OSTRACE(( "LOCK FAILED %d trying for %d but got %d\n", pFile->h,
- locktype, newLocktype ));
- rc = SQLITE_BUSY;
- }
- pFile->locktype = newLocktype;
- OSTRACE(( "LOCK %d now %d\n", pFile->h, pFile->locktype ));
- return rc;
-}
-
-/*
-** This routine checks if there is a RESERVED lock held on the specified
-** file by this or any other process. If such a lock is held, return
-** non-zero, otherwise zero.
-*/
-static int os2CheckReservedLock( sqlite3_file *id, int *pOut ){
- int r = 0;
- os2File *pFile = (os2File*)id;
- assert( pFile!=0 );
- if( pFile->locktype>=RESERVED_LOCK ){
- r = 1;
- OSTRACE(( "TEST WR-LOCK %d %d (local)\n", pFile->h, r ));
- }else{
- FILELOCK LockArea,
- UnlockArea;
- APIRET rc = NO_ERROR;
- memset(&LockArea, 0, sizeof(LockArea));
- memset(&UnlockArea, 0, sizeof(UnlockArea));
- LockArea.lOffset = RESERVED_BYTE;
- LockArea.lRange = 1L;
- UnlockArea.lOffset = 0L;
- UnlockArea.lRange = 0L;
- rc = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- OSTRACE(( "TEST WR-LOCK %d lock reserved byte rc=%d\n", pFile->h, rc ));
- if( rc == NO_ERROR ){
- APIRET rcu = NO_ERROR; /* return code for unlocking */
- LockArea.lOffset = 0L;
- LockArea.lRange = 0L;
- UnlockArea.lOffset = RESERVED_BYTE;
- UnlockArea.lRange = 1L;
- rcu = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- OSTRACE(( "TEST WR-LOCK %d unlock reserved byte r=%d\n", pFile->h, rcu ));
- }
- r = !(rc == NO_ERROR);
- OSTRACE(( "TEST WR-LOCK %d %d (remote)\n", pFile->h, r ));
- }
- *pOut = r;
- return SQLITE_OK;
-}
-
-/*
-** Lower the locking level on file descriptor id to locktype. locktype
-** must be either NO_LOCK or SHARED_LOCK.
-**
-** If the locking level of the file descriptor is already at or below
-** the requested locking level, this routine is a no-op.
-**
-** It is not possible for this routine to fail if the second argument
-** is NO_LOCK. If the second argument is SHARED_LOCK then this routine
-** might return SQLITE_IOERR;
-*/
-static int os2Unlock( sqlite3_file *id, int locktype ){
- int type;
- os2File *pFile = (os2File*)id;
- APIRET rc = SQLITE_OK;
- APIRET res = NO_ERROR;
- FILELOCK LockArea,
- UnlockArea;
- memset(&LockArea, 0, sizeof(LockArea));
- memset(&UnlockArea, 0, sizeof(UnlockArea));
- assert( pFile!=0 );
- assert( locktype<=SHARED_LOCK );
- OSTRACE(( "UNLOCK %d to %d was %d\n", pFile->h, locktype, pFile->locktype ));
- type = pFile->locktype;
- if( type>=EXCLUSIVE_LOCK ){
- LockArea.lOffset = 0L;
- LockArea.lRange = 0L;
- UnlockArea.lOffset = SHARED_FIRST;
- UnlockArea.lRange = SHARED_SIZE;
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- OSTRACE(( "UNLOCK %d exclusive lock res=%d\n", pFile->h, res ));
- if( locktype==SHARED_LOCK && getReadLock(pFile) != NO_ERROR ){
- /* This should never happen. We should always be able to
- ** reacquire the read lock */
- OSTRACE(( "UNLOCK %d to %d getReadLock() failed\n", pFile->h, locktype ));
- rc = SQLITE_IOERR_UNLOCK;
- }
- }
- if( type>=RESERVED_LOCK ){
- LockArea.lOffset = 0L;
- LockArea.lRange = 0L;
- UnlockArea.lOffset = RESERVED_BYTE;
- UnlockArea.lRange = 1L;
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- OSTRACE(( "UNLOCK %d reserved res=%d\n", pFile->h, res ));
- }
- if( locktype==NO_LOCK && type>=SHARED_LOCK ){
- res = unlockReadLock(pFile);
- OSTRACE(( "UNLOCK %d is %d want %d res=%d\n",
- pFile->h, type, locktype, res ));
- }
- if( type>=PENDING_LOCK ){
- LockArea.lOffset = 0L;
- LockArea.lRange = 0L;
- UnlockArea.lOffset = PENDING_BYTE;
- UnlockArea.lRange = 1L;
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- OSTRACE(( "UNLOCK %d pending res=%d\n", pFile->h, res ));
- }
- pFile->locktype = locktype;
- OSTRACE(( "UNLOCK %d now %d\n", pFile->h, pFile->locktype ));
- return rc;
-}
-
-/*
-** Control and query of the open file handle.
-*/
-static int os2FileControl(sqlite3_file *id, int op, void *pArg){
- switch( op ){
- case SQLITE_FCNTL_LOCKSTATE: {
- *(int*)pArg = ((os2File*)id)->locktype;
- OSTRACE(( "FCNTL_LOCKSTATE %d lock=%d\n",
- ((os2File*)id)->h, ((os2File*)id)->locktype ));
- return SQLITE_OK;
- }
- case SQLITE_FCNTL_CHUNK_SIZE: {
- ((os2File*)id)->szChunk = *(int*)pArg;
- return SQLITE_OK;
- }
- case SQLITE_FCNTL_SIZE_HINT: {
- sqlite3_int64 sz = *(sqlite3_int64*)pArg;
- SimulateIOErrorBenign(1);
- os2Truncate(id, sz);
- SimulateIOErrorBenign(0);
- return SQLITE_OK;
- }
- case SQLITE_FCNTL_SYNC_OMITTED: {
- return SQLITE_OK;
- }
- }
- return SQLITE_NOTFOUND;
-}
-
-/*
-** Return the sector size in bytes of the underlying block device for
-** the specified file. This is almost always 512 bytes, but may be
-** larger for some devices.
-**
-** SQLite code assumes this function cannot fail. It also assumes that
-** if two files are created in the same file-system directory (i.e.
-** a database and its journal file) that the sector size will be the
-** same for both.
-*/
-static int os2SectorSize(sqlite3_file *id){
- UNUSED_PARAMETER(id);
- return SQLITE_DEFAULT_SECTOR_SIZE;
-}
-
-/*
-** Return a vector of device characteristics.
-*/
-static int os2DeviceCharacteristics(sqlite3_file *id){
- UNUSED_PARAMETER(id);
- return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN;
-}
-
-
-/*
-** Character set conversion objects used by conversion routines.
-*/
-static UconvObject ucUtf8 = NULL; /* convert between UTF-8 and UCS-2 */
-static UconvObject uclCp = NULL; /* convert between local codepage and UCS-2 */
-
-/*
-** Helper function to initialize the conversion objects from and to UTF-8.
-*/
-static void initUconvObjects( void ){
- if( UniCreateUconvObject( UTF_8, &ucUtf8 ) != ULS_SUCCESS )
- ucUtf8 = NULL;
- if ( UniCreateUconvObject( (UniChar *)L"@path=yes", &uclCp ) != ULS_SUCCESS )
- uclCp = NULL;
-}
-
-/*
-** Helper function to free the conversion objects from and to UTF-8.
-*/
-static void freeUconvObjects( void ){
- if ( ucUtf8 )
- UniFreeUconvObject( ucUtf8 );
- if ( uclCp )
- UniFreeUconvObject( uclCp );
- ucUtf8 = NULL;
- uclCp = NULL;
-}
-
-/*
-** Helper function to convert UTF-8 filenames to local OS/2 codepage.
-** The two-step process: first convert the incoming UTF-8 string
-** into UCS-2 and then from UCS-2 to the current codepage.
-** The returned char pointer has to be freed.
-*/
-static char *convertUtf8PathToCp( const char *in ){
- UniChar tempPath[CCHMAXPATH];
- char *out = (char *)calloc( CCHMAXPATH, 1 );
-
- if( !out )
- return NULL;
-
- if( !ucUtf8 || !uclCp )
- initUconvObjects();
-
- /* determine string for the conversion of UTF-8 which is CP1208 */
- if( UniStrToUcs( ucUtf8, tempPath, (char *)in, CCHMAXPATH ) != ULS_SUCCESS )
- return out; /* if conversion fails, return the empty string */
-
- /* conversion for current codepage which can be used for paths */
- UniStrFromUcs( uclCp, out, tempPath, CCHMAXPATH );
-
- return out;
-}
-
-/*
-** Helper function to convert filenames from local codepage to UTF-8.
-** The two-step process: first convert the incoming codepage-specific
-** string into UCS-2 and then from UCS-2 to the codepage of UTF-8.
-** The returned char pointer has to be freed.
-**
-** This function is non-static to be able to use this in shell.c and
-** similar applications that take command line arguments.
-*/
-char *convertCpPathToUtf8( const char *in ){
- UniChar tempPath[CCHMAXPATH];
- char *out = (char *)calloc( CCHMAXPATH, 1 );
-
- if( !out )
- return NULL;
-
- if( !ucUtf8 || !uclCp )
- initUconvObjects();
-
- /* conversion for current codepage which can be used for paths */
- if( UniStrToUcs( uclCp, tempPath, (char *)in, CCHMAXPATH ) != ULS_SUCCESS )
- return out; /* if conversion fails, return the empty string */
-
- /* determine string for the conversion of UTF-8 which is CP1208 */
- UniStrFromUcs( ucUtf8, out, tempPath, CCHMAXPATH );
-
- return out;
-}
-
-
-#ifndef SQLITE_OMIT_WAL
-
-/*
-** Use main database file for interprocess locking. If un-defined
-** a separate file is created for this purpose. The file will be
-** used only to set file locks. There will be no data written to it.
-*/
-#define SQLITE_OS2_NO_WAL_LOCK_FILE
-
-#if 0
-static void _ERR_TRACE( const char *fmt, ... ) {
- va_list ap;
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- fflush(stderr);
-}
-#define ERR_TRACE(rc, msg) \
- if( (rc) != SQLITE_OK ) _ERR_TRACE msg;
-#else
-#define ERR_TRACE(rc, msg)
-#endif
-
-/*
-** Helper functions to obtain and relinquish the global mutex. The
-** global mutex is used to protect os2ShmNodeList.
-**
-** Function os2ShmMutexHeld() is used to assert() that the global mutex
-** is held when required. This function is only used as part of assert()
-** statements. e.g.
-**
-** os2ShmEnterMutex()
-** assert( os2ShmMutexHeld() );
-** os2ShmLeaveMutex()
-*/
-static void os2ShmEnterMutex(void){
- sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
-}
-static void os2ShmLeaveMutex(void){
- sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
-}
-#ifdef SQLITE_DEBUG
-static int os2ShmMutexHeld(void) {
- return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
-}
-int GetCurrentProcessId(void) {
- PPIB pib;
- DosGetInfoBlocks(NULL, &pib);
- return (int)pib->pib_ulpid;
-}
-#endif
-
-/*
-** Object used to represent a the shared memory area for a single log file.
-** When multiple threads all reference the same log-summary, each thread has
-** its own os2File object, but they all point to a single instance of this
-** object. In other words, each log-summary is opened only once per process.
-**
-** os2ShmMutexHeld() must be true when creating or destroying
-** this object or while reading or writing the following fields:
-**
-** nRef
-** pNext
-**
-** The following fields are read-only after the object is created:
-**
-** szRegion
-** hLockFile
-** shmBaseName
-**
-** Either os2ShmNode.mutex must be held or os2ShmNode.nRef==0 and
-** os2ShmMutexHeld() is true when reading or writing any other field
-** in this structure.
-**
-*/
-struct os2ShmNode {
- sqlite3_mutex *mutex; /* Mutex to access this object */
- os2ShmNode *pNext; /* Next in list of all os2ShmNode objects */
-
- int szRegion; /* Size of shared-memory regions */
-
- int nRegion; /* Size of array apRegion */
- void **apRegion; /* Array of pointers to shared-memory regions */
-
- int nRef; /* Number of os2ShmLink objects pointing to this */
- os2ShmLink *pFirst; /* First os2ShmLink object pointing to this */
-
- HFILE hLockFile; /* File used for inter-process memory locking */
- char shmBaseName[1]; /* Name of the memory object !!! must last !!! */
-};
-
-
-/*
-** Structure used internally by this VFS to record the state of an
-** open shared memory connection.
-**
-** The following fields are initialized when this object is created and
-** are read-only thereafter:
-**
-** os2Shm.pShmNode
-** os2Shm.id
-**
-** All other fields are read/write. The os2Shm.pShmNode->mutex must be held
-** while accessing any read/write fields.
-*/
-struct os2ShmLink {
- os2ShmNode *pShmNode; /* The underlying os2ShmNode object */
- os2ShmLink *pNext; /* Next os2Shm with the same os2ShmNode */
- u32 sharedMask; /* Mask of shared locks held */
- u32 exclMask; /* Mask of exclusive locks held */
-#ifdef SQLITE_DEBUG
- u8 id; /* Id of this connection with its os2ShmNode */
-#endif
-};
-
-
-/*
-** A global list of all os2ShmNode objects.
-**
-** The os2ShmMutexHeld() must be true while reading or writing this list.
-*/
-static os2ShmNode *os2ShmNodeList = NULL;
-
-/*
-** Constants used for locking
-*/
-#ifdef SQLITE_OS2_NO_WAL_LOCK_FILE
-#define OS2_SHM_BASE (PENDING_BYTE + 0x10000) /* first lock byte */
-#else
-#define OS2_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */
-#endif
-
-#define OS2_SHM_DMS (OS2_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */
-
-/*
-** Apply advisory locks for all n bytes beginning at ofst.
-*/
-#define _SHM_UNLCK 1 /* no lock */
-#define _SHM_RDLCK 2 /* shared lock, no wait */
-#define _SHM_WRLCK 3 /* exlusive lock, no wait */
-#define _SHM_WRLCK_WAIT 4 /* exclusive lock, wait */
-static int os2ShmSystemLock(
- os2ShmNode *pNode, /* Apply locks to this open shared-memory segment */
- int lockType, /* _SHM_UNLCK, _SHM_RDLCK, _SHM_WRLCK or _SHM_WRLCK_WAIT */
- int ofst, /* Offset to first byte to be locked/unlocked */
- int nByte /* Number of bytes to lock or unlock */
-){
- APIRET rc;
- FILELOCK area;
- ULONG mode, timeout;
-
- /* Access to the os2ShmNode object is serialized by the caller */
- assert( sqlite3_mutex_held(pNode->mutex) || pNode->nRef==0 );
-
- mode = 1; /* shared lock */
- timeout = 0; /* no wait */
- area.lOffset = ofst;
- area.lRange = nByte;
-
- switch( lockType ) {
- case _SHM_WRLCK_WAIT:
- timeout = (ULONG)-1; /* wait forever */
- case _SHM_WRLCK:
- mode = 0; /* exclusive lock */
- case _SHM_RDLCK:
- rc = DosSetFileLocks(pNode->hLockFile,
- NULL, &area, timeout, mode);
- break;
- /* case _SHM_UNLCK: */
- default:
- rc = DosSetFileLocks(pNode->hLockFile,
- &area, NULL, 0, 0);
- break;
- }
-
- OSTRACE(("SHM-LOCK %d %s %s 0x%08lx\n",
- pNode->hLockFile,
- rc==SQLITE_OK ? "ok" : "failed",
- lockType==_SHM_UNLCK ? "Unlock" : "Lock",
- rc));
-
- ERR_TRACE(rc, ("os2ShmSystemLock: %d %s\n", rc, pNode->shmBaseName))
-
- return ( rc == 0 ) ? SQLITE_OK : SQLITE_BUSY;
-}
-
-/*
-** Find an os2ShmNode in global list or allocate a new one, if not found.
-**
-** This is not a VFS shared-memory method; it is a utility function called
-** by VFS shared-memory methods.
-*/
-static int os2OpenSharedMemory( os2File *fd, int szRegion ) {
- os2ShmLink *pLink;
- os2ShmNode *pNode;
- int cbShmName, rc = SQLITE_OK;
- char shmName[CCHMAXPATH + 30];
-#ifndef SQLITE_OS2_NO_WAL_LOCK_FILE
- ULONG action;
-#endif
-
- /* We need some additional space at the end to append the region number */
- cbShmName = sprintf(shmName, "\\SHAREMEM\\%s", fd->zFullPathCp );
- if( cbShmName >= CCHMAXPATH-8 )
- return SQLITE_IOERR_SHMOPEN;
-
- /* Replace colon in file name to form a valid shared memory name */
- shmName[10+1] = '!';
-
- /* Allocate link object (we free it later in case of failure) */
- pLink = sqlite3_malloc( sizeof(*pLink) );
- if( !pLink )
- return SQLITE_NOMEM;
-
- /* Access node list */
- os2ShmEnterMutex();
-
- /* Find node by it's shared memory base name */
- for( pNode = os2ShmNodeList;
- pNode && stricmp(shmName, pNode->shmBaseName) != 0;
- pNode = pNode->pNext ) ;
-
- /* Not found: allocate a new node */
- if( !pNode ) {
- pNode = sqlite3_malloc( sizeof(*pNode) + cbShmName );
- if( pNode ) {
- memset(pNode, 0, sizeof(*pNode) );
- pNode->szRegion = szRegion;
- pNode->hLockFile = (HFILE)-1;
- strcpy(pNode->shmBaseName, shmName);
-
-#ifdef SQLITE_OS2_NO_WAL_LOCK_FILE
- if( DosDupHandle(fd->h, &pNode->hLockFile) != 0 ) {
-#else
- sprintf(shmName, "%s-lck", fd->zFullPathCp);
- if( DosOpen((PSZ)shmName, &pNode->hLockFile, &action, 0, FILE_NORMAL,
- OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW,
- OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYNONE |
- OPEN_FLAGS_NOINHERIT | OPEN_FLAGS_FAIL_ON_ERROR,
- NULL) != 0 ) {
-#endif
- sqlite3_free(pNode);
- rc = SQLITE_IOERR;
- } else {
- pNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
- if( !pNode->mutex ) {
- sqlite3_free(pNode);
- rc = SQLITE_NOMEM;
- }
- }
- } else {
- rc = SQLITE_NOMEM;
- }
-
- if( rc == SQLITE_OK ) {
- pNode->pNext = os2ShmNodeList;
- os2ShmNodeList = pNode;
- } else {
- pNode = NULL;
- }
- } else if( pNode->szRegion != szRegion ) {
- rc = SQLITE_IOERR_SHMSIZE;
- pNode = NULL;
- }
-
- if( pNode ) {
- sqlite3_mutex_enter(pNode->mutex);
-
- memset(pLink, 0, sizeof(*pLink));
-
- pLink->pShmNode = pNode;
- pLink->pNext = pNode->pFirst;
- pNode->pFirst = pLink;
- pNode->nRef++;
-
- fd->pShmLink = pLink;
-
- sqlite3_mutex_leave(pNode->mutex);
-
- } else {
- /* Error occured. Free our link object. */
- sqlite3_free(pLink);
- }
-
- os2ShmLeaveMutex();
-
- ERR_TRACE(rc, ("os2OpenSharedMemory: %d %s\n", rc, fd->zFullPathCp))
-
- return rc;
-}
-
-/*
-** Purge the os2ShmNodeList list of all entries with nRef==0.
-**
-** This is not a VFS shared-memory method; it is a utility function called
-** by VFS shared-memory methods.
-*/
-static void os2PurgeShmNodes( int deleteFlag ) {
- os2ShmNode *pNode;
- os2ShmNode **ppNode;
-
- os2ShmEnterMutex();
-
- ppNode = &os2ShmNodeList;
-
- while( *ppNode ) {
- pNode = *ppNode;
-
- if( pNode->nRef == 0 ) {
- *ppNode = pNode->pNext;
-
- if( pNode->apRegion ) {
- /* Prevent other processes from resizing the shared memory */
- os2ShmSystemLock(pNode, _SHM_WRLCK_WAIT, OS2_SHM_DMS, 1);
-
- while( pNode->nRegion-- ) {
-#ifdef SQLITE_DEBUG
- int rc =
-#endif
- DosFreeMem(pNode->apRegion[pNode->nRegion]);
-
- OSTRACE(("SHM-PURGE pid-%d unmap region=%d %s\n",
- (int)GetCurrentProcessId(), pNode->nRegion,
- rc == 0 ? "ok" : "failed"));
- }
-
- /* Allow other processes to resize the shared memory */
- os2ShmSystemLock(pNode, _SHM_UNLCK, OS2_SHM_DMS, 1);
-
- sqlite3_free(pNode->apRegion);
- }
-
- DosClose(pNode->hLockFile);
-
-#ifndef SQLITE_OS2_NO_WAL_LOCK_FILE
- if( deleteFlag ) {
- char fileName[CCHMAXPATH];
- /* Skip "\\SHAREMEM\\" */
- sprintf(fileName, "%s-lck", pNode->shmBaseName + 10);
- /* restore colon */
- fileName[1] = ':';
-
- DosForceDelete(fileName);
- }
-#endif
-
- sqlite3_mutex_free(pNode->mutex);
-
- sqlite3_free(pNode);
-
- } else {
- ppNode = &pNode->pNext;
- }
- }
-
- os2ShmLeaveMutex();
-}
-
-/*
-** This function is called to obtain a pointer to region iRegion of the
-** shared-memory associated with the database file id. Shared-memory regions
-** are numbered starting from zero. Each shared-memory region is szRegion
-** bytes in size.
-**
-** If an error occurs, an error code is returned and *pp is set to NULL.
-**
-** Otherwise, if the bExtend parameter is 0 and the requested shared-memory
-** region has not been allocated (by any client, including one running in a
-** separate process), then *pp is set to NULL and SQLITE_OK returned. If
-** bExtend is non-zero and the requested shared-memory region has not yet
-** been allocated, it is allocated by this function.
-**
-** If the shared-memory region has already been allocated or is allocated by
-** this call as described above, then it is mapped into this processes
-** address space (if it is not already), *pp is set to point to the mapped
-** memory and SQLITE_OK returned.
-*/
-static int os2ShmMap(
- sqlite3_file *id, /* Handle open on database file */
- int iRegion, /* Region to retrieve */
- int szRegion, /* Size of regions */
- int bExtend, /* True to extend block if necessary */
- void volatile **pp /* OUT: Mapped memory */
-){
- PVOID pvTemp;
- void **apRegion;
- os2ShmNode *pNode;
- int n, rc = SQLITE_OK;
- char shmName[CCHMAXPATH];
- os2File *pFile = (os2File*)id;
-
- *pp = NULL;
-
- if( !pFile->pShmLink )
- rc = os2OpenSharedMemory( pFile, szRegion );
-
- if( rc == SQLITE_OK ) {
- pNode = pFile->pShmLink->pShmNode ;
-
- sqlite3_mutex_enter(pNode->mutex);
-
- assert( szRegion==pNode->szRegion );
-
- /* Unmapped region ? */
- if( iRegion >= pNode->nRegion ) {
- /* Prevent other processes from resizing the shared memory */
- os2ShmSystemLock(pNode, _SHM_WRLCK_WAIT, OS2_SHM_DMS, 1);
-
- apRegion = sqlite3_realloc(
- pNode->apRegion, (iRegion + 1) * sizeof(apRegion[0]));
-
- if( apRegion ) {
- pNode->apRegion = apRegion;
-
- while( pNode->nRegion <= iRegion ) {
- sprintf(shmName, "%s-%u",
- pNode->shmBaseName, pNode->nRegion);
-
- if( DosGetNamedSharedMem(&pvTemp, (PSZ)shmName,
- PAG_READ | PAG_WRITE) != NO_ERROR ) {
- if( !bExtend )
- break;
-
- if( DosAllocSharedMem(&pvTemp, (PSZ)shmName, szRegion,
- PAG_READ | PAG_WRITE | PAG_COMMIT | OBJ_ANY) != NO_ERROR &&
- DosAllocSharedMem(&pvTemp, (PSZ)shmName, szRegion,
- PAG_READ | PAG_WRITE | PAG_COMMIT) != NO_ERROR ) {
- rc = SQLITE_NOMEM;
- break;
- }
- }
-
- apRegion[pNode->nRegion++] = pvTemp;
- }
-
- /* zero out remaining entries */
- for( n = pNode->nRegion; n <= iRegion; n++ )
- pNode->apRegion[n] = NULL;
-
- /* Return this region (maybe zero) */
- *pp = pNode->apRegion[iRegion];
- } else {
- rc = SQLITE_NOMEM;
- }
-
- /* Allow other processes to resize the shared memory */
- os2ShmSystemLock(pNode, _SHM_UNLCK, OS2_SHM_DMS, 1);
-
- } else {
- /* Region has been mapped previously */
- *pp = pNode->apRegion[iRegion];
- }
-
- sqlite3_mutex_leave(pNode->mutex);
- }
-
- ERR_TRACE(rc, ("os2ShmMap: %s iRgn = %d, szRgn = %d, bExt = %d : %d\n",
- pFile->zFullPathCp, iRegion, szRegion, bExtend, rc))
-
- return rc;
-}
-
-/*
-** Close a connection to shared-memory. Delete the underlying
-** storage if deleteFlag is true.
-**
-** If there is no shared memory associated with the connection then this
-** routine is a harmless no-op.
-*/
-static int os2ShmUnmap(
- sqlite3_file *id, /* The underlying database file */
- int deleteFlag /* Delete shared-memory if true */
-){
- os2File *pFile = (os2File*)id;
- os2ShmLink *pLink = pFile->pShmLink;
-
- if( pLink ) {
- int nRef = -1;
- os2ShmLink **ppLink;
- os2ShmNode *pNode = pLink->pShmNode;
-
- sqlite3_mutex_enter(pNode->mutex);
-
- for( ppLink = &pNode->pFirst;
- *ppLink && *ppLink != pLink;
- ppLink = &(*ppLink)->pNext ) ;
-
- assert(*ppLink);
-
- if( *ppLink ) {
- *ppLink = pLink->pNext;
- nRef = --pNode->nRef;
- } else {
- ERR_TRACE(1, ("os2ShmUnmap: link not found ! %s\n",
- pNode->shmBaseName))
- }
-
- pFile->pShmLink = NULL;
- sqlite3_free(pLink);
-
- sqlite3_mutex_leave(pNode->mutex);
-
- if( nRef == 0 )
- os2PurgeShmNodes( deleteFlag );
- }
-
- return SQLITE_OK;
-}
-
-/*
-** Change the lock state for a shared-memory segment.
-**
-** Note that the relationship between SHAREd and EXCLUSIVE locks is a little
-** different here than in posix. In xShmLock(), one can go from unlocked
-** to shared and back or from unlocked to exclusive and back. But one may
-** not go from shared to exclusive or from exclusive to shared.
-*/
-static int os2ShmLock(
- sqlite3_file *id, /* Database file holding the shared memory */
- int ofst, /* First lock to acquire or release */
- int n, /* Number of locks to acquire or release */
- int flags /* What to do with the lock */
-){
- u32 mask; /* Mask of locks to take or release */
- int rc = SQLITE_OK; /* Result code */
- os2File *pFile = (os2File*)id;
- os2ShmLink *p = pFile->pShmLink; /* The shared memory being locked */
- os2ShmLink *pX; /* For looping over all siblings */
- os2ShmNode *pShmNode = p->pShmNode; /* Our node */
-
- assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK );
- assert( n>=1 );
- assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED)
- || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE)
- || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED)
- || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) );
- assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 );
-
- mask = (u32)((1U<<(ofst+n)) - (1U<<ofst));
- assert( n>1 || mask==(1<<ofst) );
-
-
- sqlite3_mutex_enter(pShmNode->mutex);
-
- if( flags & SQLITE_SHM_UNLOCK ){
- u32 allMask = 0; /* Mask of locks held by siblings */
-
- /* See if any siblings hold this same lock */
- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
- if( pX==p ) continue;
- assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 );
- allMask |= pX->sharedMask;
- }
-
- /* Unlock the system-level locks */
- if( (mask & allMask)==0 ){
- rc = os2ShmSystemLock(pShmNode, _SHM_UNLCK, ofst+OS2_SHM_BASE, n);
- }else{
- rc = SQLITE_OK;
- }
-
- /* Undo the local locks */
- if( rc==SQLITE_OK ){
- p->exclMask &= ~mask;
- p->sharedMask &= ~mask;
- }
- }else if( flags & SQLITE_SHM_SHARED ){
- u32 allShared = 0; /* Union of locks held by connections other than "p" */
-
- /* Find out which shared locks are already held by sibling connections.
- ** If any sibling already holds an exclusive lock, go ahead and return
- ** SQLITE_BUSY.
- */
- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
- if( (pX->exclMask & mask)!=0 ){
- rc = SQLITE_BUSY;
- break;
- }
- allShared |= pX->sharedMask;
- }
-
- /* Get shared locks at the system level, if necessary */
- if( rc==SQLITE_OK ){
- if( (allShared & mask)==0 ){
- rc = os2ShmSystemLock(pShmNode, _SHM_RDLCK, ofst+OS2_SHM_BASE, n);
- }else{
- rc = SQLITE_OK;
- }
- }
-
- /* Get the local shared locks */
- if( rc==SQLITE_OK ){
- p->sharedMask |= mask;
- }
- }else{
- /* Make sure no sibling connections hold locks that will block this
- ** lock. If any do, return SQLITE_BUSY right away.
- */
- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
- if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){
- rc = SQLITE_BUSY;
- break;
- }
- }
-
- /* Get the exclusive locks at the system level. Then if successful
- ** also mark the local connection as being locked.
- */
- if( rc==SQLITE_OK ){
- rc = os2ShmSystemLock(pShmNode, _SHM_WRLCK, ofst+OS2_SHM_BASE, n);
- if( rc==SQLITE_OK ){
- assert( (p->sharedMask & mask)==0 );
- p->exclMask |= mask;
- }
- }
- }
-
- sqlite3_mutex_leave(pShmNode->mutex);
-
- OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x %s\n",
- p->id, (int)GetCurrentProcessId(), p->sharedMask, p->exclMask,
- rc ? "failed" : "ok"));
-
- ERR_TRACE(rc, ("os2ShmLock: ofst = %d, n = %d, flags = 0x%x -> %d \n",
- ofst, n, flags, rc))
-
- return rc;
-}
-
-/*
-** Implement a memory barrier or memory fence on shared memory.
-**
-** All loads and stores begun before the barrier must complete before
-** any load or store begun after the barrier.
-*/
-static void os2ShmBarrier(
- sqlite3_file *id /* Database file holding the shared memory */
-){
- UNUSED_PARAMETER(id);
- os2ShmEnterMutex();
- os2ShmLeaveMutex();
-}
-
-#else
-# define os2ShmMap 0
-# define os2ShmLock 0
-# define os2ShmBarrier 0
-# define os2ShmUnmap 0
-#endif /* #ifndef SQLITE_OMIT_WAL */
-
-
-/*
-** This vector defines all the methods that can operate on an
-** sqlite3_file for os2.
-*/
-static const sqlite3_io_methods os2IoMethod = {
- 2, /* iVersion */
- os2Close, /* xClose */
- os2Read, /* xRead */
- os2Write, /* xWrite */
- os2Truncate, /* xTruncate */
- os2Sync, /* xSync */
- os2FileSize, /* xFileSize */
- os2Lock, /* xLock */
- os2Unlock, /* xUnlock */
- os2CheckReservedLock, /* xCheckReservedLock */
- os2FileControl, /* xFileControl */
- os2SectorSize, /* xSectorSize */
- os2DeviceCharacteristics, /* xDeviceCharacteristics */
- os2ShmMap, /* xShmMap */
- os2ShmLock, /* xShmLock */
- os2ShmBarrier, /* xShmBarrier */
- os2ShmUnmap /* xShmUnmap */
-};
-
-
-/***************************************************************************
-** Here ends the I/O methods that form the sqlite3_io_methods object.
-**
-** The next block of code implements the VFS methods.
-****************************************************************************/
-
-/*
-** Create a temporary file name in zBuf. zBuf must be big enough to
-** hold at pVfs->mxPathname characters.
-*/
-static int getTempname(int nBuf, char *zBuf ){
- static const char zChars[] =
- "abcdefghijklmnopqrstuvwxyz"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "0123456789";
- int i, j;
- PSZ zTempPathCp;
- char zTempPath[CCHMAXPATH];
- ULONG ulDriveNum, ulDriveMap;
-
- /* 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.
- */
- SimulateIOError( return SQLITE_IOERR );
-
- if( sqlite3_temp_directory ) {
- sqlite3_snprintf(CCHMAXPATH-30, zTempPath, "%s", sqlite3_temp_directory);
- } else if( DosScanEnv( (PSZ)"TEMP", &zTempPathCp ) == NO_ERROR ||
- DosScanEnv( (PSZ)"TMP", &zTempPathCp ) == NO_ERROR ||
- DosScanEnv( (PSZ)"TMPDIR", &zTempPathCp ) == NO_ERROR ) {
- char *zTempPathUTF = convertCpPathToUtf8( (char *)zTempPathCp );
- sqlite3_snprintf(CCHMAXPATH-30, zTempPath, "%s", zTempPathUTF);
- free( zTempPathUTF );
- } else if( DosQueryCurrentDisk( &ulDriveNum, &ulDriveMap ) == NO_ERROR ) {
- zTempPath[0] = (char)('A' + ulDriveNum - 1);
- zTempPath[1] = ':';
- zTempPath[2] = '\0';
- } else {
- zTempPath[0] = '\0';
- }
-
- /* Strip off a trailing slashes or backslashes, otherwise we would get *
- * multiple (back)slashes which causes DosOpen() to fail. *
- * Trailing spaces are not allowed, either. */
- j = sqlite3Strlen30(zTempPath);
- while( j > 0 && ( zTempPath[j-1] == '\\' || zTempPath[j-1] == '/' ||
- zTempPath[j-1] == ' ' ) ){
- j--;
- }
- zTempPath[j] = '\0';
-
- /* We use 20 bytes to randomize the name */
- sqlite3_snprintf(nBuf-22, zBuf,
- "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath);
- j = sqlite3Strlen30(zBuf);
- sqlite3_randomness( 20, &zBuf[j] );
- for( i = 0; i < 20; i++, j++ ){
- zBuf[j] = zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
- }
- zBuf[j] = 0;
-
- OSTRACE(( "TEMP FILENAME: %s\n", zBuf ));
- return SQLITE_OK;
-}
-
-
-/*
-** Turn a relative pathname into a full pathname. Write the full
-** pathname into zFull[]. zFull[] will be at least pVfs->mxPathname
-** bytes in size.
-*/
-static int os2FullPathname(
- sqlite3_vfs *pVfs, /* Pointer to vfs object */
- const char *zRelative, /* Possibly relative input path */
- int nFull, /* Size of output buffer in bytes */
- char *zFull /* Output buffer */
-){
- char *zRelativeCp = convertUtf8PathToCp( zRelative );
- char zFullCp[CCHMAXPATH] = "\0";
- char *zFullUTF;
- APIRET rc = DosQueryPathInfo( (PSZ)zRelativeCp, FIL_QUERYFULLNAME,
- zFullCp, CCHMAXPATH );
- free( zRelativeCp );
- zFullUTF = convertCpPathToUtf8( zFullCp );
- sqlite3_snprintf( nFull, zFull, zFullUTF );
- free( zFullUTF );
- return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
-}
-
-
-/*
-** Open a file.
-*/
-static int os2Open(
- sqlite3_vfs *pVfs, /* Not used */
- const char *zName, /* Name of the file (UTF-8) */
- sqlite3_file *id, /* Write the SQLite file handle here */
- int flags, /* Open mode flags */
- int *pOutFlags /* Status return flags */
-){
- HFILE h;
- ULONG ulOpenFlags = 0;
- ULONG ulOpenMode = 0;
- ULONG ulAction = 0;
- ULONG rc;
- os2File *pFile = (os2File*)id;
- const char *zUtf8Name = zName;
- char *zNameCp;
- char zTmpname[CCHMAXPATH];
-
- int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE);
- int isCreate = (flags & SQLITE_OPEN_CREATE);
- int isReadWrite = (flags & SQLITE_OPEN_READWRITE);
-#ifndef NDEBUG
- int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE);
- int isReadonly = (flags & SQLITE_OPEN_READONLY);
- int eType = (flags & 0xFFFFFF00);
- int isOpenJournal = (isCreate && (
- eType==SQLITE_OPEN_MASTER_JOURNAL
- || eType==SQLITE_OPEN_MAIN_JOURNAL
- || eType==SQLITE_OPEN_WAL
- ));
-#endif
-
- UNUSED_PARAMETER(pVfs);
- assert( id!=0 );
-
- /* Check the following statements are true:
- **
- ** (a) Exactly one of the READWRITE and READONLY flags must be set, and
- ** (b) if CREATE is set, then READWRITE must also be set, and
- ** (c) if EXCLUSIVE is set, then CREATE must also be set.
- ** (d) if DELETEONCLOSE is set, then CREATE must also be set.
- */
- assert((isReadonly==0 || isReadWrite==0) && (isReadWrite || isReadonly));
- assert(isCreate==0 || isReadWrite);
- assert(isExclusive==0 || isCreate);
- assert(isDelete==0 || isCreate);
-
- /* The main DB, main journal, WAL file and master journal are never
- ** automatically deleted. Nor are they ever temporary files. */
- assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB );
- assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL );
- assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL );
- assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL );
-
- /* Assert that the upper layer has set one of the "file-type" flags. */
- assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB
- || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL
- || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL
- || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL
- );
-
- memset( pFile, 0, sizeof(*pFile) );
- pFile->h = (HFILE)-1;
-
- /* If the second argument to this function is NULL, generate a
- ** temporary file name to use
- */
- if( !zUtf8Name ){
- assert(isDelete && !isOpenJournal);
- rc = getTempname(CCHMAXPATH, zTmpname);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- zUtf8Name = zTmpname;
- }
-
- if( isReadWrite ){
- ulOpenMode |= OPEN_ACCESS_READWRITE;
- }else{
- ulOpenMode |= OPEN_ACCESS_READONLY;
- }
-
- /* Open in random access mode for possibly better speed. Allow full
- ** sharing because file locks will provide exclusive access when needed.
- ** The handle should not be inherited by child processes and we don't
- ** want popups from the critical error handler.
- */
- ulOpenMode |= OPEN_FLAGS_RANDOM | OPEN_SHARE_DENYNONE |
- OPEN_FLAGS_NOINHERIT | OPEN_FLAGS_FAIL_ON_ERROR;
-
- /* SQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is
- ** created. SQLite doesn't use it to indicate "exclusive access"
- ** as it is usually understood.
- */
- if( isExclusive ){
- /* Creates a new file, only if it does not already exist. */
- /* If the file exists, it fails. */
- ulOpenFlags |= OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_FAIL_IF_EXISTS;
- }else if( isCreate ){
- /* Open existing file, or create if it doesn't exist */
- ulOpenFlags |= OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS;
- }else{
- /* Opens a file, only if it exists. */
- ulOpenFlags |= OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS;
- }
-
- zNameCp = convertUtf8PathToCp( zUtf8Name );
- rc = DosOpen( (PSZ)zNameCp,
- &h,
- &ulAction,
- 0L,
- FILE_NORMAL,
- ulOpenFlags,
- ulOpenMode,
- (PEAOP2)NULL );
- free( zNameCp );
-
- if( rc != NO_ERROR ){
- OSTRACE(( "OPEN Invalid handle rc=%d: zName=%s, ulAction=%#lx, ulFlags=%#lx, ulMode=%#lx\n",
- rc, zUtf8Name, ulAction, ulOpenFlags, ulOpenMode ));
-
- if( isReadWrite ){
- return os2Open( pVfs, zName, id,
- ((flags|SQLITE_OPEN_READONLY)&~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)),
- pOutFlags );
- }else{
- return SQLITE_CANTOPEN;
- }
- }
-
- if( pOutFlags ){
- *pOutFlags = isReadWrite ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY;
- }
-
- os2FullPathname( pVfs, zUtf8Name, sizeof( zTmpname ), zTmpname );
- pFile->zFullPathCp = convertUtf8PathToCp( zTmpname );
- pFile->pMethod = &os2IoMethod;
- pFile->flags = flags;
- pFile->h = h;
-
- OpenCounter(+1);
- OSTRACE(( "OPEN %d pOutFlags=%d\n", pFile->h, pOutFlags ));
- return SQLITE_OK;
-}
-
-/*
-** Delete the named file.
-*/
-static int os2Delete(
- sqlite3_vfs *pVfs, /* Not used on os2 */
- const char *zFilename, /* Name of file to delete */
- int syncDir /* Not used on os2 */
-){
- APIRET rc;
- char *zFilenameCp;
- SimulateIOError( return SQLITE_IOERR_DELETE );
- zFilenameCp = convertUtf8PathToCp( zFilename );
- rc = DosDelete( (PSZ)zFilenameCp );
- free( zFilenameCp );
- OSTRACE(( "DELETE \"%s\"\n", zFilename ));
- return (rc == NO_ERROR ||
- rc == ERROR_FILE_NOT_FOUND ||
- rc == ERROR_PATH_NOT_FOUND ) ? SQLITE_OK : SQLITE_IOERR_DELETE;
-}
-
-/*
-** Check the existance and status of a file.
-*/
-static int os2Access(
- sqlite3_vfs *pVfs, /* Not used on os2 */
- const char *zFilename, /* Name of file to check */
- int flags, /* Type of test to make on this file */
- int *pOut /* Write results here */
-){
- APIRET rc;
- FILESTATUS3 fsts3ConfigInfo;
- char *zFilenameCp;
-
- UNUSED_PARAMETER(pVfs);
- SimulateIOError( return SQLITE_IOERR_ACCESS; );
-
- zFilenameCp = convertUtf8PathToCp( zFilename );
- rc = DosQueryPathInfo( (PSZ)zFilenameCp, FIL_STANDARD,
- &fsts3ConfigInfo, sizeof(FILESTATUS3) );
- free( zFilenameCp );
- OSTRACE(( "ACCESS fsts3ConfigInfo.attrFile=%d flags=%d rc=%d\n",
- fsts3ConfigInfo.attrFile, flags, rc ));
-
- switch( flags ){
- case SQLITE_ACCESS_EXISTS:
- /* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file
- ** as if it does not exist.
- */
- if( fsts3ConfigInfo.cbFile == 0 )
- rc = ERROR_FILE_NOT_FOUND;
- break;
- case SQLITE_ACCESS_READ:
- break;
- case SQLITE_ACCESS_READWRITE:
- if( fsts3ConfigInfo.attrFile & FILE_READONLY )
- rc = ERROR_ACCESS_DENIED;
- break;
- default:
- rc = ERROR_FILE_NOT_FOUND;
- assert( !"Invalid flags argument" );
- }
-
- *pOut = (rc == NO_ERROR);
- OSTRACE(( "ACCESS %s flags %d: rc=%d\n", zFilename, flags, *pOut ));
-
- return SQLITE_OK;
-}
-
-
-#ifndef SQLITE_OMIT_LOAD_EXTENSION
-/*
-** Interfaces for opening a shared library, finding entry points
-** within the shared library, and closing the shared library.
-*/
-/*
-** Interfaces for opening a shared library, finding entry points
-** within the shared library, and closing the shared library.
-*/
-static void *os2DlOpen(sqlite3_vfs *pVfs, const char *zFilename){
- HMODULE hmod;
- APIRET rc;
- char *zFilenameCp = convertUtf8PathToCp(zFilename);
- rc = DosLoadModule(NULL, 0, (PSZ)zFilenameCp, &hmod);
- free(zFilenameCp);
- return rc != NO_ERROR ? 0 : (void*)hmod;
-}
-/*
-** A no-op since the error code is returned on the DosLoadModule call.
-** os2Dlopen returns zero if DosLoadModule is not successful.
-*/
-static void os2DlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){
-/* no-op */
-}
-static void (*os2DlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol))(void){
- PFN pfn;
- APIRET rc;
- rc = DosQueryProcAddr((HMODULE)pHandle, 0L, (PSZ)zSymbol, &pfn);
- if( rc != NO_ERROR ){
- /* if the symbol itself was not found, search again for the same
- * symbol with an extra underscore, that might be needed depending
- * on the calling convention */
- char _zSymbol[256] = "_";
- strncat(_zSymbol, zSymbol, 254);
- rc = DosQueryProcAddr((HMODULE)pHandle, 0L, (PSZ)_zSymbol, &pfn);
- }
- return rc != NO_ERROR ? 0 : (void(*)(void))pfn;
-}
-static void os2DlClose(sqlite3_vfs *pVfs, void *pHandle){
- DosFreeModule((HMODULE)pHandle);
-}
-#else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */
- #define os2DlOpen 0
- #define os2DlError 0
- #define os2DlSym 0
- #define os2DlClose 0
-#endif
-
-
-/*
-** Write up to nBuf bytes of randomness into zBuf.
-*/
-static int os2Randomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf ){
- int n = 0;
-#if defined(SQLITE_TEST)
- n = nBuf;
- memset(zBuf, 0, nBuf);
-#else
- int i;
- PPIB ppib;
- PTIB ptib;
- DATETIME dt;
- static unsigned c = 0;
- /* Ordered by variation probability */
- static ULONG svIdx[6] = { QSV_MS_COUNT, QSV_TIME_LOW,
- QSV_MAXPRMEM, QSV_MAXSHMEM,
- QSV_TOTAVAILMEM, QSV_TOTRESMEM };
-
- /* 8 bytes; timezone and weekday don't increase the randomness much */
- if( (int)sizeof(dt)-3 <= nBuf - n ){
- c += 0x0100;
- DosGetDateTime(&dt);
- dt.year = (USHORT)((dt.year - 1900) | c);
- memcpy(&zBuf[n], &dt, sizeof(dt)-3);
- n += sizeof(dt)-3;
- }
-
- /* 4 bytes; PIDs and TIDs are 16 bit internally, so combine them */
- if( (int)sizeof(ULONG) <= nBuf - n ){
- DosGetInfoBlocks(&ptib, &ppib);
- *(PULONG)&zBuf[n] = MAKELONG(ppib->pib_ulpid,
- ptib->tib_ptib2->tib2_ultid);
- n += sizeof(ULONG);
- }
-
- /* Up to 6 * 4 bytes; variables depend on the system state */
- for( i = 0; i < 6 && (int)sizeof(ULONG) <= nBuf - n; i++ ){
- DosQuerySysInfo(svIdx[i], svIdx[i],
- (PULONG)&zBuf[n], sizeof(ULONG));
- n += sizeof(ULONG);
- }
-#endif
-
- return n;
-}
-
-/*
-** Sleep for a little while. Return the amount of time slept.
-** The argument is the number of microseconds we want to sleep.
-** The return value is the number of microseconds of sleep actually
-** requested from the underlying operating system, a number which
-** might be greater than or equal to the argument, but not less
-** than the argument.
-*/
-static int os2Sleep( sqlite3_vfs *pVfs, int microsec ){
- DosSleep( (microsec/1000) );
- return microsec;
-}
-
-/*
-** The following variable, if set to a non-zero value, becomes the result
-** returned from sqlite3OsCurrentTime(). This is used for testing.
-*/
-#ifdef SQLITE_TEST
-SQLITE_API int sqlite3_current_time = 0;
-#endif
-
-/*
-** Find the current time (in Universal Coordinated Time). Write into *piNow
-** the current time and date as a Julian Day number times 86_400_000. In
-** other words, write into *piNow the number of milliseconds since the Julian
-** epoch of noon in Greenwich on November 24, 4714 B.C according to the
-** proleptic Gregorian calendar.
-**
-** On success, return 0. Return 1 if the time and date cannot be found.
-*/
-static int os2CurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){
-#ifdef SQLITE_TEST
- static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000;
-#endif
- int year, month, datepart, timepart;
-
- DATETIME dt;
- DosGetDateTime( &dt );
-
- year = dt.year;
- month = dt.month;
-
- /* Calculations from http://www.astro.keele.ac.uk/~rno/Astronomy/hjd.html
- ** http://www.astro.keele.ac.uk/~rno/Astronomy/hjd-0.1.c
- ** Calculate the Julian days
- */
- datepart = (int)dt.day - 32076 +
- 1461*(year + 4800 + (month - 14)/12)/4 +
- 367*(month - 2 - (month - 14)/12*12)/12 -
- 3*((year + 4900 + (month - 14)/12)/100)/4;
-
- /* Time in milliseconds, hours to noon added */
- timepart = 12*3600*1000 + dt.hundredths*10 + dt.seconds*1000 +
- ((int)dt.minutes + dt.timezone)*60*1000 + dt.hours*3600*1000;
-
- *piNow = (sqlite3_int64)datepart*86400*1000 + timepart;
-
-#ifdef SQLITE_TEST
- if( sqlite3_current_time ){
- *piNow = 1000*(sqlite3_int64)sqlite3_current_time + unixEpoch;
- }
-#endif
-
- UNUSED_PARAMETER(pVfs);
- return 0;
-}
-
-/*
-** Find the current time (in Universal Coordinated Time). Write the
-** current time and date as a Julian Day number into *prNow and
-** return 0. Return 1 if the time and date cannot be found.
-*/
-static int os2CurrentTime( sqlite3_vfs *pVfs, double *prNow ){
- int rc;
- sqlite3_int64 i;
- rc = os2CurrentTimeInt64(pVfs, &i);
- if( !rc ){
- *prNow = i/86400000.0;
- }
- return rc;
-}
-
-/*
-** The idea is that this function works like a combination of
-** GetLastError() and FormatMessage() on windows (or errno and
-** strerror_r() on unix). After an error is returned by an OS
-** function, SQLite calls this function with zBuf pointing to
-** a buffer of nBuf bytes. The OS layer should populate the
-** buffer with a nul-terminated UTF-8 encoded error message
-** describing the last IO error to have occurred within the calling
-** thread.
-**
-** If the error message is too large for the supplied buffer,
-** it should be truncated. The return value of xGetLastError
-** is zero if the error message fits in the buffer, or non-zero
-** otherwise (if the message was truncated). If non-zero is returned,
-** then it is not necessary to include the nul-terminator character
-** in the output buffer.
-**
-** Not supplying an error message will have no adverse effect
-** on SQLite. It is fine to have an implementation that never
-** returns an error message:
-**
-** int xGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
-** assert(zBuf[0]=='\0');
-** return 0;
-** }
-**
-** However if an error message is supplied, it will be incorporated
-** by sqlite into the error message available to the user using
-** sqlite3_errmsg(), possibly making IO errors easier to debug.
-*/
-static int os2GetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
- assert(zBuf[0]=='\0');
- return 0;
-}
-
-/*
-** Initialize and deinitialize the operating system interface.
-*/
-SQLITE_API int sqlite3_os_init(void){
- static sqlite3_vfs os2Vfs = {
- 3, /* iVersion */
- sizeof(os2File), /* szOsFile */
- CCHMAXPATH, /* mxPathname */
- 0, /* pNext */
- "os2", /* zName */
- 0, /* pAppData */
-
- os2Open, /* xOpen */
- os2Delete, /* xDelete */
- os2Access, /* xAccess */
- os2FullPathname, /* xFullPathname */
- os2DlOpen, /* xDlOpen */
- os2DlError, /* xDlError */
- os2DlSym, /* xDlSym */
- os2DlClose, /* xDlClose */
- os2Randomness, /* xRandomness */
- os2Sleep, /* xSleep */
- os2CurrentTime, /* xCurrentTime */
- os2GetLastError, /* xGetLastError */
- os2CurrentTimeInt64, /* xCurrentTimeInt64 */
- 0, /* xSetSystemCall */
- 0, /* xGetSystemCall */
- 0 /* xNextSystemCall */
- };
- sqlite3_vfs_register(&os2Vfs, 1);
- initUconvObjects();
-/* sqlite3OSTrace = 1; */
- return SQLITE_OK;
-}
-SQLITE_API int sqlite3_os_end(void){
- freeUconvObjects();
- return SQLITE_OK;
-}
-
-#endif /* SQLITE_OS_OS2 */
-
-/************** End of os_os2.c **********************************************/
/************** Begin file os_unix.c *****************************************/
/*
** 2004 May 22
@@ -25074,7 +22784,7 @@ struct unixFile {
#if OS_VXWORKS
struct vxworksFileId *pId; /* Unique file ID */
#endif
-#ifndef NDEBUG
+#ifdef SQLITE_DEBUG
/* The next group of variables are used to track whether or not the
** transaction counter in bytes 24-27 of database files are updated
** whenever any part of the database changes. An assertion fault will
@@ -25109,7 +22819,6 @@ struct unixFile {
#define UNIXFILE_DELETE 0x20 /* Delete on close */
#define UNIXFILE_URI 0x40 /* Filename might have query parameters */
#define UNIXFILE_NOLOCK 0x80 /* Do no file locking */
-#define UNIXFILE_CHOWN 0x100 /* File ownership was changed */
/*
** Include code that is common to all os_*.c files
@@ -25363,6 +23072,15 @@ static int posixOpen(const char *zFile, int flags, int mode){
return open(zFile, flags, mode);
}
+/*
+** On some systems, calls to fchown() will trigger a message in a security
+** log if they come from non-root processes. So avoid calling fchown() if
+** we are not running as root.
+*/
+static int posixFchown(int fd, uid_t uid, gid_t gid){
+ return geteuid() ? 0 : fchown(fd,uid,gid);
+}
+
/* Forward reference */
static int openDirectory(const char*, int*);
@@ -25474,7 +23192,7 @@ static struct unix_syscall {
{ "rmdir", (sqlite3_syscall_ptr)rmdir, 0 },
#define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent)
- { "fchown", (sqlite3_syscall_ptr)fchown, 0 },
+ { "fchown", (sqlite3_syscall_ptr)posixFchown, 0 },
#define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent)
{ "umask", (sqlite3_syscall_ptr)umask, 0 },
@@ -25762,9 +23480,9 @@ static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) {
case EACCES:
/* EACCES is like EAGAIN during locking operations, but not any other time*/
if( (sqliteIOErr == SQLITE_IOERR_LOCK) ||
- (sqliteIOErr == SQLITE_IOERR_UNLOCK) ||
- (sqliteIOErr == SQLITE_IOERR_RDLOCK) ||
- (sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK) ){
+ (sqliteIOErr == SQLITE_IOERR_UNLOCK) ||
+ (sqliteIOErr == SQLITE_IOERR_RDLOCK) ||
+ (sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK) ){
return SQLITE_BUSY;
}
/* else fall through */
@@ -26099,7 +23817,7 @@ static unixInodeInfo *inodeList = 0;
** 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 (e.g. "unlink", "open") and the the associated file-system path,
+** failed (e.g. "unlink", "open") and the associated file-system path,
** if any.
*/
#define unixLogError(a,b,c) unixLogErrorAtLine(a,b,c,__LINE__)
@@ -26122,7 +23840,7 @@ static int unixLogErrorAtLine(
zErr = aErr;
/* If STRERROR_R_CHAR_P (set by autoconf scripts) or __USE_GNU is defined,
- ** assume that the system provides the the GNU version of strerror_r() that
+ ** assume that the system provides the GNU version of strerror_r() that
** returns a pointer to a buffer containing the error message. That pointer
** may point to aErr[], or it may point to some static storage somewhere.
** Otherwise, assume that the system provides the POSIX version of
@@ -26618,7 +24336,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){
}
-#ifndef NDEBUG
+#ifdef SQLITE_DEBUG
/* Set up the transaction-counter change checking flags when
** transitioning from a SHARED to a RESERVED lock. The change
** from SHARED to RESERVED marks the beginning of a normal
@@ -26697,7 +24415,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
if( pFile->eFileLock>SHARED_LOCK ){
assert( pInode->eFileLock==pFile->eFileLock );
-#ifndef NDEBUG
+#ifdef SQLITE_DEBUG
/* When reducing a lock such that other processes can start
** reading the database file again, make sure that the
** transaction counter was updated if any part of the database
@@ -26811,7 +24529,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
pInode->eFileLock = NO_LOCK;
}else{
rc = SQLITE_IOERR_UNLOCK;
- pFile->lastErrno = errno;
+ pFile->lastErrno = errno;
pInode->eFileLock = NO_LOCK;
pFile->eFileLock = NO_LOCK;
}
@@ -26827,7 +24545,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
closePendingFds(pFile);
}
}
-
+
end_unlock:
unixLeaveMutex();
if( rc==SQLITE_OK ) pFile->eFileLock = eFileLock;
@@ -27094,7 +24812,7 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) {
assert( pFile );
OSTRACE(("UNLOCK %d %d was %d pid=%d (dotlock)\n", pFile->h, eFileLock,
- pFile->eFileLock, getpid()));
+ pFile->eFileLock, getpid()));
assert( eFileLock<=SHARED_LOCK );
/* no-op if possible */
@@ -27481,7 +25199,7 @@ static int semUnlock(sqlite3_file *id, int eFileLock) {
assert( pFile );
assert( pSem );
OSTRACE(("UNLOCK %d %d was %d pid=%d (sem)\n", pFile->h, eFileLock,
- pFile->eFileLock, getpid()));
+ pFile->eFileLock, getpid()));
assert( eFileLock<=SHARED_LOCK );
/* no-op if possible */
@@ -27896,7 +25614,7 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) {
SimulateIOError( h=(-1) )
SimulateIOErrorBenign(0);
-#ifndef NDEBUG
+#ifdef SQLITE_DEBUG
/* When reducing a lock such that other processes can start
** reading the database file again, make sure that the
** transaction counter was updated if any part of the database
@@ -28071,7 +25789,7 @@ static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){
if( newOffset == -1 ){
((unixFile*)id)->lastErrno = errno;
}else{
- ((unixFile*)id)->lastErrno = 0;
+ ((unixFile*)id)->lastErrno = 0;
}
return -1;
}
@@ -28159,7 +25877,7 @@ static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){
if( newOffset == -1 ){
((unixFile*)id)->lastErrno = errno;
}else{
- ((unixFile*)id)->lastErrno = 0;
+ ((unixFile*)id)->lastErrno = 0;
}
return -1;
}
@@ -28200,7 +25918,7 @@ static int unixWrite(
);
#endif
-#ifndef NDEBUG
+#ifdef SQLITE_DEBUG
/* If we are doing a normal write to a database file (as opposed to
** doing a hot-journal rollback or a write to some file other than a
** normal database file) then record the fact that the database
@@ -28491,7 +26209,7 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){
pFile->lastErrno = errno;
return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath);
}else{
-#ifndef NDEBUG
+#ifdef SQLITE_DEBUG
/* If we are doing a normal write to a database file (as opposed to
** doing a hot-journal rollback or a write to some file other than a
** normal database file) and we truncate the file to zero length,
@@ -28648,7 +26366,7 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
*(char**)pArg = sqlite3_mprintf("%s", pFile->pVfs->zName);
return SQLITE_OK;
}
-#ifndef NDEBUG
+#ifdef SQLITE_DEBUG
/* The pager calls this method to signal that it has done
** a rollback and that the database is therefore unchanged and
** it hence it is OK for the transaction change counter to be
@@ -28999,14 +26717,9 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
/* If this process is running as root, make sure that the SHM file
** is owned by the same user that owns the original database. Otherwise,
- ** the original owner will not be able to connect. If this process is
- ** not root, the following fchown() will fail, but we don't care. The
- ** if(){..} and the UNIXFILE_CHOWN flag are purely to silence compiler
- ** warnings.
+ ** the original owner will not be able to connect.
*/
- if( osFchown(pShmNode->h, sStat.st_uid, sStat.st_gid)==0 ){
- pDbFd->ctrlFlags |= UNIXFILE_CHOWN;
- }
+ osFchown(pShmNode->h, sStat.st_uid, sStat.st_gid);
/* Check to see if another process is holding the dead-man switch.
** If not, truncate the file to zero length.
@@ -30212,13 +27925,10 @@ static int unixOpen(
/* If this process is running as root and if creating a new rollback
** journal or WAL file, set the ownership of the journal or WAL to be
- ** the same as the original database. If we are not running as root,
- ** then the fchown() call will fail, but that's ok. The "if(){}" and
- ** the setting of the UNIXFILE_CHOWN flag are purely to silence compiler
- ** warnings from gcc.
+ ** the same as the original database.
*/
if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){
- if( osFchown(fd, uid, gid)==0 ){ p->ctrlFlags |= UNIXFILE_CHOWN; }
+ osFchown(fd, uid, gid);
}
}
assert( fd>=0 );
@@ -30681,7 +28391,7 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
** address in the shared range is taken for a SHARED lock, the entire
** shared range is taken for an EXCLUSIVE lock):
**
-** PENDING_BYTE 0x40000000
+** PENDING_BYTE 0x40000000
** RESERVED_BYTE 0x40000001
** SHARED_RANGE 0x40000002 -> 0x40000200
**
@@ -32180,15 +29890,32 @@ SQLITE_API int sqlite3_open_file_count = 0;
/************** Continuing where we left off in os_win.c *********************/
/*
+** Macro to find the minimum of two numeric values.
+*/
+#ifndef MIN
+# define MIN(x,y) ((x)<(y)?(x):(y))
+#endif
+
+/*
** Some Microsoft compilers lack this definition.
*/
#ifndef INVALID_FILE_ATTRIBUTES
# define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
#endif
+#ifndef FILE_FLAG_MASK
+# define FILE_FLAG_MASK (0xFF3C0000)
+#endif
+
+#ifndef FILE_ATTRIBUTE_MASK
+# define FILE_ATTRIBUTE_MASK (0x0003FFF7)
+#endif
+
+#ifndef SQLITE_OMIT_WAL
/* Forward references */
typedef struct winShm winShm; /* A connection to shared-memory */
typedef struct winShmNode winShmNode; /* A region of shared-memory */
+#endif
/*
** WinCE lacks native support for file locking so we have to fake it
@@ -32216,7 +29943,9 @@ struct winFile {
short sharedLockByte; /* Randomly chosen byte used as a shared lock */
u8 ctrlFlags; /* Flags. See WINFILE_* below */
DWORD lastErrno; /* The Windows errno from the last I/O error */
+#ifndef SQLITE_OMIT_WAL
winShm *pShm; /* Instance of shared memory on this file */
+#endif
const char *zPath; /* Full pathname of this file */
int szChunk; /* Chunk size configured by FCNTL_CHUNK_SIZE */
#if SQLITE_OS_WINCE
@@ -32235,10 +29964,52 @@ struct winFile {
#define WINFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */
/*
+ * The size of the buffer used by sqlite3_win32_write_debug().
+ */
+#ifndef SQLITE_WIN32_DBG_BUF_SIZE
+# define SQLITE_WIN32_DBG_BUF_SIZE ((int)(4096-sizeof(DWORD)))
+#endif
+
+/*
+ * The value used with sqlite3_win32_set_directory() to specify that
+ * the data directory should be changed.
+ */
+#ifndef SQLITE_WIN32_DATA_DIRECTORY_TYPE
+# define SQLITE_WIN32_DATA_DIRECTORY_TYPE (1)
+#endif
+
+/*
+ * The value used with sqlite3_win32_set_directory() to specify that
+ * the temporary directory should be changed.
+ */
+#ifndef SQLITE_WIN32_TEMP_DIRECTORY_TYPE
+# define SQLITE_WIN32_TEMP_DIRECTORY_TYPE (2)
+#endif
+
+/*
* 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
+
+/*
+ * If this is non-zero, an isolated heap will be created by the native Win32
+ * allocator subsystem; otherwise, the default process heap will be used. This
+ * setting has no effect when compiling for WinRT. By default, this is enabled
+ * and an isolated heap will be created to store all allocated data.
+ *
+ ******************************************************************************
+ * WARNING: It is important to note that when this setting is non-zero and the
+ * winMemShutdown function is called (e.g. by the sqlite3_shutdown
+ * function), all data that was allocated using the isolated heap will
+ * be freed immediately and any attempt to access any of that freed
+ * data will almost certainly result in an immediate access violation.
+ ******************************************************************************
+ */
+#ifndef SQLITE_WIN32_HEAP_CREATE
+# define SQLITE_WIN32_HEAP_CREATE (TRUE)
+#endif
+
/*
* The initial size of the Win32-specific heap. This value may be zero.
*/
@@ -32323,17 +30094,11 @@ SQLITE_API int sqlite3_os_type = 0;
static int sqlite3_os_type = 0;
#endif
-/*
-** 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
-** to all overrideable system calls.
-*/
-#if !SQLITE_OS_WINCE
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
# define SQLITE_WIN32_HAS_ANSI
#endif
-#if SQLITE_OS_WINCE || SQLITE_OS_WINNT
+#if SQLITE_OS_WINCE || SQLITE_OS_WINNT || SQLITE_OS_WINRT
# define SQLITE_WIN32_HAS_WIDE
#endif
@@ -32341,40 +30106,35 @@ static int sqlite3_os_type = 0;
# define SYSCALL sqlite3_syscall_ptr
#endif
-#if SQLITE_OS_WINCE
-/*
-** These macros are necessary because Windows CE does not natively support the
-** Win32 APIs LockFile, UnlockFile, and LockFileEx.
- */
-
-# define LockFile(a,b,c,d,e) winceLockFile(&a, b, c, d, e)
-# define UnlockFile(a,b,c,d,e) winceUnlockFile(&a, b, c, d, e)
-# define LockFileEx(a,b,c,d,e,f) winceLockFileEx(&a, b, c, d, e, f)
-
/*
-** These are the special syscall hacks for Windows CE. The locking related
-** defines here refer to the macros defined just above.
+** This function is not available on Windows CE or WinRT.
*/
+#if SQLITE_OS_WINCE || SQLITE_OS_WINRT
# define osAreFileApisANSI() 1
-# define osLockFile LockFile
-# define osUnlockFile UnlockFile
-# define osLockFileEx LockFileEx
#endif
+/*
+** 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
+** to all overrideable system calls.
+*/
static struct win_syscall {
const char *zName; /* Name of the sytem call */
sqlite3_syscall_ptr pCurrent; /* Current value of the system call */
sqlite3_syscall_ptr pDefault; /* Default value */
} aSyscall[] = {
-#if !SQLITE_OS_WINCE
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
{ "AreFileApisANSI", (SYSCALL)AreFileApisANSI, 0 },
-
-#define osAreFileApisANSI ((BOOL(WINAPI*)(VOID))aSyscall[0].pCurrent)
#else
{ "AreFileApisANSI", (SYSCALL)0, 0 },
#endif
+#ifndef osAreFileApisANSI
+#define osAreFileApisANSI ((BOOL(WINAPI*)(VOID))aSyscall[0].pCurrent)
+#endif
+
#if SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE)
{ "CharLowerW", (SYSCALL)CharLowerW, 0 },
#else
@@ -32404,7 +30164,7 @@ static struct win_syscall {
#define osCreateFileA ((HANDLE(WINAPI*)(LPCSTR,DWORD,DWORD, \
LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[4].pCurrent)
-#if defined(SQLITE_WIN32_HAS_WIDE)
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
{ "CreateFileW", (SYSCALL)CreateFileW, 0 },
#else
{ "CreateFileW", (SYSCALL)0, 0 },
@@ -32413,28 +30173,24 @@ static struct win_syscall {
#define osCreateFileW ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD, \
LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[5].pCurrent)
- { "CreateFileMapping", (SYSCALL)CreateFileMapping, 0 },
-
-#define osCreateFileMapping ((HANDLE(WINAPI*)(HANDLE,LPSECURITY_ATTRIBUTES, \
- DWORD,DWORD,DWORD,LPCTSTR))aSyscall[6].pCurrent)
-
-#if defined(SQLITE_WIN32_HAS_WIDE)
+#if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \
+ !defined(SQLITE_OMIT_WAL))
{ "CreateFileMappingW", (SYSCALL)CreateFileMappingW, 0 },
#else
{ "CreateFileMappingW", (SYSCALL)0, 0 },
#endif
#define osCreateFileMappingW ((HANDLE(WINAPI*)(HANDLE,LPSECURITY_ATTRIBUTES, \
- DWORD,DWORD,DWORD,LPCWSTR))aSyscall[7].pCurrent)
+ DWORD,DWORD,DWORD,LPCWSTR))aSyscall[6].pCurrent)
-#if defined(SQLITE_WIN32_HAS_WIDE)
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
{ "CreateMutexW", (SYSCALL)CreateMutexW, 0 },
#else
{ "CreateMutexW", (SYSCALL)0, 0 },
#endif
#define osCreateMutexW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,BOOL, \
- LPCWSTR))aSyscall[8].pCurrent)
+ LPCWSTR))aSyscall[7].pCurrent)
#if defined(SQLITE_WIN32_HAS_ANSI)
{ "DeleteFileA", (SYSCALL)DeleteFileA, 0 },
@@ -32442,7 +30198,7 @@ static struct win_syscall {
{ "DeleteFileA", (SYSCALL)0, 0 },
#endif
-#define osDeleteFileA ((BOOL(WINAPI*)(LPCSTR))aSyscall[9].pCurrent)
+#define osDeleteFileA ((BOOL(WINAPI*)(LPCSTR))aSyscall[8].pCurrent)
#if defined(SQLITE_WIN32_HAS_WIDE)
{ "DeleteFileW", (SYSCALL)DeleteFileW, 0 },
@@ -32450,7 +30206,7 @@ static struct win_syscall {
{ "DeleteFileW", (SYSCALL)0, 0 },
#endif
-#define osDeleteFileW ((BOOL(WINAPI*)(LPCWSTR))aSyscall[10].pCurrent)
+#define osDeleteFileW ((BOOL(WINAPI*)(LPCWSTR))aSyscall[9].pCurrent)
#if SQLITE_OS_WINCE
{ "FileTimeToLocalFileTime", (SYSCALL)FileTimeToLocalFileTime, 0 },
@@ -32459,7 +30215,7 @@ static struct win_syscall {
#endif
#define osFileTimeToLocalFileTime ((BOOL(WINAPI*)(CONST FILETIME*, \
- LPFILETIME))aSyscall[11].pCurrent)
+ LPFILETIME))aSyscall[10].pCurrent)
#if SQLITE_OS_WINCE
{ "FileTimeToSystemTime", (SYSCALL)FileTimeToSystemTime, 0 },
@@ -32468,11 +30224,11 @@ static struct win_syscall {
#endif
#define osFileTimeToSystemTime ((BOOL(WINAPI*)(CONST FILETIME*, \
- LPSYSTEMTIME))aSyscall[12].pCurrent)
+ LPSYSTEMTIME))aSyscall[11].pCurrent)
{ "FlushFileBuffers", (SYSCALL)FlushFileBuffers, 0 },
-#define osFlushFileBuffers ((BOOL(WINAPI*)(HANDLE))aSyscall[13].pCurrent)
+#define osFlushFileBuffers ((BOOL(WINAPI*)(HANDLE))aSyscall[12].pCurrent)
#if defined(SQLITE_WIN32_HAS_ANSI)
{ "FormatMessageA", (SYSCALL)FormatMessageA, 0 },
@@ -32481,7 +30237,7 @@ static struct win_syscall {
#endif
#define osFormatMessageA ((DWORD(WINAPI*)(DWORD,LPCVOID,DWORD,DWORD,LPSTR, \
- DWORD,va_list*))aSyscall[14].pCurrent)
+ DWORD,va_list*))aSyscall[13].pCurrent)
#if defined(SQLITE_WIN32_HAS_WIDE)
{ "FormatMessageW", (SYSCALL)FormatMessageW, 0 },
@@ -32490,15 +30246,15 @@ static struct win_syscall {
#endif
#define osFormatMessageW ((DWORD(WINAPI*)(DWORD,LPCVOID,DWORD,DWORD,LPWSTR, \
- DWORD,va_list*))aSyscall[15].pCurrent)
+ DWORD,va_list*))aSyscall[14].pCurrent)
{ "FreeLibrary", (SYSCALL)FreeLibrary, 0 },
-#define osFreeLibrary ((BOOL(WINAPI*)(HMODULE))aSyscall[16].pCurrent)
+#define osFreeLibrary ((BOOL(WINAPI*)(HMODULE))aSyscall[15].pCurrent)
{ "GetCurrentProcessId", (SYSCALL)GetCurrentProcessId, 0 },
-#define osGetCurrentProcessId ((DWORD(WINAPI*)(VOID))aSyscall[17].pCurrent)
+#define osGetCurrentProcessId ((DWORD(WINAPI*)(VOID))aSyscall[16].pCurrent)
#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_ANSI)
{ "GetDiskFreeSpaceA", (SYSCALL)GetDiskFreeSpaceA, 0 },
@@ -32507,16 +30263,16 @@ static struct win_syscall {
#endif
#define osGetDiskFreeSpaceA ((BOOL(WINAPI*)(LPCSTR,LPDWORD,LPDWORD,LPDWORD, \
- LPDWORD))aSyscall[18].pCurrent)
+ LPDWORD))aSyscall[17].pCurrent)
-#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE)
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
{ "GetDiskFreeSpaceW", (SYSCALL)GetDiskFreeSpaceW, 0 },
#else
{ "GetDiskFreeSpaceW", (SYSCALL)0, 0 },
#endif
#define osGetDiskFreeSpaceW ((BOOL(WINAPI*)(LPCWSTR,LPDWORD,LPDWORD,LPDWORD, \
- LPDWORD))aSyscall[19].pCurrent)
+ LPDWORD))aSyscall[18].pCurrent)
#if defined(SQLITE_WIN32_HAS_ANSI)
{ "GetFileAttributesA", (SYSCALL)GetFileAttributesA, 0 },
@@ -32524,15 +30280,15 @@ static struct win_syscall {
{ "GetFileAttributesA", (SYSCALL)0, 0 },
#endif
-#define osGetFileAttributesA ((DWORD(WINAPI*)(LPCSTR))aSyscall[20].pCurrent)
+#define osGetFileAttributesA ((DWORD(WINAPI*)(LPCSTR))aSyscall[19].pCurrent)
-#if defined(SQLITE_WIN32_HAS_WIDE)
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
{ "GetFileAttributesW", (SYSCALL)GetFileAttributesW, 0 },
#else
{ "GetFileAttributesW", (SYSCALL)0, 0 },
#endif
-#define osGetFileAttributesW ((DWORD(WINAPI*)(LPCWSTR))aSyscall[21].pCurrent)
+#define osGetFileAttributesW ((DWORD(WINAPI*)(LPCWSTR))aSyscall[20].pCurrent)
#if defined(SQLITE_WIN32_HAS_WIDE)
{ "GetFileAttributesExW", (SYSCALL)GetFileAttributesExW, 0 },
@@ -32541,11 +30297,15 @@ static struct win_syscall {
#endif
#define osGetFileAttributesExW ((BOOL(WINAPI*)(LPCWSTR,GET_FILEEX_INFO_LEVELS, \
- LPVOID))aSyscall[22].pCurrent)
+ LPVOID))aSyscall[21].pCurrent)
+#if !SQLITE_OS_WINRT
{ "GetFileSize", (SYSCALL)GetFileSize, 0 },
+#else
+ { "GetFileSize", (SYSCALL)0, 0 },
+#endif
-#define osGetFileSize ((DWORD(WINAPI*)(HANDLE,LPDWORD))aSyscall[23].pCurrent)
+#define osGetFileSize ((DWORD(WINAPI*)(HANDLE,LPDWORD))aSyscall[22].pCurrent)
#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_ANSI)
{ "GetFullPathNameA", (SYSCALL)GetFullPathNameA, 0 },
@@ -32554,20 +30314,20 @@ static struct win_syscall {
#endif
#define osGetFullPathNameA ((DWORD(WINAPI*)(LPCSTR,DWORD,LPSTR, \
- LPSTR*))aSyscall[24].pCurrent)
+ LPSTR*))aSyscall[23].pCurrent)
-#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE)
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
{ "GetFullPathNameW", (SYSCALL)GetFullPathNameW, 0 },
#else
{ "GetFullPathNameW", (SYSCALL)0, 0 },
#endif
#define osGetFullPathNameW ((DWORD(WINAPI*)(LPCWSTR,DWORD,LPWSTR, \
- LPWSTR*))aSyscall[25].pCurrent)
+ LPWSTR*))aSyscall[24].pCurrent)
{ "GetLastError", (SYSCALL)GetLastError, 0 },
-#define osGetLastError ((DWORD(WINAPI*)(VOID))aSyscall[26].pCurrent)
+#define osGetLastError ((DWORD(WINAPI*)(VOID))aSyscall[25].pCurrent)
#if SQLITE_OS_WINCE
/* The GetProcAddressA() routine is only available on Windows CE. */
@@ -32579,15 +30339,19 @@ static struct win_syscall {
#endif
#define osGetProcAddressA ((FARPROC(WINAPI*)(HMODULE, \
- LPCSTR))aSyscall[27].pCurrent)
+ LPCSTR))aSyscall[26].pCurrent)
+#if !SQLITE_OS_WINRT
{ "GetSystemInfo", (SYSCALL)GetSystemInfo, 0 },
+#else
+ { "GetSystemInfo", (SYSCALL)0, 0 },
+#endif
-#define osGetSystemInfo ((VOID(WINAPI*)(LPSYSTEM_INFO))aSyscall[28].pCurrent)
+#define osGetSystemInfo ((VOID(WINAPI*)(LPSYSTEM_INFO))aSyscall[27].pCurrent)
{ "GetSystemTime", (SYSCALL)GetSystemTime, 0 },
-#define osGetSystemTime ((VOID(WINAPI*)(LPSYSTEMTIME))aSyscall[29].pCurrent)
+#define osGetSystemTime ((VOID(WINAPI*)(LPSYSTEMTIME))aSyscall[28].pCurrent)
#if !SQLITE_OS_WINCE
{ "GetSystemTimeAsFileTime", (SYSCALL)GetSystemTimeAsFileTime, 0 },
@@ -32596,7 +30360,7 @@ static struct win_syscall {
#endif
#define osGetSystemTimeAsFileTime ((VOID(WINAPI*)( \
- LPFILETIME))aSyscall[30].pCurrent)
+ LPFILETIME))aSyscall[29].pCurrent)
#if defined(SQLITE_WIN32_HAS_ANSI)
{ "GetTempPathA", (SYSCALL)GetTempPathA, 0 },
@@ -32604,19 +30368,23 @@ static struct win_syscall {
{ "GetTempPathA", (SYSCALL)0, 0 },
#endif
-#define osGetTempPathA ((DWORD(WINAPI*)(DWORD,LPSTR))aSyscall[31].pCurrent)
+#define osGetTempPathA ((DWORD(WINAPI*)(DWORD,LPSTR))aSyscall[30].pCurrent)
-#if defined(SQLITE_WIN32_HAS_WIDE)
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
{ "GetTempPathW", (SYSCALL)GetTempPathW, 0 },
#else
{ "GetTempPathW", (SYSCALL)0, 0 },
#endif
-#define osGetTempPathW ((DWORD(WINAPI*)(DWORD,LPWSTR))aSyscall[32].pCurrent)
+#define osGetTempPathW ((DWORD(WINAPI*)(DWORD,LPWSTR))aSyscall[31].pCurrent)
+#if !SQLITE_OS_WINRT
{ "GetTickCount", (SYSCALL)GetTickCount, 0 },
+#else
+ { "GetTickCount", (SYSCALL)0, 0 },
+#endif
-#define osGetTickCount ((DWORD(WINAPI*)(VOID))aSyscall[33].pCurrent)
+#define osGetTickCount ((DWORD(WINAPI*)(VOID))aSyscall[32].pCurrent)
#if defined(SQLITE_WIN32_HAS_ANSI)
{ "GetVersionExA", (SYSCALL)GetVersionExA, 0 },
@@ -32625,40 +30393,52 @@ static struct win_syscall {
#endif
#define osGetVersionExA ((BOOL(WINAPI*)( \
- LPOSVERSIONINFOA))aSyscall[34].pCurrent)
+ LPOSVERSIONINFOA))aSyscall[33].pCurrent)
{ "HeapAlloc", (SYSCALL)HeapAlloc, 0 },
#define osHeapAlloc ((LPVOID(WINAPI*)(HANDLE,DWORD, \
- SIZE_T))aSyscall[35].pCurrent)
+ SIZE_T))aSyscall[34].pCurrent)
+#if !SQLITE_OS_WINRT
{ "HeapCreate", (SYSCALL)HeapCreate, 0 },
+#else
+ { "HeapCreate", (SYSCALL)0, 0 },
+#endif
#define osHeapCreate ((HANDLE(WINAPI*)(DWORD,SIZE_T, \
- SIZE_T))aSyscall[36].pCurrent)
+ SIZE_T))aSyscall[35].pCurrent)
+#if !SQLITE_OS_WINRT
{ "HeapDestroy", (SYSCALL)HeapDestroy, 0 },
+#else
+ { "HeapDestroy", (SYSCALL)0, 0 },
+#endif
-#define osHeapDestroy ((BOOL(WINAPI*)(HANDLE))aSyscall[37].pCurrent)
+#define osHeapDestroy ((BOOL(WINAPI*)(HANDLE))aSyscall[36].pCurrent)
{ "HeapFree", (SYSCALL)HeapFree, 0 },
-#define osHeapFree ((BOOL(WINAPI*)(HANDLE,DWORD,LPVOID))aSyscall[38].pCurrent)
+#define osHeapFree ((BOOL(WINAPI*)(HANDLE,DWORD,LPVOID))aSyscall[37].pCurrent)
{ "HeapReAlloc", (SYSCALL)HeapReAlloc, 0 },
#define osHeapReAlloc ((LPVOID(WINAPI*)(HANDLE,DWORD,LPVOID, \
- SIZE_T))aSyscall[39].pCurrent)
+ SIZE_T))aSyscall[38].pCurrent)
{ "HeapSize", (SYSCALL)HeapSize, 0 },
#define osHeapSize ((SIZE_T(WINAPI*)(HANDLE,DWORD, \
- LPCVOID))aSyscall[40].pCurrent)
+ LPCVOID))aSyscall[39].pCurrent)
+#if !SQLITE_OS_WINRT
{ "HeapValidate", (SYSCALL)HeapValidate, 0 },
+#else
+ { "HeapValidate", (SYSCALL)0, 0 },
+#endif
#define osHeapValidate ((BOOL(WINAPI*)(HANDLE,DWORD, \
- LPCVOID))aSyscall[41].pCurrent)
+ LPCVOID))aSyscall[40].pCurrent)
#if defined(SQLITE_WIN32_HAS_ANSI)
{ "LoadLibraryA", (SYSCALL)LoadLibraryA, 0 },
@@ -32666,107 +30446,251 @@ static struct win_syscall {
{ "LoadLibraryA", (SYSCALL)0, 0 },
#endif
-#define osLoadLibraryA ((HMODULE(WINAPI*)(LPCSTR))aSyscall[42].pCurrent)
+#define osLoadLibraryA ((HMODULE(WINAPI*)(LPCSTR))aSyscall[41].pCurrent)
-#if defined(SQLITE_WIN32_HAS_WIDE)
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
{ "LoadLibraryW", (SYSCALL)LoadLibraryW, 0 },
#else
{ "LoadLibraryW", (SYSCALL)0, 0 },
#endif
-#define osLoadLibraryW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[43].pCurrent)
+#define osLoadLibraryW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[42].pCurrent)
+#if !SQLITE_OS_WINRT
{ "LocalFree", (SYSCALL)LocalFree, 0 },
+#else
+ { "LocalFree", (SYSCALL)0, 0 },
+#endif
-#define osLocalFree ((HLOCAL(WINAPI*)(HLOCAL))aSyscall[44].pCurrent)
+#define osLocalFree ((HLOCAL(WINAPI*)(HLOCAL))aSyscall[43].pCurrent)
-#if !SQLITE_OS_WINCE
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
{ "LockFile", (SYSCALL)LockFile, 0 },
-
-#define osLockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
- DWORD))aSyscall[45].pCurrent)
#else
{ "LockFile", (SYSCALL)0, 0 },
#endif
+#ifndef osLockFile
+#define osLockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
+ DWORD))aSyscall[44].pCurrent)
+#endif
+
#if !SQLITE_OS_WINCE
{ "LockFileEx", (SYSCALL)LockFileEx, 0 },
-
-#define osLockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD,DWORD, \
- LPOVERLAPPED))aSyscall[46].pCurrent)
#else
{ "LockFileEx", (SYSCALL)0, 0 },
#endif
+#ifndef osLockFileEx
+#define osLockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD,DWORD, \
+ LPOVERLAPPED))aSyscall[45].pCurrent)
+#endif
+
+#if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && !defined(SQLITE_OMIT_WAL))
{ "MapViewOfFile", (SYSCALL)MapViewOfFile, 0 },
+#else
+ { "MapViewOfFile", (SYSCALL)0, 0 },
+#endif
#define osMapViewOfFile ((LPVOID(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
- SIZE_T))aSyscall[47].pCurrent)
+ SIZE_T))aSyscall[46].pCurrent)
{ "MultiByteToWideChar", (SYSCALL)MultiByteToWideChar, 0 },
#define osMultiByteToWideChar ((int(WINAPI*)(UINT,DWORD,LPCSTR,int,LPWSTR, \
- int))aSyscall[48].pCurrent)
+ int))aSyscall[47].pCurrent)
{ "QueryPerformanceCounter", (SYSCALL)QueryPerformanceCounter, 0 },
#define osQueryPerformanceCounter ((BOOL(WINAPI*)( \
- LARGE_INTEGER*))aSyscall[49].pCurrent)
+ LARGE_INTEGER*))aSyscall[48].pCurrent)
{ "ReadFile", (SYSCALL)ReadFile, 0 },
#define osReadFile ((BOOL(WINAPI*)(HANDLE,LPVOID,DWORD,LPDWORD, \
- LPOVERLAPPED))aSyscall[50].pCurrent)
+ LPOVERLAPPED))aSyscall[49].pCurrent)
{ "SetEndOfFile", (SYSCALL)SetEndOfFile, 0 },
-#define osSetEndOfFile ((BOOL(WINAPI*)(HANDLE))aSyscall[51].pCurrent)
+#define osSetEndOfFile ((BOOL(WINAPI*)(HANDLE))aSyscall[50].pCurrent)
+#if !SQLITE_OS_WINRT
{ "SetFilePointer", (SYSCALL)SetFilePointer, 0 },
+#else
+ { "SetFilePointer", (SYSCALL)0, 0 },
+#endif
#define osSetFilePointer ((DWORD(WINAPI*)(HANDLE,LONG,PLONG, \
- DWORD))aSyscall[52].pCurrent)
+ DWORD))aSyscall[51].pCurrent)
+#if !SQLITE_OS_WINRT
{ "Sleep", (SYSCALL)Sleep, 0 },
+#else
+ { "Sleep", (SYSCALL)0, 0 },
+#endif
-#define osSleep ((VOID(WINAPI*)(DWORD))aSyscall[53].pCurrent)
+#define osSleep ((VOID(WINAPI*)(DWORD))aSyscall[52].pCurrent)
{ "SystemTimeToFileTime", (SYSCALL)SystemTimeToFileTime, 0 },
#define osSystemTimeToFileTime ((BOOL(WINAPI*)(CONST SYSTEMTIME*, \
- LPFILETIME))aSyscall[54].pCurrent)
+ LPFILETIME))aSyscall[53].pCurrent)
-#if !SQLITE_OS_WINCE
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
{ "UnlockFile", (SYSCALL)UnlockFile, 0 },
-
-#define osUnlockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
- DWORD))aSyscall[55].pCurrent)
#else
{ "UnlockFile", (SYSCALL)0, 0 },
#endif
+#ifndef osUnlockFile
+#define osUnlockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
+ DWORD))aSyscall[54].pCurrent)
+#endif
+
#if !SQLITE_OS_WINCE
{ "UnlockFileEx", (SYSCALL)UnlockFileEx, 0 },
-
-#define osUnlockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
- LPOVERLAPPED))aSyscall[56].pCurrent)
#else
{ "UnlockFileEx", (SYSCALL)0, 0 },
#endif
+#define osUnlockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
+ LPOVERLAPPED))aSyscall[55].pCurrent)
+
+#if SQLITE_OS_WINCE || !defined(SQLITE_OMIT_WAL)
{ "UnmapViewOfFile", (SYSCALL)UnmapViewOfFile, 0 },
+#else
+ { "UnmapViewOfFile", (SYSCALL)0, 0 },
+#endif
-#define osUnmapViewOfFile ((BOOL(WINAPI*)(LPCVOID))aSyscall[57].pCurrent)
+#define osUnmapViewOfFile ((BOOL(WINAPI*)(LPCVOID))aSyscall[56].pCurrent)
{ "WideCharToMultiByte", (SYSCALL)WideCharToMultiByte, 0 },
#define osWideCharToMultiByte ((int(WINAPI*)(UINT,DWORD,LPCWSTR,int,LPSTR,int, \
- LPCSTR,LPBOOL))aSyscall[58].pCurrent)
+ LPCSTR,LPBOOL))aSyscall[57].pCurrent)
{ "WriteFile", (SYSCALL)WriteFile, 0 },
#define osWriteFile ((BOOL(WINAPI*)(HANDLE,LPCVOID,DWORD,LPDWORD, \
- LPOVERLAPPED))aSyscall[59].pCurrent)
+ LPOVERLAPPED))aSyscall[58].pCurrent)
+
+#if SQLITE_OS_WINRT
+ { "CreateEventExW", (SYSCALL)CreateEventExW, 0 },
+#else
+ { "CreateEventExW", (SYSCALL)0, 0 },
+#endif
+
+#define osCreateEventExW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,LPCWSTR, \
+ DWORD,DWORD))aSyscall[59].pCurrent)
+
+#if !SQLITE_OS_WINRT
+ { "WaitForSingleObject", (SYSCALL)WaitForSingleObject, 0 },
+#else
+ { "WaitForSingleObject", (SYSCALL)0, 0 },
+#endif
+
+#define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \
+ DWORD))aSyscall[60].pCurrent)
+
+#if SQLITE_OS_WINRT
+ { "WaitForSingleObjectEx", (SYSCALL)WaitForSingleObjectEx, 0 },
+#else
+ { "WaitForSingleObjectEx", (SYSCALL)0, 0 },
+#endif
+
+#define osWaitForSingleObjectEx ((DWORD(WINAPI*)(HANDLE,DWORD, \
+ BOOL))aSyscall[61].pCurrent)
+
+#if SQLITE_OS_WINRT
+ { "SetFilePointerEx", (SYSCALL)SetFilePointerEx, 0 },
+#else
+ { "SetFilePointerEx", (SYSCALL)0, 0 },
+#endif
+
+#define osSetFilePointerEx ((BOOL(WINAPI*)(HANDLE,LARGE_INTEGER, \
+ PLARGE_INTEGER,DWORD))aSyscall[62].pCurrent)
+
+#if SQLITE_OS_WINRT
+ { "GetFileInformationByHandleEx", (SYSCALL)GetFileInformationByHandleEx, 0 },
+#else
+ { "GetFileInformationByHandleEx", (SYSCALL)0, 0 },
+#endif
+
+#define osGetFileInformationByHandleEx ((BOOL(WINAPI*)(HANDLE, \
+ FILE_INFO_BY_HANDLE_CLASS,LPVOID,DWORD))aSyscall[63].pCurrent)
+
+#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_WAL)
+ { "MapViewOfFileFromApp", (SYSCALL)MapViewOfFileFromApp, 0 },
+#else
+ { "MapViewOfFileFromApp", (SYSCALL)0, 0 },
+#endif
+
+#define osMapViewOfFileFromApp ((LPVOID(WINAPI*)(HANDLE,ULONG,ULONG64, \
+ SIZE_T))aSyscall[64].pCurrent)
+
+#if SQLITE_OS_WINRT
+ { "CreateFile2", (SYSCALL)CreateFile2, 0 },
+#else
+ { "CreateFile2", (SYSCALL)0, 0 },
+#endif
+
+#define osCreateFile2 ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD,DWORD, \
+ LPCREATEFILE2_EXTENDED_PARAMETERS))aSyscall[65].pCurrent)
+
+#if SQLITE_OS_WINRT
+ { "LoadPackagedLibrary", (SYSCALL)LoadPackagedLibrary, 0 },
+#else
+ { "LoadPackagedLibrary", (SYSCALL)0, 0 },
+#endif
+
+#define osLoadPackagedLibrary ((HMODULE(WINAPI*)(LPCWSTR, \
+ DWORD))aSyscall[66].pCurrent)
+
+#if SQLITE_OS_WINRT
+ { "GetTickCount64", (SYSCALL)GetTickCount64, 0 },
+#else
+ { "GetTickCount64", (SYSCALL)0, 0 },
+#endif
+
+#define osGetTickCount64 ((ULONGLONG(WINAPI*)(VOID))aSyscall[67].pCurrent)
+
+#if SQLITE_OS_WINRT
+ { "GetNativeSystemInfo", (SYSCALL)GetNativeSystemInfo, 0 },
+#else
+ { "GetNativeSystemInfo", (SYSCALL)0, 0 },
+#endif
+
+#define osGetNativeSystemInfo ((VOID(WINAPI*)( \
+ LPSYSTEM_INFO))aSyscall[68].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_ANSI)
+ { "OutputDebugStringA", (SYSCALL)OutputDebugStringA, 0 },
+#else
+ { "OutputDebugStringA", (SYSCALL)0, 0 },
+#endif
+
+#define osOutputDebugStringA ((VOID(WINAPI*)(LPCSTR))aSyscall[69].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_WIDE)
+ { "OutputDebugStringW", (SYSCALL)OutputDebugStringW, 0 },
+#else
+ { "OutputDebugStringW", (SYSCALL)0, 0 },
+#endif
+
+#define osOutputDebugStringW ((VOID(WINAPI*)(LPCWSTR))aSyscall[70].pCurrent)
+
+ { "GetProcessHeap", (SYSCALL)GetProcessHeap, 0 },
+
+#define osGetProcessHeap ((HANDLE(WINAPI*)(VOID))aSyscall[71].pCurrent)
+
+#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_WAL)
+ { "CreateFileMappingFromApp", (SYSCALL)CreateFileMappingFromApp, 0 },
+#else
+ { "CreateFileMappingFromApp", (SYSCALL)0, 0 },
+#endif
+
+#define osCreateFileMappingFromApp ((HANDLE(WINAPI*)(HANDLE, \
+ LPSECURITY_ATTRIBUTES,ULONG,ULONG64,LPCWSTR))aSyscall[72].pCurrent)
}; /* End of the overrideable system calls */
@@ -32854,6 +30778,64 @@ static const char *winNextSystemCall(sqlite3_vfs *p, const char *zName){
}
/*
+** This function outputs the specified (ANSI) string to the Win32 debugger
+** (if available).
+*/
+
+SQLITE_API void sqlite3_win32_write_debug(char *zBuf, int nBuf){
+ char zDbgBuf[SQLITE_WIN32_DBG_BUF_SIZE];
+ int nMin = MIN(nBuf, (SQLITE_WIN32_DBG_BUF_SIZE - 1)); /* may be negative. */
+ if( nMin<-1 ) nMin = -1; /* all negative values become -1. */
+ assert( nMin==-1 || nMin==0 || nMin<SQLITE_WIN32_DBG_BUF_SIZE );
+#if defined(SQLITE_WIN32_HAS_ANSI)
+ if( nMin>0 ){
+ memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE);
+ memcpy(zDbgBuf, zBuf, nMin);
+ osOutputDebugStringA(zDbgBuf);
+ }else{
+ osOutputDebugStringA(zBuf);
+ }
+#elif defined(SQLITE_WIN32_HAS_WIDE)
+ memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE);
+ if ( osMultiByteToWideChar(
+ osAreFileApisANSI() ? CP_ACP : CP_OEMCP, 0, zBuf,
+ nMin, (LPWSTR)zDbgBuf, SQLITE_WIN32_DBG_BUF_SIZE/sizeof(WCHAR))<=0 ){
+ return;
+ }
+ osOutputDebugStringW((LPCWSTR)zDbgBuf);
+#else
+ if( nMin>0 ){
+ memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE);
+ memcpy(zDbgBuf, zBuf, nMin);
+ fprintf(stderr, "%s", zDbgBuf);
+ }else{
+ fprintf(stderr, "%s", zBuf);
+ }
+#endif
+}
+
+/*
+** The following routine suspends the current thread for at least ms
+** milliseconds. This is equivalent to the Win32 Sleep() interface.
+*/
+#if SQLITE_OS_WINRT
+static HANDLE sleepObj = NULL;
+#endif
+
+SQLITE_API void sqlite3_win32_sleep(DWORD milliseconds){
+#if SQLITE_OS_WINRT
+ if ( sleepObj==NULL ){
+ sleepObj = osCreateEventExW(NULL, NULL, CREATE_EVENT_MANUAL_RESET,
+ SYNCHRONIZE);
+ }
+ assert( sleepObj!=NULL );
+ osWaitForSingleObjectEx(sleepObj, milliseconds, FALSE);
+#else
+ osSleep(milliseconds);
+#endif
+}
+
+/*
** Return true (non-zero) if we are running under WinNT, Win2K, WinXP,
** or WinCE. Return false (zero) for Win95, Win98, or WinME.
**
@@ -32864,7 +30846,7 @@ static const char *winNextSystemCall(sqlite3_vfs *p, const char *zName){
** WinNT/2K/XP so that we will know whether or not we can safely call
** the LockFileEx() API.
*/
-#if SQLITE_OS_WINCE
+#if SQLITE_OS_WINCE || SQLITE_OS_WINRT
# define isNT() (1)
#else
static int isNT(void){
@@ -32890,7 +30872,7 @@ static void *winMemMalloc(int nBytes){
hHeap = winMemGetHeap();
assert( hHeap!=0 );
assert( hHeap!=INVALID_HANDLE_VALUE );
-#ifdef SQLITE_WIN32_MALLOC_VALIDATE
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
assert ( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
#endif
assert( nBytes>=0 );
@@ -32912,7 +30894,7 @@ static void winMemFree(void *pPrior){
hHeap = winMemGetHeap();
assert( hHeap!=0 );
assert( hHeap!=INVALID_HANDLE_VALUE );
-#ifdef SQLITE_WIN32_MALLOC_VALIDATE
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
assert ( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) );
#endif
if( !pPrior ) return; /* Passing NULL to HeapFree is undefined. */
@@ -32933,7 +30915,7 @@ static void *winMemRealloc(void *pPrior, int nBytes){
hHeap = winMemGetHeap();
assert( hHeap!=0 );
assert( hHeap!=INVALID_HANDLE_VALUE );
-#ifdef SQLITE_WIN32_MALLOC_VALIDATE
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
assert ( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) );
#endif
assert( nBytes>=0 );
@@ -32961,7 +30943,7 @@ static int winMemSize(void *p){
hHeap = winMemGetHeap();
assert( hHeap!=0 );
assert( hHeap!=INVALID_HANDLE_VALUE );
-#ifdef SQLITE_WIN32_MALLOC_VALIDATE
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
assert ( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
#endif
if( !p ) return 0;
@@ -32989,6 +30971,8 @@ static int winMemInit(void *pAppData){
if( !pWinMemData ) return SQLITE_ERROR;
assert( pWinMemData->magic==WINMEM_MAGIC );
+
+#if !SQLITE_OS_WINRT && SQLITE_WIN32_HEAP_CREATE
if( !pWinMemData->hHeap ){
pWinMemData->hHeap = osHeapCreate(SQLITE_WIN32_HEAP_FLAGS,
SQLITE_WIN32_HEAP_INIT_SIZE,
@@ -33001,10 +30985,21 @@ static int winMemInit(void *pAppData){
return SQLITE_NOMEM;
}
pWinMemData->bOwned = TRUE;
+ assert( pWinMemData->bOwned );
}
+#else
+ pWinMemData->hHeap = osGetProcessHeap();
+ if( !pWinMemData->hHeap ){
+ sqlite3_log(SQLITE_NOMEM,
+ "failed to GetProcessHeap (%d)", osGetLastError());
+ return SQLITE_NOMEM;
+ }
+ pWinMemData->bOwned = FALSE;
+ assert( !pWinMemData->bOwned );
+#endif
assert( pWinMemData->hHeap!=0 );
assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE );
-#ifdef SQLITE_WIN32_MALLOC_VALIDATE
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
assert( osHeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
#endif
return SQLITE_OK;
@@ -33019,7 +31014,7 @@ static void winMemShutdown(void *pAppData){
if( !pWinMemData ) return;
if( pWinMemData->hHeap ){
assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE );
-#ifdef SQLITE_WIN32_MALLOC_VALIDATE
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
assert( osHeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
#endif
if( pWinMemData->bOwned ){
@@ -33204,6 +31199,42 @@ SQLITE_API char *sqlite3_win32_utf8_to_mbcs(const char *zFilename){
return zFilenameMbcs;
}
+/*
+** This function sets the data directory or the temporary directory based on
+** the provided arguments. The type argument must be 1 in order to set the
+** data directory or 2 in order to set the temporary directory. The zValue
+** argument is the name of the directory to use. The return value will be
+** SQLITE_OK if successful.
+*/
+SQLITE_API int sqlite3_win32_set_directory(DWORD type, LPCWSTR zValue){
+ char **ppDirectory = 0;
+#ifndef SQLITE_OMIT_AUTOINIT
+ int rc = sqlite3_initialize();
+ if( rc ) return rc;
+#endif
+ if( type==SQLITE_WIN32_DATA_DIRECTORY_TYPE ){
+ ppDirectory = &sqlite3_data_directory;
+ }else if( type==SQLITE_WIN32_TEMP_DIRECTORY_TYPE ){
+ ppDirectory = &sqlite3_temp_directory;
+ }
+ assert( !ppDirectory || type==SQLITE_WIN32_DATA_DIRECTORY_TYPE
+ || type==SQLITE_WIN32_TEMP_DIRECTORY_TYPE
+ );
+ assert( !ppDirectory || sqlite3MemdebugHasType(*ppDirectory, MEMTYPE_HEAP) );
+ if( ppDirectory ){
+ char *zValueUtf8 = 0;
+ if( zValue && zValue[0] ){
+ zValueUtf8 = unicodeToUtf8(zValue);
+ if ( zValueUtf8==0 ){
+ return SQLITE_NOMEM;
+ }
+ }
+ sqlite3_free(*ppDirectory);
+ *ppDirectory = zValueUtf8;
+ return SQLITE_OK;
+ }
+ return SQLITE_ERROR;
+}
/*
** The return value of getLastErrorMsg
@@ -33219,6 +31250,17 @@ static int getLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){
char *zOut = 0;
if( isNT() ){
+#if SQLITE_OS_WINRT
+ WCHAR zTempWide[MAX_PATH+1]; /* NOTE: Somewhat arbitrary. */
+ dwLen = osFormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ lastErrno,
+ 0,
+ zTempWide,
+ MAX_PATH,
+ 0);
+#else
LPWSTR zTempWide = NULL;
dwLen = osFormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
@@ -33229,20 +31271,20 @@ static int getLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){
(LPWSTR) &zTempWide,
0,
0);
+#endif
if( dwLen > 0 ){
/* allocate a buffer and convert to UTF8 */
sqlite3BeginBenignMalloc();
zOut = unicodeToUtf8(zTempWide);
sqlite3EndBenignMalloc();
+#if !SQLITE_OS_WINRT
/* free the system buffer allocated by FormatMessage */
osLocalFree(zTempWide);
+#endif
}
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-** Since the ANSI 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{
+ }
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
char *zTemp = NULL;
dwLen = osFormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
@@ -33261,8 +31303,8 @@ static int getLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){
/* free the system buffer allocated by FormatMessage */
osLocalFree(zTemp);
}
-#endif
}
+#endif
if( 0 == dwLen ){
sqlite3_snprintf(nBuf, zBuf, "OsError 0x%x (%u)", lastErrno, lastErrno);
}else{
@@ -33287,7 +31329,7 @@ static int getLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){
** 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.
+** failed and the associated file-system path, if any.
*/
#define winLogError(a,b,c,d) winLogErrorAtLine(a,b,c,d,__LINE__)
static int winLogErrorAtLine(
@@ -33345,7 +31387,7 @@ static int retryIoerr(int *pnRetry, DWORD *pError){
if( e==ERROR_ACCESS_DENIED ||
e==ERROR_LOCK_VIOLATION ||
e==ERROR_SHARING_VIOLATION ){
- osSleep(win32IoerrRetryDelay*(1+*pnRetry));
+ sqlite3_win32_sleep(win32IoerrRetryDelay*(1+*pnRetry));
++*pnRetry;
return 1;
}
@@ -33406,7 +31448,7 @@ struct tm *__cdecl localtime(const time_t *t)
static void winceMutexAcquire(HANDLE h){
DWORD dwErr;
do {
- dwErr = WaitForSingleObject(h, INFINITE);
+ dwErr = osWaitForSingleObject(h, INFINITE);
} while (dwErr != WAIT_OBJECT_0 && dwErr != WAIT_ABANDONED);
}
/*
@@ -33537,7 +31579,7 @@ static void winceDestroyLock(winFile *pFile){
** An implementation of the LockFile() API of Windows for CE
*/
static BOOL winceLockFile(
- HANDLE *phFile,
+ LPHANDLE phFile,
DWORD dwFileOffsetLow,
DWORD dwFileOffsetHigh,
DWORD nNumberOfBytesToLockLow,
@@ -33601,7 +31643,7 @@ static BOOL winceLockFile(
** An implementation of the UnlockFile API of Windows for CE
*/
static BOOL winceUnlockFile(
- HANDLE *phFile,
+ LPHANDLE phFile,
DWORD dwFileOffsetLow,
DWORD dwFileOffsetHigh,
DWORD nNumberOfBytesToUnlockLow,
@@ -33658,34 +31700,73 @@ static BOOL winceUnlockFile(
winceMutexRelease(pFile->hMutex);
return bReturn;
}
+/*
+** End of the special code for wince
+*****************************************************************************/
+#endif /* SQLITE_OS_WINCE */
/*
-** An implementation of the LockFileEx() API of Windows for CE
+** Lock a file region.
*/
-static BOOL winceLockFileEx(
- HANDLE *phFile,
- DWORD dwFlags,
- DWORD dwReserved,
- DWORD nNumberOfBytesToLockLow,
- DWORD nNumberOfBytesToLockHigh,
- LPOVERLAPPED lpOverlapped
+static BOOL winLockFile(
+ LPHANDLE phFile,
+ DWORD flags,
+ DWORD offsetLow,
+ DWORD offsetHigh,
+ DWORD numBytesLow,
+ DWORD numBytesHigh
){
- UNUSED_PARAMETER(dwReserved);
- UNUSED_PARAMETER(nNumberOfBytesToLockHigh);
-
- /* If the caller wants a shared read lock, forward this call
- ** to winceLockFile */
- if (lpOverlapped->Offset == (DWORD)SHARED_FIRST &&
- dwFlags == 1 &&
- nNumberOfBytesToLockLow == (DWORD)SHARED_SIZE){
- return winceLockFile(phFile, SHARED_FIRST, 0, 1, 0);
+#if SQLITE_OS_WINCE
+ /*
+ ** NOTE: Windows CE is handled differently here due its lack of the Win32
+ ** API LockFile.
+ */
+ return winceLockFile(phFile, offsetLow, offsetHigh,
+ numBytesLow, numBytesHigh);
+#else
+ if( isNT() ){
+ OVERLAPPED ovlp;
+ memset(&ovlp, 0, sizeof(OVERLAPPED));
+ ovlp.Offset = offsetLow;
+ ovlp.OffsetHigh = offsetHigh;
+ return osLockFileEx(*phFile, flags, 0, numBytesLow, numBytesHigh, &ovlp);
+ }else{
+ return osLockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
+ numBytesHigh);
}
- return FALSE;
+#endif
}
+
/*
-** End of the special code for wince
-*****************************************************************************/
-#endif /* SQLITE_OS_WINCE */
+** Unlock a file region.
+ */
+static BOOL winUnlockFile(
+ LPHANDLE phFile,
+ DWORD offsetLow,
+ DWORD offsetHigh,
+ DWORD numBytesLow,
+ DWORD numBytesHigh
+){
+#if SQLITE_OS_WINCE
+ /*
+ ** NOTE: Windows CE is handled differently here due its lack of the Win32
+ ** API UnlockFile.
+ */
+ return winceUnlockFile(phFile, offsetLow, offsetHigh,
+ numBytesLow, numBytesHigh);
+#else
+ if( isNT() ){
+ OVERLAPPED ovlp;
+ memset(&ovlp, 0, sizeof(OVERLAPPED));
+ ovlp.Offset = offsetLow;
+ ovlp.OffsetHigh = offsetHigh;
+ return osUnlockFileEx(*phFile, 0, numBytesLow, numBytesHigh, &ovlp);
+ }else{
+ return osUnlockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
+ numBytesHigh);
+ }
+#endif
+}
/*****************************************************************************
** The next group of routines implement the I/O methods specified
@@ -33705,6 +31786,7 @@ static BOOL winceLockFileEx(
** Otherwise, set pFile->lastErrno and return non-zero.
*/
static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){
+#if !SQLITE_OS_WINRT
LONG upperBits; /* Most sig. 32 bits of new offset */
LONG lowerBits; /* Least sig. 32 bits of new offset */
DWORD dwRet; /* Value returned by SetFilePointer() */
@@ -33731,6 +31813,26 @@ static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){
}
return 0;
+#else
+ /*
+ ** Same as above, except that this implementation works for WinRT.
+ */
+
+ LARGE_INTEGER x; /* The new offset */
+ BOOL bRet; /* Value returned by SetFilePointerEx() */
+
+ x.QuadPart = iOffset;
+ bRet = osSetFilePointerEx(pFile->h, x, 0, FILE_BEGIN);
+
+ if(!bRet){
+ pFile->lastErrno = osGetLastError();
+ winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno,
+ "seekWinFile", pFile->zPath);
+ return 1;
+ }
+
+ return 0;
+#endif
}
/*
@@ -33749,12 +31851,14 @@ static int winClose(sqlite3_file *id){
winFile *pFile = (winFile*)id;
assert( id!=0 );
+#ifndef SQLITE_OMIT_WAL
assert( pFile->pShm==0 );
+#endif
OSTRACE(("CLOSE %d\n", pFile->h));
do{
rc = osCloseHandle(pFile->h);
/* SimulateIOError( rc=0; cnt=MX_CLOSE_ATTEMPT; ); */
- }while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (osSleep(100), 1) );
+ }while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (sqlite3_win32_sleep(100), 1) );
#if SQLITE_OS_WINCE
#define WINCE_DELETION_ATTEMPTS 3
winceDestroyLock(pFile);
@@ -33765,7 +31869,7 @@ static int winClose(sqlite3_file *id){
&& osGetFileAttributesW(pFile->zDeleteOnClose)!=0xffffffff
&& cnt++ < WINCE_DELETION_ATTEMPTS
){
- osSleep(100); /* Wait a little before trying again */
+ sqlite3_win32_sleep(100); /* Wait a little before trying again */
}
sqlite3_free(pFile->zDeleteOnClose);
}
@@ -34020,23 +32124,40 @@ static int winSync(sqlite3_file *id, int flags){
** Determine the current size of a file in bytes
*/
static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){
- DWORD upperBits;
- DWORD lowerBits;
winFile *pFile = (winFile*)id;
- DWORD lastErrno;
+ int rc = SQLITE_OK;
assert( id!=0 );
SimulateIOError(return SQLITE_IOERR_FSTAT);
- lowerBits = osGetFileSize(pFile->h, &upperBits);
- if( (lowerBits == INVALID_FILE_SIZE)
- && ((lastErrno = osGetLastError())!=NO_ERROR) )
+#if SQLITE_OS_WINRT
{
- pFile->lastErrno = lastErrno;
- return winLogError(SQLITE_IOERR_FSTAT, pFile->lastErrno,
+ FILE_STANDARD_INFO info;
+ if( osGetFileInformationByHandleEx(pFile->h, FileStandardInfo,
+ &info, sizeof(info)) ){
+ *pSize = info.EndOfFile.QuadPart;
+ }else{
+ pFile->lastErrno = osGetLastError();
+ rc = winLogError(SQLITE_IOERR_FSTAT, pFile->lastErrno,
+ "winFileSize", pFile->zPath);
+ }
+ }
+#else
+ {
+ DWORD upperBits;
+ DWORD lowerBits;
+ DWORD lastErrno;
+
+ lowerBits = osGetFileSize(pFile->h, &upperBits);
+ *pSize = (((sqlite3_int64)upperBits)<<32) + lowerBits;
+ if( (lowerBits == INVALID_FILE_SIZE)
+ && ((lastErrno = osGetLastError())!=NO_ERROR) ){
+ pFile->lastErrno = lastErrno;
+ rc = winLogError(SQLITE_IOERR_FSTAT, pFile->lastErrno,
"winFileSize", pFile->zPath);
+ }
}
- *pSize = (((sqlite3_int64)upperBits)<<32) + lowerBits;
- return SQLITE_OK;
+#endif
+ return rc;
}
/*
@@ -34046,6 +32167,30 @@ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){
# define LOCKFILE_FAIL_IMMEDIATELY 1
#endif
+#ifndef LOCKFILE_EXCLUSIVE_LOCK
+# define LOCKFILE_EXCLUSIVE_LOCK 2
+#endif
+
+/*
+** Historically, SQLite has used both the LockFile and LockFileEx functions.
+** When the LockFile function was used, it was always expected to fail
+** immediately if the lock could not be obtained. Also, it always expected to
+** obtain an exclusive lock. These flags are used with the LockFileEx function
+** and reflect those expectations; therefore, they should not be changed.
+*/
+#ifndef SQLITE_LOCKFILE_FLAGS
+# define SQLITE_LOCKFILE_FLAGS (LOCKFILE_FAIL_IMMEDIATELY | \
+ LOCKFILE_EXCLUSIVE_LOCK)
+#endif
+
+/*
+** Currently, SQLite never calls the LockFileEx function without wanting the
+** call to fail immediately if the lock cannot be obtained.
+*/
+#ifndef SQLITE_LOCKFILEEX_FLAGS
+# define SQLITE_LOCKFILEEX_FLAGS (LOCKFILE_FAIL_IMMEDIATELY)
+#endif
+
/*
** Acquire a reader lock.
** Different API routines are called depending on whether or not this
@@ -34054,22 +32199,26 @@ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){
static int getReadLock(winFile *pFile){
int res;
if( isNT() ){
- OVERLAPPED ovlp;
- ovlp.Offset = SHARED_FIRST;
- ovlp.OffsetHigh = 0;
- ovlp.hEvent = 0;
- res = osLockFileEx(pFile->h, LOCKFILE_FAIL_IMMEDIATELY,
- 0, SHARED_SIZE, 0, &ovlp);
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-*/
-#if SQLITE_OS_WINCE==0
- }else{
+#if SQLITE_OS_WINCE
+ /*
+ ** NOTE: Windows CE is handled differently here due its lack of the Win32
+ ** API LockFileEx.
+ */
+ res = winceLockFile(&pFile->h, SHARED_FIRST, 0, 1, 0);
+#else
+ res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS, SHARED_FIRST, 0,
+ SHARED_SIZE, 0);
+#endif
+ }
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
int lk;
sqlite3_randomness(sizeof(lk), &lk);
pFile->sharedLockByte = (short)((lk & 0x7fffffff)%(SHARED_SIZE - 1));
- res = osLockFile(pFile->h, SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0);
-#endif
+ res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS,
+ SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0);
}
+#endif
if( res == 0 ){
pFile->lastErrno = osGetLastError();
/* No need to log a failure to lock */
@@ -34084,14 +32233,13 @@ static int unlockReadLock(winFile *pFile){
int res;
DWORD lastErrno;
if( isNT() ){
- res = osUnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-*/
-#if SQLITE_OS_WINCE==0
- }else{
- res = osUnlockFile(pFile->h, SHARED_FIRST + pFile->sharedLockByte, 0, 1, 0);
-#endif
+ res = winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
+ }
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
+ res = winUnlockFile(&pFile->h, SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0);
}
+#endif
if( res==0 && ((lastErrno = osGetLastError())!=ERROR_NOT_LOCKED) ){
pFile->lastErrno = lastErrno;
winLogError(SQLITE_IOERR_UNLOCK, pFile->lastErrno,
@@ -34162,7 +32310,8 @@ static int winLock(sqlite3_file *id, int locktype){
&& (pFile->locktype==RESERVED_LOCK))
){
int cnt = 3;
- while( cnt-->0 && (res = osLockFile(pFile->h, PENDING_BYTE, 0, 1, 0))==0 ){
+ while( cnt-->0 && (res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS,
+ PENDING_BYTE, 0, 1, 0))==0 ){
/* Try 3 times to get the pending lock. This is needed to work
** around problems caused by indexing and/or anti-virus software on
** Windows systems.
@@ -34170,7 +32319,7 @@ static int winLock(sqlite3_file *id, int locktype){
** copy this retry logic. It is a hack intended for Windows only.
*/
OSTRACE(("could not get a PENDING lock. cnt=%d\n", cnt));
- if( cnt ) osSleep(1);
+ if( cnt ) sqlite3_win32_sleep(1);
}
gotPendingLock = res;
if( !res ){
@@ -34194,7 +32343,7 @@ static int winLock(sqlite3_file *id, int locktype){
*/
if( locktype==RESERVED_LOCK && res ){
assert( pFile->locktype==SHARED_LOCK );
- res = osLockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
+ res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, RESERVED_BYTE, 0, 1, 0);
if( res ){
newLocktype = RESERVED_LOCK;
}else{
@@ -34215,7 +32364,8 @@ static int winLock(sqlite3_file *id, int locktype){
assert( pFile->locktype>=SHARED_LOCK );
res = unlockReadLock(pFile);
OSTRACE(("unreadlock = %d\n", res));
- res = osLockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
+ res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, SHARED_FIRST, 0,
+ SHARED_SIZE, 0);
if( res ){
newLocktype = EXCLUSIVE_LOCK;
}else{
@@ -34229,7 +32379,7 @@ static int winLock(sqlite3_file *id, int locktype){
** release it now.
*/
if( gotPendingLock && locktype==SHARED_LOCK ){
- osUnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0);
+ winUnlockFile(&pFile->h, PENDING_BYTE, 0, 1, 0);
}
/* Update the state of the lock has held in the file descriptor then
@@ -34263,9 +32413,9 @@ static int winCheckReservedLock(sqlite3_file *id, int *pResOut){
rc = 1;
OSTRACE(("TEST WR-LOCK %d %d (local)\n", pFile->h, rc));
}else{
- rc = osLockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
+ rc = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, RESERVED_BYTE, 0, 1, 0);
if( rc ){
- osUnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
+ winUnlockFile(&pFile->h, RESERVED_BYTE, 0, 1, 0);
}
rc = !rc;
OSTRACE(("TEST WR-LOCK %d %d (remote)\n", pFile->h, rc));
@@ -34295,7 +32445,7 @@ static int winUnlock(sqlite3_file *id, int locktype){
pFile->locktype, pFile->sharedLockByte));
type = pFile->locktype;
if( type>=EXCLUSIVE_LOCK ){
- osUnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
+ winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
if( locktype==SHARED_LOCK && !getReadLock(pFile) ){
/* This should never happen. We should always be able to
** reacquire the read lock */
@@ -34304,13 +32454,13 @@ static int winUnlock(sqlite3_file *id, int locktype){
}
}
if( type>=RESERVED_LOCK ){
- osUnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
+ winUnlockFile(&pFile->h, RESERVED_BYTE, 0, 1, 0);
}
if( locktype==NO_LOCK && type>=SHARED_LOCK ){
unlockReadLock(pFile);
}
if( type>=PENDING_LOCK ){
- osUnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0);
+ winUnlockFile(&pFile->h, PENDING_BYTE, 0, 1, 0);
}
pFile->locktype = (u8)locktype;
return rc;
@@ -34548,25 +32698,19 @@ static int winShmSystemLock(
int ofst, /* Offset to first byte to be locked/unlocked */
int nByte /* Number of bytes to lock or unlock */
){
- OVERLAPPED ovlp;
- DWORD dwFlags;
int rc = 0; /* Result code form Lock/UnlockFileEx() */
/* Access to the winShmNode object is serialized by the caller */
assert( sqlite3_mutex_held(pFile->mutex) || pFile->nRef==0 );
- /* Initialize the locking parameters */
- dwFlags = LOCKFILE_FAIL_IMMEDIATELY;
- if( lockType == _SHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
-
- memset(&ovlp, 0, sizeof(OVERLAPPED));
- ovlp.Offset = ofst;
-
/* Release/Acquire the system-level lock */
if( lockType==_SHM_UNLCK ){
- rc = osUnlockFileEx(pFile->hFile.h, 0, nByte, 0, &ovlp);
+ rc = winUnlockFile(&pFile->hFile.h, ofst, 0, nByte, 0);
}else{
- rc = osLockFileEx(pFile->hFile.h, dwFlags, 0, nByte, 0, &ovlp);
+ /* Initialize the locking parameters */
+ DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY;
+ if( lockType == _SHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
+ rc = winLockFile(&pFile->hFile.h, dwFlags, ofst, 0, nByte, 0);
}
if( rc!= 0 ){
@@ -35004,18 +33148,30 @@ static int winShmMap(
HANDLE hMap; /* file-mapping handle */
void *pMap = 0; /* Mapped memory region */
- hMap = osCreateFileMapping(pShmNode->hFile.h,
+#if SQLITE_OS_WINRT
+ hMap = osCreateFileMappingFromApp(pShmNode->hFile.h,
+ NULL, PAGE_READWRITE, nByte, NULL
+ );
+#else
+ hMap = osCreateFileMappingW(pShmNode->hFile.h,
NULL, PAGE_READWRITE, 0, nByte, NULL
);
+#endif
OSTRACE(("SHM-MAP pid-%d create region=%d nbyte=%d %s\n",
(int)osGetCurrentProcessId(), pShmNode->nRegion, nByte,
hMap ? "ok" : "failed"));
if( hMap ){
int iOffset = pShmNode->nRegion*szRegion;
int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity;
+#if SQLITE_OS_WINRT
+ pMap = osMapViewOfFileFromApp(hMap, FILE_MAP_WRITE | FILE_MAP_READ,
+ iOffset - iOffsetShift, szRegion + iOffsetShift
+ );
+#else
pMap = osMapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ,
0, iOffset - iOffsetShift, szRegion + iOffsetShift
);
+#endif
OSTRACE(("SHM-MAP pid-%d map region=%d offset=%d size=%d %s\n",
(int)osGetCurrentProcessId(), pShmNode->nRegion, iOffset,
szRegion, pMap ? "ok" : "failed"));
@@ -35101,13 +33257,12 @@ static void *convertUtf8Filename(const char *zFilename){
void *zConverted = 0;
if( isNT() ){
zConverted = utf8ToUnicode(zFilename);
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-*/
-#if SQLITE_OS_WINCE==0
- }else{
+ }
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
zConverted = sqlite3_win32_utf8_to_mbcs(zFilename);
-#endif
}
+#endif
/* caller will handle out of memory */
return zConverted;
}
@@ -35122,6 +33277,7 @@ static int getTempname(int nBuf, char *zBuf){
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789";
size_t i, j;
+ int nTempPath;
char zTempPath[MAX_PATH+2];
/* It's odd to simulate an io-error here, but really this is just
@@ -35130,9 +33286,13 @@ static int getTempname(int nBuf, char *zBuf){
*/
SimulateIOError( return SQLITE_IOERR );
+ memset(zTempPath, 0, MAX_PATH+2);
+
if( sqlite3_temp_directory ){
sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", sqlite3_temp_directory);
- }else if( isNT() ){
+ }
+#if !SQLITE_OS_WINRT
+ else if( isNT() ){
char *zMulti;
WCHAR zWidePath[MAX_PATH];
osGetTempPathW(MAX_PATH-30, zWidePath);
@@ -35143,12 +33303,9 @@ static int getTempname(int nBuf, char *zBuf){
}else{
return SQLITE_IOERR_NOMEM;
}
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-** Since the ANSI 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{
+ }
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
char *zUtf8;
char zMbcsPath[MAX_PATH];
osGetTempPathA(MAX_PATH-30, zMbcsPath);
@@ -35159,21 +33316,25 @@ static int getTempname(int nBuf, char *zBuf){
}else{
return SQLITE_IOERR_NOMEM;
}
-#endif
}
+#endif
+#endif
/* Check that the output buffer is large enough for the temporary file
** name. If it is not, return SQLITE_ERROR.
*/
- if( (sqlite3Strlen30(zTempPath) + sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX) + 18) >= nBuf ){
+ nTempPath = sqlite3Strlen30(zTempPath);
+
+ if( (nTempPath + sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX) + 18) >= nBuf ){
return SQLITE_ERROR;
}
- for(i=sqlite3Strlen30(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){}
+ for(i=nTempPath; i>0 && zTempPath[i-1]=='\\'; i--){}
zTempPath[i] = 0;
- sqlite3_snprintf(nBuf-18, zBuf,
- "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath);
+ sqlite3_snprintf(nBuf-18, zBuf, (nTempPath > 0) ?
+ "%s\\"SQLITE_TEMP_FILE_PREFIX : SQLITE_TEMP_FILE_PREFIX,
+ zTempPath);
j = sqlite3Strlen30(zBuf);
sqlite3_randomness(15, &zBuf[j]);
for(i=0; i<15; i++, j++){
@@ -35294,6 +33455,13 @@ static int winOpen(
assert( id!=0 );
UNUSED_PARAMETER(pVfs);
+#if SQLITE_OS_WINRT
+ if( !sqlite3_temp_directory ){
+ sqlite3_log(SQLITE_ERROR,
+ "sqlite3_temp_directory variable should be set for WinRT");
+ }
+#endif
+
pFile->h = INVALID_HANDLE_VALUE;
/* If the second argument to this function is NULL, generate a
@@ -35369,6 +33537,24 @@ static int winOpen(
#endif
if( isNT() ){
+#if SQLITE_OS_WINRT
+ CREATEFILE2_EXTENDED_PARAMETERS extendedParameters;
+ extendedParameters.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
+ extendedParameters.dwFileAttributes =
+ dwFlagsAndAttributes & FILE_ATTRIBUTE_MASK;
+ extendedParameters.dwFileFlags = dwFlagsAndAttributes & FILE_FLAG_MASK;
+ extendedParameters.dwSecurityQosFlags = SECURITY_ANONYMOUS;
+ extendedParameters.lpSecurityAttributes = NULL;
+ extendedParameters.hTemplateFile = NULL;
+ while( (h = osCreateFile2((LPCWSTR)zConverted,
+ dwDesiredAccess,
+ dwShareMode,
+ dwCreationDisposition,
+ &extendedParameters))==INVALID_HANDLE_VALUE &&
+ retryIoerr(&cnt, &lastErrno) ){
+ /* Noop */
+ }
+#else
while( (h = osCreateFileW((LPCWSTR)zConverted,
dwDesiredAccess,
dwShareMode, NULL,
@@ -35378,8 +33564,10 @@ static int winOpen(
retryIoerr(&cnt, &lastErrno) ){
/* Noop */
}
-#if SQLITE_OS_WINCE==0
- }else{
+#endif
+ }
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
while( (h = osCreateFileA((LPCSTR)zConverted,
dwDesiredAccess,
dwShareMode, NULL,
@@ -35389,9 +33577,8 @@ static int winOpen(
retryIoerr(&cnt, &lastErrno) ){
/* Noop */
}
-#endif
}
-
+#endif
logIoerr(cnt);
OSTRACE(("OPEN %d %s 0x%lx %s\n",
@@ -35423,7 +33610,9 @@ static int winOpen(
pFile->h = h;
pFile->lastErrno = NO_ERROR;
pFile->pVfs = pVfs;
+#ifndef SQLITE_OMIT_WAL
pFile->pShm = 0;
+#endif
pFile->zPath = zName;
if( sqlite3_uri_boolean(zName, "psow", SQLITE_POWERSAFE_OVERWRITE) ){
pFile->ctrlFlags |= WINFILE_PSOW;
@@ -35481,7 +33670,19 @@ static int winDelete(
}
if( isNT() ){
do {
+#if SQLITE_OS_WINRT
+ WIN32_FILE_ATTRIBUTE_DATA sAttrData;
+ memset(&sAttrData, 0, sizeof(sAttrData));
+ if ( osGetFileAttributesExW(zConverted, GetFileExInfoStandard,
+ &sAttrData) ){
+ attr = sAttrData.dwFileAttributes;
+ }else{
+ rc = SQLITE_OK; /* Already gone? */
+ break;
+ }
+#else
attr = osGetFileAttributesW(zConverted);
+#endif
if ( attr==INVALID_FILE_ATTRIBUTES ){
rc = SQLITE_OK; /* Already gone? */
break;
@@ -35499,12 +33700,9 @@ static int winDelete(
break;
}
} while(1);
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-** Since the ANSI 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{
+ }
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
do {
attr = osGetFileAttributesA(zConverted);
if ( attr==INVALID_FILE_ATTRIBUTES ){
@@ -35524,8 +33722,8 @@ static int winDelete(
break;
}
} while(1);
-#endif
}
+#endif
if( rc ){
rc = winLogError(SQLITE_IOERR_DELETE, lastErrno,
"winDelete", zFilename);
@@ -35577,7 +33775,7 @@ static int winAccess(
}
}else{
logIoerr(cnt);
- if( lastErrno!=ERROR_FILE_NOT_FOUND ){
+ if( lastErrno!=ERROR_FILE_NOT_FOUND && lastErrno!=ERROR_PATH_NOT_FOUND ){
winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess", zFilename);
sqlite3_free(zConverted);
return SQLITE_IOERR_ACCESS;
@@ -35585,15 +33783,12 @@ static int winAccess(
attr = INVALID_FILE_ATTRIBUTES;
}
}
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-** Since the ANSI 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{
+ }
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
attr = osGetFileAttributesA((char*)zConverted);
-#endif
}
+#endif
sqlite3_free(zConverted);
switch( flags ){
case SQLITE_ACCESS_READ:
@@ -35613,6 +33808,43 @@ static int winAccess(
/*
+** Returns non-zero if the specified path name should be used verbatim. If
+** non-zero is returned from this function, the calling function must simply
+** use the provided path name verbatim -OR- resolve it into a full path name
+** using the GetFullPathName Win32 API function (if available).
+*/
+static BOOL winIsVerbatimPathname(
+ const char *zPathname
+){
+ /*
+ ** If the path name starts with a forward slash or a backslash, it is either
+ ** a legal UNC name, a volume relative path, or an absolute path name in the
+ ** "Unix" format on Windows. There is no easy way to differentiate between
+ ** the final two cases; therefore, we return the safer return value of TRUE
+ ** so that callers of this function will simply use it verbatim.
+ */
+ if ( zPathname[0]=='/' || zPathname[0]=='\\' ){
+ return TRUE;
+ }
+
+ /*
+ ** If the path name starts with a letter and a colon it is either a volume
+ ** relative path or an absolute path. Callers of this function must not
+ ** attempt to treat it as a relative path name (i.e. they should simply use
+ ** it verbatim).
+ */
+ if ( sqlite3Isalpha(zPathname[0]) && zPathname[1]==':' ){
+ return TRUE;
+ }
+
+ /*
+ ** If we get to this point, the path name should almost certainly be a purely
+ ** relative one (i.e. not a UNC name, not absolute, and not volume relative).
+ */
+ return FALSE;
+}
+
+/*
** Turn a relative pathname into a full pathname. Write the full
** pathname into zOut[]. zOut[] will be at least pVfs->mxPathname
** bytes in size.
@@ -35627,19 +33859,51 @@ static int winFullPathname(
#if defined(__CYGWIN__)
SimulateIOError( return SQLITE_ERROR );
UNUSED_PARAMETER(nFull);
- cygwin_conv_to_full_win32_path(zRelative, zFull);
+ assert( pVfs->mxPathname>=MAX_PATH );
+ assert( nFull>=pVfs->mxPathname );
+ if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
+ /*
+ ** NOTE: We are dealing with a relative path name and the data
+ ** directory has been set. Therefore, use it as the basis
+ ** for converting the relative path name to an absolute
+ ** one by prepending the data directory and a slash.
+ */
+ char zOut[MAX_PATH+1];
+ memset(zOut, 0, MAX_PATH+1);
+ cygwin_conv_to_win32_path(zRelative, zOut); /* POSIX to Win32 */
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s\\%s",
+ sqlite3_data_directory, zOut);
+ }else{
+ /*
+ ** NOTE: The Cygwin docs state that the maximum length needed
+ ** for the buffer passed to cygwin_conv_to_full_win32_path
+ ** is MAX_PATH.
+ */
+ cygwin_conv_to_full_win32_path(zRelative, zFull);
+ }
return SQLITE_OK;
#endif
-#if SQLITE_OS_WINCE
+#if (SQLITE_OS_WINCE || SQLITE_OS_WINRT) && !defined(__CYGWIN__)
SimulateIOError( return SQLITE_ERROR );
- UNUSED_PARAMETER(nFull);
/* WinCE has no concept of a relative pathname, or so I am told. */
- sqlite3_snprintf(pVfs->mxPathname, zFull, "%s", zRelative);
+ /* WinRT has no way to convert a relative path to an absolute one. */
+ if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
+ /*
+ ** NOTE: We are dealing with a relative path name and the data
+ ** directory has been set. Therefore, use it as the basis
+ ** for converting the relative path name to an absolute
+ ** one by prepending the data directory and a backslash.
+ */
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s\\%s",
+ sqlite3_data_directory, zRelative);
+ }else{
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zRelative);
+ }
return SQLITE_OK;
#endif
-#if !SQLITE_OS_WINCE && !defined(__CYGWIN__)
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__)
int nByte;
void *zConverted;
char *zOut;
@@ -35657,7 +33921,17 @@ static int winFullPathname(
** current working directory has been unlinked.
*/
SimulateIOError( return SQLITE_ERROR );
- UNUSED_PARAMETER(nFull);
+ if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
+ /*
+ ** NOTE: We are dealing with a relative path name and the data
+ ** directory has been set. Therefore, use it as the basis
+ ** for converting the relative path name to an absolute
+ ** one by prepending the data directory and a backslash.
+ */
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s\\%s",
+ sqlite3_data_directory, zRelative);
+ return SQLITE_OK;
+ }
zConverted = convertUtf8Filename(zRelative);
if( zConverted==0 ){
return SQLITE_IOERR_NOMEM;
@@ -35674,12 +33948,9 @@ static int winFullPathname(
sqlite3_free(zConverted);
zOut = unicodeToUtf8(zTemp);
sqlite3_free(zTemp);
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-** Since the ANSI 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{
+ }
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
char *zTemp;
nByte = osGetFullPathNameA((char*)zConverted, 0, 0, 0) + 3;
zTemp = sqlite3_malloc( nByte*sizeof(zTemp[0]) );
@@ -35691,10 +33962,10 @@ static int winFullPathname(
sqlite3_free(zConverted);
zOut = sqlite3_win32_mbcs_to_utf8(zTemp);
sqlite3_free(zTemp);
-#endif
}
+#endif
if( zOut ){
- sqlite3_snprintf(pVfs->mxPathname, zFull, "%s", zOut);
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut);
sqlite3_free(zOut);
return SQLITE_OK;
}else{
@@ -35720,16 +33991,17 @@ static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){
return 0;
}
if( isNT() ){
+#if SQLITE_OS_WINRT
+ h = osLoadPackagedLibrary((LPCWSTR)zConverted, 0);
+#else
h = osLoadLibraryW((LPCWSTR)zConverted);
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-** Since the ANSI 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 = osLoadLibraryA((char*)zConverted);
#endif
}
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
+ h = osLoadLibraryA((char*)zConverted);
+ }
+#endif
sqlite3_free(zConverted);
return (void*)h;
}
@@ -35774,11 +34046,19 @@ static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
memcpy(&zBuf[n], &pid, sizeof(pid));
n += sizeof(pid);
}
+#if SQLITE_OS_WINRT
+ if( sizeof(ULONGLONG)<=nBuf-n ){
+ ULONGLONG cnt = osGetTickCount64();
+ memcpy(&zBuf[n], &cnt, sizeof(cnt));
+ n += sizeof(cnt);
+ }
+#else
if( sizeof(DWORD)<=nBuf-n ){
DWORD cnt = osGetTickCount();
memcpy(&zBuf[n], &cnt, sizeof(cnt));
n += sizeof(cnt);
}
+#endif
if( sizeof(LARGE_INTEGER)<=nBuf-n ){
LARGE_INTEGER i;
osQueryPerformanceCounter(&i);
@@ -35794,7 +34074,7 @@ static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
** Sleep for a little while. Return the amount of time slept.
*/
static int winSleep(sqlite3_vfs *pVfs, int microsec){
- osSleep((microsec+999)/1000);
+ sqlite3_win32_sleep((microsec+999)/1000);
UNUSED_PARAMETER(pVfs);
return ((microsec+999)/1000)*1000;
}
@@ -35936,12 +34216,16 @@ SQLITE_API int sqlite3_os_init(void){
/* Double-check that the aSyscall[] array has been constructed
** correctly. See ticket [bb3a86e890c8e96ab] */
- assert( ArraySize(aSyscall)==60 );
+ assert( ArraySize(aSyscall)==73 );
#ifndef SQLITE_OMIT_WAL
/* get memory map allocation granularity */
memset(&winSysInfo, 0, sizeof(SYSTEM_INFO));
+#if SQLITE_OS_WINRT
+ osGetNativeSystemInfo(&winSysInfo);
+#else
osGetSystemInfo(&winSysInfo);
+#endif
assert(winSysInfo.dwAllocationGranularity > 0);
#endif
@@ -35950,6 +34234,12 @@ SQLITE_API int sqlite3_os_init(void){
}
SQLITE_API int sqlite3_os_end(void){
+#if SQLITE_OS_WINRT
+ if( sleepObj != NULL ){
+ osCloseHandle(sleepObj);
+ sleepObj = NULL;
+ }
+#endif
return SQLITE_OK;
}
@@ -36298,10 +34588,9 @@ SQLITE_PRIVATE int sqlite3BitvecBuiltinTest(int sz, int *aOp){
/* Allocate the Bitvec to be tested and a linear array of
** bits to act as the reference */
pBitvec = sqlite3BitvecCreate( sz );
- pV = sqlite3_malloc( (sz+7)/8 + 1 );
+ pV = sqlite3MallocZero( (sz+7)/8 + 1 );
pTmpSpace = sqlite3_malloc(BITVEC_SZ);
if( pBitvec==0 || pV==0 || pTmpSpace==0 ) goto bitvec_end;
- memset(pV, 0, (sz+7)/8 + 1);
/* NULL pBitvec tests */
sqlite3BitvecSet(0, 1);
@@ -37201,12 +35490,14 @@ static void *pcache1Alloc(int nByte){
** it from sqlite3Malloc instead.
*/
p = sqlite3Malloc(nByte);
+#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS
if( p ){
int sz = sqlite3MallocSize(p);
sqlite3_mutex_enter(pcache1.mutex);
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz);
sqlite3_mutex_leave(pcache1.mutex);
}
+#endif
sqlite3MemdebugSetType(p, MEMTYPE_PCACHE);
}
return p;
@@ -37233,9 +35524,11 @@ static int pcache1Free(void *p){
assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
nFreed = sqlite3MallocSize(p);
+#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS
sqlite3_mutex_enter(pcache1.mutex);
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -nFreed);
sqlite3_mutex_leave(pcache1.mutex);
+#endif
sqlite3_free(p);
}
return nFreed;
@@ -37381,11 +35674,10 @@ static int pcache1ResizeHash(PCache1 *p){
pcache1LeaveMutex(p->pGroup);
if( p->nHash ){ sqlite3BeginBenignMalloc(); }
- apNew = (PgHdr1 **)sqlite3_malloc(sizeof(PgHdr1 *)*nNew);
+ apNew = (PgHdr1 **)sqlite3MallocZero(sizeof(PgHdr1 *)*nNew);
if( p->nHash ){ sqlite3EndBenignMalloc(); }
pcache1EnterMutex(p->pGroup);
if( apNew ){
- memset(apNew, 0, sizeof(PgHdr1 *)*nNew);
for(i=0; i<p->nHash; i++){
PgHdr1 *pPage;
PgHdr1 *pNext = p->apHash[i];
@@ -37569,9 +35861,8 @@ static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){
assert( szExtra < 300 );
sz = sizeof(PCache1) + sizeof(PGroup)*separateCache;
- pCache = (PCache1 *)sqlite3_malloc(sz);
+ pCache = (PCache1 *)sqlite3MallocZero(sz);
if( pCache ){
- memset(pCache, 0, sz);
if( separateCache ){
pGroup = (PGroup*)&pCache[1];
pGroup->mxPinned = 10;
@@ -38449,7 +36740,7 @@ SQLITE_PRIVATE int sqlite3RowSetNext(RowSet *p, i64 *pRowid){
}
/*
-** Check to see if element iRowid was inserted into the the rowset as
+** Check to see if element iRowid was inserted into the rowset as
** part of any insert batch prior to iBatch. Return 1 or 0.
**
** If this is the first test of a new batch and if there exist entires
@@ -38733,7 +37024,7 @@ SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal);
**
** Definition: Two databases (or the same database at two points it time)
** are said to be "logically equivalent" if they give the same answer to
-** all queries. Note in particular the the content of freelist leaf
+** all queries. Note in particular the content of freelist leaf
** pages can be changed arbitarily without effecting the logical equivalence
** of the database.
**
@@ -42507,7 +40798,7 @@ SQLITE_PRIVATE void sqlite3PagerRef(DbPage *pPg){
**
** If the Pager.noSync flag is set, then this function is a no-op.
** Otherwise, the actions required depend on the journal-mode and the
-** device characteristics of the the file-system, as follows:
+** device characteristics of the file-system, as follows:
**
** * If the journal file is an in-memory journal file, no action need
** be taken.
@@ -43018,7 +41309,12 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
#ifndef SQLITE_OMIT_MEMORYDB
if( flags & PAGER_MEMORY ){
memDb = 1;
- zFilename = 0;
+ if( zFilename && zFilename[0] ){
+ zPathname = sqlite3DbStrDup(0, zFilename);
+ if( zPathname==0 ) return SQLITE_NOMEM;
+ nPathname = sqlite3Strlen30(zPathname);
+ zFilename = 0;
+ }
}
#endif
@@ -43029,7 +41325,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
if( zFilename && zFilename[0] ){
const char *z;
nPathname = pVfs->mxPathname+1;
- zPathname = sqlite3Malloc(nPathname*2);
+ zPathname = sqlite3DbMallocRaw(0, nPathname*2);
if( zPathname==0 ){
return SQLITE_NOMEM;
}
@@ -43053,7 +41349,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
rc = SQLITE_CANTOPEN_BKPT;
}
if( rc!=SQLITE_OK ){
- sqlite3_free(zPathname);
+ sqlite3DbFree(0, zPathname);
return rc;
}
}
@@ -43083,7 +41379,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
);
assert( EIGHT_BYTE_ALIGNMENT(SQLITE_INT_TO_PTR(journalFileSize)) );
if( !pPtr ){
- sqlite3_free(zPathname);
+ sqlite3DbFree(0, zPathname);
return SQLITE_NOMEM;
}
pPager = (Pager*)(pPtr);
@@ -43099,7 +41395,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
assert( nPathname>0 );
pPager->zJournal = (char*)(pPtr += nPathname + 1 + nUri);
memcpy(pPager->zFilename, zPathname, nPathname);
- memcpy(&pPager->zFilename[nPathname+1], zUri, nUri);
+ if( nUri ) memcpy(&pPager->zFilename[nPathname+1], zUri, nUri);
memcpy(pPager->zJournal, zPathname, nPathname);
memcpy(&pPager->zJournal[nPathname], "-journal\000", 8+1);
sqlite3FileSuffix3(pPager->zFilename, pPager->zJournal);
@@ -43109,7 +41405,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
memcpy(&pPager->zWal[nPathname], "-wal\000", 4+1);
sqlite3FileSuffix3(pPager->zFilename, pPager->zWal);
#endif
- sqlite3_free(zPathname);
+ sqlite3DbFree(0, zPathname);
}
pPager->pVfs = pVfs;
pPager->vfsFlags = vfsFlags;
@@ -44954,9 +43250,16 @@ SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
/*
** Return the full pathname of the database file.
+**
+** Except, if the pager is in-memory only, then return an empty string if
+** nullIfMemDb is true. This routine is called with nullIfMemDb==1 when
+** used to report the filename to the user, for compatibility with legacy
+** behavior. But when the Btree needs to know the filename for matching to
+** shared cache, it uses nullIfMemDb==0 so that in-memory databases can
+** participate in shared-cache.
*/
-SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager *pPager){
- return pPager->zFilename;
+SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager *pPager, int nullIfMemDb){
+ return (nullIfMemDb && pPager->memDb) ? "" : pPager->zFilename;
}
/*
@@ -45726,14 +44029,15 @@ SQLITE_PRIVATE void *sqlite3PagerCodec(PgHdr *pPg){
** byte order of the host computer.
**
** The purpose of the wal-index is to answer this question quickly: Given
-** a page number P, return the index of the last frame for page P in the WAL,
-** or return NULL if there are no frames for page P in the WAL.
+** a page number P and a maximum frame index M, return the index of the
+** last frame in the wal before frame M for page P in the WAL, or return
+** NULL if there are no frames for page P in the WAL prior to M.
**
** The wal-index consists of a header region, followed by an one or
** more index blocks.
**
** The wal-index header contains the total number of frames within the WAL
-** in the the mxFrame field.
+** in the mxFrame field.
**
** Each index block except for the first contains information on
** HASHTABLE_NPAGE frames. The first index block contains information on
@@ -46781,6 +45085,7 @@ finished:
pInfo->nBackfill = 0;
pInfo->aReadMark[0] = 0;
for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
+ if( pWal->hdr.mxFrame ) pInfo->aReadMark[1] = pWal->hdr.mxFrame;
/* If more than one frame was recovered from the log file, report an
** event via sqlite3_log(). This is to help with identifying performance
@@ -47281,7 +45586,7 @@ static int walCheckpoint(
assert( y<=pWal->hdr.mxFrame );
rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1);
if( rc==SQLITE_OK ){
- pInfo->aReadMark[i] = READMARK_NOT_USED;
+ pInfo->aReadMark[i] = (i==1 ? mxSafeFrame : READMARK_NOT_USED);
walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
}else if( rc==SQLITE_BUSY ){
mxSafeFrame = y;
@@ -48194,7 +46499,8 @@ static int walRestartLog(Wal *pWal){
aSalt[1] = salt1;
walIndexWriteHdr(pWal);
pInfo->nBackfill = 0;
- for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
+ pInfo->aReadMark[1] = 0;
+ for(i=2; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
assert( pInfo->aReadMark[0]==0 );
walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
}else if( rc!=SQLITE_BUSY ){
@@ -49197,6 +47503,7 @@ struct BtCursor {
#ifndef SQLITE_OMIT_INCRBLOB
u8 isIncrblobHandle; /* True if this cursor is an incr. io handle */
#endif
+ u8 hints; /* As configured by CursorSetHints() */
i16 iPage; /* Index of current page in apPage */
u16 aiIdx[BTCURSOR_MAX_DEPTH]; /* Current index in apPage[i] */
MemPage *apPage[BTCURSOR_MAX_DEPTH]; /* Pages from root to current page */
@@ -51081,7 +49388,7 @@ static int btreeInitPage(MemPage *pPage){
size = get2byte(&data[pc+2]);
if( (next>0 && next<=pc+size+3) || pc+size>usableSize ){
/* Free blocks must be in ascending order. And the last byte of
- ** the free-block must lie on the database page. */
+ ** the free-block must lie on the database page. */
return SQLITE_CORRUPT_BKPT;
}
nFree = nFree + size;
@@ -51341,7 +49648,8 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
const int isMemdb = 0;
#else
const int isMemdb = (zFilename && strcmp(zFilename, ":memory:")==0)
- || (isTempDb && sqlite3TempInMemory(db));
+ || (isTempDb && sqlite3TempInMemory(db))
+ || (vfsFlags & SQLITE_OPEN_MEMORY)!=0;
#endif
assert( db!=0 );
@@ -51377,7 +49685,7 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
** If this Btree is a candidate for shared cache, try to find an
** existing BtShared object that we can share with
*/
- if( isMemdb==0 && isTempDb==0 ){
+ if( isTempDb==0 && (isMemdb==0 || (vfsFlags&SQLITE_OPEN_URI)!=0) ){
if( vfsFlags & SQLITE_OPEN_SHAREDCACHE ){
int nFullPathname = pVfs->mxPathname+1;
char *zFullPathname = sqlite3Malloc(nFullPathname);
@@ -51387,11 +49695,16 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
sqlite3_free(p);
return SQLITE_NOMEM;
}
- rc = sqlite3OsFullPathname(pVfs, zFilename, nFullPathname, zFullPathname);
- if( rc ){
- sqlite3_free(zFullPathname);
- sqlite3_free(p);
- return rc;
+ if( isMemdb ){
+ memcpy(zFullPathname, zFilename, sqlite3Strlen30(zFilename)+1);
+ }else{
+ rc = sqlite3OsFullPathname(pVfs, zFilename,
+ nFullPathname, zFullPathname);
+ if( rc ){
+ sqlite3_free(zFullPathname);
+ sqlite3_free(p);
+ return rc;
+ }
}
#if SQLITE_THREADSAFE
mutexOpen = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_OPEN);
@@ -51401,7 +49714,7 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
#endif
for(pBt=GLOBAL(BtShared*,sqlite3SharedCacheList); pBt; pBt=pBt->pNext){
assert( pBt->nRef>0 );
- if( 0==strcmp(zFullPathname, sqlite3PagerFilename(pBt->pPager))
+ if( 0==strcmp(zFullPathname, sqlite3PagerFilename(pBt->pPager, 0))
&& sqlite3PagerVfs(pBt->pPager)==pVfs ){
int iDb;
for(iDb=db->nDb-1; iDb>=0; iDb--){
@@ -52249,7 +50562,7 @@ SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
pBt->nTransaction++;
#ifndef SQLITE_OMIT_SHARED_CACHE
if( p->sharable ){
- assert( p->lock.pBtree==p && p->lock.iTable==1 );
+ assert( p->lock.pBtree==p && p->lock.iTable==1 );
p->lock.eLock = READ_LOCK;
p->lock.pNext = pBt->pLock;
pBt->pLock = &p->lock;
@@ -55536,11 +53849,15 @@ static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){
** If aOvflSpace is set to a null pointer, this function returns
** SQLITE_NOMEM.
*/
+#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_M_ARM)
+#pragma optimize("", off)
+#endif
static int balance_nonroot(
MemPage *pParent, /* Parent page of siblings being balanced */
int iParentIdx, /* Index of "the page" in pParent */
u8 *aOvflSpace, /* page-size bytes of space for parent ovfl */
- int isRoot /* True if pParent is a root-page */
+ int isRoot, /* True if pParent is a root-page */
+ int bBulk /* True if this call is part of a bulk load */
){
BtShared *pBt; /* The whole database */
int nCell = 0; /* Number of cells in apCell[] */
@@ -55604,18 +53921,19 @@ static int balance_nonroot(
i = pParent->nOverflow + pParent->nCell;
if( i<2 ){
nxDiv = 0;
- nOld = i+1;
}else{
- nOld = 3;
+ assert( bBulk==0 || bBulk==1 );
if( iParentIdx==0 ){
nxDiv = 0;
}else if( iParentIdx==i ){
- nxDiv = i-2;
+ nxDiv = i-2+bBulk;
}else{
+ assert( bBulk==0 );
nxDiv = iParentIdx-1;
}
- i = 2;
+ i = 2-bBulk;
}
+ nOld = i+1;
if( (i+nxDiv-pParent->nOverflow)==pParent->nCell ){
pRight = &pParent->aData[pParent->hdrOffset+8];
}else{
@@ -55695,7 +54013,7 @@ static int balance_nonroot(
/*
** Load pointers to all cells on sibling pages and the divider cells
** into the local apCell[] array. Make copies of the divider cells
- ** into space obtained from aSpace1[] and remove the the divider Cells
+ ** into space obtained from aSpace1[] and remove the divider cells
** from pParent.
**
** If the siblings are on leaf pages, then the child pointers of the
@@ -55824,7 +54142,9 @@ static int balance_nonroot(
d = r + 1 - leafData;
assert( d<nMaxCells );
assert( r<nMaxCells );
- while( szRight==0 || szRight+szCell[d]+2<=szLeft-(szCell[r]+2) ){
+ while( szRight==0
+ || (!bBulk && szRight+szCell[d]+2<=szLeft-(szCell[r]+2))
+ ){
szRight += szCell[d] + 2;
szLeft -= szCell[r] + 2;
cntNew[i-1]--;
@@ -55871,7 +54191,7 @@ static int balance_nonroot(
if( rc ) goto balance_cleanup;
}else{
assert( i>0 );
- rc = allocateBtreePage(pBt, &pNew, &pgno, pgno, 0);
+ rc = allocateBtreePage(pBt, &pNew, &pgno, (bBulk ? 1 : pgno), 0);
if( rc ) goto balance_cleanup;
apNew[i] = pNew;
nNew++;
@@ -56083,6 +54403,7 @@ static int balance_nonroot(
** sibling page j. If the siblings are not leaf pages of an
** intkey b-tree, then cell i was a divider cell. */
assert( j+1 < ArraySize(apCopy) );
+ assert( j+1 < nOld );
pOld = apCopy[++j];
iNextOld = i + !leafData + pOld->nCell + pOld->nOverflow;
if( pOld->nOverflow ){
@@ -56161,6 +54482,9 @@ balance_cleanup:
return rc;
}
+#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_M_ARM)
+#pragma optimize("", on)
+#endif
/*
@@ -56321,7 +54645,7 @@ static int balance(BtCursor *pCur){
** pSpace buffer passed to the latter call to balance_nonroot().
*/
u8 *pSpace = sqlite3PageMalloc(pCur->pBt->pageSize);
- rc = balance_nonroot(pParent, iIdx, pSpace, iPage==1);
+ rc = balance_nonroot(pParent, iIdx, pSpace, iPage==1, pCur->hints);
if( pFree ){
/* If pFree is not NULL, it points to the pSpace buffer used
** by a previous call to balance_nonroot(). Its contents are
@@ -57666,14 +55990,15 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
/*
-** Return the full pathname of the underlying database file.
+** Return the full pathname of the underlying database file. Return
+** an empty string if the database is in-memory or a TEMP database.
**
** The pager filename is invariant as long as the pager is
** open so it is safe to access without the BtShared mutex.
*/
SQLITE_PRIVATE const char *sqlite3BtreeGetFilename(Btree *p){
assert( p->pBt->pPager!=0 );
- return sqlite3PagerFilename(p->pBt->pPager);
+ return sqlite3PagerFilename(p->pBt->pPager, 1);
}
/*
@@ -57908,6 +56233,15 @@ SQLITE_PRIVATE int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){
return rc;
}
+/*
+** set the mask of hint flags for cursor pCsr. Currently the only valid
+** values are 0 and BTREE_BULKLOAD.
+*/
+SQLITE_PRIVATE void sqlite3BtreeCursorHints(BtCursor *pCsr, unsigned int mask){
+ assert( mask==BTREE_BULKLOAD || mask==0 );
+ pCsr->hints = mask;
+}
+
/************** End of btree.c ***********************************************/
/************** Begin file backup.c ******************************************/
/*
@@ -58074,7 +56408,7 @@ SQLITE_API sqlite3_backup *sqlite3_backup_init(
** EVIDENCE-OF: R-64852-21591 The sqlite3_backup object is created by a
** call to sqlite3_backup_init() and is destroyed by a call to
** sqlite3_backup_finish(). */
- p = (sqlite3_backup *)sqlite3_malloc(sizeof(sqlite3_backup));
+ p = (sqlite3_backup *)sqlite3MallocZero(sizeof(sqlite3_backup));
if( !p ){
sqlite3Error(pDestDb, SQLITE_NOMEM, 0);
}
@@ -58082,7 +56416,6 @@ SQLITE_API sqlite3_backup *sqlite3_backup_init(
/* If the allocation succeeded, populate the new object. */
if( p ){
- memset(p, 0, sizeof(sqlite3_backup));
p->pSrc = findBtree(pDestDb, pSrcDb, zSrcDb);
p->pDest = findBtree(pDestDb, pDestDb, zDestDb);
p->pDestDb = pDestDb;
@@ -58324,7 +56657,7 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
rc = sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1);
if( rc==SQLITE_OK ){
if( p->pDestDb ){
- sqlite3ResetInternalSchema(p->pDestDb, -1);
+ sqlite3ResetAllSchemasOfConnection(p->pDestDb);
}
if( destMode==PAGER_JOURNALMODE_WAL ){
rc = sqlite3BtreeSetVersion(p->pDest, 2);
@@ -58453,14 +56786,14 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
*/
SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p){
sqlite3_backup **pp; /* Ptr to head of pagers backup list */
- MUTEX_LOGIC( sqlite3_mutex *mutex; ) /* Mutex to protect source database */
+ sqlite3 *pSrcDb; /* Source database connection */
int rc; /* Value to return */
/* Enter the mutexes */
if( p==0 ) return SQLITE_OK;
- sqlite3_mutex_enter(p->pSrcDb->mutex);
+ pSrcDb = p->pSrcDb;
+ sqlite3_mutex_enter(pSrcDb->mutex);
sqlite3BtreeEnter(p->pSrc);
- MUTEX_LOGIC( mutex = p->pSrcDb->mutex; )
if( p->pDestDb ){
sqlite3_mutex_enter(p->pDestDb->mutex);
}
@@ -58486,7 +56819,7 @@ SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p){
/* Exit the mutexes and free the backup context structure. */
if( p->pDestDb ){
- sqlite3_mutex_leave(p->pDestDb->mutex);
+ sqlite3LeaveMutexAndCloseZombie(p->pDestDb);
}
sqlite3BtreeLeave(p->pSrc);
if( p->pDestDb ){
@@ -58495,7 +56828,7 @@ SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p){
** sqlite3_backup_finish(). */
sqlite3_free(p);
}
- sqlite3_mutex_leave(mutex);
+ sqlite3LeaveMutexAndCloseZombie(pSrcDb);
return rc;
}
@@ -60564,7 +58897,7 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int
#ifndef NDEBUG
/*
-** Change the comment on the the most recently coded instruction. Or
+** Change the comment on the most recently coded instruction. Or
** insert a No-op and add the comment to that new instruction. This
** makes the code easier to read during debugging. None of this happens
** in a production build.
@@ -62259,6 +60592,7 @@ SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe *p){
if( NEVER(p==0) ) return;
db = p->db;
+ assert( sqlite3_mutex_held(db->mutex) );
if( p->pPrev ){
p->pPrev->pNext = p->pNext;
}else{
@@ -63098,17 +61432,11 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt){
}else{
Vdbe *v = (Vdbe*)pStmt;
sqlite3 *db = v->db;
-#if SQLITE_THREADSAFE
- sqlite3_mutex *mutex;
-#endif
if( vdbeSafety(v) ) return SQLITE_MISUSE_BKPT;
-#if SQLITE_THREADSAFE
- mutex = v->db->mutex;
-#endif
- sqlite3_mutex_enter(mutex);
+ sqlite3_mutex_enter(db->mutex);
rc = sqlite3VdbeFinalize(v);
rc = sqlite3ApiExit(db, rc);
- sqlite3_mutex_leave(mutex);
+ sqlite3LeaveMutexAndCloseZombie(db);
}
return rc;
}
@@ -64511,9 +62839,8 @@ SQLITE_PRIVATE void sqlite3ExplainBegin(Vdbe *pVdbe){
if( pVdbe ){
Explain *p;
sqlite3BeginBenignMalloc();
- p = sqlite3_malloc( sizeof(Explain) );
+ p = (Explain *)sqlite3MallocZero( sizeof(Explain) );
if( p ){
- memset(p, 0, sizeof(*p));
p->pVdbe = pVdbe;
sqlite3_free(pVdbe->pExplain);
pVdbe->pExplain = p;
@@ -65782,7 +64109,7 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
}
#endif
- /* On any opcode with the "out2-prerelase" tag, free any
+ /* On any opcode with the "out2-prerelease" tag, free any
** external allocations out of mem[p2] and set mem[p2] to be
** an undefined integer. Opcodes will either fill in the integer
** value or convert mem[p2] to a different type.
@@ -67909,7 +66236,7 @@ case OP_Savepoint: {
}
if( u.ar.p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){
sqlite3ExpirePreparedStatements(db);
- sqlite3ResetInternalSchema(db, -1);
+ sqlite3ResetAllSchemasOfConnection(db);
db->flags = (db->flags | SQLITE_InternChanges);
}
}
@@ -68223,7 +66550,7 @@ case OP_VerifyCookie: {
** a v-table method.
*/
if( db->aDb[pOp->p1].pSchema->schema_cookie!=u.aw.iMeta ){
- sqlite3ResetInternalSchema(db, pOp->p1);
+ sqlite3ResetOneSchema(db, pOp->p1);
}
p->expired = 1;
@@ -68294,6 +66621,9 @@ case OP_OpenWrite: {
Db *pDb;
#endif /* local variables moved into u.ax */
+ assert( (pOp->p5&(OPFLAG_P2ISREG|OPFLAG_BULKCSR))==pOp->p5 );
+ assert( pOp->opcode==OP_OpenWrite || pOp->p5==0 );
+
if( p->expired ){
rc = SQLITE_ABORT;
break;
@@ -68317,7 +66647,7 @@ case OP_OpenWrite: {
}else{
u.ax.wrFlag = 0;
}
- if( pOp->p5 ){
+ if( pOp->p5 & OPFLAG_P2ISREG ){
assert( u.ax.p2>0 );
assert( u.ax.p2<=p->nMem );
pIn2 = &aMem[u.ax.p2];
@@ -68348,6 +66678,8 @@ case OP_OpenWrite: {
u.ax.pCur->isOrdered = 1;
rc = sqlite3BtreeCursor(u.ax.pX, u.ax.p2, u.ax.wrFlag, u.ax.pKeyInfo, u.ax.pCur->pCursor);
u.ax.pCur->pKeyInfo = u.ax.pKeyInfo;
+ assert( OPFLAG_BULKCSR==BTREE_BULKLOAD );
+ sqlite3BtreeCursorHints(u.ax.pCur->pCursor, (pOp->p5 & OPFLAG_BULKCSR));
/* Since it performs no memory allocation or IO, the only value that
** sqlite3BtreeCursor() may return is SQLITE_OK. */
@@ -69416,7 +67748,6 @@ case OP_RowData: {
assert( u.bl.pC!=0 );
assert( u.bl.pC->nullRow==0 );
assert( u.bl.pC->pseudoTableReg==0 );
- assert( !u.bl.pC->isSorter );
assert( u.bl.pC->pCursor!=0 );
u.bl.pCrsr = u.bl.pC->pCursor;
assert( sqlite3BtreeCursorIsValid(u.bl.pCrsr) );
@@ -70092,7 +68423,7 @@ case OP_ParseSchema: {
db->init.busy = 0;
}
}
- if( rc ) sqlite3ResetInternalSchema(db, -1);
+ if( rc ) sqlite3ResetAllSchemasOfConnection(db);
if( rc==SQLITE_NOMEM ){
goto no_mem;
}
@@ -70759,7 +69090,7 @@ case OP_JournalMode: { /* out2-prerelease */
if( !sqlite3PagerOkToChangeJournalMode(u.ci.pPager) ) u.ci.eNew = u.ci.eOld;
#ifndef SQLITE_OMIT_WAL
- u.ci.zFilename = sqlite3PagerFilename(u.ci.pPager);
+ u.ci.zFilename = sqlite3PagerFilename(u.ci.pPager, 1);
/* Do not allow a transition to journal_mode=WAL for a database
** in temporary storage or if the VFS does not support shared memory
@@ -71425,7 +69756,7 @@ vdbe_error_halt:
if( rc==SQLITE_IOERR_NOMEM ) db->mallocFailed = 1;
rc = SQLITE_ERROR;
if( resetSchemaOnFault>0 ){
- sqlite3ResetInternalSchema(db, resetSchemaOnFault-1);
+ sqlite3ResetOneSchema(db, resetSchemaOnFault-1);
}
/* This is the only way out of this procedure. We have to
@@ -71968,6 +70299,7 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){
typedef struct VdbeSorterIter VdbeSorterIter;
typedef struct SorterRecord SorterRecord;
+typedef struct FileWriter FileWriter;
/*
** NOTES ON DATA STRUCTURE USED FOR N-WAY MERGES:
@@ -72065,6 +70397,24 @@ struct VdbeSorterIter {
sqlite3_file *pFile; /* File iterator is reading from */
u8 *aAlloc; /* Allocated space */
u8 *aKey; /* Pointer to current key */
+ u8 *aBuffer; /* Current read buffer */
+ int nBuffer; /* Size of read buffer in bytes */
+};
+
+/*
+** An instance of this structure is used to organize the stream of records
+** being written to files by the merge-sort code into aligned, page-sized
+** blocks. Doing all I/O in aligned page-sized blocks helps I/O to go
+** faster on many operating systems.
+*/
+struct FileWriter {
+ int eFWErr; /* Non-zero if in an error state */
+ u8 *aBuffer; /* Pointer to write buffer */
+ int nBuffer; /* Size of write buffer in bytes */
+ int iBufStart; /* First byte of buffer to write */
+ int iBufEnd; /* Last byte of buffer to write */
+ i64 iWriteOff; /* Offset of start of buffer in file */
+ sqlite3_file *pFile; /* File to write to */
};
/*
@@ -72090,108 +70440,144 @@ struct SorterRecord {
*/
static void vdbeSorterIterZero(sqlite3 *db, VdbeSorterIter *pIter){
sqlite3DbFree(db, pIter->aAlloc);
+ sqlite3DbFree(db, pIter->aBuffer);
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;
+** Read nByte bytes of data from the stream of data iterated by object p.
+** If successful, set *ppOut to point to a buffer containing the data
+** and return SQLITE_OK. Otherwise, if an error occurs, return an SQLite
+** error code.
+**
+** The buffer indicated by *ppOut may only be considered valid until the
+** next call to this function.
+*/
+static int vdbeSorterIterRead(
+ sqlite3 *db, /* Database handle (for malloc) */
+ VdbeSorterIter *p, /* Iterator */
+ int nByte, /* Bytes of data to read */
+ u8 **ppOut /* OUT: Pointer to buffer containing data */
+){
+ int iBuf; /* Offset within buffer to read from */
+ int nAvail; /* Bytes of data available in buffer */
+ assert( p->aBuffer );
+
+ /* If there is no more data to be read from the buffer, read the next
+ ** p->nBuffer bytes of data from the file into it. Or, if there are less
+ ** than p->nBuffer bytes remaining in the PMA, read all remaining data. */
+ iBuf = p->iReadOff % p->nBuffer;
+ if( iBuf==0 ){
+ int nRead; /* Bytes to read from disk */
+ int rc; /* sqlite3OsRead() return code */
+
+ /* Determine how many bytes of data to read. */
+ nRead = (int)(p->iEof - p->iReadOff);
+ if( nRead>p->nBuffer ) nRead = p->nBuffer;
+ assert( nRead>0 );
+
+ /* Read data from the file. Return early if an error occurs. */
+ rc = sqlite3OsRead(p->pFile, p->aBuffer, nRead, p->iReadOff);
+ assert( rc!=SQLITE_IOERR_SHORT_READ );
+ if( rc!=SQLITE_OK ) return rc;
}
+ nAvail = p->nBuffer - iBuf;
+
+ if( nByte<=nAvail ){
+ /* The requested data is available in the in-memory buffer. In this
+ ** case there is no need to make a copy of the data, just return a
+ ** pointer into the buffer to the caller. */
+ *ppOut = &p->aBuffer[iBuf];
+ p->iReadOff += nByte;
+ }else{
+ /* The requested data is not all available in the in-memory buffer.
+ ** In this case, allocate space at p->aAlloc[] to copy the requested
+ ** range into. Then return a copy of pointer p->aAlloc to the caller. */
+ int nRem; /* Bytes remaining to copy */
+
+ /* Extend the p->aAlloc[] allocation if required. */
+ if( p->nAlloc<nByte ){
+ int nNew = p->nAlloc*2;
+ while( nByte>nNew ) nNew = nNew*2;
+ p->aAlloc = sqlite3DbReallocOrFree(db, p->aAlloc, nNew);
+ if( !p->aAlloc ) return SQLITE_NOMEM;
+ p->nAlloc = nNew;
+ }
+
+ /* Copy as much data as is available in the buffer into the start of
+ ** p->aAlloc[]. */
+ memcpy(p->aAlloc, &p->aBuffer[iBuf], nAvail);
+ p->iReadOff += nAvail;
+ nRem = nByte - nAvail;
+
+ /* The following loop copies up to p->nBuffer bytes per iteration into
+ ** the p->aAlloc[] buffer. */
+ while( nRem>0 ){
+ int rc; /* vdbeSorterIterRead() return code */
+ int nCopy; /* Number of bytes to copy */
+ u8 *aNext; /* Pointer to buffer to copy data from */
- 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
- );
+ nCopy = nRem;
+ if( nRem>p->nBuffer ) nCopy = p->nBuffer;
+ rc = vdbeSorterIterRead(db, p, nCopy, &aNext);
+ if( rc!=SQLITE_OK ) return rc;
+ assert( aNext!=p->aAlloc );
+ memcpy(&p->aAlloc[nByte - nRem], aNext, nCopy);
+ nRem -= nCopy;
}
+
+ *ppOut = p->aAlloc;
}
- assert( rc!=SQLITE_OK || nRec>0 );
- pIter->iReadOff += iOff+nRec;
- pIter->nKey = nRec;
- pIter->aKey = &pIter->aAlloc[iOff];
- return rc;
+ return SQLITE_OK;
}
/*
-** 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.
+** Read a varint from the stream of data accessed by p. Set *pnOut to
+** the value read.
*/
-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 */
+static int vdbeSorterIterVarint(sqlite3 *db, VdbeSorterIter *p, u64 *pnOut){
+ int iBuf;
- nVarint = sqlite3PutVarint(aVarint, iVal);
- rc = sqlite3OsWrite(pFile, aVarint, nVarint, *piOffset);
- *piOffset += nVarint;
+ iBuf = p->iReadOff % p->nBuffer;
+ if( iBuf && (p->nBuffer-iBuf)>=9 ){
+ p->iReadOff += sqlite3GetVarint(&p->aBuffer[iBuf], pnOut);
+ }else{
+ u8 aVarint[16], *a;
+ int i = 0, rc;
+ do{
+ rc = vdbeSorterIterRead(db, p, 1, &a);
+ if( rc ) return rc;
+ aVarint[(i++)&0xf] = a[0];
+ }while( (a[0]&0x80)!=0 );
+ sqlite3GetVarint(aVarint, pnOut);
+ }
- return rc;
+ return SQLITE_OK;
}
+
/*
-** 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.
+** 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 vdbeSorterReadVarint(
- sqlite3_file *pFile, /* File to read from */
- i64 *piOffset, /* IN/OUT: Read offset in pFile */
- i64 *piVal /* OUT: Value read from file */
+static int vdbeSorterIterNext(
+ sqlite3 *db, /* Database handle (for sqlite3DbMalloc() ) */
+ VdbeSorterIter *pIter /* Iterator to advance */
){
- u8 aVarint[9]; /* Buffer large enough for a varint */
- i64 iOff = *piOffset; /* Offset in file to read from */
- int rc; /* Return code */
+ int rc; /* Return Code */
+ u64 nRec = 0; /* Size of record in bytes */
+
+ if( pIter->iReadOff>=pIter->iEof ){
+ /* This is an EOF condition */
+ vdbeSorterIterZero(db, pIter);
+ return SQLITE_OK;
+ }
- rc = sqlite3OsRead(pFile, aVarint, 9, iOff);
+ rc = vdbeSorterIterVarint(db, pIter, &nRec);
if( rc==SQLITE_OK ){
- *piOffset += getVarint(aVarint, (u64 *)piVal);
+ pIter->nKey = (int)nRec;
+ rc = vdbeSorterIterRead(db, pIter, (int)nRec, &pIter->aKey);
}
return rc;
@@ -72205,27 +70591,52 @@ static int vdbeSorterReadVarint(
*/
static int vdbeSorterIterInit(
sqlite3 *db, /* Database handle */
- VdbeSorter *pSorter, /* Sorter object */
+ const 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;
+ int rc = SQLITE_OK;
+ int nBuf;
+
+ nBuf = sqlite3BtreeGetPageSize(db->aDb[0].pBt);
assert( pSorter->iWriteOff>iStart );
assert( pIter->aAlloc==0 );
+ assert( pIter->aBuffer==0 );
pIter->pFile = pSorter->pTemp1;
pIter->iReadOff = iStart;
pIter->nAlloc = 128;
pIter->aAlloc = (u8 *)sqlite3DbMallocRaw(db, pIter->nAlloc);
- if( !pIter->aAlloc ){
+ pIter->nBuffer = nBuf;
+ pIter->aBuffer = (u8 *)sqlite3DbMallocRaw(db, nBuf);
+
+ if( !pIter->aBuffer ){
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;
+ int iBuf;
+
+ iBuf = iStart % nBuf;
+ if( iBuf ){
+ int nRead = nBuf - iBuf;
+ if( (iStart + nRead) > pSorter->iWriteOff ){
+ nRead = (int)(pSorter->iWriteOff - iStart);
+ }
+ rc = sqlite3OsRead(
+ pSorter->pTemp1, &pIter->aBuffer[iBuf], nRead, iStart
+ );
+ assert( rc!=SQLITE_IOERR_SHORT_READ );
+ }
+
+ if( rc==SQLITE_OK ){
+ u64 nByte; /* Size of PMA in bytes */
+ pIter->iEof = pSorter->iWriteOff;
+ rc = vdbeSorterIterVarint(db, pIter, &nByte);
+ pIter->iEof = pIter->iReadOff + nByte;
+ *pnByte += nByte;
+ }
}
+
if( rc==SQLITE_OK ){
rc = vdbeSorterIterNext(db, pIter);
}
@@ -72249,10 +70660,10 @@ static int vdbeSorterIterInit(
** has been allocated and contains an unpacked record that is used as key2.
*/
static void vdbeSorterCompare(
- VdbeCursor *pCsr, /* Cursor object (for pKeyInfo) */
+ const 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 */
+ const void *pKey1, int nKey1, /* Left side of comparison */
+ const void *pKey2, int nKey2, /* Right side of comparison */
int *pRes /* OUT: Result of comparison */
){
KeyInfo *pKeyInfo = pCsr->pKeyInfo;
@@ -72284,7 +70695,7 @@ static void vdbeSorterCompare(
** multiple b-tree segments. Parameter iOut is the index of the aTree[]
** value to recalculate.
*/
-static int vdbeSorterDoCompare(VdbeCursor *pCsr, int iOut){
+static int vdbeSorterDoCompare(const VdbeCursor *pCsr, int iOut){
VdbeSorter *pSorter = pCsr->pSorter;
int i1;
int i2;
@@ -72410,7 +70821,7 @@ static int vdbeSorterOpenTempFile(sqlite3 *db, sqlite3_file **ppFile){
** Set *ppOut to the head of the new list.
*/
static void vdbeSorterMerge(
- VdbeCursor *pCsr, /* For pKeyInfo */
+ const VdbeCursor *pCsr, /* For pKeyInfo */
SorterRecord *p1, /* First list to merge */
SorterRecord *p2, /* Second list to merge */
SorterRecord **ppOut /* OUT: Head of merged list */
@@ -72444,7 +70855,7 @@ static void vdbeSorterMerge(
** if successful, or an SQLite error code (i.e. SQLITE_NOMEM) if an error
** occurs.
*/
-static int vdbeSorterSort(VdbeCursor *pCsr){
+static int vdbeSorterSort(const VdbeCursor *pCsr){
int i;
SorterRecord **aSlot;
SorterRecord *p;
@@ -72477,6 +70888,91 @@ static int vdbeSorterSort(VdbeCursor *pCsr){
return SQLITE_OK;
}
+/*
+** Initialize a file-writer object.
+*/
+static void fileWriterInit(
+ sqlite3 *db, /* Database (for malloc) */
+ sqlite3_file *pFile, /* File to write to */
+ FileWriter *p, /* Object to populate */
+ i64 iStart /* Offset of pFile to begin writing at */
+){
+ int nBuf = sqlite3BtreeGetPageSize(db->aDb[0].pBt);
+
+ memset(p, 0, sizeof(FileWriter));
+ p->aBuffer = (u8 *)sqlite3DbMallocRaw(db, nBuf);
+ if( !p->aBuffer ){
+ p->eFWErr = SQLITE_NOMEM;
+ }else{
+ p->iBufEnd = p->iBufStart = (iStart % nBuf);
+ p->iWriteOff = iStart - p->iBufStart;
+ p->nBuffer = nBuf;
+ p->pFile = pFile;
+ }
+}
+
+/*
+** Write nData bytes of data to the file-write object. Return SQLITE_OK
+** if successful, or an SQLite error code if an error occurs.
+*/
+static void fileWriterWrite(FileWriter *p, u8 *pData, int nData){
+ int nRem = nData;
+ while( nRem>0 && p->eFWErr==0 ){
+ int nCopy = nRem;
+ if( nCopy>(p->nBuffer - p->iBufEnd) ){
+ nCopy = p->nBuffer - p->iBufEnd;
+ }
+
+ memcpy(&p->aBuffer[p->iBufEnd], &pData[nData-nRem], nCopy);
+ p->iBufEnd += nCopy;
+ if( p->iBufEnd==p->nBuffer ){
+ p->eFWErr = sqlite3OsWrite(p->pFile,
+ &p->aBuffer[p->iBufStart], p->iBufEnd - p->iBufStart,
+ p->iWriteOff + p->iBufStart
+ );
+ p->iBufStart = p->iBufEnd = 0;
+ p->iWriteOff += p->nBuffer;
+ }
+ assert( p->iBufEnd<p->nBuffer );
+
+ nRem -= nCopy;
+ }
+}
+
+/*
+** Flush any buffered data to disk and clean up the file-writer object.
+** The results of using the file-writer after this call are undefined.
+** Return SQLITE_OK if flushing the buffered data succeeds or is not
+** required. Otherwise, return an SQLite error code.
+**
+** Before returning, set *piEof to the offset immediately following the
+** last byte written to the file.
+*/
+static int fileWriterFinish(sqlite3 *db, FileWriter *p, i64 *piEof){
+ int rc;
+ if( p->eFWErr==0 && ALWAYS(p->aBuffer) && p->iBufEnd>p->iBufStart ){
+ p->eFWErr = sqlite3OsWrite(p->pFile,
+ &p->aBuffer[p->iBufStart], p->iBufEnd - p->iBufStart,
+ p->iWriteOff + p->iBufStart
+ );
+ }
+ *piEof = (p->iWriteOff + p->iBufEnd);
+ sqlite3DbFree(db, p->aBuffer);
+ rc = p->eFWErr;
+ memset(p, 0, sizeof(FileWriter));
+ return rc;
+}
+
+/*
+** Write value iVal encoded as a varint to the file-write object. Return
+** SQLITE_OK if successful, or an SQLite error code if an error occurs.
+*/
+static void fileWriterWriteVarint(FileWriter *p, u64 iVal){
+ int nByte;
+ u8 aByte[10];
+ nByte = sqlite3PutVarint(aByte, iVal);
+ fileWriterWrite(p, aByte, nByte);
+}
/*
** Write the current contents of the in-memory linked-list to a PMA. Return
@@ -72491,9 +70987,12 @@ static int vdbeSorterSort(VdbeCursor *pCsr){
** 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){
+static int vdbeSorterListToPMA(sqlite3 *db, const VdbeCursor *pCsr){
int rc = SQLITE_OK; /* Return code */
VdbeSorter *pSorter = pCsr->pSorter;
+ FileWriter writer;
+
+ memset(&writer, 0, sizeof(FileWriter));
if( pSorter->nInMemory==0 ){
assert( pSorter->pRecord==0 );
@@ -72511,39 +71010,20 @@ static int vdbeSorterListToPMA(sqlite3 *db, VdbeCursor *pCsr){
}
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 };
+ fileWriterInit(db, pSorter->pTemp1, &writer, pSorter->iWriteOff);
pSorter->nPMA++;
- rc = vdbeSorterWriteVarint(pSorter->pTemp1, pSorter->nInMemory, &iOff);
- for(p=pSorter->pRecord; rc==SQLITE_OK && p; p=pNext){
+ fileWriterWriteVarint(&writer, pSorter->nInMemory);
+ for(p=pSorter->pRecord; 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;
- }
-
+ fileWriterWriteVarint(&writer, p->nVal);
+ fileWriterWrite(&writer, p->pVal, 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;
+ rc = fileWriterFinish(db, &writer, &pSorter->iWriteOff);
}
return rc;
@@ -72554,7 +71034,7 @@ static int vdbeSorterListToPMA(sqlite3 *db, VdbeCursor *pCsr){
*/
SQLITE_PRIVATE int sqlite3VdbeSorterWrite(
sqlite3 *db, /* Database handle */
- VdbeCursor *pCsr, /* Sorter cursor */
+ const VdbeCursor *pCsr, /* Sorter cursor */
Mem *pVal /* Memory cell containing record */
){
VdbeSorter *pSorter = pCsr->pSorter;
@@ -72588,8 +71068,14 @@ SQLITE_PRIVATE int sqlite3VdbeSorterWrite(
(pSorter->nInMemory>pSorter->mxPmaSize)
|| (pSorter->nInMemory>pSorter->mnPmaSize && sqlite3HeapNearlyFull())
)){
+#ifdef SQLITE_DEBUG
+ i64 nExpect = pSorter->iWriteOff
+ + sqlite3VarintLen(pSorter->nInMemory)
+ + pSorter->nInMemory;
+#endif
rc = vdbeSorterListToPMA(db, pCsr);
pSorter->nInMemory = 0;
+ assert( rc!=SQLITE_OK || (nExpect==pSorter->iWriteOff) );
}
return rc;
@@ -72600,7 +71086,7 @@ SQLITE_PRIVATE int sqlite3VdbeSorterWrite(
*/
static int vdbeSorterInitMerge(
sqlite3 *db, /* Database handle */
- VdbeCursor *pCsr, /* Cursor handle for this sorter */
+ const VdbeCursor *pCsr, /* Cursor handle for this sorter */
i64 *pnByte /* Sum of bytes in all opened PMAs */
){
VdbeSorter *pSorter = pCsr->pSorter;
@@ -72630,7 +71116,7 @@ static int vdbeSorterInitMerge(
** 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){
+SQLITE_PRIVATE int sqlite3VdbeSorterRewind(sqlite3 *db, const VdbeCursor *pCsr, int *pbEof){
VdbeSorter *pSorter = pCsr->pSorter;
int rc; /* Return code */
sqlite3_file *pTemp2 = 0; /* Second temp file to use */
@@ -72650,7 +71136,7 @@ SQLITE_PRIVATE int sqlite3VdbeSorterRewind(sqlite3 *db, VdbeCursor *pCsr, int *p
return vdbeSorterSort(pCsr);
}
- /* Write the current b-tree to a PMA. Close the b-tree cursor. */
+ /* Write the current in-memory list to a PMA. */
rc = vdbeSorterListToPMA(db, pCsr);
if( rc!=SQLITE_OK ) return rc;
@@ -72672,8 +71158,12 @@ SQLITE_PRIVATE int sqlite3VdbeSorterRewind(sqlite3 *db, VdbeCursor *pCsr, int *p
rc==SQLITE_OK && iNew*SORTER_MAX_MERGE_COUNT<pSorter->nPMA;
iNew++
){
+ int rc2; /* Return code from fileWriterFinish() */
+ FileWriter writer; /* Object used to write to disk */
i64 nWrite; /* Number of bytes in new PMA */
+ memset(&writer, 0, sizeof(FileWriter));
+
/* 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
@@ -72696,22 +71186,19 @@ SQLITE_PRIVATE int sqlite3VdbeSorterRewind(sqlite3 *db, VdbeCursor *pCsr, int *p
}
if( rc==SQLITE_OK ){
- rc = vdbeSorterWriteVarint(pTemp2, nWrite, &iWrite2);
- }
-
- if( rc==SQLITE_OK ){
int bEof = 0;
+ fileWriterInit(db, pTemp2, &writer, iWrite2);
+ fileWriterWriteVarint(&writer, nWrite);
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);
- }
+
+ fileWriterWriteVarint(&writer, pIter->nKey);
+ fileWriterWrite(&writer, pIter->aKey, pIter->nKey);
+ rc = sqlite3VdbeSorterNext(db, pCsr, &bEof);
}
+ rc2 = fileWriterFinish(db, &writer, &iWrite2);
+ if( rc==SQLITE_OK ) rc = rc2;
}
}
@@ -72738,7 +71225,7 @@ SQLITE_PRIVATE int sqlite3VdbeSorterRewind(sqlite3 *db, VdbeCursor *pCsr, int *p
/*
** Advance to the next element in the sorter.
*/
-SQLITE_PRIVATE int sqlite3VdbeSorterNext(sqlite3 *db, VdbeCursor *pCsr, int *pbEof){
+SQLITE_PRIVATE int sqlite3VdbeSorterNext(sqlite3 *db, const VdbeCursor *pCsr, int *pbEof){
VdbeSorter *pSorter = pCsr->pSorter;
int rc; /* Return code */
@@ -72768,7 +71255,7 @@ SQLITE_PRIVATE int sqlite3VdbeSorterNext(sqlite3 *db, VdbeCursor *pCsr, int *pbE
** current key.
*/
static void *vdbeSorterRowkey(
- VdbeSorter *pSorter, /* Sorter object */
+ const VdbeSorter *pSorter, /* Sorter object */
int *pnKey /* OUT: Size of current key in bytes */
){
void *pKey;
@@ -72787,7 +71274,7 @@ static void *vdbeSorterRowkey(
/*
** Copy the current sorter key into the memory cell pOut.
*/
-SQLITE_PRIVATE int sqlite3VdbeSorterRowkey(VdbeCursor *pCsr, Mem *pOut){
+SQLITE_PRIVATE int sqlite3VdbeSorterRowkey(const VdbeCursor *pCsr, Mem *pOut){
VdbeSorter *pSorter = pCsr->pSorter;
void *pKey; int nKey; /* Sorter key to copy into pOut */
@@ -72813,7 +71300,7 @@ SQLITE_PRIVATE int sqlite3VdbeSorterRowkey(VdbeCursor *pCsr, Mem *pOut){
** key.
*/
SQLITE_PRIVATE int sqlite3VdbeSorterCompare(
- VdbeCursor *pCsr, /* Sorter cursor */
+ const VdbeCursor *pCsr, /* Sorter cursor */
Mem *pVal, /* Value to compare to current sorter key */
int *pRes /* OUT: Result of comparison */
){
@@ -73456,13 +71943,19 @@ SQLITE_PRIVATE int sqlite3WalkSelect(Walker *pWalker, Select *p){
int rc;
if( p==0 || pWalker->xSelectCallback==0 ) return WRC_Continue;
rc = WRC_Continue;
- while( p ){
+ pWalker->walkerDepth++;
+ while( p ){
rc = pWalker->xSelectCallback(pWalker, p);
if( rc ) break;
- if( sqlite3WalkSelectExpr(pWalker, p) ) return WRC_Abort;
- if( sqlite3WalkSelectFrom(pWalker, p) ) return WRC_Abort;
+ if( sqlite3WalkSelectExpr(pWalker, p)
+ || sqlite3WalkSelectFrom(pWalker, p)
+ ){
+ pWalker->walkerDepth--;
+ return WRC_Abort;
+ }
p = p->pPrior;
}
+ pWalker->walkerDepth--;
return rc & WRC_Abort;
}
@@ -73488,6 +71981,29 @@ SQLITE_PRIVATE int sqlite3WalkSelect(Walker *pWalker, Select *p){
/* #include <string.h> */
/*
+** Walk the expression tree pExpr and increase the aggregate function
+** depth (the Expr.op2 field) by N on every TK_AGG_FUNCTION node.
+** This needs to occur when copying a TK_AGG_FUNCTION node from an
+** outer query into an inner subquery.
+**
+** incrAggFunctionDepth(pExpr,n) is the main routine. incrAggDepth(..)
+** is a helper function - a callback for the tree walker.
+*/
+static int incrAggDepth(Walker *pWalker, Expr *pExpr){
+ if( pExpr->op==TK_AGG_FUNCTION ) pExpr->op2 += pWalker->u.i;
+ return WRC_Continue;
+}
+static void incrAggFunctionDepth(Expr *pExpr, int N){
+ if( N>0 ){
+ Walker w;
+ memset(&w, 0, sizeof(w));
+ w.xExprCallback = incrAggDepth;
+ w.u.i = N;
+ sqlite3WalkExpr(&w, pExpr);
+ }
+}
+
+/*
** Turn the pExpr expression into an alias for the iCol-th column of the
** result set in pEList.
**
@@ -73513,13 +72029,20 @@ SQLITE_PRIVATE int sqlite3WalkSelect(Walker *pWalker, Select *p){
** The result of random()%5 in the GROUP BY clause is probably different
** from the result in the result-set. We might fix this someday. Or
** then again, we might not...
+**
+** The nSubquery parameter specifies how many levels of subquery the
+** alias is removed from the original expression. The usually value is
+** zero but it might be more if the alias is contained within a subquery
+** of the original expression. The Expr.op2 field of TK_AGG_FUNCTION
+** structures must be increased by the nSubquery amount.
*/
static void resolveAlias(
Parse *pParse, /* Parsing context */
ExprList *pEList, /* A result set */
int iCol, /* A column in the result set. 0..pEList->nExpr-1 */
Expr *pExpr, /* Transform this into an alias to the result set */
- const char *zType /* "GROUP" or "ORDER" or "" */
+ const char *zType, /* "GROUP" or "ORDER" or "" */
+ int nSubquery /* Number of subqueries that the label is moving */
){
Expr *pOrig; /* The iCol-th column of the result set */
Expr *pDup; /* Copy of pOrig */
@@ -73532,6 +72055,7 @@ static void resolveAlias(
db = pParse->db;
if( pOrig->op!=TK_COLUMN && zType[0]!='G' ){
pDup = sqlite3ExprDup(db, pOrig, 0);
+ incrAggFunctionDepth(pDup, nSubquery);
pDup = sqlite3PExpr(pParse, TK_AS, pDup, 0, 0);
if( pDup==0 ) return;
if( pEList->a[iCol].iAlias==0 ){
@@ -73620,9 +72144,10 @@ static int lookupName(
NameContext *pNC, /* The name context used to resolve the name */
Expr *pExpr /* Make this EXPR node point to the selected column */
){
- int i, j; /* Loop counters */
+ int i, j; /* Loop counters */
int cnt = 0; /* Number of matching column names */
int cntTab = 0; /* Number of matching table names */
+ int nSubquery = 0; /* How many levels of subquery */
sqlite3 *db = pParse->db; /* The database connection */
struct SrcList_item *pItem; /* Use for looping over pSrcList items */
struct SrcList_item *pMatch = 0; /* The matching pSrcList item */
@@ -73784,7 +72309,7 @@ static int lookupName(
sqlite3ErrorMsg(pParse, "misuse of aliased aggregate %s", zAs);
return WRC_Abort;
}
- resolveAlias(pParse, pEList, j, pExpr, "");
+ resolveAlias(pParse, pEList, j, pExpr, "", nSubquery);
cnt = 1;
pMatch = 0;
assert( zTab==0 && zDb==0 );
@@ -73798,6 +72323,7 @@ static int lookupName(
*/
if( cnt==0 ){
pNC = pNC->pNext;
+ nSubquery++;
}
}
@@ -74037,13 +72563,19 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
nId, zId);
pNC->nErr++;
}
+ if( is_agg ) pNC->ncFlags &= ~NC_AllowAgg;
+ sqlite3WalkExprList(pWalker, pList);
if( is_agg ){
+ NameContext *pNC2 = pNC;
pExpr->op = TK_AGG_FUNCTION;
- pNC->ncFlags |= NC_HasAgg;
+ pExpr->op2 = 0;
+ while( pNC2 && !sqlite3FunctionUsesThisSrc(pExpr, pNC2->pSrcList) ){
+ pExpr->op2++;
+ pNC2 = pNC2->pNext;
+ }
+ if( pNC2 ) pNC2->ncFlags |= NC_HasAgg;
+ pNC->ncFlags |= NC_AllowAgg;
}
- if( is_agg ) pNC->ncFlags &= ~NC_AllowAgg;
- sqlite3WalkExprList(pWalker, pList);
- if( is_agg ) pNC->ncFlags |= NC_AllowAgg;
/* FIX ME: Compute pExpr->affinity based on the expected return
** type of the function
*/
@@ -74322,7 +72854,7 @@ SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(
resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr);
return 1;
}
- resolveAlias(pParse, pEList, pItem->iOrderByCol-1, pItem->pExpr, zType);
+ resolveAlias(pParse, pEList, pItem->iOrderByCol-1, pItem->pExpr, zType,0);
}
}
return 0;
@@ -76400,7 +74932,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
assert( !isRowid );
sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
- dest.affinity = (u8)affinity;
+ dest.affSdst = (u8)affinity;
assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
pExpr->x.pSelect->iLimit = 0;
if( sqlite3Select(pParse, pExpr->x.pSelect, &dest) ){
@@ -76493,11 +75025,11 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
sqlite3SelectDestInit(&dest, 0, ++pParse->nMem);
if( pExpr->op==TK_SELECT ){
dest.eDest = SRT_Mem;
- sqlite3VdbeAddOp2(v, OP_Null, 0, dest.iParm);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, dest.iSDParm);
VdbeComment((v, "Init subquery result"));
}else{
dest.eDest = SRT_Exists;
- sqlite3VdbeAddOp2(v, OP_Integer, 0, dest.iParm);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, dest.iSDParm);
VdbeComment((v, "Init EXISTS result"));
}
sqlite3ExprDelete(pParse->db, pSel->pLimit);
@@ -76507,7 +75039,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
if( sqlite3Select(pParse, pSel, &dest) ){
return 0;
}
- rReg = dest.iParm;
+ rReg = dest.iSDParm;
ExprSetIrreducible(pExpr);
break;
}
@@ -77822,9 +76354,12 @@ SQLITE_PRIVATE void sqlite3ExplainExpr(Vdbe *pOut, Expr *pExpr){
}else{
pFarg = pExpr->x.pList;
}
- sqlite3ExplainPrintf(pOut, "%sFUNCTION:%s(",
- op==TK_AGG_FUNCTION ? "AGG_" : "",
- pExpr->u.zToken);
+ if( op==TK_AGG_FUNCTION ){
+ sqlite3ExplainPrintf(pOut, "AGG_FUNCTION%d:%s(",
+ pExpr->op2, pExpr->u.zToken);
+ }else{
+ sqlite3ExplainPrintf(pOut, "FUNCTION:%s(", pExpr->u.zToken);
+ }
if( pFarg ){
sqlite3ExplainExprList(pOut, pFarg);
}
@@ -78515,38 +77050,60 @@ SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList *pA, ExprList *pB){
}
/*
-** This is the expression callback for sqlite3FunctionUsesOtherSrc().
-**
-** Determine if an expression references any table other than one of the
-** tables in pWalker->u.pSrcList and abort if it does.
+** An instance of the following structure is used by the tree walker
+** to count references to table columns in the arguments of an
+** aggregate function, in order to implement the
+** sqlite3FunctionThisSrc() routine.
+*/
+struct SrcCount {
+ SrcList *pSrc; /* One particular FROM clause in a nested query */
+ int nThis; /* Number of references to columns in pSrcList */
+ int nOther; /* Number of references to columns in other FROM clauses */
+};
+
+/*
+** Count the number of references to columns.
*/
-static int exprUsesOtherSrc(Walker *pWalker, Expr *pExpr){
- if( pExpr->op==TK_COLUMN || pExpr->op==TK_AGG_COLUMN ){
+static int exprSrcCount(Walker *pWalker, Expr *pExpr){
+ /* The NEVER() on the second term is because sqlite3FunctionUsesThisSrc()
+ ** is always called before sqlite3ExprAnalyzeAggregates() and so the
+ ** TK_COLUMNs have not yet been converted into TK_AGG_COLUMN. If
+ ** sqlite3FunctionUsesThisSrc() is used differently in the future, the
+ ** NEVER() will need to be removed. */
+ if( pExpr->op==TK_COLUMN || NEVER(pExpr->op==TK_AGG_COLUMN) ){
int i;
- SrcList *pSrc = pWalker->u.pSrcList;
+ struct SrcCount *p = pWalker->u.pSrcCount;
+ SrcList *pSrc = p->pSrc;
for(i=0; i<pSrc->nSrc; i++){
- if( pExpr->iTable==pSrc->a[i].iCursor ) return WRC_Continue;
+ if( pExpr->iTable==pSrc->a[i].iCursor ) break;
+ }
+ if( i<pSrc->nSrc ){
+ p->nThis++;
+ }else{
+ p->nOther++;
}
- return WRC_Abort;
- }else{
- return WRC_Continue;
}
+ return WRC_Continue;
}
/*
-** Determine if any of the arguments to the pExpr Function references
-** any SrcList other than pSrcList. Return true if they do. Return
-** false if pExpr has no argument or has only constant arguments or
-** only references tables named in pSrcList.
+** Determine if any of the arguments to the pExpr Function reference
+** pSrcList. Return true if they do. Also return true if the function
+** has no arguments or has only constant arguments. Return false if pExpr
+** references columns but not columns of tables found in pSrcList.
*/
-static int sqlite3FunctionUsesOtherSrc(Expr *pExpr, SrcList *pSrcList){
+SQLITE_PRIVATE int sqlite3FunctionUsesThisSrc(Expr *pExpr, SrcList *pSrcList){
Walker w;
+ struct SrcCount cnt;
assert( pExpr->op==TK_AGG_FUNCTION );
memset(&w, 0, sizeof(w));
- w.xExprCallback = exprUsesOtherSrc;
- w.u.pSrcList = pSrcList;
- if( sqlite3WalkExprList(&w, pExpr->x.pList)!=WRC_Continue ) return 1;
- return 0;
+ w.xExprCallback = exprSrcCount;
+ w.u.pSrcCount = &cnt;
+ cnt.pSrc = pSrcList;
+ cnt.nThis = 0;
+ cnt.nOther = 0;
+ sqlite3WalkExprList(&w, pExpr->x.pList);
+ return cnt.nThis>0 || cnt.nOther==0;
}
/*
@@ -78665,7 +77222,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
}
case TK_AGG_FUNCTION: {
if( (pNC->ncFlags & NC_InAggFunc)==0
- && !sqlite3FunctionUsesOtherSrc(pExpr, pSrcList)
+ && pWalker->walkerDepth==pExpr->op2
){
/* Check to see if pExpr is a duplicate of another aggregate
** function that is already in the pAggInfo structure
@@ -79821,7 +78378,7 @@ static void openStatTable(
"CREATE TABLE %Q.%s(%s)", pDb->zName, zTab, aTable[i].zCols
);
aRoot[i] = pParse->regRoot;
- aCreateTbl[i] = 1;
+ aCreateTbl[i] = OPFLAG_P2ISREG;
}else{
/* The table already exists. If zWhere is not NULL, delete all entries
** associated with the table zWhere. If zWhere is NULL, delete the
@@ -79901,12 +78458,11 @@ static void stat3Init(
nRow = (tRowcnt)sqlite3_value_int64(argv[0]);
mxSample = sqlite3_value_int(argv[1]);
n = sizeof(*p) + sizeof(p->a[0])*mxSample;
- p = sqlite3_malloc( n );
+ p = sqlite3MallocZero( n );
if( p==0 ){
sqlite3_result_error_nomem(context);
return;
}
- memset(p, 0, n);
p->a = (struct Stat3Sample*)&p[1];
p->nRow = nRow;
p->mxSample = mxSample;
@@ -80988,7 +79544,7 @@ static void attachFunc(
db->aDb[iDb].pBt = 0;
db->aDb[iDb].pSchema = 0;
}
- sqlite3ResetInternalSchema(db, -1);
+ sqlite3ResetAllSchemasOfConnection(db);
db->nDb = iDb;
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
db->mallocFailed = 1;
@@ -81060,7 +79616,7 @@ static void detachFunc(
sqlite3BtreeClose(pDb->pBt);
pDb->pBt = 0;
pDb->pSchema = 0;
- sqlite3ResetInternalSchema(db, -1);
+ sqlite3ResetAllSchemasOfConnection(db);
return;
detach_error:
@@ -81976,58 +80532,15 @@ SQLITE_PRIVATE void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char
}
/*
-** Erase all schema information from the in-memory hash tables of
-** a single database. This routine is called to reclaim memory
-** before the database closes. It is also called during a rollback
-** if there were schema changes during the transaction or if a
-** schema-cookie mismatch occurs.
+** Look through the list of open database files in db->aDb[] and if
+** any have been closed, remove them from the list. Reallocate the
+** db->aDb[] structure to a smaller size, if possible.
**
-** If iDb<0 then reset the internal schema tables for all database
-** files. If iDb>=0 then reset the internal schema for only the
-** single file indicated.
+** Entry 0 (the "main" database) and entry 1 (the "temp" database)
+** are never candidates for being collapsed.
*/
-SQLITE_PRIVATE void sqlite3ResetInternalSchema(sqlite3 *db, int iDb){
+SQLITE_PRIVATE void sqlite3CollapseDatabaseArray(sqlite3 *db){
int i, j;
- assert( iDb<db->nDb );
-
- if( iDb>=0 ){
- /* Case 1: Reset the single schema identified by iDb */
- Db *pDb = &db->aDb[iDb];
- assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
- assert( pDb->pSchema!=0 );
- sqlite3SchemaClear(pDb->pSchema);
-
- /* If any database other than TEMP is reset, then also reset TEMP
- ** since TEMP might be holding triggers that reference tables in the
- ** other database.
- */
- if( iDb!=1 ){
- pDb = &db->aDb[1];
- assert( pDb->pSchema!=0 );
- sqlite3SchemaClear(pDb->pSchema);
- }
- return;
- }
- /* Case 2 (from here to the end): Reset all schemas for all attached
- ** databases. */
- assert( iDb<0 );
- sqlite3BtreeEnterAll(db);
- for(i=0; i<db->nDb; i++){
- Db *pDb = &db->aDb[i];
- if( pDb->pSchema ){
- sqlite3SchemaClear(pDb->pSchema);
- }
- }
- db->flags &= ~SQLITE_InternChanges;
- sqlite3VtabUnlockList(db);
- sqlite3BtreeLeaveAll(db);
-
- /* If one or more of the auxiliary database files has been closed,
- ** then remove them from the auxiliary database list. We take the
- ** opportunity to do this here since we have just deleted all of the
- ** schema hash tables and therefore do not have to make any changes
- ** to any of those tables.
- */
for(i=j=2; i<db->nDb; i++){
struct Db *pDb = &db->aDb[i];
if( pDb->pBt==0 ){
@@ -82050,6 +80563,51 @@ SQLITE_PRIVATE void sqlite3ResetInternalSchema(sqlite3 *db, int iDb){
}
/*
+** Reset the schema for the database at index iDb. Also reset the
+** TEMP schema.
+*/
+SQLITE_PRIVATE void sqlite3ResetOneSchema(sqlite3 *db, int iDb){
+ Db *pDb;
+ assert( iDb<db->nDb );
+
+ /* Case 1: Reset the single schema identified by iDb */
+ pDb = &db->aDb[iDb];
+ assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
+ assert( pDb->pSchema!=0 );
+ sqlite3SchemaClear(pDb->pSchema);
+
+ /* If any database other than TEMP is reset, then also reset TEMP
+ ** since TEMP might be holding triggers that reference tables in the
+ ** other database.
+ */
+ if( iDb!=1 ){
+ pDb = &db->aDb[1];
+ assert( pDb->pSchema!=0 );
+ sqlite3SchemaClear(pDb->pSchema);
+ }
+ return;
+}
+
+/*
+** Erase all schema information from all attached databases (including
+** "main" and "temp") for a single database connection.
+*/
+SQLITE_PRIVATE void sqlite3ResetAllSchemasOfConnection(sqlite3 *db){
+ int i;
+ sqlite3BtreeEnterAll(db);
+ for(i=0; i<db->nDb; i++){
+ Db *pDb = &db->aDb[i];
+ if( pDb->pSchema ){
+ sqlite3SchemaClear(pDb->pSchema);
+ }
+ }
+ db->flags &= ~SQLITE_InternChanges;
+ sqlite3VtabUnlockList(db);
+ sqlite3BtreeLeaveAll(db);
+ sqlite3CollapseDatabaseArray(db);
+}
+
+/*
** This routine is called when a commit occurs.
*/
SQLITE_PRIVATE void sqlite3CommitInternalChanges(sqlite3 *db){
@@ -82114,7 +80672,7 @@ SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
if( !db || db->pnBytesFreed==0 ){
char *zName = pIndex->zName;
TESTONLY ( Index *pOld = ) sqlite3HashInsert(
- &pIndex->pSchema->idxHash, zName, sqlite3Strlen30(zName), 0
+ &pIndex->pSchema->idxHash, zName, sqlite3Strlen30(zName), 0
);
assert( db==0 || sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) );
assert( pOld==pIndex || pOld==0 );
@@ -83161,7 +81719,7 @@ SQLITE_PRIVATE void sqlite3EndTable(
assert(pParse->nTab==1);
sqlite3VdbeAddOp3(v, OP_OpenWrite, 1, pParse->regRoot, iDb);
- sqlite3VdbeChangeP5(v, 1);
+ sqlite3VdbeChangeP5(v, OPFLAG_P2ISREG);
pParse->nTab = 2;
sqlite3SelectDestInit(&dest, SRT_Table, 1);
sqlite3Select(pParse, pSelect, &dest);
@@ -83977,9 +82535,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
pKey = sqlite3IndexKeyinfo(pParse, pIndex);
sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdx, tnum, iDb,
(char *)pKey, P4_KEYINFO_HANDOFF);
- if( memRootPage>=0 ){
- sqlite3VdbeChangeP5(v, 1);
- }
+ sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR|((memRootPage>=0)?OPFLAG_P2ISREG:0));
#ifndef SQLITE_OMIT_MERGE_SORT
/* Open the sorter cursor if we are to use one. */
@@ -84118,7 +82674,7 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
assert( pName && pName->z );
#ifndef SQLITE_OMIT_TEMPDB
- /* If the index name was unqualified, check if the the table
+ /* If the index name was unqualified, check if the table
** is a temp table. If so, set the database to 1. Do not do this
** if initialising a database schema.
*/
@@ -84345,7 +82901,7 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
}else{
zColl = pTab->aCol[j].zColl;
if( !zColl ){
- zColl = db->pDfltColl->zName;
+ zColl = "BINARY";
}
}
if( !db->init.busy && !sqlite3LocateCollSeq(pParse, zColl) ){
@@ -86272,7 +84828,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
*/
sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet);
pWInfo = sqlite3WhereBegin(
- pParse, pTabList, pWhere, 0, 0, WHERE_DUPLICATES_OK
+ pParse, pTabList, pWhere, 0, 0, WHERE_DUPLICATES_OK, 0
);
if( pWInfo==0 ) goto delete_from_cleanup;
regRowid = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, iRowid, 0);
@@ -87416,8 +85972,19 @@ static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
assert( argc==1 );
UNUSED_PARAMETER(argc);
switch( sqlite3_value_type(argv[0]) ){
- case SQLITE_INTEGER:
case SQLITE_FLOAT: {
+ double r1, r2;
+ char zBuf[50];
+ r1 = sqlite3_value_double(argv[0]);
+ sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.15g", r1);
+ sqlite3AtoF(zBuf, &r2, 20, SQLITE_UTF8);
+ if( r1!=r2 ){
+ sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.20e", r1);
+ }
+ sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
+ break;
+ }
+ case SQLITE_INTEGER: {
sqlite3_result_value(context, argv[0]);
break;
}
@@ -88729,7 +87296,7 @@ static void fkScanChildren(
** 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, 0);
+ pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0);
if( nIncr>0 && pFKey->isDeferred==0 ){
sqlite3ParseToplevel(pParse)->mayAbort = 1;
}
@@ -89987,7 +88554,7 @@ SQLITE_PRIVATE void sqlite3Insert(
VdbeComment((v, "SELECT eof flag"));
sqlite3SelectDestInit(&dest, SRT_Coroutine, ++pParse->nMem);
addrSelect = sqlite3VdbeCurrentAddr(v)+2;
- sqlite3VdbeAddOp2(v, OP_Integer, addrSelect-1, dest.iParm);
+ sqlite3VdbeAddOp2(v, OP_Integer, addrSelect-1, dest.iSDParm);
j1 = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
VdbeComment((v, "Jump over SELECT coroutine"));
@@ -89998,15 +88565,15 @@ SQLITE_PRIVATE void sqlite3Insert(
goto insert_cleanup;
}
sqlite3VdbeAddOp2(v, OP_Integer, 1, regEof); /* EOF <- 1 */
- sqlite3VdbeAddOp1(v, OP_Yield, dest.iParm); /* yield X */
+ sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm); /* yield X */
sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_INTERNAL, OE_Abort);
VdbeComment((v, "End of SELECT coroutine"));
sqlite3VdbeJumpHere(v, j1); /* label B: */
- regFromSelect = dest.iMem;
+ regFromSelect = dest.iSdst;
assert( pSelect->pEList );
nColumn = pSelect->pEList->nExpr;
- assert( dest.nMem==nColumn );
+ assert( dest.nSdst==nColumn );
/* Set useTempTable to TRUE if the result of the SELECT statement
** should be written into a temporary table (template 4). Set to
@@ -90042,7 +88609,7 @@ SQLITE_PRIVATE void sqlite3Insert(
regRec = sqlite3GetTempReg(pParse);
regTempRowid = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, srcTab, nColumn);
- addrTop = sqlite3VdbeAddOp1(v, OP_Yield, dest.iParm);
+ addrTop = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm);
addrIf = sqlite3VdbeAddOp1(v, OP_If, regEof);
sqlite3VdbeAddOp3(v, OP_MakeRecord, regFromSelect, nColumn, regRec);
sqlite3VdbeAddOp2(v, OP_NewRowid, srcTab, regTempRowid);
@@ -90179,7 +88746,7 @@ SQLITE_PRIVATE void sqlite3Insert(
** goto C
** D: ...
*/
- addrCont = sqlite3VdbeAddOp1(v, OP_Yield, dest.iParm);
+ addrCont = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm);
addrInsTop = sqlite3VdbeAddOp1(v, OP_If, regEof);
}
@@ -90661,7 +89228,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
case OE_Replace: {
/* If there are DELETE triggers on this table and the
** recursive-triggers flag is set, call GenerateRowDelete() to
- ** remove the conflicting row from the the table. This will fire
+ ** remove the conflicting row from the table. This will fire
** the triggers and remove both the table and index b-tree entries.
**
** Otherwise, if there are no triggers or the recursive-triggers
@@ -92620,7 +91187,7 @@ static int invalidateTempStorage(Parse *pParse){
}
sqlite3BtreeClose(db->aDb[1].pBt);
db->aDb[1].pBt = 0;
- sqlite3ResetInternalSchema(db, -1);
+ sqlite3ResetAllSchemasOfConnection(db);
}
return SQLITE_OK;
}
@@ -93306,6 +91873,50 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
}else
+#if SQLITE_OS_WIN
+ /*
+ ** PRAGMA data_store_directory
+ ** PRAGMA data_store_directory = ""|"directory_name"
+ **
+ ** Return or set the local value of the data_store_directory flag. Changing
+ ** the value sets a specific directory to be used for database files that
+ ** were specified with a relative pathname. Setting to a null string reverts
+ ** to the default database directory, which for database files specified with
+ ** a relative path will probably be based on the current directory for the
+ ** process. Database file specified with an absolute path are not impacted
+ ** by this setting, regardless of its value.
+ **
+ */
+ if( sqlite3StrICmp(zLeft, "data_store_directory")==0 ){
+ if( !zRight ){
+ if( sqlite3_data_directory ){
+ sqlite3VdbeSetNumCols(v, 1);
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME,
+ "data_store_directory", SQLITE_STATIC);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, sqlite3_data_directory, 0);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
+ }
+ }else{
+#ifndef SQLITE_OMIT_WSD
+ if( zRight[0] ){
+ int res;
+ rc = sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res);
+ if( rc!=SQLITE_OK || res==0 ){
+ sqlite3ErrorMsg(pParse, "not a writable directory");
+ goto pragma_out;
+ }
+ }
+ sqlite3_free(sqlite3_data_directory);
+ if( zRight[0] ){
+ sqlite3_data_directory = sqlite3_mprintf("%s", zRight);
+ }else{
+ sqlite3_data_directory = 0;
+ }
+#endif /* SQLITE_OMIT_WSD */
+ }
+ }else
+#endif
+
#if !defined(SQLITE_ENABLE_LOCKING_STYLE)
# if defined(__APPLE__)
# define SQLITE_ENABLE_LOCKING_STYLE 1
@@ -93672,6 +92283,19 @@ SQLITE_PRIVATE void sqlite3Pragma(
int isQuick = (sqlite3Tolower(zLeft[0])=='q');
+ /* If the PRAGMA command was of the form "PRAGMA <db>.integrity_check",
+ ** then iDb is set to the index of the database identified by <db>.
+ ** In this case, the integrity of database iDb only is verified by
+ ** the VDBE created below.
+ **
+ ** Otherwise, if the command was simply "PRAGMA integrity_check" (or
+ ** "PRAGMA quick_check"), then iDb is set to 0. In this case, set iDb
+ ** to -1 here, to indicate that the VDBE should verify the integrity
+ ** of all attached databases. */
+ assert( iDb>=0 );
+ assert( iDb==0 || pId2->z );
+ if( pId2->z==0 ) iDb = -1;
+
/* Initialize the VDBE program */
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
pParse->nMem = 6;
@@ -93695,6 +92319,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
int cnt = 0;
if( OMIT_TEMPDB && i==1 ) continue;
+ if( iDb>=0 && i!=iDb ) continue;
sqlite3CodeVerifySchema(pParse, i);
addr = sqlite3VdbeAddOp1(v, OP_IfPos, 1); /* Halt if out of errors */
@@ -93706,7 +92331,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
** Begin by filling registers 2, 3, ... with the root pages numbers
** for all tables and indices in the database.
*/
- assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
+ assert( sqlite3SchemaMutexHeld(db, i, 0) );
pTbls = &db->aDb[i].pSchema->tblHash;
for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
Table *pTab = sqliteHashData(x);
@@ -94392,7 +93017,6 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
encoding = (u8)meta[BTREE_TEXT_ENCODING-1] & 3;
if( encoding==0 ) encoding = SQLITE_UTF8;
ENC(db) = encoding;
- db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, "BINARY", 0);
}else{
/* If opening an attached database, the encoding much match ENC(db) */
if( meta[BTREE_TEXT_ENCODING-1]!=ENC(db) ){
@@ -94472,7 +93096,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
}
if( db->mallocFailed ){
rc = SQLITE_NOMEM;
- sqlite3ResetInternalSchema(db, -1);
+ sqlite3ResetAllSchemasOfConnection(db);
}
if( rc==SQLITE_OK || (db->flags&SQLITE_RecoveryMode)){
/* Black magic: If the SQLITE_RecoveryMode flag is set, then consider
@@ -94525,7 +93149,7 @@ SQLITE_PRIVATE int sqlite3Init(sqlite3 *db, char **pzErrMsg){
if( DbHasProperty(db, i, DB_SchemaLoaded) || i==1 ) continue;
rc = sqlite3InitOne(db, i, pzErrMsg);
if( rc ){
- sqlite3ResetInternalSchema(db, i);
+ sqlite3ResetOneSchema(db, i);
}
}
@@ -94538,7 +93162,7 @@ SQLITE_PRIVATE int sqlite3Init(sqlite3 *db, char **pzErrMsg){
&& !DbHasProperty(db, 1, DB_SchemaLoaded) ){
rc = sqlite3InitOne(db, 1, pzErrMsg);
if( rc ){
- sqlite3ResetInternalSchema(db, 1);
+ sqlite3ResetOneSchema(db, 1);
}
}
#endif
@@ -94606,7 +93230,7 @@ static void schemaIsValid(Parse *pParse){
sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&cookie);
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
if( cookie!=db->aDb[iDb].pSchema->schema_cookie ){
- sqlite3ResetInternalSchema(db, iDb);
+ sqlite3ResetOneSchema(db, iDb);
pParse->rc = SQLITE_SCHEMA;
}
@@ -95031,10 +93655,10 @@ static void clearSelect(sqlite3 *db, Select *p){
*/
SQLITE_PRIVATE void sqlite3SelectDestInit(SelectDest *pDest, int eDest, int iParm){
pDest->eDest = (u8)eDest;
- pDest->iParm = iParm;
- pDest->affinity = 0;
- pDest->iMem = 0;
- pDest->nMem = 0;
+ pDest->iSDParm = iParm;
+ pDest->affSdst = 0;
+ pDest->iSdst = 0;
+ pDest->nSdst = 0;
}
@@ -95546,7 +94170,7 @@ static void selectInnerLoop(
int hasDistinct; /* True if the DISTINCT keyword is present */
int regResult; /* Start of memory holding result set */
int eDest = pDest->eDest; /* How to dispose of results */
- int iParm = pDest->iParm; /* First argument to disposal method */
+ int iParm = pDest->iSDParm; /* First argument to disposal method */
int nResultCol; /* Number of result columns */
assert( v );
@@ -95564,14 +94188,14 @@ static void selectInnerLoop(
}else{
nResultCol = pEList->nExpr;
}
- if( pDest->iMem==0 ){
- pDest->iMem = pParse->nMem+1;
- pDest->nMem = nResultCol;
+ if( pDest->iSdst==0 ){
+ pDest->iSdst = pParse->nMem+1;
+ pDest->nSdst = nResultCol;
pParse->nMem += nResultCol;
}else{
- assert( pDest->nMem==nResultCol );
+ assert( pDest->nSdst==nResultCol );
}
- regResult = pDest->iMem;
+ regResult = pDest->iSdst;
if( nColumn>0 ){
for(i=0; i<nColumn; i++){
sqlite3VdbeAddOp3(v, OP_Column, srcTab, i, regResult+i);
@@ -95650,7 +94274,7 @@ static void selectInnerLoop(
*/
case SRT_Set: {
assert( nColumn==1 );
- p->affinity = sqlite3CompareAffinity(pEList->a[0].pExpr, pDest->affinity);
+ p->affinity = sqlite3CompareAffinity(pEList->a[0].pExpr, pDest->affSdst);
if( pOrderBy ){
/* At first glance you would think we could optimize out the
** ORDER BY in this case since the order of entries in the set
@@ -95705,7 +94329,7 @@ static void selectInnerLoop(
pushOntoSorter(pParse, pOrderBy, p, r1);
sqlite3ReleaseTempReg(pParse, r1);
}else if( eDest==SRT_Coroutine ){
- sqlite3VdbeAddOp1(v, OP_Yield, pDest->iParm);
+ sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm);
}else{
sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, nColumn);
sqlite3ExprCacheAffinityChange(pParse, regResult, nColumn);
@@ -95885,7 +94509,7 @@ static void generateSortTail(
ExprList *pOrderBy = p->pOrderBy;
int eDest = pDest->eDest;
- int iParm = pDest->iParm;
+ int iParm = pDest->iSDParm;
int regRow;
int regRowid;
@@ -95944,17 +94568,17 @@ static void generateSortTail(
testcase( eDest==SRT_Output );
testcase( eDest==SRT_Coroutine );
for(i=0; i<nColumn; i++){
- assert( regRow!=pDest->iMem+i );
- sqlite3VdbeAddOp3(v, OP_Column, pseudoTab, i, pDest->iMem+i);
+ assert( regRow!=pDest->iSdst+i );
+ sqlite3VdbeAddOp3(v, OP_Column, pseudoTab, i, pDest->iSdst+i);
if( i==0 ){
sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE);
}
}
if( eDest==SRT_Output ){
- sqlite3VdbeAddOp2(v, OP_ResultRow, pDest->iMem, nColumn);
- sqlite3ExprCacheAffinityChange(pParse, pDest->iMem, nColumn);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, pDest->iSdst, nColumn);
+ sqlite3ExprCacheAffinityChange(pParse, pDest->iSdst, nColumn);
}else{
- sqlite3VdbeAddOp1(v, OP_Yield, pDest->iParm);
+ sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm);
}
break;
}
@@ -96605,7 +95229,7 @@ static int multiSelect(
*/
if( dest.eDest==SRT_EphemTab ){
assert( p->pEList );
- sqlite3VdbeAddOp2(v, OP_OpenEphemeral, dest.iParm, p->pEList->nExpr);
+ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, dest.iSDParm, p->pEList->nExpr);
sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
dest.eDest = SRT_Table;
}
@@ -96691,7 +95315,7 @@ static int multiSelect(
** of a 3-way or more compound */
assert( p->pLimit==0 ); /* Not allowed on leftward elements */
assert( p->pOffset==0 ); /* Not allowed on leftward elements */
- unionTab = dest.iParm;
+ unionTab = dest.iSDParm;
}else{
/* We will need to create our own temporary table to hold the
** intermediate results.
@@ -96748,7 +95372,7 @@ static int multiSelect(
/* Convert the data in the temporary table into whatever form
** it is that we currently need.
*/
- assert( unionTab==dest.iParm || dest.eDest!=priorOp );
+ assert( unionTab==dest.iSDParm || dest.eDest!=priorOp );
if( dest.eDest!=priorOp ){
int iCont, iBreak, iStart;
assert( p->pEList );
@@ -96812,7 +95436,7 @@ static int multiSelect(
p->pLimit = 0;
pOffset = p->pOffset;
p->pOffset = 0;
- intersectdest.iParm = tab2;
+ intersectdest.iSDParm = tab2;
explainSetInteger(iSub2, pParse->iNextSelectId);
rc = sqlite3Select(pParse, p, &intersectdest);
testcase( rc!=SQLITE_OK );
@@ -96906,8 +95530,8 @@ static int multiSelect(
}
multi_select_end:
- pDest->iMem = dest.iMem;
- pDest->nMem = dest.nMem;
+ pDest->iSdst = dest.iSdst;
+ pDest->nSdst = dest.nSdst;
sqlite3SelectDelete(db, pDelete);
return rc;
}
@@ -96917,8 +95541,8 @@ multi_select_end:
** Code an output subroutine for a coroutine implementation of a
** SELECT statment.
**
-** The data to be output is contained in pIn->iMem. There are
-** pIn->nMem columns to be output. pDest is where the output should
+** The data to be output is contained in pIn->iSdst. There are
+** pIn->nSdst columns to be output. pDest is where the output should
** be sent.
**
** regReturn is the number of the register holding the subroutine
@@ -96956,16 +95580,16 @@ static int generateOutputSubroutine(
if( regPrev ){
int j1, j2;
j1 = sqlite3VdbeAddOp1(v, OP_IfNot, regPrev);
- j2 = sqlite3VdbeAddOp4(v, OP_Compare, pIn->iMem, regPrev+1, pIn->nMem,
+ j2 = sqlite3VdbeAddOp4(v, OP_Compare, pIn->iSdst, regPrev+1, pIn->nSdst,
(char*)pKeyInfo, p4type);
sqlite3VdbeAddOp3(v, OP_Jump, j2+2, iContinue, j2+2);
sqlite3VdbeJumpHere(v, j1);
- sqlite3ExprCodeCopy(pParse, pIn->iMem, regPrev+1, pIn->nMem);
+ sqlite3ExprCodeCopy(pParse, pIn->iSdst, regPrev+1, pIn->nSdst);
sqlite3VdbeAddOp2(v, OP_Integer, 1, regPrev);
}
if( pParse->db->mallocFailed ) return 0;
- /* Suppress the the first OFFSET entries if there is an OFFSET clause
+ /* Suppress the first OFFSET entries if there is an OFFSET clause
*/
codeOffset(v, p, iContinue);
@@ -96978,9 +95602,9 @@ static int generateOutputSubroutine(
int r2 = sqlite3GetTempReg(pParse);
testcase( pDest->eDest==SRT_Table );
testcase( pDest->eDest==SRT_EphemTab );
- sqlite3VdbeAddOp3(v, OP_MakeRecord, pIn->iMem, pIn->nMem, r1);
- sqlite3VdbeAddOp2(v, OP_NewRowid, pDest->iParm, r2);
- sqlite3VdbeAddOp3(v, OP_Insert, pDest->iParm, r1, r2);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst, r1);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, pDest->iSDParm, r2);
+ sqlite3VdbeAddOp3(v, OP_Insert, pDest->iSDParm, r1, r2);
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
sqlite3ReleaseTempReg(pParse, r2);
sqlite3ReleaseTempReg(pParse, r1);
@@ -96994,13 +95618,13 @@ static int generateOutputSubroutine(
*/
case SRT_Set: {
int r1;
- assert( pIn->nMem==1 );
+ assert( pIn->nSdst==1 );
p->affinity =
- sqlite3CompareAffinity(p->pEList->a[0].pExpr, pDest->affinity);
+ sqlite3CompareAffinity(p->pEList->a[0].pExpr, pDest->affSdst);
r1 = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp4(v, OP_MakeRecord, pIn->iMem, 1, r1, &p->affinity, 1);
- sqlite3ExprCacheAffinityChange(pParse, pIn->iMem, 1);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, pDest->iParm, r1);
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, pIn->iSdst, 1, r1, &p->affinity, 1);
+ sqlite3ExprCacheAffinityChange(pParse, pIn->iSdst, 1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, pDest->iSDParm, r1);
sqlite3ReleaseTempReg(pParse, r1);
break;
}
@@ -97009,7 +95633,7 @@ static int generateOutputSubroutine(
/* If any row exist in the result set, record that fact and abort.
*/
case SRT_Exists: {
- sqlite3VdbeAddOp2(v, OP_Integer, 1, pDest->iParm);
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, pDest->iSDParm);
/* The LIMIT clause will terminate the loop for us */
break;
}
@@ -97020,23 +95644,23 @@ static int generateOutputSubroutine(
** of the scan loop.
*/
case SRT_Mem: {
- assert( pIn->nMem==1 );
- sqlite3ExprCodeMove(pParse, pIn->iMem, pDest->iParm, 1);
+ assert( pIn->nSdst==1 );
+ sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSDParm, 1);
/* The LIMIT clause will jump out of the loop for us */
break;
}
#endif /* #ifndef SQLITE_OMIT_SUBQUERY */
/* The results are stored in a sequence of registers
- ** starting at pDest->iMem. Then the co-routine yields.
+ ** starting at pDest->iSdst. Then the co-routine yields.
*/
case SRT_Coroutine: {
- if( pDest->iMem==0 ){
- pDest->iMem = sqlite3GetTempRange(pParse, pIn->nMem);
- pDest->nMem = pIn->nMem;
+ if( pDest->iSdst==0 ){
+ pDest->iSdst = sqlite3GetTempRange(pParse, pIn->nSdst);
+ pDest->nSdst = pIn->nSdst;
}
- sqlite3ExprCodeMove(pParse, pIn->iMem, pDest->iMem, pDest->nMem);
- sqlite3VdbeAddOp1(v, OP_Yield, pDest->iParm);
+ sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSdst, pDest->nSdst);
+ sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm);
break;
}
@@ -97050,8 +95674,8 @@ static int generateOutputSubroutine(
*/
default: {
assert( pDest->eDest==SRT_Output );
- sqlite3VdbeAddOp2(v, OP_ResultRow, pIn->iMem, pIn->nMem);
- sqlite3ExprCacheAffinityChange(pParse, pIn->iMem, pIn->nMem);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, pIn->iSdst, pIn->nSdst);
+ sqlite3ExprCacheAffinityChange(pParse, pIn->iSdst, pIn->nSdst);
break;
}
}
@@ -97470,7 +96094,7 @@ static int multiSelectOrderBy(
*/
sqlite3VdbeResolveLabel(v, labelCmpr);
sqlite3VdbeAddOp4(v, OP_Permutation, 0, 0, 0, (char*)aPermute, P4_INTARRAY);
- sqlite3VdbeAddOp4(v, OP_Compare, destA.iMem, destB.iMem, nOrderBy,
+ sqlite3VdbeAddOp4(v, OP_Compare, destA.iSdst, destB.iSdst, nOrderBy,
(char*)pKeyMerge, P4_KEYINFO_HANDOFF);
sqlite3VdbeAddOp3(v, OP_Jump, addrAltB, addrAeqB, addrAgtB);
@@ -97684,6 +96308,12 @@ static void substSelect(
** operators have an implied DISTINCT which is disallowed by
** restriction (4).
**
+** Also, each component of the sub-query must return the same number
+** of result columns. This is actually a requirement for any compound
+** SELECT statement, but all the code here does is make sure that no
+** such (illegal) sub-query is flattened. The caller will detect the
+** syntax error and return a detailed message.
+**
** (18) If the sub-query is a compound select, then all terms of the
** ORDER by clause of the parent must be simple references to
** columns of the sub-query.
@@ -97827,6 +96457,7 @@ static int flattenSubquery(
if( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))!=0
|| (pSub1->pPrior && pSub1->op!=TK_ALL)
|| pSub1->pSrc->nSrc<1
+ || pSub->pEList->nExpr!=pSub1->pEList->nExpr
){
return 0;
}
@@ -98144,7 +96775,7 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){
if( IsVirtual(pTab) ) return 0;
if( pExpr->op!=TK_AGG_FUNCTION ) return 0;
- if( pAggInfo->nFunc==0 ) return 0;
+ if( NEVER(pAggInfo->nFunc==0) ) return 0;
if( (pAggInfo->aFunc[0].pFunc->flags&SQLITE_FUNC_COUNT)==0 ) return 0;
if( pExpr->flags&EP_Distinct ) return 0;
@@ -98516,7 +97147,7 @@ static void sqlite3SelectAddTypeInfo(Parse *pParse, Select *pSelect){
/*
-** This routine sets of a SELECT statement for processing. The
+** This routine sets up a SELECT statement for processing. The
** following is accomplished:
**
** * VDBE Cursor numbers are assigned to all FROM-clause terms.
@@ -98548,7 +97179,8 @@ SQLITE_PRIVATE void sqlite3SelectPrep(
**
** The aggregate accumulator is a set of memory cells that hold
** intermediate results while calculating an aggregate. This
-** routine simply stores NULLs in all of those memory cells.
+** routine generates code that stores NULLs in all of those memory
+** cells.
*/
static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
Vdbe *v = pParse->pVdbe;
@@ -98716,23 +97348,24 @@ static void explainSimpleCount(
**
** SRT_Mem Only valid if the result is a single column.
** Store the first column of the first result row
-** in register pDest->iParm then abandon the rest
+** in register pDest->iSDParm then abandon the rest
** of the query. This destination implies "LIMIT 1".
**
** SRT_Set The result must be a single column. Store each
-** row of result as the key in table pDest->iParm.
-** Apply the affinity pDest->affinity before storing
+** row of result as the key in table pDest->iSDParm.
+** Apply the affinity pDest->affSdst before storing
** results. Used to implement "IN (SELECT ...)".
**
-** SRT_Union Store results as a key in a temporary table pDest->iParm.
+** SRT_Union Store results as a key in a temporary table
+** identified by pDest->iSDParm.
**
-** SRT_Except Remove results from the temporary table pDest->iParm.
+** SRT_Except Remove results from the temporary table pDest->iSDParm.
**
-** SRT_Table Store results in temporary table pDest->iParm.
+** SRT_Table Store results in temporary table pDest->iSDParm.
** This is like SRT_EphemTab except that the table
** is assumed to already be open.
**
-** SRT_EphemTab Create an temporary table pDest->iParm and store
+** SRT_EphemTab Create an temporary table pDest->iSDParm and store
** the result there. The cursor is left open after
** returning. This is like SRT_Table except that
** this destination uses OP_OpenEphemeral to create
@@ -98740,9 +97373,9 @@ static void explainSimpleCount(
**
** SRT_Coroutine Generate a co-routine that returns a new row of
** results each time it is invoked. The entry point
-** of the co-routine is stored in register pDest->iParm.
+** of the co-routine is stored in register pDest->iSDParm.
**
-** SRT_Exists Store a 1 in memory cell pDest->iParm if the result
+** SRT_Exists Store a 1 in memory cell pDest->iSDParm if the result
** set is not empty.
**
** SRT_Discard Throw the results away. This is used by SELECT
@@ -98986,7 +97619,7 @@ SQLITE_PRIVATE int sqlite3Select(
/* If the output is destined for a temporary table, open that table.
*/
if( pDest->eDest==SRT_EphemTab ){
- sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pDest->iParm, pEList->nExpr);
+ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pDest->iSDParm, pEList->nExpr);
}
/* Set the limiter.
@@ -99017,7 +97650,7 @@ SQLITE_PRIVATE int sqlite3Select(
ExprList *pDist = (isDistinct ? p->pEList : 0);
/* Begin the database scan. */
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy, pDist, 0);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy, pDist, 0,0);
if( pWInfo==0 ) goto select_end;
if( pWInfo->nRowOut < p->nSelectRow ) p->nSelectRow = pWInfo->nRowOut;
@@ -99190,7 +97823,7 @@ SQLITE_PRIVATE int sqlite3Select(
** in the right order to begin with.
*/
sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pGroupBy, 0, 0);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pGroupBy, 0, 0, 0);
if( pWInfo==0 ) goto select_end;
if( pGroupBy==0 ){
/* The optimizer is able to deliver rows in group by order so
@@ -99459,7 +98092,7 @@ SQLITE_PRIVATE int sqlite3Select(
** of output.
*/
resetAccumulator(pParse, &sAggInfo);
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax, 0, flag);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax,0,flag,0);
if( pWInfo==0 ){
sqlite3ExprListDelete(db, pDel);
goto select_end;
@@ -99931,7 +98564,7 @@ SQLITE_PRIVATE void sqlite3BeginTrigger(
iDb = 1;
pName = pName1;
}else{
- /* Figure out the db that the the trigger will be created in */
+ /* Figure out the db that the trigger will be created in */
iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName);
if( iDb<0 ){
goto trigger_cleanup;
@@ -101259,7 +99892,7 @@ SQLITE_PRIVATE void sqlite3Update(
*/
sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid);
pWInfo = sqlite3WhereBegin(
- pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED
+ pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED, 0
);
if( pWInfo==0 ) goto update_cleanup;
okOnePass = pWInfo->okOnePass;
@@ -101959,7 +100592,7 @@ end_of_vacuum:
/* This both clears the schemas and reduces the size of the db->aDb[]
** array. */
- sqlite3ResetInternalSchema(db, -1);
+ sqlite3ResetAllSchemasOfConnection(db);
return rc;
}
@@ -101991,8 +100624,8 @@ end_of_vacuum:
** are invoked only from within xCreate and xConnect methods.
*/
struct VtabCtx {
- Table *pTab;
- VTable *pVTable;
+ VTable *pVTable; /* The virtual table being constructed */
+ Table *pTab; /* The Table object to which the virtual table belongs */
};
/*
@@ -102007,33 +100640,35 @@ static int createModule(
void *pAux, /* Context pointer for xCreate/xConnect */
void (*xDestroy)(void *) /* Module destructor function */
){
- int rc, nName;
- Module *pMod;
+ int rc = SQLITE_OK;
+ int nName;
sqlite3_mutex_enter(db->mutex);
nName = sqlite3Strlen30(zName);
- pMod = (Module *)sqlite3DbMallocRaw(db, sizeof(Module) + nName + 1);
- if( pMod ){
- Module *pDel;
- char *zCopy = (char *)(&pMod[1]);
- memcpy(zCopy, zName, nName+1);
- pMod->zName = zCopy;
- pMod->pModule = pModule;
- pMod->pAux = pAux;
- 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;
+ if( sqlite3HashFind(&db->aModule, zName, nName) ){
+ rc = SQLITE_MISUSE_BKPT;
+ }else{
+ Module *pMod;
+ pMod = (Module *)sqlite3DbMallocRaw(db, sizeof(Module) + nName + 1);
+ if( pMod ){
+ Module *pDel;
+ char *zCopy = (char *)(&pMod[1]);
+ memcpy(zCopy, zName, nName+1);
+ pMod->zName = zCopy;
+ pMod->pModule = pModule;
+ pMod->pAux = pAux;
+ pMod->xDestroy = xDestroy;
+ pDel = (Module *)sqlite3HashInsert(&db->aModule,zCopy,nName,(void*)pMod);
+ assert( pDel==0 || pDel==pMod );
+ if( pDel ){
+ db->mallocFailed = 1;
+ sqlite3DbFree(db, pDel);
+ }
}
- }else if( xDestroy ){
- xDestroy(pAux);
}
- rc = sqlite3ApiExit(db, SQLITE_OK);
+ rc = sqlite3ApiExit(db, rc);
+ if( rc!=SQLITE_OK && xDestroy ) xDestroy(pAux);
+
sqlite3_mutex_leave(db->mutex);
return rc;
}
@@ -102098,7 +100733,7 @@ SQLITE_PRIVATE void sqlite3VtabUnlock(VTable *pVTab){
assert( db );
assert( pVTab->nRef>0 );
- assert( sqlite3SafetyCheckOk(db) );
+ assert( db->magic==SQLITE_MAGIC_OPEN || db->magic==SQLITE_MAGIC_ZOMBIE );
pVTab->nRef--;
if( pVTab->nRef==0 ){
@@ -102149,6 +100784,31 @@ static VTable *vtabDisconnectAll(sqlite3 *db, Table *p){
return pRet;
}
+/*
+** Table *p is a virtual table. This function removes the VTable object
+** for table *p associated with database connection db from the linked
+** list in p->pVTab. It also decrements the VTable ref count. This is
+** used when closing database connection db to free all of its VTable
+** objects without disturbing the rest of the Schema object (which may
+** be being used by other shared-cache connections).
+*/
+SQLITE_PRIVATE void sqlite3VtabDisconnect(sqlite3 *db, Table *p){
+ VTable **ppVTab;
+
+ assert( IsVirtual(p) );
+ assert( sqlite3BtreeHoldsAllMutexes(db) );
+ assert( sqlite3_mutex_held(db->mutex) );
+
+ for(ppVTab=&p->pVTable; *ppVTab; ppVTab=&(*ppVTab)->pNext){
+ if( (*ppVTab)->db==db ){
+ VTable *pVTab = *ppVTab;
+ *ppVTab = pVTab->pNext;
+ sqlite3VtabUnlock(pVTab);
+ break;
+ }
+ }
+}
+
/*
** Disconnect all the virtual table objects in the sqlite3.pDisconnect list.
@@ -106661,7 +105321,7 @@ static int codeAllEqualityTerms(
int r1;
int k = pIdx->aiColumn[j];
pTerm = findTerm(pWC, iCur, k, notReady, pLevel->plan.wsFlags, pIdx);
- if( NEVER(pTerm==0) ) break;
+ if( pTerm==0 ) break;
/* The following true for indices with redundant columns.
** Ex: CREATE INDEX i1 ON t1(a,b,a); SELECT * FROM t1 WHERE a=0 AND b=0; */
testcase( (pTerm->wtFlags & TERM_CODED)!=0 );
@@ -107336,6 +105996,8 @@ static Bitmask codeOneLoopStart(
*/
WhereClause *pOrWc; /* The OR-clause broken out into subterms */
SrcList *pOrTab; /* Shortened table list or OR-clause generation */
+ Index *pCov = 0; /* Potential covering index (or NULL) */
+ int iCovCur = pParse->nTab++; /* Cursor used for index scans (if any) */
int regReturn = ++pParse->nMem; /* Register used with OP_Gosub */
int regRowset = 0; /* Register for RowSet object */
@@ -107354,7 +106016,7 @@ static Bitmask codeOneLoopStart(
pLevel->op = OP_Return;
pLevel->p1 = regReturn;
- /* Set up a new SrcList ni pOrTab containing the table being scanned
+ /* Set up a new SrcList in pOrTab containing the table being scanned
** by this loop in the a[0] slot and all notReady tables in a[1..] slots.
** This becomes the SrcList in the recursive call to sqlite3WhereBegin().
*/
@@ -107431,8 +106093,10 @@ static Bitmask codeOneLoopStart(
/* Loop through table entries that match term pOrTerm. */
pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0,
WHERE_OMIT_OPEN_CLOSE | WHERE_AND_ONLY |
- WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY);
+ WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY, iCovCur);
+ assert( pSubWInfo || pParse->nErr || pParse->db->mallocFailed );
if( pSubWInfo ){
+ WhereLevel *pLvl;
explainOneScan(
pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0
);
@@ -107453,11 +106117,36 @@ static Bitmask codeOneLoopStart(
*/
if( pSubWInfo->untestedTerms ) untestedTerms = 1;
+ /* If all of the OR-connected terms are optimized using the same
+ ** index, and the index is opened using the same cursor number
+ ** by each call to sqlite3WhereBegin() made by this loop, it may
+ ** be possible to use that index as a covering index.
+ **
+ ** If the call to sqlite3WhereBegin() above resulted in a scan that
+ ** uses an index, and this is either the first OR-connected term
+ ** processed or the index is the same as that used by all previous
+ ** terms, set pCov to the candidate covering index. Otherwise, set
+ ** pCov to NULL to indicate that no candidate covering index will
+ ** be available.
+ */
+ pLvl = &pSubWInfo->a[0];
+ if( (pLvl->plan.wsFlags & WHERE_INDEXED)!=0
+ && (pLvl->plan.wsFlags & WHERE_TEMP_INDEX)==0
+ && (ii==0 || pLvl->plan.u.pIdx==pCov)
+ ){
+ assert( pLvl->iIdxCur==iCovCur );
+ pCov = pLvl->plan.u.pIdx;
+ }else{
+ pCov = 0;
+ }
+
/* Finish the loop through table entries that match term pOrTerm. */
sqlite3WhereEnd(pSubWInfo);
}
}
}
+ pLevel->u.pCovidx = pCov;
+ if( pCov ) pLevel->iIdxCur = iCovCur;
if( pAndExpr ){
pAndExpr->pLeft = 0;
sqlite3ExprDelete(pParse->db, pAndExpr);
@@ -107675,7 +106364,8 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
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 */
+ u16 wctrlFlags, /* One of the WHERE_* flags defined in sqliteInt.h */
+ int iIdxCur /* If WHERE_ONETABLE_ONLY is set, index cursor number */
){
int i; /* Loop counter */
int nByteWInfo; /* Num. bytes allocated for WhereInfo struct */
@@ -107995,7 +106685,13 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
testcase( bestPlan.plan.wsFlags & WHERE_INDEXED );
testcase( bestPlan.plan.wsFlags & WHERE_TEMP_INDEX );
if( bestPlan.plan.wsFlags & (WHERE_INDEXED|WHERE_TEMP_INDEX) ){
- pLevel->iIdxCur = pParse->nTab++;
+ if( (wctrlFlags & WHERE_ONETABLE_ONLY)
+ && (bestPlan.plan.wsFlags & WHERE_TEMP_INDEX)==0
+ ){
+ pLevel->iIdxCur = iIdxCur;
+ }else{
+ pLevel->iIdxCur = pParse->nTab++;
+ }
}else{
pLevel->iIdxCur = -1;
}
@@ -108096,10 +106792,10 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){
Index *pIx = pLevel->plan.u.pIdx;
KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIx);
- int iIdxCur = pLevel->iIdxCur;
+ int iIndexCur = pLevel->iIdxCur;
assert( pIx->pSchema==pTab->pSchema );
- assert( iIdxCur>=0 );
- sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIx->tnum, iDb,
+ assert( iIndexCur>=0 );
+ sqlite3VdbeAddOp4(v, OP_OpenRead, iIndexCur, pIx->tnum, iDb,
(char*)pKey, P4_KEYINFO_HANDOFF);
VdbeComment((v, "%s", pIx->zName));
}
@@ -108247,6 +106943,7 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
*/
assert( pWInfo->nLevel==1 || pWInfo->nLevel==pTabList->nSrc );
for(i=0, pLevel=pWInfo->a; i<pWInfo->nLevel; i++, pLevel++){
+ Index *pIdx = 0;
struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom];
Table *pTab = pTabItem->pTab;
assert( pTab!=0 );
@@ -108276,12 +106973,15 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
** that reference the table and converts them into opcodes that
** reference the index.
*/
- if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 && !db->mallocFailed){
+ if( pLevel->plan.wsFlags & WHERE_INDEXED ){
+ pIdx = pLevel->plan.u.pIdx;
+ }else if( pLevel->plan.wsFlags & WHERE_MULTI_OR ){
+ pIdx = pLevel->u.pCovidx;
+ }
+ if( pIdx && !db->mallocFailed){
int k, j, last;
VdbeOp *pOp;
- Index *pIdx = pLevel->plan.u.pIdx;
- assert( pIdx!=0 );
pOp = sqlite3VdbeGetOp(v, pWInfo->iTop);
last = sqlite3VdbeCurrentAddr(v);
for(k=pWInfo->iTop; k<last; k++, pOp++){
@@ -113039,6 +111739,15 @@ SQLITE_PRIVATE void (*sqlite3IoTrace)(const char*, ...) = 0;
SQLITE_API char *sqlite3_temp_directory = 0;
/*
+** If the following global variable points to a string which is the
+** name of a directory, then that directory will be used to store
+** all database files specified with a relative pathname.
+**
+** See also the "PRAGMA data_store_directory" SQL command.
+*/
+SQLITE_API char *sqlite3_data_directory = 0;
+
+/*
** Initialize SQLite.
**
** This routine must be called to initialize the memory allocation,
@@ -113236,6 +111945,18 @@ SQLITE_API int sqlite3_shutdown(void){
if( sqlite3GlobalConfig.isMallocInit ){
sqlite3MallocEnd();
sqlite3GlobalConfig.isMallocInit = 0;
+
+#ifndef SQLITE_OMIT_SHUTDOWN_DIRECTORIES
+ /* The heap subsystem has now been shutdown and these values are supposed
+ ** to be NULL or point to memory that was obtained from sqlite3_malloc(),
+ ** which would rely on that heap subsystem; therefore, make sure these
+ ** values cannot refer to heap memory that was just invalidated when the
+ ** heap subsystem was shutdown. This is only done if the current call to
+ ** this function resulted in the heap subsystem actually being shutdown.
+ */
+ sqlite3_data_directory = 0;
+ sqlite3_temp_directory = 0;
+#endif
}
if( sqlite3GlobalConfig.isMutexInit ){
sqlite3MutexEnd();
@@ -113685,12 +112406,48 @@ static void functionDestroy(sqlite3 *db, FuncDef *p){
}
/*
-** Close an existing SQLite database
+** Disconnect all sqlite3_vtab objects that belong to database connection
+** db. This is called when db is being closed.
*/
-SQLITE_API int sqlite3_close(sqlite3 *db){
- HashElem *i; /* Hash table iterator */
+static void disconnectAllVtab(sqlite3 *db){
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ int i;
+ sqlite3BtreeEnterAll(db);
+ for(i=0; i<db->nDb; i++){
+ Schema *pSchema = db->aDb[i].pSchema;
+ if( db->aDb[i].pSchema ){
+ HashElem *p;
+ for(p=sqliteHashFirst(&pSchema->tblHash); p; p=sqliteHashNext(p)){
+ Table *pTab = (Table *)sqliteHashData(p);
+ if( IsVirtual(pTab) ) sqlite3VtabDisconnect(db, pTab);
+ }
+ }
+ }
+ sqlite3BtreeLeaveAll(db);
+#else
+ UNUSED_PARAMETER(db);
+#endif
+}
+
+/*
+** Return TRUE if database connection db has unfinalized prepared
+** statements or unfinished sqlite3_backup objects.
+*/
+static int connectionIsBusy(sqlite3 *db){
int j;
+ assert( sqlite3_mutex_held(db->mutex) );
+ if( db->pVdbe ) return 1;
+ for(j=0; j<db->nDb; j++){
+ Btree *pBt = db->aDb[j].pBt;
+ if( pBt && sqlite3BtreeIsInBackup(pBt) ) return 1;
+ }
+ return 0;
+}
+/*
+** Close an existing SQLite database
+*/
+static int sqlite3Close(sqlite3 *db, int forceZombie){
if( !db ){
return SQLITE_OK;
}
@@ -113699,10 +112456,10 @@ SQLITE_API int sqlite3_close(sqlite3 *db){
}
sqlite3_mutex_enter(db->mutex);
- /* Force xDestroy calls on all virtual tables */
- sqlite3ResetInternalSchema(db, -1);
+ /* Force xDisconnect calls on all virtual tables */
+ disconnectAllVtab(db);
- /* If a transaction is open, the ResetInternalSchema() call above
+ /* If a transaction is open, the disconnectAllVtab() call above
** will not have called the xDisconnect() method on any virtual
** tables in the db->aVTrans[] array. The following sqlite3VtabRollback()
** call will do so. We need to do this before the check for active
@@ -113711,28 +112468,67 @@ SQLITE_API int sqlite3_close(sqlite3 *db){
*/
sqlite3VtabRollback(db);
- /* If there are any outstanding VMs, return SQLITE_BUSY. */
- if( db->pVdbe ){
- sqlite3Error(db, SQLITE_BUSY,
- "unable to close due to unfinalised statements");
+ /* Legacy behavior (sqlite3_close() behavior) is to return
+ ** SQLITE_BUSY if the connection can not be closed immediately.
+ */
+ if( !forceZombie && connectionIsBusy(db) ){
+ sqlite3Error(db, SQLITE_BUSY, "unable to close due to unfinalized "
+ "statements or unfinished backups");
sqlite3_mutex_leave(db->mutex);
return SQLITE_BUSY;
}
- assert( sqlite3SafetyCheckSickOrOk(db) );
- for(j=0; j<db->nDb; j++){
- Btree *pBt = db->aDb[j].pBt;
- if( pBt && sqlite3BtreeIsInBackup(pBt) ){
- sqlite3Error(db, SQLITE_BUSY,
- "unable to close due to unfinished backup operation");
- sqlite3_mutex_leave(db->mutex);
- return SQLITE_BUSY;
- }
+ /* Convert the connection into a zombie and then close it.
+ */
+ db->magic = SQLITE_MAGIC_ZOMBIE;
+ sqlite3LeaveMutexAndCloseZombie(db);
+ return SQLITE_OK;
+}
+
+/*
+** Two variations on the public interface for closing a database
+** connection. The sqlite3_close() version returns SQLITE_BUSY and
+** leaves the connection option if there are unfinalized prepared
+** statements or unfinished sqlite3_backups. The sqlite3_close_v2()
+** version forces the connection to become a zombie if there are
+** unclosed resources, and arranges for deallocation when the last
+** prepare statement or sqlite3_backup closes.
+*/
+SQLITE_API int sqlite3_close(sqlite3 *db){ return sqlite3Close(db,0); }
+SQLITE_API int sqlite3_close_v2(sqlite3 *db){ return sqlite3Close(db,1); }
+
+
+/*
+** Close the mutex on database connection db.
+**
+** Furthermore, if database connection db is a zombie (meaning that there
+** has been a prior call to sqlite3_close(db) or sqlite3_close_v2(db)) and
+** every sqlite3_stmt has now been finalized and every sqlite3_backup has
+** finished, then free all resources.
+*/
+SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){
+ HashElem *i; /* Hash table iterator */
+ int j;
+
+ /* If there are outstanding sqlite3_stmt or sqlite3_backup objects
+ ** or if the connection has not yet been closed by sqlite3_close_v2(),
+ ** then just leave the mutex and return.
+ */
+ if( db->magic!=SQLITE_MAGIC_ZOMBIE || connectionIsBusy(db) ){
+ sqlite3_mutex_leave(db->mutex);
+ return;
}
+ /* If we reach this point, it means that the database connection has
+ ** closed all sqlite3_stmt and sqlite3_backup objects and has been
+ ** pased to sqlite3_close (meaning that it is a zombie). Therefore,
+ ** go ahead and free all resources.
+ */
+
/* Free any outstanding Savepoint structures. */
sqlite3CloseSavepoints(db);
+ /* Close all database connections */
for(j=0; j<db->nDb; j++){
struct Db *pDb = &db->aDb[j];
if( pDb->pBt ){
@@ -113743,15 +112539,22 @@ SQLITE_API int sqlite3_close(sqlite3 *db){
}
}
}
- sqlite3ResetInternalSchema(db, -1);
+ /* Clear the TEMP schema separately and last */
+ if( db->aDb[1].pSchema ){
+ sqlite3SchemaClear(db->aDb[1].pSchema);
+ }
+ sqlite3VtabUnlockList(db);
+
+ /* Free up the array of auxiliary databases */
+ sqlite3CollapseDatabaseArray(db);
+ assert( db->nDb<=2 );
+ assert( db->aDb==db->aDbStatic );
/* Tell the code in notify.c that the connection no longer holds any
** locks and does not require any further unlock-notify callbacks.
*/
sqlite3ConnectionClosed(db);
- assert( db->nDb<=2 );
- assert( db->aDb==db->aDbStatic );
for(j=0; j<ArraySize(db->aFunc.a); j++){
FuncDef *pNext, *pHash, *p;
for(p=db->aFunc.a[j]; p; p=pHash){
@@ -113809,7 +112612,6 @@ SQLITE_API int sqlite3_close(sqlite3 *db){
sqlite3_free(db->lookaside.pStart);
}
sqlite3_free(db);
- return SQLITE_OK;
}
/*
@@ -113838,7 +112640,7 @@ SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3 *db, int tripCode){
if( db->flags&SQLITE_InternChanges ){
sqlite3ExpirePreparedStatements(db);
- sqlite3ResetInternalSchema(db, -1);
+ sqlite3ResetAllSchemasOfConnection(db);
}
/* Any deferred constraint violations have now been resolved. */
@@ -114976,10 +113778,12 @@ SQLITE_PRIVATE int sqlite3ParseUri(
{ "ro", SQLITE_OPEN_READONLY },
{ "rw", SQLITE_OPEN_READWRITE },
{ "rwc", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE },
+ { "memory", SQLITE_OPEN_MEMORY },
{ 0, 0 }
};
- mask = SQLITE_OPEN_READONLY|SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
+ mask = SQLITE_OPEN_READONLY | SQLITE_OPEN_READWRITE
+ | SQLITE_OPEN_CREATE | SQLITE_OPEN_MEMORY;
aMode = aOpenMode;
limit = mask & flags;
zModeType = "access";
@@ -115000,7 +113804,7 @@ SQLITE_PRIVATE int sqlite3ParseUri(
rc = SQLITE_ERROR;
goto parse_uri_out;
}
- if( mode>limit ){
+ if( (mode & ~SQLITE_OPEN_MEMORY)>limit ){
*pzErrMsg = sqlite3_mprintf("%s mode not allowed: %s",
zModeType, zVal);
rc = SQLITE_PERM;
@@ -115019,6 +113823,7 @@ SQLITE_PRIVATE int sqlite3ParseUri(
memcpy(zFile, zUri, nUri);
zFile[nUri] = '\0';
zFile[nUri+1] = '\0';
+ flags &= ~SQLITE_OPEN_URI;
}
*ppVfs = sqlite3_vfs_find(zVfs);
@@ -117350,10 +116155,20 @@ SQLITE_PRIVATE int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char **, int*
SQLITE_PRIVATE int sqlite3Fts3SelectDoctotal(Fts3Table *, sqlite3_stmt **);
SQLITE_PRIVATE int sqlite3Fts3SelectDocsize(Fts3Table *, sqlite3_int64, sqlite3_stmt **);
+#ifndef SQLITE_DISABLE_FTS4_DEFERRED
SQLITE_PRIVATE void sqlite3Fts3FreeDeferredTokens(Fts3Cursor *);
SQLITE_PRIVATE int sqlite3Fts3DeferToken(Fts3Cursor *, Fts3PhraseToken *, int);
SQLITE_PRIVATE int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *);
SQLITE_PRIVATE void sqlite3Fts3FreeDeferredDoclists(Fts3Cursor *);
+SQLITE_PRIVATE int sqlite3Fts3DeferredTokenList(Fts3DeferredToken *, char **, int *);
+#else
+# define sqlite3Fts3FreeDeferredTokens(x)
+# define sqlite3Fts3DeferToken(x,y,z) SQLITE_OK
+# define sqlite3Fts3CacheDeferredDoclists(x) SQLITE_OK
+# define sqlite3Fts3FreeDeferredDoclists(x)
+# define sqlite3Fts3DeferredTokenList(x,y,z) SQLITE_OK
+#endif
+
SQLITE_PRIVATE void sqlite3Fts3SegmentsClose(Fts3Table *);
SQLITE_PRIVATE int sqlite3Fts3MaxLevel(Fts3Table *, int *);
@@ -117462,7 +116277,12 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist(Fts3Cursor *, Fts3Expr *, int iC
SQLITE_PRIVATE int sqlite3Fts3MsrOvfl(Fts3Cursor *, Fts3MultiSegReader *, int *);
SQLITE_PRIVATE int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr);
-SQLITE_PRIVATE int sqlite3Fts3DeferredTokenList(Fts3DeferredToken *, char **, int *);
+/* fts3_unicode2.c (functions generated by parsing unicode text files) */
+#ifdef SQLITE_ENABLE_FTS4_UNICODE61
+SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int, int);
+SQLITE_PRIVATE int sqlite3FtsUnicodeIsalnum(int);
+SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int);
+#endif
#endif /* !SQLITE_CORE || SQLITE_ENABLE_FTS3 */
#endif /* _FTSINT_H */
@@ -120732,6 +119552,9 @@ static void hashDestroy(void *p){
*/
SQLITE_PRIVATE void sqlite3Fts3SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule);
SQLITE_PRIVATE void sqlite3Fts3PorterTokenizerModule(sqlite3_tokenizer_module const**ppModule);
+#ifdef SQLITE_ENABLE_FTS4_UNICODE61
+SQLITE_PRIVATE void sqlite3Fts3UnicodeTokenizer(sqlite3_tokenizer_module const**ppModule);
+#endif
#ifdef SQLITE_ENABLE_ICU
SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(sqlite3_tokenizer_module const**ppModule);
#endif
@@ -120747,12 +119570,19 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){
Fts3Hash *pHash = 0;
const sqlite3_tokenizer_module *pSimple = 0;
const sqlite3_tokenizer_module *pPorter = 0;
+#ifdef SQLITE_ENABLE_FTS4_UNICODE61
+ const sqlite3_tokenizer_module *pUnicode = 0;
+#endif
#ifdef SQLITE_ENABLE_ICU
const sqlite3_tokenizer_module *pIcu = 0;
sqlite3Fts3IcuTokenizerModule(&pIcu);
#endif
+#ifdef SQLITE_ENABLE_FTS4_UNICODE61
+ sqlite3Fts3UnicodeTokenizer(&pUnicode);
+#endif
+
#ifdef SQLITE_TEST
rc = sqlite3Fts3InitTerm(db);
if( rc!=SQLITE_OK ) return rc;
@@ -120776,6 +119606,10 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){
if( rc==SQLITE_OK ){
if( sqlite3Fts3HashInsert(pHash, "simple", 7, (void *)pSimple)
|| sqlite3Fts3HashInsert(pHash, "porter", 7, (void *)pPorter)
+
+#ifdef SQLITE_ENABLE_FTS4_UNICODE61
+ || sqlite3Fts3HashInsert(pHash, "unicode61", 10, (void *)pUnicode)
+#endif
#ifdef SQLITE_ENABLE_ICU
|| (pIcu && sqlite3Fts3HashInsert(pHash, "icu", 4, (void *)pIcu))
#endif
@@ -121600,6 +120434,7 @@ static int fts3EvalStart(Fts3Cursor *pCsr){
fts3EvalAllocateReaders(pCsr, pCsr->pExpr, &nToken, &nOr, &rc);
/* Determine which, if any, tokens in the expression should be deferred. */
+#ifndef SQLITE_DISABLE_FTS4_DEFERRED
if( rc==SQLITE_OK && nToken>1 && pTab->bFts4 ){
Fts3TokenAndCost *aTC;
Fts3Expr **apOr;
@@ -121630,6 +120465,7 @@ static int fts3EvalStart(Fts3Cursor *pCsr){
sqlite3_free(aTC);
}
}
+#endif
fts3EvalStartReaders(pCsr, pCsr->pExpr, 1, &rc);
return rc;
@@ -122013,6 +120849,7 @@ static int fts3EvalTestExpr(
break;
default: {
+#ifndef SQLITE_DISABLE_FTS4_DEFERRED
if( pCsr->pDeferred
&& (pExpr->iDocid==pCsr->iPrevId || pExpr->bDeferred)
){
@@ -122024,7 +120861,9 @@ static int fts3EvalTestExpr(
*pRc = fts3EvalDeferredPhrase(pCsr, pPhrase);
bHit = (pPhrase->doclist.pList!=0);
pExpr->iDocid = pCsr->iPrevId;
- }else{
+ }else
+#endif
+ {
bHit = (pExpr->bEof==0 && pExpr->iDocid==pCsr->iPrevId);
}
break;
@@ -125238,10 +124077,9 @@ SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(
/*
** Implementation of a special SQL scalar function for testing tokenizers
** designed to be used in concert with the Tcl testing framework. This
-** function must be called with two arguments:
+** function must be called with two or more arguments:
**
-** SELECT <function-name>(<key-name>, <input-string>);
-** SELECT <function-name>(<key-name>, <pointer>);
+** SELECT <function-name>(<key-name>, ..., <input-string>);
**
** where <function-name> is the name passed as the second argument
** to the sqlite3Fts3InitHashTable() function (e.g. 'fts3_tokenizer')
@@ -125278,27 +124116,27 @@ static void testFunc(
const char *zInput;
int nInput;
- const char *zArg = 0;
+ const char *azArg[64];
const char *zToken;
int nToken;
int iStart;
int iEnd;
int iPos;
+ int i;
Tcl_Obj *pRet;
- assert( argc==2 || argc==3 );
+ if( argc<2 ){
+ sqlite3_result_error(context, "insufficient arguments", -1);
+ return;
+ }
nName = sqlite3_value_bytes(argv[0]);
zName = (const char *)sqlite3_value_text(argv[0]);
nInput = sqlite3_value_bytes(argv[argc-1]);
zInput = (const char *)sqlite3_value_text(argv[argc-1]);
- if( argc==3 ){
- zArg = (const char *)sqlite3_value_text(argv[1]);
- }
-
pHash = (Fts3Hash *)sqlite3_user_data(context);
p = (sqlite3_tokenizer_module *)sqlite3Fts3HashFind(pHash, zName, nName+1);
@@ -125312,7 +124150,11 @@ static void testFunc(
pRet = Tcl_NewObj();
Tcl_IncrRefCount(pRet);
- if( SQLITE_OK!=p->xCreate(zArg ? 1 : 0, &zArg, &pTokenizer) ){
+ for(i=1; i<argc-1; i++){
+ azArg[i-1] = (const char *)sqlite3_value_text(argv[i]);
+ }
+
+ if( SQLITE_OK!=p->xCreate(argc-2, azArg, &pTokenizer) ){
zErr = "error in xCreate()";
goto finish;
}
@@ -125496,10 +124338,7 @@ SQLITE_PRIVATE int sqlite3Fts3InitHashTable(
}
#ifdef SQLITE_TEST
if( SQLITE_OK==rc ){
- rc = sqlite3_create_function(db, zTest, 2, any, p, testFunc, 0, 0);
- }
- if( SQLITE_OK==rc ){
- rc = sqlite3_create_function(db, zTest, 3, any, p, testFunc, 0, 0);
+ rc = sqlite3_create_function(db, zTest, -1, any, p, testFunc, 0, 0);
}
if( SQLITE_OK==rc ){
rc = sqlite3_create_function(db, zTest2, 0, any, pdb, intTestFunc, 0, 0);
@@ -125890,7 +124729,8 @@ struct Fts3DeferredToken {
*/
struct Fts3SegReader {
int iIdx; /* Index within level, or 0x7FFFFFFF for PT */
- int bLookup; /* True for a lookup only */
+ u8 bLookup; /* True for a lookup only */
+ u8 rootOnly; /* True for a root-only reader */
sqlite3_int64 iStartBlock; /* Rowid of first leaf block to traverse */
sqlite3_int64 iLeafEndBlock; /* Rowid of final leaf block to traverse */
@@ -125924,7 +124764,7 @@ struct Fts3SegReader {
};
#define fts3SegReaderIsPending(p) ((p)->ppNextElem!=0)
-#define fts3SegReaderIsRootOnly(p) ((p)->aNode==(char *)&(p)[1])
+#define fts3SegReaderIsRootOnly(p) ((p)->rootOnly!=0)
/*
** An instance of this structure is used to create a segment b-tree in the
@@ -127335,7 +126175,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderNew(
}
memset(pReader, 0, sizeof(Fts3SegReader));
pReader->iIdx = iAge;
- pReader->bLookup = bLookup;
+ pReader->bLookup = bLookup!=0;
pReader->iStartBlock = iStartLeaf;
pReader->iLeafEndBlock = iEndLeaf;
pReader->iEndBlock = iEndBlock;
@@ -127343,6 +126183,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderNew(
if( nExtra ){
/* The entire segment is stored in the root node. */
pReader->aNode = (char *)&pReader[1];
+ pReader->rootOnly = 1;
pReader->nNode = nRoot;
memcpy(pReader->aNode, zRoot, nRoot);
memset(&pReader->aNode[nRoot], 0, FTS3_NODE_PADDING);
@@ -128721,7 +127562,7 @@ static int fts3SegmentMerge(
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
+ ** segment. The level of the new segment is equal to the numerically
** greatest segment level currently present in the database for this
** index. The idx of the new segment is always 0. */
if( csr.nSegment==1 ){
@@ -128928,7 +127769,12 @@ static void fts3UpdateDocTotals(
}else{
memset(a, 0, sizeof(u32)*(nStat) );
}
- sqlite3_reset(pStmt);
+ rc = sqlite3_reset(pStmt);
+ if( rc!=SQLITE_OK ){
+ sqlite3_free(a);
+ *pRC = rc;
+ return;
+ }
if( nChng<0 && a[0]<(u32)(-nChng) ){
a[0] = 0;
}else{
@@ -129346,7 +128192,7 @@ static int fts3IncrmergePush(
pNode->key.n = nTerm;
}
}else{
- /* Otherwise, flush the the current node of layer iLayer to disk.
+ /* Otherwise, flush the current node of layer iLayer to disk.
** Then allocate a new, empty sibling node. The key will be written
** into the parent of this node. */
rc = fts3WriteSegment(p, pNode->iBlock, pNode->block.a, pNode->block.n);
@@ -130793,6 +129639,7 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
return rc;
}
+#ifndef SQLITE_DISABLE_FTS4_DEFERRED
/*
** Delete all cached deferred doclists. Deferred doclists are cached
** (allocated) by the sqlite3Fts3CacheDeferredDoclists() function.
@@ -130930,6 +129777,7 @@ SQLITE_PRIVATE int sqlite3Fts3DeferToken(
return SQLITE_OK;
}
+#endif
/*
** SQLite value pRowid contains the rowid of a row that may or may not be
@@ -132651,6 +131499,769 @@ SQLITE_PRIVATE void sqlite3Fts3Matchinfo(
#endif
/************** End of fts3_snippet.c ****************************************/
+/************** Begin file fts3_unicode.c ************************************/
+/*
+** 2012 May 24
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** Implementation of the "unicode" full-text-search tokenizer.
+*/
+
+#ifdef SQLITE_ENABLE_FTS4_UNICODE61
+
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
+
+/* #include <assert.h> */
+/* #include <stdlib.h> */
+/* #include <stdio.h> */
+/* #include <string.h> */
+
+
+/*
+** The following two macros - READ_UTF8 and WRITE_UTF8 - have been copied
+** from the sqlite3 source file utf.c. If this file is compiled as part
+** of the amalgamation, they are not required.
+*/
+#ifndef SQLITE_AMALGAMATION
+
+static const unsigned char sqlite3Utf8Trans1[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
+};
+
+#define READ_UTF8(zIn, zTerm, c) \
+ c = *(zIn++); \
+ if( c>=0xc0 ){ \
+ c = sqlite3Utf8Trans1[c-0xc0]; \
+ while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \
+ c = (c<<6) + (0x3f & *(zIn++)); \
+ } \
+ if( c<0x80 \
+ || (c&0xFFFFF800)==0xD800 \
+ || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \
+ }
+
+#define WRITE_UTF8(zOut, c) { \
+ if( c<0x00080 ){ \
+ *zOut++ = (u8)(c&0xFF); \
+ } \
+ else if( c<0x00800 ){ \
+ *zOut++ = 0xC0 + (u8)((c>>6)&0x1F); \
+ *zOut++ = 0x80 + (u8)(c & 0x3F); \
+ } \
+ else if( c<0x10000 ){ \
+ *zOut++ = 0xE0 + (u8)((c>>12)&0x0F); \
+ *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); \
+ *zOut++ = 0x80 + (u8)(c & 0x3F); \
+ }else{ \
+ *zOut++ = 0xF0 + (u8)((c>>18) & 0x07); \
+ *zOut++ = 0x80 + (u8)((c>>12) & 0x3F); \
+ *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); \
+ *zOut++ = 0x80 + (u8)(c & 0x3F); \
+ } \
+}
+
+#endif /* ifndef SQLITE_AMALGAMATION */
+
+typedef struct unicode_tokenizer unicode_tokenizer;
+typedef struct unicode_cursor unicode_cursor;
+
+struct unicode_tokenizer {
+ sqlite3_tokenizer base;
+ int bRemoveDiacritic;
+ int nException;
+ int *aiException;
+};
+
+struct unicode_cursor {
+ sqlite3_tokenizer_cursor base;
+ const unsigned char *aInput; /* Input text being tokenized */
+ int nInput; /* Size of aInput[] in bytes */
+ int iOff; /* Current offset within aInput[] */
+ int iToken; /* Index of next token to be returned */
+ char *zToken; /* storage for current token */
+ int nAlloc; /* space allocated at zToken */
+};
+
+
+/*
+** Destroy a tokenizer allocated by unicodeCreate().
+*/
+static int unicodeDestroy(sqlite3_tokenizer *pTokenizer){
+ if( pTokenizer ){
+ unicode_tokenizer *p = (unicode_tokenizer *)pTokenizer;
+ sqlite3_free(p->aiException);
+ sqlite3_free(p);
+ }
+ return SQLITE_OK;
+}
+
+/*
+** As part of a tokenchars= or separators= option, the CREATE VIRTUAL TABLE
+** statement has specified that the tokenizer for this table shall consider
+** all characters in string zIn/nIn to be separators (if bAlnum==0) or
+** token characters (if bAlnum==1).
+**
+** For each codepoint in the zIn/nIn string, this function checks if the
+** sqlite3FtsUnicodeIsalnum() function already returns the desired result.
+** If so, no action is taken. Otherwise, the codepoint is added to the
+** unicode_tokenizer.aiException[] array. For the purposes of tokenization,
+** the return value of sqlite3FtsUnicodeIsalnum() is inverted for all
+** codepoints in the aiException[] array.
+**
+** If a standalone diacritic mark (one that sqlite3FtsUnicodeIsdiacritic()
+** identifies as a diacritic) occurs in the zIn/nIn string it is ignored.
+** It is not possible to change the behaviour of the tokenizer with respect
+** to these codepoints.
+*/
+static int unicodeAddExceptions(
+ unicode_tokenizer *p, /* Tokenizer to add exceptions to */
+ int bAlnum, /* Replace Isalnum() return value with this */
+ const char *zIn, /* Array of characters to make exceptions */
+ int nIn /* Length of z in bytes */
+){
+ const unsigned char *z = (const unsigned char *)zIn;
+ const unsigned char *zTerm = &z[nIn];
+ int iCode;
+ int nEntry = 0;
+
+ assert( bAlnum==0 || bAlnum==1 );
+
+ while( z<zTerm ){
+ READ_UTF8(z, zTerm, iCode);
+ assert( (sqlite3FtsUnicodeIsalnum(iCode) & 0xFFFFFFFE)==0 );
+ if( sqlite3FtsUnicodeIsalnum(iCode)!=bAlnum
+ && sqlite3FtsUnicodeIsdiacritic(iCode)==0
+ ){
+ nEntry++;
+ }
+ }
+
+ if( nEntry ){
+ int *aNew; /* New aiException[] array */
+ int nNew; /* Number of valid entries in array aNew[] */
+
+ aNew = sqlite3_realloc(p->aiException, (p->nException+nEntry)*sizeof(int));
+ if( aNew==0 ) return SQLITE_NOMEM;
+ nNew = p->nException;
+
+ z = (const unsigned char *)zIn;
+ while( z<zTerm ){
+ READ_UTF8(z, zTerm, iCode);
+ if( sqlite3FtsUnicodeIsalnum(iCode)!=bAlnum
+ && sqlite3FtsUnicodeIsdiacritic(iCode)==0
+ ){
+ int i, j;
+ for(i=0; i<nNew && aNew[i]<iCode; i++);
+ for(j=nNew; j>i; j--) aNew[j] = aNew[j-1];
+ aNew[i] = iCode;
+ nNew++;
+ }
+ }
+ p->aiException = aNew;
+ p->nException = nNew;
+ }
+
+ return SQLITE_OK;
+}
+
+/*
+** Return true if the p->aiException[] array contains the value iCode.
+*/
+static int unicodeIsException(unicode_tokenizer *p, int iCode){
+ if( p->nException>0 ){
+ int *a = p->aiException;
+ int iLo = 0;
+ int iHi = p->nException-1;
+
+ while( iHi>=iLo ){
+ int iTest = (iHi + iLo) / 2;
+ if( iCode==a[iTest] ){
+ return 1;
+ }else if( iCode>a[iTest] ){
+ iLo = iTest+1;
+ }else{
+ iHi = iTest-1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+** Return true if, for the purposes of tokenization, codepoint iCode is
+** considered a token character (not a separator).
+*/
+static int unicodeIsAlnum(unicode_tokenizer *p, int iCode){
+ assert( (sqlite3FtsUnicodeIsalnum(iCode) & 0xFFFFFFFE)==0 );
+ return sqlite3FtsUnicodeIsalnum(iCode) ^ unicodeIsException(p, iCode);
+}
+
+/*
+** Create a new tokenizer instance.
+*/
+static int unicodeCreate(
+ int nArg, /* Size of array argv[] */
+ const char * const *azArg, /* Tokenizer creation arguments */
+ sqlite3_tokenizer **pp /* OUT: New tokenizer handle */
+){
+ unicode_tokenizer *pNew; /* New tokenizer object */
+ int i;
+ int rc = SQLITE_OK;
+
+ pNew = (unicode_tokenizer *) sqlite3_malloc(sizeof(unicode_tokenizer));
+ if( pNew==NULL ) return SQLITE_NOMEM;
+ memset(pNew, 0, sizeof(unicode_tokenizer));
+ pNew->bRemoveDiacritic = 1;
+
+ for(i=0; rc==SQLITE_OK && i<nArg; i++){
+ const char *z = azArg[i];
+ int n = strlen(z);
+
+ if( n==19 && memcmp("remove_diacritics=1", z, 19)==0 ){
+ pNew->bRemoveDiacritic = 1;
+ }
+ else if( n==19 && memcmp("remove_diacritics=0", z, 19)==0 ){
+ pNew->bRemoveDiacritic = 0;
+ }
+ else if( n>=11 && memcmp("tokenchars=", z, 11)==0 ){
+ rc = unicodeAddExceptions(pNew, 1, &z[11], n-11);
+ }
+ else if( n>=11 && memcmp("separators=", z, 11)==0 ){
+ rc = unicodeAddExceptions(pNew, 0, &z[11], n-11);
+ }
+ else{
+ /* Unrecognized argument */
+ rc = SQLITE_ERROR;
+ }
+ }
+
+ if( rc!=SQLITE_OK ){
+ unicodeDestroy((sqlite3_tokenizer *)pNew);
+ pNew = 0;
+ }
+ *pp = (sqlite3_tokenizer *)pNew;
+ return rc;
+}
+
+/*
+** Prepare to begin tokenizing a particular string. The input
+** string to be tokenized is pInput[0..nBytes-1]. A cursor
+** used to incrementally tokenize this string is returned in
+** *ppCursor.
+*/
+static int unicodeOpen(
+ sqlite3_tokenizer *p, /* The tokenizer */
+ const char *aInput, /* Input string */
+ int nInput, /* Size of string aInput in bytes */
+ sqlite3_tokenizer_cursor **pp /* OUT: New cursor object */
+){
+ unicode_cursor *pCsr;
+
+ pCsr = (unicode_cursor *)sqlite3_malloc(sizeof(unicode_cursor));
+ if( pCsr==0 ){
+ return SQLITE_NOMEM;
+ }
+ memset(pCsr, 0, sizeof(unicode_cursor));
+
+ pCsr->aInput = (const unsigned char *)aInput;
+ if( aInput==0 ){
+ pCsr->nInput = 0;
+ }else if( nInput<0 ){
+ pCsr->nInput = (int)strlen(aInput);
+ }else{
+ pCsr->nInput = nInput;
+ }
+
+ *pp = &pCsr->base;
+ UNUSED_PARAMETER(p);
+ return SQLITE_OK;
+}
+
+/*
+** Close a tokenization cursor previously opened by a call to
+** simpleOpen() above.
+*/
+static int unicodeClose(sqlite3_tokenizer_cursor *pCursor){
+ unicode_cursor *pCsr = (unicode_cursor *) pCursor;
+ sqlite3_free(pCsr->zToken);
+ sqlite3_free(pCsr);
+ return SQLITE_OK;
+}
+
+/*
+** Extract the next token from a tokenization cursor. The cursor must
+** have been opened by a prior call to simpleOpen().
+*/
+static int unicodeNext(
+ sqlite3_tokenizer_cursor *pC, /* Cursor returned by simpleOpen */
+ const char **paToken, /* OUT: Token text */
+ int *pnToken, /* OUT: Number of bytes at *paToken */
+ int *piStart, /* OUT: Starting offset of token */
+ int *piEnd, /* OUT: Ending offset of token */
+ int *piPos /* OUT: Position integer of token */
+){
+ unicode_cursor *pCsr = (unicode_cursor *)pC;
+ unicode_tokenizer *p = ((unicode_tokenizer *)pCsr->base.pTokenizer);
+ int iCode;
+ char *zOut;
+ const unsigned char *z = &pCsr->aInput[pCsr->iOff];
+ const unsigned char *zStart = z;
+ const unsigned char *zEnd;
+ const unsigned char *zTerm = &pCsr->aInput[pCsr->nInput];
+
+ /* Scan past any delimiter characters before the start of the next token.
+ ** Return SQLITE_DONE early if this takes us all the way to the end of
+ ** the input. */
+ while( z<zTerm ){
+ READ_UTF8(z, zTerm, iCode);
+ if( unicodeIsAlnum(p, iCode) ) break;
+ zStart = z;
+ }
+ if( zStart>=zTerm ) return SQLITE_DONE;
+
+ zOut = pCsr->zToken;
+ do {
+ int iOut;
+
+ /* Grow the output buffer if required. */
+ if( (zOut-pCsr->zToken)>=(pCsr->nAlloc-4) ){
+ char *zNew = sqlite3_realloc(pCsr->zToken, pCsr->nAlloc+64);
+ if( !zNew ) return SQLITE_NOMEM;
+ zOut = &zNew[zOut - pCsr->zToken];
+ pCsr->zToken = zNew;
+ pCsr->nAlloc += 64;
+ }
+
+ /* Write the folded case of the last character read to the output */
+ zEnd = z;
+ iOut = sqlite3FtsUnicodeFold(iCode, p->bRemoveDiacritic);
+ if( iOut ){
+ WRITE_UTF8(zOut, iOut);
+ }
+
+ /* If the cursor is not at EOF, read the next character */
+ if( z>=zTerm ) break;
+ READ_UTF8(z, zTerm, iCode);
+ }while( unicodeIsAlnum(p, iCode)
+ || sqlite3FtsUnicodeIsdiacritic(iCode)
+ );
+
+ /* Set the output variables and return. */
+ pCsr->iOff = (z - pCsr->aInput);
+ *paToken = pCsr->zToken;
+ *pnToken = zOut - pCsr->zToken;
+ *piStart = (zStart - pCsr->aInput);
+ *piEnd = (zEnd - pCsr->aInput);
+ *piPos = pCsr->iToken++;
+ return SQLITE_OK;
+}
+
+/*
+** Set *ppModule to a pointer to the sqlite3_tokenizer_module
+** structure for the unicode tokenizer.
+*/
+SQLITE_PRIVATE void sqlite3Fts3UnicodeTokenizer(sqlite3_tokenizer_module const **ppModule){
+ static const sqlite3_tokenizer_module module = {
+ 0,
+ unicodeCreate,
+ unicodeDestroy,
+ unicodeOpen,
+ unicodeClose,
+ unicodeNext,
+ 0,
+ };
+ *ppModule = &module;
+}
+
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */
+#endif /* ifndef SQLITE_ENABLE_FTS4_UNICODE61 */
+
+/************** End of fts3_unicode.c ****************************************/
+/************** Begin file fts3_unicode2.c ***********************************/
+/*
+** 2012 May 25
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+*/
+
+/*
+** DO NOT EDIT THIS MACHINE GENERATED FILE.
+*/
+
+#if defined(SQLITE_ENABLE_FTS4_UNICODE61)
+#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
+
+/* #include <assert.h> */
+
+/*
+** Return true if the argument corresponds to a unicode codepoint
+** classified as either a letter or a number. Otherwise false.
+**
+** The results are undefined if the value passed to this function
+** is less than zero.
+*/
+SQLITE_PRIVATE int sqlite3FtsUnicodeIsalnum(int c){
+ /* Each unsigned integer in the following array corresponds to a contiguous
+ ** range of unicode codepoints that are not either letters or numbers (i.e.
+ ** codepoints for which this function should return 0).
+ **
+ ** The most significant 22 bits in each 32-bit value contain the first
+ ** codepoint in the range. The least significant 10 bits are used to store
+ ** the size of the range (always at least 1). In other words, the value
+ ** ((C<<22) + N) represents a range of N codepoints starting with codepoint
+ ** C. It is not possible to represent a range larger than 1023 codepoints
+ ** using this format.
+ */
+ const static unsigned int aEntry[] = {
+ 0x00000030, 0x0000E807, 0x00016C06, 0x0001EC2F, 0x0002AC07,
+ 0x0002D001, 0x0002D803, 0x0002EC01, 0x0002FC01, 0x00035C01,
+ 0x0003DC01, 0x000B0804, 0x000B480E, 0x000B9407, 0x000BB401,
+ 0x000BBC81, 0x000DD401, 0x000DF801, 0x000E1002, 0x000E1C01,
+ 0x000FD801, 0x00120808, 0x00156806, 0x00162402, 0x00163C01,
+ 0x00164437, 0x0017CC02, 0x00180005, 0x00181816, 0x00187802,
+ 0x00192C15, 0x0019A804, 0x0019C001, 0x001B5001, 0x001B580F,
+ 0x001B9C07, 0x001BF402, 0x001C000E, 0x001C3C01, 0x001C4401,
+ 0x001CC01B, 0x001E980B, 0x001FAC09, 0x001FD804, 0x00205804,
+ 0x00206C09, 0x00209403, 0x0020A405, 0x0020C00F, 0x00216403,
+ 0x00217801, 0x0023901B, 0x00240004, 0x0024E803, 0x0024F812,
+ 0x00254407, 0x00258804, 0x0025C001, 0x00260403, 0x0026F001,
+ 0x0026F807, 0x00271C02, 0x00272C03, 0x00275C01, 0x00278802,
+ 0x0027C802, 0x0027E802, 0x00280403, 0x0028F001, 0x0028F805,
+ 0x00291C02, 0x00292C03, 0x00294401, 0x0029C002, 0x0029D401,
+ 0x002A0403, 0x002AF001, 0x002AF808, 0x002B1C03, 0x002B2C03,
+ 0x002B8802, 0x002BC002, 0x002C0403, 0x002CF001, 0x002CF807,
+ 0x002D1C02, 0x002D2C03, 0x002D5802, 0x002D8802, 0x002DC001,
+ 0x002E0801, 0x002EF805, 0x002F1803, 0x002F2804, 0x002F5C01,
+ 0x002FCC08, 0x00300403, 0x0030F807, 0x00311803, 0x00312804,
+ 0x00315402, 0x00318802, 0x0031FC01, 0x00320802, 0x0032F001,
+ 0x0032F807, 0x00331803, 0x00332804, 0x00335402, 0x00338802,
+ 0x00340802, 0x0034F807, 0x00351803, 0x00352804, 0x00355C01,
+ 0x00358802, 0x0035E401, 0x00360802, 0x00372801, 0x00373C06,
+ 0x00375801, 0x00376008, 0x0037C803, 0x0038C401, 0x0038D007,
+ 0x0038FC01, 0x00391C09, 0x00396802, 0x003AC401, 0x003AD006,
+ 0x003AEC02, 0x003B2006, 0x003C041F, 0x003CD00C, 0x003DC417,
+ 0x003E340B, 0x003E6424, 0x003EF80F, 0x003F380D, 0x0040AC14,
+ 0x00412806, 0x00415804, 0x00417803, 0x00418803, 0x00419C07,
+ 0x0041C404, 0x0042080C, 0x00423C01, 0x00426806, 0x0043EC01,
+ 0x004D740C, 0x004E400A, 0x00500001, 0x0059B402, 0x005A0001,
+ 0x005A6C02, 0x005BAC03, 0x005C4803, 0x005CC805, 0x005D4802,
+ 0x005DC802, 0x005ED023, 0x005F6004, 0x005F7401, 0x0060000F,
+ 0x0062A401, 0x0064800C, 0x0064C00C, 0x00650001, 0x00651002,
+ 0x0066C011, 0x00672002, 0x00677822, 0x00685C05, 0x00687802,
+ 0x0069540A, 0x0069801D, 0x0069FC01, 0x006A8007, 0x006AA006,
+ 0x006C0005, 0x006CD011, 0x006D6823, 0x006E0003, 0x006E840D,
+ 0x006F980E, 0x006FF004, 0x00709014, 0x0070EC05, 0x0071F802,
+ 0x00730008, 0x00734019, 0x0073B401, 0x0073C803, 0x00770027,
+ 0x0077F004, 0x007EF401, 0x007EFC03, 0x007F3403, 0x007F7403,
+ 0x007FB403, 0x007FF402, 0x00800065, 0x0081A806, 0x0081E805,
+ 0x00822805, 0x0082801A, 0x00834021, 0x00840002, 0x00840C04,
+ 0x00842002, 0x00845001, 0x00845803, 0x00847806, 0x00849401,
+ 0x00849C01, 0x0084A401, 0x0084B801, 0x0084E802, 0x00850005,
+ 0x00852804, 0x00853C01, 0x00864264, 0x00900027, 0x0091000B,
+ 0x0092704E, 0x00940200, 0x009C0475, 0x009E53B9, 0x00AD400A,
+ 0x00B39406, 0x00B3BC03, 0x00B3E404, 0x00B3F802, 0x00B5C001,
+ 0x00B5FC01, 0x00B7804F, 0x00B8C00C, 0x00BA001A, 0x00BA6C59,
+ 0x00BC00D6, 0x00BFC00C, 0x00C00005, 0x00C02019, 0x00C0A807,
+ 0x00C0D802, 0x00C0F403, 0x00C26404, 0x00C28001, 0x00C3EC01,
+ 0x00C64002, 0x00C6580A, 0x00C70024, 0x00C8001F, 0x00C8A81E,
+ 0x00C94001, 0x00C98020, 0x00CA2827, 0x00CB003F, 0x00CC0100,
+ 0x01370040, 0x02924037, 0x0293F802, 0x02983403, 0x0299BC10,
+ 0x029A7C01, 0x029BC008, 0x029C0017, 0x029C8002, 0x029E2402,
+ 0x02A00801, 0x02A01801, 0x02A02C01, 0x02A08C09, 0x02A0D804,
+ 0x02A1D004, 0x02A20002, 0x02A2D011, 0x02A33802, 0x02A38012,
+ 0x02A3E003, 0x02A4980A, 0x02A51C0D, 0x02A57C01, 0x02A60004,
+ 0x02A6CC1B, 0x02A77802, 0x02A8A40E, 0x02A90C01, 0x02A93002,
+ 0x02A97004, 0x02A9DC03, 0x02A9EC01, 0x02AAC001, 0x02AAC803,
+ 0x02AADC02, 0x02AAF802, 0x02AB0401, 0x02AB7802, 0x02ABAC07,
+ 0x02ABD402, 0x02AF8C0B, 0x03600001, 0x036DFC02, 0x036FFC02,
+ 0x037FFC02, 0x03E3FC01, 0x03EC7801, 0x03ECA401, 0x03EEC810,
+ 0x03F4F802, 0x03F7F002, 0x03F8001A, 0x03F88007, 0x03F8C023,
+ 0x03F95013, 0x03F9A004, 0x03FBFC01, 0x03FC040F, 0x03FC6807,
+ 0x03FCEC06, 0x03FD6C0B, 0x03FF8007, 0x03FFA007, 0x03FFE405,
+ 0x04040003, 0x0404DC09, 0x0405E411, 0x0406400C, 0x0407402E,
+ 0x040E7C01, 0x040F4001, 0x04215C01, 0x04247C01, 0x0424FC01,
+ 0x04280403, 0x04281402, 0x04283004, 0x0428E003, 0x0428FC01,
+ 0x04294009, 0x0429FC01, 0x042CE407, 0x04400003, 0x0440E016,
+ 0x04420003, 0x0442C012, 0x04440003, 0x04449C0E, 0x04450004,
+ 0x04460003, 0x0446CC0E, 0x04471404, 0x045AAC0D, 0x0491C004,
+ 0x05BD442E, 0x05BE3C04, 0x074000F6, 0x07440027, 0x0744A4B5,
+ 0x07480046, 0x074C0057, 0x075B0401, 0x075B6C01, 0x075BEC01,
+ 0x075C5401, 0x075CD401, 0x075D3C01, 0x075DBC01, 0x075E2401,
+ 0x075EA401, 0x075F0C01, 0x07BBC002, 0x07C0002C, 0x07C0C064,
+ 0x07C2800F, 0x07C2C40E, 0x07C3040F, 0x07C3440F, 0x07C4401F,
+ 0x07C4C03C, 0x07C5C02B, 0x07C7981D, 0x07C8402B, 0x07C90009,
+ 0x07C94002, 0x07CC0021, 0x07CCC006, 0x07CCDC46, 0x07CE0014,
+ 0x07CE8025, 0x07CF1805, 0x07CF8011, 0x07D0003F, 0x07D10001,
+ 0x07D108B6, 0x07D3E404, 0x07D4003E, 0x07D50004, 0x07D54018,
+ 0x07D7EC46, 0x07D9140B, 0x07DA0046, 0x07DC0074, 0x38000401,
+ 0x38008060, 0x380400F0, 0x3C000001, 0x3FFFF401, 0x40000001,
+ 0x43FFF401,
+ };
+ static const unsigned int aAscii[4] = {
+ 0xFFFFFFFF, 0xFC00FFFF, 0xF8000001, 0xF8000001,
+ };
+
+ if( c<128 ){
+ return ( (aAscii[c >> 5] & (1 << (c & 0x001F)))==0 );
+ }else if( c<(1<<22) ){
+ unsigned int key = (((unsigned int)c)<<10) | 0x000003FF;
+ int iRes;
+ int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1;
+ int iLo = 0;
+ while( iHi>=iLo ){
+ int iTest = (iHi + iLo) / 2;
+ if( key >= aEntry[iTest] ){
+ iRes = iTest;
+ iLo = iTest+1;
+ }else{
+ iHi = iTest-1;
+ }
+ }
+ assert( aEntry[0]<key );
+ assert( key>=aEntry[iRes] );
+ return (((unsigned int)c) >= ((aEntry[iRes]>>10) + (aEntry[iRes]&0x3FF)));
+ }
+ return 1;
+}
+
+
+/*
+** If the argument is a codepoint corresponding to a lowercase letter
+** in the ASCII range with a diacritic added, return the codepoint
+** of the ASCII letter only. For example, if passed 235 - "LATIN
+** SMALL LETTER E WITH DIAERESIS" - return 65 ("LATIN SMALL LETTER
+** E"). The resuls of passing a codepoint that corresponds to an
+** uppercase letter are undefined.
+*/
+static int remove_diacritic(int c){
+ unsigned short aDia[] = {
+ 0, 1797, 1848, 1859, 1891, 1928, 1940, 1995,
+ 2024, 2040, 2060, 2110, 2168, 2206, 2264, 2286,
+ 2344, 2383, 2472, 2488, 2516, 2596, 2668, 2732,
+ 2782, 2842, 2894, 2954, 2984, 3000, 3028, 3336,
+ 3456, 3696, 3712, 3728, 3744, 3896, 3912, 3928,
+ 3968, 4008, 4040, 4106, 4138, 4170, 4202, 4234,
+ 4266, 4296, 4312, 4344, 4408, 4424, 4472, 4504,
+ 6148, 6198, 6264, 6280, 6360, 6429, 6505, 6529,
+ 61448, 61468, 61534, 61592, 61642, 61688, 61704, 61726,
+ 61784, 61800, 61836, 61880, 61914, 61948, 61998, 62122,
+ 62154, 62200, 62218, 62302, 62364, 62442, 62478, 62536,
+ 62554, 62584, 62604, 62640, 62648, 62656, 62664, 62730,
+ 62924, 63050, 63082, 63274, 63390,
+ };
+ char aChar[] = {
+ '\0', 'a', 'c', 'e', 'i', 'n', 'o', 'u', 'y', 'y', 'a', 'c',
+ 'd', 'e', 'e', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'o', 'r',
+ 's', 't', 'u', 'u', 'w', 'y', 'z', 'o', 'u', 'a', 'i', 'o',
+ 'u', 'g', 'k', 'o', 'j', 'g', 'n', 'a', 'e', 'i', 'o', 'r',
+ 'u', 's', 't', 'h', 'a', 'e', 'o', 'y', '\0', '\0', '\0', '\0',
+ '\0', '\0', '\0', '\0', 'a', 'b', 'd', 'd', 'e', 'f', 'g', 'h',
+ 'h', 'i', 'k', 'l', 'l', 'm', 'n', 'p', 'r', 'r', 's', 't',
+ 'u', 'v', 'w', 'w', 'x', 'y', 'z', 'h', 't', 'w', 'y', 'a',
+ 'e', 'i', 'o', 'u', 'y',
+ };
+
+ unsigned int key = (((unsigned int)c)<<3) | 0x00000007;
+ int iRes = 0;
+ int iHi = sizeof(aDia)/sizeof(aDia[0]) - 1;
+ int iLo = 0;
+ while( iHi>=iLo ){
+ int iTest = (iHi + iLo) / 2;
+ if( key >= aDia[iTest] ){
+ iRes = iTest;
+ iLo = iTest+1;
+ }else{
+ iHi = iTest-1;
+ }
+ }
+ assert( key>=aDia[iRes] );
+ return ((c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : (int)aChar[iRes]);
+};
+
+
+/*
+** Return true if the argument interpreted as a unicode codepoint
+** is a diacritical modifier character.
+*/
+SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int c){
+ unsigned int mask0 = 0x08029FDF;
+ unsigned int mask1 = 0x000361F8;
+ if( c<768 || c>817 ) return 0;
+ return (c < 768+32) ?
+ (mask0 & (1 << (c-768))) :
+ (mask1 & (1 << (c-768-32)));
+}
+
+
+/*
+** Interpret the argument as a unicode codepoint. If the codepoint
+** is an upper case character that has a lower case equivalent,
+** return the codepoint corresponding to the lower case version.
+** Otherwise, return a copy of the argument.
+**
+** The results are undefined if the value passed to this function
+** is less than zero.
+*/
+SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int bRemoveDiacritic){
+ /* Each entry in the following array defines a rule for folding a range
+ ** of codepoints to lower case. The rule applies to a range of nRange
+ ** codepoints starting at codepoint iCode.
+ **
+ ** If the least significant bit in flags is clear, then the rule applies
+ ** to all nRange codepoints (i.e. all nRange codepoints are upper case and
+ ** need to be folded). Or, if it is set, then the rule only applies to
+ ** every second codepoint in the range, starting with codepoint C.
+ **
+ ** The 7 most significant bits in flags are an index into the aiOff[]
+ ** array. If a specific codepoint C does require folding, then its lower
+ ** case equivalent is ((C + aiOff[flags>>1]) & 0xFFFF).
+ **
+ ** The contents of this array are generated by parsing the CaseFolding.txt
+ ** file distributed as part of the "Unicode Character Database". See
+ ** http://www.unicode.org for details.
+ */
+ static const struct TableEntry {
+ unsigned short iCode;
+ unsigned char flags;
+ unsigned char nRange;
+ } aEntry[] = {
+ {65, 14, 26}, {181, 64, 1}, {192, 14, 23},
+ {216, 14, 7}, {256, 1, 48}, {306, 1, 6},
+ {313, 1, 16}, {330, 1, 46}, {376, 116, 1},
+ {377, 1, 6}, {383, 104, 1}, {385, 50, 1},
+ {386, 1, 4}, {390, 44, 1}, {391, 0, 1},
+ {393, 42, 2}, {395, 0, 1}, {398, 32, 1},
+ {399, 38, 1}, {400, 40, 1}, {401, 0, 1},
+ {403, 42, 1}, {404, 46, 1}, {406, 52, 1},
+ {407, 48, 1}, {408, 0, 1}, {412, 52, 1},
+ {413, 54, 1}, {415, 56, 1}, {416, 1, 6},
+ {422, 60, 1}, {423, 0, 1}, {425, 60, 1},
+ {428, 0, 1}, {430, 60, 1}, {431, 0, 1},
+ {433, 58, 2}, {435, 1, 4}, {439, 62, 1},
+ {440, 0, 1}, {444, 0, 1}, {452, 2, 1},
+ {453, 0, 1}, {455, 2, 1}, {456, 0, 1},
+ {458, 2, 1}, {459, 1, 18}, {478, 1, 18},
+ {497, 2, 1}, {498, 1, 4}, {502, 122, 1},
+ {503, 134, 1}, {504, 1, 40}, {544, 110, 1},
+ {546, 1, 18}, {570, 70, 1}, {571, 0, 1},
+ {573, 108, 1}, {574, 68, 1}, {577, 0, 1},
+ {579, 106, 1}, {580, 28, 1}, {581, 30, 1},
+ {582, 1, 10}, {837, 36, 1}, {880, 1, 4},
+ {886, 0, 1}, {902, 18, 1}, {904, 16, 3},
+ {908, 26, 1}, {910, 24, 2}, {913, 14, 17},
+ {931, 14, 9}, {962, 0, 1}, {975, 4, 1},
+ {976, 140, 1}, {977, 142, 1}, {981, 146, 1},
+ {982, 144, 1}, {984, 1, 24}, {1008, 136, 1},
+ {1009, 138, 1}, {1012, 130, 1}, {1013, 128, 1},
+ {1015, 0, 1}, {1017, 152, 1}, {1018, 0, 1},
+ {1021, 110, 3}, {1024, 34, 16}, {1040, 14, 32},
+ {1120, 1, 34}, {1162, 1, 54}, {1216, 6, 1},
+ {1217, 1, 14}, {1232, 1, 88}, {1329, 22, 38},
+ {4256, 66, 38}, {4295, 66, 1}, {4301, 66, 1},
+ {7680, 1, 150}, {7835, 132, 1}, {7838, 96, 1},
+ {7840, 1, 96}, {7944, 150, 8}, {7960, 150, 6},
+ {7976, 150, 8}, {7992, 150, 8}, {8008, 150, 6},
+ {8025, 151, 8}, {8040, 150, 8}, {8072, 150, 8},
+ {8088, 150, 8}, {8104, 150, 8}, {8120, 150, 2},
+ {8122, 126, 2}, {8124, 148, 1}, {8126, 100, 1},
+ {8136, 124, 4}, {8140, 148, 1}, {8152, 150, 2},
+ {8154, 120, 2}, {8168, 150, 2}, {8170, 118, 2},
+ {8172, 152, 1}, {8184, 112, 2}, {8186, 114, 2},
+ {8188, 148, 1}, {8486, 98, 1}, {8490, 92, 1},
+ {8491, 94, 1}, {8498, 12, 1}, {8544, 8, 16},
+ {8579, 0, 1}, {9398, 10, 26}, {11264, 22, 47},
+ {11360, 0, 1}, {11362, 88, 1}, {11363, 102, 1},
+ {11364, 90, 1}, {11367, 1, 6}, {11373, 84, 1},
+ {11374, 86, 1}, {11375, 80, 1}, {11376, 82, 1},
+ {11378, 0, 1}, {11381, 0, 1}, {11390, 78, 2},
+ {11392, 1, 100}, {11499, 1, 4}, {11506, 0, 1},
+ {42560, 1, 46}, {42624, 1, 24}, {42786, 1, 14},
+ {42802, 1, 62}, {42873, 1, 4}, {42877, 76, 1},
+ {42878, 1, 10}, {42891, 0, 1}, {42893, 74, 1},
+ {42896, 1, 4}, {42912, 1, 10}, {42922, 72, 1},
+ {65313, 14, 26},
+ };
+ static const unsigned short aiOff[] = {
+ 1, 2, 8, 15, 16, 26, 28, 32,
+ 37, 38, 40, 48, 63, 64, 69, 71,
+ 79, 80, 116, 202, 203, 205, 206, 207,
+ 209, 210, 211, 213, 214, 217, 218, 219,
+ 775, 7264, 10792, 10795, 23228, 23256, 30204, 54721,
+ 54753, 54754, 54756, 54787, 54793, 54809, 57153, 57274,
+ 57921, 58019, 58363, 61722, 65268, 65341, 65373, 65406,
+ 65408, 65410, 65415, 65424, 65436, 65439, 65450, 65462,
+ 65472, 65476, 65478, 65480, 65482, 65488, 65506, 65511,
+ 65514, 65521, 65527, 65528, 65529,
+ };
+
+ int ret = c;
+
+ assert( c>=0 );
+ assert( sizeof(unsigned short)==2 && sizeof(unsigned char)==1 );
+
+ if( c<128 ){
+ if( c>='A' && c<='Z' ) ret = c + ('a' - 'A');
+ }else if( c<65536 ){
+ int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1;
+ int iLo = 0;
+ int iRes = -1;
+
+ while( iHi>=iLo ){
+ int iTest = (iHi + iLo) / 2;
+ int cmp = (c - aEntry[iTest].iCode);
+ if( cmp>=0 ){
+ iRes = iTest;
+ iLo = iTest+1;
+ }else{
+ iHi = iTest-1;
+ }
+ }
+ assert( iRes<0 || c>=aEntry[iRes].iCode );
+
+ if( iRes>=0 ){
+ const struct TableEntry *p = &aEntry[iRes];
+ if( c<(p->iCode + p->nRange) && 0==(0x01 & p->flags & (p->iCode ^ c)) ){
+ ret = (c + (aiOff[p->flags>>1])) & 0x0000FFFF;
+ assert( ret>0 );
+ }
+ }
+
+ if( bRemoveDiacritic ) ret = remove_diacritic(ret);
+ }
+
+ else if( c>=66560 && c<66600 ){
+ ret = c + 40;
+ }
+
+ return ret;
+}
+#endif /* defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) */
+#endif /* !defined(SQLITE_ENABLE_FTS4_UNICODE61) */
+
+/************** End of fts3_unicode2.c ***************************************/
/************** Begin file rtree.c *******************************************/
/*
** 2001 September 15
@@ -135392,6 +135003,36 @@ static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){
}
/*
+** Rounding constants for float->double conversion.
+*/
+#define RNDTOWARDS (1.0 - 1.0/8388608.0) /* Round towards zero */
+#define RNDAWAY (1.0 + 1.0/8388608.0) /* Round away from zero */
+
+#if !defined(SQLITE_RTREE_INT_ONLY)
+/*
+** Convert an sqlite3_value into an RtreeValue (presumably a float)
+** while taking care to round toward negative or positive, respectively.
+*/
+static RtreeValue rtreeValueDown(sqlite3_value *v){
+ double d = sqlite3_value_double(v);
+ float f = (float)d;
+ if( f>d ){
+ f = (float)(d*(d<0 ? RNDAWAY : RNDTOWARDS));
+ }
+ return f;
+}
+static RtreeValue rtreeValueUp(sqlite3_value *v){
+ double d = sqlite3_value_double(v);
+ float f = (float)d;
+ if( f<d ){
+ f = (float)(d*(d<0 ? RNDTOWARDS : RNDAWAY));
+ }
+ return f;
+}
+#endif /* !defined(SQLITE_RTREE_INT_ONLY) */
+
+
+/*
** The xUpdate method for rtree module virtual tables.
*/
static int rtreeUpdate(
@@ -135427,8 +135068,8 @@ static int rtreeUpdate(
#ifndef SQLITE_RTREE_INT_ONLY
if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
for(ii=0; ii<(pRtree->nDim*2); ii+=2){
- cell.aCoord[ii].f = (RtreeValue)sqlite3_value_double(azData[ii+3]);
- cell.aCoord[ii+1].f = (RtreeValue)sqlite3_value_double(azData[ii+4]);
+ cell.aCoord[ii].f = rtreeValueDown(azData[ii+3]);
+ cell.aCoord[ii+1].f = rtreeValueUp(azData[ii+4]);
if( cell.aCoord[ii].f>cell.aCoord[ii+1].f ){
rc = SQLITE_CONSTRAINT;
goto constraint;
@@ -136681,7 +136322,7 @@ static int icuNext(
while( iStart<iEnd ){
int iWhite = iStart;
- U8_NEXT(pCsr->aChar, iWhite, pCsr->nChar, c);
+ U16_NEXT(pCsr->aChar, iWhite, pCsr->nChar, c);
if( u_isspace(c) ){
iStart = iWhite;
}else{
diff --git a/libgda/sqlite/sqlite-src/sqlite3.h b/libgda/sqlite/sqlite-src/sqlite3.h
index a198489..5a1f9d4 100644
--- a/libgda/sqlite/sqlite-src/sqlite3.h
+++ b/libgda/sqlite/sqlite-src/sqlite3.h
@@ -107,9 +107,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.7.12.1"
-#define SQLITE_VERSION_NUMBER 3007012
-#define SQLITE_SOURCE_ID "2012-05-22 02:45:53 6d326d44fd1d626aae0e8456e5fa2049f1ce0789"
+#define SQLITE_VERSION "3.7.14.1"
+#define SQLITE_VERSION_NUMBER 3007014
+#define SQLITE_SOURCE_ID "2012-10-04 19:37:12 091570e46d04e84b67228e0bdbcd6e1fb60c6bdb"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -219,7 +219,8 @@ SQLITE_API int sqlite3_threadsafe(void);
** the opaque structure named "sqlite3". It is useful to think of an sqlite3
** pointer as an object. The [sqlite3_open()], [sqlite3_open16()], and
** [sqlite3_open_v2()] interfaces are its constructors, and [sqlite3_close()]
-** is its destructor. There are many other interfaces (such as
+** and [sqlite3_close_v2()] are its destructors. There are many other
+** interfaces (such as
** [sqlite3_prepare_v2()], [sqlite3_create_function()], and
** [sqlite3_busy_timeout()] to name but three) that are methods on an
** sqlite3 object.
@@ -266,28 +267,46 @@ typedef sqlite_uint64 sqlite3_uint64;
/*
** CAPI3REF: Closing A Database Connection
**
-** ^The sqlite3_close() routine is the destructor for the [sqlite3] object.
-** ^Calls to sqlite3_close() return SQLITE_OK if the [sqlite3] object is
-** successfully destroyed and all associated resources are deallocated.
-**
-** Applications must [sqlite3_finalize | finalize] all [prepared statements]
-** and [sqlite3_blob_close | close] all [BLOB handles] associated with
-** the [sqlite3] object prior to attempting to close the object. ^If
+** ^The sqlite3_close() and sqlite3_close_v2() routines are destructors
+** for the [sqlite3] object.
+** ^Calls to sqlite3_close() and sqlite3_close_v2() return SQLITE_OK if
+** the [sqlite3] object is successfully destroyed and all associated
+** resources are deallocated.
+**
+** ^If the database connection is associated with unfinalized prepared
+** statements or unfinished sqlite3_backup objects then sqlite3_close()
+** will leave the database connection open and return [SQLITE_BUSY].
+** ^If sqlite3_close_v2() is called with unfinalized prepared statements
+** and unfinished sqlite3_backups, then the database connection becomes
+** an unusable "zombie" which will automatically be deallocated when the
+** last prepared statement is finalized or the last sqlite3_backup is
+** finished. The sqlite3_close_v2() interface is intended for use with
+** host languages that are garbage collected, and where the order in which
+** destructors are called is arbitrary.
+**
+** Applications should [sqlite3_finalize | finalize] all [prepared statements],
+** [sqlite3_blob_close | close] all [BLOB handles], and
+** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated
+** with the [sqlite3] object prior to attempting to close the object. ^If
** sqlite3_close() is called on a [database connection] that still has
-** outstanding [prepared statements] or [BLOB handles], then it returns
-** SQLITE_BUSY.
+** outstanding [prepared statements], [BLOB handles], and/or
+** [sqlite3_backup] objects then it returns SQLITE_OK but the deallocation
+** of resources is deferred until all [prepared statements], [BLOB handles],
+** and [sqlite3_backup] objects are also destroyed.
**
-** ^If [sqlite3_close()] is invoked while a transaction is open,
+** ^If an [sqlite3] object is destroyed while a transaction is open,
** the transaction is automatically rolled back.
**
-** The C parameter to [sqlite3_close(C)] must be either a NULL
+** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)]
+** must be either a NULL
** pointer or an [sqlite3] object pointer obtained
** from [sqlite3_open()], [sqlite3_open16()], or
** [sqlite3_open_v2()], and not previously closed.
-** ^Calling sqlite3_close() with a NULL pointer argument is a
-** harmless no-op.
+** ^Calling sqlite3_close() or sqlite3_close_v2() with a NULL pointer
+** argument is a harmless no-op.
*/
-SQLITE_API int sqlite3_close(sqlite3 *);
+SQLITE_API int sqlite3_close(sqlite3*);
+SQLITE_API int sqlite3_close_v2(sqlite3*);
/*
** The type for a callback function.
@@ -478,6 +497,7 @@ SQLITE_API int sqlite3_exec(
#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_MEMORY 0x00000080 /* 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 */
@@ -497,7 +517,7 @@ SQLITE_API int sqlite3_exec(
** CAPI3REF: Device Characteristics
**
** The xDeviceCharacteristics method of the [sqlite3_io_methods]
-** object returns an integer which is a vector of the these
+** object returns an integer which is a vector of these
** bit values expressing I/O characteristics of the mass storage
** device that holds the file that the [sqlite3_io_methods]
** refers to.
@@ -2169,12 +2189,12 @@ SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list);
** implementation of these routines to be omitted. That capability
** is no longer provided. Only built-in memory allocators can be used.
**
-** The Windows OS interface layer calls
+** Prior to SQLite version 3.7.10, the Windows OS interface layer called
** the system malloc() and free() directly when converting
** filenames between the UTF-8 encoding used by SQLite
** and whatever filename encoding is used by the particular Windows
-** installation. Memory allocation errors are detected, but
-** they are reported back as [SQLITE_CANTOPEN] or
+** installation. Memory allocation errors were detected, but
+** they were reported back as [SQLITE_CANTOPEN] or
** [SQLITE_IOERR] rather than [SQLITE_NOMEM].
**
** The pointer arguments to [sqlite3_free()] and [sqlite3_realloc()]
@@ -2575,18 +2595,20 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** 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)^.
+** <li> <b>mode</b>: ^(The mode parameter may be set to either "ro", "rw",
+** "rwc", or "memory". 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.
+** SQLITE_OPEN_READWRITE and SQLITE_OPEN_CREATE. ^If the mode option is
+** set to "memory" then a pure [in-memory database] that never reads
+** or writes from disk 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 in the third parameter to sqlite3_open_v2().
**
** <li> <b>cache</b>: ^The cache parameter may be set to either "shared" or
** "private". ^Setting it to "shared" is equivalent to setting the
@@ -2645,6 +2667,12 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** codepage is currently defined. Filenames containing international
** characters must be converted to UTF-8 prior to passing them into
** sqlite3_open() or sqlite3_open_v2().
+**
+** <b>Note to Windows Runtime users:</b> The temporary directory must be set
+** prior to calling sqlite3_open() or sqlite3_open_v2(). Otherwise, various
+** features that require the use of temporary files may fail.
+**
+** See also: [sqlite3_temp_directory]
*/
SQLITE_API int sqlite3_open(
const char *filename, /* Database filename (UTF-8) */
@@ -3137,8 +3165,11 @@ typedef struct sqlite3_context sqlite3_context;
** ^(In those routines that have a fourth argument, its value is the
** number of bytes in the parameter. To be clear: the value is the
** number of <u>bytes</u> in the value, not the number of characters.)^
-** ^If the fourth parameter is negative, the length of the string is
+** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16()
+** is negative, then the length of the string is
** the number of bytes up to the first zero terminator.
+** If the fourth parameter to sqlite3_bind_blob() is negative, then
+** the behavior is undefined.
** If a non-negative fourth parameter is provided to sqlite3_bind_text()
** or sqlite3_bind_text16() then that parameter must be the byte offset
** where the NUL terminator would occur assuming the string were NUL
@@ -4135,11 +4166,11 @@ typedef void (*sqlite3_destructor_type)(void*);
** the error code is SQLITE_ERROR. ^A subsequent call to sqlite3_result_error()
** or sqlite3_result_error16() resets the error code to SQLITE_ERROR.
**
-** ^The sqlite3_result_toobig() interface causes SQLite to throw an error
-** indicating that a string or BLOB is too long to represent.
+** ^The sqlite3_result_error_toobig() interface causes SQLite to throw an
+** error indicating that a string or BLOB is too long to represent.
**
-** ^The sqlite3_result_nomem() interface causes SQLite to throw an error
-** indicating that a memory allocation failed.
+** ^The sqlite3_result_error_nomem() interface causes SQLite to throw an
+** error indicating that a memory allocation failed.
**
** ^The sqlite3_result_int() interface sets the return value
** of the application-defined function to be the 32-bit signed integer
@@ -4446,10 +4477,62 @@ SQLITE_API int sqlite3_sleep(int);
** Hence, if this variable is modified directly, either it should be
** made NULL or made to point to memory obtained from [sqlite3_malloc]
** or else the use of the [temp_store_directory pragma] should be avoided.
+**
+** <b>Note to Windows Runtime users:</b> The temporary directory must be set
+** prior to calling [sqlite3_open] or [sqlite3_open_v2]. Otherwise, various
+** features that require the use of temporary files may fail. Here is an
+** example of how to do this using C++ with the Windows Runtime:
+**
+** <blockquote><pre>
+** LPCWSTR zPath = Windows::Storage::ApplicationData::Current->
+** TemporaryFolder->Path->Data();
+** char zPathBuf[MAX_PATH + 1];
+** memset(zPathBuf, 0, sizeof(zPathBuf));
+** WideCharToMultiByte(CP_UTF8, 0, zPath, -1, zPathBuf, sizeof(zPathBuf),
+** NULL, NULL);
+** sqlite3_temp_directory = sqlite3_mprintf("%s", zPathBuf);
+** </pre></blockquote>
*/
SQLITE_API SQLITE_EXTERN char *sqlite3_temp_directory;
/*
+** CAPI3REF: Name Of The Folder Holding Database Files
+**
+** ^(If this global variable is made to point to a string which is
+** the name of a folder (a.k.a. directory), then all database files
+** specified with a relative pathname and created or accessed by
+** SQLite when using a built-in windows [sqlite3_vfs | VFS] will be assumed
+** to be relative to that directory.)^ ^If this variable is a NULL
+** pointer, then SQLite assumes that all database files specified
+** with a relative pathname are relative to the current directory
+** for the process. Only the windows VFS makes use of this global
+** variable; it is ignored by the unix VFS.
+**
+** Changing the value of this variable while a database connection is
+** open can result in a corrupt database.
+**
+** It is not safe to read or modify this variable in more than one
+** thread at a time. It is not safe to read or modify this variable
+** if a [database connection] is being used at the same time in a separate
+** thread.
+** It is intended that this variable be set once
+** as part of process initialization and before any SQLite interface
+** routines have been called and that this variable remain unchanged
+** thereafter.
+**
+** ^The [data_store_directory pragma] may modify this variable and cause
+** it to point to memory obtained from [sqlite3_malloc]. ^Furthermore,
+** the [data_store_directory pragma] always assumes that any string
+** that this variable points to is held in memory obtained from
+** [sqlite3_malloc] and the pragma may attempt to free that memory
+** using [sqlite3_free].
+** Hence, if this variable is modified directly, either it should be
+** made NULL or made to point to memory obtained from [sqlite3_malloc]
+** or else the use of the [data_store_directory pragma] should be avoided.
+*/
+SQLITE_API SQLITE_EXTERN char *sqlite3_data_directory;
+
+/*
** CAPI3REF: Test For Auto-Commit Mode
** KEYWORDS: {autocommit mode}
**
@@ -4627,7 +4710,6 @@ SQLITE_API void *sqlite3_update_hook(
/*
** CAPI3REF: Enable Or Disable Shared Pager Cache
-** KEYWORDS: {shared cache}
**
** ^(This routine enables or disables the sharing of the database cache
** and schema data structures between [database connection | connections]
@@ -5455,7 +5537,6 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);
** implementations are available in the SQLite core:
**
** <ul>
-** <li> SQLITE_MUTEX_OS2
** <li> SQLITE_MUTEX_PTHREADS
** <li> SQLITE_MUTEX_W32
** <li> SQLITE_MUTEX_NOOP
@@ -5463,9 +5544,9 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);
**
** ^The SQLITE_MUTEX_NOOP implementation is a set of routines
** that does no real locking and is appropriate for use in
-** a single-threaded application. ^The SQLITE_MUTEX_OS2,
-** SQLITE_MUTEX_PTHREADS, and SQLITE_MUTEX_W32 implementations
-** are appropriate for use on OS/2, Unix, and Windows.
+** a single-threaded application. ^The SQLITE_MUTEX_PTHREADS and
+** SQLITE_MUTEX_W32 implementations are appropriate for use on Unix
+** and Windows.
**
** ^(If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor
** macro defined (with "-DSQLITE_MUTEX_APPDEF=1"), then no mutex
diff --git a/providers/sqlcipher/sqlcipher.patch b/providers/sqlcipher/sqlcipher.patch
index 5452e53..81128dc 100644
--- a/providers/sqlcipher/sqlcipher.patch
+++ b/providers/sqlcipher/sqlcipher.patch
@@ -1,6 +1,6 @@
---- sqlite3.c.sqlite 2012-08-27 14:56:39.000000000 +0200
-+++ sqlite3.c 2012-08-27 14:56:17.000000000 +0200
-@@ -12218,9 +12218,47 @@
+--- sqlite3.c.sqlite 2012-12-22 18:15:17.484622846 +0100
++++ sqlite3.c 2012-12-22 18:14:44.407175450 +0100
+@@ -12317,9 +12317,47 @@
#endif /* _SQLITEINT_H_ */
/************** End of sqliteInt.h *******************************************/
@@ -50,7 +50,7 @@
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
-@@ -12230,220 +12268,2261 @@
+@@ -12329,220 +12367,2496 @@
** May you share freely, never taking more than you give.
**
*************************************************************************
@@ -732,6 +732,7 @@
+#ifndef SQLITE_OMIT_INCRBLOB
+ u8 isIncrblobHandle; /* True if this cursor is an incr. io handle */
+#endif
++ u8 hints; /* As configured by CursorSetHints() */
+ i16 iPage; /* Index of current page in apPage */
+ u16 aiIdx[BTCURSOR_MAX_DEPTH]; /* Current index in apPage[i] */
+ MemPage *apPage[BTCURSOR_MAX_DEPTH]; /* Pages from root to current page */
@@ -924,7 +925,7 @@
+#define FILE_HEADER_SZ 16
+
+#ifndef CIPHER_VERSION
-+#define CIPHER_VERSION "2.0.6"
++#define CIPHER_VERSION "2.1.1"
+#endif
+
+#ifndef CIPHER
@@ -942,10 +943,16 @@
+#define PBKDF2_ITER 4000
+#endif
+
-+#ifndef DEFAULT_USE_HMAC
-+#define DEFAULT_USE_HMAC 1
++/* possible flags for cipher_ctx->flags */
++#define CIPHER_FLAG_HMAC 0x01
++#define CIPHER_FLAG_LE_PGNO 0x02
++#define CIPHER_FLAG_BE_PGNO 0x04
++
++#ifndef DEFAULT_CIPHER_FLAGS
++#define DEFAULT_CIPHER_FLAGS CIPHER_FLAG_HMAC | CIPHER_FLAG_LE_PGNO
+#endif
+
++
+/* by default, sqlcipher will use a reduced number of iterations to generate
+ the HMAC key / or transform a raw cipher key
+ */
@@ -1019,13 +1026,15 @@
+typedef struct codec_ctx codec_ctx;
+
+/* utility functions */
-+int sqlcipher_ismemset(const unsigned char *a0, unsigned char value, int len);
-+int sqlcipher_memcmp(const unsigned char *a0, const unsigned char *a1, int len);
++void* sqlcipher_memset(void *v, unsigned char value, int len);
++int sqlcipher_ismemset(const void *v, unsigned char value, int len);
++int sqlcipher_memcmp(const void *v0, const void *v1, int len);
+int sqlcipher_pseudorandom(void *, int);
+void sqlcipher_free(void *, int);
+
+/* activation and initialization */
+void sqlcipher_activate();
++void sqlcipher_deactivate();
+int sqlcipher_codec_ctx_init(codec_ctx **, Db *, Pager *, sqlite3_file *, const void *, int);
+void sqlcipher_codec_ctx_free(codec_ctx **);
+int sqlcipher_codec_key_derive(codec_ctx *);
@@ -1045,19 +1054,33 @@
+int sqlcipher_codec_ctx_get_reservesize(codec_ctx *);
+
+int sqlcipher_codec_ctx_set_kdf_iter(codec_ctx *, int, int);
++int sqlcipher_codec_ctx_get_kdf_iter(codec_ctx *ctx, int);
++
+void* sqlcipher_codec_ctx_get_kdf_salt(codec_ctx *ctx);
+
+int sqlcipher_codec_ctx_set_fast_kdf_iter(codec_ctx *, int, int);
++int sqlcipher_codec_ctx_get_fast_kdf_iter(codec_ctx *, int);
+
+int sqlcipher_codec_ctx_set_cipher(codec_ctx *, const char *, int);
++const char* sqlcipher_codec_ctx_get_cipher(codec_ctx *ctx, int for_ctx);
+
+void* sqlcipher_codec_ctx_get_data(codec_ctx *);
+
+void sqlcipher_exportFunc(sqlite3_context *, int, sqlite3_value **);
+
+void sqlcipher_set_default_use_hmac(int use);
++int sqlcipher_get_default_use_hmac();
++
++void sqlcipher_set_hmac_salt_mask(unsigned char mask);
++unsigned char sqlcipher_get_hmac_salt_mask();
+
+int sqlcipher_codec_ctx_set_use_hmac(codec_ctx *ctx, int use);
++int sqlcipher_codec_ctx_get_use_hmac(codec_ctx *ctx, int for_ctx);
++
++int sqlcipher_codec_ctx_set_flag(codec_ctx *ctx, unsigned int flag);
++int sqlcipher_codec_ctx_unset_flag(codec_ctx *ctx, unsigned int flag);
++int sqlcipher_codec_ctx_get_flag(codec_ctx *ctx, unsigned int flag, int for_ctx);
++
+/* end extensions defined in crypto_impl.c */
+
+#endif
@@ -1067,6 +1090,10 @@
+/************** End of crypto.h **********************************************/
+/************** Continuing where we left off in crypto.c *********************/
+
++const char* codec_get_cipher_version() {
++ return CIPHER_VERSION;
++}
++
+/* Generate code to return a string value */
+void codec_vdbe_return_static_string(Parse *pParse, const char *zLabel, const char *value){
+ Vdbe *v = sqlite3GetVdbe(pParse);
@@ -1076,30 +1103,6 @@
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
+}
+
-+int codec_set_kdf_iter(sqlite3* db, int nDb, int kdf_iter, int for_ctx) {
-+ struct Db *pDb = &db->aDb[nDb];
-+ CODEC_TRACE(("codec_set_kdf_iter: entered db=%p nDb=%d kdf_iter=%d for_ctx=%d\n", db, nDb, kdf_iter, for_ctx));
-+
-+ if(pDb->pBt) {
-+ codec_ctx *ctx;
-+ sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx);
-+ if(ctx) return sqlcipher_codec_ctx_set_kdf_iter(ctx, kdf_iter, for_ctx);
-+ }
-+ return SQLITE_ERROR;
-+}
-+
-+int codec_set_fast_kdf_iter(sqlite3* db, int nDb, int kdf_iter, int for_ctx) {
-+ struct Db *pDb = &db->aDb[nDb];
-+ CODEC_TRACE(("codec_set_kdf_iter: entered db=%p nDb=%d kdf_iter=%d for_ctx=%d\n", db, nDb, kdf_iter, for_ctx));
-+
-+ if(pDb->pBt) {
-+ codec_ctx *ctx;
-+ sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx);
-+ if(ctx) return sqlcipher_codec_ctx_set_fast_kdf_iter(ctx, kdf_iter, for_ctx);
-+ }
-+ return SQLITE_ERROR;
-+}
-+
+static int codec_set_btree_to_codec_pagesize(sqlite3 *db, Db *pDb, codec_ctx *ctx) {
+ int rc, page_sz, reserve_sz;
+
@@ -1118,76 +1121,154 @@
+ return rc;
+}
+
-+void codec_set_default_use_hmac(int use) {
-+ sqlcipher_set_default_use_hmac(use);
-+}
-+
-+int codec_set_use_hmac(sqlite3* db, int nDb, int use) {
++int codec_set_pass_key(sqlite3* db, int nDb, const void *zKey, int nKey, int for_ctx) {
+ struct Db *pDb = &db->aDb[nDb];
-+
-+ CODEC_TRACE(("codec_set_use_hmac: entered db=%p nDb=%d use=%d\n", db, nDb, use));
-+
++ CODEC_TRACE(("codec_set_pass_key: entered db=%p nDb=%d zKey=%s nKey=%d for_ctx=%d\n", db, nDb, (char *)zKey, nKey, for_ctx));
+ if(pDb->pBt) {
-+ int rc;
+ codec_ctx *ctx;
+ sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx);
-+ if(ctx) {
-+ rc = sqlcipher_codec_ctx_set_use_hmac(ctx, use);
-+ if(rc != SQLITE_OK) return rc;
-+ /* since the use of hmac has changed, the page size may also change */
-+ return codec_set_btree_to_codec_pagesize(db, pDb, ctx);
-+ }
++ if(ctx) return sqlcipher_codec_ctx_set_pass(ctx, zKey, nKey, for_ctx);
+ }
+ return SQLITE_ERROR;
-+}
++}
+
-+int codec_set_page_size(sqlite3* db, int nDb, int size) {
-+ struct Db *pDb = &db->aDb[nDb];
-+ CODEC_TRACE(("codec_set_page_size: entered db=%p nDb=%d size=%d\n", db, nDb, size));
++int codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLeft, const char *zRight) {
++ struct Db *pDb = &db->aDb[iDb];
++ codec_ctx *ctx = NULL;
++ int rc;
+
+ if(pDb->pBt) {
-+ int rc;
-+ codec_ctx *ctx;
+ sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx);
-+
-+ if(ctx) {
-+ rc = sqlcipher_codec_ctx_set_pagesize(ctx, size);
-+ if(rc != SQLITE_OK) return rc;
-+ return codec_set_btree_to_codec_pagesize(db, pDb, ctx);
-+ }
+ }
-+ return SQLITE_ERROR;
-+}
+
-+/**
-+ *
-+ * when for_ctx == 0 then it will change for read
-+ * when for_ctx == 1 then it will change for write
-+ * when for_ctx == 2 then it will change for both
-+ */
-+int codec_set_cipher_name(sqlite3* db, int nDb, const char *cipher_name, int for_ctx) {
-+ struct Db *pDb = &db->aDb[nDb];
-+ CODEC_TRACE(("codec_set_cipher_name: entered db=%p nDb=%d cipher_name=%s for_ctx=%d\n", db, nDb, cipher_name, for_ctx));
++ CODEC_TRACE(("codec_pragma: entered db=%p iDb=%d pParse=%p zLeft=%s zRight=%s ctx=%p\n", db, iDb, pParse, zLeft, zRight, ctx));
+
-+ if(pDb->pBt) {
-+ codec_ctx *ctx;
-+ sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx);
-+ if(ctx) return sqlcipher_codec_ctx_set_cipher(ctx, cipher_name, for_ctx);
++ if( sqlite3StrICmp(zLeft, "cipher_version")==0 && !zRight ){
++ codec_vdbe_return_static_string(pParse, "cipher_version", codec_get_cipher_version());
++ }else
++ if( sqlite3StrICmp(zLeft, "cipher")==0 ){
++ if(ctx) {
++ if( zRight ) {
++ sqlcipher_codec_ctx_set_cipher(ctx, zRight, 2); // change cipher for both
++ }else {
++ codec_vdbe_return_static_string(pParse, "cipher",
++ sqlcipher_codec_ctx_get_cipher(ctx, 2));
++ }
++ }
++ }else
++ if( sqlite3StrICmp(zLeft, "rekey_cipher")==0 && zRight ){
++ if(ctx) sqlcipher_codec_ctx_set_cipher(ctx, zRight, 1); // change write cipher only
++ }else
++ if( sqlite3StrICmp(zLeft, "kdf_iter")==0 ){
++ if(ctx) {
++ if( zRight ) {
++ sqlcipher_codec_ctx_set_kdf_iter(ctx, atoi(zRight), 2); // change of RW PBKDF2 iteration
++ } else {
++ char *kdf_iter = sqlite3_mprintf("%d", sqlcipher_codec_ctx_get_kdf_iter(ctx, 2));
++ codec_vdbe_return_static_string(pParse, "kdf_iter", kdf_iter);
++ sqlite3_free(kdf_iter);
++ }
++ }
++ }else
++ if( sqlite3StrICmp(zLeft, "fast_kdf_iter")==0){
++ if(ctx) {
++ if( zRight ) {
++ sqlcipher_codec_ctx_set_fast_kdf_iter(ctx, atoi(zRight), 2); // change of RW PBKDF2 iteration
++ } else {
++ char *fast_kdf_iter = sqlite3_mprintf("%d", sqlcipher_codec_ctx_get_fast_kdf_iter(ctx, 2));
++ codec_vdbe_return_static_string(pParse, "fast_kdf_iter", fast_kdf_iter);
++ sqlite3_free(fast_kdf_iter);
++ }
++ }
++ }else
++ if( sqlite3StrICmp(zLeft, "rekey_kdf_iter")==0 && zRight ){
++ if(ctx) sqlcipher_codec_ctx_set_kdf_iter(ctx, atoi(zRight), 1); // write iterations only
++ }else
++ if( sqlite3StrICmp(zLeft,"cipher_page_size")==0 ){
++ if(ctx) {
++ if( zRight ) {
++ int size = atoi(zRight);
++ rc = sqlcipher_codec_ctx_set_pagesize(ctx, size);
++ if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc);
++ rc = codec_set_btree_to_codec_pagesize(db, pDb, ctx);
++ if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc);
++ } else {
++ char * page_size = sqlite3_mprintf("%d", sqlcipher_codec_ctx_get_pagesize(ctx));
++ codec_vdbe_return_static_string(pParse, "cipher_page_size", page_size);
++ sqlite3_free(page_size);
++ }
++ }
++ }else
++ if( sqlite3StrICmp(zLeft,"cipher_default_use_hmac")==0 ){
++ if( zRight ) {
++ sqlcipher_set_default_use_hmac(sqlite3GetBoolean(zRight,1));
++ } else {
++ char *default_use_hmac = sqlite3_mprintf("%d", sqlcipher_get_default_use_hmac());
++ codec_vdbe_return_static_string(pParse, "cipher_default_use_hmac", default_use_hmac);
++ sqlite3_free(default_use_hmac);
++ }
++ }else
++ if( sqlite3StrICmp(zLeft,"cipher_use_hmac")==0 ){
++ if(ctx) {
++ if( zRight ) {
++ rc = sqlcipher_codec_ctx_set_use_hmac(ctx, sqlite3GetBoolean(zRight,1));
++ if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc);
++ /* since the use of hmac has changed, the page size may also change */
++ rc = codec_set_btree_to_codec_pagesize(db, pDb, ctx);
++ if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc);
++ } else {
++ char *hmac_flag = sqlite3_mprintf("%d", sqlcipher_codec_ctx_get_use_hmac(ctx, 2));
++ codec_vdbe_return_static_string(pParse, "cipher_use_hmac", hmac_flag);
++ sqlite3_free(hmac_flag);
++ }
++ }
++ }else
++ if( sqlite3StrICmp(zLeft,"cipher_hmac_pgno")==0 ){
++ if(ctx) {
++ if(zRight) {
++ // clear both pgno endian flags
++ if(sqlite3StrICmp(zRight, "le") == 0) {
++ sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_BE_PGNO);
++ sqlcipher_codec_ctx_set_flag(ctx, CIPHER_FLAG_LE_PGNO);
++ } else if(sqlite3StrICmp(zRight, "be") == 0) {
++ sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_LE_PGNO);
++ sqlcipher_codec_ctx_set_flag(ctx, CIPHER_FLAG_BE_PGNO);
++ } else if(sqlite3StrICmp(zRight, "native") == 0) {
++ sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_LE_PGNO);
++ sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_BE_PGNO);
++ }
++ } else {
++ if(sqlcipher_codec_ctx_get_flag(ctx, CIPHER_FLAG_LE_PGNO, 2)) {
++ codec_vdbe_return_static_string(pParse, "cipher_hmac_pgno", "le");
++ } else if(sqlcipher_codec_ctx_get_flag(ctx, CIPHER_FLAG_BE_PGNO, 2)) {
++ codec_vdbe_return_static_string(pParse, "cipher_hmac_pgno", "be");
++ } else {
++ codec_vdbe_return_static_string(pParse, "cipher_hmac_pgno", "native");
++ }
++ }
++ }
++ }else
++ if( sqlite3StrICmp(zLeft,"cipher_hmac_salt_mask")==0 ){
++ if(ctx) {
++ if(zRight) {
++ if (sqlite3StrNICmp(zRight ,"x'", 2) == 0 && sqlite3Strlen30(zRight) == 5) {
++ unsigned char mask = 0;
++ const char *hex = zRight+2;
++ cipher_hex2bin(hex,2,&mask);
++ sqlcipher_set_hmac_salt_mask(mask);
++ }
++ } else {
++ char *hmac_salt_mask = sqlite3_mprintf("%02x", sqlcipher_get_hmac_salt_mask());
++ codec_vdbe_return_static_string(pParse, "cipher_hmac_salt_mask", hmac_salt_mask);
++ sqlite3_free(hmac_salt_mask);
++ }
++ }
++ }else {
++ return 0;
+ }
-+ return SQLITE_ERROR;
++ return 1;
+}
+
-+int codec_set_pass_key(sqlite3* db, int nDb, const void *zKey, int nKey, int for_ctx) {
-+ struct Db *pDb = &db->aDb[nDb];
-+ CODEC_TRACE(("codec_set_pass_key: entered db=%p nDb=%d zKey=%s nKey=%d for_ctx=%d\n", db, nDb, (char *)zKey, nKey, for_ctx));
-+ if(pDb->pBt) {
-+ codec_ctx *ctx;
-+ sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx);
-+ if(ctx) return sqlcipher_codec_ctx_set_pass(ctx, zKey, nKey, for_ctx);
-+ }
-+ return SQLITE_ERROR;
-+}
-+
+/*
+ * sqlite3Codec can be called in multiple modes.
+ * encrypt mode - expected to return a pointer to the
@@ -1245,6 +1326,7 @@
+ codec_ctx *ctx = (codec_ctx *) pCodecArg;
+ if(pCodecArg == NULL) return;
+ sqlcipher_codec_ctx_free(&ctx); // wipe and free allocated memory for the context
++ sqlcipher_deactivate(); /* cleanup related structures, OpenSSL etc, when codec is detatched */
+}
+
+SQLITE_PRIVATE int sqlite3CodecAttach(sqlite3* db, int nDb, const void *zKey, int nKey) {
@@ -1252,7 +1334,6 @@
+
+ CODEC_TRACE(("sqlite3CodecAttach: entered nDb=%d zKey=%s, nKey=%d\n", nDb, (char *)zKey, nKey));
+
-+ sqlcipher_activate();
+
+ if(nKey && zKey && pDb->pBt) {
+ int rc;
@@ -1260,9 +1341,15 @@
+ sqlite3_file *fd = sqlite3Pager_get_fd(pPager);
+ codec_ctx *ctx;
+
++ sqlcipher_activate(); /* perform internal initialization for sqlcipher */
++
+ /* point the internal codec argument against the contet to be prepared */
+ rc = sqlcipher_codec_ctx_init(&ctx, pDb, pDb->pBt->pBt->pPager, fd, zKey, nKey);
+
++ if(rc != SQLITE_OK) return rc; /* initialization failed, do not attach potentially corrupted context */
++
++ sqlite3_mutex_enter(db->mutex);
++
+ sqlite3pager_sqlite3PagerSetCodec(sqlite3BtreePager(pDb->pBt), sqlite3Codec, NULL, sqlite3FreeCodecArg, (void *) ctx);
+
+ codec_set_btree_to_codec_pagesize(db, pDb, ctx);
@@ -1275,7 +1362,6 @@
+ /* if fd is null, then this is an in-memory database and
+ we dont' want to overwrite the AutoVacuum settings
+ if not null, then set to the default */
-+ sqlite3_mutex_enter(db->mutex);
+ if(fd != NULL) {
+ sqlite3BtreeSetAutoVacuum(pDb->pBt, SQLITE_DEFAULT_AUTOVACUUM);
+ }
@@ -1292,8 +1378,7 @@
+ CODEC_TRACE(("sqlite3_key: entered db=%p pKey=%s nKey=%d\n", db, (char *)pKey, nKey));
+ /* attach key if db and pKey are not null and nKey is > 0 */
+ if(db && pKey && nKey) {
-+ sqlite3CodecAttach(db, 0, pKey, nKey); // operate only on the main db
-+ return SQLITE_OK;
++ return sqlite3CodecAttach(db, 0, pKey, nKey); // operate only on the main db
+ }
+ return SQLITE_ERROR;
+}
@@ -1310,7 +1395,6 @@
+*/
+SQLITE_API int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey) {
+ CODEC_TRACE(("sqlite3_rekey: entered db=%p pKey=%s, nKey=%d\n", db, (char *)pKey, nKey));
-+ sqlcipher_activate();
+ if(db && pKey && nKey) {
+ struct Db *pDb = &db->aDb[0];
+ CODEC_TRACE(("sqlite3_rekey: database pDb=%p\n", pDb));
@@ -1459,7 +1543,7 @@
+ int pass_sz;
+ int reserve_sz;
+ int hmac_sz;
-+ int use_hmac;
++ unsigned int flags;
+ unsigned char *key;
+ unsigned char *hmac_key;
+ char *pass;
@@ -1475,7 +1559,11 @@
+/* prototype for pager HMAC function */
+int sqlcipher_page_hmac(cipher_ctx *, Pgno, unsigned char *, int, unsigned char *);
+
-+static int default_use_hmac = DEFAULT_USE_HMAC;
++static unsigned int default_flags = DEFAULT_CIPHER_FLAGS;
++static unsigned char hmac_salt_mask = HMAC_SALT_MASK;
++
++static unsigned int openssl_external_init = 0;
++static unsigned int openssl_init_count = 0;
+
+struct codec_ctx {
+ int kdf_salt_sz;
@@ -1488,31 +1576,93 @@
+ cipher_ctx *write_ctx;
+};
+
++/* activate and initialize sqlcipher. Most importantly, this will automatically
++ intialize OpenSSL's EVP system if it hasn't already be externally. Note that
++ this function may be called multiple times as new codecs are intiialized.
++ Thus it performs some basic counting to ensure that only the last and final
++ sqlcipher_deactivate() will free the EVP structures.
++*/
+void sqlcipher_activate() {
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
-+ if(EVP_get_cipherbyname(CIPHER) == NULL) {
-+ OpenSSL_add_all_algorithms();
++
++ /* we'll initialize openssl and increment the internal init counter
++ but only if it hasn't been initalized outside of SQLCipher by this program
++ e.g. on startup */
++ if(openssl_init_count == 0 && EVP_get_cipherbyname(CIPHER) != NULL) {
++ openssl_external_init = 1;
++ }
++
++ if(openssl_external_init == 0) {
++ if(openssl_init_count == 0) {
++ OpenSSL_add_all_algorithms();
++ }
++ openssl_init_count++;
+ }
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+}
+
-+/* fixed time zero memory check tests every position of a memory segement
-+ matches a single value (i.e. the memory is all zeros)*/
-+int sqlcipher_ismemset(const unsigned char *a0, unsigned char value, int len) {
-+ int i = 0, noMatch = 0;
-+ for(i = 0; i < len; i++) noMatch = (noMatch || (a0[i] != value));
-+ return noMatch;
++/* deactivate SQLCipher, most imporantly decremeting the activation count and
++ freeing the EVP structures on the final deactivation to ensure that
++ OpenSSL memory is cleaned up */
++void sqlcipher_deactivate() {
++ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
++ /* If it is initialized externally, then the init counter should never be greater than zero.
++ This should prevent SQLCipher from "cleaning up" openssl
++ when something else in the program might be using it. */
++ if(openssl_external_init == 0) {
++ openssl_init_count--;
++ /* if the counter reaches zero after it's decremented release EVP memory
++ Note: this code will only be reached if OpensSSL_add_all_algorithms()
++ is called by SQLCipher internally. */
++ if(openssl_init_count == 0) {
++ EVP_cleanup();
++ }
++ }
++ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
++}
++
++/* constant time memset using volitile to avoid having the memset
++ optimized out by the compiler.
++ Note: As suggested by Joachim Schipper (joachim schipper fox-it com)
++*/
++void* sqlcipher_memset(void *v, unsigned char value, int len) {
++ int i = 0;
++ volatile unsigned char *a = v;
++
++ if (v == NULL) return v;
++
++ for(i = 0; i < len; i++) {
++ a[i] = value;
++ }
++
++ return v;
++}
++
++/* constant time memory check tests every position of a memory segement
++ matches a single value (i.e. the memory is all zeros)
++ returns 0 if match, 1 of no match */
++int sqlcipher_ismemset(const void *v, unsigned char value, int len) {
++ const unsigned char *a = v;
++ int i = 0, result = 0;
++
++ for(i = 0; i < len; i++) {
++ result |= a[i] ^ value;
++ }
++
++ return (result != 0);
+}
+
-+/* fixed time memory comparison routine */
-+int sqlcipher_memcmp(const unsigned char *a0, const unsigned char *a1, int len) {
-+ int i = 0, noMatch = 0;
++/* constant time memory comparison routine.
++ returns 0 if match, 1 if no match */
++int sqlcipher_memcmp(const void *v0, const void *v1, int len) {
++ const unsigned char *a0 = v0, *a1 = v1;
++ int i = 0, result = 0;
+
+ for(i = 0; i < len; i++) {
-+ noMatch = (noMatch || (a0[i] != a1[i]));
++ result |= a0[i] ^ a1[i];
+ }
+
-+ return noMatch;
++ return (result != 0);
+}
+
+/* generate a defined number of pseudorandom bytes */
@@ -1522,7 +1672,7 @@
+
+/**
+ * Free and wipe memory. Uses SQLites internal sqlite3_free so that memory
-+ * can be countend and memory leak detection works in the tet suite.
++ * can be countend and memory leak detection works in the test suite.
+ * If ptr is not null memory will be freed.
+ * If sz is greater than zero, the memory will be overwritten with zero before it is freed
+ * If sz is > 0, and not compiled with OMIT_MEMLOCK, system will attempt to unlock the
@@ -1531,7 +1681,7 @@
+void sqlcipher_free(void *ptr, int sz) {
+ if(ptr) {
+ if(sz > 0) {
-+ memset(ptr, 0, sz);
++ sqlcipher_memset(ptr, 0, sz);
+#ifndef OMIT_MEMLOCK
+#if defined(__unix__) || defined(__APPLE__)
+ munlock(ptr, sz);
@@ -1551,6 +1701,7 @@
+ */
+void* sqlcipher_malloc(int sz) {
+ void *ptr = sqlite3Malloc(sz);
++ sqlcipher_memset(ptr, 0, sz);
+#ifndef OMIT_MEMLOCK
+ if(ptr) {
+#if defined(__unix__) || defined(__APPLE__)
@@ -1565,7 +1716,7 @@
+
+
+/**
-+ * Initialize a a new cipher_ctx struct. This function will allocate memory
++ * Initialize new cipher_ctx struct. This function will allocate memory
+ * for the cipher context and for the key
+ *
+ * returns SQLITE_OK if initialization was successful
@@ -1576,11 +1727,15 @@
+ *iCtx = (cipher_ctx *) sqlcipher_malloc(sizeof(cipher_ctx));
+ ctx = *iCtx;
+ if(ctx == NULL) return SQLITE_NOMEM;
-+ memset(ctx, 0, sizeof(cipher_ctx));
++
+ ctx->key = (unsigned char *) sqlcipher_malloc(EVP_MAX_KEY_LENGTH);
+ ctx->hmac_key = (unsigned char *) sqlcipher_malloc(EVP_MAX_KEY_LENGTH);
+ if(ctx->key == NULL) return SQLITE_NOMEM;
+ if(ctx->hmac_key == NULL) return SQLITE_NOMEM;
++
++ /* setup default flags */
++ ctx->flags = default_flags;
++
+ return SQLITE_OK;
+}
+
@@ -1612,7 +1767,7 @@
+ && c1->fast_kdf_iter == c2->fast_kdf_iter
+ && c1->key_sz == c2->key_sz
+ && c1->pass_sz == c2->pass_sz
-+ && c1->use_hmac == c2->use_hmac
++ && c1->flags == c2->flags
+ && c1->hmac_sz == c2->hmac_sz
+ && (
+ c1->pass == c2->pass
@@ -1705,6 +1860,12 @@
+ return SQLITE_OK;
+}
+
++const char* sqlcipher_codec_ctx_get_cipher(codec_ctx *ctx, int for_ctx) {
++ cipher_ctx *c_ctx = for_ctx ? ctx->write_ctx : ctx->read_ctx;
++ EVP_CIPHER *evp_cipher = c_ctx->evp_cipher;
++ return EVP_CIPHER_name(evp_cipher);
++}
++
+int sqlcipher_codec_ctx_set_kdf_iter(codec_ctx *ctx, int kdf_iter, int for_ctx) {
+ cipher_ctx *c_ctx = for_ctx ? ctx->write_ctx : ctx->read_ctx;
+ int rc;
@@ -1719,6 +1880,11 @@
+ return SQLITE_OK;
+}
+
++int sqlcipher_codec_ctx_get_kdf_iter(codec_ctx *ctx, int for_ctx) {
++ cipher_ctx *c_ctx = for_ctx ? ctx->write_ctx : ctx->read_ctx;
++ return c_ctx->kdf_iter;
++}
++
+int sqlcipher_codec_ctx_set_fast_kdf_iter(codec_ctx *ctx, int fast_kdf_iter, int for_ctx) {
+ cipher_ctx *c_ctx = for_ctx ? ctx->write_ctx : ctx->read_ctx;
+ int rc;
@@ -1733,9 +1899,27 @@
+ return SQLITE_OK;
+}
+
++int sqlcipher_codec_ctx_get_fast_kdf_iter(codec_ctx *ctx, int for_ctx) {
++ cipher_ctx *c_ctx = for_ctx ? ctx->write_ctx : ctx->read_ctx;
++ return c_ctx->fast_kdf_iter;
++}
++
+/* set the global default flag for HMAC */
+void sqlcipher_set_default_use_hmac(int use) {
-+ default_use_hmac = use;
++ if(use) default_flags |= CIPHER_FLAG_HMAC;
++ else default_flags &= ~CIPHER_FLAG_HMAC;
++}
++
++int sqlcipher_get_default_use_hmac() {
++ return (default_flags & CIPHER_FLAG_HMAC) != 0;
++}
++
++void sqlcipher_set_hmac_salt_mask(unsigned char mask) {
++ hmac_salt_mask = mask;
++}
++
++unsigned char sqlcipher_get_hmac_salt_mask() {
++ return hmac_salt_mask;
+}
+
+/* set the codec flag for whether this individual database should be using hmac */
@@ -1752,22 +1936,46 @@
+ CODEC_TRACE(("sqlcipher_codec_ctx_set_use_hmac: use=%d block_sz=%d md_size=%d reserve=%d\n",
+ use, ctx->read_ctx->block_sz, ctx->read_ctx->hmac_sz, reserve));
+
-+ ctx->write_ctx->use_hmac = ctx->read_ctx->use_hmac = use;
++
++ if(use) {
++ sqlcipher_codec_ctx_set_flag(ctx, CIPHER_FLAG_HMAC);
++ } else {
++ sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_HMAC);
++ }
++
+ ctx->write_ctx->reserve_sz = ctx->read_ctx->reserve_sz = reserve;
+
+ return SQLITE_OK;
+}
+
++int sqlcipher_codec_ctx_get_use_hmac(codec_ctx *ctx, int for_ctx) {
++ cipher_ctx * c_ctx = for_ctx ? ctx->write_ctx : ctx->read_ctx;
++ return (c_ctx->flags & CIPHER_FLAG_HMAC) != 0;
++}
++
++int sqlcipher_codec_ctx_set_flag(codec_ctx *ctx, unsigned int flag) {
++ ctx->write_ctx->flags |= flag;
++ ctx->read_ctx->flags |= flag;
++ return SQLITE_OK;
++}
++
++int sqlcipher_codec_ctx_unset_flag(codec_ctx *ctx, unsigned int flag) {
++ ctx->write_ctx->flags &= ~flag;
++ ctx->read_ctx->flags &= ~flag;
++ return SQLITE_OK;
++}
++
++int sqlcipher_codec_ctx_get_flag(codec_ctx *ctx, unsigned int flag, int for_ctx) {
++ cipher_ctx * c_ctx = for_ctx ? ctx->write_ctx : ctx->read_ctx;
++ return (c_ctx->flags & flag) != 0;
++}
++
+void sqlcipher_codec_ctx_set_error(codec_ctx *ctx, int error) {
+ CODEC_TRACE(("sqlcipher_codec_ctx_set_error: ctx=%p, error=%d\n", ctx, error));
+ sqlite3pager_sqlite3PagerSetError(ctx->pBt->pBt->pPager, error);
+ ctx->pBt->pBt->db->errCode = error;
+}
+
-+int sqlcipher_codec_ctx_get_pagesize(codec_ctx *ctx) {
-+ return ctx->page_sz;
-+}
-+
+int sqlcipher_codec_ctx_get_reservesize(codec_ctx *ctx) {
+ return ctx->read_ctx->reserve_sz;
+}
@@ -1799,6 +2007,10 @@
+ return SQLITE_OK;
+}
+
++int sqlcipher_codec_ctx_get_pagesize(codec_ctx *ctx) {
++ return ctx->page_sz;
++}
++
+int sqlcipher_codec_ctx_init(codec_ctx **iCtx, Db *pDb, Pager *pPager, sqlite3_file *fd, const void *zKey, int nKey) {
+ int rc;
+ codec_ctx *ctx;
@@ -1807,7 +2019,6 @@
+
+ if(ctx == NULL) return SQLITE_NOMEM;
+
-+ memset(ctx, 0, sizeof(codec_ctx)); /* initialize all pointers and values to 0 */
+ ctx->pBt = pDb->pBt; /* assign pointer to database btree structure */
+
+ /* allocate space for salt data. Then read the first 16 bytes
@@ -1845,9 +2056,9 @@
+ if((rc = sqlcipher_codec_ctx_set_fast_kdf_iter(ctx, FAST_PBKDF2_ITER, 0)) != SQLITE_OK) return rc;
+ if((rc = sqlcipher_codec_ctx_set_pass(ctx, zKey, nKey, 0)) != SQLITE_OK) return rc;
+
-+ /* Use HMAC signatures by default. Note that codec_set_use_hmac will implicity call
-+ codec_set_page_size to set the default */
-+ if((rc = sqlcipher_codec_ctx_set_use_hmac(ctx, default_use_hmac)) != SQLITE_OK) return rc;
++ /* Note that use_hmac is a special case that requires recalculation of page size
++ so we call set_use_hmac to perform setup */
++ if((rc = sqlcipher_codec_ctx_set_use_hmac(ctx, default_flags & CIPHER_FLAG_HMAC)) != SQLITE_OK) return rc;
+
+ if((rc = sqlcipher_cipher_ctx_copy(ctx->write_ctx, ctx->read_ctx)) != SQLITE_OK) return rc;
+
@@ -1869,16 +2080,40 @@
+ sqlcipher_free(ctx, sizeof(codec_ctx));
+}
+
++/** convert a 32bit unsigned integer to little endian byte ordering */
++static void sqlcipher_put4byte_le(unsigned char *p, u32 v) {
++ p[0] = (u8)v;
++ p[1] = (u8)(v>>8);
++ p[2] = (u8)(v>>16);
++ p[3] = (u8)(v>>24);
++}
++
+int sqlcipher_page_hmac(cipher_ctx *ctx, Pgno pgno, unsigned char *in, int in_sz, unsigned char *out) {
++ unsigned char pgno_raw[sizeof(pgno)];
++ /* we may convert page number to consistent representation before calculating MAC for
++ compatibility across big-endian and little-endian platforms.
++
++ Note: The public release of sqlcipher 2.0.0 to 2.0.6 had a bug where the bytes of pgno
++ were used directly in the MAC. SQLCipher convert's to little endian by default to preserve
++ backwards compatibility on the most popular platforms, but can optionally be configured
++ to use either big endian or native byte ordering via pragma. */
++
++ if(ctx->flags & CIPHER_FLAG_LE_PGNO) { /* compute hmac using little endian pgno*/
++ sqlcipher_put4byte_le(pgno_raw, pgno);
++ } else if(ctx->flags & CIPHER_FLAG_BE_PGNO) { /* compute hmac using big endian pgno */
++ sqlite3Put4byte(pgno_raw, pgno); /* sqlite3Put4byte converts 32bit uint to big endian */
++ } else { /* use native byte ordering */
++ memcpy(pgno_raw, &pgno, sizeof(pgno));
++ }
++
+ HMAC_CTX_init(&ctx->hctx);
-+
+ HMAC_Init_ex(&ctx->hctx, ctx->hmac_key, ctx->key_sz, EVP_sha1(), NULL);
+
+ /* include the encrypted page data, initialization vector, and page number in HMAC. This will
+ prevent both tampering with the ciphertext, manipulation of the IV, or resequencing otherwise
+ valid pages out of order in a database */
+ HMAC_Update(&ctx->hctx, in, in_sz);
-+ HMAC_Update(&ctx->hctx, (const unsigned char*) &pgno, sizeof(Pgno));
++ HMAC_Update(&ctx->hctx, (const unsigned char*) pgno_raw, sizeof(pgno));
+ HMAC_Final(&ctx->hctx, out, NULL);
+ HMAC_CTX_cleanup(&ctx->hctx);
+ return SQLITE_OK;
@@ -1903,7 +2138,7 @@
+ iv_in = in + size;
+
+ /* hmac will be written immediately after the initialization vector. the remainder of the page reserve will contain
-+ random bytes. note, these pointers are only valid when use_hmac is true */
++ random bytes. note, these pointers are only valid when using hmac */
+ hmac_in = in + size + c_ctx->iv_sz;
+ hmac_out = out + size + c_ctx->iv_sz;
+ out_start = out; /* note the original position of the output buffer pointer, as out will be rewritten during encryption */
@@ -1914,7 +2149,7 @@
+ /* the key size should never be zero. If it is, error out. */
+ if(c_ctx->key_sz == 0) {
+ CODEC_TRACE(("codec_cipher: error possible context corruption, key_sz is zero for pgno=%d\n", pgno));
-+ memset(out, 0, page_sz);
++ sqlcipher_memset(out, 0, page_sz);
+ return SQLITE_ERROR;
+ }
+
@@ -1925,9 +2160,9 @@
+ memcpy(iv_out, iv_in, c_ctx->iv_sz); /* copy the iv from the input to output buffer */
+ }
+
-+ if(c_ctx->use_hmac && (mode == CIPHER_DECRYPT)) {
++ if((c_ctx->flags & CIPHER_FLAG_HMAC) && (mode == CIPHER_DECRYPT)) {
+ if(sqlcipher_page_hmac(c_ctx, pgno, in, size + c_ctx->iv_sz, hmac_out) != SQLITE_OK) {
-+ memset(out, 0, page_sz);
++ sqlcipher_memset(out, 0, page_sz);
+ CODEC_TRACE(("codec_cipher: hmac operations failed for pgno=%d\n", pgno));
+ return SQLITE_ERROR;
+ }
@@ -1940,14 +2175,14 @@
+ short read failures must be ignored for autovaccum mode to work so wipe the output buffer
+ and return SQLITE_OK to skip the decryption step. */
+ CODEC_TRACE(("codec_cipher: zeroed page (short read) for pgno %d, encryption but returning SQLITE_OK\n", pgno));
-+ memset(out, 0, page_sz);
++ sqlcipher_memset(out, 0, page_sz);
+ return SQLITE_OK;
+ } else {
+ /* if the page memory is not all zeros, it means the there was data and a hmac on the page.
+ since the check failed, the page was either tampered with or corrupted. wipe the output buffer,
+ and return SQLITE_ERROR to the caller */
+ CODEC_TRACE(("codec_cipher: hmac check failed for pgno=%d returning SQLITE_ERROR\n", pgno));
-+ memset(out, 0, page_sz);
++ sqlcipher_memset(out, 0, page_sz);
+ return SQLITE_ERROR;
+ }
+ }
@@ -1964,7 +2199,7 @@
+ EVP_CIPHER_CTX_cleanup(&c_ctx->ectx);
+ assert(size == csz);
+
-+ if(c_ctx->use_hmac && (mode == CIPHER_ENCRYPT)) {
++ if((c_ctx->flags & CIPHER_FLAG_HMAC) && (mode == CIPHER_ENCRYPT)) {
+ sqlcipher_page_hmac(c_ctx, pgno, out_start, size + c_ctx->iv_sz, hmac_out);
+ }
+
@@ -2009,7 +2244,7 @@
+ /* if this context is setup to use hmac checks, generate a seperate and different
+ key for HMAC. In this case, we use the output of the previous KDF as the input to
+ this KDF run. This ensures a distinct but predictable HMAC key. */
-+ if(c_ctx->use_hmac) {
++ if(c_ctx->flags & CIPHER_FLAG_HMAC) {
+ int i;
+
+ /* start by copying the kdf key into the hmac salt slot
@@ -2019,7 +2254,7 @@
+ to generate the encryption key */
+ memcpy(ctx->hmac_kdf_salt, ctx->kdf_salt, ctx->kdf_salt_sz);
+ for(i = 0; i < ctx->kdf_salt_sz; i++) {
-+ ctx->hmac_kdf_salt[i] ^= HMAC_SALT_MASK;
++ ctx->hmac_kdf_salt[i] ^= hmac_salt_mask;
+ }
+
+ CODEC_TRACE(("codec_key_derive: deriving hmac key from encryption key using PBKDF2 with %d iterations\n",
@@ -2490,7 +2725,7 @@
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
-@@ -24967,7 +27046,7 @@
+@@ -22677,7 +24991,7 @@
#include <sys/time.h>
#include <errno.h>
#ifndef SQLITE_OMIT_WAL
@@ -2499,41 +2734,10 @@
#endif
-@@ -45561,24 +47640,56 @@
- }
- #endif
+@@ -43883,6 +46197,38 @@
+
+ #endif /* SQLITE_OMIT_DISKIO */
--#ifdef SQLITE_HAS_CODEC
--/*
--** This function is called by the wal module when writing page content
--** into the log file.
--**
--** This function returns a pointer to a buffer containing the encrypted
--** page content. If a malloc fails, this function may return NULL.
--*/
--SQLITE_PRIVATE void *sqlite3PagerCodec(PgHdr *pPg){
-- void *aData = 0;
-- CODEC2(pPg->pPager, pPg->pData, pPg->pgno, 6, return 0, aData);
-- return aData;
-+#ifdef SQLITE_HAS_CODEC
-+/*
-+** This function is called by the wal module when writing page content
-+** into the log file.
-+**
-+** This function returns a pointer to a buffer containing the encrypted
-+** page content. If a malloc fails, this function may return NULL.
-+*/
-+SQLITE_PRIVATE void *sqlite3PagerCodec(PgHdr *pPg){
-+ void *aData = 0;
-+ CODEC2(pPg->pPager, pPg->pData, pPg->pgno, 6, return 0, aData);
-+ return aData;
-+}
-+#endif /* SQLITE_HAS_CODEC */
-+
-+#endif /* !SQLITE_OMIT_WAL */
-+
-+#endif /* SQLITE_OMIT_DISKIO */
-+
+/* BEGIN CRYPTO */
+#ifdef SQLITE_HAS_CODEC
+SQLITE_PRIVATE void sqlite3pager_get_codec(Pager *pPager, void **ctx) {
@@ -2560,64 +2764,339 @@
+
+SQLITE_PRIVATE void sqlite3pager_sqlite3PagerSetError( Pager *pPager, int error) {
+ pPager->errCode = error;
- }
--#endif /* SQLITE_HAS_CODEC */
-
--#endif /* !SQLITE_OMIT_WAL */
++}
++
+#endif
+/* END CRYPTO */
-
--#endif /* SQLITE_OMIT_DISKIO */
-
++
++
/************** End of pager.c ***********************************************/
/************** Begin file wal.c *********************************************/
-@@ -46100,3253 +48211,2590 @@
- static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){
- int rc = SQLITE_OK;
-
-- /* Enlarge the pWal->apWiData[] array if required */
-- if( pWal->nWiData<=iPage ){
-- int nByte = sizeof(u32*)*(iPage+1);
-- volatile u32 **apNew;
-- apNew = (volatile u32 **)sqlite3_realloc((void *)pWal->apWiData, nByte);
-- if( !apNew ){
-- *ppPage = 0;
-- return SQLITE_NOMEM;
-- }
-- memset((void*)&apNew[pWal->nWiData], 0,
-- sizeof(u32*)*(iPage+1-pWal->nWiData));
-- pWal->apWiData = apNew;
-- pWal->nWiData = iPage+1;
-- }
--
-- /* Request a pointer to the required page from the VFS */
-- if( pWal->apWiData[iPage]==0 ){
-- if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){
-- pWal->apWiData[iPage] = (u32 volatile *)sqlite3MallocZero(WALINDEX_PGSZ);
-- if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM;
-- }else{
-- 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;
-- }
-- }
-- }
+ /*
+@@ -44127,3533 +46473,2869 @@
+ ** that correspond to frames greater than the new K value are removed
+ ** from the hash table at this point.
+ */
+-#ifndef SQLITE_OMIT_WAL
-
-- *ppPage = pWal->apWiData[iPage];
-- assert( iPage==0 || *ppPage || rc!=SQLITE_OK );
-- return rc;
--}
-
-/*
--** Return a pointer to the WalCkptInfo structure in the wal-index.
+-** Trace output macros
-*/
--static volatile WalCkptInfo *walCkptInfo(Wal *pWal){
-- assert( pWal->nWiData>0 && pWal->apWiData[0] );
-- return (volatile WalCkptInfo*)&(pWal->apWiData[0][sizeof(WalIndexHdr)/2]);
--}
+-#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
+-SQLITE_PRIVATE int sqlite3WalTrace = 0;
+-# define WALTRACE(X) if(sqlite3WalTrace) sqlite3DebugPrintf X
+-#else
+-# define WALTRACE(X)
+-#endif
+-
+-/*
+-** The maximum (and only) versions of the wal and wal-index formats
+-** that may be interpreted by this version of SQLite.
+-**
+-** If a client begins recovering a WAL file and finds that (a) the checksum
+-** values in the wal-header are correct and (b) the version field is not
+-** WAL_MAX_VERSION, recovery fails and SQLite returns SQLITE_CANTOPEN.
+-**
+-** Similarly, if a client successfully reads a wal-index header (i.e. the
+-** checksum test is successful) and finds that the version field is not
+-** WALINDEX_MAX_VERSION, then no read-transaction is opened and SQLite
+-** returns SQLITE_CANTOPEN.
+-*/
+-#define WAL_MAX_VERSION 3007000
+-#define WALINDEX_MAX_VERSION 3007000
+-
+-/*
+-** Indices of various locking bytes. WAL_NREADER is the number
+-** of available reader locks and should be at least 3.
+-*/
+-#define WAL_WRITE_LOCK 0
+-#define WAL_ALL_BUT_WRITE 1
+-#define WAL_CKPT_LOCK 1
+-#define WAL_RECOVER_LOCK 2
+-#define WAL_READ_LOCK(I) (3+(I))
+-#define WAL_NREADER (SQLITE_SHM_NLOCK-3)
+-
+-
+-/* Object declarations */
+-typedef struct WalIndexHdr WalIndexHdr;
+-typedef struct WalIterator WalIterator;
+-typedef struct WalCkptInfo WalCkptInfo;
+-
+-
+-/*
+-** The following object holds a copy of the wal-index header content.
+-**
+-** The actual header in the wal-index consists of two copies of this
+-** object.
+-**
+-** The szPage value can be any power of 2 between 512 and 32768, inclusive.
+-** Or it can be 1 to represent a 65536-byte page. The latter case was
+-** added in 3.7.1 when support for 64K pages was added.
+-*/
+-struct WalIndexHdr {
+- u32 iVersion; /* Wal-index version */
+- u32 unused; /* Unused (padding) field */
+- u32 iChange; /* Counter incremented each transaction */
+- u8 isInit; /* 1 when initialized */
+- u8 bigEndCksum; /* True if checksums in WAL are big-endian */
+- u16 szPage; /* Database page size in bytes. 1==64K */
+- u32 mxFrame; /* Index of last valid frame in the WAL */
+- u32 nPage; /* Size of database in pages */
+- u32 aFrameCksum[2]; /* Checksum of last frame in log */
+- u32 aSalt[2]; /* Two salt values copied from WAL header */
+- u32 aCksum[2]; /* Checksum over all prior fields */
+-};
+-
+-/*
+-** A copy of the following object occurs in the wal-index immediately
+-** following the second copy of the WalIndexHdr. This object stores
+-** information used by checkpoint.
+-**
+-** nBackfill is the number of frames in the WAL that have been written
+-** back into the database. (We call the act of moving content from WAL to
+-** database "backfilling".) The nBackfill number is never greater than
+-** WalIndexHdr.mxFrame. nBackfill can only be increased by threads
+-** holding the WAL_CKPT_LOCK lock (which includes a recovery thread).
+-** However, a WAL_WRITE_LOCK thread can move the value of nBackfill from
+-** mxFrame back to zero when the WAL is reset.
+-**
+-** There is one entry in aReadMark[] for each reader lock. If a reader
+-** holds read-lock K, then the value in aReadMark[K] is no greater than
+-** the mxFrame for that reader. The value READMARK_NOT_USED (0xffffffff)
+-** for any aReadMark[] means that entry is unused. aReadMark[0] is
+-** a special case; its value is never used and it exists as a place-holder
+-** to avoid having to offset aReadMark[] indexs by one. Readers holding
+-** WAL_READ_LOCK(0) always ignore the entire WAL and read all content
+-** directly from the database.
+-**
+-** The value of aReadMark[K] may only be changed by a thread that
+-** is holding an exclusive lock on WAL_READ_LOCK(K). Thus, the value of
+-** aReadMark[K] cannot changed while there is a reader is using that mark
+-** since the reader will be holding a shared lock on WAL_READ_LOCK(K).
+-**
+-** The checkpointer may only transfer frames from WAL to database where
+-** the frame numbers are less than or equal to every aReadMark[] that is
+-** in use (that is, every aReadMark[j] for which there is a corresponding
+-** WAL_READ_LOCK(j)). New readers (usually) pick the aReadMark[] with the
+-** largest value and will increase an unused aReadMark[] to mxFrame if there
+-** is not already an aReadMark[] equal to mxFrame. The exception to the
+-** previous sentence is when nBackfill equals mxFrame (meaning that everything
+-** in the WAL has been backfilled into the database) then new readers
+-** will choose aReadMark[0] which has value 0 and hence such reader will
+-** get all their all content directly from the database file and ignore
+-** the WAL.
+-**
+-** Writers normally append new frames to the end of the WAL. However,
+-** if nBackfill equals mxFrame (meaning that all WAL content has been
+-** written back into the database) and if no readers are using the WAL
+-** (in other words, if there are no WAL_READ_LOCK(i) where i>0) then
+-** the writer will first "reset" the WAL back to the beginning and start
+-** writing new content beginning at frame 1.
+-**
+-** We assume that 32-bit loads are atomic and so no locks are needed in
+-** order to read from any aReadMark[] entries.
+-*/
+-struct WalCkptInfo {
+- u32 nBackfill; /* Number of WAL frames backfilled into DB */
+- u32 aReadMark[WAL_NREADER]; /* Reader marks */
+-};
+-#define READMARK_NOT_USED 0xffffffff
+-
+-
+-/* A block of WALINDEX_LOCK_RESERVED bytes beginning at
+-** WALINDEX_LOCK_OFFSET is reserved for locks. Since some systems
+-** only support mandatory file-locks, we do not read or write data
+-** from the region of the file on which locks are applied.
+-*/
+-#define WALINDEX_LOCK_OFFSET (sizeof(WalIndexHdr)*2 + sizeof(WalCkptInfo))
+-#define WALINDEX_LOCK_RESERVED 16
+-#define WALINDEX_HDR_SIZE (WALINDEX_LOCK_OFFSET+WALINDEX_LOCK_RESERVED)
+-
+-/* Size of header before each frame in wal */
+-#define WAL_FRAME_HDRSIZE 24
+-
+-/* Size of write ahead log header, including checksum. */
+-/* #define WAL_HDRSIZE 24 */
+-#define WAL_HDRSIZE 32
+-
+-/* WAL magic value. Either this value, or the same value with the least
+-** significant bit also set (WAL_MAGIC | 0x00000001) is stored in 32-bit
+-** big-endian format in the first 4 bytes of a WAL file.
+-**
+-** If the LSB is set, then the checksums for each frame within the WAL
+-** file are calculated by treating all data as an array of 32-bit
+-** big-endian words. Otherwise, they are calculated by interpreting
+-** all data as 32-bit little-endian words.
+-*/
+-#define WAL_MAGIC 0x377f0682
+-
+-/*
+-** Return the offset of frame iFrame in the write-ahead log file,
+-** assuming a database page size of szPage bytes. The offset returned
+-** is to the start of the write-ahead log frame-header.
+-*/
+-#define walFrameOffset(iFrame, szPage) ( \
+- WAL_HDRSIZE + ((iFrame)-1)*(i64)((szPage)+WAL_FRAME_HDRSIZE) \
+-)
+-
+-/*
+-** An open write-ahead log file is represented by an instance of the
+-** following object.
+-*/
+-struct Wal {
+- sqlite3_vfs *pVfs; /* The VFS used to create pDbFd */
+- 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 */
+- int szFirstBlock; /* Size of first block written to WAL file */
+- volatile u32 **apWiData; /* Pointer to wal-index content in memory */
+- u32 szPage; /* Database page size */
+- i16 readLock; /* Which read lock is being held. -1 for none */
+- u8 syncFlags; /* Flags to use to sync header writes */
+- 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; /* WAL_RDWR, WAL_RDONLY, or WAL_SHM_RDONLY */
+- u8 truncateOnCommit; /* True to truncate WAL file on commit */
+- u8 syncHeader; /* Fsync the WAL header if true */
+- u8 padToSectorBoundary; /* Pad transactions out to the next sector */
+- WalIndexHdr hdr; /* Wal-index header for current transaction */
+- const char *zWalName; /* Name of WAL file */
+- u32 nCkpt; /* Checkpoint sequence counter in the wal-header */
+-#ifdef SQLITE_DEBUG
+- u8 lockError; /* True if a locking error has occurred */
+-#endif
+-};
+-
+-/*
+-** Candidate values for Wal.exclusiveMode.
+-*/
+-#define WAL_NORMAL_MODE 0
+-#define WAL_EXCLUSIVE_MODE 1
+-#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.
+-*/
+-typedef u16 ht_slot;
+-
+-/*
+-** This structure is used to implement an iterator that loops through
+-** all frames in the WAL in database page order. Where two or more frames
+-** correspond to the same database page, the iterator visits only the
+-** frame most recently written to the WAL (in other words, the frame with
+-** the largest index).
+-**
+-** The internals of this structure are only accessed by:
+-**
+-** walIteratorInit() - Create a new iterator,
+-** walIteratorNext() - Step an iterator,
+-** walIteratorFree() - Free an iterator.
+-**
+-** This functionality is used by the checkpoint code (see walCheckpoint()).
+-*/
+-struct WalIterator {
+- int iPrior; /* Last result returned from the iterator */
+- int nSegment; /* Number of entries in aSegment[] */
+- struct WalSegment {
+- int iNext; /* Next slot in aIndex[] not yet returned */
+- ht_slot *aIndex; /* i0, i1, i2... such that aPgno[iN] ascend */
+- u32 *aPgno; /* Array of page numbers. */
+- int nEntry; /* Nr. of entries in aPgno[] and aIndex[] */
+- int iZero; /* Frame number associated with aPgno[0] */
+- } aSegment[1]; /* One for every 32KB page in the wal-index */
+-};
+-
+-/*
+-** Define the parameters of the hash tables in the wal-index file. There
+-** is a hash-table following every HASHTABLE_NPAGE page numbers in the
+-** wal-index.
+-**
+-** Changing any of these constants will alter the wal-index format and
+-** create incompatibilities.
+-*/
+-#define HASHTABLE_NPAGE 4096 /* Must be power of 2 */
+-#define HASHTABLE_HASH_1 383 /* Should be prime */
+-#define HASHTABLE_NSLOT (HASHTABLE_NPAGE*2) /* Must be a power of 2 */
+-
+-/*
+-** The block of page numbers associated with the first hash-table in a
+-** wal-index is smaller than usual. This is so that there is a complete
+-** hash-table on each aligned 32KB page of the wal-index.
+-*/
+-#define HASHTABLE_NPAGE_ONE (HASHTABLE_NPAGE - (WALINDEX_HDR_SIZE/sizeof(u32)))
+-
+-/* The wal-index is divided into pages of WALINDEX_PGSZ bytes each. */
+-#define WALINDEX_PGSZ ( \
+- sizeof(ht_slot)*HASHTABLE_NSLOT + HASHTABLE_NPAGE*sizeof(u32) \
+-)
+-
+-/*
+-** Obtain a pointer to the iPage'th page of the wal-index. The wal-index
+-** is broken into pages of WALINDEX_PGSZ bytes. Wal-index pages are
+-** numbered from zero.
+-**
+-** If this call is successful, *ppPage is set to point to the wal-index
+-** page and SQLITE_OK is returned. If an error (an OOM or VFS error) occurs,
+-** then an SQLite error code is returned and *ppPage is set to 0.
+-*/
+-static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){
+- int rc = SQLITE_OK;
+-
+- /* Enlarge the pWal->apWiData[] array if required */
+- if( pWal->nWiData<=iPage ){
+- int nByte = sizeof(u32*)*(iPage+1);
+- volatile u32 **apNew;
+- apNew = (volatile u32 **)sqlite3_realloc((void *)pWal->apWiData, nByte);
+- if( !apNew ){
+- *ppPage = 0;
+- return SQLITE_NOMEM;
+- }
+- memset((void*)&apNew[pWal->nWiData], 0,
+- sizeof(u32*)*(iPage+1-pWal->nWiData));
+- pWal->apWiData = apNew;
+- pWal->nWiData = iPage+1;
+- }
+-
+- /* Request a pointer to the required page from the VFS */
+- if( pWal->apWiData[iPage]==0 ){
+- if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){
+- pWal->apWiData[iPage] = (u32 volatile *)sqlite3MallocZero(WALINDEX_PGSZ);
+- if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM;
+- }else{
+- 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;
+- }
+- }
+- }
+-
+- *ppPage = pWal->apWiData[iPage];
+- assert( iPage==0 || *ppPage || rc!=SQLITE_OK );
+- return rc;
+-}
+-
+-/*
+-** Return a pointer to the WalCkptInfo structure in the wal-index.
+-*/
+-static volatile WalCkptInfo *walCkptInfo(Wal *pWal){
+- assert( pWal->nWiData>0 && pWal->apWiData[0] );
+- return (volatile WalCkptInfo*)&(pWal->apWiData[0][sizeof(WalIndexHdr)/2]);
+-}
-
-/*
-** Return a pointer to the WalIndexHdr structure in the wal-index.
@@ -2798,13 +3277,15 @@
- *pnTruncate = sqlite3Get4byte(&aFrame[4]);
- return 1;
-}
--
--
++#ifndef SQLITE_OMIT_WAL
+
+
-#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
--/*
+ /*
-** Names of locks. This routine is used to provide debugging output and is not
-** a part of an ordinary build.
--*/
++** Trace output macros
+ */
-static const char *walLockName(int lockIdx){
- if( lockIdx==WAL_WRITE_LOCK ){
- return "WRITE-LOCK";
@@ -2821,14 +3302,30 @@
-}
-#endif /*defined(SQLITE_TEST) || defined(SQLITE_DEBUG) */
-
--
--/*
++#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
++SQLITE_PRIVATE int sqlite3WalTrace = 0;
++# define WALTRACE(X) if(sqlite3WalTrace) sqlite3DebugPrintf X
++#else
++# define WALTRACE(X)
++#endif
+
+ /*
-** Set or release locks on the WAL. Locks are either shared or exclusive.
-** A lock cannot be moved directly between shared and exclusive - it must go
-** through the unlocked state first.
--**
++** The maximum (and only) versions of the wal and wal-index formats
++** that may be interpreted by this version of SQLite.
+ **
-** In locking_mode=EXCLUSIVE, all of these routines become no-ops.
--*/
++** If a client begins recovering a WAL file and finds that (a) the checksum
++** values in the wal-header are correct and (b) the version field is not
++** WAL_MAX_VERSION, recovery fails and SQLite returns SQLITE_CANTOPEN.
++**
++** Similarly, if a client successfully reads a wal-index header (i.e. the
++** checksum test is successful) and finds that the version field is not
++** WALINDEX_MAX_VERSION, then no read-transaction is opened and SQLite
++** returns SQLITE_CANTOPEN.
+ */
-static int walLockShared(Wal *pWal, int lockIdx){
- int rc;
- if( pWal->exclusiveMode ) return SQLITE_OK;
@@ -2862,8 +3359,10 @@
- WALTRACE(("WAL%p: release EXCLUSIVE-%s cnt=%d\n", pWal,
- walLockName(lockIdx), n));
-}
--
--/*
++#define WAL_MAX_VERSION 3007000
++#define WALINDEX_MAX_VERSION 3007000
+
+ /*
-** Compute a hash on a page number. The resulting hash value must land
-** between 0 and (HASHTABLE_NSLOT-1). The walHashNext() function advances
-** the hash to the next value in the event of a collision.
@@ -2890,7 +3389,9 @@
-**
-** Finally, set *paPgno so that *paPgno[1] is the page number of the
-** first frame indexed by the hash table, frame (*piZero+1).
--*/
++** Indices of various locking bytes. WAL_NREADER is the number
++** of available reader locks and should be at least 3.
+ */
-static int walHashGet(
- Wal *pWal, /* WAL handle */
- int iHash, /* Find the iHash'th table */
@@ -2903,11 +3404,17 @@
-
- rc = walIndexPage(pWal, iHash, &aPgno);
- assert( rc==SQLITE_OK || iHash>0 );
--
++#define WAL_WRITE_LOCK 0
++#define WAL_ALL_BUT_WRITE 1
++#define WAL_CKPT_LOCK 1
++#define WAL_RECOVER_LOCK 2
++#define WAL_READ_LOCK(I) (3+(I))
++#define WAL_NREADER (SQLITE_SHM_NLOCK-3)
+
- if( rc==SQLITE_OK ){
- u32 iZero;
- volatile ht_slot *aHash;
--
+
- aHash = (volatile ht_slot *)&aPgno[HASHTABLE_NPAGE];
- if( iHash==0 ){
- aPgno = &aPgno[WALINDEX_HDR_SIZE/sizeof(u32)];
@@ -2922,7 +3429,11 @@
- }
- return rc;
-}
--
++/* Object declarations */
++typedef struct WalIndexHdr WalIndexHdr;
++typedef struct WalIterator WalIterator;
++typedef struct WalCkptInfo WalCkptInfo;
+
-/*
-** Return the number of the wal-index page that contains the hash-table
-** and page-number array that contain entries corresponding to WAL frame
@@ -2939,10 +3450,18 @@
- );
- return iHash;
-}
--
--/*
+
+ /*
-** Return the page number associated with frame iFrame in this WAL.
--*/
++** The following object holds a copy of the wal-index header content.
++**
++** The actual header in the wal-index consists of two copies of this
++** object.
++**
++** The szPage value can be any power of 2 between 512 and 32768, inclusive.
++** Or it can be 1 to represent a 65536-byte page. The latter case was
++** added in 3.7.1 when support for 64K pages was added.
+ */
-static u32 walFramePgno(Wal *pWal, u32 iFrame){
- int iHash = walFramePage(iFrame);
- if( iHash==0 ){
@@ -2950,19 +3469,77 @@
- }
- return pWal->apWiData[iHash][(iFrame-1-HASHTABLE_NPAGE_ONE)%HASHTABLE_NPAGE];
-}
--
--/*
++struct WalIndexHdr {
++ u32 iVersion; /* Wal-index version */
++ u32 unused; /* Unused (padding) field */
++ u32 iChange; /* Counter incremented each transaction */
++ u8 isInit; /* 1 when initialized */
++ u8 bigEndCksum; /* True if checksums in WAL are big-endian */
++ u16 szPage; /* Database page size in bytes. 1==64K */
++ u32 mxFrame; /* Index of last valid frame in the WAL */
++ u32 nPage; /* Size of database in pages */
++ u32 aFrameCksum[2]; /* Checksum of last frame in log */
++ u32 aSalt[2]; /* Two salt values copied from WAL header */
++ u32 aCksum[2]; /* Checksum over all prior fields */
++};
+
+ /*
-** Remove entries from the hash table that point to WAL slots greater
-** than pWal->hdr.mxFrame.
--**
++** A copy of the following object occurs in the wal-index immediately
++** following the second copy of the WalIndexHdr. This object stores
++** information used by checkpoint.
+ **
-** This function is called whenever pWal->hdr.mxFrame is decreased due
-** to a rollback or savepoint.
--**
++** nBackfill is the number of frames in the WAL that have been written
++** back into the database. (We call the act of moving content from WAL to
++** database "backfilling".) The nBackfill number is never greater than
++** WalIndexHdr.mxFrame. nBackfill can only be increased by threads
++** holding the WAL_CKPT_LOCK lock (which includes a recovery thread).
++** However, a WAL_WRITE_LOCK thread can move the value of nBackfill from
++** mxFrame back to zero when the WAL is reset.
+ **
-** At most only the hash table containing pWal->hdr.mxFrame needs to be
-** updated. Any later hash tables will be automatically cleared when
-** pWal->hdr.mxFrame advances to the point where those hash tables are
-** actually needed.
--*/
++** There is one entry in aReadMark[] for each reader lock. If a reader
++** holds read-lock K, then the value in aReadMark[K] is no greater than
++** the mxFrame for that reader. The value READMARK_NOT_USED (0xffffffff)
++** for any aReadMark[] means that entry is unused. aReadMark[0] is
++** a special case; its value is never used and it exists as a place-holder
++** to avoid having to offset aReadMark[] indexs by one. Readers holding
++** WAL_READ_LOCK(0) always ignore the entire WAL and read all content
++** directly from the database.
++**
++** The value of aReadMark[K] may only be changed by a thread that
++** is holding an exclusive lock on WAL_READ_LOCK(K). Thus, the value of
++** aReadMark[K] cannot changed while there is a reader is using that mark
++** since the reader will be holding a shared lock on WAL_READ_LOCK(K).
++**
++** The checkpointer may only transfer frames from WAL to database where
++** the frame numbers are less than or equal to every aReadMark[] that is
++** in use (that is, every aReadMark[j] for which there is a corresponding
++** WAL_READ_LOCK(j)). New readers (usually) pick the aReadMark[] with the
++** largest value and will increase an unused aReadMark[] to mxFrame if there
++** is not already an aReadMark[] equal to mxFrame. The exception to the
++** previous sentence is when nBackfill equals mxFrame (meaning that everything
++** in the WAL has been backfilled into the database) then new readers
++** will choose aReadMark[0] which has value 0 and hence such reader will
++** get all their all content directly from the database file and ignore
++** the WAL.
++**
++** Writers normally append new frames to the end of the WAL. However,
++** if nBackfill equals mxFrame (meaning that all WAL content has been
++** written back into the database) and if no readers are using the WAL
++** (in other words, if there are no WAL_READ_LOCK(i) where i>0) then
++** the writer will first "reset" the WAL back to the beginning and start
++** writing new content beginning at frame 1.
++**
++** We assume that 32-bit loads are atomic and so no locks are needed in
++** order to read from any aReadMark[] entries.
+ */
-static void walCleanupHash(Wal *pWal){
- volatile ht_slot *aHash = 0; /* Pointer to hash table to clear */
- volatile u32 *aPgno = 0; /* Page number array for hash table */
@@ -2975,9 +3552,14 @@
- testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE-1 );
- testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE );
- testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE+1 );
--
++struct WalCkptInfo {
++ u32 nBackfill; /* Number of WAL frames backfilled into DB */
++ u32 aReadMark[WAL_NREADER]; /* Reader marks */
++};
++#define READMARK_NOT_USED 0xffffffff
+
- if( pWal->hdr.mxFrame==0 ) return;
--
+
- /* Obtain pointers to the hash-table and page-number array containing
- ** the entry that corresponds to frame pWal->hdr.mxFrame. It is guaranteed
- ** that the page said hash-table and array reside on is already mapped.
@@ -2985,7 +3567,15 @@
- assert( pWal->nWiData>walFramePage(pWal->hdr.mxFrame) );
- assert( pWal->apWiData[walFramePage(pWal->hdr.mxFrame)] );
- walHashGet(pWal, walFramePage(pWal->hdr.mxFrame), &aHash, &aPgno, &iZero);
--
++/* A block of WALINDEX_LOCK_RESERVED bytes beginning at
++** WALINDEX_LOCK_OFFSET is reserved for locks. Since some systems
++** only support mandatory file-locks, we do not read or write data
++** from the region of the file on which locks are applied.
++*/
++#define WALINDEX_LOCK_OFFSET (sizeof(WalIndexHdr)*2 + sizeof(WalCkptInfo))
++#define WALINDEX_LOCK_RESERVED 16
++#define WALINDEX_HDR_SIZE (WALINDEX_LOCK_OFFSET+WALINDEX_LOCK_RESERVED)
+
- /* Zero all hash-table entries that correspond to frame numbers greater
- ** than pWal->hdr.mxFrame.
- */
@@ -3002,7 +3592,9 @@
- */
- nByte = (int)((char *)aHash - (char *)&aPgno[iLimit+1]);
- memset((void *)&aPgno[iLimit+1], 0, nByte);
--
++/* Size of header before each frame in wal */
++#define WAL_FRAME_HDRSIZE 24
+
-#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT
- /* Verify that the every entry in the mapping region is still reachable
- ** via the hash table even after the cleanup.
@@ -3019,12 +3611,28 @@
- }
-#endif /* SQLITE_ENABLE_EXPENSIVE_ASSERT */
-}
--
--
--/*
++/* Size of write ahead log header, including checksum. */
++/* #define WAL_HDRSIZE 24 */
++#define WAL_HDRSIZE 32
+
++/* WAL magic value. Either this value, or the same value with the least
++** significant bit also set (WAL_MAGIC | 0x00000001) is stored in 32-bit
++** big-endian format in the first 4 bytes of a WAL file.
++**
++** If the LSB is set, then the checksums for each frame within the WAL
++** file are calculated by treating all data as an array of 32-bit
++** big-endian words. Otherwise, they are calculated by interpreting
++** all data as 32-bit little-endian words.
++*/
++#define WAL_MAGIC 0x377f0682
+
+ /*
-** Set an entry in the wal-index that will map database page number
-** pPage into WAL frame iFrame.
--*/
++** Return the offset of frame iFrame in the write-ahead log file,
++** assuming a database page size of szPage bytes. The offset returned
++** is to the start of the write-ahead log frame-header.
+ */
-static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){
- int rc; /* Return code */
- u32 iZero = 0; /* One less than frame number of aPgno[1] */
@@ -3040,7 +3648,10 @@
- int iKey; /* Hash table key */
- int idx; /* Value to write to hash-table slot */
- int nCollide; /* Number of hash collisions */
--
++#define walFrameOffset(iFrame, szPage) ( \
++ WAL_HDRSIZE + ((iFrame)-1)*(i64)((szPage)+WAL_FRAME_HDRSIZE) \
++)
+
- idx = iFrame - iZero;
- assert( idx <= HASHTABLE_NSLOT/2 + 1 );
-
@@ -3051,7 +3662,37 @@
- int nByte = (int)((u8 *)&aHash[HASHTABLE_NSLOT] - (u8 *)&aPgno[1]);
- memset((void*)&aPgno[1], 0, nByte);
- }
--
++/*
++** An open write-ahead log file is represented by an instance of the
++** following object.
++*/
++struct Wal {
++ sqlite3_vfs *pVfs; /* The VFS used to create pDbFd */
++ 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 */
++ int szFirstBlock; /* Size of first block written to WAL file */
++ volatile u32 **apWiData; /* Pointer to wal-index content in memory */
++ u32 szPage; /* Database page size */
++ i16 readLock; /* Which read lock is being held. -1 for none */
++ u8 syncFlags; /* Flags to use to sync header writes */
++ 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; /* WAL_RDWR, WAL_RDONLY, or WAL_SHM_RDONLY */
++ u8 truncateOnCommit; /* True to truncate WAL file on commit */
++ u8 syncHeader; /* Fsync the WAL header if true */
++ u8 padToSectorBoundary; /* Pad transactions out to the next sector */
++ WalIndexHdr hdr; /* Wal-index header for current transaction */
++ const char *zWalName; /* Name of WAL file */
++ u32 nCkpt; /* Checkpoint sequence counter in the wal-header */
++#ifdef SQLITE_DEBUG
++ u8 lockError; /* True if a locking error has occurred */
++#endif
++};
+
- /* If the entry in aPgno[] is already set, then the previous writer
- ** must have exited unexpectedly in the middle of a transaction (after
- ** writing one or more dirty pages to the WAL to free up memory).
@@ -3062,7 +3703,13 @@
- walCleanupHash(pWal);
- assert( !aPgno[idx] );
- }
--
++/*
++** Candidate values for Wal.exclusiveMode.
++*/
++#define WAL_NORMAL_MODE 0
++#define WAL_EXCLUSIVE_MODE 1
++#define WAL_HEAPMEMORY_MODE 2
+
- /* Write the aPgno[] array entry and the hash-table slot. */
- nCollide = idx;
- for(iKey=walHash(iPage); aHash[iKey]; iKey=walNextHash(iKey)){
@@ -3070,7 +3717,13 @@
- }
- aPgno[idx] = iPage;
- aHash[iKey] = (ht_slot)idx;
--
++/*
++** 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 */
+
-#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT
- /* Verify that the number of entries in the hash table exactly equals
- ** the number of entries in the mapping region.
@@ -3080,20 +3733,12 @@
- int nEntry = 0; /* Number of entries in the hash table */
- for(i=0; i<HASHTABLE_NSLOT; i++){ if( aHash[i] ) nEntry++; }
- assert( nEntry==idx );
-+ /* Enlarge the pWal->apWiData[] array if required */
-+ if( pWal->nWiData<=iPage ){
-+ int nByte = sizeof(u32*)*(iPage+1);
-+ volatile u32 **apNew;
-+ apNew = (volatile u32 **)sqlite3_realloc((void *)pWal->apWiData, nByte);
-+ if( !apNew ){
-+ *ppPage = 0;
-+ return SQLITE_NOMEM;
- }
-+ memset((void*)&apNew[pWal->nWiData], 0,
-+ sizeof(u32*)*(iPage+1-pWal->nWiData));
-+ pWal->apWiData = apNew;
-+ pWal->nWiData = iPage+1;
-+ }
+- }
++/*
++** Each page of the wal-index mapping contains a hash-table made up of
++** an array of HASHTABLE_NSLOT elements of the following type.
++*/
++typedef u16 ht_slot;
- /* Verify that the every entry in the mapping region is reachable
- ** via the hash table. This turns out to be a really, really expensive
@@ -3107,47 +3752,78 @@
- if( aHash[iKey]==i ) break;
- }
- assert( aHash[iKey]==i );
-+ /* Request a pointer to the required page from the VFS */
-+ if( pWal->apWiData[iPage]==0 ){
-+ if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){
-+ pWal->apWiData[iPage] = (u32 volatile *)sqlite3MallocZero(WALINDEX_PGSZ);
-+ if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM;
-+ }else{
-+ 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;
- }
- }
+- }
+- }
-#endif /* SQLITE_ENABLE_EXPENSIVE_ASSERT */
- }
-
--
-+ *ppPage = pWal->apWiData[iPage];
-+ assert( iPage==0 || *ppPage || rc!=SQLITE_OK );
- return rc;
- }
+- }
++/*
++** This structure is used to implement an iterator that loops through
++** all frames in the WAL in database page order. Where two or more frames
++** correspond to the same database page, the iterator visits only the
++** frame most recently written to the WAL (in other words, the frame with
++** the largest index).
++**
++** The internals of this structure are only accessed by:
++**
++** walIteratorInit() - Create a new iterator,
++** walIteratorNext() - Step an iterator,
++** walIteratorFree() - Free an iterator.
++**
++** This functionality is used by the checkpoint code (see walCheckpoint()).
++*/
++struct WalIterator {
++ int iPrior; /* Last result returned from the iterator */
++ int nSegment; /* Number of entries in aSegment[] */
++ struct WalSegment {
++ int iNext; /* Next slot in aIndex[] not yet returned */
++ ht_slot *aIndex; /* i0, i1, i2... such that aPgno[iN] ascend */
++ u32 *aPgno; /* Array of page numbers. */
++ int nEntry; /* Nr. of entries in aPgno[] and aIndex[] */
++ int iZero; /* Frame number associated with aPgno[0] */
++ } aSegment[1]; /* One for every 32KB page in the wal-index */
++};
+/*
-+** Return a pointer to the WalCkptInfo structure in the wal-index.
++** Define the parameters of the hash tables in the wal-index file. There
++** is a hash-table following every HASHTABLE_NPAGE page numbers in the
++** wal-index.
++**
++** Changing any of these constants will alter the wal-index format and
++** create incompatibilities.
+*/
-+static volatile WalCkptInfo *walCkptInfo(Wal *pWal){
-+ assert( pWal->nWiData>0 && pWal->apWiData[0] );
-+ return (volatile WalCkptInfo*)&(pWal->apWiData[0][sizeof(WalIndexHdr)/2]);
-+}
++#define HASHTABLE_NPAGE 4096 /* Must be power of 2 */
++#define HASHTABLE_HASH_1 383 /* Should be prime */
++#define HASHTABLE_NSLOT (HASHTABLE_NPAGE*2) /* Must be a power of 2 */
+
+- return rc;
+-}
++/*
++** The block of page numbers associated with the first hash-table in a
++** wal-index is smaller than usual. This is so that there is a complete
++** hash-table on each aligned 32KB page of the wal-index.
++*/
++#define HASHTABLE_NPAGE_ONE (HASHTABLE_NPAGE - (WALINDEX_HDR_SIZE/sizeof(u32)))
+
++/* The wal-index is divided into pages of WALINDEX_PGSZ bytes each. */
++#define WALINDEX_PGSZ ( \
++ sizeof(ht_slot)*HASHTABLE_NSLOT + HASHTABLE_NPAGE*sizeof(u32) \
++)
/*
-** Recover the wal-index by reading the write-ahead log file.
--**
++** Obtain a pointer to the iPage'th page of the wal-index. The wal-index
++** is broken into pages of WALINDEX_PGSZ bytes. Wal-index pages are
++** numbered from zero.
+ **
-** This routine first tries to establish an exclusive lock on the
-** wal-index to prevent other threads/processes from doing anything
-** with the WAL or wal-index while recovery is running. The
-** WAL_RECOVER_LOCK is also held so that other threads will know
-** that this thread is running recovery. If unable to establish
-** the necessary locks, this routine returns SQLITE_BUSY.
-+** Return a pointer to the WalIndexHdr structure in the wal-index.
++** If this call is successful, *ppPage is set to point to the wal-index
++** page and SQLITE_OK is returned. If an error (an OOM or VFS error) occurs,
++** then an SQLite error code is returned and *ppPage is set to 0.
*/
-static int walIndexRecover(Wal *pWal){
- int rc; /* Return Code */
@@ -3155,11 +3831,7 @@
- u32 aFrameCksum[2] = {0, 0};
- int iLock; /* Lock offset to lock for checkpoint */
- int nLock; /* Number of locks to hold */
-+static volatile WalIndexHdr *walIndexHdr(Wal *pWal){
-+ assert( pWal->nWiData>0 && pWal->apWiData[0] );
-+ return (volatile WalIndexHdr*)pWal->apWiData[0];
-+}
-
+-
- /* Obtain an exclusive lock on all byte in the locking range not already
- ** locked by the caller. The caller is guaranteed to have locked the
- ** WAL_WRITE_LOCK byte, and may have also locked the WAL_CKPT_LOCK byte.
@@ -3177,47 +3849,27 @@
- return rc;
- }
- WALTRACE(("WAL%p: recovery begin...\n", pWal));
-+/*
-+** The argument to this macro must be of type u32. On a little-endian
-+** architecture, it returns the u32 value that results from interpreting
-+** the 4 bytes as a big-endian value. On a big-endian architecture, it
-+** returns the value that would be produced by intepreting the 4 bytes
-+** of the input value as a little-endian integer.
-+*/
-+#define BYTESWAP32(x) ( \
-+ (((x)&0x000000FF)<<24) + (((x)&0x0000FF00)<<8) \
-+ + (((x)&0x00FF0000)>>8) + (((x)&0xFF000000)>>24) \
-+)
-
+-
- memset(&pWal->hdr, 0, sizeof(WalIndexHdr));
-+/*
-+** Generate or extend an 8 byte checksum based on the data in
-+** array aByte[] and the initial values of aIn[0] and aIn[1] (or
-+** initial values of 0 and 0 if aIn==NULL).
-+**
-+** The checksum is written back into aOut[] before returning.
-+**
-+** nByte must be a positive multiple of 8.
-+*/
-+static void walChecksumBytes(
-+ int nativeCksum, /* True for native byte-order, false for non-native */
-+ u8 *a, /* Content to be checksummed */
-+ int nByte, /* Bytes of content in a[]. Must be a multiple of 8. */
-+ const u32 *aIn, /* Initial checksum value input */
-+ u32 *aOut /* OUT: Final checksum value output */
-+){
-+ u32 s1, s2;
-+ u32 *aData = (u32 *)a;
-+ u32 *aEnd = (u32 *)&a[nByte];
++static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){
++ int rc = SQLITE_OK;
- rc = sqlite3OsFileSize(pWal->pWalFd, &nSize);
- if( rc!=SQLITE_OK ){
- goto recovery_error;
-+ if( aIn ){
-+ s1 = aIn[0];
-+ s2 = aIn[1];
-+ }else{
-+ s1 = s2 = 0;
++ /* Enlarge the pWal->apWiData[] array if required */
++ if( pWal->nWiData<=iPage ){
++ int nByte = sizeof(u32*)*(iPage+1);
++ volatile u32 **apNew;
++ apNew = (volatile u32 **)sqlite3_realloc((void *)pWal->apWiData, nByte);
++ if( !apNew ){
++ *ppPage = 0;
++ return SQLITE_NOMEM;
++ }
++ memset((void*)&apNew[pWal->nWiData], 0,
++ sizeof(u32*)*(iPage+1-pWal->nWiData));
++ pWal->apWiData = apNew;
++ pWal->nWiData = iPage+1;
}
- if( nSize>WAL_HDRSIZE ){
@@ -3236,8 +3888,22 @@
- rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0);
- if( rc!=SQLITE_OK ){
- goto recovery_error;
-- }
--
++ /* Request a pointer to the required page from the VFS */
++ if( pWal->apWiData[iPage]==0 ){
++ if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){
++ pWal->apWiData[iPage] = (u32 volatile *)sqlite3MallocZero(WALINDEX_PGSZ);
++ if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM;
++ }else{
++ 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;
++ }
+ }
++ }
+
- /* If the database page size is not a power of two, or is greater than
- ** SQLITE_MAX_PAGE_SIZE, conclude that the WAL file contains no valid
- ** data. Similarly, if the 'magic' value is invalid, ignore the whole
@@ -3256,7 +3922,11 @@
- pWal->szPage = szPage;
- pWal->nCkpt = sqlite3Get4byte(&aBuf[12]);
- memcpy(&pWal->hdr.aSalt, &aBuf[16], 8);
--
++ *ppPage = pWal->apWiData[iPage];
++ assert( iPage==0 || *ppPage || rc!=SQLITE_OK );
++ return rc;
++}
+
- /* Verify that the WAL header checksum is correct */
- walChecksumBytes(pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN,
- aBuf, WAL_HDRSIZE-2*4, 0, pWal->hdr.aFrameCksum
@@ -3266,7 +3936,14 @@
- ){
- goto finished;
- }
--
++/*
++** Return a pointer to the WalCkptInfo structure in the wal-index.
++*/
++static volatile WalCkptInfo *walCkptInfo(Wal *pWal){
++ assert( pWal->nWiData>0 && pWal->apWiData[0] );
++ return (volatile WalCkptInfo*)&(pWal->apWiData[0][sizeof(WalIndexHdr)/2]);
++}
+
- /* Verify that the version number on the WAL format is one that
- ** are able to understand */
- version = sqlite3Get4byte(&aBuf[4]);
@@ -3274,7 +3951,14 @@
- rc = SQLITE_CANTOPEN_BKPT;
- goto finished;
- }
--
++/*
++** Return a pointer to the WalIndexHdr structure in the wal-index.
++*/
++static volatile WalIndexHdr *walIndexHdr(Wal *pWal){
++ assert( pWal->nWiData>0 && pWal->apWiData[0] );
++ return (volatile WalIndexHdr*)pWal->apWiData[0];
++}
+
- /* Malloc a buffer to read frames into. */
- szFrame = szPage + WAL_FRAME_HDRSIZE;
- aFrame = (u8 *)sqlite3_malloc(szFrame);
@@ -3283,13 +3967,43 @@
- goto recovery_error;
- }
- aData = &aFrame[WAL_FRAME_HDRSIZE];
--
++/*
++** The argument to this macro must be of type u32. On a little-endian
++** architecture, it returns the u32 value that results from interpreting
++** the 4 bytes as a big-endian value. On a big-endian architecture, it
++** returns the value that would be produced by intepreting the 4 bytes
++** of the input value as a little-endian integer.
++*/
++#define BYTESWAP32(x) ( \
++ (((x)&0x000000FF)<<24) + (((x)&0x0000FF00)<<8) \
++ + (((x)&0x00FF0000)>>8) + (((x)&0xFF000000)>>24) \
++)
+
- /* Read all frames from the log file. */
- iFrame = 0;
- for(iOffset=WAL_HDRSIZE; (iOffset+szFrame)<=nSize; iOffset+=szFrame){
- u32 pgno; /* Database page number for frame */
- u32 nTruncate; /* dbsize field from frame header */
--
++/*
++** Generate or extend an 8 byte checksum based on the data in
++** array aByte[] and the initial values of aIn[0] and aIn[1] (or
++** initial values of 0 and 0 if aIn==NULL).
++**
++** The checksum is written back into aOut[] before returning.
++**
++** nByte must be a positive multiple of 8.
++*/
++static void walChecksumBytes(
++ int nativeCksum, /* True for native byte-order, false for non-native */
++ u8 *a, /* Content to be checksummed */
++ int nByte, /* Bytes of content in a[]. Must be a multiple of 8. */
++ const u32 *aIn, /* Initial checksum value input */
++ u32 *aOut /* OUT: Final checksum value output */
++){
++ u32 s1, s2;
++ u32 *aData = (u32 *)a;
++ u32 *aEnd = (u32 *)&a[nByte];
+
- /* Read and decode the next log frame. */
- iFrame++;
- rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset);
@@ -3298,7 +4012,13 @@
- if( !isValid ) break;
- rc = walIndexAppend(pWal, iFrame, pgno);
- if( rc!=SQLITE_OK ) break;
--
++ if( aIn ){
++ s1 = aIn[0];
++ s2 = aIn[1];
++ }else{
++ s1 = s2 = 0;
++ }
+
- /* If nTruncate is non-zero, this is a commit record. */
- if( nTruncate ){
- pWal->hdr.mxFrame = iFrame;
@@ -3343,6 +4063,7 @@
- pInfo->nBackfill = 0;
- pInfo->aReadMark[0] = 0;
- for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
+- if( pWal->hdr.mxFrame ) pInfo->aReadMark[1] = pWal->hdr.mxFrame;
+ aOut[0] = s1;
+ aOut[1] = s2;
+}
@@ -3505,31 +4226,18 @@
- pRet->syncHeader = 1;
- pRet->padToSectorBoundary = 1;
- pRet->exclusiveMode = (bNoShm ? WAL_HEAPMEMORY_MODE: WAL_NORMAL_MODE);
--
-- /* Open file handle on the write-ahead log file. */
-- 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 = WAL_RDONLY;
+ /* A frame is only valid if the page number is creater than zero.
+ */
+ pgno = sqlite3Get4byte(&aFrame[0]);
+ if( pgno==0 ){
+ return 0;
- }
++ }
-- if( rc!=SQLITE_OK ){
-- walIndexClose(pRet, 0);
-- sqlite3OsClose(pRet->pWalFd);
-- sqlite3_free(pRet);
-- }else{
-- int iDC = sqlite3OsDeviceCharacteristics(pRet->pWalFd);
-- if( iDC & SQLITE_IOCAP_SEQUENTIAL ){ pRet->syncHeader = 0; }
-- if( iDC & SQLITE_IOCAP_POWERSAFE_OVERWRITE ){
-- pRet->padToSectorBoundary = 0;
-- }
-- *ppWal = pRet;
-- WALTRACE(("WAL%d: opened\n", pRet));
+- /* Open file handle on the write-ahead log file. */
+- 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 = WAL_RDONLY;
+ /* A frame is only valid if a checksum of the WAL header,
+ ** all prior frams, the first 16 bytes of this frame-header,
+ ** and the frame-data matches the checksum in the last 8
@@ -3544,25 +4252,25 @@
+ /* Checksum failed. */
+ return 0;
}
-- return rc;
-+
+
+- if( rc!=SQLITE_OK ){
+- walIndexClose(pRet, 0);
+- sqlite3OsClose(pRet->pWalFd);
+- sqlite3_free(pRet);
+ /* If we reach this point, the frame is valid. Return the page number
+ ** and the new database size.
+ */
+ *piPage = pgno;
+ *pnTruncate = sqlite3Get4byte(&aFrame[4]);
+ return 1;
- }
-
++}
++
+
+#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
- /*
--** Change the size to which the WAL file is trucated on each reset.
++/*
+** Names of locks. This routine is used to provide debugging output and is not
+** a part of an ordinary build.
- */
--SQLITE_PRIVATE void sqlite3WalLimit(Wal *pWal, i64 iLimit){
-- if( pWal ) pWal->mxWalSize = iLimit;
++*/
+static const char *walLockName(int lockIdx){
+ if( lockIdx==WAL_WRITE_LOCK ){
+ return "WRITE-LOCK";
@@ -3570,59 +4278,35 @@
+ return "CKPT-LOCK";
+ }else if( lockIdx==WAL_RECOVER_LOCK ){
+ return "RECOVER-LOCK";
-+ }else{
+ }else{
+- int iDC = sqlite3OsDeviceCharacteristics(pRet->pWalFd);
+- if( iDC & SQLITE_IOCAP_SEQUENTIAL ){ pRet->syncHeader = 0; }
+- if( iDC & SQLITE_IOCAP_POWERSAFE_OVERWRITE ){
+- pRet->padToSectorBoundary = 0;
+- }
+- *ppWal = pRet;
+- WALTRACE(("WAL%d: opened\n", pRet));
+ static char zName[15];
+ sqlite3_snprintf(sizeof(zName), zName, "READ-LOCK[%d]",
+ lockIdx-WAL_READ_LOCK(0));
+ return zName;
-+ }
+ }
+- return rc;
}
+#endif /*defined(SQLITE_TEST) || defined(SQLITE_DEBUG) */
+
/*
--** 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
--** that page was last written into the WAL. Write into *piPage the page
--** number.
+-** Change the size to which the WAL file is trucated on each reset.
+** Set or release locks on the WAL. Locks are either shared or exclusive.
+** A lock cannot be moved directly between shared and exclusive - it must go
+** through the unlocked state first.
- **
--** Return 0 on success. If there are no pages in the WAL with a page
--** number larger than *piPage, then return 1.
++**
+** In locking_mode=EXCLUSIVE, all of these routines become no-ops.
*/
--static int walIteratorNext(
-- WalIterator *p, /* Iterator */
-- u32 *piPage, /* OUT: The page number of the next page */
-- u32 *piFrame /* OUT: Wal frame index of next page */
--){
-- u32 iMin; /* Result pgno must be greater than iMin */
-- u32 iRet = 0xFFFFFFFF; /* 0xffffffff is never a valid page number */
-- int i; /* For looping through segments */
--
-- iMin = p->iPrior;
-- assert( iMin<0xffffffff );
-- for(i=p->nSegment-1; i>=0; i--){
-- struct WalSegment *pSegment = &p->aSegment[i];
-- while( pSegment->iNext<pSegment->nEntry ){
-- u32 iPg = pSegment->aPgno[pSegment->aIndex[pSegment->iNext]];
-- if( iPg>iMin ){
-- if( iPg<iRet ){
-- iRet = iPg;
-- *piFrame = pSegment->iZero + pSegment->aIndex[pSegment->iNext];
-- }
-- break;
-- }
-- pSegment->iNext++;
-- }
-- }
--
-- *piPage = p->iPrior = iRet;
-- return (iRet==0xFFFFFFFF);
-+static int walLockShared(Wal *pWal, int lockIdx){
+-SQLITE_PRIVATE void sqlite3WalLimit(Wal *pWal, i64 iLimit){
+- if( pWal ) pWal->mxWalSize = iLimit;
++static int walLockShared(Wal *pWal, int lockIdx){
+ int rc;
+ if( pWal->exclusiveMode ) return SQLITE_OK;
+ rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1,
@@ -3657,6 +4341,56 @@
}
/*
+-** 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
+-** that page was last written into the WAL. Write into *piPage the page
+-** number.
+-**
+-** Return 0 on success. If there are no pages in the WAL with a page
+-** number larger than *piPage, then return 1.
++** Compute a hash on a page number. The resulting hash value must land
++** between 0 and (HASHTABLE_NSLOT-1). The walHashNext() function advances
++** the hash to the next value in the event of a collision.
+ */
+-static int walIteratorNext(
+- WalIterator *p, /* Iterator */
+- u32 *piPage, /* OUT: The page number of the next page */
+- u32 *piFrame /* OUT: Wal frame index of next page */
+-){
+- u32 iMin; /* Result pgno must be greater than iMin */
+- u32 iRet = 0xFFFFFFFF; /* 0xffffffff is never a valid page number */
+- int i; /* For looping through segments */
+-
+- iMin = p->iPrior;
+- assert( iMin<0xffffffff );
+- for(i=p->nSegment-1; i>=0; i--){
+- struct WalSegment *pSegment = &p->aSegment[i];
+- while( pSegment->iNext<pSegment->nEntry ){
+- u32 iPg = pSegment->aPgno[pSegment->aIndex[pSegment->iNext]];
+- if( iPg>iMin ){
+- if( iPg<iRet ){
+- iRet = iPg;
+- *piFrame = pSegment->iZero + pSegment->aIndex[pSegment->iNext];
+- }
+- break;
+- }
+- pSegment->iNext++;
+- }
+- }
+-
+- *piPage = p->iPrior = iRet;
+- return (iRet==0xFFFFFFFF);
++static int walHash(u32 iPage){
++ assert( iPage>0 );
++ assert( (HASHTABLE_NSLOT & (HASHTABLE_NSLOT-1))==0 );
++ return (iPage*HASHTABLE_HASH_1) & (HASHTABLE_NSLOT-1);
++}
++static int walNextHash(int iPriorHash){
++ return (iPriorHash+1)&(HASHTABLE_NSLOT-1);
+ }
+
+-/*
-** This function merges two sorted lists into a single sorted list.
-**
-** aLeft[] and aRight[] are arrays of indices. The sort key is
@@ -3674,19 +4408,6 @@
-** The aContent[aLeft[X]] values will be unique for all X. And the
-** aContent[aRight[X]] values will be unique too. But there might be
-** one or more combinations of X and Y such that
-+** Compute a hash on a page number. The resulting hash value must land
-+** between 0 and (HASHTABLE_NSLOT-1). The walHashNext() function advances
-+** the hash to the next value in the event of a collision.
-+*/
-+static int walHash(u32 iPage){
-+ assert( iPage>0 );
-+ assert( (HASHTABLE_NSLOT & (HASHTABLE_NSLOT-1))==0 );
-+ return (iPage*HASHTABLE_HASH_1) & (HASHTABLE_NSLOT-1);
-+}
-+static int walNextHash(int iPriorHash){
-+ return (iPriorHash+1)&(HASHTABLE_NSLOT-1);
-+}
-+
+/*
+** Return pointers to the hash table and page number array stored on
+** page iHash of the wal-index. The wal-index is broken into 32KB pages
@@ -4170,12 +4891,7 @@
+ u32 aFrameCksum[2] = {0, 0};
+ int iLock; /* Lock offset to lock for checkpoint */
+ int nLock; /* Number of locks to hold */
-
-- szPage = walPagesize(pWal);
-- testcase( szPage<=32768 );
-- testcase( szPage>=65536 );
-- pInfo = walCkptInfo(pWal);
-- if( pInfo->nBackfill>=pWal->hdr.mxFrame ) return SQLITE_OK;
++
+ /* Obtain an exclusive lock on all byte in the locking range not already
+ ** locked by the caller. The caller is guaranteed to have locked the
+ ** WAL_WRITE_LOCK byte, and may have also locked the WAL_CKPT_LOCK byte.
@@ -4194,10 +4910,15 @@
+ }
+ WALTRACE(("WAL%p: recovery begin...\n", pWal));
+- szPage = walPagesize(pWal);
+- testcase( szPage<=32768 );
+- testcase( szPage>=65536 );
+- pInfo = walCkptInfo(pWal);
+- if( pInfo->nBackfill>=pWal->hdr.mxFrame ) return SQLITE_OK;
++ memset(&pWal->hdr, 0, sizeof(WalIndexHdr));
+
- /* Allocate the iterator */
- rc = walIteratorInit(pWal, &pIter);
-+ memset(&pWal->hdr, 0, sizeof(WalIndexHdr));
-+
+ rc = sqlite3OsFileSize(pWal->pWalFd, &nSize);
if( rc!=SQLITE_OK ){
- return rc;
@@ -4231,7 +4952,7 @@
- assert( y<=pWal->hdr.mxFrame );
- rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1);
- if( rc==SQLITE_OK ){
-- pInfo->aReadMark[i] = READMARK_NOT_USED;
+- pInfo->aReadMark[i] = (i==1 ? mxSafeFrame : READMARK_NOT_USED);
- walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
- }else if( rc==SQLITE_BUSY ){
- mxSafeFrame = y;
@@ -4401,6 +5122,7 @@
+ pInfo->nBackfill = 0;
+ pInfo->aReadMark[0] = 0;
+ for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
++ if( pWal->hdr.mxFrame ) pInfo->aReadMark[1] = pWal->hdr.mxFrame;
+
+ /* If more than one frame was recovered from the log file, report an
+ ** event via sqlite3_log(). This is to help with identifying performance
@@ -4425,7 +5147,8 @@
/*
-** If the WAL file is currently larger than nMax bytes in size, truncate
-** it to exactly nMax bytes. If an error occurs while doing so, ignore it.
--*/
++** Close an open wal-index.
+ */
-static void walLimitSize(Wal *pWal, i64 nMax){
- i64 sz;
- int rx;
@@ -4437,23 +5160,55 @@
- sqlite3EndBenignMalloc();
- if( rx ){
- sqlite3_log(rx, "cannot limit WAL size: %s", pWal->zWalName);
-- }
--}
--
++static void walIndexClose(Wal *pWal, int isDelete){
++ if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){
++ int i;
++ for(i=0; i<pWal->nWiData; i++){
++ sqlite3_free((void *)pWal->apWiData[i]);
++ pWal->apWiData[i] = 0;
++ }
++ }else{
++ sqlite3OsShmUnmap(pWal->pDbFd, isDelete);
+ }
+ }
+
-/*
-** Close a connection to a log file.
-+** Close an open wal-index.
++/*
++** Open a connection to the WAL file zWalName. The database file must
++** already be opened on connection pDbFd. The buffer that zWalName points
++** to must remain valid for the lifetime of the returned Wal* handle.
++**
++** A SHARED lock should be held on the database file when this function
++** is called. The purpose of this SHARED lock is to prevent any other
++** client from unlinking the WAL or wal-index file. If another process
++** were to do this just after this client opened one of these files, the
++** system would be badly broken.
++**
++** If the log file is successfully opened, SQLITE_OK is returned and
++** *ppWal is set to point to a new WAL handle. If an error occurs,
++** an SQLite error code is returned and *ppWal is left unmodified.
*/
-SQLITE_PRIVATE int sqlite3WalClose(
- Wal *pWal, /* Wal to close */
- int sync_flags, /* Flags to pass to OsSync() (or 0) */
- int nBuf,
- u8 *zBuf /* Buffer of at least nBuf bytes */
--){
++SQLITE_PRIVATE int sqlite3WalOpen(
++ sqlite3_vfs *pVfs, /* vfs module to open wal and wal-index */
++ 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 = SQLITE_OK;
- if( pWal ){
- int isDelete = 0; /* True to unlink wal and wal-index files */
--
++ int rc; /* Return Code */
++ Wal *pRet; /* Object to allocate and return */
++ int flags; /* Flags passed to OsOpen() */
+
- /* If an EXCLUSIVE lock can be obtained on the database file (using the
- ** ordinary, rollback-mode locking methods, this guarantees that the
- ** connection associated with this log file is the only connection to
@@ -4491,30 +5246,71 @@
- }
- }
- }
--
++ assert( zWalName && zWalName[0] );
++ assert( pDbFd );
+
- walIndexClose(pWal, isDelete);
- sqlite3OsClose(pWal->pWalFd);
- if( isDelete ){
- sqlite3BeginBenignMalloc();
- sqlite3OsDelete(pWal->pVfs, pWal->zWalName, 0);
- sqlite3EndBenignMalloc();
-+static void walIndexClose(Wal *pWal, int isDelete){
-+ if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){
-+ int i;
-+ for(i=0; i<pWal->nWiData; i++){
-+ sqlite3_free((void *)pWal->apWiData[i]);
-+ pWal->apWiData[i] = 0;
++ /* In the amalgamation, the os_unix.c and os_win.c source files come before
++ ** this source file. Verify that the #defines of the locking byte offsets
++ ** in os_unix.c and os_win.c agree with the WALINDEX_LOCK_OFFSET value.
++ */
++#ifdef WIN_SHM_BASE
++ assert( WIN_SHM_BASE==WALINDEX_LOCK_OFFSET );
++#endif
++#ifdef UNIX_SHM_BASE
++ assert( UNIX_SHM_BASE==WALINDEX_LOCK_OFFSET );
++#endif
++
++
++ /* Allocate an instance of struct Wal to return. */
++ *ppWal = 0;
++ pRet = (Wal*)sqlite3MallocZero(sizeof(Wal) + pVfs->szOsFile);
++ if( !pRet ){
++ return SQLITE_NOMEM;
++ }
++
++ pRet->pVfs = pVfs;
++ pRet->pWalFd = (sqlite3_file *)&pRet[1];
++ pRet->pDbFd = pDbFd;
++ pRet->readLock = -1;
++ pRet->mxWalSize = mxWalSize;
++ pRet->zWalName = zWalName;
++ pRet->syncHeader = 1;
++ pRet->padToSectorBoundary = 1;
++ pRet->exclusiveMode = (bNoShm ? WAL_HEAPMEMORY_MODE: WAL_NORMAL_MODE);
++
++ /* Open file handle on the write-ahead log file. */
++ 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 = WAL_RDONLY;
++ }
++
++ if( rc!=SQLITE_OK ){
++ walIndexClose(pRet, 0);
++ sqlite3OsClose(pRet->pWalFd);
++ sqlite3_free(pRet);
++ }else{
++ int iDC = sqlite3OsDeviceCharacteristics(pRet->pWalFd);
++ if( iDC & SQLITE_IOCAP_SEQUENTIAL ){ pRet->syncHeader = 0; }
++ if( iDC & SQLITE_IOCAP_POWERSAFE_OVERWRITE ){
++ pRet->padToSectorBoundary = 0;
}
- WALTRACE(("WAL%p: closed\n", pWal));
- sqlite3_free((void *)pWal->apWiData);
- sqlite3_free(pWal);
-+ }else{
-+ sqlite3OsShmUnmap(pWal->pDbFd, isDelete);
++ *ppWal = pRet;
++ WALTRACE(("WAL%d: opened\n", pRet));
}
-- return rc;
+ return rc;
}
--/*
+ /*
-** Try to read the wal-index header. Return 0 on success and 1 if
-** there is a problem.
-**
@@ -4523,47 +5319,23 @@
-** read it, which might result in inconsistency. A dirty read is detected
-** by verifying that both copies of the header are the same and also by
-** a checksum on the header.
-+/*
-+** Open a connection to the WAL file zWalName. The database file must
-+** already be opened on connection pDbFd. The buffer that zWalName points
-+** to must remain valid for the lifetime of the returned Wal* handle.
- **
+-**
-** If and only if the read is consistent and the header is different from
-** pWal->hdr, then pWal->hdr is updated to the content of the new header
-** and *pChanged is set to 1.
-+** A SHARED lock should be held on the database file when this function
-+** is called. The purpose of this SHARED lock is to prevent any other
-+** client from unlinking the WAL or wal-index file. If another process
-+** were to do this just after this client opened one of these files, the
-+** system would be badly broken.
- **
+-**
-** If the checksum cannot be verified return non-zero. If the header
-** is read successfully and the checksum verified, return zero.
-+** If the log file is successfully opened, SQLITE_OK is returned and
-+** *ppWal is set to point to a new WAL handle. If an error occurs,
-+** an SQLite error code is returned and *ppWal is left unmodified.
++** Change the size to which the WAL file is trucated on each reset.
*/
-static int walIndexTryHdr(Wal *pWal, int *pChanged){
- u32 aCksum[2]; /* Checksum on the header content */
- WalIndexHdr h1, h2; /* Two copies of the header content */
- WalIndexHdr volatile *aHdr; /* Header in shared memory */
-+SQLITE_PRIVATE int sqlite3WalOpen(
-+ sqlite3_vfs *pVfs, /* vfs module to open wal and wal-index */
-+ 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 */
-+ Wal *pRet; /* Object to allocate and return */
-+ int flags; /* Flags passed to OsOpen() */
-
+-
- /* The first page of the wal-index must be mapped at this point. */
- assert( pWal->nWiData>0 && pWal->apWiData[0] );
-+ assert( zWalName && zWalName[0] );
-+ assert( pDbFd );
-
+-
- /* Read the header. This might happen concurrently with a write to the
- ** same area of shared memory on a different CPU in a SMP,
- ** meaning it is possible that an inconsistent snapshot is read
@@ -4573,53 +5345,43 @@
- ** When reading, read [0] first then [1]. Writes are in the reverse order.
- ** Memory barriers are used to prevent the compiler or the hardware from
- ** reordering the reads and writes.
-+ /* In the amalgamation, the os_unix.c and os_win.c source files come before
-+ ** this source file. Verify that the #defines of the locking byte offsets
-+ ** in os_unix.c and os_win.c agree with the WALINDEX_LOCK_OFFSET value.
- */
+- */
- aHdr = walIndexHdr(pWal);
- memcpy(&h1, (void *)&aHdr[0], sizeof(h1));
- walShmBarrier(pWal);
- memcpy(&h2, (void *)&aHdr[1], sizeof(h2));
-+#ifdef WIN_SHM_BASE
-+ assert( WIN_SHM_BASE==WALINDEX_LOCK_OFFSET );
-+#endif
-+#ifdef UNIX_SHM_BASE
-+ assert( UNIX_SHM_BASE==WALINDEX_LOCK_OFFSET );
-+#endif
++SQLITE_PRIVATE void sqlite3WalLimit(Wal *pWal, i64 iLimit){
++ if( pWal ) pWal->mxWalSize = iLimit;
++}
- if( memcmp(&h1, &h2, sizeof(h1))!=0 ){
- return 1; /* Dirty read */
- }
- if( h1.isInit==0 ){
- return 1; /* Malformed header - probably all zeros */
-+
-+ /* Allocate an instance of struct Wal to return. */
-+ *ppWal = 0;
-+ pRet = (Wal*)sqlite3MallocZero(sizeof(Wal) + pVfs->szOsFile);
-+ if( !pRet ){
-+ return SQLITE_NOMEM;
- }
+- }
- walChecksumBytes(1, (u8*)&h1, sizeof(h1)-sizeof(h1.aCksum), 0, aCksum);
- if( aCksum[0]!=h1.aCksum[0] || aCksum[1]!=h1.aCksum[1] ){
- return 1; /* Checksum does not match */
-+
-+ pRet->pVfs = pVfs;
-+ pRet->pWalFd = (sqlite3_file *)&pRet[1];
-+ pRet->pDbFd = pDbFd;
-+ pRet->readLock = -1;
-+ pRet->mxWalSize = mxWalSize;
-+ pRet->zWalName = zWalName;
-+ pRet->syncHeader = 1;
-+ pRet->padToSectorBoundary = 1;
-+ pRet->exclusiveMode = (bNoShm ? WAL_HEAPMEMORY_MODE: WAL_NORMAL_MODE);
-+
-+ /* Open file handle on the write-ahead log file. */
-+ 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 = WAL_RDONLY;
- }
+- }
++/*
++** 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
++** that page was last written into the WAL. Write into *piPage the page
++** number.
++**
++** Return 0 on success. If there are no pages in the WAL with a page
++** number larger than *piPage, then return 1.
++*/
++static int walIteratorNext(
++ WalIterator *p, /* Iterator */
++ u32 *piPage, /* OUT: The page number of the next page */
++ u32 *piFrame /* OUT: Wal frame index of next page */
++){
++ u32 iMin; /* Result pgno must be greater than iMin */
++ u32 iRet = 0xFFFFFFFF; /* 0xffffffff is never a valid page number */
++ int i; /* For looping through segments */
- if( memcmp(&pWal->hdr, &h1, sizeof(WalIndexHdr)) ){
- *pChanged = 1;
@@ -4627,49 +5389,59 @@
- pWal->szPage = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16);
- testcase( pWal->szPage<=32768 );
- testcase( pWal->szPage>=65536 );
-+ if( rc!=SQLITE_OK ){
-+ walIndexClose(pRet, 0);
-+ sqlite3OsClose(pRet->pWalFd);
-+ sqlite3_free(pRet);
-+ }else{
-+ int iDC = sqlite3OsDeviceCharacteristics(pRet->pWalFd);
-+ if( iDC & SQLITE_IOCAP_SEQUENTIAL ){ pRet->syncHeader = 0; }
-+ if( iDC & SQLITE_IOCAP_POWERSAFE_OVERWRITE ){
-+ pRet->padToSectorBoundary = 0;
++ iMin = p->iPrior;
++ assert( iMin<0xffffffff );
++ for(i=p->nSegment-1; i>=0; i--){
++ struct WalSegment *pSegment = &p->aSegment[i];
++ while( pSegment->iNext<pSegment->nEntry ){
++ u32 iPg = pSegment->aPgno[pSegment->aIndex[pSegment->iNext]];
++ if( iPg>iMin ){
++ if( iPg<iRet ){
++ iRet = iPg;
++ *piFrame = pSegment->iZero + pSegment->aIndex[pSegment->iNext];
++ }
++ break;
++ }
++ pSegment->iNext++;
+ }
-+ *ppWal = pRet;
-+ WALTRACE(("WAL%d: opened\n", pRet));
}
-+ return rc;
-+}
- /* The header was successfully read. Return zero. */
- return 0;
-+/*
-+** 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;
++ *piPage = p->iPrior = iRet;
++ return (iRet==0xFFFFFFFF);
}
/*
-** Read the wal-index header from the wal-index and into pWal->hdr.
-** If the wal-header appears to be corrupt, try to reconstruct the
-** wal-index from the WAL before returning.
--**
++** This function merges two sorted lists into a single sorted list.
+ **
-** Set *pChanged to 1 if the wal-index header value in pWal->hdr is
-** changed by this opertion. If pWal->hdr is unchanged, set *pChanged
-** to 0.
-+** 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
-+** that page was last written into the WAL. Write into *piPage the page
-+** number.
++** aLeft[] and aRight[] are arrays of indices. The sort key is
++** aContent[aLeft[]] and aContent[aRight[]]. Upon entry, the following
++** is guaranteed for all J<K:
**
-** If the wal-index header is successfully read, return SQLITE_OK.
-** Otherwise an SQLite error code.
-+** Return 0 on success. If there are no pages in the WAL with a page
-+** number larger than *piPage, then return 1.
++** aContent[aLeft[J]] < aContent[aLeft[K]]
++** aContent[aRight[J]] < aContent[aRight[K]]
++**
++** This routine overwrites aRight[] with a new (probably longer) sequence
++** of indices such that the aRight[] contains every index that appears in
++** either aLeft[] or the old aRight[] and such that the second condition
++** above is still met.
++**
++** The aContent[aLeft[X]] values will be unique for all X. And the
++** aContent[aRight[X]] values will be unique too. But there might be
++** one or more combinations of X and Y such that
++**
++** aLeft[X]!=aRight[Y] && aContent[aLeft[X]] == aContent[aRight[Y]]
++**
++** When that happens, omit the aLeft[X] and use the aRight[Y] index.
*/
-static int walIndexReadHdr(Wal *pWal, int *pChanged){
- int rc; /* Return code */
@@ -4685,21 +5457,30 @@
- return rc;
- };
- assert( page0 || pWal->writeLock==0 );
--
++static void walMerge(
++ const u32 *aContent, /* Pages in wal - keys for the sort */
++ ht_slot *aLeft, /* IN: Left hand input list */
++ int nLeft, /* IN: Elements in array *paLeft */
++ ht_slot **paRight, /* IN/OUT: Right hand input list */
++ int *pnRight, /* IN/OUT: Elements in *paRight */
++ ht_slot *aTmp /* Temporary buffer */
++){
++ int iLeft = 0; /* Current index in aLeft */
++ int iRight = 0; /* Current index in aRight */
++ int iOut = 0; /* Current index in output buffer */
++ int nRight = *pnRight;
++ ht_slot *aRight = *paRight;
+
- /* If the first page of the wal-index has been mapped, try to read the
- ** wal-index header immediately, without holding any lock. This usually
- ** works, but may fail if the wal-index header is corrupt or currently
- ** being modified by another thread or process.
- */
- badHdr = (page0 ? walIndexTryHdr(pWal, pChanged) : 1);
-+static int walIteratorNext(
-+ WalIterator *p, /* Iterator */
-+ u32 *piPage, /* OUT: The page number of the next page */
-+ u32 *piFrame /* OUT: Wal frame index of next page */
-+){
-+ u32 iMin; /* Result pgno must be greater than iMin */
-+ u32 iRet = 0xFFFFFFFF; /* 0xffffffff is never a valid page number */
-+ int i; /* For looping through segments */
++ assert( nLeft>0 && nRight>0 );
++ while( iRight<nRight || iLeft<nLeft ){
++ ht_slot logpage;
++ Pgno dbpage;
- /* If the first attempt failed, it might have been due to a race
- ** with a writer. So get a WRITE lock and try again.
@@ -4722,71 +5503,10 @@
- */
- rc = walIndexRecover(pWal);
- *pChanged = 1;
-+ iMin = p->iPrior;
-+ assert( iMin<0xffffffff );
-+ for(i=p->nSegment-1; i>=0; i--){
-+ struct WalSegment *pSegment = &p->aSegment[i];
-+ while( pSegment->iNext<pSegment->nEntry ){
-+ u32 iPg = pSegment->aPgno[pSegment->aIndex[pSegment->iNext]];
-+ if( iPg>iMin ){
-+ if( iPg<iRet ){
-+ iRet = iPg;
-+ *piFrame = pSegment->iZero + pSegment->aIndex[pSegment->iNext];
- }
-+ break;
- }
+- }
+- }
- pWal->writeLock = 0;
- walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
-+ pSegment->iNext++;
-+ }
-+ }
-+
-+ *piPage = p->iPrior = iRet;
-+ return (iRet==0xFFFFFFFF);
-+}
-+
-+/*
-+** This function merges two sorted lists into a single sorted list.
-+**
-+** aLeft[] and aRight[] are arrays of indices. The sort key is
-+** aContent[aLeft[]] and aContent[aRight[]]. Upon entry, the following
-+** is guaranteed for all J<K:
-+**
-+** aContent[aLeft[J]] < aContent[aLeft[K]]
-+** aContent[aRight[J]] < aContent[aRight[K]]
-+**
-+** This routine overwrites aRight[] with a new (probably longer) sequence
-+** of indices such that the aRight[] contains every index that appears in
-+** either aLeft[] or the old aRight[] and such that the second condition
-+** above is still met.
-+**
-+** The aContent[aLeft[X]] values will be unique for all X. And the
-+** aContent[aRight[X]] values will be unique too. But there might be
-+** one or more combinations of X and Y such that
-+**
-+** aLeft[X]!=aRight[Y] && aContent[aLeft[X]] == aContent[aRight[Y]]
-+**
-+** When that happens, omit the aLeft[X] and use the aRight[Y] index.
-+*/
-+static void walMerge(
-+ const u32 *aContent, /* Pages in wal - keys for the sort */
-+ ht_slot *aLeft, /* IN: Left hand input list */
-+ int nLeft, /* IN: Elements in array *paLeft */
-+ ht_slot **paRight, /* IN/OUT: Right hand input list */
-+ int *pnRight, /* IN/OUT: Elements in *paRight */
-+ ht_slot *aTmp /* Temporary buffer */
-+){
-+ int iLeft = 0; /* Current index in aLeft */
-+ int iRight = 0; /* Current index in aRight */
-+ int iOut = 0; /* Current index in output buffer */
-+ int nRight = *pnRight;
-+ ht_slot *aRight = *paRight;
-+
-+ assert( nLeft>0 && nRight>0 );
-+ while( iRight<nRight || iLeft<nLeft ){
-+ ht_slot logpage;
-+ Pgno dbpage;
-+
+ if( (iLeft<nLeft)
+ && (iRight>=nRight || aContent[aLeft[iLeft]]<aContent[aRight[iRight]])
+ ){
@@ -5276,30 +5996,11 @@
- if( walFramePgno(pWal, iTest)==pgno ){
- iRead2 = iTest;
- break;
-- }
-- }
-- assert( iRead==iRead2 );
-- }
--#endif
+ for(i=0; rc==SQLITE_OK && i<nSegment; i++){
+ volatile ht_slot *aHash;
+ u32 iZero;
+ volatile u32 *aPgno;
-
-- /* If iRead is non-zero, then it is the log frame number that contains the
-- ** required page. Read and return data from the log file.
-- */
-- if( iRead ){
-- int sz;
-- i64 iOffset;
-- sz = pWal->hdr.szPage;
-- sz = (sz&0xfe00) + ((sz&0x0001)<<16);
-- testcase( sz<=32768 );
-- testcase( sz>=65536 );
-- iOffset = walFrameOffset(iRead, sz) + WAL_FRAME_HDRSIZE;
-- *pInWal = 1;
-- /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */
-- return sqlite3OsRead(pWal->pWalFd, pOut, (nOut>sz ? sz : nOut), iOffset);
++
+ rc = walHashGet(pWal, i, &aHash, &aPgno, &iZero);
+ if( rc==SQLITE_OK ){
+ int j; /* Counter variable */
@@ -5317,21 +6018,38 @@
+
+ for(j=0; j<nEntry; j++){
+ aIndex[j] = (ht_slot)j;
-+ }
+ }
+ walMergesort((u32 *)aPgno, aTmp, aIndex, &nEntry);
+ p->aSegment[i].iZero = iZero;
+ p->aSegment[i].nEntry = nEntry;
+ p->aSegment[i].aIndex = aIndex;
+ p->aSegment[i].aPgno = (u32 *)aPgno;
-+ }
+ }
+- assert( iRead==iRead2 );
}
+-#endif
+ sqlite3ScratchFree(aTmp);
-- *pInWal = 0;
-- return SQLITE_OK;
+- /* If iRead is non-zero, then it is the log frame number that contains the
+- ** required page. Read and return data from the log file.
+- */
+- if( iRead ){
+- int sz;
+- i64 iOffset;
+- sz = pWal->hdr.szPage;
+- sz = (sz&0xfe00) + ((sz&0x0001)<<16);
+- testcase( sz<=32768 );
+- testcase( sz>=65536 );
+- iOffset = walFrameOffset(iRead, sz) + WAL_FRAME_HDRSIZE;
+- *pInWal = 1;
+- /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */
+- return sqlite3OsRead(pWal->pWalFd, pOut, (nOut>sz ? sz : nOut), iOffset);
+ if( rc!=SQLITE_OK ){
+ walIteratorFree(p);
-+ }
+ }
+-
+- *pInWal = 0;
+- return SQLITE_OK;
+ *pp = p;
+ return rc;
}
@@ -5414,10 +6132,6 @@
*/
-SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal){
- int rc;
--
-- /* Cannot start a write transaction without first holding a read
-- ** transaction. */
-- assert( pWal->readLock>=0 );
+static int walCheckpoint(
+ Wal *pWal, /* Wal connection */
+ int eMode, /* One of PASSIVE, FULL or RESTART */
@@ -5437,31 +6151,26 @@
+ volatile WalCkptInfo *pInfo; /* The checkpoint status information */
+ int (*xBusy)(void*) = 0; /* Function to call when waiting for locks */
-- if( pWal->readOnly ){
-- return SQLITE_READONLY;
-- }
+- /* Cannot start a write transaction without first holding a read
+- ** transaction. */
+- assert( pWal->readLock>=0 );
+ szPage = walPagesize(pWal);
+ testcase( szPage<=32768 );
+ testcase( szPage>=65536 );
+ pInfo = walCkptInfo(pWal);
+ if( pInfo->nBackfill>=pWal->hdr.mxFrame ) return SQLITE_OK;
-- /* Only one writer allowed at a time. Get the write lock. Return
-- ** SQLITE_BUSY if unable.
-- */
-- rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1);
-- if( rc ){
+- if( pWal->readOnly ){
+- return SQLITE_READONLY;
+ /* Allocate the iterator */
+ rc = walIteratorInit(pWal, &pIter);
+ if( rc!=SQLITE_OK ){
- return rc;
++ return rc;
}
-- pWal->writeLock = 1;
+ assert( pIter );
-- /* If another connection has written to the database file since the
-- ** time the read transaction on this connection was started, then
-- ** the write is disallowed.
+- /* Only one writer allowed at a time. Get the write lock. Return
+- ** SQLITE_BUSY if unable.
+ if( eMode!=SQLITE_CHECKPOINT_PASSIVE ) xBusy = xBusyCall;
+
+ /* Compute in mxSafeFrame the index of the last frame of the WAL that is
@@ -5469,10 +6178,9 @@
+ ** overwrite database pages that are in use by active readers and thus
+ ** cannot be backfilled from the WAL.
*/
-- if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){
-- walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
-- pWal->writeLock = 0;
-- rc = SQLITE_BUSY;
+- rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1);
+- if( rc ){
+- return rc;
+ mxSafeFrame = pWal->hdr.mxFrame;
+ mxPage = pWal->hdr.nPage;
+ for(i=1; i<WAL_NREADER; i++){
@@ -5481,7 +6189,7 @@
+ assert( y<=pWal->hdr.mxFrame );
+ rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1);
+ if( rc==SQLITE_OK ){
-+ pInfo->aReadMark[i] = READMARK_NOT_USED;
++ pInfo->aReadMark[i] = (i==1 ? mxSafeFrame : READMARK_NOT_USED);
+ walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
+ }else if( rc==SQLITE_BUSY ){
+ mxSafeFrame = y;
@@ -5491,109 +6199,33 @@
+ }
+ }
}
+- pWal->writeLock = 1;
-- return rc;
--}
+- /* If another connection has written to the database file since the
+- ** time the read transaction on this connection was started, then
+- ** the write is disallowed.
+ if( pInfo->nBackfill<mxSafeFrame
+ && (rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(0), 1))==SQLITE_OK
+ ){
+ i64 nSize; /* Current size of database file */
+ u32 nBackfill = pInfo->nBackfill;
-
--/*
--** End a write transaction. The commit has already been done. This
--** routine merely releases the lock.
--*/
--SQLITE_PRIVATE int sqlite3WalEndWriteTransaction(Wal *pWal){
-- if( pWal->writeLock ){
-- walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
-- pWal->writeLock = 0;
-- pWal->truncateOnCommit = 0;
-- }
-- return SQLITE_OK;
--}
++
+ /* Sync the WAL to disk */
+ if( sync_flags ){
+ rc = sqlite3OsSync(pWal->pWalFd, sync_flags);
+ }
-
--/*
--** If any data has been written (but not committed) to the log file, this
--** function moves the write-pointer back to the start of the transaction.
--**
--** Additionally, the callback function is invoked for each frame written
--** to the WAL since the start of the transaction. If the callback returns
--** other than SQLITE_OK, it is not invoked again and the error code is
--** returned to the caller.
--**
--** Otherwise, if the callback function does not return an error, this
--** function returns SQLITE_OK.
--*/
--SQLITE_PRIVATE int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){
-- int rc = SQLITE_OK;
-- if( ALWAYS(pWal->writeLock) ){
-- Pgno iMax = pWal->hdr.mxFrame;
-- Pgno iFrame;
--
-- /* Restore the clients cache of the wal-index header to the state it
-- ** was in before the client began writing to the database.
++
+ /* If the database file may grow as a result of this checkpoint, hint
+ ** about the eventual size of the db file to the VFS layer.
- */
-- memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr));
--
-- for(iFrame=pWal->hdr.mxFrame+1;
-- ALWAYS(rc==SQLITE_OK) && iFrame<=iMax;
-- iFrame++
-- ){
-- /* This call cannot fail. Unless the page for which the page number
-- ** is passed as the second argument is (a) in the cache and
-- ** (b) has an outstanding reference, then xUndo is either a no-op
-- ** (if (a) is false) or simply expels the page from the cache (if (b)
-- ** is false).
-- **
-- ** If the upper layer is doing a rollback, it is guaranteed that there
-- ** are no outstanding references to any page other than page 1. And
-- ** page 1 is never written to the log until the transaction is
-- ** committed. As a result, the call to xUndo may not fail.
-- */
-- assert( walFramePgno(pWal, iFrame)!=1 );
-- rc = xUndo(pUndoCtx, walFramePgno(pWal, iFrame));
++ */
+ if( rc==SQLITE_OK ){
+ i64 nReq = ((i64)mxPage * szPage);
+ rc = sqlite3OsFileSize(pWal->pDbFd, &nSize);
+ if( rc==SQLITE_OK && nSize<nReq ){
+ sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq);
+ }
- }
-- walCleanupHash(pWal);
-- }
-- assert( rc==SQLITE_OK );
-- return rc;
--}
--
--/*
--** Argument aWalData must point to an array of WAL_SAVEPOINT_NDATA u32
--** values. This function populates the array with values required to
--** "rollback" the write position of the WAL handle back to the current
--** point in the event of a savepoint rollback (via WalSavepointUndo()).
--*/
--SQLITE_PRIVATE void sqlite3WalSavepoint(Wal *pWal, u32 *aWalData){
-- assert( pWal->writeLock );
-- aWalData[0] = pWal->hdr.mxFrame;
-- aWalData[1] = pWal->hdr.aFrameCksum[0];
-- aWalData[2] = pWal->hdr.aFrameCksum[1];
-- aWalData[3] = pWal->nCkpt;
--}
-
--/*
--** Move the write position of the WAL back to the point identified by
--** the values in the aWalData[] array. aWalData must point to an array
--** of WAL_SAVEPOINT_NDATA u32 values that has been previously populated
--** by a call to WalSavepoint().
--*/
--SQLITE_PRIVATE int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData){
-- int rc = SQLITE_OK;
++ }
++
+ /* Iterate through the contents of the WAL, copying data to the db file. */
+ while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){
+ i64 iOffset;
@@ -5608,9 +6240,7 @@
+ rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset);
+ if( rc!=SQLITE_OK ) break;
+ }
-
-- assert( pWal->writeLock );
-- assert( aWalData[3]!=pWal->nCkpt || aWalData[0]<=pWal->hdr.mxFrame );
++
+ /* If work was actually accomplished... */
+ if( rc==SQLITE_OK ){
+ if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){
@@ -5625,61 +6255,26 @@
+ pInfo->nBackfill = mxSafeFrame;
+ }
+ }
-
-- if( aWalData[3]!=pWal->nCkpt ){
-- /* This savepoint was opened immediately after the write-transaction
-- ** was started. Right after that, the writer decided to wrap around
-- ** to the start of the log. Update the savepoint values to match.
-- */
-- aWalData[0] = 0;
-- aWalData[3] = pWal->nCkpt;
++
+ /* Release the reader lock held while backfilling */
+ walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1);
- }
-
-- if( aWalData[0]<pWal->hdr.mxFrame ){
-- pWal->hdr.mxFrame = aWalData[0];
-- pWal->hdr.aFrameCksum[0] = aWalData[1];
-- pWal->hdr.aFrameCksum[1] = aWalData[2];
-- walCleanupHash(pWal);
++ }
++
+ if( rc==SQLITE_BUSY ){
+ /* Reset the return code so as not to report a checkpoint failure
+ ** just because there are active readers. */
+ rc = SQLITE_OK;
- }
-
-- return rc;
--}
--
--
--/*
--** This function is called just before writing a set of frames to the log
--** file (see sqlite3WalFrames()). It checks to see if, instead of appending
--** to the current log file, it is possible to overwrite the start of the
--** existing log file with the new frames (i.e. "reset" the log). If so,
--** it sets pWal->hdr.mxFrame to 0. Otherwise, pWal->hdr.mxFrame is left
--** unchanged.
--**
--** SQLITE_OK is returned if no error is encountered (regardless of whether
--** or not pWal->hdr.mxFrame is modified). An SQLite error code is returned
--** if an error occurs.
--*/
--static int walRestartLog(Wal *pWal){
-- int rc = SQLITE_OK;
-- int cnt;
--
-- if( pWal->readLock==0 ){
-- volatile WalCkptInfo *pInfo = walCkptInfo(pWal);
-- assert( pInfo->nBackfill==pWal->hdr.mxFrame );
-- if( pInfo->nBackfill>0 ){
-- u32 salt1;
-- sqlite3_randomness(4, &salt1);
-- rc = walLockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
++ }
++
+ /* If this is an SQLITE_CHECKPOINT_RESTART operation, and the entire wal
+ ** file has been copied into the database file, then block until all
+ ** readers have finished using the wal file. This ensures that the next
+ ** process to write to the database restarts the wal file.
-+ */
+ */
+- if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){
+- walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
+- pWal->writeLock = 0;
+- rc = SQLITE_BUSY;
+ if( rc==SQLITE_OK && eMode!=SQLITE_CHECKPOINT_PASSIVE ){
+ assert( pWal->writeLock );
+ if( pInfo->nBackfill<pWal->hdr.mxFrame ){
@@ -5687,91 +6282,28 @@
+ }else if( eMode==SQLITE_CHECKPOINT_RESTART ){
+ assert( mxSafeFrame==pWal->hdr.mxFrame );
+ rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1);
- if( rc==SQLITE_OK ){
-- /* If all readers are using WAL_READ_LOCK(0) (in other words if no
-- ** readers are currently using the WAL), then the transactions
-- ** frames will overwrite the start of the existing log. Update the
-- ** wal-index header to reflect this.
-- **
-- ** In theory it would be Ok to update the cache of the header only
-- ** at this point. But updating the actual wal-index header is also
-- ** safe and means there is no special case for sqlite3WalUndo()
-- ** to handle if this transaction is rolled back.
-- */
-- int i; /* Loop counter */
-- u32 *aSalt = pWal->hdr.aSalt; /* Big-endian salt values */
--
-- pWal->nCkpt++;
-- pWal->hdr.mxFrame = 0;
-- sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0]));
-- aSalt[1] = salt1;
-- walIndexWriteHdr(pWal);
-- pInfo->nBackfill = 0;
-- for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
-- assert( pInfo->aReadMark[0]==0 );
- walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
-- }else if( rc!=SQLITE_BUSY ){
-- return rc;
- }
- }
-- walUnlockShared(pWal, WAL_READ_LOCK(0));
-- pWal->readLock = -1;
-- cnt = 0;
-- do{
-- int notUsed;
-- rc = walTryBeginRead(pWal, ¬Used, 1, ++cnt);
-- }while( rc==WAL_RETRY );
-- assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */
-- testcase( (rc&0xff)==SQLITE_IOERR );
-- testcase( rc==SQLITE_PROTOCOL );
-- testcase( rc==SQLITE_OK );
++ if( rc==SQLITE_OK ){
++ walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
++ }
++ }
}
-+
+
+ walcheckpoint_out:
+ walIteratorFree(pIter);
return rc;
}
/*
--** Information about the current state of the WAL file and where
--** the next fsync should occur - passed from sqlite3WalFrames() into
--** walWriteToLog().
--*/
--typedef struct WalWriter {
-- Wal *pWal; /* The complete WAL information */
-- sqlite3_file *pFd; /* The WAL file to which we write */
-- sqlite3_int64 iSyncPoint; /* Fsync at this offset */
-- int syncFlags; /* Flags for the fsync */
-- int szPage; /* Size of one page */
--} WalWriter;
--
--/*
--** Write iAmt bytes of content into the WAL file beginning at iOffset.
--** Do a sync when crossing the p->iSyncPoint boundary.
--**
--** In other words, if iSyncPoint is in between iOffset and iOffset+iAmt,
--** first write the part before iSyncPoint, then sync, then write the
--** rest.
+-** End a write transaction. The commit has already been done. This
+-** routine merely releases the lock.
+** If the WAL file is currently larger than nMax bytes in size, truncate
+** it to exactly nMax bytes. If an error occurs while doing so, ignore it.
*/
--static int walWriteToLog(
-- WalWriter *p, /* WAL to write to */
-- void *pContent, /* Content to be written */
-- int iAmt, /* Number of bytes to write */
-- sqlite3_int64 iOffset /* Start writing at this offset */
--){
-- int rc;
-- if( iOffset<p->iSyncPoint && iOffset+iAmt>=p->iSyncPoint ){
-- int iFirstAmt = (int)(p->iSyncPoint - iOffset);
-- rc = sqlite3OsWrite(p->pFd, pContent, iFirstAmt, iOffset);
-- if( rc ) return rc;
-- iOffset += iFirstAmt;
-- iAmt -= iFirstAmt;
-- pContent = (void*)(iFirstAmt + (char*)pContent);
-- assert( p->syncFlags & (SQLITE_SYNC_NORMAL|SQLITE_SYNC_FULL) );
-- rc = sqlite3OsSync(p->pFd, p->syncFlags);
-- if( iAmt==0 || rc ) return rc;
+-SQLITE_PRIVATE int sqlite3WalEndWriteTransaction(Wal *pWal){
+- if( pWal->writeLock ){
+- walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
+- pWal->writeLock = 0;
+- pWal->truncateOnCommit = 0;
+static void walLimitSize(Wal *pWal, i64 nMax){
+ i64 sz;
+ int rx;
@@ -5784,39 +6316,36 @@
+ if( rx ){
+ sqlite3_log(rx, "cannot limit WAL size: %s", pWal->zWalName);
}
-- rc = sqlite3OsWrite(p->pFd, pContent, iAmt, iOffset);
-- return rc;
+- return SQLITE_OK;
}
/*
--** Write out a single frame of the WAL
+-** If any data has been written (but not committed) to the log file, this
+-** function moves the write-pointer back to the start of the transaction.
+-**
+-** Additionally, the callback function is invoked for each frame written
+-** to the WAL since the start of the transaction. If the callback returns
+-** other than SQLITE_OK, it is not invoked again and the error code is
+-** returned to the caller.
+-**
+-** Otherwise, if the callback function does not return an error, this
+-** function returns SQLITE_OK.
+** Close a connection to a log file.
*/
--static int walWriteOneFrame(
-- WalWriter *p, /* Where to write the frame */
-- PgHdr *pPage, /* The page of the frame to be written */
-- int nTruncate, /* The commit flag. Usually 0. >0 for commit */
-- sqlite3_int64 iOffset /* Byte offset at which to write */
+-SQLITE_PRIVATE int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){
+SQLITE_PRIVATE int sqlite3WalClose(
+ Wal *pWal, /* Wal to close */
+ int sync_flags, /* Flags to pass to OsSync() (or 0) */
+ int nBuf,
+ u8 *zBuf /* Buffer of at least nBuf bytes */
- ){
-- int rc; /* Result code from subfunctions */
-- void *pData; /* Data actually written */
-- u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-header in */
--#if defined(SQLITE_HAS_CODEC)
-- if( (pData = sqlite3PagerCodec(pPage))==0 ) return SQLITE_NOMEM;
--#else
-- pData = pPage->pData;
--#endif
-- walEncodeFrame(p->pWal, pPage->pgno, nTruncate, pData, aFrame);
-- rc = walWriteToLog(p, aFrame, sizeof(aFrame), iOffset);
-- if( rc ) return rc;
-- /* Write the page data */
-- rc = walWriteToLog(p, pData, p->szPage, iOffset+sizeof(aFrame));
-+ int rc = SQLITE_OK;
++){
+ int rc = SQLITE_OK;
+- if( ALWAYS(pWal->writeLock) ){
+- Pgno iMax = pWal->hdr.mxFrame;
+- Pgno iFrame;
+-
+- /* Restore the clients cache of the wal-index header to the state it
+- ** was in before the client began writing to the database.
+ if( pWal ){
+ int isDelete = 0; /* True to unlink wal and wal-index files */
+
@@ -5827,7 +6356,8 @@
+ ** the wal and wal-index files.
+ **
+ ** The EXCLUSIVE lock is not released before returning.
-+ */
+ */
+- memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr));
+ rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE);
+ if( rc==SQLITE_OK ){
+ if( pWal->exclusiveMode==WAL_NORMAL_MODE ){
@@ -5857,24 +6387,45 @@
+ }
+ }
+ }
-+
+
+- for(iFrame=pWal->hdr.mxFrame+1;
+- ALWAYS(rc==SQLITE_OK) && iFrame<=iMax;
+- iFrame++
+- ){
+- /* This call cannot fail. Unless the page for which the page number
+- ** is passed as the second argument is (a) in the cache and
+- ** (b) has an outstanding reference, then xUndo is either a no-op
+- ** (if (a) is false) or simply expels the page from the cache (if (b)
+- ** is false).
+- **
+- ** If the upper layer is doing a rollback, it is guaranteed that there
+- ** are no outstanding references to any page other than page 1. And
+- ** page 1 is never written to the log until the transaction is
+- ** committed. As a result, the call to xUndo may not fail.
+- */
+- assert( walFramePgno(pWal, iFrame)!=1 );
+- rc = xUndo(pUndoCtx, walFramePgno(pWal, iFrame));
+ walIndexClose(pWal, isDelete);
+ sqlite3OsClose(pWal->pWalFd);
+ if( isDelete ){
+ sqlite3BeginBenignMalloc();
+ sqlite3OsDelete(pWal->pVfs, pWal->zWalName, 0);
+ sqlite3EndBenignMalloc();
-+ }
+ }
+- walCleanupHash(pWal);
+ WALTRACE(("WAL%p: closed\n", pWal));
+ sqlite3_free((void *)pWal->apWiData);
+ sqlite3_free(pWal);
-+ }
+ }
+- assert( rc==SQLITE_OK );
return rc;
}
-/*
--** Write a set of frames to the log. The caller must hold the write-lock
--** on the log file (obtained using sqlite3WalBeginWriteTransaction()).
+-** Argument aWalData must point to an array of WAL_SAVEPOINT_NDATA u32
+-** values. This function populates the array with values required to
+-** "rollback" the write position of the WAL handle back to the current
+-** point in the event of a savepoint rollback (via WalSavepointUndo()).
+/*
+** Try to read the wal-index header. Return 0 on success and 1 if
+** there is a problem.
@@ -5892,35 +6443,31 @@
+** If the checksum cannot be verified return non-zero. If the header
+** is read successfully and the checksum verified, return zero.
*/
--SQLITE_PRIVATE int sqlite3WalFrames(
-- Wal *pWal, /* Wal handle to write to */
-- int szPage, /* Database page-size in bytes */
-- PgHdr *pList, /* List of dirty pages to write */
-- Pgno nTruncate, /* Database size after this commit */
-- int isCommit, /* True if this is a commit */
-- int sync_flags /* Flags to pass to OsSync() (or 0) */
--){
-- int rc; /* Used to catch return codes */
-- u32 iFrame; /* Next frame address */
-- PgHdr *p; /* Iterator to run through pList with. */
-- PgHdr *pLast = 0; /* Last frame in list */
-- int nExtra = 0; /* Number of extra copies of last page */
-- int szFrame; /* The size of a single frame */
-- i64 iOffset; /* Next byte to write in WAL file */
-- WalWriter w; /* The writer */
+-SQLITE_PRIVATE void sqlite3WalSavepoint(Wal *pWal, u32 *aWalData){
+- assert( pWal->writeLock );
+- aWalData[0] = pWal->hdr.mxFrame;
+- aWalData[1] = pWal->hdr.aFrameCksum[0];
+- aWalData[2] = pWal->hdr.aFrameCksum[1];
+- aWalData[3] = pWal->nCkpt;
+-}
+static int walIndexTryHdr(Wal *pWal, int *pChanged){
+ u32 aCksum[2]; /* Checksum on the header content */
+ WalIndexHdr h1, h2; /* Two copies of the header content */
+ WalIndexHdr volatile *aHdr; /* Header in shared memory */
-- assert( pList );
-- assert( pWal->writeLock );
+-/*
+-** Move the write position of the WAL back to the point identified by
+-** the values in the aWalData[] array. aWalData must point to an array
+-** of WAL_SAVEPOINT_NDATA u32 values that has been previously populated
+-** by a call to WalSavepoint().
+-*/
+-SQLITE_PRIVATE int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData){
+- int rc = SQLITE_OK;
+ /* The first page of the wal-index must be mapped at this point. */
+ assert( pWal->nWiData>0 && pWal->apWiData[0] );
-- /* If this frame set completes a transaction, then nTruncate>0. If
-- ** nTruncate==0 then this frame set does not complete the transaction. */
-- assert( (isCommit!=0)==(nTruncate!=0) );
+- assert( pWal->writeLock );
+- assert( aWalData[3]!=pWal->nCkpt || aWalData[0]<=pWal->hdr.mxFrame );
+ /* Read the header. This might happen concurrently with a write to the
+ ** same area of shared memory on a different CPU in a SMP,
+ ** meaning it is possible that an inconsistent snapshot is read
@@ -5936,10 +6483,13 @@
+ walShmBarrier(pWal);
+ memcpy(&h2, (void *)&aHdr[1], sizeof(h2));
--#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
-- { int cnt; for(cnt=0, p=pList; p; p=p->pDirty, cnt++){}
-- WALTRACE(("WAL%p: frame write begin. %d frames. mxFrame=%d. %s\n",
-- pWal, cnt, pWal->hdr.mxFrame, isCommit ? "Commit" : "Spill"));
+- if( aWalData[3]!=pWal->nCkpt ){
+- /* This savepoint was opened immediately after the write-transaction
+- ** was started. Right after that, the writer decided to wrap around
+- ** to the start of the log. Update the savepoint values to match.
+- */
+- aWalData[0] = 0;
+- aWalData[3] = pWal->nCkpt;
+ if( memcmp(&h1, &h2, sizeof(h1))!=0 ){
+ return 1; /* Dirty read */
+ }
@@ -5950,13 +6500,12 @@
+ if( aCksum[0]!=h1.aCksum[0] || aCksum[1]!=h1.aCksum[1] ){
+ return 1; /* Checksum does not match */
}
--#endif
-- /* See if it is possible to write these frames into the start of the
-- ** log file, instead of appending to it at pWal->hdr.mxFrame.
-- */
-- if( SQLITE_OK!=(rc = walRestartLog(pWal)) ){
-- return rc;
+- if( aWalData[0]<pWal->hdr.mxFrame ){
+- pWal->hdr.mxFrame = aWalData[0];
+- pWal->hdr.aFrameCksum[0] = aWalData[1];
+- pWal->hdr.aFrameCksum[1] = aWalData[2];
+- walCleanupHash(pWal);
+ if( memcmp(&pWal->hdr, &h1, sizeof(WalIndexHdr)) ){
+ *pChanged = 1;
+ memcpy(&pWal->hdr, &h1, sizeof(WalIndexHdr));
@@ -5965,37 +6514,64 @@
+ testcase( pWal->szPage>=65536 );
}
-- /* If this is the first frame written into the log, write the WAL
-- ** header to the start of the WAL file. See comments at the top of
-- ** this source file for a description of the WAL header format.
+- return rc;
+ /* The header was successfully read. Return zero. */
+ return 0;
-+}
-+
-+/*
+ }
+
+-
+ /*
+-** This function is called just before writing a set of frames to the log
+-** file (see sqlite3WalFrames()). It checks to see if, instead of appending
+-** to the current log file, it is possible to overwrite the start of the
+-** existing log file with the new frames (i.e. "reset" the log). If so,
+-** it sets pWal->hdr.mxFrame to 0. Otherwise, pWal->hdr.mxFrame is left
+-** unchanged.
+** Read the wal-index header from the wal-index and into pWal->hdr.
+** If the wal-header appears to be corrupt, try to reconstruct the
+** wal-index from the WAL before returning.
-+**
+ **
+-** SQLITE_OK is returned if no error is encountered (regardless of whether
+-** or not pWal->hdr.mxFrame is modified). An SQLite error code is returned
+-** if an error occurs.
+** Set *pChanged to 1 if the wal-index header value in pWal->hdr is
+** changed by this opertion. If pWal->hdr is unchanged, set *pChanged
+** to 0.
+**
+** If the wal-index header is successfully read, return SQLITE_OK.
+** Otherwise an SQLite error code.
-+*/
+ */
+-static int walRestartLog(Wal *pWal){
+- int rc = SQLITE_OK;
+- int cnt;
+static int walIndexReadHdr(Wal *pWal, int *pChanged){
+ int rc; /* Return code */
+ int badHdr; /* True if a header read failed */
+ volatile u32 *page0; /* Chunk of wal-index containing header */
-+
+
+- if( pWal->readLock==0 ){
+- volatile WalCkptInfo *pInfo = walCkptInfo(pWal);
+- assert( pInfo->nBackfill==pWal->hdr.mxFrame );
+- if( pInfo->nBackfill>0 ){
+- u32 salt1;
+- sqlite3_randomness(4, &salt1);
+- rc = walLockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
+- if( rc==SQLITE_OK ){
+- /* If all readers are using WAL_READ_LOCK(0) (in other words if no
+- ** readers are currently using the WAL), then the transactions
+- ** frames will overwrite the start of the existing log. Update the
+- ** wal-index header to reflect this.
+- **
+- ** In theory it would be Ok to update the cache of the header only
+- ** at this point. But updating the actual wal-index header is also
+- ** safe and means there is no special case for sqlite3WalUndo()
+- ** to handle if this transaction is rolled back.
+- */
+- int i; /* Loop counter */
+- u32 *aSalt = pWal->hdr.aSalt; /* Big-endian salt values */
+ /* Ensure that page 0 of the wal-index (the page that contains the
+ ** wal-index header) is mapped. Return early if an error occurs here.
- */
-- iFrame = pWal->hdr.mxFrame;
-- if( iFrame==0 ){
-- u8 aWalHdr[WAL_HDRSIZE]; /* Buffer to assemble wal-header in */
-- u32 aCksum[2]; /* Checksum for wal-header */
++ */
+ assert( pChanged );
+ rc = walIndexPage(pWal, 0, &page0);
+ if( rc!=SQLITE_OK ){
@@ -6003,32 +6579,25 @@
+ };
+ assert( page0 || pWal->writeLock==0 );
-- sqlite3Put4byte(&aWalHdr[0], (WAL_MAGIC | SQLITE_BIGENDIAN));
-- sqlite3Put4byte(&aWalHdr[4], WAL_MAX_VERSION);
-- sqlite3Put4byte(&aWalHdr[8], szPage);
-- sqlite3Put4byte(&aWalHdr[12], pWal->nCkpt);
-- if( pWal->nCkpt==0 ) sqlite3_randomness(8, pWal->hdr.aSalt);
-- memcpy(&aWalHdr[16], pWal->hdr.aSalt, 8);
-- walChecksumBytes(1, aWalHdr, WAL_HDRSIZE-2*4, 0, aCksum);
-- sqlite3Put4byte(&aWalHdr[24], aCksum[0]);
-- sqlite3Put4byte(&aWalHdr[28], aCksum[1]);
--
-- pWal->szPage = szPage;
-- pWal->hdr.bigEndCksum = SQLITE_BIGENDIAN;
-- pWal->hdr.aFrameCksum[0] = aCksum[0];
-- pWal->hdr.aFrameCksum[1] = aCksum[1];
-- pWal->truncateOnCommit = 1;
+- pWal->nCkpt++;
+- pWal->hdr.mxFrame = 0;
+- sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0]));
+- aSalt[1] = salt1;
+- walIndexWriteHdr(pWal);
+- pInfo->nBackfill = 0;
+- pInfo->aReadMark[1] = 0;
+- for(i=2; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
+- assert( pInfo->aReadMark[0]==0 );
+- walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
+- }else if( rc!=SQLITE_BUSY ){
+- return rc;
+ /* If the first page of the wal-index has been mapped, try to read the
+ ** wal-index header immediately, without holding any lock. This usually
+ ** works, but may fail if the wal-index header is corrupt or currently
+ ** being modified by another thread or process.
+ */
+ badHdr = (page0 ? walIndexTryHdr(pWal, pChanged) : 1);
-
-- rc = sqlite3OsWrite(pWal->pWalFd, aWalHdr, sizeof(aWalHdr), 0);
-- WALTRACE(("WAL%p: wal-header write %s\n", pWal, rc ? "failed" : "ok"));
-- if( rc!=SQLITE_OK ){
-- return rc;
++
+ /* If the first attempt failed, it might have been due to a race
+ ** with a writer. So get a WRITE lock and try again.
+ */
@@ -6051,23 +6620,63 @@
+ rc = walIndexRecover(pWal);
+ *pChanged = 1;
+ }
-+ }
+ }
+ pWal->writeLock = 0;
+ walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
}
-+ }
+- walUnlockShared(pWal, WAL_READ_LOCK(0));
+- pWal->readLock = -1;
+- cnt = 0;
+- do{
+- int notUsed;
+- rc = walTryBeginRead(pWal, ¬Used, 1, ++cnt);
+- }while( rc==WAL_RETRY );
+- assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */
+- testcase( (rc&0xff)==SQLITE_IOERR );
+- testcase( rc==SQLITE_PROTOCOL );
+- testcase( rc==SQLITE_OK );
+ }
+- return rc;
+-}
+-
+-/*
+-** Information about the current state of the WAL file and where
+-** the next fsync should occur - passed from sqlite3WalFrames() into
+-** walWriteToLog().
+-*/
+-typedef struct WalWriter {
+- Wal *pWal; /* The complete WAL information */
+- sqlite3_file *pFd; /* The WAL file to which we write */
+- sqlite3_int64 iSyncPoint; /* Fsync at this offset */
+- int syncFlags; /* Flags for the fsync */
+- int szPage; /* Size of one page */
+-} WalWriter;
-- /* Sync the header (unless SQLITE_IOCAP_SEQUENTIAL is true or unless
-- ** all syncing is turned off by PRAGMA synchronous=OFF). Otherwise
-- ** an out-of-order write following a WAL restart could result in
-- ** database corruption. See the ticket:
-- **
-- ** http://localhost:591/sqlite/info/ff5be73dee
-- */
-- if( pWal->syncHeader && sync_flags ){
-- rc = sqlite3OsSync(pWal->pWalFd, sync_flags & SQLITE_SYNC_MASK);
-- if( rc ) return rc;
-- }
+-/*
+-** Write iAmt bytes of content into the WAL file beginning at iOffset.
+-** Do a sync when crossing the p->iSyncPoint boundary.
+-**
+-** In other words, if iSyncPoint is in between iOffset and iOffset+iAmt,
+-** first write the part before iSyncPoint, then sync, then write the
+-** rest.
+-*/
+-static int walWriteToLog(
+- WalWriter *p, /* WAL to write to */
+- void *pContent, /* Content to be written */
+- int iAmt, /* Number of bytes to write */
+- sqlite3_int64 iOffset /* Start writing at this offset */
+-){
+- int rc;
+- if( iOffset<p->iSyncPoint && iOffset+iAmt>=p->iSyncPoint ){
+- int iFirstAmt = (int)(p->iSyncPoint - iOffset);
+- rc = sqlite3OsWrite(p->pFd, pContent, iFirstAmt, iOffset);
+- if( rc ) return rc;
+- iOffset += iFirstAmt;
+- iAmt -= iFirstAmt;
+- pContent = (void*)(iFirstAmt + (char*)pContent);
+- assert( p->syncFlags & (SQLITE_SYNC_NORMAL|SQLITE_SYNC_FULL) );
+- rc = sqlite3OsSync(p->pFd, p->syncFlags);
+- if( iAmt==0 || rc ) return rc;
+ /* If the header is read successfully, check the version number to make
+ ** sure the wal-index was not constructed with some future format that
+ ** this version of SQLite cannot understand.
@@ -6075,38 +6684,42 @@
+ if( badHdr==0 && pWal->hdr.iVersion!=WALINDEX_MAX_VERSION ){
+ rc = SQLITE_CANTOPEN_BKPT;
}
-- assert( (int)pWal->szPage==szPage );
-
-- /* Setup information needed to write frames into the WAL */
-- w.pWal = pWal;
-- w.pFd = pWal->pWalFd;
-- w.iSyncPoint = 0;
-- w.syncFlags = sync_flags;
-- w.szPage = szPage;
-- iOffset = walFrameOffset(iFrame+1, szPage);
-- szFrame = szPage + WAL_FRAME_HDRSIZE;
-+ return rc;
-+}
+- rc = sqlite3OsWrite(p->pFd, pContent, iAmt, iOffset);
++
+ return rc;
+ }
-- /* Write all frames into the log file exactly once */
-- for(p=pList; p; p=p->pDirty){
-- int nDbSize; /* 0 normally. Positive == commit flag */
-- iFrame++;
-- assert( iOffset==walFrameOffset(iFrame, szPage) );
-- nDbSize = (isCommit && p->pDirty==0) ? nTruncate : 0;
-- rc = walWriteOneFrame(&w, p, nDbSize, iOffset);
-- if( rc ) return rc;
-- pLast = p;
-- iOffset += szFrame;
-- }
-+/*
+ /*
+-** Write out a single frame of the WAL
+** This is the value that walTryBeginRead returns when it needs to
+** be retried.
-+*/
+ */
+-static int walWriteOneFrame(
+- WalWriter *p, /* Where to write the frame */
+- PgHdr *pPage, /* The page of the frame to be written */
+- int nTruncate, /* The commit flag. Usually 0. >0 for commit */
+- sqlite3_int64 iOffset /* Byte offset at which to write */
+-){
+- int rc; /* Result code from subfunctions */
+- void *pData; /* Data actually written */
+- u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-header in */
+-#if defined(SQLITE_HAS_CODEC)
+- if( (pData = sqlite3PagerCodec(pPage))==0 ) return SQLITE_NOMEM;
+-#else
+- pData = pPage->pData;
+-#endif
+- walEncodeFrame(p->pWal, pPage->pgno, nTruncate, pData, aFrame);
+- rc = walWriteToLog(p, aFrame, sizeof(aFrame), iOffset);
+- if( rc ) return rc;
+- /* Write the page data */
+- rc = walWriteToLog(p, pData, p->szPage, iOffset+sizeof(aFrame));
+- return rc;
+-}
+#define WAL_RETRY (-1)
-- /* If this is the end of a transaction, then we might need to pad
-- ** the transaction and/or sync the WAL file.
+-/*
+-** Write a set of frames to the log. The caller must hold the write-lock
+-** on the log file (obtained using sqlite3WalBeginWriteTransaction()).
+/*
+** Attempt to start a read transaction. This might fail due to a race or
+** other transient condition. When that happens, it returns WAL_RETRY to
@@ -6156,21 +6769,49 @@
+** update values of the aReadMark[] array in the header, but if it does
+** so it takes care to hold an exclusive lock on the corresponding
+** WAL_READ_LOCK() while changing values.
-+*/
+ */
+-SQLITE_PRIVATE int sqlite3WalFrames(
+- Wal *pWal, /* Wal handle to write to */
+- int szPage, /* Database page-size in bytes */
+- PgHdr *pList, /* List of dirty pages to write */
+- Pgno nTruncate, /* Database size after this commit */
+- int isCommit, /* True if this is a commit */
+- int sync_flags /* Flags to pass to OsSync() (or 0) */
+-){
+- int rc; /* Used to catch return codes */
+- u32 iFrame; /* Next frame address */
+- PgHdr *p; /* Iterator to run through pList with. */
+- PgHdr *pLast = 0; /* Last frame in list */
+- int nExtra = 0; /* Number of extra copies of last page */
+- int szFrame; /* The size of a single frame */
+- i64 iOffset; /* Next byte to write in WAL file */
+- WalWriter w; /* The writer */
+-
+- assert( pList );
+- assert( pWal->writeLock );
+-
+- /* If this frame set completes a transaction, then nTruncate>0. If
+- ** nTruncate==0 then this frame set does not complete the transaction. */
+- assert( (isCommit!=0)==(nTruncate!=0) );
+static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
+ volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */
+ u32 mxReadMark; /* Largest aReadMark[] value */
+ int mxI; /* Index of largest aReadMark[] value */
+ int i; /* Loop counter */
+ int rc = SQLITE_OK; /* Return code */
-+
+
+-#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
+- { int cnt; for(cnt=0, p=pList; p; p=p->pDirty, cnt++){}
+- WALTRACE(("WAL%p: frame write begin. %d frames. mxFrame=%d. %s\n",
+- pWal, cnt, pWal->hdr.mxFrame, isCommit ? "Commit" : "Spill"));
+- }
+-#endif
+ assert( pWal->readLock<0 ); /* Not currently locked */
-+
+
+- /* See if it is possible to write these frames into the start of the
+- ** log file, instead of appending to it at pWal->hdr.mxFrame.
+ /* Take steps to avoid spinning forever if there is a protocol error.
- **
-- ** Padding and syncing only occur if this set of frames complete a
-- ** transaction and if PRAGMA synchronous=FULL. If synchronous==NORMAL
-- ** or synchonous==OFF, then no padding or syncing are needed.
++ **
+ ** Circumstances that cause a RETRY should only last for the briefest
+ ** instances of time. No I/O or other system calls are done while the
+ ** locks are held, so the locks should not be held for very long. But
@@ -6178,13 +6819,7 @@
+ ** paged out or take a page-fault that is time-consuming to resolve,
+ ** during the few nanoseconds that it is holding the lock. In that case,
+ ** it might take longer than normal for the lock to free.
- **
-- ** If SQLITE_IOCAP_POWERSAFE_OVERWRITE is defined, then padding is not
-- ** needed and only the sync is done. If padding is needed, then the
-- ** final frame is repeated (with its commit mark) until the next sector
-- ** boundary is crossed. Only the part of the WAL prior to the last
-- ** sector boundary is synced; the part of the last frame that extends
-- ** past the sector boundary is written after the sync.
++ **
+ ** After 5 RETRYs, we begin calling sqlite3OsSleep(). The first few
+ ** calls to sqlite3OsSleep() have a delay of 1 microsecond. Really this
+ ** is more of a scheduler yield than an actual delay. But on the 10th
@@ -6192,15 +6827,8 @@
+ ** so that on the 100th (and last) RETRY we delay for 21 milliseconds.
+ ** The total delay time before giving up is less than 1 second.
*/
-- if( isCommit && (sync_flags & WAL_SYNC_TRANSACTIONS)!=0 ){
-- if( pWal->padToSectorBoundary ){
-- int sectorSize = sqlite3OsSectorSize(pWal->pWalFd);
-- w.iSyncPoint = ((iOffset+sectorSize-1)/sectorSize)*sectorSize;
-- while( iOffset<w.iSyncPoint ){
-- rc = walWriteOneFrame(&w, pLast, nTruncate, iOffset);
-- if( rc ) return rc;
-- iOffset += szFrame;
-- nExtra++;
+- if( SQLITE_OK!=(rc = walRestartLog(pWal)) ){
+- return rc;
+ if( cnt>5 ){
+ int nDelay = 1; /* Pause time in microseconds */
+ if( cnt>100 ){
@@ -6209,8 +6837,35 @@
+ }
+ if( cnt>=10 ) nDelay = (cnt-9)*238; /* Max delay 21ms. Total delay 996ms */
+ sqlite3OsSleep(pWal->pVfs, nDelay);
-+ }
-+
+ }
+
+- /* If this is the first frame written into the log, write the WAL
+- ** header to the start of the WAL file. See comments at the top of
+- ** this source file for a description of the WAL header format.
+- */
+- iFrame = pWal->hdr.mxFrame;
+- if( iFrame==0 ){
+- u8 aWalHdr[WAL_HDRSIZE]; /* Buffer to assemble wal-header in */
+- u32 aCksum[2]; /* Checksum for wal-header */
+-
+- sqlite3Put4byte(&aWalHdr[0], (WAL_MAGIC | SQLITE_BIGENDIAN));
+- sqlite3Put4byte(&aWalHdr[4], WAL_MAX_VERSION);
+- sqlite3Put4byte(&aWalHdr[8], szPage);
+- sqlite3Put4byte(&aWalHdr[12], pWal->nCkpt);
+- if( pWal->nCkpt==0 ) sqlite3_randomness(8, pWal->hdr.aSalt);
+- memcpy(&aWalHdr[16], pWal->hdr.aSalt, 8);
+- walChecksumBytes(1, aWalHdr, WAL_HDRSIZE-2*4, 0, aCksum);
+- sqlite3Put4byte(&aWalHdr[24], aCksum[0]);
+- sqlite3Put4byte(&aWalHdr[28], aCksum[1]);
+-
+- pWal->szPage = szPage;
+- pWal->hdr.bigEndCksum = SQLITE_BIGENDIAN;
+- pWal->hdr.aFrameCksum[0] = aCksum[0];
+- pWal->hdr.aFrameCksum[1] = aCksum[1];
+- pWal->truncateOnCommit = 1;
+-
+- rc = sqlite3OsWrite(pWal->pWalFd, aWalHdr, sizeof(aWalHdr), 0);
+- WALTRACE(("WAL%p: wal-header write %s\n", pWal, rc ? "failed" : "ok"));
+ if( !useWal ){
+ rc = walIndexReadHdr(pWal, pChanged);
+ if( rc==SQLITE_BUSY ){
@@ -6236,28 +6891,27 @@
+ rc = WAL_RETRY;
+ }else if( rc==SQLITE_BUSY ){
+ rc = SQLITE_BUSY_RECOVERY;
- }
-- }else{
-- rc = sqlite3OsSync(w.pFd, sync_flags & SQLITE_SYNC_MASK);
++ }
+ }
-+ if( rc!=SQLITE_OK ){
-+ return rc;
+ if( rc!=SQLITE_OK ){
+ return rc;
}
- }
++ }
-- /* If this frame set completes the first transaction in the WAL and
-- ** if PRAGMA journal_size_limit is set, then truncate the WAL to the
-- ** journal size limit, if possible.
-- */
-- if( isCommit && pWal->truncateOnCommit && pWal->mxWalSize>=0 ){
-- i64 sz = pWal->mxWalSize;
-- if( walFrameOffset(iFrame+nExtra+1, szPage)>pWal->mxWalSize ){
-- sz = walFrameOffset(iFrame+nExtra+1, szPage);
+- /* Sync the header (unless SQLITE_IOCAP_SEQUENTIAL is true or unless
+- ** all syncing is turned off by PRAGMA synchronous=OFF). Otherwise
+- ** an out-of-order write following a WAL restart could result in
+- ** database corruption. See the ticket:
+- **
+- ** http://localhost:591/sqlite/info/ff5be73dee
+ pInfo = walCkptInfo(pWal);
+ if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame ){
+ /* The WAL has been completely backfilled (or it is empty).
+ ** and can be safely ignored.
-+ */
+ */
+- if( pWal->syncHeader && sync_flags ){
+- rc = sqlite3OsSync(pWal->pWalFd, sync_flags & SQLITE_SYNC_MASK);
+- if( rc ) return rc;
+ rc = walLockShared(pWal, WAL_READ_LOCK(0));
+ walShmBarrier(pWal);
+ if( rc==SQLITE_OK ){
@@ -6283,28 +6937,60 @@
+ }else if( rc!=SQLITE_BUSY ){
+ return rc;
}
-- walLimitSize(pWal, sz);
-- pWal->truncateOnCommit = 0;
}
+- assert( (int)pWal->szPage==szPage );
+-
+- /* Setup information needed to write frames into the WAL */
+- w.pWal = pWal;
+- w.pFd = pWal->pWalFd;
+- w.iSyncPoint = 0;
+- w.syncFlags = sync_flags;
+- w.szPage = szPage;
+- iOffset = walFrameOffset(iFrame+1, szPage);
+- szFrame = szPage + WAL_FRAME_HDRSIZE;
+-
+- /* Write all frames into the log file exactly once */
+- for(p=pList; p; p=p->pDirty){
+- int nDbSize; /* 0 normally. Positive == commit flag */
+- iFrame++;
+- assert( iOffset==walFrameOffset(iFrame, szPage) );
+- nDbSize = (isCommit && p->pDirty==0) ? nTruncate : 0;
+- rc = walWriteOneFrame(&w, p, nDbSize, iOffset);
+- if( rc ) return rc;
+- pLast = p;
+- iOffset += szFrame;
+- }
-- /* Append data to the wal-index. It is not necessary to lock the
-- ** wal-index to do this as the SQLITE_SHM_WRITE lock held on the wal-index
-- ** guarantees that there are no other writers, and no data that may
-- ** be in use by existing readers is being overwritten.
+- /* If this is the end of a transaction, then we might need to pad
+- ** the transaction and/or sync the WAL file.
+- **
+- ** Padding and syncing only occur if this set of frames complete a
+- ** transaction and if PRAGMA synchronous=FULL. If synchronous==NORMAL
+- ** or synchonous==OFF, then no padding or syncing are needed.
+- **
+- ** If SQLITE_IOCAP_POWERSAFE_OVERWRITE is defined, then padding is not
+- ** needed and only the sync is done. If padding is needed, then the
+- ** final frame is repeated (with its commit mark) until the next sector
+- ** boundary is crossed. Only the part of the WAL prior to the last
+- ** sector boundary is synced; the part of the last frame that extends
+- ** past the sector boundary is written after the sync.
+ /* If we get this far, it means that the reader will want to use
+ ** the WAL to get at content from recent commits. The job now is
+ ** to select one of the aReadMark[] entries that is closest to
+ ** but not exceeding pWal->hdr.mxFrame and lock that entry.
*/
-- iFrame = pWal->hdr.mxFrame;
-- for(p=pList; p && rc==SQLITE_OK; p=p->pDirty){
-- iFrame++;
-- rc = walIndexAppend(pWal, iFrame, p->pgno);
-- }
-- while( rc==SQLITE_OK && nExtra>0 ){
-- iFrame++;
-- nExtra--;
-- rc = walIndexAppend(pWal, iFrame, pLast->pgno);
+- if( isCommit && (sync_flags & WAL_SYNC_TRANSACTIONS)!=0 ){
+- if( pWal->padToSectorBoundary ){
+- int sectorSize = sqlite3OsSectorSize(pWal->pWalFd);
+- w.iSyncPoint = ((iOffset+sectorSize-1)/sectorSize)*sectorSize;
+- while( iOffset<w.iSyncPoint ){
+- rc = walWriteOneFrame(&w, pLast, nTruncate, iOffset);
+- if( rc ) return rc;
+- iOffset += szFrame;
+- nExtra++;
+- }
+- }else{
+- rc = sqlite3OsSync(w.pFd, sync_flags & SQLITE_SYNC_MASK);
+ mxReadMark = 0;
+ mxI = 0;
+ for(i=1; i<WAL_NREADER; i++){
@@ -6313,7 +6999,7 @@
+ assert( thisMark!=READMARK_NOT_USED );
+ mxReadMark = thisMark;
+ mxI = i;
-+ }
+ }
}
+ /* There was once an "if" here. The extra "{" is to preserve indentation. */
+ {
@@ -6337,23 +7023,18 @@
+ return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTLOCK;
+ }
-- if( rc==SQLITE_OK ){
-- /* Update the private copy of the header. */
-- pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16));
-- testcase( szPage<=32768 );
-- testcase( szPage>=65536 );
-- pWal->hdr.mxFrame = iFrame;
-- if( isCommit ){
-- pWal->hdr.iChange++;
-- pWal->hdr.nPage = nTruncate;
+- /* If this frame set completes the first transaction in the WAL and
+- ** if PRAGMA journal_size_limit is set, then truncate the WAL to the
+- ** journal size limit, if possible.
+- */
+- if( isCommit && pWal->truncateOnCommit && pWal->mxWalSize>=0 ){
+- i64 sz = pWal->mxWalSize;
+- if( walFrameOffset(iFrame+nExtra+1, szPage)>pWal->mxWalSize ){
+- sz = walFrameOffset(iFrame+nExtra+1, szPage);
+ rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
+ if( rc ){
+ return rc==SQLITE_BUSY ? WAL_RETRY : rc;
- }
-- /* If this is a commit, update the wal-index header too. */
-- if( isCommit ){
-- walIndexWriteHdr(pWal);
-- pWal->iCallback = iFrame;
++ }
+ /* Now that the read-lock has been obtained, check that neither the
+ ** value in the aReadMark[] array or the contents of the wal-index
+ ** header have changed.
@@ -6384,11 +7065,27 @@
+ assert( mxReadMark<=pWal->hdr.mxFrame );
+ pWal->readLock = (i16)mxI;
}
+- walLimitSize(pWal, sz);
+- pWal->truncateOnCommit = 0;
}
+ return rc;
+}
-- WALTRACE(("WAL%p: frame write %s\n", pWal, rc ? "failed" : "ok"));
+- /* Append data to the wal-index. It is not necessary to lock the
+- ** wal-index to do this as the SQLITE_SHM_WRITE lock held on the wal-index
+- ** guarantees that there are no other writers, and no data that may
+- ** be in use by existing readers is being overwritten.
+- */
+- iFrame = pWal->hdr.mxFrame;
+- for(p=pList; p && rc==SQLITE_OK; p=p->pDirty){
+- iFrame++;
+- rc = walIndexAppend(pWal, iFrame, p->pgno);
+- }
+- while( rc==SQLITE_OK && nExtra>0 ){
+- iFrame++;
+- nExtra--;
+- rc = walIndexAppend(pWal, iFrame, pLast->pgno);
+- }
+/*
+** Begin a read transaction on the database.
+**
@@ -6406,7 +7103,22 @@
+SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
+ int rc; /* Return code */
+ int cnt = 0; /* Number of TryBeginRead attempts */
-+
+
+- if( rc==SQLITE_OK ){
+- /* Update the private copy of the header. */
+- pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16));
+- testcase( szPage<=32768 );
+- testcase( szPage>=65536 );
+- pWal->hdr.mxFrame = iFrame;
+- if( isCommit ){
+- pWal->hdr.iChange++;
+- pWal->hdr.nPage = nTruncate;
+- }
+- /* If this is a commit, update the wal-index header too. */
+- if( isCommit ){
+- walIndexWriteHdr(pWal);
+- pWal->iCallback = iFrame;
+- }
+ do{
+ rc = walTryBeginRead(pWal, pChanged, 0, ++cnt);
+ }while( rc==WAL_RETRY );
@@ -6414,15 +7126,9 @@
+ testcase( (rc&0xff)==SQLITE_IOERR );
+ testcase( rc==SQLITE_PROTOCOL );
+ testcase( rc==SQLITE_OK );
- return rc;
- }
-
--/*
--** This routine is called to implement sqlite3_wal_checkpoint() and
--** related interfaces.
--**
--** Obtain a CHECKPOINT lock and then backfill as much information as
--** we can from WAL into the database.
++ return rc;
++}
++
+/*
+** Finish with a read transaction. All this does is release the
+** read-lock.
@@ -6432,9 +7138,18 @@
+ if( pWal->readLock>=0 ){
+ walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock));
+ pWal->readLock = -1;
-+ }
-+}
-+
+ }
+-
+- WALTRACE(("WAL%p: frame write %s\n", pWal, rc ? "failed" : "ok"));
+- return rc;
+ }
+
+-/*
+-** This routine is called to implement sqlite3_wal_checkpoint() and
+-** related interfaces.
+-**
+-** Obtain a CHECKPOINT lock and then backfill as much information as
+-** we can from WAL into the database.
+/*
+** Read a page from the WAL, if it is present in the WAL and if the
+** current read transaction is configured to use the WAL.
@@ -6746,7 +7461,7 @@
-#endif
-
-#endif /* #ifndef SQLITE_OMIT_WAL */
--
+
-/************** End of wal.c *************************************************/
-/************** Begin file btmutex.c *****************************************/
-/*
@@ -6971,7 +7686,6 @@
-** SIZE DESCRIPTION
-** 4 Page number of next overflow page
-** * Data
-+
+ /* Only one writer allowed at a time. Get the write lock. Return
+ ** SQLITE_BUSY if unable.
+ */
@@ -7174,7 +7888,8 @@
+ aSalt[1] = salt1;
+ walIndexWriteHdr(pWal);
+ pInfo->nBackfill = 0;
-+ for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
++ pInfo->aReadMark[1] = 0;
++ for(i=2; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
+ assert( pInfo->aReadMark[0]==0 );
+ walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
+ }else if( rc!=SQLITE_BUSY ){
@@ -7347,40 +8062,7 @@
-#define WRITE_LOCK 2
+ assert( pList );
+ assert( pWal->writeLock );
-
--/* A Btree handle
--**
--** A database connection contains a pointer to an instance of
--** this object for every database file that it has open. This structure
--** is opaque to the database connection. The database connection cannot
--** see the internals of this structure and only deals with pointers to
--** this structure.
--**
--** For some database files, the same underlying database cache might be
--** shared between multiple connections. In that case, each connection
--** has it own instance of this object. But each instance of this object
--** points to the same BtShared object. The database cache and the
--** schema associated with the database file are all contained within
--** the BtShared object.
--**
--** All fields in this structure are accessed under sqlite3.mutex.
--** The pBt pointer itself may not be changed while there exists cursors
--** in the referenced BtShared that point back to this Btree since those
--** cursors have to go through this Btree to find their BtShared and
--** they often do so without holding sqlite3.mutex.
--*/
--struct Btree {
-- sqlite3 *db; /* The database connection holding this btree */
-- BtShared *pBt; /* Sharable content of this btree */
-- u8 inTrans; /* TRANS_NONE, TRANS_READ or TRANS_WRITE */
-- u8 sharable; /* True if we can share pBt with another db */
-- u8 locked; /* True if db currently has pBt locked */
-- int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */
-- int nBackup; /* Number of backup operations reading this btree */
-- Btree *pNext; /* List of other sharable Btrees from the same db */
-- Btree *pPrev; /* Back pointer of the same list */
--#ifndef SQLITE_OMIT_SHARED_CACHE
-- BtLock lock; /* Object used to lock page 1 */
++
+ /* If this frame set completes a transaction, then nTruncate>0. If
+ ** nTruncate==0 then this frame set does not complete the transaction. */
+ assert( (isCommit!=0)==(nTruncate!=0) );
@@ -7390,19 +8072,8 @@
+ WALTRACE(("WAL%p: frame write begin. %d frames. mxFrame=%d. %s\n",
+ pWal, cnt, pWal->hdr.mxFrame, isCommit ? "Commit" : "Spill"));
+ }
- #endif
--};
-
--/*
--** Btree.inTrans may take one of the following values.
--**
--** If the shared-data extension is enabled, there may be multiple users
--** of the Btree structure. At most one of these may open a write transaction,
--** but any number may have active read transactions.
--*/
--#define TRANS_NONE 0
--#define TRANS_READ 1
--#define TRANS_WRITE 2
++#endif
++
+ /* See if it is possible to write these frames into the start of the
+ ** log file, instead of appending to it at pWal->hdr.mxFrame.
+ */
@@ -7550,6 +8221,80 @@
+ pWal->iCallback = iFrame;
+ }
+ }
++
++ WALTRACE(("WAL%p: frame write %s\n", pWal, rc ? "failed" : "ok"));
++ return rc;
++}
+
+-/* A Btree handle
+-**
+-** A database connection contains a pointer to an instance of
+-** this object for every database file that it has open. This structure
+-** is opaque to the database connection. The database connection cannot
+-** see the internals of this structure and only deals with pointers to
+-** this structure.
++/*
++** This routine is called to implement sqlite3_wal_checkpoint() and
++** related interfaces.
+ **
+-** For some database files, the same underlying database cache might be
+-** shared between multiple connections. In that case, each connection
+-** has it own instance of this object. But each instance of this object
+-** points to the same BtShared object. The database cache and the
+-** schema associated with the database file are all contained within
+-** the BtShared object.
++** Obtain a CHECKPOINT lock and then backfill as much information as
++** we can from WAL into the database.
+ **
+-** All fields in this structure are accessed under sqlite3.mutex.
+-** The pBt pointer itself may not be changed while there exists cursors
+-** in the referenced BtShared that point back to this Btree since those
+-** cursors have to go through this Btree to find their BtShared and
+-** they often do so without holding sqlite3.mutex.
++** If parameter xBusy is not NULL, it is a pointer to a busy-handler
++** callback. In this case this function runs a blocking checkpoint.
+ */
+-struct Btree {
+- sqlite3 *db; /* The database connection holding this btree */
+- BtShared *pBt; /* Sharable content of this btree */
+- u8 inTrans; /* TRANS_NONE, TRANS_READ or TRANS_WRITE */
+- u8 sharable; /* True if we can share pBt with another db */
+- u8 locked; /* True if db currently has pBt locked */
+- int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */
+- int nBackup; /* Number of backup operations reading this btree */
+- Btree *pNext; /* List of other sharable Btrees from the same db */
+- Btree *pPrev; /* Back pointer of the same list */
+-#ifndef SQLITE_OMIT_SHARED_CACHE
+- BtLock lock; /* Object used to lock page 1 */
+-#endif
+-};
++SQLITE_PRIVATE int sqlite3WalCheckpoint(
++ Wal *pWal, /* Wal connection */
++ int eMode, /* PASSIVE, FULL or RESTART */
++ int (*xBusy)(void*), /* Function to call when busy */
++ void *pBusyArg, /* Context argument for xBusyHandler */
++ int sync_flags, /* Flags to sync db file with (or 0) */
++ int nBuf, /* Size of temporary buffer */
++ u8 *zBuf, /* Temporary buffer to use */
++ int *pnLog, /* OUT: Number of frames in WAL */
++ int *pnCkpt /* OUT: Number of backfilled frames in WAL */
++){
++ int rc; /* Return code */
++ int isChanged = 0; /* True if a new wal-index header is loaded */
++ int eMode2 = eMode; /* Mode to pass to walCheckpoint() */
+
+-/*
+-** Btree.inTrans may take one of the following values.
+-**
+-** If the shared-data extension is enabled, there may be multiple users
+-** of the Btree structure. At most one of these may open a write transaction,
+-** but any number may have active read transactions.
+-*/
+-#define TRANS_NONE 0
+-#define TRANS_READ 1
+-#define TRANS_WRITE 2
++ assert( pWal->ckptLock==0 );
++ assert( pWal->writeLock==0 );
-/*
-** An instance of this object represents a single database file.
@@ -7580,23 +8325,12 @@
-**
-** 1) The current writer (BtShared.pWriter) concludes its transaction, OR
-** 2) The number of locks held by other connections drops to zero.
-+ WALTRACE(("WAL%p: frame write %s\n", pWal, rc ? "failed" : "ok"));
-+ return rc;
-+}
-+
-+/*
-+** This routine is called to implement sqlite3_wal_checkpoint() and
-+** related interfaces.
- **
+-**
-** while in the 'pending-lock' state, no connection may start a new
-** transaction.
-+** Obtain a CHECKPOINT lock and then backfill as much information as
-+** we can from WAL into the database.
- **
+-**
-** This feature is included to help prevent writer-starvation.
-+** If parameter xBusy is not NULL, it is a pointer to a busy-handler
-+** callback. In this case this function runs a blocking checkpoint.
- */
+-*/
-struct BtShared {
- Pager *pPager; /* The page cache */
- sqlite3 *db; /* Database connection currently using this Btree */
@@ -7630,20 +8364,16 @@
-#endif
- u8 *pTmpSpace; /* BtShared.pageSize bytes of space for tmp use */
-};
-+SQLITE_PRIVATE int sqlite3WalCheckpoint(
-+ Wal *pWal, /* Wal connection */
-+ int eMode, /* PASSIVE, FULL or RESTART */
-+ int (*xBusy)(void*), /* Function to call when busy */
-+ void *pBusyArg, /* Context argument for xBusyHandler */
-+ int sync_flags, /* Flags to sync db file with (or 0) */
-+ int nBuf, /* Size of temporary buffer */
-+ u8 *zBuf, /* Temporary buffer to use */
-+ int *pnLog, /* OUT: Number of frames in WAL */
-+ int *pnCkpt /* OUT: Number of backfilled frames in WAL */
-+){
-+ int rc; /* Return code */
-+ int isChanged = 0; /* True if a new wal-index header is loaded */
-+ int eMode2 = eMode; /* Mode to pass to walCheckpoint() */
++ if( pWal->readOnly ) return SQLITE_READONLY;
++ WALTRACE(("WAL%p: checkpoint begins\n", pWal));
++ rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
++ if( rc ){
++ /* Usually this is SQLITE_BUSY meaning that another thread or process
++ ** is already running a checkpoint, or maybe a recovery. But it might
++ ** also be SQLITE_IOERR. */
++ return rc;
++ }
++ pWal->ckptLock = 1;
-/*
-** Allowed values for BtShared.btsFlags
@@ -7655,8 +8385,24 @@
-#define BTS_NO_WAL 0x0010 /* Do not open write-ahead-log files */
-#define BTS_EXCLUSIVE 0x0020 /* pWriter has an exclusive lock */
-#define BTS_PENDING 0x0040 /* Waiting for read-locks to clear */
-+ assert( pWal->ckptLock==0 );
-+ assert( pWal->writeLock==0 );
++ /* If this is a blocking-checkpoint, then obtain the write-lock as well
++ ** to prevent any writers from running while the checkpoint is underway.
++ ** This has to be done before the call to walIndexReadHdr() below.
++ **
++ ** If the writer lock cannot be obtained, then a passive checkpoint is
++ ** run instead. Since the checkpointer is not holding the writer lock,
++ ** there is no point in blocking waiting for any readers. Assuming no
++ ** other error occurs, this function will return SQLITE_BUSY to the caller.
++ */
++ if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
++ rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_WRITE_LOCK, 1);
++ if( rc==SQLITE_OK ){
++ pWal->writeLock = 1;
++ }else if( rc==SQLITE_BUSY ){
++ eMode2 = SQLITE_CHECKPOINT_PASSIVE;
++ rc = SQLITE_OK;
++ }
++ }
-/*
-** An instance of the following structure is used to hold information
@@ -7674,16 +8420,10 @@
- u16 iOverflow; /* Offset to overflow page number. Zero if no overflow */
- u16 nSize; /* Size of the cell content on the main b-tree page */
-};
-+ if( pWal->readOnly ) return SQLITE_READONLY;
-+ WALTRACE(("WAL%p: checkpoint begins\n", pWal));
-+ rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
-+ if( rc ){
-+ /* Usually this is SQLITE_BUSY meaning that another thread or process
-+ ** is already running a checkpoint, or maybe a recovery. But it might
-+ ** also be SQLITE_IOERR. */
-+ return rc;
++ /* Read the wal-index header. */
++ if( rc==SQLITE_OK ){
++ rc = walIndexReadHdr(pWal, &isChanged);
+ }
-+ pWal->ckptLock = 1;
-/*
-** Maximum depth of an SQLite B-Tree structure. Any B-Tree deeper than
@@ -7695,24 +8435,13 @@
-** assumed that the database is corrupt.
-*/
-#define BTCURSOR_MAX_DEPTH 20
-+ /* If this is a blocking-checkpoint, then obtain the write-lock as well
-+ ** to prevent any writers from running while the checkpoint is underway.
-+ ** This has to be done before the call to walIndexReadHdr() below.
-+ **
-+ ** If the writer lock cannot be obtained, then a passive checkpoint is
-+ ** run instead. Since the checkpointer is not holding the writer lock,
-+ ** there is no point in blocking waiting for any readers. Assuming no
-+ ** other error occurs, this function will return SQLITE_BUSY to the caller.
-+ */
-+ if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
-+ rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_WRITE_LOCK, 1);
-+ if( rc==SQLITE_OK ){
-+ pWal->writeLock = 1;
-+ }else if( rc==SQLITE_BUSY ){
-+ eMode2 = SQLITE_CHECKPOINT_PASSIVE;
-+ rc = SQLITE_OK;
++ /* Copy data from the log to the database file. */
++ if( rc==SQLITE_OK ){
++ if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){
++ rc = SQLITE_CORRUPT_BKPT;
++ }else{
++ rc = walCheckpoint(pWal, eMode2, xBusy, pBusyArg, sync_flags, zBuf);
+ }
-+ }
-/*
-** A cursor is a pointer to a particular entry within a particular
@@ -7749,13 +8478,16 @@
-#ifndef SQLITE_OMIT_INCRBLOB
- u8 isIncrblobHandle; /* True if this cursor is an incr. io handle */
-#endif
+- u8 hints; /* As configured by CursorSetHints() */
- i16 iPage; /* Index of current page in apPage */
- u16 aiIdx[BTCURSOR_MAX_DEPTH]; /* Current index in apPage[i] */
- MemPage *apPage[BTCURSOR_MAX_DEPTH]; /* Pages from root to current page */
-};
-+ /* Read the wal-index header. */
-+ if( rc==SQLITE_OK ){
-+ rc = walIndexReadHdr(pWal, &isChanged);
++ /* If no error occurred, set the output variables. */
++ if( rc==SQLITE_OK || rc==SQLITE_BUSY ){
++ if( pnLog ) *pnLog = (int)pWal->hdr.mxFrame;
++ if( pnCkpt ) *pnCkpt = (int)(walCkptInfo(pWal)->nBackfill);
++ }
+ }
-/*
@@ -7787,24 +8519,27 @@
-#define CURSOR_VALID 1
-#define CURSOR_REQUIRESEEK 2
-#define CURSOR_FAULT 3
-+ /* Copy data from the log to the database file. */
-+ if( rc==SQLITE_OK ){
-+ if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){
-+ rc = SQLITE_CORRUPT_BKPT;
-+ }else{
-+ rc = walCheckpoint(pWal, eMode2, xBusy, pBusyArg, sync_flags, zBuf);
-+ }
++ if( isChanged ){
++ /* If a new wal-index header was loaded before the checkpoint was
++ ** performed, then the pager-cache associated with pWal is now
++ ** out of date. So zero the cached wal-index header to ensure that
++ ** next time the pager opens a snapshot on this database it knows that
++ ** the cache needs to be reset.
++ */
++ memset(&pWal->hdr, 0, sizeof(WalIndexHdr));
++ }
-/*
-** The database page the PENDING_BYTE occupies. This page is never used.
-*/
-# define PENDING_BYTE_PAGE(pBt) PAGER_MJ_PGNO(pBt)
-+ /* If no error occurred, set the output variables. */
-+ if( rc==SQLITE_OK || rc==SQLITE_BUSY ){
-+ if( pnLog ) *pnLog = (int)pWal->hdr.mxFrame;
-+ if( pnCkpt ) *pnCkpt = (int)(walCkptInfo(pWal)->nBackfill);
-+ }
-+ }
++ /* Release the locks. */
++ sqlite3WalEndWriteTransaction(pWal);
++ walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1);
++ pWal->ckptLock = 0;
++ WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok"));
++ return (rc==SQLITE_OK && eMode!=eMode2 ? SQLITE_BUSY : rc);
++}
-/*
-** These macros define the location of the pointer-map entry for a
@@ -7820,24 +8555,6 @@
-** then pgno is returned. So (pgno==PTRMAP_PAGENO(pgsz, pgno)) can be
-** used to test if pgno is a pointer-map page. PTRMAP_ISPAGE implements
-** this test.
-+ if( isChanged ){
-+ /* If a new wal-index header was loaded before the checkpoint was
-+ ** performed, then the pager-cache associated with pWal is now
-+ ** out of date. So zero the cached wal-index header to ensure that
-+ ** next time the pager opens a snapshot on this database it knows that
-+ ** the cache needs to be reset.
-+ */
-+ memset(&pWal->hdr, 0, sizeof(WalIndexHdr));
-+ }
-+
-+ /* Release the locks. */
-+ sqlite3WalEndWriteTransaction(pWal);
-+ walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1);
-+ pWal->ckptLock = 0;
-+ WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok"));
-+ return (rc==SQLITE_OK && eMode!=eMode2 ? SQLITE_BUSY : rc);
-+}
-+
+/* Return the value to pass to a sqlite3_wal_hook callback, the
+** number of frames in the WAL at the point of the last commit since
+** sqlite3WalCallback() was called. If no commits have occurred since
@@ -8036,7 +8753,7 @@
#ifndef SQLITE_OMIT_SHARED_CACHE
#if SQLITE_THREADSAFE
-@@ -87992,10 +89440,16 @@
+@@ -86559,10 +88241,16 @@
*/
SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
int rc = sqlite3_overload_function(db, "MATCH", 2);
@@ -8053,7 +8770,34 @@
}
/*
-@@ -93389,60 +94843,6 @@
+@@ -91386,6 +93074,12 @@
+ sqlite3 *db = pParse->db; /* The database connection */
+ Db *pDb; /* The specific database being pragmaed */
+ Vdbe *v = pParse->pVdbe = sqlite3VdbeCreate(db); /* Prepared statement */
++/** BEGIN CRYPTO **/
++#ifdef SQLITE_HAS_CODEC
++ extern int codec_pragma(sqlite3*, int, Parse *, const char *, const char *);
++#endif
++/** END CRYPTO **/
++
+
+ if( v==0 ) return;
+ sqlite3VdbeRunOnlyOnce(v);
+@@ -91445,6 +93139,13 @@
+ pParse->rc = rc;
+ }else
+
++/** BEGIN CRYPTO **/
++#ifdef SQLITE_HAS_CODEC
++ if(codec_pragma(db, iDb, pParse, zLeft, zRight)) {
++ /* codec_pragma executes internal */
++ }else
++ #endif
++/** END CRYPTO **/
+
+ #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
+ /*
+@@ -92000,60 +93701,6 @@
#ifndef SQLITE_OMIT_SCHEMA_PRAGMAS
/*
@@ -8114,48 +8858,3 @@
** PRAGMA table_info(<table>)
**
** Return a single row for each column of the named table. The columns of
-@@ -94090,6 +95490,44 @@
- sqlite3_rekey(db, zKey, i/2);
- }
- }else
-+/** BEGIN CRYPTO **/
-+ if( sqlite3StrICmp(zLeft, "cipher_version")==0 && !zRight ){
-+ extern void codec_vdbe_return_static_string(Parse *pParse, const char *zLabel, const char *value);
-+ codec_vdbe_return_static_string(pParse, "cipher_version", CIPHER_VERSION);
-+ }else
-+ if( sqlite3StrICmp(zLeft, "cipher")==0 && zRight ){
-+ extern int codec_set_cipher_name(sqlite3*, int, const char *, int);
-+ codec_set_cipher_name(db, iDb, zRight, 2); // change cipher for both
-+ }else
-+ if( sqlite3StrICmp(zLeft, "rekey_cipher")==0 && zRight ){
-+ extern int codec_set_cipher_name(sqlite3*, int, const char *, int);
-+ codec_set_cipher_name(db, iDb, zRight, 1); // change write cipher only
-+ }else
-+ if( sqlite3StrICmp(zLeft, "kdf_iter")==0 && zRight ){
-+ extern int codec_set_kdf_iter(sqlite3*, int, int, int);
-+ codec_set_kdf_iter(db, iDb, atoi(zRight), 2); // change of RW PBKDF2 iteration
-+ }else
-+ if( sqlite3StrICmp(zLeft, "fast_kdf_iter")==0 && zRight ){
-+ extern int codec_set_fast_kdf_iter(sqlite3*, int, int, int);
-+ codec_set_fast_kdf_iter(db, iDb, atoi(zRight), 2); // change of RW PBKDF2 iteration
-+ }else
-+ if( sqlite3StrICmp(zLeft, "rekey_kdf_iter")==0 && zRight ){
-+ extern int codec_set_kdf_iter(sqlite3*, int, int, int);
-+ codec_set_kdf_iter(db, iDb, atoi(zRight), 1); // change # if W iterations
-+ }else
-+ if( sqlite3StrICmp(zLeft,"cipher_page_size")==0 ){
-+ extern int codec_set_page_size(sqlite3*, int, int);
-+ codec_set_page_size(db, iDb, atoi(zRight)); // change page size
-+ }else
-+ if( sqlite3StrICmp(zLeft,"cipher_default_use_hmac")==0 ){
-+ extern void codec_set_default_use_hmac(int);
-+ codec_set_default_use_hmac(sqlite3GetBoolean(zRight,1));
-+ }else
-+ if( sqlite3StrICmp(zLeft,"cipher_use_hmac")==0 ){
-+ extern int codec_set_use_hmac(sqlite3*, int, int);
-+ codec_set_use_hmac(db, iDb, sqlite3GetBoolean(zRight,1));
-+ }else
-+/** END CRYPTO **/
- #endif
- #if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD)
- if( sqlite3StrICmp(zLeft, "activate_extensions")==0 ){
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]