|
|
@@ -1188,7 +1188,7 @@ extern "C" {
|
|
|
*/
|
|
|
#define SQLITE_VERSION "3.36.0"
|
|
|
#define SQLITE_VERSION_NUMBER 3036000
|
|
|
-#define SQLITE_SOURCE_ID "2021-04-20 22:48:25 ca70c8ac72c9fe3b92f8e63151229bebdccc769c5c4d603ad4f466dc4bcdalt1"
|
|
|
+#define SQLITE_SOURCE_ID "2021-04-23 00:59:38 fac12115a994a1b4347586e68faf38895ee9cb588eaa84c6f71cf9afd4c4alt1"
|
|
|
|
|
|
/*
|
|
|
** CAPI3REF: Run-Time Library Version Numbers
|
|
|
@@ -10676,6 +10676,15 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*);
|
|
|
** triggers; or 2 for changes resulting from triggers called by top-level
|
|
|
** triggers; and so forth.
|
|
|
**
|
|
|
+** When the [sqlite3_blob_write()] API is used to update a blob column,
|
|
|
+** the pre-update hook is invoked with SQLITE_DELETE. This is because the
|
|
|
+** in this case the new values are not available. In this case, when a
|
|
|
+** callback made with op==SQLITE_DELETE is actuall a write using the
|
|
|
+** sqlite3_blob_write() API, the [sqlite3_preupdate_blobwrite()] returns
|
|
|
+** the index of the column being written. In other cases, where the
|
|
|
+** pre-update hook is being invoked for some other reason, including a
|
|
|
+** regular DELETE, sqlite3_preupdate_blobwrite() returns -1.
|
|
|
+**
|
|
|
** See also: [sqlite3_update_hook()]
|
|
|
*/
|
|
|
#if defined(SQLITE_ENABLE_PREUPDATE_HOOK)
|
|
|
@@ -10696,6 +10705,7 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *, int, sqlite3_value **);
|
|
|
SQLITE_API int sqlite3_preupdate_count(sqlite3 *);
|
|
|
SQLITE_API int sqlite3_preupdate_depth(sqlite3 *);
|
|
|
SQLITE_API int sqlite3_preupdate_new(sqlite3 *, int, sqlite3_value **);
|
|
|
+SQLITE_API int sqlite3_preupdate_blobwrite(sqlite3 *);
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
|
@@ -11236,6 +11246,37 @@ SQLITE_API int sqlite3session_create(
|
|
|
*/
|
|
|
SQLITE_API void sqlite3session_delete(sqlite3_session *pSession);
|
|
|
|
|
|
+/*
|
|
|
+** CAPIREF: Conigure a Session Object
|
|
|
+** METHOD: sqlite3_session
|
|
|
+**
|
|
|
+** This method is used to configure a session object after it has been
|
|
|
+** created. At present the only valid value for the second parameter is
|
|
|
+** [SQLITE_SESSION_OBJCONFIG_SIZE].
|
|
|
+*/
|
|
|
+SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg);
|
|
|
+
|
|
|
+/*
|
|
|
+** CAPI3REF: Arguments for sqlite3session_object_config()
|
|
|
+**
|
|
|
+** The following values may passed as the the 4th parameter to
|
|
|
+** [sqlite3session_object_config].
|
|
|
+**
|
|
|
+** <dt>SQLITE_SESSION_OBJCONFIG_SIZE <dd>
|
|
|
+** This option is used to set, clear or query the flag that enables
|
|
|
+** the [sqlite3session_changeset_size()] API. Because it imposes some
|
|
|
+** computational overhead, this API is disabled by default. Argument
|
|
|
+** pArg must point to a value of type (int). If the value is initially
|
|
|
+** 0, then the sqlite3session_changeset_size() API is disabled. If it
|
|
|
+** is greater than 0, then the same API is enabled. Or, if the initial
|
|
|
+** value is less than zero, no change is made. In all cases the (int)
|
|
|
+** variable is set to 1 if the sqlite3session_changeset_size() API is
|
|
|
+** enabled following the current call, or 0 otherwise.
|
|
|
+**
|
|
|
+** It is an error (SQLITE_MISUSE) to attempt to modify this setting after
|
|
|
+** the first table has been attached to the session object.
|
|
|
+*/
|
|
|
+#define SQLITE_SESSION_OBJCONFIG_SIZE 1
|
|
|
|
|
|
/*
|
|
|
** CAPI3REF: Enable Or Disable A Session Object
|
|
|
@@ -11480,6 +11521,22 @@ SQLITE_API int sqlite3session_changeset(
|
|
|
void **ppChangeset /* OUT: Buffer containing changeset */
|
|
|
);
|
|
|
|
|
|
+/*
|
|
|
+** CAPI3REF: Return An Upper-limit For The Size Of The Changeset
|
|
|
+** METHOD: sqlite3session_changeset_size()
|
|
|
+**
|
|
|
+** By default, this function always returns 0. For it to return
|
|
|
+** a useful result, the sqlite3_session object must have been configured
|
|
|
+** to enable this API using [sqlite3session_object_config()] with the
|
|
|
+** SQLITE_SESSION_OBJCONFIG_SIZE verb.
|
|
|
+**
|
|
|
+** When enabled, this function returns an upper limit, in bytes, for the size
|
|
|
+** of the changeset that might be produced if sqlite3session_changeset() were
|
|
|
+** called. The final changeset size might be equal to or smaller than the
|
|
|
+** size in bytes returned by this function.
|
|
|
+*/
|
|
|
+SQLITE_API sqlite3_int64 sqlite3session_changeset_size(sqlite3_session *pSession);
|
|
|
+
|
|
|
/*
|
|
|
** CAPI3REF: Load The Difference Between Tables Into A Session
|
|
|
** METHOD: sqlite3_session
|
|
|
@@ -17798,6 +17855,7 @@ struct Table {
|
|
|
#define TF_Shadow 0x1000 /* True for a shadow table */
|
|
|
#define TF_HasStat4 0x2000 /* STAT4 info available for this table */
|
|
|
#define TF_Ephemeral 0x4000 /* An ephemeral table */
|
|
|
+#define TF_Eponymous 0x8000 /* An eponymous virtual table */
|
|
|
|
|
|
/*
|
|
|
** Test to see whether or not a table is a virtual table. This is
|
|
|
@@ -21577,6 +21635,7 @@ struct PreUpdate {
|
|
|
UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */
|
|
|
UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */
|
|
|
int iNewReg; /* Register for new.* values */
|
|
|
+ int iBlobWrite; /* Value returned by preupdate_blobwrite() */
|
|
|
i64 iKey1; /* First key value passed to hook */
|
|
|
i64 iKey2; /* Second key value passed to hook */
|
|
|
Mem *aNew; /* Array of new.* values */
|
|
|
@@ -21665,7 +21724,8 @@ SQLITE_PRIVATE void sqlite3VdbeFrameMemDel(void*); /* Destructor on Mem */
|
|
|
SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame*); /* Actually deletes the Frame */
|
|
|
SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *);
|
|
|
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
|
|
-SQLITE_PRIVATE void sqlite3VdbePreUpdateHook(Vdbe*,VdbeCursor*,int,const char*,Table*,i64,int);
|
|
|
+SQLITE_PRIVATE void sqlite3VdbePreUpdateHook(
|
|
|
+ Vdbe*,VdbeCursor*,int,const char*,Table*,i64,int,int);
|
|
|
#endif
|
|
|
SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p);
|
|
|
|
|
|
@@ -72282,7 +72342,7 @@ static int rebuildPage(
|
|
|
put2byte(pCellptr, (pData - aData));
|
|
|
pCellptr += 2;
|
|
|
if( pData < pCellptr ) return SQLITE_CORRUPT_BKPT;
|
|
|
- memcpy(pData, pCell, sz);
|
|
|
+ memmove(pData, pCell, sz);
|
|
|
assert( sz==pPg->xCellSize(pPg, pCell) || CORRUPT_DB );
|
|
|
i++;
|
|
|
if( i>=iEnd ) break;
|
|
|
@@ -83872,7 +83932,8 @@ SQLITE_PRIVATE void sqlite3VdbePreUpdateHook(
|
|
|
const char *zDb, /* Database name */
|
|
|
Table *pTab, /* Modified table */
|
|
|
i64 iKey1, /* Initial key value */
|
|
|
- int iReg /* Register for new.* record */
|
|
|
+ int iReg, /* Register for new.* record */
|
|
|
+ int iBlobWrite
|
|
|
){
|
|
|
sqlite3 *db = v->db;
|
|
|
i64 iKey2;
|
|
|
@@ -83908,6 +83969,7 @@ SQLITE_PRIVATE void sqlite3VdbePreUpdateHook(
|
|
|
preupdate.iKey1 = iKey1;
|
|
|
preupdate.iKey2 = iKey2;
|
|
|
preupdate.pTab = pTab;
|
|
|
+ preupdate.iBlobWrite = iBlobWrite;
|
|
|
|
|
|
db->pPreUpdate = &preupdate;
|
|
|
db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2);
|
|
|
@@ -85834,6 +85896,17 @@ SQLITE_API int sqlite3_preupdate_depth(sqlite3 *db){
|
|
|
}
|
|
|
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
|
|
|
|
|
|
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
|
|
+/*
|
|
|
+** This function is designed to be called from within a pre-update callback
|
|
|
+** only.
|
|
|
+*/
|
|
|
+SQLITE_API int sqlite3_preupdate_blobwrite(sqlite3 *db){
|
|
|
+ PreUpdate *p = db->pPreUpdate;
|
|
|
+ return (p ? p->iBlobWrite : -1);
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
|
|
/*
|
|
|
** This function is called from within a pre-update callback to retrieve
|
|
|
@@ -91349,7 +91422,7 @@ case OP_Insert: {
|
|
|
/* Invoke the pre-update hook, if any */
|
|
|
if( pTab ){
|
|
|
if( db->xPreUpdateCallback && !(pOp->p5 & OPFLAG_ISUPDATE) ){
|
|
|
- sqlite3VdbePreUpdateHook(p, pC, SQLITE_INSERT, zDb, pTab, x.nKey,pOp->p2);
|
|
|
+ sqlite3VdbePreUpdateHook(p,pC,SQLITE_INSERT,zDb,pTab,x.nKey,pOp->p2,-1);
|
|
|
}
|
|
|
if( db->xUpdateCallback==0 || pTab->aCol==0 ){
|
|
|
/* Prevent post-update hook from running in cases when it should not */
|
|
|
@@ -91509,7 +91582,7 @@ case OP_Delete: {
|
|
|
sqlite3VdbePreUpdateHook(p, pC,
|
|
|
(opflags & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_DELETE,
|
|
|
zDb, pTab, pC->movetoTarget,
|
|
|
- pOp->p3
|
|
|
+ pOp->p3, -1
|
|
|
);
|
|
|
}
|
|
|
if( opflags & OPFLAG_ISNOOP ) break;
|
|
|
@@ -94938,7 +95011,7 @@ static int blobReadWrite(
|
|
|
sqlite3_int64 iKey;
|
|
|
iKey = sqlite3BtreeIntegerKey(p->pCsr);
|
|
|
sqlite3VdbePreUpdateHook(
|
|
|
- v, v->apCsr[0], SQLITE_DELETE, p->zDb, p->pTab, iKey, -1
|
|
|
+ v, v->apCsr[0], SQLITE_DELETE, p->zDb, p->pTab, iKey, -1, p->iCol
|
|
|
);
|
|
|
}
|
|
|
#endif
|
|
|
@@ -104144,7 +104217,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
|
|
|
*/
|
|
|
SQLITE_PRIVATE int sqlite3ExprCheckIN(Parse *pParse, Expr *pIn){
|
|
|
int nVector = sqlite3ExprVectorSize(pIn->pLeft);
|
|
|
- if( (pIn->flags & EP_xIsSelect) ){
|
|
|
+ if( (pIn->flags & EP_xIsSelect)!=0 && !pParse->db->mallocFailed ){
|
|
|
if( nVector!=pIn->x.pSelect->pEList->nExpr ){
|
|
|
sqlite3SubselectError(pParse, pIn->x.pSelect->pEList->nExpr, nVector);
|
|
|
return 1;
|
|
|
@@ -107136,6 +107209,7 @@ SQLITE_PRIVATE int sqlite3NoTempsInRange(Parse *pParse, int iFirst, int iLast){
|
|
|
static int isAlterableTable(Parse *pParse, Table *pTab){
|
|
|
if( 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7)
|
|
|
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
|
+ || (pTab->tabFlags & TF_Eponymous)!=0
|
|
|
|| ( (pTab->tabFlags & TF_Shadow)!=0
|
|
|
&& sqlite3ReadOnlyShadowTables(pParse->db)
|
|
|
)
|
|
|
@@ -108023,7 +108097,9 @@ static RenameToken *renameTokenFind(
|
|
|
void *pPtr
|
|
|
){
|
|
|
RenameToken **pp;
|
|
|
- assert( pPtr!=0 );
|
|
|
+ if( NEVER(pPtr==0) ){
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
for(pp=&pParse->pRename; (*pp); pp=&(*pp)->pNext){
|
|
|
if( (*pp)->p==pPtr ){
|
|
|
RenameToken *pToken = *pp;
|
|
|
@@ -111787,14 +111863,16 @@ SQLITE_PRIVATE int sqlite3FixTriggerStep(
|
|
|
return 1;
|
|
|
}
|
|
|
#ifndef SQLITE_OMIT_UPSERT
|
|
|
- if( pStep->pUpsert ){
|
|
|
- Upsert *pUp = pStep->pUpsert;
|
|
|
- if( sqlite3WalkExprList(&pFix->w, pUp->pUpsertTarget)
|
|
|
- || sqlite3WalkExpr(&pFix->w, pUp->pUpsertTargetWhere)
|
|
|
- || sqlite3WalkExprList(&pFix->w, pUp->pUpsertSet)
|
|
|
- || sqlite3WalkExpr(&pFix->w, pUp->pUpsertWhere)
|
|
|
- ){
|
|
|
- return 1;
|
|
|
+ {
|
|
|
+ Upsert *pUp;
|
|
|
+ for(pUp=pStep->pUpsert; pUp; pUp=pUp->pNextUpsert){
|
|
|
+ if( sqlite3WalkExprList(&pFix->w, pUp->pUpsertTarget)
|
|
|
+ || sqlite3WalkExpr(&pFix->w, pUp->pUpsertTargetWhere)
|
|
|
+ || sqlite3WalkExprList(&pFix->w, pUp->pUpsertSet)
|
|
|
+ || sqlite3WalkExpr(&pFix->w, pUp->pUpsertWhere)
|
|
|
+ ){
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
#endif
|
|
|
@@ -143956,6 +144034,7 @@ SQLITE_PRIVATE int sqlite3VtabEponymousTableInit(Parse *pParse, Module *pMod){
|
|
|
pTab->pSchema = db->aDb[0].pSchema;
|
|
|
assert( pTab->nModuleArg==0 );
|
|
|
pTab->iPKey = -1;
|
|
|
+ pTab->tabFlags |= TF_Eponymous;
|
|
|
addModuleArgument(pParse, pTab, sqlite3DbStrDup(db, pTab->zName));
|
|
|
addModuleArgument(pParse, pTab, 0);
|
|
|
addModuleArgument(pParse, pTab, sqlite3DbStrDup(db, pTab->zName));
|
|
|
@@ -145368,6 +145447,7 @@ static int codeAllEqualityTerms(
|
|
|
|
|
|
if( nSkip ){
|
|
|
int iIdxCur = pLevel->iIdxCur;
|
|
|
+ sqlite3VdbeAddOp3(v, OP_Null, 0, regBase, regBase+nSkip-1);
|
|
|
sqlite3VdbeAddOp1(v, (bRev?OP_Last:OP_Rewind), iIdxCur);
|
|
|
VdbeCoverageIf(v, bRev==0);
|
|
|
VdbeCoverageIf(v, bRev!=0);
|
|
|
@@ -151004,7 +151084,7 @@ static int whereLoopResize(sqlite3 *db, WhereLoop *p, int n){
|
|
|
static int whereLoopXfer(sqlite3 *db, WhereLoop *pTo, WhereLoop *pFrom){
|
|
|
whereLoopClearUnion(db, pTo);
|
|
|
if( whereLoopResize(db, pTo, pFrom->nLTerm) ){
|
|
|
- memset(&pTo->u, 0, sizeof(pTo->u));
|
|
|
+ memset(pTo, 0, WHERE_LOOP_XFER_SZ);
|
|
|
return SQLITE_NOMEM_BKPT;
|
|
|
}
|
|
|
memcpy(pTo, pFrom, WHERE_LOOP_XFER_SZ);
|
|
|
@@ -208077,6 +208157,7 @@ struct SessionHook {
|
|
|
struct sqlite3_session {
|
|
|
sqlite3 *db; /* Database handle session is attached to */
|
|
|
char *zDb; /* Name of database session is attached to */
|
|
|
+ int bEnableSize; /* True if changeset_size() enabled */
|
|
|
int bEnable; /* True if currently recording */
|
|
|
int bIndirect; /* True if all changes are indirect */
|
|
|
int bAutoAttach; /* True to auto-attach tables */
|
|
|
@@ -208084,6 +208165,7 @@ struct sqlite3_session {
|
|
|
void *pFilterCtx; /* First argument to pass to xTableFilter */
|
|
|
int (*xTableFilter)(void *pCtx, const char *zTab);
|
|
|
i64 nMalloc; /* Number of bytes of data allocated */
|
|
|
+ i64 nMaxChangesetSize;
|
|
|
sqlite3_value *pZeroBlob; /* Value containing X'' */
|
|
|
sqlite3_session *pNext; /* Next session object on same db. */
|
|
|
SessionTable *pTable; /* List of attached tables */
|
|
|
@@ -208326,8 +208408,9 @@ struct SessionTable {
|
|
|
** this structure stored in a SessionTable.aChange[] hash table.
|
|
|
*/
|
|
|
struct SessionChange {
|
|
|
- int op; /* One of UPDATE, DELETE, INSERT */
|
|
|
- int bIndirect; /* True if this change is "indirect" */
|
|
|
+ u8 op; /* One of UPDATE, DELETE, INSERT */
|
|
|
+ u8 bIndirect; /* True if this change is "indirect" */
|
|
|
+ int nMaxSize; /* Max size of eventual changeset record */
|
|
|
int nRecord; /* Number of bytes in buffer aRecord[] */
|
|
|
u8 *aRecord; /* Buffer containing old.* record */
|
|
|
SessionChange *pNext; /* For hash-table collisions */
|
|
|
@@ -209156,6 +209239,12 @@ static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){
|
|
|
if( 0==sqlite3_stricmp("sqlite_stat1", pTab->zName) ){
|
|
|
pTab->bStat1 = 1;
|
|
|
}
|
|
|
+
|
|
|
+ if( pSession->bEnableSize ){
|
|
|
+ pSession->nMaxChangesetSize += (
|
|
|
+ 1 + sessionVarintLen(pTab->nCol) + pTab->nCol + strlen(pTab->zName)+1
|
|
|
+ );
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
return (pSession->rc || pTab->abPK==0);
|
|
|
@@ -209201,6 +209290,103 @@ static int sessionStat1Depth(void *pCtx){
|
|
|
return p->hook.xDepth(p->hook.pCtx);
|
|
|
}
|
|
|
|
|
|
+static int sessionUpdateMaxSize(
|
|
|
+ int op,
|
|
|
+ sqlite3_session *pSession, /* Session object pTab is attached to */
|
|
|
+ SessionTable *pTab, /* Table that change applies to */
|
|
|
+ SessionChange *pC /* Update pC->nMaxSize */
|
|
|
+){
|
|
|
+ i64 nNew = 2;
|
|
|
+ if( pC->op==SQLITE_INSERT ){
|
|
|
+ if( op!=SQLITE_DELETE ){
|
|
|
+ int ii;
|
|
|
+ for(ii=0; ii<pTab->nCol; ii++){
|
|
|
+ sqlite3_value *p = 0;
|
|
|
+ pSession->hook.xNew(pSession->hook.pCtx, ii, &p);
|
|
|
+ sessionSerializeValue(0, p, &nNew);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }else if( op==SQLITE_DELETE ){
|
|
|
+ nNew += pC->nRecord;
|
|
|
+ if( sqlite3_preupdate_blobwrite(pSession->db)>=0 ){
|
|
|
+ nNew += pC->nRecord;
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ int ii;
|
|
|
+ u8 *pCsr = pC->aRecord;
|
|
|
+ for(ii=0; ii<pTab->nCol; ii++){
|
|
|
+ int bChanged = 1;
|
|
|
+ int nOld = 0;
|
|
|
+ int eType;
|
|
|
+ sqlite3_value *p = 0;
|
|
|
+ pSession->hook.xNew(pSession->hook.pCtx, ii, &p);
|
|
|
+ if( p==0 ){
|
|
|
+ return SQLITE_NOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ eType = *pCsr++;
|
|
|
+ switch( eType ){
|
|
|
+ case SQLITE_NULL:
|
|
|
+ bChanged = sqlite3_value_type(p)!=SQLITE_NULL;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case SQLITE_FLOAT:
|
|
|
+ case SQLITE_INTEGER: {
|
|
|
+ if( eType==sqlite3_value_type(p) ){
|
|
|
+ sqlite3_int64 iVal = sessionGetI64(pCsr);
|
|
|
+ if( eType==SQLITE_INTEGER ){
|
|
|
+ bChanged = (iVal!=sqlite3_value_int64(p));
|
|
|
+ }else{
|
|
|
+ double dVal;
|
|
|
+ memcpy(&dVal, &iVal, 8);
|
|
|
+ bChanged = (dVal!=sqlite3_value_double(p));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ nOld = 8;
|
|
|
+ pCsr += 8;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ default: {
|
|
|
+ int nByte;
|
|
|
+ nOld = sessionVarintGet(pCsr, &nByte);
|
|
|
+ pCsr += nOld;
|
|
|
+ nOld += nByte;
|
|
|
+ assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
|
|
|
+ if( eType==sqlite3_value_type(p)
|
|
|
+ && nByte==sqlite3_value_bytes(p)
|
|
|
+ && (nByte==0 || 0==memcmp(pCsr, sqlite3_value_blob(p), nByte))
|
|
|
+ ){
|
|
|
+ bChanged = 0;
|
|
|
+ }
|
|
|
+ pCsr += nByte;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if( bChanged && pTab->abPK[ii] ){
|
|
|
+ nNew = pC->nRecord + 2;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if( bChanged ){
|
|
|
+ nNew += 1 + nOld;
|
|
|
+ sessionSerializeValue(0, p, &nNew);
|
|
|
+ }else if( pTab->abPK[ii] ){
|
|
|
+ nNew += 2 + nOld;
|
|
|
+ }else{
|
|
|
+ nNew += 2;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if( nNew>pC->nMaxSize ){
|
|
|
+ int nIncr = nNew - pC->nMaxSize;
|
|
|
+ pC->nMaxSize = nNew;
|
|
|
+ pSession->nMaxChangesetSize += nIncr;
|
|
|
+ }
|
|
|
+ return SQLITE_OK;
|
|
|
+}
|
|
|
|
|
|
/*
|
|
|
** This function is only called from with a pre-update-hook reporting a
|
|
|
@@ -209274,7 +209460,6 @@ static void sessionPreupdateOneChange(
|
|
|
/* Create a new change object containing all the old values (if
|
|
|
** this is an SQLITE_UPDATE or SQLITE_DELETE), or just the PK
|
|
|
** values (if this is an INSERT). */
|
|
|
- SessionChange *pChange; /* New change object */
|
|
|
sqlite3_int64 nByte; /* Number of bytes to allocate */
|
|
|
int i; /* Used to iterate through columns */
|
|
|
|
|
|
@@ -209300,13 +209485,13 @@ static void sessionPreupdateOneChange(
|
|
|
}
|
|
|
|
|
|
/* Allocate the change object */
|
|
|
- pChange = (SessionChange *)sessionMalloc64(pSession, nByte);
|
|
|
- if( !pChange ){
|
|
|
+ pC = (SessionChange *)sessionMalloc64(pSession, nByte);
|
|
|
+ if( !pC ){
|
|
|
rc = SQLITE_NOMEM;
|
|
|
goto error_out;
|
|
|
}else{
|
|
|
- memset(pChange, 0, sizeof(SessionChange));
|
|
|
- pChange->aRecord = (u8 *)&pChange[1];
|
|
|
+ memset(pC, 0, sizeof(SessionChange));
|
|
|
+ pC->aRecord = (u8 *)&pC[1];
|
|
|
}
|
|
|
|
|
|
/* Populate the change object. None of the preupdate_old(),
|
|
|
@@ -209321,17 +209506,17 @@ static void sessionPreupdateOneChange(
|
|
|
}else if( pTab->abPK[i] ){
|
|
|
pSession->hook.xNew(pSession->hook.pCtx, i, &p);
|
|
|
}
|
|
|
- sessionSerializeValue(&pChange->aRecord[nByte], p, &nByte);
|
|
|
+ sessionSerializeValue(&pC->aRecord[nByte], p, &nByte);
|
|
|
}
|
|
|
|
|
|
/* Add the change to the hash-table */
|
|
|
if( pSession->bIndirect || pSession->hook.xDepth(pSession->hook.pCtx) ){
|
|
|
- pChange->bIndirect = 1;
|
|
|
+ pC->bIndirect = 1;
|
|
|
}
|
|
|
- pChange->nRecord = nByte;
|
|
|
- pChange->op = op;
|
|
|
- pChange->pNext = pTab->apChange[iHash];
|
|
|
- pTab->apChange[iHash] = pChange;
|
|
|
+ pC->nRecord = nByte;
|
|
|
+ pC->op = op;
|
|
|
+ pC->pNext = pTab->apChange[iHash];
|
|
|
+ pTab->apChange[iHash] = pC;
|
|
|
|
|
|
}else if( pC->bIndirect ){
|
|
|
/* If the existing change is considered "indirect", but this current
|
|
|
@@ -209342,8 +209527,14 @@ static void sessionPreupdateOneChange(
|
|
|
pC->bIndirect = 0;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ assert( rc==SQLITE_OK );
|
|
|
+ if( pSession->bEnableSize ){
|
|
|
+ rc = sessionUpdateMaxSize(op, pSession, pTab, pC);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+
|
|
|
/* If an error has occurred, mark the session object as failed. */
|
|
|
error_out:
|
|
|
if( pTab->bStat1 ){
|
|
|
@@ -210555,7 +210746,11 @@ SQLITE_API int sqlite3session_changeset(
|
|
|
int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
|
|
|
void **ppChangeset /* OUT: Buffer containing changeset */
|
|
|
){
|
|
|
- return sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset, ppChangeset);
|
|
|
+ int rc = sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset,ppChangeset);
|
|
|
+ assert( rc || pnChangeset==0
|
|
|
+ || pSession->bEnableSize==0 || *pnChangeset<=pSession->nMaxChangesetSize
|
|
|
+ );
|
|
|
+ return rc;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
@@ -210647,6 +210842,39 @@ SQLITE_API sqlite3_int64 sqlite3session_memory_used(sqlite3_session *pSession){
|
|
|
return pSession->nMalloc;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+** Configure the session object passed as the first argument.
|
|
|
+*/
|
|
|
+SQLITE_API int sqlite3session_object_config(sqlite3_session *pSession, int op, void *pArg){
|
|
|
+ int rc = SQLITE_OK;
|
|
|
+ switch( op ){
|
|
|
+ case SQLITE_SESSION_OBJCONFIG_SIZE: {
|
|
|
+ int iArg = *(int*)pArg;
|
|
|
+ if( iArg>=0 ){
|
|
|
+ if( pSession->pTable ){
|
|
|
+ rc = SQLITE_MISUSE;
|
|
|
+ }else{
|
|
|
+ pSession->bEnableSize = (iArg!=0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ *(int*)pArg = pSession->bEnableSize;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ default:
|
|
|
+ rc = SQLITE_MISUSE;
|
|
|
+ }
|
|
|
+
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+** Return the maximum size of sqlite3session_changeset() output.
|
|
|
+*/
|
|
|
+SQLITE_API sqlite3_int64 sqlite3session_changeset_size(sqlite3_session *pSession){
|
|
|
+ return pSession->nMaxChangesetSize;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
** Do the work for either sqlite3changeset_start() or start_strm().
|
|
|
*/
|
|
|
@@ -221539,6 +221767,7 @@ static int sqlite3Fts5ExprInit(Fts5Global *pGlobal, sqlite3 *db){
|
|
|
}
|
|
|
#else
|
|
|
int rc = SQLITE_OK;
|
|
|
+ UNUSED_PARAM2(pGlobal,db);
|
|
|
#endif
|
|
|
|
|
|
/* Avoid warnings indicating that sqlite3Fts5ParserTrace() and
|
|
|
@@ -229075,6 +229304,7 @@ static int sqlite3Fts5IndexInit(sqlite3 *db){
|
|
|
return rc;
|
|
|
#else
|
|
|
return SQLITE_OK;
|
|
|
+ UNUSED_PARAM(db);
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
@@ -231879,7 +232109,7 @@ static void fts5SourceIdFunc(
|
|
|
){
|
|
|
assert( nArg==0 );
|
|
|
UNUSED_PARAM2(nArg, apUnused);
|
|
|
- sqlite3_result_text(pCtx, "fts5: 2021-04-19 20:36:13 3e863cd09355abd80c1053d6d4dabb55841f806e3c418f923d67d36bf8313cb0", -1, SQLITE_TRANSIENT);
|
|
|
+ sqlite3_result_text(pCtx, "fts5: 2021-04-23 00:59:38 fac12115a994a1b4347586e68faf38895ee9cb588eaa84c6f71cf9afd4c488b4", -1, SQLITE_TRANSIENT);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
@@ -245388,9 +245618,9 @@ SQLITE_API int sqlite3_uuid_init(
|
|
|
}
|
|
|
|
|
|
/************** End of uuid.c ************************************************/
|
|
|
-#if __LINE__!=245391
|
|
|
+#if __LINE__!=245621
|
|
|
#undef SQLITE_SOURCE_ID
|
|
|
-#define SQLITE_SOURCE_ID "2021-04-20 22:48:25 ca70c8ac72c9fe3b92f8e63151229bebdccc769c5c4d603ad4f466dc4bcdalt2"
|
|
|
+#define SQLITE_SOURCE_ID "2021-04-23 00:59:38 fac12115a994a1b4347586e68faf38895ee9cb588eaa84c6f71cf9afd4c4alt2"
|
|
|
#endif
|
|
|
/* Return the source-id for this library */
|
|
|
SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
|