diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/backup.h | 43 | ||||
| -rw-r--r-- | src/cache.c | 375 | ||||
| -rw-r--r-- | src/cache.h | 73 | ||||
| -rw-r--r-- | src/connection.c | 1704 | ||||
| -rw-r--r-- | src/connection.h | 138 | ||||
| -rw-r--r-- | src/cursor.c | 1126 | ||||
| -rw-r--r-- | src/cursor.h | 74 | ||||
| -rw-r--r-- | src/microprotocols.c | 142 | ||||
| -rw-r--r-- | src/microprotocols.h | 55 | ||||
| -rw-r--r-- | src/module.c | 464 | ||||
| -rw-r--r-- | src/module.h | 58 | ||||
| -rw-r--r-- | src/prepare_protocol.c | 84 | ||||
| -rw-r--r-- | src/prepare_protocol.h | 41 | ||||
| -rw-r--r-- | src/row.c | 256 | ||||
| -rw-r--r-- | src/row.h | 39 | ||||
| -rw-r--r-- | src/sqlitecompat.h | 63 | ||||
| -rw-r--r-- | src/statement.c | 543 | ||||
| -rw-r--r-- | src/statement.h | 59 | ||||
| -rw-r--r-- | src/util.c | 106 | ||||
| -rw-r--r-- | src/util.h | 38 | 
20 files changed, 5481 insertions, 0 deletions
| diff --git a/src/backup.h b/src/backup.h new file mode 100644 index 0000000..10720f0 --- /dev/null +++ b/src/backup.h @@ -0,0 +1,43 @@ +/* backup.h - definitions for the backup type + * + * Copyright (C) 2010 Gerhard Häring <gh@ghaering.de> + * + * This file is part of pysqlite. + * + * This software is provided 'as-is', without any express or implied + * warranty.  In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + *    claim that you wrote the original software. If you use this software + *    in a product, an acknowledgment in the product documentation would be + *    appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + *    misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#ifndef PYSQLITE_BACKUP_H +#define PYSQLITE_BACKUP_H +#include "Python.h" + +#include "sqlite3.h" +#include "connection.h" + +typedef struct +{ +    PyObject_HEAD +    sqlite3_backup* backup; +    pysqlite_Connection* source_con; +    pysqlite_Connection* dest_con; +} pysqlite_Backup; + +extern PyTypeObject pysqlite_BackupType; + +int pysqlite_backup_setup_types(void); + +#endif diff --git a/src/cache.c b/src/cache.c new file mode 100644 index 0000000..0653367 --- /dev/null +++ b/src/cache.c @@ -0,0 +1,375 @@ +/* cache .c - a LRU cache + * + * Copyright (C) 2004-2010 Gerhard Häring <gh@ghaering.de> + * + * This file is part of pysqlite. + * + * This software is provided 'as-is', without any express or implied + * warranty.  In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + *    claim that you wrote the original software. If you use this software + *    in a product, an acknowledgment in the product documentation would be + *    appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + *    misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include "sqlitecompat.h" +#include "cache.h" +#include <limits.h> + +/* only used internally */ +pysqlite_Node* pysqlite_new_node(PyObject* key, PyObject* data) +{ +    pysqlite_Node* node; + +    node = (pysqlite_Node*) (pysqlite_NodeType.tp_alloc(&pysqlite_NodeType, 0)); +    if (!node) { +        return NULL; +    } + +    Py_INCREF(key); +    node->key = key; + +    Py_INCREF(data); +    node->data = data; + +    node->prev = NULL; +    node->next = NULL; + +    return node; +} + +void pysqlite_node_dealloc(pysqlite_Node* self) +{ +    Py_DECREF(self->key); +    Py_DECREF(self->data); + +    Py_TYPE(self)->tp_free((PyObject*)self); +} + +int pysqlite_cache_init(pysqlite_Cache* self, PyObject* args, PyObject* kwargs) +{ +    PyObject* factory; +    int size = 10; + +    self->factory = NULL; + +    if (!PyArg_ParseTuple(args, "O|i", &factory, &size)) { +        return -1; +    } + +    /* minimum cache size is 5 entries */ +    if (size < 5) { +        size = 5; +    } +    self->size = size; +    self->first = NULL; +    self->last = NULL; + +    self->mapping = PyDict_New(); +    if (!self->mapping) { +        return -1; +    } + +    Py_INCREF(factory); +    self->factory = factory; + +    self->decref_factory = 1; + +    return 0; +} + +void pysqlite_cache_dealloc(pysqlite_Cache* self) +{ +    pysqlite_Node* node; +    pysqlite_Node* delete_node; + +    if (!self->factory) { +        /* constructor failed, just get out of here */ +        return; +    } + +    /* iterate over all nodes and deallocate them */ +    node = self->first; +    while (node) { +        delete_node = node; +        node = node->next; +        Py_DECREF(delete_node); +    } + +    if (self->decref_factory) { +        Py_DECREF(self->factory); +    } +    Py_DECREF(self->mapping); + +    Py_TYPE(self)->tp_free((PyObject*)self); +} + +PyObject* pysqlite_cache_get(pysqlite_Cache* self, PyObject* args) +{ +    PyObject* key = args; +    pysqlite_Node* node; +    pysqlite_Node* ptr; +    PyObject* data; + +    node = (pysqlite_Node*)PyDict_GetItem(self->mapping, key); +    if (node) { +        /* an entry for this key already exists in the cache */ + +        /* increase usage counter of the node found */ +        if (node->count < LONG_MAX) { +            node->count++; +        } + +        /* if necessary, reorder entries in the cache by swapping positions */ +        if (node->prev && node->count > node->prev->count) { +            ptr = node->prev; + +            while (ptr->prev && node->count > ptr->prev->count) { +                ptr = ptr->prev; +            } + +            if (node->next) { +                node->next->prev = node->prev; +            } else { +                self->last = node->prev; +            } +            if (node->prev) { +                node->prev->next = node->next; +            } +            if (ptr->prev) { +                ptr->prev->next = node; +            } else { +                self->first = node; +            } + +            node->next = ptr; +            node->prev = ptr->prev; +            if (!node->prev) { +                self->first = node; +            } +            ptr->prev = node; +        } +    } else { +        /* There is no entry for this key in the cache, yet. We'll insert a new +         * entry in the cache, and make space if necessary by throwing the +         * least used item out of the cache. */ + +        if (PyDict_Size(self->mapping) == self->size) { +            if (self->last) { +                node = self->last; + +                if (PyDict_DelItem(self->mapping, self->last->key) != 0) { +                    return NULL; +                } + +                if (node->prev) { +                    node->prev->next = NULL; +                } +                self->last = node->prev; +                node->prev = NULL; + +                Py_DECREF(node); +            } +        } + +        data = PyObject_CallFunction(self->factory, "O", key); + +        if (!data) { +            return NULL; +        } + +        node = pysqlite_new_node(key, data); +        if (!node) { +            return NULL; +        } +        node->prev = self->last; + +        Py_DECREF(data); + +        if (PyDict_SetItem(self->mapping, key, (PyObject*)node) != 0) { +            Py_DECREF(node); +            return NULL; +        } + +        if (self->last) { +            self->last->next = node; +        } else { +            self->first = node; +        } +        self->last = node; +    } + +    Py_INCREF(node->data); +    return node->data; +} + +PyObject* pysqlite_cache_display(pysqlite_Cache* self, PyObject* args) +{ +    pysqlite_Node* ptr; +    PyObject* prevkey; +    PyObject* nextkey; +    PyObject* fmt_args; +    PyObject* template; +    PyObject* display_str; + +    ptr = self->first; + +    while (ptr) { +        if (ptr->prev) { +            prevkey = ptr->prev->key; +        } else { +            prevkey = Py_None; +        } +        Py_INCREF(prevkey); + +        if (ptr->next) { +            nextkey = ptr->next->key; +        } else { +            nextkey = Py_None; +        } +        Py_INCREF(nextkey); + +        fmt_args = Py_BuildValue("OOO", prevkey, ptr->key, nextkey); +        if (!fmt_args) { +            return NULL; +        } +        template = PyString_FromString("%s <- %s ->%s\n"); +        if (!template) { +            Py_DECREF(fmt_args); +            return NULL; +        } +        display_str = PyString_Format(template, fmt_args); +        Py_DECREF(template); +        Py_DECREF(fmt_args); +        if (!display_str) { +            return NULL; +        } +        PyObject_Print(display_str, stdout, Py_PRINT_RAW); +        Py_DECREF(display_str); + +        Py_DECREF(prevkey); +        Py_DECREF(nextkey); + +        ptr = ptr->next; +    } + +    Py_INCREF(Py_None); +    return Py_None; +} + +static PyMethodDef cache_methods[] = { +    {"get", (PyCFunction)pysqlite_cache_get, METH_O, +        PyDoc_STR("Gets an entry from the cache or calls the factory function to produce one.")}, +    {"display", (PyCFunction)pysqlite_cache_display, METH_NOARGS, +        PyDoc_STR("For debugging only.")}, +    {NULL, NULL} +}; + +PyTypeObject pysqlite_NodeType = { +        PyVarObject_HEAD_INIT(NULL, 0) +        MODULE_NAME "Node",                             /* tp_name */ +        sizeof(pysqlite_Node),                          /* tp_basicsize */ +        0,                                              /* tp_itemsize */ +        (destructor)pysqlite_node_dealloc,              /* tp_dealloc */ +        0,                                              /* tp_print */ +        0,                                              /* tp_getattr */ +        0,                                              /* tp_setattr */ +        0,                                              /* tp_compare */ +        0,                                              /* tp_repr */ +        0,                                              /* tp_as_number */ +        0,                                              /* tp_as_sequence */ +        0,                                              /* tp_as_mapping */ +        0,                                              /* tp_hash */ +        0,                                              /* tp_call */ +        0,                                              /* tp_str */ +        0,                                              /* tp_getattro */ +        0,                                              /* tp_setattro */ +        0,                                              /* tp_as_buffer */ +        Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,         /* tp_flags */ +        0,                                              /* tp_doc */ +        0,                                              /* tp_traverse */ +        0,                                              /* tp_clear */ +        0,                                              /* tp_richcompare */ +        0,                                              /* tp_weaklistoffset */ +        0,                                              /* tp_iter */ +        0,                                              /* tp_iternext */ +        0,                                              /* tp_methods */ +        0,                                              /* tp_members */ +        0,                                              /* tp_getset */ +        0,                                              /* tp_base */ +        0,                                              /* tp_dict */ +        0,                                              /* tp_descr_get */ +        0,                                              /* tp_descr_set */ +        0,                                              /* tp_dictoffset */ +        (initproc)0,                                    /* tp_init */ +        0,                                              /* tp_alloc */ +        0,                                              /* tp_new */ +        0                                               /* tp_free */ +}; + +PyTypeObject pysqlite_CacheType = { +        PyVarObject_HEAD_INIT(NULL, 0) +        MODULE_NAME ".Cache",                           /* tp_name */ +        sizeof(pysqlite_Cache),                         /* tp_basicsize */ +        0,                                              /* tp_itemsize */ +        (destructor)pysqlite_cache_dealloc,             /* tp_dealloc */ +        0,                                              /* tp_print */ +        0,                                              /* tp_getattr */ +        0,                                              /* tp_setattr */ +        0,                                              /* tp_compare */ +        0,                                              /* tp_repr */ +        0,                                              /* tp_as_number */ +        0,                                              /* tp_as_sequence */ +        0,                                              /* tp_as_mapping */ +        0,                                              /* tp_hash */ +        0,                                              /* tp_call */ +        0,                                              /* tp_str */ +        0,                                              /* tp_getattro */ +        0,                                              /* tp_setattro */ +        0,                                              /* tp_as_buffer */ +        Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,         /* tp_flags */ +        0,                                              /* tp_doc */ +        0,                                              /* tp_traverse */ +        0,                                              /* tp_clear */ +        0,                                              /* tp_richcompare */ +        0,                                              /* tp_weaklistoffset */ +        0,                                              /* tp_iter */ +        0,                                              /* tp_iternext */ +        cache_methods,                                  /* tp_methods */ +        0,                                              /* tp_members */ +        0,                                              /* tp_getset */ +        0,                                              /* tp_base */ +        0,                                              /* tp_dict */ +        0,                                              /* tp_descr_get */ +        0,                                              /* tp_descr_set */ +        0,                                              /* tp_dictoffset */ +        (initproc)pysqlite_cache_init,                  /* tp_init */ +        0,                                              /* tp_alloc */ +        0,                                              /* tp_new */ +        0                                               /* tp_free */ +}; + +extern int pysqlite_cache_setup_types(void) +{ +    int rc; + +    pysqlite_NodeType.tp_new = PyType_GenericNew; +    pysqlite_CacheType.tp_new = PyType_GenericNew; + +    rc = PyType_Ready(&pysqlite_NodeType); +    if (rc < 0) { +        return rc; +    } + +    rc = PyType_Ready(&pysqlite_CacheType); +    return rc; +} diff --git a/src/cache.h b/src/cache.h new file mode 100644 index 0000000..b09517c --- /dev/null +++ b/src/cache.h @@ -0,0 +1,73 @@ +/* cache.h - definitions for the LRU cache + * + * Copyright (C) 2004-2010 Gerhard Häring <gh@ghaering.de> + * + * This file is part of pysqlite. + * + * This software is provided 'as-is', without any express or implied + * warranty.  In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + *    claim that you wrote the original software. If you use this software + *    in a product, an acknowledgment in the product documentation would be + *    appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + *    misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#ifndef PYSQLITE_CACHE_H +#define PYSQLITE_CACHE_H +#include "Python.h" + +/* The LRU cache is implemented as a combination of a doubly-linked with a + * dictionary. The list items are of type 'Node' and the dictionary has the + * nodes as values. */ + +typedef struct _pysqlite_Node +{ +    PyObject_HEAD +    PyObject* key; +    PyObject* data; +    long count; +    struct _pysqlite_Node* prev; +    struct _pysqlite_Node* next; +} pysqlite_Node; + +typedef struct +{ +    PyObject_HEAD +    int size; + +    /* a dictionary mapping keys to Node entries */ +    PyObject* mapping; + +    /* the factory callable */ +    PyObject* factory; + +    pysqlite_Node* first; +    pysqlite_Node* last; + +    /* if set, decrement the factory function when the Cache is deallocated. +     * this is almost always desirable, but not in the pysqlite context */ +    int decref_factory; +} pysqlite_Cache; + +extern PyTypeObject pysqlite_NodeType; +extern PyTypeObject pysqlite_CacheType; + +int pysqlite_node_init(pysqlite_Node* self, PyObject* args, PyObject* kwargs); +void pysqlite_node_dealloc(pysqlite_Node* self); + +int pysqlite_cache_init(pysqlite_Cache* self, PyObject* args, PyObject* kwargs); +void pysqlite_cache_dealloc(pysqlite_Cache* self); +PyObject* pysqlite_cache_get(pysqlite_Cache* self, PyObject* args); + +int pysqlite_cache_setup_types(void); + +#endif diff --git a/src/connection.c b/src/connection.c new file mode 100644 index 0000000..481b9b5 --- /dev/null +++ b/src/connection.c @@ -0,0 +1,1704 @@ +/* connection.c - the connection type + * + * Copyright (C) 2004-2010 Gerhard Häring <gh@ghaering.de> + * + * This file is part of pysqlite. + *  + * This software is provided 'as-is', without any express or implied + * warranty.  In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + *    claim that you wrote the original software. If you use this software + *    in a product, an acknowledgment in the product documentation would be + *    appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + *    misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include "cache.h" +#include "module.h" +#include "connection.h" +#include "statement.h" +#include "cursor.h" +#include "prepare_protocol.h" +#include "util.h" +#include "sqlitecompat.h" + +#ifdef PYSQLITE_EXPERIMENTAL +#include "backup.h" +#endif + +#include "pythread.h" + +#define ACTION_FINALIZE 1 +#define ACTION_RESET 2 + +#if SQLITE_VERSION_NUMBER >= 3003008 +#ifndef SQLITE_OMIT_LOAD_EXTENSION +#define HAVE_LOAD_EXTENSION +#endif +#endif + +static int pysqlite_connection_set_isolation_level(pysqlite_Connection* self, PyObject* isolation_level); +static void _pysqlite_drop_unused_cursor_references(pysqlite_Connection* self); + + +static void _sqlite3_result_error(sqlite3_context* ctx, const char* errmsg, int len) +{ +    /* in older SQLite versions, calling sqlite3_result_error in callbacks +     * triggers a bug in SQLite that leads either to irritating results or +     * segfaults, depending on the SQLite version */ +#if SQLITE_VERSION_NUMBER >= 3003003 +    sqlite3_result_error(ctx, errmsg, len); +#else +    PyErr_SetString(pysqlite_OperationalError, errmsg); +#endif +} + +int pysqlite_connection_init(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) +{ +    static char *kwlist[] = {"database", "timeout", "detect_types", "isolation_level", "check_same_thread", "factory", "cached_statements", NULL, NULL}; + +    PyObject* database; +    int detect_types = 0; +    PyObject* isolation_level = NULL; +    PyObject* factory = NULL; +    int check_same_thread = 1; +    int cached_statements = 100; +    double timeout = 5.0; +    int rc; +    PyObject* class_attr = NULL; +    PyObject* class_attr_str = NULL; +    int is_apsw_connection = 0; +    PyObject* database_utf8; + +    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|diOiOi", kwlist, +                                     &database, &timeout, &detect_types, &isolation_level, &check_same_thread, &factory, &cached_statements)) +    { +        return -1; +    } + +    self->initialized = 1; + +    self->begin_statement = NULL; + +    self->statement_cache = NULL; +    self->statements = NULL; +    self->cursors = NULL; + +    Py_INCREF(Py_None); +    self->row_factory = Py_None; + +    Py_INCREF(&PyUnicode_Type); +    self->text_factory = (PyObject*)&PyUnicode_Type; + +    if (PyString_Check(database) || PyUnicode_Check(database)) { +        if (PyString_Check(database)) { +            database_utf8 = database; +            Py_INCREF(database_utf8); +        } else { +            database_utf8 = PyUnicode_AsUTF8String(database); +            if (!database_utf8) { +                return -1; +            } +        } + +        Py_BEGIN_ALLOW_THREADS +        rc = sqlite3_open(PyString_AsString(database_utf8), &self->db); +        Py_END_ALLOW_THREADS + +        Py_DECREF(database_utf8); + +        if (rc != SQLITE_OK) { +            _pysqlite_seterror(self->db, NULL); +            return -1; +        } +    } else { +        /* Create a pysqlite connection from a APSW connection */ +        class_attr = PyObject_GetAttrString(database, "__class__"); +        if (class_attr) { +            class_attr_str = PyObject_Str(class_attr); +            if (class_attr_str) { +                if (strcmp(PyString_AsString(class_attr_str), "<type 'apsw.Connection'>") == 0) { +                    /* In the APSW Connection object, the first entry after +                     * PyObject_HEAD is the sqlite3* we want to get hold of. +                     * Luckily, this is the same layout as we have in our +                     * pysqlite_Connection */ +                    self->db = ((pysqlite_Connection*)database)->db; + +                    Py_INCREF(database); +                    self->apsw_connection = database; +                    is_apsw_connection = 1; +                } +            } +        } +        Py_XDECREF(class_attr_str); +        Py_XDECREF(class_attr); + +        if (!is_apsw_connection) { +            PyErr_SetString(PyExc_ValueError, "database parameter must be string or APSW Connection object"); +            return -1; +        } +    } + +    if (!isolation_level) { +        isolation_level = PyString_FromString(""); +        if (!isolation_level) { +            return -1; +        } +    } else { +        Py_INCREF(isolation_level); +    } +    self->isolation_level = NULL; +    pysqlite_connection_set_isolation_level(self, isolation_level); +    Py_DECREF(isolation_level); + +    self->statement_cache = (pysqlite_Cache*)PyObject_CallFunction((PyObject*)&pysqlite_CacheType, "Oi", self, cached_statements); +    if (PyErr_Occurred()) { +        return -1; +    } + +    self->created_statements = 0; +    self->created_cursors = 0; + +    /* Create lists of weak references to statements/cursors */ +    self->statements = PyList_New(0); +    self->cursors = PyList_New(0); +    if (!self->statements || !self->cursors) { +        return -1; +    } + +    /* By default, the Cache class INCREFs the factory in its initializer, and +     * decrefs it in its deallocator method. Since this would create a circular +     * reference here, we're breaking it by decrementing self, and telling the +     * cache class to not decref the factory (self) in its deallocator. +     */ +    self->statement_cache->decref_factory = 0; +    Py_DECREF(self); + +    self->inTransaction = 0; +    self->detect_types = detect_types; +    self->timeout = timeout; +    (void)sqlite3_busy_timeout(self->db, (int)(timeout*1000)); +#ifdef WITH_THREAD +    self->thread_ident = PyThread_get_thread_ident(); +#endif +    self->check_same_thread = check_same_thread; + +    self->function_pinboard = PyDict_New(); +    if (!self->function_pinboard) { +        return -1; +    } + +    self->collations = PyDict_New(); +    if (!self->collations) { +        return -1; +    } + +    self->Warning               = pysqlite_Warning; +    self->Error                 = pysqlite_Error; +    self->InterfaceError        = pysqlite_InterfaceError; +    self->DatabaseError         = pysqlite_DatabaseError; +    self->DataError             = pysqlite_DataError; +    self->OperationalError      = pysqlite_OperationalError; +    self->IntegrityError        = pysqlite_IntegrityError; +    self->InternalError         = pysqlite_InternalError; +    self->ProgrammingError      = pysqlite_ProgrammingError; +    self->NotSupportedError     = pysqlite_NotSupportedError; + +    return 0; +} + +/* Empty the entire statement cache of this connection */ +void pysqlite_flush_statement_cache(pysqlite_Connection* self) +{ +    pysqlite_Node* node; +    pysqlite_Statement* statement; + +    node = self->statement_cache->first; + +    while (node) { +        statement = (pysqlite_Statement*)(node->data); +        (void)pysqlite_statement_finalize(statement); +        node = node->next; +    } + +    Py_DECREF(self->statement_cache); +    self->statement_cache = (pysqlite_Cache*)PyObject_CallFunction((PyObject*)&pysqlite_CacheType, "O", self); +    Py_DECREF(self); +    self->statement_cache->decref_factory = 0; +} + +/* action in (ACTION_RESET, ACTION_FINALIZE) */ +void pysqlite_do_all_statements(pysqlite_Connection* self, int action, int reset_cursors) +{ +    int i; +    PyObject* weakref; +    PyObject* statement; +    pysqlite_Cursor* cursor; + +    for (i = 0; i < PyList_Size(self->statements); i++) { +        weakref = PyList_GetItem(self->statements, i); +        statement = PyWeakref_GetObject(weakref); +        if (statement != Py_None) { +            if (action == ACTION_RESET) { +                (void)pysqlite_statement_reset((pysqlite_Statement*)statement); +            } else { +                (void)pysqlite_statement_finalize((pysqlite_Statement*)statement); +            } +        } +    } + +    if (reset_cursors) { +        for (i = 0; i < PyList_Size(self->cursors); i++) { +            weakref = PyList_GetItem(self->cursors, i); +            cursor = (pysqlite_Cursor*)PyWeakref_GetObject(weakref); +            if ((PyObject*)cursor != Py_None) { +                cursor->reset = 1; +            } +        } +    } +} + +void pysqlite_connection_dealloc(pysqlite_Connection* self) +{ +    PyObject* ret = NULL; + +    Py_XDECREF(self->statement_cache); + +    /* Clean up if user has not called .close() explicitly. */ +    if (self->db) { +        Py_BEGIN_ALLOW_THREADS +        sqlite3_close(self->db); +        Py_END_ALLOW_THREADS +    } else if (self->apsw_connection) { +        ret = PyObject_CallMethod(self->apsw_connection, "close", ""); +        Py_XDECREF(ret); +        Py_XDECREF(self->apsw_connection); +    } + +    if (self->begin_statement) { +        PyMem_Free(self->begin_statement); +    } +    Py_XDECREF(self->isolation_level); +    Py_XDECREF(self->function_pinboard); +    Py_XDECREF(self->row_factory); +    Py_XDECREF(self->text_factory); +    Py_XDECREF(self->collations); +    Py_XDECREF(self->statements); +    Py_XDECREF(self->cursors); + +    self->ob_type->tp_free((PyObject*)self); +} + +/* + * Registers a cursor with the connection. + * + * 0 => error; 1 => ok + */ +int pysqlite_connection_register_cursor(pysqlite_Connection* connection, PyObject* cursor) +{ +    PyObject* weakref; + +    weakref = PyWeakref_NewRef((PyObject*)cursor, NULL); +    if (!weakref) { +        goto error; +    } + +    if (PyList_Append(connection->cursors, weakref) != 0) { +        Py_CLEAR(weakref); +        goto error; +    } + +    Py_DECREF(weakref); + +    return 1; +error: +    return 0; +} + +PyObject* pysqlite_connection_cursor(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) +{ +    static char *kwlist[] = {"factory", NULL, NULL}; +    PyObject* factory = NULL; +    PyObject* cursor; + +    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O", kwlist, +                                     &factory)) { +        return NULL; +    } + +    if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { +        return NULL; +    } + +    if (factory == NULL) { +        factory = (PyObject*)&pysqlite_CursorType; +    } + +    cursor = PyObject_CallFunction(factory, "O", self); + +    _pysqlite_drop_unused_cursor_references(self); + +    if (cursor && self->row_factory != Py_None) { +        Py_XDECREF(((pysqlite_Cursor*)cursor)->row_factory); +        Py_INCREF(self->row_factory); +        ((pysqlite_Cursor*)cursor)->row_factory = self->row_factory; +    } + +    return cursor; +} + +#ifdef PYSQLITE_EXPERIMENTAL +PyObject* pysqlite_connection_backup(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) +{ +    static char *kwlist[] = {"dest_db", "source_name", "dest_db_name", NULL, NULL}; +    char* source_name; +    char* dest_name; +    pysqlite_Connection* dest_con; +    pysqlite_Backup* backup; + +    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!ss", kwlist, +                                     &pysqlite_ConnectionType, &dest_con, &source_name, &dest_name)) { +        return NULL; +    } + +    if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { +        return NULL; +    } + +    backup = PyObject_New(pysqlite_Backup, &pysqlite_BackupType); +    if (!backup) { +        return NULL; +    } + +    Py_INCREF(self); +    backup->source_con = self; + +    Py_INCREF(dest_con); +    backup->dest_con = dest_con; + +    backup->backup = sqlite3_backup_init(dest_con->db, dest_name, self->db, source_name); + +    return (PyObject*)backup; +} +#endif + +PyObject* pysqlite_connection_close(pysqlite_Connection* self, PyObject* args) +{ +    PyObject* ret; +    int rc; + +    if (!pysqlite_check_thread(self)) { +        return NULL; +    } + +    pysqlite_do_all_statements(self, ACTION_FINALIZE, 1); + +    if (self->db) { +        if (self->apsw_connection) { +            ret = PyObject_CallMethod(self->apsw_connection, "close", ""); +            Py_XDECREF(ret); +            Py_XDECREF(self->apsw_connection); +            self->apsw_connection = NULL; +            self->db = NULL; +        } else { +            Py_BEGIN_ALLOW_THREADS +            rc = sqlite3_close(self->db); +            Py_END_ALLOW_THREADS + +            if (rc != SQLITE_OK) { +                _pysqlite_seterror(self->db, NULL); +                return NULL; +            } else { +                self->db = NULL; +            } +        } +    } + +    Py_INCREF(Py_None); +    return Py_None; +} + +/* + * Checks if a connection object is usable (i. e. not closed). + * + * 0 => error; 1 => ok + */ +int pysqlite_check_connection(pysqlite_Connection* con) +{ +    if (!con->initialized) { +        PyErr_SetString(pysqlite_ProgrammingError, "Base Connection.__init__ not called."); +        return 0; +    } + +    if (!con->db) { +        PyErr_SetString(pysqlite_ProgrammingError, "Cannot operate on a closed database."); +        return 0; +    } else { +        return 1; +    } +} + +PyObject* _pysqlite_connection_begin(pysqlite_Connection* self) +{ +    int rc; +    const char* tail; +    sqlite3_stmt* statement; + +    Py_BEGIN_ALLOW_THREADS +    rc = sqlite3_prepare(self->db, self->begin_statement, -1, &statement, &tail); +    Py_END_ALLOW_THREADS + +    if (rc != SQLITE_OK) { +        _pysqlite_seterror(self->db, statement); +        goto error; +    } + +    rc = pysqlite_step(statement, self); +    if (rc == SQLITE_DONE) { +        self->inTransaction = 1; +    } else { +        _pysqlite_seterror(self->db, statement); +    } + +    Py_BEGIN_ALLOW_THREADS +    rc = sqlite3_finalize(statement); +    Py_END_ALLOW_THREADS + +    if (rc != SQLITE_OK && !PyErr_Occurred()) { +        _pysqlite_seterror(self->db, NULL); +    } + +error: +    if (PyErr_Occurred()) { +        return NULL; +    } else { +        Py_INCREF(Py_None); +        return Py_None; +    } +} + +PyObject* pysqlite_connection_commit(pysqlite_Connection* self, PyObject* args) +{ +    int rc; +    const char* tail; +    sqlite3_stmt* statement; + +    if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { +        return NULL; +    } + +    if (self->inTransaction) { +        pysqlite_do_all_statements(self, ACTION_RESET, 0); + +        Py_BEGIN_ALLOW_THREADS +        rc = sqlite3_prepare(self->db, "COMMIT", -1, &statement, &tail); +        Py_END_ALLOW_THREADS +        if (rc != SQLITE_OK) { +            _pysqlite_seterror(self->db, NULL); +            goto error; +        } + +        rc = pysqlite_step(statement, self); +        if (rc == SQLITE_DONE) { +            self->inTransaction = 0; +        } else { +            _pysqlite_seterror(self->db, statement); +        } + +        Py_BEGIN_ALLOW_THREADS +        rc = sqlite3_finalize(statement); +        Py_END_ALLOW_THREADS +        if (rc != SQLITE_OK && !PyErr_Occurred()) { +            _pysqlite_seterror(self->db, NULL); +        } + +    } + +error: +    if (PyErr_Occurred()) { +        return NULL; +    } else { +        Py_INCREF(Py_None); +        return Py_None; +    } +} + +PyObject* pysqlite_connection_rollback(pysqlite_Connection* self, PyObject* args) +{ +    int rc; +    const char* tail; +    sqlite3_stmt* statement; + +    if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { +        return NULL; +    } + +    if (self->inTransaction) { +        pysqlite_do_all_statements(self, ACTION_RESET, 1); + +        Py_BEGIN_ALLOW_THREADS +        rc = sqlite3_prepare(self->db, "ROLLBACK", -1, &statement, &tail); +        Py_END_ALLOW_THREADS +        if (rc != SQLITE_OK) { +            _pysqlite_seterror(self->db, NULL); +            goto error; +        } + +        rc = pysqlite_step(statement, self); +        if (rc == SQLITE_DONE) { +            self->inTransaction = 0; +        } else { +            _pysqlite_seterror(self->db, statement); +        } + +        Py_BEGIN_ALLOW_THREADS +        rc = sqlite3_finalize(statement); +        Py_END_ALLOW_THREADS +        if (rc != SQLITE_OK && !PyErr_Occurred()) { +            _pysqlite_seterror(self->db, NULL); +        } + +    } + +error: +    if (PyErr_Occurred()) { +        return NULL; +    } else { +        Py_INCREF(Py_None); +        return Py_None; +    } +} + +void _pysqlite_set_result(sqlite3_context* context, PyObject* py_val) +{ +    long longval; +    const char* buffer; +    Py_ssize_t buflen; +    PyObject* stringval; + +    if ((!py_val) || PyErr_Occurred()) { +        sqlite3_result_null(context); +    } else if (py_val == Py_None) { +        sqlite3_result_null(context); +    } else if (PyInt_Check(py_val)) { +        longval = PyInt_AsLong(py_val); +        sqlite3_result_int64(context, (PY_LONG_LONG)longval); +    } else if (PyFloat_Check(py_val)) { +        sqlite3_result_double(context, PyFloat_AsDouble(py_val)); +    } else if (PyBuffer_Check(py_val)) { +        if (PyObject_AsCharBuffer(py_val, &buffer, &buflen) != 0) { +            PyErr_SetString(PyExc_ValueError, "could not convert BLOB to buffer"); +        } else { +            sqlite3_result_blob(context, buffer, buflen, SQLITE_TRANSIENT); +        } +    } else if (PyString_Check(py_val)) { +        sqlite3_result_text(context, PyString_AsString(py_val), -1, SQLITE_TRANSIENT); +    } else if (PyUnicode_Check(py_val)) { +        stringval = PyUnicode_AsUTF8String(py_val); +        if (stringval) { +            sqlite3_result_text(context, PyString_AsString(stringval), -1, SQLITE_TRANSIENT); +            Py_DECREF(stringval); +        } +    } else { +        /* TODO: raise error */ +    } +} + +PyObject* _pysqlite_build_py_params(sqlite3_context *context, int argc, sqlite3_value** argv) +{ +    PyObject* args; +    int i; +    sqlite3_value* cur_value; +    PyObject* cur_py_value; +    const char* val_str; +    PY_LONG_LONG val_int; +    Py_ssize_t buflen; +    void* raw_buffer; + +    args = PyTuple_New(argc); +    if (!args) { +        return NULL; +    } + +    for (i = 0; i < argc; i++) { +        cur_value = argv[i]; +        switch (sqlite3_value_type(argv[i])) { +            case SQLITE_INTEGER: +                val_int = sqlite3_value_int64(cur_value); +                cur_py_value = PyInt_FromLong((long)val_int); +                break; +            case SQLITE_FLOAT: +                cur_py_value = PyFloat_FromDouble(sqlite3_value_double(cur_value)); +                break; +            case SQLITE_TEXT: +                val_str = (const char*)sqlite3_value_text(cur_value); +                cur_py_value = PyUnicode_DecodeUTF8(val_str, strlen(val_str), NULL); +                /* TODO: have a way to show errors here */ +                if (!cur_py_value) { +                    PyErr_Clear(); +                    Py_INCREF(Py_None); +                    cur_py_value = Py_None; +                } +                break; +            case SQLITE_BLOB: +                buflen = sqlite3_value_bytes(cur_value); +                cur_py_value = PyBuffer_New(buflen); +                if (!cur_py_value) { +                    break; +                } +                if (PyObject_AsWriteBuffer(cur_py_value, &raw_buffer, &buflen)) { +                    Py_DECREF(cur_py_value); +                    cur_py_value = NULL; +                    break; +                } +                memcpy(raw_buffer, sqlite3_value_blob(cur_value), buflen); +                break; +            case SQLITE_NULL: +            default: +                Py_INCREF(Py_None); +                cur_py_value = Py_None; +        } + +        if (!cur_py_value) { +            Py_DECREF(args); +            return NULL; +        } + +        PyTuple_SetItem(args, i, cur_py_value); + +    } + +    return args; +} + +void _pysqlite_func_callback(sqlite3_context* context, int argc, sqlite3_value** argv) +{ +    PyObject* args; +    PyObject* py_func; +    PyObject* py_retval = NULL; + +#ifdef WITH_THREAD +    PyGILState_STATE threadstate; + +    threadstate = PyGILState_Ensure(); +#endif + +    py_func = (PyObject*)sqlite3_user_data(context); + +    args = _pysqlite_build_py_params(context, argc, argv); +    if (args) { +        py_retval = PyObject_CallObject(py_func, args); +        Py_DECREF(args); +    } + +    if (py_retval) { +        _pysqlite_set_result(context, py_retval); +        Py_DECREF(py_retval); +    } else { +        if (_enable_callback_tracebacks) { +            PyErr_Print(); +        } else { +            PyErr_Clear(); +        } +        _sqlite3_result_error(context, "user-defined function raised exception", -1); +    } + +#ifdef WITH_THREAD +    PyGILState_Release(threadstate); +#endif +} + +static void _pysqlite_step_callback(sqlite3_context *context, int argc, sqlite3_value** params) +{ +    PyObject* args; +    PyObject* function_result = NULL; +    PyObject* aggregate_class; +    PyObject** aggregate_instance; +    PyObject* stepmethod = NULL; + +#ifdef WITH_THREAD +    PyGILState_STATE threadstate; + +    threadstate = PyGILState_Ensure(); +#endif + +    aggregate_class = (PyObject*)sqlite3_user_data(context); + +    aggregate_instance = (PyObject**)sqlite3_aggregate_context(context, sizeof(PyObject*)); + +    if (*aggregate_instance == 0) { +        *aggregate_instance = PyObject_CallFunction(aggregate_class, ""); + +        if (PyErr_Occurred()) { +            *aggregate_instance = 0; +            if (_enable_callback_tracebacks) { +                PyErr_Print(); +            } else { +                PyErr_Clear(); +            } +            _sqlite3_result_error(context, "user-defined aggregate's '__init__' method raised error", -1); +            goto error; +        } +    } + +    stepmethod = PyObject_GetAttrString(*aggregate_instance, "step"); +    if (!stepmethod) { +        goto error; +    } + +    args = _pysqlite_build_py_params(context, argc, params); +    if (!args) { +        goto error; +    } + +    function_result = PyObject_CallObject(stepmethod, args); +    Py_DECREF(args); + +    if (!function_result) { +        if (_enable_callback_tracebacks) { +            PyErr_Print(); +        } else { +            PyErr_Clear(); +        } +        _sqlite3_result_error(context, "user-defined aggregate's 'step' method raised error", -1); +    } + +error: +    Py_XDECREF(stepmethod); +    Py_XDECREF(function_result); + +#ifdef WITH_THREAD +    PyGILState_Release(threadstate); +#endif +} + +void _pysqlite_final_callback(sqlite3_context* context) +{ +    PyObject* function_result = NULL; +    PyObject** aggregate_instance; +    PyObject* aggregate_class; + +#ifdef WITH_THREAD +    PyGILState_STATE threadstate; + +    threadstate = PyGILState_Ensure(); +#endif + +    aggregate_class = (PyObject*)sqlite3_user_data(context); + +    aggregate_instance = (PyObject**)sqlite3_aggregate_context(context, sizeof(PyObject*)); +    if (!*aggregate_instance) { +        /* this branch is executed if there was an exception in the aggregate's +         * __init__ */ + +        goto error; +    } + +    function_result = PyObject_CallMethod(*aggregate_instance, "finalize", ""); +    if (!function_result) { +        if (_enable_callback_tracebacks) { +            PyErr_Print(); +        } else { +            PyErr_Clear(); +        } +        _sqlite3_result_error(context, "user-defined aggregate's 'finalize' method raised error", -1); +    } else { +        _pysqlite_set_result(context, function_result); +    } + +error: +    Py_XDECREF(*aggregate_instance); +    Py_XDECREF(function_result); + +#ifdef WITH_THREAD +    PyGILState_Release(threadstate); +#endif +} + +static void _pysqlite_drop_unused_statement_references(pysqlite_Connection* self) +{ +    PyObject* new_list; +    PyObject* weakref; +    int i; + +    /* we only need to do this once in a while */ +    if (self->created_statements++ < 200) { +        return; +    } + +    self->created_statements = 0; + +    new_list = PyList_New(0); +    if (!new_list) { +        return; +    } + +    for (i = 0; i < PyList_Size(self->statements); i++) { +        weakref = PyList_GetItem(self->statements, i); +        if (PyWeakref_GetObject(weakref) != Py_None) { +            if (PyList_Append(new_list, weakref) != 0) { +                Py_DECREF(new_list); +                return; +            } +        } +    } + +    Py_DECREF(self->statements); +    self->statements = new_list; +} + +static void _pysqlite_drop_unused_cursor_references(pysqlite_Connection* self) +{ +    PyObject* new_list; +    PyObject* weakref; +    int i; + +    /* we only need to do this once in a while */ +    if (self->created_cursors++ < 200) { +        return; +    } + +    self->created_cursors = 0; + +    new_list = PyList_New(0); +    if (!new_list) { +        return; +    } + +    for (i = 0; i < PyList_Size(self->cursors); i++) { +        weakref = PyList_GetItem(self->cursors, i); +        if (PyWeakref_GetObject(weakref) != Py_None) { +            if (PyList_Append(new_list, weakref) != 0) { +                Py_DECREF(new_list); +                return; +            } +        } +    } + +    Py_DECREF(self->cursors); +    self->cursors = new_list; +} + +PyObject* pysqlite_connection_create_function(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) +{ +    static char *kwlist[] = {"name", "narg", "func", NULL, NULL}; + +    PyObject* func; +    char* name; +    int narg; +    int rc; + +    if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { +        return NULL; +    } + +    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "siO", kwlist, +                                     &name, &narg, &func)) +    { +        return NULL; +    } + +    rc = sqlite3_create_function(self->db, name, narg, SQLITE_UTF8, (void*)func, _pysqlite_func_callback, NULL, NULL); + +    if (rc != SQLITE_OK) { +        /* Workaround for SQLite bug: no error code or string is available here */ +        PyErr_SetString(pysqlite_OperationalError, "Error creating function"); +        return NULL; +    } else { +        if (PyDict_SetItem(self->function_pinboard, func, Py_None) == -1) +            return NULL; + +        Py_INCREF(Py_None); +        return Py_None; +    } +} + +PyObject* pysqlite_connection_create_aggregate(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) +{ +    PyObject* aggregate_class; + +    int n_arg; +    char* name; +    static char *kwlist[] = { "name", "n_arg", "aggregate_class", NULL }; +    int rc; + +    if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { +        return NULL; +    } + +    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "siO:create_aggregate", +                                      kwlist, &name, &n_arg, &aggregate_class)) { +        return NULL; +    } + +    rc = sqlite3_create_function(self->db, name, n_arg, SQLITE_UTF8, (void*)aggregate_class, 0, &_pysqlite_step_callback, &_pysqlite_final_callback); +    if (rc != SQLITE_OK) { +        /* Workaround for SQLite bug: no error code or string is available here */ +        PyErr_SetString(pysqlite_OperationalError, "Error creating aggregate"); +        return NULL; +    } else { +        if (PyDict_SetItem(self->function_pinboard, aggregate_class, Py_None) == -1) +            return NULL; + +        Py_INCREF(Py_None); +        return Py_None; +    } +} + +static int _authorizer_callback(void* user_arg, int action, const char* arg1, const char* arg2 , const char* dbname, const char* access_attempt_source) +{ +    PyObject *ret; +    int rc; +#ifdef WITH_THREAD +    PyGILState_STATE gilstate; + +    gilstate = PyGILState_Ensure(); +#endif +    ret = PyObject_CallFunction((PyObject*)user_arg, "issss", action, arg1, arg2, dbname, access_attempt_source); + +    if (!ret) { +        if (_enable_callback_tracebacks) { +            PyErr_Print(); +        } else { +            PyErr_Clear(); +        } + +        rc = SQLITE_DENY; +    } else { +        if (PyInt_Check(ret)) { +            rc = (int)PyInt_AsLong(ret); +        } else { +            rc = SQLITE_DENY; +        } +        Py_DECREF(ret); +    } + +#ifdef WITH_THREAD +    PyGILState_Release(gilstate); +#endif +    return rc; +} + +static int _progress_handler(void* user_arg) +{ +    int rc; +    PyObject *ret; +#ifdef WITH_THREAD +    PyGILState_STATE gilstate; + +    gilstate = PyGILState_Ensure(); +#endif +    ret = PyObject_CallFunction((PyObject*)user_arg, ""); + +    if (!ret) { +        if (_enable_callback_tracebacks) { +            PyErr_Print(); +        } else { +            PyErr_Clear(); +        } + +        /* abort query if error occurred */ +        rc = 1;  +    } else { +        rc = (int)PyObject_IsTrue(ret); +        Py_DECREF(ret); +    } + +#ifdef WITH_THREAD +    PyGILState_Release(gilstate); +#endif +    return rc; +} + +static PyObject* pysqlite_connection_set_authorizer(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) +{ +    PyObject* authorizer_cb; + +    static char *kwlist[] = { "authorizer_callback", NULL }; +    int rc; + +    if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { +        return NULL; +    } + +    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:set_authorizer", +                                      kwlist, &authorizer_cb)) { +        return NULL; +    } + +    rc = sqlite3_set_authorizer(self->db, _authorizer_callback, (void*)authorizer_cb); + +    if (rc != SQLITE_OK) { +        PyErr_SetString(pysqlite_OperationalError, "Error setting authorizer callback"); +        return NULL; +    } else { +        if (PyDict_SetItem(self->function_pinboard, authorizer_cb, Py_None) == -1) +            return NULL; + +        Py_INCREF(Py_None); +        return Py_None; +    } +} + +static PyObject* pysqlite_connection_set_progress_handler(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) +{ +    PyObject* progress_handler; +    int n; + +    static char *kwlist[] = { "progress_handler", "n", NULL }; + +    if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { +        return NULL; +    } + +    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Oi:set_progress_handler", +                                      kwlist, &progress_handler, &n)) { +        return NULL; +    } + +    if (progress_handler == Py_None) { +        /* None clears the progress handler previously set */ +        sqlite3_progress_handler(self->db, 0, 0, (void*)0); +    } else { +        sqlite3_progress_handler(self->db, n, _progress_handler, progress_handler); +        if (PyDict_SetItem(self->function_pinboard, progress_handler, Py_None) == -1) +            return NULL; +    } + +    Py_INCREF(Py_None); +    return Py_None; +} + +#ifdef HAVE_LOAD_EXTENSION +static PyObject* pysqlite_enable_load_extension(pysqlite_Connection* self, PyObject* args) +{ +    int rc; +    int onoff; + +    if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { +        return NULL; +    } + +    if (!PyArg_ParseTuple(args, "i", &onoff)) { +        return NULL; +    } + +    rc = sqlite3_enable_load_extension(self->db, onoff); + +    if (rc != SQLITE_OK) { +        PyErr_SetString(pysqlite_OperationalError, "Error enabling load extension"); +        return NULL; +    } else { +        Py_INCREF(Py_None); +        return Py_None; +    } +} + +static PyObject* pysqlite_load_extension(pysqlite_Connection* self, PyObject* args) +{ +    int rc; +    char* extension_name; +    char* errmsg; + +    if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { +        return NULL; +    } + +    if (!PyArg_ParseTuple(args, "s", &extension_name)) { +        return NULL; +    } + +    rc = sqlite3_load_extension(self->db, extension_name, 0, &errmsg); +    if (rc != 0) { +        PyErr_SetString(pysqlite_OperationalError, errmsg); +        return NULL; +    } else { +        Py_INCREF(Py_None); +        return Py_None; +    } +} +#endif + +int pysqlite_check_thread(pysqlite_Connection* self) +{ +#ifdef WITH_THREAD +    if (self->check_same_thread) { +        if (PyThread_get_thread_ident() != self->thread_ident) { +            PyErr_Format(pysqlite_ProgrammingError, +                        "SQLite objects created in a thread can only be used in that same thread." +                        "The object was created in thread id %ld and this is thread id %ld", +                        self->thread_ident, PyThread_get_thread_ident()); +            return 0; +        } + +    } +#endif +    return 1; +} + +static PyObject* pysqlite_connection_get_isolation_level(pysqlite_Connection* self, void* unused) +{ +    Py_INCREF(self->isolation_level); +    return self->isolation_level; +} + +static PyObject* pysqlite_connection_get_total_changes(pysqlite_Connection* self, void* unused) +{ +    if (!pysqlite_check_connection(self)) { +        return NULL; +    } else { +        return Py_BuildValue("i", sqlite3_total_changes(self->db)); +    } +} + +static int pysqlite_connection_set_isolation_level(pysqlite_Connection* self, PyObject* isolation_level) +{ +    PyObject* res; +    PyObject* begin_statement; +    char* begin_statement_str; + +    Py_XDECREF(self->isolation_level); + +    if (self->begin_statement) { +        PyMem_Free(self->begin_statement); +        self->begin_statement = NULL; +    } + +    if (isolation_level == Py_None) { +        Py_INCREF(Py_None); +        self->isolation_level = Py_None; + +        res = pysqlite_connection_commit(self, NULL); +        if (!res) { +            return -1; +        } +        Py_DECREF(res); + +        self->inTransaction = 0; +    } else { +        Py_INCREF(isolation_level); +        self->isolation_level = isolation_level; + +        begin_statement = PyString_FromString("BEGIN "); +        if (!begin_statement) { +            return -1; +        } +        PyString_Concat(&begin_statement, isolation_level); +        if (!begin_statement) { +            return -1; +        } + +        begin_statement_str = PyString_AsString(begin_statement); +        if (!begin_statement_str) { +            Py_DECREF(begin_statement); +            return -1; +        } +        self->begin_statement = PyMem_Malloc(strlen(begin_statement_str) + 2); +        if (!self->begin_statement) { +            Py_DECREF(begin_statement); +            return -1; +        } + +        strcpy(self->begin_statement, begin_statement_str); +        Py_DECREF(begin_statement); +    } + +    return 0; +} + +PyObject* pysqlite_connection_call(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) +{ +    PyObject* sql; +    pysqlite_Statement* statement; +    PyObject* weakref; +    int rc; + +    if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { +        return NULL; +    } + +    if (!PyArg_ParseTuple(args, "O", &sql)) { +        return NULL; +    } + +    _pysqlite_drop_unused_statement_references(self); + +    statement = PyObject_New(pysqlite_Statement, &pysqlite_StatementType); +    if (!statement) { +        return NULL; +    } + +    rc = pysqlite_statement_create(statement, self, sql); + +    if (rc != SQLITE_OK) { +        if (rc == PYSQLITE_TOO_MUCH_SQL) { +            PyErr_SetString(pysqlite_ProgrammingError, "You can only execute one statement at a time."); +        } else if (rc == PYSQLITE_SQL_WRONG_TYPE) { +            PyErr_SetString(pysqlite_ProgrammingError, "SQL is of wrong type. Must be string or unicode."); +        } else { +            (void)pysqlite_statement_reset(statement); +            _pysqlite_seterror(self->db, NULL); +        } + +        Py_CLEAR(statement); +    } else { +        weakref = PyWeakref_NewRef((PyObject*)statement, NULL); +        if (!weakref) { +            Py_CLEAR(statement); +            goto error; +        } + +        if (PyList_Append(self->statements, weakref) != 0) { +            Py_CLEAR(weakref); +            goto error; +        } + +        Py_DECREF(weakref); +    } + +error: +    return (PyObject*)statement; +} + +PyObject* pysqlite_connection_execute(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) +{ +    PyObject* cursor = 0; +    PyObject* result = 0; +    PyObject* method = 0; + +    cursor = PyObject_CallMethod((PyObject*)self, "cursor", ""); +    if (!cursor) { +        goto error; +    } + +    method = PyObject_GetAttrString(cursor, "execute"); +    if (!method) { +        Py_CLEAR(cursor); +        goto error; +    } + +    result = PyObject_CallObject(method, args); +    if (!result) { +        Py_CLEAR(cursor); +    } + +error: +    Py_XDECREF(result); +    Py_XDECREF(method); + +    return cursor; +} + +PyObject* pysqlite_connection_executemany(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) +{ +    PyObject* cursor = 0; +    PyObject* result = 0; +    PyObject* method = 0; + +    cursor = PyObject_CallMethod((PyObject*)self, "cursor", ""); +    if (!cursor) { +        goto error; +    } + +    method = PyObject_GetAttrString(cursor, "executemany"); +    if (!method) { +        Py_CLEAR(cursor); +        goto error; +    } + +    result = PyObject_CallObject(method, args); +    if (!result) { +        Py_CLEAR(cursor); +    } + +error: +    Py_XDECREF(result); +    Py_XDECREF(method); + +    return cursor; +} + +PyObject* pysqlite_connection_executescript(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) +{ +    PyObject* cursor = 0; +    PyObject* result = 0; +    PyObject* method = 0; + +    cursor = PyObject_CallMethod((PyObject*)self, "cursor", ""); +    if (!cursor) { +        goto error; +    } + +    method = PyObject_GetAttrString(cursor, "executescript"); +    if (!method) { +        Py_CLEAR(cursor); +        goto error; +    } + +    result = PyObject_CallObject(method, args); +    if (!result) { +        Py_CLEAR(cursor); +    } + +error: +    Py_XDECREF(result); +    Py_XDECREF(method); + +    return cursor; +} + +/* ------------------------- COLLATION CODE ------------------------ */ + +static int +pysqlite_collation_callback( +        void* context, +        int text1_length, const void* text1_data, +        int text2_length, const void* text2_data) +{ +    PyObject* callback = (PyObject*)context; +    PyObject* string1 = 0; +    PyObject* string2 = 0; +#ifdef WITH_THREAD +    PyGILState_STATE gilstate; +#endif +    PyObject* retval = NULL; +    int result = 0; +#ifdef WITH_THREAD +    gilstate = PyGILState_Ensure(); +#endif + +    if (PyErr_Occurred()) { +        goto finally; +    } + +    string1 = PyString_FromStringAndSize((const char*)text1_data, text1_length); +    string2 = PyString_FromStringAndSize((const char*)text2_data, text2_length); + +    if (!string1 || !string2) { +        goto finally; /* failed to allocate strings */ +    } + +    retval = PyObject_CallFunctionObjArgs(callback, string1, string2, NULL); + +    if (!retval) { +        /* execution failed */ +        goto finally; +    } + +    result = PyInt_AsLong(retval); +    if (PyErr_Occurred()) { +        result = 0; +    } + +finally: +    Py_XDECREF(string1); +    Py_XDECREF(string2); +    Py_XDECREF(retval); +#ifdef WITH_THREAD +    PyGILState_Release(gilstate); +#endif +    return result; +} + +static PyObject * +pysqlite_connection_interrupt(pysqlite_Connection* self, PyObject* args) +{ +    PyObject* retval = NULL; + +    if (!pysqlite_check_connection(self)) { +        goto finally; +    } + +    sqlite3_interrupt(self->db); + +    Py_INCREF(Py_None); +    retval = Py_None; + +finally: +    return retval; +} + +/* Function author: Paul Kippes <kippesp@gmail.com> + * Class method of Connection to call the Python function _iterdump + * of the sqlite3 module. + */ +static PyObject * +pysqlite_connection_iterdump(pysqlite_Connection* self, PyObject* args) +{ +    PyObject* retval = NULL; +    PyObject* module = NULL; +    PyObject* module_dict; +    PyObject* pyfn_iterdump; + +    if (!pysqlite_check_connection(self)) { +        goto finally; +    } + +    module = PyImport_ImportModule("pysqlite2.dump"); +    if (!module) { +        goto finally; +    } + +    module_dict = PyModule_GetDict(module); +    if (!module_dict) { +        goto finally; +    } + +    pyfn_iterdump = PyDict_GetItemString(module_dict, "_iterdump"); +    if (!pyfn_iterdump) { +        PyErr_SetString(pysqlite_OperationalError, "Failed to obtain _iterdump() reference"); +        goto finally; +    } + +    args = PyTuple_New(1); +    if (!args) { +        goto finally; +    } +    Py_INCREF(self); +    PyTuple_SetItem(args, 0, (PyObject*)self); +    retval = PyObject_CallObject(pyfn_iterdump, args); + +finally: +    Py_XDECREF(args); +    Py_XDECREF(module); +    return retval; +} + +static PyObject * +pysqlite_connection_create_collation(pysqlite_Connection* self, PyObject* args) +{ +    PyObject* callable; +    PyObject* uppercase_name = 0; +    PyObject* name; +    PyObject* retval; +    char* chk; +    int rc; + +    if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { +        goto finally; +    } + +    if (!PyArg_ParseTuple(args, "O!O:create_collation(name, callback)", &PyString_Type, &name, &callable)) { +        goto finally; +    } + +    uppercase_name = PyObject_CallMethod(name, "upper", ""); +    if (!uppercase_name) { +        goto finally; +    } + +    chk = PyString_AsString(uppercase_name); +    while (*chk) { +        if ((*chk >= '0' && *chk <= '9') +         || (*chk >= 'A' && *chk <= 'Z') +         || (*chk == '_')) +        { +            chk++; +        } else { +            PyErr_SetString(pysqlite_ProgrammingError, "invalid character in collation name"); +            goto finally; +        } +    } + +    if (callable != Py_None && !PyCallable_Check(callable)) { +        PyErr_SetString(PyExc_TypeError, "parameter must be callable"); +        goto finally; +    } + +    if (callable != Py_None) { +        if (PyDict_SetItem(self->collations, uppercase_name, callable) == -1) +            goto finally; +    } else { +        if (PyDict_DelItem(self->collations, uppercase_name) == -1) +            goto finally; +    } + +    rc = sqlite3_create_collation(self->db, +                                  PyString_AsString(uppercase_name), +                                  SQLITE_UTF8, +                                  (callable != Py_None) ? callable : NULL, +                                  (callable != Py_None) ? pysqlite_collation_callback : NULL); +    if (rc != SQLITE_OK) { +        PyDict_DelItem(self->collations, uppercase_name); +        _pysqlite_seterror(self->db, NULL); +        goto finally; +    } + +finally: +    Py_XDECREF(uppercase_name); + +    if (PyErr_Occurred()) { +        retval = NULL; +    } else { +        Py_INCREF(Py_None); +        retval = Py_None; +    } + +    return retval; +} + +/* Called when the connection is used as a context manager. Returns itself as a + * convenience to the caller. */ +static PyObject * +pysqlite_connection_enter(pysqlite_Connection* self, PyObject* args) +{ +    Py_INCREF(self); +    return (PyObject*)self; +} + +/** Called when the connection is used as a context manager. If there was any + * exception, a rollback takes place; otherwise we commit. */ +static PyObject * +pysqlite_connection_exit(pysqlite_Connection* self, PyObject* args) +{ +    PyObject* exc_type, *exc_value, *exc_tb; +    char* method_name; +    PyObject* result; + +    if (!PyArg_ParseTuple(args, "OOO", &exc_type, &exc_value, &exc_tb)) { +        return NULL; +    } + +    if (exc_type == Py_None && exc_value == Py_None && exc_tb == Py_None) { +        method_name = "commit"; +    } else { +        method_name = "rollback"; +    } + +    result = PyObject_CallMethod((PyObject*)self, method_name, ""); +    if (!result) { +        return NULL; +    } +    Py_DECREF(result); + +    Py_INCREF(Py_False); +    return Py_False; +} + +static char connection_doc[] = +PyDoc_STR("SQLite database connection object."); + +static PyGetSetDef connection_getset[] = { +    {"isolation_level",  (getter)pysqlite_connection_get_isolation_level, (setter)pysqlite_connection_set_isolation_level}, +    {"total_changes",  (getter)pysqlite_connection_get_total_changes, (setter)0}, +    {NULL} +}; + +static PyMethodDef connection_methods[] = { +    #ifdef PYSQLITE_EXPERIMENTAL +    {"backup", (PyCFunction)pysqlite_connection_backup, METH_VARARGS|METH_KEYWORDS, +        PyDoc_STR("Backup database.")}, +    #endif +    {"cursor", (PyCFunction)pysqlite_connection_cursor, METH_VARARGS|METH_KEYWORDS, +        PyDoc_STR("Return a cursor for the connection.")}, +    {"close", (PyCFunction)pysqlite_connection_close, METH_NOARGS, +        PyDoc_STR("Closes the connection.")}, +    {"commit", (PyCFunction)pysqlite_connection_commit, METH_NOARGS, +        PyDoc_STR("Commit the current transaction.")}, +    {"rollback", (PyCFunction)pysqlite_connection_rollback, METH_NOARGS, +        PyDoc_STR("Roll back the current transaction.")}, +    {"create_function", (PyCFunction)pysqlite_connection_create_function, METH_VARARGS|METH_KEYWORDS, +        PyDoc_STR("Creates a new function. Non-standard.")}, +    {"create_aggregate", (PyCFunction)pysqlite_connection_create_aggregate, METH_VARARGS|METH_KEYWORDS, +        PyDoc_STR("Creates a new aggregate. Non-standard.")}, +    {"set_authorizer", (PyCFunction)pysqlite_connection_set_authorizer, METH_VARARGS|METH_KEYWORDS, +        PyDoc_STR("Sets authorizer callback. Non-standard.")}, +    #ifdef HAVE_LOAD_EXTENSION +    {"enable_load_extension", (PyCFunction)pysqlite_enable_load_extension, METH_VARARGS, +        PyDoc_STR("Enable dynamic loading of SQLite extension modules. Non-standard.")}, +    {"load_extension", (PyCFunction)pysqlite_load_extension, METH_VARARGS, +        PyDoc_STR("Load SQLite extension module. Non-standard.")}, +    #endif +    {"set_progress_handler", (PyCFunction)pysqlite_connection_set_progress_handler, METH_VARARGS|METH_KEYWORDS, +        PyDoc_STR("Sets progress handler callback. Non-standard.")}, +    {"execute", (PyCFunction)pysqlite_connection_execute, METH_VARARGS, +        PyDoc_STR("Executes a SQL statement. Non-standard.")}, +    {"executemany", (PyCFunction)pysqlite_connection_executemany, METH_VARARGS, +        PyDoc_STR("Repeatedly executes a SQL statement. Non-standard.")}, +    {"executescript", (PyCFunction)pysqlite_connection_executescript, METH_VARARGS, +        PyDoc_STR("Executes a multiple SQL statements at once. Non-standard.")}, +    {"create_collation", (PyCFunction)pysqlite_connection_create_collation, METH_VARARGS, +        PyDoc_STR("Creates a collation function. Non-standard.")}, +    {"interrupt", (PyCFunction)pysqlite_connection_interrupt, METH_NOARGS, +        PyDoc_STR("Abort any pending database operation. Non-standard.")}, +    {"iterdump", (PyCFunction)pysqlite_connection_iterdump, METH_NOARGS, +        PyDoc_STR("Returns iterator to the dump of the database in an SQL text format. Non-standard.")}, +    {"__enter__", (PyCFunction)pysqlite_connection_enter, METH_NOARGS, +        PyDoc_STR("For context manager. Non-standard.")}, +    {"__exit__", (PyCFunction)pysqlite_connection_exit, METH_VARARGS, +        PyDoc_STR("For context manager. Non-standard.")}, +    {NULL, NULL} +}; + +static struct PyMemberDef connection_members[] = +{ +    {"Warning", T_OBJECT, offsetof(pysqlite_Connection, Warning), RO}, +    {"Error", T_OBJECT, offsetof(pysqlite_Connection, Error), RO}, +    {"InterfaceError", T_OBJECT, offsetof(pysqlite_Connection, InterfaceError), RO}, +    {"DatabaseError", T_OBJECT, offsetof(pysqlite_Connection, DatabaseError), RO}, +    {"DataError", T_OBJECT, offsetof(pysqlite_Connection, DataError), RO}, +    {"OperationalError", T_OBJECT, offsetof(pysqlite_Connection, OperationalError), RO}, +    {"IntegrityError", T_OBJECT, offsetof(pysqlite_Connection, IntegrityError), RO}, +    {"InternalError", T_OBJECT, offsetof(pysqlite_Connection, InternalError), RO}, +    {"ProgrammingError", T_OBJECT, offsetof(pysqlite_Connection, ProgrammingError), RO}, +    {"NotSupportedError", T_OBJECT, offsetof(pysqlite_Connection, NotSupportedError), RO}, +    {"row_factory", T_OBJECT, offsetof(pysqlite_Connection, row_factory)}, +    {"text_factory", T_OBJECT, offsetof(pysqlite_Connection, text_factory)}, +    {NULL} +}; + +PyTypeObject pysqlite_ConnectionType = { +        PyVarObject_HEAD_INIT(NULL, 0) +        MODULE_NAME ".Connection",                      /* tp_name */ +        sizeof(pysqlite_Connection),                    /* tp_basicsize */ +        0,                                              /* tp_itemsize */ +        (destructor)pysqlite_connection_dealloc,        /* tp_dealloc */ +        0,                                              /* tp_print */ +        0,                                              /* tp_getattr */ +        0,                                              /* tp_setattr */ +        0,                                              /* tp_compare */ +        0,                                              /* tp_repr */ +        0,                                              /* tp_as_number */ +        0,                                              /* tp_as_sequence */ +        0,                                              /* tp_as_mapping */ +        0,                                              /* tp_hash */ +        (ternaryfunc)pysqlite_connection_call,          /* tp_call */ +        0,                                              /* tp_str */ +        0,                                              /* tp_getattro */ +        0,                                              /* tp_setattro */ +        0,                                              /* tp_as_buffer */ +        Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,         /* tp_flags */ +        connection_doc,                                 /* tp_doc */ +        0,                                              /* tp_traverse */ +        0,                                              /* tp_clear */ +        0,                                              /* tp_richcompare */ +        0,                                              /* tp_weaklistoffset */ +        0,                                              /* tp_iter */ +        0,                                              /* tp_iternext */ +        connection_methods,                             /* tp_methods */ +        connection_members,                             /* tp_members */ +        connection_getset,                              /* tp_getset */ +        0,                                              /* tp_base */ +        0,                                              /* tp_dict */ +        0,                                              /* tp_descr_get */ +        0,                                              /* tp_descr_set */ +        0,                                              /* tp_dictoffset */ +        (initproc)pysqlite_connection_init,             /* tp_init */ +        0,                                              /* tp_alloc */ +        0,                                              /* tp_new */ +        0                                               /* tp_free */ +}; + +extern int pysqlite_connection_setup_types(void) +{ +    pysqlite_ConnectionType.tp_new = PyType_GenericNew; +    return PyType_Ready(&pysqlite_ConnectionType); +} diff --git a/src/connection.h b/src/connection.h new file mode 100644 index 0000000..3fc5a70 --- /dev/null +++ b/src/connection.h @@ -0,0 +1,138 @@ +/* connection.h - definitions for the connection type + * + * Copyright (C) 2004-2010 Gerhard Häring <gh@ghaering.de> + * + * This file is part of pysqlite. + * + * This software is provided 'as-is', without any express or implied + * warranty.  In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + *    claim that you wrote the original software. If you use this software + *    in a product, an acknowledgment in the product documentation would be + *    appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + *    misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#ifndef PYSQLITE_CONNECTION_H +#define PYSQLITE_CONNECTION_H +#include "Python.h" +#include "pythread.h" +#include "structmember.h" + +#include "cache.h" +#include "module.h" + +#include "sqlite3.h" + +typedef struct +{ +    PyObject_HEAD +    sqlite3* db; + +    /* 1 if we are currently within a transaction, i. e. if a BEGIN has been +     * issued */ +    int inTransaction; + +    /* the type detection mode. Only 0, PARSE_DECLTYPES, PARSE_COLNAMES or a +     * bitwise combination thereof makes sense */ +    int detect_types; + +    /* the timeout value in seconds for database locks */ +    double timeout; + +    /* for internal use in the timeout handler: when did the timeout handler +     * first get called with count=0? */ +    double timeout_started; + +    /* None for autocommit, otherwise a PyString with the isolation level */ +    PyObject* isolation_level; + +    /* NULL for autocommit, otherwise a string with the BEGIN statment; will be +     * freed in connection destructor */ +    char* begin_statement; + +    /* 1 if a check should be performed for each API call if the connection is +     * used from the same thread it was created in */ +    int check_same_thread; + +    int initialized; + +    /* thread identification of the thread the connection was created in */ +    long thread_ident; + +    pysqlite_Cache* statement_cache; + +    /* Lists of weak references to statements and cursors used within this connection */ +    PyObject* statements; +    PyObject* cursors; + +    /* Counters for how many statements/cursors were created in the connection. May be +     * reset to 0 at certain intervals */ +    int created_statements; +    int created_cursors; + +    PyObject* row_factory; + +    /* Determines how bytestrings from SQLite are converted to Python objects: +     * - PyUnicode_Type:        Python Unicode objects are constructed from UTF-8 bytestrings +     * - OptimizedUnicode:      Like before, but for ASCII data, only PyStrings are created. +     * - PyString_Type:         PyStrings are created as-is. +     * - Any custom callable:   Any object returned from the callable called with the bytestring +     *                          as single parameter. +     */ +    PyObject* text_factory; + +    /* remember references to functions/classes used in +     * create_function/create/aggregate, use these as dictionary keys, so we +     * can keep the total system refcount constant by clearing that dictionary +     * in connection_dealloc */ +    PyObject* function_pinboard; + +    /* a dictionary of registered collation name => collation callable mappings */ +    PyObject* collations; + +    /* if our connection was created from a APSW connection, we keep a +     * reference to the APSW connection around and get rid of it in our +     * destructor */ +    PyObject* apsw_connection; + +    /* Exception objects */ +    PyObject* Warning; +    PyObject* Error; +    PyObject* InterfaceError; +    PyObject* DatabaseError; +    PyObject* DataError; +    PyObject* OperationalError; +    PyObject* IntegrityError; +    PyObject* InternalError; +    PyObject* ProgrammingError; +    PyObject* NotSupportedError; +} pysqlite_Connection; + +extern PyTypeObject pysqlite_ConnectionType; + +PyObject* pysqlite_connection_alloc(PyTypeObject* type, int aware); +void pysqlite_connection_dealloc(pysqlite_Connection* self); +PyObject* pysqlite_connection_cursor(pysqlite_Connection* self, PyObject* args, PyObject* kwargs); +PyObject* pysqlite_connection_close(pysqlite_Connection* self, PyObject* args); +PyObject* _pysqlite_connection_begin(pysqlite_Connection* self); +PyObject* pysqlite_connection_commit(pysqlite_Connection* self, PyObject* args); +PyObject* pysqlite_connection_rollback(pysqlite_Connection* self, PyObject* args); +PyObject* pysqlite_connection_new(PyTypeObject* type, PyObject* args, PyObject* kw); +int pysqlite_connection_init(pysqlite_Connection* self, PyObject* args, PyObject* kwargs); + +int pysqlite_connection_register_cursor(pysqlite_Connection* connection, PyObject* cursor); +int pysqlite_check_thread(pysqlite_Connection* self); +int pysqlite_check_connection(pysqlite_Connection* con); + +int pysqlite_connection_setup_types(void); + +#endif diff --git a/src/cursor.c b/src/cursor.c new file mode 100644 index 0000000..26e3307 --- /dev/null +++ b/src/cursor.c @@ -0,0 +1,1126 @@ +/* cursor.c - the cursor type + * + * Copyright (C) 2004-2010 Gerhard Häring <gh@ghaering.de> + * + * This file is part of pysqlite. + * + * This software is provided 'as-is', without any express or implied + * warranty.  In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + *    claim that you wrote the original software. If you use this software + *    in a product, an acknowledgment in the product documentation would be + *    appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + *    misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include "cursor.h" +#include "module.h" +#include "util.h" +#include "sqlitecompat.h" + +/* used to decide wether to call PyInt_FromLong or PyLong_FromLongLong */ +#ifndef INT32_MIN +#define INT32_MIN (-2147483647 - 1) +#endif +#ifndef INT32_MAX +#define INT32_MAX 2147483647 +#endif + +PyObject* pysqlite_cursor_iternext(pysqlite_Cursor* self); + +static char* errmsg_fetch_across_rollback = "Cursor needed to be reset because of commit/rollback and can no longer be fetched from."; + +static pysqlite_StatementKind detect_statement_type(char* statement) +{ +    char buf[20]; +    char* src; +    char* dst; + +    src = statement; +    /* skip over whitepace */ +    while (*src == '\r' || *src == '\n' || *src == ' ' || *src == '\t') { +        src++; +    } + +    if (*src == 0) +        return STATEMENT_INVALID; + +    dst = buf; +    *dst = 0; +    while (isalpha(*src) && dst - buf < sizeof(buf) - 2) { +        *dst++ = tolower(*src++); +    } + +    *dst = 0; + +    if (!strcmp(buf, "select")) { +        return STATEMENT_SELECT; +    } else if (!strcmp(buf, "insert")) { +        return STATEMENT_INSERT; +    } else if (!strcmp(buf, "update")) { +        return STATEMENT_UPDATE; +    } else if (!strcmp(buf, "delete")) { +        return STATEMENT_DELETE; +    } else if (!strcmp(buf, "replace")) { +        return STATEMENT_REPLACE; +    } else { +        return STATEMENT_OTHER; +    } +} + +static int pysqlite_cursor_init(pysqlite_Cursor* self, PyObject* args, PyObject* kwargs) +{ +    pysqlite_Connection* connection; + +    if (!PyArg_ParseTuple(args, "O!", &pysqlite_ConnectionType, &connection)) +    { +        return -1; +    } + +    Py_INCREF(connection); +    self->connection = connection; +    self->statement = NULL; +    self->next_row = NULL; +    self->in_weakreflist = NULL; + +    self->row_cast_map = PyList_New(0); +    if (!self->row_cast_map) { +        return -1; +    } + +    Py_INCREF(Py_None); +    self->description = Py_None; + +    Py_INCREF(Py_None); +    self->lastrowid= Py_None; + +    self->arraysize = 1; +    self->closed = 0; +    self->reset = 0; + +    self->rowcount = -1L; + +    Py_INCREF(Py_None); +    self->row_factory = Py_None; + +    if (!pysqlite_check_thread(self->connection)) { +        return -1; +    } + +    if (!pysqlite_connection_register_cursor(connection, (PyObject*)self)) { +        return -1; +    } + +    self->initialized = 1; + +    return 0; +} + +static void pysqlite_cursor_dealloc(pysqlite_Cursor* self) +{ +    int rc; + +    /* Reset the statement if the user has not closed the cursor */ +    if (self->statement) { +        rc = pysqlite_statement_reset(self->statement); +        Py_DECREF(self->statement); +    } + +    Py_XDECREF(self->connection); +    Py_XDECREF(self->row_cast_map); +    Py_XDECREF(self->description); +    Py_XDECREF(self->lastrowid); +    Py_XDECREF(self->row_factory); +    Py_XDECREF(self->next_row); + +    if (self->in_weakreflist != NULL) { +        PyObject_ClearWeakRefs((PyObject*)self); +    } + +    self->ob_type->tp_free((PyObject*)self); +} + +PyObject* _pysqlite_get_converter(PyObject* key) +{ +    PyObject* upcase_key; +    PyObject* retval; + +    upcase_key = PyObject_CallMethod(key, "upper", ""); +    if (!upcase_key) { +        return NULL; +    } + +    retval = PyDict_GetItem(converters, upcase_key); +    Py_DECREF(upcase_key); + +    return retval; +} + +int pysqlite_build_row_cast_map(pysqlite_Cursor* self) +{ +    int i; +    const char* type_start = (const char*)-1; +    const char* pos; + +    const char* colname; +    const char* decltype; +    PyObject* py_decltype; +    PyObject* converter; +    PyObject* key; + +    if (!self->connection->detect_types) { +        return 0; +    } + +    Py_XDECREF(self->row_cast_map); +    self->row_cast_map = PyList_New(0); + +    for (i = 0; i < sqlite3_column_count(self->statement->st); i++) { +        converter = NULL; + +        if (self->connection->detect_types & PARSE_COLNAMES) { +            colname = sqlite3_column_name(self->statement->st, i); +            if (colname) { +                for (pos = colname; *pos != 0; pos++) { +                    if (*pos == '[') { +                        type_start = pos + 1; +                    } else if (*pos == ']' && type_start != (const char*)-1) { +                        key = PyString_FromStringAndSize(type_start, pos - type_start); +                        if (!key) { +                            /* creating a string failed, but it is too complicated +                             * to propagate the error here, we just assume there is +                             * no converter and proceed */ +                            break; +                        } + +                        converter = _pysqlite_get_converter(key); +                        Py_DECREF(key); +                        break; +                    } +                } +            } +        } + +        if (!converter && self->connection->detect_types & PARSE_DECLTYPES) { +            decltype = sqlite3_column_decltype(self->statement->st, i); +            if (decltype) { +                for (pos = decltype;;pos++) { +                    /* Converter names are split at '(' and blanks. +                     * This allows 'INTEGER NOT NULL' to be treated as 'INTEGER' and +                     * 'NUMBER(10)' to be treated as 'NUMBER', for example. +                     * In other words, it will work as people expect it to work.*/ +                    if (*pos == ' ' || *pos == '(' || *pos == 0) { +                        py_decltype = PyString_FromStringAndSize(decltype, pos - decltype); +                        if (!py_decltype) { +                            return -1; +                        } +                        break; +                    } +                } + +                converter = _pysqlite_get_converter(py_decltype); +                Py_DECREF(py_decltype); +            } +        } + +        if (!converter) { +            converter = Py_None; +        } + +        if (PyList_Append(self->row_cast_map, converter) != 0) { +            if (converter != Py_None) { +                Py_DECREF(converter); +            } +            Py_XDECREF(self->row_cast_map); +            self->row_cast_map = NULL; + +            return -1; +        } +    } + +    return 0; +} + +PyObject* _pysqlite_build_column_name(const char* colname) +{ +    const char* pos; + +    if (!colname) { +        Py_INCREF(Py_None); +        return Py_None; +    } + +    for (pos = colname;; pos++) { +        if (*pos == 0 || *pos == '[') { +            if ((*pos == '[') && (pos > colname) && (*(pos-1) == ' ')) { +                pos--; +            } +            return PyString_FromStringAndSize(colname, pos - colname); +        } +    } +} + +PyObject* pysqlite_unicode_from_string(const char* val_str, int optimize) +{ +    const char* check; +    int is_ascii = 0; + +    if (optimize) { +        is_ascii = 1; + +        check = val_str; +        while (*check) { +            if (*check & 0x80) { +                is_ascii = 0; +                break; +            } + +            check++; +        } +    } + +    if (is_ascii) { +        return PyString_FromString(val_str); +    } else { +        return PyUnicode_DecodeUTF8(val_str, strlen(val_str), NULL); +    } +} + +/* + * Returns a row from the currently active SQLite statement + * + * Precondidition: + * - sqlite3_step() has been called before and it returned SQLITE_ROW. + */ +PyObject* _pysqlite_fetch_one_row(pysqlite_Cursor* self) +{ +    int i, numcols; +    PyObject* row; +    PyObject* item = NULL; +    int coltype; +    PY_LONG_LONG intval; +    PyObject* converter; +    PyObject* converted; +    Py_ssize_t nbytes; +    PyObject* buffer; +    void* raw_buffer; +    const char* val_str; +    char buf[200]; +    const char* colname; + +    if (self->reset) { +        PyErr_SetString(pysqlite_InterfaceError, errmsg_fetch_across_rollback); +        return NULL; +    } + +    Py_BEGIN_ALLOW_THREADS +    numcols = sqlite3_data_count(self->statement->st); +    Py_END_ALLOW_THREADS + +    row = PyTuple_New(numcols); +    if (!row) { +        return NULL; +    } + +    for (i = 0; i < numcols; i++) { +        if (self->connection->detect_types) { +            converter = PyList_GetItem(self->row_cast_map, i); +            if (!converter) { +                converter = Py_None; +            } +        } else { +            converter = Py_None; +        } + +        if (converter != Py_None) { +            nbytes = sqlite3_column_bytes(self->statement->st, i); +            val_str = (const char*)sqlite3_column_blob(self->statement->st, i); +            if (!val_str) { +                Py_INCREF(Py_None); +                converted = Py_None; +            } else { +                item = PyString_FromStringAndSize(val_str, nbytes); +                if (!item) { +                    return NULL; +                } +                converted = PyObject_CallFunction(converter, "O", item); +                Py_DECREF(item); +                if (!converted) { +                    break; +                } +            } +        } else { +            Py_BEGIN_ALLOW_THREADS +            coltype = sqlite3_column_type(self->statement->st, i); +            Py_END_ALLOW_THREADS +            if (coltype == SQLITE_NULL) { +                Py_INCREF(Py_None); +                converted = Py_None; +            } else if (coltype == SQLITE_INTEGER) { +                intval = sqlite3_column_int64(self->statement->st, i); +                if (intval < INT32_MIN || intval > INT32_MAX) { +                    converted = PyLong_FromLongLong(intval); +                } else { +                    converted = PyInt_FromLong((long)intval); +                } +            } else if (coltype == SQLITE_FLOAT) { +                converted = PyFloat_FromDouble(sqlite3_column_double(self->statement->st, i)); +            } else if (coltype == SQLITE_TEXT) { +                val_str = (const char*)sqlite3_column_text(self->statement->st, i); +                if ((self->connection->text_factory == (PyObject*)&PyUnicode_Type) +                    || (self->connection->text_factory == pysqlite_OptimizedUnicode)) { + +                    converted = pysqlite_unicode_from_string(val_str, +                        self->connection->text_factory == pysqlite_OptimizedUnicode ? 1 : 0); + +                    if (!converted) { +                        colname = sqlite3_column_name(self->statement->st, i); +                        if (!colname) { +                            colname = "<unknown column name>"; +                        } +                        PyOS_snprintf(buf, sizeof(buf) - 1, "Could not decode to UTF-8 column '%s' with text '%s'", +                                     colname , val_str); +                        PyErr_SetString(pysqlite_OperationalError, buf); +                    } +                } else if (self->connection->text_factory == (PyObject*)&PyString_Type) { +                    converted = PyString_FromString(val_str); +                } else { +                    converted = PyObject_CallFunction(self->connection->text_factory, "s", val_str); +                } +            } else { +                /* coltype == SQLITE_BLOB */ +                nbytes = sqlite3_column_bytes(self->statement->st, i); +                buffer = PyBuffer_New(nbytes); +                if (!buffer) { +                    break; +                } +                if (PyObject_AsWriteBuffer(buffer, &raw_buffer, &nbytes)) { +                    break; +                } +                memcpy(raw_buffer, sqlite3_column_blob(self->statement->st, i), nbytes); +                converted = buffer; +            } +        } + +        if (converted) { +            PyTuple_SetItem(row, i, converted); +        } else { +            Py_INCREF(Py_None); +            PyTuple_SetItem(row, i, Py_None); +        } +    } + +    if (PyErr_Occurred()) { +        Py_DECREF(row); +        row = NULL; +    } + +    return row; +} + +/* + * Checks if a cursor object is usable. + * + * 0 => error; 1 => ok + */ +static int check_cursor(pysqlite_Cursor* cur) +{ +    if (!cur->initialized) { +        PyErr_SetString(pysqlite_ProgrammingError, "Base Cursor.__init__ not called."); +        return 0; +    } + +    if (cur->closed) { +        PyErr_SetString(pysqlite_ProgrammingError, "Cannot operate on a closed cursor."); +        return 0; +    } else { +        return pysqlite_check_thread(cur->connection) && pysqlite_check_connection(cur->connection); +    } +} + +PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* args) +{ +    PyObject* operation; +    PyObject* operation_bytestr = NULL; +    char* operation_cstr; +    PyObject* parameters_list = NULL; +    PyObject* parameters_iter = NULL; +    PyObject* parameters = NULL; +    int i; +    int rc; +    PyObject* func_args; +    PyObject* result; +    int numcols; +    PY_LONG_LONG lastrowid; +    int statement_type; +    PyObject* descriptor; +    PyObject* second_argument = NULL; +    int allow_8bit_chars; + +    if (!check_cursor(self)) { +        return NULL; +    } + +    self->reset = 0; + +    /* Make shooting yourself in the foot with not utf-8 decodable 8-bit-strings harder */ +    allow_8bit_chars = ((self->connection->text_factory != (PyObject*)&PyUnicode_Type) && +        (self->connection->text_factory != pysqlite_OptimizedUnicode)); + +    Py_XDECREF(self->next_row); +    self->next_row = NULL; + +    if (multiple) { +        /* executemany() */ +        if (!PyArg_ParseTuple(args, "OO", &operation, &second_argument)) { +            return NULL; +        } + +        if (!PyString_Check(operation) && !PyUnicode_Check(operation)) { +            PyErr_SetString(PyExc_ValueError, "operation parameter must be str or unicode"); +            return NULL; +        } + +        if (PyIter_Check(second_argument)) { +            /* iterator */ +            Py_INCREF(second_argument); +            parameters_iter = second_argument; +        } else { +            /* sequence */ +            parameters_iter = PyObject_GetIter(second_argument); +            if (!parameters_iter) { +                return NULL; +            } +        } +    } else { +        /* execute() */ +        if (!PyArg_ParseTuple(args, "O|O", &operation, &second_argument)) { +            return NULL; +        } + +        if (!PyString_Check(operation) && !PyUnicode_Check(operation)) { +            PyErr_SetString(PyExc_ValueError, "operation parameter must be str or unicode"); +            return NULL; +        } + +        parameters_list = PyList_New(0); +        if (!parameters_list) { +            return NULL; +        } + +        if (second_argument == NULL) { +            second_argument = PyTuple_New(0); +            if (!second_argument) { +                goto error; +            } +        } else { +            Py_INCREF(second_argument); +        } +        if (PyList_Append(parameters_list, second_argument) != 0) { +            Py_DECREF(second_argument); +            goto error; +        } +        Py_DECREF(second_argument); + +        parameters_iter = PyObject_GetIter(parameters_list); +        if (!parameters_iter) { +            goto error; +        } +    } + +    if (self->statement != NULL) { +        /* There is an active statement */ +        rc = pysqlite_statement_reset(self->statement); +    } + +    if (PyString_Check(operation)) { +        operation_cstr = PyString_AsString(operation); +    } else { +        operation_bytestr = PyUnicode_AsUTF8String(operation); +        if (!operation_bytestr) { +            goto error; +        } + +        operation_cstr = PyString_AsString(operation_bytestr); +    } + +    /* reset description and rowcount */ +    Py_DECREF(self->description); +    Py_INCREF(Py_None); +    self->description = Py_None; +    self->rowcount = -1L; + +    func_args = PyTuple_New(1); +    if (!func_args) { +        goto error; +    } +    Py_INCREF(operation); +    if (PyTuple_SetItem(func_args, 0, operation) != 0) { +        goto error; +    } + +    if (self->statement) { +        (void)pysqlite_statement_reset(self->statement); +        Py_DECREF(self->statement); +    } + +    self->statement = (pysqlite_Statement*)pysqlite_cache_get(self->connection->statement_cache, func_args); +    Py_DECREF(func_args); + +    if (!self->statement) { +        goto error; +    } + +    if (self->statement->in_use) { +        Py_DECREF(self->statement); +        self->statement = PyObject_New(pysqlite_Statement, &pysqlite_StatementType); +        if (!self->statement) { +            goto error; +        } +        rc = pysqlite_statement_create(self->statement, self->connection, operation); +        if (rc != SQLITE_OK) { +            Py_CLEAR(self->statement); +            goto error; +        } +    } + +    pysqlite_statement_reset(self->statement); +    pysqlite_statement_mark_dirty(self->statement); + +    statement_type = detect_statement_type(operation_cstr); +    if (self->connection->begin_statement) { +        switch (statement_type) { +            case STATEMENT_UPDATE: +            case STATEMENT_DELETE: +            case STATEMENT_INSERT: +            case STATEMENT_REPLACE: +                if (!self->connection->inTransaction) { +                    result = _pysqlite_connection_begin(self->connection); +                    if (!result) { +                        goto error; +                    } +                    Py_DECREF(result); +                } +                break; +            case STATEMENT_OTHER: +                /* it's a DDL statement or something similar +                   - we better COMMIT first so it works for all cases */ +                if (self->connection->inTransaction) { +                    result = pysqlite_connection_commit(self->connection, NULL); +                    if (!result) { +                        goto error; +                    } +                    Py_DECREF(result); +                } +                break; +            case STATEMENT_SELECT: +                if (multiple) { +                    PyErr_SetString(pysqlite_ProgrammingError, +                                "You cannot execute SELECT statements in executemany()."); +                    goto error; +                } +                break; +        } +    } + +    while (1) { +        parameters = PyIter_Next(parameters_iter); +        if (!parameters) { +            break; +        } + +        pysqlite_statement_mark_dirty(self->statement); + +        pysqlite_statement_bind_parameters(self->statement, parameters, allow_8bit_chars); +        if (PyErr_Occurred()) { +            goto error; +        } + +        /* Keep trying the SQL statement until the schema stops changing. */ +        while (1) { +            /* Actually execute the SQL statement. */ +            rc = pysqlite_step(self->statement->st, self->connection); +            if (rc == SQLITE_DONE ||  rc == SQLITE_ROW) { +                /* If it worked, let's get out of the loop */ +                break; +            } +            /* Something went wrong.  Re-set the statement and try again. */ +            rc = pysqlite_statement_reset(self->statement); +            if (rc == SQLITE_SCHEMA) { +                /* If this was a result of the schema changing, let's try +                   again. */ +                rc = pysqlite_statement_recompile(self->statement, parameters); +                if (rc == SQLITE_OK) { +                    continue; +                } else { +                    /* If the database gave us an error, promote it to Python. */ +                    (void)pysqlite_statement_reset(self->statement); +                    _pysqlite_seterror(self->connection->db, NULL); +                    goto error; +                } +            } else { +                if (PyErr_Occurred()) { +                    /* there was an error that occurred in a user-defined callback */ +                    if (_enable_callback_tracebacks) { +                        PyErr_Print(); +                    } else { +                        PyErr_Clear(); +                    } +                } +                (void)pysqlite_statement_reset(self->statement); +                _pysqlite_seterror(self->connection->db, NULL); +                goto error; +            } +        } + +        if (pysqlite_build_row_cast_map(self) != 0) { +            PyErr_SetString(pysqlite_OperationalError, "Error while building row_cast_map"); +            goto error; +        } + +        if (rc == SQLITE_ROW || (rc == SQLITE_DONE && statement_type == STATEMENT_SELECT)) { +            if (self->description == Py_None) { +                Py_BEGIN_ALLOW_THREADS +                numcols = sqlite3_column_count(self->statement->st); +                Py_END_ALLOW_THREADS + +                Py_DECREF(self->description); +                self->description = PyTuple_New(numcols); +                if (!self->description) { +                    goto error; +                } +                for (i = 0; i < numcols; i++) { +                    descriptor = PyTuple_New(7); +                    if (!descriptor) { +                        goto error; +                    } +                    PyTuple_SetItem(descriptor, 0, _pysqlite_build_column_name(sqlite3_column_name(self->statement->st, i))); +                    Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 1, Py_None); +                    Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 2, Py_None); +                    Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 3, Py_None); +                    Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 4, Py_None); +                    Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 5, Py_None); +                    Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 6, Py_None); +                    PyTuple_SetItem(self->description, i, descriptor); +                } +            } +        } + +        if (rc == SQLITE_ROW) { +            if (multiple) { +                PyErr_SetString(pysqlite_ProgrammingError, "executemany() can only execute DML statements."); +                goto error; +            } + +            self->next_row = _pysqlite_fetch_one_row(self); +        } else if (rc == SQLITE_DONE && !multiple) { +            pysqlite_statement_reset(self->statement); +            Py_CLEAR(self->statement); +        } + +        switch (statement_type) { +            case STATEMENT_UPDATE: +            case STATEMENT_DELETE: +            case STATEMENT_INSERT: +            case STATEMENT_REPLACE: +                if (self->rowcount == -1L) { +                    self->rowcount = 0L; +                } +                self->rowcount += (long)sqlite3_changes(self->connection->db); +        } + +        Py_DECREF(self->lastrowid); +        if (!multiple && statement_type == STATEMENT_INSERT) { +            Py_BEGIN_ALLOW_THREADS +            lastrowid = sqlite3_last_insert_rowid(self->connection->db); +            Py_END_ALLOW_THREADS +            self->lastrowid = PyInt_FromLong((long)lastrowid); +        } else { +            Py_INCREF(Py_None); +            self->lastrowid = Py_None; +        } + +        if (multiple) { +            rc = pysqlite_statement_reset(self->statement); +        } +        Py_XDECREF(parameters); +    } + +error: +    /* just to be sure (implicit ROLLBACKs with ON CONFLICT ROLLBACK/OR +     * ROLLBACK could have happened */ +    #ifdef SQLITE_VERSION_NUMBER +    #if SQLITE_VERSION_NUMBER >= 3002002 +    self->connection->inTransaction = !sqlite3_get_autocommit(self->connection->db); +    #endif +    #endif + +    Py_XDECREF(operation_bytestr); +    Py_XDECREF(parameters); +    Py_XDECREF(parameters_iter); +    Py_XDECREF(parameters_list); + +    if (PyErr_Occurred()) { +        self->rowcount = -1L; +        return NULL; +    } else { +        Py_INCREF(self); +        return (PyObject*)self; +    } +} + +PyObject* pysqlite_cursor_execute(pysqlite_Cursor* self, PyObject* args) +{ +    return _pysqlite_query_execute(self, 0, args); +} + +PyObject* pysqlite_cursor_executemany(pysqlite_Cursor* self, PyObject* args) +{ +    return _pysqlite_query_execute(self, 1, args); +} + +PyObject* pysqlite_cursor_executescript(pysqlite_Cursor* self, PyObject* args) +{ +    PyObject* script_obj; +    PyObject* script_str = NULL; +    const char* script_cstr; +    sqlite3_stmt* statement; +    int rc; +    PyObject* result; + +    if (!PyArg_ParseTuple(args, "O", &script_obj)) { +        return NULL; +    } + +    if (!check_cursor(self)) { +        return NULL; +    } + +    self->reset = 0; + +    if (PyString_Check(script_obj)) { +        script_cstr = PyString_AsString(script_obj); +    } else if (PyUnicode_Check(script_obj)) { +        script_str = PyUnicode_AsUTF8String(script_obj); +        if (!script_str) { +            return NULL; +        } + +        script_cstr = PyString_AsString(script_str); +    } else { +        PyErr_SetString(PyExc_ValueError, "script argument must be unicode or string."); +        return NULL; +    } + +    /* commit first */ +    result = pysqlite_connection_commit(self->connection, NULL); +    if (!result) { +        goto error; +    } +    Py_DECREF(result); + +    while (1) { +        Py_BEGIN_ALLOW_THREADS +        rc = sqlite3_prepare(self->connection->db, +                             script_cstr, +                             -1, +                             &statement, +                             &script_cstr); +        Py_END_ALLOW_THREADS +        if (rc != SQLITE_OK) { +            _pysqlite_seterror(self->connection->db, NULL); +            goto error; +        } + +        /* execute statement, and ignore results of SELECT statements */ +        rc = SQLITE_ROW; +        while (rc == SQLITE_ROW) { +            rc = pysqlite_step(statement, self->connection); +            /* TODO: we probably need more error handling here */ +        } + +        if (rc != SQLITE_DONE) { +            (void)sqlite3_finalize(statement); +            _pysqlite_seterror(self->connection->db, NULL); +            goto error; +        } + +        rc = sqlite3_finalize(statement); +        if (rc != SQLITE_OK) { +            _pysqlite_seterror(self->connection->db, NULL); +            goto error; +        } + +        if (*script_cstr == (char)0) { +            break; +        } +    } + +error: +    Py_XDECREF(script_str); + +    if (PyErr_Occurred()) { +        return NULL; +    } else { +        Py_INCREF(self); +        return (PyObject*)self; +    } +} + +PyObject* pysqlite_cursor_getiter(pysqlite_Cursor *self) +{ +    Py_INCREF(self); +    return (PyObject*)self; +} + +PyObject* pysqlite_cursor_iternext(pysqlite_Cursor *self) +{ +    PyObject* next_row_tuple; +    PyObject* next_row; +    int rc; + +    if (!check_cursor(self)) { +        return NULL; +    } + +    if (self->reset) { +        PyErr_SetString(pysqlite_InterfaceError, errmsg_fetch_across_rollback); +        return NULL; +    } + +    if (!self->next_row) { +         if (self->statement) { +            (void)pysqlite_statement_reset(self->statement); +            Py_DECREF(self->statement); +            self->statement = NULL; +        } +        return NULL; +    } + +    next_row_tuple = self->next_row; +    self->next_row = NULL; + +    if (self->row_factory != Py_None) { +        next_row = PyObject_CallFunction(self->row_factory, "OO", self, next_row_tuple); +        Py_DECREF(next_row_tuple); +    } else { +        next_row = next_row_tuple; +    } + +    if (self->statement) { +        rc = pysqlite_step(self->statement->st, self->connection); +        if (rc != SQLITE_DONE && rc != SQLITE_ROW) { +            (void)pysqlite_statement_reset(self->statement); +            Py_DECREF(next_row); +            _pysqlite_seterror(self->connection->db, NULL); +            return NULL; +        } + +        if (rc == SQLITE_ROW) { +            self->next_row = _pysqlite_fetch_one_row(self); +        } +    } + +    return next_row; +} + +PyObject* pysqlite_cursor_fetchone(pysqlite_Cursor* self, PyObject* args) +{ +    PyObject* row; + +    row = pysqlite_cursor_iternext(self); +    if (!row && !PyErr_Occurred()) { +        Py_INCREF(Py_None); +        return Py_None; +    } + +    return row; +} + +PyObject* pysqlite_cursor_fetchmany(pysqlite_Cursor* self, PyObject* args, PyObject* kwargs) +{ +    static char *kwlist[] = {"size", NULL, NULL}; + +    PyObject* row; +    PyObject* list; +    int maxrows = self->arraysize; +    int counter = 0; + +    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i:fetchmany", kwlist, &maxrows)) { +        return NULL; +    } + +    list = PyList_New(0); +    if (!list) { +        return NULL; +    } + +    /* just make sure we enter the loop */ +    row = Py_None; + +    while (row) { +        row = pysqlite_cursor_iternext(self); +        if (row) { +            PyList_Append(list, row); +            Py_DECREF(row); +        } else { +            break; +        } + +        if (++counter == maxrows) { +            break; +        } +    } + +    if (PyErr_Occurred()) { +        Py_DECREF(list); +        return NULL; +    } else { +        return list; +    } +} + +PyObject* pysqlite_cursor_fetchall(pysqlite_Cursor* self, PyObject* args) +{ +    PyObject* row; +    PyObject* list; + +    list = PyList_New(0); +    if (!list) { +        return NULL; +    } + +    /* just make sure we enter the loop */ +    row = (PyObject*)Py_None; + +    while (row) { +        row = pysqlite_cursor_iternext(self); +        if (row) { +            PyList_Append(list, row); +            Py_DECREF(row); +        } +    } + +    if (PyErr_Occurred()) { +        Py_DECREF(list); +        return NULL; +    } else { +        return list; +    } +} + +PyObject* pysqlite_noop(pysqlite_Connection* self, PyObject* args) +{ +    /* don't care, return None */ +    Py_INCREF(Py_None); +    return Py_None; +} + +PyObject* pysqlite_cursor_close(pysqlite_Cursor* self, PyObject* args) +{ +    if (!pysqlite_check_thread(self->connection) || !pysqlite_check_connection(self->connection)) { +        return NULL; +    } + +    if (self->statement) { +        (void)pysqlite_statement_reset(self->statement); +        Py_CLEAR(self->statement); +    } + +    self->closed = 1; + +    Py_INCREF(Py_None); +    return Py_None; +} + +static PyMethodDef cursor_methods[] = { +    {"execute", (PyCFunction)pysqlite_cursor_execute, METH_VARARGS, +        PyDoc_STR("Executes a SQL statement.")}, +    {"executemany", (PyCFunction)pysqlite_cursor_executemany, METH_VARARGS, +        PyDoc_STR("Repeatedly executes a SQL statement.")}, +    {"executescript", (PyCFunction)pysqlite_cursor_executescript, METH_VARARGS, +        PyDoc_STR("Executes a multiple SQL statements at once. Non-standard.")}, +    {"fetchone", (PyCFunction)pysqlite_cursor_fetchone, METH_NOARGS, +        PyDoc_STR("Fetches one row from the resultset.")}, +    {"fetchmany", (PyCFunction)pysqlite_cursor_fetchmany, METH_VARARGS|METH_KEYWORDS, +        PyDoc_STR("Fetches several rows from the resultset.")}, +    {"fetchall", (PyCFunction)pysqlite_cursor_fetchall, METH_NOARGS, +        PyDoc_STR("Fetches all rows from the resultset.")}, +    {"close", (PyCFunction)pysqlite_cursor_close, METH_NOARGS, +        PyDoc_STR("Closes the cursor.")}, +    {"setinputsizes", (PyCFunction)pysqlite_noop, METH_VARARGS, +        PyDoc_STR("Required by DB-API. Does nothing in pysqlite.")}, +    {"setoutputsize", (PyCFunction)pysqlite_noop, METH_VARARGS, +        PyDoc_STR("Required by DB-API. Does nothing in pysqlite.")}, +    {NULL, NULL} +}; + +static struct PyMemberDef cursor_members[] = +{ +    {"connection", T_OBJECT, offsetof(pysqlite_Cursor, connection), RO}, +    {"description", T_OBJECT, offsetof(pysqlite_Cursor, description), RO}, +    {"arraysize", T_INT, offsetof(pysqlite_Cursor, arraysize), 0}, +    {"lastrowid", T_OBJECT, offsetof(pysqlite_Cursor, lastrowid), RO}, +    {"rowcount", T_LONG, offsetof(pysqlite_Cursor, rowcount), RO}, +    {"row_factory", T_OBJECT, offsetof(pysqlite_Cursor, row_factory), 0}, +    {NULL} +}; + +static char cursor_doc[] = +PyDoc_STR("SQLite database cursor class."); + +PyTypeObject pysqlite_CursorType = { +        PyVarObject_HEAD_INIT(NULL, 0) +        MODULE_NAME ".Cursor",                          /* tp_name */ +        sizeof(pysqlite_Cursor),                        /* tp_basicsize */ +        0,                                              /* tp_itemsize */ +        (destructor)pysqlite_cursor_dealloc,            /* tp_dealloc */ +        0,                                              /* tp_print */ +        0,                                              /* tp_getattr */ +        0,                                              /* tp_setattr */ +        0,                                              /* tp_compare */ +        0,                                              /* tp_repr */ +        0,                                              /* tp_as_number */ +        0,                                              /* tp_as_sequence */ +        0,                                              /* tp_as_mapping */ +        0,                                              /* tp_hash */ +        0,                                              /* tp_call */ +        0,                                              /* tp_str */ +        0,                                              /* tp_getattro */ +        0,                                              /* tp_setattro */ +        0,                                              /* tp_as_buffer */ +        Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_ITER|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_WEAKREFS, /* tp_flags */ +        cursor_doc,                                     /* tp_doc */ +        0,                                              /* tp_traverse */ +        0,                                              /* tp_clear */ +        0,                                              /* tp_richcompare */ +        offsetof(pysqlite_Cursor, in_weakreflist),      /* tp_weaklistoffset */ +        (getiterfunc)pysqlite_cursor_getiter,           /* tp_iter */ +        (iternextfunc)pysqlite_cursor_iternext,         /* tp_iternext */ +        cursor_methods,                                 /* tp_methods */ +        cursor_members,                                 /* tp_members */ +        0,                                              /* tp_getset */ +        0,                                              /* tp_base */ +        0,                                              /* tp_dict */ +        0,                                              /* tp_descr_get */ +        0,                                              /* tp_descr_set */ +        0,                                              /* tp_dictoffset */ +        (initproc)pysqlite_cursor_init,                 /* tp_init */ +        0,                                              /* tp_alloc */ +        0,                                              /* tp_new */ +        0                                               /* tp_free */ +}; + +extern int pysqlite_cursor_setup_types(void) +{ +    pysqlite_CursorType.tp_new = PyType_GenericNew; +    return PyType_Ready(&pysqlite_CursorType); +} diff --git a/src/cursor.h b/src/cursor.h new file mode 100644 index 0000000..82f5972 --- /dev/null +++ b/src/cursor.h @@ -0,0 +1,74 @@ +/* cursor.h - definitions for the cursor type + * + * Copyright (C) 2004-2010 Gerhard Häring <gh@ghaering.de> + * + * This file is part of pysqlite. + * + * This software is provided 'as-is', without any express or implied + * warranty.  In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + *    claim that you wrote the original software. If you use this software + *    in a product, an acknowledgment in the product documentation would be + *    appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + *    misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#ifndef PYSQLITE_CURSOR_H +#define PYSQLITE_CURSOR_H +#include "Python.h" + +#include "statement.h" +#include "connection.h" +#include "module.h" + +typedef struct +{ +    PyObject_HEAD +    pysqlite_Connection* connection; +    PyObject* description; +    PyObject* row_cast_map; +    int arraysize; +    PyObject* lastrowid; +    long rowcount; +    PyObject* row_factory; +    pysqlite_Statement* statement; +    int closed; +    int reset; +    int initialized; + +    /* the next row to be returned, NULL if no next row available */ +    PyObject* next_row; + +    PyObject* in_weakreflist; /* List of weak references */ +} pysqlite_Cursor; + +typedef enum { +    STATEMENT_INVALID, STATEMENT_INSERT, STATEMENT_DELETE, +    STATEMENT_UPDATE, STATEMENT_REPLACE, STATEMENT_SELECT, +    STATEMENT_OTHER +} pysqlite_StatementKind; + +extern PyTypeObject pysqlite_CursorType; + +PyObject* pysqlite_cursor_execute(pysqlite_Cursor* self, PyObject* args); +PyObject* pysqlite_cursor_executemany(pysqlite_Cursor* self, PyObject* args); +PyObject* pysqlite_cursor_getiter(pysqlite_Cursor *self); +PyObject* pysqlite_cursor_iternext(pysqlite_Cursor *self); +PyObject* pysqlite_cursor_fetchone(pysqlite_Cursor* self, PyObject* args); +PyObject* pysqlite_cursor_fetchmany(pysqlite_Cursor* self, PyObject* args, PyObject* kwargs); +PyObject* pysqlite_cursor_fetchall(pysqlite_Cursor* self, PyObject* args); +PyObject* pysqlite_noop(pysqlite_Connection* self, PyObject* args); +PyObject* pysqlite_cursor_close(pysqlite_Cursor* self, PyObject* args); + +int pysqlite_cursor_setup_types(void); + +#define UNKNOWN (-1) +#endif diff --git a/src/microprotocols.c b/src/microprotocols.c new file mode 100644 index 0000000..c730afa --- /dev/null +++ b/src/microprotocols.c @@ -0,0 +1,142 @@ +/* microprotocols.c - minimalist and non-validating protocols implementation + * + * Copyright (C) 2003-2004 Federico Di Gregorio <fog@debian.org> + * + * This file is part of psycopg and was adapted for pysqlite. Federico Di + * Gregorio gave the permission to use it within pysqlite under the following + * license: + * + * This software is provided 'as-is', without any express or implied + * warranty.  In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + *    claim that you wrote the original software. If you use this software + *    in a product, an acknowledgment in the product documentation would be + *    appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + *    misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include <Python.h> +#include <structmember.h> + +#include "cursor.h" +#include "microprotocols.h" +#include "prepare_protocol.h" + + +/** the adapters registry **/ + +PyObject *psyco_adapters; + +/* pysqlite_microprotocols_init - initialize the adapters dictionary */ + +int +pysqlite_microprotocols_init(PyObject *dict) +{ +    /* create adapters dictionary and put it in module namespace */ +    if ((psyco_adapters = PyDict_New()) == NULL) { +        return -1; +    } + +    return PyDict_SetItemString(dict, "adapters", psyco_adapters); +} + + +/* pysqlite_microprotocols_add - add a reverse type-caster to the dictionary */ + +int +pysqlite_microprotocols_add(PyTypeObject *type, PyObject *proto, PyObject *cast) +{ +    PyObject* key; +    int rc; + +    if (proto == NULL) proto = (PyObject*)&pysqlite_PrepareProtocolType; + +    key = Py_BuildValue("(OO)", (PyObject*)type, proto); +    if (!key) { +        return -1; +    } + +    rc = PyDict_SetItem(psyco_adapters, key, cast); +    Py_DECREF(key); + +    return rc; +} + +/* pysqlite_microprotocols_adapt - adapt an object to the built-in protocol */ + +PyObject * +pysqlite_microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt) +{ +    PyObject *adapter, *key; + +    /* we don't check for exact type conformance as specified in PEP 246 +       because the pysqlite_PrepareProtocolType type is abstract and there is no +       way to get a quotable object to be its instance */ + +    /* look for an adapter in the registry */ +    key = Py_BuildValue("(OO)", (PyObject*)obj->ob_type, proto); +    if (!key) { +        return NULL; +    } +    adapter = PyDict_GetItem(psyco_adapters, key); +    Py_DECREF(key); +    if (adapter) { +        PyObject *adapted = PyObject_CallFunctionObjArgs(adapter, obj, NULL); +        return adapted; +    } + +    /* try to have the protocol adapt this object*/ +    if (PyObject_HasAttrString(proto, "__adapt__")) { +        PyObject *adapted = PyObject_CallMethod(proto, "__adapt__", "O", obj); +        if (adapted) { +            if (adapted != Py_None) { +                return adapted; +            } else { +                Py_DECREF(adapted); +            } +        } + +        if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError)) +            return NULL; +    } + +    /* and finally try to have the object adapt itself */ +    if (PyObject_HasAttrString(obj, "__conform__")) { +        PyObject *adapted = PyObject_CallMethod(obj, "__conform__","O", proto); +        if (adapted) { +            if (adapted != Py_None) { +                return adapted; +            } else { +                Py_DECREF(adapted); +            } +        } + +        if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError)) { +            return NULL; +        } +    } + +    /* else set the right exception and return NULL */ +    PyErr_SetString(pysqlite_ProgrammingError, "can't adapt"); +    return NULL; +} + +/** module-level functions **/ + +PyObject * +pysqlite_adapt(pysqlite_Cursor *self, PyObject *args) +{ +    PyObject *obj, *alt = NULL; +    PyObject *proto = (PyObject*)&pysqlite_PrepareProtocolType; + +    if (!PyArg_ParseTuple(args, "O|OO", &obj, &proto, &alt)) return NULL; +    return pysqlite_microprotocols_adapt(obj, proto, alt); +} diff --git a/src/microprotocols.h b/src/microprotocols.h new file mode 100644 index 0000000..3a9944f --- /dev/null +++ b/src/microprotocols.h @@ -0,0 +1,55 @@ +/* microprotocols.c - definitions for minimalist and non-validating protocols + * + * Copyright (C) 2003-2004 Federico Di Gregorio <fog@debian.org> + * + * This file is part of psycopg and was adapted for pysqlite. Federico Di + * Gregorio gave the permission to use it within pysqlite under the following + * license: + * + * This software is provided 'as-is', without any express or implied + * warranty.  In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + *    claim that you wrote the original software. If you use this software + *    in a product, an acknowledgment in the product documentation would be + *    appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + *    misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#ifndef PSYCOPG_MICROPROTOCOLS_H +#define PSYCOPG_MICROPROTOCOLS_H 1 + +#include <Python.h> + +/** adapters registry **/ + +extern PyObject *psyco_adapters; + +/** the names of the three mandatory methods **/ + +#define MICROPROTOCOLS_GETQUOTED_NAME "getquoted" +#define MICROPROTOCOLS_GETSTRING_NAME "getstring" +#define MICROPROTOCOLS_GETBINARY_NAME "getbinary" + +/** exported functions **/ + +/* used by module.c to init the microprotocols system */ +extern int pysqlite_microprotocols_init(PyObject *dict); +extern int pysqlite_microprotocols_add( +    PyTypeObject *type, PyObject *proto, PyObject *cast); +extern PyObject *pysqlite_microprotocols_adapt( +    PyObject *obj, PyObject *proto, PyObject *alt); + +extern PyObject * +    pysqlite_adapt(pysqlite_Cursor* self, PyObject *args);    +#define pysqlite_adapt_doc \ +    "adapt(obj, protocol, alternate) -> adapt obj to given protocol. Non-standard." + +#endif /* !defined(PSYCOPG_MICROPROTOCOLS_H) */ diff --git a/src/module.c b/src/module.c new file mode 100644 index 0000000..dbc015c --- /dev/null +++ b/src/module.c @@ -0,0 +1,464 @@ +/* module.c - the module itself + * + * Copyright (C) 2004-2010 Gerhard Häring <gh@ghaering.de> + * + * This file is part of pysqlite. + * + * This software is provided 'as-is', without any express or implied + * warranty.  In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + *    claim that you wrote the original software. If you use this software + *    in a product, an acknowledgment in the product documentation would be + *    appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + *    misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include "connection.h" +#include "statement.h" +#include "cursor.h" +#include "cache.h" +#include "prepare_protocol.h" +#include "microprotocols.h" +#include "row.h" + +#ifdef PYSQLITE_EXPERIMENTAL +#include "backup.h" +#endif + +#if SQLITE_VERSION_NUMBER >= 3003003 +#define HAVE_SHARED_CACHE +#endif + +/* static objects at module-level */ + +PyObject* pysqlite_Error, *pysqlite_Warning, *pysqlite_InterfaceError, *pysqlite_DatabaseError, +    *pysqlite_InternalError, *pysqlite_OperationalError, *pysqlite_ProgrammingError, +    *pysqlite_IntegrityError, *pysqlite_DataError, *pysqlite_NotSupportedError, *pysqlite_OptimizedUnicode; + +PyObject* converters; +int _enable_callback_tracebacks; +int pysqlite_BaseTypeAdapted; + +static PyObject* module_connect(PyObject* self, PyObject* args, PyObject* +        kwargs) +{ +    /* Python seems to have no way of extracting a single keyword-arg at +     * C-level, so this code is redundant with the one in connection_init in +     * connection.c and must always be copied from there ... */ + +    static char *kwlist[] = {"database", "timeout", "detect_types", "isolation_level", "check_same_thread", "factory", "cached_statements", NULL, NULL}; +    PyObject* database; +    int detect_types = 0; +    PyObject* isolation_level; +    PyObject* factory = NULL; +    int check_same_thread = 1; +    int cached_statements; +    double timeout = 5.0; + +    PyObject* result; + +    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|diOiOi", kwlist, +                                     &database, &timeout, &detect_types, &isolation_level, &check_same_thread, &factory, &cached_statements)) +    { +        return NULL;  +    } + +    if (factory == NULL) { +        factory = (PyObject*)&pysqlite_ConnectionType; +    } + +    result = PyObject_Call(factory, args, kwargs); + +    return result; +} + +PyDoc_STRVAR(module_connect_doc, +"connect(database[, timeout, isolation_level, detect_types, factory])\n\ +\n\ +Opens a connection to the SQLite database file *database*. You can use\n\ +\":memory:\" to open a database connection to a database that resides in\n\ +RAM instead of on disk."); + +static PyObject* module_complete(PyObject* self, PyObject* args, PyObject* +        kwargs) +{ +    static char *kwlist[] = {"statement", NULL, NULL}; +    char* statement; + +    PyObject* result; + +    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &statement)) +    { +        return NULL;  +    } + +    if (sqlite3_complete(statement)) { +        result = Py_True; +    } else { +        result = Py_False; +    } + +    Py_INCREF(result); + +    return result; +} + +PyDoc_STRVAR(module_complete_doc, +"complete_statement(sql)\n\ +\n\ +Checks if a string contains a complete SQL statement. Non-standard."); + +#ifdef HAVE_SHARED_CACHE +static PyObject* module_enable_shared_cache(PyObject* self, PyObject* args, PyObject* +        kwargs) +{ +    static char *kwlist[] = {"do_enable", NULL, NULL}; +    int do_enable; +    int rc; + +    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &do_enable)) +    { +        return NULL;  +    } + +    rc = sqlite3_enable_shared_cache(do_enable); + +    if (rc != SQLITE_OK) { +        PyErr_SetString(pysqlite_OperationalError, "Changing the shared_cache flag failed"); +        return NULL; +    } else { +        Py_INCREF(Py_None); +        return Py_None; +    } +} + +PyDoc_STRVAR(module_enable_shared_cache_doc, +"enable_shared_cache(do_enable)\n\ +\n\ +Enable or disable shared cache mode for the calling thread.\n\ +Experimental/Non-standard."); +#endif /* HAVE_SHARED_CACHE */ + +static PyObject* module_register_adapter(PyObject* self, PyObject* args) +{ +    PyTypeObject* type; +    PyObject* caster; +    int rc; + +    if (!PyArg_ParseTuple(args, "OO", &type, &caster)) { +        return NULL; +    } + +    /* a basic type is adapted; there's a performance optimization if that's not the case +     * (99 % of all usages) */ +    if (type == &PyInt_Type || type == &PyLong_Type || type == &PyFloat_Type +            || type == &PyString_Type || type == &PyUnicode_Type || type == &PyBuffer_Type) { +        pysqlite_BaseTypeAdapted = 1; +    } + +    rc = pysqlite_microprotocols_add(type, (PyObject*)&pysqlite_PrepareProtocolType, caster); +    if (rc == -1) +        return NULL; + +    Py_INCREF(Py_None); +    return Py_None; +} + +PyDoc_STRVAR(module_register_adapter_doc, +"register_adapter(type, callable)\n\ +\n\ +Registers an adapter with pysqlite's adapter registry. Non-standard."); + +static PyObject* module_register_converter(PyObject* self, PyObject* args) +{ +    PyObject* orig_name; +    PyObject* name = NULL; +    PyObject* callable; +    PyObject* retval = NULL; + +    if (!PyArg_ParseTuple(args, "SO", &orig_name, &callable)) { +        return NULL; +    } + +    /* convert the name to upper case */ +    name = PyObject_CallMethod(orig_name, "upper", ""); +    if (!name) { +        goto error; +    } + +    if (PyDict_SetItem(converters, name, callable) != 0) { +        goto error; +    } + +    Py_INCREF(Py_None); +    retval = Py_None; +error: +    Py_XDECREF(name); +    return retval; +} + +PyDoc_STRVAR(module_register_converter_doc, +"register_converter(typename, callable)\n\ +\n\ +Registers a converter with pysqlite. Non-standard."); + +static PyObject* enable_callback_tracebacks(PyObject* self, PyObject* args) +{ +    if (!PyArg_ParseTuple(args, "i", &_enable_callback_tracebacks)) { +        return NULL; +    } + +    Py_INCREF(Py_None); +    return Py_None; +} + +PyDoc_STRVAR(enable_callback_tracebacks_doc, +"enable_callback_tracebacks(flag)\n\ +\n\ +Enable or disable callback functions throwing errors to stderr."); + +static void converters_init(PyObject* dict) +{ +    converters = PyDict_New(); +    if (!converters) { +        return; +    } + +    PyDict_SetItemString(dict, "converters", converters); +} + +static PyMethodDef module_methods[] = { +    {"connect",  (PyCFunction)module_connect, +     METH_VARARGS | METH_KEYWORDS, module_connect_doc}, +    {"complete_statement",  (PyCFunction)module_complete, +     METH_VARARGS | METH_KEYWORDS, module_complete_doc}, +#ifdef HAVE_SHARED_CACHE +    {"enable_shared_cache",  (PyCFunction)module_enable_shared_cache, +     METH_VARARGS | METH_KEYWORDS, module_enable_shared_cache_doc}, +#endif +    {"register_adapter", (PyCFunction)module_register_adapter, +     METH_VARARGS, module_register_adapter_doc}, +    {"register_converter", (PyCFunction)module_register_converter, +     METH_VARARGS, module_register_converter_doc}, +    {"adapt",  (PyCFunction)pysqlite_adapt, METH_VARARGS, +     pysqlite_adapt_doc}, +    {"enable_callback_tracebacks",  (PyCFunction)enable_callback_tracebacks, +     METH_VARARGS, enable_callback_tracebacks_doc}, +    {NULL, NULL} +}; + +struct _IntConstantPair { +    char* constant_name; +    int constant_value; +}; + +typedef struct _IntConstantPair IntConstantPair; + +static IntConstantPair _int_constants[] = { +    {"PARSE_DECLTYPES", PARSE_DECLTYPES}, +    {"PARSE_COLNAMES", PARSE_COLNAMES}, + +    {"SQLITE_OK", SQLITE_OK}, +    {"SQLITE_DENY", SQLITE_DENY}, +    {"SQLITE_IGNORE", SQLITE_IGNORE}, +    {"SQLITE_CREATE_INDEX", SQLITE_CREATE_INDEX}, +    {"SQLITE_CREATE_TABLE", SQLITE_CREATE_TABLE}, +    {"SQLITE_CREATE_TEMP_INDEX", SQLITE_CREATE_TEMP_INDEX}, +    {"SQLITE_CREATE_TEMP_TABLE", SQLITE_CREATE_TEMP_TABLE}, +    {"SQLITE_CREATE_TEMP_TRIGGER", SQLITE_CREATE_TEMP_TRIGGER}, +    {"SQLITE_CREATE_TEMP_VIEW", SQLITE_CREATE_TEMP_VIEW}, +    {"SQLITE_CREATE_TRIGGER", SQLITE_CREATE_TRIGGER}, +    {"SQLITE_CREATE_VIEW", SQLITE_CREATE_VIEW}, +    {"SQLITE_DELETE", SQLITE_DELETE}, +    {"SQLITE_DROP_INDEX", SQLITE_DROP_INDEX}, +    {"SQLITE_DROP_TABLE", SQLITE_DROP_TABLE}, +    {"SQLITE_DROP_TEMP_INDEX", SQLITE_DROP_TEMP_INDEX}, +    {"SQLITE_DROP_TEMP_TABLE", SQLITE_DROP_TEMP_TABLE}, +    {"SQLITE_DROP_TEMP_TRIGGER", SQLITE_DROP_TEMP_TRIGGER}, +    {"SQLITE_DROP_TEMP_VIEW", SQLITE_DROP_TEMP_VIEW}, +    {"SQLITE_DROP_TRIGGER", SQLITE_DROP_TRIGGER}, +    {"SQLITE_DROP_VIEW", SQLITE_DROP_VIEW}, +    {"SQLITE_INSERT", SQLITE_INSERT}, +    {"SQLITE_PRAGMA", SQLITE_PRAGMA}, +    {"SQLITE_READ", SQLITE_READ}, +    {"SQLITE_SELECT", SQLITE_SELECT}, +    {"SQLITE_TRANSACTION", SQLITE_TRANSACTION}, +    {"SQLITE_UPDATE", SQLITE_UPDATE}, +    {"SQLITE_ATTACH", SQLITE_ATTACH}, +    {"SQLITE_DETACH", SQLITE_DETACH}, +#if SQLITE_VERSION_NUMBER >= 3002001 +    {"SQLITE_ALTER_TABLE", SQLITE_ALTER_TABLE}, +    {"SQLITE_REINDEX", SQLITE_REINDEX}, +#endif +#if SQLITE_VERSION_NUMBER >= 3003000 +    {"SQLITE_ANALYZE", SQLITE_ANALYZE}, +#endif +    {(char*)NULL, 0} +}; + +PyMODINIT_FUNC init_sqlite(void) +{ +    PyObject *module, *dict; +    PyObject *tmp_obj; +    int i; + +    module = Py_InitModule("pysqlcipher._sqlite", module_methods); + +    if (!module || +        (pysqlite_row_setup_types() < 0) || +        (pysqlite_cursor_setup_types() < 0) || +        (pysqlite_connection_setup_types() < 0) || +        (pysqlite_cache_setup_types() < 0) || +        (pysqlite_statement_setup_types() < 0) || +        #ifdef PYSQLITE_EXPERIMENTAL +        (pysqlite_backup_setup_types() < 0) || +        #endif +        (pysqlite_prepare_protocol_setup_types() < 0) +       ) { +        return; +    } + +    Py_INCREF(&pysqlite_ConnectionType); +    PyModule_AddObject(module, "Connection", (PyObject*) &pysqlite_ConnectionType); +    Py_INCREF(&pysqlite_CursorType); +    PyModule_AddObject(module, "Cursor", (PyObject*) &pysqlite_CursorType); +    Py_INCREF(&pysqlite_CacheType); +    PyModule_AddObject(module, "Statement", (PyObject*)&pysqlite_StatementType); +    Py_INCREF(&pysqlite_StatementType); +    PyModule_AddObject(module, "Cache", (PyObject*) &pysqlite_CacheType); +    Py_INCREF(&pysqlite_PrepareProtocolType); +    PyModule_AddObject(module, "PrepareProtocol", (PyObject*) &pysqlite_PrepareProtocolType); +    Py_INCREF(&pysqlite_RowType); +    PyModule_AddObject(module, "Row", (PyObject*) &pysqlite_RowType); + +    if (!(dict = PyModule_GetDict(module))) { +        goto error; +    } + +    /*** Create DB-API Exception hierarchy */ + +    if (!(pysqlite_Error = PyErr_NewException(MODULE_NAME ".Error", PyExc_StandardError, NULL))) { +        goto error; +    } +    PyDict_SetItemString(dict, "Error", pysqlite_Error); + +    if (!(pysqlite_Warning = PyErr_NewException(MODULE_NAME ".Warning", PyExc_StandardError, NULL))) { +        goto error; +    } +    PyDict_SetItemString(dict, "Warning", pysqlite_Warning); + +    /* Error subclasses */ + +    if (!(pysqlite_InterfaceError = PyErr_NewException(MODULE_NAME ".InterfaceError", pysqlite_Error, NULL))) { +        goto error; +    } +    PyDict_SetItemString(dict, "InterfaceError", pysqlite_InterfaceError); + +    if (!(pysqlite_DatabaseError = PyErr_NewException(MODULE_NAME ".DatabaseError", pysqlite_Error, NULL))) { +        goto error; +    } +    PyDict_SetItemString(dict, "DatabaseError", pysqlite_DatabaseError); + +    /* pysqlite_DatabaseError subclasses */ + +    if (!(pysqlite_InternalError = PyErr_NewException(MODULE_NAME ".InternalError", pysqlite_DatabaseError, NULL))) { +        goto error; +    } +    PyDict_SetItemString(dict, "InternalError", pysqlite_InternalError); + +    if (!(pysqlite_OperationalError = PyErr_NewException(MODULE_NAME ".OperationalError", pysqlite_DatabaseError, NULL))) { +        goto error; +    } +    PyDict_SetItemString(dict, "OperationalError", pysqlite_OperationalError); + +    if (!(pysqlite_ProgrammingError = PyErr_NewException(MODULE_NAME ".ProgrammingError", pysqlite_DatabaseError, NULL))) { +        goto error; +    } +    PyDict_SetItemString(dict, "ProgrammingError", pysqlite_ProgrammingError); + +    if (!(pysqlite_IntegrityError = PyErr_NewException(MODULE_NAME ".IntegrityError", pysqlite_DatabaseError,NULL))) { +        goto error; +    } +    PyDict_SetItemString(dict, "IntegrityError", pysqlite_IntegrityError); + +    if (!(pysqlite_DataError = PyErr_NewException(MODULE_NAME ".DataError", pysqlite_DatabaseError, NULL))) { +        goto error; +    } +    PyDict_SetItemString(dict, "DataError", pysqlite_DataError); + +    if (!(pysqlite_NotSupportedError = PyErr_NewException(MODULE_NAME ".NotSupportedError", pysqlite_DatabaseError, NULL))) { +        goto error; +    } +    PyDict_SetItemString(dict, "NotSupportedError", pysqlite_NotSupportedError); + +    /* We just need "something" unique for pysqlite_OptimizedUnicode. It does not really +     * need to be a string subclass. Just anything that can act as a special +     * marker for us. So I pulled PyCell_Type out of my magic hat. +     */ +    Py_INCREF((PyObject*)&PyCell_Type); +    pysqlite_OptimizedUnicode = (PyObject*)&PyCell_Type; +    PyDict_SetItemString(dict, "OptimizedUnicode", pysqlite_OptimizedUnicode); + +    /* Set integer constants */ +    for (i = 0; _int_constants[i].constant_name != 0; i++) { +        tmp_obj = PyInt_FromLong(_int_constants[i].constant_value); +        if (!tmp_obj) { +            goto error; +        } +        PyDict_SetItemString(dict, _int_constants[i].constant_name, tmp_obj); +        Py_DECREF(tmp_obj); +    } + +    if (!(tmp_obj = PyString_FromString(PYSQLITE_VERSION))) { +        goto error; +    } +    PyDict_SetItemString(dict, "version", tmp_obj); +    Py_DECREF(tmp_obj); + +    if (!(tmp_obj = PyString_FromString(sqlite3_libversion()))) { +        goto error; +    } +    PyDict_SetItemString(dict, "sqlite_version", tmp_obj); +    Py_DECREF(tmp_obj); + +    /* initialize microprotocols layer */ +    pysqlite_microprotocols_init(dict); + +    /* initialize the default converters */ +    converters_init(dict); + +    _enable_callback_tracebacks = 0; + +    pysqlite_BaseTypeAdapted = 0; + +    /* Original comment from _bsddb.c in the Python core. This is also still +     * needed nowadays for Python 2.3/2.4. +     *  +     * PyEval_InitThreads is called here due to a quirk in python 1.5 +     * - 2.2.1 (at least) according to Russell Williamson <merel@wt.net>: +     * The global interpreter lock is not initialized until the first +     * thread is created using thread.start_new_thread() or fork() is +     * called.  that would cause the ALLOW_THREADS here to segfault due +     * to a null pointer reference if no threads or child processes +     * have been created.  This works around that and is a no-op if +     * threads have already been initialized. +     *  (see pybsddb-users mailing list post on 2002-08-07) +     */ +#ifdef WITH_THREAD +    PyEval_InitThreads(); +#endif + +error: +    if (PyErr_Occurred()) +    { +        PyErr_SetString(PyExc_ImportError, "pysqlcipher._sqlite: init failed"); +    } +} diff --git a/src/module.h b/src/module.h new file mode 100644 index 0000000..0b1dab3 --- /dev/null +++ b/src/module.h @@ -0,0 +1,58 @@ +/* module.h - definitions for the module + * + * Copyright (C) 2004-2010 Gerhard Häring <gh@ghaering.de> + * + * This file is part of pysqlite. + * + * This software is provided 'as-is', without any express or implied + * warranty.  In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + *    claim that you wrote the original software. If you use this software + *    in a product, an acknowledgment in the product documentation would be + *    appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + *    misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#ifndef PYSQLITE_MODULE_H +#define PYSQLITE_MODULE_H +#include "Python.h" + +#define PYSQLITE_VERSION "2.6.3" + +extern PyObject* pysqlite_Error; +extern PyObject* pysqlite_Warning; +extern PyObject* pysqlite_InterfaceError; +extern PyObject* pysqlite_DatabaseError; +extern PyObject* pysqlite_InternalError; +extern PyObject* pysqlite_OperationalError; +extern PyObject* pysqlite_ProgrammingError; +extern PyObject* pysqlite_IntegrityError; +extern PyObject* pysqlite_DataError; +extern PyObject* pysqlite_NotSupportedError; + +extern PyObject* pysqlite_OptimizedUnicode; + +/* the functions time.time() and time.sleep() */ +extern PyObject* time_time; +extern PyObject* time_sleep; + +/* A dictionary, mapping colum types (INTEGER, VARCHAR, etc.) to converter + * functions, that convert the SQL value to the appropriate Python value. + * The key is uppercase. + */ +extern PyObject* converters; + +extern int _enable_callback_tracebacks; +extern int pysqlite_BaseTypeAdapted; + +#define PARSE_DECLTYPES 1 +#define PARSE_COLNAMES 2 +#endif diff --git a/src/prepare_protocol.c b/src/prepare_protocol.c new file mode 100644 index 0000000..9ed25e1 --- /dev/null +++ b/src/prepare_protocol.c @@ -0,0 +1,84 @@ +/* prepare_protocol.c - the protocol for preparing values for SQLite + * + * Copyright (C) 2005-2010 Gerhard Häring <gh@ghaering.de> + * + * This file is part of pysqlite. + * + * This software is provided 'as-is', without any express or implied + * warranty.  In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + *    claim that you wrote the original software. If you use this software + *    in a product, an acknowledgment in the product documentation would be + *    appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + *    misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include "sqlitecompat.h" +#include "prepare_protocol.h" + +int pysqlite_prepare_protocol_init(pysqlite_PrepareProtocol* self, PyObject* args, PyObject* kwargs) +{ +    return 0; +} + +void pysqlite_prepare_protocol_dealloc(pysqlite_PrepareProtocol* self) +{ +    Py_TYPE(self)->tp_free((PyObject*)self); +} + +PyTypeObject pysqlite_PrepareProtocolType= { +        PyVarObject_HEAD_INIT(NULL, 0) +        MODULE_NAME ".PrepareProtocol",                 /* tp_name */ +        sizeof(pysqlite_PrepareProtocol),               /* tp_basicsize */ +        0,                                              /* tp_itemsize */ +        (destructor)pysqlite_prepare_protocol_dealloc,  /* tp_dealloc */ +        0,                                              /* tp_print */ +        0,                                              /* tp_getattr */ +        0,                                              /* tp_setattr */ +        0,                                              /* tp_compare */ +        0,                                              /* tp_repr */ +        0,                                              /* tp_as_number */ +        0,                                              /* tp_as_sequence */ +        0,                                              /* tp_as_mapping */ +        0,                                              /* tp_hash */ +        0,                                              /* tp_call */ +        0,                                              /* tp_str */ +        0,                                              /* tp_getattro */ +        0,                                              /* tp_setattro */ +        0,                                              /* tp_as_buffer */ +        Py_TPFLAGS_DEFAULT,                             /* tp_flags */ +        0,                                              /* tp_doc */ +        0,                                              /* tp_traverse */ +        0,                                              /* tp_clear */ +        0,                                              /* tp_richcompare */ +        0,                                              /* tp_weaklistoffset */ +        0,                                              /* tp_iter */ +        0,                                              /* tp_iternext */ +        0,                                              /* tp_methods */ +        0,                                              /* tp_members */ +        0,                                              /* tp_getset */ +        0,                                              /* tp_base */ +        0,                                              /* tp_dict */ +        0,                                              /* tp_descr_get */ +        0,                                              /* tp_descr_set */ +        0,                                              /* tp_dictoffset */ +        (initproc)pysqlite_prepare_protocol_init,       /* tp_init */ +        0,                                              /* tp_alloc */ +        0,                                              /* tp_new */ +        0                                               /* tp_free */ +}; + +extern int pysqlite_prepare_protocol_setup_types(void) +{ +    pysqlite_PrepareProtocolType.tp_new = PyType_GenericNew; +    Py_TYPE(&pysqlite_PrepareProtocolType)= &PyType_Type; +    return PyType_Ready(&pysqlite_PrepareProtocolType); +} diff --git a/src/prepare_protocol.h b/src/prepare_protocol.h new file mode 100644 index 0000000..1cdf708 --- /dev/null +++ b/src/prepare_protocol.h @@ -0,0 +1,41 @@ +/* prepare_protocol.h - the protocol for preparing values for SQLite + * + * Copyright (C) 2005-2010 Gerhard Häring <gh@ghaering.de> + * + * This file is part of pysqlite. + * + * This software is provided 'as-is', without any express or implied + * warranty.  In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + *    claim that you wrote the original software. If you use this software + *    in a product, an acknowledgment in the product documentation would be + *    appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + *    misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#ifndef PYSQLITE_PREPARE_PROTOCOL_H +#define PYSQLITE_PREPARE_PROTOCOL_H +#include "Python.h" + +typedef struct +{ +    PyObject_HEAD +} pysqlite_PrepareProtocol; + +extern PyTypeObject pysqlite_PrepareProtocolType; + +int pysqlite_prepare_protocol_init(pysqlite_PrepareProtocol* self, PyObject* args, PyObject* kwargs); +void pysqlite_prepare_protocol_dealloc(pysqlite_PrepareProtocol* self); + +int pysqlite_prepare_protocol_setup_types(void); + +#define UNKNOWN (-1) +#endif diff --git a/src/row.c b/src/row.c new file mode 100644 index 0000000..480b482 --- /dev/null +++ b/src/row.c @@ -0,0 +1,256 @@ +/* row.c - an enhanced tuple for database rows + * + * Copyright (C) 2005-2010 Gerhard Häring <gh@ghaering.de> + * + * This file is part of pysqlite. + * + * This software is provided 'as-is', without any express or implied + * warranty.  In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + *    claim that you wrote the original software. If you use this software + *    in a product, an acknowledgment in the product documentation would be + *    appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + *    misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include "row.h" +#include "cursor.h" +#include "sqlitecompat.h" + +void pysqlite_row_dealloc(pysqlite_Row* self) +{ +    Py_XDECREF(self->data); +    Py_XDECREF(self->description); + +    Py_TYPE(self)->tp_free((PyObject*)self); +} + +int pysqlite_row_init(pysqlite_Row* self, PyObject* args, PyObject* kwargs) +{ +    PyObject* data; +    pysqlite_Cursor* cursor; + +    self->data = 0; +    self->description = 0; + +    if (!PyArg_ParseTuple(args, "OO", &cursor, &data)) { +        return -1; +    } + +    if (!PyObject_IsInstance((PyObject*)cursor, (PyObject*)&pysqlite_CursorType)) { +        PyErr_SetString(PyExc_TypeError, "instance of cursor required for first argument"); +        return -1; +    } + +    if (!PyTuple_Check(data)) { +        PyErr_SetString(PyExc_TypeError, "tuple required for second argument"); +        return -1; +    } + +    Py_INCREF(data); +    self->data = data; + +    Py_INCREF(cursor->description); +    self->description = cursor->description; + +    return 0; +} + +PyObject* pysqlite_row_subscript(pysqlite_Row* self, PyObject* idx) +{ +    long _idx; +    char* key; +    int nitems, i; +    char* compare_key; + +    char* p1; +    char* p2; + +    PyObject* item; + +    if (PyInt_Check(idx)) { +        _idx = PyInt_AsLong(idx); +        item = PyTuple_GetItem(self->data, _idx); +        Py_XINCREF(item); +        return item; +    } else if (PyLong_Check(idx)) { +        _idx = PyLong_AsLong(idx); +        item = PyTuple_GetItem(self->data, _idx); +        Py_XINCREF(item); +        return item; +    } else if (PyString_Check(idx)) { +        key = PyString_AsString(idx); + +        nitems = PyTuple_Size(self->description); + +        for (i = 0; i < nitems; i++) { +            compare_key = PyString_AsString(PyTuple_GET_ITEM(PyTuple_GET_ITEM(self->description, i), 0)); +            if (!compare_key) { +                return NULL; +            } + +            p1 = key; +            p2 = compare_key; + +            while (1) { +                if ((*p1 == (char)0) || (*p2 == (char)0)) { +                    break; +                } + +                if ((*p1 | 0x20) != (*p2 | 0x20)) { +                    break; +                } + +                p1++; +                p2++; +            } + +            if ((*p1 == (char)0) && (*p2 == (char)0)) { +                /* found item */ +                item = PyTuple_GetItem(self->data, i); +                Py_INCREF(item); +                return item; +            } + +        } + +        PyErr_SetString(PyExc_IndexError, "No item with that key"); +        return NULL; +    } else if (PySlice_Check(idx)) { +        PyErr_SetString(PyExc_ValueError, "slices not implemented, yet"); +        return NULL; +    } else { +        PyErr_SetString(PyExc_IndexError, "Index must be int or string"); +        return NULL; +    } +} + +Py_ssize_t pysqlite_row_length(pysqlite_Row* self, PyObject* args, PyObject* kwargs) +{ +    return PyTuple_GET_SIZE(self->data); +} + +PyObject* pysqlite_row_keys(pysqlite_Row* self, PyObject* args, PyObject* kwargs) +{ +    PyObject* list; +    int nitems, i; + +    list = PyList_New(0); +    if (!list) { +        return NULL; +    } +    nitems = PyTuple_Size(self->description); + +    for (i = 0; i < nitems; i++) { +        if (PyList_Append(list, PyTuple_GET_ITEM(PyTuple_GET_ITEM(self->description, i), 0)) != 0) { +            Py_DECREF(list); +            return NULL; +        } +    } + +    return list; +} + +static int pysqlite_row_print(pysqlite_Row* self, FILE *fp, int flags) +{ +    return (&PyTuple_Type)->tp_print(self->data, fp, flags); +} + +static PyObject* pysqlite_iter(pysqlite_Row* self) +{ +    return PyObject_GetIter(self->data); +} + +static long pysqlite_row_hash(pysqlite_Row *self) +{ +    return PyObject_Hash(self->description) ^ PyObject_Hash(self->data); +} + +static PyObject* pysqlite_row_richcompare(pysqlite_Row *self, PyObject *_other, int opid) +{ +    if (opid != Py_EQ && opid != Py_NE) { +        Py_INCREF(Py_NotImplemented); +        return Py_NotImplemented; +    } +    if (PyType_IsSubtype(Py_TYPE(_other), &pysqlite_RowType)) { +        pysqlite_Row *other = (pysqlite_Row *)_other; +        PyObject *res = PyObject_RichCompare(self->description, other->description, opid); +        if ((opid == Py_EQ && res == Py_True) +            || (opid == Py_NE && res == Py_False)) { +            Py_DECREF(res); +            return PyObject_RichCompare(self->data, other->data, opid); +        } +    } +    Py_INCREF(Py_NotImplemented); +    return Py_NotImplemented; +} + +PyMappingMethods pysqlite_row_as_mapping = { +    /* mp_length        */ (lenfunc)pysqlite_row_length, +    /* mp_subscript     */ (binaryfunc)pysqlite_row_subscript, +    /* mp_ass_subscript */ (objobjargproc)0, +}; + +static PyMethodDef pysqlite_row_methods[] = { +    {"keys", (PyCFunction)pysqlite_row_keys, METH_NOARGS, +        PyDoc_STR("Returns the keys of the row.")}, +    {NULL, NULL} +}; + + +PyTypeObject pysqlite_RowType = { +        PyVarObject_HEAD_INIT(NULL, 0) +        MODULE_NAME ".Row",                             /* tp_name */ +        sizeof(pysqlite_Row),                           /* tp_basicsize */ +        0,                                              /* tp_itemsize */ +        (destructor)pysqlite_row_dealloc,               /* tp_dealloc */ +        (printfunc)pysqlite_row_print,                  /* tp_print */ +        0,                                              /* tp_getattr */ +        0,                                              /* tp_setattr */ +        0,                                              /* tp_compare */ +        0,                                              /* tp_repr */ +        0,                                              /* tp_as_number */ +        0,                                              /* tp_as_sequence */ +        0,                                              /* tp_as_mapping */ +        (hashfunc)pysqlite_row_hash,                    /* tp_hash */ +        0,                                              /* tp_call */ +        0,                                              /* tp_str */ +        0,                                              /* tp_getattro */ +        0,                                              /* tp_setattro */ +        0,                                              /* tp_as_buffer */ +        Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,         /* tp_flags */ +        0,                                              /* tp_doc */ +        (traverseproc)0,                                /* tp_traverse */ +        0,                                              /* tp_clear */ +        (richcmpfunc)pysqlite_row_richcompare,          /* tp_richcompare */ +        0,                                              /* tp_weaklistoffset */ +        (getiterfunc)pysqlite_iter,                     /* tp_iter */ +        0,                                              /* tp_iternext */ +        pysqlite_row_methods,                           /* tp_methods */ +        0,                                              /* tp_members */ +        0,                                              /* tp_getset */ +        0,                                              /* tp_base */ +        0,                                              /* tp_dict */ +        0,                                              /* tp_descr_get */ +        0,                                              /* tp_descr_set */ +        0,                                              /* tp_dictoffset */ +        (initproc)pysqlite_row_init,                    /* tp_init */ +        0,                                              /* tp_alloc */ +        0,                                              /* tp_new */ +        0                                               /* tp_free */ +}; + +extern int pysqlite_row_setup_types(void) +{ +    pysqlite_RowType.tp_new = PyType_GenericNew; +    pysqlite_RowType.tp_as_mapping = &pysqlite_row_as_mapping; +    return PyType_Ready(&pysqlite_RowType); +} diff --git a/src/row.h b/src/row.h new file mode 100644 index 0000000..dd9b0c3 --- /dev/null +++ b/src/row.h @@ -0,0 +1,39 @@ +/* row.h - an enhanced tuple for database rows + * + * Copyright (C) 2005-2010 Gerhard Häring <gh@ghaering.de> + * + * This file is part of pysqlite. + * + * This software is provided 'as-is', without any express or implied + * warranty.  In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + *    claim that you wrote the original software. If you use this software + *    in a product, an acknowledgment in the product documentation would be + *    appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + *    misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#ifndef PYSQLITE_ROW_H +#define PYSQLITE_ROW_H +#include "Python.h" + +typedef struct _Row +{ +    PyObject_HEAD +    PyObject* data; +    PyObject* description; +} pysqlite_Row; + +extern PyTypeObject pysqlite_RowType; + +int pysqlite_row_setup_types(void); + +#endif diff --git a/src/sqlitecompat.h b/src/sqlitecompat.h new file mode 100644 index 0000000..3408fc2 --- /dev/null +++ b/src/sqlitecompat.h @@ -0,0 +1,63 @@ +/* sqlitecompat.h - compatibility macros + * + * Copyright (C) 2006-2010 Gerhard Häring <gh@ghaering.de> + * + * This file is part of pysqlite. + * + * This software is provided 'as-is', without any express or implied + * warranty.  In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + *    claim that you wrote the original software. If you use this software + *    in a product, an acknowledgment in the product documentation would be + *    appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + *    misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include "Python.h" + +#ifndef PYSQLITE_COMPAT_H +#define PYSQLITE_COMPAT_H + +/* define Py_ssize_t for pre-2.5 versions of Python */ + +#if PY_VERSION_HEX < 0x02050000 +typedef int Py_ssize_t; +typedef int (*lenfunc)(PyObject*); +#endif + + +/* define PyDict_CheckExact for pre-2.4 versions of Python */ +#ifndef PyDict_CheckExact +#define PyDict_CheckExact(op) ((op)->ob_type == &PyDict_Type) +#endif + +/* define Py_CLEAR for pre-2.4 versions of Python */ +#ifndef Py_CLEAR +#define Py_CLEAR(op)				\ +        do {                            	\ +                if (op) {			\ +                        PyObject *tmp = (PyObject *)(op);	\ +                        (op) = NULL;		\ +                        Py_DECREF(tmp);		\ +                }				\ +        } while (0) +#endif + +#ifndef PyVarObject_HEAD_INIT +#define PyVarObject_HEAD_INIT(type, size) \ +    PyObject_HEAD_INIT(type) size, +#endif + +#ifndef Py_TYPE +#define Py_TYPE(ob) ((ob)->ob_type) +#endif + +#endif diff --git a/src/statement.c b/src/statement.c new file mode 100644 index 0000000..9d08f2b --- /dev/null +++ b/src/statement.c @@ -0,0 +1,543 @@ +/* statement.c - the statement type + * + * Copyright (C) 2005-2010 Gerhard Häring <gh@ghaering.de> + * + * This file is part of pysqlite. + * + * This software is provided 'as-is', without any express or implied + * warranty.  In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + *    claim that you wrote the original software. If you use this software + *    in a product, an acknowledgment in the product documentation would be + *    appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + *    misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include "statement.h" +#include "cursor.h" +#include "connection.h" +#include "microprotocols.h" +#include "prepare_protocol.h" +#include "sqlitecompat.h" + +/* prototypes */ +static int pysqlite_check_remaining_sql(const char* tail); + +typedef enum { +    LINECOMMENT_1, +    IN_LINECOMMENT, +    COMMENTSTART_1, +    IN_COMMENT, +    COMMENTEND_1, +    NORMAL +} parse_remaining_sql_state; + +typedef enum { +    TYPE_INT, +    TYPE_LONG, +    TYPE_FLOAT, +    TYPE_STRING, +    TYPE_UNICODE, +    TYPE_BUFFER, +    TYPE_UNKNOWN +} parameter_type; + +int pysqlite_statement_create(pysqlite_Statement* self, pysqlite_Connection* connection, PyObject* sql) +{ +    const char* tail; +    int rc; +    PyObject* sql_str; +    char* sql_cstr; + +    self->st = NULL; +    self->in_use = 0; + +    if (PyString_Check(sql)) { +        sql_str = sql; +        Py_INCREF(sql_str); +    } else if (PyUnicode_Check(sql)) { +        sql_str = PyUnicode_AsUTF8String(sql); +        if (!sql_str) { +            rc = PYSQLITE_SQL_WRONG_TYPE; +            return rc; +        } +    } else { +        rc = PYSQLITE_SQL_WRONG_TYPE; +        return rc; +    } + +    self->in_weakreflist = NULL; +    self->sql = sql_str; + +    sql_cstr = PyString_AsString(sql_str); + +    Py_BEGIN_ALLOW_THREADS +    rc = sqlite3_prepare(connection->db, +                         sql_cstr, +                         -1, +                         &self->st, +                         &tail); +    Py_END_ALLOW_THREADS + +    self->db = connection->db; + +    if (rc == SQLITE_OK && pysqlite_check_remaining_sql(tail)) { +        (void)sqlite3_finalize(self->st); +        self->st = NULL; +        rc = PYSQLITE_TOO_MUCH_SQL; +    } + +    return rc; +} + +int pysqlite_statement_bind_parameter(pysqlite_Statement* self, int pos, PyObject* parameter, int allow_8bit_chars) +{ +    int rc = SQLITE_OK; +    long longval; +    PY_LONG_LONG longlongval; +    const char* buffer; +    char* string; +    Py_ssize_t buflen; +    PyObject* stringval; +    parameter_type paramtype; +    char* c; + +    if (parameter == Py_None) { +        rc = sqlite3_bind_null(self->st, pos); +        goto final; +    } + +    if (PyInt_CheckExact(parameter)) { +        paramtype = TYPE_INT; +    } else if (PyLong_CheckExact(parameter)) { +        paramtype = TYPE_LONG; +    } else if (PyFloat_CheckExact(parameter)) { +        paramtype = TYPE_FLOAT; +    } else if (PyString_CheckExact(parameter)) { +        paramtype = TYPE_STRING; +    } else if (PyUnicode_CheckExact(parameter)) { +        paramtype = TYPE_UNICODE; +    } else if (PyBuffer_Check(parameter)) { +        paramtype = TYPE_BUFFER; +    } else if (PyInt_Check(parameter)) { +        paramtype = TYPE_INT; +    } else if (PyLong_Check(parameter)) { +        paramtype = TYPE_LONG; +    } else if (PyFloat_Check(parameter)) { +        paramtype = TYPE_FLOAT; +    } else if (PyString_Check(parameter)) { +        paramtype = TYPE_STRING; +    } else if (PyUnicode_Check(parameter)) { +        paramtype = TYPE_UNICODE; +    } else { +        paramtype = TYPE_UNKNOWN; +    } + +    if (paramtype == TYPE_STRING && !allow_8bit_chars) { +        string = PyString_AS_STRING(parameter); +        for (c = string; *c != 0; c++) { +            if (*c & 0x80) { +                PyErr_SetString(pysqlite_ProgrammingError, "You must not use 8-bit bytestrings unless you use a text_factory that can interpret 8-bit bytestrings (like text_factory = str). It is highly recommended that you instead just switch your application to Unicode strings."); +                rc = -1; +                goto final; +            } +        } +    } + +    switch (paramtype) { +        case TYPE_INT: +            longval = PyInt_AsLong(parameter); +            rc = sqlite3_bind_int64(self->st, pos, (sqlite_int64)longval); +            break; +        case TYPE_LONG: +            longlongval = PyLong_AsLongLong(parameter); +            /* in the overflow error case, longlongval is -1, and an exception is set */ +            rc = sqlite3_bind_int64(self->st, pos, (sqlite_int64)longlongval); +            break; +        case TYPE_FLOAT: +            rc = sqlite3_bind_double(self->st, pos, PyFloat_AsDouble(parameter)); +            break; +        case TYPE_STRING: +            string = PyString_AS_STRING(parameter); +            rc = sqlite3_bind_text(self->st, pos, string, -1, SQLITE_TRANSIENT); +            break; +        case TYPE_UNICODE: +            stringval = PyUnicode_AsUTF8String(parameter); +            string = PyString_AsString(stringval); +            rc = sqlite3_bind_text(self->st, pos, string, -1, SQLITE_TRANSIENT); +            Py_DECREF(stringval); +            break; +        case TYPE_BUFFER: +            if (PyObject_AsCharBuffer(parameter, &buffer, &buflen) == 0) { +                rc = sqlite3_bind_blob(self->st, pos, buffer, buflen, SQLITE_TRANSIENT); +            } else { +                PyErr_SetString(PyExc_ValueError, "could not convert BLOB to buffer"); +                rc = -1; +            } +            break; +        case TYPE_UNKNOWN: +            rc = -1; +    } + +final: +    return rc; +} + +/* returns 0 if the object is one of Python's internal ones that don't need to be adapted */ +static int _need_adapt(PyObject* obj) +{ +    if (pysqlite_BaseTypeAdapted) { +        return 1; +    } + +    if (PyInt_CheckExact(obj) || PyLong_CheckExact(obj)  +            || PyFloat_CheckExact(obj) || PyString_CheckExact(obj) +            || PyUnicode_CheckExact(obj) || PyBuffer_Check(obj)) { +        return 0; +    } else { +        return 1; +    } +} + +void pysqlite_statement_bind_parameters(pysqlite_Statement* self, PyObject* parameters, int allow_8bit_chars) +{ +    PyObject* current_param; +    PyObject* adapted; +    const char* binding_name; +    int i; +    int rc; +    int num_params_needed; +    int num_params; + +    Py_BEGIN_ALLOW_THREADS +    num_params_needed = sqlite3_bind_parameter_count(self->st); +    Py_END_ALLOW_THREADS + +    if (PyTuple_CheckExact(parameters) || PyList_CheckExact(parameters) || (!PyDict_Check(parameters) && PySequence_Check(parameters))) { +        /* parameters passed as sequence */ +        if (PyTuple_CheckExact(parameters)) { +            num_params = PyTuple_GET_SIZE(parameters); +        } else if (PyList_CheckExact(parameters)) { +            num_params = PyList_GET_SIZE(parameters); +        } else { +            num_params = PySequence_Size(parameters); +        } +        if (num_params != num_params_needed) { +            PyErr_Format(pysqlite_ProgrammingError, "Incorrect number of bindings supplied. The current statement uses %d, and there are %d supplied.", +                         num_params_needed, num_params); +            return; +        } +        for (i = 0; i < num_params; i++) { +            if (PyTuple_CheckExact(parameters)) { +                current_param = PyTuple_GET_ITEM(parameters, i); +                Py_XINCREF(current_param); +            } else if (PyList_CheckExact(parameters)) { +                current_param = PyList_GET_ITEM(parameters, i); +                Py_XINCREF(current_param); +            } else { +                current_param = PySequence_GetItem(parameters, i); +            } +            if (!current_param) { +                return; +            } + +            if (!_need_adapt(current_param)) { +                adapted = current_param; +            } else { +                adapted = pysqlite_microprotocols_adapt(current_param, (PyObject*)&pysqlite_PrepareProtocolType, NULL); +                if (adapted) { +                    Py_DECREF(current_param); +                } else { +                    PyErr_Clear(); +                    adapted = current_param; +                } +            } + +            rc = pysqlite_statement_bind_parameter(self, i + 1, adapted, allow_8bit_chars); +            Py_DECREF(adapted); + +            if (rc != SQLITE_OK) { +                if (!PyErr_Occurred()) { +                    PyErr_Format(pysqlite_InterfaceError, "Error binding parameter %d - probably unsupported type.", i); +                } +                return; +            } +        } +    } else if (PyDict_Check(parameters)) { +        /* parameters passed as dictionary */ +        for (i = 1; i <= num_params_needed; i++) { +            Py_BEGIN_ALLOW_THREADS +            binding_name = sqlite3_bind_parameter_name(self->st, i); +            Py_END_ALLOW_THREADS +            if (!binding_name) { +                PyErr_Format(pysqlite_ProgrammingError, "Binding %d has no name, but you supplied a dictionary (which has only names).", i); +                return; +            } + +            binding_name++; /* skip first char (the colon) */ +            if (PyDict_CheckExact(parameters)) { +                current_param = PyDict_GetItemString(parameters, binding_name); +                Py_XINCREF(current_param); +            } else { +                current_param = PyMapping_GetItemString(parameters, (char*)binding_name); +            } +            if (!current_param) { +                PyErr_Format(pysqlite_ProgrammingError, "You did not supply a value for binding %d.", i); +                return; +            } + +            if (!_need_adapt(current_param)) { +                adapted = current_param; +            } else { +                adapted = pysqlite_microprotocols_adapt(current_param, (PyObject*)&pysqlite_PrepareProtocolType, NULL); +                if (adapted) { +                    Py_DECREF(current_param); +                } else { +                    PyErr_Clear(); +                    adapted = current_param; +                } +            } + +            rc = pysqlite_statement_bind_parameter(self, i, adapted, allow_8bit_chars); +            Py_DECREF(adapted); + +            if (rc != SQLITE_OK) { +                if (!PyErr_Occurred()) { +                    PyErr_Format(pysqlite_InterfaceError, "Error binding parameter :%s - probably unsupported type.", binding_name); +                } +                return; +           } +        } +    } else { +        PyErr_SetString(PyExc_ValueError, "parameters are of unsupported type"); +    } +} + +int pysqlite_statement_recompile(pysqlite_Statement* self, PyObject* params) +{ +    const char* tail; +    int rc; +    char* sql_cstr; +    sqlite3_stmt* new_st; + +    sql_cstr = PyString_AsString(self->sql); + +    Py_BEGIN_ALLOW_THREADS +    rc = sqlite3_prepare(self->db, +                         sql_cstr, +                         -1, +                         &new_st, +                         &tail); +    Py_END_ALLOW_THREADS + +    if (rc == SQLITE_OK) { +        /* The efficient sqlite3_transfer_bindings is only available in SQLite +         * version 3.2.2 or later. For older SQLite releases, that might not +         * even define SQLITE_VERSION_NUMBER, we do it the manual way. +         */ +        #ifdef SQLITE_VERSION_NUMBER +        #if SQLITE_VERSION_NUMBER >= 3002002 +        /* The check for the number of parameters is necessary to not trigger a +         * bug in certain SQLite versions (experienced in 3.2.8 and 3.3.4). */ +        if (sqlite3_bind_parameter_count(self->st) > 0) { +            (void)sqlite3_transfer_bindings(self->st, new_st); +        } +        #endif +        #else +        statement_bind_parameters(self, params); +        #endif + +        (void)sqlite3_finalize(self->st); +        self->st = new_st; +    } + +    return rc; +} + +int pysqlite_statement_finalize(pysqlite_Statement* self) +{ +    int rc; + +    rc = SQLITE_OK; +    if (self->st) { +        Py_BEGIN_ALLOW_THREADS +        rc = sqlite3_finalize(self->st); +        Py_END_ALLOW_THREADS +        self->st = NULL; +    } + +    self->in_use = 0; + +    return rc; +} + +int pysqlite_statement_reset(pysqlite_Statement* self) +{ +    int rc; + +    rc = SQLITE_OK; + +    if (self->in_use && self->st) { +        Py_BEGIN_ALLOW_THREADS +        rc = sqlite3_reset(self->st); +        Py_END_ALLOW_THREADS + +        if (rc == SQLITE_OK) { +            self->in_use = 0; +        } +    } + +    return rc; +} + +void pysqlite_statement_mark_dirty(pysqlite_Statement* self) +{ +    self->in_use = 1; +} + +void pysqlite_statement_dealloc(pysqlite_Statement* self) +{ +    int rc; + +    if (self->st) { +        Py_BEGIN_ALLOW_THREADS +        rc = sqlite3_finalize(self->st); +        Py_END_ALLOW_THREADS +    } + +    self->st = NULL; + +    Py_XDECREF(self->sql); + +    if (self->in_weakreflist != NULL) { +        PyObject_ClearWeakRefs((PyObject*)self); +    } + +    Py_TYPE(self)->tp_free((PyObject*)self); +} + +/* + * Checks if there is anything left in an SQL string after SQLite compiled it. + * This is used to check if somebody tried to execute more than one SQL command + * with one execute()/executemany() command, which the DB-API and we don't + * allow. + * + * Returns 1 if there is more left than should be. 0 if ok. + */ +static int pysqlite_check_remaining_sql(const char* tail) +{ +    const char* pos = tail; + +    parse_remaining_sql_state state = NORMAL; + +    for (;;) { +        switch (*pos) { +            case 0: +                return 0; +            case '-': +                if (state == NORMAL) { +                    state  = LINECOMMENT_1; +                } else if (state == LINECOMMENT_1) { +                    state = IN_LINECOMMENT; +                } +                break; +            case ' ': +            case '\t': +                break; +            case '\n': +            case 13: +                if (state == IN_LINECOMMENT) { +                    state = NORMAL; +                } +                break; +            case '/': +                if (state == NORMAL) { +                    state = COMMENTSTART_1; +                } else if (state == COMMENTEND_1) { +                    state = NORMAL; +                } else if (state == COMMENTSTART_1) { +                    return 1; +                } +                break; +            case '*': +                if (state == NORMAL) { +                    return 1; +                } else if (state == LINECOMMENT_1) { +                    return 1; +                } else if (state == COMMENTSTART_1) { +                    state = IN_COMMENT; +                } else if (state == IN_COMMENT) { +                    state = COMMENTEND_1; +                } +                break; +            default: +                if (state == COMMENTEND_1) { +                    state = IN_COMMENT; +                } else if (state == IN_LINECOMMENT) { +                } else if (state == IN_COMMENT) { +                } else { +                    return 1; +                } +        } + +        pos++; +    } + +    return 0; +} + +PyTypeObject pysqlite_StatementType = { +        PyVarObject_HEAD_INIT(NULL, 0) +        MODULE_NAME ".Statement",                       /* tp_name */ +        sizeof(pysqlite_Statement),                     /* tp_basicsize */ +        0,                                              /* tp_itemsize */ +        (destructor)pysqlite_statement_dealloc,         /* tp_dealloc */ +        0,                                              /* tp_print */ +        0,                                              /* tp_getattr */ +        0,                                              /* tp_setattr */ +        0,                                              /* tp_compare */ +        0,                                              /* tp_repr */ +        0,                                              /* tp_as_number */ +        0,                                              /* tp_as_sequence */ +        0,                                              /* tp_as_mapping */ +        0,                                              /* tp_hash */ +        0,                                              /* tp_call */ +        0,                                              /* tp_str */ +        0,                                              /* tp_getattro */ +        0,                                              /* tp_setattro */ +        0,                                              /* tp_as_buffer */ +        Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_WEAKREFS,  /* tp_flags */ +        0,                                              /* tp_doc */ +        0,                                              /* tp_traverse */ +        0,                                              /* tp_clear */ +        0,                                              /* tp_richcompare */ +        offsetof(pysqlite_Statement, in_weakreflist),   /* tp_weaklistoffset */ +        0,                                              /* tp_iter */ +        0,                                              /* tp_iternext */ +        0,                                              /* tp_methods */ +        0,                                              /* tp_members */ +        0,                                              /* tp_getset */ +        0,                                              /* tp_base */ +        0,                                              /* tp_dict */ +        0,                                              /* tp_descr_get */ +        0,                                              /* tp_descr_set */ +        0,                                              /* tp_dictoffset */ +        (initproc)0,                                    /* tp_init */ +        0,                                              /* tp_alloc */ +        0,                                              /* tp_new */ +        0                                               /* tp_free */ +}; + +extern int pysqlite_statement_setup_types(void) +{ +    pysqlite_StatementType.tp_new = PyType_GenericNew; +    return PyType_Ready(&pysqlite_StatementType); +} diff --git a/src/statement.h b/src/statement.h new file mode 100644 index 0000000..05fd5ff --- /dev/null +++ b/src/statement.h @@ -0,0 +1,59 @@ +/* statement.h - definitions for the statement type + * + * Copyright (C) 2005-2010 Gerhard Häring <gh@ghaering.de> + * + * This file is part of pysqlite. + * + * This software is provided 'as-is', without any express or implied + * warranty.  In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + *    claim that you wrote the original software. If you use this software + *    in a product, an acknowledgment in the product documentation would be + *    appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + *    misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#ifndef PYSQLITE_STATEMENT_H +#define PYSQLITE_STATEMENT_H +#include "Python.h" + +#include "connection.h" +#include "sqlite3.h" + +#define PYSQLITE_TOO_MUCH_SQL (-100) +#define PYSQLITE_SQL_WRONG_TYPE (-101) + +typedef struct +{ +    PyObject_HEAD +    sqlite3* db; +    sqlite3_stmt* st; +    PyObject* sql; +    int in_use; +    PyObject* in_weakreflist; /* List of weak references */ +} pysqlite_Statement; + +extern PyTypeObject pysqlite_StatementType; + +int pysqlite_statement_create(pysqlite_Statement* self, pysqlite_Connection* connection, PyObject* sql); +void pysqlite_statement_dealloc(pysqlite_Statement* self); + +int pysqlite_statement_bind_parameter(pysqlite_Statement* self, int pos, PyObject* parameter, int allow_8bit_chars); +void pysqlite_statement_bind_parameters(pysqlite_Statement* self, PyObject* parameters, int allow_8bit_chars); + +int pysqlite_statement_recompile(pysqlite_Statement* self, PyObject* parameters); +int pysqlite_statement_finalize(pysqlite_Statement* self); +int pysqlite_statement_reset(pysqlite_Statement* self); +void pysqlite_statement_mark_dirty(pysqlite_Statement* self); + +int pysqlite_statement_setup_types(void); + +#endif diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..6b57b76 --- /dev/null +++ b/src/util.c @@ -0,0 +1,106 @@ +/* util.c - various utility functions + * + * Copyright (C) 2005-2010 Gerhard Häring <gh@ghaering.de> + * + * This file is part of pysqlite. + * + * This software is provided 'as-is', without any express or implied + * warranty.  In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + *    claim that you wrote the original software. If you use this software + *    in a product, an acknowledgment in the product documentation would be + *    appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + *    misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include "module.h" +#include "connection.h" + +int pysqlite_step(sqlite3_stmt* statement, pysqlite_Connection* connection) +{ +    int rc; + +    if (statement == NULL) { +        /* this is a workaround for SQLite 3.5 and later. it now apparently +         * returns NULL for "no-operation" statements */ +        rc = SQLITE_OK; +    } else { +        Py_BEGIN_ALLOW_THREADS +        rc = sqlite3_step(statement); +        Py_END_ALLOW_THREADS +    } + +    return rc; +} + +/** + * Checks the SQLite error code and sets the appropriate DB-API exception. + * Returns the error code (0 means no error occurred). + */ +int _pysqlite_seterror(sqlite3* db, sqlite3_stmt* st) +{ +    int errorcode; + +    /* SQLite often doesn't report anything useful, unless you reset the statement first */ +    if (st != NULL) { +        (void)sqlite3_reset(st); +    } + +    errorcode = sqlite3_errcode(db); + +    switch (errorcode) +    { +        case SQLITE_OK: +            PyErr_Clear(); +            break; +        case SQLITE_INTERNAL: +        case SQLITE_NOTFOUND: +            PyErr_SetString(pysqlite_InternalError, sqlite3_errmsg(db)); +            break; +        case SQLITE_NOMEM: +            (void)PyErr_NoMemory(); +            break; +        case SQLITE_ERROR: +        case SQLITE_PERM: +        case SQLITE_ABORT: +        case SQLITE_BUSY: +        case SQLITE_LOCKED: +        case SQLITE_READONLY: +        case SQLITE_INTERRUPT: +        case SQLITE_IOERR: +        case SQLITE_FULL: +        case SQLITE_CANTOPEN: +        case SQLITE_PROTOCOL: +        case SQLITE_EMPTY: +        case SQLITE_SCHEMA: +            PyErr_SetString(pysqlite_OperationalError, sqlite3_errmsg(db)); +            break; +        case SQLITE_CORRUPT: +            PyErr_SetString(pysqlite_DatabaseError, sqlite3_errmsg(db)); +            break; +        case SQLITE_TOOBIG: +            PyErr_SetString(pysqlite_DataError, sqlite3_errmsg(db)); +            break; +        case SQLITE_CONSTRAINT: +        case SQLITE_MISMATCH: +            PyErr_SetString(pysqlite_IntegrityError, sqlite3_errmsg(db)); +            break; +        case SQLITE_MISUSE: +            PyErr_SetString(pysqlite_ProgrammingError, sqlite3_errmsg(db)); +            break; +        default: +            PyErr_SetString(pysqlite_DatabaseError, sqlite3_errmsg(db)); +            break; +    } + +    return errorcode; +} + diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..4269003 --- /dev/null +++ b/src/util.h @@ -0,0 +1,38 @@ +/* util.h - various utility functions + * + * Copyright (C) 2005-2010 Gerhard Häring <gh@ghaering.de> + * + * This file is part of pysqlite. + * + * This software is provided 'as-is', without any express or implied + * warranty.  In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + *    claim that you wrote the original software. If you use this software + *    in a product, an acknowledgment in the product documentation would be + *    appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + *    misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#ifndef PYSQLITE_UTIL_H +#define PYSQLITE_UTIL_H +#include "Python.h" +#include "pythread.h" +#include "sqlite3.h" +#include "connection.h" + +int pysqlite_step(sqlite3_stmt* statement, pysqlite_Connection* connection); + +/** + * Checks the SQLite error code and sets the appropriate DB-API exception. + * Returns the error code (0 means no error occurred). + */ +int _pysqlite_seterror(sqlite3* db, sqlite3_stmt* st); +#endif | 
