diff --git a/Doc/includes/sqlite3/load_extension.py b/Doc/includes/sqlite3/load_extension.py new file mode 100644 index 00000000000..7f893c9286d --- /dev/null +++ b/Doc/includes/sqlite3/load_extension.py @@ -0,0 +1,26 @@ +import sqlite3 + +con = sqlite3.connect(":memory:") + +# enable extension loading +con.enable_load_extension(True) + +# Load the fulltext search extension +con.execute("select load_extension('./fts3.so')") + +# alternatively you can load the extension using an API call: +# con.load_extension("./fts3.so") + +# disable extension laoding again +con.enable_load_extension(False) + +# example from SQLite wiki +con.execute("create virtual table recipe using fts3(name, ingredients)") +con.executescript(""" + insert into recipe (name, ingredients) values ('broccoli stew', 'broccoli peppers cheese tomatoes'); + insert into recipe (name, ingredients) values ('pumpkin stew', 'pumpkin onions garlic celery'); + insert into recipe (name, ingredients) values ('broccoli pie', 'broccoli cheese onions flour'); + insert into recipe (name, ingredients) values ('pumpkin pie', 'pumpkin sugar flour butter'); + """) +for row in con.execute("select rowid, name, ingredients from recipe where name match 'pie'"): + print row diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 8ccb0ab2ab1..439ca230698 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -368,6 +368,25 @@ Connection Objects method with :const:`None` for *handler*. +.. method:: Connection.enable_load_extension(enabled) + + .. versionadded:: 2.7 + + This routine allows/disallows the SQLite engine to load SQLite extensions + from shared libraries. SQLite extensions can define new functions, + aggregates or whole new virtual table implementations. One well-known + extension is the fulltext-search extension distributed with SQLite. + + .. literalinclude:: ../includes/sqlite3/load_extension.py + +.. method:: Connection.load_extension(path) + + .. versionadded:: 2.7 + + This routine loads a SQLite extension from a shared library. You have to + enable extension loading with ``enable_load_extension`` before you can use + this routine. + .. attribute:: Connection.row_factory You can change this attribute to a callable that accepts the cursor and the @@ -439,7 +458,7 @@ Connection Objects Cursor Objects -------------- -.. class:: Cursor +A :class:`Cursor` instance has the following attributes and methods: A SQLite database cursor has the following attributes and methods: diff --git a/Lib/sqlite3/test/dbapi.py b/Lib/sqlite3/test/dbapi.py index 83ce17424dc..9d872122930 100644 --- a/Lib/sqlite3/test/dbapi.py +++ b/Lib/sqlite3/test/dbapi.py @@ -1,7 +1,7 @@ #-*- coding: ISO-8859-1 -*- # pysqlite2/test/dbapi.py: tests for DB-API compliance # -# Copyright (C) 2004-2007 Gerhard Häring +# Copyright (C) 2004-2010 Gerhard Häring # # This file is part of pysqlite. # @@ -672,13 +672,13 @@ class ExtensionTests(unittest.TestCase): res = cur.fetchone()[0] self.assertEqual(res, 6) - def CheckScriptErrorIncomplete(self): + def CheckScriptSyntaxError(self): con = sqlite.connect(":memory:") cur = con.cursor() raised = False try: - cur.executescript("create table test(sadfsadfdsa") - except sqlite.ProgrammingError: + cur.executescript("create table test(x); asdf; create table test2(x)") + except sqlite.OperationalError: raised = True self.assertEqual(raised, True, "should have raised an exception") @@ -711,7 +711,7 @@ class ExtensionTests(unittest.TestCase): result = con.execute("select foo from test").fetchone()[0] self.assertEqual(result, 5, "Basic test of Connection.executescript") -class ClosedTests(unittest.TestCase): +class ClosedConTests(unittest.TestCase): def setUp(self): pass @@ -763,6 +763,102 @@ class ClosedTests(unittest.TestCase): except: self.fail("Should have raised a ProgrammingError") + def CheckClosedCreateFunction(self): + con = sqlite.connect(":memory:") + con.close() + def f(x): return 17 + try: + con.create_function("foo", 1, f) + self.fail("Should have raised a ProgrammingError") + except sqlite.ProgrammingError: + pass + except: + self.fail("Should have raised a ProgrammingError") + + def CheckClosedCreateAggregate(self): + con = sqlite.connect(":memory:") + con.close() + class Agg: + def __init__(self): + pass + def step(self, x): + pass + def finalize(self): + return 17 + try: + con.create_aggregate("foo", 1, Agg) + self.fail("Should have raised a ProgrammingError") + except sqlite.ProgrammingError: + pass + except: + self.fail("Should have raised a ProgrammingError") + + def CheckClosedSetAuthorizer(self): + con = sqlite.connect(":memory:") + con.close() + def authorizer(*args): + return sqlite.DENY + try: + con.set_authorizer(authorizer) + self.fail("Should have raised a ProgrammingError") + except sqlite.ProgrammingError: + pass + except: + self.fail("Should have raised a ProgrammingError") + + def CheckClosedSetProgressCallback(self): + con = sqlite.connect(":memory:") + con.close() + def progress(): pass + try: + con.set_progress_handler(progress, 100) + self.fail("Should have raised a ProgrammingError") + except sqlite.ProgrammingError: + pass + except: + self.fail("Should have raised a ProgrammingError") + + def CheckClosedCall(self): + con = sqlite.connect(":memory:") + con.close() + try: + con() + self.fail("Should have raised a ProgrammingError") + except sqlite.ProgrammingError: + pass + except: + self.fail("Should have raised a ProgrammingError") + +class ClosedCurTests(unittest.TestCase): + def setUp(self): + pass + + def tearDown(self): + pass + + def CheckClosed(self): + con = sqlite.connect(":memory:") + cur = con.cursor() + cur.close() + + for method_name in ("execute", "executemany", "executescript", "fetchall", "fetchmany", "fetchone"): + if method_name in ("execute", "executescript"): + params = ("select 4 union select 5",) + elif method_name == "executemany": + params = ("insert into foo(bar) values (?)", [(3,), (4,)]) + else: + params = [] + + try: + method = getattr(cur, method_name) + + method(*params) + self.fail("Should have raised a ProgrammingError: method " + method_name) + except sqlite.ProgrammingError: + pass + except: + self.fail("Should have raised a ProgrammingError: " + method_name) + def suite(): module_suite = unittest.makeSuite(ModuleTests, "Check") connection_suite = unittest.makeSuite(ConnectionTests, "Check") @@ -770,8 +866,9 @@ def suite(): thread_suite = unittest.makeSuite(ThreadTests, "Check") constructor_suite = unittest.makeSuite(ConstructorTests, "Check") ext_suite = unittest.makeSuite(ExtensionTests, "Check") - closed_suite = unittest.makeSuite(ClosedTests, "Check") - return unittest.TestSuite((module_suite, connection_suite, cursor_suite, thread_suite, constructor_suite, ext_suite, closed_suite)) + closed_con_suite = unittest.makeSuite(ClosedConTests, "Check") + closed_cur_suite = unittest.makeSuite(ClosedCurTests, "Check") + return unittest.TestSuite((module_suite, connection_suite, cursor_suite, thread_suite, constructor_suite, ext_suite, closed_con_suite, closed_cur_suite)) def test(): runner = unittest.TextTestRunner() diff --git a/Lib/sqlite3/test/regression.py b/Lib/sqlite3/test/regression.py index 49e1651a36b..85eb3b4fd78 100644 --- a/Lib/sqlite3/test/regression.py +++ b/Lib/sqlite3/test/regression.py @@ -70,16 +70,6 @@ class RegressionTests(unittest.TestCase): cur.execute('select 1 as "foo baz"') self.assertEqual(cur.description[0][0], "foo baz") - def CheckStatementAvailable(self): - # pysqlite up to 2.3.2 crashed on this, because the active statement handle was not checked - # before trying to fetch data from it. close() destroys the active statement ... - con = sqlite.connect(":memory:", detect_types=sqlite.PARSE_DECLTYPES) - cur = con.cursor() - cur.execute("select 4 union select 5") - cur.close() - cur.fetchone() - cur.fetchone() - def CheckStatementFinalizationOnCloseDb(self): # pysqlite versions <= 2.3.3 only finalized statements in the statement # cache when closing the database. statements that were still @@ -167,6 +157,107 @@ class RegressionTests(unittest.TestCase): self.assertRaises(UnicodeEncodeError, setattr, con, "isolation_level", u"\xe9") + def CheckCursorConstructorCallCheck(self): + """ + Verifies that cursor methods check wether base class __init__ was called. + """ + class Cursor(sqlite.Cursor): + def __init__(self, con): + pass + + con = sqlite.connect(":memory:") + cur = Cursor(con) + try: + cur.execute("select 4+5").fetchall() + self.fail("should have raised ProgrammingError") + except sqlite.ProgrammingError: + pass + except: + self.fail("should have raised ProgrammingError") + + def CheckConnectionConstructorCallCheck(self): + """ + Verifies that connection methods check wether base class __init__ was called. + """ + class Connection(sqlite.Connection): + def __init__(self, name): + pass + + con = Connection(":memory:") + try: + cur = con.cursor() + self.fail("should have raised ProgrammingError") + except sqlite.ProgrammingError: + pass + except: + self.fail("should have raised ProgrammingError") + + def CheckCursorRegistration(self): + """ + Verifies that subclassed cursor classes are correctly registered with + the connection object, too. (fetch-across-rollback problem) + """ + class Connection(sqlite.Connection): + def cursor(self): + return Cursor(self) + + class Cursor(sqlite.Cursor): + def __init__(self, con): + sqlite.Cursor.__init__(self, con) + + con = Connection(":memory:") + cur = con.cursor() + cur.execute("create table foo(x)") + cur.executemany("insert into foo(x) values (?)", [(3,), (4,), (5,)]) + cur.execute("select x from foo") + con.rollback() + try: + cur.fetchall() + self.fail("should have raised InterfaceError") + except sqlite.InterfaceError: + pass + except: + self.fail("should have raised InterfaceError") + + def CheckAutoCommit(self): + """ + Verifies that creating a connection in autocommit mode works. + 2.5.3 introduced a regression so that these could no longer + be created. + """ + con = sqlite.connect(":memory:", isolation_level=None) + + def CheckPragmaAutocommit(self): + """ + Verifies that running a PRAGMA statement that does an autocommit does + work. This did not work in 2.5.3/2.5.4. + """ + con = sqlite.connect(":memory:") + cur = con.cursor() + cur.execute("create table foo(bar)") + cur.execute("insert into foo(bar) values (5)") + + cur.execute("pragma page_size") + row = cur.fetchone() + + def CheckSetDict(self): + """ + See http://bugs.python.org/issue7478 + + It was possible to successfully register callbacks that could not be + hashed. Return codes of PyDict_SetItem were not checked properly. + """ + class NotHashable: + def __call__(self, *args, **kw): + pass + def __hash__(self): + raise TypeError() + var = NotHashable() + con = sqlite.connect(":memory:") + self.assertRaises(TypeError, con.create_function, var) + self.assertRaises(TypeError, con.create_aggregate, var) + self.assertRaises(TypeError, con.set_authorizer, var) + self.assertRaises(TypeError, con.set_progress_handler, var) def suite(): regression_suite = unittest.makeSuite(RegressionTests, "Check") diff --git a/Lib/sqlite3/test/transactions.py b/Lib/sqlite3/test/transactions.py index c08b56f1756..a732251b2d4 100644 --- a/Lib/sqlite3/test/transactions.py +++ b/Lib/sqlite3/test/transactions.py @@ -148,6 +148,26 @@ class TransactionTests(unittest.TestCase): # NO self.con2.rollback() HERE!!! self.con1.commit() + def CheckRollbackCursorConsistency(self): + """ + Checks if cursors on the connection are set into a "reset" state + when a rollback is done on the connection. + """ + con = sqlite.connect(":memory:") + cur = con.cursor() + cur.execute("create table test(x)") + cur.execute("insert into test(x) values (5)") + cur.execute("select 1 union select 2 union select 3") + + con.rollback() + try: + cur.fetchall() + self.fail("InterfaceError should have been raised") + except sqlite.InterfaceError, e: + pass + except: + self.fail("InterfaceError should have been raised") + class SpecialCommandTests(unittest.TestCase): def setUp(self): self.con = sqlite.connect(":memory:") diff --git a/Lib/sqlite3/test/types.py b/Lib/sqlite3/test/types.py index b38c8f4fdb2..70d20dcdce5 100644 --- a/Lib/sqlite3/test/types.py +++ b/Lib/sqlite3/test/types.py @@ -78,6 +78,33 @@ class SqliteTypeTests(unittest.TestCase): row = self.cur.fetchone() self.assertEqual(row[0], u"Österreich") + def CheckNonUtf8_Default(self): + try: + self.cur.execute("select ?", (chr(150),)) + self.fail("should have raised a ProgrammingError") + except sqlite.ProgrammingError: + pass + + def CheckNonUtf8_TextFactoryString(self): + orig_text_factory = self.con.text_factory + try: + self.con.text_factory = str + self.cur.execute("select ?", (chr(150),)) + finally: + self.con.text_factory = orig_text_factory + + def CheckNonUtf8_TextFactoryOptimizedUnicode(self): + orig_text_factory = self.con.text_factory + try: + try: + self.con.text_factory = sqlite.OptimizedUnicode + self.cur.execute("select ?", (chr(150),)) + self.fail("should have raised a ProgrammingError") + except sqlite.ProgrammingError: + pass + finally: + self.con.text_factory = orig_text_factory + class DeclTypesTests(unittest.TestCase): class Foo: def __init__(self, _val): diff --git a/Misc/NEWS b/Misc/NEWS index 107c9912762..24aab35c8be 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -103,6 +103,9 @@ Library Extension Modules ----------------- +- The sqlite3 module was updated to pysqlite 2.6.0. This fixes several obscure + bugs and allows loading SQLite extensions from shared libraries. + - Issue #7808: Fix reference leaks in _bsddb and related tests. - Issue #6544: fix a reference leak in the kqueue implementation's error diff --git a/Modules/_sqlite/cache.c b/Modules/_sqlite/cache.c index 85118fc78e4..0653367ddb9 100644 --- a/Modules/_sqlite/cache.c +++ b/Modules/_sqlite/cache.c @@ -1,6 +1,6 @@ /* cache .c - a LRU cache * - * Copyright (C) 2004-2007 Gerhard Häring + * Copyright (C) 2004-2010 Gerhard Häring * * This file is part of pysqlite. * @@ -21,6 +21,7 @@ * 3. This notice may not be removed or altered from any source distribution. */ +#include "sqlitecompat.h" #include "cache.h" #include diff --git a/Modules/_sqlite/cache.h b/Modules/_sqlite/cache.h index d6f7f13296e..b09517c1513 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-2007 Gerhard Häring + * Copyright (C) 2004-2010 Gerhard Häring * * This file is part of pysqlite. * diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 74177910baa..6bc495a7f40 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -1,6 +1,6 @@ /* connection.c - the connection type * - * Copyright (C) 2004-2007 Gerhard Häring + * Copyright (C) 2004-2010 Gerhard Häring * * This file is part of pysqlite. * @@ -35,7 +35,14 @@ #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) @@ -73,10 +80,13 @@ int pysqlite_connection_init(pysqlite_Connection* self, PyObject* args, PyObject 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; @@ -150,11 +160,15 @@ int pysqlite_connection_init(pysqlite_Connection* self, PyObject* args, PyObject return -1; } + self->created_statements = 0; + self->created_cursors = 0; + + /* Create lists of weak references to statements/cursors */ self->statements = PyList_New(0); - if (!self->statements) { + self->cursors = PyList_New(0); + if (!self->statements || !self->cursors) { 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 @@ -218,11 +232,12 @@ void pysqlite_flush_statement_cache(pysqlite_Connection* self) } /* action in (ACTION_RESET, ACTION_FINALIZE) */ -void pysqlite_do_all_statements(pysqlite_Connection* self, int action) +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); @@ -235,6 +250,16 @@ void pysqlite_do_all_statements(pysqlite_Connection* self, int action) } } } + + 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) @@ -263,17 +288,43 @@ void pysqlite_connection_dealloc(pysqlite_Connection* self) 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; @@ -289,6 +340,8 @@ PyObject* pysqlite_connection_cursor(pysqlite_Connection* self, PyObject* args, 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); @@ -307,7 +360,7 @@ PyObject* pysqlite_connection_close(pysqlite_Connection* self, PyObject* args) return NULL; } - pysqlite_do_all_statements(self, ACTION_FINALIZE); + pysqlite_do_all_statements(self, ACTION_FINALIZE, 1); if (self->db) { if (self->apsw_connection) { @@ -341,6 +394,11 @@ PyObject* pysqlite_connection_close(pysqlite_Connection* self, PyObject* args) */ 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; @@ -399,6 +457,8 @@ PyObject* pysqlite_connection_commit(pysqlite_Connection* self, PyObject* args) } 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 @@ -443,7 +503,7 @@ PyObject* pysqlite_connection_rollback(pysqlite_Connection* self, PyObject* args } if (self->inTransaction) { - pysqlite_do_all_statements(self, ACTION_RESET); + pysqlite_do_all_statements(self, ACTION_RESET, 1); Py_BEGIN_ALLOW_THREADS rc = sqlite3_prepare(self->db, "ROLLBACK", -1, &statement, &tail); @@ -724,7 +784,7 @@ error: #endif } -void _pysqlite_drop_unused_statement_references(pysqlite_Connection* self) +static void _pysqlite_drop_unused_statement_references(pysqlite_Connection* self) { PyObject* new_list; PyObject* weakref; @@ -756,6 +816,38 @@ void _pysqlite_drop_unused_statement_references(pysqlite_Connection* self) 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}; @@ -765,6 +857,10 @@ PyObject* pysqlite_connection_create_function(pysqlite_Connection* self, PyObjec 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)) { @@ -778,7 +874,8 @@ PyObject* pysqlite_connection_create_function(pysqlite_Connection* self, PyObjec PyErr_SetString(pysqlite_OperationalError, "Error creating function"); return NULL; } else { - PyDict_SetItem(self->function_pinboard, func, Py_None); + if (PyDict_SetItem(self->function_pinboard, func, Py_None) == -1) + return NULL; Py_INCREF(Py_None); return Py_None; @@ -794,6 +891,10 @@ PyObject* pysqlite_connection_create_aggregate(pysqlite_Connection* self, PyObje 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; @@ -805,7 +906,8 @@ PyObject* pysqlite_connection_create_aggregate(pysqlite_Connection* self, PyObje PyErr_SetString(pysqlite_OperationalError, "Error creating aggregate"); return NULL; } else { - PyDict_SetItem(self->function_pinboard, aggregate_class, Py_None); + if (PyDict_SetItem(self->function_pinboard, aggregate_class, Py_None) == -1) + return NULL; Py_INCREF(Py_None); return Py_None; @@ -877,13 +979,17 @@ static int _progress_handler(void* user_arg) return rc; } -PyObject* pysqlite_connection_set_authorizer(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) +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; @@ -895,20 +1001,25 @@ PyObject* pysqlite_connection_set_authorizer(pysqlite_Connection* self, PyObject PyErr_SetString(pysqlite_OperationalError, "Error setting authorizer callback"); return NULL; } else { - PyDict_SetItem(self->function_pinboard, authorizer_cb, Py_None); + if (PyDict_SetItem(self->function_pinboard, authorizer_cb, Py_None) == -1) + return NULL; Py_INCREF(Py_None); return Py_None; } } -PyObject* pysqlite_connection_set_progress_handler(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) +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; @@ -919,13 +1030,64 @@ PyObject* pysqlite_connection_set_progress_handler(pysqlite_Connection* self, Py sqlite3_progress_handler(self->db, 0, 0, (void*)0); } else { sqlite3_progress_handler(self->db, n, _progress_handler, progress_handler); - PyDict_SetItem(self->function_pinboard, progress_handler, Py_None); + 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 @@ -1020,6 +1182,10 @@ PyObject* pysqlite_connection_call(pysqlite_Connection* self, PyObject* args, Py PyObject* weakref; int rc; + if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { + return NULL; + } + if (!PyArg_ParseTuple(args, "O", &sql)) { return NULL; } @@ -1309,9 +1475,11 @@ pysqlite_connection_create_collation(pysqlite_Connection* self, PyObject* args) } if (callable != Py_None) { - PyDict_SetItem(self->collations, uppercase_name, callable); + if (PyDict_SetItem(self->collations, uppercase_name, callable) == -1) + goto finally; } else { - PyDict_DelItem(self->collations, uppercase_name); + if (PyDict_DelItem(self->collations, uppercase_name) == -1) + goto finally; } rc = sqlite3_create_collation(self->db, @@ -1400,6 +1568,12 @@ static PyMethodDef connection_methods[] = { 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, diff --git a/Modules/_sqlite/connection.h b/Modules/_sqlite/connection.h index 3b1c632db8c..3fc5a70f4b0 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-2007 Gerhard Häring + * Copyright (C) 2004-2010 Gerhard Häring * * This file is part of pysqlite. * @@ -63,17 +63,21 @@ typedef struct * 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; - /* A list of weak references to statements used within this connection */ + /* Lists of weak references to statements and cursors used within this connection */ PyObject* statements; + PyObject* cursors; - /* a counter for how many statements were created in the connection. May be + /* 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; @@ -125,6 +129,7 @@ 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); diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index 6572a559a65..26e3307f885 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -1,6 +1,6 @@ /* cursor.c - the cursor type * - * Copyright (C) 2004-2007 Gerhard Häring + * Copyright (C) 2004-2010 Gerhard Häring * * This file is part of pysqlite. * @@ -36,6 +36,8 @@ 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]; @@ -74,7 +76,7 @@ static pysqlite_StatementKind detect_statement_type(char* statement) } } -int pysqlite_cursor_init(pysqlite_Cursor* self, PyObject* args, PyObject* kwargs) +static int pysqlite_cursor_init(pysqlite_Cursor* self, PyObject* args, PyObject* kwargs) { pysqlite_Connection* connection; @@ -87,6 +89,7 @@ int pysqlite_cursor_init(pysqlite_Cursor* self, PyObject* args, PyObject* kwargs 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) { @@ -100,6 +103,8 @@ int pysqlite_cursor_init(pysqlite_Cursor* self, PyObject* args, PyObject* kwargs self->lastrowid= Py_None; self->arraysize = 1; + self->closed = 0; + self->reset = 0; self->rowcount = -1L; @@ -110,10 +115,16 @@ int pysqlite_cursor_init(pysqlite_Cursor* self, PyObject* args, PyObject* kwargs return -1; } + if (!pysqlite_connection_register_cursor(connection, (PyObject*)self)) { + return -1; + } + + self->initialized = 1; + return 0; } -void pysqlite_cursor_dealloc(pysqlite_Cursor* self) +static void pysqlite_cursor_dealloc(pysqlite_Cursor* self) { int rc; @@ -130,7 +141,11 @@ void pysqlite_cursor_dealloc(pysqlite_Cursor* self) Py_XDECREF(self->row_factory); Py_XDECREF(self->next_row); - Py_TYPE(self)->tp_free((PyObject*)self); + if (self->in_weakreflist != NULL) { + PyObject_ClearWeakRefs((PyObject*)self); + } + + self->ob_type->tp_free((PyObject*)self); } PyObject* _pysqlite_get_converter(PyObject* key) @@ -301,6 +316,11 @@ PyObject* _pysqlite_fetch_one_row(pysqlite_Cursor* self) 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 @@ -406,6 +426,26 @@ PyObject* _pysqlite_fetch_one_row(pysqlite_Cursor* self) 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; @@ -425,13 +465,15 @@ PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* PyObject* second_argument = NULL; int allow_8bit_chars; - if (!pysqlite_check_thread(self->connection) || !pysqlite_check_connection(self->connection)) { + 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 != (PyObject*)&PyUnicode_Type && pysqlite_OptimizedUnicode)); + (self->connection->text_factory != pysqlite_OptimizedUnicode)); Py_XDECREF(self->next_row); self->next_row = NULL; @@ -753,16 +795,17 @@ PyObject* pysqlite_cursor_executescript(pysqlite_Cursor* self, PyObject* args) sqlite3_stmt* statement; int rc; PyObject* result; - int statement_completed = 0; if (!PyArg_ParseTuple(args, "O", &script_obj)) { return NULL; } - if (!pysqlite_check_thread(self->connection) || !pysqlite_check_connection(self->connection)) { + 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)) { @@ -785,11 +828,6 @@ PyObject* pysqlite_cursor_executescript(pysqlite_Cursor* self, PyObject* args) Py_DECREF(result); while (1) { - if (!sqlite3_complete(script_cstr)) { - break; - } - statement_completed = 1; - Py_BEGIN_ALLOW_THREADS rc = sqlite3_prepare(self->connection->db, script_cstr, @@ -820,15 +858,15 @@ PyObject* pysqlite_cursor_executescript(pysqlite_Cursor* self, PyObject* args) _pysqlite_seterror(self->connection->db, NULL); goto error; } + + if (*script_cstr == (char)0) { + break; + } } error: Py_XDECREF(script_str); - if (!statement_completed) { - PyErr_SetString(pysqlite_ProgrammingError, "you did not provide a complete SQL statement"); - } - if (PyErr_Occurred()) { return NULL; } else { @@ -849,7 +887,12 @@ PyObject* pysqlite_cursor_iternext(pysqlite_Cursor *self) PyObject* next_row; int rc; - if (!pysqlite_check_thread(self->connection) || !pysqlite_check_connection(self->connection)) { + if (!check_cursor(self)) { + return NULL; + } + + if (self->reset) { + PyErr_SetString(pysqlite_InterfaceError, errmsg_fetch_across_rollback); return NULL; } @@ -992,6 +1035,8 @@ PyObject* pysqlite_cursor_close(pysqlite_Cursor* self, PyObject* args) Py_CLEAR(self->statement); } + self->closed = 1; + Py_INCREF(Py_None); return Py_None; } @@ -1052,12 +1097,12 @@ PyTypeObject pysqlite_CursorType = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_ITER|Py_TPFLAGS_BASETYPE, /* tp_flags */ + 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 */ - 0, /* tp_weaklistoffset */ + offsetof(pysqlite_Cursor, in_weakreflist), /* tp_weaklistoffset */ (getiterfunc)pysqlite_cursor_getiter, /* tp_iter */ (iternextfunc)pysqlite_cursor_iternext, /* tp_iternext */ cursor_methods, /* tp_methods */ diff --git a/Modules/_sqlite/cursor.h b/Modules/_sqlite/cursor.h index 54d816db91b..82f5972a273 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-2007 Gerhard Häring + * Copyright (C) 2004-2010 Gerhard Häring * * This file is part of pysqlite. * @@ -40,9 +40,14 @@ typedef struct 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 { @@ -53,8 +58,6 @@ typedef enum { extern PyTypeObject pysqlite_CursorType; -int pysqlite_cursor_init(pysqlite_Cursor* self, PyObject* args, PyObject* kwargs); -void pysqlite_cursor_dealloc(pysqlite_Cursor* self); 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); diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index c16b7c71ea5..33611d9dad2 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -1,6 +1,6 @@ /* module.c - the module itself * - * Copyright (C) 2004-2007 Gerhard Häring + * Copyright (C) 2004-2010 Gerhard Häring * * This file is part of pysqlite. * diff --git a/Modules/_sqlite/module.h b/Modules/_sqlite/module.h index b14be2a9ecb..8d17d6173f0 100644 --- a/Modules/_sqlite/module.h +++ b/Modules/_sqlite/module.h @@ -1,6 +1,6 @@ /* module.h - definitions for the module * - * Copyright (C) 2004-2007 Gerhard Häring + * Copyright (C) 2004-2010 Gerhard Häring * * This file is part of pysqlite. * @@ -25,7 +25,7 @@ #define PYSQLITE_MODULE_H #include "Python.h" -#define PYSQLITE_VERSION "2.4.1" +#define PYSQLITE_VERSION "2.6.0" extern PyObject* pysqlite_Error; extern PyObject* pysqlite_Warning; diff --git a/Modules/_sqlite/prepare_protocol.c b/Modules/_sqlite/prepare_protocol.c index c018c965bb2..9ed25e18adf 100644 --- a/Modules/_sqlite/prepare_protocol.c +++ b/Modules/_sqlite/prepare_protocol.c @@ -1,6 +1,6 @@ /* prepare_protocol.c - the protocol for preparing values for SQLite * - * Copyright (C) 2005-2006 Gerhard Häring + * Copyright (C) 2005-2010 Gerhard Häring * * This file is part of pysqlite. * @@ -21,6 +21,7 @@ * 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) diff --git a/Modules/_sqlite/prepare_protocol.h b/Modules/_sqlite/prepare_protocol.h index 153472e9dea..1cdf7086eb4 100644 --- a/Modules/_sqlite/prepare_protocol.h +++ b/Modules/_sqlite/prepare_protocol.h @@ -1,6 +1,6 @@ /* prepare_protocol.h - the protocol for preparing values for SQLite * - * Copyright (C) 2005-2007 Gerhard Häring + * Copyright (C) 2005-2010 Gerhard Häring * * This file is part of pysqlite. * diff --git a/Modules/_sqlite/row.c b/Modules/_sqlite/row.c index f1e1c43c52c..480b482ed85 100644 --- a/Modules/_sqlite/row.c +++ b/Modules/_sqlite/row.c @@ -1,6 +1,6 @@ /* row.c - an enhanced tuple for database rows * - * Copyright (C) 2005-2006 Gerhard Häring + * Copyright (C) 2005-2010 Gerhard Häring * * This file is part of pysqlite. * @@ -177,17 +177,17 @@ static long pysqlite_row_hash(pysqlite_Row *self) 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; + 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); - } + 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; diff --git a/Modules/_sqlite/row.h b/Modules/_sqlite/row.h index 8ed69aee214..dd9b0c3c6fb 100644 --- a/Modules/_sqlite/row.h +++ b/Modules/_sqlite/row.h @@ -1,6 +1,6 @@ /* row.h - an enhanced tuple for database rows * - * Copyright (C) 2005-2007 Gerhard Häring + * Copyright (C) 2005-2010 Gerhard Häring * * This file is part of pysqlite. * diff --git a/Modules/_sqlite/sqlitecompat.h b/Modules/_sqlite/sqlitecompat.h index c3798257bf7..3408fc2119a 100644 --- a/Modules/_sqlite/sqlitecompat.h +++ b/Modules/_sqlite/sqlitecompat.h @@ -1,6 +1,6 @@ /* sqlitecompat.h - compatibility macros * - * Copyright (C) 2006 Gerhard Häring + * Copyright (C) 2006-2010 Gerhard Häring * * This file is part of pysqlite. * @@ -21,6 +21,8 @@ * 3. This notice may not be removed or altered from any source distribution. */ +#include "Python.h" + #ifndef PYSQLITE_COMPAT_H #define PYSQLITE_COMPAT_H @@ -31,4 +33,31 @@ 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/Modules/_sqlite/statement.c b/Modules/_sqlite/statement.c index f200c563991..9d08f2bd094 100644 --- a/Modules/_sqlite/statement.c +++ b/Modules/_sqlite/statement.c @@ -1,6 +1,6 @@ /* statement.c - the statement type * - * Copyright (C) 2005-2007 Gerhard Häring + * Copyright (C) 2005-2010 Gerhard Häring * * This file is part of pysqlite. * diff --git a/Modules/_sqlite/statement.h b/Modules/_sqlite/statement.h index bfa20916246..05fd5ff3d86 100644 --- a/Modules/_sqlite/statement.h +++ b/Modules/_sqlite/statement.h @@ -1,6 +1,6 @@ /* statement.h - definitions for the statement type * - * Copyright (C) 2005-2007 Gerhard Häring + * Copyright (C) 2005-2010 Gerhard Häring * * This file is part of pysqlite. * diff --git a/Modules/_sqlite/util.c b/Modules/_sqlite/util.c index 6be39ae737e..6b57b763ddb 100644 --- a/Modules/_sqlite/util.c +++ b/Modules/_sqlite/util.c @@ -1,6 +1,6 @@ /* util.c - various utility functions * - * Copyright (C) 2005-2007 Gerhard Häring + * Copyright (C) 2005-2010 Gerhard Häring * * This file is part of pysqlite. * diff --git a/Modules/_sqlite/util.h b/Modules/_sqlite/util.h index 2a45636d564..42690032a70 100644 --- a/Modules/_sqlite/util.h +++ b/Modules/_sqlite/util.h @@ -1,6 +1,6 @@ /* util.h - various utility functions * - * Copyright (C) 2005-2007 Gerhard Häring + * Copyright (C) 2005-2010 Gerhard Häring * * This file is part of pysqlite. * diff --git a/setup.py b/setup.py index 63278794e97..c65f2860073 100644 --- a/setup.py +++ b/setup.py @@ -955,6 +955,8 @@ class PyBuildExt(build_ext): else: sqlite_defines.append(('MODULE_NAME', '\\"sqlite3\\"')) + # Comment this out if you want the sqlite3 module to be able to load extensions. + sqlite_defines.append(("SQLITE_OMIT_LOAD_EXTENSION", "1")) if sys.platform == 'darwin': # In every directory on the search path search for a dynamic