mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	
		
			
	
	
		
			352 lines
		
	
	
	
		
			8.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			352 lines
		
	
	
	
		
			8.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|   | #include "blob.h"
 | ||
|  | #include "util.h"
 | ||
|  | 
 | ||
|  | #define clinic_state() (pysqlite_get_state_by_type(Py_TYPE(self)))
 | ||
|  | #include "clinic/blob.c.h"
 | ||
|  | #undef clinic_state
 | ||
|  | 
 | ||
|  | /*[clinic input]
 | ||
|  | module _sqlite3 | ||
|  | class _sqlite3.Blob "pysqlite_Blob *" "clinic_state()->BlobType" | ||
|  | [clinic start generated code]*/ | ||
|  | /*[clinic end generated code: output=da39a3ee5e6b4b0d input=908d3e16a45f8da7]*/ | ||
|  | 
 | ||
|  | static void | ||
|  | close_blob(pysqlite_Blob *self) | ||
|  | { | ||
|  |     if (self->blob) { | ||
|  |         sqlite3_blob *blob = self->blob; | ||
|  |         self->blob = NULL; | ||
|  | 
 | ||
|  |         Py_BEGIN_ALLOW_THREADS | ||
|  |         sqlite3_blob_close(blob); | ||
|  |         Py_END_ALLOW_THREADS | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | static int | ||
|  | blob_traverse(pysqlite_Blob *self, visitproc visit, void *arg) | ||
|  | { | ||
|  |     Py_VISIT(Py_TYPE(self)); | ||
|  |     Py_VISIT(self->connection); | ||
|  |     return 0; | ||
|  | } | ||
|  | 
 | ||
|  | static int | ||
|  | blob_clear(pysqlite_Blob *self) | ||
|  | { | ||
|  |     Py_CLEAR(self->connection); | ||
|  |     return 0; | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | blob_dealloc(pysqlite_Blob *self) | ||
|  | { | ||
|  |     PyTypeObject *tp = Py_TYPE(self); | ||
|  |     PyObject_GC_UnTrack(self); | ||
|  | 
 | ||
|  |     close_blob(self); | ||
|  | 
 | ||
|  |     if (self->in_weakreflist != NULL) { | ||
|  |         PyObject_ClearWeakRefs((PyObject*)self); | ||
|  |     } | ||
|  |     tp->tp_clear((PyObject *)self); | ||
|  |     tp->tp_free(self); | ||
|  |     Py_DECREF(tp); | ||
|  | } | ||
|  | 
 | ||
|  | // Return 1 if the blob object is usable, 0 if not.
 | ||
|  | static int | ||
|  | check_blob(pysqlite_Blob *self) | ||
|  | { | ||
|  |     if (!pysqlite_check_connection(self->connection) || | ||
|  |         !pysqlite_check_thread(self->connection)) { | ||
|  |         return 0; | ||
|  |     } | ||
|  |     if (self->blob == NULL) { | ||
|  |         pysqlite_state *state = self->connection->state; | ||
|  |         PyErr_SetString(state->ProgrammingError, | ||
|  |                         "Cannot operate on a closed blob."); | ||
|  |         return 0; | ||
|  |     } | ||
|  |     return 1; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /*[clinic input]
 | ||
|  | _sqlite3.Blob.close as blob_close | ||
|  | 
 | ||
|  | Close the blob. | ||
|  | [clinic start generated code]*/ | ||
|  | 
 | ||
|  | static PyObject * | ||
|  | blob_close_impl(pysqlite_Blob *self) | ||
|  | /*[clinic end generated code: output=848accc20a138d1b input=7bc178a402a40bd8]*/ | ||
|  | { | ||
|  |     if (!pysqlite_check_connection(self->connection) || | ||
|  |         !pysqlite_check_thread(self->connection)) | ||
|  |     { | ||
|  |         return NULL; | ||
|  |     } | ||
|  |     close_blob(self); | ||
|  |     Py_RETURN_NONE; | ||
|  | }; | ||
|  | 
 | ||
|  | void | ||
|  | pysqlite_close_all_blobs(pysqlite_Connection *self) | ||
|  | { | ||
|  |     for (int i = 0; i < PyList_GET_SIZE(self->blobs); i++) { | ||
|  |         PyObject *weakref = PyList_GET_ITEM(self->blobs, i); | ||
|  |         PyObject *blob = PyWeakref_GetObject(weakref); | ||
|  |         if (!Py_IsNone(blob)) { | ||
|  |             close_blob((pysqlite_Blob *)blob); | ||
|  |         } | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | blob_seterror(pysqlite_Blob *self, int rc) | ||
|  | { | ||
|  |     assert(self->connection != NULL); | ||
|  | #if SQLITE_VERSION_NUMBER < 3008008
 | ||
|  |     // SQLite pre 3.8.8 does not set this blob error on the connection
 | ||
|  |     if (rc == SQLITE_ABORT) { | ||
|  |         PyErr_SetString(self->connection->OperationalError, | ||
|  |                         "Cannot operate on an expired blob handle"); | ||
|  |         return; | ||
|  |     } | ||
|  | #endif
 | ||
|  |     _pysqlite_seterror(self->connection->state, self->connection->db); | ||
|  | } | ||
|  | 
 | ||
|  | static PyObject * | ||
|  | inner_read(pysqlite_Blob *self, int length, int offset) | ||
|  | { | ||
|  |     PyObject *buffer = PyBytes_FromStringAndSize(NULL, length); | ||
|  |     if (buffer == NULL) { | ||
|  |         return NULL; | ||
|  |     } | ||
|  | 
 | ||
|  |     char *raw_buffer = PyBytes_AS_STRING(buffer); | ||
|  |     int rc; | ||
|  |     Py_BEGIN_ALLOW_THREADS | ||
|  |     rc = sqlite3_blob_read(self->blob, raw_buffer, length, offset); | ||
|  |     Py_END_ALLOW_THREADS | ||
|  | 
 | ||
|  |     if (rc != SQLITE_OK) { | ||
|  |         Py_DECREF(buffer); | ||
|  |         blob_seterror(self, rc); | ||
|  |         return NULL; | ||
|  |     } | ||
|  |     return buffer; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /*[clinic input]
 | ||
|  | _sqlite3.Blob.read as blob_read | ||
|  | 
 | ||
|  |     length: int = -1 | ||
|  |         Read length in bytes. | ||
|  |     / | ||
|  | 
 | ||
|  | Read data at the current offset position. | ||
|  | 
 | ||
|  | If the end of the blob is reached, the data up to end of file will be returned. | ||
|  | When length is not specified, or is negative, Blob.read() will read until the | ||
|  | end of the blob. | ||
|  | [clinic start generated code]*/ | ||
|  | 
 | ||
|  | static PyObject * | ||
|  | blob_read_impl(pysqlite_Blob *self, int length) | ||
|  | /*[clinic end generated code: output=1fc99b2541360dde input=f2e4aa4378837250]*/ | ||
|  | { | ||
|  |     if (!check_blob(self)) { | ||
|  |         return NULL; | ||
|  |     } | ||
|  | 
 | ||
|  |     /* Make sure we never read past "EOB". Also read the rest of the blob if a
 | ||
|  |      * negative length is specified. */ | ||
|  |     int blob_len = sqlite3_blob_bytes(self->blob); | ||
|  |     int max_read_len = blob_len - self->offset; | ||
|  |     if (length < 0 || length > max_read_len) { | ||
|  |         length = max_read_len; | ||
|  |     } | ||
|  | 
 | ||
|  |     PyObject *buffer = inner_read(self, length, self->offset); | ||
|  |     if (buffer == NULL) { | ||
|  |         return NULL; | ||
|  |     } | ||
|  |     self->offset += length; | ||
|  |     return buffer; | ||
|  | }; | ||
|  | 
 | ||
|  | static int | ||
|  | inner_write(pysqlite_Blob *self, const void *buf, Py_ssize_t len, int offset) | ||
|  | { | ||
|  |     int remaining_len = sqlite3_blob_bytes(self->blob) - self->offset; | ||
|  |     if (len > remaining_len) { | ||
|  |         PyErr_SetString(PyExc_ValueError, "data longer than blob length"); | ||
|  |         return -1; | ||
|  |     } | ||
|  | 
 | ||
|  |     int rc; | ||
|  |     Py_BEGIN_ALLOW_THREADS | ||
|  |     rc = sqlite3_blob_write(self->blob, buf, (int)len, offset); | ||
|  |     Py_END_ALLOW_THREADS | ||
|  | 
 | ||
|  |     if (rc != SQLITE_OK) { | ||
|  |         blob_seterror(self, rc); | ||
|  |         return -1; | ||
|  |     } | ||
|  |     return 0; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /*[clinic input]
 | ||
|  | _sqlite3.Blob.write as blob_write | ||
|  | 
 | ||
|  |     data: Py_buffer | ||
|  |     / | ||
|  | 
 | ||
|  | Write data at the current offset. | ||
|  | 
 | ||
|  | This function cannot change the blob length.  Writing beyond the end of the | ||
|  | blob will result in an exception being raised. | ||
|  | [clinic start generated code]*/ | ||
|  | 
 | ||
|  | static PyObject * | ||
|  | blob_write_impl(pysqlite_Blob *self, Py_buffer *data) | ||
|  | /*[clinic end generated code: output=b34cf22601b570b2 input=a84712f24a028e6d]*/ | ||
|  | { | ||
|  |     if (!check_blob(self)) { | ||
|  |         return NULL; | ||
|  |     } | ||
|  | 
 | ||
|  |     int rc = inner_write(self, data->buf, data->len, self->offset); | ||
|  |     if (rc < 0) { | ||
|  |         return NULL; | ||
|  |     } | ||
|  |     self->offset += (int)data->len; | ||
|  |     Py_RETURN_NONE; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /*[clinic input]
 | ||
|  | _sqlite3.Blob.seek as blob_seek | ||
|  | 
 | ||
|  |     offset: int | ||
|  |     origin: int = 0 | ||
|  |     / | ||
|  | 
 | ||
|  | Set the current access position to offset. | ||
|  | 
 | ||
|  | The origin argument defaults to os.SEEK_SET (absolute blob positioning). | ||
|  | Other values for origin are os.SEEK_CUR (seek relative to the current position) | ||
|  | and os.SEEK_END (seek relative to the blob's end). | ||
|  | [clinic start generated code]*/ | ||
|  | 
 | ||
|  | static PyObject * | ||
|  | blob_seek_impl(pysqlite_Blob *self, int offset, int origin) | ||
|  | /*[clinic end generated code: output=854c5a0e208547a5 input=5da9a07e55fe6bb6]*/ | ||
|  | { | ||
|  |     if (!check_blob(self)) { | ||
|  |         return NULL; | ||
|  |     } | ||
|  | 
 | ||
|  |     int blob_len = sqlite3_blob_bytes(self->blob); | ||
|  |     switch (origin) { | ||
|  |         case SEEK_SET: | ||
|  |             break; | ||
|  |         case SEEK_CUR: | ||
|  |             if (offset > INT_MAX - self->offset) { | ||
|  |                 goto overflow; | ||
|  |             } | ||
|  |             offset += self->offset; | ||
|  |             break; | ||
|  |         case SEEK_END: | ||
|  |             if (offset > INT_MAX - blob_len) { | ||
|  |                 goto overflow; | ||
|  |             } | ||
|  |             offset += blob_len; | ||
|  |             break; | ||
|  |         default: | ||
|  |             PyErr_SetString(PyExc_ValueError, | ||
|  |                             "'origin' should be os.SEEK_SET, os.SEEK_CUR, or " | ||
|  |                             "os.SEEK_END"); | ||
|  |             return NULL; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (offset < 0 || offset > blob_len) { | ||
|  |         PyErr_SetString(PyExc_ValueError, "offset out of blob range"); | ||
|  |         return NULL; | ||
|  |     } | ||
|  | 
 | ||
|  |     self->offset = offset; | ||
|  |     Py_RETURN_NONE; | ||
|  | 
 | ||
|  | overflow: | ||
|  |     PyErr_SetString(PyExc_OverflowError, "seek offset results in overflow"); | ||
|  |     return NULL; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /*[clinic input]
 | ||
|  | _sqlite3.Blob.tell as blob_tell | ||
|  | 
 | ||
|  | Return the current access position for the blob. | ||
|  | [clinic start generated code]*/ | ||
|  | 
 | ||
|  | static PyObject * | ||
|  | blob_tell_impl(pysqlite_Blob *self) | ||
|  | /*[clinic end generated code: output=3d3ba484a90b3a99 input=7e34057aa303612c]*/ | ||
|  | { | ||
|  |     if (!check_blob(self)) { | ||
|  |         return NULL; | ||
|  |     } | ||
|  |     return PyLong_FromLong(self->offset); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static PyMethodDef blob_methods[] = { | ||
|  |     BLOB_CLOSE_METHODDEF | ||
|  |     BLOB_READ_METHODDEF | ||
|  |     BLOB_SEEK_METHODDEF | ||
|  |     BLOB_TELL_METHODDEF | ||
|  |     BLOB_WRITE_METHODDEF | ||
|  |     {NULL, NULL} | ||
|  | }; | ||
|  | 
 | ||
|  | static struct PyMemberDef blob_members[] = { | ||
|  |     {"__weaklistoffset__", T_PYSSIZET, offsetof(pysqlite_Blob, in_weakreflist), READONLY}, | ||
|  |     {NULL}, | ||
|  | }; | ||
|  | 
 | ||
|  | static PyType_Slot blob_slots[] = { | ||
|  |     {Py_tp_dealloc, blob_dealloc}, | ||
|  |     {Py_tp_traverse, blob_traverse}, | ||
|  |     {Py_tp_clear, blob_clear}, | ||
|  |     {Py_tp_methods, blob_methods}, | ||
|  |     {Py_tp_members, blob_members}, | ||
|  |     {0, NULL}, | ||
|  | }; | ||
|  | 
 | ||
|  | static PyType_Spec blob_spec = { | ||
|  |     .name = MODULE_NAME ".Blob", | ||
|  |     .basicsize = sizeof(pysqlite_Blob), | ||
|  |     .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | | ||
|  |               Py_TPFLAGS_IMMUTABLETYPE), | ||
|  |     .slots = blob_slots, | ||
|  | }; | ||
|  | 
 | ||
|  | int | ||
|  | pysqlite_blob_setup_types(PyObject *mod) | ||
|  | { | ||
|  |     PyObject *type = PyType_FromModuleAndSpec(mod, &blob_spec, NULL); | ||
|  |     if (type == NULL) { | ||
|  |         return -1; | ||
|  |     } | ||
|  |     pysqlite_state *state = pysqlite_get_state(mod); | ||
|  |     state->BlobType = (PyTypeObject *)type; | ||
|  |     return 0; | ||
|  | } |