diff options
-rw-r--r-- | Lib/sqlite3/test/dbapi.py | 6 | ||||
-rw-r--r-- | Lib/sqlite3/test/hooks.py | 115 | ||||
-rw-r--r-- | Lib/sqlite3/test/regression.py | 48 | ||||
-rw-r--r-- | Lib/sqlite3/test/transactions.py | 4 | ||||
-rw-r--r-- | Modules/_sqlite/cache.c | 41 | ||||
-rw-r--r-- | Modules/_sqlite/connection.c | 156 | ||||
-rw-r--r-- | Modules/_sqlite/connection.h | 3 | ||||
-rw-r--r-- | Modules/_sqlite/cursor.c | 109 | ||||
-rw-r--r-- | Modules/_sqlite/microprotocols.c | 18 | ||||
-rw-r--r-- | Modules/_sqlite/module.c | 91 | ||||
-rw-r--r-- | Modules/_sqlite/prepare_protocol.c | 2 | ||||
-rw-r--r-- | Modules/_sqlite/row.c | 15 | ||||
-rw-r--r-- | Modules/_sqlite/sqlitecompat.h | 34 | ||||
-rw-r--r-- | Modules/_sqlite/statement.c | 1 | ||||
-rw-r--r-- | Modules/_sqlite/util.c | 71 | ||||
-rw-r--r-- | Modules/_sqlite/util.h | 4 | ||||
-rw-r--r-- | setup.py | 2 |
17 files changed, 589 insertions, 131 deletions
diff --git a/Lib/sqlite3/test/dbapi.py b/Lib/sqlite3/test/dbapi.py index 0d47977..b08da9c 100644 --- a/Lib/sqlite3/test/dbapi.py +++ b/Lib/sqlite3/test/dbapi.py @@ -268,6 +268,12 @@ class CursorTests(unittest.TestCase): self.cu.executemany("insert into test(name) values (?)", [(1,), (2,), (3,)]) self.failUnlessEqual(self.cu.rowcount, 3) + def CheckTotalChanges(self): + self.cu.execute("insert into test(name) values ('foo')") + self.cu.execute("insert into test(name) values ('foo')") + if self.cx.total_changes < 2: + self.fail("total changes reported wrong value") + # Checks for executemany: # Sequences are required by the DB-API, iterators # enhancements in pysqlite. diff --git a/Lib/sqlite3/test/hooks.py b/Lib/sqlite3/test/hooks.py new file mode 100644 index 0000000..21f7b88 --- /dev/null +++ b/Lib/sqlite3/test/hooks.py @@ -0,0 +1,115 @@ +#-*- coding: ISO-8859-1 -*- +# pysqlite2/test/hooks.py: tests for various SQLite-specific hooks +# +# Copyright (C) 2006 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. + +import os, unittest +import pysqlite2.dbapi2 as sqlite + +class CollationTests(unittest.TestCase): + def setUp(self): + pass + + def tearDown(self): + pass + + def CheckCreateCollationNotCallable(self): + con = sqlite.connect(":memory:") + try: + con.create_collation("X", 42) + self.fail("should have raised a TypeError") + except TypeError, e: + self.failUnlessEqual(e.args[0], "parameter must be callable") + + def CheckCreateCollationNotAscii(self): + con = sqlite.connect(":memory:") + try: + con.create_collation("collä", cmp) + self.fail("should have raised a ProgrammingError") + except sqlite.ProgrammingError, e: + pass + + def CheckCollationIsUsed(self): + def mycoll(x, y): + # reverse order + return -cmp(x, y) + + con = sqlite.connect(":memory:") + con.create_collation("mycoll", mycoll) + sql = """ + select x from ( + select 'a' as x + union + select 'b' as x + union + select 'c' as x + ) order by x collate mycoll + """ + result = con.execute(sql).fetchall() + if result[0][0] != "c" or result[1][0] != "b" or result[2][0] != "a": + self.fail("the expected order was not returned") + + con.create_collation("mycoll", None) + try: + 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") + + def CheckCollationRegisterTwice(self): + """ + Register two different collation functions under the same name. + Verify that the last one is actually used. + """ + con = sqlite.connect(":memory:") + con.create_collation("mycoll", cmp) + con.create_collation("mycoll", lambda x, y: -cmp(x, y)) + result = con.execute(""" + select x from (select 'a' as x union select 'b' as x) order by x collate mycoll + """).fetchall() + if result[0][0] != 'b' or result[1][0] != 'a': + self.fail("wrong collation function is used") + + def CheckDeregisterCollation(self): + """ + Register a collation, then deregister it. Make sure an error is raised if we try + to use it. + """ + con = sqlite.connect(":memory:") + con.create_collation("mycoll", cmp) + con.create_collation("mycoll", None) + try: + con.execute("select 'a' as x union select 'b' as x order by x collate mycoll") + self.fail("should have raised an OperationalError") + except sqlite.OperationalError, e: + if not e.args[0].startswith("no such collation sequence"): + self.fail("wrong OperationalError raised") + +def suite(): + collation_suite = unittest.makeSuite(CollationTests, "Check") + return unittest.TestSuite((collation_suite,)) + +def test(): + runner = unittest.TextTestRunner() + runner.run(suite()) + +if __name__ == "__main__": + test() diff --git a/Lib/sqlite3/test/regression.py b/Lib/sqlite3/test/regression.py new file mode 100644 index 0000000..648ada5 --- /dev/null +++ b/Lib/sqlite3/test/regression.py @@ -0,0 +1,48 @@ +#-*- coding: ISO-8859-1 -*- +# pysqlite2/test/regression.py: pysqlite regression tests +# +# Copyright (C) 2006 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. + +import unittest +import pysqlite2.dbapi2 as sqlite + +class RegressionTests(unittest.TestCase): + def setUp(self): + self.con = sqlite.connect(":memory:") + + def tearDown(self): + self.con.close() + + def CheckPragmaUserVersion(self): + # This used to crash pysqlite because this pragma command returns NULL for the column name + cur = self.con.cursor() + cur.execute("pragma user_version") + +def suite(): + regression_suite = unittest.makeSuite(RegressionTests, "Check") + return unittest.TestSuite((regression_suite,)) + +def test(): + runner = unittest.TextTestRunner() + runner.run(suite()) + +if __name__ == "__main__": + test() diff --git a/Lib/sqlite3/test/transactions.py b/Lib/sqlite3/test/transactions.py index 28202cb..1f0b19a 100644 --- a/Lib/sqlite3/test/transactions.py +++ b/Lib/sqlite3/test/transactions.py @@ -25,7 +25,7 @@ import os, unittest import sqlite3 as sqlite def get_db_path(): - return "testdb" + return "sqlite_testdb" class TransactionTests(unittest.TestCase): def setUp(self): @@ -47,6 +47,8 @@ class TransactionTests(unittest.TestCase): self.cur2.close() self.con2.close() + os.unlink(get_db_path()) + def CheckDMLdoesAutoCommitBefore(self): self.cur1.execute("create table test(i)") self.cur1.execute("insert into test(i) values (5)") diff --git a/Modules/_sqlite/cache.c b/Modules/_sqlite/cache.c index 0c7d4a3..d36b52b 100644 --- a/Modules/_sqlite/cache.c +++ b/Modules/_sqlite/cache.c @@ -1,6 +1,6 @@ /* cache .c - a LRU cache * - * Copyright (C) 2004-2005 Gerhard Häring <gh@ghaering.de> + * Copyright (C) 2004-2006 Gerhard Häring <gh@ghaering.de> * * This file is part of pysqlite. * @@ -29,7 +29,6 @@ Node* new_node(PyObject* key, PyObject* data) Node* node; node = (Node*) (NodeType.tp_alloc(&NodeType, 0)); - /*node = PyObject_New(Node, &NodeType);*/ if (!node) { return NULL; } @@ -72,7 +71,12 @@ int cache_init(Cache* self, PyObject* args, PyObject* kwargs) self->size = size; self->first = NULL; self->last = NULL; + self->mapping = PyDict_New(); + if (!self->mapping) { + return -1; + } + Py_INCREF(factory); self->factory = factory; @@ -108,16 +112,11 @@ void cache_dealloc(Cache* self) PyObject* cache_get(Cache* self, PyObject* args) { - PyObject* key; + PyObject* key = args; Node* node; Node* ptr; PyObject* data; - if (!PyArg_ParseTuple(args, "O", &key)) - { - return NULL; - } - node = (Node*)PyDict_GetItem(self->mapping, key); if (node) { node->count++; @@ -153,7 +152,11 @@ PyObject* cache_get(Cache* self, PyObject* args) if (PyDict_Size(self->mapping) == self->size) { if (self->last) { node = self->last; - PyDict_DelItem(self->mapping, self->last->key); + + if (PyDict_DelItem(self->mapping, self->last->key) != 0) { + return NULL; + } + if (node->prev) { node->prev->next = NULL; } @@ -171,17 +174,24 @@ PyObject* cache_get(Cache* self, PyObject* args) } node = 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; - PyDict_SetItem(self->mapping, key, (PyObject*)node); } Py_INCREF(node->data); @@ -215,10 +225,19 @@ PyObject* cache_display(Cache* self, PyObject* args) 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) { + 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); @@ -233,7 +252,7 @@ PyObject* cache_display(Cache* self, PyObject* args) } static PyMethodDef cache_methods[] = { - {"get", (PyCFunction)cache_get, METH_VARARGS, + {"get", (PyCFunction)cache_get, METH_O, PyDoc_STR("Gets an entry from the cache.")}, {"display", (PyCFunction)cache_display, METH_NOARGS, PyDoc_STR("For debugging only.")}, diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 0445b38..3e97f6e 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -3,7 +3,7 @@ * Copyright (C) 2004-2006 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. @@ -28,6 +28,8 @@ #include "cursor.h" #include "prepare_protocol.h" #include "util.h" +#include "sqlitecompat.h" + #include "pythread.h" static int connection_set_isolation_level(Connection* self, PyObject* isolation_level); @@ -101,6 +103,14 @@ int connection_init(Connection* self, PyObject* args, PyObject* kwargs) 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 = Warning; self->Error = Error; @@ -167,6 +177,7 @@ void connection_dealloc(Connection* self) Py_XDECREF(self->function_pinboard); Py_XDECREF(self->row_factory); Py_XDECREF(self->text_factory); + Py_XDECREF(self->collations); self->ob_type->tp_free((PyObject*)self); } @@ -420,6 +431,9 @@ PyObject* _build_py_params(sqlite3_context *context, int argc, sqlite3_value** a void* raw_buffer; args = PyTuple_New(argc); + if (!args) { + return NULL; + } for (i = 0; i < argc; i++) { cur_value = argv[i]; @@ -655,6 +669,15 @@ static PyObject* connection_get_isolation_level(Connection* self, void* unused) return self->isolation_level; } +static PyObject* connection_get_total_changes(Connection* self, void* unused) +{ + if (!check_connection(self)) { + return NULL; + } else { + return Py_BuildValue("i", sqlite3_total_changes(self->db)); + } +} + static int connection_set_isolation_level(Connection* self, PyObject* isolation_level) { PyObject* empty; @@ -669,7 +692,13 @@ static int connection_set_isolation_level(Connection* self, PyObject* isolation_ self->isolation_level = Py_None; empty = PyTuple_New(0); + if (!empty) { + return -1; + } res = connection_commit(self, empty); + if (!res) { + return -1; + } Py_DECREF(empty); Py_DECREF(res); @@ -825,11 +854,134 @@ error: return cursor; } +/* ------------------------- COLLATION CODE ------------------------ */ + +static int +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; + PyGILState_STATE gilstate; + + PyObject* retval = NULL; + int result = 0; + + gilstate = PyGILState_Ensure(); + + 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); + + PyGILState_Release(gilstate); + + return result; +} + +static PyObject * +connection_create_collation(Connection* self, PyObject* args) +{ + PyObject* callable; + PyObject* uppercase_name = 0; + PyObject* name; + PyObject* retval; + char* chk; + int rc; + + if (!check_thread(self) || !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(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) { + PyDict_SetItem(self->collations, uppercase_name, callable); + } else { + PyDict_DelItem(self->collations, uppercase_name); + } + + rc = sqlite3_create_collation(self->db, + PyString_AsString(uppercase_name), + SQLITE_UTF8, + (callable != Py_None) ? callable : NULL, + (callable != Py_None) ? collation_callback : NULL); + if (rc != SQLITE_OK) { + PyDict_DelItem(self->collations, uppercase_name); + _seterror(self->db); + goto finally; + } + +finally: + Py_XDECREF(uppercase_name); + + if (PyErr_Occurred()) { + retval = NULL; + } else { + Py_INCREF(Py_None); + retval = Py_None; + } + + return retval; +} + static char connection_doc[] = PyDoc_STR("<missing docstring>"); static PyGetSetDef connection_getset[] = { {"isolation_level", (getter)connection_get_isolation_level, (setter)connection_set_isolation_level}, + {"total_changes", (getter)connection_get_total_changes, (setter)0}, {NULL} }; @@ -852,6 +1004,8 @@ static PyMethodDef connection_methods[] = { PyDoc_STR("Repeatedly executes a SQL statement. Non-standard.")}, {"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.")}, {NULL, NULL} }; diff --git a/Modules/_sqlite/connection.h b/Modules/_sqlite/connection.h index ef03bc4..faae6e4 100644 --- a/Modules/_sqlite/connection.h +++ b/Modules/_sqlite/connection.h @@ -69,6 +69,9 @@ typedef struct * in connection_dealloc */ PyObject* function_pinboard; + /* a dictionary of registered collation name => collation callable mappings */ + PyObject* collations; + /* Exception objects */ PyObject* Warning; PyObject* Error; diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index fe6cff9..e68a275 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -24,6 +24,7 @@ #include "cursor.h" #include "module.h" #include "util.h" +#include "sqlitecompat.h" /* used to decide wether to call PyInt_FromLong or PyLong_FromLongLong */ #define INT32_MIN (-2147483647 - 1) @@ -84,6 +85,9 @@ int cursor_init(Cursor* self, PyObject* args, PyObject* kwargs) self->next_row = NULL; self->row_cast_map = PyList_New(0); + if (!self->row_cast_map) { + return -1; + } Py_INCREF(Py_None); self->description = Py_None; @@ -94,6 +98,9 @@ int cursor_init(Cursor* self, PyObject* args, PyObject* kwargs) self->arraysize = 1; self->rowcount = PyInt_FromLong(-1L); + if (!self->rowcount) { + return -1; + } Py_INCREF(Py_None); self->row_factory = Py_None; @@ -126,7 +133,7 @@ void cursor_dealloc(Cursor* self) self->ob_type->tp_free((PyObject*)self); } -void build_row_cast_map(Cursor* self) +int build_row_cast_map(Cursor* self) { int i; const char* type_start = (const char*)-1; @@ -139,10 +146,10 @@ void build_row_cast_map(Cursor* self) PyObject* key; if (!self->connection->detect_types) { - return; + return 0; } - Py_DECREF(self->row_cast_map); + Py_XDECREF(self->row_cast_map); self->row_cast_map = PyList_New(0); for (i = 0; i < sqlite3_column_count(self->statement->st); i++) { @@ -156,6 +163,13 @@ void build_row_cast_map(Cursor* self) 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 = PyDict_GetItem(converters, key); Py_DECREF(key); break; @@ -170,6 +184,9 @@ void build_row_cast_map(Cursor* self) for (pos = decltype;;pos++) { if (*pos == ' ' || *pos == 0) { py_decltype = PyString_FromStringAndSize(decltype, pos - decltype); + if (!py_decltype) { + return -1; + } break; } } @@ -179,18 +196,33 @@ void build_row_cast_map(Cursor* self) } } - if (converter) { - PyList_Append(self->row_cast_map, converter); - } else { - PyList_Append(self->row_cast_map, Py_None); + 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* _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 == ' ') { return PyString_FromStringAndSize(colname, pos - colname); @@ -250,6 +282,9 @@ PyObject* _fetch_one_row(Cursor* self) Py_END_ALLOW_THREADS row = PyTuple_New(numcols); + if (!row) { + return NULL; + } for (i = 0; i < numcols; i++) { if (self->connection->detect_types) { @@ -268,6 +303,9 @@ PyObject* _fetch_one_row(Cursor* self) converted = Py_None; } else { item = PyString_FromString(val_str); + if (!item) { + return NULL; + } converted = PyObject_CallFunction(converter, "O", item); if (!converted) { /* TODO: have a way to log these errors */ @@ -404,10 +442,16 @@ PyObject* _query_execute(Cursor* self, int multiple, PyObject* args) if (second_argument == NULL) { second_argument = PyTuple_New(0); + if (!second_argument) { + return NULL; + } } else { Py_INCREF(second_argument); } - PyList_Append(parameters_list, second_argument); + if (PyList_Append(parameters_list, second_argument) != 0) { + Py_DECREF(second_argument); + return NULL; + } Py_DECREF(second_argument); parameters_iter = PyObject_GetIter(parameters_list); @@ -436,6 +480,9 @@ PyObject* _query_execute(Cursor* self, int multiple, PyObject* args) Py_DECREF(self->rowcount); self->rowcount = PyInt_FromLong(-1L); + if (!self->rowcount) { + goto error; + } statement_type = detect_statement_type(operation_cstr); if (self->connection->begin_statement) { @@ -457,6 +504,9 @@ PyObject* _query_execute(Cursor* self, int multiple, PyObject* args) - 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); if (!result) { @@ -471,12 +521,18 @@ PyObject* _query_execute(Cursor* self, int multiple, PyObject* args) "You cannot execute SELECT statements in executemany()."); goto error; } + break; } } func_args = PyTuple_New(1); + if (!func_args) { + goto error; + } Py_INCREF(operation); - PyTuple_SetItem(func_args, 0, operation); + if (PyTuple_SetItem(func_args, 0, operation) != 0) { + goto error; + } if (self->statement) { (void)statement_reset(self->statement); @@ -493,6 +549,9 @@ PyObject* _query_execute(Cursor* self, int multiple, PyObject* args) if (self->statement->in_use) { Py_DECREF(self->statement); self->statement = PyObject_New(Statement, &StatementType); + if (!self->statement) { + goto error; + } rc = statement_create(self->statement, self->connection, operation); if (rc != SQLITE_OK) { self->statement = 0; @@ -516,7 +575,10 @@ PyObject* _query_execute(Cursor* self, int multiple, PyObject* args) goto error; } - build_row_cast_map(self); + if (build_row_cast_map(self) != 0) { + PyErr_SetString(OperationalError, "Error while building row_cast_map"); + goto error; + } rc = _sqlite_step_with_busyhandler(self->statement->st, self->connection); if (rc != SQLITE_DONE && rc != SQLITE_ROW) { @@ -543,8 +605,14 @@ PyObject* _query_execute(Cursor* self, int multiple, PyObject* args) if (self->description == Py_None) { 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, _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); @@ -608,8 +676,8 @@ error: if (PyErr_Occurred()) { return NULL; } else { - Py_INCREF(Py_None); - return Py_None; + Py_INCREF(self); + return (PyObject*)self; } } @@ -658,6 +726,9 @@ 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); if (!result) { @@ -710,8 +781,8 @@ error: if (PyErr_Occurred()) { return NULL; } else { - Py_INCREF(Py_None); - return Py_None; + Py_INCREF(self); + return (PyObject*)self; } } @@ -789,9 +860,12 @@ PyObject* cursor_fetchmany(Cursor* self, PyObject* args) } list = PyList_New(0); + if (!list) { + return NULL; + } /* just make sure we enter the loop */ - row = (PyObject*)1; + row = Py_None; while (row) { row = cursor_iternext(self); @@ -821,9 +895,12 @@ PyObject* cursor_fetchall(Cursor* self, PyObject* args) PyObject* list; list = PyList_New(0); + if (!list) { + return NULL; + } /* just make sure we enter the loop */ - row = (PyObject*)1; + row = (PyObject*)Py_None; while (row) { row = cursor_iternext(self); diff --git a/Modules/_sqlite/microprotocols.c b/Modules/_sqlite/microprotocols.c index 5df41a1..5040acd 100644 --- a/Modules/_sqlite/microprotocols.c +++ b/Modules/_sqlite/microprotocols.c @@ -45,9 +45,7 @@ microprotocols_init(PyObject *dict) return -1; } - PyDict_SetItemString(dict, "adapters", psyco_adapters); - - return 0; + return PyDict_SetItemString(dict, "adapters", psyco_adapters); } @@ -65,8 +63,17 @@ microprotocols_add(PyTypeObject *type, PyObject *proto, PyObject *cast) cast, type->tp_name); */ + key = Py_BuildValue("(OO)", (PyObject*)type, proto); - PyDict_SetItem(psyco_adapters, key, cast); + if (!key) { + return -1; + } + + if (PyDict_SetItem(psyco_adapters, key, cast) != 0) { + Py_DECREF(key); + return -1; + } + Py_DECREF(key); return 0; @@ -85,6 +92,9 @@ microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt) /* 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) { diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index 70993d0..60d0d63 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -39,9 +39,6 @@ PyObject* Error, *Warning, *InterfaceError, *DatabaseError, *InternalError, *OperationalError, *ProgrammingError, *IntegrityError, *DataError, *NotSupportedError, *OptimizedUnicode; -PyObject* time_time; -PyObject* time_sleep; - PyObject* converters; static PyObject* module_connect(PyObject* self, PyObject* args, PyObject* @@ -150,7 +147,9 @@ static PyObject* module_register_converter(PyObject* self, PyObject* args, PyObj return NULL; } - PyDict_SetItem(converters, name, callable); + if (PyDict_SetItem(converters, name, callable) != 0) { + return NULL; + } Py_INCREF(Py_None); return Py_None; @@ -159,6 +158,9 @@ static PyObject* module_register_converter(PyObject* self, PyObject* args, PyObj void converters_init(PyObject* dict) { converters = PyDict_New(); + if (!converters) { + return; + } PyDict_SetItemString(dict, "converters", converters); } @@ -169,8 +171,8 @@ static PyMethodDef module_methods[] = { #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.")}, #endif - {"register_adapter", (PyCFunction)module_register_adapter, METH_VARARGS, PyDoc_STR("Registers an adapter with pysqlite's adapter registry.")}, - {"register_converter", (PyCFunction)module_register_converter, METH_VARARGS, PyDoc_STR("Registers a converter with pysqlite.")}, + {"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.")}, {"adapt", (PyCFunction)psyco_microprotocols_adapt, METH_VARARGS, psyco_microprotocols_adapt_doc}, {NULL, NULL} }; @@ -178,11 +180,11 @@ static PyMethodDef module_methods[] = { PyMODINIT_FUNC init_sqlite3(void) { PyObject *module, *dict; - PyObject* time_module; + PyObject *tmp_obj; module = Py_InitModule("_sqlite3", module_methods); - if ( + if (!module || (row_setup_types() < 0) || (cursor_setup_types() < 0) || (connection_setup_types() < 0) || @@ -206,56 +208,93 @@ PyMODINIT_FUNC init_sqlite3(void) Py_INCREF(&RowType); PyModule_AddObject(module, "Row", (PyObject*) &RowType); - if (!(dict = PyModule_GetDict(module))) - { + if (!(dict = PyModule_GetDict(module))) { goto error; } /*** Create DB-API Exception hierarchy */ - Error = PyErr_NewException("sqlite3.Error", PyExc_StandardError, NULL); + if (!(Error = PyErr_NewException("sqlite3.Error", PyExc_StandardError, NULL))) { + goto error; + } PyDict_SetItemString(dict, "Error", Error); - Warning = PyErr_NewException("sqlite3.Warning", PyExc_StandardError, NULL); + if (!(Warning = PyErr_NewException("sqlite3.Warning", PyExc_StandardError, NULL))) { + goto error; + } PyDict_SetItemString(dict, "Warning", Warning); /* Error subclasses */ - InterfaceError = PyErr_NewException("sqlite3.InterfaceError", Error, NULL); + if (!(InterfaceError = PyErr_NewException("sqlite3.InterfaceError", Error, NULL))) { + goto error; + } PyDict_SetItemString(dict, "InterfaceError", InterfaceError); - DatabaseError = PyErr_NewException("sqlite3.DatabaseError", Error, NULL); + if (!(DatabaseError = PyErr_NewException("sqlite3.DatabaseError", Error, NULL))) { + goto error; + } PyDict_SetItemString(dict, "DatabaseError", DatabaseError); /* DatabaseError subclasses */ - InternalError = PyErr_NewException("sqlite3.InternalError", DatabaseError, NULL); + if (!(InternalError = PyErr_NewException("sqlite3.InternalError", DatabaseError, NULL))) { + goto error; + } PyDict_SetItemString(dict, "InternalError", InternalError); - OperationalError = PyErr_NewException("sqlite3.OperationalError", DatabaseError, NULL); + if (!(OperationalError = PyErr_NewException("sqlite3.OperationalError", DatabaseError, NULL))) { + goto error; + } PyDict_SetItemString(dict, "OperationalError", OperationalError); - ProgrammingError = PyErr_NewException("sqlite3.ProgrammingError", DatabaseError, NULL); + if (!(ProgrammingError = PyErr_NewException("sqlite3.ProgrammingError", DatabaseError, NULL))) { + goto error; + } PyDict_SetItemString(dict, "ProgrammingError", ProgrammingError); - IntegrityError = PyErr_NewException("sqlite3.IntegrityError", DatabaseError,NULL); + if (!(IntegrityError = PyErr_NewException("sqlite3.IntegrityError", DatabaseError,NULL))) { + goto error; + } PyDict_SetItemString(dict, "IntegrityError", IntegrityError); - DataError = PyErr_NewException("sqlite3.DataError", DatabaseError, NULL); + if (!(DataError = PyErr_NewException("sqlite3.DataError", DatabaseError, NULL))) { + goto error; + } PyDict_SetItemString(dict, "DataError", DataError); - NotSupportedError = PyErr_NewException("sqlite3.NotSupportedError", DatabaseError, NULL); + if (!(NotSupportedError = PyErr_NewException("sqlite3.NotSupportedError", DatabaseError, NULL))) { + goto error; + } PyDict_SetItemString(dict, "NotSupportedError", NotSupportedError); + /* We just need "something" unique for 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); OptimizedUnicode = (PyObject*)&PyCell_Type; PyDict_SetItemString(dict, "OptimizedUnicode", OptimizedUnicode); - PyDict_SetItemString(dict, "PARSE_DECLTYPES", PyInt_FromLong(PARSE_DECLTYPES)); - PyDict_SetItemString(dict, "PARSE_COLNAMES", PyInt_FromLong(PARSE_COLNAMES)); + if (!(tmp_obj = PyInt_FromLong(PARSE_DECLTYPES))) { + goto error; + } + PyDict_SetItemString(dict, "PARSE_DECLTYPES", tmp_obj); - PyDict_SetItemString(dict, "version", PyString_FromString(PYSQLITE_VERSION)); - PyDict_SetItemString(dict, "sqlite_version", PyString_FromString(sqlite3_libversion())); + if (!(tmp_obj = PyInt_FromLong(PARSE_COLNAMES))) { + goto error; + } + PyDict_SetItemString(dict, "PARSE_COLNAMES", tmp_obj); + + if (!(tmp_obj = PyString_FromString(PYSQLITE_VERSION))) { + goto error; + } + PyDict_SetItemString(dict, "version", tmp_obj); + + if (!(tmp_obj = PyString_FromString(sqlite3_libversion()))) { + goto error; + } + PyDict_SetItemString(dict, "sqlite_version", tmp_obj); /* initialize microprotocols layer */ microprotocols_init(dict); @@ -263,10 +302,6 @@ PyMODINIT_FUNC init_sqlite3(void) /* initialize the default converters */ converters_init(dict); - time_module = PyImport_ImportModule("time"); - time_time = PyObject_GetAttrString(time_module, "time"); - time_sleep = PyObject_GetAttrString(time_module, "sleep"); - /* Original comment form _bsddb.c in the Python core. This is also still * needed nowadays for Python 2.3/2.4. * diff --git a/Modules/_sqlite/prepare_protocol.c b/Modules/_sqlite/prepare_protocol.c index 2e8349c..522f1d1 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 Gerhard Häring <gh@ghaering.de> + * Copyright (C) 2005-2006 Gerhard Häring <gh@ghaering.de> * * This file is part of pysqlite. * diff --git a/Modules/_sqlite/row.c b/Modules/_sqlite/row.c index 61de801..77c7896 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 Gerhard Häring <gh@ghaering.de> + * Copyright (C) 2005-2006 Gerhard Häring <gh@ghaering.de> * * This file is part of pysqlite. * @@ -23,6 +23,7 @@ #include "row.h" #include "cursor.h" +#include "sqlitecompat.h" void row_dealloc(Row* self) { @@ -78,9 +79,12 @@ PyObject* row_subscript(Row* self, PyObject* idx) if (PyInt_Check(idx)) { _idx = PyInt_AsLong(idx); item = PyTuple_GetItem(self->data, _idx); - if (item) { - Py_INCREF(item); - } + 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); @@ -89,6 +93,9 @@ PyObject* row_subscript(Row* self, PyObject* idx) 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; diff --git a/Modules/_sqlite/sqlitecompat.h b/Modules/_sqlite/sqlitecompat.h new file mode 100644 index 0000000..c379825 --- /dev/null +++ b/Modules/_sqlite/sqlitecompat.h @@ -0,0 +1,34 @@ +/* sqlitecompat.h - compatibility macros + * + * Copyright (C) 2006 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_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 + +#endif diff --git a/Modules/_sqlite/statement.c b/Modules/_sqlite/statement.c index 4a957d6..ae48b4b 100644 --- a/Modules/_sqlite/statement.c +++ b/Modules/_sqlite/statement.c @@ -26,6 +26,7 @@ #include "connection.h" #include "microprotocols.h" #include "prepare_protocol.h" +#include "sqlitecompat.h" /* prototypes */ int check_remaining_sql(const char* tail); diff --git a/Modules/_sqlite/util.c b/Modules/_sqlite/util.c index ec400a1..33748a6 100644 --- a/Modules/_sqlite/util.c +++ b/Modules/_sqlite/util.c @@ -1,6 +1,6 @@ /* util.c - various utility functions * - * Copyright (C) 2005 Gerhard Häring <gh@ghaering.de> + * Copyright (C) 2005-2006 Gerhard Häring <gh@ghaering.de> * * This file is part of pysqlite. * @@ -24,30 +24,6 @@ #include "module.h" #include "connection.h" -/* - * it's not so trivial to write a portable sleep in C. For now, the simplest - * solution is to just use Python's sleep(). - */ -void pysqlite_sleep(double seconds) -{ - PyObject* ret; - - ret = PyObject_CallFunction(time_sleep, "d", seconds); - Py_DECREF(ret); -} - -double pysqlite_time(void) -{ - PyObject* ret; - double time; - - ret = PyObject_CallFunction(time_time, ""); - time = PyFloat_AsDouble(ret); - Py_DECREF(ret); - - return time; -} - int _sqlite_step_with_busyhandler(sqlite3_stmt* statement, Connection* connection ) { @@ -75,63 +51,35 @@ int _seterror(sqlite3* db) case SQLITE_OK: PyErr_Clear(); break; - case SQLITE_ERROR: - PyErr_SetString(OperationalError, sqlite3_errmsg(db)); - break; case SQLITE_INTERNAL: + case SQLITE_NOTFOUND: PyErr_SetString(InternalError, sqlite3_errmsg(db)); break; - case SQLITE_PERM: - PyErr_SetString(OperationalError, sqlite3_errmsg(db)); + case SQLITE_NOMEM: + (void)PyErr_NoMemory(); break; + case SQLITE_ERROR: + case SQLITE_PERM: case SQLITE_ABORT: - PyErr_SetString(OperationalError, sqlite3_errmsg(db)); - break; case SQLITE_BUSY: - PyErr_SetString(OperationalError, sqlite3_errmsg(db)); - break; case SQLITE_LOCKED: - PyErr_SetString(OperationalError, sqlite3_errmsg(db)); - break; - case SQLITE_NOMEM: - (void)PyErr_NoMemory(); - break; case SQLITE_READONLY: - PyErr_SetString(OperationalError, sqlite3_errmsg(db)); - break; case SQLITE_INTERRUPT: - PyErr_SetString(OperationalError, sqlite3_errmsg(db)); - break; case SQLITE_IOERR: - PyErr_SetString(OperationalError, sqlite3_errmsg(db)); - break; - case SQLITE_CORRUPT: - PyErr_SetString(DatabaseError, sqlite3_errmsg(db)); - break; - case SQLITE_NOTFOUND: - PyErr_SetString(InternalError, sqlite3_errmsg(db)); - break; case SQLITE_FULL: - PyErr_SetString(OperationalError, sqlite3_errmsg(db)); - break; case SQLITE_CANTOPEN: - PyErr_SetString(OperationalError, sqlite3_errmsg(db)); - break; case SQLITE_PROTOCOL: - PyErr_SetString(OperationalError, sqlite3_errmsg(db)); - break; case SQLITE_EMPTY: - PyErr_SetString(OperationalError, sqlite3_errmsg(db)); - break; case SQLITE_SCHEMA: PyErr_SetString(OperationalError, sqlite3_errmsg(db)); break; + case SQLITE_CORRUPT: + PyErr_SetString(DatabaseError, sqlite3_errmsg(db)); + break; case SQLITE_TOOBIG: PyErr_SetString(DataError, sqlite3_errmsg(db)); break; case SQLITE_CONSTRAINT: - PyErr_SetString(IntegrityError, sqlite3_errmsg(db)); - break; case SQLITE_MISMATCH: PyErr_SetString(IntegrityError, sqlite3_errmsg(db)); break; @@ -140,6 +88,7 @@ int _seterror(sqlite3* db) break; default: PyErr_SetString(DatabaseError, sqlite3_errmsg(db)); + break; } return errorcode; diff --git a/Modules/_sqlite/util.h b/Modules/_sqlite/util.h index 6e74b2d..e99a4dd 100644 --- a/Modules/_sqlite/util.h +++ b/Modules/_sqlite/util.h @@ -1,6 +1,6 @@ /* util.h - various utility functions * - * Copyright (C) 2005 Gerhard Häring <gh@ghaering.de> + * Copyright (C) 2005-2006 Gerhard Häring <gh@ghaering.de> * * This file is part of pysqlite. * @@ -28,8 +28,6 @@ #include "sqlite3.h" #include "connection.h" -void pysqlite_sleep(double seconds); - int _sqlite_step_with_busyhandler(sqlite3_stmt* statement, Connection* connection); /** @@ -752,7 +752,7 @@ class PyBuildExt(build_ext): '_sqlite/statement.c', '_sqlite/util.c', ] - PYSQLITE_VERSION = "2.1.3" + PYSQLITE_VERSION = "2.2.0" sqlite_defines = [] if sys.platform != "win32": sqlite_defines.append(('PYSQLITE_VERSION', |