Imported Upstream version 0.3.0 upstream/0.3.0
authorHyunjee Kim <hj0426.kim@samsung.com>
Fri, 10 Apr 2020 05:19:02 +0000 (14:19 +0900)
committerHyunjee Kim <hj0426.kim@samsung.com>
Fri, 10 Apr 2020 05:19:08 +0000 (14:19 +0900)
Change-Id: Ib1970338bc1075f5eee893cb85e20eb64ec0411d
Signed-off-by: Hyunjee Kim <hj0426.kim@samsung.com>
PKG-INFO
pysqlite3.egg-info/PKG-INFO
pysqlite3.egg-info/SOURCES.txt
setup.py
src/blob.c [new file with mode: 0644]
src/blob.h [new file with mode: 0644]
src/connection.c
src/connection.h
src/module.c
src/module.h
src/util.c

index c2d844fdc8a4c02079cf349e91ec517e2669ea36..dde9cc234644e2d4c18143564c85d36a6c7626d4 100644 (file)
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: pysqlite3
-Version: 0.2.2
+Version: 0.3.0
 Summary: DB-API 2.0 interface for Sqlite 3.x
 Home-page: https://github.com/coleifer/pysqlite3
 Author: Charles Leifer
index c2d844fdc8a4c02079cf349e91ec517e2669ea36..dde9cc234644e2d4c18143564c85d36a6c7626d4 100644 (file)
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: pysqlite3
-Version: 0.2.2
+Version: 0.3.0
 Summary: DB-API 2.0 interface for Sqlite 3.x
 Home-page: https://github.com/coleifer/pysqlite3
 Author: Charles Leifer
index e27f0d5c79e0abe05648edd97d05430c894b9bc3..2be7d6b7a85e69d9040e2e1b8637aeea6018cf38 100644 (file)
@@ -9,6 +9,8 @@ pysqlite3.egg-info/PKG-INFO
 pysqlite3.egg-info/SOURCES.txt
 pysqlite3.egg-info/dependency_links.txt
 pysqlite3.egg-info/top_level.txt
+src/blob.c
+src/blob.h
 src/cache.c
 src/cache.h
 src/connection.c
index 5e2080669185bd29dcb269d12fab9e73ccf0ebcc..2481da0ea01f6bd38e2a33ac4abe82c238e131f9 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -12,13 +12,13 @@ from setuptools import Extension
 # If you need to change anything, it should be enough to change setup.cfg.
 
 PACKAGE_NAME = 'pysqlite3'
-VERSION = '0.2.2'
+VERSION = '0.3.0'
 
 # define sqlite sources
 sources = [os.path.join('src', source)
            for source in ["module.c", "connection.c", "cursor.c", "cache.c",
                           "microprotocols.c", "prepare_protocol.c",
-                          "statement.c", "util.c", "row.c"]]
+                          "statement.c", "util.c", "row.c", "blob.c"]]
 
 # define packages
 packages = [PACKAGE_NAME]
diff --git a/src/blob.c b/src/blob.c
new file mode 100644 (file)
index 0000000..3112132
--- /dev/null
@@ -0,0 +1,644 @@
+#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);
+}
diff --git a/src/blob.h b/src/blob.h
new file mode 100644 (file)
index 0000000..649f09e
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef PYSQLITE_BLOB_H
+#define PYSQLITE_BLOB_H
+#include "Python.h"
+#include "sqlite3.h"
+#include "connection.h"
+
+typedef struct
+{
+    PyObject_HEAD
+    pysqlite_Connection* connection;
+    sqlite3_blob *blob;
+    int offset;
+    int length;
+
+    PyObject* in_weakreflist; /* List of weak references */
+} pysqlite_Blob;
+
+extern PyTypeObject pysqlite_BlobType;
+
+int pysqlite_blob_init(pysqlite_Blob* self, pysqlite_Connection* connection,
+                       sqlite3_blob *blob);
+PyObject* pysqlite_blob_close(pysqlite_Blob *self);
+
+int pysqlite_blob_setup_types(void);
+
+#endif
index 376502e4b2af0137b820a26e3910d02ec8083bdf..ed22cc6483b523db0338088caea334d6086a231a 100644 (file)
@@ -27,6 +27,7 @@
 #include "connection.h"
 #include "statement.h"
 #include "cursor.h"
+#include "blob.h"
 #include "prepare_protocol.h"
 #include "util.h"
 
@@ -114,6 +115,7 @@ int pysqlite_connection_init(pysqlite_Connection* self, PyObject* args, PyObject
     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);
@@ -162,10 +164,11 @@ int pysqlite_connection_init(pysqlite_Connection* self, PyObject* args, PyObject
     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;
     }
 
@@ -261,6 +264,7 @@ void pysqlite_connection_dealloc(pysqlite_Connection* self)
     Py_XDECREF(self->collations);
     Py_XDECREF(self->statements);
     Py_XDECREF(self->cursors);
+    Py_XDECREF(self->blobs);
 
     Py_TYPE(self)->tp_free((PyObject*)self);
 }
