| | @@ -16,11 +16,11 @@ |
| 16 | 16 | ** if you want a wrapper to interface SQLite with your choice of programming |
| 17 | 17 | ** language. The code for the "sqlite3" command-line shell is also in a |
| 18 | 18 | ** separate file. This file contains only code for the core SQLite library. |
| 19 | 19 | ** |
| 20 | 20 | ** The content in this amalgamation comes from Fossil check-in |
| 21 | | -** 9ac4a33a2932d353c4871fd8e09c10addf82 with changes in files: |
| 21 | +** 87c37dab7e53d1bd891f3fed624963b35ab1 with changes in files: |
| 22 | 22 | ** |
| 23 | 23 | ** |
| 24 | 24 | */ |
| 25 | 25 | #ifndef SQLITE_AMALGAMATION |
| 26 | 26 | #define SQLITE_CORE 1 |
| | @@ -467,14 +467,14 @@ |
| 467 | 467 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 468 | 468 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 469 | 469 | */ |
| 470 | 470 | #define SQLITE_VERSION "3.54.0" |
| 471 | 471 | #define SQLITE_VERSION_NUMBER 3054000 |
| 472 | | -#define SQLITE_SOURCE_ID "2026-05-21 15:14:35 9ac4a33a2932d353c4871fd8e09c10addf827f1fc3fc9380037d738cf2cd0353" |
| 472 | +#define SQLITE_SOURCE_ID "2026-05-28 11:29:05 87c37dab7e53d1bd891f3fed624963b35ab15a785706d0964b5d07ab70421c10" |
| 473 | 473 | #define SQLITE_SCM_BRANCH "trunk" |
| 474 | 474 | #define SQLITE_SCM_TAGS "" |
| 475 | | -#define SQLITE_SCM_DATETIME "2026-05-21T15:14:35.420Z" |
| 475 | +#define SQLITE_SCM_DATETIME "2026-05-28T11:29:05.704Z" |
| 476 | 476 | |
| 477 | 477 | /* |
| 478 | 478 | ** CAPI3REF: Run-Time Library Version Numbers |
| 479 | 479 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 480 | 480 | ** |
| | @@ -13183,15 +13183,27 @@ |
| 13183 | 13183 | ** <dt>SQLITE_CHANGESETAPPLY_FKNOACTION <dd> |
| 13184 | 13184 | ** If this flag it set, then all foreign key constraints in the target |
| 13185 | 13185 | ** database behave as if they were declared with "ON UPDATE NO ACTION ON |
| 13186 | 13186 | ** DELETE NO ACTION", even if they are actually CASCADE, RESTRICT, SET NULL |
| 13187 | 13187 | ** or SET DEFAULT. |
| 13188 | +** |
| 13189 | +** <dt>SQLITE_CHANGESETAPPLY_NOUPDATELOOP <dd> |
| 13190 | +** Sometimes, a changeset contains two or more update statements such that |
| 13191 | +** although after applying all updates the database will contain no |
| 13192 | +** constraint violations, no single update can be applied before the others. |
| 13193 | +** The simplest example of this is a pair of UPDATEs that have "swapped" |
| 13194 | +** two column values with a UNIQUE constraint. |
| 13195 | +** <p> |
| 13196 | +** Usually, sqlite3changeset_apply() and similar functions work hard to try |
| 13197 | +** to find a way to apply such a changeset. However, if this flag is set, |
| 13198 | +** then all such updates are considered CONSTRAINT conflicts. |
| 13188 | 13199 | */ |
| 13189 | 13200 | #define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001 |
| 13190 | 13201 | #define SQLITE_CHANGESETAPPLY_INVERT 0x0002 |
| 13191 | 13202 | #define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004 |
| 13192 | 13203 | #define SQLITE_CHANGESETAPPLY_FKNOACTION 0x0008 |
| 13204 | +#define SQLITE_CHANGESETAPPLY_NOUPDATELOOP 0x0010 |
| 13193 | 13205 | |
| 13194 | 13206 | /* |
| 13195 | 13207 | ** CAPI3REF: Constants Passed To The Conflict Handler |
| 13196 | 13208 | ** |
| 13197 | 13209 | ** Values that may be passed as the second argument to a conflict-handler. |
| | @@ -61121,11 +61133,11 @@ |
| 61121 | 61133 | */ |
| 61122 | 61134 | rc = sqlite3OsFileSize(pSuper, &nSuperJournal); |
| 61123 | 61135 | if( rc!=SQLITE_OK ) goto delsuper_out; |
| 61124 | 61136 | nSuperPtr = 1 + (i64)pVfs->mxPathname; |
| 61125 | 61137 | assert( nSuperJournal>=0 && nSuperPtr>0 ); |
| 61126 | | - zFree = sqlite3Malloc(4 + nSuperJournal + nSuperPtr + 2); |
| 61138 | + zFree = sqlite3Malloc(4 + nSuperJournal + 2 + nSuperPtr + 2); |
| 61127 | 61139 | if( !zFree ){ |
| 61128 | 61140 | rc = SQLITE_NOMEM_BKPT; |
| 61129 | 61141 | goto delsuper_out; |
| 61130 | 61142 | }else{ |
| 61131 | 61143 | assert( nSuperJournal<=0x7fffffff ); |
| | @@ -61382,14 +61394,14 @@ |
| 61382 | 61394 | ** present on disk, then the journal is not hot and does not need to be |
| 61383 | 61395 | ** played back. |
| 61384 | 61396 | ** |
| 61385 | 61397 | ** TODO: Technically the following is an error because it assumes that |
| 61386 | 61398 | ** buffer Pager.pTmpSpace is (mxPathname+1) bytes or larger. i.e. that |
| 61387 | | - ** (pPager->pageSize >= pPager->pVfs->mxPathname+1). Using os_unix.c, |
| 61399 | + ** ((pPager->pageSize+8) >= pPager->pVfs->mxPathname+1). Using os_unix.c, |
| 61388 | 61400 | ** mxPathname is 512, which is the same as the minimum allowable value |
| 61389 | | - ** for pageSize. |
| 61390 | | - */ |
| 61401 | + ** for pageSize, and so this assumption holds. But it might not for some |
| 61402 | + ** custom VFS. */ |
| 61391 | 61403 | zSuper = pPager->pTmpSpace; |
| 61392 | 61404 | rc = readSuperJournal(pPager->jfd, zSuper, 1+(i64)pPager->pVfs->mxPathname); |
| 61393 | 61405 | if( rc==SQLITE_OK && zSuper[0] ){ |
| 61394 | 61406 | rc = sqlite3OsAccess(pVfs, zSuper, SQLITE_ACCESS_EXISTS, &res); |
| 61395 | 61407 | } |
| | @@ -77285,11 +77297,13 @@ |
| 77285 | 77297 | ** in the overflow chain. The page number of the first overflow page is |
| 77286 | 77298 | ** stored in aOverflow[0], etc. A value of 0 in the aOverflow[] array |
| 77287 | 77299 | ** means "not yet known" (the cache is lazily populated). |
| 77288 | 77300 | */ |
| 77289 | 77301 | if( (pCur->curFlags & BTCF_ValidOvfl)==0 ){ |
| 77290 | | - int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize; |
| 77302 | + i64 nOvfl = pCur->info.nPayload; |
| 77303 | + testcase( nOvfl - pCur->info.nLocal + ovflSize - 1 > 0xffffffffU ); |
| 77304 | + nOvfl = (nOvfl - pCur->info.nLocal + ovflSize-1)/ovflSize; |
| 77291 | 77305 | if( pCur->aOverflow==0 |
| 77292 | 77306 | || nOvfl*(int)sizeof(Pgno) > sqlite3MallocSize(pCur->aOverflow) |
| 77293 | 77307 | ){ |
| 77294 | 77308 | Pgno *aNew; |
| 77295 | 77309 | if( sqlite3FaultSim(413) ){ |
| | @@ -213876,13 +213890,14 @@ |
| 213876 | 213890 | */ |
| 213877 | 213891 | static u32 jsonbPayloadSize(const JsonParse *pParse, u32 i, u32 *pSz){ |
| 213878 | 213892 | u8 x; |
| 213879 | 213893 | u32 sz; |
| 213880 | 213894 | u32 n; |
| 213881 | | - assert( i<=pParse->nBlob ); |
| 213882 | | - x = pParse->aBlob[i]>>4; |
| 213883 | | - if( x<=11 ){ |
| 213895 | + if( i>=pParse->nBlob ){ |
| 213896 | + *pSz = 0; |
| 213897 | + return 0; |
| 213898 | + }else if( (x = pParse->aBlob[i]>>4)<=11 ){ |
| 213884 | 213899 | sz = x; |
| 213885 | 213900 | n = 1; |
| 213886 | 213901 | }else if( x==12 ){ |
| 213887 | 213902 | if( i+1>=pParse->nBlob ){ |
| 213888 | 213903 | *pSz = 0; |
| | @@ -217005,10 +217020,19 @@ |
| 217005 | 217020 | }else{ |
| 217006 | 217021 | jsonPrintf(sz+2,&p->path,".%.*s", sz, z); |
| 217007 | 217022 | } |
| 217008 | 217023 | } |
| 217009 | 217024 | } |
| 217025 | + |
| 217026 | +/* Report a "malformed JSON" or OOM error against the cursor. |
| 217027 | +*/ |
| 217028 | +static int jsonEachMalformedInput(sqlite3_vtab_cursor *cur){ |
| 217029 | + sqlite3_free(cur->pVtab->zErrMsg); |
| 217030 | + cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON"); |
| 217031 | + jsonEachCursorReset((JsonEachCursor*)cur); |
| 217032 | + return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; |
| 217033 | +} |
| 217010 | 217034 | |
| 217011 | 217035 | /* Advance the cursor to the next element for json_tree() */ |
| 217012 | 217036 | static int jsonEachNext(sqlite3_vtab_cursor *cur){ |
| 217013 | 217037 | JsonEachCursor *p = (JsonEachCursor*)cur; |
| 217014 | 217038 | int rc = SQLITE_OK; |
| | @@ -217017,10 +217041,11 @@ |
| 217017 | 217041 | u8 levelChange = 0; |
| 217018 | 217042 | u32 n, sz = 0; |
| 217019 | 217043 | u32 i = jsonSkipLabel(p); |
| 217020 | 217044 | x = p->sParse.aBlob[i] & 0x0f; |
| 217021 | 217045 | n = jsonbPayloadSize(&p->sParse, i, &sz); |
| 217046 | + if( n==0 )return jsonEachMalformedInput(cur); |
| 217022 | 217047 | if( x==JSONB_OBJECT || x==JSONB_ARRAY ){ |
| 217023 | 217048 | JsonParent *pParent; |
| 217024 | 217049 | if( p->nParent>=p->nParentAlloc ){ |
| 217025 | 217050 | JsonParent *pNew; |
| 217026 | 217051 | u64 nNew; |
| | @@ -217062,10 +217087,11 @@ |
| 217062 | 217087 | } |
| 217063 | 217088 | }else{ |
| 217064 | 217089 | u32 n, sz = 0; |
| 217065 | 217090 | u32 i = jsonSkipLabel(p); |
| 217066 | 217091 | n = jsonbPayloadSize(&p->sParse, i, &sz); |
| 217092 | + if( n==0 )return jsonEachMalformedInput(cur); |
| 217067 | 217093 | p->i = i + n + sz; |
| 217068 | 217094 | } |
| 217069 | 217095 | if( p->eType==JSONB_ARRAY && p->nParent ){ |
| 217070 | 217096 | p->aParent[p->nParent-1].iKey++; |
| 217071 | 217097 | } |
| | @@ -217298,11 +217324,11 @@ |
| 217298 | 217324 | } |
| 217299 | 217325 | if( jsonConvertTextToBlob(&p->sParse, 0) ){ |
| 217300 | 217326 | if( p->sParse.oom ){ |
| 217301 | 217327 | return SQLITE_NOMEM; |
| 217302 | 217328 | } |
| 217303 | | - goto json_each_malformed_input; |
| 217329 | + return jsonEachMalformedInput(cur); |
| 217304 | 217330 | } |
| 217305 | 217331 | } |
| 217306 | 217332 | if( idxNum==3 ){ |
| 217307 | 217333 | zRoot = (const char*)sqlite3_value_text(argv[1]); |
| 217308 | 217334 | if( zRoot==0 ) return SQLITE_OK; |
| | @@ -217359,16 +217385,10 @@ |
| 217359 | 217385 | p->aParent[0].iEnd = p->iEnd; |
| 217360 | 217386 | p->aParent[0].iHead = p->i; |
| 217361 | 217387 | p->aParent[0].iValue = i; |
| 217362 | 217388 | } |
| 217363 | 217389 | return SQLITE_OK; |
| 217364 | | - |
| 217365 | | -json_each_malformed_input: |
| 217366 | | - sqlite3_free(cur->pVtab->zErrMsg); |
| 217367 | | - cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON"); |
| 217368 | | - jsonEachCursorReset(p); |
| 217369 | | - return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; |
| 217370 | 217390 | } |
| 217371 | 217391 | |
| 217372 | 217392 | /* The methods of the json_each virtual table */ |
| 217373 | 217393 | static sqlite3_module jsonEachModule = { |
| 217374 | 217394 | 0, /* iVersion */ |
| | @@ -224339,10 +224359,11 @@ |
| 224339 | 224359 | for(i=0; i<sizeof(aStrength)/sizeof(aStrength[0]); i++){ |
| 224340 | 224360 | sqlite3_str_appendf(pStr, " %s", aStrength[i].zName); |
| 224341 | 224361 | } |
| 224342 | 224362 | sqlite3_result_error(p, sqlite3_str_value(pStr), -1); |
| 224343 | 224363 | sqlite3_free(sqlite3_str_finish(pStr)); |
| 224364 | + ucol_close(pUCollator); |
| 224344 | 224365 | return; |
| 224345 | 224366 | } |
| 224346 | 224367 | } |
| 224347 | 224368 | rc = sqlite3_create_collation_v2(db, zName, SQLITE_UTF16, (void *)pUCollator, |
| 224348 | 224369 | icuCollationColl, icuCollationDel |
| | @@ -234295,10 +234316,20 @@ |
| 234295 | 234316 | } |
| 234296 | 234317 | sqlite3_free(sql.aBuf); |
| 234297 | 234318 | |
| 234298 | 234319 | return rc; |
| 234299 | 234320 | } |
| 234321 | + |
| 234322 | +/* |
| 234323 | +** Finalize statement pStmt. If (*pRc) is SQLITE_OK when this function is |
| 234324 | +** called, set it to the results of the sqlite3_finalize() call. Or, if |
| 234325 | +** it is already set to an error code, leave it as is. |
| 234326 | +*/ |
| 234327 | +static void sessionFinalizeStmt(sqlite3_stmt *pStmt, int *pRc){ |
| 234328 | + int rc = sqlite3_finalize(pStmt); |
| 234329 | + if( *pRc==SQLITE_OK ) *pRc = rc; |
| 234330 | +} |
| 234300 | 234331 | |
| 234301 | 234332 | /* |
| 234302 | 234333 | ** Table pTab has one or more existing change-records with old.* records |
| 234303 | 234334 | ** with fewer than pTab->nCol columns. This function updates all such |
| 234304 | 234335 | ** change-records with the default values for the missing columns. |
| | @@ -234318,13 +234349,12 @@ |
| 234318 | 234349 | } |
| 234319 | 234350 | } |
| 234320 | 234351 | } |
| 234321 | 234352 | } |
| 234322 | 234353 | |
| 234354 | + sessionFinalizeStmt(pStmt, &rc); |
| 234323 | 234355 | pSession->rc = rc; |
| 234324 | | - rc = sqlite3_finalize(pStmt); |
| 234325 | | - if( pSession->rc==SQLITE_OK ) pSession->rc = rc; |
| 234326 | 234356 | return pSession->rc; |
| 234327 | 234357 | } |
| 234328 | 234358 | |
| 234329 | 234359 | /* |
| 234330 | 234360 | ** Versions of the four methods in object SessionHook for use with the |
| | @@ -235646,15 +235676,15 @@ |
| 235646 | 235676 | sessionAppendStr(&pkvar, |
| 235647 | 235677 | "?1, (CASE WHEN ?2=X'' THEN NULL ELSE ?2 END)", &rc |
| 235648 | 235678 | ); |
| 235649 | 235679 | sessionAppendStr(&cols, "tbl, ?2, stat", &rc); |
| 235650 | 235680 | }else{ |
| 235651 | | - #if 0 |
| 235681 | +#if 0 |
| 235652 | 235682 | if( bRowid ){ |
| 235653 | 235683 | sessionAppendStr(&cols, SESSIONS_ROWID, &rc); |
| 235654 | 235684 | } |
| 235655 | | - #endif |
| 235685 | +#endif |
| 235656 | 235686 | for(i=0; i<nCol; i++){ |
| 235657 | 235687 | if( cols.nBuf ) sessionAppendStr(&cols, ", ", &rc); |
| 235658 | 235688 | sessionAppendIdent(&cols, azCol[i], &rc); |
| 235659 | 235689 | if( abPK[i] ){ |
| 235660 | 235690 | sessionAppendStr(&pkfield, zSep, &rc); |
| | @@ -236908,11 +236938,17 @@ |
| 236908 | 236938 | while( 1 ){ |
| 236909 | 236939 | u8 eType; |
| 236910 | 236940 | |
| 236911 | 236941 | /* Test for EOF. */ |
| 236912 | 236942 | if( (rc = sessionInputBuffer(pInput, 2)) ) goto finished_invert; |
| 236913 | | - if( pInput->iNext>=pInput->nData ) break; |
| 236943 | + if( pInput->iNext+1>=pInput->nData ){ |
| 236944 | + if( pInput->iNext!=pInput->nData ){ |
| 236945 | + rc = SQLITE_CORRUPT_BKPT; |
| 236946 | + goto finished_invert; |
| 236947 | + } |
| 236948 | + break; |
| 236949 | + } |
| 236914 | 236950 | eType = pInput->aData[pInput->iNext]; |
| 236915 | 236951 | |
| 236916 | 236952 | switch( eType ){ |
| 236917 | 236953 | case 'T': { |
| 236918 | 236954 | /* A 'table' record consists of: |
| | @@ -237104,10 +237140,11 @@ |
| 237104 | 237140 | SessionBuffer constraints; /* Deferred constraints are stored here */ |
| 237105 | 237141 | SessionBuffer rebase; /* Rebase information (if any) here */ |
| 237106 | 237142 | u8 bRebaseStarted; /* If table header is already in rebase */ |
| 237107 | 237143 | u8 bRebase; /* True to collect rebase information */ |
| 237108 | 237144 | u8 bIgnoreNoop; /* True to ignore no-op conflicts */ |
| 237145 | + u8 bNoUpdateLoop; /* No update-loop processing */ |
| 237109 | 237146 | int bRowid; |
| 237110 | 237147 | char *zErr; /* Error message, if any */ |
| 237111 | 237148 | }; |
| 237112 | 237149 | |
| 237113 | 237150 | /* Number of prepared UPDATE statements to cache. */ |
| | @@ -237677,11 +237714,11 @@ |
| 237677 | 237714 | /* Instead of invoking the conflict handler, append the change blob |
| 237678 | 237715 | ** to the SessionApplyCtx.constraints buffer. */ |
| 237679 | 237716 | u8 *aBlob = &pIter->in.aData[pIter->in.iCurrent]; |
| 237680 | 237717 | int nBlob = pIter->in.iNext - pIter->in.iCurrent; |
| 237681 | 237718 | sessionAppendBlob(&p->constraints, aBlob, nBlob, &rc); |
| 237682 | | - return SQLITE_OK; |
| 237719 | + return rc; |
| 237683 | 237720 | }else if( p->bIgnoreNoop==0 || op!=SQLITE_DELETE |
| 237684 | 237721 | || eType==SQLITE_CHANGESET_CONFLICT |
| 237685 | 237722 | ){ |
| 237686 | 237723 | /* No other row with the new.* primary key. */ |
| 237687 | 237724 | res = xConflict(pCtx, eType+1, pIter); |
| | @@ -237925,11 +237962,268 @@ |
| 237925 | 237962 | |
| 237926 | 237963 | return rc; |
| 237927 | 237964 | } |
| 237928 | 237965 | |
| 237929 | 237966 | /* |
| 237930 | | -** Retry the changes accumulated in the pApply->constraints buffer. |
| 237967 | +** Create an iterator to iterate through the retry buffer pRetry. |
| 237968 | +*/ |
| 237969 | +static int sessionRetryIterInit( |
| 237970 | + SessionBuffer *pRetry, /* Buffer to iterate through */ |
| 237971 | + int bPatchset, /* True for patchset, false for changeset */ |
| 237972 | + const char *zTab, /* Table name */ |
| 237973 | + SessionApplyCtx *pApply, /* Session apply context */ |
| 237974 | + sqlite3_changeset_iter **ppIter /* OUT: New iterator */ |
| 237975 | +){ |
| 237976 | + sqlite3_changeset_iter *pRet = 0; |
| 237977 | + int rc = SQLITE_OK; |
| 237978 | + |
| 237979 | + rc = sessionChangesetStart( |
| 237980 | + &pRet, 0, 0, pRetry->nBuf, pRetry->aBuf, pApply->bInvertConstraints, 1 |
| 237981 | + ); |
| 237982 | + if( rc==SQLITE_OK ){ |
| 237983 | + size_t nByte = 2*pApply->nCol*sizeof(sqlite3_value*); |
| 237984 | + pRet->bPatchset = bPatchset; |
| 237985 | + pRet->zTab = (char*)zTab; |
| 237986 | + pRet->nCol = pApply->nCol; |
| 237987 | + pRet->abPK = pApply->abPK; |
| 237988 | + sessionBufferGrow(&pRet->tblhdr, nByte, &rc); |
| 237989 | + pRet->apValue = (sqlite3_value**)pRet->tblhdr.aBuf; |
| 237990 | + if( rc==SQLITE_OK ){ |
| 237991 | + memset(pRet->apValue, 0, nByte); |
| 237992 | + }else{ |
| 237993 | + sqlite3changeset_finalize(pRet); |
| 237994 | + pRet = 0; |
| 237995 | + } |
| 237996 | + } |
| 237997 | + |
| 237998 | + *ppIter = pRet; |
| 237999 | + return rc; |
| 238000 | +} |
| 238001 | + |
| 238002 | +/* |
| 238003 | +** Attempt to apply all the changes in retry buffer pRetry to the database. |
| 238004 | +** Except, if parameter iSkip is greater than or equal to 0, skip change |
| 238005 | +** iSkip. |
| 238006 | +*/ |
| 238007 | +static int sessionApplyRetryBuffer( |
| 238008 | + SessionBuffer *pRetry, /* Buffer to apply changes from */ |
| 238009 | + int iSkip, /* If >=0, index of change to omit */ |
| 238010 | + sqlite3 *db, /* Database handle */ |
| 238011 | + int bPatchset, /* True for patchset, false for changeset */ |
| 238012 | + const char *zTab, /* Name of table to write to */ |
| 238013 | + SessionApplyCtx *pApply, /* Apply context */ |
| 238014 | + int(*xConflict)(void*, int, sqlite3_changeset_iter*), |
| 238015 | + void *pCtx /* First argument passed to xConflict */ |
| 238016 | +){ |
| 238017 | + int rc = SQLITE_OK; |
| 238018 | + int rc2 = SQLITE_OK; |
| 238019 | + int ii = 0; |
| 238020 | + sqlite3_changeset_iter *pIter = 0; |
| 238021 | + |
| 238022 | + assert( pApply->constraints.nBuf==0 ); |
| 238023 | + |
| 238024 | + rc = sessionRetryIterInit(pRetry, bPatchset, zTab, pApply, &pIter); |
| 238025 | + |
| 238026 | + for(ii=0; rc==SQLITE_OK && SQLITE_ROW==sqlite3changeset_next(pIter); ii++){ |
| 238027 | + if( ii!=iSkip ){ |
| 238028 | + rc = sessionApplyOneWithRetry(db, pIter, pApply, xConflict, pCtx); |
| 238029 | + } |
| 238030 | + } |
| 238031 | + |
| 238032 | + rc2 = sqlite3changeset_finalize(pIter); |
| 238033 | + if( rc==SQLITE_OK ) rc = rc2; |
| 238034 | + assert( pApply->bDeferConstraints || pApply->constraints.nBuf==0 ); |
| 238035 | + |
| 238036 | + return rc; |
| 238037 | +} |
| 238038 | + |
| 238039 | +/* |
| 238040 | +** Check if table zTab in the "main" database of db is a WITHOUT ROWID |
| 238041 | +** table. |
| 238042 | +** |
| 238043 | +** If no error occurs, return SQLITE_OK and set output variable (*pbWR) to |
| 238044 | +** true if zTab is a WITHOUT ROWID table, or false otherwise. Or, if an |
| 238045 | +** error does occur, return an SQLite error code. The final value of (*pbWR) |
| 238046 | +** is undefined in this case. |
| 238047 | +*/ |
| 238048 | +static int sessionTableIsWithoutRowid(sqlite3 *db, const char *zTab, int *pbWR){ |
| 238049 | + sqlite3_stmt *pList = 0; |
| 238050 | + char *zSql = 0; |
| 238051 | + int rc = SQLITE_OK; |
| 238052 | + |
| 238053 | + zSql = sqlite3_mprintf("PRAGMA table_list = %Q", zTab); |
| 238054 | + if( zSql==0 ){ |
| 238055 | + rc = SQLITE_NOMEM; |
| 238056 | + }else{ |
| 238057 | + rc = sqlite3_prepare_v2(db, zSql, -1, &pList, 0); |
| 238058 | + sqlite3_free(zSql); |
| 238059 | + } |
| 238060 | + |
| 238061 | + if( rc==SQLITE_OK ){ |
| 238062 | + sqlite3_step(pList); |
| 238063 | + *pbWR = sqlite3_column_int(pList, 4); |
| 238064 | + rc = sqlite3_finalize(pList); |
| 238065 | + } |
| 238066 | + |
| 238067 | + return rc; |
| 238068 | +} |
| 238069 | + |
| 238070 | +/* |
| 238071 | +** Iterator pUp points to an UPDATE change. This function deletes the |
| 238072 | +** affected row from the database and creates an INSERT statement that |
| 238073 | +** may be used to reinsert the row as it is after the UPDATE change |
| 238074 | +** has been applied. |
| 238075 | +** |
| 238076 | +** If successful, SQLITE_OK is returned and output variable (*ppInsert) |
| 238077 | +** is left pointing to a prepared INSERT statement. It is the responsibility |
| 238078 | +** of the caller to eventually free this statement using sqlite3_finalize(). |
| 238079 | +** Or, if an error occurs, an SQLite error code is returned and (*ppInsert) |
| 238080 | +** set to NULL. pApply->zErr may be set to an error message in this case. |
| 238081 | +*/ |
| 238082 | +static int sessionUpdateToDeleteInsert( |
| 238083 | + sqlite3 *db, /* Database to write to */ |
| 238084 | + const char *zTab, /* Table name */ |
| 238085 | + SessionApplyCtx *pApply, /* Apply context */ |
| 238086 | + sqlite3_changeset_iter *pUp, /* Iterator pointing to UPDATE change */ |
| 238087 | + sqlite3_stmt **ppInsert /* OUT: INSERT statement */ |
| 238088 | +){ |
| 238089 | + sqlite3_stmt *pRet = 0; /* The INSERT statement */ |
| 238090 | + sqlite3_stmt *pSelect = 0; /* SELECT to read current values of row */ |
| 238091 | + int rc = SQLITE_OK; |
| 238092 | + int bWR = 0; |
| 238093 | + |
| 238094 | + rc = sessionTableIsWithoutRowid(db, zTab, &bWR); |
| 238095 | + if( rc==SQLITE_OK ){ |
| 238096 | + char *zSelect = 0; |
| 238097 | + char *zInsert = 0; |
| 238098 | + SessionBuffer cols = {0, 0, 0}; |
| 238099 | + SessionBuffer insbind = {0, 0, 0}; |
| 238100 | + SessionBuffer pkcols = {0, 0, 0}; |
| 238101 | + SessionBuffer selbind = {0, 0, 0}; |
| 238102 | + |
| 238103 | + const char *zComma = ""; |
| 238104 | + const char *zComma2 = ""; |
| 238105 | + int ii; |
| 238106 | + for(ii=0; ii<pApply->nCol; ii++){ |
| 238107 | + sessionAppendStr(&cols, zComma, &rc); |
| 238108 | + sessionAppendIdent(&cols, pApply->azCol[ii], &rc); |
| 238109 | + sessionAppendStr(&insbind, zComma, &rc); |
| 238110 | + sessionAppendStr(&insbind, "?", &rc); |
| 238111 | + zComma = ", "; |
| 238112 | + |
| 238113 | + if( pApply->abPK[ii] ){ |
| 238114 | + sessionAppendStr(&pkcols, zComma2, &rc); |
| 238115 | + sessionAppendIdent(&pkcols, pApply->azCol[ii], &rc); |
| 238116 | + sessionAppendStr(&selbind, zComma2, &rc); |
| 238117 | + sessionAppendPrintf(&selbind, &rc, "?%d", ii+1); |
| 238118 | + zComma2 = ", "; |
| 238119 | + } |
| 238120 | + } |
| 238121 | + if( bWR==0 ){ |
| 238122 | + sessionAppendStr(&cols, zComma, &rc); |
| 238123 | + sessionAppendStr(&cols, SESSIONS_ROWID, &rc); |
| 238124 | + sessionAppendStr(&insbind, zComma, &rc); |
| 238125 | + sessionAppendStr(&insbind, "?", &rc); |
| 238126 | + } |
| 238127 | + |
| 238128 | + if( rc==SQLITE_OK ){ |
| 238129 | + zSelect = sqlite3_mprintf("SELECT %s FROM %Q WHERE (%s) IS (%s)", |
| 238130 | + cols.aBuf, zTab, pkcols.aBuf, selbind.aBuf |
| 238131 | + ); |
| 238132 | + if( zSelect==0 ) rc = SQLITE_NOMEM; |
| 238133 | + } |
| 238134 | + if( rc==SQLITE_OK ){ |
| 238135 | + zInsert = sqlite3_mprintf("INSERT INTO %Q(%s) VALUES(%s)", |
| 238136 | + zTab, cols.aBuf, insbind.aBuf |
| 238137 | + ); |
| 238138 | + if( zInsert==0 ) rc = SQLITE_NOMEM; |
| 238139 | + } |
| 238140 | + |
| 238141 | + if( rc==SQLITE_OK ){ |
| 238142 | + rc = sessionPrepare(db, &pSelect, &pApply->zErr, zSelect); |
| 238143 | + } |
| 238144 | + if( rc==SQLITE_OK ){ |
| 238145 | + rc = sessionPrepare(db, &pRet, &pApply->zErr, zInsert); |
| 238146 | + } |
| 238147 | + |
| 238148 | + sqlite3_free(zSelect); |
| 238149 | + sqlite3_free(zInsert); |
| 238150 | + sqlite3_free(cols.aBuf); |
| 238151 | + sqlite3_free(insbind.aBuf); |
| 238152 | + sqlite3_free(pkcols.aBuf); |
| 238153 | + sqlite3_free(selbind.aBuf); |
| 238154 | + } |
| 238155 | + |
| 238156 | + if( rc==SQLITE_OK ){ |
| 238157 | + rc = sessionBindRow( |
| 238158 | + pUp, sqlite3changeset_old, pApply->nCol, pApply->abPK, pSelect |
| 238159 | + ); |
| 238160 | + } |
| 238161 | + |
| 238162 | + if( rc==SQLITE_OK && sqlite3_step(pSelect)==SQLITE_ROW ){ |
| 238163 | + int iCol; |
| 238164 | + for(iCol=0; iCol<pApply->nCol; iCol++){ |
| 238165 | + sqlite3_value *pVal = pUp->apValue[iCol+pApply->nCol]; |
| 238166 | + if( pVal==0 ){ |
| 238167 | + pVal = sqlite3_column_value(pSelect, iCol); |
| 238168 | + } |
| 238169 | + rc = sqlite3_bind_value(pRet, iCol+1, pVal); |
| 238170 | + } |
| 238171 | + if( bWR==0 ){ |
| 238172 | + sqlite3_bind_int64(pRet, iCol+1, sqlite3_column_int64(pSelect, iCol)); |
| 238173 | + } |
| 238174 | + } |
| 238175 | + sessionFinalizeStmt(pSelect, &rc); |
| 238176 | + |
| 238177 | + /* Delete the row from the database. */ |
| 238178 | + if( rc==SQLITE_OK ){ |
| 238179 | + rc = sessionBindRow( |
| 238180 | + pUp, sqlite3changeset_old, pApply->nCol, pApply->abPK, pApply->pDelete |
| 238181 | + ); |
| 238182 | + sqlite3_bind_int(pApply->pDelete, pApply->nCol+1, 1); |
| 238183 | + } |
| 238184 | + if( rc==SQLITE_OK ){ |
| 238185 | + sqlite3_step(pApply->pDelete); |
| 238186 | + rc = sqlite3_reset(pApply->pDelete); |
| 238187 | + } |
| 238188 | + |
| 238189 | + if( rc!=SQLITE_OK ){ |
| 238190 | + sqlite3_finalize(pRet); |
| 238191 | + pRet = 0; |
| 238192 | + } |
| 238193 | + |
| 238194 | + *ppInsert = pRet; |
| 238195 | + return rc; |
| 238196 | +} |
| 238197 | + |
| 238198 | +/* |
| 238199 | +** Retry the changes accumulated in the pApply->constraints buffer. The |
| 238200 | +** pApply->constraints buffer contains all changes to table zTab that |
| 238201 | +** could not be applied due to SQLITE_CONSTRAINT errors. This function |
| 238202 | +** attempts to apply them as follows: |
| 238203 | +** |
| 238204 | +** 1) It runs through the buffer and attempts to retry each change, |
| 238205 | +** removing any that are successfully applied from the buffer. This |
| 238206 | +** is repeated until no further progress can be made. |
| 238207 | +** |
| 238208 | +** 2) For each UPDATE change in the buffer, try the following in a |
| 238209 | +** savepoint transaction: |
| 238210 | +** |
| 238211 | +** a) DELETE the affected row, |
| 238212 | +** b) Attempt step (1) with remaining changes, |
| 238213 | +** c) Attempt to INSERT a row equivalent to the one that would be |
| 238214 | +** created by applying this UPDATE change. |
| 238215 | +** |
| 238216 | +** If the INSERT in (c) succeeds, the savepoint is committed and all |
| 238217 | +** successfully applied changes are removed from the buffer. Step (2) |
| 238218 | +** is then repeated. |
| 238219 | +** |
| 238220 | +** 3) Once step (2) has been attempted for each UPDATE in the change, |
| 238221 | +** a final attempt is made to apply each remaining change. This time, |
| 238222 | +** if an SQLITE_CONSTRAINT error is encountered, the conflict handler |
| 238223 | +** is invoked and the user has to decide whether to omit the change |
| 238224 | +** or rollback the entire _apply() operation. |
| 237931 | 238225 | */ |
| 237932 | 238226 | static int sessionRetryConstraints( |
| 237933 | 238227 | sqlite3 *db, |
| 237934 | 238228 | int bPatchset, |
| 237935 | 238229 | const char *zTab, |
| | @@ -237936,45 +238230,105 @@ |
| 237936 | 238230 | SessionApplyCtx *pApply, |
| 237937 | 238231 | int(*xConflict)(void*, int, sqlite3_changeset_iter*), |
| 237938 | 238232 | void *pCtx /* First argument passed to xConflict */ |
| 237939 | 238233 | ){ |
| 237940 | 238234 | int rc = SQLITE_OK; |
| 238235 | + int iUpdate = 0; |
| 237941 | 238236 | |
| 238237 | + /* Step (1) */ |
| 237942 | 238238 | while( pApply->constraints.nBuf ){ |
| 237943 | | - sqlite3_changeset_iter *pIter2 = 0; |
| 237944 | 238239 | SessionBuffer cons = pApply->constraints; |
| 237945 | 238240 | memset(&pApply->constraints, 0, sizeof(SessionBuffer)); |
| 237946 | 238241 | |
| 237947 | | - rc = sessionChangesetStart( |
| 237948 | | - &pIter2, 0, 0, cons.nBuf, cons.aBuf, pApply->bInvertConstraints, 1 |
| 238242 | + rc = sessionApplyRetryBuffer( |
| 238243 | + &cons, -1, db, bPatchset, zTab, pApply, xConflict, pCtx |
| 237949 | 238244 | ); |
| 237950 | | - if( rc==SQLITE_OK ){ |
| 237951 | | - size_t nByte = 2*pApply->nCol*sizeof(sqlite3_value*); |
| 237952 | | - int rc2; |
| 237953 | | - pIter2->bPatchset = bPatchset; |
| 237954 | | - pIter2->zTab = (char*)zTab; |
| 237955 | | - pIter2->nCol = pApply->nCol; |
| 237956 | | - pIter2->abPK = pApply->abPK; |
| 237957 | | - sessionBufferGrow(&pIter2->tblhdr, nByte, &rc); |
| 237958 | | - pIter2->apValue = (sqlite3_value**)pIter2->tblhdr.aBuf; |
| 237959 | | - if( rc==SQLITE_OK ) memset(pIter2->apValue, 0, nByte); |
| 237960 | | - |
| 237961 | | - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3changeset_next(pIter2) ){ |
| 237962 | | - rc = sessionApplyOneWithRetry(db, pIter2, pApply, xConflict, pCtx); |
| 237963 | | - } |
| 237964 | | - |
| 237965 | | - rc2 = sqlite3changeset_finalize(pIter2); |
| 237966 | | - if( rc==SQLITE_OK ) rc = rc2; |
| 237967 | | - } |
| 237968 | | - assert( pApply->bDeferConstraints || pApply->constraints.nBuf==0 ); |
| 237969 | 238245 | |
| 237970 | 238246 | sqlite3_free(cons.aBuf); |
| 237971 | 238247 | if( rc!=SQLITE_OK ) break; |
| 237972 | | - if( pApply->constraints.nBuf>=cons.nBuf ){ |
| 237973 | | - /* No progress was made on the last round. */ |
| 237974 | | - pApply->bDeferConstraints = 0; |
| 238248 | + |
| 238249 | + /* If no progress has been made this round, break out of the loop. */ |
| 238250 | + if( pApply->constraints.nBuf>=cons.nBuf ) break; |
| 238251 | + } |
| 238252 | + |
| 238253 | + /* Step (2) */ |
| 238254 | + while( rc==SQLITE_OK && pApply->constraints.nBuf && !pApply->bNoUpdateLoop ){ |
| 238255 | + SessionBuffer cons = {0, 0, 0}; |
| 238256 | + sqlite3_changeset_iter *pUp = 0; |
| 238257 | + sqlite3_stmt *pInsert = 0; |
| 238258 | + int iSkip = 0; |
| 238259 | + |
| 238260 | + rc = sessionRetryIterInit( |
| 238261 | + &pApply->constraints, bPatchset, zTab, pApply, &pUp |
| 238262 | + ); |
| 238263 | + if( rc==SQLITE_OK ){ |
| 238264 | + int iThis = -1; |
| 238265 | + while( SQLITE_ROW==sqlite3changeset_next(pUp) ){ |
| 238266 | + if( pUp->op==SQLITE_UPDATE ) iThis++; |
| 238267 | + if( iThis==iUpdate ) break; |
| 238268 | + iSkip++; |
| 238269 | + } |
| 238270 | + if( iThis==iUpdate ){ |
| 238271 | + rc = sqlite3_exec(db, "SAVEPOINT update_op", 0, 0, 0); |
| 238272 | + if( rc==SQLITE_OK ){ |
| 238273 | + rc = sessionUpdateToDeleteInsert(db, zTab, pApply, pUp, &pInsert); |
| 238274 | + } |
| 238275 | + } |
| 238276 | + sqlite3changeset_finalize(pUp); |
| 238277 | + if( iThis!=iUpdate ) break; |
| 237975 | 238278 | } |
| 238279 | + |
| 238280 | + if( rc==SQLITE_OK ){ |
| 238281 | + cons = pApply->constraints; |
| 238282 | + |
| 238283 | + while( rc==SQLITE_OK && pApply->constraints.nBuf>0 ){ |
| 238284 | + SessionBuffer app = pApply->constraints; |
| 238285 | + memset(&pApply->constraints, 0, sizeof(SessionBuffer)); |
| 238286 | + rc = sessionApplyRetryBuffer( |
| 238287 | + &app, iSkip, db, bPatchset, zTab, pApply, xConflict, pCtx |
| 238288 | + ); |
| 238289 | + if( app.aBuf!=cons.aBuf ){ |
| 238290 | + sqlite3_free(app.aBuf); |
| 238291 | + } |
| 238292 | + if( pApply->constraints.nBuf>=app.nBuf ){ |
| 238293 | + break; |
| 238294 | + } |
| 238295 | + iSkip = -1; |
| 238296 | + } |
| 238297 | + } |
| 238298 | + |
| 238299 | + iUpdate++; |
| 238300 | + if( rc==SQLITE_OK ){ |
| 238301 | + sqlite3_step(pInsert); |
| 238302 | + rc = sqlite3_finalize(pInsert); |
| 238303 | + if( rc==SQLITE_CONSTRAINT ){ |
| 238304 | + rc = sqlite3_exec(db, "ROLLBACK TO update_op", 0, 0, 0); |
| 238305 | + sqlite3_free(pApply->constraints.aBuf); |
| 238306 | + pApply->constraints = cons; |
| 238307 | + memset(&cons, 0, sizeof(cons)); |
| 238308 | + }else if( rc==SQLITE_OK ){ |
| 238309 | + iUpdate = 0; |
| 238310 | + } |
| 238311 | + if( rc==SQLITE_OK ){ |
| 238312 | + rc = sqlite3_exec(db, "RELEASE update_op", 0, 0, 0); |
| 238313 | + } |
| 238314 | + }else{ |
| 238315 | + sqlite3_finalize(pInsert); |
| 238316 | + } |
| 238317 | + |
| 238318 | + sqlite3_free(cons.aBuf); |
| 238319 | + } |
| 238320 | + |
| 238321 | + /* Step (3) */ |
| 238322 | + if( rc==SQLITE_OK && pApply->constraints.nBuf ){ |
| 238323 | + SessionBuffer cons = pApply->constraints; |
| 238324 | + memset(&pApply->constraints, 0, sizeof(SessionBuffer)); |
| 238325 | + pApply->bDeferConstraints = 0; |
| 238326 | + rc = sessionApplyRetryBuffer( |
| 238327 | + &cons, -1, db, bPatchset, zTab, pApply, xConflict, pCtx |
| 238328 | + ); |
| 238329 | + sqlite3_free(cons.aBuf); |
| 237976 | 238330 | } |
| 237977 | 238331 | |
| 237978 | 238332 | return rc; |
| 237979 | 238333 | } |
| 237980 | 238334 | |
| | @@ -238024,10 +238378,11 @@ |
| 238024 | 238378 | pIter->in.bNoDiscard = 1; |
| 238025 | 238379 | memset(&sApply, 0, sizeof(sApply)); |
| 238026 | 238380 | sApply.bRebase = (ppRebase && pnRebase); |
| 238027 | 238381 | sApply.bInvertConstraints = !!(flags & SQLITE_CHANGESETAPPLY_INVERT); |
| 238028 | 238382 | sApply.bIgnoreNoop = !!(flags & SQLITE_CHANGESETAPPLY_IGNORENOOP); |
| 238383 | + sApply.bNoUpdateLoop = !!(flags & SQLITE_CHANGESETAPPLY_NOUPDATELOOP); |
| 238029 | 238384 | if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){ |
| 238030 | 238385 | rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0); |
| 238031 | 238386 | } |
| 238032 | 238387 | if( rc==SQLITE_OK ){ |
| 238033 | 238388 | rc = sqlite3_exec(db, "PRAGMA defer_foreign_keys = 1", 0, 0, 0); |
| | @@ -239835,11 +240190,11 @@ |
| 239835 | 240190 | int bNew, |
| 239836 | 240191 | int iCol, |
| 239837 | 240192 | const void *pVal, |
| 239838 | 240193 | int nVal |
| 239839 | 240194 | ){ |
| 239840 | | - sqlite3_int64 nByte = 1 + sessionVarintLen(nVal) + nVal; |
| 240195 | + sqlite3_int64 nByte = 1 + sessionVarintLen(nVal) + (i64)nVal; |
| 239841 | 240196 | int rc = SQLITE_OK; |
| 239842 | 240197 | SessionBuffer *pBuf = 0; |
| 239843 | 240198 | |
| 239844 | 240199 | if( SQLITE_OK!=(rc = checkChangeParams(pGrp, bNew, iCol, nByte, &pBuf)) ){ |
| 239845 | 240200 | return rc; |
| | @@ -253306,12 +253661,15 @@ |
| 253306 | 253661 | */ |
| 253307 | 253662 | static void fts5IterSetOutputs_Col100(Fts5Iter *pIter, Fts5SegIter *pSeg){ |
| 253308 | 253663 | |
| 253309 | 253664 | assert( pIter->pIndex->pConfig->eDetail==FTS5_DETAIL_COLUMNS ); |
| 253310 | 253665 | assert( pIter->pColset ); |
| 253666 | + assert( pIter->poslist.nSpace>=pIter->pIndex->pConfig->nCol ); |
| 253311 | 253667 | |
| 253312 | | - if( pSeg->iLeafOffset+pSeg->nPos>pSeg->pLeaf->szLeaf ){ |
| 253668 | + if( pSeg->iLeafOffset+pSeg->nPos>pSeg->pLeaf->szLeaf |
| 253669 | + || pSeg->nPos>pIter->pIndex->pConfig->nCol |
| 253670 | + ){ |
| 253313 | 253671 | fts5IterSetOutputs_Col(pIter, pSeg); |
| 253314 | 253672 | }else{ |
| 253315 | 253673 | u8 *a = (u8*)&pSeg->pLeaf->p[pSeg->iLeafOffset]; |
| 253316 | 253674 | u8 *pEnd = (u8*)&a[pSeg->nPos]; |
| 253317 | 253675 | int iPrev = 0; |
| | @@ -262677,11 +263035,11 @@ |
| 262677 | 263035 | int nArg, /* Number of args */ |
| 262678 | 263036 | sqlite3_value **apUnused /* Function arguments */ |
| 262679 | 263037 | ){ |
| 262680 | 263038 | assert( nArg==0 ); |
| 262681 | 263039 | UNUSED_PARAM2(nArg, apUnused); |
| 262682 | | - sqlite3_result_text(pCtx, "fts5: 2026-05-21 15:14:35 9ac4a33a2932d353c4871fd8e09c10addf827f1fc3fc9380037d738cf2cd0353", -1, SQLITE_TRANSIENT); |
| 263040 | + sqlite3_result_text(pCtx, "fts5: 2026-05-28 11:29:05 87c37dab7e53d1bd891f3fed624963b35ab15a785706d0964b5d07ab70421c10", -1, SQLITE_TRANSIENT); |
| 262683 | 263041 | } |
| 262684 | 263042 | |
| 262685 | 263043 | /* |
| 262686 | 263044 | ** Implementation of fts5_locale(LOCALE, TEXT) function. |
| 262687 | 263045 | ** |
| 262688 | 263046 | |