--- /dev/null
+#include "blob.h"
+#include "util.h"
+
+
+int pysqlite_blob_init(pysqlite_Blob *self, pysqlite_Connection* connection,
+ sqlite3_blob *blob)
+{
+ Py_INCREF(connection);
+ self->connection = connection;
+ self->offset = 0;
+ self->blob = blob;
+ self->in_weakreflist = NULL;
+
+ Py_BEGIN_ALLOW_THREADS
+ self->length = sqlite3_blob_bytes(self->blob);
+ Py_END_ALLOW_THREADS
+
+ if (!pysqlite_check_thread(self->connection)) {
+ return -1;
+ }
+ return 0;
+}
+
+static void remove_blob_from_connection_blob_list(pysqlite_Blob *self)
+{
+ Py_ssize_t i;
+ PyObject *item;
+
+ for (i = 0; i < PyList_GET_SIZE(self->connection->blobs); i++) {
+ item = PyList_GET_ITEM(self->connection->blobs, i);
+ if (PyWeakref_GetObject(item) == (PyObject *)self) {
+ PyList_SetSlice(self->connection->blobs, i, i+1, NULL);
+ break;
+ }
+ }
+}
+
+static void _close_blob_inner(pysqlite_Blob* self)
+{
+ sqlite3_blob *blob;
+
+ /* close the blob */
+ blob = self->blob;
+ self->blob = NULL;
+ if (blob) {
+ Py_BEGIN_ALLOW_THREADS
+ sqlite3_blob_close(blob);
+ Py_END_ALLOW_THREADS
+ }
+
+ /* remove from connection weaklist */
+ remove_blob_from_connection_blob_list(self);
+ if (self->in_weakreflist != NULL) {
+ PyObject_ClearWeakRefs((PyObject*)self);
+ }
+}
+
+static void pysqlite_blob_dealloc(pysqlite_Blob* self)
+{
+ _close_blob_inner(self);
+ Py_XDECREF(self->connection);
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+
+/*
+ * Checks if a blob object is usable (i. e. not closed).
+ *
+ * 0 => error; 1 => ok
+ */
+int pysqlite_check_blob(pysqlite_Blob *blob)
+{
+
+ if (!blob->blob) {
+ PyErr_SetString(pysqlite_ProgrammingError,
+ "Cannot operate on a closed blob.");
+ return 0;
+ } else if (!pysqlite_check_connection(blob->connection) ||
+ !pysqlite_check_thread(blob->connection)) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+
+PyObject* pysqlite_blob_close(pysqlite_Blob *self)
+{
+
+ if (!pysqlite_check_blob(self)) {
+ return NULL;
+ }
+
+ _close_blob_inner(self);
+ Py_RETURN_NONE;
+};
+
+
+static Py_ssize_t pysqlite_blob_length(pysqlite_Blob *self)
+{
+ if (!pysqlite_check_blob(self)) {
+ return -1;
+ }
+
+ return self->length;
+};
+
+static PyObject* inner_read(pysqlite_Blob *self, int read_length, int offset)
+{
+ PyObject *buffer;
+ char *raw_buffer;
+ int rc;
+
+ buffer = PyBytes_FromStringAndSize(NULL, read_length);
+ if (!buffer) {
+ return NULL;
+ }
+ raw_buffer = PyBytes_AS_STRING(buffer);
+
+ Py_BEGIN_ALLOW_THREADS
+ rc = sqlite3_blob_read(self->blob, raw_buffer, read_length, self->offset);
+ Py_END_ALLOW_THREADS
+
+ if (rc != SQLITE_OK){
+ Py_DECREF(buffer);
+ /* For some reason after modifying blob the
+ error is not set on the connection db. */
+ if (rc == SQLITE_ABORT) {
+ PyErr_SetString(pysqlite_OperationalError,
+ "Cannot operate on modified blob");
+ } else {
+ _pysqlite_seterror(self->connection->db, NULL);
+ }
+ return NULL;
+ }
+ return buffer;
+}
+
+
+PyObject* pysqlite_blob_read(pysqlite_Blob *self, PyObject *args)
+{
+ int read_length = -1;
+ PyObject *buffer;
+
+ if (!PyArg_ParseTuple(args, "|i", &read_length)) {
+ return NULL;
+ }
+
+ if (!pysqlite_check_blob(self)) {
+ return NULL;
+ }
+
+ if (read_length < 0) {
+ /* same as file read. */
+ read_length = self->length;
+ }
+
+ /* making sure we don't read more then blob size */
+ if (read_length > self->length - self->offset) {
+ read_length = self->length - self->offset;
+ }
+
+ buffer = inner_read(self, read_length, self->offset);
+
+ if (buffer != NULL) {
+ /* update offset on sucess. */
+ self->offset += read_length;
+ }
+
+ return buffer;
+};
+
+static int write_inner(pysqlite_Blob *self, const void *buf, Py_ssize_t len, int offset)
+{
+ int rc;
+
+ Py_BEGIN_ALLOW_THREADS
+ rc = sqlite3_blob_write(self->blob, buf, len, offset);
+ Py_END_ALLOW_THREADS
+ if (rc != SQLITE_OK) {
+ /* For some reason after modifying blob the
+ error is not set on the connection db. */
+ if (rc == SQLITE_ABORT) {
+ PyErr_SetString(pysqlite_OperationalError,
+ "Cannot operate on modified blob");
+ } else {
+ _pysqlite_seterror(self->connection->db, NULL);
+ }
+ return -1;
+ }
+ return 0;
+}
+
+
+PyObject* pysqlite_blob_write(pysqlite_Blob *self, PyObject *data)
+{
+ Py_buffer data_buffer;
+ int rc;
+
+ if (PyObject_GetBuffer(data, &data_buffer, PyBUF_SIMPLE) < 0) {
+ return NULL;
+ }
+
+ if (data_buffer.len > INT_MAX) {
+ PyErr_SetString(PyExc_OverflowError,
+ "data longer than INT_MAX bytes");
+ PyBuffer_Release(&data_buffer);
+ return NULL;
+ }
+
+ if (!pysqlite_check_blob(self)) {
+ PyBuffer_Release(&data_buffer);
+ return NULL;
+ }
+
+ /* TODO: throw better error on data bigger then blob. */
+
+ rc = write_inner(self, data_buffer.buf, data_buffer.len, self->offset);
+
+ if (rc == 0) {
+ self->offset += (int)data_buffer.len;
+ PyBuffer_Release(&data_buffer);
+ Py_RETURN_NONE;
+ } else {
+ PyBuffer_Release(&data_buffer);
+ return NULL;
+ }
+}
+
+
+PyObject* pysqlite_blob_seek(pysqlite_Blob *self, PyObject *args)
+{
+ int offset, from_what = 0;
+
+ if (!PyArg_ParseTuple(args, "i|i", &offset, &from_what)) {
+ return NULL;
+ }
+
+
+ if (!pysqlite_check_blob(self)) {
+ return NULL;
+ }
+
+ switch (from_what) {
+ case 0: // relative to blob begin
+ break;
+ case 1: // relative to current position
+ if (offset > INT_MAX - self->offset) {
+ goto overflow;
+ }
+ offset = self->offset + offset;
+ break;
+ case 2: // relative to blob end
+ if (offset > INT_MAX - self->length) {
+ goto overflow;
+ }
+ offset = self->length + offset;
+ break;
+ default:
+ return PyErr_Format(PyExc_ValueError,
+ "from_what should be 0, 1 or 2");
+ }
+
+ if (offset < 0 || offset > self->length) {
+ return PyErr_Format(PyExc_ValueError, "offset out of blob range");
+ }
+
+ self->offset = offset;
+ Py_RETURN_NONE;
+
+overflow:
+ return PyErr_Format(PyExc_OverflowError, "seek offset result in overflow");
+}
+
+
+PyObject* pysqlite_blob_tell(pysqlite_Blob *self)
+{
+ if (!pysqlite_check_blob(self)) {
+ return NULL;
+ }
+
+ return PyLong_FromLong(self->offset);
+}
+
+
+PyObject* pysqlite_blob_enter(pysqlite_Blob *self)
+{
+ if (!pysqlite_check_blob(self)) {
+ return NULL;
+ }
+
+ Py_INCREF(self);
+ return (PyObject *)self;
+}
+
+
+PyObject* pysqlite_blob_exit(pysqlite_Blob *self, PyObject *args)
+{
+ PyObject *res;
+ if (!pysqlite_check_blob(self)) {
+ return NULL;
+ }
+
+ res = pysqlite_blob_close(self);
+ if (!res) {
+ return NULL;
+ }
+ Py_XDECREF(res);
+
+ Py_RETURN_FALSE;
+}
+
+static PyObject* pysqlite_blob_concat(pysqlite_Blob *self, PyObject *args)
+{
+ if (pysqlite_check_blob(self)) {
+ PyErr_SetString(PyExc_SystemError,
+ "Blob don't support concatenation");
+ }
+ return NULL;
+}
+
+static PyObject* pysqlite_blob_repeat(pysqlite_Blob *self, PyObject *args)
+{
+ if (pysqlite_check_blob(self)) {
+ PyErr_SetString(PyExc_SystemError,
+ "Blob don't support repeat operation");
+ }
+ return NULL;
+}
+
+static int pysqlite_blob_contains(pysqlite_Blob *self, PyObject *args)
+{
+ if (pysqlite_check_blob(self)) {
+ PyErr_SetString(PyExc_SystemError,
+ "Blob don't support cotains operation");
+ }
+ return -1;
+}
+
+static PyObject* pysqlite_blob_item(pysqlite_Blob *self, Py_ssize_t i)
+{
+ if (!pysqlite_check_blob(self)) {
+ return NULL;
+ }
+
+ if (i < 0 || i >= self->length) {
+ PyErr_SetString(PyExc_IndexError, "Blob index out of range");
+ return NULL;
+ }
+
+ return inner_read(self, 1, i);
+}
+
+static int pysqlite_blob_ass_item(pysqlite_Blob *self, Py_ssize_t i, PyObject *v)
+{
+ const char *buf;
+
+ if (!pysqlite_check_blob(self)) {
+ return -1;
+ }
+
+ if (i < 0 || i >= self->length) {
+ PyErr_SetString(PyExc_IndexError, "Blob index out of range");
+ return -1;
+ }
+ if (v == NULL) {
+ PyErr_SetString(PyExc_TypeError,
+ "Blob object doesn't support item deletion");
+ return -1;
+ }
+ if (! (PyBytes_Check(v) && PyBytes_Size(v)==1) ) {
+ PyErr_SetString(PyExc_IndexError,
+ "Blob assignment must be length-1 bytes()");
+ return -1;
+ }
+
+ buf = PyBytes_AsString(v);
+ return write_inner(self, buf, 1, i);
+}
+
+
+static PyObject * pysqlite_blob_subscript(pysqlite_Blob *self, PyObject *item)
+{
+ if (!pysqlite_check_blob(self)) {
+ return NULL;
+ }
+
+ if (PyIndex_Check(item)) {
+ Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
+ if (i == -1 && PyErr_Occurred())
+ return NULL;
+ if (i < 0)
+ i += self->length;
+ if (i < 0 || i >= self->length) {
+ PyErr_SetString(PyExc_IndexError,
+ "Blob index out of range");
+ return NULL;
+ }
+ // TODO: I am not sure...
+ return inner_read(self, 1, i);
+ }
+ else if (PySlice_Check(item)) {
+ Py_ssize_t start, stop, step, slicelen;
+
+ if (PySlice_GetIndicesEx(item, self->length,
+ &start, &stop, &step, &slicelen) < 0) {
+ return NULL;
+ }
+
+ if (slicelen <= 0)
+ return PyBytes_FromStringAndSize("", 0);
+ else if (step == 1)
+ return inner_read(self, slicelen, start);
+ else {
+ char *result_buf = (char *)PyMem_Malloc(slicelen);
+ char *data_buff = NULL;
+ Py_ssize_t cur, i;
+ PyObject *result;
+ int rc;
+
+ if (result_buf == NULL)
+ return PyErr_NoMemory();
+
+ data_buff = (char *)PyMem_Malloc(stop - start);
+ if (data_buff == NULL) {
+ PyMem_Free(result_buf);
+ return PyErr_NoMemory();
+ }
+
+ Py_BEGIN_ALLOW_THREADS
+ rc = sqlite3_blob_read(self->blob, data_buff, stop - start, start);
+ Py_END_ALLOW_THREADS
+
+ if (rc != SQLITE_OK){
+ /* For some reason after modifying blob the
+ error is not set on the connection db. */
+ if (rc == SQLITE_ABORT) {
+ PyErr_SetString(pysqlite_OperationalError,
+ "Cannot operate on modified blob");
+ } else {
+ _pysqlite_seterror(self->connection->db, NULL);
+ }
+ PyMem_Free(result_buf);
+ PyMem_Free(data_buff);
+ return NULL;
+ }
+
+ for (cur = 0, i = 0; i < slicelen;
+ cur += step, i++) {
+ result_buf[i] = data_buff[cur];
+ }
+ result = PyBytes_FromStringAndSize(result_buf,
+ slicelen);
+ PyMem_Free(result_buf);
+ PyMem_Free(data_buff);
+ return result;
+ }
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "Blob indices must be integers");
+ return NULL;
+ }
+}
+
+
+static int pysqlite_blob_ass_subscript(pysqlite_Blob *self, PyObject *item, PyObject *value)
+{
+ int rc;
+
+ if (!pysqlite_check_blob(self)) {
+ return -1;
+ }
+
+ if (PyIndex_Check(item)) {
+ Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
+ const char *buf;
+
+ if (i == -1 && PyErr_Occurred())
+ return -1;
+ if (i < 0)
+ i += self->length;
+ if (i < 0 || i >= self->length) {
+ PyErr_SetString(PyExc_IndexError,
+ "Blob index out of range");
+ return -1;
+ }
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError,
+ "Blob doesn't support item deletion");
+ return -1;
+ }
+ if (! (PyBytes_Check(value) && PyBytes_Size(value)==1) ) {
+ PyErr_SetString(PyExc_IndexError,
+ "Blob assignment must be length-1 bytes()");
+ return -1;
+ }
+
+ buf = PyBytes_AsString(value);
+ return write_inner(self, buf, 1, i);
+ }
+ else if (PySlice_Check(item)) {
+ Py_ssize_t start, stop, step, slicelen;
+ Py_buffer vbuf;
+
+ if (PySlice_GetIndicesEx(item,
+ self->length, &start, &stop,
+ &step, &slicelen) < 0) {
+ return -1;
+ }
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError,
+ "Blob object doesn't support slice deletion");
+ return -1;
+ }
+ if (PyObject_GetBuffer(value, &vbuf, PyBUF_SIMPLE) < 0)
+ return -1;
+ if (vbuf.len != slicelen) {
+ PyErr_SetString(PyExc_IndexError,
+ "Blob slice assignment is wrong size");
+ PyBuffer_Release(&vbuf);
+ return -1;
+ }
+
+ if (slicelen == 0) {
+ }
+ else if (step == 1) {
+ rc = write_inner(self, vbuf.buf, slicelen, start);
+ }
+ else {
+ Py_ssize_t cur, i;
+ char *data_buff;
+
+
+ data_buff = (char *)PyMem_Malloc(stop - start);
+ if (data_buff == NULL) {
+ PyErr_NoMemory();
+ return -1;
+ }
+
+ Py_BEGIN_ALLOW_THREADS
+ rc = sqlite3_blob_read(self->blob, data_buff, stop - start, start);
+ Py_END_ALLOW_THREADS
+
+ if (rc != SQLITE_OK){
+ /* For some reason after modifying blob the
+ error is not set on the connection db. */
+ if (rc == SQLITE_ABORT) {
+ PyErr_SetString(pysqlite_OperationalError,
+ "Cannot operate on modified blob");
+ } else {
+ _pysqlite_seterror(self->connection->db, NULL);
+ }
+ PyMem_Free(data_buff);
+ rc = -1;
+ }
+
+ for (cur = 0, i = 0;
+ i < slicelen;
+ cur += step, i++)
+ {
+ data_buff[cur] = ((char *)vbuf.buf)[i];
+ }
+
+ Py_BEGIN_ALLOW_THREADS
+ rc = sqlite3_blob_write(self->blob, data_buff, stop - start, start);
+ Py_END_ALLOW_THREADS
+
+ if (rc != SQLITE_OK){
+ /* For some reason after modifying blob the
+ error is not set on the connection db. */
+ if (rc == SQLITE_ABORT) {
+ PyErr_SetString(pysqlite_OperationalError,
+ "Cannot operate on modified blob");
+ } else {
+ _pysqlite_seterror(self->connection->db, NULL);
+ }
+ PyMem_Free(data_buff);
+ rc = -1;
+ }
+ rc = 0;
+
+ }
+ PyBuffer_Release(&vbuf);
+ return rc;
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "mmap indices must be integer");
+ return -1;
+ }
+}
+
+
+static PyMethodDef blob_methods[] = {
+ {"read", (PyCFunction)pysqlite_blob_read, METH_VARARGS,
+ PyDoc_STR("read data from blob")},
+ {"write", (PyCFunction)pysqlite_blob_write, METH_O,
+ PyDoc_STR("write data to blob")},
+ {"close", (PyCFunction)pysqlite_blob_close, METH_NOARGS,
+ PyDoc_STR("close blob")},
+ {"seek", (PyCFunction)pysqlite_blob_seek, METH_VARARGS,
+ PyDoc_STR("change blob current offset")},
+ {"tell", (PyCFunction)pysqlite_blob_tell, METH_NOARGS,
+ PyDoc_STR("return blob current offset")},
+ {"__enter__", (PyCFunction)pysqlite_blob_enter, METH_NOARGS,
+ PyDoc_STR("blob context manager enter")},
+ {"__exit__", (PyCFunction)pysqlite_blob_exit, METH_VARARGS,
+ PyDoc_STR("blob context manager exit")},
+ {NULL, NULL}
+};
+
+static PySequenceMethods blob_sequence_methods = {
+ .sq_length = (lenfunc)pysqlite_blob_length,
+ .sq_concat = (binaryfunc)pysqlite_blob_concat,
+ .sq_repeat = (ssizeargfunc)pysqlite_blob_repeat,
+ .sq_item = (ssizeargfunc)pysqlite_blob_item,
+ .sq_ass_item = (ssizeobjargproc)pysqlite_blob_ass_item,
+ .sq_contains = (objobjproc)pysqlite_blob_contains,
+};
+
+static PyMappingMethods blob_mapping_methods = {
+ (lenfunc)pysqlite_blob_length,
+ (binaryfunc)pysqlite_blob_subscript,
+ (objobjargproc)pysqlite_blob_ass_subscript,
+};
+
+PyTypeObject pysqlite_BlobType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ MODULE_NAME ".Blob",
+ .tp_basicsize = sizeof(pysqlite_Blob),
+ .tp_dealloc = (destructor)pysqlite_blob_dealloc,
+ .tp_as_sequence = &blob_sequence_methods,
+ .tp_as_mapping = &blob_mapping_methods,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_weaklistoffset = offsetof(pysqlite_Blob, in_weakreflist),
+ .tp_methods = blob_methods,
+};
+
+extern int pysqlite_blob_setup_types(void)
+{
+ pysqlite_BlobType.tp_new = PyType_GenericNew;
+ return PyType_Ready(&pysqlite_BlobType);
+}
#include "connection.h"
#include "statement.h"
#include "cursor.h"
+#include "blob.h"
#include "prepare_protocol.h"
#include "util.h"
Py_CLEAR(self->statement_cache);
Py_CLEAR(self->statements);
Py_CLEAR(self->cursors);
+ Py_CLEAR(self->blobs);
Py_INCREF(Py_None);
Py_XSETREF(self->row_factory, Py_None);
self->created_statements = 0;
self->created_cursors = 0;
- /* Create lists of weak references to statements/cursors */
+ /* Create lists of weak references to statements/cursors/blobs */
self->statements = PyList_New(0);
self->cursors = PyList_New(0);
- if (!self->statements || !self->cursors) {
+ self->blobs = PyList_New(0);
+ if (!self->statements || !self->cursors || !self->blobs) {
return -1;
}
Py_XDECREF(self->collations);
Py_XDECREF(self->statements);
Py_XDECREF(self->cursors);
+ Py_XDECREF(self->blobs);
Py_TYPE(self)->tp_free((PyObject*)self);
}
return cursor;
}
+PyObject* pysqlite_connection_blob(pysqlite_Connection *self, PyObject *args,
+ PyObject *kwargs)
+{
+ static char *kwlist[] = {"table", "column", "row", "readonly",
+ "dbname", NULL, NULL};
+ int rc;
+ const char *dbname = "main", *table, *column;
+ long long row;
+ int readonly = 0;
+ sqlite3_blob *blob;
+ pysqlite_Blob *pyblob = NULL;
+ PyObject *weakref;
+
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ssL|ps", kwlist,
+ &table, &column, &row, &readonly,
+ &dbname)) {
+ return NULL;
+ }
+
+ Py_BEGIN_ALLOW_THREADS
+ rc = sqlite3_blob_open(self->db, dbname, table, column, row,
+ !readonly, &blob);
+ Py_END_ALLOW_THREADS
+
+ if (rc != SQLITE_OK) {
+ _pysqlite_seterror(self->db, NULL);
+ return NULL;
+ }
+
+ pyblob = PyObject_New(pysqlite_Blob, &pysqlite_BlobType);
+ if (!pyblob) {
+ goto error;
+ }
+
+ rc = pysqlite_blob_init(pyblob, self, blob);
+ if (rc) {
+ Py_CLEAR(pyblob);
+ goto error;
+ }
+
+ // Add our blob to connection blobs list
+ weakref = PyWeakref_NewRef((PyObject*)pyblob, NULL);
+ if (!weakref) {
+ Py_CLEAR(pyblob);
+ goto error;
+ }
+ if (PyList_Append(self->blobs, weakref) != 0) {
+ Py_CLEAR(weakref);
+ Py_CLEAR(pyblob);
+ goto error;
+ }
+ Py_DECREF(weakref);
+
+ return (PyObject*)pyblob;
+
+error:
+ Py_BEGIN_ALLOW_THREADS
+ sqlite3_blob_close(blob);
+ Py_END_ALLOW_THREADS
+ return NULL;
+}
+
+static void pysqlite_close_all_blobs(pysqlite_Connection *self)
+{
+ int i;
+ PyObject *weakref;
+ PyObject *blob;
+
+ for (i = 0; i < PyList_GET_SIZE(self->blobs); i++) {
+ weakref = PyList_GET_ITEM(self->blobs, i);
+ blob = PyWeakref_GetObject(weakref);
+ if (blob != Py_None) {
+ pysqlite_blob_close((pysqlite_Blob*)blob);
+ }
+ }
+}
+
PyObject* pysqlite_connection_close(pysqlite_Connection* self, PyObject* args)
{
int rc;
pysqlite_do_all_statements(self, ACTION_FINALIZE, 1);
+ pysqlite_close_all_blobs(self);
+
if (self->db) {
rc = SQLITE3_CLOSE(self->db);
static PyMethodDef connection_methods[] = {
{"cursor", (PyCFunction)(void(*)(void))pysqlite_connection_cursor, METH_VARARGS|METH_KEYWORDS,
PyDoc_STR("Return a cursor for the connection.")},
+ {"open_blob", (PyCFunction)pysqlite_connection_blob, METH_VARARGS|METH_KEYWORDS,
+ PyDoc_STR("return a blob object")},
{"close", (PyCFunction)pysqlite_connection_close, METH_NOARGS,
PyDoc_STR("Closes the connection.")},
{"commit", (PyCFunction)pysqlite_connection_commit, METH_NOARGS,
#include "prepare_protocol.h"
#include "microprotocols.h"
#include "row.h"
+#include "blob.h"
#if SQLITE_VERSION_NUMBER >= 3003003
#define HAVE_SHARED_CACHE
typedef struct _IntConstantPair IntConstantPair;
+/* sqlite API error codes */
+static const IntConstantPair _error_codes[] = {
+ {"SQLITE_OK", SQLITE_OK},
+ {"SQLITE_ERROR", SQLITE_ERROR},
+ {"SQLITE_INTERNAL", SQLITE_INTERNAL},
+ {"SQLITE_PERM", SQLITE_PERM},
+ {"SQLITE_ABORT", SQLITE_ABORT},
+ {"SQLITE_BUSY", SQLITE_BUSY},
+ {"SQLITE_LOCKED", SQLITE_LOCKED},
+ {"SQLITE_NOMEM", SQLITE_NOMEM},
+ {"SQLITE_READONLY", SQLITE_READONLY},
+ {"SQLITE_INTERRUPT", SQLITE_INTERRUPT},
+ {"SQLITE_IOERR", SQLITE_IOERR},
+ {"SQLITE_CORRUPT", SQLITE_CORRUPT},
+ {"SQLITE_NOTFOUND", SQLITE_NOTFOUND},
+ {"SQLITE_FULL", SQLITE_FULL},
+ {"SQLITE_CANTOPEN", SQLITE_CANTOPEN},
+ {"SQLITE_PROTOCOL", SQLITE_PROTOCOL},
+ {"SQLITE_EMPTY", SQLITE_EMPTY},
+ {"SQLITE_SCHEMA", SQLITE_SCHEMA},
+ {"SQLITE_TOOBIG", SQLITE_TOOBIG},
+ {"SQLITE_CONSTRAINT", SQLITE_CONSTRAINT},
+ {"SQLITE_MISMATCH", SQLITE_MISMATCH},
+ {"SQLITE_MISUSE", SQLITE_MISUSE},
+#ifdef SQLITE_NOLFS
+ {"SQLITE_NOLFS", SQLITE_NOLFS},
+#endif
+#ifdef SQLITE_AUTH
+ {"SQLITE_AUTH", SQLITE_AUTH},
+#endif
+#ifdef SQLITE_FORMAT
+ {"SQLITE_FORMAT", SQLITE_FORMAT},
+#endif
+#ifdef SQLITE_RANGE
+ {"SQLITE_RANGE", SQLITE_RANGE},
+#endif
+#ifdef SQLITE_NOTADB
+ {"SQLITE_NOTADB", SQLITE_NOTADB},
+#endif
+ {"SQLITE_DONE", SQLITE_DONE},
+ {"SQLITE_ROW", SQLITE_ROW},
+ {(char*)NULL, 0},
+ {"SQLITE_UNKNOWN", -1}
+};
+
+const char *sqlite3ErrName(int rc) {
+ int i;
+ for (i = 0; _error_codes[i].constant_name != 0; i++) {
+ if (_error_codes[i].constant_value == rc)
+ return _error_codes[i].constant_name;
+ }
+ // No error code matched.
+ return _error_codes[i+1].constant_name;
+}
+
static const IntConstantPair _int_constants[] = {
{"PARSE_DECLTYPES", PARSE_DECLTYPES},
{"PARSE_COLNAMES", PARSE_COLNAMES},
{"SQLITE_OK", SQLITE_OK},
+ /* enumerated return values for sqlite3_set_authorizer() callback */
{"SQLITE_DENY", SQLITE_DENY},
{"SQLITE_IGNORE", SQLITE_IGNORE},
+
+ /* enumerated values for sqlite3_set_authorizer() callback */
{"SQLITE_CREATE_INDEX", SQLITE_CREATE_INDEX},
{"SQLITE_CREATE_TABLE", SQLITE_CREATE_TABLE},
{"SQLITE_CREATE_TEMP_INDEX", SQLITE_CREATE_TEMP_INDEX},
NULL
};
+
+static int add_to_dict(PyObject *dict, const char *key, int value)
+{
+ int sawerror;
+ PyObject *value_obj = PyLong_FromLong(value);
+ PyObject *name = PyUnicode_FromString(key);
+
+ if (!value_obj || !name) {
+ Py_XDECREF(name);
+ Py_XDECREF(value_obj);
+ return 1;
+ }
+
+ sawerror = PyDict_SetItem(dict, name, value_obj) < 0;
+
+ Py_DECREF(value_obj);
+ Py_DECREF(name);
+
+ if (sawerror)
+ return 1;
+ return 0;
+}
+
PyMODINIT_FUNC PyInit__sqlite3(void)
{
PyObject *module, *dict;
(pysqlite_connection_setup_types() < 0) ||
(pysqlite_cache_setup_types() < 0) ||
(pysqlite_statement_setup_types() < 0) ||
- (pysqlite_prepare_protocol_setup_types() < 0)
+ (pysqlite_prepare_protocol_setup_types() < 0) ||
+ (pysqlite_blob_setup_types() < 0)
) {
Py_XDECREF(module);
return NULL;
/* Set integer constants */
for (i = 0; _int_constants[i].constant_name != NULL; i++) {
- tmp_obj = PyLong_FromLong(_int_constants[i].constant_value);
- if (!tmp_obj) {
+ if (add_to_dict(dict, _int_constants[i].constant_name,
+ _int_constants[i].constant_value) != 0)
+ goto error;
+ }
+
+ /* Set error constants */
+ for (i = 0; _error_codes[i].constant_name != 0; i++) {
+ if (add_to_dict(dict, _error_codes[i].constant_name,
+ _error_codes[i].constant_value) != 0)
goto error;
- }
- PyDict_SetItemString(dict, _int_constants[i].constant_name, tmp_obj);
- Py_DECREF(tmp_obj);
}
if (!(tmp_obj = PyUnicode_FromString(PYSQLITE_VERSION))) {