@@ -331,6 +335,84 @@ PyObject* pysqlite_connection_cursor(pysqlite_Connection* self, PyObject* args,
     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;
@@ -341,6 +423,8 @@ PyObject* pysqlite_connection_close(pysqlite_Connection* self, PyObject* args)
 
     pysqlite_do_all_statements(self, ACTION_FINALIZE, 1);
 
+    pysqlite_close_all_blobs(self);
+
     if (self->db) {
         rc = SQLITE3_CLOSE(self->db);
 
@@ -1875,6 +1959,8 @@ static PyGetSetDef connection_getset[] = {
 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,
index e88247d59e3887d86b667d193183e7c134a2d08a..8ccf8a6ac795e989aff91d8e9e4863cd94538935 100644 (file)
@@ -66,9 +66,10 @@ typedef struct
 
     pysqlite_Cache* statement_cache;
 
-    /* Lists of weak references to statements and cursors used within this connection */
+    /* Lists of weak references to statements, blobs and cursors used within this connection */
     PyObject* statements;
     PyObject* cursors;
+    PyObject* blobs;
 
     /* Counters for how many statements/cursors were created in the connection. May be
      * reset to 0 at certain intervals */
index 2a90c00a09354ca44f1b7294a5d2da3d449ea1d7..7ba9e4266ae3b23f1f685f844fc4ec53258db098 100644 (file)
@@ -28,6 +28,7 @@
 #include "prepare_protocol.h"
 #include "microprotocols.h"
 #include "row.h"
+#include "blob.h"
 
 #if SQLITE_VERSION_NUMBER >= 3003003
 #define HAVE_SHARED_CACHE
@@ -274,13 +275,71 @@ struct _IntConstantPair {
 
 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},
@@ -354,6 +413,29 @@ static struct PyModuleDef _sqlite3module = {
         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;
@@ -368,7 +450,8 @@ PyMODINIT_FUNC PyInit__sqlite3(void)
         (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;
@@ -457,12 +540,16 @@ PyMODINIT_FUNC PyInit__sqlite3(void)
 
     /* 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))) {
index 3185ec978885676830e6b251da9604628969b28d..8db22339e7437f4e33db6261ca90b0f92f6501e7 100644 (file)
@@ -48,6 +48,8 @@ extern PyObject* _pysqlite_converters;
 extern int _pysqlite_enable_callback_tracebacks;
 extern int pysqlite_BaseTypeAdapted;
 
+extern const char *sqlite3ErrName(int rc);
+
 #define PARSE_DECLTYPES 1
 #define PARSE_COLNAMES 2
 #endif
index 3fa671d052b0d83c28cdbb6491e5f60300da11d9..e8de4f66432c4b4fe709c1e6d10605e77839cdf5 100644 (file)
@@ -47,20 +47,21 @@ int pysqlite_step(sqlite3_stmt* statement, pysqlite_Connection* connection)
  */
 int _pysqlite_seterror(sqlite3* db, sqlite3_stmt* st)
 {
+    PyObject *exc_class;
     int errorcode = sqlite3_errcode(db);
 
     switch (errorcode)
     {
         case SQLITE_OK:
             PyErr_Clear();
-            break;
+            return errorcode;
         case SQLITE_INTERNAL:
         case SQLITE_NOTFOUND:
-            PyErr_SetString(pysqlite_InternalError, sqlite3_errmsg(db));
+            exc_class = pysqlite_InternalError;
             break;
         case SQLITE_NOMEM:
             (void)PyErr_NoMemory();
-            break;
+            return errorcode;
         case SQLITE_ERROR:
         case SQLITE_PERM:
         case SQLITE_ABORT:
@@ -74,26 +75,70 @@ int _pysqlite_seterror(sqlite3* db, sqlite3_stmt* st)
         case SQLITE_PROTOCOL:
         case SQLITE_EMPTY:
         case SQLITE_SCHEMA:
-            PyErr_SetString(pysqlite_OperationalError, sqlite3_errmsg(db));
+            exc_class = pysqlite_OperationalError;
             break;
         case SQLITE_CORRUPT:
-            PyErr_SetString(pysqlite_DatabaseError, sqlite3_errmsg(db));
+            exc_class = pysqlite_DatabaseError;
             break;
         case SQLITE_TOOBIG:
-            PyErr_SetString(pysqlite_DataError, sqlite3_errmsg(db));
+            exc_class = pysqlite_DataError;
             break;
         case SQLITE_CONSTRAINT:
         case SQLITE_MISMATCH:
-            PyErr_SetString(pysqlite_IntegrityError, sqlite3_errmsg(db));
+            exc_class = pysqlite_IntegrityError;
             break;
         case SQLITE_MISUSE:
-            PyErr_SetString(pysqlite_ProgrammingError, sqlite3_errmsg(db));
+            exc_class = pysqlite_ProgrammingError;;
             break;
         default:
-            PyErr_SetString(pysqlite_DatabaseError, sqlite3_errmsg(db));
+            exc_class = pysqlite_DatabaseError;
             break;
     }
 
+    /* Create and set the exception. */
+    {
+        const char *error_msg;
+        const char *error_name;
+        PyObject *exc = NULL;
+        PyObject *args = NULL;
+        PyObject *py_code = NULL;
+        PyObject *py_name = NULL;
+
+        error_name = sqlite3ErrName(errorcode);
+
+        error_msg = sqlite3_errmsg(db);
+
+        args = Py_BuildValue("(s)", error_msg);
+        if (!args)
+            goto error;
+
+        exc = PyObject_Call(exc_class, args, NULL);
+        if (!exc)
+            goto error;
+
+        py_code = Py_BuildValue("i", errorcode);
+        if (!py_code)
+            goto error;
+
+        if (PyObject_SetAttrString(exc, "sqlite_errorcode", py_code) < 0)
+            goto error;
+
+        py_name = Py_BuildValue("s", error_name);
+        if (!py_name)
+            goto error;
+
+        if (PyObject_SetAttrString(exc, "sqlite_errorname", py_name) < 0)
+            goto error;
+
+        PyErr_SetObject((PyObject *) Py_TYPE(exc), exc);
+
+    error:
+        Py_XDECREF(py_code);
+        Py_XDECREF(py_name);
+        Py_XDECREF(args);
+        Py_XDECREF(exc);
+    }
+
     return errorcode;
 }