aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/tools/python3/Modules/_sqlite/blob.c
diff options
context:
space:
mode:
authorAlexSm <alex@ydb.tech>2024-03-05 10:40:59 +0100
committerGitHub <noreply@github.com>2024-03-05 12:40:59 +0300
commit1ac13c847b5358faba44dbb638a828e24369467b (patch)
tree07672b4dd3604ad3dee540a02c6494cb7d10dc3d /contrib/tools/python3/Modules/_sqlite/blob.c
parentffcca3e7f7958ddc6487b91d3df8c01054bd0638 (diff)
downloadydb-1ac13c847b5358faba44dbb638a828e24369467b.tar.gz
Library import 16 (#2433)
Co-authored-by: robot-piglet <robot-piglet@yandex-team.com> Co-authored-by: deshevoy <deshevoy@yandex-team.com> Co-authored-by: robot-contrib <robot-contrib@yandex-team.com> Co-authored-by: thegeorg <thegeorg@yandex-team.com> Co-authored-by: robot-ya-builder <robot-ya-builder@yandex-team.com> Co-authored-by: svidyuk <svidyuk@yandex-team.com> Co-authored-by: shadchin <shadchin@yandex-team.com> Co-authored-by: robot-ratatosk <robot-ratatosk@yandex-team.com> Co-authored-by: innokentii <innokentii@yandex-team.com> Co-authored-by: arkady-e1ppa <arkady-e1ppa@yandex-team.com> Co-authored-by: snermolaev <snermolaev@yandex-team.com> Co-authored-by: dimdim11 <dimdim11@yandex-team.com> Co-authored-by: kickbutt <kickbutt@yandex-team.com> Co-authored-by: abdullinsaid <abdullinsaid@yandex-team.com> Co-authored-by: korsunandrei <korsunandrei@yandex-team.com> Co-authored-by: petrk <petrk@yandex-team.com> Co-authored-by: miroslav2 <miroslav2@yandex-team.com> Co-authored-by: serjflint <serjflint@yandex-team.com> Co-authored-by: akhropov <akhropov@yandex-team.com> Co-authored-by: prettyboy <prettyboy@yandex-team.com> Co-authored-by: ilikepugs <ilikepugs@yandex-team.com> Co-authored-by: hiddenpath <hiddenpath@yandex-team.com> Co-authored-by: mikhnenko <mikhnenko@yandex-team.com> Co-authored-by: spreis <spreis@yandex-team.com> Co-authored-by: andreyshspb <andreyshspb@yandex-team.com> Co-authored-by: dimaandreev <dimaandreev@yandex-team.com> Co-authored-by: rashid <rashid@yandex-team.com> Co-authored-by: robot-ydb-importer <robot-ydb-importer@yandex-team.com> Co-authored-by: r-vetrov <r-vetrov@yandex-team.com> Co-authored-by: ypodlesov <ypodlesov@yandex-team.com> Co-authored-by: zaverden <zaverden@yandex-team.com> Co-authored-by: vpozdyayev <vpozdyayev@yandex-team.com> Co-authored-by: robot-cozmo <robot-cozmo@yandex-team.com> Co-authored-by: v-korovin <v-korovin@yandex-team.com> Co-authored-by: arikon <arikon@yandex-team.com> Co-authored-by: khoden <khoden@yandex-team.com> Co-authored-by: psydmm <psydmm@yandex-team.com> Co-authored-by: robot-javacom <robot-javacom@yandex-team.com> Co-authored-by: dtorilov <dtorilov@yandex-team.com> Co-authored-by: sennikovmv <sennikovmv@yandex-team.com> Co-authored-by: hcpp <hcpp@ydb.tech>
Diffstat (limited to 'contrib/tools/python3/Modules/_sqlite/blob.c')
-rw-r--r--contrib/tools/python3/Modules/_sqlite/blob.c617
1 files changed, 617 insertions, 0 deletions
diff --git a/contrib/tools/python3/Modules/_sqlite/blob.c b/contrib/tools/python3/Modules/_sqlite/blob.c
new file mode 100644
index 0000000000..76d261baf0
--- /dev/null
+++ b/contrib/tools/python3/Modules/_sqlite/blob.c
@@ -0,0 +1,617 @@
+#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 *
+read_single(pysqlite_Blob *self, Py_ssize_t offset)
+{
+ unsigned char buf = 0;
+ int rc;
+ Py_BEGIN_ALLOW_THREADS
+ rc = sqlite3_blob_read(self->blob, (void *)&buf, 1, (int)offset);
+ Py_END_ALLOW_THREADS
+
+ if (rc != SQLITE_OK) {
+ blob_seterror(self, rc);
+ return NULL;
+ }
+ return PyLong_FromUnsignedLong((unsigned long)buf);
+}
+
+static PyObject *
+read_multiple(pysqlite_Blob *self, Py_ssize_t length, Py_ssize_t offset)
+{
+ assert(length <= sqlite3_blob_bytes(self->blob));
+ assert(offset < sqlite3_blob_bytes(self->blob));
+
+ 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, (int)length, (int)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;
+ }
+
+ assert(length >= 0);
+ if (length == 0) {
+ return PyBytes_FromStringAndSize(NULL, 0);
+ }
+
+ PyObject *buffer = read_multiple(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,
+ Py_ssize_t offset)
+{
+ Py_ssize_t blob_len = sqlite3_blob_bytes(self->blob);
+ Py_ssize_t remaining_len = blob_len - offset;
+ if (len > remaining_len) {
+ PyErr_SetString(PyExc_ValueError, "data longer than blob length");
+ return -1;
+ }
+
+ assert(offset <= blob_len);
+ int rc;
+ Py_BEGIN_ALLOW_THREADS
+ rc = sqlite3_blob_write(self->blob, buf, (int)len, (int)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);
+}
+
+
+/*[clinic input]
+_sqlite3.Blob.__enter__ as blob_enter
+
+Blob context manager enter.
+[clinic start generated code]*/
+
+static PyObject *
+blob_enter_impl(pysqlite_Blob *self)
+/*[clinic end generated code: output=4fd32484b071a6cd input=fe4842c3c582d5a7]*/
+{
+ if (!check_blob(self)) {
+ return NULL;
+ }
+ return Py_NewRef(self);
+}
+
+
+/*[clinic input]
+_sqlite3.Blob.__exit__ as blob_exit
+
+ type: object
+ val: object
+ tb: object
+ /
+
+Blob context manager exit.
+[clinic start generated code]*/
+
+static PyObject *
+blob_exit_impl(pysqlite_Blob *self, PyObject *type, PyObject *val,
+ PyObject *tb)
+/*[clinic end generated code: output=fc86ceeb2b68c7b2 input=575d9ecea205f35f]*/
+{
+ if (!check_blob(self)) {
+ return NULL;
+ }
+ close_blob(self);
+ Py_RETURN_FALSE;
+}
+
+static Py_ssize_t
+blob_length(pysqlite_Blob *self)
+{
+ if (!check_blob(self)) {
+ return -1;
+ }
+ return sqlite3_blob_bytes(self->blob);
+};
+
+static Py_ssize_t
+get_subscript_index(pysqlite_Blob *self, PyObject *item)
+{
+ Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
+ if (i == -1 && PyErr_Occurred()) {
+ return -1;
+ }
+ int blob_len = sqlite3_blob_bytes(self->blob);
+ if (i < 0) {
+ i += blob_len;
+ }
+ if (i < 0 || i >= blob_len) {
+ PyErr_SetString(PyExc_IndexError, "Blob index out of range");
+ return -1;
+ }
+ return i;
+}
+
+static PyObject *
+subscript_index(pysqlite_Blob *self, PyObject *item)
+{
+ Py_ssize_t i = get_subscript_index(self, item);
+ if (i < 0) {
+ return NULL;
+ }
+ return read_single(self, i);
+}
+
+static int
+get_slice_info(pysqlite_Blob *self, PyObject *item, Py_ssize_t *start,
+ Py_ssize_t *stop, Py_ssize_t *step, Py_ssize_t *slicelen)
+{
+ if (PySlice_Unpack(item, start, stop, step) < 0) {
+ return -1;
+ }
+ int len = sqlite3_blob_bytes(self->blob);
+ *slicelen = PySlice_AdjustIndices(len, start, stop, *step);
+ return 0;
+}
+
+static PyObject *
+subscript_slice(pysqlite_Blob *self, PyObject *item)
+{
+ Py_ssize_t start, stop, step, len;
+ if (get_slice_info(self, item, &start, &stop, &step, &len) < 0) {
+ return NULL;
+ }
+
+ if (step == 1) {
+ return read_multiple(self, len, start);
+ }
+ PyObject *blob = read_multiple(self, stop - start, start);
+ if (blob == NULL) {
+ return NULL;
+ }
+ PyObject *result = PyBytes_FromStringAndSize(NULL, len);
+ if (result != NULL) {
+ char *blob_buf = PyBytes_AS_STRING(blob);
+ char *res_buf = PyBytes_AS_STRING(result);
+ for (Py_ssize_t i = 0, j = 0; i < len; i++, j += step) {
+ res_buf[i] = blob_buf[j];
+ }
+ Py_DECREF(blob);
+ }
+ return result;
+}
+
+static PyObject *
+blob_subscript(pysqlite_Blob *self, PyObject *item)
+{
+ if (!check_blob(self)) {
+ return NULL;
+ }
+
+ if (PyIndex_Check(item)) {
+ return subscript_index(self, item);
+ }
+ if (PySlice_Check(item)) {
+ return subscript_slice(self, item);
+ }
+
+ PyErr_SetString(PyExc_TypeError, "Blob indices must be integers");
+ return NULL;
+}
+
+static int
+ass_subscript_index(pysqlite_Blob *self, PyObject *item, PyObject *value)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError,
+ "Blob doesn't support item deletion");
+ return -1;
+ }
+ if (!PyLong_Check(value)) {
+ PyErr_Format(PyExc_TypeError,
+ "'%s' object cannot be interpreted as an integer",
+ Py_TYPE(value)->tp_name);
+ return -1;
+ }
+ Py_ssize_t i = get_subscript_index(self, item);
+ if (i < 0) {
+ return -1;
+ }
+
+ long val = PyLong_AsLong(value);
+ if (val == -1 && PyErr_Occurred()) {
+ PyErr_Clear();
+ val = -1;
+ }
+ if (val < 0 || val > 255) {
+ PyErr_SetString(PyExc_ValueError, "byte must be in range(0, 256)");
+ return -1;
+ }
+ // Downcast to avoid endianness problems.
+ unsigned char byte = (unsigned char)val;
+ return inner_write(self, (const void *)&byte, 1, i);
+}
+
+static int
+ass_subscript_slice(pysqlite_Blob *self, PyObject *item, PyObject *value)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError,
+ "Blob doesn't support slice deletion");
+ return -1;
+ }
+
+ Py_ssize_t start, stop, step, len;
+ if (get_slice_info(self, item, &start, &stop, &step, &len) < 0) {
+ return -1;
+ }
+
+ if (len == 0) {
+ return 0;
+ }
+
+ Py_buffer vbuf;
+ if (PyObject_GetBuffer(value, &vbuf, PyBUF_SIMPLE) < 0) {
+ return -1;
+ }
+
+ int rc = -1;
+ if (vbuf.len != len) {
+ PyErr_SetString(PyExc_IndexError,
+ "Blob slice assignment is wrong size");
+ }
+ else if (step == 1) {
+ rc = inner_write(self, vbuf.buf, len, start);
+ }
+ else {
+ PyObject *blob_bytes = read_multiple(self, stop - start, start);
+ if (blob_bytes != NULL) {
+ char *blob_buf = PyBytes_AS_STRING(blob_bytes);
+ for (Py_ssize_t i = 0, j = 0; i < len; i++, j += step) {
+ blob_buf[j] = ((char *)vbuf.buf)[i];
+ }
+ rc = inner_write(self, blob_buf, stop - start, start);
+ Py_DECREF(blob_bytes);
+ }
+ }
+ PyBuffer_Release(&vbuf);
+ return rc;
+}
+
+static int
+blob_ass_subscript(pysqlite_Blob *self, PyObject *item, PyObject *value)
+{
+ if (!check_blob(self)) {
+ return -1;
+ }
+
+ if (PyIndex_Check(item)) {
+ return ass_subscript_index(self, item, value);
+ }
+ if (PySlice_Check(item)) {
+ return ass_subscript_slice(self, item, value);
+ }
+
+ PyErr_SetString(PyExc_TypeError, "Blob indices must be integers");
+ return -1;
+}
+
+
+static PyMethodDef blob_methods[] = {
+ BLOB_CLOSE_METHODDEF
+ BLOB_ENTER_METHODDEF
+ BLOB_EXIT_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},
+
+ // Mapping protocol
+ {Py_mp_length, blob_length},
+ {Py_mp_subscript, blob_subscript},
+ {Py_mp_ass_subscript, blob_ass_subscript},
+ {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 | Py_TPFLAGS_DISALLOW_INSTANTIATION),
+ .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;
+}