From 3e99c0ad649de0393d9a8af17f34d9d1f55f4ab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerhard=20H=C3=A4ring?= Date: Sun, 23 Apr 2006 15:24:26 +0000 Subject: Updated the sqlite3 module to the external pysqlite 2.2.2 version. --- Lib/sqlite3/dbapi2.py | 68 ++++++------- Lib/sqlite3/test/hooks.py | 4 +- Lib/sqlite3/test/regression.py | 27 +++++- Lib/sqlite3/test/userfunctions.py | 14 +++ Lib/test/test_sqlite.py | 5 +- Modules/_sqlite/adapters.c | 40 -------- Modules/_sqlite/adapters.h | 33 ------- Modules/_sqlite/cache.c | 23 ++++- Modules/_sqlite/cache.h | 14 ++- Modules/_sqlite/connection.c | 197 +++++++++++++++++++++++++------------- Modules/_sqlite/connection.h | 25 ++++- Modules/_sqlite/converters.c | 40 -------- Modules/_sqlite/converters.h | 33 ------- Modules/_sqlite/cursor.c | 58 ++++++----- Modules/_sqlite/cursor.h | 2 +- Modules/_sqlite/microprotocols.h | 4 +- Modules/_sqlite/module.c | 8 +- Modules/_sqlite/module.h | 4 +- Modules/_sqlite/statement.c | 9 +- Modules/_sqlite/statement.h | 1 + PCbuild/_sqlite3.vcproj | 6 -- setup.py | 10 +- 22 files changed, 311 insertions(+), 314 deletions(-) delete mode 100644 Modules/_sqlite/adapters.c delete mode 100644 Modules/_sqlite/adapters.h delete mode 100644 Modules/_sqlite/converters.c delete mode 100644 Modules/_sqlite/converters.h diff --git a/Lib/sqlite3/dbapi2.py b/Lib/sqlite3/dbapi2.py index e0c8a84..665dbb2 100644 --- a/Lib/sqlite3/dbapi2.py +++ b/Lib/sqlite3/dbapi2.py @@ -22,6 +22,9 @@ # 3. This notice may not be removed or altered from any source distribution. import datetime +import time + +from _sqlite3 import * paramstyle = "qmark" @@ -29,10 +32,6 @@ threadsafety = 1 apilevel = "2.0" -from _sqlite3 import * - -import datetime, time - Date = datetime.date Time = datetime.time @@ -40,45 +39,50 @@ Time = datetime.time Timestamp = datetime.datetime def DateFromTicks(ticks): - return apply(Date,time.localtime(ticks)[:3]) + return apply(Date, time.localtime(ticks)[:3]) def TimeFromTicks(ticks): - return apply(Time,time.localtime(ticks)[3:6]) + return apply(Time, time.localtime(ticks)[3:6]) def TimestampFromTicks(ticks): - return apply(Timestamp,time.localtime(ticks)[:6]) + return apply(Timestamp, time.localtime(ticks)[:6]) -_major, _minor, _micro = version.split(".") -version_info = (int(_major), int(_minor), _micro) -_major, _minor, _micro = sqlite_version.split(".") -sqlite_version_info = (int(_major), int(_minor), _micro) +version_info = tuple([int(x) for x in version.split(".")]) +sqlite_version_info = tuple([int(x) for x in sqlite_version.split(".")]) Binary = buffer -def adapt_date(val): - return val.isoformat() +def register_adapters_and_converters(): + def adapt_date(val): + return val.isoformat() + + def adapt_datetime(val): + return val.isoformat(" ") + + def convert_date(val): + return datetime.date(*map(int, val.split("-"))) + + def convert_timestamp(val): + datepart, timepart = val.split(" ") + year, month, day = map(int, datepart.split("-")) + timepart_full = timepart.split(".") + hours, minutes, seconds = map(int, timepart_full[0].split(":")) + if len(timepart_full) == 2: + microseconds = int(float("0." + timepart_full[1]) * 1000000) + else: + microseconds = 0 -def adapt_datetime(val): - return val.isoformat(" ") + val = datetime.datetime(year, month, day, hours, minutes, seconds, microseconds) + return val -def convert_date(val): - return datetime.date(*map(int, val.split("-"))) -def convert_timestamp(val): - datepart, timepart = val.split(" ") - year, month, day = map(int, datepart.split("-")) - timepart_full = timepart.split(".") - hours, minutes, seconds = map(int, timepart_full[0].split(":")) - if len(timepart_full) == 2: - microseconds = int(float("0." + timepart_full[1]) * 1000000) - else: - microseconds = 0 + register_adapter(datetime.date, adapt_date) + register_adapter(datetime.datetime, adapt_datetime) + register_converter("date", convert_date) + register_converter("timestamp", convert_timestamp) - val = datetime.datetime(year, month, day, hours, minutes, seconds, microseconds) - return val +register_adapters_and_converters() +# Clean up namespace -register_adapter(datetime.date, adapt_date) -register_adapter(datetime.datetime, adapt_datetime) -register_converter("date", convert_date) -register_converter("timestamp", convert_timestamp) +del(register_adapters_and_converters) diff --git a/Lib/sqlite3/test/hooks.py b/Lib/sqlite3/test/hooks.py index 21f7b88..b10b3ef 100644 --- a/Lib/sqlite3/test/hooks.py +++ b/Lib/sqlite3/test/hooks.py @@ -22,7 +22,7 @@ # 3. This notice may not be removed or altered from any source distribution. import os, unittest -import pysqlite2.dbapi2 as sqlite +import sqlite3 as sqlite class CollationTests(unittest.TestCase): def setUp(self): @@ -72,7 +72,7 @@ class CollationTests(unittest.TestCase): result = con.execute(sql).fetchall() self.fail("should have raised an OperationalError") except sqlite.OperationalError, e: - self.failUnlessEqual(e.args[0], "no such collation sequence: mycoll") + self.failUnlessEqual(e.args[0].lower(), "no such collation sequence: mycoll") def CheckCollationRegisterTwice(self): """ diff --git a/Lib/sqlite3/test/regression.py b/Lib/sqlite3/test/regression.py index 648ada5..25e4b63 100644 --- a/Lib/sqlite3/test/regression.py +++ b/Lib/sqlite3/test/regression.py @@ -22,7 +22,7 @@ # 3. This notice may not be removed or altered from any source distribution. import unittest -import pysqlite2.dbapi2 as sqlite +import sqlite3 as sqlite class RegressionTests(unittest.TestCase): def setUp(self): @@ -36,6 +36,31 @@ class RegressionTests(unittest.TestCase): cur = self.con.cursor() cur.execute("pragma user_version") + def CheckPragmaSchemaVersion(self): + # This still crashed pysqlite <= 2.2.1 + con = sqlite.connect(":memory:", detect_types=sqlite.PARSE_COLNAMES) + try: + cur = self.con.cursor() + cur.execute("pragma schema_version") + finally: + cur.close() + con.close() + + def CheckStatementReset(self): + # pysqlite 2.1.0 to 2.2.0 have the problem that not all statements are + # reset before a rollback, but only those that are still in the + # statement cache. The others are not accessible from the connection object. + con = sqlite.connect(":memory:", cached_statements=5) + cursors = [con.cursor() for x in xrange(5)] + cursors[0].execute("create table test(x)") + for i in range(10): + cursors[0].executemany("insert into test(x) values (?)", [(x,) for x in xrange(10)]) + + for i in range(5): + cursors[i].execute(" " * i + "select x from test") + + con.rollback() + def suite(): regression_suite = unittest.makeSuite(RegressionTests, "Check") return unittest.TestSuite((regression_suite,)) diff --git a/Lib/sqlite3/test/userfunctions.py b/Lib/sqlite3/test/userfunctions.py index ff7db9c..78656e7 100644 --- a/Lib/sqlite3/test/userfunctions.py +++ b/Lib/sqlite3/test/userfunctions.py @@ -134,6 +134,13 @@ class FunctionTests(unittest.TestCase): def tearDown(self): self.con.close() + def CheckFuncErrorOnCreate(self): + try: + self.con.create_function("bla", -100, lambda x: 2*x) + self.fail("should have raised an OperationalError") + except sqlite.OperationalError: + pass + def CheckFuncRefCount(self): def getfunc(): def f(): @@ -251,6 +258,13 @@ class AggregateTests(unittest.TestCase): #self.con.close() pass + def CheckAggrErrorOnCreate(self): + try: + self.con.create_function("bla", -100, AggrSum) + self.fail("should have raised an OperationalError") + except sqlite.OperationalError: + pass + def CheckAggrNoStep(self): cur = self.con.cursor() cur.execute("select nostep(t) from test") diff --git a/Lib/test/test_sqlite.py b/Lib/test/test_sqlite.py index 1b1d0e5..f033772 100644 --- a/Lib/test/test_sqlite.py +++ b/Lib/test/test_sqlite.py @@ -6,11 +6,12 @@ try: except ImportError: raise TestSkipped('no sqlite available') from sqlite3.test import (dbapi, types, userfunctions, - factory, transactions) + factory, transactions, hooks, regression) def test_main(): run_unittest(dbapi.suite(), types.suite(), userfunctions.suite(), - factory.suite(), transactions.suite()) + factory.suite(), transactions.suite(), hooks.suite(), + regression.suite()) if __name__ == "__main__": test_main() diff --git a/Modules/_sqlite/adapters.c b/Modules/_sqlite/adapters.c deleted file mode 100644 index e6fde03..0000000 --- a/Modules/_sqlite/adapters.c +++ /dev/null @@ -1,40 +0,0 @@ -/* adapters.c - default adapters - * - * Copyright (C) 2005 Gerhard Häring - * - * 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 "util.h" -#include "module.h" -#include "adapters.h" - -/* dummy, will be implemented in a later version */ - -PyObject* adapt_date(PyObject* self, PyObject* args, PyObject* kwargs) -{ - Py_INCREF(Py_None); - return Py_None; -} - -PyObject* adapt_datetime(PyObject* self, PyObject* args, PyObject* kwargs) -{ - Py_INCREF(Py_None); - return Py_None; -} diff --git a/Modules/_sqlite/adapters.h b/Modules/_sqlite/adapters.h deleted file mode 100644 index d2e8479..0000000 --- a/Modules/_sqlite/adapters.h +++ /dev/null @@ -1,33 +0,0 @@ -/* adapters.h - default adapters - * - * Copyright (C) 2005 Gerhard Häring - * - * 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_ADAPTERS_H -#define PYSQLITE_ADAPTERS_H -#include "Python.h" -#include "pythread.h" -#include "sqlite3.h" - -PyObject* adapt_date(PyObject* self, PyObject* args, PyObject* kwargs); -PyObject* adapt_datetime(PyObject* self, PyObject* args, PyObject* kwargs); - -#endif diff --git a/Modules/_sqlite/cache.c b/Modules/_sqlite/cache.c index d102e97..6962695 100644 --- a/Modules/_sqlite/cache.c +++ b/Modules/_sqlite/cache.c @@ -22,6 +22,7 @@ */ #include "cache.h" +#include /* only used internally */ Node* new_node(PyObject* key, PyObject* data) @@ -60,11 +61,11 @@ int cache_init(Cache* self, PyObject* args, PyObject* kwargs) self->factory = NULL; - if (!PyArg_ParseTuple(args, "O|i", &factory, &size)) - { - return -1; + if (!PyArg_ParseTuple(args, "O|i", &factory, &size)) { + return -1; } + /* minimum cache size is 5 entries */ if (size < 5) { size = 5; } @@ -95,6 +96,7 @@ void cache_dealloc(Cache* self) return; } + /* iterate over all nodes and deallocate them */ node = self->first; while (node) { delete_node = node; @@ -119,7 +121,14 @@ PyObject* cache_get(Cache* self, PyObject* args) node = (Node*)PyDict_GetItem(self->mapping, key); if (node) { - node->count++; + /* 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; @@ -149,6 +158,10 @@ PyObject* cache_get(Cache* self, PyObject* args) 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; @@ -253,7 +266,7 @@ PyObject* cache_display(Cache* self, PyObject* args) static PyMethodDef cache_methods[] = { {"get", (PyCFunction)cache_get, METH_O, - PyDoc_STR("Gets an entry from the cache.")}, + PyDoc_STR("Gets an entry from the cache or calls the factory function to produce one.")}, {"display", (PyCFunction)cache_display, METH_NOARGS, PyDoc_STR("For debugging only.")}, {NULL, NULL} diff --git a/Modules/_sqlite/cache.h b/Modules/_sqlite/cache.h index 5cc16f3..1f13907 100644 --- a/Modules/_sqlite/cache.h +++ b/Modules/_sqlite/cache.h @@ -1,6 +1,6 @@ /* cache.h - definitions for the LRU cache * - * Copyright (C) 2004-2005 Gerhard Häring + * Copyright (C) 2004-2006 Gerhard Häring * * This file is part of pysqlite. * @@ -25,6 +25,10 @@ #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 _Node { PyObject_HEAD @@ -39,10 +43,18 @@ typedef struct { PyObject_HEAD int size; + + /* a dictionary mapping keys to Node entries */ PyObject* mapping; + + /* the factory callable */ PyObject* factory; + Node* first; 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; } Cache; diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 78aad37..64e43eb 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -56,6 +56,7 @@ int connection_init(Connection* self, PyObject* args, PyObject* kwargs) self->begin_statement = NULL; self->statement_cache = NULL; + self->statements = NULL; Py_INCREF(Py_None); self->row_factory = Py_None; @@ -74,6 +75,9 @@ int connection_init(Connection* self, PyObject* args, PyObject* kwargs) if (!isolation_level) { isolation_level = PyString_FromString(""); + if (!isolation_level) { + return -1; + } } else { Py_INCREF(isolation_level); } @@ -86,6 +90,12 @@ int connection_init(Connection* self, PyObject* args, PyObject* kwargs) return -1; } + self->statements = PyList_New(0); + if (!self->statements) { + return -1; + } + self->created_statements = 0; + /* 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 @@ -126,6 +136,7 @@ int connection_init(Connection* self, PyObject* args, PyObject* kwargs) return 0; } +/* Empty the entire statement cache of this connection */ void flush_statement_cache(Connection* self) { Node* node; @@ -147,15 +158,16 @@ void flush_statement_cache(Connection* self) void reset_all_statements(Connection* self) { - Node* node; - Statement* statement; - - node = self->statement_cache->first; - - while (node) { - statement = (Statement*)(node->data); - (void)statement_reset(statement); - node = node->next; + int i; + PyObject* weakref; + PyObject* statement; + + for (i = 0; i < PyList_Size(self->statements); i++) { + weakref = PyList_GetItem(self->statements, i); + statement = PyWeakref_GetObject(weakref); + if (statement != Py_None) { + (void)statement_reset((Statement*)statement); + } } } @@ -178,6 +190,7 @@ void connection_dealloc(Connection* self) Py_XDECREF(self->row_factory); Py_XDECREF(self->text_factory); Py_XDECREF(self->collations); + Py_XDECREF(self->statements); self->ob_type->tp_free((PyObject*)self); } @@ -391,7 +404,7 @@ void _set_result(sqlite3_context* context, PyObject* py_val) Py_ssize_t buflen; PyObject* stringval; - if (PyErr_Occurred()) { + if ((!py_val) || PyErr_Occurred()) { /* Errors in callbacks are ignored, and we return NULL */ PyErr_Clear(); sqlite3_result_null(context); @@ -399,21 +412,23 @@ void _set_result(sqlite3_context* context, PyObject* py_val) sqlite3_result_null(context); } else if (PyInt_Check(py_val)) { longval = PyInt_AsLong(py_val); - /* TODO: investigate what to do with range overflows - long vs. long long */ 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); } - 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); - sqlite3_result_text(context, PyString_AsString(stringval), -1, SQLITE_TRANSIENT); - Py_DECREF(stringval); + if (stringval) { + sqlite3_result_text(context, PyString_AsString(stringval), -1, SQLITE_TRANSIENT); + Py_DECREF(stringval); + } } else { /* TODO: raise error */ } @@ -450,6 +465,7 @@ PyObject* _build_py_params(sqlite3_context *context, int argc, sqlite3_value** a 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; } @@ -458,10 +474,12 @@ PyObject* _build_py_params(sqlite3_context *context, int argc, sqlite3_value** a buflen = sqlite3_value_bytes(cur_value); cur_py_value = PyBuffer_New(buflen); if (!cur_py_value) { - /* TODO: error */ + break; } if (PyObject_AsWriteBuffer(cur_py_value, &raw_buffer, &buflen)) { - /* TODO: error */ + Py_DECREF(cur_py_value); + cur_py_value = NULL; + break; } memcpy(raw_buffer, sqlite3_value_blob(cur_value), buflen); break; @@ -470,6 +488,12 @@ PyObject* _build_py_params(sqlite3_context *context, int argc, sqlite3_value** a 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); } @@ -481,8 +505,7 @@ void _func_callback(sqlite3_context* context, int argc, sqlite3_value** argv) { PyObject* args; PyObject* py_func; - PyObject* py_retval; - + PyObject* py_retval = NULL; PyGILState_STATE threadstate; @@ -491,9 +514,10 @@ void _func_callback(sqlite3_context* context, int argc, sqlite3_value** argv) py_func = (PyObject*)sqlite3_user_data(context); args = _build_py_params(context, argc, argv); - - py_retval = PyObject_CallObject(py_func, args); - Py_DECREF(args); + if (args) { + py_retval = PyObject_CallObject(py_func, args); + Py_DECREF(args); + } _set_result(context, py_retval); Py_XDECREF(py_retval); @@ -504,10 +528,10 @@ void _func_callback(sqlite3_context* context, int argc, sqlite3_value** argv) static void _step_callback(sqlite3_context *context, int argc, sqlite3_value** params) { PyObject* args; - PyObject* function_result; + PyObject* function_result = NULL; PyObject* aggregate_class; PyObject** aggregate_instance; - PyObject* stepmethod; + PyObject* stepmethod = NULL; PyGILState_STATE threadstate; @@ -520,44 +544,42 @@ static void _step_callback(sqlite3_context *context, int argc, sqlite3_value** p if (*aggregate_instance == 0) { *aggregate_instance = PyObject_CallFunction(aggregate_class, ""); - if (PyErr_Occurred()) - { + if (PyErr_Occurred()) { PyErr_Clear(); *aggregate_instance = 0; - PyGILState_Release(threadstate); - return; + goto error; } } stepmethod = PyObject_GetAttrString(*aggregate_instance, "step"); - if (!stepmethod) - { - PyGILState_Release(threadstate); - return; + if (!stepmethod) { + goto error; } args = _build_py_params(context, argc, params); + if (!args) { + goto error; + } function_result = PyObject_CallObject(stepmethod, args); Py_DECREF(args); - Py_DECREF(stepmethod); - if (function_result == NULL) { + if (!function_result) { PyErr_Clear(); - } else { - Py_DECREF(function_result); } +error: + Py_XDECREF(stepmethod); + Py_XDECREF(function_result); + PyGILState_Release(threadstate); } void _final_callback(sqlite3_context* context) { - PyObject* args; - PyObject* function_result; + PyObject* function_result = NULL; PyObject** aggregate_instance; PyObject* aggregate_class; - PyObject* finalizemethod; PyGILState_STATE threadstate; @@ -570,35 +592,56 @@ void _final_callback(sqlite3_context* context) /* this branch is executed if there was an exception in the aggregate's * __init__ */ - PyGILState_Release(threadstate); - return; + goto error; } - finalizemethod = PyObject_GetAttrString(*aggregate_instance, "finalize"); - - if (!finalizemethod) { - /* - PyErr_SetString(ProgrammingError, "finalize method missing"); - goto error; - */ + function_result = PyObject_CallMethod(*aggregate_instance, "finalize", ""); + if (!function_result) { + PyErr_Clear(); Py_INCREF(Py_None); function_result = Py_None; - } else { - args = PyTuple_New(0); - if (!args) - return; - function_result = PyObject_CallObject(finalizemethod, args); - Py_DECREF(args); - Py_DECREF(finalizemethod); } _set_result(context, function_result); + +error: Py_XDECREF(*aggregate_instance); Py_XDECREF(function_result); PyGILState_Release(threadstate); } +void _drop_unused_statement_references(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 (weakref != Py_None) { + if (PyList_Append(new_list, weakref) != 0) { + Py_DECREF(new_list); + return; + } + } + } + + Py_DECREF(self->statements); + self->statements = new_list; +} PyObject* connection_create_function(Connection* self, PyObject* args, PyObject* kwargs) { @@ -617,10 +660,16 @@ PyObject* connection_create_function(Connection* self, PyObject* args, PyObject* rc = sqlite3_create_function(self->db, name, narg, SQLITE_UTF8, (void*)func, _func_callback, NULL, NULL); - PyDict_SetItem(self->function_pinboard, func, Py_None); + if (rc != SQLITE_OK) { + /* Workaround for SQLite bug: no error code or string is available here */ + PyErr_SetString(OperationalError, "Error creating function"); + return NULL; + } else { + PyDict_SetItem(self->function_pinboard, func, Py_None); - Py_INCREF(Py_None); - return Py_None; + Py_INCREF(Py_None); + return Py_None; + } } PyObject* connection_create_aggregate(Connection* self, PyObject* args, PyObject* kwargs) @@ -639,7 +688,8 @@ PyObject* connection_create_aggregate(Connection* self, PyObject* args, PyObject rc = sqlite3_create_function(self->db, name, n_arg, SQLITE_UTF8, (void*)aggregate_class, 0, &_step_callback, &_final_callback); if (rc != SQLITE_OK) { - _seterror(self->db); + /* Workaround for SQLite bug: no error code or string is available here */ + PyErr_SetString(OperationalError, "Error creating aggregate"); return NULL; } else { PyDict_SetItem(self->function_pinboard, aggregate_class, Py_None); @@ -682,7 +732,6 @@ static PyObject* connection_get_total_changes(Connection* self, void* unused) static int connection_set_isolation_level(Connection* self, PyObject* isolation_level) { - PyObject* empty; PyObject* res; PyObject* begin_statement; @@ -697,15 +746,10 @@ static int connection_set_isolation_level(Connection* self, PyObject* isolation_ Py_INCREF(Py_None); self->isolation_level = Py_None; - empty = PyTuple_New(0); - if (!empty) { - return -1; - } - res = connection_commit(self, empty); + res = connection_commit(self, NULL); if (!res) { return -1; } - Py_DECREF(empty); Py_DECREF(res); self->inTransaction = 0; @@ -738,12 +782,15 @@ PyObject* connection_call(Connection* self, PyObject* args, PyObject* kwargs) { PyObject* sql; Statement* statement; + PyObject* weakref; int rc; if (!PyArg_ParseTuple(args, "O", &sql)) { return NULL; } + _drop_unused_statement_references(self); + statement = PyObject_New(Statement, &StatementType); if (!statement) { return NULL; @@ -762,8 +809,24 @@ PyObject* connection_call(Connection* self, PyObject* args, PyObject* kwargs) Py_DECREF(statement); statement = 0; + } else { + weakref = PyWeakref_NewRef((PyObject*)statement, NULL); + if (!weakref) { + Py_DECREF(statement); + statement = 0; + goto error; + } + + if (PyList_Append(self->statements, weakref) != 0) { + Py_DECREF(weakref); + statement = 0; + goto error; + } + + Py_DECREF(weakref); } +error: return (PyObject*)statement; } @@ -983,7 +1046,7 @@ finally: } static char connection_doc[] = -PyDoc_STR(""); +PyDoc_STR("SQLite database connection object."); static PyGetSetDef connection_getset[] = { {"isolation_level", (getter)connection_get_isolation_level, (setter)connection_set_isolation_level}, @@ -1011,7 +1074,7 @@ static PyMethodDef connection_methods[] = { {"executescript", (PyCFunction)connection_executescript, METH_VARARGS, PyDoc_STR("Executes a multiple SQL statements at once. Non-standard.")}, {"create_collation", (PyCFunction)connection_create_collation, METH_VARARGS, - PyDoc_STR("Creates a collation function.")}, + PyDoc_STR("Creates a collation function. Non-standard.")}, {NULL, NULL} }; diff --git a/Modules/_sqlite/connection.h b/Modules/_sqlite/connection.h index faae6e4..8f4d36e 100644 --- a/Modules/_sqlite/connection.h +++ b/Modules/_sqlite/connection.h @@ -1,6 +1,6 @@ /* connection.h - definitions for the connection type * - * Copyright (C) 2004-2005 Gerhard Häring + * Copyright (C) 2004-2006 Gerhard Häring * * This file is part of pysqlite. * @@ -37,7 +37,12 @@ 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 */ @@ -54,13 +59,31 @@ typedef struct * 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; + + /* thread identification of the thread the connection was created in */ long thread_ident; Cache* statement_cache; + /* A list of weak references to statements used within this connection */ + PyObject* statements; + + /* a counter for how many statements were created in the connection. May be + * reset to 0 at certain intervals */ + int created_statements; + 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 diff --git a/Modules/_sqlite/converters.c b/Modules/_sqlite/converters.c deleted file mode 100644 index 018063a..0000000 --- a/Modules/_sqlite/converters.c +++ /dev/null @@ -1,40 +0,0 @@ -/* converters.c - default converters - * - * Copyright (C) 2005 Gerhard Häring - * - * 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 "util.h" -#include "module.h" -#include "adapters.h" - -/* dummy, will be implemented in a later version */ - -PyObject* convert_date(PyObject* self, PyObject* args, PyObject* kwargs) -{ - Py_INCREF(Py_None); - return Py_None; -} - -PyObject* convert_timestamp(PyObject* self, PyObject* args, PyObject* kwargs) -{ - Py_INCREF(Py_None); - return Py_None; -} diff --git a/Modules/_sqlite/converters.h b/Modules/_sqlite/converters.h deleted file mode 100644 index df3768a..0000000 --- a/Modules/_sqlite/converters.h +++ /dev/null @@ -1,33 +0,0 @@ -/* converters.h - default converters - * - * Copyright (C) 2005 Gerhard Häring - * - * 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_CONVERTERS_H -#define PYSQLITE_CONVERTERS_H -#include "Python.h" -#include "pythread.h" -#include "sqlite3.h" - -PyObject* convert_date(PyObject* self, PyObject* args, PyObject* kwargs); -PyObject* convert_timestamp(PyObject* self, PyObject* args, PyObject* kwargs); - -#endif diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index c6b8c77..8c72412 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -157,24 +157,24 @@ int build_row_cast_map(Cursor* self) 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; + } - 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 */ + converter = PyDict_GetItem(converters, key); + Py_DECREF(key); break; } - - converter = PyDict_GetItem(converters, key); - Py_DECREF(key); - break; } - } } @@ -276,6 +276,7 @@ PyObject* _fetch_one_row(Cursor* self) void* raw_buffer; const char* val_str; char buf[200]; + const char* colname; Py_BEGIN_ALLOW_THREADS numcols = sqlite3_data_count(self->statement->st); @@ -340,8 +341,12 @@ PyObject* _fetch_one_row(Cursor* self) self->connection->text_factory == OptimizedUnicode ? 1 : 0); if (!converted) { + colname = sqlite3_column_name(self->statement->st, i); + if (colname) { + colname = ""; + } PyOS_snprintf(buf, sizeof(buf) - 1, "Could not decode to UTF-8 column %s with text %s", - sqlite3_column_name(self->statement->st, i), val_str); + colname , val_str); PyErr_SetString(OperationalError, buf); } } else if (self->connection->text_factory == (PyObject*)&PyString_Type) { @@ -419,8 +424,7 @@ PyObject* _query_execute(Cursor* self, int multiple, PyObject* args) } else { /* sequence */ parameters_iter = PyObject_GetIter(second_argument); - if (!parameters_iter) - { + if (!parameters_iter) { return NULL; } } @@ -506,12 +510,7 @@ PyObject* _query_execute(Cursor* self, int multiple, PyObject* args) /* it's a DDL statement or something similar - we better COMMIT first so it works for all cases */ if (self->connection->inTransaction) { - func_args = PyTuple_New(0); - if (!func_args) { - goto error; - } - result = connection_commit(self->connection, func_args); - Py_DECREF(func_args); + result = connection_commit(self->connection, NULL); if (!result) { goto error; } @@ -701,7 +700,6 @@ PyObject* cursor_executescript(Cursor* self, PyObject* args) const char* script_cstr; sqlite3_stmt* statement; int rc; - PyObject* func_args; PyObject* result; int statement_completed = 0; @@ -728,12 +726,7 @@ PyObject* cursor_executescript(Cursor* self, PyObject* args) } /* commit first */ - func_args = PyTuple_New(0); - if (!func_args) { - goto error; - } - result = connection_commit(self->connection, func_args); - Py_DECREF(func_args); + result = connection_commit(self->connection, NULL); if (!result) { goto error; } @@ -977,6 +970,9 @@ static struct PyMemberDef cursor_members[] = {NULL} }; +static char cursor_doc[] = +PyDoc_STR("SQLite database cursor class."); + PyTypeObject CursorType = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ @@ -999,7 +995,7 @@ PyTypeObject CursorType = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_ITER|Py_TPFLAGS_BASETYPE, /* tp_flags */ - 0, /* tp_doc */ + cursor_doc, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ diff --git a/Modules/_sqlite/cursor.h b/Modules/_sqlite/cursor.h index 7f56799..831ff81 100644 --- a/Modules/_sqlite/cursor.h +++ b/Modules/_sqlite/cursor.h @@ -1,6 +1,6 @@ /* cursor.h - definitions for the cursor type * - * Copyright (C) 2004-2005 Gerhard Häring + * Copyright (C) 2004-2006 Gerhard Häring * * This file is part of pysqlite. * diff --git a/Modules/_sqlite/microprotocols.h b/Modules/_sqlite/microprotocols.h index d2d9b65..f601bb3 100644 --- a/Modules/_sqlite/microprotocols.h +++ b/Modules/_sqlite/microprotocols.h @@ -54,6 +54,6 @@ extern PyObject *microprotocols_adapt( extern PyObject * psyco_microprotocols_adapt(Cursor* self, PyObject *args); #define psyco_microprotocols_adapt_doc \ - "adapt(obj, protocol, alternate) -> adapt obj to given protocol" - + "adapt(obj, protocol, alternate) -> adapt obj to given protocol. Non-standard." + #endif /* !defined(PSYCOPG_MICROPROTOCOLS_H) */ diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index 1537e79..fb6eb06 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -167,12 +167,12 @@ void converters_init(PyObject* dict) static PyMethodDef module_methods[] = { {"connect", (PyCFunction)module_connect, METH_VARARGS|METH_KEYWORDS, PyDoc_STR("Creates a connection.")}, - {"complete_statement", (PyCFunction)module_complete, METH_VARARGS|METH_KEYWORDS, PyDoc_STR("Checks if a string contains a complete SQL statement.")}, + {"complete_statement", (PyCFunction)module_complete, METH_VARARGS|METH_KEYWORDS, PyDoc_STR("Checks if a string contains a complete SQL statement. Non-standard.")}, #ifdef HAVE_SHARED_CACHE - {"enable_shared_cache", (PyCFunction)module_enable_shared_cache, METH_VARARGS|METH_KEYWORDS, PyDoc_STR("Enable or disable shared cache mode for the calling thread.")}, + {"enable_shared_cache", (PyCFunction)module_enable_shared_cache, METH_VARARGS|METH_KEYWORDS, PyDoc_STR("Enable or disable shared cache mode for the calling thread. Experimental/Non-standard.")}, #endif - {"register_adapter", (PyCFunction)module_register_adapter, METH_VARARGS, PyDoc_STR("Registers an adapter with sqlite's adapter registry.")}, - {"register_converter", (PyCFunction)module_register_converter, METH_VARARGS, PyDoc_STR("Registers a converter with sqlite.")}, + {"register_adapter", (PyCFunction)module_register_adapter, METH_VARARGS, PyDoc_STR("Registers an adapter with pysqlite's adapter registry. Non-standard.")}, + {"register_converter", (PyCFunction)module_register_converter, METH_VARARGS, PyDoc_STR("Registers a converter with pysqlite. Non-standard.")}, {"adapt", (PyCFunction)psyco_microprotocols_adapt, METH_VARARGS, psyco_microprotocols_adapt_doc}, {NULL, NULL} }; diff --git a/Modules/_sqlite/module.h b/Modules/_sqlite/module.h index 6694735..f3e2aa1 100644 --- a/Modules/_sqlite/module.h +++ b/Modules/_sqlite/module.h @@ -1,6 +1,6 @@ /* module.h - definitions for the module * - * Copyright (C) 2004-2005 Gerhard Häring + * Copyright (C) 2004-2006 Gerhard Häring * * This file is part of pysqlite. * @@ -25,7 +25,7 @@ #define PYSQLITE_MODULE_H #include "Python.h" -#define PYSQLITE_VERSION "2.2.0" +#define PYSQLITE_VERSION "2.2.2" extern PyObject* Error; extern PyObject* Warning; diff --git a/Modules/_sqlite/statement.c b/Modules/_sqlite/statement.c index 0c93651..55923e7 100644 --- a/Modules/_sqlite/statement.c +++ b/Modules/_sqlite/statement.c @@ -64,6 +64,7 @@ int statement_create(Statement* self, Connection* connection, PyObject* sql) return rc; } + self->in_weakreflist = NULL; self->sql = sql_str; sql_cstr = PyString_AsString(sql_str); @@ -304,6 +305,10 @@ void statement_dealloc(Statement* self) Py_XDECREF(self->sql); + if (self->in_weakreflist != NULL) { + PyObject_ClearWeakRefs((PyObject*)self); + } + self->ob_type->tp_free((PyObject*)self); } @@ -398,12 +403,12 @@ PyTypeObject StatementType = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_WEAKREFS, /* tp_flags */ 0, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ + offsetof(Statement, in_weakreflist), /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ diff --git a/Modules/_sqlite/statement.h b/Modules/_sqlite/statement.h index e45a0fc..57ee36f 100644 --- a/Modules/_sqlite/statement.h +++ b/Modules/_sqlite/statement.h @@ -38,6 +38,7 @@ typedef struct sqlite3_stmt* st; PyObject* sql; int in_use; + PyObject* in_weakreflist; /* List of weak references */ } Statement; extern PyTypeObject StatementType; diff --git a/PCbuild/_sqlite3.vcproj b/PCbuild/_sqlite3.vcproj index e81d3a0..a0408a6 100644 --- a/PCbuild/_sqlite3.vcproj +++ b/PCbuild/_sqlite3.vcproj @@ -251,18 +251,12 @@ - - - -