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