AryaWu/sqlite
0
1/*2** 2007 May 13**4** The author disclaims copyright to this source code. In place of5** a legal notice, here is a blessing:6**7** May you do good and not evil.8** May you find forgiveness for yourself and forgive others.9** May you share freely, never taking more than you give.10**11*************************************************************************12**13** This file contains code used to implement incremental BLOB I/O.14*/15 16#include "sqliteInt.h"17#include "vdbeInt.h"18 19#ifndef SQLITE_OMIT_INCRBLOB20 21/*22** Valid sqlite3_blob* handles point to Incrblob structures.23*/24typedef struct Incrblob Incrblob;25struct Incrblob {26 int nByte; /* Size of open blob, in bytes */27 int iOffset; /* Byte offset of blob in cursor data */28 u16 iCol; /* Table column this handle is open on */29 BtCursor *pCsr; /* Cursor pointing at blob row */30 sqlite3_stmt *pStmt; /* Statement holding cursor open */31 sqlite3 *db; /* The associated database */32 char *zDb; /* Database name */33 Table *pTab; /* Table object */34};35 36 37/*38** This function is used by both blob_open() and blob_reopen(). It seeks39** the b-tree cursor associated with blob handle p to point to row iRow.40** If successful, SQLITE_OK is returned and subsequent calls to41** sqlite3_blob_read() or sqlite3_blob_write() access the specified row.42**43** If an error occurs, or if the specified row does not exist or does not44** contain a value of type TEXT or BLOB in the column nominated when the45** blob handle was opened, then an error code is returned and *pzErr may46** be set to point to a buffer containing an error message. It is the47** responsibility of the caller to free the error message buffer using48** sqlite3DbFree().49**50** If an error does occur, then the b-tree cursor is closed. All subsequent51** calls to sqlite3_blob_read(), blob_write() or blob_reopen() will 52** immediately return SQLITE_ABORT.53*/54static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){55 int rc; /* Error code */56 char *zErr = 0; /* Error message */57 Vdbe *v = (Vdbe *)p->pStmt;58 59 /* Set the value of register r[1] in the SQL statement to integer iRow. 60 ** This is done directly as a performance optimization61 */62 sqlite3VdbeMemSetInt64(&v->aMem[1], iRow);63 64 /* If the statement has been run before (and is paused at the OP_ResultRow)65 ** then back it up to the point where it does the OP_NotExists. This could66 ** have been down with an extra OP_Goto, but simply setting the program67 ** counter is faster. */68 if( v->pc>4 ){69 v->pc = 4;70 assert( v->aOp[v->pc].opcode==OP_NotExists );71 rc = sqlite3VdbeExec(v);72 }else{73 rc = sqlite3_step(p->pStmt);74 }75 if( rc==SQLITE_ROW ){76 VdbeCursor *pC = v->apCsr[0];77 u32 type;78 assert( pC!=0 );79 assert( pC->eCurType==CURTYPE_BTREE );80 type = pC->nHdrParsed>p->iCol ? pC->aType[p->iCol] : 0;81 testcase( pC->nHdrParsed==p->iCol );82 testcase( pC->nHdrParsed==p->iCol+1 );83 if( type<12 ){84 zErr = sqlite3MPrintf(p->db, "cannot open value of type %s",85 type==0?"null": type==7?"real": "integer"86 );87 rc = SQLITE_ERROR;88 sqlite3_finalize(p->pStmt);89 p->pStmt = 0;90 }else{91 p->iOffset = pC->aType[p->iCol + pC->nField];92 p->nByte = sqlite3VdbeSerialTypeLen(type);93 p->pCsr = pC->uc.pCursor;94 sqlite3BtreeIncrblobCursor(p->pCsr);95 }96 }97 98 if( rc==SQLITE_ROW ){99 rc = SQLITE_OK;100 }else if( p->pStmt ){101 rc = sqlite3_finalize(p->pStmt);102 p->pStmt = 0;103 if( rc==SQLITE_OK ){104 zErr = sqlite3MPrintf(p->db, "no such rowid: %lld", iRow);105 rc = SQLITE_ERROR;106 }else{107 zErr = sqlite3MPrintf(p->db, "%s", sqlite3_errmsg(p->db));108 }109 }110 111 assert( rc!=SQLITE_OK || zErr==0 );112 assert( rc!=SQLITE_ROW && rc!=SQLITE_DONE );113 114 *pzErr = zErr;115 return rc;116}117 118/*119** Open a blob handle.120*/121int sqlite3_blob_open(122 sqlite3* db, /* The database connection */123 const char *zDb, /* The attached database containing the blob */124 const char *zTable, /* The table containing the blob */125 const char *zColumn, /* The column containing the blob */126 sqlite_int64 iRow, /* The row containing the glob */127 int wrFlag, /* True -> read/write access, false -> read-only */128 sqlite3_blob **ppBlob /* Handle for accessing the blob returned here */129){130 int nAttempt = 0;131 int iCol; /* Index of zColumn in row-record */132 int rc = SQLITE_OK;133 char *zErr = 0;134 Table *pTab;135 Incrblob *pBlob = 0;136 int iDb;137 Parse sParse;138 139#ifdef SQLITE_ENABLE_API_ARMOR140 if( ppBlob==0 ){141 return SQLITE_MISUSE_BKPT;142 }143#endif144 *ppBlob = 0;145#ifdef SQLITE_ENABLE_API_ARMOR146 if( !sqlite3SafetyCheckOk(db) || zTable==0 || zColumn==0 ){147 return SQLITE_MISUSE_BKPT;148 }149#endif150 wrFlag = !!wrFlag; /* wrFlag = (wrFlag ? 1 : 0); */151 152 sqlite3_mutex_enter(db->mutex);153 154 pBlob = (Incrblob *)sqlite3DbMallocZero(db, sizeof(Incrblob));155 while(1){156 sqlite3ParseObjectInit(&sParse,db);157 if( !pBlob ) goto blob_open_out;158 sqlite3DbFree(db, zErr);159 zErr = 0;160 161 sqlite3BtreeEnterAll(db);162 pTab = sqlite3LocateTable(&sParse, 0, zTable, zDb);163 if( pTab && IsVirtual(pTab) ){164 pTab = 0;165 sqlite3ErrorMsg(&sParse, "cannot open virtual table: %s", zTable);166 }167 if( pTab && !HasRowid(pTab) ){168 pTab = 0;169 sqlite3ErrorMsg(&sParse, "cannot open table without rowid: %s", zTable);170 }171 if( pTab && (pTab->tabFlags&TF_HasGenerated)!=0 ){172 pTab = 0;173 sqlite3ErrorMsg(&sParse, "cannot open table with generated columns: %s",174 zTable);175 }176#ifndef SQLITE_OMIT_VIEW177 if( pTab && IsView(pTab) ){178 pTab = 0;179 sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable);180 }181#endif182 if( pTab==0183 || ((iDb = sqlite3SchemaToIndex(db, pTab->pSchema))==1 &&184 sqlite3OpenTempDatabase(&sParse))185 ){186 if( sParse.zErrMsg ){187 sqlite3DbFree(db, zErr);188 zErr = sParse.zErrMsg;189 sParse.zErrMsg = 0;190 }191 rc = SQLITE_ERROR;192 sqlite3BtreeLeaveAll(db);193 goto blob_open_out;194 }195 pBlob->pTab = pTab;196 pBlob->zDb = db->aDb[iDb].zDbSName;197 198 /* Now search pTab for the exact column. */199 iCol = sqlite3ColumnIndex(pTab, zColumn);200 if( iCol<0 ){201 sqlite3DbFree(db, zErr);202 zErr = sqlite3MPrintf(db, "no such column: \"%s\"", zColumn);203 rc = SQLITE_ERROR;204 sqlite3BtreeLeaveAll(db);205 goto blob_open_out;206 }207 208 /* If the value is being opened for writing, check that the209 ** column is not indexed, and that it is not part of a foreign key. 210 */211 if( wrFlag ){212 const char *zFault = 0;213 Index *pIdx;214#ifndef SQLITE_OMIT_FOREIGN_KEY215 if( db->flags&SQLITE_ForeignKeys ){216 /* Check that the column is not part of an FK child key definition. It217 ** is not necessary to check if it is part of a parent key, as parent218 ** key columns must be indexed. The check below will pick up this 219 ** case. */220 FKey *pFKey;221 assert( IsOrdinaryTable(pTab) );222 for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){223 int j;224 for(j=0; j<pFKey->nCol; j++){225 if( pFKey->aCol[j].iFrom==iCol ){226 zFault = "foreign key";227 }228 }229 }230 }231#endif232 for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){233 int j;234 for(j=0; j<pIdx->nKeyCol; j++){235 /* FIXME: Be smarter about indexes that use expressions */236 if( pIdx->aiColumn[j]==iCol || pIdx->aiColumn[j]==XN_EXPR ){237 zFault = "indexed";238 }239 }240 }241 if( zFault ){242 sqlite3DbFree(db, zErr);243 zErr = sqlite3MPrintf(db, "cannot open %s column for writing", zFault);244 rc = SQLITE_ERROR;245 sqlite3BtreeLeaveAll(db);246 goto blob_open_out;247 }248 }249 250 pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(&sParse);251 assert( pBlob->pStmt || db->mallocFailed );252 if( pBlob->pStmt ){253 254 /* This VDBE program seeks a btree cursor to the identified 255 ** db/table/row entry. The reason for using a vdbe program instead256 ** of writing code to use the b-tree layer directly is that the257 ** vdbe program will take advantage of the various transaction,258 ** locking and error handling infrastructure built into the vdbe.259 **260 ** After seeking the cursor, the vdbe executes an OP_ResultRow.261 ** Code external to the Vdbe then "borrows" the b-tree cursor and262 ** uses it to implement the blob_read(), blob_write() and 263 ** blob_bytes() functions.264 **265 ** The sqlite3_blob_close() function finalizes the vdbe program,266 ** which closes the b-tree cursor and (possibly) commits the 267 ** transaction.268 */269 static const int iLn = VDBE_OFFSET_LINENO(2);270 static const VdbeOpList openBlob[] = {271 {OP_TableLock, 0, 0, 0}, /* 0: Acquire a read or write lock */272 {OP_OpenRead, 0, 0, 0}, /* 1: Open a cursor */273 /* blobSeekToRow() will initialize r[1] to the desired rowid */274 {OP_NotExists, 0, 5, 1}, /* 2: Seek the cursor to rowid=r[1] */275 {OP_Column, 0, 0, 1}, /* 3 */276 {OP_ResultRow, 1, 0, 0}, /* 4 */277 {OP_Halt, 0, 0, 0}, /* 5 */278 };279 Vdbe *v = (Vdbe *)pBlob->pStmt;280 VdbeOp *aOp;281 282 sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag, 283 pTab->pSchema->schema_cookie,284 pTab->pSchema->iGeneration);285 sqlite3VdbeChangeP5(v, 1);286 assert( sqlite3VdbeCurrentAddr(v)==2 || db->mallocFailed );287 aOp = sqlite3VdbeAddOpList(v, ArraySize(openBlob), openBlob, iLn);288 289 /* Make sure a mutex is held on the table to be accessed */290 sqlite3VdbeUsesBtree(v, iDb); 291 292 if( db->mallocFailed==0 ){293 assert( aOp!=0 );294 /* Configure the OP_TableLock instruction */295#ifdef SQLITE_OMIT_SHARED_CACHE296 aOp[0].opcode = OP_Noop;297#else298 aOp[0].p1 = iDb;299 aOp[0].p2 = pTab->tnum;300 aOp[0].p3 = wrFlag;301 sqlite3VdbeChangeP4(v, 2, pTab->zName, P4_TRANSIENT);302 }303 if( db->mallocFailed==0 ){304#endif305 306 /* Remove either the OP_OpenWrite or OpenRead. Set the P2 307 ** parameter of the other to pTab->tnum. */308 if( wrFlag ) aOp[1].opcode = OP_OpenWrite;309 aOp[1].p2 = pTab->tnum;310 aOp[1].p3 = iDb; 311 312 /* Configure the number of columns. Configure the cursor to313 ** think that the table has one more column than it really314 ** does. An OP_Column to retrieve this imaginary column will315 ** always return an SQL NULL. This is useful because it means316 ** we can invoke OP_Column to fill in the vdbe cursors type 317 ** and offset cache without causing any IO.318 */319 aOp[1].p4type = P4_INT32;320 aOp[1].p4.i = pTab->nCol+1;321 aOp[3].p2 = pTab->nCol;322 323 sParse.nVar = 0;324 sParse.nMem = 1;325 sParse.nTab = 1;326 sqlite3VdbeMakeReady(v, &sParse);327 }328 }329 330 pBlob->iCol = iCol;331 pBlob->db = db;332 sqlite3BtreeLeaveAll(db);333 if( db->mallocFailed ){334 goto blob_open_out;335 }336 rc = blobSeekToRow(pBlob, iRow, &zErr);337 if( (++nAttempt)>=SQLITE_MAX_SCHEMA_RETRY || rc!=SQLITE_SCHEMA ) break;338 sqlite3ParseObjectReset(&sParse);339 }340 341blob_open_out:342 if( rc==SQLITE_OK && db->mallocFailed==0 ){343 *ppBlob = (sqlite3_blob *)pBlob;344 }else{345 if( pBlob && pBlob->pStmt ) sqlite3VdbeFinalize((Vdbe *)pBlob->pStmt);346 sqlite3DbFree(db, pBlob);347 }348 sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : (char*)0), zErr);349 sqlite3DbFree(db, zErr);350 sqlite3ParseObjectReset(&sParse);351 rc = sqlite3ApiExit(db, rc);352 sqlite3_mutex_leave(db->mutex);353 return rc;354}355 356/*357** Close a blob handle that was previously created using358** sqlite3_blob_open().359*/360int sqlite3_blob_close(sqlite3_blob *pBlob){361 Incrblob *p = (Incrblob *)pBlob;362 int rc;363 sqlite3 *db;364 365 if( p ){366 sqlite3_stmt *pStmt = p->pStmt;367 db = p->db;368 sqlite3_mutex_enter(db->mutex);369 sqlite3DbFree(db, p);370 sqlite3_mutex_leave(db->mutex);371 rc = sqlite3_finalize(pStmt);372 }else{373 rc = SQLITE_OK;374 }375 return rc;376}377 378/*379** Perform a read or write operation on a blob380*/381static int blobReadWrite(382 sqlite3_blob *pBlob, 383 void *z, 384 int n, 385 int iOffset, 386 int (*xCall)(BtCursor*, u32, u32, void*)387){388 int rc = SQLITE_OK;389 Incrblob *p = (Incrblob *)pBlob;390 Vdbe *v;391 sqlite3 *db;392 393 if( p==0 ) return SQLITE_MISUSE_BKPT;394 db = p->db;395 sqlite3_mutex_enter(db->mutex);396 v = (Vdbe*)p->pStmt;397 398 if( n<0 || iOffset<0 || ((sqlite3_int64)iOffset+n)>p->nByte ){399 /* Request is out of range. Return a transient error. */400 rc = SQLITE_ERROR;401 }else if( v==0 ){402 /* If there is no statement handle, then the blob-handle has403 ** already been invalidated. Return SQLITE_ABORT in this case.404 */405 rc = SQLITE_ABORT;406 }else{407 /* Call either BtreeData() or BtreePutData(). If SQLITE_ABORT is408 ** returned, clean-up the statement handle.409 */410 assert( db == v->db );411 sqlite3BtreeEnterCursor(p->pCsr);412 413#ifdef SQLITE_ENABLE_PREUPDATE_HOOK414 if( xCall==sqlite3BtreePutData && db->xPreUpdateCallback ){415 /* If a pre-update hook is registered and this is a write cursor, 416 ** invoke it here. 417 ** 418 ** TODO: The preupdate-hook is passed SQLITE_DELETE, even though this419 ** operation should really be an SQLITE_UPDATE. This is probably420 ** incorrect, but is convenient because at this point the new.* values 421 ** are not easily obtainable. And for the sessions module, an 422 ** SQLITE_UPDATE where the PK columns do not change is handled in the 423 ** same way as an SQLITE_DELETE (the SQLITE_DELETE code is actually424 ** slightly more efficient). Since you cannot write to a PK column425 ** using the incremental-blob API, this works. For the sessions module426 ** anyhow.427 */428 if( sqlite3BtreeCursorIsValidNN(p->pCsr)==0 ){429 /* If the cursor is not currently valid, try to reseek it. This 430 ** always either fails or finds the correct row - the cursor will431 ** have been marked permanently CURSOR_INVALID if the open row has432 ** been deleted. */433 int bDiff = 0;434 rc = sqlite3BtreeCursorRestore(p->pCsr, &bDiff);435 assert( bDiff==0 || sqlite3BtreeCursorIsValidNN(p->pCsr)==0 );436 }437 if( sqlite3BtreeCursorIsValidNN(p->pCsr) ){438 sqlite3_int64 iKey;439 iKey = sqlite3BtreeIntegerKey(p->pCsr);440 assert( v->apCsr[0]!=0 );441 assert( v->apCsr[0]->eCurType==CURTYPE_BTREE );442 sqlite3VdbePreUpdateHook(443 v, v->apCsr[0], SQLITE_DELETE, p->zDb, p->pTab, iKey, -1, p->iCol444 );445 }446 }447 if( rc==SQLITE_OK ){448 rc = xCall(p->pCsr, iOffset+p->iOffset, n, z);449 }450#else451 rc = xCall(p->pCsr, iOffset+p->iOffset, n, z);452#endif453 454 sqlite3BtreeLeaveCursor(p->pCsr);455 if( rc==SQLITE_ABORT ){456 sqlite3VdbeFinalize(v);457 p->pStmt = 0;458 }else{459 v->rc = rc;460 }461 }462 sqlite3Error(db, rc);463 rc = sqlite3ApiExit(db, rc);464 sqlite3_mutex_leave(db->mutex);465 return rc;466}467 468/*469** Read data from a blob handle.470*/471int sqlite3_blob_read(sqlite3_blob *pBlob, void *z, int n, int iOffset){472 return blobReadWrite(pBlob, z, n, iOffset, sqlite3BtreePayloadChecked);473}474 475/*476** Write data to a blob handle.477*/478int sqlite3_blob_write(sqlite3_blob *pBlob, const void *z, int n, int iOffset){479 return blobReadWrite(pBlob, (void *)z, n, iOffset, sqlite3BtreePutData);480}481 482/*483** Query a blob handle for the size of the data.484**485** The Incrblob.nByte field is fixed for the lifetime of the Incrblob486** so no mutex is required for access.487*/488int sqlite3_blob_bytes(sqlite3_blob *pBlob){489 Incrblob *p = (Incrblob *)pBlob;490 return (p && p->pStmt) ? p->nByte : 0;491}492 493/*494** Move an existing blob handle to point to a different row of the same495** database table.496**497** If an error occurs, or if the specified row does not exist or does not498** contain a blob or text value, then an error code is returned and the499** database handle error code and message set. If this happens, then all 500** subsequent calls to sqlite3_blob_xxx() functions (except blob_close()) 501** immediately return SQLITE_ABORT.502*/503int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){504 int rc;505 Incrblob *p = (Incrblob *)pBlob;506 sqlite3 *db;507 508 if( p==0 ) return SQLITE_MISUSE_BKPT;509 db = p->db;510 sqlite3_mutex_enter(db->mutex);511 512 if( p->pStmt==0 ){513 /* If there is no statement handle, then the blob-handle has514 ** already been invalidated. Return SQLITE_ABORT in this case.515 */516 rc = SQLITE_ABORT;517 }else{518 char *zErr;519 ((Vdbe*)p->pStmt)->rc = SQLITE_OK;520 rc = blobSeekToRow(p, iRow, &zErr);521 if( rc!=SQLITE_OK ){522 sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : (char*)0), zErr);523 sqlite3DbFree(db, zErr);524 }525 assert( rc!=SQLITE_SCHEMA );526 }527 528 rc = sqlite3ApiExit(db, rc);529 assert( rc==SQLITE_OK || p->pStmt==0 );530 sqlite3_mutex_leave(db->mutex);531 return rc;532}533 534#endif /* #ifndef SQLITE_OMIT_INCRBLOB */535 