Annotation of embedaddon/sqlite3/src/vdbeblob.c, revision 1.1
1.1 ! misho 1: /*
! 2: ** 2007 May 1
! 3: **
! 4: ** The author disclaims copyright to this source code. In place of
! 5: ** 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_INCRBLOB
! 20:
! 21: /*
! 22: ** Valid sqlite3_blob* handles point to Incrblob structures.
! 23: */
! 24: typedef struct Incrblob Incrblob;
! 25: struct Incrblob {
! 26: int flags; /* Copy of "flags" passed to sqlite3_blob_open() */
! 27: int nByte; /* Size of open blob, in bytes */
! 28: int iOffset; /* Byte offset of blob in cursor data */
! 29: int iCol; /* Table column this handle is open on */
! 30: BtCursor *pCsr; /* Cursor pointing at blob row */
! 31: sqlite3_stmt *pStmt; /* Statement holding cursor open */
! 32: sqlite3 *db; /* The associated database */
! 33: };
! 34:
! 35:
! 36: /*
! 37: ** This function is used by both blob_open() and blob_reopen(). It seeks
! 38: ** the b-tree cursor associated with blob handle p to point to row iRow.
! 39: ** If successful, SQLITE_OK is returned and subsequent calls to
! 40: ** sqlite3_blob_read() or sqlite3_blob_write() access the specified row.
! 41: **
! 42: ** If an error occurs, or if the specified row does not exist or does not
! 43: ** contain a value of type TEXT or BLOB in the column nominated when the
! 44: ** blob handle was opened, then an error code is returned and *pzErr may
! 45: ** be set to point to a buffer containing an error message. It is the
! 46: ** responsibility of the caller to free the error message buffer using
! 47: ** sqlite3DbFree().
! 48: **
! 49: ** If an error does occur, then the b-tree cursor is closed. All subsequent
! 50: ** calls to sqlite3_blob_read(), blob_write() or blob_reopen() will
! 51: ** immediately return SQLITE_ABORT.
! 52: */
! 53: static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){
! 54: int rc; /* Error code */
! 55: char *zErr = 0; /* Error message */
! 56: Vdbe *v = (Vdbe *)p->pStmt;
! 57:
! 58: /* Set the value of the SQL statements only variable to integer iRow.
! 59: ** This is done directly instead of using sqlite3_bind_int64() to avoid
! 60: ** triggering asserts related to mutexes.
! 61: */
! 62: assert( v->aVar[0].flags&MEM_Int );
! 63: v->aVar[0].u.i = iRow;
! 64:
! 65: rc = sqlite3_step(p->pStmt);
! 66: if( rc==SQLITE_ROW ){
! 67: u32 type = v->apCsr[0]->aType[p->iCol];
! 68: if( type<12 ){
! 69: zErr = sqlite3MPrintf(p->db, "cannot open value of type %s",
! 70: type==0?"null": type==7?"real": "integer"
! 71: );
! 72: rc = SQLITE_ERROR;
! 73: sqlite3_finalize(p->pStmt);
! 74: p->pStmt = 0;
! 75: }else{
! 76: p->iOffset = v->apCsr[0]->aOffset[p->iCol];
! 77: p->nByte = sqlite3VdbeSerialTypeLen(type);
! 78: p->pCsr = v->apCsr[0]->pCursor;
! 79: sqlite3BtreeEnterCursor(p->pCsr);
! 80: sqlite3BtreeCacheOverflow(p->pCsr);
! 81: sqlite3BtreeLeaveCursor(p->pCsr);
! 82: }
! 83: }
! 84:
! 85: if( rc==SQLITE_ROW ){
! 86: rc = SQLITE_OK;
! 87: }else if( p->pStmt ){
! 88: rc = sqlite3_finalize(p->pStmt);
! 89: p->pStmt = 0;
! 90: if( rc==SQLITE_OK ){
! 91: zErr = sqlite3MPrintf(p->db, "no such rowid: %lld", iRow);
! 92: rc = SQLITE_ERROR;
! 93: }else{
! 94: zErr = sqlite3MPrintf(p->db, "%s", sqlite3_errmsg(p->db));
! 95: }
! 96: }
! 97:
! 98: assert( rc!=SQLITE_OK || zErr==0 );
! 99: assert( rc!=SQLITE_ROW && rc!=SQLITE_DONE );
! 100:
! 101: *pzErr = zErr;
! 102: return rc;
! 103: }
! 104:
! 105: /*
! 106: ** Open a blob handle.
! 107: */
! 108: int sqlite3_blob_open(
! 109: sqlite3* db, /* The database connection */
! 110: const char *zDb, /* The attached database containing the blob */
! 111: const char *zTable, /* The table containing the blob */
! 112: const char *zColumn, /* The column containing the blob */
! 113: sqlite_int64 iRow, /* The row containing the glob */
! 114: int flags, /* True -> read/write access, false -> read-only */
! 115: sqlite3_blob **ppBlob /* Handle for accessing the blob returned here */
! 116: ){
! 117: int nAttempt = 0;
! 118: int iCol; /* Index of zColumn in row-record */
! 119:
! 120: /* This VDBE program seeks a btree cursor to the identified
! 121: ** db/table/row entry. The reason for using a vdbe program instead
! 122: ** of writing code to use the b-tree layer directly is that the
! 123: ** vdbe program will take advantage of the various transaction,
! 124: ** locking and error handling infrastructure built into the vdbe.
! 125: **
! 126: ** After seeking the cursor, the vdbe executes an OP_ResultRow.
! 127: ** Code external to the Vdbe then "borrows" the b-tree cursor and
! 128: ** uses it to implement the blob_read(), blob_write() and
! 129: ** blob_bytes() functions.
! 130: **
! 131: ** The sqlite3_blob_close() function finalizes the vdbe program,
! 132: ** which closes the b-tree cursor and (possibly) commits the
! 133: ** transaction.
! 134: */
! 135: static const VdbeOpList openBlob[] = {
! 136: {OP_Transaction, 0, 0, 0}, /* 0: Start a transaction */
! 137: {OP_VerifyCookie, 0, 0, 0}, /* 1: Check the schema cookie */
! 138: {OP_TableLock, 0, 0, 0}, /* 2: Acquire a read or write lock */
! 139:
! 140: /* One of the following two instructions is replaced by an OP_Noop. */
! 141: {OP_OpenRead, 0, 0, 0}, /* 3: Open cursor 0 for reading */
! 142: {OP_OpenWrite, 0, 0, 0}, /* 4: Open cursor 0 for read/write */
! 143:
! 144: {OP_Variable, 1, 1, 1}, /* 5: Push the rowid to the stack */
! 145: {OP_NotExists, 0, 10, 1}, /* 6: Seek the cursor */
! 146: {OP_Column, 0, 0, 1}, /* 7 */
! 147: {OP_ResultRow, 1, 0, 0}, /* 8 */
! 148: {OP_Goto, 0, 5, 0}, /* 9 */
! 149: {OP_Close, 0, 0, 0}, /* 10 */
! 150: {OP_Halt, 0, 0, 0}, /* 11 */
! 151: };
! 152:
! 153: int rc = SQLITE_OK;
! 154: char *zErr = 0;
! 155: Table *pTab;
! 156: Parse *pParse = 0;
! 157: Incrblob *pBlob = 0;
! 158:
! 159: flags = !!flags; /* flags = (flags ? 1 : 0); */
! 160: *ppBlob = 0;
! 161:
! 162: sqlite3_mutex_enter(db->mutex);
! 163:
! 164: pBlob = (Incrblob *)sqlite3DbMallocZero(db, sizeof(Incrblob));
! 165: if( !pBlob ) goto blob_open_out;
! 166: pParse = sqlite3StackAllocRaw(db, sizeof(*pParse));
! 167: if( !pParse ) goto blob_open_out;
! 168:
! 169: do {
! 170: memset(pParse, 0, sizeof(Parse));
! 171: pParse->db = db;
! 172: sqlite3DbFree(db, zErr);
! 173: zErr = 0;
! 174:
! 175: sqlite3BtreeEnterAll(db);
! 176: pTab = sqlite3LocateTable(pParse, 0, zTable, zDb);
! 177: if( pTab && IsVirtual(pTab) ){
! 178: pTab = 0;
! 179: sqlite3ErrorMsg(pParse, "cannot open virtual table: %s", zTable);
! 180: }
! 181: #ifndef SQLITE_OMIT_VIEW
! 182: if( pTab && pTab->pSelect ){
! 183: pTab = 0;
! 184: sqlite3ErrorMsg(pParse, "cannot open view: %s", zTable);
! 185: }
! 186: #endif
! 187: if( !pTab ){
! 188: if( pParse->zErrMsg ){
! 189: sqlite3DbFree(db, zErr);
! 190: zErr = pParse->zErrMsg;
! 191: pParse->zErrMsg = 0;
! 192: }
! 193: rc = SQLITE_ERROR;
! 194: sqlite3BtreeLeaveAll(db);
! 195: goto blob_open_out;
! 196: }
! 197:
! 198: /* Now search pTab for the exact column. */
! 199: for(iCol=0; iCol<pTab->nCol; iCol++) {
! 200: if( sqlite3StrICmp(pTab->aCol[iCol].zName, zColumn)==0 ){
! 201: break;
! 202: }
! 203: }
! 204: if( iCol==pTab->nCol ){
! 205: sqlite3DbFree(db, zErr);
! 206: zErr = sqlite3MPrintf(db, "no such column: \"%s\"", zColumn);
! 207: rc = SQLITE_ERROR;
! 208: sqlite3BtreeLeaveAll(db);
! 209: goto blob_open_out;
! 210: }
! 211:
! 212: /* If the value is being opened for writing, check that the
! 213: ** column is not indexed, and that it is not part of a foreign key.
! 214: ** It is against the rules to open a column to which either of these
! 215: ** descriptions applies for writing. */
! 216: if( flags ){
! 217: const char *zFault = 0;
! 218: Index *pIdx;
! 219: #ifndef SQLITE_OMIT_FOREIGN_KEY
! 220: if( db->flags&SQLITE_ForeignKeys ){
! 221: /* Check that the column is not part of an FK child key definition. It
! 222: ** is not necessary to check if it is part of a parent key, as parent
! 223: ** key columns must be indexed. The check below will pick up this
! 224: ** case. */
! 225: FKey *pFKey;
! 226: for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){
! 227: int j;
! 228: for(j=0; j<pFKey->nCol; j++){
! 229: if( pFKey->aCol[j].iFrom==iCol ){
! 230: zFault = "foreign key";
! 231: }
! 232: }
! 233: }
! 234: }
! 235: #endif
! 236: for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
! 237: int j;
! 238: for(j=0; j<pIdx->nColumn; j++){
! 239: if( pIdx->aiColumn[j]==iCol ){
! 240: zFault = "indexed";
! 241: }
! 242: }
! 243: }
! 244: if( zFault ){
! 245: sqlite3DbFree(db, zErr);
! 246: zErr = sqlite3MPrintf(db, "cannot open %s column for writing", zFault);
! 247: rc = SQLITE_ERROR;
! 248: sqlite3BtreeLeaveAll(db);
! 249: goto blob_open_out;
! 250: }
! 251: }
! 252:
! 253: pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(db);
! 254: assert( pBlob->pStmt || db->mallocFailed );
! 255: if( pBlob->pStmt ){
! 256: Vdbe *v = (Vdbe *)pBlob->pStmt;
! 257: int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
! 258:
! 259: sqlite3VdbeAddOpList(v, sizeof(openBlob)/sizeof(VdbeOpList), openBlob);
! 260:
! 261:
! 262: /* Configure the OP_Transaction */
! 263: sqlite3VdbeChangeP1(v, 0, iDb);
! 264: sqlite3VdbeChangeP2(v, 0, flags);
! 265:
! 266: /* Configure the OP_VerifyCookie */
! 267: sqlite3VdbeChangeP1(v, 1, iDb);
! 268: sqlite3VdbeChangeP2(v, 1, pTab->pSchema->schema_cookie);
! 269: sqlite3VdbeChangeP3(v, 1, pTab->pSchema->iGeneration);
! 270:
! 271: /* Make sure a mutex is held on the table to be accessed */
! 272: sqlite3VdbeUsesBtree(v, iDb);
! 273:
! 274: /* Configure the OP_TableLock instruction */
! 275: #ifdef SQLITE_OMIT_SHARED_CACHE
! 276: sqlite3VdbeChangeToNoop(v, 2);
! 277: #else
! 278: sqlite3VdbeChangeP1(v, 2, iDb);
! 279: sqlite3VdbeChangeP2(v, 2, pTab->tnum);
! 280: sqlite3VdbeChangeP3(v, 2, flags);
! 281: sqlite3VdbeChangeP4(v, 2, pTab->zName, P4_TRANSIENT);
! 282: #endif
! 283:
! 284: /* Remove either the OP_OpenWrite or OpenRead. Set the P2
! 285: ** parameter of the other to pTab->tnum. */
! 286: sqlite3VdbeChangeToNoop(v, 4 - flags);
! 287: sqlite3VdbeChangeP2(v, 3 + flags, pTab->tnum);
! 288: sqlite3VdbeChangeP3(v, 3 + flags, iDb);
! 289:
! 290: /* Configure the number of columns. Configure the cursor to
! 291: ** think that the table has one more column than it really
! 292: ** does. An OP_Column to retrieve this imaginary column will
! 293: ** always return an SQL NULL. This is useful because it means
! 294: ** we can invoke OP_Column to fill in the vdbe cursors type
! 295: ** and offset cache without causing any IO.
! 296: */
! 297: sqlite3VdbeChangeP4(v, 3+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32);
! 298: sqlite3VdbeChangeP2(v, 7, pTab->nCol);
! 299: if( !db->mallocFailed ){
! 300: pParse->nVar = 1;
! 301: pParse->nMem = 1;
! 302: pParse->nTab = 1;
! 303: sqlite3VdbeMakeReady(v, pParse);
! 304: }
! 305: }
! 306:
! 307: pBlob->flags = flags;
! 308: pBlob->iCol = iCol;
! 309: pBlob->db = db;
! 310: sqlite3BtreeLeaveAll(db);
! 311: if( db->mallocFailed ){
! 312: goto blob_open_out;
! 313: }
! 314: sqlite3_bind_int64(pBlob->pStmt, 1, iRow);
! 315: rc = blobSeekToRow(pBlob, iRow, &zErr);
! 316: } while( (++nAttempt)<5 && rc==SQLITE_SCHEMA );
! 317:
! 318: blob_open_out:
! 319: if( rc==SQLITE_OK && db->mallocFailed==0 ){
! 320: *ppBlob = (sqlite3_blob *)pBlob;
! 321: }else{
! 322: if( pBlob && pBlob->pStmt ) sqlite3VdbeFinalize((Vdbe *)pBlob->pStmt);
! 323: sqlite3DbFree(db, pBlob);
! 324: }
! 325: sqlite3Error(db, rc, (zErr ? "%s" : 0), zErr);
! 326: sqlite3DbFree(db, zErr);
! 327: sqlite3StackFree(db, pParse);
! 328: rc = sqlite3ApiExit(db, rc);
! 329: sqlite3_mutex_leave(db->mutex);
! 330: return rc;
! 331: }
! 332:
! 333: /*
! 334: ** Close a blob handle that was previously created using
! 335: ** sqlite3_blob_open().
! 336: */
! 337: int sqlite3_blob_close(sqlite3_blob *pBlob){
! 338: Incrblob *p = (Incrblob *)pBlob;
! 339: int rc;
! 340: sqlite3 *db;
! 341:
! 342: if( p ){
! 343: db = p->db;
! 344: sqlite3_mutex_enter(db->mutex);
! 345: rc = sqlite3_finalize(p->pStmt);
! 346: sqlite3DbFree(db, p);
! 347: sqlite3_mutex_leave(db->mutex);
! 348: }else{
! 349: rc = SQLITE_OK;
! 350: }
! 351: return rc;
! 352: }
! 353:
! 354: /*
! 355: ** Perform a read or write operation on a blob
! 356: */
! 357: static int blobReadWrite(
! 358: sqlite3_blob *pBlob,
! 359: void *z,
! 360: int n,
! 361: int iOffset,
! 362: int (*xCall)(BtCursor*, u32, u32, void*)
! 363: ){
! 364: int rc;
! 365: Incrblob *p = (Incrblob *)pBlob;
! 366: Vdbe *v;
! 367: sqlite3 *db;
! 368:
! 369: if( p==0 ) return SQLITE_MISUSE_BKPT;
! 370: db = p->db;
! 371: sqlite3_mutex_enter(db->mutex);
! 372: v = (Vdbe*)p->pStmt;
! 373:
! 374: if( n<0 || iOffset<0 || (iOffset+n)>p->nByte ){
! 375: /* Request is out of range. Return a transient error. */
! 376: rc = SQLITE_ERROR;
! 377: sqlite3Error(db, SQLITE_ERROR, 0);
! 378: }else if( v==0 ){
! 379: /* If there is no statement handle, then the blob-handle has
! 380: ** already been invalidated. Return SQLITE_ABORT in this case.
! 381: */
! 382: rc = SQLITE_ABORT;
! 383: }else{
! 384: /* Call either BtreeData() or BtreePutData(). If SQLITE_ABORT is
! 385: ** returned, clean-up the statement handle.
! 386: */
! 387: assert( db == v->db );
! 388: sqlite3BtreeEnterCursor(p->pCsr);
! 389: rc = xCall(p->pCsr, iOffset+p->iOffset, n, z);
! 390: sqlite3BtreeLeaveCursor(p->pCsr);
! 391: if( rc==SQLITE_ABORT ){
! 392: sqlite3VdbeFinalize(v);
! 393: p->pStmt = 0;
! 394: }else{
! 395: db->errCode = rc;
! 396: v->rc = rc;
! 397: }
! 398: }
! 399: rc = sqlite3ApiExit(db, rc);
! 400: sqlite3_mutex_leave(db->mutex);
! 401: return rc;
! 402: }
! 403:
! 404: /*
! 405: ** Read data from a blob handle.
! 406: */
! 407: int sqlite3_blob_read(sqlite3_blob *pBlob, void *z, int n, int iOffset){
! 408: return blobReadWrite(pBlob, z, n, iOffset, sqlite3BtreeData);
! 409: }
! 410:
! 411: /*
! 412: ** Write data to a blob handle.
! 413: */
! 414: int sqlite3_blob_write(sqlite3_blob *pBlob, const void *z, int n, int iOffset){
! 415: return blobReadWrite(pBlob, (void *)z, n, iOffset, sqlite3BtreePutData);
! 416: }
! 417:
! 418: /*
! 419: ** Query a blob handle for the size of the data.
! 420: **
! 421: ** The Incrblob.nByte field is fixed for the lifetime of the Incrblob
! 422: ** so no mutex is required for access.
! 423: */
! 424: int sqlite3_blob_bytes(sqlite3_blob *pBlob){
! 425: Incrblob *p = (Incrblob *)pBlob;
! 426: return (p && p->pStmt) ? p->nByte : 0;
! 427: }
! 428:
! 429: /*
! 430: ** Move an existing blob handle to point to a different row of the same
! 431: ** database table.
! 432: **
! 433: ** If an error occurs, or if the specified row does not exist or does not
! 434: ** contain a blob or text value, then an error code is returned and the
! 435: ** database handle error code and message set. If this happens, then all
! 436: ** subsequent calls to sqlite3_blob_xxx() functions (except blob_close())
! 437: ** immediately return SQLITE_ABORT.
! 438: */
! 439: int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){
! 440: int rc;
! 441: Incrblob *p = (Incrblob *)pBlob;
! 442: sqlite3 *db;
! 443:
! 444: if( p==0 ) return SQLITE_MISUSE_BKPT;
! 445: db = p->db;
! 446: sqlite3_mutex_enter(db->mutex);
! 447:
! 448: if( p->pStmt==0 ){
! 449: /* If there is no statement handle, then the blob-handle has
! 450: ** already been invalidated. Return SQLITE_ABORT in this case.
! 451: */
! 452: rc = SQLITE_ABORT;
! 453: }else{
! 454: char *zErr;
! 455: rc = blobSeekToRow(p, iRow, &zErr);
! 456: if( rc!=SQLITE_OK ){
! 457: sqlite3Error(db, rc, (zErr ? "%s" : 0), zErr);
! 458: sqlite3DbFree(db, zErr);
! 459: }
! 460: assert( rc!=SQLITE_SCHEMA );
! 461: }
! 462:
! 463: rc = sqlite3ApiExit(db, rc);
! 464: assert( rc==SQLITE_OK || p->pStmt==0 );
! 465: sqlite3_mutex_leave(db->mutex);
! 466: return rc;
! 467: }
! 468:
! 469: #endif /* #ifndef SQLITE_OMIT_INCRBLOB */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>