mirror of https://github.com/python/cpython
618 lines
15 KiB
C
618 lines
15 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 *
|
|
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;
|
|
}
|