Branch data Line data Source code
1 : : #include "blob.h"
2 : : #include "util.h"
3 : :
4 : : #define clinic_state() (pysqlite_get_state_by_type(Py_TYPE(self)))
5 : : #include "clinic/blob.c.h"
6 : : #undef clinic_state
7 : :
8 : : /*[clinic input]
9 : : module _sqlite3
10 : : class _sqlite3.Blob "pysqlite_Blob *" "clinic_state()->BlobType"
11 : : [clinic start generated code]*/
12 : : /*[clinic end generated code: output=da39a3ee5e6b4b0d input=908d3e16a45f8da7]*/
13 : :
14 : : static void
15 : 123 : close_blob(pysqlite_Blob *self)
16 : : {
17 [ + + ]: 123 : if (self->blob) {
18 : 42 : sqlite3_blob *blob = self->blob;
19 : 42 : self->blob = NULL;
20 : :
21 : 42 : Py_BEGIN_ALLOW_THREADS
22 : 42 : sqlite3_blob_close(blob);
23 : 42 : Py_END_ALLOW_THREADS
24 : : }
25 : 123 : }
26 : :
27 : : static int
28 : 0 : blob_traverse(pysqlite_Blob *self, visitproc visit, void *arg)
29 : : {
30 [ # # # # ]: 0 : Py_VISIT(Py_TYPE(self));
31 [ # # # # ]: 0 : Py_VISIT(self->connection);
32 : 0 : return 0;
33 : : }
34 : :
35 : : static int
36 : 42 : blob_clear(pysqlite_Blob *self)
37 : : {
38 [ + - ]: 42 : Py_CLEAR(self->connection);
39 : 42 : return 0;
40 : : }
41 : :
42 : : static void
43 : 42 : blob_dealloc(pysqlite_Blob *self)
44 : : {
45 : 42 : PyTypeObject *tp = Py_TYPE(self);
46 : 42 : PyObject_GC_UnTrack(self);
47 : :
48 : 42 : close_blob(self);
49 : :
50 [ + - ]: 42 : if (self->in_weakreflist != NULL) {
51 : 42 : PyObject_ClearWeakRefs((PyObject*)self);
52 : : }
53 : 42 : tp->tp_clear((PyObject *)self);
54 : 42 : tp->tp_free(self);
55 : 42 : Py_DECREF(tp);
56 : 42 : }
57 : :
58 : : // Return 1 if the blob object is usable, 0 if not.
59 : : static int
60 : 103 : check_blob(pysqlite_Blob *self)
61 : : {
62 [ + + - + ]: 205 : if (!pysqlite_check_connection(self->connection) ||
63 : 102 : !pysqlite_check_thread(self->connection)) {
64 : 1 : return 0;
65 : : }
66 [ + + ]: 102 : if (self->blob == NULL) {
67 : 11 : pysqlite_state *state = self->connection->state;
68 : 11 : PyErr_SetString(state->ProgrammingError,
69 : : "Cannot operate on a closed blob.");
70 : 11 : return 0;
71 : : }
72 : 91 : return 1;
73 : : }
74 : :
75 : :
76 : : /*[clinic input]
77 : : _sqlite3.Blob.close as blob_close
78 : :
79 : : Close the blob.
80 : : [clinic start generated code]*/
81 : :
82 : : static PyObject *
83 : 39 : blob_close_impl(pysqlite_Blob *self)
84 : : /*[clinic end generated code: output=848accc20a138d1b input=7bc178a402a40bd8]*/
85 : : {
86 [ + - - + ]: 78 : if (!pysqlite_check_connection(self->connection) ||
87 : 39 : !pysqlite_check_thread(self->connection))
88 : : {
89 : 0 : return NULL;
90 : : }
91 : 39 : close_blob(self);
92 : 39 : Py_RETURN_NONE;
93 : : };
94 : :
95 : : void
96 : 391 : pysqlite_close_all_blobs(pysqlite_Connection *self)
97 : : {
98 [ + + ]: 434 : for (int i = 0; i < PyList_GET_SIZE(self->blobs); i++) {
99 : 43 : PyObject *weakref = PyList_GET_ITEM(self->blobs, i);
100 : 43 : PyObject *blob = PyWeakref_GetObject(weakref);
101 [ + + ]: 43 : if (!Py_IsNone(blob)) {
102 : 40 : close_blob((pysqlite_Blob *)blob);
103 : : }
104 : : }
105 : 391 : }
106 : :
107 : : static void
108 : 4 : blob_seterror(pysqlite_Blob *self, int rc)
109 : : {
110 : : assert(self->connection != NULL);
111 : : #if SQLITE_VERSION_NUMBER < 3008008
112 : : // SQLite pre 3.8.8 does not set this blob error on the connection
113 : : if (rc == SQLITE_ABORT) {
114 : : PyErr_SetString(self->connection->OperationalError,
115 : : "Cannot operate on an expired blob handle");
116 : : return;
117 : : }
118 : : #endif
119 : 4 : _pysqlite_seterror(self->connection->state, self->connection->db);
120 : 4 : }
121 : :
122 : : static PyObject *
123 : 7 : read_single(pysqlite_Blob *self, Py_ssize_t offset)
124 : : {
125 : 7 : unsigned char buf = 0;
126 : : int rc;
127 : 7 : Py_BEGIN_ALLOW_THREADS
128 : 7 : rc = sqlite3_blob_read(self->blob, (void *)&buf, 1, (int)offset);
129 : 7 : Py_END_ALLOW_THREADS
130 : :
131 [ + + ]: 7 : if (rc != SQLITE_OK) {
132 : 1 : blob_seterror(self, rc);
133 : 1 : return NULL;
134 : : }
135 : 6 : return PyLong_FromUnsignedLong((unsigned long)buf);
136 : : }
137 : :
138 : : static PyObject *
139 : 15 : read_multiple(pysqlite_Blob *self, Py_ssize_t length, Py_ssize_t offset)
140 : : {
141 : : assert(length <= sqlite3_blob_bytes(self->blob));
142 : : assert(offset < sqlite3_blob_bytes(self->blob));
143 : :
144 : 15 : PyObject *buffer = PyBytes_FromStringAndSize(NULL, length);
145 [ - + ]: 15 : if (buffer == NULL) {
146 : 0 : return NULL;
147 : : }
148 : :
149 : 15 : char *raw_buffer = PyBytes_AS_STRING(buffer);
150 : : int rc;
151 : 15 : Py_BEGIN_ALLOW_THREADS
152 : 15 : rc = sqlite3_blob_read(self->blob, raw_buffer, (int)length, (int)offset);
153 : 15 : Py_END_ALLOW_THREADS
154 : :
155 [ + + ]: 15 : if (rc != SQLITE_OK) {
156 : 1 : Py_DECREF(buffer);
157 : 1 : blob_seterror(self, rc);
158 : 1 : return NULL;
159 : : }
160 : 14 : return buffer;
161 : : }
162 : :
163 : :
164 : : /*[clinic input]
165 : : _sqlite3.Blob.read as blob_read
166 : :
167 : : length: int = -1
168 : : Read length in bytes.
169 : : /
170 : :
171 : : Read data at the current offset position.
172 : :
173 : : If the end of the blob is reached, the data up to end of file will be returned.
174 : : When length is not specified, or is negative, Blob.read() will read until the
175 : : end of the blob.
176 : : [clinic start generated code]*/
177 : :
178 : : static PyObject *
179 : 10 : blob_read_impl(pysqlite_Blob *self, int length)
180 : : /*[clinic end generated code: output=1fc99b2541360dde input=f2e4aa4378837250]*/
181 : : {
182 [ + + ]: 10 : if (!check_blob(self)) {
183 : 3 : return NULL;
184 : : }
185 : :
186 : : /* Make sure we never read past "EOB". Also read the rest of the blob if a
187 : : * negative length is specified. */
188 : 7 : int blob_len = sqlite3_blob_bytes(self->blob);
189 : 7 : int max_read_len = blob_len - self->offset;
190 [ + + + + ]: 7 : if (length < 0 || length > max_read_len) {
191 : 5 : length = max_read_len;
192 : : }
193 : :
194 : : assert(length >= 0);
195 [ + + ]: 7 : if (length == 0) {
196 : 1 : return PyBytes_FromStringAndSize(NULL, 0);
197 : : }
198 : :
199 : 6 : PyObject *buffer = read_multiple(self, length, self->offset);
200 [ + + ]: 6 : if (buffer == NULL) {
201 : 1 : return NULL;
202 : : }
203 : 5 : self->offset += length;
204 : 5 : return buffer;
205 : : };
206 : :
207 : : static int
208 : 19 : inner_write(pysqlite_Blob *self, const void *buf, Py_ssize_t len,
209 : : Py_ssize_t offset)
210 : : {
211 : 19 : Py_ssize_t blob_len = sqlite3_blob_bytes(self->blob);
212 : 19 : Py_ssize_t remaining_len = blob_len - offset;
213 [ + + ]: 19 : if (len > remaining_len) {
214 : 2 : PyErr_SetString(PyExc_ValueError, "data longer than blob length");
215 : 2 : return -1;
216 : : }
217 : :
218 : : assert(offset <= blob_len);
219 : : int rc;
220 : 17 : Py_BEGIN_ALLOW_THREADS
221 : 17 : rc = sqlite3_blob_write(self->blob, buf, (int)len, (int)offset);
222 : 17 : Py_END_ALLOW_THREADS
223 : :
224 [ + + ]: 17 : if (rc != SQLITE_OK) {
225 : 2 : blob_seterror(self, rc);
226 : 2 : return -1;
227 : : }
228 : 15 : return 0;
229 : : }
230 : :
231 : :
232 : : /*[clinic input]
233 : : _sqlite3.Blob.write as blob_write
234 : :
235 : : data: Py_buffer
236 : : /
237 : :
238 : : Write data at the current offset.
239 : :
240 : : This function cannot change the blob length. Writing beyond the end of the
241 : : blob will result in an exception being raised.
242 : : [clinic start generated code]*/
243 : :
244 : : static PyObject *
245 : 11 : blob_write_impl(pysqlite_Blob *self, Py_buffer *data)
246 : : /*[clinic end generated code: output=b34cf22601b570b2 input=a84712f24a028e6d]*/
247 : : {
248 [ + + ]: 11 : if (!check_blob(self)) {
249 : 1 : return NULL;
250 : : }
251 : :
252 : 10 : int rc = inner_write(self, data->buf, data->len, self->offset);
253 [ + + ]: 10 : if (rc < 0) {
254 : 4 : return NULL;
255 : : }
256 : 6 : self->offset += (int)data->len;
257 : 6 : Py_RETURN_NONE;
258 : : }
259 : :
260 : :
261 : : /*[clinic input]
262 : : _sqlite3.Blob.seek as blob_seek
263 : :
264 : : offset: int
265 : : origin: int = 0
266 : : /
267 : :
268 : : Set the current access position to offset.
269 : :
270 : : The origin argument defaults to os.SEEK_SET (absolute blob positioning).
271 : : Other values for origin are os.SEEK_CUR (seek relative to the current position)
272 : : and os.SEEK_END (seek relative to the blob's end).
273 : : [clinic start generated code]*/
274 : :
275 : : static PyObject *
276 : 17 : blob_seek_impl(pysqlite_Blob *self, int offset, int origin)
277 : : /*[clinic end generated code: output=854c5a0e208547a5 input=5da9a07e55fe6bb6]*/
278 : : {
279 [ + + ]: 17 : if (!check_blob(self)) {
280 : 1 : return NULL;
281 : : }
282 : :
283 : 16 : int blob_len = sqlite3_blob_bytes(self->blob);
284 [ + + + + ]: 16 : switch (origin) {
285 : 9 : case SEEK_SET:
286 : 9 : break;
287 : 2 : case SEEK_CUR:
288 [ + + ]: 2 : if (offset > INT_MAX - self->offset) {
289 : 1 : goto overflow;
290 : : }
291 : 1 : offset += self->offset;
292 : 1 : break;
293 : 3 : case SEEK_END:
294 [ + + ]: 3 : if (offset > INT_MAX - blob_len) {
295 : 1 : goto overflow;
296 : : }
297 : 2 : offset += blob_len;
298 : 2 : break;
299 : 2 : default:
300 : 2 : PyErr_SetString(PyExc_ValueError,
301 : : "'origin' should be os.SEEK_SET, os.SEEK_CUR, or "
302 : : "os.SEEK_END");
303 : 2 : return NULL;
304 : : }
305 : :
306 [ + + + + ]: 12 : if (offset < 0 || offset > blob_len) {
307 : 2 : PyErr_SetString(PyExc_ValueError, "offset out of blob range");
308 : 2 : return NULL;
309 : : }
310 : :
311 : 10 : self->offset = offset;
312 : 10 : Py_RETURN_NONE;
313 : :
314 : 2 : overflow:
315 : 2 : PyErr_SetString(PyExc_OverflowError, "seek offset results in overflow");
316 : 2 : return NULL;
317 : : }
318 : :
319 : :
320 : : /*[clinic input]
321 : : _sqlite3.Blob.tell as blob_tell
322 : :
323 : : Return the current access position for the blob.
324 : : [clinic start generated code]*/
325 : :
326 : : static PyObject *
327 : 7 : blob_tell_impl(pysqlite_Blob *self)
328 : : /*[clinic end generated code: output=3d3ba484a90b3a99 input=7e34057aa303612c]*/
329 : : {
330 [ + + ]: 7 : if (!check_blob(self)) {
331 : 1 : return NULL;
332 : : }
333 : 6 : return PyLong_FromLong(self->offset);
334 : : }
335 : :
336 : :
337 : : /*[clinic input]
338 : : _sqlite3.Blob.__enter__ as blob_enter
339 : :
340 : : Blob context manager enter.
341 : : [clinic start generated code]*/
342 : :
343 : : static PyObject *
344 : 3 : blob_enter_impl(pysqlite_Blob *self)
345 : : /*[clinic end generated code: output=4fd32484b071a6cd input=fe4842c3c582d5a7]*/
346 : : {
347 [ + + ]: 3 : if (!check_blob(self)) {
348 : 1 : return NULL;
349 : : }
350 : 2 : return Py_NewRef(self);
351 : : }
352 : :
353 : :
354 : : /*[clinic input]
355 : : _sqlite3.Blob.__exit__ as blob_exit
356 : :
357 : : type: object
358 : : val: object
359 : : tb: object
360 : : /
361 : :
362 : : Blob context manager exit.
363 : : [clinic start generated code]*/
364 : :
365 : : static PyObject *
366 : 3 : blob_exit_impl(pysqlite_Blob *self, PyObject *type, PyObject *val,
367 : : PyObject *tb)
368 : : /*[clinic end generated code: output=fc86ceeb2b68c7b2 input=575d9ecea205f35f]*/
369 : : {
370 [ + + ]: 3 : if (!check_blob(self)) {
371 : 1 : return NULL;
372 : : }
373 : 2 : close_blob(self);
374 : 2 : Py_RETURN_FALSE;
375 : : }
376 : :
377 : : static Py_ssize_t
378 : 4 : blob_length(pysqlite_Blob *self)
379 : : {
380 [ + + ]: 4 : if (!check_blob(self)) {
381 : 1 : return -1;
382 : : }
383 : 3 : return sqlite3_blob_bytes(self->blob);
384 : : };
385 : :
386 : : static Py_ssize_t
387 : 19 : get_subscript_index(pysqlite_Blob *self, PyObject *item)
388 : : {
389 : 19 : Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
390 [ + + + + ]: 19 : if (i == -1 && PyErr_Occurred()) {
391 : 1 : return -1;
392 : : }
393 : 18 : int blob_len = sqlite3_blob_bytes(self->blob);
394 [ + + ]: 18 : if (i < 0) {
395 : 5 : i += blob_len;
396 : : }
397 [ + + + + ]: 18 : if (i < 0 || i >= blob_len) {
398 : 4 : PyErr_SetString(PyExc_IndexError, "Blob index out of range");
399 : 4 : return -1;
400 : : }
401 : 14 : return i;
402 : : }
403 : :
404 : : static PyObject *
405 : 11 : subscript_index(pysqlite_Blob *self, PyObject *item)
406 : : {
407 : 11 : Py_ssize_t i = get_subscript_index(self, item);
408 [ + + ]: 11 : if (i < 0) {
409 : 4 : return NULL;
410 : : }
411 : 7 : return read_single(self, i);
412 : : }
413 : :
414 : : static int
415 : 19 : get_slice_info(pysqlite_Blob *self, PyObject *item, Py_ssize_t *start,
416 : : Py_ssize_t *stop, Py_ssize_t *step, Py_ssize_t *slicelen)
417 : : {
418 [ + + ]: 19 : if (PySlice_Unpack(item, start, stop, step) < 0) {
419 : 2 : return -1;
420 : : }
421 : 17 : int len = sqlite3_blob_bytes(self->blob);
422 : 17 : *slicelen = PySlice_AdjustIndices(len, start, stop, *step);
423 : 17 : return 0;
424 : : }
425 : :
426 : : static PyObject *
427 : 9 : subscript_slice(pysqlite_Blob *self, PyObject *item)
428 : : {
429 : : Py_ssize_t start, stop, step, len;
430 [ + + ]: 9 : if (get_slice_info(self, item, &start, &stop, &step, &len) < 0) {
431 : 1 : return NULL;
432 : : }
433 : :
434 [ + + ]: 8 : if (step == 1) {
435 : 7 : return read_multiple(self, len, start);
436 : : }
437 : 1 : PyObject *blob = read_multiple(self, stop - start, start);
438 [ - + ]: 1 : if (blob == NULL) {
439 : 0 : return NULL;
440 : : }
441 : 1 : PyObject *result = PyBytes_FromStringAndSize(NULL, len);
442 [ + - ]: 1 : if (result != NULL) {
443 : 1 : char *blob_buf = PyBytes_AS_STRING(blob);
444 : 1 : char *res_buf = PyBytes_AS_STRING(result);
445 [ + + ]: 6 : for (Py_ssize_t i = 0, j = 0; i < len; i++, j += step) {
446 : 5 : res_buf[i] = blob_buf[j];
447 : : }
448 : 1 : Py_DECREF(blob);
449 : : }
450 : 1 : return result;
451 : : }
452 : :
453 : : static PyObject *
454 : 23 : blob_subscript(pysqlite_Blob *self, PyObject *item)
455 : : {
456 [ + + ]: 23 : if (!check_blob(self)) {
457 : 2 : return NULL;
458 : : }
459 : :
460 [ + + ]: 21 : if (PyIndex_Check(item)) {
461 : 11 : return subscript_index(self, item);
462 : : }
463 [ + + ]: 10 : if (PySlice_Check(item)) {
464 : 9 : return subscript_slice(self, item);
465 : : }
466 : :
467 : 1 : PyErr_SetString(PyExc_TypeError, "Blob indices must be integers");
468 : 1 : return NULL;
469 : : }
470 : :
471 : : static int
472 : 12 : ass_subscript_index(pysqlite_Blob *self, PyObject *item, PyObject *value)
473 : : {
474 [ + + ]: 12 : if (value == NULL) {
475 : 1 : PyErr_SetString(PyExc_TypeError,
476 : : "Blob doesn't support item deletion");
477 : 1 : return -1;
478 : : }
479 [ + + ]: 11 : if (!PyLong_Check(value)) {
480 : 3 : PyErr_Format(PyExc_TypeError,
481 : : "'%s' object cannot be interpreted as an integer",
482 : 3 : Py_TYPE(value)->tp_name);
483 : 3 : return -1;
484 : : }
485 : 8 : Py_ssize_t i = get_subscript_index(self, item);
486 [ + + ]: 8 : if (i < 0) {
487 : 1 : return -1;
488 : : }
489 : :
490 : 7 : long val = PyLong_AsLong(value);
491 [ + + + + ]: 7 : if (val == -1 && PyErr_Occurred()) {
492 : 1 : PyErr_Clear();
493 : 1 : val = -1;
494 : : }
495 [ + + + + ]: 7 : if (val < 0 || val > 255) {
496 : 3 : PyErr_SetString(PyExc_ValueError, "byte must be in range(0, 256)");
497 : 3 : return -1;
498 : : }
499 : : // Downcast to avoid endianness problems.
500 : 4 : unsigned char byte = (unsigned char)val;
501 : 4 : return inner_write(self, (const void *)&byte, 1, i);
502 : : }
503 : :
504 : : static int
505 : 11 : ass_subscript_slice(pysqlite_Blob *self, PyObject *item, PyObject *value)
506 : : {
507 [ + + ]: 11 : if (value == NULL) {
508 : 1 : PyErr_SetString(PyExc_TypeError,
509 : : "Blob doesn't support slice deletion");
510 : 1 : return -1;
511 : : }
512 : :
513 : : Py_ssize_t start, stop, step, len;
514 [ + + ]: 10 : if (get_slice_info(self, item, &start, &stop, &step, &len) < 0) {
515 : 1 : return -1;
516 : : }
517 : :
518 [ + + ]: 9 : if (len == 0) {
519 : 1 : return 0;
520 : : }
521 : :
522 : : Py_buffer vbuf;
523 [ + + ]: 8 : if (PyObject_GetBuffer(value, &vbuf, PyBUF_SIMPLE) < 0) {
524 : 1 : return -1;
525 : : }
526 : :
527 : 7 : int rc = -1;
528 [ + + ]: 7 : if (vbuf.len != len) {
529 : 2 : PyErr_SetString(PyExc_IndexError,
530 : : "Blob slice assignment is wrong size");
531 : : }
532 [ + + ]: 5 : else if (step == 1) {
533 : 4 : rc = inner_write(self, vbuf.buf, len, start);
534 : : }
535 : : else {
536 : 1 : PyObject *blob_bytes = read_multiple(self, stop - start, start);
537 [ + - ]: 1 : if (blob_bytes != NULL) {
538 : 1 : char *blob_buf = PyBytes_AS_STRING(blob_bytes);
539 [ + + ]: 6 : for (Py_ssize_t i = 0, j = 0; i < len; i++, j += step) {
540 : 5 : blob_buf[j] = ((char *)vbuf.buf)[i];
541 : : }
542 : 1 : rc = inner_write(self, blob_buf, stop - start, start);
543 : 1 : Py_DECREF(blob_bytes);
544 : : }
545 : : }
546 : 7 : PyBuffer_Release(&vbuf);
547 : 7 : return rc;
548 : : }
549 : :
550 : : static int
551 : 25 : blob_ass_subscript(pysqlite_Blob *self, PyObject *item, PyObject *value)
552 : : {
553 [ + + ]: 25 : if (!check_blob(self)) {
554 : 1 : return -1;
555 : : }
556 : :
557 [ + + ]: 24 : if (PyIndex_Check(item)) {
558 : 12 : return ass_subscript_index(self, item, value);
559 : : }
560 [ + + ]: 12 : if (PySlice_Check(item)) {
561 : 11 : return ass_subscript_slice(self, item, value);
562 : : }
563 : :
564 : 1 : PyErr_SetString(PyExc_TypeError, "Blob indices must be integers");
565 : 1 : return -1;
566 : : }
567 : :
568 : :
569 : : static PyMethodDef blob_methods[] = {
570 : : BLOB_CLOSE_METHODDEF
571 : : BLOB_ENTER_METHODDEF
572 : : BLOB_EXIT_METHODDEF
573 : : BLOB_READ_METHODDEF
574 : : BLOB_SEEK_METHODDEF
575 : : BLOB_TELL_METHODDEF
576 : : BLOB_WRITE_METHODDEF
577 : : {NULL, NULL}
578 : : };
579 : :
580 : : static struct PyMemberDef blob_members[] = {
581 : : {"__weaklistoffset__", T_PYSSIZET, offsetof(pysqlite_Blob, in_weakreflist), READONLY},
582 : : {NULL},
583 : : };
584 : :
585 : : static PyType_Slot blob_slots[] = {
586 : : {Py_tp_dealloc, blob_dealloc},
587 : : {Py_tp_traverse, blob_traverse},
588 : : {Py_tp_clear, blob_clear},
589 : : {Py_tp_methods, blob_methods},
590 : : {Py_tp_members, blob_members},
591 : :
592 : : // Mapping protocol
593 : : {Py_mp_length, blob_length},
594 : : {Py_mp_subscript, blob_subscript},
595 : : {Py_mp_ass_subscript, blob_ass_subscript},
596 : : {0, NULL},
597 : : };
598 : :
599 : : static PyType_Spec blob_spec = {
600 : : .name = MODULE_NAME ".Blob",
601 : : .basicsize = sizeof(pysqlite_Blob),
602 : : .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
603 : : Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION),
604 : : .slots = blob_slots,
605 : : };
606 : :
607 : : int
608 : 5 : pysqlite_blob_setup_types(PyObject *mod)
609 : : {
610 : 5 : PyObject *type = PyType_FromModuleAndSpec(mod, &blob_spec, NULL);
611 [ - + ]: 5 : if (type == NULL) {
612 : 0 : return -1;
613 : : }
614 : 5 : pysqlite_state *state = pysqlite_get_state(mod);
615 : 5 : state->BlobType = (PyTypeObject *)type;
616 : 5 : return 0;
617 : : }
|