diff options
author | dan miller | 2007-10-20 05:34:26 +0000 |
---|---|---|
committer | dan miller | 2007-10-20 05:34:26 +0000 |
commit | 354ea97baf765759911f0c56d3ed511350ebe348 (patch) | |
tree | 1adf96a98045d24b8741ba02bf21d195e70993ca /libraries/sqlite/win32/vdbeblob.c | |
parent | sqlite source (unix build) added to libraries (diff) | |
download | opensim-SC-354ea97baf765759911f0c56d3ed511350ebe348.zip opensim-SC-354ea97baf765759911f0c56d3ed511350ebe348.tar.gz opensim-SC-354ea97baf765759911f0c56d3ed511350ebe348.tar.bz2 opensim-SC-354ea97baf765759911f0c56d3ed511350ebe348.tar.xz |
sqlite 3.5.1 windows source
Diffstat (limited to '')
-rwxr-xr-x | libraries/sqlite/win32/vdbeblob.c | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/libraries/sqlite/win32/vdbeblob.c b/libraries/sqlite/win32/vdbeblob.c new file mode 100755 index 0000000..d56fbd1 --- /dev/null +++ b/libraries/sqlite/win32/vdbeblob.c | |||
@@ -0,0 +1,340 @@ | |||
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.16 2007/08/30 01:19:59 drh 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_Callback. | ||
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_Integer, 0, 0, 0}, /* 2: Database number */ | ||
70 | |||
71 | /* One of the following two instructions is replaced by an | ||
72 | ** OP_Noop before exection. | ||
73 | */ | ||
74 | {OP_OpenRead, 0, 0, 0}, /* 3: Open cursor 0 for reading */ | ||
75 | {OP_OpenWrite, 0, 0, 0}, /* 4: Open cursor 0 for read/write */ | ||
76 | {OP_SetNumColumns, 0, 0, 0}, /* 5: Num cols for cursor */ | ||
77 | |||
78 | {OP_Variable, 1, 0, 0}, /* 6: Push the rowid to the stack */ | ||
79 | {OP_NotExists, 0, 10, 0}, /* 7: Seek the cursor */ | ||
80 | {OP_Column, 0, 0, 0}, /* 8 */ | ||
81 | {OP_Callback, 0, 0, 0}, /* 9 */ | ||
82 | {OP_Close, 0, 0, 0}, /* 10 */ | ||
83 | {OP_Halt, 0, 0, 0}, /* 11 */ | ||
84 | }; | ||
85 | |||
86 | Vdbe *v = 0; | ||
87 | int rc = SQLITE_OK; | ||
88 | char zErr[128]; | ||
89 | |||
90 | zErr[0] = 0; | ||
91 | sqlite3_mutex_enter(db->mutex); | ||
92 | do { | ||
93 | Parse sParse; | ||
94 | Table *pTab; | ||
95 | |||
96 | memset(&sParse, 0, sizeof(Parse)); | ||
97 | sParse.db = db; | ||
98 | |||
99 | rc = sqlite3SafetyOn(db); | ||
100 | if( rc!=SQLITE_OK ){ | ||
101 | sqlite3_mutex_leave(db->mutex); | ||
102 | return rc; | ||
103 | } | ||
104 | |||
105 | sqlite3BtreeEnterAll(db); | ||
106 | pTab = sqlite3LocateTable(&sParse, zTable, zDb); | ||
107 | if( !pTab ){ | ||
108 | if( sParse.zErrMsg ){ | ||
109 | sqlite3_snprintf(sizeof(zErr), zErr, "%s", sParse.zErrMsg); | ||
110 | } | ||
111 | sqlite3_free(sParse.zErrMsg); | ||
112 | rc = SQLITE_ERROR; | ||
113 | sqlite3SafetyOff(db); | ||
114 | sqlite3BtreeLeaveAll(db); | ||
115 | goto blob_open_out; | ||
116 | } | ||
117 | |||
118 | /* Now search pTab for the exact column. */ | ||
119 | for(iCol=0; iCol < pTab->nCol; iCol++) { | ||
120 | if( sqlite3StrICmp(pTab->aCol[iCol].zName, zColumn)==0 ){ | ||
121 | break; | ||
122 | } | ||
123 | } | ||
124 | if( iCol==pTab->nCol ){ | ||
125 | sqlite3_snprintf(sizeof(zErr), zErr, "no such column: \"%s\"", zColumn); | ||
126 | rc = SQLITE_ERROR; | ||
127 | sqlite3SafetyOff(db); | ||
128 | sqlite3BtreeLeaveAll(db); | ||
129 | goto blob_open_out; | ||
130 | } | ||
131 | |||
132 | /* If the value is being opened for writing, check that the | ||
133 | ** column is not indexed. It is against the rules to open an | ||
134 | ** indexed column for writing. | ||
135 | */ | ||
136 | if( flags ){ | ||
137 | Index *pIdx; | ||
138 | for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ | ||
139 | int j; | ||
140 | for(j=0; j<pIdx->nColumn; j++){ | ||
141 | if( pIdx->aiColumn[j]==iCol ){ | ||
142 | sqlite3_snprintf(sizeof(zErr), zErr, | ||
143 | "cannot open indexed column for writing"); | ||
144 | rc = SQLITE_ERROR; | ||
145 | sqlite3SafetyOff(db); | ||
146 | sqlite3BtreeLeaveAll(db); | ||
147 | goto blob_open_out; | ||
148 | } | ||
149 | } | ||
150 | } | ||
151 | } | ||
152 | |||
153 | v = sqlite3VdbeCreate(db); | ||
154 | if( v ){ | ||
155 | int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); | ||
156 | sqlite3VdbeAddOpList(v, sizeof(openBlob)/sizeof(VdbeOpList), openBlob); | ||
157 | |||
158 | /* Configure the OP_Transaction */ | ||
159 | sqlite3VdbeChangeP1(v, 0, iDb); | ||
160 | sqlite3VdbeChangeP2(v, 0, (flags ? 1 : 0)); | ||
161 | |||
162 | /* Configure the OP_VerifyCookie */ | ||
163 | sqlite3VdbeChangeP1(v, 1, iDb); | ||
164 | sqlite3VdbeChangeP2(v, 1, pTab->pSchema->schema_cookie); | ||
165 | |||
166 | /* Make sure a mutex is held on the table to be accessed */ | ||
167 | sqlite3VdbeUsesBtree(v, iDb); | ||
168 | |||
169 | /* Configure the db number pushed onto the stack */ | ||
170 | sqlite3VdbeChangeP1(v, 2, iDb); | ||
171 | |||
172 | /* Remove either the OP_OpenWrite or OpenRead. Set the P2 | ||
173 | ** parameter of the other to pTab->tnum. | ||
174 | */ | ||
175 | sqlite3VdbeChangeToNoop(v, (flags ? 3 : 4), 1); | ||
176 | sqlite3VdbeChangeP2(v, (flags ? 4 : 3), pTab->tnum); | ||
177 | |||
178 | /* Configure the OP_SetNumColumns. Configure the cursor to | ||
179 | ** think that the table has one more column than it really | ||
180 | ** does. An OP_Column to retrieve this imaginary column will | ||
181 | ** always return an SQL NULL. This is useful because it means | ||
182 | ** we can invoke OP_Column to fill in the vdbe cursors type | ||
183 | ** and offset cache without causing any IO. | ||
184 | */ | ||
185 | sqlite3VdbeChangeP2(v, 5, pTab->nCol+1); | ||
186 | if( !db->mallocFailed ){ | ||
187 | sqlite3VdbeMakeReady(v, 1, 0, 1, 0); | ||
188 | } | ||
189 | } | ||
190 | |||
191 | sqlite3BtreeLeaveAll(db); | ||
192 | rc = sqlite3SafetyOff(db); | ||
193 | if( rc!=SQLITE_OK || db->mallocFailed ){ | ||
194 | goto blob_open_out; | ||
195 | } | ||
196 | |||
197 | sqlite3_bind_int64((sqlite3_stmt *)v, 1, iRow); | ||
198 | rc = sqlite3_step((sqlite3_stmt *)v); | ||
199 | if( rc!=SQLITE_ROW ){ | ||
200 | nAttempt++; | ||
201 | rc = sqlite3_finalize((sqlite3_stmt *)v); | ||
202 | sqlite3_snprintf(sizeof(zErr), zErr, sqlite3_errmsg(db)); | ||
203 | v = 0; | ||
204 | } | ||
205 | } while( nAttempt<5 && rc==SQLITE_SCHEMA ); | ||
206 | |||
207 | if( rc==SQLITE_ROW ){ | ||
208 | /* The row-record has been opened successfully. Check that the | ||
209 | ** column in question contains text or a blob. If it contains | ||
210 | ** text, it is up to the caller to get the encoding right. | ||
211 | */ | ||
212 | Incrblob *pBlob; | ||
213 | u32 type = v->apCsr[0]->aType[iCol]; | ||
214 | |||
215 | if( type<12 ){ | ||
216 | sqlite3_snprintf(sizeof(zErr), zErr, "cannot open value of type %s", | ||
217 | type==0?"null": type==7?"real": "integer" | ||
218 | ); | ||
219 | rc = SQLITE_ERROR; | ||
220 | goto blob_open_out; | ||
221 | } | ||
222 | pBlob = (Incrblob *)sqlite3DbMallocZero(db, sizeof(Incrblob)); | ||
223 | if( db->mallocFailed ){ | ||
224 | sqlite3_free(pBlob); | ||
225 | goto blob_open_out; | ||
226 | } | ||
227 | pBlob->flags = flags; | ||
228 | pBlob->pCsr = v->apCsr[0]->pCursor; | ||
229 | sqlite3BtreeEnterCursor(pBlob->pCsr); | ||
230 | sqlite3BtreeCacheOverflow(pBlob->pCsr); | ||
231 | sqlite3BtreeLeaveCursor(pBlob->pCsr); | ||
232 | pBlob->pStmt = (sqlite3_stmt *)v; | ||
233 | pBlob->iOffset = v->apCsr[0]->aOffset[iCol]; | ||
234 | pBlob->nByte = sqlite3VdbeSerialTypeLen(type); | ||
235 | pBlob->db = db; | ||
236 | *ppBlob = (sqlite3_blob *)pBlob; | ||
237 | rc = SQLITE_OK; | ||
238 | }else if( rc==SQLITE_OK ){ | ||
239 | sqlite3_snprintf(sizeof(zErr), zErr, "no such rowid: %lld", iRow); | ||
240 | rc = SQLITE_ERROR; | ||
241 | } | ||
242 | |||
243 | blob_open_out: | ||
244 | zErr[sizeof(zErr)-1] = '\0'; | ||
245 | if( rc!=SQLITE_OK || db->mallocFailed ){ | ||
246 | sqlite3_finalize((sqlite3_stmt *)v); | ||
247 | } | ||
248 | sqlite3Error(db, rc, (rc==SQLITE_OK?0:zErr)); | ||
249 | rc = sqlite3ApiExit(db, rc); | ||
250 | sqlite3_mutex_leave(db->mutex); | ||
251 | return rc; | ||
252 | } | ||
253 | |||
254 | /* | ||
255 | ** Close a blob handle that was previously created using | ||
256 | ** sqlite3_blob_open(). | ||
257 | */ | ||
258 | int sqlite3_blob_close(sqlite3_blob *pBlob){ | ||
259 | Incrblob *p = (Incrblob *)pBlob; | ||
260 | int rc; | ||
261 | |||
262 | rc = sqlite3_finalize(p->pStmt); | ||
263 | sqlite3_free(p); | ||
264 | return rc; | ||
265 | } | ||
266 | |||
267 | /* | ||
268 | ** Perform a read or write operation on a blob | ||
269 | */ | ||
270 | static int blobReadWrite( | ||
271 | sqlite3_blob *pBlob, | ||
272 | void *z, | ||
273 | int n, | ||
274 | int iOffset, | ||
275 | int (*xCall)(BtCursor*, u32, u32, void*) | ||
276 | ){ | ||
277 | int rc; | ||
278 | Incrblob *p = (Incrblob *)pBlob; | ||
279 | Vdbe *v; | ||
280 | sqlite3 *db = p->db; | ||
281 | |||
282 | /* Request is out of range. Return a transient error. */ | ||
283 | if( (iOffset+n)>p->nByte ){ | ||
284 | return SQLITE_ERROR; | ||
285 | } | ||
286 | sqlite3_mutex_enter(db->mutex); | ||
287 | |||
288 | /* If there is no statement handle, then the blob-handle has | ||
289 | ** already been invalidated. Return SQLITE_ABORT in this case. | ||
290 | */ | ||
291 | v = (Vdbe*)p->pStmt; | ||
292 | if( v==0 ){ | ||
293 | rc = SQLITE_ABORT; | ||
294 | }else{ | ||
295 | /* Call either BtreeData() or BtreePutData(). If SQLITE_ABORT is | ||
296 | ** returned, clean-up the statement handle. | ||
297 | */ | ||
298 | assert( db == v->db ); | ||
299 | sqlite3BtreeEnterCursor(p->pCsr); | ||
300 | rc = xCall(p->pCsr, iOffset+p->iOffset, n, z); | ||
301 | sqlite3BtreeLeaveCursor(p->pCsr); | ||
302 | if( rc==SQLITE_ABORT ){ | ||
303 | sqlite3VdbeFinalize(v); | ||
304 | p->pStmt = 0; | ||
305 | }else{ | ||
306 | db->errCode = rc; | ||
307 | v->rc = rc; | ||
308 | } | ||
309 | } | ||
310 | rc = sqlite3ApiExit(db, rc); | ||
311 | sqlite3_mutex_leave(db->mutex); | ||
312 | return rc; | ||
313 | } | ||
314 | |||
315 | /* | ||
316 | ** Read data from a blob handle. | ||
317 | */ | ||
318 | int sqlite3_blob_read(sqlite3_blob *pBlob, void *z, int n, int iOffset){ | ||
319 | return blobReadWrite(pBlob, z, n, iOffset, sqlite3BtreeData); | ||
320 | } | ||
321 | |||
322 | /* | ||
323 | ** Write data to a blob handle. | ||
324 | */ | ||
325 | int sqlite3_blob_write(sqlite3_blob *pBlob, const void *z, int n, int iOffset){ | ||
326 | return blobReadWrite(pBlob, (void *)z, n, iOffset, sqlite3BtreePutData); | ||
327 | } | ||
328 | |||
329 | /* | ||
330 | ** Query a blob handle for the size of the data. | ||
331 | ** | ||
332 | ** The Incrblob.nByte field is fixed for the lifetime of the Incrblob | ||
333 | ** so no mutex is required for access. | ||
334 | */ | ||
335 | int sqlite3_blob_bytes(sqlite3_blob *pBlob){ | ||
336 | Incrblob *p = (Incrblob *)pBlob; | ||
337 | return p->nByte; | ||
338 | } | ||
339 | |||
340 | #endif /* #ifndef SQLITE_OMIT_INCRBLOB */ | ||