Kaydet (Commit) e7ea7451 authored tarafından Gerhard Häring's avatar Gerhard Häring

Bring sqlite3 module up-to-date with what's now in 2.6. Almost. I intentionally

left out the stuff about creating a connection object from a APSW connection.
üst b1b9382d
#-*- coding: ISO-8859-1 -*- #-*- coding: ISO-8859-1 -*-
# pysqlite2/test/dbapi.py: tests for DB-API compliance # pysqlite2/test/dbapi.py: tests for DB-API compliance
# #
# Copyright (C) 2004-2005 Gerhard Hring <gh@ghaering.de> # Copyright (C) 2004-2007 Gerhard Hring <gh@ghaering.de>
# #
# This file is part of pysqlite. # This file is part of pysqlite.
# #
...@@ -223,12 +223,41 @@ class CursorTests(unittest.TestCase): ...@@ -223,12 +223,41 @@ class CursorTests(unittest.TestCase):
except sqlite.ProgrammingError: except sqlite.ProgrammingError:
pass pass
def CheckExecuteParamList(self):
self.cu.execute("insert into test(name) values ('foo')")
self.cu.execute("select name from test where name=?", ["foo"])
row = self.cu.fetchone()
self.failUnlessEqual(row[0], "foo")
def CheckExecuteParamSequence(self):
class L(object):
def __len__(self):
return 1
def __getitem__(self, x):
assert x == 0
return "foo"
self.cu.execute("insert into test(name) values ('foo')")
self.cu.execute("select name from test where name=?", L())
row = self.cu.fetchone()
self.failUnlessEqual(row[0], "foo")
def CheckExecuteDictMapping(self): def CheckExecuteDictMapping(self):
self.cu.execute("insert into test(name) values ('foo')") self.cu.execute("insert into test(name) values ('foo')")
self.cu.execute("select name from test where name=:name", {"name": "foo"}) self.cu.execute("select name from test where name=:name", {"name": "foo"})
row = self.cu.fetchone() row = self.cu.fetchone()
self.failUnlessEqual(row[0], "foo") self.failUnlessEqual(row[0], "foo")
def CheckExecuteDictMapping_Mapping(self):
class D(dict):
def __missing__(self, key):
return "foo"
self.cu.execute("insert into test(name) values ('foo')")
self.cu.execute("select name from test where name=:name", D())
row = self.cu.fetchone()
self.failUnlessEqual(row[0], "foo")
def CheckExecuteDictMappingTooLittleArgs(self): def CheckExecuteDictMappingTooLittleArgs(self):
self.cu.execute("insert into test(name) values ('foo')") self.cu.execute("insert into test(name) values ('foo')")
try: try:
...@@ -378,6 +407,12 @@ class CursorTests(unittest.TestCase): ...@@ -378,6 +407,12 @@ class CursorTests(unittest.TestCase):
res = self.cu.fetchmany(100) res = self.cu.fetchmany(100)
self.failUnlessEqual(res, []) self.failUnlessEqual(res, [])
def CheckFetchmanyKwArg(self):
"""Checks if fetchmany works with keyword arguments"""
self.cu.execute("select name from test")
res = self.cu.fetchmany(size=100)
self.failUnlessEqual(len(res), 1)
def CheckFetchall(self): def CheckFetchall(self):
self.cu.execute("select name from test") self.cu.execute("select name from test")
res = self.cu.fetchall() res = self.cu.fetchall()
...@@ -609,20 +644,6 @@ class ExtensionTests(unittest.TestCase): ...@@ -609,20 +644,6 @@ class ExtensionTests(unittest.TestCase):
res = cur.fetchone()[0] res = cur.fetchone()[0]
self.failUnlessEqual(res, 5) self.failUnlessEqual(res, 5)
def CheckScriptStringUnicode(self):
con = sqlite.connect(":memory:")
cur = con.cursor()
cur.executescript("""
create table a(i);
insert into a(i) values (5);
select i from a;
delete from a;
insert into a(i) values (6);
""")
cur.execute("select i from a")
res = cur.fetchone()[0]
self.failUnlessEqual(res, 6)
def CheckScriptErrorIncomplete(self): def CheckScriptErrorIncomplete(self):
con = sqlite.connect(":memory:") con = sqlite.connect(":memory:")
cur = con.cursor() cur = con.cursor()
......
#-*- coding: ISO-8859-1 -*- #-*- coding: ISO-8859-1 -*-
# pysqlite2/test/factory.py: tests for the various factories in pysqlite # pysqlite2/test/factory.py: tests for the various factories in pysqlite
# #
# Copyright (C) 2005 Gerhard Hring <gh@ghaering.de> # Copyright (C) 2005-2007 Gerhard Hring <gh@ghaering.de>
# #
# This file is part of pysqlite. # This file is part of pysqlite.
# #
......
#-*- coding: ISO-8859-1 -*- #-*- coding: ISO-8859-1 -*-
# pysqlite2/test/hooks.py: tests for various SQLite-specific hooks # pysqlite2/test/hooks.py: tests for various SQLite-specific hooks
# #
# Copyright (C) 2006 Gerhard Häring <gh@ghaering.de> # Copyright (C) 2006-2007 Gerhard Häring <gh@ghaering.de>
# #
# This file is part of pysqlite. # This file is part of pysqlite.
# #
...@@ -105,9 +105,80 @@ class CollationTests(unittest.TestCase): ...@@ -105,9 +105,80 @@ class CollationTests(unittest.TestCase):
if not e.args[0].startswith("no such collation sequence"): if not e.args[0].startswith("no such collation sequence"):
self.fail("wrong OperationalError raised") self.fail("wrong OperationalError raised")
class ProgressTests(unittest.TestCase):
def CheckProgressHandlerUsed(self):
"""
Test that the progress handler is invoked once it is set.
"""
con = sqlite.connect(":memory:")
progress_calls = []
def progress():
progress_calls.append(None)
return 0
con.set_progress_handler(progress, 1)
con.execute("""
create table foo(a, b)
""")
self.failUnless(progress_calls)
def CheckOpcodeCount(self):
"""
Test that the opcode argument is respected.
"""
con = sqlite.connect(":memory:")
progress_calls = []
def progress():
progress_calls.append(None)
return 0
con.set_progress_handler(progress, 1)
curs = con.cursor()
curs.execute("""
create table foo (a, b)
""")
first_count = len(progress_calls)
progress_calls = []
con.set_progress_handler(progress, 2)
curs.execute("""
create table bar (a, b)
""")
second_count = len(progress_calls)
self.failUnless(first_count > second_count)
def CheckCancelOperation(self):
"""
Test that returning a non-zero value stops the operation in progress.
"""
con = sqlite.connect(":memory:")
progress_calls = []
def progress():
progress_calls.append(None)
return 1
con.set_progress_handler(progress, 1)
curs = con.cursor()
self.assertRaises(
sqlite.OperationalError,
curs.execute,
"create table bar (a, b)")
def CheckClearHandler(self):
"""
Test that setting the progress handler to None clears the previously set handler.
"""
con = sqlite.connect(":memory:")
action = 0
def progress():
action = 1
return 0
con.set_progress_handler(progress, 1)
con.set_progress_handler(None, 1)
con.execute("select 1 union select 2 union select 3").fetchall()
self.failUnlessEqual(action, 0, "progress handler was not cleared")
def suite(): def suite():
collation_suite = unittest.makeSuite(CollationTests, "Check") collation_suite = unittest.makeSuite(CollationTests, "Check")
return unittest.TestSuite((collation_suite,)) progress_suite = unittest.makeSuite(ProgressTests, "Check")
return unittest.TestSuite((collation_suite, progress_suite))
def test(): def test():
runner = unittest.TextTestRunner() runner = unittest.TextTestRunner()
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
# misrepresented as being the original software. # misrepresented as being the original software.
# 3. This notice may not be removed or altered from any source distribution. # 3. This notice may not be removed or altered from any source distribution.
import datetime
import unittest import unittest
import sqlite3 as sqlite import sqlite3 as sqlite
...@@ -79,6 +80,67 @@ class RegressionTests(unittest.TestCase): ...@@ -79,6 +80,67 @@ class RegressionTests(unittest.TestCase):
cur.fetchone() cur.fetchone()
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
# referenced in cursors weren't closed an could provoke "
# "OperationalError: Unable to close due to unfinalised statements".
con = sqlite.connect(":memory:")
cursors = []
# default statement cache size is 100
for i in range(105):
cur = con.cursor()
cursors.append(cur)
cur.execute("select 1 x union select " + str(i))
con.close()
def CheckOnConflictRollback(self):
if sqlite.sqlite_version_info < (3, 2, 2):
return
con = sqlite.connect(":memory:")
con.execute("create table foo(x, unique(x) on conflict rollback)")
con.execute("insert into foo(x) values (1)")
try:
con.execute("insert into foo(x) values (1)")
except sqlite.DatabaseError:
pass
con.execute("insert into foo(x) values (2)")
try:
con.commit()
except sqlite.OperationalError:
self.fail("pysqlite knew nothing about the implicit ROLLBACK")
def CheckWorkaroundForBuggySqliteTransferBindings(self):
"""
pysqlite would crash with older SQLite versions unless
a workaround is implemented.
"""
self.con.execute("create table foo(bar)")
self.con.execute("drop table foo")
self.con.execute("create table foo(bar)")
def CheckEmptyStatement(self):
"""
pysqlite used to segfault with SQLite versions 3.5.x. These return NULL
for "no-operation" statements
"""
self.con.execute("")
def CheckTypeMapUsage(self):
"""
pysqlite until 2.4.1 did not rebuild the row_cast_map when recompiling
a statement. This test exhibits the problem.
"""
SELECT = "select * from foo"
con = sqlite.connect(":memory:",detect_types=sqlite.PARSE_DECLTYPES)
con.execute("create table foo(bar timestamp)")
con.execute("insert into foo(bar) values (?)", (datetime.datetime.now(),))
con.execute(SELECT)
con.execute("drop table foo")
con.execute("create table foo(bar integer)")
con.execute("insert into foo(bar) values (5)")
con.execute(SELECT)
def CheckErrorMsgDecodeError(self): def CheckErrorMsgDecodeError(self):
# When porting the module to Python 3.0, the error message about # When porting the module to Python 3.0, the error message about
# decoding errors disappeared. This verifies they're back again. # decoding errors disappeared. This verifies they're back again.
......
#-*- coding: ISO-8859-1 -*- #-*- coding: ISO-8859-1 -*-
# pysqlite2/test/transactions.py: tests transactions # pysqlite2/test/transactions.py: tests transactions
# #
# Copyright (C) 2005 Gerhard Hring <gh@ghaering.de> # Copyright (C) 2005-2007 Gerhard Hring <gh@ghaering.de>
# #
# This file is part of pysqlite. # This file is part of pysqlite.
# #
...@@ -122,6 +122,23 @@ class TransactionTests(unittest.TestCase): ...@@ -122,6 +122,23 @@ class TransactionTests(unittest.TestCase):
except: except:
self.fail("should have raised an OperationalError") self.fail("should have raised an OperationalError")
def CheckLocking(self):
"""
This tests the improved concurrency with pysqlite 2.3.4. You needed
to roll back con2 before you could commit con1.
"""
self.cur1.execute("create table test(i)")
self.cur1.execute("insert into test(i) values (5)")
try:
self.cur2.execute("insert into test(i) values (5)")
self.fail("should have raised an OperationalError")
except sqlite.OperationalError:
pass
except:
self.fail("should have raised an OperationalError")
# NO self.con2.rollback() HERE!!!
self.con1.commit()
class SpecialCommandTests(unittest.TestCase): class SpecialCommandTests(unittest.TestCase):
def setUp(self): def setUp(self):
self.con = sqlite.connect(":memory:") self.con = sqlite.connect(":memory:")
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
# misrepresented as being the original software. # misrepresented as being the original software.
# 3. This notice may not be removed or altered from any source distribution. # 3. This notice may not be removed or altered from any source distribution.
import bz2, datetime import zlib, datetime
import unittest import unittest
import sqlite3 as sqlite import sqlite3 as sqlite
...@@ -221,11 +221,13 @@ class ColNamesTests(unittest.TestCase): ...@@ -221,11 +221,13 @@ class ColNamesTests(unittest.TestCase):
self.cur = self.con.cursor() self.cur = self.con.cursor()
self.cur.execute("create table test(x foo)") self.cur.execute("create table test(x foo)")
sqlite.converters["BAR"] = lambda x: b"<" + x + b">" sqlite.converters["FOO"] = lambda x: "[%s]" % x.decode("ascii")
sqlite.converters["BAR"] = lambda x: "<%s>" % x.decode("ascii")
sqlite.converters["EXC"] = lambda x: 5/0 sqlite.converters["EXC"] = lambda x: 5/0
sqlite.converters["B1B1"] = lambda x: "MARKER" sqlite.converters["B1B1"] = lambda x: "MARKER"
def tearDown(self): def tearDown(self):
del sqlite.converters["FOO"]
del sqlite.converters["BAR"] del sqlite.converters["BAR"]
del sqlite.converters["EXC"] del sqlite.converters["EXC"]
del sqlite.converters["B1B1"] del sqlite.converters["B1B1"]
...@@ -252,7 +254,7 @@ class ColNamesTests(unittest.TestCase): ...@@ -252,7 +254,7 @@ class ColNamesTests(unittest.TestCase):
self.cur.execute("insert into test(x) values (?)", ("xxx",)) self.cur.execute("insert into test(x) values (?)", ("xxx",))
self.cur.execute('select x as "x [bar]" from test') self.cur.execute('select x as "x [bar]" from test')
val = self.cur.fetchone()[0] val = self.cur.fetchone()[0]
self.failUnlessEqual(val, b"<xxx>") self.failUnlessEqual(val, "<xxx>")
# Check if the stripping of colnames works. Everything after the first # Check if the stripping of colnames works. Everything after the first
# whitespace should be stripped. # whitespace should be stripped.
...@@ -297,7 +299,7 @@ class ObjectAdaptationTests(unittest.TestCase): ...@@ -297,7 +299,7 @@ class ObjectAdaptationTests(unittest.TestCase):
class BinaryConverterTests(unittest.TestCase): class BinaryConverterTests(unittest.TestCase):
def convert(s): def convert(s):
return bz2.decompress(s) return zlib.decompress(s)
convert = staticmethod(convert) convert = staticmethod(convert)
def setUp(self): def setUp(self):
...@@ -309,7 +311,7 @@ class BinaryConverterTests(unittest.TestCase): ...@@ -309,7 +311,7 @@ class BinaryConverterTests(unittest.TestCase):
def CheckBinaryInputForConverter(self): def CheckBinaryInputForConverter(self):
testdata = b"abcdefg" * 10 testdata = b"abcdefg" * 10
result = self.con.execute('select ? as "x [bin]"', (memoryview(bz2.compress(testdata)),)).fetchone()[0] result = self.con.execute('select ? as "x [bin]"', (memoryview(zlib.compress(testdata)),)).fetchone()[0]
self.failUnlessEqual(testdata, result) self.failUnlessEqual(testdata, result)
class DateTimeTests(unittest.TestCase): class DateTimeTests(unittest.TestCase):
...@@ -341,7 +343,8 @@ class DateTimeTests(unittest.TestCase): ...@@ -341,7 +343,8 @@ class DateTimeTests(unittest.TestCase):
if sqlite.sqlite_version_info < (3, 1): if sqlite.sqlite_version_info < (3, 1):
return return
now = datetime.datetime.utcnow() # SQLite's current_timestamp uses UTC time, while datetime.datetime.now() uses local time.
now = datetime.datetime.now()
self.cur.execute("insert into test(ts) values (current_timestamp)") self.cur.execute("insert into test(ts) values (current_timestamp)")
self.cur.execute("select ts from test") self.cur.execute("select ts from test")
ts = self.cur.fetchone()[0] ts = self.cur.fetchone()[0]
......
/* cache .c - a LRU cache /* cache .c - a LRU cache
* *
* Copyright (C) 2004-2006 Gerhard Hring <gh@ghaering.de> * Copyright (C) 2004-2007 Gerhard Hring <gh@ghaering.de>
* *
* This file is part of pysqlite. * This file is part of pysqlite.
* *
......
/* cache.h - definitions for the LRU cache /* cache.h - definitions for the LRU cache
* *
* Copyright (C) 2004-2006 Gerhard Häring <gh@ghaering.de> * Copyright (C) 2004-2007 Gerhard Häring <gh@ghaering.de>
* *
* This file is part of pysqlite. * This file is part of pysqlite.
* *
......
/* connection.c - the connection type /* connection.c - the connection type
* *
* Copyright (C) 2004-2006 Gerhard H�ring <gh@ghaering.de> * Copyright (C) 2004-2007 Gerhard Hring <gh@ghaering.de>
* *
* This file is part of pysqlite. * This file is part of pysqlite.
* *
...@@ -32,6 +32,9 @@ ...@@ -32,6 +32,9 @@
#include "pythread.h" #include "pythread.h"
#define ACTION_FINALIZE 1
#define ACTION_RESET 2
static int pysqlite_connection_set_isolation_level(pysqlite_Connection* self, PyObject* isolation_level); static int pysqlite_connection_set_isolation_level(pysqlite_Connection* self, PyObject* isolation_level);
...@@ -63,7 +66,7 @@ int pysqlite_connection_init(pysqlite_Connection* self, PyObject* args, PyObject ...@@ -63,7 +66,7 @@ int pysqlite_connection_init(pysqlite_Connection* self, PyObject* args, PyObject
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|diOiOi", kwlist, if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|diOiOi", kwlist,
&database, &timeout, &detect_types, &isolation_level, &check_same_thread, &factory, &cached_statements)) &database, &timeout, &detect_types, &isolation_level, &check_same_thread, &factory, &cached_statements))
{ {
return -1; return -1;
} }
self->begin_statement = NULL; self->begin_statement = NULL;
...@@ -82,7 +85,7 @@ int pysqlite_connection_init(pysqlite_Connection* self, PyObject* args, PyObject ...@@ -82,7 +85,7 @@ int pysqlite_connection_init(pysqlite_Connection* self, PyObject* args, PyObject
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (rc != SQLITE_OK) { if (rc != SQLITE_OK) {
_pysqlite_seterror(self->db); _pysqlite_seterror(self->db, NULL);
return -1; return -1;
} }
...@@ -169,7 +172,8 @@ void pysqlite_flush_statement_cache(pysqlite_Connection* self) ...@@ -169,7 +172,8 @@ void pysqlite_flush_statement_cache(pysqlite_Connection* self)
self->statement_cache->decref_factory = 0; self->statement_cache->decref_factory = 0;
} }
void pysqlite_reset_all_statements(pysqlite_Connection* self) /* action in (ACTION_RESET, ACTION_FINALIZE) */
void pysqlite_do_all_statements(pysqlite_Connection* self, int action)
{ {
int i; int i;
PyObject* weakref; PyObject* weakref;
...@@ -179,7 +183,11 @@ void pysqlite_reset_all_statements(pysqlite_Connection* self) ...@@ -179,7 +183,11 @@ void pysqlite_reset_all_statements(pysqlite_Connection* self)
weakref = PyList_GetItem(self->statements, i); weakref = PyList_GetItem(self->statements, i);
statement = PyWeakref_GetObject(weakref); statement = PyWeakref_GetObject(weakref);
if (statement != Py_None) { if (statement != Py_None) {
(void)pysqlite_statement_reset((pysqlite_Statement*)statement); if (action == ACTION_RESET) {
(void)pysqlite_statement_reset((pysqlite_Statement*)statement);
} else {
(void)pysqlite_statement_finalize((pysqlite_Statement*)statement);
}
} }
} }
} }
...@@ -247,7 +255,7 @@ PyObject* pysqlite_connection_close(pysqlite_Connection* self, PyObject* args) ...@@ -247,7 +255,7 @@ PyObject* pysqlite_connection_close(pysqlite_Connection* self, PyObject* args)
return NULL; return NULL;
} }
pysqlite_flush_statement_cache(self); pysqlite_do_all_statements(self, ACTION_FINALIZE);
if (self->db) { if (self->db) {
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
...@@ -255,7 +263,7 @@ PyObject* pysqlite_connection_close(pysqlite_Connection* self, PyObject* args) ...@@ -255,7 +263,7 @@ PyObject* pysqlite_connection_close(pysqlite_Connection* self, PyObject* args)
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (rc != SQLITE_OK) { if (rc != SQLITE_OK) {
_pysqlite_seterror(self->db); _pysqlite_seterror(self->db, NULL);
return NULL; return NULL;
} else { } else {
self->db = NULL; self->db = NULL;
...@@ -292,7 +300,7 @@ PyObject* _pysqlite_connection_begin(pysqlite_Connection* self) ...@@ -292,7 +300,7 @@ PyObject* _pysqlite_connection_begin(pysqlite_Connection* self)
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (rc != SQLITE_OK) { if (rc != SQLITE_OK) {
_pysqlite_seterror(self->db); _pysqlite_seterror(self->db, statement);
goto error; goto error;
} }
...@@ -300,7 +308,7 @@ PyObject* _pysqlite_connection_begin(pysqlite_Connection* self) ...@@ -300,7 +308,7 @@ PyObject* _pysqlite_connection_begin(pysqlite_Connection* self)
if (rc == SQLITE_DONE) { if (rc == SQLITE_DONE) {
self->inTransaction = 1; self->inTransaction = 1;
} else { } else {
_pysqlite_seterror(self->db); _pysqlite_seterror(self->db, statement);
} }
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
...@@ -308,7 +316,7 @@ PyObject* _pysqlite_connection_begin(pysqlite_Connection* self) ...@@ -308,7 +316,7 @@ PyObject* _pysqlite_connection_begin(pysqlite_Connection* self)
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (rc != SQLITE_OK && !PyErr_Occurred()) { if (rc != SQLITE_OK && !PyErr_Occurred()) {
_pysqlite_seterror(self->db); _pysqlite_seterror(self->db, NULL);
} }
error: error:
...@@ -335,7 +343,7 @@ PyObject* pysqlite_connection_commit(pysqlite_Connection* self, PyObject* args) ...@@ -335,7 +343,7 @@ PyObject* pysqlite_connection_commit(pysqlite_Connection* self, PyObject* args)
rc = sqlite3_prepare(self->db, "COMMIT", -1, &statement, &tail); rc = sqlite3_prepare(self->db, "COMMIT", -1, &statement, &tail);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (rc != SQLITE_OK) { if (rc != SQLITE_OK) {
_pysqlite_seterror(self->db); _pysqlite_seterror(self->db, NULL);
goto error; goto error;
} }
...@@ -343,14 +351,14 @@ PyObject* pysqlite_connection_commit(pysqlite_Connection* self, PyObject* args) ...@@ -343,14 +351,14 @@ PyObject* pysqlite_connection_commit(pysqlite_Connection* self, PyObject* args)
if (rc == SQLITE_DONE) { if (rc == SQLITE_DONE) {
self->inTransaction = 0; self->inTransaction = 0;
} else { } else {
_pysqlite_seterror(self->db); _pysqlite_seterror(self->db, statement);
} }
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
rc = sqlite3_finalize(statement); rc = sqlite3_finalize(statement);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (rc != SQLITE_OK && !PyErr_Occurred()) { if (rc != SQLITE_OK && !PyErr_Occurred()) {
_pysqlite_seterror(self->db); _pysqlite_seterror(self->db, NULL);
} }
} }
...@@ -375,13 +383,13 @@ PyObject* pysqlite_connection_rollback(pysqlite_Connection* self, PyObject* args ...@@ -375,13 +383,13 @@ PyObject* pysqlite_connection_rollback(pysqlite_Connection* self, PyObject* args
} }
if (self->inTransaction) { if (self->inTransaction) {
pysqlite_reset_all_statements(self); pysqlite_do_all_statements(self, ACTION_RESET);
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
rc = sqlite3_prepare(self->db, "ROLLBACK", -1, &statement, &tail); rc = sqlite3_prepare(self->db, "ROLLBACK", -1, &statement, &tail);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (rc != SQLITE_OK) { if (rc != SQLITE_OK) {
_pysqlite_seterror(self->db); _pysqlite_seterror(self->db, NULL);
goto error; goto error;
} }
...@@ -389,14 +397,14 @@ PyObject* pysqlite_connection_rollback(pysqlite_Connection* self, PyObject* args ...@@ -389,14 +397,14 @@ PyObject* pysqlite_connection_rollback(pysqlite_Connection* self, PyObject* args
if (rc == SQLITE_DONE) { if (rc == SQLITE_DONE) {
self->inTransaction = 0; self->inTransaction = 0;
} else { } else {
_pysqlite_seterror(self->db); _pysqlite_seterror(self->db, statement);
} }
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
rc = sqlite3_finalize(statement); rc = sqlite3_finalize(statement);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (rc != SQLITE_OK && !PyErr_Occurred()) { if (rc != SQLITE_OK && !PyErr_Occurred()) {
_pysqlite_seterror(self->db); _pysqlite_seterror(self->db, NULL);
} }
} }
...@@ -746,6 +754,33 @@ static int _authorizer_callback(void* user_arg, int action, const char* arg1, co ...@@ -746,6 +754,33 @@ static int _authorizer_callback(void* user_arg, int action, const char* arg1, co
return rc; return rc;
} }
static int _progress_handler(void* user_arg)
{
int rc;
PyObject *ret;
PyGILState_STATE gilstate;
gilstate = PyGILState_Ensure();
ret = PyObject_CallFunction((PyObject*)user_arg, "");
if (!ret) {
if (_enable_callback_tracebacks) {
PyErr_Print();
} else {
PyErr_Clear();
}
/* abort query if error occured */
rc = 1;
} else {
rc = (int)PyObject_IsTrue(ret);
Py_DECREF(ret);
}
PyGILState_Release(gilstate);
return rc;
}
PyObject* pysqlite_connection_set_authorizer(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) PyObject* pysqlite_connection_set_authorizer(pysqlite_Connection* self, PyObject* args, PyObject* kwargs)
{ {
PyObject* authorizer_cb; PyObject* authorizer_cb;
...@@ -771,6 +806,30 @@ PyObject* pysqlite_connection_set_authorizer(pysqlite_Connection* self, PyObject ...@@ -771,6 +806,30 @@ PyObject* pysqlite_connection_set_authorizer(pysqlite_Connection* self, PyObject
} }
} }
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 (!PyArg_ParseTupleAndKeywords(args, kwargs, "Oi:set_progress_handler",
kwlist, &progress_handler, &n)) {
return NULL;
}
if (progress_handler == Py_None) {
/* None clears the progress handler previously set */
sqlite3_progress_handler(self->db, 0, 0, (void*)0);
} else {
sqlite3_progress_handler(self->db, n, _progress_handler, progress_handler);
PyDict_SetItem(self->function_pinboard, progress_handler, Py_None);
}
Py_INCREF(Py_None);
return Py_None;
}
int pysqlite_check_thread(pysqlite_Connection* self) int pysqlite_check_thread(pysqlite_Connection* self)
{ {
if (self->check_same_thread) { if (self->check_same_thread) {
...@@ -881,7 +940,8 @@ PyObject* pysqlite_connection_call(pysqlite_Connection* self, PyObject* args, Py ...@@ -881,7 +940,8 @@ PyObject* pysqlite_connection_call(pysqlite_Connection* self, PyObject* args, Py
} else if (rc == PYSQLITE_SQL_WRONG_TYPE) { } else if (rc == PYSQLITE_SQL_WRONG_TYPE) {
PyErr_SetString(pysqlite_Warning, "SQL is of wrong type. Must be string or unicode."); PyErr_SetString(pysqlite_Warning, "SQL is of wrong type. Must be string or unicode.");
} else { } else {
_pysqlite_seterror(self->db); (void)pysqlite_statement_reset(statement);
_pysqlite_seterror(self->db, NULL);
} }
Py_DECREF(statement); Py_DECREF(statement);
...@@ -1169,7 +1229,7 @@ pysqlite_connection_create_collation(pysqlite_Connection* self, PyObject* args) ...@@ -1169,7 +1229,7 @@ pysqlite_connection_create_collation(pysqlite_Connection* self, PyObject* args)
(callable != Py_None) ? pysqlite_collation_callback : NULL); (callable != Py_None) ? pysqlite_collation_callback : NULL);
if (rc != SQLITE_OK) { if (rc != SQLITE_OK) {
PyDict_DelItem(self->collations, uppercase_name); PyDict_DelItem(self->collations, uppercase_name);
_pysqlite_seterror(self->db); _pysqlite_seterror(self->db, NULL);
goto finally; goto finally;
} }
...@@ -1247,6 +1307,8 @@ static PyMethodDef connection_methods[] = { ...@@ -1247,6 +1307,8 @@ static PyMethodDef connection_methods[] = {
PyDoc_STR("Creates a new aggregate. Non-standard.")}, PyDoc_STR("Creates a new aggregate. Non-standard.")},
{"set_authorizer", (PyCFunction)pysqlite_connection_set_authorizer, METH_VARARGS|METH_KEYWORDS, {"set_authorizer", (PyCFunction)pysqlite_connection_set_authorizer, METH_VARARGS|METH_KEYWORDS,
PyDoc_STR("Sets authorizer callback. Non-standard.")}, PyDoc_STR("Sets authorizer callback. Non-standard.")},
{"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, {"execute", (PyCFunction)pysqlite_connection_execute, METH_VARARGS,
PyDoc_STR("Executes a SQL statement. Non-standard.")}, PyDoc_STR("Executes a SQL statement. Non-standard.")},
{"executemany", (PyCFunction)pysqlite_connection_executemany, METH_VARARGS, {"executemany", (PyCFunction)pysqlite_connection_executemany, METH_VARARGS,
......
/* connection.h - definitions for the connection type /* connection.h - definitions for the connection type
* *
* Copyright (C) 2004-2006 Gerhard Häring <gh@ghaering.de> * Copyright (C) 2004-2007 Gerhard Häring <gh@ghaering.de>
* *
* This file is part of pysqlite. * This file is part of pysqlite.
* *
......
This diff is collapsed.
/* cursor.h - definitions for the cursor type /* cursor.h - definitions for the cursor type
* *
* Copyright (C) 2004-2006 Gerhard Hring <gh@ghaering.de> * Copyright (C) 2004-2007 Gerhard Hring <gh@ghaering.de>
* *
* This file is part of pysqlite. * This file is part of pysqlite.
* *
...@@ -60,7 +60,7 @@ PyObject* pysqlite_cursor_executemany(pysqlite_Cursor* self, PyObject* args); ...@@ -60,7 +60,7 @@ PyObject* pysqlite_cursor_executemany(pysqlite_Cursor* self, PyObject* args);
PyObject* pysqlite_cursor_getiter(pysqlite_Cursor *self); PyObject* pysqlite_cursor_getiter(pysqlite_Cursor *self);
PyObject* pysqlite_cursor_iternext(pysqlite_Cursor *self); PyObject* pysqlite_cursor_iternext(pysqlite_Cursor *self);
PyObject* pysqlite_cursor_fetchone(pysqlite_Cursor* self, PyObject* args); PyObject* pysqlite_cursor_fetchone(pysqlite_Cursor* self, PyObject* args);
PyObject* pysqlite_cursor_fetchmany(pysqlite_Cursor* self, PyObject* args); PyObject* pysqlite_cursor_fetchmany(pysqlite_Cursor* self, PyObject* args, PyObject* kwargs);
PyObject* pysqlite_cursor_fetchall(pysqlite_Cursor* self, PyObject* args); PyObject* pysqlite_cursor_fetchall(pysqlite_Cursor* self, PyObject* args);
PyObject* pysqlite_noop(pysqlite_Connection* self, PyObject* args); PyObject* pysqlite_noop(pysqlite_Connection* self, PyObject* args);
PyObject* pysqlite_cursor_close(pysqlite_Cursor* self, PyObject* args); PyObject* pysqlite_cursor_close(pysqlite_Cursor* self, PyObject* args);
......
...@@ -28,10 +28,6 @@ ...@@ -28,10 +28,6 @@
#include <Python.h> #include <Python.h>
#ifdef __cplusplus
extern "C" {
#endif
/** adapters registry **/ /** adapters registry **/
extern PyObject *psyco_adapters; extern PyObject *psyco_adapters;
......
/* module.c - the module itself /* module.c - the module itself
* *
* Copyright (C) 2004-2006 Gerhard Hring <gh@ghaering.de> * Copyright (C) 2004-2007 Gerhard Hring <gh@ghaering.de>
* *
* This file is part of pysqlite. * This file is part of pysqlite.
* *
* This software is provided 'as-is', without any express or implied * This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages * warranty. In no event will the authors be held liable for any damages
* arising from the use of this software. * arising from the use of this software.
* *
* Permission is granted to anyone to use this software for any purpose, * Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it * including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions: * freely, subject to the following restrictions:
* *
* 1. The origin of this software must not be misrepresented; you must not * 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 * claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be * in a product, an acknowledgment in the product documentation would be
* appreciated but is not required. * appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be * 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software. * misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution. * 3. This notice may not be removed or altered from any source distribution.
*/ */
#include "connection.h" #include "connection.h"
#include "statement.h" #include "statement.h"
...@@ -41,6 +41,7 @@ PyObject* pysqlite_Error, *pysqlite_Warning, *pysqlite_InterfaceError, *pysqlite ...@@ -41,6 +41,7 @@ PyObject* pysqlite_Error, *pysqlite_Warning, *pysqlite_InterfaceError, *pysqlite
PyObject* converters; PyObject* converters;
int _enable_callback_tracebacks; int _enable_callback_tracebacks;
int pysqlite_BaseTypeAdapted;
static PyObject* module_connect(PyObject* self, PyObject* args, PyObject* static PyObject* module_connect(PyObject* self, PyObject* args, PyObject*
kwargs) kwargs)
...@@ -133,6 +134,13 @@ static PyObject* module_register_adapter(PyObject* self, PyObject* args, PyObjec ...@@ -133,6 +134,13 @@ static PyObject* module_register_adapter(PyObject* self, PyObject* args, PyObjec
return NULL; return NULL;
} }
/* a basic type is adapted; there's a performance optimization if that's not the case
* (99 % of all usages) */
if (type == &PyLong_Type || type == &PyFloat_Type
|| type == &PyUnicode_Type || type == &PyBytes_Type) {
pysqlite_BaseTypeAdapted = 1;
}
microprotocols_add(type, (PyObject*)&pysqlite_PrepareProtocolType, caster); microprotocols_add(type, (PyObject*)&pysqlite_PrepareProtocolType, caster);
Py_INCREF(Py_None); Py_INCREF(Py_None);
...@@ -379,6 +387,8 @@ PyMODINIT_FUNC init_sqlite3(void) ...@@ -379,6 +387,8 @@ PyMODINIT_FUNC init_sqlite3(void)
_enable_callback_tracebacks = 0; _enable_callback_tracebacks = 0;
pysqlite_BaseTypeAdapted = 0;
/* Original comment form _bsddb.c in the Python core. This is also still /* Original comment form _bsddb.c in the Python core. This is also still
* needed nowadays for Python 2.3/2.4. * needed nowadays for Python 2.3/2.4.
* *
......
/* module.h - definitions for the module /* module.h - definitions for the module
* *
* Copyright (C) 2004-2006 Gerhard Häring <gh@ghaering.de> * Copyright (C) 2004-2007 Gerhard Häring <gh@ghaering.de>
* *
* This file is part of pysqlite. * This file is part of pysqlite.
* *
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
#define PYSQLITE_MODULE_H #define PYSQLITE_MODULE_H
#include "Python.h" #include "Python.h"
#define PYSQLITE_VERSION "2.3.3" #define PYSQLITE_VERSION "2.4.1"
extern PyObject* pysqlite_Error; extern PyObject* pysqlite_Error;
extern PyObject* pysqlite_Warning; extern PyObject* pysqlite_Warning;
...@@ -51,6 +51,7 @@ extern PyObject* time_sleep; ...@@ -51,6 +51,7 @@ extern PyObject* time_sleep;
extern PyObject* converters; extern PyObject* converters;
extern int _enable_callback_tracebacks; extern int _enable_callback_tracebacks;
extern int pysqlite_BaseTypeAdapted;
#define PARSE_DECLTYPES 1 #define PARSE_DECLTYPES 1
#define PARSE_COLNAMES 2 #define PARSE_COLNAMES 2
......
/* prepare_protocol.h - the protocol for preparing values for SQLite /* prepare_protocol.h - the protocol for preparing values for SQLite
* *
* Copyright (C) 2005 Gerhard Häring <gh@ghaering.de> * Copyright (C) 2005-2007 Gerhard Häring <gh@ghaering.de>
* *
* This file is part of pysqlite. * This file is part of pysqlite.
* *
......
...@@ -154,6 +154,11 @@ PyObject* pysqlite_row_keys(pysqlite_Row* self, PyObject* args, PyObject* kwargs ...@@ -154,6 +154,11 @@ PyObject* pysqlite_row_keys(pysqlite_Row* self, PyObject* args, PyObject* kwargs
return list; return list;
} }
static int pysqlite_row_print(pysqlite_Row* self, FILE *fp, int flags)
{
return (&PyTuple_Type)->tp_print(self->data, fp, flags);
}
static PyObject* pysqlite_iter(pysqlite_Row* self) static PyObject* pysqlite_iter(pysqlite_Row* self)
{ {
return PyObject_GetIter(self->data); return PyObject_GetIter(self->data);
...@@ -178,7 +183,7 @@ PyTypeObject pysqlite_RowType = { ...@@ -178,7 +183,7 @@ PyTypeObject pysqlite_RowType = {
sizeof(pysqlite_Row), /* tp_basicsize */ sizeof(pysqlite_Row), /* tp_basicsize */
0, /* tp_itemsize */ 0, /* tp_itemsize */
(destructor)pysqlite_row_dealloc, /* tp_dealloc */ (destructor)pysqlite_row_dealloc, /* tp_dealloc */
0, /* tp_print */ (printfunc)pysqlite_row_print, /* tp_print */
0, /* tp_getattr */ 0, /* tp_getattr */
0, /* tp_setattr */ 0, /* tp_setattr */
0, /* tp_compare */ 0, /* tp_compare */
......
/* row.h - an enhanced tuple for database rows /* row.h - an enhanced tuple for database rows
* *
* Copyright (C) 2005 Gerhard Häring <gh@ghaering.de> * Copyright (C) 2005-2007 Gerhard Häring <gh@ghaering.de>
* *
* This file is part of pysqlite. * This file is part of pysqlite.
* *
......
This diff is collapsed.
/* statement.h - definitions for the statement type /* statement.h - definitions for the statement type
* *
* Copyright (C) 2005 Gerhard Häring <gh@ghaering.de> * Copyright (C) 2005-2007 Gerhard Häring <gh@ghaering.de>
* *
* This file is part of pysqlite. * This file is part of pysqlite.
* *
...@@ -46,8 +46,8 @@ extern PyTypeObject pysqlite_StatementType; ...@@ -46,8 +46,8 @@ extern PyTypeObject pysqlite_StatementType;
int pysqlite_statement_create(pysqlite_Statement* self, pysqlite_Connection* connection, PyObject* sql); int pysqlite_statement_create(pysqlite_Statement* self, pysqlite_Connection* connection, PyObject* sql);
void pysqlite_statement_dealloc(pysqlite_Statement* self); void pysqlite_statement_dealloc(pysqlite_Statement* self);
int pysqlite_statement_bind_parameter(pysqlite_Statement* self, int pos, PyObject* parameter); int pysqlite_statement_bind_parameter(pysqlite_Statement* self, int pos, PyObject* parameter, int allow_8bit_chars);
void pysqlite_statement_bind_parameters(pysqlite_Statement* self, PyObject* parameters); void pysqlite_statement_bind_parameters(pysqlite_Statement* self, PyObject* parameters, int allow_8bit_chars);
int pysqlite_statement_recompile(pysqlite_Statement* self, PyObject* parameters); int pysqlite_statement_recompile(pysqlite_Statement* self, PyObject* parameters);
int pysqlite_statement_finalize(pysqlite_Statement* self); int pysqlite_statement_finalize(pysqlite_Statement* self);
......
/* util.c - various utility functions /* util.c - various utility functions
* *
* Copyright (C) 2005-2006 Gerhard Hring <gh@ghaering.de> * Copyright (C) 2005-2007 Gerhard Hring <gh@ghaering.de>
* *
* This file is part of pysqlite. * This file is part of pysqlite.
* *
...@@ -45,10 +45,15 @@ int _sqlite_step_with_busyhandler(sqlite3_stmt* statement, pysqlite_Connection* ...@@ -45,10 +45,15 @@ int _sqlite_step_with_busyhandler(sqlite3_stmt* statement, pysqlite_Connection*
* Checks the SQLite error code and sets the appropriate DB-API exception. * Checks the SQLite error code and sets the appropriate DB-API exception.
* Returns the error code (0 means no error occurred). * Returns the error code (0 means no error occurred).
*/ */
int _pysqlite_seterror(sqlite3* db) int _pysqlite_seterror(sqlite3* db, sqlite3_stmt* st)
{ {
int errorcode; int errorcode;
/* SQLite often doesn't report anything useful, unless you reset the statement first */
if (st != NULL) {
(void)sqlite3_reset(st);
}
errorcode = sqlite3_errcode(db); errorcode = sqlite3_errcode(db);
switch (errorcode) switch (errorcode)
......
/* util.h - various utility functions /* util.h - various utility functions
* *
* Copyright (C) 2005-2006 Gerhard Häring <gh@ghaering.de> * Copyright (C) 2005-2007 Gerhard Häring <gh@ghaering.de>
* *
* This file is part of pysqlite. * This file is part of pysqlite.
* *
...@@ -34,5 +34,5 @@ int _sqlite_step_with_busyhandler(sqlite3_stmt* statement, pysqlite_Connection* ...@@ -34,5 +34,5 @@ int _sqlite_step_with_busyhandler(sqlite3_stmt* statement, pysqlite_Connection*
* Checks the SQLite error code and sets the appropriate DB-API exception. * Checks the SQLite error code and sets the appropriate DB-API exception.
* Returns the error code (0 means no error occurred). * Returns the error code (0 means no error occurred).
*/ */
int _pysqlite_seterror(sqlite3* db); int _pysqlite_seterror(sqlite3* db, sqlite3_stmt* st);
#endif #endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment