From 5e801579346f51b924ab2161705b00e5a24b4258 Mon Sep 17 00:00:00 2001 From: wuyichen <664334617@qq.com> Date: Sat, 2 Nov 2024 10:40:13 +0800 Subject: [PATCH 01/12] 1102-addShmLockPrint Signed-off-by: wuyichen <664334617@qq.com> --- src/sqlite3.c | 65 +++++++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/src/sqlite3.c b/src/sqlite3.c index 93fd40f..2b77ee8 100644 --- a/src/sqlite3.c +++ b/src/sqlite3.c @@ -41809,14 +41809,14 @@ 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; } @@ -246227,65 +246227,68 @@ export_finish: #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) +static void printLockInfo(unixFile *dbFile, int walStat) { - const char *lock_type[DB_LOCK_NUM] = {"F_RDLCK", "F_WRLCK", "F_UNLCK"}; + const char *lockType[DB_LOCK_NUM] = {"F_RDLCK", "F_WRLCK", "F_UNLCK"}; sqlite3_log(SQLITE_WARNING, "*** SQLITE_LOG DB Lock ***"); - if( uFile_db==NULL ){ + if( dbFile==NULL ){ sqlite3_log(SQLITE_WARNING, "NO DB FILE !"); return; } - unixInodeInfo *inode = uFile_db->pInode; + unixInodeInfo *inode = dbFile->pInode; if( inode==NULL ){ sqlite3_log(SQLITE_ERROR, "Inode is NULL !"); 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"}; + dbFile->eFileLock, inode->nRef, inode->nLock, inode->eFileLock, inode->bProcessLock); + + /* Set param list */ + int fdDb = dbFile->h; + const char *lockNameList[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; ipInode->pShmNode; + int hasShm = (dbFile->pShm==NULL || pShmNode==NULL) ? 0 : 1; + off_t startPos = PENDING_BYTE; for(int i=0; ihShm; } - off_t lock_len = i==DB_SHARED_LOCK_OFFSET ? SHARED_SIZE : 1; - struct flock fl_db = { .l_type=F_WRLCK, .l_start=start_pos, .l_len=lock_len, .l_whence=SEEK_SET }; - int ret = osFcntl(fd_db, F_GETLK, &fl_db); + off_t lockLen = i==DB_SHARED_LOCK_OFFSET ? SHARED_SIZE : 1; + struct flock flDb = { .l_type=F_WRLCK, .l_start=startPos, .l_len=lockLen, .l_whence=SEEK_SET }; + int ret = osFcntl(fdDb, F_GETLK, &flDb); if( ret!=SQLITE_OK ){ sqlite3_log(SQLITE_ERROR, "Get File Lock Info Fail! errno: %d", ret); return; } - if( fl_db.l_type!=F_UNLCK ){ + if( flDb.l_type!=F_UNLCK ){ sqlite3_log(SQLITE_WARNING, "lock name: %s, pos: 0x%x, len: 0x%x, lock type: %s, owner pId: %d", - lock_name_list[i], start_pos, lock_len, lock_type[fl_db.l_type], fl_db.l_pid); + lockNameList[i], startPos, lockLen, lockType[flDb.l_type], flDb.l_pid); } - start_pos++; + startPos++; } /* thread WAL Lock Info */ - if( uFile_db->pShm==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]); + if( dbFile->pShm->pShmNode->aLock[i] ){ + sqlite3_log(SQLITE_WARNING, "Local WAL Lock[%d] for DB file: %d", i, dbFile->pShm->pShmNode->aLock[i]); } } } -- Gitee From 146cfaaca9e80a0e148b5f94455c3ebddd5d369b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=B5=B7=E7=A1=95?= <2627006591@qq.com> Date: Mon, 18 Nov 2024 10:50:49 +0800 Subject: [PATCH 02/12] sqlite fts adapt icu Signed-off-by: wanghaishuo --- BUILD.gn | 12 ++++++++++++ bundle.json | 3 ++- sqlite.gni | 16 ++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 sqlite.gni diff --git a/BUILD.gn b/BUILD.gn index 570cebb..349c79a 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -12,6 +12,7 @@ # limitations under the License. import("//build/ohos.gni") +import("//third_party/sqlite/sqlite.gni") is_cross_platform_build = defined(is_arkui_x) && is_arkui_x @@ -46,9 +47,11 @@ 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_FTS4", "SQLITE_ENABLE_FTS5", + "SQLITE_ENABLE_FTS3_TOKENIZER", "SQLITE_OMIT_COMPILEOPTION_DIAGS", "SQLITE_DEFAULT_FILE_PERMISSIONS=0660", "SQLITE_SECURE_DELETE", @@ -68,6 +71,9 @@ ohos_shared_library("sqlite") { "FDSAN_ENABLE", "HARMONY_OS", ] + if (os_level == "standard" && sqlite_support_icu) { + defines += [ "SQLITE_ENABLE_ICU" ] + } cflags_c = [ "-fvisibility=hidden", "-Wno-implicit-fallthrough", @@ -95,6 +101,12 @@ ohos_shared_library("sqlite") { "c_utils:utils", "openssl:libcrypto_shared", ] + if (os_level == "standard" && sqlite_support_icu) { + external_deps += [ + "icu:shared_icui18n", + "icu:shared_icuuc", + ] + } } } diff --git a/bundle.json b/bundle.json index c2501b6..1fea87d 100644 --- a/bundle.json +++ b/bundle.json @@ -14,13 +14,14 @@ "name": "sqlite", "subsystem": "thirdparty", "syscap": [], - "features": [], + "features": ["sqlite_support_icu"], "adapted_system_type": [ "standard" ], "rom": "2200KB", "ram": "1024KB", "deps": { "components": [ "c_utils", + "icu", "openssl" ], "third_party": [ diff --git a/sqlite.gni b/sqlite.gni new file mode 100644 index 0000000..97c7d72 --- /dev/null +++ b/sqlite.gni @@ -0,0 +1,16 @@ +# Copyright (c) 2024 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +declare_args() { + sqlite_support_icu = true +} -- Gitee From 9f6c9046dae888797d7311fa7224843e827db115 Mon Sep 17 00:00:00 2001 From: lxlxlxl Date: Thu, 7 Nov 2024 21:47:57 +0800 Subject: [PATCH 03/12] =?UTF-8?q?1.=E4=BF=AE=E5=A4=8D=E4=B8=8D=E5=90=8CDB?= =?UTF-8?q?=E7=9A=84=E5=85=A8=E5=B1=80=E5=8A=A0=E5=AF=86=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E7=9B=B8=E4=BA=92=E5=BD=B1=E5=93=8D=E9=97=AE=E9=A2=98=202.?= =?UTF-8?q?=E4=BF=AE=E5=A4=8Dattach=E9=BB=98=E8=AE=A4=E5=8A=A0=E5=AF=86?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E4=B8=8D=E7=94=9F=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: ryne3366 --- src/sqlite3.c | 139 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 84 insertions(+), 55 deletions(-) diff --git a/src/sqlite3.c b/src/sqlite3.c index 2b77ee8..dedbd85 100644 --- a/src/sqlite3.c +++ b/src/sqlite3.c @@ -17004,6 +17004,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 +17164,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 }; /* @@ -20128,6 +20142,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 @@ -67209,9 +67226,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. @@ -176979,8 +176996,14 @@ opendb_out: sqlite3GlobalConfig.xSqllog(pArg, db, zFilename, 0); } #endif + #if defined(SQLITE_HAS_CODEC) - if( rc==SQLITE_OK ) sqlite3CodecQueryParameters(db, 0, zOpen); + if( rc==SQLITE_OK ) { +#ifdef SQLITE_CODEC_ATTACH_CHANGED + sqlite3CodecResetParameters(&db->codecParm); +#endif + sqlite3CodecQueryParameters(db, 0, zOpen); + } #endif sqlite3_free_filename(zOpen); return rc; @@ -245165,28 +245188,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 +245220,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 +245367,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 +245375,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 +245440,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 +245907,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 +245920,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 -- Gitee From 7b2e612f85c0289df70d09f9984735105872b4ea Mon Sep 17 00:00:00 2001 From: Lee <18800203378@163.com> Date: Wed, 27 Nov 2024 20:49:04 +0800 Subject: [PATCH 04/12] =?UTF-8?q?=E6=B7=BB=E5=8A=A0busy=E6=97=B6=E7=BB=B4?= =?UTF-8?q?=E6=B5=8B=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: ryne3366 --- src/sqlite3.c | 391 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 306 insertions(+), 85 deletions(-) diff --git a/src/sqlite3.c b/src/sqlite3.c index dedbd85..58ae20b 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 */ @@ -36784,6 +36785,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 @@ -38558,6 +38597,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; } @@ -38582,6 +38622,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; } @@ -38597,6 +38638,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; } @@ -38618,11 +38660,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. */ @@ -38638,7 +38680,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; @@ -38663,6 +38705,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 @@ -38687,6 +38730,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ storeLastErrno(pFile, tErrno); } } + MarkLockStatusByRc(rc, TRX_LOCK_IDX, 1, eFileLock, LOCK_BY_PROCESS); } @@ -38884,7 +38928,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. @@ -41196,6 +41239,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 */ @@ -41438,6 +41482,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 @@ -41449,11 +41495,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; } @@ -41838,7 +41888,7 @@ static int unixShmLock( 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 ); @@ -41877,6 +41927,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; @@ -41887,21 +41938,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 ){ @@ -41912,12 +41964,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{ @@ -41936,14 +41990,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); @@ -59811,6 +59868,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 ){ @@ -60720,6 +60778,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 @@ -60809,6 +60868,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; @@ -60845,6 +60905,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; } @@ -61459,6 +61520,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; } @@ -61471,6 +61533,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 @@ -61478,8 +61541,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); } } @@ -62980,6 +63045,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); @@ -63144,6 +63210,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, @@ -63227,6 +63294,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 @@ -63242,6 +63310,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); @@ -64616,6 +64685,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; } @@ -65453,6 +65523,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); @@ -65545,6 +65616,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 ){ @@ -65563,11 +65636,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 @@ -65665,8 +65740,9 @@ SQLITE_PRIVATE int sqlite3WalClose( walLimitSize(pWal, 0); } } + } else { + MARK_LAST_BUSY_LINE(rc); } - walIndexClose(pWal, isDelete); sqlite3OsClose(pWal->pWalFd); if( isDelete ){ @@ -65819,6 +65895,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)) ){ @@ -65839,6 +65916,7 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){ walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); } } + MARK_LAST_BUSY_LINE(rc); } } @@ -65858,6 +65936,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; } @@ -66089,7 +66168,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 */ @@ -66128,7 +66207,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); } @@ -66156,11 +66235,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 ){ @@ -66183,6 +66265,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)) ){ @@ -66200,6 +66283,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; @@ -66244,6 +66328,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 ){ @@ -66253,6 +66338,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 @@ -66295,6 +66381,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 ); @@ -66421,7 +66508,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; } @@ -67258,7 +67345,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 ); @@ -67280,6 +67367,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 ){ @@ -67296,6 +67384,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 ){ @@ -71049,7 +71138,7 @@ static void pageReinit(DbPage *pData){ } } -static void printLockInfoUsingPager(Pager *pPager); +static void DumpLocksByPager(Pager *pPager); /* ** Invoke the busy handler for a btree. */ @@ -71059,8 +71148,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; @@ -72151,7 +72240,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. @@ -79521,6 +79610,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); } @@ -85367,6 +85457,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ nTrans++; } rc = sqlite3PagerExclusiveLock(pPager); + MARK_LAST_BUSY_LINE(rc); sqlite3BtreeLeave(pBt); } } @@ -246249,106 +246340,236 @@ export_finish: } /************** End file hw_codec.c *****************************************/ #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 *dbFile, int walStat) +#if defined(__linux__) && !defined(gettid) +#include +#define gettid() syscall(__NR_gettid) +#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] = gettid(); + } + } +} +/* +** 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] == gettid()) { + tidBuf[i] = 0; + } + } +} + +static void MarkLockBusy(u32 lockIdx, u32 lockLen, u8 lockType, u8 lockByProcess) { - const char *lockType[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( dbFile==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 = dbFile->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", - dbFile->eFileLock, inode->nRef, inode->nLock, inode->eFileLock, inode->bProcessLock); - - /* Set param list */ - int fdDb = dbFile->h; - const char *lockNameList[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; ipInode->pShmNode; - int hasShm = (dbFile->pShm==NULL || pShmNode==NULL) ? 0 : 1; - off_t startPos = PENDING_BYTE; - for(int i=0; ihShm; - } - off_t lockLen = i==DB_SHARED_LOCK_OFFSET ? SHARED_SIZE : 1; - struct flock flDb = { .l_type=F_WRLCK, .l_start=startPos, .l_len=lockLen, .l_whence=SEEK_SET }; - int ret = osFcntl(fdDb, F_GETLK, &flDb); - if( ret!=SQLITE_OK ){ - sqlite3_log(SQLITE_ERROR, "Get File Lock Info Fail! errno: %d", ret); - return; - } - if( flDb.l_type!=F_UNLCK ){ - sqlite3_log(SQLITE_WARNING, "lock name: %s, pos: 0x%x, len: 0x%x, lock type: %s, owner pId: %d", - lockNameList[i], startPos, lockLen, lockType[flDb.l_type], flDb.l_pid); + if (rc == SQLITE_OK) { + MarkLockStatus(lockIdx, lockLen, lockType); + } + // only busy error code need record + if (lockIdx == g_lockStatus.busyLockIdx && g_lockStatus.lockLen != 0) { + g_lockStatus.busyLockIdx = 0; + g_lockStatus.busyLockType = NO_LOCK; + g_lockStatus.lockLen = 0; + } +} + +static inline const char *TrxLockName(int eLock) +{ + return eLock == NO_LOCK ? "NO_LOCK" : + eLock == RESERVED_LOCK ? "RESERVED" : + eLock == EXCLUSIVE_LOCK ? "EXCLUSIVE" : + eLock == SHARED_LOCK ? "SHARED" : + eLock == UNKNOWN_LOCK ? "UNKNOWN" : "?error?"; +} + +static inline const char *IdxToLockName(u32 idx) +{ + const char *lockName[MAX_LOCK_NUM] = {"write", "ckpt", "recover", "read0", + "read1", "read2", "read3", "read4", "wal_dms", "trxLock"}; + return (idx < MAX_LOCK_NUM) ? lockName[idx] : "errLock"; +} + +static void DumpHandleLock(char *dumpBuf, int dumpBufLen) +{ + char *tmp = dumpBuf; + u8 *lockStatus = g_lockStatus.lockStatus; + int availLen = dumpBufLen - 1; + dumpBuf[availLen] = '\0'; + for (int i = 0; i < MAX_LOCK_NUM && availLen > 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; } - startPos++; } - - /* thread WAL Lock Info */ - for(int i=0; ipShm->pShmNode->aLock[i] ){ - sqlite3_log(SQLITE_WARNING, "Local WAL Lock[%d] for DB file: %d", i, dbFile->pShm->pShmNode->aLock[i]); + 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; + } + 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 */ -- Gitee From 7817b4da298b2fa225a76b8de36219739f93d89f Mon Sep 17 00:00:00 2001 From: Lee <18800203378@163.com> Date: Sat, 7 Dec 2024 14:41:18 +0800 Subject: [PATCH 05/12] fix gettid not work in mac os Signed-off-by: ryne3366 --- src/sqlite3.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/sqlite3.c b/src/sqlite3.c index 58ae20b..0a697fa 100644 --- a/src/sqlite3.c +++ b/src/sqlite3.c @@ -246340,11 +246340,21 @@ export_finish: } /************** End file hw_codec.c *****************************************/ #endif + #if SQLITE_OS_UNIX -#if defined(__linux__) && !defined(gettid) +#include #include -#define gettid() syscall(__NR_gettid) +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)); @@ -246357,7 +246367,7 @@ 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] = gettid(); + tidBuf[i] = OsGetTid(); } } } @@ -246368,7 +246378,7 @@ static void TryClearTid(int *tidBuf, int ofs, int lockLen) { int lockOfs = ofs + lockLen; for (int i = ofs; i < lockOfs; i++) { - if (tidBuf[i] == gettid()) { + if (tidBuf[i] == OsGetTid()) { tidBuf[i] = 0; } } -- Gitee From 24f19934528557fcbd9ee72cad3bd810107f1dce Mon Sep 17 00:00:00 2001 From: MartinChoo <214582617@qq.com> Date: Thu, 21 Nov 2024 10:57:20 +0800 Subject: [PATCH 06/12] Report corruption when runtime detected Signed-off-by: MartinChoo <214582617@qq.com> --- include/sqlite3.h | 16 ++ src/sqlite3.c | 664 ++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 594 insertions(+), 86 deletions(-) diff --git a/include/sqlite3.h b/include/sqlite3.h index 445640f..e383b92 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,7 @@ 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 */ /* ** CAPI3REF: Database Connection Configuration Options diff --git a/src/sqlite3.c b/src/sqlite3.c index dedbd85..d887a74 100644 --- a/src/sqlite3.c +++ b/src/sqlite3.c @@ -2430,6 +2430,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 +2476,7 @@ 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 */ /* ** CAPI3REF: Database Connection Configuration Options @@ -19541,6 +19557,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 */ @@ -19792,6 +19810,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 @@ -19800,10 +19869,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 @@ -19815,12 +19885,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 @@ -22299,6 +22370,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 @@ -58554,7 +58627,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 ){ @@ -61087,7 +61160,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; @@ -61119,7 +61197,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; } @@ -61201,7 +61283,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) ); @@ -62784,7 +62869,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 ){ @@ -64548,7 +64637,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); @@ -65496,7 +65591,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); } @@ -66588,7 +66689,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); } @@ -67321,7 +67426,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); } @@ -68768,7 +68878,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", @@ -68779,11 +68889,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 @@ -69461,7 +69571,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); } @@ -69641,7 +69756,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; } @@ -69655,12 +69770,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 ); @@ -69705,7 +69832,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 ); @@ -69713,7 +69845,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; } @@ -70105,7 +70245,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]); @@ -70163,10 +70310,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; @@ -70174,16 +70340,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; @@ -70216,13 +70417,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 ); @@ -70236,7 +70448,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); @@ -70261,7 +70479,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 */ @@ -70293,7 +70511,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 @@ -70308,14 +70534,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; } @@ -70364,7 +70601,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); } } @@ -70382,7 +70625,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; } @@ -70461,12 +70708,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 ); @@ -70478,10 +70736,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]); @@ -70494,13 +70766,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]; @@ -70509,8 +70795,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{ @@ -70606,7 +70905,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; @@ -70657,12 +70962,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]); @@ -70672,11 +70985,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); } } @@ -70688,7 +71009,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; @@ -70719,12 +71046,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; @@ -70756,7 +71091,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); @@ -70770,7 +71105,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 @@ -70925,7 +71265,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); @@ -70946,7 +71290,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; @@ -72375,7 +72724,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{ @@ -72394,7 +72748,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); @@ -72403,7 +72763,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); @@ -72415,7 +72781,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); } @@ -72548,7 +72918,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 ){ @@ -73617,7 +73991,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) ); @@ -73632,7 +74011,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. */ @@ -73694,7 +74078,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 ); @@ -73779,7 +74172,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; } @@ -74055,7 +74452,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: @@ -74309,7 +74711,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); } } } @@ -74592,7 +74998,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 ); @@ -74920,7 +75331,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. */ @@ -74976,7 +75393,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); } @@ -75005,7 +75427,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 @@ -75039,7 +75468,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 ); @@ -75104,7 +75540,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 ); @@ -75289,7 +75732,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 ){ @@ -75378,7 +75828,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; @@ -75395,7 +75850,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); @@ -75668,7 +76130,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); @@ -77578,7 +78045,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, @@ -82760,7 +83234,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) */ @@ -90417,7 +90891,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) */ @@ -174150,6 +174624,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; @@ -177220,9 +177700,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 ); -- Gitee From daba14817398f64264c700300f6c43b02f443774 Mon Sep 17 00:00:00 2001 From: zwtmichael Date: Thu, 26 Dec 2024 15:26:55 +0800 Subject: [PATCH 07/12] icu optimize Signed-off-by: zwtmichael --- BUILD.gn | 59 +- bundle.json | 2 +- include/sqlite3.h | 1 + include/sqlite3icu.h | 50 ++ include/sqlite3tokenizer.h | 145 +++++ sqlite.gni | 16 - src/sqlite3.c | 1095 ++++-------------------------------- src/sqlite3icu.c | 892 +++++++++++++++++++++++++++++ 8 files changed, 1251 insertions(+), 1009 deletions(-) create mode 100644 include/sqlite3icu.h create mode 100644 include/sqlite3tokenizer.h delete mode 100644 sqlite.gni create mode 100644 src/sqlite3icu.c diff --git a/BUILD.gn b/BUILD.gn index 349c79a..8258b41 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -12,7 +12,6 @@ # limitations under the License. import("//build/ohos.gni") -import("//third_party/sqlite/sqlite.gni") is_cross_platform_build = defined(is_arkui_x) && is_arkui_x @@ -29,7 +28,51 @@ 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" + 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") { @@ -49,9 +92,9 @@ ohos_shared_library("sqlite") { "SQLITE_ENABLE_MEMORY_MANAGEMENT=1", "SQLITE_ENABLE_LOAD_EXTENSION", "SQLITE_ENABLE_FTS3", + "SQLITE_ENABLE_FTS3_TOKENIZER", "SQLITE_ENABLE_FTS4", "SQLITE_ENABLE_FTS5", - "SQLITE_ENABLE_FTS3_TOKENIZER", "SQLITE_OMIT_COMPILEOPTION_DIAGS", "SQLITE_DEFAULT_FILE_PERMISSIONS=0660", "SQLITE_SECURE_DELETE", @@ -70,10 +113,8 @@ ohos_shared_library("sqlite") { "LOG_DUMP", "FDSAN_ENABLE", "HARMONY_OS", + "SQLITE_ENABLE_ICU", ] - if (os_level == "standard" && sqlite_support_icu) { - defines += [ "SQLITE_ENABLE_ICU" ] - } cflags_c = [ "-fvisibility=hidden", "-Wno-implicit-fallthrough", @@ -101,12 +142,6 @@ ohos_shared_library("sqlite") { "c_utils:utils", "openssl:libcrypto_shared", ] - if (os_level == "standard" && sqlite_support_icu) { - external_deps += [ - "icu:shared_icui18n", - "icu:shared_icuuc", - ] - } } } diff --git a/bundle.json b/bundle.json index 1fea87d..ce2296e 100644 --- a/bundle.json +++ b/bundle.json @@ -14,7 +14,7 @@ "name": "sqlite", "subsystem": "thirdparty", "syscap": [], - "features": ["sqlite_support_icu"], + "features": [], "adapted_system_type": [ "standard" ], "rom": "2200KB", "ram": "1024KB", diff --git a/include/sqlite3.h b/include/sqlite3.h index e383b92..4e24bca 100644 --- a/include/sqlite3.h +++ b/include/sqlite3.h @@ -2167,6 +2167,7 @@ struct sqlite3_mem_methods { #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 0000000..97505ba --- /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 "sqlite3.h" +#include "sqlite3tokenizer.h" + +#if 0 +extern "C" { +#endif /* __cplusplus */ + +SQLITE_API int sqlite3IcuInit(sqlite3 *db); + +#if 0 +} /* extern "C" */ +#endif /* __cplusplus */ + +/************** 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 \ No newline at end of file diff --git a/include/sqlite3tokenizer.h b/include/sqlite3tokenizer.h new file mode 100644 index 0000000..b1a1f09 --- /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/sqlite.gni b/sqlite.gni deleted file mode 100644 index 97c7d72..0000000 --- a/sqlite.gni +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) 2024 Huawei Device Co., Ltd. -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -declare_args() { - sqlite_support_icu = true -} diff --git a/src/sqlite3.c b/src/sqlite3.c index 1b81464..c0eb3a9 100644 --- a/src/sqlite3.c +++ b/src/sqlite3.c @@ -2478,6 +2478,7 @@ struct sqlite3_mem_methods { #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 @@ -3210,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 ** @@ -3252,7 +3263,7 @@ SQLITE_API void sqlite3_free_table(char **result); */ SQLITE_API char *sqlite3_mprintf(const char*,...); SQLITE_API char *sqlite3_vmprintf(const char*, va_list); -SQLITE_API char *sqlite3_snprintf(int,char*,const char*, ...); +EXPORT_SYMBOLS SQLITE_API char *sqlite3_snprintf(int,char*,const char*, ...); SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list); /* @@ -3331,10 +3342,10 @@ SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list); ** [sqlite3_free()] or [sqlite3_realloc()]. */ SQLITE_API void *sqlite3_malloc(int); -SQLITE_API void *sqlite3_malloc64(sqlite3_uint64); -SQLITE_API void *sqlite3_realloc(void*, int); +EXPORT_SYMBOLS SQLITE_API void *sqlite3_malloc64(sqlite3_uint64); +EXPORT_SYMBOLS SQLITE_API void *sqlite3_realloc(void*, int); SQLITE_API void *sqlite3_realloc64(void*, sqlite3_uint64); -SQLITE_API void sqlite3_free(void*); +EXPORT_SYMBOLS SQLITE_API void sqlite3_free(void*); SQLITE_API sqlite3_uint64 sqlite3_msize(void*); /* @@ -5946,12 +5957,12 @@ SQLITE_API double sqlite3_value_double(sqlite3_value*); SQLITE_API int sqlite3_value_int(sqlite3_value*); SQLITE_API sqlite3_int64 sqlite3_value_int64(sqlite3_value*); SQLITE_API void *sqlite3_value_pointer(sqlite3_value*, const char*); -SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value*); -SQLITE_API const void *sqlite3_value_text16(sqlite3_value*); +EXPORT_SYMBOLS SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value*); +EXPORT_SYMBOLS SQLITE_API const void *sqlite3_value_text16(sqlite3_value*); SQLITE_API const void *sqlite3_value_text16le(sqlite3_value*); SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*); -SQLITE_API int sqlite3_value_bytes(sqlite3_value*); -SQLITE_API int sqlite3_value_bytes16(sqlite3_value*); +EXPORT_SYMBOLS SQLITE_API int sqlite3_value_bytes(sqlite3_value*); +EXPORT_SYMBOLS SQLITE_API int sqlite3_value_bytes16(sqlite3_value*); SQLITE_API int sqlite3_value_type(sqlite3_value*); SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); SQLITE_API int sqlite3_value_nochange(sqlite3_value*); @@ -6046,7 +6057,7 @@ SQLITE_API void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); ** This routine must be called from the same thread in which ** the application-defined function is running. */ -SQLITE_API void *sqlite3_user_data(sqlite3_context*); +EXPORT_SYMBOLS SQLITE_API void *sqlite3_user_data(sqlite3_context*); /* ** CAPI3REF: Database Connection For Functions @@ -6117,8 +6128,8 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); ** These routines must be called from the same thread in which ** the SQL function is running. */ -SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N); -SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); +EXPORT_SYMBOLS SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N); +EXPORT_SYMBOLS SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); /* @@ -6290,18 +6301,18 @@ SQLITE_API void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)( SQLITE_API void sqlite3_result_blob64(sqlite3_context*,const void*, sqlite3_uint64,void(*)(void*)); SQLITE_API void sqlite3_result_double(sqlite3_context*, double); -SQLITE_API void sqlite3_result_error(sqlite3_context*, const char*, int); +EXPORT_SYMBOLS SQLITE_API void sqlite3_result_error(sqlite3_context*, const char*, int); SQLITE_API void sqlite3_result_error16(sqlite3_context*, const void*, int); SQLITE_API void sqlite3_result_error_toobig(sqlite3_context*); -SQLITE_API void sqlite3_result_error_nomem(sqlite3_context*); +EXPORT_SYMBOLS SQLITE_API void sqlite3_result_error_nomem(sqlite3_context*); SQLITE_API void sqlite3_result_error_code(sqlite3_context*, int); -SQLITE_API void sqlite3_result_int(sqlite3_context*, int); +EXPORT_SYMBOLS SQLITE_API void sqlite3_result_int(sqlite3_context*, int); SQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64); SQLITE_API void sqlite3_result_null(sqlite3_context*); SQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); SQLITE_API void sqlite3_result_text64(sqlite3_context*, const char*,sqlite3_uint64, void(*)(void*), unsigned char encoding); -SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); +EXPORT_SYMBOLS SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*); @@ -6412,7 +6423,7 @@ SQLITE_API int sqlite3_create_collation( void *pArg, int(*xCompare)(void*,int,const void*,int,const void*) ); -SQLITE_API int sqlite3_create_collation_v2( +EXPORT_SYMBOLS SQLITE_API int sqlite3_create_collation_v2( sqlite3*, const char *zName, int eTextRep, @@ -29453,7 +29464,7 @@ SQLITE_API void *sqlite3_malloc(int n){ #endif return n<=0 ? 0 : sqlite3Malloc(n); } -SQLITE_API void *sqlite3_malloc64(sqlite3_uint64 n){ +EXPORT_SYMBOLS SQLITE_API void *sqlite3_malloc64(sqlite3_uint64 n){ #ifndef SQLITE_OMIT_AUTOINIT if( sqlite3_initialize() ) return 0; #endif @@ -29522,7 +29533,7 @@ SQLITE_API sqlite3_uint64 sqlite3_msize(void *p){ /* ** Free memory previously obtained from sqlite3Malloc(). */ -SQLITE_API void sqlite3_free(void *p){ +EXPORT_SYMBOLS SQLITE_API void sqlite3_free(void *p){ if( p==0 ) return; /* IMP: R-49053-54554 */ assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); @@ -29693,7 +29704,7 @@ SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, u64 nBytes){ ** The public interface to sqlite3Realloc. Make sure that the memory ** subsystem is initialized prior to invoking sqliteRealloc. */ -SQLITE_API void *sqlite3_realloc(void *pOld, int n){ +EXPORT_SYMBOLS SQLITE_API void *sqlite3_realloc(void *pOld, int n){ #ifndef SQLITE_OMIT_AUTOINIT if( sqlite3_initialize() ) return 0; #endif @@ -31334,7 +31345,7 @@ SQLITE_API char *sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_li zBuf[acc.nChar] = 0; return zBuf; } -SQLITE_API char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){ +EXPORT_SYMBOLS SQLITE_API char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){ char *z; va_list ap; va_start(ap,zFormat); @@ -88631,10 +88642,10 @@ SQLITE_API const void *sqlite3_value_blob(sqlite3_value *pVal){ return sqlite3_value_text(pVal); } } -SQLITE_API int sqlite3_value_bytes(sqlite3_value *pVal){ +EXPORT_SYMBOLS SQLITE_API int sqlite3_value_bytes(sqlite3_value *pVal){ return sqlite3ValueBytes(pVal, SQLITE_UTF8); } -SQLITE_API int sqlite3_value_bytes16(sqlite3_value *pVal){ +EXPORT_SYMBOLS SQLITE_API int sqlite3_value_bytes16(sqlite3_value *pVal){ return sqlite3ValueBytes(pVal, SQLITE_UTF16NATIVE); } SQLITE_API double sqlite3_value_double(sqlite3_value *pVal){ @@ -88663,11 +88674,11 @@ SQLITE_API void *sqlite3_value_pointer(sqlite3_value *pVal, const char *zPType){ return 0; } } -SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value *pVal){ +EXPORT_SYMBOLS SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value *pVal){ return (const unsigned char *)sqlite3ValueText(pVal, SQLITE_UTF8); } #ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlite3_value_text16(sqlite3_value* pVal){ +EXPORT_SYMBOLS SQLITE_API const void *sqlite3_value_text16(sqlite3_value* pVal){ return sqlite3ValueText(pVal, SQLITE_UTF16NATIVE); } SQLITE_API const void *sqlite3_value_text16be(sqlite3_value *pVal){ @@ -88892,7 +88903,7 @@ SQLITE_API void sqlite3_result_double(sqlite3_context *pCtx, double rVal){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetDouble(pCtx->pOut, rVal); } -SQLITE_API void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){ +EXPORT_SYMBOLS SQLITE_API void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); pCtx->isError = SQLITE_ERROR; sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF8, SQLITE_TRANSIENT); @@ -88904,7 +88915,7 @@ SQLITE_API void sqlite3_result_error16(sqlite3_context *pCtx, const void *z, int sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT); } #endif -SQLITE_API void sqlite3_result_int(sqlite3_context *pCtx, int iVal){ +EXPORT_SYMBOLS SQLITE_API void sqlite3_result_int(sqlite3_context *pCtx, int iVal){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetInt64(pCtx->pOut, (i64)iVal); } @@ -88960,7 +88971,7 @@ SQLITE_API void sqlite3_result_text64( } } #ifndef SQLITE_OMIT_UTF16 -SQLITE_API void sqlite3_result_text16( +EXPORT_SYMBOLS SQLITE_API void sqlite3_result_text16( sqlite3_context *pCtx, const void *z, int n, @@ -89034,7 +89045,7 @@ SQLITE_API void sqlite3_result_error_toobig(sqlite3_context *pCtx){ } /* An SQLITE_NOMEM error. */ -SQLITE_API void sqlite3_result_error_nomem(sqlite3_context *pCtx){ +EXPORT_SYMBOLS SQLITE_API void sqlite3_result_error_nomem(sqlite3_context *pCtx){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetNull(pCtx->pOut); pCtx->isError = SQLITE_NOMEM_BKPT; @@ -89325,7 +89336,7 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){ ** Extract the user data from a sqlite3_context structure and return a ** pointer to it. */ -SQLITE_API void *sqlite3_user_data(sqlite3_context *p){ +EXPORT_SYMBOLS SQLITE_API void *sqlite3_user_data(sqlite3_context *p){ assert( p && p->pFunc ); return p->pFunc->pUserData; } @@ -89498,7 +89509,7 @@ SQLITE_API void *sqlite3_aggregate_context(sqlite3_context *p, int nByte){ ** auxiliary data pointers that is available to all functions within a ** single prepared statement. The iArg values must match. */ -SQLITE_API void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){ +EXPORT_SYMBOLS SQLITE_API void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){ AuxData *pAuxData; assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); @@ -89526,7 +89537,7 @@ SQLITE_API void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){ ** to all functions within the current prepared statement using iArg as an ** access code. */ -SQLITE_API void sqlite3_set_auxdata( +EXPORT_SYMBOLS SQLITE_API void sqlite3_set_auxdata( sqlite3_context *pCtx, int iArg, void *pAux, @@ -174008,13 +174019,55 @@ 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 +#include "sqlite3tokenizer.h" + +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 @@ -174064,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, @@ -174422,6 +174475,18 @@ SQLITE_API int sqlite3_config(int op, ...){ va_list ap; int rc = SQLITE_OK; + #if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS) + case SQLITE_CONFIG_ENABLE_ICU: { + int iVal = va_arg(ap, int); + if( iVal==0 ){ + icuEnable = 0u; + }else{ + icuEnable = 1u; + } + break; + } +#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; @@ -176016,7 +176081,7 @@ static int createFunctionApi( /* ** Create new user functions. */ -SQLITE_API int sqlite3_create_function( +EXPORT_SYMBOLS SQLITE_API int sqlite3_create_function( sqlite3 *db, const char *zFunc, int nArg, @@ -177497,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 && i 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); @@ -184286,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 @@ -184307,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 @@ -184343,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; @@ -207955,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 @@ -247076,15 +246220,6 @@ static void DumpLocksByPager(Pager *pPager) } #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 0000000..8495098 --- /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 -- Gitee From 9c097afbdd05a3ce20e3e54778cc5aa1c4bdccd0 Mon Sep 17 00:00:00 2001 From: zwtmichael Date: Fri, 27 Dec 2024 16:13:44 +0800 Subject: [PATCH 08/12] fix codecheck Signed-off-by: zwtmichael --- include/sqlite3icu.h | 2 +- src/sqlite3.c | 68 ++++++++++++++++++++++---------------------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/include/sqlite3icu.h b/include/sqlite3icu.h index 97505ba..f35c4d0 100644 --- a/include/sqlite3icu.h +++ b/include/sqlite3icu.h @@ -28,7 +28,7 @@ ** This header file is used by programs that want to link against the ** ICU extension. All it does is declare the sqlite3IcuInit() interface. */ -#include "sqlite3.h" +#include "sqlite3sym.h" #include "sqlite3tokenizer.h" #if 0 diff --git a/src/sqlite3.c b/src/sqlite3.c index c0eb3a9..a1ffb0c 100644 --- a/src/sqlite3.c +++ b/src/sqlite3.c @@ -3263,7 +3263,7 @@ SQLITE_API void sqlite3_free_table(char **result); */ SQLITE_API char *sqlite3_mprintf(const char*,...); SQLITE_API char *sqlite3_vmprintf(const char*, va_list); -EXPORT_SYMBOLS SQLITE_API char *sqlite3_snprintf(int,char*,const char*, ...); +SQLITE_API char *sqlite3_snprintf(int,char*,const char*, ...); SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list); /* @@ -3342,10 +3342,10 @@ SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list); ** [sqlite3_free()] or [sqlite3_realloc()]. */ SQLITE_API void *sqlite3_malloc(int); -EXPORT_SYMBOLS SQLITE_API void *sqlite3_malloc64(sqlite3_uint64); -EXPORT_SYMBOLS SQLITE_API void *sqlite3_realloc(void*, int); +SQLITE_API void *sqlite3_malloc64(sqlite3_uint64); +SQLITE_API void *sqlite3_realloc(void*, int); SQLITE_API void *sqlite3_realloc64(void*, sqlite3_uint64); -EXPORT_SYMBOLS SQLITE_API void sqlite3_free(void*); +SQLITE_API void sqlite3_free(void*); SQLITE_API sqlite3_uint64 sqlite3_msize(void*); /* @@ -5957,12 +5957,12 @@ SQLITE_API double sqlite3_value_double(sqlite3_value*); SQLITE_API int sqlite3_value_int(sqlite3_value*); SQLITE_API sqlite3_int64 sqlite3_value_int64(sqlite3_value*); SQLITE_API void *sqlite3_value_pointer(sqlite3_value*, const char*); -EXPORT_SYMBOLS SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value*); -EXPORT_SYMBOLS SQLITE_API const void *sqlite3_value_text16(sqlite3_value*); +SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value*); +SQLITE_API const void *sqlite3_value_text16(sqlite3_value*); SQLITE_API const void *sqlite3_value_text16le(sqlite3_value*); SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*); -EXPORT_SYMBOLS SQLITE_API int sqlite3_value_bytes(sqlite3_value*); -EXPORT_SYMBOLS SQLITE_API int sqlite3_value_bytes16(sqlite3_value*); +SQLITE_API int sqlite3_value_bytes(sqlite3_value*); +SQLITE_API int sqlite3_value_bytes16(sqlite3_value*); SQLITE_API int sqlite3_value_type(sqlite3_value*); SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); SQLITE_API int sqlite3_value_nochange(sqlite3_value*); @@ -6057,7 +6057,7 @@ SQLITE_API void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); ** This routine must be called from the same thread in which ** the application-defined function is running. */ -EXPORT_SYMBOLS SQLITE_API void *sqlite3_user_data(sqlite3_context*); +SQLITE_API void *sqlite3_user_data(sqlite3_context*); /* ** CAPI3REF: Database Connection For Functions @@ -6128,8 +6128,8 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); ** These routines must be called from the same thread in which ** the SQL function is running. */ -EXPORT_SYMBOLS SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N); -EXPORT_SYMBOLS SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); +SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N); +SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); /* @@ -6301,18 +6301,18 @@ SQLITE_API void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)( SQLITE_API void sqlite3_result_blob64(sqlite3_context*,const void*, sqlite3_uint64,void(*)(void*)); SQLITE_API void sqlite3_result_double(sqlite3_context*, double); -EXPORT_SYMBOLS SQLITE_API void sqlite3_result_error(sqlite3_context*, const char*, int); +SQLITE_API void sqlite3_result_error(sqlite3_context*, const char*, int); SQLITE_API void sqlite3_result_error16(sqlite3_context*, const void*, int); SQLITE_API void sqlite3_result_error_toobig(sqlite3_context*); -EXPORT_SYMBOLS SQLITE_API void sqlite3_result_error_nomem(sqlite3_context*); +SQLITE_API void sqlite3_result_error_nomem(sqlite3_context*); SQLITE_API void sqlite3_result_error_code(sqlite3_context*, int); -EXPORT_SYMBOLS SQLITE_API void sqlite3_result_int(sqlite3_context*, int); +SQLITE_API void sqlite3_result_int(sqlite3_context*, int); SQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64); SQLITE_API void sqlite3_result_null(sqlite3_context*); SQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); SQLITE_API void sqlite3_result_text64(sqlite3_context*, const char*,sqlite3_uint64, void(*)(void*), unsigned char encoding); -EXPORT_SYMBOLS SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); +SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*); @@ -6423,7 +6423,7 @@ SQLITE_API int sqlite3_create_collation( void *pArg, int(*xCompare)(void*,int,const void*,int,const void*) ); -EXPORT_SYMBOLS SQLITE_API int sqlite3_create_collation_v2( +SQLITE_API int sqlite3_create_collation_v2( sqlite3*, const char *zName, int eTextRep, @@ -29464,7 +29464,7 @@ SQLITE_API void *sqlite3_malloc(int n){ #endif return n<=0 ? 0 : sqlite3Malloc(n); } -EXPORT_SYMBOLS SQLITE_API void *sqlite3_malloc64(sqlite3_uint64 n){ +SQLITE_API void *sqlite3_malloc64(sqlite3_uint64 n){ #ifndef SQLITE_OMIT_AUTOINIT if( sqlite3_initialize() ) return 0; #endif @@ -29533,7 +29533,7 @@ SQLITE_API sqlite3_uint64 sqlite3_msize(void *p){ /* ** Free memory previously obtained from sqlite3Malloc(). */ -EXPORT_SYMBOLS SQLITE_API void sqlite3_free(void *p){ +SQLITE_API void sqlite3_free(void *p){ if( p==0 ) return; /* IMP: R-49053-54554 */ assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); @@ -29704,7 +29704,7 @@ SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, u64 nBytes){ ** The public interface to sqlite3Realloc. Make sure that the memory ** subsystem is initialized prior to invoking sqliteRealloc. */ -EXPORT_SYMBOLS SQLITE_API void *sqlite3_realloc(void *pOld, int n){ +SQLITE_API void *sqlite3_realloc(void *pOld, int n){ #ifndef SQLITE_OMIT_AUTOINIT if( sqlite3_initialize() ) return 0; #endif @@ -31345,7 +31345,7 @@ SQLITE_API char *sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_li zBuf[acc.nChar] = 0; return zBuf; } -EXPORT_SYMBOLS SQLITE_API char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){ +SQLITE_API char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){ char *z; va_list ap; va_start(ap,zFormat); @@ -88642,10 +88642,10 @@ SQLITE_API const void *sqlite3_value_blob(sqlite3_value *pVal){ return sqlite3_value_text(pVal); } } -EXPORT_SYMBOLS SQLITE_API int sqlite3_value_bytes(sqlite3_value *pVal){ +SQLITE_API int sqlite3_value_bytes(sqlite3_value *pVal){ return sqlite3ValueBytes(pVal, SQLITE_UTF8); } -EXPORT_SYMBOLS SQLITE_API int sqlite3_value_bytes16(sqlite3_value *pVal){ +SQLITE_API int sqlite3_value_bytes16(sqlite3_value *pVal){ return sqlite3ValueBytes(pVal, SQLITE_UTF16NATIVE); } SQLITE_API double sqlite3_value_double(sqlite3_value *pVal){ @@ -88674,11 +88674,11 @@ SQLITE_API void *sqlite3_value_pointer(sqlite3_value *pVal, const char *zPType){ return 0; } } -EXPORT_SYMBOLS SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value *pVal){ +SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value *pVal){ return (const unsigned char *)sqlite3ValueText(pVal, SQLITE_UTF8); } #ifndef SQLITE_OMIT_UTF16 -EXPORT_SYMBOLS SQLITE_API const void *sqlite3_value_text16(sqlite3_value* pVal){ +SQLITE_API const void *sqlite3_value_text16(sqlite3_value* pVal){ return sqlite3ValueText(pVal, SQLITE_UTF16NATIVE); } SQLITE_API const void *sqlite3_value_text16be(sqlite3_value *pVal){ @@ -88903,7 +88903,7 @@ SQLITE_API void sqlite3_result_double(sqlite3_context *pCtx, double rVal){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetDouble(pCtx->pOut, rVal); } -EXPORT_SYMBOLS SQLITE_API void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){ +SQLITE_API void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); pCtx->isError = SQLITE_ERROR; sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF8, SQLITE_TRANSIENT); @@ -88915,7 +88915,7 @@ SQLITE_API void sqlite3_result_error16(sqlite3_context *pCtx, const void *z, int sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT); } #endif -EXPORT_SYMBOLS SQLITE_API void sqlite3_result_int(sqlite3_context *pCtx, int iVal){ +SQLITE_API void sqlite3_result_int(sqlite3_context *pCtx, int iVal){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetInt64(pCtx->pOut, (i64)iVal); } @@ -88971,7 +88971,7 @@ SQLITE_API void sqlite3_result_text64( } } #ifndef SQLITE_OMIT_UTF16 -EXPORT_SYMBOLS SQLITE_API void sqlite3_result_text16( +SQLITE_API void sqlite3_result_text16( sqlite3_context *pCtx, const void *z, int n, @@ -89045,7 +89045,7 @@ SQLITE_API void sqlite3_result_error_toobig(sqlite3_context *pCtx){ } /* An SQLITE_NOMEM error. */ -EXPORT_SYMBOLS SQLITE_API void sqlite3_result_error_nomem(sqlite3_context *pCtx){ +SQLITE_API void sqlite3_result_error_nomem(sqlite3_context *pCtx){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetNull(pCtx->pOut); pCtx->isError = SQLITE_NOMEM_BKPT; @@ -89336,7 +89336,7 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){ ** Extract the user data from a sqlite3_context structure and return a ** pointer to it. */ -EXPORT_SYMBOLS SQLITE_API void *sqlite3_user_data(sqlite3_context *p){ +SQLITE_API void *sqlite3_user_data(sqlite3_context *p){ assert( p && p->pFunc ); return p->pFunc->pUserData; } @@ -89509,7 +89509,7 @@ SQLITE_API void *sqlite3_aggregate_context(sqlite3_context *p, int nByte){ ** auxiliary data pointers that is available to all functions within a ** single prepared statement. The iArg values must match. */ -EXPORT_SYMBOLS SQLITE_API void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){ +SQLITE_API void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){ AuxData *pAuxData; assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); @@ -89537,7 +89537,7 @@ EXPORT_SYMBOLS SQLITE_API void *sqlite3_get_auxdata(sqlite3_context *pCtx, int i ** to all functions within the current prepared statement using iArg as an ** access code. */ -EXPORT_SYMBOLS SQLITE_API void sqlite3_set_auxdata( +SQLITE_API void sqlite3_set_auxdata( sqlite3_context *pCtx, int iArg, void *pAux, @@ -173995,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 ***************************************/ @@ -174029,7 +174030,6 @@ SQLITE_PRIVATE int sqlite3IcuInitInner(sqlite3 *db); #ifndef _WIN32 #include #endif -#include "sqlite3tokenizer.h" typedef void (*sqlite3Fts3IcuTokenizerModule_ptr)(sqlite3_tokenizer_module const** ppModule); typedef int (*sqlite3IcuInit_ptr)(sqlite3 *db); @@ -176081,7 +176081,7 @@ static int createFunctionApi( /* ** Create new user functions. */ -EXPORT_SYMBOLS SQLITE_API int sqlite3_create_function( +SQLITE_API int sqlite3_create_function( sqlite3 *db, const char *zFunc, int nArg, @@ -177727,7 +177727,7 @@ SQLITE_API int sqlite3_create_collation( /* ** Register a new collation sequence with the database handle db. */ -EXPORT_SYMBOLS SQLITE_API int sqlite3_create_collation_v2( +SQLITE_API int sqlite3_create_collation_v2( sqlite3* db, const char *zName, int enc, -- Gitee From 30ad6b3c4b8269d2b184cdb3dc74f933a751e962 Mon Sep 17 00:00:00 2001 From: zwtmichael Date: Fri, 27 Dec 2024 17:02:48 +0800 Subject: [PATCH 09/12] modify icuEnable Signed-off-by: zwtmichael --- include/sqlite3icu.h | 12 ++++++------ src/sqlite3.c | 20 ++++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/include/sqlite3icu.h b/include/sqlite3icu.h index f35c4d0..8c680bc 100644 --- a/include/sqlite3icu.h +++ b/include/sqlite3icu.h @@ -31,20 +31,20 @@ #include "sqlite3sym.h" #include "sqlite3tokenizer.h" -#if 0 +#ifdef __cplusplus extern "C" { -#endif /* __cplusplus */ +#endif SQLITE_API int sqlite3IcuInit(sqlite3 *db); -#if 0 -} /* extern "C" */ -#endif /* __cplusplus */ - /************** 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/src/sqlite3.c b/src/sqlite3.c index a1ffb0c..4c27691 100644 --- a/src/sqlite3.c +++ b/src/sqlite3.c @@ -174474,24 +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) - case SQLITE_CONFIG_ENABLE_ICU: { - int iVal = va_arg(ap, int); - if( iVal==0 ){ - icuEnable = 0u; - }else{ - icuEnable = 1u; - } - break; +#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 -- Gitee From 8c1ee670cae9cdb7009bd12a873cc56c15ccbce8 Mon Sep 17 00:00:00 2001 From: zwtmichael Date: Sat, 28 Dec 2024 14:49:25 +0800 Subject: [PATCH 10/12] fix cross platform build Signed-off-by: zwtmichael --- BUILD.gn | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/BUILD.gn b/BUILD.gn index 8258b41..3ae3aed 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -60,6 +60,7 @@ ohos_shared_library("sqliteicu") { 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" ] @@ -67,7 +68,7 @@ ohos_shared_library("sqliteicu") { deps += [ "//commonlibrary/c_utils/base:utils" ] } } else { - external_deps = [ "c_utils:utils" ] + external_deps += [ "c_utils:utils" ] } external_deps += [ "icu:shared_icui18n", -- Gitee From 96bd6e973fd59c2114ddcbfe58dc5773f00cd1b2 Mon Sep 17 00:00:00 2001 From: zwtmichael Date: Mon, 30 Dec 2024 19:44:48 +0800 Subject: [PATCH 11/12] optimize build Signed-off-by: zwtmichael --- bundle.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bundle.json b/bundle.json index ce2296e..1970820 100644 --- a/bundle.json +++ b/bundle.json @@ -29,7 +29,10 @@ ] }, "build": { - "sub_component": [], + "sub_component": [ + "//third_party/sqlite:sqlite", + "//third_party/sqlite:sqliteicu" + ], "inner_kits": [ { "name": "//third_party/sqlite:sqlite", -- Gitee From c096b09d23a68415e4bc464c0708ad26f5756b38 Mon Sep 17 00:00:00 2001 From: Lee <18800203378@163.com> Date: Mon, 13 Jan 2025 09:41:54 +0800 Subject: [PATCH 12/12] chg README Signed-off-by: Lee <18800203378@163.com> --- README.OpenSource | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.OpenSource b/README.OpenSource index e4ece76..d1cd452 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", -- Gitee