From 8aa07d5e57e0bf65fbcc1e9590847544d5304222 Mon Sep 17 00:00:00 2001 From: Marc Prud'hommeaux Date: Tue, 20 Jan 2026 15:12:56 -0500 Subject: [PATCH] Update to SQLite 3.51.2 and SQLCipher 4.13.0 --- .github/workflows/ci.yml | 6 +- Sources/SQLCipher/sqlite/sqlite3.c | 167 ++++++++++++++++++----------- Sources/SQLCipher/sqlite/sqlite3.h | 10 +- 3 files changed, 115 insertions(+), 68 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ae10e1..3145c6d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: sudo rm -rf /usr/share/dotnet /opt/ghc /opt/hostedtoolcache/CodeQL docker image prune --all --force docker builder prune -a - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: "Test Swift Package Android" uses: skiptools/swift-android-action@v2 - name: "Test Swift Package Linux" @@ -25,7 +25,7 @@ jobs: macos-ios: runs-on: macos-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: "Test Swift Package macOS" run: swift test - name: "Test Swift Package iOS" @@ -37,6 +37,6 @@ jobs: with: branch: swift-6.2-release tag: 6.2-RELEASE - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - run: swift test diff --git a/Sources/SQLCipher/sqlite/sqlite3.c b/Sources/SQLCipher/sqlite/sqlite3.c index db28b5b..94c0004 100644 --- a/Sources/SQLCipher/sqlite/sqlite3.c +++ b/Sources/SQLCipher/sqlite/sqlite3.c @@ -8,7 +8,7 @@ #pragma GCC diagnostic ignored "-Wambiguous-macro" /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.51.1. By combining all the individual C code files into this +** version 3.51.2. 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 @@ -26,7 +26,7 @@ ** separate file. This file contains only code for the core SQLite library. ** ** The content in this amalgamation comes from Fossil check-in -** 281fc0e9afc38674b9b0991943b9e9d1e64c with changes in files: +** b270f8339eb13b504d0b2ba154ebca966b7d with changes in files: ** ** .fossil-settings/binary-glob ** .fossil-settings/empty-dirs @@ -504,12 +504,12 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.51.1" -#define SQLITE_VERSION_NUMBER 3051001 -#define SQLITE_SOURCE_ID "2025-11-28 17:28:25 281fc0e9afc38674b9b0991943b9e9d1e64c6cbdb133d35f6f5c87ff6af3alt1" +#define SQLITE_VERSION "3.51.2" +#define SQLITE_VERSION_NUMBER 3051002 +#define SQLITE_SOURCE_ID "2026-01-09 17:27:48 b270f8339eb13b504d0b2ba154ebca966b7dde08e40c3ed7d559749818cbalt1" #define SQLITE_SCM_BRANCH "branch-3.51" -#define SQLITE_SCM_TAGS "release version-3.51.1" -#define SQLITE_SCM_DATETIME "2025-11-28T17:28:25.933Z" +#define SQLITE_SCM_TAGS "release version-3.51.2" +#define SQLITE_SCM_DATETIME "2026-01-09T17:27:48.405Z" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -41367,12 +41367,18 @@ static int unixLock(sqlite3_file *id, int eFileLock){ pInode->nLock++; pInode->nShared = 1; } - }else if( (eFileLock==EXCLUSIVE_LOCK && pInode->nShared>1) - || unixIsSharingShmNode(pFile) - ){ + }else if( eFileLock==EXCLUSIVE_LOCK && pInode->nShared>1 ){ /* We are trying for an exclusive lock but another thread in this ** same process is still holding a shared lock. */ rc = SQLITE_BUSY; + }else if( unixIsSharingShmNode(pFile) ){ + /* We are in WAL mode and attempting to delete the SHM and WAL + ** files due to closing the connection or changing out of WAL mode, + ** but another process still holds locks on the SHM file, thus + ** indicating that database locks have been broken, perhaps due + ** to a rogue close(open(dbFile)) or similar. + */ + rc = SQLITE_BUSY; }else{ /* The request was for a RESERVED or EXCLUSIVE lock. It is ** assumed that there is a SHARED or greater lock on the file @@ -44011,26 +44017,21 @@ static int unixFcntlExternalReader(unixFile *pFile, int *piOut){ ** still not a disaster. */ static int unixIsSharingShmNode(unixFile *pFile){ - int rc; unixShmNode *pShmNode; + struct flock lock; if( pFile->pShm==0 ) return 0; if( pFile->ctrlFlags & UNIXFILE_EXCL ) return 0; pShmNode = pFile->pShm->pShmNode; - rc = 1; - unixEnterMutex(); - if( ALWAYS(pShmNode->nRef==1) ){ - struct flock lock; - lock.l_whence = SEEK_SET; - lock.l_start = UNIX_SHM_DMS; - lock.l_len = 1; - lock.l_type = F_WRLCK; - osFcntl(pShmNode->hShm, F_GETLK, &lock); - if( lock.l_type==F_UNLCK ){ - rc = 0; - } - } - unixLeaveMutex(); - return rc; +#if SQLITE_ATOMIC_INTRINSICS + assert( AtomicLoad(&pShmNode->nRef)==1 ); +#endif + memset(&lock, 0, sizeof(lock)); + lock.l_whence = SEEK_SET; + lock.l_start = UNIX_SHM_DMS; + lock.l_len = 1; + lock.l_type = F_WRLCK; + osFcntl(pShmNode->hShm, F_GETLK, &lock); + return (lock.l_type!=F_UNLCK); } /* @@ -109502,7 +109503,7 @@ SQLITE_API void sqlite3pager_reset(Pager *pPager); #define CIPHER_STR(s) #s #ifndef CIPHER_VERSION_NUMBER -#define CIPHER_VERSION_NUMBER 4.12.0 +#define CIPHER_VERSION_NUMBER 4.13.0 #endif #ifndef CIPHER_VERSION_BUILD @@ -109819,7 +109820,7 @@ static void (*const sqlcipher_fini_func)(void) __attribute__((used, section(".fi static void sqlcipher_exportFunc(sqlite3_context*, int, sqlite3_value**); static int sqlcipher_export_init(sqlite3* db, const char** errmsg, const struct sqlite3_api_routines* api) { - sqlite3_create_function_v2(db, "sqlcipher_export", -1, SQLITE_TEXT, 0, sqlcipher_exportFunc, 0, 0, 0); + sqlite3_create_function_v2(db, "sqlcipher_export", -1, SQLITE_UTF8, 0, sqlcipher_exportFunc, 0, 0, 0); return SQLITE_OK; } @@ -120420,9 +120421,22 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ pParse->nMem += nReg; if( pExpr->op==TK_SELECT ){ dest.eDest = SRT_Mem; - dest.iSdst = dest.iSDParm; + if( (pSel->selFlags&SF_Distinct) && pSel->pLimit && pSel->pLimit->pRight ){ + /* If there is both a DISTINCT and an OFFSET clause, then allocate + ** a separate dest.iSdst array for sqlite3Select() and other + ** routines to populate. In this case results will be copied over + ** into the dest.iSDParm array only after OFFSET processing. This + ** ensures that in the case where OFFSET excludes all rows, the + ** dest.iSDParm array is not left populated with the contents of the + ** last row visited - it should be all NULLs if all rows were + ** excluded by OFFSET. */ + dest.iSdst = pParse->nMem+1; + pParse->nMem += nReg; + }else{ + dest.iSdst = dest.iSDParm; + } dest.nSdst = nReg; - sqlite3VdbeAddOp3(v, OP_Null, 0, dest.iSDParm, dest.iSDParm+nReg-1); + sqlite3VdbeAddOp3(v, OP_Null, 0, dest.iSDParm, pParse->nMem); VdbeComment((v, "Init subquery result")); }else{ dest.eDest = SRT_Exists; @@ -153441,9 +153455,14 @@ static void selectInnerLoop( assert( nResultCol<=pDest->nSdst ); pushOntoSorter( pParse, pSort, p, regResult, regOrig, nResultCol, nPrefixReg); + pDest->iSDParm = regResult; }else{ assert( nResultCol==pDest->nSdst ); - assert( regResult==iParm ); + if( regResult!=iParm ){ + /* This occurs in cases where the SELECT had both a DISTINCT and + ** an OFFSET clause. */ + sqlite3VdbeAddOp3(v, OP_Copy, regResult, iParm, nResultCol-1); + } /* The LIMIT clause will jump out of the loop for us */ } break; @@ -159458,12 +159477,24 @@ static SQLITE_NOINLINE void existsToJoin( && (pSub->selFlags & SF_Aggregate)==0 && !pSub->pSrc->a[0].fg.isSubquery && pSub->pLimit==0 + && pSub->pPrior==0 ){ + /* Before combining the sub-select with the parent, renumber the + ** cursor used by the subselect. This is because the EXISTS expression + ** might be a copy of another EXISTS expression from somewhere + ** else in the tree, and in this case it is important that it use + ** a unique cursor number. */ + sqlite3 *db = pParse->db; + int *aCsrMap = sqlite3DbMallocZero(db, (pParse->nTab+2)*sizeof(int)); + if( aCsrMap==0 ) return; + aCsrMap[0] = (pParse->nTab+1); + renumberCursors(pParse, pSub, -1, aCsrMap); + sqlite3DbFree(db, aCsrMap); + memset(pWhere, 0, sizeof(*pWhere)); pWhere->op = TK_INTEGER; pWhere->u.iValue = 1; ExprSetProperty(pWhere, EP_IntValue); - assert( p->pWhere!=0 ); pSub->pSrc->a[0].fg.fromExists = 1; pSub->pSrc->a[0].fg.jointype |= JT_CROSS; @@ -179271,6 +179302,9 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ sqlite3 *db = pParse->db; int iEnd = sqlite3VdbeCurrentAddr(v); int nRJ = 0; +#ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT + int addrSeek = 0; +#endif /* Generate loop termination code. */ @@ -179283,7 +179317,10 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ ** the RIGHT JOIN table */ WhereRightJoin *pRJ = pLevel->pRJ; sqlite3VdbeResolveLabel(v, pLevel->addrCont); - pLevel->addrCont = 0; + /* Replace addrCont with a new label that will never be used, just so + ** the subsequent call to resolve pLevel->addrCont will have something + ** to resolve. */ + pLevel->addrCont = sqlite3VdbeMakeLabel(pParse); pRJ->endSubrtn = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp3(v, OP_Return, pRJ->regReturn, pRJ->addrSubrtn, 1); VdbeCoverage(v); @@ -179292,7 +179329,6 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ pLoop = pLevel->pWLoop; if( pLevel->op!=OP_Noop ){ #ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT - int addrSeek = 0; Index *pIdx; int n; if( pWInfo->eDistinct==WHERE_DISTINCT_ORDERED @@ -179315,25 +179351,26 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ sqlite3VdbeAddOp2(v, OP_Goto, 1, pLevel->p2); } #endif /* SQLITE_DISABLE_SKIPAHEAD_DISTINCT */ - if( pTabList->a[pLevel->iFrom].fg.fromExists && i==pWInfo->nLevel-1 ){ - /* If the EXISTS-to-JOIN optimization was applied, then the EXISTS - ** loop(s) will be the inner-most loops of the join. There might be - ** multiple EXISTS loops, but they will all be nested, and the join - ** order will not have been changed by the query planner. If the - ** inner-most EXISTS loop sees a single successful row, it should - ** break out of *all* EXISTS loops. But only the inner-most of the - ** nested EXISTS loops should do this breakout. */ - int nOuter = 0; /* Nr of outer EXISTS that this one is nested within */ - while( nOutera[pLevel[-nOuter-1].iFrom].fg.fromExists ) break; - nOuter++; - } - testcase( nOuter>0 ); - sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel[-nOuter].addrBrk); - VdbeComment((v, "EXISTS break")); - } - /* The common case: Advance to the next row */ - if( pLevel->addrCont ) sqlite3VdbeResolveLabel(v, pLevel->addrCont); + } + if( pTabList->a[pLevel->iFrom].fg.fromExists && i==pWInfo->nLevel-1 ){ + /* If the EXISTS-to-JOIN optimization was applied, then the EXISTS + ** loop(s) will be the inner-most loops of the join. There might be + ** multiple EXISTS loops, but they will all be nested, and the join + ** order will not have been changed by the query planner. If the + ** inner-most EXISTS loop sees a single successful row, it should + ** break out of *all* EXISTS loops. But only the inner-most of the + ** nested EXISTS loops should do this breakout. */ + int nOuter = 0; /* Nr of outer EXISTS that this one is nested within */ + while( nOutera[pLevel[-nOuter-1].iFrom].fg.fromExists ) break; + nOuter++; + } + testcase( nOuter>0 ); + sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel[-nOuter].addrBrk); + VdbeComment((v, "EXISTS break")); + } + sqlite3VdbeResolveLabel(v, pLevel->addrCont); + if( pLevel->op!=OP_Noop ){ sqlite3VdbeAddOp3(v, pLevel->op, pLevel->p1, pLevel->p2, pLevel->p3); sqlite3VdbeChangeP5(v, pLevel->p5); VdbeCoverage(v); @@ -179346,10 +179383,11 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ VdbeCoverage(v); } #ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT - if( addrSeek ) sqlite3VdbeJumpHere(v, addrSeek); + if( addrSeek ){ + sqlite3VdbeJumpHere(v, addrSeek); + addrSeek = 0; + } #endif - }else if( pLevel->addrCont ){ - sqlite3VdbeResolveLabel(v, pLevel->addrCont); } if( (pLoop->wsFlags & WHERE_IN_ABLE)!=0 && pLevel->u.in.nIn>0 ){ struct InLoop *pIn; @@ -224765,7 +224803,7 @@ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){ if( node.zData==0 ) return; nData = sqlite3_value_bytes(apArg[1]); if( nData<4 ) return; - if( nDatanLevel ){ if( fts5IndexMerge(p, &pStruct, nMerge, nMin) ){ @@ -265616,7 +265660,7 @@ static void fts5SourceIdFunc( ){ assert( nArg==0 ); UNUSED_PARAM2(nArg, apUnused); - sqlite3_result_text(pCtx, "fts5: 2025-11-28 17:28:25 281fc0e9afc38674b9b0991943b9e9d1e64c6cbdb133d35f6f5c87ff6af38a88", -1, SQLITE_TRANSIENT); + sqlite3_result_text(pCtx, "fts5: 2026-01-09 17:27:48 b270f8339eb13b504d0b2ba154ebca966b7dde08e40c3ed7d559749818cb2075", -1, SQLITE_TRANSIENT); } /* @@ -271251,6 +271295,9 @@ SQLITE_API int sqlite3_stmt_init( // // This file was obtained from the last known release: // https://raw.githubusercontent.com/sqlcipher/sqlcipher/refs/tags/v4.11.0/src/crypto_libtomcrypt.c +// +// Note that this file is not compiled directly, but instead is +// added to the SQLite amalgamation by scripts/build_sqlcipher.sh /* BEGIN SQLCIPHER */ #ifdef SQLITE_HAS_CODEC diff --git a/Sources/SQLCipher/sqlite/sqlite3.h b/Sources/SQLCipher/sqlite/sqlite3.h index 3afd20e..6e548d8 100644 --- a/Sources/SQLCipher/sqlite/sqlite3.h +++ b/Sources/SQLCipher/sqlite/sqlite3.h @@ -146,12 +146,12 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.51.1" -#define SQLITE_VERSION_NUMBER 3051001 -#define SQLITE_SOURCE_ID "2025-11-28 17:28:25 281fc0e9afc38674b9b0991943b9e9d1e64c6cbdb133d35f6f5c87ff6af3alt1" +#define SQLITE_VERSION "3.51.2" +#define SQLITE_VERSION_NUMBER 3051002 +#define SQLITE_SOURCE_ID "2026-01-09 17:27:48 b270f8339eb13b504d0b2ba154ebca966b7dde08e40c3ed7d559749818cbalt1" #define SQLITE_SCM_BRANCH "branch-3.51" -#define SQLITE_SCM_TAGS "release version-3.51.1" -#define SQLITE_SCM_DATETIME "2025-11-28T17:28:25.933Z" +#define SQLITE_SCM_TAGS "release version-3.51.2" +#define SQLITE_SCM_DATETIME "2026-01-09T17:27:48.405Z" /* ** CAPI3REF: Run-Time Library Version Numbers