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