| | @@ -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 | | -** 8c0f69e0e4ae0a446838cc193bfd4395fd25. |
| 21 | +** 42d67c6fed3a5f21d7b71515aca471ba61d3. |
| 22 | 22 | */ |
| 23 | 23 | #define SQLITE_CORE 1 |
| 24 | 24 | #define SQLITE_AMALGAMATION 1 |
| 25 | 25 | #ifndef SQLITE_PRIVATE |
| 26 | 26 | # define SQLITE_PRIVATE static |
| | @@ -459,11 +459,11 @@ |
| 459 | 459 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 460 | 460 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 461 | 461 | */ |
| 462 | 462 | #define SQLITE_VERSION "3.46.0" |
| 463 | 463 | #define SQLITE_VERSION_NUMBER 3046000 |
| 464 | | -#define SQLITE_SOURCE_ID "2024-04-18 16:11:01 8c0f69e0e4ae0a446838cc193bfd4395fd251f3c7659b35ac388e5a0a7650a66" |
| 464 | +#define SQLITE_SOURCE_ID "2024-05-08 11:51:56 42d67c6fed3a5f21d7b71515aca471ba61d387e620022735a2e7929fa3a237cf" |
| 465 | 465 | |
| 466 | 466 | /* |
| 467 | 467 | ** CAPI3REF: Run-Time Library Version Numbers |
| 468 | 468 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 469 | 469 | ** |
| | @@ -12314,10 +12314,34 @@ |
| 12314 | 12314 | ** |
| 12315 | 12315 | ** In all cases, if an error occurs the state of the final contents of the |
| 12316 | 12316 | ** changegroup is undefined. If no error occurs, SQLITE_OK is returned. |
| 12317 | 12317 | */ |
| 12318 | 12318 | SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData); |
| 12319 | + |
| 12320 | +/* |
| 12321 | +** CAPI3REF: Add A Single Change To A Changegroup |
| 12322 | +** METHOD: sqlite3_changegroup |
| 12323 | +** |
| 12324 | +** This function adds the single change currently indicated by the iterator |
| 12325 | +** passed as the second argument to the changegroup object. The rules for |
| 12326 | +** adding the change are just as described for [sqlite3changegroup_add()]. |
| 12327 | +** |
| 12328 | +** If the change is successfully added to the changegroup, SQLITE_OK is |
| 12329 | +** returned. Otherwise, an SQLite error code is returned. |
| 12330 | +** |
| 12331 | +** The iterator must point to a valid entry when this function is called. |
| 12332 | +** If it does not, SQLITE_ERROR is returned and no change is added to the |
| 12333 | +** changegroup. Additionally, the iterator must not have been opened with |
| 12334 | +** the SQLITE_CHANGESETAPPLY_INVERT flag. In this case SQLITE_ERROR is also |
| 12335 | +** returned. |
| 12336 | +*/ |
| 12337 | +SQLITE_API int sqlite3changegroup_add_change( |
| 12338 | + sqlite3_changegroup*, |
| 12339 | + sqlite3_changeset_iter* |
| 12340 | +); |
| 12341 | + |
| 12342 | + |
| 12319 | 12343 | |
| 12320 | 12344 | /* |
| 12321 | 12345 | ** CAPI3REF: Obtain A Composite Changeset From A Changegroup |
| 12322 | 12346 | ** METHOD: sqlite3_changegroup |
| 12323 | 12347 | ** |
| | @@ -14612,12 +14636,12 @@ |
| 14612 | 14636 | #define TK_AGG_FUNCTION 168 |
| 14613 | 14637 | #define TK_AGG_COLUMN 169 |
| 14614 | 14638 | #define TK_TRUEFALSE 170 |
| 14615 | 14639 | #define TK_ISNOT 171 |
| 14616 | 14640 | #define TK_FUNCTION 172 |
| 14617 | | -#define TK_UMINUS 173 |
| 14618 | | -#define TK_UPLUS 174 |
| 14641 | +#define TK_UPLUS 173 |
| 14642 | +#define TK_UMINUS 174 |
| 14619 | 14643 | #define TK_TRUTH 175 |
| 14620 | 14644 | #define TK_REGISTER 176 |
| 14621 | 14645 | #define TK_VECTOR 177 |
| 14622 | 14646 | #define TK_SELECT_COLUMN 178 |
| 14623 | 14647 | #define TK_IF_NULL_ROW 179 |
| | @@ -31945,11 +31969,11 @@ |
| 31945 | 31969 | sqlite3_str_appendall(pAccum, pItem->zName); |
| 31946 | 31970 | }else if( pItem->zAlias ){ |
| 31947 | 31971 | sqlite3_str_appendall(pAccum, pItem->zAlias); |
| 31948 | 31972 | }else{ |
| 31949 | 31973 | Select *pSel = pItem->pSelect; |
| 31950 | | - assert( pSel!=0 ); |
| 31974 | + assert( pSel!=0 ); /* Because of tag-20240424-1 */ |
| 31951 | 31975 | if( pSel->selFlags & SF_NestedFrom ){ |
| 31952 | 31976 | sqlite3_str_appendf(pAccum, "(join-%u)", pSel->selId); |
| 31953 | 31977 | }else if( pSel->selFlags & SF_MultiValue ){ |
| 31954 | 31978 | assert( !pItem->fg.isTabFunc && !pItem->fg.isIndexedBy ); |
| 31955 | 31979 | sqlite3_str_appendf(pAccum, "%u-ROW VALUES CLAUSE", |
| | @@ -32769,16 +32793,18 @@ |
| 32769 | 32793 | if( pItem->fg.isUsing ) n++; |
| 32770 | 32794 | if( pItem->fg.isUsing ){ |
| 32771 | 32795 | sqlite3TreeViewIdList(pView, pItem->u3.pUsing, (--n)>0, "USING"); |
| 32772 | 32796 | } |
| 32773 | 32797 | if( pItem->pSelect ){ |
| 32798 | + sqlite3TreeViewPush(&pView, i+1<pSrc->nSrc); |
| 32774 | 32799 | if( pItem->pTab ){ |
| 32775 | 32800 | Table *pTab = pItem->pTab; |
| 32776 | 32801 | sqlite3TreeViewColumnList(pView, pTab->aCol, pTab->nCol, 1); |
| 32777 | 32802 | } |
| 32778 | 32803 | assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem->pSelect) ); |
| 32779 | 32804 | sqlite3TreeViewSelect(pView, pItem->pSelect, (--n)>0); |
| 32805 | + sqlite3TreeViewPop(&pView); |
| 32780 | 32806 | } |
| 32781 | 32807 | if( pItem->fg.isTabFunc ){ |
| 32782 | 32808 | sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:"); |
| 32783 | 32809 | } |
| 32784 | 32810 | sqlite3TreeViewPop(&pView); |
| | @@ -32878,11 +32904,11 @@ |
| 32878 | 32904 | } |
| 32879 | 32905 | if( p->pLimit ){ |
| 32880 | 32906 | sqlite3TreeViewItem(pView, "LIMIT", (n--)>0); |
| 32881 | 32907 | sqlite3TreeViewExpr(pView, p->pLimit->pLeft, p->pLimit->pRight!=0); |
| 32882 | 32908 | if( p->pLimit->pRight ){ |
| 32883 | | - sqlite3TreeViewItem(pView, "OFFSET", (n--)>0); |
| 32909 | + sqlite3TreeViewItem(pView, "OFFSET", 0); |
| 32884 | 32910 | sqlite3TreeViewExpr(pView, p->pLimit->pRight, 0); |
| 32885 | 32911 | sqlite3TreeViewPop(&pView); |
| 32886 | 32912 | } |
| 32887 | 32913 | sqlite3TreeViewPop(&pView); |
| 32888 | 32914 | } |
| | @@ -84677,14 +84703,14 @@ |
| 84677 | 84703 | int nRec, /* Size of buffer pRec in bytes */ |
| 84678 | 84704 | int iCol, /* Column to extract */ |
| 84679 | 84705 | sqlite3_value **ppVal /* OUT: Extracted value */ |
| 84680 | 84706 | ){ |
| 84681 | 84707 | u32 t = 0; /* a column type code */ |
| 84682 | | - int nHdr; /* Size of the header in the record */ |
| 84683 | | - int iHdr; /* Next unread header byte */ |
| 84684 | | - int iField; /* Next unread data byte */ |
| 84685 | | - int szField = 0; /* Size of the current data field */ |
| 84708 | + u32 nHdr; /* Size of the header in the record */ |
| 84709 | + u32 iHdr; /* Next unread header byte */ |
| 84710 | + i64 iField; /* Next unread data byte */ |
| 84711 | + u32 szField = 0; /* Size of the current data field */ |
| 84686 | 84712 | int i; /* Column index */ |
| 84687 | 84713 | u8 *a = (u8*)pRec; /* Typecast byte array */ |
| 84688 | 84714 | Mem *pMem = *ppVal; /* Write result into this Mem object */ |
| 84689 | 84715 | |
| 84690 | 84716 | assert( iCol>0 ); |
| | @@ -97641,11 +97667,12 @@ |
| 97641 | 97667 | ** row output from the sorter so that the row can be decomposed into |
| 97642 | 97668 | ** individual columns using the OP_Column opcode. The OP_Column opcode |
| 97643 | 97669 | ** is the only cursor opcode that works with a pseudo-table. |
| 97644 | 97670 | ** |
| 97645 | 97671 | ** P3 is the number of fields in the records that will be stored by |
| 97646 | | -** the pseudo-table. |
| 97672 | +** the pseudo-table. If P2 is 0 or negative then the pseudo-cursor |
| 97673 | +** will return NULL for every column. |
| 97647 | 97674 | */ |
| 97648 | 97675 | case OP_OpenPseudo: { |
| 97649 | 97676 | VdbeCursor *pCx; |
| 97650 | 97677 | |
| 97651 | 97678 | assert( pOp->p1>=0 ); |
| | @@ -105799,14 +105826,14 @@ |
| 105799 | 105826 | break; |
| 105800 | 105827 | } |
| 105801 | 105828 | |
| 105802 | 105829 | #ifdef SQLITE_ENABLE_STMT_SCANSTATUS |
| 105803 | 105830 | case 9: /* nexec */ |
| 105804 | | - sqlite3_result_int(ctx, pOp->nExec); |
| 105831 | + sqlite3_result_int64(ctx, pOp->nExec); |
| 105805 | 105832 | break; |
| 105806 | 105833 | case 10: /* ncycle */ |
| 105807 | | - sqlite3_result_int(ctx, pOp->nCycle); |
| 105834 | + sqlite3_result_int64(ctx, pOp->nCycle); |
| 105808 | 105835 | break; |
| 105809 | 105836 | #else |
| 105810 | 105837 | case 9: /* nexec */ |
| 105811 | 105838 | case 10: /* ncycle */ |
| 105812 | 105839 | sqlite3_result_int(ctx, 0); |
| | @@ -107195,11 +107222,12 @@ |
| 107195 | 107222 | int op = pParse->eTriggerOp; |
| 107196 | 107223 | assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT ); |
| 107197 | 107224 | if( pParse->bReturning ){ |
| 107198 | 107225 | if( (pNC->ncFlags & NC_UBaseReg)!=0 |
| 107199 | 107226 | && ALWAYS(zTab==0 |
| 107200 | | - || sqlite3StrICmp(zTab,pParse->pTriggerTab->zName)==0) |
| 107227 | + || sqlite3StrICmp(zTab,pParse->pTriggerTab->zName)==0 |
| 107228 | + || isValidSchemaTableName(zTab, pParse->pTriggerTab, 0)) |
| 107201 | 107229 | ){ |
| 107202 | 107230 | pExpr->iTable = op!=TK_DELETE; |
| 107203 | 107231 | pTab = pParse->pTriggerTab; |
| 107204 | 107232 | } |
| 107205 | 107233 | }else if( op!=TK_DELETE && zTab && sqlite3StrICmp("new",zTab) == 0 ){ |
| | @@ -108556,10 +108584,11 @@ |
| 108556 | 108584 | /* Recursively resolve names in all subqueries in the FROM clause |
| 108557 | 108585 | */ |
| 108558 | 108586 | if( pOuterNC ) pOuterNC->nNestedSelect++; |
| 108559 | 108587 | for(i=0; i<p->pSrc->nSrc; i++){ |
| 108560 | 108588 | SrcItem *pItem = &p->pSrc->a[i]; |
| 108589 | + assert( pItem->zName!=0 || pItem->pSelect!=0 );/* Test of tag-20240424-1*/ |
| 108561 | 108590 | if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){ |
| 108562 | 108591 | int nRef = pOuterNC ? pOuterNC->nRef : 0; |
| 108563 | 108592 | const char *zSavedContext = pParse->zAuthContext; |
| 108564 | 108593 | |
| 108565 | 108594 | if( pItem->zName ) pParse->zAuthContext = pItem->zName; |
| | @@ -110315,10 +110344,11 @@ |
| 110315 | 110344 | ** Recursively delete an expression tree. |
| 110316 | 110345 | */ |
| 110317 | 110346 | static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){ |
| 110318 | 110347 | assert( p!=0 ); |
| 110319 | 110348 | assert( db!=0 ); |
| 110349 | +exprDeleteRestart: |
| 110320 | 110350 | assert( !ExprUseUValue(p) || p->u.iValue>=0 ); |
| 110321 | 110351 | assert( !ExprUseYWin(p) || !ExprUseYSub(p) ); |
| 110322 | 110352 | assert( !ExprUseYWin(p) || p->y.pWin!=0 || db->mallocFailed ); |
| 110323 | 110353 | assert( p->op!=TK_FUNCTION || !ExprUseYSub(p) ); |
| 110324 | 110354 | #ifdef SQLITE_DEBUG |
| | @@ -110330,11 +110360,10 @@ |
| 110330 | 110360 | } |
| 110331 | 110361 | #endif |
| 110332 | 110362 | if( !ExprHasProperty(p, (EP_TokenOnly|EP_Leaf)) ){ |
| 110333 | 110363 | /* The Expr.x union is never used at the same time as Expr.pRight */ |
| 110334 | 110364 | assert( (ExprUseXList(p) && p->x.pList==0) || p->pRight==0 ); |
| 110335 | | - if( p->pLeft && p->op!=TK_SELECT_COLUMN ) sqlite3ExprDeleteNN(db, p->pLeft); |
| 110336 | 110365 | if( p->pRight ){ |
| 110337 | 110366 | assert( !ExprHasProperty(p, EP_WinFunc) ); |
| 110338 | 110367 | sqlite3ExprDeleteNN(db, p->pRight); |
| 110339 | 110368 | }else if( ExprUseXSelect(p) ){ |
| 110340 | 110369 | assert( !ExprHasProperty(p, EP_WinFunc) ); |
| | @@ -110344,10 +110373,23 @@ |
| 110344 | 110373 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 110345 | 110374 | if( ExprHasProperty(p, EP_WinFunc) ){ |
| 110346 | 110375 | sqlite3WindowDelete(db, p->y.pWin); |
| 110347 | 110376 | } |
| 110348 | 110377 | #endif |
| 110378 | + } |
| 110379 | + if( p->pLeft && p->op!=TK_SELECT_COLUMN ){ |
| 110380 | + Expr *pLeft = p->pLeft; |
| 110381 | + if( !ExprHasProperty(p, EP_Static) |
| 110382 | + && !ExprHasProperty(pLeft, EP_Static) |
| 110383 | + ){ |
| 110384 | + /* Avoid unnecessary recursion on unary operators */ |
| 110385 | + sqlite3DbNNFreeNN(db, p); |
| 110386 | + p = pLeft; |
| 110387 | + goto exprDeleteRestart; |
| 110388 | + }else{ |
| 110389 | + sqlite3ExprDeleteNN(db, pLeft); |
| 110390 | + } |
| 110349 | 110391 | } |
| 110350 | 110392 | } |
| 110351 | 110393 | if( !ExprHasProperty(p, EP_Static) ){ |
| 110352 | 110394 | sqlite3DbNNFreeNN(db, p); |
| 110353 | 110395 | } |
| | @@ -111340,11 +111382,11 @@ |
| 111340 | 111382 | || ExprHasProperty(pExpr, EP_WinFunc) |
| 111341 | 111383 | ){ |
| 111342 | 111384 | pWalker->eCode = 0; |
| 111343 | 111385 | return WRC_Abort; |
| 111344 | 111386 | } |
| 111345 | | - return WRC_Continue; |
| 111387 | + return WRC_Prune; |
| 111346 | 111388 | } |
| 111347 | 111389 | |
| 111348 | 111390 | |
| 111349 | 111391 | /* |
| 111350 | 111392 | ** These routines are Walker callbacks used to check expressions to |
| | @@ -124219,13 +124261,10 @@ |
| 124219 | 124261 | if( p->tabFlags & TF_HasGenerated ){ |
| 124220 | 124262 | sqlite3VdbeAddOp4(v, OP_SqlExec, 0x0001, 0, 0, |
| 124221 | 124263 | sqlite3MPrintf(db, "SELECT*FROM\"%w\".\"%w\"", |
| 124222 | 124264 | db->aDb[iDb].zDbSName, p->zName), P4_DYNAMIC); |
| 124223 | 124265 | } |
| 124224 | | - sqlite3VdbeAddOp4(v, OP_SqlExec, 0x0001, 0, 0, |
| 124225 | | - sqlite3MPrintf(db, "PRAGMA \"%w\".integrity_check(%Q)", |
| 124226 | | - db->aDb[iDb].zDbSName, p->zName), P4_DYNAMIC); |
| 124227 | 124266 | } |
| 124228 | 124267 | |
| 124229 | 124268 | /* Add the table to the in-memory representation of the database. |
| 124230 | 124269 | */ |
| 124231 | 124270 | if( db->init.busy ){ |
| | @@ -140351,11 +140390,11 @@ |
| 140351 | 140390 | pParse->nMem = 6; |
| 140352 | 140391 | |
| 140353 | 140392 | /* Set the maximum error count */ |
| 140354 | 140393 | mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; |
| 140355 | 140394 | if( zRight ){ |
| 140356 | | - if( sqlite3GetInt32(zRight, &mxErr) ){ |
| 140395 | + if( sqlite3GetInt32(pValue->z, &mxErr) ){ |
| 140357 | 140396 | if( mxErr<=0 ){ |
| 140358 | 140397 | mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; |
| 140359 | 140398 | } |
| 140360 | 140399 | }else{ |
| 140361 | 140400 | pObjTab = sqlite3LocateTable(pParse, 0, zRight, |
| | @@ -141559,10 +141598,11 @@ |
| 141559 | 141598 | /* Clear all content from pragma virtual table cursor. */ |
| 141560 | 141599 | static void pragmaVtabCursorClear(PragmaVtabCursor *pCsr){ |
| 141561 | 141600 | int i; |
| 141562 | 141601 | sqlite3_finalize(pCsr->pPragma); |
| 141563 | 141602 | pCsr->pPragma = 0; |
| 141603 | + pCsr->iRowid = 0; |
| 141564 | 141604 | for(i=0; i<ArraySize(pCsr->azArg); i++){ |
| 141565 | 141605 | sqlite3_free(pCsr->azArg[i]); |
| 141566 | 141606 | pCsr->azArg[i] = 0; |
| 141567 | 141607 | } |
| 141568 | 141608 | } |
| | @@ -145144,21 +145184,26 @@ |
| 145144 | 145184 | memset(&sNC, 0, sizeof(sNC)); |
| 145145 | 145185 | sNC.pSrcList = pSelect->pSrc; |
| 145146 | 145186 | for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){ |
| 145147 | 145187 | const char *zType; |
| 145148 | 145188 | i64 n; |
| 145189 | + int m = 0; |
| 145190 | + Select *pS2 = pSelect; |
| 145149 | 145191 | pTab->tabFlags |= (pCol->colFlags & COLFLAG_NOINSERT); |
| 145150 | 145192 | p = a[i].pExpr; |
| 145151 | 145193 | /* pCol->szEst = ... // Column size est for SELECT tables never used */ |
| 145152 | 145194 | pCol->affinity = sqlite3ExprAffinity(p); |
| 145195 | + while( pCol->affinity<=SQLITE_AFF_NONE && pS2->pNext!=0 ){ |
| 145196 | + m |= sqlite3ExprDataType(pS2->pEList->a[i].pExpr); |
| 145197 | + pS2 = pS2->pNext; |
| 145198 | + pCol->affinity = sqlite3ExprAffinity(pS2->pEList->a[i].pExpr); |
| 145199 | + } |
| 145153 | 145200 | if( pCol->affinity<=SQLITE_AFF_NONE ){ |
| 145154 | 145201 | pCol->affinity = aff; |
| 145155 | 145202 | } |
| 145156 | | - if( pCol->affinity>=SQLITE_AFF_TEXT && pSelect->pNext ){ |
| 145157 | | - int m = 0; |
| 145158 | | - Select *pS2; |
| 145159 | | - for(m=0, pS2=pSelect->pNext; pS2; pS2=pS2->pNext){ |
| 145203 | + if( pCol->affinity>=SQLITE_AFF_TEXT && (pS2->pNext || pS2!=pSelect) ){ |
| 145204 | + for(pS2=pS2->pNext; pS2; pS2=pS2->pNext){ |
| 145160 | 145205 | m |= sqlite3ExprDataType(pS2->pEList->a[i].pExpr); |
| 145161 | 145206 | } |
| 145162 | 145207 | if( pCol->affinity==SQLITE_AFF_TEXT && (m&0x01)!=0 ){ |
| 145163 | 145208 | pCol->affinity = SQLITE_AFF_BLOB; |
| 145164 | 145209 | }else |
| | @@ -152576,10 +152621,76 @@ |
| 152576 | 152621 | } |
| 152577 | 152622 | } |
| 152578 | 152623 | } |
| 152579 | 152624 | return pNew; |
| 152580 | 152625 | } |
| 152626 | + |
| 152627 | +/* If the Expr node is a subquery or an EXISTS operator or an IN operator that |
| 152628 | +** uses a subquery, and if the subquery is SF_Correlated, then mark the |
| 152629 | +** expression as EP_VarSelect. |
| 152630 | +*/ |
| 152631 | +static int sqlite3ReturningSubqueryVarSelect(Walker *NotUsed, Expr *pExpr){ |
| 152632 | + UNUSED_PARAMETER(NotUsed); |
| 152633 | + if( ExprUseXSelect(pExpr) |
| 152634 | + && (pExpr->x.pSelect->selFlags & SF_Correlated)!=0 |
| 152635 | + ){ |
| 152636 | + testcase( ExprHasProperty(pExpr, EP_VarSelect) ); |
| 152637 | + ExprSetProperty(pExpr, EP_VarSelect); |
| 152638 | + } |
| 152639 | + return WRC_Continue; |
| 152640 | +} |
| 152641 | + |
| 152642 | + |
| 152643 | +/* |
| 152644 | +** If the SELECT references the table pWalker->u.pTab, then do two things: |
| 152645 | +** |
| 152646 | +** (1) Mark the SELECT as as SF_Correlated. |
| 152647 | +** (2) Set pWalker->eCode to non-zero so that the caller will know |
| 152648 | +** that (1) has happened. |
| 152649 | +*/ |
| 152650 | +static int sqlite3ReturningSubqueryCorrelated(Walker *pWalker, Select *pSelect){ |
| 152651 | + int i; |
| 152652 | + SrcList *pSrc; |
| 152653 | + assert( pSelect!=0 ); |
| 152654 | + pSrc = pSelect->pSrc; |
| 152655 | + assert( pSrc!=0 ); |
| 152656 | + for(i=0; i<pSrc->nSrc; i++){ |
| 152657 | + if( pSrc->a[i].pTab==pWalker->u.pTab ){ |
| 152658 | + testcase( pSelect->selFlags & SF_Correlated ); |
| 152659 | + pSelect->selFlags |= SF_Correlated; |
| 152660 | + pWalker->eCode = 1; |
| 152661 | + break; |
| 152662 | + } |
| 152663 | + } |
| 152664 | + return WRC_Continue; |
| 152665 | +} |
| 152666 | + |
| 152667 | +/* |
| 152668 | +** Scan the expression list that is the argument to RETURNING looking |
| 152669 | +** for subqueries that depend on the table which is being modified in the |
| 152670 | +** statement that is hosting the RETURNING clause (pTab). Mark all such |
| 152671 | +** subqueries as SF_Correlated. If the subqueries are part of an |
| 152672 | +** expression, mark the expression as EP_VarSelect. |
| 152673 | +** |
| 152674 | +** https://sqlite.org/forum/forumpost/2c83569ce8945d39 |
| 152675 | +*/ |
| 152676 | +static void sqlite3ProcessReturningSubqueries( |
| 152677 | + ExprList *pEList, |
| 152678 | + Table *pTab |
| 152679 | +){ |
| 152680 | + Walker w; |
| 152681 | + memset(&w, 0, sizeof(w)); |
| 152682 | + w.xExprCallback = sqlite3ExprWalkNoop; |
| 152683 | + w.xSelectCallback = sqlite3ReturningSubqueryCorrelated; |
| 152684 | + w.u.pTab = pTab; |
| 152685 | + sqlite3WalkExprList(&w, pEList); |
| 152686 | + if( w.eCode ){ |
| 152687 | + w.xExprCallback = sqlite3ReturningSubqueryVarSelect; |
| 152688 | + w.xSelectCallback = sqlite3SelectWalkNoop; |
| 152689 | + sqlite3WalkExprList(&w, pEList); |
| 152690 | + } |
| 152691 | +} |
| 152581 | 152692 | |
| 152582 | 152693 | /* |
| 152583 | 152694 | ** Generate code for the RETURNING trigger. Unlike other triggers |
| 152584 | 152695 | ** that invoke a subprogram in the bytecode, the code for RETURNING |
| 152585 | 152696 | ** is generated in-line. |
| | @@ -152613,10 +152724,11 @@ |
| 152613 | 152724 | memset(&sFrom, 0, sizeof(sFrom)); |
| 152614 | 152725 | sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0); |
| 152615 | 152726 | sSelect.pSrc = &sFrom; |
| 152616 | 152727 | sFrom.nSrc = 1; |
| 152617 | 152728 | sFrom.a[0].pTab = pTab; |
| 152729 | + sFrom.a[0].zName = pTab->zName; /* tag-20240424-1 */ |
| 152618 | 152730 | sFrom.a[0].iCursor = -1; |
| 152619 | 152731 | sqlite3SelectPrep(pParse, &sSelect, 0); |
| 152620 | 152732 | if( pParse->nErr==0 ){ |
| 152621 | 152733 | assert( db->mallocFailed==0 ); |
| 152622 | 152734 | sqlite3GenerateColumnNames(pParse, &sSelect); |
| | @@ -152639,10 +152751,11 @@ |
| 152639 | 152751 | && ALWAYS(!db->mallocFailed) |
| 152640 | 152752 | ){ |
| 152641 | 152753 | int i; |
| 152642 | 152754 | int nCol = pNew->nExpr; |
| 152643 | 152755 | int reg = pParse->nMem+1; |
| 152756 | + sqlite3ProcessReturningSubqueries(pNew, pTab); |
| 152644 | 152757 | pParse->nMem += nCol+2; |
| 152645 | 152758 | pReturning->iRetReg = reg; |
| 152646 | 152759 | for(i=0; i<nCol; i++){ |
| 152647 | 152760 | Expr *pCol = pNew->a[i].pExpr; |
| 152648 | 152761 | assert( pCol!=0 ); /* Due to !db->mallocFailed ~9 lines above */ |
| | @@ -158590,10 +158703,31 @@ |
| 158590 | 158703 | } |
| 158591 | 158704 | pLevel->regFilter = 0; |
| 158592 | 158705 | pLevel->addrBrk = 0; |
| 158593 | 158706 | } |
| 158594 | 158707 | } |
| 158708 | + |
| 158709 | +/* |
| 158710 | +** Loop pLoop is a WHERE_INDEXED level that uses at least one IN(...) |
| 158711 | +** operator. Return true if level pLoop is guaranteed to visit only one |
| 158712 | +** row for each key generated for the index. |
| 158713 | +*/ |
| 158714 | +static int whereLoopIsOneRow(WhereLoop *pLoop){ |
| 158715 | + if( pLoop->u.btree.pIndex->onError |
| 158716 | + && pLoop->nSkip==0 |
| 158717 | + && pLoop->u.btree.nEq==pLoop->u.btree.pIndex->nKeyCol |
| 158718 | + ){ |
| 158719 | + int ii; |
| 158720 | + for(ii=0; ii<pLoop->u.btree.nEq; ii++){ |
| 158721 | + if( pLoop->aLTerm[ii]->eOperator & (WO_IS|WO_ISNULL) ){ |
| 158722 | + return 0; |
| 158723 | + } |
| 158724 | + } |
| 158725 | + return 1; |
| 158726 | + } |
| 158727 | + return 0; |
| 158728 | +} |
| 158595 | 158729 | |
| 158596 | 158730 | /* |
| 158597 | 158731 | ** Generate code for the start of the iLevel-th loop in the WHERE clause |
| 158598 | 158732 | ** implementation described by pWInfo. |
| 158599 | 158733 | */ |
| | @@ -159338,11 +159472,13 @@ |
| 159338 | 159472 | ** a LEFT JOIN: */ |
| 159339 | 159473 | assert( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))==0 ); |
| 159340 | 159474 | } |
| 159341 | 159475 | |
| 159342 | 159476 | /* Record the instruction used to terminate the loop. */ |
| 159343 | | - if( pLoop->wsFlags & WHERE_ONEROW ){ |
| 159477 | + if( (pLoop->wsFlags & WHERE_ONEROW) |
| 159478 | + || (pLevel->u.in.nIn && regBignull==0 && whereLoopIsOneRow(pLoop)) |
| 159479 | + ){ |
| 159344 | 159480 | pLevel->op = OP_Noop; |
| 159345 | 159481 | }else if( bRev ){ |
| 159346 | 159482 | pLevel->op = OP_Prev; |
| 159347 | 159483 | }else{ |
| 159348 | 159484 | pLevel->op = OP_Next; |
| | @@ -159993,16 +160129,15 @@ |
| 159993 | 160129 | if( pRight->fg.viaCoroutine ){ |
| 159994 | 160130 | sqlite3VdbeAddOp3( |
| 159995 | 160131 | v, OP_Null, 0, pRight->regResult, |
| 159996 | 160132 | pRight->regResult + pRight->pSelect->pEList->nExpr-1 |
| 159997 | 160133 | ); |
| 159998 | | - }else{ |
| 159999 | | - sqlite3VdbeAddOp1(v, OP_NullRow, pWInfo->a[k].iTabCur); |
| 160000 | | - iIdxCur = pWInfo->a[k].iIdxCur; |
| 160001 | | - if( iIdxCur ){ |
| 160002 | | - sqlite3VdbeAddOp1(v, OP_NullRow, iIdxCur); |
| 160003 | | - } |
| 160134 | + } |
| 160135 | + sqlite3VdbeAddOp1(v, OP_NullRow, pWInfo->a[k].iTabCur); |
| 160136 | + iIdxCur = pWInfo->a[k].iIdxCur; |
| 160137 | + if( iIdxCur ){ |
| 160138 | + sqlite3VdbeAddOp1(v, OP_NullRow, iIdxCur); |
| 160004 | 160139 | } |
| 160005 | 160140 | } |
| 160006 | 160141 | if( (pTabItem->fg.jointype & JT_LTORJ)==0 ){ |
| 160007 | 160142 | mAll |= pLoop->maskSelf; |
| 160008 | 160143 | for(k=0; k<pWC->nTerm; k++){ |
| | @@ -161700,10 +161835,11 @@ |
| 161700 | 161835 | ** will only be added if each of the child terms passes the |
| 161701 | 161836 | ** (leftCursor==iCsr) test below. */ |
| 161702 | 161837 | continue; |
| 161703 | 161838 | } |
| 161704 | 161839 | if( pWC->a[ii].leftCursor!=iCsr ) return; |
| 161840 | + if( pWC->a[ii].prereqRight!=0 ) return; |
| 161705 | 161841 | } |
| 161706 | 161842 | |
| 161707 | 161843 | /* Check condition (5). Return early if it is not met. */ |
| 161708 | 161844 | if( pOrderBy ){ |
| 161709 | 161845 | for(ii=0; ii<pOrderBy->nExpr; ii++){ |
| | @@ -161714,16 +161850,18 @@ |
| 161714 | 161850 | } |
| 161715 | 161851 | } |
| 161716 | 161852 | |
| 161717 | 161853 | /* All conditions are met. Add the terms to the where-clause object. */ |
| 161718 | 161854 | assert( p->pLimit->op==TK_LIMIT ); |
| 161719 | | - whereAddLimitExpr(pWC, p->iLimit, p->pLimit->pLeft, |
| 161720 | | - iCsr, SQLITE_INDEX_CONSTRAINT_LIMIT); |
| 161721 | | - if( p->iOffset>0 ){ |
| 161855 | + if( p->iOffset!=0 && (p->selFlags & SF_Compound)==0 ){ |
| 161722 | 161856 | whereAddLimitExpr(pWC, p->iOffset, p->pLimit->pRight, |
| 161723 | 161857 | iCsr, SQLITE_INDEX_CONSTRAINT_OFFSET); |
| 161724 | 161858 | } |
| 161859 | + if( p->iOffset==0 || (p->selFlags & SF_Compound)==0 ){ |
| 161860 | + whereAddLimitExpr(pWC, p->iLimit, p->pLimit->pLeft, |
| 161861 | + iCsr, SQLITE_INDEX_CONSTRAINT_LIMIT); |
| 161862 | + } |
| 161725 | 161863 | } |
| 161726 | 161864 | } |
| 161727 | 161865 | |
| 161728 | 161866 | /* |
| 161729 | 161867 | ** Initialize a preallocated WhereClause structure. |
| | @@ -162236,10 +162374,46 @@ |
| 162236 | 162374 | if( ALWAYS(p!=0) && p->op==TK_COLUMN && !ExprHasProperty(p, EP_FixedCol) ){ |
| 162237 | 162375 | return p; |
| 162238 | 162376 | } |
| 162239 | 162377 | return 0; |
| 162240 | 162378 | } |
| 162379 | + |
| 162380 | +/* |
| 162381 | +** Term pTerm is guaranteed to be a WO_IN term. It may be a component term |
| 162382 | +** of a vector IN expression of the form "(x, y, ...) IN (SELECT ...)". |
| 162383 | +** This function checks to see if the term is compatible with an index |
| 162384 | +** column with affinity idxaff (one of the SQLITE_AFF_XYZ values). If so, |
| 162385 | +** it returns a pointer to the name of the collation sequence (e.g. "BINARY" |
| 162386 | +** or "NOCASE") used by the comparison in pTerm. If it is not compatible |
| 162387 | +** with affinity idxaff, NULL is returned. |
| 162388 | +*/ |
| 162389 | +static SQLITE_NOINLINE const char *indexInAffinityOk( |
| 162390 | + Parse *pParse, |
| 162391 | + WhereTerm *pTerm, |
| 162392 | + u8 idxaff |
| 162393 | +){ |
| 162394 | + Expr *pX = pTerm->pExpr; |
| 162395 | + Expr inexpr; |
| 162396 | + |
| 162397 | + assert( pTerm->eOperator & WO_IN ); |
| 162398 | + |
| 162399 | + if( sqlite3ExprIsVector(pX->pLeft) ){ |
| 162400 | + int iField = pTerm->u.x.iField - 1; |
| 162401 | + inexpr.flags = 0; |
| 162402 | + inexpr.op = TK_EQ; |
| 162403 | + inexpr.pLeft = pX->pLeft->x.pList->a[iField].pExpr; |
| 162404 | + assert( ExprUseXSelect(pX) ); |
| 162405 | + inexpr.pRight = pX->x.pSelect->pEList->a[iField].pExpr; |
| 162406 | + pX = &inexpr; |
| 162407 | + } |
| 162408 | + |
| 162409 | + if( sqlite3IndexAffinityOk(pX, idxaff) ){ |
| 162410 | + CollSeq *pRet = sqlite3ExprCompareCollSeq(pParse, pX); |
| 162411 | + return pRet ? pRet->zName : sqlite3StrBINARY; |
| 162412 | + } |
| 162413 | + return 0; |
| 162414 | +} |
| 162241 | 162415 | |
| 162242 | 162416 | /* |
| 162243 | 162417 | ** Advance to the next WhereTerm that matches according to the criteria |
| 162244 | 162418 | ** established when the pScan object was initialized by whereScanInit(). |
| 162245 | 162419 | ** Return NULL if there are no more matching WhereTerms. |
| | @@ -162287,20 +162461,28 @@ |
| 162287 | 162461 | } |
| 162288 | 162462 | } |
| 162289 | 162463 | if( (pTerm->eOperator & pScan->opMask)!=0 ){ |
| 162290 | 162464 | /* Verify the affinity and collating sequence match */ |
| 162291 | 162465 | if( pScan->zCollName && (pTerm->eOperator & WO_ISNULL)==0 ){ |
| 162292 | | - CollSeq *pColl; |
| 162466 | + const char *zCollName; |
| 162293 | 162467 | Parse *pParse = pWC->pWInfo->pParse; |
| 162294 | 162468 | pX = pTerm->pExpr; |
| 162295 | | - if( !sqlite3IndexAffinityOk(pX, pScan->idxaff) ){ |
| 162296 | | - continue; |
| 162469 | + |
| 162470 | + if( (pTerm->eOperator & WO_IN) ){ |
| 162471 | + zCollName = indexInAffinityOk(pParse, pTerm, pScan->idxaff); |
| 162472 | + if( !zCollName ) continue; |
| 162473 | + }else{ |
| 162474 | + CollSeq *pColl; |
| 162475 | + if( !sqlite3IndexAffinityOk(pX, pScan->idxaff) ){ |
| 162476 | + continue; |
| 162477 | + } |
| 162478 | + assert(pX->pLeft); |
| 162479 | + pColl = sqlite3ExprCompareCollSeq(pParse, pX); |
| 162480 | + zCollName = pColl ? pColl->zName : sqlite3StrBINARY; |
| 162297 | 162481 | } |
| 162298 | | - assert(pX->pLeft); |
| 162299 | | - pColl = sqlite3ExprCompareCollSeq(pParse, pX); |
| 162300 | | - if( pColl==0 ) pColl = pParse->db->pDfltColl; |
| 162301 | | - if( sqlite3StrICmp(pColl->zName, pScan->zCollName) ){ |
| 162482 | + |
| 162483 | + if( sqlite3StrICmp(zCollName, pScan->zCollName) ){ |
| 162302 | 162484 | continue; |
| 162303 | 162485 | } |
| 162304 | 162486 | } |
| 162305 | 162487 | if( (pTerm->eOperator & (WO_EQ|WO_IS))!=0 |
| 162306 | 162488 | && (pX = pTerm->pExpr->pRight, ALWAYS(pX!=0)) |
| | @@ -165947,10 +166129,25 @@ |
| 165947 | 166129 | static int isLimitTerm(WhereTerm *pTerm){ |
| 165948 | 166130 | assert( pTerm->eOperator==WO_AUX || pTerm->eMatchOp==0 ); |
| 165949 | 166131 | return pTerm->eMatchOp>=SQLITE_INDEX_CONSTRAINT_LIMIT |
| 165950 | 166132 | && pTerm->eMatchOp<=SQLITE_INDEX_CONSTRAINT_OFFSET; |
| 165951 | 166133 | } |
| 166134 | + |
| 166135 | +/* |
| 166136 | +** Return true if the first nCons constraints in the pUsage array are |
| 166137 | +** marked as in-use (have argvIndex>0). False otherwise. |
| 166138 | +*/ |
| 166139 | +static int allConstraintsUsed( |
| 166140 | + struct sqlite3_index_constraint_usage *aUsage, |
| 166141 | + int nCons |
| 166142 | +){ |
| 166143 | + int ii; |
| 166144 | + for(ii=0; ii<nCons; ii++){ |
| 166145 | + if( aUsage[ii].argvIndex<=0 ) return 0; |
| 166146 | + } |
| 166147 | + return 1; |
| 166148 | +} |
| 165952 | 166149 | |
| 165953 | 166150 | /* |
| 165954 | 166151 | ** Argument pIdxInfo is already populated with all constraints that may |
| 165955 | 166152 | ** be used by the virtual table identified by pBuilder->pNew->iTab. This |
| 165956 | 166153 | ** function marks a subset of those constraints usable, invokes the |
| | @@ -166088,17 +166285,24 @@ |
| 166088 | 166285 | pIdxInfo->orderByConsumed = 0; |
| 166089 | 166286 | pIdxInfo->idxFlags &= ~SQLITE_INDEX_SCAN_UNIQUE; |
| 166090 | 166287 | *pbIn = 1; assert( (mExclude & WO_IN)==0 ); |
| 166091 | 166288 | } |
| 166092 | 166289 | |
| 166290 | + /* Unless pbRetryLimit is non-NULL, there should be no LIMIT/OFFSET |
| 166291 | + ** terms. And if there are any, they should follow all other terms. */ |
| 166093 | 166292 | assert( pbRetryLimit || !isLimitTerm(pTerm) ); |
| 166094 | | - if( isLimitTerm(pTerm) && *pbIn ){ |
| 166293 | + assert( !isLimitTerm(pTerm) || i>=nConstraint-2 ); |
| 166294 | + assert( !isLimitTerm(pTerm) || i==nConstraint-1 || isLimitTerm(pTerm+1) ); |
| 166295 | + |
| 166296 | + if( isLimitTerm(pTerm) && (*pbIn || !allConstraintsUsed(pUsage, i)) ){ |
| 166095 | 166297 | /* If there is an IN(...) term handled as an == (separate call to |
| 166096 | 166298 | ** xFilter for each value on the RHS of the IN) and a LIMIT or |
| 166097 | | - ** OFFSET term handled as well, the plan is unusable. Set output |
| 166098 | | - ** variable *pbRetryLimit to true to tell the caller to retry with |
| 166099 | | - ** LIMIT and OFFSET disabled. */ |
| 166299 | + ** OFFSET term handled as well, the plan is unusable. Similarly, |
| 166300 | + ** if there is a LIMIT/OFFSET and there are other unused terms, |
| 166301 | + ** the plan cannot be used. In these cases set variable *pbRetryLimit |
| 166302 | + ** to true to tell the caller to retry with LIMIT and OFFSET |
| 166303 | + ** disabled. */ |
| 166100 | 166304 | if( pIdxInfo->needToFreeIdxStr ){ |
| 166101 | 166305 | sqlite3_free(pIdxInfo->idxStr); |
| 166102 | 166306 | pIdxInfo->idxStr = 0; |
| 166103 | 166307 | pIdxInfo->needToFreeIdxStr = 0; |
| 166104 | 166308 | } |
| | @@ -167857,10 +168061,62 @@ |
| 167857 | 168061 | } |
| 167858 | 168062 | } |
| 167859 | 168063 | nSearch += pLoop->nOut; |
| 167860 | 168064 | } |
| 167861 | 168065 | } |
| 168066 | + |
| 168067 | +/* |
| 168068 | +** Expression Node callback for sqlite3ExprCanReturnSubtype(). |
| 168069 | +** |
| 168070 | +** Only a function call is able to return a subtype. So if the node |
| 168071 | +** is not a function call, return WRC_Prune immediately. |
| 168072 | +** |
| 168073 | +** A function call is able to return a subtype if it has the |
| 168074 | +** SQLITE_RESULT_SUBTYPE property. |
| 168075 | +** |
| 168076 | +** Assume that every function is able to pass-through a subtype from |
| 168077 | +** one of its argument (using sqlite3_result_value()). Most functions |
| 168078 | +** are not this way, but we don't have a mechanism to distinguish those |
| 168079 | +** that are from those that are not, so assume they all work this way. |
| 168080 | +** That means that if one of its arguments is another function and that |
| 168081 | +** other function is able to return a subtype, then this function is |
| 168082 | +** able to return a subtype. |
| 168083 | +*/ |
| 168084 | +static int exprNodeCanReturnSubtype(Walker *pWalker, Expr *pExpr){ |
| 168085 | + int n; |
| 168086 | + FuncDef *pDef; |
| 168087 | + sqlite3 *db; |
| 168088 | + if( pExpr->op!=TK_FUNCTION ){ |
| 168089 | + return WRC_Prune; |
| 168090 | + } |
| 168091 | + assert( ExprUseXList(pExpr) ); |
| 168092 | + db = pWalker->pParse->db; |
| 168093 | + n = pExpr->x.pList ? pExpr->x.pList->nExpr : 0; |
| 168094 | + pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0); |
| 168095 | + if( pDef==0 || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){ |
| 168096 | + pWalker->eCode = 1; |
| 168097 | + return WRC_Prune; |
| 168098 | + } |
| 168099 | + return WRC_Continue; |
| 168100 | +} |
| 168101 | + |
| 168102 | +/* |
| 168103 | +** Return TRUE if expression pExpr is able to return a subtype. |
| 168104 | +** |
| 168105 | +** A TRUE return does not guarantee that a subtype will be returned. |
| 168106 | +** It only indicates that a subtype return is possible. False positives |
| 168107 | +** are acceptable as they only disable an optimization. False negatives, |
| 168108 | +** on the other hand, can lead to incorrect answers. |
| 168109 | +*/ |
| 168110 | +static int sqlite3ExprCanReturnSubtype(Parse *pParse, Expr *pExpr){ |
| 168111 | + Walker w; |
| 168112 | + memset(&w, 0, sizeof(w)); |
| 168113 | + w.pParse = pParse; |
| 168114 | + w.xExprCallback = exprNodeCanReturnSubtype; |
| 168115 | + sqlite3WalkExpr(&w, pExpr); |
| 168116 | + return w.eCode; |
| 168117 | +} |
| 167862 | 168118 | |
| 167863 | 168119 | /* |
| 167864 | 168120 | ** The index pIdx is used by a query and contains one or more expressions. |
| 167865 | 168121 | ** In other words pIdx is an index on an expression. iIdxCur is the cursor |
| 167866 | 168122 | ** number for the index and iDataCur is the cursor number for the corresponding |
| | @@ -167891,23 +168147,15 @@ |
| 167891 | 168147 | pExpr = sqlite3ColumnExpr(pTab, &pTab->aCol[j]); |
| 167892 | 168148 | }else{ |
| 167893 | 168149 | continue; |
| 167894 | 168150 | } |
| 167895 | 168151 | if( sqlite3ExprIsConstant(0,pExpr) ) continue; |
| 167896 | | - if( pExpr->op==TK_FUNCTION ){ |
| 168152 | + if( pExpr->op==TK_FUNCTION && sqlite3ExprCanReturnSubtype(pParse,pExpr) ){ |
| 167897 | 168153 | /* Functions that might set a subtype should not be replaced by the |
| 167898 | 168154 | ** value taken from an expression index since the index omits the |
| 167899 | 168155 | ** subtype. https://sqlite.org/forum/forumpost/68d284c86b082c3e */ |
| 167900 | | - int n; |
| 167901 | | - FuncDef *pDef; |
| 167902 | | - sqlite3 *db = pParse->db; |
| 167903 | | - assert( ExprUseXList(pExpr) ); |
| 167904 | | - n = pExpr->x.pList ? pExpr->x.pList->nExpr : 0; |
| 167905 | | - pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0); |
| 167906 | | - if( pDef==0 || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){ |
| 167907 | | - continue; |
| 167908 | | - } |
| 168156 | + continue; |
| 167909 | 168157 | } |
| 167910 | 168158 | p = sqlite3DbMallocRaw(pParse->db, sizeof(IndexedExpr)); |
| 167911 | 168159 | if( p==0 ) break; |
| 167912 | 168160 | p->pIENext = pParse->pIdxEpr; |
| 167913 | 168161 | #ifdef WHERETRACE_ENABLED |
| | @@ -168883,13 +169131,12 @@ |
| 168883 | 169131 | int m, n; |
| 168884 | 169132 | n = pSrc->regResult; |
| 168885 | 169133 | assert( pSrc->pTab!=0 ); |
| 168886 | 169134 | m = pSrc->pTab->nCol; |
| 168887 | 169135 | sqlite3VdbeAddOp3(v, OP_Null, 0, n, n+m-1); |
| 168888 | | - }else{ |
| 168889 | | - sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iTabCur); |
| 168890 | 169136 | } |
| 169137 | + sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iTabCur); |
| 168891 | 169138 | } |
| 168892 | 169139 | if( (ws & WHERE_INDEXED) |
| 168893 | 169140 | || ((ws & WHERE_MULTI_OR) && pLevel->u.pCoveringIdx) |
| 168894 | 169141 | ){ |
| 168895 | 169142 | if( ws & WHERE_MULTI_OR ){ |
| | @@ -172588,12 +172835,12 @@ |
| 172588 | 172835 | #define TK_AGG_FUNCTION 168 |
| 172589 | 172836 | #define TK_AGG_COLUMN 169 |
| 172590 | 172837 | #define TK_TRUEFALSE 170 |
| 172591 | 172838 | #define TK_ISNOT 171 |
| 172592 | 172839 | #define TK_FUNCTION 172 |
| 172593 | | -#define TK_UMINUS 173 |
| 172594 | | -#define TK_UPLUS 174 |
| 172840 | +#define TK_UPLUS 173 |
| 172841 | +#define TK_UMINUS 174 |
| 172595 | 172842 | #define TK_TRUTH 175 |
| 172596 | 172843 | #define TK_REGISTER 176 |
| 172597 | 172844 | #define TK_VECTOR 177 |
| 172598 | 172845 | #define TK_SELECT_COLUMN 178 |
| 172599 | 172846 | #define TK_IF_NULL_ROW 179 |
| | @@ -173620,12 +173867,12 @@ |
| 173620 | 173867 | 0, /* AGG_FUNCTION => nothing */ |
| 173621 | 173868 | 0, /* AGG_COLUMN => nothing */ |
| 173622 | 173869 | 0, /* TRUEFALSE => nothing */ |
| 173623 | 173870 | 0, /* ISNOT => nothing */ |
| 173624 | 173871 | 0, /* FUNCTION => nothing */ |
| 173625 | | - 0, /* UMINUS => nothing */ |
| 173626 | 173872 | 0, /* UPLUS => nothing */ |
| 173873 | + 0, /* UMINUS => nothing */ |
| 173627 | 173874 | 0, /* TRUTH => nothing */ |
| 173628 | 173875 | 0, /* REGISTER => nothing */ |
| 173629 | 173876 | 0, /* VECTOR => nothing */ |
| 173630 | 173877 | 0, /* SELECT_COLUMN => nothing */ |
| 173631 | 173878 | 0, /* IF_NULL_ROW => nothing */ |
| | @@ -173889,12 +174136,12 @@ |
| 173889 | 174136 | /* 168 */ "AGG_FUNCTION", |
| 173890 | 174137 | /* 169 */ "AGG_COLUMN", |
| 173891 | 174138 | /* 170 */ "TRUEFALSE", |
| 173892 | 174139 | /* 171 */ "ISNOT", |
| 173893 | 174140 | /* 172 */ "FUNCTION", |
| 173894 | | - /* 173 */ "UMINUS", |
| 173895 | | - /* 174 */ "UPLUS", |
| 174141 | + /* 173 */ "UPLUS", |
| 174142 | + /* 174 */ "UMINUS", |
| 173896 | 174143 | /* 175 */ "TRUTH", |
| 173897 | 174144 | /* 176 */ "REGISTER", |
| 173898 | 174145 | /* 177 */ "VECTOR", |
| 173899 | 174146 | /* 178 */ "SELECT_COLUMN", |
| 173900 | 174147 | /* 179 */ "IF_NULL_ROW", |
| | @@ -176751,12 +176998,21 @@ |
| 176751 | 176998 | case 215: /* expr ::= BITNOT expr */ yytestcase(yyruleno==215); |
| 176752 | 176999 | {yymsp[-1].minor.yy454 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy454, 0);/*A-overwrites-B*/} |
| 176753 | 177000 | break; |
| 176754 | 177001 | case 216: /* expr ::= PLUS|MINUS expr */ |
| 176755 | 177002 | { |
| 176756 | | - yymsp[-1].minor.yy454 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy454, 0); |
| 176757 | | - /*A-overwrites-B*/ |
| 177003 | + Expr *p = yymsp[0].minor.yy454; |
| 177004 | + u8 op = yymsp[-1].major + (TK_UPLUS-TK_PLUS); |
| 177005 | + assert( TK_UPLUS>TK_PLUS ); |
| 177006 | + assert( TK_UMINUS == TK_MINUS + (TK_UPLUS - TK_PLUS) ); |
| 177007 | + if( p && p->op==TK_UPLUS ){ |
| 177008 | + p->op = op; |
| 177009 | + yymsp[-1].minor.yy454 = p; |
| 177010 | + }else{ |
| 177011 | + yymsp[-1].minor.yy454 = sqlite3PExpr(pParse, op, p, 0); |
| 177012 | + /*A-overwrites-B*/ |
| 177013 | + } |
| 176758 | 177014 | } |
| 176759 | 177015 | break; |
| 176760 | 177016 | case 217: /* expr ::= expr PTR expr */ |
| 176761 | 177017 | { |
| 176762 | 177018 | ExprList *pList = sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy454); |
| | @@ -228296,18 +228552,18 @@ |
| 228296 | 228552 | ** sufficient either for the 'T' or 'P' byte and the varint that follows |
| 228297 | 228553 | ** it, or for the two single byte values otherwise. */ |
| 228298 | 228554 | p->rc = sessionInputBuffer(&p->in, 2); |
| 228299 | 228555 | if( p->rc!=SQLITE_OK ) return p->rc; |
| 228300 | 228556 | |
| 228557 | + sessionDiscardData(&p->in); |
| 228558 | + p->in.iCurrent = p->in.iNext; |
| 228559 | + |
| 228301 | 228560 | /* If the iterator is already at the end of the changeset, return DONE. */ |
| 228302 | 228561 | if( p->in.iNext>=p->in.nData ){ |
| 228303 | 228562 | return SQLITE_DONE; |
| 228304 | 228563 | } |
| 228305 | 228564 | |
| 228306 | | - sessionDiscardData(&p->in); |
| 228307 | | - p->in.iCurrent = p->in.iNext; |
| 228308 | | - |
| 228309 | 228565 | op = p->in.aData[p->in.iNext++]; |
| 228310 | 228566 | while( op=='T' || op=='P' ){ |
| 228311 | 228567 | if( pbNew ) *pbNew = 1; |
| 228312 | 228568 | p->bPatchset = (op=='P'); |
| 228313 | 228569 | if( sessionChangesetReadTblhdr(p) ) return p->rc; |
| | @@ -230038,10 +230294,11 @@ |
| 230038 | 230294 | */ |
| 230039 | 230295 | struct sqlite3_changegroup { |
| 230040 | 230296 | int rc; /* Error code */ |
| 230041 | 230297 | int bPatch; /* True to accumulate patchsets */ |
| 230042 | 230298 | SessionTable *pList; /* List of tables in current patch */ |
| 230299 | + SessionBuffer rec; |
| 230043 | 230300 | |
| 230044 | 230301 | sqlite3 *db; /* Configured by changegroup_schema() */ |
| 230045 | 230302 | char *zDb; /* Configured by changegroup_schema() */ |
| 230046 | 230303 | }; |
| 230047 | 230304 | |
| | @@ -230336,112 +230593,132 @@ |
| 230336 | 230593 | |
| 230337 | 230594 | return rc; |
| 230338 | 230595 | } |
| 230339 | 230596 | |
| 230340 | 230597 | /* |
| 230341 | | -** Add all changes in the changeset traversed by the iterator passed as |
| 230342 | | -** the first argument to the changegroup hash tables. |
| 230598 | +** Locate or create a SessionTable object that may be used to add the |
| 230599 | +** change currently pointed to by iterator pIter to changegroup pGrp. |
| 230600 | +** If successful, set output variable (*ppTab) to point to the table |
| 230601 | +** object and return SQLITE_OK. Otherwise, if some error occurs, return |
| 230602 | +** an SQLite error code and leave (*ppTab) set to NULL. |
| 230343 | 230603 | */ |
| 230344 | | -static int sessionChangesetToHash( |
| 230345 | | - sqlite3_changeset_iter *pIter, /* Iterator to read from */ |
| 230346 | | - sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */ |
| 230347 | | - int bRebase /* True if hash table is for rebasing */ |
| 230604 | +static int sessionChangesetFindTable( |
| 230605 | + sqlite3_changegroup *pGrp, |
| 230606 | + const char *zTab, |
| 230607 | + sqlite3_changeset_iter *pIter, |
| 230608 | + SessionTable **ppTab |
| 230348 | 230609 | ){ |
| 230349 | | - u8 *aRec; |
| 230350 | | - int nRec; |
| 230351 | 230610 | int rc = SQLITE_OK; |
| 230611 | + SessionTable *pTab = 0; |
| 230612 | + int nTab = (int)strlen(zTab); |
| 230613 | + u8 *abPK = 0; |
| 230614 | + int nCol = 0; |
| 230615 | + |
| 230616 | + *ppTab = 0; |
| 230617 | + sqlite3changeset_pk(pIter, &abPK, &nCol); |
| 230618 | + |
| 230619 | + /* Search the list for an existing table */ |
| 230620 | + for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){ |
| 230621 | + if( 0==sqlite3_strnicmp(pTab->zName, zTab, nTab+1) ) break; |
| 230622 | + } |
| 230623 | + |
| 230624 | + /* If one was not found above, create a new table now */ |
| 230625 | + if( !pTab ){ |
| 230626 | + SessionTable **ppNew; |
| 230627 | + |
| 230628 | + pTab = sqlite3_malloc64(sizeof(SessionTable) + nCol + nTab+1); |
| 230629 | + if( !pTab ){ |
| 230630 | + return SQLITE_NOMEM; |
| 230631 | + } |
| 230632 | + memset(pTab, 0, sizeof(SessionTable)); |
| 230633 | + pTab->nCol = nCol; |
| 230634 | + pTab->abPK = (u8*)&pTab[1]; |
| 230635 | + memcpy(pTab->abPK, abPK, nCol); |
| 230636 | + pTab->zName = (char*)&pTab->abPK[nCol]; |
| 230637 | + memcpy(pTab->zName, zTab, nTab+1); |
| 230638 | + |
| 230639 | + if( pGrp->db ){ |
| 230640 | + pTab->nCol = 0; |
| 230641 | + rc = sessionInitTable(0, pTab, pGrp->db, pGrp->zDb); |
| 230642 | + if( rc ){ |
| 230643 | + assert( pTab->azCol==0 ); |
| 230644 | + sqlite3_free(pTab); |
| 230645 | + return rc; |
| 230646 | + } |
| 230647 | + } |
| 230648 | + |
| 230649 | + /* The new object must be linked on to the end of the list, not |
| 230650 | + ** simply added to the start of it. This is to ensure that the |
| 230651 | + ** tables within the output of sqlite3changegroup_output() are in |
| 230652 | + ** the right order. */ |
| 230653 | + for(ppNew=&pGrp->pList; *ppNew; ppNew=&(*ppNew)->pNext); |
| 230654 | + *ppNew = pTab; |
| 230655 | + } |
| 230656 | + |
| 230657 | + /* Check that the table is compatible. */ |
| 230658 | + if( !sessionChangesetCheckCompat(pTab, nCol, abPK) ){ |
| 230659 | + rc = SQLITE_SCHEMA; |
| 230660 | + } |
| 230661 | + |
| 230662 | + *ppTab = pTab; |
| 230663 | + return rc; |
| 230664 | +} |
| 230665 | + |
| 230666 | +/* |
| 230667 | +** Add the change currently indicated by iterator pIter to the hash table |
| 230668 | +** belonging to changegroup pGrp. |
| 230669 | +*/ |
| 230670 | +static int sessionOneChangeToHash( |
| 230671 | + sqlite3_changegroup *pGrp, |
| 230672 | + sqlite3_changeset_iter *pIter, |
| 230673 | + int bRebase |
| 230674 | +){ |
| 230675 | + int rc = SQLITE_OK; |
| 230676 | + int nCol = 0; |
| 230677 | + int op = 0; |
| 230678 | + int iHash = 0; |
| 230679 | + int bIndirect = 0; |
| 230680 | + SessionChange *pChange = 0; |
| 230681 | + SessionChange *pExist = 0; |
| 230682 | + SessionChange **pp = 0; |
| 230352 | 230683 | SessionTable *pTab = 0; |
| 230353 | | - SessionBuffer rec = {0, 0, 0}; |
| 230354 | | - |
| 230355 | | - while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec, 0) ){ |
| 230356 | | - const char *zNew; |
| 230357 | | - int nCol; |
| 230358 | | - int op; |
| 230359 | | - int iHash; |
| 230360 | | - int bIndirect; |
| 230361 | | - SessionChange *pChange; |
| 230362 | | - SessionChange *pExist = 0; |
| 230363 | | - SessionChange **pp; |
| 230364 | | - |
| 230365 | | - /* Ensure that only changesets, or only patchsets, but not a mixture |
| 230366 | | - ** of both, are being combined. It is an error to try to combine a |
| 230367 | | - ** changeset and a patchset. */ |
| 230368 | | - if( pGrp->pList==0 ){ |
| 230369 | | - pGrp->bPatch = pIter->bPatchset; |
| 230370 | | - }else if( pIter->bPatchset!=pGrp->bPatch ){ |
| 230371 | | - rc = SQLITE_ERROR; |
| 230372 | | - break; |
| 230373 | | - } |
| 230374 | | - |
| 230375 | | - sqlite3changeset_op(pIter, &zNew, &nCol, &op, &bIndirect); |
| 230376 | | - if( !pTab || sqlite3_stricmp(zNew, pTab->zName) ){ |
| 230377 | | - /* Search the list for a matching table */ |
| 230378 | | - int nNew = (int)strlen(zNew); |
| 230379 | | - u8 *abPK; |
| 230380 | | - |
| 230381 | | - sqlite3changeset_pk(pIter, &abPK, 0); |
| 230382 | | - for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){ |
| 230383 | | - if( 0==sqlite3_strnicmp(pTab->zName, zNew, nNew+1) ) break; |
| 230384 | | - } |
| 230385 | | - if( !pTab ){ |
| 230386 | | - SessionTable **ppTab; |
| 230387 | | - |
| 230388 | | - pTab = sqlite3_malloc64(sizeof(SessionTable) + nCol + nNew+1); |
| 230389 | | - if( !pTab ){ |
| 230390 | | - rc = SQLITE_NOMEM; |
| 230391 | | - break; |
| 230392 | | - } |
| 230393 | | - memset(pTab, 0, sizeof(SessionTable)); |
| 230394 | | - pTab->nCol = nCol; |
| 230395 | | - pTab->abPK = (u8*)&pTab[1]; |
| 230396 | | - memcpy(pTab->abPK, abPK, nCol); |
| 230397 | | - pTab->zName = (char*)&pTab->abPK[nCol]; |
| 230398 | | - memcpy(pTab->zName, zNew, nNew+1); |
| 230399 | | - |
| 230400 | | - if( pGrp->db ){ |
| 230401 | | - pTab->nCol = 0; |
| 230402 | | - rc = sessionInitTable(0, pTab, pGrp->db, pGrp->zDb); |
| 230403 | | - if( rc ){ |
| 230404 | | - assert( pTab->azCol==0 ); |
| 230405 | | - sqlite3_free(pTab); |
| 230406 | | - break; |
| 230407 | | - } |
| 230408 | | - } |
| 230409 | | - |
| 230410 | | - /* The new object must be linked on to the end of the list, not |
| 230411 | | - ** simply added to the start of it. This is to ensure that the |
| 230412 | | - ** tables within the output of sqlite3changegroup_output() are in |
| 230413 | | - ** the right order. */ |
| 230414 | | - for(ppTab=&pGrp->pList; *ppTab; ppTab=&(*ppTab)->pNext); |
| 230415 | | - *ppTab = pTab; |
| 230416 | | - } |
| 230417 | | - |
| 230418 | | - if( !sessionChangesetCheckCompat(pTab, nCol, abPK) ){ |
| 230419 | | - rc = SQLITE_SCHEMA; |
| 230420 | | - break; |
| 230421 | | - } |
| 230422 | | - } |
| 230423 | | - |
| 230424 | | - if( nCol<pTab->nCol ){ |
| 230425 | | - assert( pGrp->db ); |
| 230426 | | - rc = sessionChangesetExtendRecord(pGrp, pTab, nCol, op, aRec, nRec, &rec); |
| 230427 | | - if( rc ) break; |
| 230428 | | - aRec = rec.aBuf; |
| 230429 | | - nRec = rec.nBuf; |
| 230430 | | - } |
| 230431 | | - |
| 230432 | | - if( sessionGrowHash(0, pIter->bPatchset, pTab) ){ |
| 230433 | | - rc = SQLITE_NOMEM; |
| 230434 | | - break; |
| 230435 | | - } |
| 230684 | + u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2]; |
| 230685 | + int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2; |
| 230686 | + |
| 230687 | + /* Ensure that only changesets, or only patchsets, but not a mixture |
| 230688 | + ** of both, are being combined. It is an error to try to combine a |
| 230689 | + ** changeset and a patchset. */ |
| 230690 | + if( pGrp->pList==0 ){ |
| 230691 | + pGrp->bPatch = pIter->bPatchset; |
| 230692 | + }else if( pIter->bPatchset!=pGrp->bPatch ){ |
| 230693 | + rc = SQLITE_ERROR; |
| 230694 | + } |
| 230695 | + |
| 230696 | + if( rc==SQLITE_OK ){ |
| 230697 | + const char *zTab = 0; |
| 230698 | + sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect); |
| 230699 | + rc = sessionChangesetFindTable(pGrp, zTab, pIter, &pTab); |
| 230700 | + } |
| 230701 | + |
| 230702 | + if( rc==SQLITE_OK && nCol<pTab->nCol ){ |
| 230703 | + SessionBuffer *pBuf = &pGrp->rec; |
| 230704 | + rc = sessionChangesetExtendRecord(pGrp, pTab, nCol, op, aRec, nRec, pBuf); |
| 230705 | + aRec = pBuf->aBuf; |
| 230706 | + nRec = pBuf->nBuf; |
| 230707 | + assert( pGrp->db ); |
| 230708 | + } |
| 230709 | + |
| 230710 | + if( rc==SQLITE_OK && sessionGrowHash(0, pIter->bPatchset, pTab) ){ |
| 230711 | + rc = SQLITE_NOMEM; |
| 230712 | + } |
| 230713 | + |
| 230714 | + if( rc==SQLITE_OK ){ |
| 230715 | + /* Search for existing entry. If found, remove it from the hash table. |
| 230716 | + ** Code below may link it back in. */ |
| 230436 | 230717 | iHash = sessionChangeHash( |
| 230437 | 230718 | pTab, (pIter->bPatchset && op==SQLITE_DELETE), aRec, pTab->nChange |
| 230438 | 230719 | ); |
| 230439 | | - |
| 230440 | | - /* Search for existing entry. If found, remove it from the hash table. |
| 230441 | | - ** Code below may link it back in. |
| 230442 | | - */ |
| 230443 | 230720 | for(pp=&pTab->apChange[iHash]; *pp; pp=&(*pp)->pNext){ |
| 230444 | 230721 | int bPkOnly1 = 0; |
| 230445 | 230722 | int bPkOnly2 = 0; |
| 230446 | 230723 | if( pIter->bPatchset ){ |
| 230447 | 230724 | bPkOnly1 = (*pp)->op==SQLITE_DELETE; |
| | @@ -230452,23 +230729,45 @@ |
| 230452 | 230729 | *pp = (*pp)->pNext; |
| 230453 | 230730 | pTab->nEntry--; |
| 230454 | 230731 | break; |
| 230455 | 230732 | } |
| 230456 | 230733 | } |
| 230734 | + } |
| 230457 | 230735 | |
| 230736 | + if( rc==SQLITE_OK ){ |
| 230458 | 230737 | rc = sessionChangeMerge(pTab, bRebase, |
| 230459 | 230738 | pIter->bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange |
| 230460 | 230739 | ); |
| 230461 | | - if( rc ) break; |
| 230462 | | - if( pChange ){ |
| 230463 | | - pChange->pNext = pTab->apChange[iHash]; |
| 230464 | | - pTab->apChange[iHash] = pChange; |
| 230465 | | - pTab->nEntry++; |
| 230466 | | - } |
| 230740 | + } |
| 230741 | + if( rc==SQLITE_OK && pChange ){ |
| 230742 | + pChange->pNext = pTab->apChange[iHash]; |
| 230743 | + pTab->apChange[iHash] = pChange; |
| 230744 | + pTab->nEntry++; |
| 230467 | 230745 | } |
| 230468 | 230746 | |
| 230469 | | - sqlite3_free(rec.aBuf); |
| 230747 | + if( rc==SQLITE_OK ) rc = pIter->rc; |
| 230748 | + return rc; |
| 230749 | +} |
| 230750 | + |
| 230751 | +/* |
| 230752 | +** Add all changes in the changeset traversed by the iterator passed as |
| 230753 | +** the first argument to the changegroup hash tables. |
| 230754 | +*/ |
| 230755 | +static int sessionChangesetToHash( |
| 230756 | + sqlite3_changeset_iter *pIter, /* Iterator to read from */ |
| 230757 | + sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */ |
| 230758 | + int bRebase /* True if hash table is for rebasing */ |
| 230759 | +){ |
| 230760 | + u8 *aRec; |
| 230761 | + int nRec; |
| 230762 | + int rc = SQLITE_OK; |
| 230763 | + |
| 230764 | + while( SQLITE_ROW==(sessionChangesetNext(pIter, &aRec, &nRec, 0)) ){ |
| 230765 | + rc = sessionOneChangeToHash(pGrp, pIter, bRebase); |
| 230766 | + if( rc!=SQLITE_OK ) break; |
| 230767 | + } |
| 230768 | + |
| 230470 | 230769 | if( rc==SQLITE_OK ) rc = pIter->rc; |
| 230471 | 230770 | return rc; |
| 230472 | 230771 | } |
| 230473 | 230772 | |
| 230474 | 230773 | /* |
| | @@ -230591,10 +230890,27 @@ |
| 230591 | 230890 | rc = sessionChangesetToHash(pIter, pGrp, 0); |
| 230592 | 230891 | } |
| 230593 | 230892 | sqlite3changeset_finalize(pIter); |
| 230594 | 230893 | return rc; |
| 230595 | 230894 | } |
| 230895 | + |
| 230896 | +/* |
| 230897 | +** Add a single change to a changeset-group. |
| 230898 | +*/ |
| 230899 | +SQLITE_API int sqlite3changegroup_add_change( |
| 230900 | + sqlite3_changegroup *pGrp, |
| 230901 | + sqlite3_changeset_iter *pIter |
| 230902 | +){ |
| 230903 | + if( pIter->in.iCurrent==pIter->in.iNext |
| 230904 | + || pIter->rc!=SQLITE_OK |
| 230905 | + || pIter->bInvert |
| 230906 | + ){ |
| 230907 | + /* Iterator does not point to any valid entry or is an INVERT iterator. */ |
| 230908 | + return SQLITE_ERROR; |
| 230909 | + } |
| 230910 | + return sessionOneChangeToHash(pGrp, pIter, 0); |
| 230911 | +} |
| 230596 | 230912 | |
| 230597 | 230913 | /* |
| 230598 | 230914 | ** Obtain a buffer containing a changeset representing the concatenation |
| 230599 | 230915 | ** of all changesets added to the group so far. |
| 230600 | 230916 | */ |
| | @@ -230641,10 +230957,11 @@ |
| 230641 | 230957 | */ |
| 230642 | 230958 | SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){ |
| 230643 | 230959 | if( pGrp ){ |
| 230644 | 230960 | sqlite3_free(pGrp->zDb); |
| 230645 | 230961 | sessionDeleteTable(0, pGrp->pList); |
| 230962 | + sqlite3_free(pGrp->rec.aBuf); |
| 230646 | 230963 | sqlite3_free(pGrp); |
| 230647 | 230964 | } |
| 230648 | 230965 | } |
| 230649 | 230966 | |
| 230650 | 230967 | /* |
| | @@ -231042,10 +231359,11 @@ |
| 231042 | 231359 | ** Destroy a rebaser object |
| 231043 | 231360 | */ |
| 231044 | 231361 | SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p){ |
| 231045 | 231362 | if( p ){ |
| 231046 | 231363 | sessionDeleteTable(0, p->grp.pList); |
| 231364 | + sqlite3_free(p->grp.rec.aBuf); |
| 231047 | 231365 | sqlite3_free(p); |
| 231048 | 231366 | } |
| 231049 | 231367 | } |
| 231050 | 231368 | |
| 231051 | 231369 | /* |
| | @@ -252193,11 +252511,11 @@ |
| 252193 | 252511 | int nArg, /* Number of args */ |
| 252194 | 252512 | sqlite3_value **apUnused /* Function arguments */ |
| 252195 | 252513 | ){ |
| 252196 | 252514 | assert( nArg==0 ); |
| 252197 | 252515 | UNUSED_PARAM2(nArg, apUnused); |
| 252198 | | - sqlite3_result_text(pCtx, "fts5: 2024-04-18 16:11:01 8c0f69e0e4ae0a446838cc193bfd4395fd251f3c7659b35ac388e5a0a7650a66", -1, SQLITE_TRANSIENT); |
| 252516 | + sqlite3_result_text(pCtx, "fts5: 2024-05-08 11:51:56 42d67c6fed3a5f21d7b71515aca471ba61d387e620022735a2e7929fa3a237cf", -1, SQLITE_TRANSIENT); |
| 252199 | 252517 | } |
| 252200 | 252518 | |
| 252201 | 252519 | /* |
| 252202 | 252520 | ** Return true if zName is the extension on one of the shadow tables used |
| 252203 | 252521 | ** by this module. |
| 252204 | 252522 | |