| OLD | NEW | 
 | (Empty) | 
|    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 ** $Id: vdbeblob.c,v 1.35 2009/07/02 07:47:33 danielk1977 Exp $ |  | 
|   16 */ |  | 
|   17  |  | 
|   18 #include "sqliteInt.h" |  | 
|   19 #include "vdbeInt.h" |  | 
|   20  |  | 
|   21 #ifndef SQLITE_OMIT_INCRBLOB |  | 
|   22  |  | 
|   23 /* |  | 
|   24 ** Valid sqlite3_blob* handles point to Incrblob structures. |  | 
|   25 */ |  | 
|   26 typedef struct Incrblob Incrblob; |  | 
|   27 struct Incrblob { |  | 
|   28   int flags;              /* Copy of "flags" passed to sqlite3_blob_open() */ |  | 
|   29   int nByte;              /* Size of open blob, in bytes */ |  | 
|   30   int iOffset;            /* Byte offset of blob in cursor data */ |  | 
|   31   BtCursor *pCsr;         /* Cursor pointing at blob row */ |  | 
|   32   sqlite3_stmt *pStmt;    /* Statement holding cursor open */ |  | 
|   33   sqlite3 *db;            /* The associated database */ |  | 
|   34 }; |  | 
|   35  |  | 
|   36 /* |  | 
|   37 ** Open a blob handle. |  | 
|   38 */ |  | 
|   39 int sqlite3_blob_open( |  | 
|   40   sqlite3* db,            /* The database connection */ |  | 
|   41   const char *zDb,        /* The attached database containing the blob */ |  | 
|   42   const char *zTable,     /* The table containing the blob */ |  | 
|   43   const char *zColumn,    /* The column containing the blob */ |  | 
|   44   sqlite_int64 iRow,      /* The row containing the glob */ |  | 
|   45   int flags,              /* True -> read/write access, false -> read-only */ |  | 
|   46   sqlite3_blob **ppBlob   /* Handle for accessing the blob returned here */ |  | 
|   47 ){ |  | 
|   48   int nAttempt = 0; |  | 
|   49   int iCol;               /* Index of zColumn in row-record */ |  | 
|   50  |  | 
|   51   /* This VDBE program seeks a btree cursor to the identified  |  | 
|   52   ** db/table/row entry. The reason for using a vdbe program instead |  | 
|   53   ** of writing code to use the b-tree layer directly is that the |  | 
|   54   ** vdbe program will take advantage of the various transaction, |  | 
|   55   ** locking and error handling infrastructure built into the vdbe. |  | 
|   56   ** |  | 
|   57   ** After seeking the cursor, the vdbe executes an OP_ResultRow. |  | 
|   58   ** Code external to the Vdbe then "borrows" the b-tree cursor and |  | 
|   59   ** uses it to implement the blob_read(), blob_write() and  |  | 
|   60   ** blob_bytes() functions. |  | 
|   61   ** |  | 
|   62   ** The sqlite3_blob_close() function finalizes the vdbe program, |  | 
|   63   ** which closes the b-tree cursor and (possibly) commits the  |  | 
|   64   ** transaction. |  | 
|   65   */ |  | 
|   66   static const VdbeOpList openBlob[] = { |  | 
|   67     {OP_Transaction, 0, 0, 0},     /* 0: Start a transaction */ |  | 
|   68     {OP_VerifyCookie, 0, 0, 0},    /* 1: Check the schema cookie */ |  | 
|   69     {OP_TableLock, 0, 0, 0},       /* 2: Acquire a read or write lock */ |  | 
|   70  |  | 
|   71     /* One of the following two instructions is replaced by an OP_Noop. */ |  | 
|   72     {OP_OpenRead, 0, 0, 0},        /* 3: Open cursor 0 for reading */ |  | 
|   73     {OP_OpenWrite, 0, 0, 0},       /* 4: Open cursor 0 for read/write */ |  | 
|   74  |  | 
|   75     {OP_Variable, 1, 1, 1},        /* 5: Push the rowid to the stack */ |  | 
|   76     {OP_NotExists, 0, 9, 1},       /* 6: Seek the cursor */ |  | 
|   77     {OP_Column, 0, 0, 1},          /* 7  */ |  | 
|   78     {OP_ResultRow, 1, 0, 0},       /* 8  */ |  | 
|   79     {OP_Close, 0, 0, 0},           /* 9  */ |  | 
|   80     {OP_Halt, 0, 0, 0},            /* 10 */ |  | 
|   81   }; |  | 
|   82  |  | 
|   83   Vdbe *v = 0; |  | 
|   84   int rc = SQLITE_OK; |  | 
|   85   char *zErr = 0; |  | 
|   86   Table *pTab; |  | 
|   87   Parse *pParse; |  | 
|   88  |  | 
|   89   *ppBlob = 0; |  | 
|   90   sqlite3_mutex_enter(db->mutex); |  | 
|   91   pParse = sqlite3StackAllocRaw(db, sizeof(*pParse)); |  | 
|   92   if( pParse==0 ){ |  | 
|   93     rc = SQLITE_NOMEM; |  | 
|   94     goto blob_open_out; |  | 
|   95   } |  | 
|   96   do { |  | 
|   97     memset(pParse, 0, sizeof(Parse)); |  | 
|   98     pParse->db = db; |  | 
|   99  |  | 
|  100     if( sqlite3SafetyOn(db) ){ |  | 
|  101       sqlite3DbFree(db, zErr); |  | 
|  102       sqlite3StackFree(db, pParse); |  | 
|  103       sqlite3_mutex_leave(db->mutex); |  | 
|  104       return SQLITE_MISUSE; |  | 
|  105     } |  | 
|  106  |  | 
|  107     sqlite3BtreeEnterAll(db); |  | 
|  108     pTab = sqlite3LocateTable(pParse, 0, zTable, zDb); |  | 
|  109     if( pTab && IsVirtual(pTab) ){ |  | 
|  110       pTab = 0; |  | 
|  111       sqlite3ErrorMsg(pParse, "cannot open virtual table: %s", zTable); |  | 
|  112     } |  | 
|  113 #ifndef SQLITE_OMIT_VIEW |  | 
|  114     if( pTab && pTab->pSelect ){ |  | 
|  115       pTab = 0; |  | 
|  116       sqlite3ErrorMsg(pParse, "cannot open view: %s", zTable); |  | 
|  117     } |  | 
|  118 #endif |  | 
|  119     if( !pTab ){ |  | 
|  120       if( pParse->zErrMsg ){ |  | 
|  121         sqlite3DbFree(db, zErr); |  | 
|  122         zErr = pParse->zErrMsg; |  | 
|  123         pParse->zErrMsg = 0; |  | 
|  124       } |  | 
|  125       rc = SQLITE_ERROR; |  | 
|  126       (void)sqlite3SafetyOff(db); |  | 
|  127       sqlite3BtreeLeaveAll(db); |  | 
|  128       goto blob_open_out; |  | 
|  129     } |  | 
|  130  |  | 
|  131     /* Now search pTab for the exact column. */ |  | 
|  132     for(iCol=0; iCol < pTab->nCol; iCol++) { |  | 
|  133       if( sqlite3StrICmp(pTab->aCol[iCol].zName, zColumn)==0 ){ |  | 
|  134         break; |  | 
|  135       } |  | 
|  136     } |  | 
|  137     if( iCol==pTab->nCol ){ |  | 
|  138       sqlite3DbFree(db, zErr); |  | 
|  139       zErr = sqlite3MPrintf(db, "no such column: \"%s\"", zColumn); |  | 
|  140       rc = SQLITE_ERROR; |  | 
|  141       (void)sqlite3SafetyOff(db); |  | 
|  142       sqlite3BtreeLeaveAll(db); |  | 
|  143       goto blob_open_out; |  | 
|  144     } |  | 
|  145  |  | 
|  146     /* If the value is being opened for writing, check that the |  | 
|  147     ** column is not indexed. It is against the rules to open an |  | 
|  148     ** indexed column for writing. |  | 
|  149     */ |  | 
|  150     if( flags ){ |  | 
|  151       Index *pIdx; |  | 
|  152       for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ |  | 
|  153         int j; |  | 
|  154         for(j=0; j<pIdx->nColumn; j++){ |  | 
|  155           if( pIdx->aiColumn[j]==iCol ){ |  | 
|  156             sqlite3DbFree(db, zErr); |  | 
|  157             zErr = sqlite3MPrintf(db, |  | 
|  158                              "cannot open indexed column for writing"); |  | 
|  159             rc = SQLITE_ERROR; |  | 
|  160             (void)sqlite3SafetyOff(db); |  | 
|  161             sqlite3BtreeLeaveAll(db); |  | 
|  162             goto blob_open_out; |  | 
|  163           } |  | 
|  164         } |  | 
|  165       } |  | 
|  166     } |  | 
|  167  |  | 
|  168     v = sqlite3VdbeCreate(db); |  | 
|  169     if( v ){ |  | 
|  170       int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |  | 
|  171       sqlite3VdbeAddOpList(v, sizeof(openBlob)/sizeof(VdbeOpList), openBlob); |  | 
|  172       flags = !!flags;                 /* flags = (flags ? 1 : 0); */ |  | 
|  173  |  | 
|  174       /* Configure the OP_Transaction */ |  | 
|  175       sqlite3VdbeChangeP1(v, 0, iDb); |  | 
|  176       sqlite3VdbeChangeP2(v, 0, flags); |  | 
|  177  |  | 
|  178       /* Configure the OP_VerifyCookie */ |  | 
|  179       sqlite3VdbeChangeP1(v, 1, iDb); |  | 
|  180       sqlite3VdbeChangeP2(v, 1, pTab->pSchema->schema_cookie); |  | 
|  181  |  | 
|  182       /* Make sure a mutex is held on the table to be accessed */ |  | 
|  183       sqlite3VdbeUsesBtree(v, iDb);  |  | 
|  184  |  | 
|  185       /* Configure the OP_TableLock instruction */ |  | 
|  186       sqlite3VdbeChangeP1(v, 2, iDb); |  | 
|  187       sqlite3VdbeChangeP2(v, 2, pTab->tnum); |  | 
|  188       sqlite3VdbeChangeP3(v, 2, flags); |  | 
|  189       sqlite3VdbeChangeP4(v, 2, pTab->zName, P4_TRANSIENT); |  | 
|  190  |  | 
|  191       /* Remove either the OP_OpenWrite or OpenRead. Set the P2  |  | 
|  192       ** parameter of the other to pTab->tnum.  */ |  | 
|  193       sqlite3VdbeChangeToNoop(v, 4 - flags, 1); |  | 
|  194       sqlite3VdbeChangeP2(v, 3 + flags, pTab->tnum); |  | 
|  195       sqlite3VdbeChangeP3(v, 3 + flags, iDb); |  | 
|  196  |  | 
|  197       /* Configure the number of columns. Configure the cursor to |  | 
|  198       ** think that the table has one more column than it really |  | 
|  199       ** does. An OP_Column to retrieve this imaginary column will |  | 
|  200       ** always return an SQL NULL. This is useful because it means |  | 
|  201       ** we can invoke OP_Column to fill in the vdbe cursors type  |  | 
|  202       ** and offset cache without causing any IO. |  | 
|  203       */ |  | 
|  204       sqlite3VdbeChangeP4(v, 3+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32); |  | 
|  205       sqlite3VdbeChangeP2(v, 7, pTab->nCol); |  | 
|  206       if( !db->mallocFailed ){ |  | 
|  207         sqlite3VdbeMakeReady(v, 1, 1, 1, 0, 0, 0); |  | 
|  208       } |  | 
|  209     } |  | 
|  210     |  | 
|  211     sqlite3BtreeLeaveAll(db); |  | 
|  212     rc = sqlite3SafetyOff(db); |  | 
|  213     if( NEVER(rc!=SQLITE_OK) || db->mallocFailed ){ |  | 
|  214       goto blob_open_out; |  | 
|  215     } |  | 
|  216  |  | 
|  217     sqlite3_bind_int64((sqlite3_stmt *)v, 1, iRow); |  | 
|  218     rc = sqlite3_step((sqlite3_stmt *)v); |  | 
|  219     if( rc!=SQLITE_ROW ){ |  | 
|  220       nAttempt++; |  | 
|  221       rc = sqlite3_finalize((sqlite3_stmt *)v); |  | 
|  222       sqlite3DbFree(db, zErr); |  | 
|  223       zErr = sqlite3MPrintf(db, sqlite3_errmsg(db)); |  | 
|  224       v = 0; |  | 
|  225     } |  | 
|  226   } while( nAttempt<5 && rc==SQLITE_SCHEMA ); |  | 
|  227  |  | 
|  228   if( rc==SQLITE_ROW ){ |  | 
|  229     /* The row-record has been opened successfully. Check that the |  | 
|  230     ** column in question contains text or a blob. If it contains |  | 
|  231     ** text, it is up to the caller to get the encoding right. |  | 
|  232     */ |  | 
|  233     Incrblob *pBlob; |  | 
|  234     u32 type = v->apCsr[0]->aType[iCol]; |  | 
|  235  |  | 
|  236     if( type<12 ){ |  | 
|  237       sqlite3DbFree(db, zErr); |  | 
|  238       zErr = sqlite3MPrintf(db, "cannot open value of type %s", |  | 
|  239           type==0?"null": type==7?"real": "integer" |  | 
|  240       ); |  | 
|  241       rc = SQLITE_ERROR; |  | 
|  242       goto blob_open_out; |  | 
|  243     } |  | 
|  244     pBlob = (Incrblob *)sqlite3DbMallocZero(db, sizeof(Incrblob)); |  | 
|  245     if( db->mallocFailed ){ |  | 
|  246       sqlite3DbFree(db, pBlob); |  | 
|  247       goto blob_open_out; |  | 
|  248     } |  | 
|  249     pBlob->flags = flags; |  | 
|  250     pBlob->pCsr =  v->apCsr[0]->pCursor; |  | 
|  251     sqlite3BtreeEnterCursor(pBlob->pCsr); |  | 
|  252     sqlite3BtreeCacheOverflow(pBlob->pCsr); |  | 
|  253     sqlite3BtreeLeaveCursor(pBlob->pCsr); |  | 
|  254     pBlob->pStmt = (sqlite3_stmt *)v; |  | 
|  255     pBlob->iOffset = v->apCsr[0]->aOffset[iCol]; |  | 
|  256     pBlob->nByte = sqlite3VdbeSerialTypeLen(type); |  | 
|  257     pBlob->db = db; |  | 
|  258     *ppBlob = (sqlite3_blob *)pBlob; |  | 
|  259     rc = SQLITE_OK; |  | 
|  260   }else if( rc==SQLITE_OK ){ |  | 
|  261     sqlite3DbFree(db, zErr); |  | 
|  262     zErr = sqlite3MPrintf(db, "no such rowid: %lld", iRow); |  | 
|  263     rc = SQLITE_ERROR; |  | 
|  264   } |  | 
|  265  |  | 
|  266 blob_open_out: |  | 
|  267   if( v && (rc!=SQLITE_OK || db->mallocFailed) ){ |  | 
|  268     sqlite3VdbeFinalize(v); |  | 
|  269   } |  | 
|  270   sqlite3Error(db, rc, zErr); |  | 
|  271   sqlite3DbFree(db, zErr); |  | 
|  272   sqlite3StackFree(db, pParse); |  | 
|  273   rc = sqlite3ApiExit(db, rc); |  | 
|  274   sqlite3_mutex_leave(db->mutex); |  | 
|  275   return rc; |  | 
|  276 } |  | 
|  277  |  | 
|  278 /* |  | 
|  279 ** Close a blob handle that was previously created using |  | 
|  280 ** sqlite3_blob_open(). |  | 
|  281 */ |  | 
|  282 int sqlite3_blob_close(sqlite3_blob *pBlob){ |  | 
|  283   Incrblob *p = (Incrblob *)pBlob; |  | 
|  284   int rc; |  | 
|  285   sqlite3 *db; |  | 
|  286  |  | 
|  287   if( p ){ |  | 
|  288     db = p->db; |  | 
|  289     sqlite3_mutex_enter(db->mutex); |  | 
|  290     rc = sqlite3_finalize(p->pStmt); |  | 
|  291     sqlite3DbFree(db, p); |  | 
|  292     sqlite3_mutex_leave(db->mutex); |  | 
|  293   }else{ |  | 
|  294     rc = SQLITE_OK; |  | 
|  295   } |  | 
|  296   return rc; |  | 
|  297 } |  | 
|  298  |  | 
|  299 /* |  | 
|  300 ** Perform a read or write operation on a blob |  | 
|  301 */ |  | 
|  302 static int blobReadWrite( |  | 
|  303   sqlite3_blob *pBlob,  |  | 
|  304   void *z,  |  | 
|  305   int n,  |  | 
|  306   int iOffset,  |  | 
|  307   int (*xCall)(BtCursor*, u32, u32, void*) |  | 
|  308 ){ |  | 
|  309   int rc; |  | 
|  310   Incrblob *p = (Incrblob *)pBlob; |  | 
|  311   Vdbe *v; |  | 
|  312   sqlite3 *db; |  | 
|  313  |  | 
|  314   if( p==0 ) return SQLITE_MISUSE; |  | 
|  315   db = p->db; |  | 
|  316   sqlite3_mutex_enter(db->mutex); |  | 
|  317   v = (Vdbe*)p->pStmt; |  | 
|  318  |  | 
|  319   if( n<0 || iOffset<0 || (iOffset+n)>p->nByte ){ |  | 
|  320     /* Request is out of range. Return a transient error. */ |  | 
|  321     rc = SQLITE_ERROR; |  | 
|  322     sqlite3Error(db, SQLITE_ERROR, 0); |  | 
|  323   } else if( v==0 ){ |  | 
|  324     /* If there is no statement handle, then the blob-handle has |  | 
|  325     ** already been invalidated. Return SQLITE_ABORT in this case. |  | 
|  326     */ |  | 
|  327     rc = SQLITE_ABORT; |  | 
|  328   }else{ |  | 
|  329     /* Call either BtreeData() or BtreePutData(). If SQLITE_ABORT is |  | 
|  330     ** returned, clean-up the statement handle. |  | 
|  331     */ |  | 
|  332     assert( db == v->db ); |  | 
|  333     sqlite3BtreeEnterCursor(p->pCsr); |  | 
|  334     rc = xCall(p->pCsr, iOffset+p->iOffset, n, z); |  | 
|  335     sqlite3BtreeLeaveCursor(p->pCsr); |  | 
|  336     if( rc==SQLITE_ABORT ){ |  | 
|  337       sqlite3VdbeFinalize(v); |  | 
|  338       p->pStmt = 0; |  | 
|  339     }else{ |  | 
|  340       db->errCode = rc; |  | 
|  341       v->rc = rc; |  | 
|  342     } |  | 
|  343   } |  | 
|  344   rc = sqlite3ApiExit(db, rc); |  | 
|  345   sqlite3_mutex_leave(db->mutex); |  | 
|  346   return rc; |  | 
|  347 } |  | 
|  348  |  | 
|  349 /* |  | 
|  350 ** Read data from a blob handle. |  | 
|  351 */ |  | 
|  352 int sqlite3_blob_read(sqlite3_blob *pBlob, void *z, int n, int iOffset){ |  | 
|  353   return blobReadWrite(pBlob, z, n, iOffset, sqlite3BtreeData); |  | 
|  354 } |  | 
|  355  |  | 
|  356 /* |  | 
|  357 ** Write data to a blob handle. |  | 
|  358 */ |  | 
|  359 int sqlite3_blob_write(sqlite3_blob *pBlob, const void *z, int n, int iOffset){ |  | 
|  360   return blobReadWrite(pBlob, (void *)z, n, iOffset, sqlite3BtreePutData); |  | 
|  361 } |  | 
|  362  |  | 
|  363 /* |  | 
|  364 ** Query a blob handle for the size of the data. |  | 
|  365 ** |  | 
|  366 ** The Incrblob.nByte field is fixed for the lifetime of the Incrblob |  | 
|  367 ** so no mutex is required for access. |  | 
|  368 */ |  | 
|  369 int sqlite3_blob_bytes(sqlite3_blob *pBlob){ |  | 
|  370   Incrblob *p = (Incrblob *)pBlob; |  | 
|  371   return p ? p->nByte : 0; |  | 
|  372 } |  | 
|  373  |  | 
|  374 #endif /* #ifndef SQLITE_OMIT_INCRBLOB */ |  | 
| OLD | NEW |