diff --git a/BUILD.gn b/BUILD.gn
index 570cebbf962dbc6ee439db88d4a4e73cae809735..3ae3aedb3d2bed28974d64e207082c12a855c5ef 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -28,7 +28,52 @@ config("sqlite3_private_config") {
}
group("libsqlite") {
- public_deps = [ ":sqlite" ]
+ public_deps = [
+ ":sqlite",
+ ":sqliteicu",
+ ]
+}
+
+ohos_shared_library("sqliteicu") {
+ branch_protector_ret = "pac_ret"
+ sources = [ "src/sqlite3icu.c" ]
+
+ defines = [
+ "NDEBUG=1",
+ "SQLITE_EXPORT_SYMBOLS",
+ "SQLITE_ENABLE_FTS3",
+ "HARMONY_OS",
+ "SQLITE_ENABLE_ICU",
+ ]
+ cflags_c = [
+ "-fvisibility=hidden",
+ "-Wno-implicit-fallthrough",
+ ]
+ if (target_os != "ios") {
+ ldflags = [ "-Wl,--exclude-libs,ALL" ]
+ }
+ deps = [ ":sqlite" ]
+ public_configs = [ ":sqlite_config" ]
+ configs = [ ":sqlite3_private_config" ]
+ innerapi_tags = [ "platformsdk_indirect" ]
+ part_name = "sqlite"
+ subsystem_name = "thirdparty"
+ install_images = [ system_base_dir ]
+ relative_install_dir = "platformsdk"
+ external_deps = []
+ if (is_cross_platform_build) {
+ if (target_os == "ios") {
+ deps += [ "//third_party/bounds_checking_function:libsec_shared" ]
+ } else {
+ deps += [ "//commonlibrary/c_utils/base:utils" ]
+ }
+ } else {
+ external_deps += [ "c_utils:utils" ]
+ }
+ external_deps += [
+ "icu:shared_icui18n",
+ "icu:shared_icuuc",
+ ]
}
ohos_shared_library("sqlite") {
@@ -46,7 +91,9 @@ ohos_shared_library("sqlite") {
"SQLITE_DEFAULT_FILE_FORMAT=4",
"SQLITE_DEFAULT_AUTOVACUUM=1",
"SQLITE_ENABLE_MEMORY_MANAGEMENT=1",
+ "SQLITE_ENABLE_LOAD_EXTENSION",
"SQLITE_ENABLE_FTS3",
+ "SQLITE_ENABLE_FTS3_TOKENIZER",
"SQLITE_ENABLE_FTS4",
"SQLITE_ENABLE_FTS5",
"SQLITE_OMIT_COMPILEOPTION_DIAGS",
@@ -67,6 +114,7 @@ ohos_shared_library("sqlite") {
"LOG_DUMP",
"FDSAN_ENABLE",
"HARMONY_OS",
+ "SQLITE_ENABLE_ICU",
]
cflags_c = [
"-fvisibility=hidden",
diff --git a/README.OpenSource b/README.OpenSource
index e4ece76267a67d440ff0d4419b7b98b40cacb9c6..d1cd45244a8c4fe5b0ea9b90be9bd6656a017d22 100644
--- a/README.OpenSource
+++ b/README.OpenSource
@@ -1,6 +1,6 @@
[
{
- "Name": "sqlite",
+ "Name": "SQLite",
"License": "Public Domain",
"License File": "LICENSE",
"Version Number": "3.40.1",
diff --git a/bundle.json b/bundle.json
index c2501b66ea765436bda9916299d994b5cd034fa6..197082009ac9d50d9af5ca825798fd9cda15a344 100644
--- a/bundle.json
+++ b/bundle.json
@@ -21,6 +21,7 @@
"deps": {
"components": [
"c_utils",
+ "icu",
"openssl"
],
"third_party": [
@@ -28,7 +29,10 @@
]
},
"build": {
- "sub_component": [],
+ "sub_component": [
+ "//third_party/sqlite:sqlite",
+ "//third_party/sqlite:sqliteicu"
+ ],
"inner_kits": [
{
"name": "//third_party/sqlite:sqlite",
diff --git a/include/sqlite3.h b/include/sqlite3.h
index 445640f4740f6a93ce7323c37761e930c4b8fa41..4e24bca219f7617799c1b4e974c45b7da8facce7 100644
--- a/include/sqlite3.h
+++ b/include/sqlite3.h
@@ -2120,6 +2120,21 @@ struct sqlite3_mem_methods {
** configuration setting is never used, then the default maximum is determined
** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that
** compile-time option is not set, then the default maximum is 1073741824.
+**
+** [[SQLITE_CONFIG_CORRUPTION]]
SQLITE_CONFIG_CORRUPTION
+** The SQLITE_CONFIG_CORRUPTION option is used to configure the SQLite
+** global [ corruption error].
+** (^The SQLITE_CONFIG_CORRUPTION option takes two arguments: a pointer to a
+** function with a call signature of void(*)(void*,const void*),
+** and a pointer to void. ^If the function pointer is not NULL, it is
+** invoked to process each data corruption event. ^If the
+** function pointer is NULL, no=op will do when corruption detect.
+** ^The void pointer that is the second argument to SQLITE_CONFIG_CORRUPTION is
+** passed through as the first parameter to the application-defined corruption
+** function whenever that function is invoked. ^The second parameter to
+** the corruption function is a corruption message after formatting via [sqlite3_snprintf()].
+** In a multi-threaded application, the application-defined corruption
+** function must be threadsafe.
**
*/
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
@@ -2151,6 +2166,8 @@ struct sqlite3_mem_methods {
#define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */
#define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */
#define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */
+#define SQLITE_CONFIG_CORRUPTION 30 /* xCorruption */
+#define SQLITE_CONFIG_ENABLE_ICU 31 /* boolean */
/*
** CAPI3REF: Database Connection Configuration Options
diff --git a/include/sqlite3icu.h b/include/sqlite3icu.h
new file mode 100644
index 0000000000000000000000000000000000000000..8c680bc7cab8a2aa730a4bdfa8d0f10954c392e6
--- /dev/null
+++ b/include/sqlite3icu.h
@@ -0,0 +1,50 @@
+/*
+** 2001-09-15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+*/
+#if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS)
+/************** Include sqliteicu.h in the middle of main.c ******************/
+/************** Begin file sqliteicu.h ***************************************/
+/*
+** 2008 May 26`
+**
+** 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 header file is used by programs that want to link against the
+** ICU extension. All it does is declare the sqlite3IcuInit() interface.
+*/
+#include "sqlite3sym.h"
+#include "sqlite3tokenizer.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+SQLITE_API int sqlite3IcuInit(sqlite3 *db);
+
+/************** End of sqliteicu.h *******************************************/
+/************** Continuing where we left off in main.c ***********************/
+#endif
+
+#ifdef SQLITE_ENABLE_ICU
+SQLITE_API void sqlite3Fts3IcuTokenizerModule(sqlite3_tokenizer_module const**ppModule);
+#endif
+
+#ifdef __cplusplus
+} /* end of the 'extern "C"' block */
+#endif
\ No newline at end of file
diff --git a/include/sqlite3tokenizer.h b/include/sqlite3tokenizer.h
new file mode 100644
index 0000000000000000000000000000000000000000..b1a1f097e5f4a98011323ad4afa18214f12b9f42
--- /dev/null
+++ b/include/sqlite3tokenizer.h
@@ -0,0 +1,145 @@
+/*
+** 2001-09-15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+*/
+/* TODO(shess) Only used for SQLITE_OK and SQLITE_DONE at this time.
+** If tokenizers are to be allowed to call sqlite3_*() functions, then
+** we will need a way to register the API consistently.
+*/
+/* #include "sqlite3.h" */
+
+/*
+** Structures used by the tokenizer interface. When a new tokenizer
+** implementation is registered, the caller provides a pointer to
+** an sqlite3_tokenizer_module containing pointers to the callback
+** functions that make up an implementation.
+**
+** When an fts3 table is created, it passes any arguments passed to
+** the tokenizer clause of the CREATE VIRTUAL TABLE statement to the
+** sqlite3_tokenizer_module.xCreate() function of the requested tokenizer
+** implementation. The xCreate() function in turn returns an
+** sqlite3_tokenizer structure representing the specific tokenizer to
+** be used for the fts3 table (customized by the tokenizer clause arguments).
+**
+** To tokenize an input buffer, the sqlite3_tokenizer_module.xOpen()
+** method is called. It returns an sqlite3_tokenizer_cursor object
+** that may be used to tokenize a specific input buffer based on
+** the tokenization rules supplied by a specific sqlite3_tokenizer
+** object.
+*/
+typedef struct sqlite3_tokenizer_module sqlite3_tokenizer_module;
+typedef struct sqlite3_tokenizer sqlite3_tokenizer;
+typedef struct sqlite3_tokenizer_cursor sqlite3_tokenizer_cursor;
+
+struct sqlite3_tokenizer_module {
+
+ /*
+ ** Structure version. Should always be set to 0 or 1.
+ */
+ int iVersion;
+
+ /*
+ ** Create a new tokenizer. The values in the argv[] array are the
+ ** arguments passed to the "tokenizer" clause of the CREATE VIRTUAL
+ ** TABLE statement that created the fts3 table. For example, if
+ ** the following SQL is executed:
+ **
+ ** CREATE .. USING fts3( ... , tokenizer arg1 arg2)
+ **
+ ** then argc is set to 2, and the argv[] array contains pointers
+ ** to the strings "arg1" and "arg2".
+ **
+ ** This method should return either SQLITE_OK (0), or an SQLite error
+ ** code. If SQLITE_OK is returned, then *ppTokenizer should be set
+ ** to point at the newly created tokenizer structure. The generic
+ ** sqlite3_tokenizer.pModule variable should not be initialized by
+ ** this callback. The caller will do so.
+ */
+ int (*xCreate)(
+ int argc, /* Size of argv array */
+ const char *const*argv, /* Tokenizer argument strings */
+ sqlite3_tokenizer **ppTokenizer /* OUT: Created tokenizer */
+ );
+
+ /*
+ ** Destroy an existing tokenizer. The fts3 module calls this method
+ ** exactly once for each successful call to xCreate().
+ */
+ int (*xDestroy)(sqlite3_tokenizer *pTokenizer);
+
+ /*
+ ** Create a tokenizer cursor to tokenize an input buffer. The caller
+ ** is responsible for ensuring that the input buffer remains valid
+ ** until the cursor is closed (using the xClose() method).
+ */
+ int (*xOpen)(
+ sqlite3_tokenizer *pTokenizer, /* Tokenizer object */
+ const char *pInput, int nBytes, /* Input buffer */
+ sqlite3_tokenizer_cursor **ppCursor /* OUT: Created tokenizer cursor */
+ );
+
+ /*
+ ** Destroy an existing tokenizer cursor. The fts3 module calls this
+ ** method exactly once for each successful call to xOpen().
+ */
+ int (*xClose)(sqlite3_tokenizer_cursor *pCursor);
+
+ /*
+ ** Retrieve the next token from the tokenizer cursor pCursor. This
+ ** method should either return SQLITE_OK and set the values of the
+ ** "OUT" variables identified below, or SQLITE_DONE to indicate that
+ ** the end of the buffer has been reached, or an SQLite error code.
+ **
+ ** *ppToken should be set to point at a buffer containing the
+ ** normalized version of the token (i.e. after any case-folding and/or
+ ** stemming has been performed). *pnBytes should be set to the length
+ ** of this buffer in bytes. The input text that generated the token is
+ ** identified by the byte offsets returned in *piStartOffset and
+ ** *piEndOffset. *piStartOffset should be set to the index of the first
+ ** byte of the token in the input buffer. *piEndOffset should be set
+ ** to the index of the first byte just past the end of the token in
+ ** the input buffer.
+ **
+ ** The buffer *ppToken is set to point at is managed by the tokenizer
+ ** implementation. It is only required to be valid until the next call
+ ** to xNext() or xClose().
+ */
+ /* TODO(shess) current implementation requires pInput to be
+ ** nul-terminated. This should either be fixed, or pInput/nBytes
+ ** should be converted to zInput.
+ */
+ int (*xNext)(
+ sqlite3_tokenizer_cursor *pCursor, /* Tokenizer cursor */
+ const char **ppToken, int *pnBytes, /* OUT: Normalized text for token */
+ int *piStartOffset, /* OUT: Byte offset of token in input buffer */
+ int *piEndOffset, /* OUT: Byte offset of end of token in input buffer */
+ int *piPosition /* OUT: Number of tokens returned before this one */
+ );
+
+ /***********************************************************************
+ ** Methods below this point are only available if iVersion>=1.
+ */
+
+ /*
+ ** Configure the language id of a tokenizer cursor.
+ */
+ int (*xLanguageid)(sqlite3_tokenizer_cursor *pCsr, int iLangid);
+};
+
+struct sqlite3_tokenizer {
+ const sqlite3_tokenizer_module *pModule; /* The module for this tokenizer */
+ /* Tokenizer implementations will typically add additional fields */
+};
+
+struct sqlite3_tokenizer_cursor {
+ sqlite3_tokenizer *pTokenizer; /* Tokenizer for this cursor. */
+ /* Tokenizer implementations will typically add additional fields */
+};
\ No newline at end of file
diff --git a/src/sqlite3.c b/src/sqlite3.c
index 93fd40f4c62f2aab61ada517a15683a61fc5f804..4c27691bad16bcc3487e17d09ae46508819da57d 100644
--- a/src/sqlite3.c
+++ b/src/sqlite3.c
@@ -874,6 +874,7 @@ SQLITE_API int sqlite3_exec(
#define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8))
#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8))
#define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8))
+#define SQLITE_WARNING_DUMP (SQLITE_WARNING | (2<<8))
#define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8))
#define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8))
#define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) /* internal use only */
@@ -2430,6 +2431,21 @@ struct sqlite3_mem_methods {
** configuration setting is never used, then the default maximum is determined
** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that
** compile-time option is not set, then the default maximum is 1073741824.
+**
+** [[SQLITE_CONFIG_CORRUPTION]] SQLITE_CONFIG_CORRUPTION
+** The SQLITE_CONFIG_CORRUPTION option is used to configure the SQLite
+** global [ corruption error].
+** (^The SQLITE_CONFIG_CORRUPTION option takes two arguments: a pointer to a
+** function with a call signature of void(*)(void*,const void*),
+** and a pointer to void. ^If the function pointer is not NULL, it is
+** invoked to process each data corruption event. ^If the
+** function pointer is NULL, no=op will do when corruption detect.
+** ^The void pointer that is the second argument to SQLITE_CONFIG_CORRUPTION is
+** passed through as the first parameter to the application-defined corruption
+** function whenever that function is invoked. ^The second parameter to
+** the corruption function is a corruption message after formatting via [sqlite3_snprintf()].
+** In a multi-threaded application, the application-defined corruption
+** function must be threadsafe.
**
*/
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
@@ -2461,6 +2477,8 @@ struct sqlite3_mem_methods {
#define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */
#define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */
#define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */
+#define SQLITE_CONFIG_CORRUPTION 30 /* xCorruption */
+#define SQLITE_CONFIG_ENABLE_ICU 31 /* boolean */
/*
** CAPI3REF: Database Connection Configuration Options
@@ -3193,6 +3211,16 @@ SQLITE_API int sqlite3_get_table(
);
SQLITE_API void sqlite3_free_table(char **result);
+// hw export the symbols
+#ifdef SQLITE_EXPORT_SYMBOLS
+#if defined(__GNUC__)
+# define EXPORT_SYMBOLS __attribute__ ((visibility ("default")))
+#elif defined(_MSC_VER)
+# define EXPORT_SYMBOLS __declspec(dllexport)
+#else
+# define EXPORT_SYMBOLS
+#endif
+
/*
** CAPI3REF: Formatted String Printing Functions
**
@@ -17004,6 +17032,17 @@ SQLITE_PRIVATE void sqlite3CryptFunc(sqlite3_context*,int,sqlite3_value**);
typedef void (*sqlite3_xDropTableHandle)(sqlite3*, const char*, const char*);
+#if defined(SQLITE_HAS_CODEC) && defined(SQLITE_CODEC_ATTACH_CHANGED)
+typedef struct CodecParameter {
+ int kdfIter;
+ int pageSize;
+ u8 cipher;
+ u8 hmacAlgo;
+ u8 kdfAlgo;
+ u8 reserved;
+} CodecParameter;
+#endif
+
/*
** Each database connection is an instance of the following structure.
*/
@@ -17153,6 +17192,9 @@ struct sqlite3 {
char *mDropSchemaName;
sqlite3_xDropTableHandle xDropTableHandle; /* User drop table callback */
#endif
+#if defined(SQLITE_HAS_CODEC) && defined(SQLITE_CODEC_ATTACH_CHANGED)
+ CodecParameter codecParm;
+#endif
};
/*
@@ -19527,6 +19569,8 @@ struct Sqlite3Config {
int iOnceResetThreshold; /* When to reset OP_Once counters */
u32 szSorterRef; /* Min size in bytes to use sorter-refs */
unsigned int iPrngSeed; /* Alternative fixed seed for the PRNG */
+ void (*xCorruption)(void *, const void *);
+ void *pCorruptionArg;
/* vvvv--- must be last ---vvv */
#ifdef SQLITE_DEBUG
sqlite3_int64 aTune[SQLITE_NTUNE]; /* Tuning parameters */
@@ -19778,6 +19822,57 @@ SQLITE_PRIVATE Window *sqlite3WindowAssemble(Parse*, Window*, ExprList*, ExprLis
} \
}
+#define SQLITE_PRINT_CORRUPT_SIZE (SQLITE_PRINT_BUF_SIZE * 2)
+
+#define SQLITE_CORRUPT_CONTEXT(N,P,T,O,S,M,R) \
+ { .nPage=(N), .pgno=(P), .type=(T), \
+ .zoneRange={(O),(S)}, .zMsg=(M), .reservedArgs=(R) }
+
+typedef struct {
+ int offset;
+ size_t size;
+}sqlite3CorruptRange;
+
+typedef enum {
+ CORRUPT_TYPE_PAGE_BTREE_LEAF,
+ CORRUPT_TYPE_PAGE_BTREE_INTERIOR,
+ CORRUPT_TYPE_PAGE_INDEX_LEAF,
+ CORRUPT_TYPE_PAGE_INDEX_INTERIOR,
+ CORRUPT_TYPE_PAGE_OVERFLOW,
+ CORRUPT_TYPE_PAGE_PTR_MAP,
+ CORRUPT_TYPE_PAGE_FREE_LIST,
+ CORRUPT_TYPE_FRAME_WAL,
+ CORRUPT_TYPE_ENTRY_JOURNAL,
+ CORRUPT_TYPE_VDBE,
+ CORRUPT_TYPE_FILE_HEADER,
+ CORRUPT_TYPE_UNKOWN,
+} CorruptType;
+
+typedef struct {
+ size_t nPage; /* Number of pages */
+ unsigned int pgno; /* Page number for corrupted page */
+ CorruptType type;
+ sqlite3CorruptRange zoneRange;
+ const char *zMsg;
+ void *reservedArgs;
+}sqlite3CorruptContext;
+
+// Encode buffer with base16, return size after encode
+static size_t sqlite3base16Encode(const unsigned char *buffer, size_t bufSize, char *encodeBuf, size_t encodeBufSize)
+{
+ if (buffer == NULL || bufSize == 0 || encodeBuf == NULL || encodeBufSize == 0) {
+ return 0;
+ }
+ static const char base16Code[] = "0123456789ABCDEF";
+ size_t i = 0;
+ for (; i < bufSize && (i * 2 < encodeBufSize - 1); i++) {
+ *encodeBuf++ = base16Code[(buffer[i] >> 4) & 0x0F];
+ *encodeBuf++ = base16Code[buffer[i] & 0x0F];
+ }
+ *encodeBuf = '\0';
+ return i * 2;
+}
+
/*
** The SQLITE_*_BKPT macros are substitutes for the error codes with
** the same name but without the _BKPT suffix. These macros invoke
@@ -19786,10 +19881,11 @@ SQLITE_PRIVATE Window *sqlite3WindowAssemble(Parse*, Window*, ExprList*, ExprLis
** to set a debugger breakpoint.
*/
SQLITE_PRIVATE int sqlite3ReportError(int iErr, int lineno, const char *zType);
-SQLITE_PRIVATE int sqlite3CorruptError(int);
+SQLITE_PRIVATE int sqlite3CorruptError(int lineno, sqlite3CorruptContext *context);
SQLITE_PRIVATE int sqlite3MisuseError(int);
SQLITE_PRIVATE int sqlite3CantopenError(int);
-#define SQLITE_CORRUPT_BKPT sqlite3CorruptError(__LINE__)
+#define SQLITE_CORRUPT_REPORT(context) sqlite3CorruptError(__LINE__,(context))
+#define SQLITE_CORRUPT_BKPT sqlite3CorruptError(__LINE__,NULL)
#define SQLITE_MISUSE_BKPT sqlite3MisuseError(__LINE__)
#define SQLITE_CANTOPEN_BKPT sqlite3CantopenError(__LINE__)
#ifdef SQLITE_DEBUG
@@ -19801,12 +19897,13 @@ SQLITE_PRIVATE int sqlite3IoerrnomemError(int);
# define SQLITE_NOMEM_BKPT SQLITE_NOMEM
# define SQLITE_IOERR_NOMEM_BKPT SQLITE_IOERR_NOMEM
#endif
-#if defined(SQLITE_DEBUG) || defined(SQLITE_ENABLE_CORRUPT_PGNO)
+#if (defined(SQLITE_DEBUG) || defined(SQLITE_ENABLE_CORRUPT_PGNO))
SQLITE_PRIVATE int sqlite3CorruptPgnoError(int,Pgno);
-# define SQLITE_CORRUPT_PGNO(P) sqlite3CorruptPgnoError(__LINE__,(P))
+# define SQLITE_CORRUPT_PGNO(P,context) sqlite3CorruptPgnoError(__LINE__,(P))
#else
-# define SQLITE_CORRUPT_PGNO(P) sqlite3CorruptError(__LINE__)
+# define SQLITE_CORRUPT_PGNO(P,context) sqlite3CorruptError(__LINE__,(context))
#endif
+# define SQLITE_CORRUPT_REPORT_PGNO(context) SQLITE_CORRUPT_PGNO((context)->pgno,(context))
/*
** FTS3 and FTS4 both require virtual table support
@@ -20128,6 +20225,9 @@ SQLITE_PRIVATE int sqlite3CodecQueryParameters(sqlite3*,const char*,const char
#else
# define sqlite3CodecQueryParameters(A,B,C) 0
#endif
+#if defined(SQLITE_HAS_CODEC) && defined(SQLITE_CODEC_ATTACH_CHANGED)
+SQLITE_PRIVATE void sqlite3CodecResetParameters(CodecParameter *p);
+#endif
SQLITE_PRIVATE Btree *sqlite3DbNameToBtree(sqlite3*,const char*);
#ifdef SQLITE_UNTESTABLE
@@ -22282,6 +22382,8 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = {
0x7ffffffe, /* iOnceResetThreshold */
SQLITE_DEFAULT_SORTERREF_SIZE, /* szSorterRef */
0, /* iPrngSeed */
+ 0, /* xCorruption */
+ 0, /* pCorruptionArg */
#ifdef SQLITE_DEBUG
{0,0,0,0,0,0} /* aTune */
#endif
@@ -36767,6 +36869,44 @@ SQLITE_PRIVATE int sqlite3KvvfsInit(void){
#endif
/************** End of os_kv.c ***********************************************/
+/************** Define dump function *****************************************/
+#if SQLITE_OS_UNIX
+#define DB_LOCK_NUM 3
+#define WAL_LOCK_NUM 9
+// 8 wal lock, 1 SHM_DMS lock, 1 TRX lock
+#define MAX_LOCK_NUM (WAL_LOCK_NUM + 1)
+#define SHM_DMS_IDX (WAL_LOCK_NUM - 1)
+#define TRX_LOCK_IDX WAL_LOCK_NUM
+#define LOCK_BY_PROCESS 1
+#define LOCK_BY_THREAD 0
+#define DUMP_BUF_MAX_LEN 180
+#define DUMP_MAX_STR_LEN 21
+
+typedef struct LocalLockStatus {
+ u8 busyLockIdx;
+ u8 busyLockType;
+ u8 lockByProcess;
+ u8 reserved;
+ u32 lockLen;
+ u32 busyLine;
+ u8 lockStatus[MAX_LOCK_NUM]; // last index is trx lock
+} LocalLockStatus;
+__thread LocalLockStatus g_lockStatus = {0};
+
+#define MARK_LAST_BUSY_LINE(rc) {if ((rc)==SQLITE_BUSY || (rc) == SQLITE_LOCKED) g_lockStatus.busyLine = __LINE__;}
+static void ResetLockStatus(void);
+static void TryRecordTid(int *tidBuf, int ofs, int lockLen);
+static void TryClearTid(int *tidBuf, int ofs, int lockLen);
+static void MarkLockBusy(u32 lockIdx, u32 lockLen, u8 lockType, u8 lockByProcess);
+static void MarkLockStatus(u32 lockIdx, u32 lockLen, u8 lockType);
+static void MarkLockStatusByRc(int rc, u32 lockIdx, u32 lockLen, u8 lockType, u8 lockByProcess);
+#else
+#define MARK_LAST_BUSY_LINE(rc)
+#define ResetLockStatus(void)
+#define MarkLockBusy(A, B, C, D)
+#define MarkLockStatusByRc(A, B, C, D, E)
+#endif
+/************** End define dump function *************************************/
/************** Begin file os_unix.c *****************************************/
/*
** 2004 May 22
@@ -38541,6 +38681,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){
if( pFile->eFileLock>=eFileLock ){
OSTRACE(("LOCK %d %s ok (already held) (unix)\n", pFile->h,
azFileLock(eFileLock)));
+ MarkLockStatus(TRX_LOCK_IDX, 1, eFileLock);
return SQLITE_OK;
}
@@ -38565,6 +38706,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){
(pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK))
){
rc = SQLITE_BUSY;
+ MarkLockBusy(TRX_LOCK_IDX, 1, eFileLock, LOCK_BY_THREAD);
goto end_lock;
}
@@ -38580,6 +38722,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){
pFile->eFileLock = SHARED_LOCK;
pInode->nShared++;
pInode->nLock++;
+ MarkLockStatus(TRX_LOCK_IDX, 1, eFileLock);
goto end_lock;
}
@@ -38601,11 +38744,11 @@ static int unixLock(sqlite3_file *id, int eFileLock){
if( rc!=SQLITE_BUSY ){
storeLastErrno(pFile, tErrno);
}
+ MarkLockStatusByRc(rc, TRX_LOCK_IDX, 1, SHARED_LOCK, LOCK_BY_PROCESS);
goto end_lock;
}
}
-
/* If control gets to this point, then actually go ahead and make
** operating system calls for the specified lock.
*/
@@ -38621,7 +38764,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){
tErrno = errno;
rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
}
-
+ MarkLockStatusByRc(rc, TRX_LOCK_IDX, 1, SHARED_LOCK, LOCK_BY_PROCESS);
/* Drop the temporary PENDING lock */
lock.l_start = PENDING_BYTE;
lock.l_len = 1L;
@@ -38646,6 +38789,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){
/* We are trying for an exclusive lock but another thread in this
** same process is still holding a shared lock. */
rc = SQLITE_BUSY;
+ MarkLockBusy(TRX_LOCK_IDX, 1, eFileLock, LOCK_BY_THREAD);
}else{
/* The request was for a RESERVED or EXCLUSIVE lock. It is
** assumed that there is a SHARED or greater lock on the file
@@ -38670,6 +38814,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){
storeLastErrno(pFile, tErrno);
}
}
+ MarkLockStatusByRc(rc, TRX_LOCK_IDX, 1, eFileLock, LOCK_BY_PROCESS);
}
@@ -38867,7 +39012,6 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
pFile->eFileLock = NO_LOCK;
}
}
-
/* Decrement the count of locks against this same file. When the
** count reaches zero, close any other file descriptors whose close
** was deferred because of outstanding locks.
@@ -41179,6 +41323,7 @@ struct unixShmNode {
int nRef; /* Number of unixShm objects pointing to this */
unixShm *pFirst; /* All unixShm objects pointing to this */
int aLock[SQLITE_SHM_NLOCK]; /* # shared locks on slot, -1==excl lock */
+ int aLockTid[SQLITE_SHM_NLOCK];
#ifdef SQLITE_DEBUG
u8 exclMask; /* Mask of exclusive locks held */
u8 sharedMask; /* Mask of shared locks held */
@@ -41421,6 +41566,8 @@ static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){
rc = SQLITE_READONLY_CANTINIT;
}else{
rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1);
+ MarkLockStatusByRc(rc, SHM_DMS_IDX, 1, EXCLUSIVE_LOCK, LOCK_BY_PROCESS);
+ MARK_LAST_BUSY_LINE(rc);
/* The first connection to attach must truncate the -shm file. We
** truncate to 3 bytes (an arbitrary small number, less than the
** -shm header size) rather than 0 as a system debugging aid, to
@@ -41432,11 +41579,15 @@ static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){
}
}else if( lock.l_type==F_WRLCK ){
rc = SQLITE_BUSY;
+ MarkLockStatusByRc(rc, SHM_DMS_IDX, 1, SHARED_LOCK, LOCK_BY_PROCESS);
+ MARK_LAST_BUSY_LINE(rc);
}
if( rc==SQLITE_OK ){
assert( lock.l_type==F_UNLCK || lock.l_type==F_RDLCK );
rc = unixShmSystemLock(pDbFd, F_RDLCK, UNIX_SHM_DMS, 1);
+ MarkLockStatusByRc(rc, SHM_DMS_IDX, 1, SHARED_LOCK, LOCK_BY_PROCESS);
+ MARK_LAST_BUSY_LINE(rc);
}
return rc;
}
@@ -41809,19 +41960,19 @@ static int unixShmLock(
p = pDbFd->pShm;
if( p==0 ){
#ifdef LOG_DUMP
- sqlite3_log(SQLITE_IOERR_SHMLOCK, "unixShmLock-pShm, fd[%d], ofst[%d], n[%d], flags[%d]", ofst, n, flags);
+ sqlite3_log(SQLITE_IOERR_SHMLOCK, "unixShmLock-pShm, fd[%d], ofst[%d], n[%d], flags[%d]", pDbFd->h, ofst, n, flags);
#endif
return SQLITE_IOERR_SHMLOCK;
}
pShmNode = p->pShmNode;
if( NEVER(pShmNode==0) ){
#ifdef LOG_DUMP
- sqlite3_log(SQLITE_IOERR_SHMLOCK, "unixShmLock-pShmNode, fd[%d], ofst[%d], n[%d], flags[%d]", ofst, n, flags);
+ sqlite3_log(SQLITE_IOERR_SHMLOCK, "unixShmLock-pShmNode, fd[%d], ofst[%d], n[%d], flags[%d]", pDbFd->h, ofst, n, flags);
#endif
return SQLITE_IOERR_SHMLOCK;
}
aLock = pShmNode->aLock;
-
+ int *aLockTid = pShmNode->aLockTid;
assert( pShmNode==pDbFd->pInode->pShmNode );
assert( pShmNode->pInode==pDbFd->pInode );
assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK );
@@ -41860,6 +42011,7 @@ static int unixShmLock(
assert( n>1 || mask==(1<pShmMutex);
assert( assertLockingArrayOk(pShmNode) );
+ u8 useProcessLock = LOCK_BY_THREAD;
if( flags & SQLITE_SHM_UNLOCK ){
if( (p->exclMask|p->sharedMask) & mask ){
int ii;
@@ -41870,21 +42022,22 @@ static int unixShmLock(
bUnlock = 0;
}
}
-
if( bUnlock ){
rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n);
if( rc==SQLITE_OK ){
memset(&aLock[ofst], 0, sizeof(int)*n);
}
+ useProcessLock = LOCK_BY_PROCESS;
}else if( ALWAYS(p->sharedMask & (1<1 );
aLock[ofst]--;
}
-
+ MarkLockStatusByRc(rc, ofst, n, NO_LOCK, useProcessLock);
/* Undo the local locks */
if( rc==SQLITE_OK ){
p->exclMask &= ~mask;
p->sharedMask &= ~mask;
+ TryClearTid(aLockTid, ofst, n);
}
}
}else if( flags & SQLITE_SHM_SHARED ){
@@ -41895,12 +42048,14 @@ static int unixShmLock(
rc = SQLITE_BUSY;
}else if( aLock[ofst]==0 ){
rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n);
+ useProcessLock = LOCK_BY_PROCESS;
}
-
+ MarkLockStatusByRc(rc, ofst, n, SHARED_LOCK, useProcessLock);
/* Get the local shared locks */
if( rc==SQLITE_OK ){
p->sharedMask |= mask;
aLock[ofst]++;
+ TryRecordTid(aLockTid, ofst, n);
}
}
}else{
@@ -41919,14 +42074,17 @@ static int unixShmLock(
** also update the in-memory values. */
if( rc==SQLITE_OK ){
rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n);
+ useProcessLock = LOCK_BY_PROCESS;
if( rc==SQLITE_OK ){
assert( (p->sharedMask & mask)==0 );
p->exclMask |= mask;
for(ii=ofst; iipShmMutex);
@@ -58537,7 +58695,7 @@ static int readDbPage(PgHdr *pPg){
assert( isOpen(pPager->fd) );
if( pagerUseWal(pPager) ){
- rc = sqlite3WalFindFrame(pPager->pWal, pPg->pgno, &iFrame);
+ rc = sqlite3WalFindFrame(pPager->pWal, pPg->pgno, &iFrame); // find in wal-index
if( rc ) return rc;
}
if( iFrame ){
@@ -59794,6 +59952,7 @@ static int syncJournal(Pager *pPager, int newHdr){
assert( !pagerUseWal(pPager) );
rc = sqlite3PagerExclusiveLock(pPager);
+ MARK_LAST_BUSY_LINE(rc);
if( rc!=SQLITE_OK ) return rc;
if( !pPager->noSync ){
@@ -60703,6 +60862,7 @@ static int hasHotJournal(Pager *pPager, int *pExists){
sqlite3OsDelete(pVfs, pPager->zJournal, 0);
if( !pPager->exclusiveMode ) pagerUnlockDb(pPager, SHARED_LOCK);
}
+ MARK_LAST_BUSY_LINE(rc);
sqlite3EndBenignMalloc();
}else{
/* The journal file exists and no other connection has a reserved
@@ -60792,6 +60952,7 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
assert( pPager->tempFile==0 || pPager->eLock==EXCLUSIVE_LOCK );
rc = pager_wait_on_lock(pPager, SHARED_LOCK);
+ MARK_LAST_BUSY_LINE(rc);
if( rc!=SQLITE_OK ){
assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK );
goto failed;
@@ -60828,6 +60989,7 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
** downgraded to SHARED_LOCK before this function returns.
*/
rc = pagerLockDb(pPager, EXCLUSIVE_LOCK);
+ MARK_LAST_BUSY_LINE(rc);
if( rc!=SQLITE_OK ){
goto failed;
}
@@ -61070,7 +61232,12 @@ static int getPageNormal(
assert( assert_pager_state(pPager) );
assert( pPager->hasHeldSharedLock==1 );
- if( pgno==0 ) return SQLITE_CORRUPT_BKPT;
+ if( pgno==0 ) {
+ const char *zMsg = "pgno should not be 0";
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPager->dbSize, 0, CORRUPT_TYPE_UNKOWN,
+ -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_REPORT(&context);
+ }
pBase = sqlite3PcacheFetch(pPager->pPCache, pgno, 3);
if( pBase==0 ){
pPg = 0;
@@ -61102,7 +61269,11 @@ static int getPageNormal(
** (2) Never try to fetch the locking page
*/
if( pgno==PAGER_SJ_PGNO(pPager) ){
- rc = SQLITE_CORRUPT_BKPT;
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "try fetching the locking page(%u)", pgno);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPager->dbSize, pgno, CORRUPT_TYPE_UNKOWN,
+ -1, 0, zMsg, NULL);
+ rc = SQLITE_CORRUPT_REPORT(&context);
goto pager_acquire_err;
}
@@ -61184,7 +61355,10 @@ static int getPageMMap(
** test in the previous statement, and avoid testing pgno==0 in the
** common case where pgno is large. */
if( pgno<=1 && pgno==0 ){
- return SQLITE_CORRUPT_BKPT;
+ const char *zMsg = "pgno should not be 0";
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPager->dbSize, 0, CORRUPT_TYPE_UNKOWN,
+ -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_REPORT(&context);
}
assert( pPager->eState>=PAGER_READER );
assert( assert_pager_state(pPager) );
@@ -61442,6 +61616,7 @@ SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory
*/
if( pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, -1) ){
rc = pagerLockDb(pPager, EXCLUSIVE_LOCK);
+ MARK_LAST_BUSY_LINE(rc);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -61454,6 +61629,7 @@ SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory
** holds the write-lock. If possible, the upper layer will call it.
*/
rc = sqlite3WalBeginWriteTransaction(pPager->pWal);
+ MARK_LAST_BUSY_LINE(rc);
}else{
/* Obtain a RESERVED lock on the database file. If the exFlag parameter
** is true, then immediately upgrade this to an EXCLUSIVE lock. The
@@ -61461,8 +61637,10 @@ SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory
** lock, but not when obtaining the RESERVED lock.
*/
rc = pagerLockDb(pPager, RESERVED_LOCK);
+ MARK_LAST_BUSY_LINE(rc);
if( rc==SQLITE_OK && exFlag ){
rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
+ MARK_LAST_BUSY_LINE(rc);
}
}
@@ -62767,7 +62945,11 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i
if( pPgOld ){
if( NEVER(pPgOld->nRef>1) ){
sqlite3PagerUnrefNotNull(pPgOld);
- return SQLITE_CORRUPT_BKPT;
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "page(%u) should be no references, ref cnt:%d", pgno, (int)pPgOld->nRef);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPager->dbSize, pgno, CORRUPT_TYPE_UNKOWN,
+ -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_REPORT(&context);
}
pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC);
if( pPager->tempFile ){
@@ -62963,6 +63145,7 @@ SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){
if( pPager->eState==PAGER_READER ){
assert( rc==SQLITE_OK );
rc = pagerLockDb(pPager, RESERVED_LOCK);
+ MARK_LAST_BUSY_LINE(rc);
}
if( rc==SQLITE_OK ){
sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
@@ -63127,6 +63310,7 @@ static int pagerOpenWal(Pager *pPager){
*/
if( pPager->exclusiveMode ){
rc = pagerExclusiveLock(pPager);
+ MARK_LAST_BUSY_LINE(rc);
}
/* Open the connection to the log file. If this operation fails,
@@ -63210,6 +63394,7 @@ SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3 *db){
if( !pPager->pWal ){
int logexists = 0;
rc = pagerLockDb(pPager, SHARED_LOCK);
+ MARK_LAST_BUSY_LINE(rc);
if( rc==SQLITE_OK ){
rc = sqlite3OsAccess(
pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &logexists
@@ -63225,6 +63410,7 @@ SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3 *db){
*/
if( rc==SQLITE_OK && pPager->pWal ){
rc = pagerExclusiveLock(pPager);
+ MARK_LAST_BUSY_LINE(rc);
if( rc==SQLITE_OK ){
rc = sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags,
pPager->pageSize, (u8*)pPager->pTmpSpace);
@@ -64531,7 +64717,13 @@ static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){
/* Write the aPgno[] array entry and the hash-table slot. */
nCollide = idx;
for(iKey=walHash(iPage); sLoc.aHash[iKey]; iKey=walNextHash(iKey)){
- if( (nCollide--)==0 ) return SQLITE_CORRUPT_BKPT;
+ if( (nCollide--)==0 ){
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "no place for page(%u) to map into WAL, idx:%d", iPage, idx);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pWal->hdr.nPage, iPage, CORRUPT_TYPE_FRAME_WAL,
+ -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_REPORT(&context);
+ }
}
sLoc.aPgno[idx-1] = iPage;
AtomicStore(&sLoc.aHash[iKey], (ht_slot)idx);
@@ -64599,6 +64791,7 @@ static int walIndexRecover(Wal *pWal){
iLock = WAL_ALL_BUT_WRITE + pWal->ckptLock;
rc = walLockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock);
if( rc ){
+ MARK_LAST_BUSY_LINE(rc);
return rc;
}
@@ -65436,6 +65629,7 @@ static int walCheckpoint(
if( mxSafeFrame>y ){
assert( y<=pWal->hdr.mxFrame );
rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1);
+ MARK_LAST_BUSY_LINE(rc);
if( rc==SQLITE_OK ){
u32 iMark = (i==1 ? mxSafeFrame : READMARK_NOT_USED);
AtomicStore(pInfo->aReadMark+i, iMark);
@@ -65479,7 +65673,13 @@ static int walCheckpoint(
** database plus the amount of data in the wal file, plus the
** maximum size of the pending-byte page (65536 bytes), then
** must be corruption somewhere. */
- rc = SQLITE_CORRUPT_BKPT;
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg,
+ "final db size unexpected,nSize=%lld,mxFrame=%u,pageSize=%d,nReq=%lld",
+ nSize, pWal->hdr.mxFrame, szPage, nReq);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(mxPage, 0, CORRUPT_TYPE_FRAME_WAL,
+ -1, 0, zMsg, NULL);
+ rc = SQLITE_CORRUPT_REPORT(&context);
}else{
sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT,&nReq);
}
@@ -65528,6 +65728,8 @@ static int walCheckpoint(
/* Release the reader lock held while backfilling */
walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1);
+ } else {
+ MARK_LAST_BUSY_LINE(rc);
}
if( rc==SQLITE_BUSY ){
@@ -65546,11 +65748,13 @@ static int walCheckpoint(
assert( pWal->writeLock );
if( pInfo->nBackfillhdr.mxFrame ){
rc = SQLITE_BUSY;
+ MARK_LAST_BUSY_LINE(rc);
}else if( eMode>=SQLITE_CHECKPOINT_RESTART ){
u32 salt1;
sqlite3_randomness(4, &salt1);
assert( pInfo->nBackfill==pWal->hdr.mxFrame );
rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1);
+ MARK_LAST_BUSY_LINE(rc);
if( rc==SQLITE_OK ){
if( eMode==SQLITE_CHECKPOINT_TRUNCATE ){
/* IMPLEMENTATION-OF: R-44699-57140 This mode works the same way as
@@ -65648,8 +65852,9 @@ SQLITE_PRIVATE int sqlite3WalClose(
walLimitSize(pWal, 0);
}
}
+ } else {
+ MARK_LAST_BUSY_LINE(rc);
}
-
walIndexClose(pWal, isDelete);
sqlite3OsClose(pWal->pWalFd);
if( isDelete ){
@@ -65802,6 +66007,7 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){
walUnlockShared(pWal, WAL_WRITE_LOCK);
rc = SQLITE_READONLY_RECOVERY;
}
+ MARK_LAST_BUSY_LINE(rc);
}else{
int bWriteLock = pWal->writeLock;
if( bWriteLock || SQLITE_OK==(rc = walLockWriter(pWal)) ){
@@ -65822,6 +66028,7 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){
walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
}
}
+ MARK_LAST_BUSY_LINE(rc);
}
}
@@ -65841,6 +66048,7 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){
** writer truncated the WAL out from under it. If that happens, it
** indicates that a writer has fixed the SHM file for us, so retry */
if( rc==SQLITE_IOERR_SHORT_READ ) rc = WAL_RETRY;
+ MARK_LAST_BUSY_LINE(rc);
}
pWal->exclusiveMode = WAL_NORMAL_MODE;
}
@@ -66072,7 +66280,7 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){
** so it takes care to hold an exclusive lock on the corresponding
** WAL_READ_LOCK() while changing values.
*/
-static void printLockInfoUsingWal(Wal *pWal);
+static void DumpLocksByWal(Wal *pWal);
static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */
u32 mxReadMark; /* Largest aReadMark[] value */
@@ -66111,7 +66319,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
}
if( cnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39;
#if SQLITE_OS_UNIX
- if( cnt>=15 ) printLockInfoUsingWal(pWal);
+ if( cnt>=15 ) DumpLocksByWal(pWal);
#endif /* SQLITE_OS_UNIX */
sqlite3OsSleep(pWal->pVfs, nDelay);
}
@@ -66139,11 +66347,14 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
** must be zeroed before the requested page is returned.
*/
rc = WAL_RETRY;
+ MARK_LAST_BUSY_LINE(SQLITE_BUSY);
}else if( SQLITE_OK==(rc = walLockShared(pWal, WAL_RECOVER_LOCK)) ){
walUnlockShared(pWal, WAL_RECOVER_LOCK);
rc = WAL_RETRY;
+ MARK_LAST_BUSY_LINE(SQLITE_BUSY);
}else if( rc==SQLITE_BUSY ){
rc = SQLITE_BUSY_RECOVERY;
+ MARK_LAST_BUSY_LINE(SQLITE_BUSY);
}
}
if( rc!=SQLITE_OK ){
@@ -66166,6 +66377,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
** and can be safely ignored.
*/
rc = walLockShared(pWal, WAL_READ_LOCK(0));
+ MARK_LAST_BUSY_LINE(SQLITE_BUSY);
walShmBarrier(pWal);
if( rc==SQLITE_OK ){
if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){
@@ -66183,6 +66395,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
** it finished. Leaving a corrupt image in the database file.
*/
walUnlockShared(pWal, WAL_READ_LOCK(0));
+ MARK_LAST_BUSY_LINE(SQLITE_BUSY);
return WAL_RETRY;
}
pWal->readLock = 0;
@@ -66227,6 +66440,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
}else if( rc!=SQLITE_BUSY ){
return rc;
}
+ MARK_LAST_BUSY_LINE(rc);
}
}
if( mxI==0 ){
@@ -66236,6 +66450,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
if( rc ){
+ MARK_LAST_BUSY_LINE(rc);
return rc==SQLITE_BUSY ? WAL_RETRY : rc;
}
/* Now that the read-lock has been obtained, check that neither the
@@ -66278,6 +66493,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
|| memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
){
walUnlockShared(pWal, WAL_READ_LOCK(mxI));
+ MARK_LAST_BUSY_LINE(rc);
return WAL_RETRY;
}else{
assert( mxReadMark<=pWal->hdr.mxFrame );
@@ -66404,7 +66620,7 @@ SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
(void)walEnableBlocking(pWal);
rc = walLockShared(pWal, WAL_CKPT_LOCK);
walDisableBlocking(pWal);
-
+ MARK_LAST_BUSY_LINE(rc);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -66571,7 +66787,11 @@ SQLITE_PRIVATE int sqlite3WalFindFrame(
iRead = iFrame;
}
if( (nCollide--)==0 ){
- return SQLITE_CORRUPT_BKPT;
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "mis-match page(%u) to map into WAL", pgno);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pWal->hdr.nPage, pgno, CORRUPT_TYPE_FRAME_WAL,
+ -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_REPORT(&context);
}
iKey = walNextHash(iKey);
}
@@ -67209,9 +67429,9 @@ SQLITE_PRIVATE int sqlite3WalFrames(
WALTRACE(("WAL%p: frame write %s\n", pWal, rc ? "failed" : "ok"));
return rc;
}
-
+#ifdef LOG_DUMP
static sqlite3_int64 g_lastCkptTime = 0;
-
+#endif
/*
** This routine is called to implement sqlite3_wal_checkpoint() and
** related interfaces.
@@ -67241,7 +67461,7 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint(
assert( pWal->ckptLock==0 );
assert( pWal->writeLock==0 );
-
+ ResetLockStatus();
/* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked
** in the SQLITE_CHECKPOINT_PASSIVE mode. */
assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 );
@@ -67263,6 +67483,7 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint(
** it will not be invoked in this case.
*/
rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
+ MARK_LAST_BUSY_LINE(rc);
testcase( rc==SQLITE_BUSY );
testcase( rc!=SQLITE_OK && xBusy2!=0 );
if( rc==SQLITE_OK ){
@@ -67279,6 +67500,7 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint(
*/
if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
rc = walBusyLock(pWal, xBusy2, pBusyArg, WAL_WRITE_LOCK, 1);
+ MARK_LAST_BUSY_LINE(rc);
if( rc==SQLITE_OK ){
pWal->writeLock = 1;
}else if( rc==SQLITE_BUSY ){
@@ -67304,7 +67526,12 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint(
if( rc==SQLITE_OK ){
if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){
- rc = SQLITE_CORRUPT_BKPT;
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "mis-match between pageSize=%d and bufferSize=%d, mxFrame=%u",
+ walPagesize(pWal),nBuf,pWal->hdr.mxFrame);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pWal->hdr.nPage, 0, CORRUPT_TYPE_FRAME_WAL,
+ -1, 0, zMsg, NULL);
+ rc = SQLITE_CORRUPT_REPORT(&context);
}else{
rc = walCheckpoint(pWal, db, eMode2, xBusy2, pBusyArg, sync_flags, zBuf);
}
@@ -68751,7 +68978,7 @@ static int checkDbHeaderValid(sqlite3 *db, int iDbpage, u8 *zBuf){
** with the page number and filename associated with the (MemPage*).
*/
#ifdef SQLITE_DEBUG
-int corruptPageError(int lineno, MemPage *p){
+int corruptPageError(int lineno, MemPage *p, sqlite3CorruptContext *context){
char *zMsg;
sqlite3BeginBenignMalloc();
zMsg = sqlite3_mprintf("database corruption page %d of %s",
@@ -68762,11 +68989,11 @@ int corruptPageError(int lineno, MemPage *p){
sqlite3ReportError(SQLITE_CORRUPT, lineno, zMsg);
}
sqlite3_free(zMsg);
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_REPORT(context);
}
-# define SQLITE_CORRUPT_PAGE(pMemPage) corruptPageError(__LINE__, pMemPage)
+# define SQLITE_CORRUPT_PAGE(context,pMemPage) corruptPageError(__LINE__, (pMemPage),(context))
#else
-# define SQLITE_CORRUPT_PAGE(pMemPage) SQLITE_CORRUPT_PGNO(pMemPage->pgno)
+# define SQLITE_CORRUPT_PAGE(context,pMemPage) SQLITE_CORRUPT_PGNO((pMemPage)->pgno,(context))
#endif
#ifndef SQLITE_OMIT_SHARED_CACHE
@@ -69444,7 +69671,12 @@ static int btreeMoveto(
if( pIdxKey==0 ) return SQLITE_NOMEM_BKPT;
sqlite3VdbeRecordUnpack(pKeyInfo, (int)nKey, pKey, pIdxKey);
if( pIdxKey->nField==0 || pIdxKey->nField>pKeyInfo->nAllField ){
- rc = SQLITE_CORRUPT_BKPT;
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "unexpected fields in total:%u, should != 0 and < %u",
+ pIdxKey->nField,pKeyInfo->nAllField);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pCur->pBt->nPage, 0, CORRUPT_TYPE_PAGE_BTREE_LEAF,
+ -1, 0, zMsg, NULL);
+ rc = SQLITE_CORRUPT_REPORT(&context);
}else{
rc = sqlite3BtreeIndexMoveto(pCur, pIdxKey, pRes);
}
@@ -69624,7 +69856,7 @@ static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){
assert( 0==PTRMAP_ISPAGE(pBt, PENDING_BYTE_PAGE(pBt)) );
assert( pBt->autoVacuum );
- if( key==0 ){
+ if( key==0 ){ // The pgno of each entry on ptrmap page starts from 3, an unexpected pgno indicates data corrupted
*pRC = SQLITE_CORRUPT_BKPT;
return;
}
@@ -69638,12 +69870,24 @@ static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){
/* The first byte of the extra data is the MemPage.isInit byte.
** If that byte is set, it means this page is also being used
** as a btree page. */
- *pRC = SQLITE_CORRUPT_BKPT;
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ char xBuffer[SQLITE_PRINT_BUF_SIZE] = {0};
+ (void)sqlite3base16Encode((unsigned char *)sqlite3PagerGetExtra(pDbPage), 8, xBuffer, sizeof(xBuffer));
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "page(%u) been initialized before as a btree page, base16:%s",
+ iPtrmap, xBuffer);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pBt->nPage, iPtrmap, CORRUPT_TYPE_PAGE_PTR_MAP,
+ -1, 0, zMsg, NULL);
+ *pRC = SQLITE_CORRUPT_REPORT(&context);
goto ptrmap_exit;
}
offset = PTRMAP_PTROFFSET(iPtrmap, key);
if( offset<0 ){
- *pRC = SQLITE_CORRUPT_BKPT;
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "unexpect offset in ptrmap page(%u), target:%u, page usableSize=%u",
+ iPtrmap, key, pBt->usableSize);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pBt->nPage, iPtrmap, CORRUPT_TYPE_PAGE_PTR_MAP,
+ -1, 0, zMsg, NULL);
+ *pRC = SQLITE_CORRUPT_REPORT(&context);
goto ptrmap_exit;
}
assert( offset <= (int)pBt->usableSize-5 );
@@ -69688,7 +69932,12 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
offset = PTRMAP_PTROFFSET(iPtrmap, key);
if( offset<0 ){
sqlite3PagerUnref(pDbPage);
- return SQLITE_CORRUPT_BKPT;
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "unexpect offset in ptrmap page(%d), target:%u, page usableSize=%u",
+ iPtrmap, key, pBt->usableSize);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pBt->nPage, iPtrmap, CORRUPT_TYPE_PAGE_PTR_MAP,
+ -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_REPORT(&context);
}
assert( offset <= (int)pBt->usableSize-5 );
assert( pEType!=0 );
@@ -69696,7 +69945,15 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
if( pPgno ) *pPgno = get4byte(&pPtrmap[offset+1]);
sqlite3PagerUnref(pDbPage);
- if( *pEType<1 || *pEType>5 ) return SQLITE_CORRUPT_PGNO(iPtrmap);
+ if( *pEType<1 || *pEType>5 ){
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ char xBuffer[SQLITE_PRINT_BUF_SIZE] = {0}; // 5 bytes for each entry on ptrmap page
+ (void)sqlite3base16Encode(pPtrmap, 5, xBuffer, sizeof(xBuffer));
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "unexpect entry type:%d, base16:%s", (int)*pEType, xBuffer);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pBt->nPage, iPtrmap, CORRUPT_TYPE_PAGE_PTR_MAP,
+ offset, 5, zMsg, NULL);
+ return SQLITE_CORRUPT_REPORT_PGNO(&context);
+ }
return SQLITE_OK;
}
@@ -70088,7 +70345,14 @@ static void ptrmapPutOvflPtr(MemPage *pPage, MemPage *pSrc, u8 *pCell,int *pRC){
Pgno ovfl;
if( SQLITE_WITHIN(pSrc->aDataEnd, pCell, pCell+info.nLocal) ){
testcase( pSrc!=pPage );
- *pRC = SQLITE_CORRUPT_BKPT;
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ char xBuffer[SQLITE_PRINT_BUF_SIZE] = {0}; // Output cell header as much as possible, 4 bytes for overflow pgno
+ (void)sqlite3base16Encode(pCell, info.nSize - info.nLocal - 4, xBuffer, sizeof(xBuffer));
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "cell overflow, offset=%d, rest=%d, length=%u, base16:%s",
+ (int)(pCell - pPage->aData), (int)(pSrc->aDataEnd - pCell), info.nSize, xBuffer);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(0, pPage->pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF,
+ pCell - pPage->aData, info.nSize, zMsg, NULL);
+ *pRC = SQLITE_CORRUPT_REPORT(&context);
return;
}
ovfl = get4byte(&pCell[info.nSize-4]);
@@ -70146,10 +70410,29 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){
** reconstruct the entire page. */
if( (int)data[hdr+7]<=nMaxFrag ){
int iFree = get2byte(&data[hdr+1]);
- if( iFree>usableSize-4 ) return SQLITE_CORRUPT_PAGE(pPage);
+ if( iFree>usableSize-4 ){
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ char xBuffer[SQLITE_PRINT_BUF_SIZE] = {0};
+ (void)sqlite3base16Encode(data, 8, xBuffer, sizeof(xBuffer)); // Output first 8 bytes as it's page header
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "freeblock offset=%d overflow, usableSize=%d, base16:%s",
+ iFree, usableSize, xBuffer);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(0, pPage->pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF,
+ 0, 8, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
+ }
if( iFree ){
int iFree2 = get2byte(&data[iFree]);
- if( iFree2>usableSize-4 ) return SQLITE_CORRUPT_PAGE(pPage);
+ if( iFree2>usableSize-4 ){
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ char xBuffer[SQLITE_PRINT_BUF_SIZE] = {0};
+ (void)sqlite3base16Encode(data, 4, xBuffer, sizeof(xBuffer)); // Output first freeblock's header 4 bytes
+ sqlite3_snprintf(sizeof(zMsg), zMsg,
+ "1st freeblock's next pointer overflow, point:%d, usableSize=%d, base16:%s",
+ iFree2, usableSize, xBuffer);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(0, pPage->pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF,
+ iFree, 4, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
+ }
if( 0==iFree2 || (data[iFree2]==0 && data[iFree2+1]==0) ){
u8 *pEnd = &data[cellOffset + nCell*2];
u8 *pAddr;
@@ -70157,16 +70440,51 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){
int sz = get2byte(&data[iFree+2]);
int top = get2byte(&data[hdr+5]);
if( top>=iFree ){
- return SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ char xBuffer[SQLITE_PRINT_BUF_SIZE] = {0};
+ (void)sqlite3base16Encode(data, 8, xBuffer, sizeof(xBuffer)); // Print first 8 bytes which is page header
+ sqlite3_snprintf(sizeof(zMsg), zMsg,
+ "1st freeblock's offset:%d should > CellContentArea's offset:%d, base16:%s",
+ iFree, top, xBuffer);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(0, pPage->pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF,
+ iFree, 8, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
if( iFree2 ){
- if( iFree+sz>iFree2 ) return SQLITE_CORRUPT_PAGE(pPage);
+ if( iFree+sz>iFree2 ){
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ char xBuffer[SQLITE_PRINT_BUF_SIZE] = {0};
+ (void)sqlite3base16Encode(data, 8, xBuffer, sizeof(xBuffer)); // Print first 8 bytes which is page header
+ sqlite3_snprintf(sizeof(zMsg), zMsg,
+ "the 1st 2 freeblocks mis-order, 1st block offset:%d, size:%d, 2nd block offset:%d, base16:%s",
+ iFree, sz, iFree2, xBuffer);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(0, pPage->pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF,
+ iFree, 4, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
+ }
sz2 = get2byte(&data[iFree2+2]);
- if( iFree2+sz2 > usableSize ) return SQLITE_CORRUPT_PAGE(pPage);
+ if( iFree2+sz2 > usableSize ){
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ char xBuffer[SQLITE_PRINT_BUF_SIZE] = {0};
+ (void)sqlite3base16Encode(data + iFree2, 4, xBuffer, sizeof(xBuffer)); // Print 4 bytes belong to 2nd block
+ sqlite3_snprintf(sizeof(zMsg), zMsg,
+ "the 2nd freeblock overflow, offset:%d, size:%d, usableSize:%d, base16:%s",
+ iFree2, sz2, usableSize, xBuffer);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(0, pPage->pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF,
+ iFree2, 4, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
+ }
memmove(&data[iFree+sz+sz2], &data[iFree+sz], iFree2-(iFree+sz));
sz += sz2;
}else if( iFree+sz>usableSize ){
- return SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ char xBuffer[SQLITE_PRINT_BUF_SIZE] = {0};
+ (void)sqlite3base16Encode(data + iFree, 4, xBuffer, sizeof(xBuffer)); // Print 4 bytes belong to 1st block
+ sqlite3_snprintf(sizeof(zMsg), zMsg,
+ "the 1st freeblock overflow, offset:%d, size:%d, usableSize:%d, base16:%s", iFree, sz, usableSize, xBuffer);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(0, pPage->pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF,
+ iFree, 4, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
cbrk = top+sz;
@@ -70199,13 +70517,24 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){
** if PRAGMA cell_size_check=ON.
*/
if( pciCellLast ){
- return SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ char xBuffer[SQLITE_PRINT_BUF_SIZE] = {0}; // Print 4 bytes belong to 1st block
+ (void)sqlite3base16Encode(data + cellOffset + i*2, 2, xBuffer, sizeof(xBuffer));
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "%d-th cell pointer:%d out of range[%d, %d], base16:%s",
+ i, pc, iCellStart, iCellLast, xBuffer);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(0, pPage->pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF,
+ cellOffset + i*2, 2, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
assert( pc>=iCellStart && pc<=iCellLast );
size = pPage->xCellSize(pPage, &src[pc]);
cbrk -= size;
if( cbrkusableSize ){
- return SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "move %d-th cell from %d using unexpected size:%d", i, pc, size);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(0, pPage->pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF,
+ -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
assert( cbrk+size<=usableSize && cbrk>=iCellStart );
testcase( cbrk+size==usableSize );
@@ -70219,7 +70548,13 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){
defragment_out:
assert( pPage->nFree>=0 );
if( data[hdr+7]+cbrk-iCellFirst!=pPage->nFree ){
- return SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg,
+ "after defragment, free bytes should not change, fragment bytes:%d, free space:%d, total:%d",
+ (int)data[hdr+7], cbrk-iCellFirst, pPage->nFree);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(0, pPage->pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF,
+ -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
assert( cbrk>=iCellFirst );
put2byte(&data[hdr+5], cbrk);
@@ -70244,7 +70579,7 @@ defragment_out:
** will be ignored if adding the extra space to the fragmentation count
** causes the fragmentation count to exceed 60.
*/
-static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){
+static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ // search on B-tree page
const int hdr = pPg->hdrOffset; /* Offset to page header */
u8 * const aData = pPg->aData; /* Page data */
int iAddr = hdr + 1; /* Address of ptr to pc */
@@ -70276,7 +70611,15 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){
return &aData[pc];
}else if( x+pc > maxPC ){
/* This slot extends off the end of the usable part of the page */
- *pRc = SQLITE_CORRUPT_PAGE(pPg);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ char xBuffer[SQLITE_PRINT_BUF_SIZE] = {0};
+ (void)sqlite3base16Encode(aData + pc, 4, xBuffer, sizeof(xBuffer)); // Print 4 bytes belong to free block
+ sqlite3_snprintf(sizeof(zMsg), zMsg,
+ "freeblock rest bytes:%d begin at %d which cost %d, still exceed usableSize:%u, base16:%s",
+ x, pc, nByte, pPg->pBt->usableSize, xBuffer);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(0, pPg->pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF,
+ pc, 4, zMsg, NULL);
+ *pRc = SQLITE_CORRUPT_PAGE(&context, pPg);
return 0;
}else{
/* The slot remains on the free-list. Reduce its size to account
@@ -70291,14 +70634,25 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){
if( pc<=iAddr ){
if( pc ){
/* The next slot in the chain comes before the current slot */
- *pRc = SQLITE_CORRUPT_PAGE(pPg);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ char xBuffer[SQLITE_PRINT_BUF_SIZE] = {0};
+ (void)sqlite3base16Encode(pTmp, 2, xBuffer, sizeof(xBuffer)); // Print 4 bytes belong to free block
+ sqlite3_snprintf(sizeof(zMsg), zMsg,
+ "the next slot:%d in chain comes before current slot:%d, base16:%s", pc, iAddr, xBuffer);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(0, pPg->pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF,
+ iAddr, 2, zMsg, NULL);
+ *pRc = SQLITE_CORRUPT_PAGE(&context, pPg);
}
return 0;
}
}
if( pc>maxPC+nByte-4 ){
/* The free slot chain extends off the end of the page */
- *pRc = SQLITE_CORRUPT_PAGE(pPg);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "free slot:%d overflow, end:%d", pc, maxPC+nByte-4);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(0, pPg->pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF,
+ -1, 0, zMsg, NULL);
+ *pRc = SQLITE_CORRUPT_PAGE(&context, pPg);
}
return 0;
}
@@ -70347,7 +70701,13 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
if( top==0 && pPage->pBt->usableSize==65536 ){
top = 65536;
}else{
- return SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ char xBuffer[SQLITE_PRINT_BUF_SIZE] = {0};
+ (void)sqlite3base16Encode(data, 8, xBuffer, sizeof(xBuffer)); // Print 8 bytes belong to page header
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "unexpected cellContentArea offset:%d, base16:%s", top, xBuffer);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPage->pBt->nPage, pPage->pgno,
+ CORRUPT_TYPE_PAGE_BTREE_LEAF, 0, 8, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
}
@@ -70365,7 +70725,11 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
assert( pSpace+nByte<=data+pPage->pBt->usableSize );
*pIdx = g2 = (int)(pSpace-data);
if( g2<=gap ){
- return SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "cellpointers(end:%d) overlap with freeblock(%d)", gap, g2);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPage->pBt->nPage, pPage->pgno,
+ CORRUPT_TYPE_PAGE_BTREE_LEAF, -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}else{
return SQLITE_OK;
}
@@ -70444,12 +70808,23 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
while( (iFreeBlk = get2byte(&data[iPtr]))pBt->nPage, pPage->pgno,
+ CORRUPT_TYPE_PAGE_BTREE_LEAF, -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
iPtr = iFreeBlk;
}
if( iFreeBlk>pPage->pBt->usableSize-4 ){ /* TH3: corrupt081.100 */
- return SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ char xBuffer[SQLITE_PRINT_BUF_SIZE] = {0};
+ (void)sqlite3base16Encode(data + iPtr, 4, xBuffer, sizeof(xBuffer)); // Print 4 bytes belong to freeblock
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "freeblock offset:%d overflow, base16:%s", (int)iFreeBlk, xBuffer);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPage->pBt->nPage, pPage->pgno,
+ CORRUPT_TYPE_PAGE_BTREE_LEAF, iPtr, 4, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
assert( iFreeBlk>iPtr || iFreeBlk==0 || CORRUPT_DB );
@@ -70461,10 +70836,24 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
*/
if( iFreeBlk && iEnd+3>=iFreeBlk ){
nFrag = iFreeBlk - iEnd;
- if( iEnd>iFreeBlk ) return SQLITE_CORRUPT_PAGE(pPage);
+ if( iEnd>iFreeBlk ){
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "freeblock offset:%d overlaps with pre block's end:%u",
+ (int)iFreeBlk, iEnd);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPage->pBt->nPage, pPage->pgno,
+ CORRUPT_TYPE_PAGE_BTREE_LEAF, -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
+ }
iEnd = iFreeBlk + get2byte(&data[iFreeBlk+2]);
if( iEnd > pPage->pBt->usableSize ){
- return SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ char xBuffer[SQLITE_PRINT_BUF_SIZE] = {0};
+ (void)sqlite3base16Encode(data + iFreeBlk, 4, xBuffer, sizeof(xBuffer)); // Print 4 bytes belong to freeblock
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "freeblock offset:%d, end:%u overflow, usableSize:%u, base16:%s",
+ (int)iFreeBlk, iEnd, pPage->pBt->usableSize, xBuffer);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPage->pBt->nPage, pPage->pgno,
+ CORRUPT_TYPE_PAGE_BTREE_LEAF, iFreeBlk, 4, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
iSize = iEnd - iStart;
iFreeBlk = get2byte(&data[iFreeBlk]);
@@ -70477,13 +70866,27 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
if( iPtr>hdr+1 ){
int iPtrEnd = iPtr + get2byte(&data[iPtr+2]);
if( iPtrEnd+3>=iStart ){
- if( iPtrEnd>iStart ) return SQLITE_CORRUPT_PAGE(pPage);
+ if( iPtrEnd>iStart ){
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "check pre freeblock end:%d overlaps with the pending free block:%d",
+ iPtrEnd, (int)iStart);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPage->pBt->nPage, pPage->pgno,
+ CORRUPT_TYPE_PAGE_BTREE_LEAF, iPtr, iPtrEnd - iPtr, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
+ }
nFrag += iStart - iPtrEnd;
iSize = iEnd - iPtr;
iStart = iPtr;
}
}
- if( nFrag>data[hdr+7] ) return SQLITE_CORRUPT_PAGE(pPage);
+ if( nFrag>data[hdr+7] ){
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "fragment free bytes:%d increase unexpectly, should be %d",
+ (int)nFrag, (int)data[hdr+7]);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPage->pBt->nPage, pPage->pgno,
+ CORRUPT_TYPE_PAGE_BTREE_LEAF, -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
+ }
data[hdr+7] -= nFrag;
}
pTmp = &data[hdr+5];
@@ -70492,8 +70895,21 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
/* The new freeblock is at the beginning of the cell content area,
** so just extend the cell content area rather than create another
** freelist entry */
- if( iStart= the beginning of the CellContentArea:%d", (int)x, (int)iStart);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPage->pBt->nPage, pPage->pgno,
+ CORRUPT_TYPE_PAGE_BTREE_LEAF, 0, 8, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
+ }
+ if( iPtr!=hdr+1 ){
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "1st freeblock's pos incorrect, hdr:%d, iPtr:%d", (int)hdr, (int)iPtr);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPage->pBt->nPage, pPage->pgno,
+ CORRUPT_TYPE_PAGE_BTREE_LEAF, 0, 8, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
+ }
put2byte(&data[hdr+1], iFreeBlk);
put2byte(&data[hdr+5], iEnd);
}else{
@@ -70589,7 +71005,13 @@ static int decodeFlags(MemPage *pPage, int flagByte){
pPage->maxLocal,
g_lastCkptTime);
#endif
- return SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ char xBuffer[SQLITE_PRINT_BUF_SIZE] = {0};
+ (void)sqlite3base16Encode(pPage->aData, 8, xBuffer, sizeof(xBuffer));
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "unrecognized flag:%d, base16:%s", flagByte, xBuffer);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPage->pBt->nPage, pPage->pgno,
+ CORRUPT_TYPE_PAGE_BTREE_LEAF, -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
pPage->max1bytePayload = pBt->max1bytePayload;
return SQLITE_OK;
@@ -70640,12 +71062,20 @@ static int btreeComputeFreeSpace(MemPage *pPage){
/* EVIDENCE-OF: R-55530-52930 In a well-formed b-tree page, there will
** always be at least one cell before the first freeblock.
*/
- return SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "the 1st freeblock:%d before all cells:%d", pc, top);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPage->pBt->nPage, pPage->pgno,
+ CORRUPT_TYPE_PAGE_BTREE_LEAF, -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
while( 1 ){
if( pc>iCellLast ){
/* Freeblock off the end of the page */
- return SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "freeblock end:%d out of page range:%d", pc, iCellLast);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPage->pBt->nPage, pPage->pgno,
+ CORRUPT_TYPE_PAGE_BTREE_LEAF, -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
next = get2byte(&data[pc]);
size = get2byte(&data[pc+2]);
@@ -70655,11 +71085,19 @@ static int btreeComputeFreeSpace(MemPage *pPage){
}
if( next>0 ){
/* Freeblock not in ascending order */
- return SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "all freeblocks should order by asc, pre:%d, cur:%u", pc, next);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPage->pBt->nPage, pPage->pgno,
+ CORRUPT_TYPE_PAGE_BTREE_LEAF, -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
if( pc+size>(unsigned int)usableSize ){
/* Last freeblock extends past page end */
- return SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "last freeblock overflow, offset:%d, size:%u", pc, size);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPage->pBt->nPage, pPage->pgno,
+ CORRUPT_TYPE_PAGE_BTREE_LEAF, -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
}
@@ -70671,7 +71109,13 @@ static int btreeComputeFreeSpace(MemPage *pPage){
** area, according to the page header, lies within the page.
*/
if( nFree>usableSize || nFreepBt->nPage, pPage->pgno,
+ CORRUPT_TYPE_PAGE_BTREE_LEAF, -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
pPage->nFree = (u16)(nFree - iCellFirst);
return SQLITE_OK;
@@ -70702,12 +71146,20 @@ static SQLITE_NOINLINE int btreeCellSizeCheck(MemPage *pPage){
testcase( pc==iCellFirst );
testcase( pc==iCellLast );
if( pciCellLast ){
- return SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "cell pointer:%d indicate out of range:[%d, %d]", pc, iCellFirst, iCellLast);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPage->pBt->nPage, pPage->pgno,
+ CORRUPT_TYPE_PAGE_BTREE_LEAF, -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
sz = pPage->xCellSize(pPage, &data[pc]);
testcase( pc+sz==usableSize );
if( pc+sz>usableSize ){
- return SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "unexpected cell size:%d,offset:%d, out of range:%d", sz, pc, usableSize);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPage->pBt->nPage, pPage->pgno,
+ CORRUPT_TYPE_PAGE_BTREE_LEAF, -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
}
return SQLITE_OK;
@@ -70739,7 +71191,7 @@ static int btreeInitPage(MemPage *pPage){
/* EVIDENCE-OF: R-28594-02890 The one-byte flag at offset 0 indicating
** the b-tree page type. */
if( decodeFlags(pPage, data[0]) ){
- return SQLITE_CORRUPT_PAGE(pPage);
+ return SQLITE_CORRUPT_PAGE(NULL, pPage);
}
assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
pPage->maskPage = (u16)(pBt->pageSize - 1);
@@ -70753,7 +71205,12 @@ static int btreeInitPage(MemPage *pPage){
pPage->nCell = get2byte(&data[3]);
if( pPage->nCell>MX_CELL(pBt) ){
/* To many cells for a single page. The page must be corrupt */
- return SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "too many cells(%d) for the page:%u, offset:%d, out of range:%u",
+ (int)pPage->nCell, pPage->pgno, (int)pPage->hdrOffset + 3, MX_CELL(pBt));
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPage->pBt->nPage, pPage->pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF,
+ -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
testcase( pPage->nCell==MX_CELL(pBt) );
/* EVIDENCE-OF: R-24089-57979 If a page contains no cells (which is only
@@ -70908,7 +71365,11 @@ static int getAndInitPage(
assert( pCur==0 || pCur->iPage>0 );
if( pgno>btreePagecount(pBt) ){
- rc = SQLITE_CORRUPT_BKPT;
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "page number(%u) > db file size(%u)", pgno, btreePagecount(pBt));
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pBt->nPage, pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF,
+ -1, 0, zMsg, NULL);
+ rc = SQLITE_CORRUPT_REPORT(&context);
goto getAndInitPage_error1;
}
rc = sqlite3PagerGet(pBt->pPager, pgno, (DbPage**)&pDbPage, bReadOnly);
@@ -70929,7 +71390,12 @@ static int getAndInitPage(
/* If obtaining a child page for a cursor, we must verify that the page is
** compatible with the root page. */
if( pCur && ((*ppPage)->nCell<1 || (*ppPage)->intKey!=pCur->curIntKey) ){
- rc = SQLITE_CORRUPT_PGNO(pgno);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "check btree page, nCell:%u, intKey:%u, cursor->curIntKey:%u",
+ (*ppPage)->nCell, (*ppPage)->intKey, pCur->curIntKey);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pBt->nPage, pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF,
+ -1, 0, zMsg, NULL);
+ rc = SQLITE_CORRUPT_REPORT_PGNO(&context);
goto getAndInitPage_error2;
}
return SQLITE_OK;
@@ -71032,7 +71498,7 @@ static void pageReinit(DbPage *pData){
}
}
-static void printLockInfoUsingPager(Pager *pPager);
+static void DumpLocksByPager(Pager *pPager);
/*
** Invoke the busy handler for a btree.
*/
@@ -71042,8 +71508,8 @@ static int btreeInvokeBusyHandler(void *pArg){
assert( sqlite3_mutex_held(pBt->db->mutex) );
int rc = sqlite3InvokeBusyHandler(&pBt->db->busyHandler);
#if SQLITE_OS_UNIX
- if( rc==0 ){
- printLockInfoUsingPager( pBt->pPager );
+ if (rc == 0) {
+ DumpLocksByPager(pBt->pPager);
}
#endif /* SQLITE_OS_UNIX */
return rc;
@@ -72134,7 +72600,7 @@ SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVers
sqlite3BtreeEnter(p);
btreeIntegrity(p);
-
+ ResetLockStatus();
/* If the btree is already in a write-transaction, or it
** is already in a read-transaction and a read-transaction
** is requested, this is a no-op.
@@ -72358,7 +72824,12 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){
if( eType==PTRMAP_OVERFLOW2 ){
/* The pointer is always the first 4 bytes of the page in this case. */
if( get4byte(pPage->aData)!=iFrom ){
- return SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "1st 4 bytes of ovrflow page(%u) point to next(%u), should be %u",
+ pPage->pgno, get4byte(pPage->aData), iFrom);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(0, pPage->pgno, CORRUPT_TYPE_PAGE_PTR_MAP,
+ 0, 4, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
put4byte(pPage->aData, iTo);
}else{
@@ -72377,7 +72848,13 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){
pPage->xParseCell(pPage, pCell, &info);
if( info.nLocal pPage->aData+pPage->pBt->usableSize ){
- return SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg,
+ "btree cell contain ovrflow pointer overflow, offset:%d, size:%u, usableSize:%u",
+ (int)(pCell - pPage->aData), info.nSize, pPage->pBt->usableSize);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPage->pBt->nPage, pPage->pgno,
+ CORRUPT_TYPE_PAGE_PTR_MAP, pCell - pPage->aData, info.nSize, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
if( iFrom==get4byte(pCell+info.nSize-4) ){
put4byte(pCell+info.nSize-4, iTo);
@@ -72386,7 +72863,13 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){
}
}else{
if( pCell+4 > pPage->aData+pPage->pBt->usableSize ){
- return SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg,
+ "btree cell contain child pointer overflow, offset:%d, size:4, usableSize:%u",
+ (int)(pCell - pPage->aData), pPage->pBt->usableSize);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPage->pBt->nPage, pPage->pgno,
+ CORRUPT_TYPE_PAGE_PTR_MAP, pCell - pPage->aData, 4, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
if( get4byte(pCell)==iFrom ){
put4byte(pCell, iTo);
@@ -72398,7 +72881,11 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){
if( i==nCell ){
if( eType!=PTRMAP_BTREE ||
get4byte(&pPage->aData[pPage->hdrOffset+8])!=iFrom ){
- return SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "missing pointer point to overflow page on btree page(%u)", pPage->pgno);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPage->pBt->nPage, pPage->pgno,
+ CORRUPT_TYPE_PAGE_BTREE_LEAF, -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
put4byte(&pPage->aData[pPage->hdrOffset+8], iTo);
}
@@ -72531,7 +73018,11 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg, int bCommit){
return rc;
}
if( eType==PTRMAP_ROOTPAGE ){
- return SQLITE_CORRUPT_BKPT;
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "try vacuum root page(%u), should not happened", nFin);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pBt->nPage, PTRMAP_PAGENO(pBt, iLastPg),
+ CORRUPT_TYPE_PAGE_PTR_MAP, -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_REPORT(&context);
}
if( eType==PTRMAP_FREEPAGE ){
@@ -73600,7 +74091,12 @@ static int accessPayload(
assert( eOp==0 || eOp==1 );
assert( pCur->eState==CURSOR_VALID );
if( pCur->ix>=pPage->nCell ){
- return SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "cell index:%u exceed limit:%u on the page:%u",
+ pCur->ix, pPage->nCell, pPage->pgno);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pBt->nPage, pPage->pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF,
+ -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
assert( cursorHoldsMutex(pCur) );
@@ -73615,7 +74111,12 @@ static int accessPayload(
** &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize]
** but is recast into its current form to avoid integer overflow problems
*/
- return SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "base on payload size:%u, the max offset(%d) should > %d",
+ pCur->info.nLocal, (int)(aPayload - pPage->aData), (int)(pBt->usableSize - pCur->info.nLocal));
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pBt->nPage, pPage->pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF,
+ 0, 8, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
/* Check if data must be read/written to/from the btree page itself. */
@@ -73677,7 +74178,16 @@ static int accessPayload(
assert( rc==SQLITE_OK && amt>0 );
while( nextPage ){
/* If required, populate the overflow page-list cache. */
- if( nextPage > pBt->nPage ) return SQLITE_CORRUPT_BKPT;
+ if( nextPage > pBt->nPage ){
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ char xBuffer[SQLITE_PRINT_BUF_SIZE] = {0};
+ (void)sqlite3base16Encode(aPayload + pCur->info.nLocal, 4, xBuffer, sizeof(xBuffer));
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "overflow page:%u should not exceed the size of database file, base16:%s",
+ nextPage, xBuffer);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pBt->nPage, pPage->pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF,
+ aPayload - pPage->aData + pCur->info.nLocal, 4, zMsg, NULL);
+ return SQLITE_CORRUPT_REPORT(&context);
+ }
assert( pCur->aOverflow[iIdx]==0
|| pCur->aOverflow[iIdx]==nextPage
|| CORRUPT_DB );
@@ -73762,7 +74272,11 @@ static int accessPayload(
if( rc==SQLITE_OK && amt>0 ){
/* Overflow chain ends prematurely */
- return SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "overflow chain ends prematurely, rest:%d", amt);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pBt->nPage, pPage->pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF,
+ -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
return rc;
}
@@ -74038,7 +74552,12 @@ static int moveToRoot(BtCursor *pCur){
** (or the freelist). */
assert( pRoot->intKey==1 || pRoot->intKey==0 );
if( pRoot->isInit==0 || (pCur->pKeyInfo==0)!=pRoot->intKey ){
- return SQLITE_CORRUPT_PAGE(pCur->pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "the page(%u) state illegal, isInit:%u, pKeyInfo%s0, intKey:%u",
+ pRoot->pgno, pRoot->isInit, ((pCur->pKeyInfo==0)?"==":"!="), pRoot->intKey);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pCur->pBt->nPage, pRoot->pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF,
+ -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pCur->pPage);
}
skip_init:
@@ -74292,7 +74811,11 @@ SQLITE_PRIVATE int sqlite3BtreeTableMoveto(
if( pPage->intKeyLeaf ){
while( 0x80 <= *(pCell++) ){
if( pCell>=pPage->aDataEnd ){
- return SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "cell idx(%d) point to a cell should not out of page", idx);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPage->pBt->nPage, pPage->pgno,
+ CORRUPT_TYPE_PAGE_BTREE_LEAF, -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
}
}
@@ -74575,7 +75098,12 @@ bypass_moveto_root:
testcase( nCell==1 ); /* Invalid key size: 0x80 0x80 0x01 */
testcase( nCell==2 ); /* Minimum legal index key size */
if( nCell<2 || nCell/pCur->pBt->usableSize>pCur->pBt->nPage ){
- rc = SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "nCell:%d illegal, usableSize:%u, nPage:%u",
+ nCell, pCur->pBt->usableSize, pCur->pBt->nPage);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPage->pBt->nPage, pPage->pgno,
+ CORRUPT_TYPE_PAGE_BTREE_LEAF, -1, 0, zMsg, NULL);
+ rc = SQLITE_CORRUPT_PAGE(&context, pPage);
goto moveto_index_finish;
}
pCellKey = sqlite3Malloc( nCell+nOverrun );
@@ -74903,7 +75431,13 @@ static int allocateBtreePage(
n = get4byte(&pPage1->aData[36]);
testcase( n==mxPage-1 );
if( n>=mxPage ){
- return SQLITE_CORRUPT_BKPT;
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ char xBuffer[SQLITE_PRINT_BUF_SIZE*3] = {0};
+ (void)sqlite3base16Encode(pPage1->aData, 100, xBuffer, sizeof(xBuffer));
+ sqlite3_snprintf(sizeof(zMsg), zMsg,
+ "total pages(%u) in freelist should not over the total size of db file(%u), base16:%s", n, mxPage, xBuffer);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(mxPage, 1, CORRUPT_TYPE_FILE_HEADER, 36, 4, zMsg, NULL);
+ return SQLITE_CORRUPT_REPORT(&context);
}
if( n>0 ){
/* There are pages on the freelist. Reuse one of those pages. */
@@ -74959,7 +75493,12 @@ static int allocateBtreePage(
}
testcase( iTrunk==mxPage );
if( iTrunk>mxPage || nSearch++ > n ){
- rc = SQLITE_CORRUPT_PGNO(pPrevTrunk ? pPrevTrunk->pgno : 1);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "freelist trunk page(%u) should <= the size of db(%u)",
+ iTrunk, mxPage);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(mxPage, (pPrevTrunk ? pPrevTrunk->pgno : 1),
+ CORRUPT_TYPE_PAGE_FREE_LIST, -1, 0, zMsg, NULL);
+ rc = SQLITE_CORRUPT_REPORT_PGNO(&context);
}else{
rc = btreeGetUnusedPage(pBt, iTrunk, &pTrunk, 0);
}
@@ -74988,7 +75527,14 @@ static int allocateBtreePage(
TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1));
}else if( k>(u32)(pBt->usableSize/4 - 2) ){
/* Value of k is out of range. Database corruption */
- rc = SQLITE_CORRUPT_PGNO(iTrunk);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ char xBuffer[SQLITE_PRINT_BUF_SIZE] = {0};
+ (void)sqlite3base16Encode(pTrunk->aData, 8, xBuffer, sizeof(xBuffer));
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "total leaf pages(%u) on trunk page over limit(%u), base16:%s",
+ k, (u32)(pBt->usableSize/4 - 2), xBuffer);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(mxPage, iTrunk, CORRUPT_TYPE_PAGE_FREE_LIST,
+ 0, 8, zMsg, NULL);
+ rc = SQLITE_CORRUPT_REPORT_PGNO(&context);
goto end_allocate_page;
#ifndef SQLITE_OMIT_AUTOVACUUM
}else if( searchList
@@ -75022,7 +75568,14 @@ static int allocateBtreePage(
MemPage *pNewTrunk;
Pgno iNewTrunk = get4byte(&pTrunk->aData[8]);
if( iNewTrunk>mxPage ){
- rc = SQLITE_CORRUPT_PGNO(iTrunk);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ char xBuffer[SQLITE_PRINT_BUF_SIZE] = {0};
+ (void)sqlite3base16Encode(pTrunk->aData, 12, xBuffer, sizeof(xBuffer));
+ sqlite3_snprintf(sizeof(zMsg), zMsg,
+ "leaf page's pgno(%u) on trunk page exceed db file size(%u), base16:%s", iNewTrunk, mxPage, xBuffer);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(mxPage, iTrunk, CORRUPT_TYPE_PAGE_FREE_LIST,
+ 8, 4, zMsg, NULL);
+ rc = SQLITE_CORRUPT_REPORT_PGNO(&context);
goto end_allocate_page;
}
testcase( iNewTrunk==mxPage );
@@ -75087,7 +75640,14 @@ static int allocateBtreePage(
iPage = get4byte(&aData[8+closest*4]);
testcase( iPage==mxPage );
if( iPage>mxPage || iPage<2 ){
- rc = SQLITE_CORRUPT_PGNO(iTrunk);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ char xBuffer[SQLITE_PRINT_BUF_SIZE] = {0};
+ (void)sqlite3base16Encode(aData+8+closest*4, 4, xBuffer, sizeof(xBuffer));
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "leaf page's pgno(%u) out of range:[3, %d], base16:%s",
+ iPage, mxPage, xBuffer);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(mxPage, iTrunk, CORRUPT_TYPE_PAGE_FREE_LIST,
+ 8+closest*4, 4, zMsg, NULL);
+ rc = SQLITE_CORRUPT_REPORT_PGNO(&context);
goto end_allocate_page;
}
testcase( iPage==mxPage );
@@ -75272,7 +75832,14 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
nLeaf = get4byte(&pTrunk->aData[4]);
assert( pBt->usableSize>32 );
if( nLeaf > (u32)pBt->usableSize/4 - 2 ){
- rc = SQLITE_CORRUPT_BKPT;
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ char xBuffer[SQLITE_PRINT_BUF_SIZE] = {0};
+ (void)sqlite3base16Encode(pTrunk->aData, 4, xBuffer, sizeof(xBuffer));
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "the number of leaf page(%u) on trunk page(%d) exceed limit, base16:%s",
+ nLeaf, iTrunk, xBuffer);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pBt->nPage, iTrunk, CORRUPT_TYPE_PAGE_FREE_LIST,
+ 0, 4, zMsg, NULL);
+ rc = SQLITE_CORRUPT_REPORT(&context);
goto freepage_out;
}
if( nLeaf < (u32)pBt->usableSize/4 - 8 ){
@@ -75361,7 +75928,12 @@ static SQLITE_NOINLINE int clearCellOverflow(
testcase( pCell + (pInfo->nSize-1) == pPage->aDataEnd );
if( pCell + pInfo->nSize > pPage->aDataEnd ){
/* Cell extends past end of page */
- return SQLITE_CORRUPT_PAGE(pPage);
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "overflow end of page, pgno:%u, offset:%d, size:%u, usableSize:%u",
+ pPage->pgno, (int)(pCell - pPage->aData), pInfo->nSize, pPage->pBt->usableSize);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(btreePagecount(pPage->pBt), pPage->pgno,
+ CORRUPT_TYPE_PAGE_BTREE_LEAF, -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_PAGE(&context, pPage);
}
ovflPgno = get4byte(pCell + pInfo->nSize - 4);
pBt = pPage->pBt;
@@ -75378,7 +75950,14 @@ static SQLITE_NOINLINE int clearCellOverflow(
/* 0 is not a legal page number and page 1 cannot be an
** overflow page. Therefore if ovflPgno<2 or past the end of the
** file the database must be corrupt. */
- return SQLITE_CORRUPT_BKPT;
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ char xBuffer[SQLITE_PRINT_BUF_SIZE] = {0};
+ (void)sqlite3base16Encode(pCell + pInfo->nSize - 4, 4, xBuffer, sizeof(xBuffer));
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "overflow page's pgno(%u) illegal, out of range:[2, %u], base16:%s",
+ ovflPgno, btreePagecount(pBt), xBuffer);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(btreePagecount(pBt), pPage->pgno,
+ CORRUPT_TYPE_PAGE_BTREE_LEAF, -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_REPORT(&context);
}
if( nOvfl ){
rc = getOverflowPage(pBt, ovflPgno, &pOvfl, &iNext);
@@ -75651,7 +76230,12 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
testcase( pc==(u32)get2byte(&data[hdr+5]) );
testcase( pc+sz==pPage->pBt->usableSize );
if( pc+sz > pPage->pBt->usableSize ){
- *pRC = SQLITE_CORRUPT_BKPT;
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "cell offset:%u size:%d, idx:%d overflow, usableSize:%u",
+ pc, sz, idx, pPage->pBt->usableSize);
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPage->pBt->nPage, pPage->pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF,
+ -1, 0, zMsg, NULL);
+ *pRC = SQLITE_CORRUPT_REPORT(&context);
return;
}
rc = freeSpace(pPage, pc, sz);
@@ -77561,7 +78145,14 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){
if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd
|| pCur->info.pPayload < pPage->aData + pPage->cellOffset
){
- return SQLITE_CORRUPT_BKPT;
+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0};
+ sqlite3_snprintf(sizeof(zMsg), zMsg,
+ "cell payload cursor point to(%d), size:%u overlaps with non-cell content area:[%u, %d]",
+ (int)(pCur->info.pPayload - pPage->aData), pCur->info.nLocal, pPage->cellOffset,
+ (int)(pPage->aDataEnd - pPage->aData));
+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(0, pPage->pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF,
+ -1, 0, zMsg, NULL);
+ return SQLITE_CORRUPT_REPORT(&context);
}
/* Overwrite the local portion first */
rc = btreeOverwriteContent(pPage, pCur->info.pPayload, pX,
@@ -79504,6 +80095,7 @@ SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree *p, int eMode, int *pnLog, int *
sqlite3BtreeEnter(p);
if( pBt->inTransaction!=TRANS_NONE ){
rc = SQLITE_LOCKED;
+ MARK_LAST_BUSY_LINE(rc);
}else{
rc = sqlite3PagerCheckpoint(pBt->pPager, p->db, eMode, pnLog, pnCkpt);
}
@@ -82743,7 +83335,7 @@ static int growOpArray(Vdbe *v, int nOp){
**
** Other useful labels for breakpoints include:
** test_trace_breakpoint(pc,pOp)
-** sqlite3CorruptError(lineno)
+** sqlite3CorruptError(lineno,context)
** sqlite3MisuseError(lineno)
** sqlite3CantopenError(lineno)
*/
@@ -85350,6 +85942,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
nTrans++;
}
rc = sqlite3PagerExclusiveLock(pPager);
+ MARK_LAST_BUSY_LINE(rc);
sqlite3BtreeLeave(pBt);
}
}
@@ -90400,7 +90993,7 @@ SQLITE_API int sqlite3_found_count = 0;
**
** Other useful labels for breakpoints include:
** test_addop_breakpoint(pc,pOp)
-** sqlite3CorruptError(lineno)
+** sqlite3CorruptError(lineno,context)
** sqlite3MisuseError(lineno)
** sqlite3CantopenError(lineno)
*/
@@ -173402,6 +173995,7 @@ SQLITE_PRIVATE int sqlite3RtreeInit(sqlite3 *db);
/************** End of rtree.h ***********************************************/
/************** Continuing where we left off in main.c ***********************/
#endif
+#include "sqlite3tokenizer.h"
#if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS)
/************** Include sqliteicu.h in the middle of main.c ******************/
/************** Begin file sqliteicu.h ***************************************/
@@ -173426,13 +174020,54 @@ SQLITE_PRIVATE int sqlite3RtreeInit(sqlite3 *db);
extern "C" {
#endif /* __cplusplus */
-SQLITE_PRIVATE int sqlite3IcuInit(sqlite3 *db);
+SQLITE_PRIVATE int sqlite3IcuInitInner(sqlite3 *db);
#if 0
} /* extern "C" */
#endif /* __cplusplus */
/************** End of sqliteicu.h *******************************************/
+#ifndef _WIN32
+#include
+#endif
+
+typedef void (*sqlite3Fts3IcuTokenizerModule_ptr)(sqlite3_tokenizer_module const** ppModule);
+typedef int (*sqlite3IcuInit_ptr)(sqlite3 *db);
+static sqlite3Fts3IcuTokenizerModule_ptr tokenModulePtr = NULL;
+static sqlite3IcuInit_ptr icuInitPtr = NULL;
+static u32 icuEnable = 0u;
+static u32 icuInit = 0u;
+static void *g_library = NULL;
+
+int sqlite3IcuModuleInit(){
+ int rc = SQLITE_OK;
+ if( icuInit ){
+ return rc;
+ }
+#ifndef _WIN32
+ g_library = dlopen("libsqliteicu.z.so", RTLD_LAZY);
+ if( g_library==NULL ){
+ sqlite3_log(SQLITE_ERROR, "load icu so failed");
+ return SQLITE_ERROR;
+ }
+ tokenModulePtr = (sqlite3Fts3IcuTokenizerModule_ptr)dlsym(g_library, "sqlite3Fts3IcuTokenizerModule");
+ icuInitPtr = (sqlite3IcuInit_ptr)dlsym(g_library, "sqlite3IcuInit");
+ if( tokenModulePtr==NULL || icuInitPtr==NULL ){
+ sqlite3_log(SQLITE_ERROR, "load icu init function failed");
+ return SQLITE_ERROR;
+ }
+ icuInit = 1u;
+#endif
+ return rc;
+}
+
+SQLITE_PRIVATE int sqlite3IcuInitInner(sqlite3 *db)
+{
+ if( !icuEnable ){
+ return SQLITE_OK;
+ }
+ return icuInitPtr(db);
+}
/************** Continuing where we left off in main.c ***********************/
#endif
@@ -173482,7 +174117,7 @@ static int (*const sqlite3BuiltinExtensions[])(sqlite3*) = {
sqlite3Fts5Init,
#endif
#if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS)
- sqlite3IcuInit,
+ sqlite3IcuInitInner,
#endif
#ifdef SQLITE_ENABLE_RTREE
sqlite3RtreeInit,
@@ -173839,12 +174474,24 @@ SQLITE_API int sqlite3_shutdown(void){
SQLITE_API int sqlite3_config(int op, ...){
va_list ap;
int rc = SQLITE_OK;
+ va_start(ap, op);
+
+#if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS)
+ if( op==SQLITE_CONFIG_ENABLE_ICU ){
+ int iVal = va_arg(ap, int);
+ if( iVal==0 ){
+ icuEnable = 0u;
+ }else{
+ icuEnable = 1u;
+ }
+ return rc;
+ }
+#endif /* SQLITE_ENABLE_ICU */
/* sqlite3_config() shall return SQLITE_MISUSE if it is invoked while
** the SQLite library is in use. */
if( sqlite3GlobalConfig.isInit ) return SQLITE_MISUSE_BKPT;
- va_start(ap, op);
switch( op ){
/* Mutex configuration options are only available in a threadsafe
@@ -174133,6 +174780,12 @@ SQLITE_API int sqlite3_config(int op, ...){
break;
}
#endif /* SQLITE_OMIT_DESERIALIZE */
+ case SQLITE_CONFIG_CORRUPTION: {
+ typedef void(*CORRUPTION_FUNC_t)(void*, const void*);
+ sqlite3GlobalConfig.xCorruption = va_arg(ap, CORRUPTION_FUNC_t);
+ sqlite3GlobalConfig.pCorruptionArg = va_arg(ap, void*);
+ break;
+ }
default: {
rc = SQLITE_ERROR;
@@ -176909,6 +177562,12 @@ static int openDatabase(
sqlite3RegisterPerConnectionBuiltinFunctions(db);
rc = sqlite3_errcode(db);
+#if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS)
+ if( icuEnable ){
+ rc = sqlite3IcuModuleInit();
+ if( rc!=SQLITE_OK ) return rc;
+ }
+#endif
/* Load compiled-in extensions */
for(i=0; rc==SQLITE_OK && icodecParm);
+#endif
+ sqlite3CodecQueryParameters(db, 0, zOpen);
+ }
#endif
sqlite3_free_filename(zOpen);
return rc;
@@ -177197,9 +177862,21 @@ SQLITE_PRIVATE int sqlite3ReportError(int iErr, int lineno, const char *zType){
zType, lineno, 20+sqlite3_sourceid());
return iErr;
}
-SQLITE_PRIVATE int sqlite3CorruptError(int lineno){
+SQLITE_PRIVATE int sqlite3CorruptError(int lineno, sqlite3CorruptContext *context){
testcase( sqlite3GlobalConfig.xLog!=0 );
- return sqlite3ReportError(SQLITE_CORRUPT, lineno, "database corruption");
+ if (context!=NULL && sqlite3GlobalConfig.xCorruption != 0) {
+ char zMsg[SQLITE_PRINT_BUF_SIZE] = {0}; /* Complete corruption log message */
+ sqlite3_snprintf(sizeof(zMsg), zMsg, "pgno:%u,type:%d,range:{%d,%d},line:%d",
+ context->pgno, (int)context->type, (int)context->zoneRange.offset, (int)context->zoneRange.size, lineno);
+ sqlite3GlobalConfig.xCorruption(sqlite3GlobalConfig.pCorruptionArg, zMsg);
+ }
+ char zCorruptMsg[SQLITE_PRINT_BUF_SIZE * 10] = {0};
+ if (context!=NULL && context->zMsg != NULL){
+ sqlite3_snprintf(sizeof(zCorruptMsg), zCorruptMsg, "database corruption, %s", context->zMsg);
+ } else {
+ sqlite3_snprintf(sizeof(zCorruptMsg), zCorruptMsg, "database corruption");
+ }
+ return sqlite3ReportError(SQLITE_CORRUPT, lineno, zCorruptMsg);
}
SQLITE_PRIVATE int sqlite3MisuseError(int lineno){
testcase( sqlite3GlobalConfig.xLog!=0 );
@@ -179088,114 +179765,6 @@ SQLITE_EXTENSION_INIT3
** the tokenization rules supplied by a specific sqlite3_tokenizer
** object.
*/
-typedef struct sqlite3_tokenizer_module sqlite3_tokenizer_module;
-typedef struct sqlite3_tokenizer sqlite3_tokenizer;
-typedef struct sqlite3_tokenizer_cursor sqlite3_tokenizer_cursor;
-
-struct sqlite3_tokenizer_module {
-
- /*
- ** Structure version. Should always be set to 0 or 1.
- */
- int iVersion;
-
- /*
- ** Create a new tokenizer. The values in the argv[] array are the
- ** arguments passed to the "tokenizer" clause of the CREATE VIRTUAL
- ** TABLE statement that created the fts3 table. For example, if
- ** the following SQL is executed:
- **
- ** CREATE .. USING fts3( ... , tokenizer arg1 arg2)
- **
- ** then argc is set to 2, and the argv[] array contains pointers
- ** to the strings "arg1" and "arg2".
- **
- ** This method should return either SQLITE_OK (0), or an SQLite error
- ** code. If SQLITE_OK is returned, then *ppTokenizer should be set
- ** to point at the newly created tokenizer structure. The generic
- ** sqlite3_tokenizer.pModule variable should not be initialized by
- ** this callback. The caller will do so.
- */
- int (*xCreate)(
- int argc, /* Size of argv array */
- const char *const*argv, /* Tokenizer argument strings */
- sqlite3_tokenizer **ppTokenizer /* OUT: Created tokenizer */
- );
-
- /*
- ** Destroy an existing tokenizer. The fts3 module calls this method
- ** exactly once for each successful call to xCreate().
- */
- int (*xDestroy)(sqlite3_tokenizer *pTokenizer);
-
- /*
- ** Create a tokenizer cursor to tokenize an input buffer. The caller
- ** is responsible for ensuring that the input buffer remains valid
- ** until the cursor is closed (using the xClose() method).
- */
- int (*xOpen)(
- sqlite3_tokenizer *pTokenizer, /* Tokenizer object */
- const char *pInput, int nBytes, /* Input buffer */
- sqlite3_tokenizer_cursor **ppCursor /* OUT: Created tokenizer cursor */
- );
-
- /*
- ** Destroy an existing tokenizer cursor. The fts3 module calls this
- ** method exactly once for each successful call to xOpen().
- */
- int (*xClose)(sqlite3_tokenizer_cursor *pCursor);
-
- /*
- ** Retrieve the next token from the tokenizer cursor pCursor. This
- ** method should either return SQLITE_OK and set the values of the
- ** "OUT" variables identified below, or SQLITE_DONE to indicate that
- ** the end of the buffer has been reached, or an SQLite error code.
- **
- ** *ppToken should be set to point at a buffer containing the
- ** normalized version of the token (i.e. after any case-folding and/or
- ** stemming has been performed). *pnBytes should be set to the length
- ** of this buffer in bytes. The input text that generated the token is
- ** identified by the byte offsets returned in *piStartOffset and
- ** *piEndOffset. *piStartOffset should be set to the index of the first
- ** byte of the token in the input buffer. *piEndOffset should be set
- ** to the index of the first byte just past the end of the token in
- ** the input buffer.
- **
- ** The buffer *ppToken is set to point at is managed by the tokenizer
- ** implementation. It is only required to be valid until the next call
- ** to xNext() or xClose().
- */
- /* TODO(shess) current implementation requires pInput to be
- ** nul-terminated. This should either be fixed, or pInput/nBytes
- ** should be converted to zInput.
- */
- int (*xNext)(
- sqlite3_tokenizer_cursor *pCursor, /* Tokenizer cursor */
- const char **ppToken, int *pnBytes, /* OUT: Normalized text for token */
- int *piStartOffset, /* OUT: Byte offset of token in input buffer */
- int *piEndOffset, /* OUT: Byte offset of end of token in input buffer */
- int *piPosition /* OUT: Number of tokens returned before this one */
- );
-
- /***********************************************************************
- ** Methods below this point are only available if iVersion>=1.
- */
-
- /*
- ** Configure the language id of a tokenizer cursor.
- */
- int (*xLanguageid)(sqlite3_tokenizer_cursor *pCsr, int iLangid);
-};
-
-struct sqlite3_tokenizer {
- const sqlite3_tokenizer_module *pModule; /* The module for this tokenizer */
- /* Tokenizer implementations will typically add additional fields */
-};
-
-struct sqlite3_tokenizer_cursor {
- sqlite3_tokenizer *pTokenizer; /* Tokenizer for this cursor. */
- /* Tokenizer implementations will typically add additional fields */
-};
int fts3_global_term_cnt(int iTerm, int iCol);
int fts3_term_cnt(int iTerm, int iCol);
@@ -183680,9 +184249,6 @@ SQLITE_PRIVATE void sqlite3Fts3PorterTokenizerModule(sqlite3_tokenizer_module co
#ifndef SQLITE_DISABLE_FTS3_UNICODE
SQLITE_PRIVATE void sqlite3Fts3UnicodeTokenizer(sqlite3_tokenizer_module const**ppModule);
#endif
-#ifdef SQLITE_ENABLE_ICU
-SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(sqlite3_tokenizer_module const**ppModule);
-#endif
/*
** Initialize the fts3 extension. If this extension is built as part
@@ -183701,7 +184267,14 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){
#ifdef SQLITE_ENABLE_ICU
const sqlite3_tokenizer_module *pIcu = 0;
- sqlite3Fts3IcuTokenizerModule(&pIcu);
+ if( icuEnable ){
+ if( tokenModulePtr!=NULL ){
+ tokenModulePtr(&pIcu);
+ }else{
+ sqlite3_log(SQLITE_ERROR, "icu module ptr is null");
+ return SQLITE_ERROR;
+ }
+ }
#endif
#ifndef SQLITE_DISABLE_FTS3_UNICODE
@@ -183737,7 +184310,7 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){
|| sqlite3Fts3HashInsert(&pHash->hash, "unicode61", 10, (void *)pUnicode)
#endif
#ifdef SQLITE_ENABLE_ICU
- || (pIcu && sqlite3Fts3HashInsert(&pHash->hash, "icu", 4, (void *)pIcu))
+ || (icuEnable && pIcu && sqlite3Fts3HashInsert(&pHash->hash, "icu", 4, (void *)pIcu))
#endif
){
rc = SQLITE_NOMEM;
@@ -207349,829 +207922,6 @@ SQLITE_API int sqlite3_rtree_init(
#endif
/************** End of rtree.c ***********************************************/
-/************** Begin file icu.c *********************************************/
-/*
-** 2007 May 6
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-*************************************************************************
-** $Id: icu.c,v 1.7 2007/12/13 21:54:11 drh Exp $
-**
-** This file implements an integration between the ICU library
-** ("International Components for Unicode", an open-source library
-** for handling unicode data) and SQLite. The integration uses
-** ICU to provide the following to SQLite:
-**
-** * An implementation of the SQL regexp() function (and hence REGEXP
-** operator) using the ICU uregex_XX() APIs.
-**
-** * Implementations of the SQL scalar upper() and lower() functions
-** for case mapping.
-**
-** * Integration of ICU and SQLite collation sequences.
-**
-** * An implementation of the LIKE operator that uses ICU to
-** provide case-independent matching.
-*/
-
-#if !defined(SQLITE_CORE) \
- || defined(SQLITE_ENABLE_ICU) \
- || defined(SQLITE_ENABLE_ICU_COLLATIONS)
-
-/* Include ICU headers */
-#include
-#include
-#include
-#include
-
-/* #include */
-
-#ifndef SQLITE_CORE
-/* #include "sqlite3ext.h" */
- SQLITE_EXTENSION_INIT1
-#else
-/* #include "sqlite3.h" */
-#endif
-
-/*
-** This function is called when an ICU function called from within
-** the implementation of an SQL scalar function returns an error.
-**
-** The scalar function context passed as the first argument is
-** loaded with an error message based on the following two args.
-*/
-static void icuFunctionError(
- sqlite3_context *pCtx, /* SQLite scalar function context */
- const char *zName, /* Name of ICU function that failed */
- UErrorCode e /* Error code returned by ICU function */
-){
- char zBuf[128];
- sqlite3_snprintf(128, zBuf, "ICU error: %s(): %s", zName, u_errorName(e));
- zBuf[127] = '\0';
- sqlite3_result_error(pCtx, zBuf, -1);
-}
-
-#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU)
-
-/*
-** Maximum length (in bytes) of the pattern in a LIKE or GLOB
-** operator.
-*/
-#ifndef SQLITE_MAX_LIKE_PATTERN_LENGTH
-# define SQLITE_MAX_LIKE_PATTERN_LENGTH 50000
-#endif
-
-/*
-** Version of sqlite3_free() that is always a function, never a macro.
-*/
-static void xFree(void *p){
- sqlite3_free(p);
-}
-
-/*
-** This lookup table is used to help decode the first byte of
-** a multi-byte UTF8 character. It is copied here from SQLite source
-** code file utf8.c.
-*/
-static const unsigned char icuUtf8Trans1[] = {
- 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 SQLITE_ICU_READ_UTF8(zIn, c) \
- c = *(zIn++); \
- if( c>=0xc0 ){ \
- c = icuUtf8Trans1[c-0xc0]; \
- while( (*zIn & 0xc0)==0x80 ){ \
- c = (c<<6) + (0x3f & *(zIn++)); \
- } \
- }
-
-#define SQLITE_ICU_SKIP_UTF8(zIn) \
- assert( *zIn ); \
- if( *(zIn++)>=0xc0 ){ \
- while( (*zIn & 0xc0)==0x80 ){zIn++;} \
- }
-
-
-/*
-** Compare two UTF-8 strings for equality where the first string is
-** a "LIKE" expression. Return true (1) if they are the same and
-** false (0) if they are different.
-*/
-static int icuLikeCompare(
- const uint8_t *zPattern, /* LIKE pattern */
- const uint8_t *zString, /* The UTF-8 string to compare against */
- const UChar32 uEsc /* The escape character */
-){
- static const uint32_t MATCH_ONE = (uint32_t)'_';
- static const uint32_t MATCH_ALL = (uint32_t)'%';
-
- int prevEscape = 0; /* True if the previous character was uEsc */
-
- while( 1 ){
-
- /* Read (and consume) the next character from the input pattern. */
- uint32_t uPattern;
- SQLITE_ICU_READ_UTF8(zPattern, uPattern);
- if( uPattern==0 ) break;
-
- /* There are now 4 possibilities:
- **
- ** 1. uPattern is an unescaped match-all character "%",
- ** 2. uPattern is an unescaped match-one character "_",
- ** 3. uPattern is an unescaped escape character, or
- ** 4. uPattern is to be handled as an ordinary character
- */
- if( uPattern==MATCH_ALL && !prevEscape && uPattern!=(uint32_t)uEsc ){
- /* Case 1. */
- uint8_t c;
-
- /* Skip any MATCH_ALL or MATCH_ONE characters that follow a
- ** MATCH_ALL. For each MATCH_ONE, skip one character in the
- ** test string.
- */
- while( (c=*zPattern) == MATCH_ALL || c == MATCH_ONE ){
- if( c==MATCH_ONE ){
- if( *zString==0 ) return 0;
- SQLITE_ICU_SKIP_UTF8(zString);
- }
- zPattern++;
- }
-
- if( *zPattern==0 ) return 1;
-
- while( *zString ){
- if( icuLikeCompare(zPattern, zString, uEsc) ){
- return 1;
- }
- SQLITE_ICU_SKIP_UTF8(zString);
- }
- return 0;
-
- }else if( uPattern==MATCH_ONE && !prevEscape && uPattern!=(uint32_t)uEsc ){
- /* Case 2. */
- if( *zString==0 ) return 0;
- SQLITE_ICU_SKIP_UTF8(zString);
-
- }else if( uPattern==(uint32_t)uEsc && !prevEscape ){
- /* Case 3. */
- prevEscape = 1;
-
- }else{
- /* Case 4. */
- uint32_t uString;
- SQLITE_ICU_READ_UTF8(zString, uString);
- uString = (uint32_t)u_foldCase((UChar32)uString, U_FOLD_CASE_DEFAULT);
- uPattern = (uint32_t)u_foldCase((UChar32)uPattern, U_FOLD_CASE_DEFAULT);
- if( uString!=uPattern ){
- return 0;
- }
- prevEscape = 0;
- }
- }
-
- return *zString==0;
-}
-
-/*
-** Implementation of the like() SQL function. This function implements
-** the build-in LIKE operator. The first argument to the function is the
-** pattern and the second argument is the string. So, the SQL statements:
-**
-** A LIKE B
-**
-** is implemented as like(B, A). If there is an escape character E,
-**
-** A LIKE B ESCAPE E
-**
-** is mapped to like(B, A, E).
-*/
-static void icuLikeFunc(
- sqlite3_context *context,
- int argc,
- sqlite3_value **argv
-){
- const unsigned char *zA = sqlite3_value_text(argv[0]);
- const unsigned char *zB = sqlite3_value_text(argv[1]);
- UChar32 uEsc = 0;
-
- /* Limit the length of the LIKE or GLOB pattern to avoid problems
- ** of deep recursion and N*N behavior in patternCompare().
- */
- if( sqlite3_value_bytes(argv[0])>SQLITE_MAX_LIKE_PATTERN_LENGTH ){
- sqlite3_result_error(context, "LIKE or GLOB pattern too complex", -1);
- return;
- }
-
-
- if( argc==3 ){
- /* The escape character string must consist of a single UTF-8 character.
- ** Otherwise, return an error.
- */
- int nE= sqlite3_value_bytes(argv[2]);
- const unsigned char *zE = sqlite3_value_text(argv[2]);
- int i = 0;
- if( zE==0 ) return;
- U8_NEXT(zE, i, nE, uEsc);
- if( i!=nE){
- sqlite3_result_error(context,
- "ESCAPE expression must be a single character", -1);
- return;
- }
- }
-
- if( zA && zB ){
- sqlite3_result_int(context, icuLikeCompare(zA, zB, uEsc));
- }
-}
-
-/*
-** Function to delete compiled regexp objects. Registered as
-** a destructor function with sqlite3_set_auxdata().
-*/
-static void icuRegexpDelete(void *p){
- URegularExpression *pExpr = (URegularExpression *)p;
- uregex_close(pExpr);
-}
-
-/*
-** Implementation of SQLite REGEXP operator. This scalar function takes
-** two arguments. The first is a regular expression pattern to compile
-** the second is a string to match against that pattern. If either
-** argument is an SQL NULL, then NULL Is returned. Otherwise, the result
-** is 1 if the string matches the pattern, or 0 otherwise.
-**
-** SQLite maps the regexp() function to the regexp() operator such
-** that the following two are equivalent:
-**
-** zString REGEXP zPattern
-** regexp(zPattern, zString)
-**
-** Uses the following ICU regexp APIs:
-**
-** uregex_open()
-** uregex_matches()
-** uregex_close()
-*/
-static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){
- UErrorCode status = U_ZERO_ERROR;
- URegularExpression *pExpr;
- UBool res;
- const UChar *zString = sqlite3_value_text16(apArg[1]);
-
- (void)nArg; /* Unused parameter */
-
- /* If the left hand side of the regexp operator is NULL,
- ** then the result is also NULL.
- */
- if( !zString ){
- return;
- }
-
- pExpr = sqlite3_get_auxdata(p, 0);
- if( !pExpr ){
- const UChar *zPattern = sqlite3_value_text16(apArg[0]);
- if( !zPattern ){
- return;
- }
- pExpr = uregex_open(zPattern, -1, 0, 0, &status);
-
- if( U_SUCCESS(status) ){
- sqlite3_set_auxdata(p, 0, pExpr, icuRegexpDelete);
- pExpr = sqlite3_get_auxdata(p, 0);
- }
- if( !pExpr ){
- icuFunctionError(p, "uregex_open", status);
- return;
- }
- }
-
- /* Configure the text that the regular expression operates on. */
- uregex_setText(pExpr, zString, -1, &status);
- if( !U_SUCCESS(status) ){
- icuFunctionError(p, "uregex_setText", status);
- return;
- }
-
- /* Attempt the match */
- res = uregex_matches(pExpr, 0, &status);
- if( !U_SUCCESS(status) ){
- icuFunctionError(p, "uregex_matches", status);
- return;
- }
-
- /* Set the text that the regular expression operates on to a NULL
- ** pointer. This is not really necessary, but it is tidier than
- ** leaving the regular expression object configured with an invalid
- ** pointer after this function returns.
- */
- uregex_setText(pExpr, 0, 0, &status);
-
- /* Return 1 or 0. */
- sqlite3_result_int(p, res ? 1 : 0);
-}
-
-/*
-** Implementations of scalar functions for case mapping - upper() and
-** lower(). Function upper() converts its input to upper-case (ABC).
-** Function lower() converts to lower-case (abc).
-**
-** ICU provides two types of case mapping, "general" case mapping and
-** "language specific". Refer to ICU documentation for the differences
-** between the two.
-**
-** To utilise "general" case mapping, the upper() or lower() scalar
-** functions are invoked with one argument:
-**
-** upper('ABC') -> 'abc'
-** lower('abc') -> 'ABC'
-**
-** To access ICU "language specific" case mapping, upper() or lower()
-** should be invoked with two arguments. The second argument is the name
-** of the locale to use. Passing an empty string ("") or SQL NULL value
-** as the second argument is the same as invoking the 1 argument version
-** of upper() or lower().
-**
-** lower('I', 'en_us') -> 'i'
-** lower('I', 'tr_tr') -> '\u131' (small dotless i)
-**
-** http://www.icu-project.org/userguide/posix.html#case_mappings
-*/
-static void icuCaseFunc16(sqlite3_context *p, int nArg, sqlite3_value **apArg){
- const UChar *zInput; /* Pointer to input string */
- UChar *zOutput = 0; /* Pointer to output buffer */
- int nInput; /* Size of utf-16 input string in bytes */
- int nOut; /* Size of output buffer in bytes */
- int cnt;
- int bToUpper; /* True for toupper(), false for tolower() */
- UErrorCode status;
- const char *zLocale = 0;
-
- assert(nArg==1 || nArg==2);
- bToUpper = (sqlite3_user_data(p)!=0);
- if( nArg==2 ){
- zLocale = (const char *)sqlite3_value_text(apArg[1]);
- }
-
- zInput = sqlite3_value_text16(apArg[0]);
- if( !zInput ){
- return;
- }
- nOut = nInput = sqlite3_value_bytes16(apArg[0]);
- if( nOut==0 ){
- sqlite3_result_text16(p, "", 0, SQLITE_STATIC);
- return;
- }
-
- for(cnt=0; cnt<2; cnt++){
- UChar *zNew = sqlite3_realloc(zOutput, nOut);
- if( zNew==0 ){
- sqlite3_free(zOutput);
- sqlite3_result_error_nomem(p);
- return;
- }
- zOutput = zNew;
- status = U_ZERO_ERROR;
- if( bToUpper ){
- nOut = 2*u_strToUpper(zOutput,nOut/2,zInput,nInput/2,zLocale,&status);
- }else{
- nOut = 2*u_strToLower(zOutput,nOut/2,zInput,nInput/2,zLocale,&status);
- }
-
- if( U_SUCCESS(status) ){
- sqlite3_result_text16(p, zOutput, nOut, xFree);
- }else if( status==U_BUFFER_OVERFLOW_ERROR ){
- assert( cnt==0 );
- continue;
- }else{
- icuFunctionError(p, bToUpper ? "u_strToUpper" : "u_strToLower", status);
- }
- return;
- }
- assert( 0 ); /* Unreachable */
-}
-
-#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU) */
-
-/*
-** Collation sequence destructor function. The pCtx argument points to
-** a UCollator structure previously allocated using ucol_open().
-*/
-static void icuCollationDel(void *pCtx){
- UCollator *p = (UCollator *)pCtx;
- ucol_close(p);
-}
-
-/*
-** Collation sequence comparison function. The pCtx argument points to
-** a UCollator structure previously allocated using ucol_open().
-*/
-static int icuCollationColl(
- void *pCtx,
- int nLeft,
- const void *zLeft,
- int nRight,
- const void *zRight
-){
- UCollationResult res;
- UCollator *p = (UCollator *)pCtx;
- res = ucol_strcoll(p, (UChar *)zLeft, nLeft/2, (UChar *)zRight, nRight/2);
- switch( res ){
- case UCOL_LESS: return -1;
- case UCOL_GREATER: return +1;
- case UCOL_EQUAL: return 0;
- }
- assert(!"Unexpected return value from ucol_strcoll()");
- return 0;
-}
-
-/*
-** Implementation of the scalar function icu_load_collation().
-**
-** This scalar function is used to add ICU collation based collation
-** types to an SQLite database connection. It is intended to be called
-** as follows:
-**
-** SELECT icu_load_collation(, );
-**
-** Where is a string containing an ICU locale identifier (i.e.
-** "en_AU", "tr_TR" etc.) and is the name of the
-** collation sequence to create.
-*/
-static void icuLoadCollation(
- sqlite3_context *p,
- int nArg,
- sqlite3_value **apArg
-){
- sqlite3 *db = (sqlite3 *)sqlite3_user_data(p);
- UErrorCode status = U_ZERO_ERROR;
- const char *zLocale; /* Locale identifier - (eg. "jp_JP") */
- const char *zName; /* SQL Collation sequence name (eg. "japanese") */
- UCollator *pUCollator; /* ICU library collation object */
- int rc; /* Return code from sqlite3_create_collation_x() */
-
- assert(nArg==2);
- (void)nArg; /* Unused parameter */
- zLocale = (const char *)sqlite3_value_text(apArg[0]);
- zName = (const char *)sqlite3_value_text(apArg[1]);
-
- if( !zLocale || !zName ){
- return;
- }
-
- pUCollator = ucol_open(zLocale, &status);
- if( !U_SUCCESS(status) ){
- icuFunctionError(p, "ucol_open", status);
- return;
- }
- assert(p);
-
- rc = sqlite3_create_collation_v2(db, zName, SQLITE_UTF16, (void *)pUCollator,
- icuCollationColl, icuCollationDel
- );
- if( rc!=SQLITE_OK ){
- ucol_close(pUCollator);
- sqlite3_result_error(p, "Error registering collation function", -1);
- }
-}
-
-/*
-** Register the ICU extension functions with database db.
-*/
-SQLITE_PRIVATE int sqlite3IcuInit(sqlite3 *db){
-# define SQLITEICU_EXTRAFLAGS (SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS)
- static const struct IcuScalar {
- const char *zName; /* Function name */
- unsigned char nArg; /* Number of arguments */
- unsigned int enc; /* Optimal text encoding */
- unsigned char iContext; /* sqlite3_user_data() context */
- void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
- } scalars[] = {
- {"icu_load_collation",2,SQLITE_UTF8|SQLITE_DIRECTONLY,1, icuLoadCollation},
-#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU)
- {"regexp", 2, SQLITE_ANY|SQLITEICU_EXTRAFLAGS, 0, icuRegexpFunc},
- {"lower", 1, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
- {"lower", 2, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
- {"upper", 1, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
- {"upper", 2, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
- {"lower", 1, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
- {"lower", 2, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
- {"upper", 1, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
- {"upper", 2, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
- {"like", 2, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuLikeFunc},
- {"like", 3, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuLikeFunc},
-#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU) */
- };
- int rc = SQLITE_OK;
- int i;
-
- for(i=0; rc==SQLITE_OK && i<(int)(sizeof(scalars)/sizeof(scalars[0])); i++){
- const struct IcuScalar *p = &scalars[i];
- rc = sqlite3_create_function(
- db, p->zName, p->nArg, p->enc,
- p->iContext ? (void*)db : (void*)0,
- p->xFunc, 0, 0
- );
- }
-
- return rc;
-}
-
-#if !SQLITE_CORE
-#ifdef _WIN32
-__declspec(dllexport)
-#endif
-SQLITE_API int sqlite3_icu_init(
- sqlite3 *db,
- char **pzErrMsg,
- const sqlite3_api_routines *pApi
-){
- SQLITE_EXTENSION_INIT2(pApi)
- return sqlite3IcuInit(db);
-}
-#endif
-
-#endif
-
-/************** End of icu.c *************************************************/
-/************** Begin file fts3_icu.c ****************************************/
-/*
-** 2007 June 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 implements a tokenizer for fts3 based on the ICU library.
-*/
-/* #include "fts3Int.h" */
-#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
-#ifdef SQLITE_ENABLE_ICU
-
-/* #include */
-/* #include */
-/* #include "fts3_tokenizer.h" */
-
-#include
-/* #include */
-/* #include */
-#include
-
-typedef struct IcuTokenizer IcuTokenizer;
-typedef struct IcuCursor IcuCursor;
-
-struct IcuTokenizer {
- sqlite3_tokenizer base;
- char *zLocale;
-};
-
-struct IcuCursor {
- sqlite3_tokenizer_cursor base;
-
- UBreakIterator *pIter; /* ICU break-iterator object */
- int nChar; /* Number of UChar elements in pInput */
- UChar *aChar; /* Copy of input using utf-16 encoding */
- int *aOffset; /* Offsets of each character in utf-8 input */
-
- int nBuffer;
- char *zBuffer;
-
- int iToken;
-};
-
-/*
-** Create a new tokenizer instance.
-*/
-static int icuCreate(
- int argc, /* Number of entries in argv[] */
- const char * const *argv, /* Tokenizer creation arguments */
- sqlite3_tokenizer **ppTokenizer /* OUT: Created tokenizer */
-){
- IcuTokenizer *p;
- int n = 0;
-
- if( argc>0 ){
- n = strlen(argv[0])+1;
- }
- p = (IcuTokenizer *)sqlite3_malloc64(sizeof(IcuTokenizer)+n);
- if( !p ){
- return SQLITE_NOMEM;
- }
- memset(p, 0, sizeof(IcuTokenizer));
-
- if( n ){
- p->zLocale = (char *)&p[1];
- memcpy(p->zLocale, argv[0], n);
- }
-
- *ppTokenizer = (sqlite3_tokenizer *)p;
-
- return SQLITE_OK;
-}
-
-/*
-** Destroy a tokenizer
-*/
-static int icuDestroy(sqlite3_tokenizer *pTokenizer){
- IcuTokenizer *p = (IcuTokenizer *)pTokenizer;
- sqlite3_free(p);
- return SQLITE_OK;
-}
-
-/*
-** 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 icuOpen(
- sqlite3_tokenizer *pTokenizer, /* The tokenizer */
- const char *zInput, /* Input string */
- int nInput, /* Length of zInput in bytes */
- sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */
-){
- IcuTokenizer *p = (IcuTokenizer *)pTokenizer;
- IcuCursor *pCsr;
-
- const int32_t opt = U_FOLD_CASE_DEFAULT;
- UErrorCode status = U_ZERO_ERROR;
- int nChar;
-
- UChar32 c;
- int iInput = 0;
- int iOut = 0;
-
- *ppCursor = 0;
-
- if( zInput==0 ){
- nInput = 0;
- zInput = "";
- }else if( nInput<0 ){
- nInput = strlen(zInput);
- }
- nChar = nInput+1;
- pCsr = (IcuCursor *)sqlite3_malloc64(
- sizeof(IcuCursor) + /* IcuCursor */
- ((nChar+3)&~3) * sizeof(UChar) + /* IcuCursor.aChar[] */
- (nChar+1) * sizeof(int) /* IcuCursor.aOffset[] */
- );
- if( !pCsr ){
- return SQLITE_NOMEM;
- }
- memset(pCsr, 0, sizeof(IcuCursor));
- pCsr->aChar = (UChar *)&pCsr[1];
- pCsr->aOffset = (int *)&pCsr->aChar[(nChar+3)&~3];
-
- pCsr->aOffset[iOut] = iInput;
- U8_NEXT(zInput, iInput, nInput, c);
- while( c>0 ){
- int isError = 0;
- c = u_foldCase(c, opt);
- U16_APPEND(pCsr->aChar, iOut, nChar, c, isError);
- if( isError ){
- sqlite3_free(pCsr);
- return SQLITE_ERROR;
- }
- pCsr->aOffset[iOut] = iInput;
-
- if( iInputpIter = ubrk_open(UBRK_WORD, p->zLocale, pCsr->aChar, iOut, &status);
- if( !U_SUCCESS(status) ){
- sqlite3_free(pCsr);
- return SQLITE_ERROR;
- }
- pCsr->nChar = iOut;
-
- ubrk_first(pCsr->pIter);
- *ppCursor = (sqlite3_tokenizer_cursor *)pCsr;
- return SQLITE_OK;
-}
-
-/*
-** Close a tokenization cursor previously opened by a call to icuOpen().
-*/
-static int icuClose(sqlite3_tokenizer_cursor *pCursor){
- IcuCursor *pCsr = (IcuCursor *)pCursor;
- ubrk_close(pCsr->pIter);
- sqlite3_free(pCsr->zBuffer);
- sqlite3_free(pCsr);
- return SQLITE_OK;
-}
-
-/*
-** Extract the next token from a tokenization cursor.
-*/
-static int icuNext(
- sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by simpleOpen */
- const char **ppToken, /* OUT: *ppToken is the token text */
- int *pnBytes, /* OUT: Number of bytes in token */
- int *piStartOffset, /* OUT: Starting offset of token */
- int *piEndOffset, /* OUT: Ending offset of token */
- int *piPosition /* OUT: Position integer of token */
-){
- IcuCursor *pCsr = (IcuCursor *)pCursor;
-
- int iStart = 0;
- int iEnd = 0;
- int nByte = 0;
-
- while( iStart==iEnd ){
- UChar32 c;
-
- iStart = ubrk_current(pCsr->pIter);
- iEnd = ubrk_next(pCsr->pIter);
- if( iEnd==UBRK_DONE ){
- return SQLITE_DONE;
- }
-
- while( iStartaChar, iWhite, pCsr->nChar, c);
- if( u_isspace(c) ){
- iStart = iWhite;
- }else{
- break;
- }
- }
- assert(iStart<=iEnd);
- }
-
- do {
- UErrorCode status = U_ZERO_ERROR;
- if( nByte ){
- char *zNew = sqlite3_realloc(pCsr->zBuffer, nByte);
- if( !zNew ){
- return SQLITE_NOMEM;
- }
- pCsr->zBuffer = zNew;
- pCsr->nBuffer = nByte;
- }
-
- u_strToUTF8(
- pCsr->zBuffer, pCsr->nBuffer, &nByte, /* Output vars */
- &pCsr->aChar[iStart], iEnd-iStart, /* Input vars */
- &status /* Output success/failure */
- );
- } while( nByte>pCsr->nBuffer );
-
- *ppToken = pCsr->zBuffer;
- *pnBytes = nByte;
- *piStartOffset = pCsr->aOffset[iStart];
- *piEndOffset = pCsr->aOffset[iEnd];
- *piPosition = pCsr->iToken++;
-
- return SQLITE_OK;
-}
-
-/*
-** The set of routines that implement the simple tokenizer
-*/
-static const sqlite3_tokenizer_module icuTokenizerModule = {
- 0, /* iVersion */
- icuCreate, /* xCreate */
- icuDestroy, /* xCreate */
- icuOpen, /* xOpen */
- icuClose, /* xClose */
- icuNext, /* xNext */
- 0, /* xLanguageid */
-};
-
-/*
-** Set *ppModule to point at the implementation of the ICU tokenizer.
-*/
-SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(
- sqlite3_tokenizer_module const**ppModule
-){
- *ppModule = &icuTokenizerModule;
-}
-
-#endif /* defined(SQLITE_ENABLE_ICU) */
-#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */
-
-/************** End of fts3_icu.c ********************************************/
/************** Begin file sqlite3rbu.c **************************************/
/*
** 2014 August 30
@@ -245165,28 +244915,31 @@ CODEC_STATIC int sqlite3CodecSetIter(KeyContext *keyCtx, int iter){
#define CIPHER_NAME_AES_256_CBC "aes-256-cbc"
#define CIPHER_NAME_AES_256_GCM "aes-256-gcm"
-static int g_defaultAttachKdfIter = 10000;
-static int g_defaultAttachCipher = CIPHER_ID_AES_256_GCM;
-static int g_defaultAttachHmacAlgo = DEFAULT_HMAC_ALGORITHM;
-static int g_defaultAttachKdfAlgo = DEFAULT_KDF_ALGORITHM;
-static int g_defaultAttachPageSize = DEFAULT_PAGE_SIZE;
-
struct CodecCipherNameId {
int cipherId;
const char *cipherName;
};
-struct CodecCipherNameId g_cipherNameIdMap[CIPHER_TOTAL_NUM] = {
+static const struct CodecCipherNameId g_cipherNameIdMap[CIPHER_TOTAL_NUM] = {
{ CIPHER_ID_AES_256_CBC, CIPHER_NAME_AES_256_CBC },
{ CIPHER_ID_AES_256_GCM, CIPHER_NAME_AES_256_GCM }
};
-CODEC_STATIC void sqlite3CodecSetDefaultAttachCipher(const char *cipherName){
+SQLITE_PRIVATE void sqlite3CodecResetParameters(CodecParameter *p)
+{
+ p->kdfIter = DEFAULT_ITER;
+ p->pageSize = DEFAULT_PAGE_SIZE;
+ p->cipher = CIPHER_ID_AES_256_GCM;
+ p->hmacAlgo = DEFAULT_HMAC_ALGORITHM;
+ p->kdfAlgo = DEFAULT_KDF_ALGORITHM;
+}
+
+CODEC_STATIC void sqlite3CodecSetDefaultAttachCipher(CodecParameter *parm, const char *cipherName){
int i;
for( i=0; icipher = g_cipherNameIdMap[i].cipherId;
sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
return;
}
@@ -245194,67 +244947,67 @@ CODEC_STATIC void sqlite3CodecSetDefaultAttachCipher(const char *cipherName){
sqlite3_log(SQLITE_WARNING, "invalid attach cipher algorithm");
}
-CODEC_STATIC const char *sqlite3CodecGetDefaultAttachCipher(){
+CODEC_STATIC const char *sqlite3CodecGetDefaultAttachCipher(CodecParameter *parm){
const char *attachedCipher = CIPHER_NAME_AES_256_GCM;
sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
- if( (g_defaultAttachCipher>=0) && (g_defaultAttachCiphercipher>=0) && (parm->ciphercipher].cipherName;
}
sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
return attachedCipher;
}
-CODEC_STATIC void sqlite3CodecSetDefaultAttachKdfIter(int iter){
+CODEC_STATIC void sqlite3CodecSetDefaultAttachKdfIter(CodecParameter *parm, int iter){
if( iter<=0 ){
return;
}
sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
- g_defaultAttachKdfIter = iter;
+ parm->kdfIter = iter;
sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
}
-CODEC_STATIC int sqlite3CodecGetDefaultAttachKdfIter(){
+CODEC_STATIC int sqlite3CodecGetDefaultAttachKdfIter(CodecParameter *parm){
sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
- int iterNum = g_defaultAttachKdfIter;
+ int iterNum = parm->kdfIter;
sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
return iterNum;
}
-CODEC_STATIC void sqlite3CodecSetDefaultAttachHmacAlgo(int hmacAlgo){
+CODEC_STATIC void sqlite3CodecSetDefaultAttachHmacAlgo(CodecParameter *parm, int hmacAlgo){
sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
- g_defaultAttachHmacAlgo = hmacAlgo;
+ parm->hmacAlgo = hmacAlgo;
sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
}
-CODEC_STATIC int sqlite3CodecGetDefaultAttachHmacAlgo(){
+CODEC_STATIC int sqlite3CodecGetDefaultAttachHmacAlgo(CodecParameter *parm){
sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
- int hmacAlgo = g_defaultAttachHmacAlgo;
+ int hmacAlgo = parm->hmacAlgo;
sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
return hmacAlgo;
}
-CODEC_STATIC void sqlite3CodecSetDefaultAttachKdfAlgo(int kdfAlgo){
+CODEC_STATIC void sqlite3CodecSetDefaultAttachKdfAlgo(CodecParameter *parm, int kdfAlgo){
sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
- g_defaultAttachKdfAlgo = kdfAlgo;
+ parm->kdfAlgo = kdfAlgo;
sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
}
-CODEC_STATIC int sqlite3CodecGetDefaultAttachKdfAlgo(){
+CODEC_STATIC int sqlite3CodecGetDefaultAttachKdfAlgo(CodecParameter *parm){
sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
- int kdfAlgo = g_defaultAttachKdfAlgo;
+ int kdfAlgo = parm->kdfAlgo;
sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
return kdfAlgo;
}
-CODEC_STATIC void sqlite3CodecSetDefaultAttachPageSize(int pageSize){
+CODEC_STATIC void sqlite3CodecSetDefaultAttachPageSize(CodecParameter *parm, int pageSize){
sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
- g_defaultAttachPageSize = pageSize;
+ parm->pageSize = pageSize;
sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
}
-CODEC_STATIC int sqlite3CodecGetDefaultAttachPageSize(){
+CODEC_STATIC int sqlite3CodecGetDefaultAttachPageSize(CodecParameter *parm){
sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
- int pageSize = g_defaultAttachPageSize;
+ int pageSize = parm->pageSize;
sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
return pageSize;
}
@@ -245341,8 +245094,7 @@ CODEC_STATIC int sqlite3CodecCopyKeyContext(KeyContext *input, KeyContext *outpu
// You should clear key context before you call this function
#ifdef SQLITE_CODEC_ATTACH_CHANGED
-CODEC_STATIC int sqlite3CodecInitKeyContext(CodecContext *ctx, Btree *p, const void *zKey, int nKey, int attachFlag,
- int hmacAlgo){
+CODEC_STATIC int sqlite3CodecInitKeyContext(CodecContext *ctx, Btree *p, const void *zKey, int nKey, int attachFlag){
#else
CODEC_STATIC int sqlite3CodecInitKeyContext(CodecContext *ctx, Btree *p, const void *zKey, int nKey){
#endif
@@ -245350,16 +245102,18 @@ CODEC_STATIC int sqlite3CodecInitKeyContext(CodecContext *ctx, Btree *p, const v
KeyContext *keyCtx = ctx->readCtx;
#ifdef SQLITE_CODEC_ATTACH_CHANGED
if( attachFlag!=0 ){
- rc = sqlite3CodecSetCodecConstant(keyCtx, sqlite3CodecGetDefaultAttachCipher());
- rc += sqlite3CodecSetIter(keyCtx, sqlite3CodecGetDefaultAttachKdfIter());
+ CodecParameter *parm = &p->db->codecParm;
+ int hmacAlgo = sqlite3CodecGetDefaultAttachHmacAlgo(parm);
+ rc = sqlite3CodecSetCodecConstant(keyCtx, sqlite3CodecGetDefaultAttachCipher(parm));
+ rc += sqlite3CodecSetIter(keyCtx, sqlite3CodecGetDefaultAttachKdfIter(parm));
if( hmacAlgo!=0 ){
rc += sqlite3CodecSetHmacAlgorithm(keyCtx, hmacAlgo);
}
- int attachKdfAlgo = sqlite3CodecGetDefaultAttachKdfAlgo();
+ int attachKdfAlgo = sqlite3CodecGetDefaultAttachKdfAlgo(parm);
if( attachKdfAlgo!=0 ){
rc += sqlite3CodecSetKdfAlgorithm(keyCtx, attachKdfAlgo);
}
- int cipherPageSize = sqlite3CodecGetDefaultAttachPageSize();
+ int cipherPageSize = sqlite3CodecGetDefaultAttachPageSize(parm);
if( cipherPageSize!=0 ){
rc += sqlite3CodecSetCipherPageSize(ctx, cipherPageSize);
if ( rc != SQLITE_OK ) {
@@ -245413,31 +245167,32 @@ CODEC_STATIC void sqlite3CodecFreeContext(CodecContext *ctx){
#ifdef SQLITE_CODEC_ATTACH_CHANGED
CODEC_STATIC int sqlite3CodecInitContext(CodecContext *ctx, Btree *p, const void *zKey, int nKey, int nDb){
int attachFlag = (nDb > 1) ? 1 : 0;
+ int defaultPageSz = attachFlag ? p->db->codecParm.pageSize : DEFAULT_PAGE_SIZE;
#else
CODEC_STATIC int sqlite3CodecInitContext(CodecContext *ctx, Btree *p, const void *zKey, int nKey){
+ int defaultPageSz = DEFAULT_PAGE_SIZE;
#endif
sqlite3_file *fd = p->pBt->pPager->fd;
ctx->pBt = p;
ctx->savePassword = 0;
- ctx->buffer = (unsigned char *)sqlite3Malloc(DEFAULT_PAGE_SIZE);
+ ctx->buffer = (unsigned char *)sqlite3Malloc(defaultPageSz);
ctx->readCtx = (KeyContext *)sqlite3Malloc(sizeof(KeyContext));
ctx->writeCtx = (KeyContext *)sqlite3Malloc(sizeof(KeyContext));
if(ctx->buffer == NULL || ctx->readCtx == NULL || ctx->writeCtx == NULL){
sqlite3CodecFreeContext(ctx);
return SQLITE_NOMEM;
}
- errno_t memsetRc = memset_s(ctx->buffer, DEFAULT_PAGE_SIZE, 0, DEFAULT_PAGE_SIZE);
+ errno_t memsetRc = memset_s(ctx->buffer, defaultPageSz, 0, defaultPageSz);
memsetRc += memset_s(ctx->readCtx, sizeof(KeyContext), 0, sizeof(KeyContext));
memsetRc += memset_s(ctx->writeCtx, sizeof(KeyContext), 0, sizeof(KeyContext));
if(memsetRc != EOK){
sqlite3CodecFreeContext(ctx);
return SQLITE_ERROR;
}
- ctx->readCtx->codecConst.cipherPageSize = DEFAULT_PAGE_SIZE;
- ctx->writeCtx->codecConst.cipherPageSize = DEFAULT_PAGE_SIZE;
+ ctx->readCtx->codecConst.cipherPageSize = defaultPageSz;
+ ctx->writeCtx->codecConst.cipherPageSize = defaultPageSz;
#ifdef SQLITE_CODEC_ATTACH_CHANGED
- int attachHmacAlgo = sqlite3CodecGetDefaultAttachHmacAlgo();
- int rc = sqlite3CodecInitKeyContext(ctx, p, zKey, nKey, attachFlag, attachHmacAlgo);
+ int rc = sqlite3CodecInitKeyContext(ctx, p, zKey, nKey, attachFlag);
#else
int rc = sqlite3CodecInitKeyContext(ctx, p, zKey, nKey);
#endif
@@ -245879,11 +245634,12 @@ int sqlite3CodecPragma(sqlite3 *db, int iDb, Parse *parse, const char *zLeft, co
}
CodecContext *ctx = (CodecContext *)(p->pBt->pPager->pCodec);
#ifdef SQLITE_CODEC_ATTACH_CHANGED
+ CodecParameter *parm = &db->codecParm;
if(sqlite3StrICmp(zLeft, "cipher_default_attach_cipher") == 0 && zRight != NULL){
- (void)sqlite3CodecSetDefaultAttachCipher(zRight);
+ (void)sqlite3CodecSetDefaultAttachCipher(parm, zRight);
return 1;
}else if(sqlite3StrICmp(zLeft, "cipher_default_attach_kdf_iter") == 0 && zRight != NULL){
- (void)sqlite3CodecSetDefaultAttachKdfIter(atoi(zRight));
+ (void)sqlite3CodecSetDefaultAttachKdfIter(parm, atoi(zRight));
return 1;
}else if( sqlite3StrICmp(zLeft, "cipher_default_attach_hmac_algo")==0 && zRight!=NULL ){
/*
@@ -245891,31 +245647,31 @@ int sqlite3CodecPragma(sqlite3 *db, int iDb, Parse *parse, const char *zLeft, co
** This behavior is to ensure backward compatible.
*/
if( sqlite3_stricmp(zRight, CIPHER_HMAC_ALGORITHM_NAME_SHA1)==0 ){
- sqlite3CodecSetDefaultAttachHmacAlgo(CIPHER_HMAC_ALGORITHM_SHA1);
- sqlite3CodecSetDefaultAttachKdfAlgo(CIPHER_KDF_ALGORITHM_SHA1);
+ sqlite3CodecSetDefaultAttachHmacAlgo(parm, CIPHER_HMAC_ALGORITHM_SHA1);
+ sqlite3CodecSetDefaultAttachKdfAlgo(parm, CIPHER_KDF_ALGORITHM_SHA1);
}else if( sqlite3_stricmp(zRight, CIPHER_HMAC_ALGORITHM_NAME_SHA256)==0 ){
- sqlite3CodecSetDefaultAttachHmacAlgo(CIPHER_HMAC_ALGORITHM_SHA256);
- sqlite3CodecSetDefaultAttachKdfAlgo(CIPHER_KDF_ALGORITHM_SHA256);
+ sqlite3CodecSetDefaultAttachHmacAlgo(parm, CIPHER_HMAC_ALGORITHM_SHA256);
+ sqlite3CodecSetDefaultAttachKdfAlgo(parm, CIPHER_KDF_ALGORITHM_SHA256);
}else if( sqlite3_stricmp(zRight, CIPHER_HMAC_ALGORITHM_NAME_SHA512)==0 ){
- sqlite3CodecSetDefaultAttachHmacAlgo(CIPHER_HMAC_ALGORITHM_SHA512);
- sqlite3CodecSetDefaultAttachKdfAlgo(CIPHER_KDF_ALGORITHM_SHA512);
+ sqlite3CodecSetDefaultAttachHmacAlgo(parm, CIPHER_HMAC_ALGORITHM_SHA512);
+ sqlite3CodecSetDefaultAttachKdfAlgo(parm, CIPHER_KDF_ALGORITHM_SHA512);
}else{
return 0;
}
return 1;
}else if( sqlite3StrICmp(zLeft, "cipher_default_attach_kdf_algo")==0 && zRight!=NULL ){
if( sqlite3_stricmp(zRight, CIPHER_KDF_ALGORITHM_NAME_SHA1)==0 ){
- sqlite3CodecSetDefaultAttachKdfAlgo(CIPHER_KDF_ALGORITHM_SHA1);
+ sqlite3CodecSetDefaultAttachKdfAlgo(parm, CIPHER_KDF_ALGORITHM_SHA1);
}else if( sqlite3_stricmp(zRight, CIPHER_KDF_ALGORITHM_NAME_SHA256)==0 ){
- sqlite3CodecSetDefaultAttachKdfAlgo(CIPHER_KDF_ALGORITHM_SHA256);
+ sqlite3CodecSetDefaultAttachKdfAlgo(parm, CIPHER_KDF_ALGORITHM_SHA256);
}else if( sqlite3_stricmp(zRight, CIPHER_KDF_ALGORITHM_NAME_SHA512)==0 ){
- sqlite3CodecSetDefaultAttachKdfAlgo(CIPHER_KDF_ALGORITHM_SHA512);
+ sqlite3CodecSetDefaultAttachKdfAlgo(parm, CIPHER_KDF_ALGORITHM_SHA512);
}else{
return 0;
}
return 1;
}else if( sqlite3StrICmp(zLeft, "cipher_default_attach_page_size")==0 && zRight!=NULL ){
- (void)sqlite3CodecSetDefaultAttachPageSize(atoi(zRight));
+ (void)sqlite3CodecSetDefaultAttachPageSize(parm, atoi(zRight));
return 1;
}
#endif
@@ -246222,114 +245978,248 @@ export_finish:
#endif
#if SQLITE_OS_UNIX
-#define DB_LOCK_NUM 3
-#define DB_SHARED_LOCK_OFFSET 2
-#define WAL_LOCK_NUM 8
-#define WAL_READ_LOCK_POS 3
-#define LOCK_NUM (DB_LOCK_NUM+WAL_LOCK_NUM)
-static void printLockInfo(unixFile *uFile_db, int walStat)
+#include
+#include
+static inline int OsGetTid(void)
+{
+#if defined(__linux__)
+ return (int)syscall(__NR_gettid);
+#elif defined(__APPLE__)
+ return (int)syscall(SYS_thread_selfid);
+#else
+ return 0;
+#endif
+}
+
+static void ResetLockStatus(void)
+{
+ (void)memset(&g_lockStatus, 0, sizeof(g_lockStatus));
+}
+/*
+** Record lock info, correspond wal aLock buf, 1 aLock: 1
+*/
+static void TryRecordTid(int *tidBuf, int ofs, int lockLen)
+{
+ int lockOfs = ofs + lockLen;
+ for (int i = ofs; i < lockOfs; i++) {
+ if (tidBuf[i] == 0) {
+ tidBuf[i] = OsGetTid();
+ }
+ }
+}
+/*
+** Clear locks info.
+*/
+static void TryClearTid(int *tidBuf, int ofs, int lockLen)
+{
+ int lockOfs = ofs + lockLen;
+ for (int i = ofs; i < lockOfs; i++) {
+ if (tidBuf[i] == OsGetTid()) {
+ tidBuf[i] = 0;
+ }
+ }
+}
+
+static void MarkLockBusy(u32 lockIdx, u32 lockLen, u8 lockType, u8 lockByProcess)
{
- const char *lock_type[DB_LOCK_NUM] = {"F_RDLCK", "F_WRLCK", "F_UNLCK"};
+ g_lockStatus.busyLockIdx = lockIdx;
+ g_lockStatus.busyLockType = lockType;
+ g_lockStatus.lockByProcess = lockByProcess;
+ g_lockStatus.lockLen = lockLen;
+}
- sqlite3_log(SQLITE_WARNING, "*** SQLITE_LOG DB Lock ***");
- if( uFile_db==NULL ){
- sqlite3_log(SQLITE_WARNING, "NO DB FILE !");
+static void MarkLockStatus(u32 lockIdx, u32 lockLen, u8 lockType)
+{
+ if ((lockIdx + lockLen) > MAX_LOCK_NUM || lockLen == 0) {
+ sqlite3_log(SQLITE_ERROR, "Unexpect lock index %u lockLen %d!", lockIdx, lockLen);
return;
}
- unixInodeInfo *inode = uFile_db->pInode;
- if( inode==NULL ){
- sqlite3_log(SQLITE_ERROR, "Inode is NULL !");
+ if (lockIdx == g_lockStatus.busyLockIdx) {
+ g_lockStatus.busyLockIdx = 0;
+ g_lockStatus.lockLen = 0;
+ }
+ if (lockLen == 1) {
+ g_lockStatus.lockStatus[lockIdx] = lockType;
+ } else {
+ size_t len = sizeof(u8) * lockLen;
+ (void)memset(&g_lockStatus.lockStatus[lockIdx], lockType, len);
+ }
+}
+
+static void MarkLockStatusByRc(int rc, u32 lockIdx, u32 lockLen, u8 lockType, u8 lockByProcess)
+{
+ if (rc == SQLITE_BUSY) {
+ MarkLockBusy(lockIdx, lockLen, lockType, lockByProcess);
return;
}
- sqlite3_log(SQLITE_WARNING, "fileLock %d inodeRef %d inodeLockCnt %d inodeFileLock %d inodeProcessLock %d",
- uFile_db->eFileLock, inode->nRef, inode->nLock, inode->eFileLock, inode->bProcessLock);
-
- /* File Lock Info */
- int fd_db = uFile_db->h;
- const char *lock_name_list[LOCK_NUM] = {"pending lock ",
- "reserved lock",
- "shared lock ",
- "wal_write lock ",
- "wal_ckpt lock ",
- "wal_recover lock"};
- for(int i=DB_LOCK_NUM+WAL_READ_LOCK_POS; i DUMP_MAX_STR_LEN; i++) {
+ if (lockStatus[i] != NO_LOCK) {
+ tmp[0] = '\0';
+ sqlite3_snprintf(availLen, tmp, "<%s, %s>", IdxToLockName((u32)i), TrxLockName(lockStatus[i]));
+ int len = strlen(tmp);
+ tmp += len;
+ availLen -= len;
}
- start_pos++;
}
-
- /* thread WAL Lock Info */
- if( uFile_db->pShm==NULL ){
+ sqlite3_log(SQLITE_WARNING_DUMP, "[SQLite]BusyLine:%d, idx:%d, type:%d, fileLock:%d, len:%d, handleLocks:%s",
+ g_lockStatus.busyLine, g_lockStatus.busyLockIdx, g_lockStatus.busyLockType, g_lockStatus.lockByProcess,
+ g_lockStatus.lockLen, tmp != dumpBuf ? dumpBuf : "none");
+}
+
+static const char *FlockToName(int l_type)
+{
+ return l_type == F_RDLCK ? "F_RDLCK" :
+ l_type == F_WRLCK ? "F_WRLCK" :
+ l_type == F_UNLCK ? "F_UNLCK" : "F_UNKNOWN";
+}
+
+static int DumpProcessLocks(int fd, struct flock *lock, const char *lockName, char *dumpBuf, int bufLen)
+{
+ dumpBuf[0] = '\0';
+ if (osFcntl(fd, F_GETLK, lock) != SQLITE_OK) {
+ sqlite3_log(SQLITE_ERROR, "[SQLite]Get wal file lock ofs %u failed, errno: %d", lock->l_start, errno);
+ return 0;
+ }
+ if (lock->l_type != F_UNLCK) {
+ sqlite3_snprintf(bufLen, dumpBuf, "<%s, pid:%u, %s>", lockName, lock->l_pid, FlockToName(lock->l_type));
+ return strlen(dumpBuf);
+ }
+ return 0;
+}
+
+static void DumpTrxProcessLocks(unixFile *file, char *dumpBuf, int dumpBufLen)
+{
+ unixInodeInfo *inode = file->pInode;
+ if (inode == NULL) {
+ sqlite3_log(SQLITE_ERROR, "[SQLite]Inode is null!");
return;
}
- for(int i=0; ipShm->pShmNode->aLock[i] ){
- sqlite3_log(SQLITE_WARNING, "Local WAL Lock[%d] for DB file: %d", i, uFile_db->pShm->pShmNode->aLock[i]);
+ sqlite3_log(SQLITE_WARNING_DUMP, "[SQLite]acqLock:%s, dbRef:%d, lockCnt:%d, curLock:%s, processLock:%d",
+ TrxLockName(file->eFileLock), inode->nRef, inode->nLock, TrxLockName(inode->eFileLock), inode->bProcessLock);
+ const char *lockName[DB_LOCK_NUM] = {"pending", "reserved", "shared_first"};
+ char *tmp = dumpBuf;
+ int availLen = dumpBufLen - 1;
+ dumpBuf[availLen] = '\0';
+ for (int i = 0; i < DB_LOCK_NUM && availLen > DUMP_MAX_STR_LEN; i++) {
+ off_t ofs = i + PENDING_BYTE;
+ off_t lockLen = (ofs == SHARED_FIRST) ? SHARED_SIZE : 1;
+ struct flock lock = {.l_type = F_WRLCK, .l_start = ofs, .l_len = lockLen, .l_whence = SEEK_SET};
+ int lockBufLen = DumpProcessLocks(file->h, &lock, lockName[i], tmp, availLen);
+ tmp += lockBufLen;
+ availLen -= lockBufLen;
+ }
+ if (tmp != dumpBuf) {
+ sqlite3_log(SQLITE_WARNING_DUMP, "[SQLite]Trx locks: %s", dumpBuf);
+ }
+}
+
+static void DumpWalLocks(unixFile *file, u8 walEnabled, char *dumpBuf, int dumpBufLen)
+{
+ if (!walEnabled || file->pShm == NULL || file->pShm->pShmNode == NULL) {
+ sqlite3_log(SQLITE_ERROR, "[SQLite]Wal mode disabled!");
+ return;
+ }
+ unixShmNode *pShmNode = file->pShm->pShmNode;
+ char *tmp = dumpBuf;
+ int availLen = dumpBufLen - 1;
+ dumpBuf[availLen] = '\0';
+ for (int i = 0; i < WAL_LOCK_NUM && availLen > DUMP_MAX_STR_LEN; i++) {
+ if (pShmNode->aLock[i] && i < SQLITE_SHM_NLOCK) {
+ tmp[0] = '\0';
+ sqlite3_snprintf(availLen, tmp, "<%s, %d, tid:%d>", IdxToLockName((u32)i), pShmNode->aLock[i],
+ pShmNode->aLockTid[i]);
+ int strLen = strlen(tmp);
+ tmp += strLen;
+ availLen -= strLen;
}
+ off_t ofs = i + WALINDEX_LOCK_OFFSET;
+ struct flock lock = {.l_type = F_WRLCK, .l_start = ofs, .l_len = 1, .l_whence = SEEK_SET};
+ int bufLen = DumpProcessLocks(pShmNode->hShm, &lock, IdxToLockName((u32)i), tmp, availLen);
+ tmp += bufLen;
+ availLen -= bufLen;
+ }
+ if (tmp != dumpBuf) {
+ sqlite3_log(SQLITE_WARNING_DUMP, "[SQLite]Wal locks: %s", dumpBuf);
+ }
+}
+
+static void DumpLocksInfo(unixFile *file, int walEnabled)
+{
+ char *dumpBuf = sqlite3Malloc(DUMP_BUF_MAX_LEN);
+ if (dumpBuf == NULL) {
+ sqlite3_log(SQLITE_ERROR, "[SQLite]Can't alloc bufferSz %d for dump!", DUMP_BUF_MAX_LEN);
+ return;
}
+ DumpHandleLock(dumpBuf, DUMP_BUF_MAX_LEN);
+ DumpTrxProcessLocks(file, dumpBuf, DUMP_BUF_MAX_LEN);
+ DumpWalLocks(file, walEnabled, dumpBuf, DUMP_BUF_MAX_LEN);
+ sqlite3_free(dumpBuf);
}
#ifndef SQLITE_OMIT_WAL
-static void printLockInfoUsingWal(Wal *pWal)
+static void DumpLocksByWal(Wal *pWal)
{
- if( pWal==NULL ){
+ if (pWal == NULL) {
sqlite3_log(SQLITE_ERROR, "Wal ptr is NULL!");
return;
}
- if( pWal->pVfs==NULL || sqlite3_stricmp(pWal->pVfs->zName, "unix")!=0 ){
+ if (pWal->pVfs == NULL || sqlite3_stricmp(pWal->pVfs->zName, "unix") != 0) {
return;
}
- printLockInfo((unixFile *)(pWal->pDbFd), 1);
+ DumpLocksInfo((unixFile *)(pWal->pDbFd), 1);
}
#endif /* #ifndef SQLITE_OMIT_WAL */
-static void printLockInfoUsingPager(Pager *pPager)
+static void DumpLocksByPager(Pager *pPager)
{
- if( pPager==NULL ){
+ if (pPager == NULL) {
sqlite3_log(SQLITE_ERROR, "Pager ptr is NULL!");
return;
}
- if( pPager->pVfs==NULL || sqlite3_stricmp(pPager->pVfs->zName, "unix")!=0 ){
+ if (pPager->pVfs == NULL || sqlite3_stricmp(pPager->pVfs->zName, "unix") != 0) {
return;
}
#ifndef SQLITE_OMIT_WAL
- printLockInfo((unixFile *)(pPager->fd), pPager->pWal!=NULL);
+ DumpLocksInfo((unixFile *)(pPager->fd), pPager->pWal != NULL);
#else /* #ifndef SQLITE_OMIT_WAL */
- printLockInfo((unixFile *)(pPager->fd), 0);
+ DumpLocksInfo((unixFile *)(pPager->fd), 0);
#endif /* #ifndef SQLITE_OMIT_WAL */
}
#endif /* SQLITE_OS_UNIX */
-// hw export the symbols
-#ifdef SQLITE_EXPORT_SYMBOLS
-#if defined(__GNUC__)
-# define EXPORT_SYMBOLS __attribute__ ((visibility ("default")))
-#elif defined(_MSC_VER)
-# define EXPORT_SYMBOLS __declspec(dllexport)
-#else
-# define EXPORT_SYMBOLS
-#endif
struct sqlite3_api_routines_hw {
int (*initialize)();
int (*config)(int,...);
diff --git a/src/sqlite3icu.c b/src/sqlite3icu.c
new file mode 100644
index 0000000000000000000000000000000000000000..8495098582bac3e0f93786e3ee614a01ca13c1c9
--- /dev/null
+++ b/src/sqlite3icu.c
@@ -0,0 +1,892 @@
+/******************************************************************************
+** This file is an amalgamation of many separate C source files from SQLite
+** version 3.40.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
+** of 5% or more are commonly seen when SQLite is compiled as a single
+** translation unit.
+**
+** This file is all you need to compile SQLite. To use SQLite in other
+** programs, you need this file and the "sqlite3.h" header file that defines
+** the programming interface to the SQLite library. (If you do not have
+** the "sqlite3.h" header file at hand, you will find a copy embedded within
+** the text of this file. Search for "Begin file sqlite3.h" to find the start
+** of the embedded sqlite3.h header file.) Additional code files may be needed
+** if you want a wrapper to interface SQLite with your choice of programming
+** language. The code for the "sqlite3" command-line shell is also in a
+** separate file. This file contains only code for the core SQLite library.
+*/
+/*
+** 2019.09.02-Complete codec logic for encryption and decryption.
+** Huawei Technologies Co, Ltd.
+*/
+/************** Begin file icu.c *********************************************/
+/*
+** 2007 May 6
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** $Id: icu.c,v 1.7 2007/12/13 21:54:11 drh Exp $
+**
+** This file implements an integration between the ICU library
+** ("International Components for Unicode", an open-source library
+** for handling unicode data) and SQLite. The integration uses
+** ICU to provide the following to SQLite:
+**
+** * An implementation of the SQL regexp() function (and hence REGEXP
+** operator) using the ICU uregex_XX() APIs.
+**
+** * Implementations of the SQL scalar upper() and lower() functions
+** for case mapping.
+**
+** * Integration of ICU and SQLite collation sequences.
+**
+** * An implementation of the LIKE operator that uses ICU to
+** provide case-independent matching.
+*/
+#include
+#include
+#include
+#include
+#include
+
+#include "sqlite3icu.h"
+#include "sqlite3.h"
+
+#ifdef HARMONY_OS
+#include "common/unicode/putil.h"
+#endif
+
+#if !defined(SQLITE_CORE) \
+ || defined(SQLITE_ENABLE_ICU) \
+ || defined(SQLITE_ENABLE_ICU_COLLATIONS)
+
+/* Include ICU headers */
+#include
+#include
+#include
+#include
+
+#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
+ /* This case when the file really is being compiled as a loadable
+ ** extension */
+# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
+# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
+# define SQLITE_EXTENSION_INIT3 \
+ extern const sqlite3_api_routines *sqlite3_api;
+#else
+ /* This case when the file is being statically linked into the
+ ** application */
+# define SQLITE_EXTENSION_INIT1 /*no-op*/
+# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
+# define SQLITE_EXTENSION_INIT3 /*no-op*/
+#endif
+
+/* #include */
+
+#ifndef SQLITE_CORE
+/* #include "sqlite3ext.h" */
+ SQLITE_EXTENSION_INIT1
+#else
+/* #include "sqlite3.h" */
+#endif
+
+// hw export the symbols
+#ifdef SQLITE_EXPORT_SYMBOLS
+#if defined(__GNUC__)
+# define EXPORT_SYMBOLS __attribute__ ((visibility ("default")))
+#elif defined(_MSC_VER)
+# define EXPORT_SYMBOLS __declspec(dllexport)
+#else
+# define EXPORT_SYMBOLS
+#endif
+#endif
+
+EXPORT_SYMBOLS SQLITE_API int sqlite3IcuInit(sqlite3 *db);
+#ifdef SQLITE_ENABLE_ICU
+EXPORT_SYMBOLS SQLITE_API void sqlite3Fts3IcuTokenizerModule(sqlite3_tokenizer_module const**ppModule);
+#endif
+/*
+** This function is called when an ICU function called from within
+** the implementation of an SQL scalar function returns an error.
+**
+** The scalar function context passed as the first argument is
+** loaded with an error message based on the following two args.
+*/
+static void icuFunctionError(
+ sqlite3_context *pCtx, /* SQLite scalar function context */
+ const char *zName, /* Name of ICU function that failed */
+ UErrorCode e /* Error code returned by ICU function */
+){
+ char zBuf[128];
+ sqlite3_snprintf(128, zBuf, "ICU error: %s(): %s", zName, u_errorName(e));
+ zBuf[127] = '\0';
+ sqlite3_result_error(pCtx, zBuf, -1);
+}
+
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU)
+
+/*
+** Maximum length (in bytes) of the pattern in a LIKE or GLOB
+** operator.
+*/
+#ifndef SQLITE_MAX_LIKE_PATTERN_LENGTH
+# define SQLITE_MAX_LIKE_PATTERN_LENGTH 50000
+#endif
+
+/*
+** Version of sqlite3_free() that is always a function, never a macro.
+*/
+static void xFree(void *p){
+ sqlite3_free(p);
+}
+
+/*
+** This lookup table is used to help decode the first byte of
+** a multi-byte UTF8 character. It is copied here from SQLite source
+** code file utf8.c.
+*/
+static const unsigned char icuUtf8Trans1[] = {
+ 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 SQLITE_ICU_READ_UTF8(zIn, c) \
+ c = *(zIn++); \
+ if( c>=0xc0 ){ \
+ c = icuUtf8Trans1[c-0xc0]; \
+ while( (*zIn & 0xc0)==0x80 ){ \
+ c = (c<<6) + (0x3f & *(zIn++)); \
+ } \
+ }
+
+#define SQLITE_ICU_SKIP_UTF8(zIn) \
+ assert( *zIn ); \
+ if( *(zIn++)>=0xc0 ){ \
+ while( (*zIn & 0xc0)==0x80 ){zIn++;} \
+ }
+
+
+/*
+** Compare two UTF-8 strings for equality where the first string is
+** a "LIKE" expression. Return true (1) if they are the same and
+** false (0) if they are different.
+*/
+static int icuLikeCompare(
+ const uint8_t *zPattern, /* LIKE pattern */
+ const uint8_t *zString, /* The UTF-8 string to compare against */
+ const UChar32 uEsc /* The escape character */
+){
+ static const uint32_t MATCH_ONE = (uint32_t)'_';
+ static const uint32_t MATCH_ALL = (uint32_t)'%';
+
+ int prevEscape = 0; /* True if the previous character was uEsc */
+
+ while( 1 ){
+
+ /* Read (and consume) the next character from the input pattern. */
+ uint32_t uPattern;
+ SQLITE_ICU_READ_UTF8(zPattern, uPattern);
+ if( uPattern==0 ) break;
+
+ /* There are now 4 possibilities:
+ **
+ ** 1. uPattern is an unescaped match-all character "%",
+ ** 2. uPattern is an unescaped match-one character "_",
+ ** 3. uPattern is an unescaped escape character, or
+ ** 4. uPattern is to be handled as an ordinary character
+ */
+ if( uPattern==MATCH_ALL && !prevEscape && uPattern!=(uint32_t)uEsc ){
+ /* Case 1. */
+ uint8_t c;
+
+ /* Skip any MATCH_ALL or MATCH_ONE characters that follow a
+ ** MATCH_ALL. For each MATCH_ONE, skip one character in the
+ ** test string.
+ */
+ while( (c=*zPattern) == MATCH_ALL || c == MATCH_ONE ){
+ if( c==MATCH_ONE ){
+ if( *zString==0 ) return 0;
+ SQLITE_ICU_SKIP_UTF8(zString);
+ }
+ zPattern++;
+ }
+
+ if( *zPattern==0 ) return 1;
+
+ while( *zString ){
+ if( icuLikeCompare(zPattern, zString, uEsc) ){
+ return 1;
+ }
+ SQLITE_ICU_SKIP_UTF8(zString);
+ }
+ return 0;
+
+ }else if( uPattern==MATCH_ONE && !prevEscape && uPattern!=(uint32_t)uEsc ){
+ /* Case 2. */
+ if( *zString==0 ) return 0;
+ SQLITE_ICU_SKIP_UTF8(zString);
+
+ }else if( uPattern==(uint32_t)uEsc && !prevEscape ){
+ /* Case 3. */
+ prevEscape = 1;
+
+ }else{
+ /* Case 4. */
+ uint32_t uString;
+ SQLITE_ICU_READ_UTF8(zString, uString);
+ uString = (uint32_t)u_foldCase((UChar32)uString, U_FOLD_CASE_DEFAULT);
+ uPattern = (uint32_t)u_foldCase((UChar32)uPattern, U_FOLD_CASE_DEFAULT);
+ if( uString!=uPattern ){
+ return 0;
+ }
+ prevEscape = 0;
+ }
+ }
+
+ return *zString==0;
+}
+
+/*
+** Implementation of the like() SQL function. This function implements
+** the build-in LIKE operator. The first argument to the function is the
+** pattern and the second argument is the string. So, the SQL statements:
+**
+** A LIKE B
+**
+** is implemented as like(B, A). If there is an escape character E,
+**
+** A LIKE B ESCAPE E
+**
+** is mapped to like(B, A, E).
+*/
+static void icuLikeFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ const unsigned char *zA = sqlite3_value_text(argv[0]);
+ const unsigned char *zB = sqlite3_value_text(argv[1]);
+ UChar32 uEsc = 0;
+
+ /* Limit the length of the LIKE or GLOB pattern to avoid problems
+ ** of deep recursion and N*N behavior in patternCompare().
+ */
+ if( sqlite3_value_bytes(argv[0])>SQLITE_MAX_LIKE_PATTERN_LENGTH ){
+ sqlite3_result_error(context, "LIKE or GLOB pattern too complex", -1);
+ return;
+ }
+
+
+ if( argc==3 ){
+ /* The escape character string must consist of a single UTF-8 character.
+ ** Otherwise, return an error.
+ */
+ int nE= sqlite3_value_bytes(argv[2]);
+ const unsigned char *zE = sqlite3_value_text(argv[2]);
+ int i = 0;
+ if( zE==0 ) return;
+ U8_NEXT(zE, i, nE, uEsc);
+ if( i!=nE){
+ sqlite3_result_error(context,
+ "ESCAPE expression must be a single character", -1);
+ return;
+ }
+ }
+
+ if( zA && zB ){
+ sqlite3_result_int(context, icuLikeCompare(zA, zB, uEsc));
+ }
+}
+
+/*
+** Function to delete compiled regexp objects. Registered as
+** a destructor function with sqlite3_set_auxdata().
+*/
+static void icuRegexpDelete(void *p){
+ URegularExpression *pExpr = (URegularExpression *)p;
+ uregex_close(pExpr);
+}
+
+/*
+** Implementation of SQLite REGEXP operator. This scalar function takes
+** two arguments. The first is a regular expression pattern to compile
+** the second is a string to match against that pattern. If either
+** argument is an SQL NULL, then NULL Is returned. Otherwise, the result
+** is 1 if the string matches the pattern, or 0 otherwise.
+**
+** SQLite maps the regexp() function to the regexp() operator such
+** that the following two are equivalent:
+**
+** zString REGEXP zPattern
+** regexp(zPattern, zString)
+**
+** Uses the following ICU regexp APIs:
+**
+** uregex_open()
+** uregex_matches()
+** uregex_close()
+*/
+static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){
+ UErrorCode status = U_ZERO_ERROR;
+ URegularExpression *pExpr;
+ UBool res;
+ const UChar *zString = sqlite3_value_text16(apArg[1]);
+
+ (void)nArg; /* Unused parameter */
+
+ /* If the left hand side of the regexp operator is NULL,
+ ** then the result is also NULL.
+ */
+ if( !zString ){
+ return;
+ }
+
+ pExpr = sqlite3_get_auxdata(p, 0);
+ if( !pExpr ){
+ const UChar *zPattern = sqlite3_value_text16(apArg[0]);
+ if( !zPattern ){
+ return;
+ }
+ pExpr = uregex_open(zPattern, -1, 0, 0, &status);
+
+ if( U_SUCCESS(status) ){
+ sqlite3_set_auxdata(p, 0, pExpr, icuRegexpDelete);
+ pExpr = sqlite3_get_auxdata(p, 0);
+ }
+ if( !pExpr ){
+ icuFunctionError(p, "uregex_open", status);
+ return;
+ }
+ }
+
+ /* Configure the text that the regular expression operates on. */
+ uregex_setText(pExpr, zString, -1, &status);
+ if( !U_SUCCESS(status) ){
+ icuFunctionError(p, "uregex_setText", status);
+ return;
+ }
+
+ /* Attempt the match */
+ res = uregex_matches(pExpr, 0, &status);
+ if( !U_SUCCESS(status) ){
+ icuFunctionError(p, "uregex_matches", status);
+ return;
+ }
+
+ /* Set the text that the regular expression operates on to a NULL
+ ** pointer. This is not really necessary, but it is tidier than
+ ** leaving the regular expression object configured with an invalid
+ ** pointer after this function returns.
+ */
+ uregex_setText(pExpr, 0, 0, &status);
+
+ /* Return 1 or 0. */
+ sqlite3_result_int(p, res ? 1 : 0);
+}
+
+/*
+** Implementations of scalar functions for case mapping - upper() and
+** lower(). Function upper() converts its input to upper-case (ABC).
+** Function lower() converts to lower-case (abc).
+**
+** ICU provides two types of case mapping, "general" case mapping and
+** "language specific". Refer to ICU documentation for the differences
+** between the two.
+**
+** To utilise "general" case mapping, the upper() or lower() scalar
+** functions are invoked with one argument:
+**
+** upper('ABC') -> 'abc'
+** lower('abc') -> 'ABC'
+**
+** To access ICU "language specific" case mapping, upper() or lower()
+** should be invoked with two arguments. The second argument is the name
+** of the locale to use. Passing an empty string ("") or SQL NULL value
+** as the second argument is the same as invoking the 1 argument version
+** of upper() or lower().
+**
+** lower('I', 'en_us') -> 'i'
+** lower('I', 'tr_tr') -> '\u131' (small dotless i)
+**
+** http://www.icu-project.org/userguide/posix.html#case_mappings
+*/
+static void icuCaseFunc16(sqlite3_context *p, int nArg, sqlite3_value **apArg){
+ const UChar *zInput; /* Pointer to input string */
+ UChar *zOutput = 0; /* Pointer to output buffer */
+ int nInput; /* Size of utf-16 input string in bytes */
+ int nOut; /* Size of output buffer in bytes */
+ int cnt;
+ int bToUpper; /* True for toupper(), false for tolower() */
+ UErrorCode status;
+ const char *zLocale = 0;
+
+ assert(nArg==1 || nArg==2);
+ bToUpper = (sqlite3_user_data(p)!=0);
+ if( nArg==2 ){
+ zLocale = (const char *)sqlite3_value_text(apArg[1]);
+ }
+
+ zInput = sqlite3_value_text16(apArg[0]);
+ if( !zInput ){
+ return;
+ }
+ nOut = nInput = sqlite3_value_bytes16(apArg[0]);
+ if( nOut==0 ){
+ sqlite3_result_text16(p, "", 0, SQLITE_STATIC);
+ return;
+ }
+
+ for(cnt=0; cnt<2; cnt++){
+ UChar *zNew = sqlite3_realloc(zOutput, nOut);
+ if( zNew==0 ){
+ sqlite3_free(zOutput);
+ sqlite3_result_error_nomem(p);
+ return;
+ }
+ zOutput = zNew;
+ status = U_ZERO_ERROR;
+ if( bToUpper ){
+ nOut = 2*u_strToUpper(zOutput,nOut/2,zInput,nInput/2,zLocale,&status);
+ }else{
+ nOut = 2*u_strToLower(zOutput,nOut/2,zInput,nInput/2,zLocale,&status);
+ }
+
+ if( U_SUCCESS(status) ){
+ sqlite3_result_text16(p, zOutput, nOut, xFree);
+ }else if( status==U_BUFFER_OVERFLOW_ERROR ){
+ assert( cnt==0 );
+ continue;
+ }else{
+ icuFunctionError(p, bToUpper ? "u_strToUpper" : "u_strToLower", status);
+ }
+ return;
+ }
+ assert( 0 ); /* Unreachable */
+}
+
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU) */
+
+/*
+** Collation sequence destructor function. The pCtx argument points to
+** a UCollator structure previously allocated using ucol_open().
+*/
+static void icuCollationDel(void *pCtx){
+ UCollator *p = (UCollator *)pCtx;
+ ucol_close(p);
+}
+
+/*
+** Collation sequence comparison function. The pCtx argument points to
+** a UCollator structure previously allocated using ucol_open().
+*/
+static int icuCollationColl(
+ void *pCtx,
+ int nLeft,
+ const void *zLeft,
+ int nRight,
+ const void *zRight
+){
+ UCollationResult res;
+ UCollator *p = (UCollator *)pCtx;
+ res = ucol_strcoll(p, (UChar *)zLeft, nLeft/2, (UChar *)zRight, nRight/2);
+ switch( res ){
+ case UCOL_LESS: return -1;
+ case UCOL_GREATER: return +1;
+ case UCOL_EQUAL: return 0;
+ }
+ assert(!"Unexpected return value from ucol_strcoll()");
+ return 0;
+}
+
+/*
+** Implementation of the scalar function icu_load_collation().
+**
+** This scalar function is used to add ICU collation based collation
+** types to an SQLite database connection. It is intended to be called
+** as follows:
+**
+** SELECT icu_load_collation(, );
+**
+** Where is a string containing an ICU locale identifier (i.e.
+** "en_AU", "tr_TR" etc.) and is the name of the
+** collation sequence to create.
+*/
+static void icuLoadCollation(
+ sqlite3_context *p,
+ int nArg,
+ sqlite3_value **apArg
+){
+ sqlite3 *db = (sqlite3 *)sqlite3_user_data(p);
+ UErrorCode status = U_ZERO_ERROR;
+ const char *zLocale; /* Locale identifier - (eg. "jp_JP") */
+ const char *zName; /* SQL Collation sequence name (eg. "japanese") */
+ UCollator *pUCollator; /* ICU library collation object */
+ int rc; /* Return code from sqlite3_create_collation_x() */
+
+ assert(nArg==2);
+ (void)nArg; /* Unused parameter */
+ zLocale = (const char *)sqlite3_value_text(apArg[0]);
+ zName = (const char *)sqlite3_value_text(apArg[1]);
+
+ if( !zLocale || !zName ){
+ return;
+ }
+
+ pUCollator = ucol_open(zLocale, &status);
+ if( !U_SUCCESS(status) ){
+ icuFunctionError(p, "ucol_open", status);
+ return;
+ }
+ assert(p);
+
+ rc = sqlite3_create_collation_v2(db, zName, SQLITE_UTF16, (void *)pUCollator,
+ icuCollationColl, icuCollationDel
+ );
+ if( rc!=SQLITE_OK ){
+ ucol_close(pUCollator);
+ sqlite3_result_error(p, "Error registering collation function", -1);
+ }
+}
+
+/*
+** Register the ICU extension functions with database db.
+*/
+EXPORT_SYMBOLS SQLITE_API int sqlite3IcuInit(sqlite3 *db){
+# define SQLITEICU_EXTRAFLAGS (SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS)
+ static const struct IcuScalar {
+ const char *zName; /* Function name */
+ unsigned char nArg; /* Number of arguments */
+ unsigned int enc; /* Optimal text encoding */
+ unsigned char iContext; /* sqlite3_user_data() context */
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
+ } scalars[] = {
+ {"icu_load_collation",2,SQLITE_UTF8|SQLITE_DIRECTONLY,1, icuLoadCollation},
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU)
+ {"regexp", 2, SQLITE_ANY|SQLITEICU_EXTRAFLAGS, 0, icuRegexpFunc},
+ {"lower", 1, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
+ {"lower", 2, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
+ {"upper", 1, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
+ {"upper", 2, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
+ {"lower", 1, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
+ {"lower", 2, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
+ {"upper", 1, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
+ {"upper", 2, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
+ {"like", 2, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuLikeFunc},
+ {"like", 3, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuLikeFunc},
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU) */
+ };
+#ifdef HARMONY_OS
+ const char *directory = "/system/usr/ohos_icu";
+ u_setDataDirectory(directory);
+#endif
+ int rc = SQLITE_OK;
+ int i;
+
+ for(i=0; rc==SQLITE_OK && i<(int)(sizeof(scalars)/sizeof(scalars[0])); i++){
+ const struct IcuScalar *p = &scalars[i];
+ rc = sqlite3_create_function(
+ db, p->zName, p->nArg, p->enc,
+ p->iContext ? (void*)db : (void*)0,
+ p->xFunc, 0, 0
+ );
+ }
+
+ return rc;
+}
+
+#if !SQLITE_CORE
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+SQLITE_API int sqlite3_icu_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ SQLITE_EXTENSION_INIT2(pApi)
+ return sqlite3IcuInit(db);
+}
+#endif
+
+#endif
+
+/************** End of icu.c *************************************************/
+/************** Begin file fts3_icu.c ****************************************/
+/*
+** 2007 June 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 implements a tokenizer for fts3 based on the ICU library.
+*/
+/* #include "fts3Int.h" */
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
+#ifdef SQLITE_ENABLE_ICU
+
+/* #include */
+/* #include */
+/* #include "fts3_tokenizer.h" */
+
+#include
+/* #include */
+/* #include */
+#include
+
+typedef struct IcuTokenizer IcuTokenizer;
+typedef struct IcuCursor IcuCursor;
+
+struct IcuTokenizer {
+ sqlite3_tokenizer base;
+ char *zLocale;
+};
+
+struct IcuCursor {
+ sqlite3_tokenizer_cursor base;
+
+ UBreakIterator *pIter; /* ICU break-iterator object */
+ int nChar; /* Number of UChar elements in pInput */
+ UChar *aChar; /* Copy of input using utf-16 encoding */
+ int *aOffset; /* Offsets of each character in utf-8 input */
+
+ int nBuffer;
+ char *zBuffer;
+
+ int iToken;
+};
+
+/*
+** Create a new tokenizer instance.
+*/
+static int icuCreate(
+ int argc, /* Number of entries in argv[] */
+ const char * const *argv, /* Tokenizer creation arguments */
+ sqlite3_tokenizer **ppTokenizer /* OUT: Created tokenizer */
+){
+ IcuTokenizer *p;
+ int n = 0;
+
+ if( argc>0 ){
+ n = strlen(argv[0])+1;
+ }
+ p = (IcuTokenizer *)sqlite3_malloc64(sizeof(IcuTokenizer)+n);
+ if( !p ){
+ return SQLITE_NOMEM;
+ }
+ memset(p, 0, sizeof(IcuTokenizer));
+
+ if( n ){
+ p->zLocale = (char *)&p[1];
+ memcpy(p->zLocale, argv[0], n);
+ }
+
+ *ppTokenizer = (sqlite3_tokenizer *)p;
+
+ return SQLITE_OK;
+}
+
+/*
+** Destroy a tokenizer
+*/
+static int icuDestroy(sqlite3_tokenizer *pTokenizer){
+ IcuTokenizer *p = (IcuTokenizer *)pTokenizer;
+ sqlite3_free(p);
+ return SQLITE_OK;
+}
+
+/*
+** 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 icuOpen(
+ sqlite3_tokenizer *pTokenizer, /* The tokenizer */
+ const char *zInput, /* Input string */
+ int nInput, /* Length of zInput in bytes */
+ sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */
+){
+ IcuTokenizer *p = (IcuTokenizer *)pTokenizer;
+ IcuCursor *pCsr;
+
+ const int32_t opt = U_FOLD_CASE_DEFAULT;
+ UErrorCode status = U_ZERO_ERROR;
+ int nChar;
+
+ UChar32 c;
+ int iInput = 0;
+ int iOut = 0;
+
+ *ppCursor = 0;
+
+ if( zInput==0 ){
+ nInput = 0;
+ zInput = "";
+ }else if( nInput<0 ){
+ nInput = strlen(zInput);
+ }
+ nChar = nInput+1;
+ pCsr = (IcuCursor *)sqlite3_malloc64(
+ sizeof(IcuCursor) + /* IcuCursor */
+ ((nChar+3)&~3) * sizeof(UChar) + /* IcuCursor.aChar[] */
+ (nChar+1) * sizeof(int) /* IcuCursor.aOffset[] */
+ );
+ if( !pCsr ){
+ return SQLITE_NOMEM;
+ }
+ memset(pCsr, 0, sizeof(IcuCursor));
+ pCsr->aChar = (UChar *)&pCsr[1];
+ pCsr->aOffset = (int *)&pCsr->aChar[(nChar+3)&~3];
+
+ pCsr->aOffset[iOut] = iInput;
+ U8_NEXT(zInput, iInput, nInput, c);
+ while( c>0 ){
+ int isError = 0;
+ c = u_foldCase(c, opt);
+ U16_APPEND(pCsr->aChar, iOut, nChar, c, isError);
+ if( isError ){
+ sqlite3_free(pCsr);
+ return SQLITE_ERROR;
+ }
+ pCsr->aOffset[iOut] = iInput;
+
+ if( iInputpIter = ubrk_open(UBRK_WORD, p->zLocale, pCsr->aChar, iOut, &status);
+ if( !U_SUCCESS(status) ){
+ sqlite3_free(pCsr);
+ return SQLITE_ERROR;
+ }
+ pCsr->nChar = iOut;
+
+ ubrk_first(pCsr->pIter);
+ *ppCursor = (sqlite3_tokenizer_cursor *)pCsr;
+ return SQLITE_OK;
+}
+
+/*
+** Close a tokenization cursor previously opened by a call to icuOpen().
+*/
+static int icuClose(sqlite3_tokenizer_cursor *pCursor){
+ IcuCursor *pCsr = (IcuCursor *)pCursor;
+ ubrk_close(pCsr->pIter);
+ sqlite3_free(pCsr->zBuffer);
+ sqlite3_free(pCsr);
+ return SQLITE_OK;
+}
+
+/*
+** Extract the next token from a tokenization cursor.
+*/
+static int icuNext(
+ sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by simpleOpen */
+ const char **ppToken, /* OUT: *ppToken is the token text */
+ int *pnBytes, /* OUT: Number of bytes in token */
+ int *piStartOffset, /* OUT: Starting offset of token */
+ int *piEndOffset, /* OUT: Ending offset of token */
+ int *piPosition /* OUT: Position integer of token */
+){
+ IcuCursor *pCsr = (IcuCursor *)pCursor;
+
+ int iStart = 0;
+ int iEnd = 0;
+ int nByte = 0;
+
+ while( iStart==iEnd ){
+ UChar32 c;
+
+ iStart = ubrk_current(pCsr->pIter);
+ iEnd = ubrk_next(pCsr->pIter);
+ if( iEnd==UBRK_DONE ){
+ return SQLITE_DONE;
+ }
+
+ while( iStartaChar, iWhite, pCsr->nChar, c);
+ if( u_isspace(c) ){
+ iStart = iWhite;
+ }else{
+ break;
+ }
+ }
+ assert(iStart<=iEnd);
+ }
+
+ do {
+ UErrorCode status = U_ZERO_ERROR;
+ if( nByte ){
+ char *zNew = sqlite3_realloc(pCsr->zBuffer, nByte);
+ if( !zNew ){
+ return SQLITE_NOMEM;
+ }
+ pCsr->zBuffer = zNew;
+ pCsr->nBuffer = nByte;
+ }
+
+ u_strToUTF8(
+ pCsr->zBuffer, pCsr->nBuffer, &nByte, /* Output vars */
+ &pCsr->aChar[iStart], iEnd-iStart, /* Input vars */
+ &status /* Output success/failure */
+ );
+ } while( nByte>pCsr->nBuffer );
+
+ *ppToken = pCsr->zBuffer;
+ *pnBytes = nByte;
+ *piStartOffset = pCsr->aOffset[iStart];
+ *piEndOffset = pCsr->aOffset[iEnd];
+ *piPosition = pCsr->iToken++;
+
+ return SQLITE_OK;
+}
+
+/*
+** The set of routines that implement the simple tokenizer
+*/
+static const sqlite3_tokenizer_module icuTokenizerModule = {
+ 0, /* iVersion */
+ icuCreate, /* xCreate */
+ icuDestroy, /* xCreate */
+ icuOpen, /* xOpen */
+ icuClose, /* xClose */
+ icuNext, /* xNext */
+ 0, /* xLanguageid */
+};
+
+/*
+** Set *ppModule to point at the implementation of the ICU tokenizer.
+*/
+EXPORT_SYMBOLS SQLITE_API void sqlite3Fts3IcuTokenizerModule(
+ sqlite3_tokenizer_module const**ppModule
+){
+ *ppModule = &icuTokenizerModule;
+}
+
+#endif /* defined(SQLITE_ENABLE_ICU) */
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */
+
+/************** End of fts3_icu.c ********************************************/
\ No newline at end of file