xref: /sqlite-3.40.0/src/vdbeblob.c (revision c692df27)
1b4e9af9fSdanielk1977 /*
2b4e9af9fSdanielk1977 ** 2007 May 1
3b4e9af9fSdanielk1977 **
4b4e9af9fSdanielk1977 ** The author disclaims copyright to this source code.  In place of
5b4e9af9fSdanielk1977 ** a legal notice, here is a blessing:
6b4e9af9fSdanielk1977 **
7b4e9af9fSdanielk1977 **    May you do good and not evil.
8b4e9af9fSdanielk1977 **    May you find forgiveness for yourself and forgive others.
9b4e9af9fSdanielk1977 **    May you share freely, never taking more than you give.
10b4e9af9fSdanielk1977 **
11b4e9af9fSdanielk1977 *************************************************************************
12b4e9af9fSdanielk1977 **
1316a9b836Sdrh ** This file contains code used to implement incremental BLOB I/O.
14b4e9af9fSdanielk1977 */
15b4e9af9fSdanielk1977 
16b4e9af9fSdanielk1977 #include "sqliteInt.h"
17b4e9af9fSdanielk1977 #include "vdbeInt.h"
18b4e9af9fSdanielk1977 
19b4e9af9fSdanielk1977 #ifndef SQLITE_OMIT_INCRBLOB
20b4e9af9fSdanielk1977 
21b4e9af9fSdanielk1977 /*
22b4e9af9fSdanielk1977 ** Valid sqlite3_blob* handles point to Incrblob structures.
23b4e9af9fSdanielk1977 */
24b4e9af9fSdanielk1977 typedef struct Incrblob Incrblob;
25b4e9af9fSdanielk1977 struct Incrblob {
26b4e9af9fSdanielk1977   int nByte;              /* Size of open blob, in bytes */
27b4e9af9fSdanielk1977   int iOffset;            /* Byte offset of blob in cursor data */
2836cae856Sdrh   u16 iCol;               /* Table column this handle is open on */
29b4e9af9fSdanielk1977   BtCursor *pCsr;         /* Cursor pointing at blob row */
30b4e9af9fSdanielk1977   sqlite3_stmt *pStmt;    /* Statement holding cursor open */
31605264d2Sdrh   sqlite3 *db;            /* The associated database */
32e437ca5eSdan   char *zDb;              /* Database name */
33e437ca5eSdan   Table *pTab;            /* Table object */
34b4e9af9fSdanielk1977 };
35b4e9af9fSdanielk1977 
364e76cc36Sdan 
37e3d82a87Sdan /*
38e3d82a87Sdan ** This function is used by both blob_open() and blob_reopen(). It seeks
39e3d82a87Sdan ** the b-tree cursor associated with blob handle p to point to row iRow.
40e3d82a87Sdan ** If successful, SQLITE_OK is returned and subsequent calls to
41e3d82a87Sdan ** sqlite3_blob_read() or sqlite3_blob_write() access the specified row.
42e3d82a87Sdan **
43e3d82a87Sdan ** If an error occurs, or if the specified row does not exist or does not
44e3d82a87Sdan ** contain a value of type TEXT or BLOB in the column nominated when the
45e3d82a87Sdan ** blob handle was opened, then an error code is returned and *pzErr may
46e3d82a87Sdan ** be set to point to a buffer containing an error message. It is the
47e3d82a87Sdan ** responsibility of the caller to free the error message buffer using
48e3d82a87Sdan ** sqlite3DbFree().
49e3d82a87Sdan **
50e3d82a87Sdan ** If an error does occur, then the b-tree cursor is closed. All subsequent
51e3d82a87Sdan ** calls to sqlite3_blob_read(), blob_write() or blob_reopen() will
52e3d82a87Sdan ** immediately return SQLITE_ABORT.
53e3d82a87Sdan */
blobSeekToRow(Incrblob * p,sqlite3_int64 iRow,char ** pzErr)544e76cc36Sdan static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){
554e76cc36Sdan   int rc;                         /* Error code */
564e76cc36Sdan   char *zErr = 0;                 /* Error message */
574e76cc36Sdan   Vdbe *v = (Vdbe *)p->pStmt;
584e76cc36Sdan 
594df65fc2Sdrh   /* Set the value of register r[1] in the SQL statement to integer iRow.
604df65fc2Sdrh   ** This is done directly as a performance optimization
61e3d82a87Sdan   */
624df65fc2Sdrh   v->aMem[1].flags = MEM_Int;
634df65fc2Sdrh   v->aMem[1].u.i = iRow;
644df65fc2Sdrh 
654df65fc2Sdrh   /* If the statement has been run before (and is paused at the OP_ResultRow)
66952523f6Sdan   ** then back it up to the point where it does the OP_NotExists.  This could
674df65fc2Sdrh   ** have been down with an extra OP_Goto, but simply setting the program
684df65fc2Sdrh   ** counter is faster. */
69952523f6Sdan   if( v->pc>4 ){
70952523f6Sdan     v->pc = 4;
71952523f6Sdan     assert( v->aOp[v->pc].opcode==OP_NotExists );
72baf5dec7Sdrh     rc = sqlite3VdbeExec(v);
733b2936faSdrh   }else{
74e3d82a87Sdan     rc = sqlite3_step(p->pStmt);
753b2936faSdrh   }
764e76cc36Sdan   if( rc==SQLITE_ROW ){
7714da87f8Sdrh     VdbeCursor *pC = v->apCsr[0];
783ab4ffceSdrh     u32 type;
793ab4ffceSdrh     assert( pC!=0 );
803ab4ffceSdrh     assert( pC->eCurType==CURTYPE_BTREE );
813ab4ffceSdrh     type = pC->nHdrParsed>p->iCol ? pC->aType[p->iCol] : 0;
82210b0d0eSdrh     testcase( pC->nHdrParsed==p->iCol );
83210b0d0eSdrh     testcase( pC->nHdrParsed==p->iCol+1 );
844e76cc36Sdan     if( type<12 ){
854e76cc36Sdan       zErr = sqlite3MPrintf(p->db, "cannot open value of type %s",
864e76cc36Sdan           type==0?"null": type==7?"real": "integer"
874e76cc36Sdan       );
884e76cc36Sdan       rc = SQLITE_ERROR;
894e76cc36Sdan       sqlite3_finalize(p->pStmt);
904e76cc36Sdan       p->pStmt = 0;
914e76cc36Sdan     }else{
9214da87f8Sdrh       p->iOffset = pC->aType[p->iCol + pC->nField];
934e76cc36Sdan       p->nByte = sqlite3VdbeSerialTypeLen(type);
94c960dcbaSdrh       p->pCsr =  pC->uc.pCursor;
955a500afdSdan       sqlite3BtreeIncrblobCursor(p->pCsr);
964e76cc36Sdan     }
974e76cc36Sdan   }
984e76cc36Sdan 
994e76cc36Sdan   if( rc==SQLITE_ROW ){
1004e76cc36Sdan     rc = SQLITE_OK;
1014e76cc36Sdan   }else if( p->pStmt ){
1024e76cc36Sdan     rc = sqlite3_finalize(p->pStmt);
1034e76cc36Sdan     p->pStmt = 0;
1044e76cc36Sdan     if( rc==SQLITE_OK ){
1054e76cc36Sdan       zErr = sqlite3MPrintf(p->db, "no such rowid: %lld", iRow);
1064e76cc36Sdan       rc = SQLITE_ERROR;
1074e76cc36Sdan     }else{
1084e76cc36Sdan       zErr = sqlite3MPrintf(p->db, "%s", sqlite3_errmsg(p->db));
1094e76cc36Sdan     }
1104e76cc36Sdan   }
1114e76cc36Sdan 
1124e76cc36Sdan   assert( rc!=SQLITE_OK || zErr==0 );
1134e76cc36Sdan   assert( rc!=SQLITE_ROW && rc!=SQLITE_DONE );
1144e76cc36Sdan 
1154e76cc36Sdan   *pzErr = zErr;
1164e76cc36Sdan   return rc;
1174e76cc36Sdan }
1184e76cc36Sdan 
119b4e9af9fSdanielk1977 /*
120b4e9af9fSdanielk1977 ** Open a blob handle.
121b4e9af9fSdanielk1977 */
sqlite3_blob_open(sqlite3 * db,const char * zDb,const char * zTable,const char * zColumn,sqlite_int64 iRow,int wrFlag,sqlite3_blob ** ppBlob)122b4e9af9fSdanielk1977 int sqlite3_blob_open(
12316a9b836Sdrh   sqlite3* db,            /* The database connection */
12416a9b836Sdrh   const char *zDb,        /* The attached database containing the blob */
12516a9b836Sdrh   const char *zTable,     /* The table containing the blob */
12616a9b836Sdrh   const char *zColumn,    /* The column containing the blob */
12716a9b836Sdrh   sqlite_int64 iRow,      /* The row containing the glob */
12836cae856Sdrh   int wrFlag,             /* True -> read/write access, false -> read-only */
12916a9b836Sdrh   sqlite3_blob **ppBlob   /* Handle for accessing the blob returned here */
130b4e9af9fSdanielk1977 ){
131b4e9af9fSdanielk1977   int nAttempt = 0;
132b4e9af9fSdanielk1977   int iCol;               /* Index of zColumn in row-record */
1338cbadb02Sdanielk1977   int rc = SQLITE_OK;
1346ee700c3Sdrh   char *zErr = 0;
1356ee700c3Sdrh   Table *pTab;
13661c7f59cSdan   Incrblob *pBlob = 0;
1376903bf6dSdrh   Parse sParse;
138b4e9af9fSdanielk1977 
1399ca95730Sdrh #ifdef SQLITE_ENABLE_API_ARMOR
140d10d18daSdrh   if( ppBlob==0 ){
141d10d18daSdrh     return SQLITE_MISUSE_BKPT;
142d10d18daSdrh   }
143d10d18daSdrh #endif
144d10d18daSdrh   *ppBlob = 0;
145d10d18daSdrh #ifdef SQLITE_ENABLE_API_ARMOR
146d10d18daSdrh   if( !sqlite3SafetyCheckOk(db) || zTable==0 ){
1479ca95730Sdrh     return SQLITE_MISUSE_BKPT;
1489ca95730Sdrh   }
1499ca95730Sdrh #endif
15036cae856Sdrh   wrFlag = !!wrFlag;                /* wrFlag = (wrFlag ? 1 : 0); */
1514e76cc36Sdan 
152605264d2Sdrh   sqlite3_mutex_enter(db->mutex);
1534e76cc36Sdan 
1544e76cc36Sdan   pBlob = (Incrblob *)sqlite3DbMallocZero(db, sizeof(Incrblob));
155*c692df27Sdrh   while(1){
156*c692df27Sdrh     sqlite3ParseObjectInit(&sParse,db);
1576903bf6dSdrh     if( !pBlob ) goto blob_open_out;
15861c7f59cSdan     sqlite3DbFree(db, zErr);
15961c7f59cSdan     zErr = 0;
160b4e9af9fSdanielk1977 
161ff0587c6Sdrh     sqlite3BtreeEnterAll(db);
1626903bf6dSdrh     pTab = sqlite3LocateTable(&sParse, 0, zTable, zDb);
16336961ed2Sdanielk1977     if( pTab && IsVirtual(pTab) ){
16436961ed2Sdanielk1977       pTab = 0;
1656903bf6dSdrh       sqlite3ErrorMsg(&sParse, "cannot open virtual table: %s", zTable);
16636961ed2Sdanielk1977     }
16763f0eedfSdrh     if( pTab && !HasRowid(pTab) ){
16863f0eedfSdrh       pTab = 0;
1696903bf6dSdrh       sqlite3ErrorMsg(&sParse, "cannot open table without rowid: %s", zTable);
17063f0eedfSdrh     }
17136961ed2Sdanielk1977 #ifndef SQLITE_OMIT_VIEW
172f38524d2Sdrh     if( pTab && IsView(pTab) ){
17336961ed2Sdanielk1977       pTab = 0;
1746903bf6dSdrh       sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable);
17536961ed2Sdanielk1977     }
17636961ed2Sdanielk1977 #endif
177b4e9af9fSdanielk1977     if( !pTab ){
1786903bf6dSdrh       if( sParse.zErrMsg ){
1796ee700c3Sdrh         sqlite3DbFree(db, zErr);
1806903bf6dSdrh         zErr = sParse.zErrMsg;
1816903bf6dSdrh         sParse.zErrMsg = 0;
1828cbadb02Sdanielk1977       }
1838cbadb02Sdanielk1977       rc = SQLITE_ERROR;
184ff0587c6Sdrh       sqlite3BtreeLeaveAll(db);
185b4e9af9fSdanielk1977       goto blob_open_out;
186b4e9af9fSdanielk1977     }
187e437ca5eSdan     pBlob->pTab = pTab;
18869c33826Sdrh     pBlob->zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName;
189b4e9af9fSdanielk1977 
190b4e9af9fSdanielk1977     /* Now search pTab for the exact column. */
191b4e9af9fSdanielk1977     for(iCol=0; iCol<pTab->nCol; iCol++) {
192cf9d36d1Sdrh       if( sqlite3StrICmp(pTab->aCol[iCol].zCnName, zColumn)==0 ){
193b4e9af9fSdanielk1977         break;
194b4e9af9fSdanielk1977       }
195b4e9af9fSdanielk1977     }
196b4e9af9fSdanielk1977     if( iCol==pTab->nCol ){
1976ee700c3Sdrh       sqlite3DbFree(db, zErr);
1986ee700c3Sdrh       zErr = sqlite3MPrintf(db, "no such column: \"%s\"", zColumn);
199b4e9af9fSdanielk1977       rc = SQLITE_ERROR;
200ff0587c6Sdrh       sqlite3BtreeLeaveAll(db);
201b4e9af9fSdanielk1977       goto blob_open_out;
202b4e9af9fSdanielk1977     }
203b4e9af9fSdanielk1977 
204f1819244Sdanielk1977     /* If the value is being opened for writing, check that the
2051da40a38Sdan     ** column is not indexed, and that it is not part of a foreign key.
20636cae856Sdrh     */
20736cae856Sdrh     if( wrFlag ){
2081da40a38Sdan       const char *zFault = 0;
209f1819244Sdanielk1977       Index *pIdx;
2101da40a38Sdan #ifndef SQLITE_OMIT_FOREIGN_KEY
2111da40a38Sdan       if( db->flags&SQLITE_ForeignKeys ){
2121316700eSdan         /* Check that the column is not part of an FK child key definition. It
2131316700eSdan         ** is not necessary to check if it is part of a parent key, as parent
2141316700eSdan         ** key columns must be indexed. The check below will pick up this
2151316700eSdan         ** case.  */
2161da40a38Sdan         FKey *pFKey;
21778b2fa86Sdrh         assert( IsOrdinaryTable(pTab) );
218f38524d2Sdrh         for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){
2191da40a38Sdan           int j;
2201da40a38Sdan           for(j=0; j<pFKey->nCol; j++){
2211da40a38Sdan             if( pFKey->aCol[j].iFrom==iCol ){
2221da40a38Sdan               zFault = "foreign key";
2231da40a38Sdan             }
2241da40a38Sdan           }
2251da40a38Sdan         }
2261da40a38Sdan       }
2271da40a38Sdan #endif
228f1819244Sdanielk1977       for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
229f1819244Sdanielk1977         int j;
230bbbdc83bSdrh         for(j=0; j<pIdx->nKeyCol; j++){
2311f9ca2c8Sdrh           /* FIXME: Be smarter about indexes that use expressions */
2324b92f98cSdrh           if( pIdx->aiColumn[j]==iCol || pIdx->aiColumn[j]==XN_EXPR ){
2331da40a38Sdan             zFault = "indexed";
2341da40a38Sdan           }
2351da40a38Sdan         }
2361da40a38Sdan       }
2371da40a38Sdan       if( zFault ){
2386ee700c3Sdrh         sqlite3DbFree(db, zErr);
2391da40a38Sdan         zErr = sqlite3MPrintf(db, "cannot open %s column for writing", zFault);
240f1819244Sdanielk1977         rc = SQLITE_ERROR;
241ff0587c6Sdrh         sqlite3BtreeLeaveAll(db);
242f1819244Sdanielk1977         goto blob_open_out;
243f1819244Sdanielk1977       }
244f1819244Sdanielk1977     }
245f1819244Sdanielk1977 
2466903bf6dSdrh     pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(&sParse);
24761c7f59cSdan     assert( pBlob->pStmt || db->mallocFailed );
24861c7f59cSdan     if( pBlob->pStmt ){
2492ce1865dSdrh 
2502ce1865dSdrh       /* This VDBE program seeks a btree cursor to the identified
2512ce1865dSdrh       ** db/table/row entry. The reason for using a vdbe program instead
2522ce1865dSdrh       ** of writing code to use the b-tree layer directly is that the
2532ce1865dSdrh       ** vdbe program will take advantage of the various transaction,
2542ce1865dSdrh       ** locking and error handling infrastructure built into the vdbe.
2552ce1865dSdrh       **
2562ce1865dSdrh       ** After seeking the cursor, the vdbe executes an OP_ResultRow.
2572ce1865dSdrh       ** Code external to the Vdbe then "borrows" the b-tree cursor and
2582ce1865dSdrh       ** uses it to implement the blob_read(), blob_write() and
2592ce1865dSdrh       ** blob_bytes() functions.
2602ce1865dSdrh       **
2612ce1865dSdrh       ** The sqlite3_blob_close() function finalizes the vdbe program,
2622ce1865dSdrh       ** which closes the b-tree cursor and (possibly) commits the
2632ce1865dSdrh       ** transaction.
2642ce1865dSdrh       */
2651b32554bSdrh       static const int iLn = VDBE_OFFSET_LINENO(2);
2662ce1865dSdrh       static const VdbeOpList openBlob[] = {
2671b32554bSdrh         {OP_TableLock,      0, 0, 0},  /* 0: Acquire a read or write lock */
2681b32554bSdrh         {OP_OpenRead,       0, 0, 0},  /* 1: Open a cursor */
2694df65fc2Sdrh         /* blobSeekToRow() will initialize r[1] to the desired rowid */
2704df65fc2Sdrh         {OP_NotExists,      0, 5, 1},  /* 2: Seek the cursor to rowid=r[1] */
2714df65fc2Sdrh         {OP_Column,         0, 0, 1},  /* 3  */
2724df65fc2Sdrh         {OP_ResultRow,      1, 0, 0},  /* 4  */
2734df65fc2Sdrh         {OP_Halt,           0, 0, 0},  /* 5  */
2742ce1865dSdrh       };
27561c7f59cSdan       Vdbe *v = (Vdbe *)pBlob->pStmt;
276b4e9af9fSdanielk1977       int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
2772ce1865dSdrh       VdbeOp *aOp;
278b22f7c83Sdrh 
27936cae856Sdrh       sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag,
280b22f7c83Sdrh                            pTab->pSchema->schema_cookie,
281b22f7c83Sdrh                            pTab->pSchema->iGeneration);
282b22f7c83Sdrh       sqlite3VdbeChangeP5(v, 1);
28355965619Sdrh       assert( sqlite3VdbeCurrentAddr(v)==2 || db->mallocFailed );
2842ce1865dSdrh       aOp = sqlite3VdbeAddOpList(v, ArraySize(openBlob), openBlob, iLn);
285b4e9af9fSdanielk1977 
286ff0587c6Sdrh       /* Make sure a mutex is held on the table to be accessed */
287fb98264aSdrh       sqlite3VdbeUsesBtree(v, iDb);
288ff0587c6Sdrh 
2892ce1865dSdrh       if( db->mallocFailed==0 ){
2902ce1865dSdrh         assert( aOp!=0 );
29196d48e96Sdanielk1977         /* Configure the OP_TableLock instruction */
292c548b783Sdan #ifdef SQLITE_OMIT_SHARED_CACHE
2932ce1865dSdrh         aOp[0].opcode = OP_Noop;
294c548b783Sdan #else
2952ce1865dSdrh         aOp[0].p1 = iDb;
2962ce1865dSdrh         aOp[0].p2 = pTab->tnum;
29736cae856Sdrh         aOp[0].p3 = wrFlag;
29855965619Sdrh         sqlite3VdbeChangeP4(v, 2, pTab->zName, P4_TRANSIENT);
2992ce1865dSdrh       }
3002ce1865dSdrh       if( db->mallocFailed==0 ){
301c548b783Sdan #endif
30296d48e96Sdanielk1977 
303f1819244Sdanielk1977         /* Remove either the OP_OpenWrite or OpenRead. Set the P2
30496d48e96Sdanielk1977         ** parameter of the other to pTab->tnum.  */
30536cae856Sdrh         if( wrFlag ) aOp[1].opcode = OP_OpenWrite;
306e617bc8cSdrh         aOp[1].p2 = pTab->tnum;
307e617bc8cSdrh         aOp[1].p3 = iDb;
308b4e9af9fSdanielk1977 
309d336e222Sdanielk1977         /* Configure the number of columns. Configure the cursor to
310b4e9af9fSdanielk1977         ** think that the table has one more column than it really
311b4e9af9fSdanielk1977         ** does. An OP_Column to retrieve this imaginary column will
312b4e9af9fSdanielk1977         ** always return an SQL NULL. This is useful because it means
313b4e9af9fSdanielk1977         ** we can invoke OP_Column to fill in the vdbe cursors type
314b4e9af9fSdanielk1977         ** and offset cache without causing any IO.
315b4e9af9fSdanielk1977         */
316e617bc8cSdrh         aOp[1].p4type = P4_INT32;
317e617bc8cSdrh         aOp[1].p4.i = pTab->nCol+1;
3184df65fc2Sdrh         aOp[3].p2 = pTab->nCol;
3192ce1865dSdrh 
3206903bf6dSdrh         sParse.nVar = 0;
3216903bf6dSdrh         sParse.nMem = 1;
3226903bf6dSdrh         sParse.nTab = 1;
3236903bf6dSdrh         sqlite3VdbeMakeReady(v, &sParse);
324b4e9af9fSdanielk1977       }
32592d4d7a9Sdanielk1977     }
326b4e9af9fSdanielk1977 
3274e76cc36Sdan     pBlob->iCol = iCol;
328605264d2Sdrh     pBlob->db = db;
3294e76cc36Sdan     sqlite3BtreeLeaveAll(db);
3304e76cc36Sdan     if( db->mallocFailed ){
3314e76cc36Sdan       goto blob_open_out;
332b4e9af9fSdanielk1977     }
3334e76cc36Sdan     rc = blobSeekToRow(pBlob, iRow, &zErr);
334*c692df27Sdrh     if( (++nAttempt)>=SQLITE_MAX_SCHEMA_RETRY || rc!=SQLITE_SCHEMA ) break;
335*c692df27Sdrh     sqlite3ParseObjectReset(&sParse);
336*c692df27Sdrh   }
3374e76cc36Sdan 
338b4e9af9fSdanielk1977 blob_open_out:
3394e76cc36Sdan   if( rc==SQLITE_OK && db->mallocFailed==0 ){
3404e76cc36Sdan     *ppBlob = (sqlite3_blob *)pBlob;
3414e76cc36Sdan   }else{
3424e76cc36Sdan     if( pBlob && pBlob->pStmt ) sqlite3VdbeFinalize((Vdbe *)pBlob->pStmt);
3434e76cc36Sdan     sqlite3DbFree(db, pBlob);
344b4e9af9fSdanielk1977   }
34513f40da3Sdrh   sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr);
3466ee700c3Sdrh   sqlite3DbFree(db, zErr);
347*c692df27Sdrh   sqlite3ParseObjectReset(&sParse);
348605264d2Sdrh   rc = sqlite3ApiExit(db, rc);
349605264d2Sdrh   sqlite3_mutex_leave(db->mutex);
350605264d2Sdrh   return rc;
351b4e9af9fSdanielk1977 }
352b4e9af9fSdanielk1977 
353b4e9af9fSdanielk1977 /*
35416a9b836Sdrh ** Close a blob handle that was previously created using
35516a9b836Sdrh ** sqlite3_blob_open().
356b4e9af9fSdanielk1977 */
sqlite3_blob_close(sqlite3_blob * pBlob)357b4e9af9fSdanielk1977 int sqlite3_blob_close(sqlite3_blob *pBlob){
358b4e9af9fSdanielk1977   Incrblob *p = (Incrblob *)pBlob;
359605264d2Sdrh   int rc;
360d9da78a2Sdrh   sqlite3 *db;
361605264d2Sdrh 
36228414ee9Sdrh   if( p ){
363e464802dSdan     sqlite3_stmt *pStmt = p->pStmt;
364d9da78a2Sdrh     db = p->db;
365d9da78a2Sdrh     sqlite3_mutex_enter(db->mutex);
366d9da78a2Sdrh     sqlite3DbFree(db, p);
367d9da78a2Sdrh     sqlite3_mutex_leave(db->mutex);
368e464802dSdan     rc = sqlite3_finalize(pStmt);
36928414ee9Sdrh   }else{
37028414ee9Sdrh     rc = SQLITE_OK;
37128414ee9Sdrh   }
372605264d2Sdrh   return rc;
373b4e9af9fSdanielk1977 }
374b4e9af9fSdanielk1977 
375605264d2Sdrh /*
376605264d2Sdrh ** Perform a read or write operation on a blob
377605264d2Sdrh */
blobReadWrite(sqlite3_blob * pBlob,void * z,int n,int iOffset,int (* xCall)(BtCursor *,u32,u32,void *))378ee85813cSdrh static int blobReadWrite(
379dcbb5d3fSdanielk1977   sqlite3_blob *pBlob,
380dcbb5d3fSdanielk1977   void *z,
381dcbb5d3fSdanielk1977   int n,
382dcbb5d3fSdanielk1977   int iOffset,
383dcbb5d3fSdanielk1977   int (*xCall)(BtCursor*, u32, u32, void*)
384dcbb5d3fSdanielk1977 ){
385dcbb5d3fSdanielk1977   int rc;
386dcbb5d3fSdanielk1977   Incrblob *p = (Incrblob *)pBlob;
387605264d2Sdrh   Vdbe *v;
38828414ee9Sdrh   sqlite3 *db;
389dcbb5d3fSdanielk1977 
390413c3d36Sdrh   if( p==0 ) return SQLITE_MISUSE_BKPT;
39128414ee9Sdrh   db = p->db;
392605264d2Sdrh   sqlite3_mutex_enter(db->mutex);
393a8b3018dSdanielk1977   v = (Vdbe*)p->pStmt;
394dcbb5d3fSdanielk1977 
395d10d18daSdrh   if( n<0 || iOffset<0 || ((sqlite3_int64)iOffset+n)>p->nByte ){
396a8b3018dSdanielk1977     /* Request is out of range. Return a transient error. */
397a8b3018dSdanielk1977     rc = SQLITE_ERROR;
398a8b3018dSdanielk1977   }else if( v==0 ){
399605264d2Sdrh     /* If there is no statement handle, then the blob-handle has
400605264d2Sdrh     ** already been invalidated. Return SQLITE_ABORT in this case.
401605264d2Sdrh     */
402605264d2Sdrh     rc = SQLITE_ABORT;
403605264d2Sdrh   }else{
404dcbb5d3fSdanielk1977     /* Call either BtreeData() or BtreePutData(). If SQLITE_ABORT is
405dcbb5d3fSdanielk1977     ** returned, clean-up the statement handle.
406dcbb5d3fSdanielk1977     */
407605264d2Sdrh     assert( db == v->db );
408ff0587c6Sdrh     sqlite3BtreeEnterCursor(p->pCsr);
409e437ca5eSdan 
410e437ca5eSdan #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
4117f197bb7Sdrh     if( xCall==sqlite3BtreePutData && db->xPreUpdateCallback ){
412e437ca5eSdan       /* If a pre-update hook is registered and this is a write cursor,
413e437ca5eSdan       ** invoke it here.
414e437ca5eSdan       **
415e437ca5eSdan       ** TODO: The preupdate-hook is passed SQLITE_DELETE, even though this
416e437ca5eSdan       ** operation should really be an SQLITE_UPDATE. This is probably
417e437ca5eSdan       ** incorrect, but is convenient because at this point the new.* values
418e437ca5eSdan       ** are not easily obtainable. And for the sessions module, an
419e437ca5eSdan       ** SQLITE_UPDATE where the PK columns do not change is handled in the
420e437ca5eSdan       ** same way as an SQLITE_DELETE (the SQLITE_DELETE code is actually
421e437ca5eSdan       ** slightly more efficient). Since you cannot write to a PK column
422e437ca5eSdan       ** using the incremental-blob API, this works. For the sessions module
423e437ca5eSdan       ** anyhow.
424e437ca5eSdan       */
425e437ca5eSdan       sqlite3_int64 iKey;
426a7c90c42Sdrh       iKey = sqlite3BtreeIntegerKey(p->pCsr);
4273ab4ffceSdrh       assert( v->apCsr[0]!=0 );
4283ab4ffceSdrh       assert( v->apCsr[0]->eCurType==CURTYPE_BTREE );
429e437ca5eSdan       sqlite3VdbePreUpdateHook(
430a23a873fSdan           v, v->apCsr[0], SQLITE_DELETE, p->zDb, p->pTab, iKey, -1, p->iCol
431e437ca5eSdan       );
432e437ca5eSdan     }
433e437ca5eSdan #endif
434e437ca5eSdan 
435dcbb5d3fSdanielk1977     rc = xCall(p->pCsr, iOffset+p->iOffset, n, z);
436ff0587c6Sdrh     sqlite3BtreeLeaveCursor(p->pCsr);
437dcbb5d3fSdanielk1977     if( rc==SQLITE_ABORT ){
438dcbb5d3fSdanielk1977       sqlite3VdbeFinalize(v);
439dcbb5d3fSdanielk1977       p->pStmt = 0;
440dcbb5d3fSdanielk1977     }else{
441dcbb5d3fSdanielk1977       v->rc = rc;
442dcbb5d3fSdanielk1977     }
443605264d2Sdrh   }
444923c4b35Sdan   sqlite3Error(db, rc);
445605264d2Sdrh   rc = sqlite3ApiExit(db, rc);
446605264d2Sdrh   sqlite3_mutex_leave(db->mutex);
447605264d2Sdrh   return rc;
448dcbb5d3fSdanielk1977 }
449dcbb5d3fSdanielk1977 
450b4e9af9fSdanielk1977 /*
451b4e9af9fSdanielk1977 ** Read data from a blob handle.
452b4e9af9fSdanielk1977 */
sqlite3_blob_read(sqlite3_blob * pBlob,void * z,int n,int iOffset)453b4e9af9fSdanielk1977 int sqlite3_blob_read(sqlite3_blob *pBlob, void *z, int n, int iOffset){
454cb3cabd0Sdrh   return blobReadWrite(pBlob, z, n, iOffset, sqlite3BtreePayloadChecked);
455b4e9af9fSdanielk1977 }
456b4e9af9fSdanielk1977 
457b4e9af9fSdanielk1977 /*
458b4e9af9fSdanielk1977 ** Write data to a blob handle.
459b4e9af9fSdanielk1977 */
sqlite3_blob_write(sqlite3_blob * pBlob,const void * z,int n,int iOffset)460b4e9af9fSdanielk1977 int sqlite3_blob_write(sqlite3_blob *pBlob, const void *z, int n, int iOffset){
461dcbb5d3fSdanielk1977   return blobReadWrite(pBlob, (void *)z, n, iOffset, sqlite3BtreePutData);
462b4e9af9fSdanielk1977 }
463b4e9af9fSdanielk1977 
464b4e9af9fSdanielk1977 /*
465b4e9af9fSdanielk1977 ** Query a blob handle for the size of the data.
466605264d2Sdrh **
467605264d2Sdrh ** The Incrblob.nByte field is fixed for the lifetime of the Incrblob
468605264d2Sdrh ** so no mutex is required for access.
469b4e9af9fSdanielk1977 */
sqlite3_blob_bytes(sqlite3_blob * pBlob)470b4e9af9fSdanielk1977 int sqlite3_blob_bytes(sqlite3_blob *pBlob){
471b4e9af9fSdanielk1977   Incrblob *p = (Incrblob *)pBlob;
472eefab751Sdan   return (p && p->pStmt) ? p->nByte : 0;
473b4e9af9fSdanielk1977 }
474b4e9af9fSdanielk1977 
4754e76cc36Sdan /*
4764e76cc36Sdan ** Move an existing blob handle to point to a different row of the same
4774e76cc36Sdan ** database table.
4784e76cc36Sdan **
4794e76cc36Sdan ** If an error occurs, or if the specified row does not exist or does not
4804e76cc36Sdan ** contain a blob or text value, then an error code is returned and the
4814e76cc36Sdan ** database handle error code and message set. If this happens, then all
4824e76cc36Sdan ** subsequent calls to sqlite3_blob_xxx() functions (except blob_close())
4834e76cc36Sdan ** immediately return SQLITE_ABORT.
4844e76cc36Sdan */
sqlite3_blob_reopen(sqlite3_blob * pBlob,sqlite3_int64 iRow)4854e76cc36Sdan int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){
4864e76cc36Sdan   int rc;
4874e76cc36Sdan   Incrblob *p = (Incrblob *)pBlob;
4884e76cc36Sdan   sqlite3 *db;
4894e76cc36Sdan 
4904e76cc36Sdan   if( p==0 ) return SQLITE_MISUSE_BKPT;
4914e76cc36Sdan   db = p->db;
4924e76cc36Sdan   sqlite3_mutex_enter(db->mutex);
4934e76cc36Sdan 
4944e76cc36Sdan   if( p->pStmt==0 ){
4954e76cc36Sdan     /* If there is no statement handle, then the blob-handle has
4964e76cc36Sdan     ** already been invalidated. Return SQLITE_ABORT in this case.
4974e76cc36Sdan     */
4984e76cc36Sdan     rc = SQLITE_ABORT;
4994e76cc36Sdan   }else{
5004e76cc36Sdan     char *zErr;
501385b9828Sdan     ((Vdbe*)p->pStmt)->rc = SQLITE_OK;
5024e76cc36Sdan     rc = blobSeekToRow(p, iRow, &zErr);
5034e76cc36Sdan     if( rc!=SQLITE_OK ){
50413f40da3Sdrh       sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr);
5054e76cc36Sdan       sqlite3DbFree(db, zErr);
5064e76cc36Sdan     }
5074e76cc36Sdan     assert( rc!=SQLITE_SCHEMA );
5084e76cc36Sdan   }
5094e76cc36Sdan 
5104e76cc36Sdan   rc = sqlite3ApiExit(db, rc);
511eefab751Sdan   assert( rc==SQLITE_OK || p->pStmt==0 );
5124e76cc36Sdan   sqlite3_mutex_leave(db->mutex);
5134e76cc36Sdan   return rc;
5144e76cc36Sdan }
5154e76cc36Sdan 
516b4e9af9fSdanielk1977 #endif /* #ifndef SQLITE_OMIT_INCRBLOB */
517