| Index: third_party/sqlite/src/src/vdbeblob.c
|
| diff --git a/third_party/sqlite/src/src/vdbeblob.c b/third_party/sqlite/src/src/vdbeblob.c
|
| index a4718efacc78a4c56bd856dd95bbd1d55d4e6e66..5692c1c606d77001ac7f62be5788ef23352b9671 100644
|
| --- a/third_party/sqlite/src/src/vdbeblob.c
|
| +++ b/third_party/sqlite/src/src/vdbeblob.c
|
| @@ -23,13 +23,14 @@
|
| */
|
| typedef struct Incrblob Incrblob;
|
| struct Incrblob {
|
| - int flags; /* Copy of "flags" passed to sqlite3_blob_open() */
|
| int nByte; /* Size of open blob, in bytes */
|
| int iOffset; /* Byte offset of blob in cursor data */
|
| - int iCol; /* Table column this handle is open on */
|
| + u16 iCol; /* Table column this handle is open on */
|
| BtCursor *pCsr; /* Cursor pointing at blob row */
|
| sqlite3_stmt *pStmt; /* Statement holding cursor open */
|
| sqlite3 *db; /* The associated database */
|
| + char *zDb; /* Database name */
|
| + Table *pTab; /* Table object */
|
| };
|
|
|
|
|
| @@ -55,17 +56,27 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){
|
| char *zErr = 0; /* Error message */
|
| Vdbe *v = (Vdbe *)p->pStmt;
|
|
|
| - /* Set the value of the SQL statements only variable to integer iRow.
|
| - ** This is done directly instead of using sqlite3_bind_int64() to avoid
|
| - ** triggering asserts related to mutexes.
|
| + /* Set the value of register r[1] in the SQL statement to integer iRow.
|
| + ** This is done directly as a performance optimization
|
| */
|
| - assert( v->aVar[0].flags&MEM_Int );
|
| - v->aVar[0].u.i = iRow;
|
| -
|
| - rc = sqlite3_step(p->pStmt);
|
| + v->aMem[1].flags = MEM_Int;
|
| + v->aMem[1].u.i = iRow;
|
| +
|
| + /* If the statement has been run before (and is paused at the OP_ResultRow)
|
| + ** then back it up to the point where it does the OP_SeekRowid. This could
|
| + ** have been down with an extra OP_Goto, but simply setting the program
|
| + ** counter is faster. */
|
| + if( v->pc>3 ){
|
| + v->pc = 3;
|
| + rc = sqlite3VdbeExec(v);
|
| + }else{
|
| + rc = sqlite3_step(p->pStmt);
|
| + }
|
| if( rc==SQLITE_ROW ){
|
| VdbeCursor *pC = v->apCsr[0];
|
| - u32 type = pC->aType[p->iCol];
|
| + u32 type = pC->nHdrParsed>p->iCol ? pC->aType[p->iCol] : 0;
|
| + testcase( pC->nHdrParsed==p->iCol );
|
| + testcase( pC->nHdrParsed==p->iCol+1 );
|
| if( type<12 ){
|
| zErr = sqlite3MPrintf(p->db, "cannot open value of type %s",
|
| type==0?"null": type==7?"real": "integer"
|
| @@ -110,43 +121,11 @@ int sqlite3_blob_open(
|
| const char *zTable, /* The table containing the blob */
|
| const char *zColumn, /* The column containing the blob */
|
| sqlite_int64 iRow, /* The row containing the glob */
|
| - int flags, /* True -> read/write access, false -> read-only */
|
| + int wrFlag, /* True -> read/write access, false -> read-only */
|
| sqlite3_blob **ppBlob /* Handle for accessing the blob returned here */
|
| ){
|
| int nAttempt = 0;
|
| int iCol; /* Index of zColumn in row-record */
|
| -
|
| - /* This VDBE program seeks a btree cursor to the identified
|
| - ** db/table/row entry. The reason for using a vdbe program instead
|
| - ** of writing code to use the b-tree layer directly is that the
|
| - ** vdbe program will take advantage of the various transaction,
|
| - ** locking and error handling infrastructure built into the vdbe.
|
| - **
|
| - ** After seeking the cursor, the vdbe executes an OP_ResultRow.
|
| - ** Code external to the Vdbe then "borrows" the b-tree cursor and
|
| - ** uses it to implement the blob_read(), blob_write() and
|
| - ** blob_bytes() functions.
|
| - **
|
| - ** The sqlite3_blob_close() function finalizes the vdbe program,
|
| - ** which closes the b-tree cursor and (possibly) commits the
|
| - ** transaction.
|
| - */
|
| - static const int iLn = VDBE_OFFSET_LINENO(4);
|
| - static const VdbeOpList openBlob[] = {
|
| - /* {OP_Transaction, 0, 0, 0}, // 0: Inserted separately */
|
| - {OP_TableLock, 0, 0, 0}, /* 1: Acquire a read or write lock */
|
| - /* One of the following two instructions is replaced by an OP_Noop. */
|
| - {OP_OpenRead, 0, 0, 0}, /* 2: Open cursor 0 for reading */
|
| - {OP_OpenWrite, 0, 0, 0}, /* 3: Open cursor 0 for read/write */
|
| - {OP_Variable, 1, 1, 1}, /* 4: Push the rowid to the stack */
|
| - {OP_NotExists, 0, 10, 1}, /* 5: Seek the cursor */
|
| - {OP_Column, 0, 0, 1}, /* 6 */
|
| - {OP_ResultRow, 1, 0, 0}, /* 7 */
|
| - {OP_Goto, 0, 4, 0}, /* 8 */
|
| - {OP_Close, 0, 0, 0}, /* 9 */
|
| - {OP_Halt, 0, 0, 0}, /* 10 */
|
| - };
|
| -
|
| int rc = SQLITE_OK;
|
| char *zErr = 0;
|
| Table *pTab;
|
| @@ -164,7 +143,7 @@ int sqlite3_blob_open(
|
| return SQLITE_MISUSE_BKPT;
|
| }
|
| #endif
|
| - flags = !!flags; /* flags = (flags ? 1 : 0); */
|
| + wrFlag = !!wrFlag; /* wrFlag = (wrFlag ? 1 : 0); */
|
|
|
| sqlite3_mutex_enter(db->mutex);
|
|
|
| @@ -205,6 +184,8 @@ int sqlite3_blob_open(
|
| sqlite3BtreeLeaveAll(db);
|
| goto blob_open_out;
|
| }
|
| + pBlob->pTab = pTab;
|
| + pBlob->zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName;
|
|
|
| /* Now search pTab for the exact column. */
|
| for(iCol=0; iCol<pTab->nCol; iCol++) {
|
| @@ -222,9 +203,8 @@ int sqlite3_blob_open(
|
|
|
| /* If the value is being opened for writing, check that the
|
| ** column is not indexed, and that it is not part of a foreign key.
|
| - ** It is against the rules to open a column to which either of these
|
| - ** descriptions applies for writing. */
|
| - if( flags ){
|
| + */
|
| + if( wrFlag ){
|
| const char *zFault = 0;
|
| Index *pIdx;
|
| #ifndef SQLITE_OMIT_FOREIGN_KEY
|
| @@ -265,60 +245,89 @@ int sqlite3_blob_open(
|
| pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(pParse);
|
| assert( pBlob->pStmt || db->mallocFailed );
|
| if( pBlob->pStmt ){
|
| +
|
| + /* This VDBE program seeks a btree cursor to the identified
|
| + ** db/table/row entry. The reason for using a vdbe program instead
|
| + ** of writing code to use the b-tree layer directly is that the
|
| + ** vdbe program will take advantage of the various transaction,
|
| + ** locking and error handling infrastructure built into the vdbe.
|
| + **
|
| + ** After seeking the cursor, the vdbe executes an OP_ResultRow.
|
| + ** Code external to the Vdbe then "borrows" the b-tree cursor and
|
| + ** uses it to implement the blob_read(), blob_write() and
|
| + ** blob_bytes() functions.
|
| + **
|
| + ** The sqlite3_blob_close() function finalizes the vdbe program,
|
| + ** which closes the b-tree cursor and (possibly) commits the
|
| + ** transaction.
|
| + */
|
| + static const int iLn = VDBE_OFFSET_LINENO(2);
|
| + static const VdbeOpList openBlob[] = {
|
| + {OP_TableLock, 0, 0, 0}, /* 0: Acquire a read or write lock */
|
| + {OP_OpenRead, 0, 0, 0}, /* 1: Open a cursor */
|
| + /* blobSeekToRow() will initialize r[1] to the desired rowid */
|
| + {OP_NotExists, 0, 5, 1}, /* 2: Seek the cursor to rowid=r[1] */
|
| + {OP_Column, 0, 0, 1}, /* 3 */
|
| + {OP_ResultRow, 1, 0, 0}, /* 4 */
|
| + {OP_Halt, 0, 0, 0}, /* 5 */
|
| + };
|
| Vdbe *v = (Vdbe *)pBlob->pStmt;
|
| int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
| + VdbeOp *aOp;
|
|
|
| -
|
| - sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, flags,
|
| + sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag,
|
| pTab->pSchema->schema_cookie,
|
| pTab->pSchema->iGeneration);
|
| sqlite3VdbeChangeP5(v, 1);
|
| - sqlite3VdbeAddOpList(v, ArraySize(openBlob), openBlob, iLn);
|
| + aOp = sqlite3VdbeAddOpList(v, ArraySize(openBlob), openBlob, iLn);
|
|
|
| /* Make sure a mutex is held on the table to be accessed */
|
| sqlite3VdbeUsesBtree(v, iDb);
|
|
|
| - /* Configure the OP_TableLock instruction */
|
| + if( db->mallocFailed==0 ){
|
| + assert( aOp!=0 );
|
| + /* Configure the OP_TableLock instruction */
|
| #ifdef SQLITE_OMIT_SHARED_CACHE
|
| - sqlite3VdbeChangeToNoop(v, 1);
|
| + aOp[0].opcode = OP_Noop;
|
| #else
|
| - sqlite3VdbeChangeP1(v, 1, iDb);
|
| - sqlite3VdbeChangeP2(v, 1, pTab->tnum);
|
| - sqlite3VdbeChangeP3(v, 1, flags);
|
| - sqlite3VdbeChangeP4(v, 1, pTab->zName, P4_TRANSIENT);
|
| + aOp[0].p1 = iDb;
|
| + aOp[0].p2 = pTab->tnum;
|
| + aOp[0].p3 = wrFlag;
|
| + sqlite3VdbeChangeP4(v, 1, pTab->zName, P4_TRANSIENT);
|
| + }
|
| + if( db->mallocFailed==0 ){
|
| #endif
|
|
|
| - /* Remove either the OP_OpenWrite or OpenRead. Set the P2
|
| - ** parameter of the other to pTab->tnum. */
|
| - sqlite3VdbeChangeToNoop(v, 3 - flags);
|
| - sqlite3VdbeChangeP2(v, 2 + flags, pTab->tnum);
|
| - sqlite3VdbeChangeP3(v, 2 + flags, iDb);
|
| -
|
| - /* Configure the number of columns. Configure the cursor to
|
| - ** think that the table has one more column than it really
|
| - ** does. An OP_Column to retrieve this imaginary column will
|
| - ** always return an SQL NULL. This is useful because it means
|
| - ** we can invoke OP_Column to fill in the vdbe cursors type
|
| - ** and offset cache without causing any IO.
|
| - */
|
| - sqlite3VdbeChangeP4(v, 2+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32);
|
| - sqlite3VdbeChangeP2(v, 6, pTab->nCol);
|
| - if( !db->mallocFailed ){
|
| - pParse->nVar = 1;
|
| + /* Remove either the OP_OpenWrite or OpenRead. Set the P2
|
| + ** parameter of the other to pTab->tnum. */
|
| + if( wrFlag ) aOp[1].opcode = OP_OpenWrite;
|
| + aOp[1].p2 = pTab->tnum;
|
| + aOp[1].p3 = iDb;
|
| +
|
| + /* Configure the number of columns. Configure the cursor to
|
| + ** think that the table has one more column than it really
|
| + ** does. An OP_Column to retrieve this imaginary column will
|
| + ** always return an SQL NULL. This is useful because it means
|
| + ** we can invoke OP_Column to fill in the vdbe cursors type
|
| + ** and offset cache without causing any IO.
|
| + */
|
| + aOp[1].p4type = P4_INT32;
|
| + aOp[1].p4.i = pTab->nCol+1;
|
| + aOp[3].p2 = pTab->nCol;
|
| +
|
| + pParse->nVar = 0;
|
| pParse->nMem = 1;
|
| pParse->nTab = 1;
|
| sqlite3VdbeMakeReady(v, pParse);
|
| }
|
| }
|
|
|
| - pBlob->flags = flags;
|
| pBlob->iCol = iCol;
|
| pBlob->db = db;
|
| sqlite3BtreeLeaveAll(db);
|
| if( db->mallocFailed ){
|
| goto blob_open_out;
|
| }
|
| - sqlite3_bind_int64(pBlob->pStmt, 1, iRow);
|
| rc = blobSeekToRow(pBlob, iRow, &zErr);
|
| } while( (++nAttempt)<SQLITE_MAX_SCHEMA_RETRY && rc==SQLITE_SCHEMA );
|
|
|
| @@ -393,6 +402,30 @@ static int blobReadWrite(
|
| */
|
| assert( db == v->db );
|
| sqlite3BtreeEnterCursor(p->pCsr);
|
| +
|
| +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
| + if( xCall==sqlite3BtreePutData && db->xPreUpdateCallback ){
|
| + /* If a pre-update hook is registered and this is a write cursor,
|
| + ** invoke it here.
|
| + **
|
| + ** TODO: The preupdate-hook is passed SQLITE_DELETE, even though this
|
| + ** operation should really be an SQLITE_UPDATE. This is probably
|
| + ** incorrect, but is convenient because at this point the new.* values
|
| + ** are not easily obtainable. And for the sessions module, an
|
| + ** SQLITE_UPDATE where the PK columns do not change is handled in the
|
| + ** same way as an SQLITE_DELETE (the SQLITE_DELETE code is actually
|
| + ** slightly more efficient). Since you cannot write to a PK column
|
| + ** using the incremental-blob API, this works. For the sessions module
|
| + ** anyhow.
|
| + */
|
| + sqlite3_int64 iKey;
|
| + iKey = sqlite3BtreeIntegerKey(p->pCsr);
|
| + sqlite3VdbePreUpdateHook(
|
| + v, v->apCsr[0], SQLITE_DELETE, p->zDb, p->pTab, iKey, -1
|
| + );
|
| + }
|
| +#endif
|
| +
|
| rc = xCall(p->pCsr, iOffset+p->iOffset, n, z);
|
| sqlite3BtreeLeaveCursor(p->pCsr);
|
| if( rc==SQLITE_ABORT ){
|
| @@ -412,7 +445,7 @@ static int blobReadWrite(
|
| ** Read data from a blob handle.
|
| */
|
| int sqlite3_blob_read(sqlite3_blob *pBlob, void *z, int n, int iOffset){
|
| - return blobReadWrite(pBlob, z, n, iOffset, sqlite3BtreeData);
|
| + return blobReadWrite(pBlob, z, n, iOffset, sqlite3BtreePayloadChecked);
|
| }
|
|
|
| /*
|
|
|