summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnthony Baxter <anthonybaxter@gmail.com>2006-04-01 00:57:31 (GMT)
committerAnthony Baxter <anthonybaxter@gmail.com>2006-04-01 00:57:31 (GMT)
commitc51ee69b27a35bb45e501766dd33674eae7ddb30 (patch)
tree7853c32c266a623463cdd632178a298b1c0e53a1
parentc17976e9833f3093adb1019356737e728a24f7c9 (diff)
downloadcpython-c51ee69b27a35bb45e501766dd33674eae7ddb30.zip
cpython-c51ee69b27a35bb45e501766dd33674eae7ddb30.tar.gz
cpython-c51ee69b27a35bb45e501766dd33674eae7ddb30.tar.bz2
merged the sqlite-integration branch.
This is based on pysqlite2.1.3, and provides a DB-API interface in the standard library. You'll need sqlite 3.2.2 or later to build this - if you have an earlier version, the C extension module will not be built.
-rw-r--r--Lib/sqlite3/__init__.py24
-rw-r--r--Lib/sqlite3/dbapi2.py84
-rw-r--r--Lib/sqlite3/test/__init__.py0
-rw-r--r--Lib/sqlite3/test/dbapi.py726
-rw-r--r--Lib/sqlite3/test/factory.py164
-rw-r--r--Lib/sqlite3/test/transactions.py154
-rw-r--r--Lib/sqlite3/test/types.py339
-rw-r--r--Lib/sqlite3/test/userfunctions.py330
-rwxr-xr-xLib/test/regrtest.py16
-rw-r--r--Lib/test/test_sqlite.py16
-rw-r--r--Misc/NEWS5
-rw-r--r--Modules/_sqlite/adapters.c40
-rw-r--r--Modules/_sqlite/adapters.h33
-rw-r--r--Modules/_sqlite/cache.c343
-rw-r--r--Modules/_sqlite/cache.h61
-rw-r--r--Modules/_sqlite/connection.c922
-rw-r--r--Modules/_sqlite/connection.h103
-rw-r--r--Modules/_sqlite/converters.c40
-rw-r--r--Modules/_sqlite/converters.h33
-rw-r--r--Modules/_sqlite/cursor.c1067
-rw-r--r--Modules/_sqlite/cursor.h71
-rw-r--r--Modules/_sqlite/microprotocols.c141
-rw-r--r--Modules/_sqlite/microprotocols.h59
-rw-r--r--Modules/_sqlite/module.c290
-rw-r--r--Modules/_sqlite/module.h53
-rw-r--r--Modules/_sqlite/prepare_protocol.c84
-rw-r--r--Modules/_sqlite/prepare_protocol.h41
-rw-r--r--Modules/_sqlite/row.c195
-rw-r--r--Modules/_sqlite/row.h39
-rw-r--r--Modules/_sqlite/statement.c285
-rw-r--r--Modules/_sqlite/statement.h55
-rw-r--r--Modules/_sqlite/util.c147
-rw-r--r--Modules/_sqlite/util.h40
-rw-r--r--setup.py75
34 files changed, 6075 insertions, 0 deletions
diff --git a/Lib/sqlite3/__init__.py b/Lib/sqlite3/__init__.py
new file mode 100644
index 0000000..41ef2b7
--- /dev/null
+++ b/Lib/sqlite3/__init__.py
@@ -0,0 +1,24 @@
+#-*- coding: ISO-8859-1 -*-
+# pysqlite2/__init__.py: the pysqlite2 package.
+#
+# Copyright (C) 2005 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.
+
+from dbapi2 import *
diff --git a/Lib/sqlite3/dbapi2.py b/Lib/sqlite3/dbapi2.py
new file mode 100644
index 0000000..e0c8a84
--- /dev/null
+++ b/Lib/sqlite3/dbapi2.py
@@ -0,0 +1,84 @@
+#-*- coding: ISO-8859-1 -*-
+# pysqlite2/dbapi2.py: the DB-API 2.0 interface
+#
+# Copyright (C) 2004-2005 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 datetime
+
+paramstyle = "qmark"
+
+threadsafety = 1
+
+apilevel = "2.0"
+
+from _sqlite3 import *
+
+import datetime, time
+
+Date = datetime.date
+
+Time = datetime.time
+
+Timestamp = datetime.datetime
+
+def DateFromTicks(ticks):
+ return apply(Date,time.localtime(ticks)[:3])
+
+def TimeFromTicks(ticks):
+ return apply(Time,time.localtime(ticks)[3:6])
+
+def TimestampFromTicks(ticks):
+ return apply(Timestamp,time.localtime(ticks)[:6])
+
+_major, _minor, _micro = version.split(".")
+version_info = (int(_major), int(_minor), _micro)
+_major, _minor, _micro = sqlite_version.split(".")
+sqlite_version_info = (int(_major), int(_minor), _micro)
+
+Binary = buffer
+
+def adapt_date(val):
+ return val.isoformat()
+
+def adapt_datetime(val):
+ return val.isoformat(" ")
+
+def convert_date(val):
+ return datetime.date(*map(int, val.split("-")))
+
+def convert_timestamp(val):
+ datepart, timepart = val.split(" ")
+ year, month, day = map(int, datepart.split("-"))
+ timepart_full = timepart.split(".")
+ hours, minutes, seconds = map(int, timepart_full[0].split(":"))
+ if len(timepart_full) == 2:
+ microseconds = int(float("0." + timepart_full[1]) * 1000000)
+ else:
+ microseconds = 0
+
+ val = datetime.datetime(year, month, day, hours, minutes, seconds, microseconds)
+ return val
+
+
+register_adapter(datetime.date, adapt_date)
+register_adapter(datetime.datetime, adapt_datetime)
+register_converter("date", convert_date)
+register_converter("timestamp", convert_timestamp)
diff --git a/Lib/sqlite3/test/__init__.py b/Lib/sqlite3/test/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/sqlite3/test/__init__.py
diff --git a/Lib/sqlite3/test/dbapi.py b/Lib/sqlite3/test/dbapi.py
new file mode 100644
index 0000000..0d47977
--- /dev/null
+++ b/Lib/sqlite3/test/dbapi.py
@@ -0,0 +1,726 @@
+#-*- coding: ISO-8859-1 -*-
+# pysqlite2/test/dbapi.py: tests for DB-API compliance
+#
+# Copyright (C) 2004-2005 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 threading
+import sqlite3 as sqlite
+
+class ModuleTests(unittest.TestCase):
+ def CheckAPILevel(self):
+ self.assertEqual(sqlite.apilevel, "2.0",
+ "apilevel is %s, should be 2.0" % sqlite.apilevel)
+
+ def CheckThreadSafety(self):
+ self.assertEqual(sqlite.threadsafety, 1,
+ "threadsafety is %d, should be 1" % sqlite.threadsafety)
+
+ def CheckParamStyle(self):
+ self.assertEqual(sqlite.paramstyle, "qmark",
+ "paramstyle is '%s', should be 'qmark'" %
+ sqlite.paramstyle)
+
+ def CheckWarning(self):
+ self.assert_(issubclass(sqlite.Warning, StandardError),
+ "Warning is not a subclass of StandardError")
+
+ def CheckError(self):
+ self.failUnless(issubclass(sqlite.Error, StandardError),
+ "Error is not a subclass of StandardError")
+
+ def CheckInterfaceError(self):
+ self.failUnless(issubclass(sqlite.InterfaceError, sqlite.Error),
+ "InterfaceError is not a subclass of Error")
+
+ def CheckDatabaseError(self):
+ self.failUnless(issubclass(sqlite.DatabaseError, sqlite.Error),
+ "DatabaseError is not a subclass of Error")
+
+ def CheckDataError(self):
+ self.failUnless(issubclass(sqlite.DataError, sqlite.DatabaseError),
+ "DataError is not a subclass of DatabaseError")
+
+ def CheckOperationalError(self):
+ self.failUnless(issubclass(sqlite.OperationalError, sqlite.DatabaseError),
+ "OperationalError is not a subclass of DatabaseError")
+
+ def CheckIntegrityError(self):
+ self.failUnless(issubclass(sqlite.IntegrityError, sqlite.DatabaseError),
+ "IntegrityError is not a subclass of DatabaseError")
+
+ def CheckInternalError(self):
+ self.failUnless(issubclass(sqlite.InternalError, sqlite.DatabaseError),
+ "InternalError is not a subclass of DatabaseError")
+
+ def CheckProgrammingError(self):
+ self.failUnless(issubclass(sqlite.ProgrammingError, sqlite.DatabaseError),
+ "ProgrammingError is not a subclass of DatabaseError")
+
+ def CheckNotSupportedError(self):
+ self.failUnless(issubclass(sqlite.NotSupportedError,
+ sqlite.DatabaseError),
+ "NotSupportedError is not a subclass of DatabaseError")
+
+class ConnectionTests(unittest.TestCase):
+ def setUp(self):
+ self.cx = sqlite.connect(":memory:")
+ cu = self.cx.cursor()
+ cu.execute("create table test(id integer primary key, name text)")
+ cu.execute("insert into test(name) values (?)", ("foo",))
+
+ def tearDown(self):
+ self.cx.close()
+
+ def CheckCommit(self):
+ self.cx.commit()
+
+ def CheckCommitAfterNoChanges(self):
+ """
+ A commit should also work when no changes were made to the database.
+ """
+ self.cx.commit()
+ self.cx.commit()
+
+ def CheckRollback(self):
+ self.cx.rollback()
+
+ def CheckRollbackAfterNoChanges(self):
+ """
+ A rollback should also work when no changes were made to the database.
+ """
+ self.cx.rollback()
+ self.cx.rollback()
+
+ def CheckCursor(self):
+ cu = self.cx.cursor()
+
+ def CheckFailedOpen(self):
+ YOU_CANNOT_OPEN_THIS = "/foo/bar/bla/23534/mydb.db"
+ try:
+ con = sqlite.connect(YOU_CANNOT_OPEN_THIS)
+ except sqlite.OperationalError:
+ return
+ self.fail("should have raised an OperationalError")
+
+ def CheckClose(self):
+ self.cx.close()
+
+ def CheckExceptions(self):
+ # Optional DB-API extension.
+ self.failUnlessEqual(self.cx.Warning, sqlite.Warning)
+ self.failUnlessEqual(self.cx.Error, sqlite.Error)
+ self.failUnlessEqual(self.cx.InterfaceError, sqlite.InterfaceError)
+ self.failUnlessEqual(self.cx.DatabaseError, sqlite.DatabaseError)
+ self.failUnlessEqual(self.cx.DataError, sqlite.DataError)
+ self.failUnlessEqual(self.cx.OperationalError, sqlite.OperationalError)
+ self.failUnlessEqual(self.cx.IntegrityError, sqlite.IntegrityError)
+ self.failUnlessEqual(self.cx.InternalError, sqlite.InternalError)
+ self.failUnlessEqual(self.cx.ProgrammingError, sqlite.ProgrammingError)
+ self.failUnlessEqual(self.cx.NotSupportedError, sqlite.NotSupportedError)
+
+class CursorTests(unittest.TestCase):
+ def setUp(self):
+ self.cx = sqlite.connect(":memory:")
+ self.cu = self.cx.cursor()
+ self.cu.execute("create table test(id integer primary key, name text, income number)")
+ self.cu.execute("insert into test(name) values (?)", ("foo",))
+
+ def tearDown(self):
+ self.cu.close()
+ self.cx.close()
+
+ def CheckExecuteNoArgs(self):
+ self.cu.execute("delete from test")
+
+ def CheckExecuteIllegalSql(self):
+ try:
+ self.cu.execute("select asdf")
+ self.fail("should have raised an OperationalError")
+ except sqlite.OperationalError:
+ return
+ except:
+ self.fail("raised wrong exception")
+
+ def CheckExecuteTooMuchSql(self):
+ try:
+ self.cu.execute("select 5+4; select 4+5")
+ self.fail("should have raised a Warning")
+ except sqlite.Warning:
+ return
+ except:
+ self.fail("raised wrong exception")
+
+ def CheckExecuteTooMuchSql2(self):
+ self.cu.execute("select 5+4; -- foo bar")
+
+ def CheckExecuteTooMuchSql3(self):
+ self.cu.execute("""
+ select 5+4;
+
+ /*
+ foo
+ */
+ """)
+
+ def CheckExecuteWrongSqlArg(self):
+ try:
+ self.cu.execute(42)
+ self.fail("should have raised a ValueError")
+ except ValueError:
+ return
+ except:
+ self.fail("raised wrong exception.")
+
+ def CheckExecuteArgInt(self):
+ self.cu.execute("insert into test(id) values (?)", (42,))
+
+ def CheckExecuteArgFloat(self):
+ self.cu.execute("insert into test(income) values (?)", (2500.32,))
+
+ def CheckExecuteArgString(self):
+ self.cu.execute("insert into test(name) values (?)", ("Hugo",))
+
+ def CheckExecuteWrongNoOfArgs1(self):
+ # too many parameters
+ try:
+ self.cu.execute("insert into test(id) values (?)", (17, "Egon"))
+ self.fail("should have raised ProgrammingError")
+ except sqlite.ProgrammingError:
+ pass
+
+ def CheckExecuteWrongNoOfArgs2(self):
+ # too little parameters
+ try:
+ self.cu.execute("insert into test(id) values (?)")
+ self.fail("should have raised ProgrammingError")
+ except sqlite.ProgrammingError:
+ pass
+
+ def CheckExecuteWrongNoOfArgs3(self):
+ # no parameters, parameters are needed
+ try:
+ self.cu.execute("insert into test(id) values (?)")
+ self.fail("should have raised ProgrammingError")
+ except sqlite.ProgrammingError:
+ pass
+
+ def CheckExecuteDictMapping(self):
+ self.cu.execute("insert into test(name) values ('foo')")
+ self.cu.execute("select name from test where name=:name", {"name": "foo"})
+ row = self.cu.fetchone()
+ self.failUnlessEqual(row[0], "foo")
+
+ def CheckExecuteDictMappingTooLittleArgs(self):
+ self.cu.execute("insert into test(name) values ('foo')")
+ try:
+ self.cu.execute("select name from test where name=:name and id=:id", {"name": "foo"})
+ self.fail("should have raised ProgrammingError")
+ except sqlite.ProgrammingError:
+ pass
+
+ def CheckExecuteDictMappingNoArgs(self):
+ self.cu.execute("insert into test(name) values ('foo')")
+ try:
+ self.cu.execute("select name from test where name=:name")
+ self.fail("should have raised ProgrammingError")
+ except sqlite.ProgrammingError:
+ pass
+
+ def CheckExecuteDictMappingUnnamed(self):
+ self.cu.execute("insert into test(name) values ('foo')")
+ try:
+ self.cu.execute("select name from test where name=?", {"name": "foo"})
+ self.fail("should have raised ProgrammingError")
+ except sqlite.ProgrammingError:
+ pass
+
+ def CheckClose(self):
+ self.cu.close()
+
+ def CheckRowcountExecute(self):
+ self.cu.execute("delete from test")
+ self.cu.execute("insert into test(name) values ('foo')")
+ self.cu.execute("insert into test(name) values ('foo')")
+ self.cu.execute("update test set name='bar'")
+ self.failUnlessEqual(self.cu.rowcount, 2)
+
+ def CheckRowcountExecutemany(self):
+ self.cu.execute("delete from test")
+ self.cu.executemany("insert into test(name) values (?)", [(1,), (2,), (3,)])
+ self.failUnlessEqual(self.cu.rowcount, 3)
+
+ # Checks for executemany:
+ # Sequences are required by the DB-API, iterators
+ # enhancements in pysqlite.
+
+ def CheckExecuteManySequence(self):
+ self.cu.executemany("insert into test(income) values (?)", [(x,) for x in range(100, 110)])
+
+ def CheckExecuteManyIterator(self):
+ class MyIter:
+ def __init__(self):
+ self.value = 5
+
+ def next(self):
+ if self.value == 10:
+ raise StopIteration
+ else:
+ self.value += 1
+ return (self.value,)
+
+ self.cu.executemany("insert into test(income) values (?)", MyIter())
+
+ def CheckExecuteManyGenerator(self):
+ def mygen():
+ for i in range(5):
+ yield (i,)
+
+ self.cu.executemany("insert into test(income) values (?)", mygen())
+
+ def CheckExecuteManyWrongSqlArg(self):
+ try:
+ self.cu.executemany(42, [(3,)])
+ self.fail("should have raised a ValueError")
+ except ValueError:
+ return
+ except:
+ self.fail("raised wrong exception.")
+
+ def CheckExecuteManySelect(self):
+ try:
+ self.cu.executemany("select ?", [(3,)])
+ self.fail("should have raised a ProgrammingError")
+ except sqlite.ProgrammingError:
+ return
+ except:
+ self.fail("raised wrong exception.")
+
+ def CheckExecuteManyNotIterable(self):
+ try:
+ self.cu.executemany("insert into test(income) values (?)", 42)
+ self.fail("should have raised a TypeError")
+ except TypeError:
+ return
+ except Exception, e:
+ print "raised", e.__class__
+ self.fail("raised wrong exception.")
+
+ def CheckFetchIter(self):
+ # Optional DB-API extension.
+ self.cu.execute("delete from test")
+ self.cu.execute("insert into test(id) values (?)", (5,))
+ self.cu.execute("insert into test(id) values (?)", (6,))
+ self.cu.execute("select id from test order by id")
+ lst = []
+ for row in self.cu:
+ lst.append(row[0])
+ self.failUnlessEqual(lst[0], 5)
+ self.failUnlessEqual(lst[1], 6)
+
+ def CheckFetchone(self):
+ self.cu.execute("select name from test")
+ row = self.cu.fetchone()
+ self.failUnlessEqual(row[0], "foo")
+ row = self.cu.fetchone()
+ self.failUnlessEqual(row, None)
+
+ def CheckFetchoneNoStatement(self):
+ cur = self.cx.cursor()
+ row = cur.fetchone()
+ self.failUnlessEqual(row, None)
+
+ def CheckArraySize(self):
+ # must default ot 1
+ self.failUnlessEqual(self.cu.arraysize, 1)
+
+ # now set to 2
+ self.cu.arraysize = 2
+
+ # now make the query return 3 rows
+ self.cu.execute("delete from test")
+ self.cu.execute("insert into test(name) values ('A')")
+ self.cu.execute("insert into test(name) values ('B')")
+ self.cu.execute("insert into test(name) values ('C')")
+ self.cu.execute("select name from test")
+ res = self.cu.fetchmany()
+
+ self.failUnlessEqual(len(res), 2)
+
+ def CheckFetchmany(self):
+ self.cu.execute("select name from test")
+ res = self.cu.fetchmany(100)
+ self.failUnlessEqual(len(res), 1)
+ res = self.cu.fetchmany(100)
+ self.failUnlessEqual(res, [])
+
+ def CheckFetchall(self):
+ self.cu.execute("select name from test")
+ res = self.cu.fetchall()
+ self.failUnlessEqual(len(res), 1)
+ res = self.cu.fetchall()
+ self.failUnlessEqual(res, [])
+
+ def CheckSetinputsizes(self):
+ self.cu.setinputsizes([3, 4, 5])
+
+ def CheckSetoutputsize(self):
+ self.cu.setoutputsize(5, 0)
+
+ def CheckSetoutputsizeNoColumn(self):
+ self.cu.setoutputsize(42)
+
+ def CheckCursorConnection(self):
+ # Optional DB-API extension.
+ self.failUnlessEqual(self.cu.connection, self.cx)
+
+ def CheckWrongCursorCallable(self):
+ try:
+ def f(): pass
+ cur = self.cx.cursor(f)
+ self.fail("should have raised a TypeError")
+ except TypeError:
+ return
+ self.fail("should have raised a ValueError")
+
+ def CheckCursorWrongClass(self):
+ class Foo: pass
+ foo = Foo()
+ try:
+ cur = sqlite.Cursor(foo)
+ self.fail("should have raised a ValueError")
+ except TypeError:
+ pass
+
+class ThreadTests(unittest.TestCase):
+ def setUp(self):
+ self.con = sqlite.connect(":memory:")
+ self.cur = self.con.cursor()
+ self.cur.execute("create table test(id integer primary key, name text, bin binary, ratio number, ts timestamp)")
+
+ def tearDown(self):
+ self.cur.close()
+ self.con.close()
+
+ def CheckConCursor(self):
+ def run(con, errors):
+ try:
+ cur = con.cursor()
+ errors.append("did not raise ProgrammingError")
+ return
+ except sqlite.ProgrammingError:
+ return
+ except:
+ errors.append("raised wrong exception")
+
+ errors = []
+ t = threading.Thread(target=run, kwargs={"con": self.con, "errors": errors})
+ t.start()
+ t.join()
+ if len(errors) > 0:
+ self.fail("\n".join(errors))
+
+ def CheckConCommit(self):
+ def run(con, errors):
+ try:
+ con.commit()
+ errors.append("did not raise ProgrammingError")
+ return
+ except sqlite.ProgrammingError:
+ return
+ except:
+ errors.append("raised wrong exception")
+
+ errors = []
+ t = threading.Thread(target=run, kwargs={"con": self.con, "errors": errors})
+ t.start()
+ t.join()
+ if len(errors) > 0:
+ self.fail("\n".join(errors))
+
+ def CheckConRollback(self):
+ def run(con, errors):
+ try:
+ con.rollback()
+ errors.append("did not raise ProgrammingError")
+ return
+ except sqlite.ProgrammingError:
+ return
+ except:
+ errors.append("raised wrong exception")
+
+ errors = []
+ t = threading.Thread(target=run, kwargs={"con": self.con, "errors": errors})
+ t.start()
+ t.join()
+ if len(errors) > 0:
+ self.fail("\n".join(errors))
+
+ def CheckConClose(self):
+ def run(con, errors):
+ try:
+ con.close()
+ errors.append("did not raise ProgrammingError")
+ return
+ except sqlite.ProgrammingError:
+ return
+ except:
+ errors.append("raised wrong exception")
+
+ errors = []
+ t = threading.Thread(target=run, kwargs={"con": self.con, "errors": errors})
+ t.start()
+ t.join()
+ if len(errors) > 0:
+ self.fail("\n".join(errors))
+
+ def CheckCurImplicitBegin(self):
+ def run(cur, errors):
+ try:
+ cur.execute("insert into test(name) values ('a')")
+ errors.append("did not raise ProgrammingError")
+ return
+ except sqlite.ProgrammingError:
+ return
+ except:
+ errors.append("raised wrong exception")
+
+ errors = []
+ t = threading.Thread(target=run, kwargs={"cur": self.cur, "errors": errors})
+ t.start()
+ t.join()
+ if len(errors) > 0:
+ self.fail("\n".join(errors))
+
+ def CheckCurClose(self):
+ def run(cur, errors):
+ try:
+ cur.close()
+ errors.append("did not raise ProgrammingError")
+ return
+ except sqlite.ProgrammingError:
+ return
+ except:
+ errors.append("raised wrong exception")
+
+ errors = []
+ t = threading.Thread(target=run, kwargs={"cur": self.cur, "errors": errors})
+ t.start()
+ t.join()
+ if len(errors) > 0:
+ self.fail("\n".join(errors))
+
+ def CheckCurExecute(self):
+ def run(cur, errors):
+ try:
+ cur.execute("select name from test")
+ errors.append("did not raise ProgrammingError")
+ return
+ except sqlite.ProgrammingError:
+ return
+ except:
+ errors.append("raised wrong exception")
+
+ errors = []
+ self.cur.execute("insert into test(name) values ('a')")
+ t = threading.Thread(target=run, kwargs={"cur": self.cur, "errors": errors})
+ t.start()
+ t.join()
+ if len(errors) > 0:
+ self.fail("\n".join(errors))
+
+ def CheckCurIterNext(self):
+ def run(cur, errors):
+ try:
+ row = cur.fetchone()
+ errors.append("did not raise ProgrammingError")
+ return
+ except sqlite.ProgrammingError:
+ return
+ except:
+ errors.append("raised wrong exception")
+
+ errors = []
+ self.cur.execute("insert into test(name) values ('a')")
+ self.cur.execute("select name from test")
+ t = threading.Thread(target=run, kwargs={"cur": self.cur, "errors": errors})
+ t.start()
+ t.join()
+ if len(errors) > 0:
+ self.fail("\n".join(errors))
+
+class ConstructorTests(unittest.TestCase):
+ def CheckDate(self):
+ d = sqlite.Date(2004, 10, 28)
+
+ def CheckTime(self):
+ t = sqlite.Time(12, 39, 35)
+
+ def CheckTimestamp(self):
+ ts = sqlite.Timestamp(2004, 10, 28, 12, 39, 35)
+
+ def CheckDateFromTicks(self):
+ d = sqlite.DateFromTicks(42)
+
+ def CheckTimeFromTicks(self):
+ t = sqlite.TimeFromTicks(42)
+
+ def CheckTimestampFromTicks(self):
+ ts = sqlite.TimestampFromTicks(42)
+
+ def CheckBinary(self):
+ b = sqlite.Binary(chr(0) + "'")
+
+class ExtensionTests(unittest.TestCase):
+ def CheckScriptStringSql(self):
+ con = sqlite.connect(":memory:")
+ cur = con.cursor()
+ cur.executescript("""
+ -- bla bla
+ /* a stupid comment */
+ create table a(i);
+ insert into a(i) values (5);
+ """)
+ cur.execute("select i from a")
+ res = cur.fetchone()[0]
+ self.failUnlessEqual(res, 5)
+
+ def CheckScriptStringUnicode(self):
+ con = sqlite.connect(":memory:")
+ cur = con.cursor()
+ cur.executescript(u"""
+ 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):
+ con = sqlite.connect(":memory:")
+ cur = con.cursor()
+ raised = False
+ try:
+ cur.executescript("create table test(sadfsadfdsa")
+ except sqlite.ProgrammingError:
+ raised = True
+ self.failUnlessEqual(raised, True, "should have raised an exception")
+
+ def CheckScriptErrorNormal(self):
+ con = sqlite.connect(":memory:")
+ cur = con.cursor()
+ raised = False
+ try:
+ cur.executescript("create table test(sadfsadfdsa); select foo from hurz;")
+ except sqlite.OperationalError:
+ raised = True
+ self.failUnlessEqual(raised, True, "should have raised an exception")
+
+ def CheckConnectionExecute(self):
+ con = sqlite.connect(":memory:")
+ result = con.execute("select 5").fetchone()[0]
+ self.failUnlessEqual(result, 5, "Basic test of Connection.execute")
+
+ def CheckConnectionExecutemany(self):
+ con = sqlite.connect(":memory:")
+ con.execute("create table test(foo)")
+ con.executemany("insert into test(foo) values (?)", [(3,), (4,)])
+ result = con.execute("select foo from test order by foo").fetchall()
+ self.failUnlessEqual(result[0][0], 3, "Basic test of Connection.executemany")
+ self.failUnlessEqual(result[1][0], 4, "Basic test of Connection.executemany")
+
+ def CheckConnectionExecutescript(self):
+ con = sqlite.connect(":memory:")
+ con.executescript("create table test(foo); insert into test(foo) values (5);")
+ result = con.execute("select foo from test").fetchone()[0]
+ self.failUnlessEqual(result, 5, "Basic test of Connection.executescript")
+
+class ClosedTests(unittest.TestCase):
+ def setUp(self):
+ pass
+
+ def tearDown(self):
+ pass
+
+ def CheckClosedConCursor(self):
+ con = sqlite.connect(":memory:")
+ con.close()
+ try:
+ cur = con.cursor()
+ self.fail("Should have raised a ProgrammingError")
+ except sqlite.ProgrammingError:
+ pass
+ except:
+ self.fail("Should have raised a ProgrammingError")
+
+ def CheckClosedConCommit(self):
+ con = sqlite.connect(":memory:")
+ con.close()
+ try:
+ con.commit()
+ self.fail("Should have raised a ProgrammingError")
+ except sqlite.ProgrammingError:
+ pass
+ except:
+ self.fail("Should have raised a ProgrammingError")
+
+ def CheckClosedConRollback(self):
+ con = sqlite.connect(":memory:")
+ con.close()
+ try:
+ con.rollback()
+ self.fail("Should have raised a ProgrammingError")
+ except sqlite.ProgrammingError:
+ pass
+ except:
+ self.fail("Should have raised a ProgrammingError")
+
+ def CheckClosedCurExecute(self):
+ con = sqlite.connect(":memory:")
+ cur = con.cursor()
+ con.close()
+ try:
+ cur.execute("select 4")
+ self.fail("Should have raised a ProgrammingError")
+ except sqlite.ProgrammingError:
+ pass
+ except:
+ self.fail("Should have raised a ProgrammingError")
+
+def suite():
+ module_suite = unittest.makeSuite(ModuleTests, "Check")
+ connection_suite = unittest.makeSuite(ConnectionTests, "Check")
+ cursor_suite = unittest.makeSuite(CursorTests, "Check")
+ 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))
+
+def test():
+ runner = unittest.TextTestRunner()
+ runner.run(suite())
+
+if __name__ == "__main__":
+ test()
diff --git a/Lib/sqlite3/test/factory.py b/Lib/sqlite3/test/factory.py
new file mode 100644
index 0000000..8778056
--- /dev/null
+++ b/Lib/sqlite3/test/factory.py
@@ -0,0 +1,164 @@
+#-*- coding: ISO-8859-1 -*-
+# pysqlite2/test/factory.py: tests for the various factories in pysqlite
+#
+# Copyright (C) 2005 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 sqlite3 as sqlite
+
+class MyConnection(sqlite.Connection):
+ def __init__(self, *args, **kwargs):
+ sqlite.Connection.__init__(self, *args, **kwargs)
+
+def dict_factory(cursor, row):
+ d = {}
+ for idx, col in enumerate(cursor.description):
+ d[col[0]] = row[idx]
+ return d
+
+class MyCursor(sqlite.Cursor):
+ def __init__(self, *args, **kwargs):
+ sqlite.Cursor.__init__(self, *args, **kwargs)
+ self.row_factory = dict_factory
+
+class ConnectionFactoryTests(unittest.TestCase):
+ def setUp(self):
+ self.con = sqlite.connect(":memory:", factory=MyConnection)
+
+ def tearDown(self):
+ self.con.close()
+
+ def CheckIsInstance(self):
+ self.failUnless(isinstance(self.con,
+ MyConnection),
+ "connection is not instance of MyConnection")
+
+class CursorFactoryTests(unittest.TestCase):
+ def setUp(self):
+ self.con = sqlite.connect(":memory:")
+
+ def tearDown(self):
+ self.con.close()
+
+ def CheckIsInstance(self):
+ cur = self.con.cursor(factory=MyCursor)
+ self.failUnless(isinstance(cur,
+ MyCursor),
+ "cursor is not instance of MyCursor")
+
+class RowFactoryTestsBackwardsCompat(unittest.TestCase):
+ def setUp(self):
+ self.con = sqlite.connect(":memory:")
+
+ def CheckIsProducedByFactory(self):
+ cur = self.con.cursor(factory=MyCursor)
+ cur.execute("select 4+5 as foo")
+ row = cur.fetchone()
+ self.failUnless(isinstance(row,
+ dict),
+ "row is not instance of dict")
+ cur.close()
+
+ def tearDown(self):
+ self.con.close()
+
+class RowFactoryTests(unittest.TestCase):
+ def setUp(self):
+ self.con = sqlite.connect(":memory:")
+
+ def CheckCustomFactory(self):
+ self.con.row_factory = lambda cur, row: list(row)
+ row = self.con.execute("select 1, 2").fetchone()
+ self.failUnless(isinstance(row,
+ list),
+ "row is not instance of list")
+
+ def CheckSqliteRow(self):
+ self.con.row_factory = sqlite.Row
+ row = self.con.execute("select 1 as a, 2 as b").fetchone()
+ self.failUnless(isinstance(row,
+ sqlite.Row),
+ "row is not instance of sqlite.Row")
+
+ col1, col2 = row["a"], row["b"]
+ self.failUnless(col1 == 1, "by name: wrong result for column 'a'")
+ self.failUnless(col2 == 2, "by name: wrong result for column 'a'")
+
+ col1, col2 = row["A"], row["B"]
+ self.failUnless(col1 == 1, "by name: wrong result for column 'A'")
+ self.failUnless(col2 == 2, "by name: wrong result for column 'B'")
+
+ col1, col2 = row[0], row[1]
+ self.failUnless(col1 == 1, "by index: wrong result for column 0")
+ self.failUnless(col2 == 2, "by index: wrong result for column 1")
+
+ def tearDown(self):
+ self.con.close()
+
+class TextFactoryTests(unittest.TestCase):
+ def setUp(self):
+ self.con = sqlite.connect(":memory:")
+
+ def CheckUnicode(self):
+ austria = unicode("Österreich", "latin1")
+ row = self.con.execute("select ?", (austria,)).fetchone()
+ self.failUnless(type(row[0]) == unicode, "type of row[0] must be unicode")
+
+ def CheckString(self):
+ self.con.text_factory = str
+ austria = unicode("Österreich", "latin1")
+ row = self.con.execute("select ?", (austria,)).fetchone()
+ self.failUnless(type(row[0]) == str, "type of row[0] must be str")
+ self.failUnless(row[0] == austria.encode("utf-8"), "column must equal original data in UTF-8")
+
+ def CheckCustom(self):
+ self.con.text_factory = lambda x: unicode(x, "utf-8", "ignore")
+ austria = unicode("Österreich", "latin1")
+ row = self.con.execute("select ?", (austria.encode("latin1"),)).fetchone()
+ self.failUnless(type(row[0]) == unicode, "type of row[0] must be unicode")
+ self.failUnless(row[0].endswith(u"reich"), "column must contain original data")
+
+ def CheckOptimizedUnicode(self):
+ self.con.text_factory = sqlite.OptimizedUnicode
+ austria = unicode("Österreich", "latin1")
+ germany = unicode("Deutchland")
+ a_row = self.con.execute("select ?", (austria,)).fetchone()
+ d_row = self.con.execute("select ?", (germany,)).fetchone()
+ self.failUnless(type(a_row[0]) == unicode, "type of non-ASCII row must be unicode")
+ self.failUnless(type(d_row[0]) == str, "type of ASCII-only row must be str")
+
+ def tearDown(self):
+ self.con.close()
+
+def suite():
+ connection_suite = unittest.makeSuite(ConnectionFactoryTests, "Check")
+ cursor_suite = unittest.makeSuite(CursorFactoryTests, "Check")
+ row_suite_compat = unittest.makeSuite(RowFactoryTestsBackwardsCompat, "Check")
+ row_suite = unittest.makeSuite(RowFactoryTests, "Check")
+ text_suite = unittest.makeSuite(TextFactoryTests, "Check")
+ return unittest.TestSuite((connection_suite, cursor_suite, row_suite_compat, row_suite, text_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
new file mode 100644
index 0000000..28202cb
--- /dev/null
+++ b/Lib/sqlite3/test/transactions.py
@@ -0,0 +1,154 @@
+#-*- coding: ISO-8859-1 -*-
+# pysqlite2/test/transactions.py: tests transactions
+#
+# Copyright (C) 2005 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 sqlite3 as sqlite
+
+def get_db_path():
+ return "testdb"
+
+class TransactionTests(unittest.TestCase):
+ def setUp(self):
+ try:
+ os.remove(get_db_path())
+ except:
+ pass
+
+ self.con1 = sqlite.connect(get_db_path(), timeout=0.1)
+ self.cur1 = self.con1.cursor()
+
+ self.con2 = sqlite.connect(get_db_path(), timeout=0.1)
+ self.cur2 = self.con2.cursor()
+
+ def tearDown(self):
+ self.cur1.close()
+ self.con1.close()
+
+ self.cur2.close()
+ self.con2.close()
+
+ def CheckDMLdoesAutoCommitBefore(self):
+ self.cur1.execute("create table test(i)")
+ self.cur1.execute("insert into test(i) values (5)")
+ self.cur1.execute("create table test2(j)")
+ self.cur2.execute("select i from test")
+ res = self.cur2.fetchall()
+ self.failUnlessEqual(len(res), 1)
+
+ def CheckInsertStartsTransaction(self):
+ self.cur1.execute("create table test(i)")
+ self.cur1.execute("insert into test(i) values (5)")
+ self.cur2.execute("select i from test")
+ res = self.cur2.fetchall()
+ self.failUnlessEqual(len(res), 0)
+
+ def CheckUpdateStartsTransaction(self):
+ self.cur1.execute("create table test(i)")
+ self.cur1.execute("insert into test(i) values (5)")
+ self.con1.commit()
+ self.cur1.execute("update test set i=6")
+ self.cur2.execute("select i from test")
+ res = self.cur2.fetchone()[0]
+ self.failUnlessEqual(res, 5)
+
+ def CheckDeleteStartsTransaction(self):
+ self.cur1.execute("create table test(i)")
+ self.cur1.execute("insert into test(i) values (5)")
+ self.con1.commit()
+ self.cur1.execute("delete from test")
+ self.cur2.execute("select i from test")
+ res = self.cur2.fetchall()
+ self.failUnlessEqual(len(res), 1)
+
+ def CheckReplaceStartsTransaction(self):
+ self.cur1.execute("create table test(i)")
+ self.cur1.execute("insert into test(i) values (5)")
+ self.con1.commit()
+ self.cur1.execute("replace into test(i) values (6)")
+ self.cur2.execute("select i from test")
+ res = self.cur2.fetchall()
+ self.failUnlessEqual(len(res), 1)
+ self.failUnlessEqual(res[0][0], 5)
+
+ def CheckToggleAutoCommit(self):
+ self.cur1.execute("create table test(i)")
+ self.cur1.execute("insert into test(i) values (5)")
+ self.con1.isolation_level = None
+ self.failUnlessEqual(self.con1.isolation_level, None)
+ self.cur2.execute("select i from test")
+ res = self.cur2.fetchall()
+ self.failUnlessEqual(len(res), 1)
+
+ self.con1.isolation_level = "DEFERRED"
+ self.failUnlessEqual(self.con1.isolation_level , "DEFERRED")
+ self.cur1.execute("insert into test(i) values (5)")
+ self.cur2.execute("select i from test")
+ res = self.cur2.fetchall()
+ self.failUnlessEqual(len(res), 1)
+
+ def CheckRaiseTimeout(self):
+ 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")
+
+class SpecialCommandTests(unittest.TestCase):
+ def setUp(self):
+ self.con = sqlite.connect(":memory:")
+ self.cur = self.con.cursor()
+
+ def CheckVacuum(self):
+ self.cur.execute("create table test(i)")
+ self.cur.execute("insert into test(i) values (5)")
+ self.cur.execute("vacuum")
+
+ def CheckDropTable(self):
+ self.cur.execute("create table test(i)")
+ self.cur.execute("insert into test(i) values (5)")
+ self.cur.execute("drop table test")
+
+ def CheckPragma(self):
+ self.cur.execute("create table test(i)")
+ self.cur.execute("insert into test(i) values (5)")
+ self.cur.execute("pragma count_changes=1")
+
+ def tearDown(self):
+ self.cur.close()
+ self.con.close()
+
+def suite():
+ default_suite = unittest.makeSuite(TransactionTests, "Check")
+ special_command_suite = unittest.makeSuite(SpecialCommandTests, "Check")
+ return unittest.TestSuite((default_suite, special_command_suite))
+
+def test():
+ runner = unittest.TextTestRunner()
+ runner.run(suite())
+
+if __name__ == "__main__":
+ test()
diff --git a/Lib/sqlite3/test/types.py b/Lib/sqlite3/test/types.py
new file mode 100644
index 0000000..e49f7dd
--- /dev/null
+++ b/Lib/sqlite3/test/types.py
@@ -0,0 +1,339 @@
+#-*- coding: ISO-8859-1 -*-
+# pysqlite2/test/types.py: tests for type conversion and detection
+#
+# Copyright (C) 2005 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 datetime
+import unittest
+import sqlite3 as sqlite
+
+class SqliteTypeTests(unittest.TestCase):
+ def setUp(self):
+ self.con = sqlite.connect(":memory:")
+ self.cur = self.con.cursor()
+ self.cur.execute("create table test(i integer, s varchar, f number, b blob)")
+
+ def tearDown(self):
+ self.cur.close()
+ self.con.close()
+
+ def CheckString(self):
+ self.cur.execute("insert into test(s) values (?)", (u"Österreich",))
+ self.cur.execute("select s from test")
+ row = self.cur.fetchone()
+ self.failUnlessEqual(row[0], u"Österreich")
+
+ def CheckSmallInt(self):
+ self.cur.execute("insert into test(i) values (?)", (42,))
+ self.cur.execute("select i from test")
+ row = self.cur.fetchone()
+ self.failUnlessEqual(row[0], 42)
+
+ def CheckLargeInt(self):
+ num = 2**40
+ self.cur.execute("insert into test(i) values (?)", (num,))
+ self.cur.execute("select i from test")
+ row = self.cur.fetchone()
+ self.failUnlessEqual(row[0], num)
+
+ def CheckFloat(self):
+ val = 3.14
+ self.cur.execute("insert into test(f) values (?)", (val,))
+ self.cur.execute("select f from test")
+ row = self.cur.fetchone()
+ self.failUnlessEqual(row[0], val)
+
+ def CheckBlob(self):
+ val = buffer("Guglhupf")
+ self.cur.execute("insert into test(b) values (?)", (val,))
+ self.cur.execute("select b from test")
+ row = self.cur.fetchone()
+ self.failUnlessEqual(row[0], val)
+
+ def CheckUnicodeExecute(self):
+ self.cur.execute(u"select 'Österreich'")
+ row = self.cur.fetchone()
+ self.failUnlessEqual(row[0], u"Österreich")
+
+class DeclTypesTests(unittest.TestCase):
+ class Foo:
+ def __init__(self, _val):
+ self.val = _val
+
+ def __cmp__(self, other):
+ if not isinstance(other, DeclTypesTests.Foo):
+ raise ValueError
+ if self.val == other.val:
+ return 0
+ else:
+ return 1
+
+ def __conform__(self, protocol):
+ if protocol is sqlite.PrepareProtocol:
+ return self.val
+ else:
+ return None
+
+ def __str__(self):
+ return "<%s>" % self.val
+
+ def setUp(self):
+ self.con = sqlite.connect(":memory:", detect_types=sqlite.PARSE_DECLTYPES)
+ self.cur = self.con.cursor()
+ self.cur.execute("create table test(i int, s str, f float, b bool, u unicode, foo foo, bin blob)")
+
+ # override float, make them always return the same number
+ sqlite.converters["float"] = lambda x: 47.2
+
+ # and implement two custom ones
+ sqlite.converters["bool"] = lambda x: bool(int(x))
+ sqlite.converters["foo"] = DeclTypesTests.Foo
+
+ def tearDown(self):
+ del sqlite.converters["float"]
+ del sqlite.converters["bool"]
+ del sqlite.converters["foo"]
+ self.cur.close()
+ self.con.close()
+
+ def CheckString(self):
+ # default
+ self.cur.execute("insert into test(s) values (?)", ("foo",))
+ self.cur.execute("select s from test")
+ row = self.cur.fetchone()
+ self.failUnlessEqual(row[0], "foo")
+
+ def CheckSmallInt(self):
+ # default
+ self.cur.execute("insert into test(i) values (?)", (42,))
+ self.cur.execute("select i from test")
+ row = self.cur.fetchone()
+ self.failUnlessEqual(row[0], 42)
+
+ def CheckLargeInt(self):
+ # default
+ num = 2**40
+ self.cur.execute("insert into test(i) values (?)", (num,))
+ self.cur.execute("select i from test")
+ row = self.cur.fetchone()
+ self.failUnlessEqual(row[0], num)
+
+ def CheckFloat(self):
+ # custom
+ val = 3.14
+ self.cur.execute("insert into test(f) values (?)", (val,))
+ self.cur.execute("select f from test")
+ row = self.cur.fetchone()
+ self.failUnlessEqual(row[0], 47.2)
+
+ def CheckBool(self):
+ # custom
+ self.cur.execute("insert into test(b) values (?)", (False,))
+ self.cur.execute("select b from test")
+ row = self.cur.fetchone()
+ self.failUnlessEqual(row[0], False)
+
+ self.cur.execute("delete from test")
+ self.cur.execute("insert into test(b) values (?)", (True,))
+ self.cur.execute("select b from test")
+ row = self.cur.fetchone()
+ self.failUnlessEqual(row[0], True)
+
+ def CheckUnicode(self):
+ # default
+ val = u"\xd6sterreich"
+ self.cur.execute("insert into test(u) values (?)", (val,))
+ self.cur.execute("select u from test")
+ row = self.cur.fetchone()
+ self.failUnlessEqual(row[0], val)
+
+ def CheckFoo(self):
+ val = DeclTypesTests.Foo("bla")
+ self.cur.execute("insert into test(foo) values (?)", (val,))
+ self.cur.execute("select foo from test")
+ row = self.cur.fetchone()
+ self.failUnlessEqual(row[0], val)
+
+ def CheckUnsupportedSeq(self):
+ class Bar: pass
+ val = Bar()
+ try:
+ self.cur.execute("insert into test(f) values (?)", (val,))
+ self.fail("should have raised an InterfaceError")
+ except sqlite.InterfaceError:
+ pass
+ except:
+ self.fail("should have raised an InterfaceError")
+
+ def CheckUnsupportedDict(self):
+ class Bar: pass
+ val = Bar()
+ try:
+ self.cur.execute("insert into test(f) values (:val)", {"val": val})
+ self.fail("should have raised an InterfaceError")
+ except sqlite.InterfaceError:
+ pass
+ except:
+ self.fail("should have raised an InterfaceError")
+
+ def CheckBlob(self):
+ # default
+ val = buffer("Guglhupf")
+ self.cur.execute("insert into test(bin) values (?)", (val,))
+ self.cur.execute("select bin from test")
+ row = self.cur.fetchone()
+ self.failUnlessEqual(row[0], val)
+
+class ColNamesTests(unittest.TestCase):
+ def setUp(self):
+ self.con = sqlite.connect(":memory:", detect_types=sqlite.PARSE_COLNAMES|sqlite.PARSE_DECLTYPES)
+ self.cur = self.con.cursor()
+ self.cur.execute("create table test(x foo)")
+
+ sqlite.converters["foo"] = lambda x: "[%s]" % x
+ sqlite.converters["bar"] = lambda x: "<%s>" % x
+ sqlite.converters["exc"] = lambda x: 5/0
+
+ def tearDown(self):
+ del sqlite.converters["foo"]
+ del sqlite.converters["bar"]
+ del sqlite.converters["exc"]
+ self.cur.close()
+ self.con.close()
+
+ def CheckDeclType(self):
+ self.cur.execute("insert into test(x) values (?)", ("xxx",))
+ self.cur.execute("select x from test")
+ val = self.cur.fetchone()[0]
+ self.failUnlessEqual(val, "[xxx]")
+
+ def CheckNone(self):
+ self.cur.execute("insert into test(x) values (?)", (None,))
+ self.cur.execute("select x from test")
+ val = self.cur.fetchone()[0]
+ self.failUnlessEqual(val, None)
+
+ def CheckExc(self):
+ # Exceptions in type converters result in returned Nones
+ self.cur.execute('select 5 as "x [exc]"')
+ val = self.cur.fetchone()[0]
+ self.failUnlessEqual(val, None)
+
+ def CheckColName(self):
+ self.cur.execute("insert into test(x) values (?)", ("xxx",))
+ self.cur.execute('select x as "x [bar]" from test')
+ val = self.cur.fetchone()[0]
+ self.failUnlessEqual(val, "<xxx>")
+
+ # Check if the stripping of colnames works. Everything after the first
+ # whitespace should be stripped.
+ self.failUnlessEqual(self.cur.description[0][0], "x")
+
+ def CheckCursorDescriptionNoRow(self):
+ """
+ cursor.description should at least provide the column name(s), even if
+ no row returned.
+ """
+ self.cur.execute("select * from test where 0 = 1")
+ self.assert_(self.cur.description[0][0] == "x")
+
+class ObjectAdaptationTests(unittest.TestCase):
+ def cast(obj):
+ return float(obj)
+ cast = staticmethod(cast)
+
+ def setUp(self):
+ self.con = sqlite.connect(":memory:")
+ try:
+ del sqlite.adapters[int]
+ except:
+ pass
+ sqlite.register_adapter(int, ObjectAdaptationTests.cast)
+ self.cur = self.con.cursor()
+
+ def tearDown(self):
+ del sqlite.adapters[(int, sqlite.PrepareProtocol)]
+ self.cur.close()
+ self.con.close()
+
+ def CheckCasterIsUsed(self):
+ self.cur.execute("select ?", (4,))
+ val = self.cur.fetchone()[0]
+ self.failUnlessEqual(type(val), float)
+
+class DateTimeTests(unittest.TestCase):
+ def setUp(self):
+ self.con = sqlite.connect(":memory:", detect_types=sqlite.PARSE_DECLTYPES)
+ self.cur = self.con.cursor()
+ self.cur.execute("create table test(d date, ts timestamp)")
+
+ def tearDown(self):
+ self.cur.close()
+ self.con.close()
+
+ def CheckSqliteDate(self):
+ d = sqlite.Date(2004, 2, 14)
+ self.cur.execute("insert into test(d) values (?)", (d,))
+ self.cur.execute("select d from test")
+ d2 = self.cur.fetchone()[0]
+ self.failUnlessEqual(d, d2)
+
+ def CheckSqliteTimestamp(self):
+ ts = sqlite.Timestamp(2004, 2, 14, 7, 15, 0)
+ self.cur.execute("insert into test(ts) values (?)", (ts,))
+ self.cur.execute("select ts from test")
+ ts2 = self.cur.fetchone()[0]
+ self.failUnlessEqual(ts, ts2)
+
+ def CheckSqlTimestamp(self):
+ # The date functions are only available in SQLite version 3.1 or later
+ if sqlite.sqlite_version_info < (3, 1):
+ return
+
+ # 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("select ts from test")
+ ts = self.cur.fetchone()[0]
+ self.failUnlessEqual(type(ts), datetime.datetime)
+ self.failUnlessEqual(ts.year, now.year)
+
+ def CheckDateTimeSubSeconds(self):
+ ts = sqlite.Timestamp(2004, 2, 14, 7, 15, 0, 500000)
+ self.cur.execute("insert into test(ts) values (?)", (ts,))
+ self.cur.execute("select ts from test")
+ ts2 = self.cur.fetchone()[0]
+ self.failUnlessEqual(ts, ts2)
+
+def suite():
+ sqlite_type_suite = unittest.makeSuite(SqliteTypeTests, "Check")
+ decltypes_type_suite = unittest.makeSuite(DeclTypesTests, "Check")
+ colnames_type_suite = unittest.makeSuite(ColNamesTests, "Check")
+ adaptation_suite = unittest.makeSuite(ObjectAdaptationTests, "Check")
+ date_suite = unittest.makeSuite(DateTimeTests, "Check")
+ return unittest.TestSuite((sqlite_type_suite, decltypes_type_suite, colnames_type_suite, adaptation_suite, date_suite))
+
+def test():
+ runner = unittest.TextTestRunner()
+ runner.run(suite())
+
+if __name__ == "__main__":
+ test()
diff --git a/Lib/sqlite3/test/userfunctions.py b/Lib/sqlite3/test/userfunctions.py
new file mode 100644
index 0000000..ff7db9c
--- /dev/null
+++ b/Lib/sqlite3/test/userfunctions.py
@@ -0,0 +1,330 @@
+#-*- coding: ISO-8859-1 -*-
+# pysqlite2/test/userfunctions.py: tests for user-defined functions and
+# aggregates.
+#
+# Copyright (C) 2005 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 sqlite3 as sqlite
+
+def func_returntext():
+ return "foo"
+def func_returnunicode():
+ return u"bar"
+def func_returnint():
+ return 42
+def func_returnfloat():
+ return 3.14
+def func_returnnull():
+ return None
+def func_returnblob():
+ return buffer("blob")
+def func_raiseexception():
+ 5/0
+
+def func_isstring(v):
+ return type(v) is unicode
+def func_isint(v):
+ return type(v) is int
+def func_isfloat(v):
+ return type(v) is float
+def func_isnone(v):
+ return type(v) is type(None)
+def func_isblob(v):
+ return type(v) is buffer
+
+class AggrNoStep:
+ def __init__(self):
+ pass
+
+class AggrNoFinalize:
+ def __init__(self):
+ pass
+
+ def step(self, x):
+ pass
+
+class AggrExceptionInInit:
+ def __init__(self):
+ 5/0
+
+ def step(self, x):
+ pass
+
+ def finalize(self):
+ pass
+
+class AggrExceptionInStep:
+ def __init__(self):
+ pass
+
+ def step(self, x):
+ 5/0
+
+ def finalize(self):
+ return 42
+
+class AggrExceptionInFinalize:
+ def __init__(self):
+ pass
+
+ def step(self, x):
+ pass
+
+ def finalize(self):
+ 5/0
+
+class AggrCheckType:
+ def __init__(self):
+ self.val = None
+
+ def step(self, whichType, val):
+ theType = {"str": unicode, "int": int, "float": float, "None": type(None), "blob": buffer}
+ self.val = int(theType[whichType] is type(val))
+
+ def finalize(self):
+ return self.val
+
+class AggrSum:
+ def __init__(self):
+ self.val = 0.0
+
+ def step(self, val):
+ self.val += val
+
+ def finalize(self):
+ return self.val
+
+class FunctionTests(unittest.TestCase):
+ def setUp(self):
+ self.con = sqlite.connect(":memory:")
+
+ self.con.create_function("returntext", 0, func_returntext)
+ self.con.create_function("returnunicode", 0, func_returnunicode)
+ self.con.create_function("returnint", 0, func_returnint)
+ self.con.create_function("returnfloat", 0, func_returnfloat)
+ self.con.create_function("returnnull", 0, func_returnnull)
+ self.con.create_function("returnblob", 0, func_returnblob)
+ self.con.create_function("raiseexception", 0, func_raiseexception)
+
+ self.con.create_function("isstring", 1, func_isstring)
+ self.con.create_function("isint", 1, func_isint)
+ self.con.create_function("isfloat", 1, func_isfloat)
+ self.con.create_function("isnone", 1, func_isnone)
+ self.con.create_function("isblob", 1, func_isblob)
+
+ def tearDown(self):
+ self.con.close()
+
+ def CheckFuncRefCount(self):
+ def getfunc():
+ def f():
+ return val
+ return f
+ self.con.create_function("reftest", 0, getfunc())
+ cur = self.con.cursor()
+ cur.execute("select reftest()")
+
+ def CheckFuncReturnText(self):
+ cur = self.con.cursor()
+ cur.execute("select returntext()")
+ val = cur.fetchone()[0]
+ self.failUnlessEqual(type(val), unicode)
+ self.failUnlessEqual(val, "foo")
+
+ def CheckFuncReturnUnicode(self):
+ cur = self.con.cursor()
+ cur.execute("select returnunicode()")
+ val = cur.fetchone()[0]
+ self.failUnlessEqual(type(val), unicode)
+ self.failUnlessEqual(val, u"bar")
+
+ def CheckFuncReturnInt(self):
+ cur = self.con.cursor()
+ cur.execute("select returnint()")
+ val = cur.fetchone()[0]
+ self.failUnlessEqual(type(val), int)
+ self.failUnlessEqual(val, 42)
+
+ def CheckFuncReturnFloat(self):
+ cur = self.con.cursor()
+ cur.execute("select returnfloat()")
+ val = cur.fetchone()[0]
+ self.failUnlessEqual(type(val), float)
+ if val < 3.139 or val > 3.141:
+ self.fail("wrong value")
+
+ def CheckFuncReturnNull(self):
+ cur = self.con.cursor()
+ cur.execute("select returnnull()")
+ val = cur.fetchone()[0]
+ self.failUnlessEqual(type(val), type(None))
+ self.failUnlessEqual(val, None)
+
+ def CheckFuncReturnBlob(self):
+ cur = self.con.cursor()
+ cur.execute("select returnblob()")
+ val = cur.fetchone()[0]
+ self.failUnlessEqual(type(val), buffer)
+ self.failUnlessEqual(val, buffer("blob"))
+
+ def CheckFuncException(self):
+ cur = self.con.cursor()
+ cur.execute("select raiseexception()")
+ val = cur.fetchone()[0]
+ self.failUnlessEqual(val, None)
+
+ def CheckParamString(self):
+ cur = self.con.cursor()
+ cur.execute("select isstring(?)", ("foo",))
+ val = cur.fetchone()[0]
+ self.failUnlessEqual(val, 1)
+
+ def CheckParamInt(self):
+ cur = self.con.cursor()
+ cur.execute("select isint(?)", (42,))
+ val = cur.fetchone()[0]
+ self.failUnlessEqual(val, 1)
+
+ def CheckParamFloat(self):
+ cur = self.con.cursor()
+ cur.execute("select isfloat(?)", (3.14,))
+ val = cur.fetchone()[0]
+ self.failUnlessEqual(val, 1)
+
+ def CheckParamNone(self):
+ cur = self.con.cursor()
+ cur.execute("select isnone(?)", (None,))
+ val = cur.fetchone()[0]
+ self.failUnlessEqual(val, 1)
+
+ def CheckParamBlob(self):
+ cur = self.con.cursor()
+ cur.execute("select isblob(?)", (buffer("blob"),))
+ val = cur.fetchone()[0]
+ self.failUnlessEqual(val, 1)
+
+class AggregateTests(unittest.TestCase):
+ def setUp(self):
+ self.con = sqlite.connect(":memory:")
+ cur = self.con.cursor()
+ cur.execute("""
+ create table test(
+ t text,
+ i integer,
+ f float,
+ n,
+ b blob
+ )
+ """)
+ cur.execute("insert into test(t, i, f, n, b) values (?, ?, ?, ?, ?)",
+ ("foo", 5, 3.14, None, buffer("blob"),))
+
+ self.con.create_aggregate("nostep", 1, AggrNoStep)
+ self.con.create_aggregate("nofinalize", 1, AggrNoFinalize)
+ self.con.create_aggregate("excInit", 1, AggrExceptionInInit)
+ self.con.create_aggregate("excStep", 1, AggrExceptionInStep)
+ self.con.create_aggregate("excFinalize", 1, AggrExceptionInFinalize)
+ self.con.create_aggregate("checkType", 2, AggrCheckType)
+ self.con.create_aggregate("mysum", 1, AggrSum)
+
+ def tearDown(self):
+ #self.cur.close()
+ #self.con.close()
+ pass
+
+ def CheckAggrNoStep(self):
+ cur = self.con.cursor()
+ cur.execute("select nostep(t) from test")
+
+ def CheckAggrNoFinalize(self):
+ cur = self.con.cursor()
+ cur.execute("select nofinalize(t) from test")
+ val = cur.fetchone()[0]
+ self.failUnlessEqual(val, None)
+
+ def CheckAggrExceptionInInit(self):
+ cur = self.con.cursor()
+ cur.execute("select excInit(t) from test")
+ val = cur.fetchone()[0]
+ self.failUnlessEqual(val, None)
+
+ def CheckAggrExceptionInStep(self):
+ cur = self.con.cursor()
+ cur.execute("select excStep(t) from test")
+ val = cur.fetchone()[0]
+ self.failUnlessEqual(val, 42)
+
+ def CheckAggrExceptionInFinalize(self):
+ cur = self.con.cursor()
+ cur.execute("select excFinalize(t) from test")
+ val = cur.fetchone()[0]
+ self.failUnlessEqual(val, None)
+
+ def CheckAggrCheckParamStr(self):
+ cur = self.con.cursor()
+ cur.execute("select checkType('str', ?)", ("foo",))
+ val = cur.fetchone()[0]
+ self.failUnlessEqual(val, 1)
+
+ def CheckAggrCheckParamInt(self):
+ cur = self.con.cursor()
+ cur.execute("select checkType('int', ?)", (42,))
+ val = cur.fetchone()[0]
+ self.failUnlessEqual(val, 1)
+
+ def CheckAggrCheckParamFloat(self):
+ cur = self.con.cursor()
+ cur.execute("select checkType('float', ?)", (3.14,))
+ val = cur.fetchone()[0]
+ self.failUnlessEqual(val, 1)
+
+ def CheckAggrCheckParamNone(self):
+ cur = self.con.cursor()
+ cur.execute("select checkType('None', ?)", (None,))
+ val = cur.fetchone()[0]
+ self.failUnlessEqual(val, 1)
+
+ def CheckAggrCheckParamBlob(self):
+ cur = self.con.cursor()
+ cur.execute("select checkType('blob', ?)", (buffer("blob"),))
+ val = cur.fetchone()[0]
+ self.failUnlessEqual(val, 1)
+
+ def CheckAggrCheckAggrSum(self):
+ cur = self.con.cursor()
+ cur.execute("delete from test")
+ cur.executemany("insert into test(i) values (?)", [(10,), (20,), (30,)])
+ cur.execute("select mysum(i) from test")
+ val = cur.fetchone()[0]
+ self.failUnlessEqual(val, 60)
+
+def suite():
+ function_suite = unittest.makeSuite(FunctionTests, "Check")
+ aggregate_suite = unittest.makeSuite(AggregateTests, "Check")
+ return unittest.TestSuite((function_suite, aggregate_suite))
+
+def test():
+ runner = unittest.TextTestRunner()
+ runner.run(suite())
+
+if __name__ == "__main__":
+ test()
diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py
index f229360..be60023 100755
--- a/Lib/test/regrtest.py
+++ b/Lib/test/regrtest.py
@@ -741,6 +741,7 @@ _expectations = {
test_pwd
test_resource
test_signal
+ test_sqlite
test_sunaudiodev
test_threadsignals
test_timing
@@ -763,6 +764,7 @@ _expectations = {
test_nis
test_ntpath
test_ossaudiodev
+ test_sqlite
test_sunaudiodev
""",
'mac':
@@ -802,6 +804,7 @@ _expectations = {
test_pwd
test_resource
test_signal
+ test_sqlite
test_sunaudiodev
test_sundry
test_tarfile
@@ -826,6 +829,7 @@ _expectations = {
test_openpty
test_pyexpat
test_sax
+ test_sqlite
test_sunaudiodev
test_sundry
""",
@@ -848,6 +852,7 @@ _expectations = {
test_openpty
test_pyexpat
test_sax
+ test_sqlite
test_sunaudiodev
test_sundry
""",
@@ -875,6 +880,7 @@ _expectations = {
test_pyexpat
test_queue
test_sax
+ test_sqlite
test_sunaudiodev
test_sundry
test_thread
@@ -915,6 +921,7 @@ _expectations = {
test_pty
test_pwd
test_strop
+ test_sqlite
test_sunaudiodev
test_sundry
test_thread
@@ -944,6 +951,7 @@ _expectations = {
test_ntpath
test_ossaudiodev
test_poll
+ test_sqlite
test_sunaudiodev
""",
'sunos5':
@@ -962,6 +970,7 @@ _expectations = {
test_imgfile
test_linuxaudiodev
test_openpty
+ test_sqlite
test_zipfile
test_zlib
""",
@@ -988,6 +997,7 @@ _expectations = {
test_openpty
test_pyexpat
test_sax
+ test_sqlite
test_sunaudiodev
test_zipfile
test_zlib
@@ -1013,6 +1023,7 @@ _expectations = {
test_poll
test_popen2
test_resource
+ test_sqlite
test_sunaudiodev
""",
'cygwin':
@@ -1034,6 +1045,7 @@ _expectations = {
test_nis
test_ossaudiodev
test_socketserver
+ test_sqlite
test_sunaudiodev
""",
'os2emx':
@@ -1060,6 +1072,7 @@ _expectations = {
test_pty
test_resource
test_signal
+ test_sqlite
test_sunaudiodev
""",
'freebsd4':
@@ -1086,6 +1099,7 @@ _expectations = {
test_scriptpackages
test_socket_ssl
test_socketserver
+ test_sqlite
test_sunaudiodev
test_tcl
test_timeout
@@ -1115,6 +1129,7 @@ _expectations = {
test_macostools
test_nis
test_ossaudiodev
+ test_sqlite
test_sunaudiodev
test_tcl
test_winreg
@@ -1147,6 +1162,7 @@ _expectations = {
test_plistlib
test_scriptpackages
test_tcl
+ test_sqlite
test_sunaudiodev
test_unicode_file
test_winreg
diff --git a/Lib/test/test_sqlite.py b/Lib/test/test_sqlite.py
new file mode 100644
index 0000000..1b1d0e5
--- /dev/null
+++ b/Lib/test/test_sqlite.py
@@ -0,0 +1,16 @@
+from test.test_support import run_unittest, TestSkipped
+import unittest
+
+try:
+ import _sqlite3
+except ImportError:
+ raise TestSkipped('no sqlite available')
+from sqlite3.test import (dbapi, types, userfunctions,
+ factory, transactions)
+
+def test_main():
+ run_unittest(dbapi.suite(), types.suite(), userfunctions.suite(),
+ factory.suite(), transactions.suite())
+
+if __name__ == "__main__":
+ test_main()
diff --git a/Misc/NEWS b/Misc/NEWS
index 974712a..b877fe6 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -489,6 +489,11 @@ Extension Modules
Library
-------
+- Added the sqlite3 package. This is based on pysqlite2.1.3, and provides
+ a DB-API interface in the standard library. You'll need sqlite 3.2.2 or
+ later to build this - if you have an earlier version, the C extension
+ module will not be built.
+
- Bug #1460340: ``random.sample(dict)`` failed in various ways. Dicts
aren't officially supported here, and trying to use them will probably
raise an exception some day. But dicts have been allowed, and "mostly
diff --git a/Modules/_sqlite/adapters.c b/Modules/_sqlite/adapters.c
new file mode 100644
index 0000000..e6fde03
--- /dev/null
+++ b/Modules/_sqlite/adapters.c
@@ -0,0 +1,40 @@
+/* adapters.c - default adapters
+ *
+ * Copyright (C) 2005 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#include "util.h"
+#include "module.h"
+#include "adapters.h"
+
+/* dummy, will be implemented in a later version */
+
+PyObject* adapt_date(PyObject* self, PyObject* args, PyObject* kwargs)
+{
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyObject* adapt_datetime(PyObject* self, PyObject* args, PyObject* kwargs)
+{
+ Py_INCREF(Py_None);
+ return Py_None;
+}
diff --git a/Modules/_sqlite/adapters.h b/Modules/_sqlite/adapters.h
new file mode 100644
index 0000000..d2e8479
--- /dev/null
+++ b/Modules/_sqlite/adapters.h
@@ -0,0 +1,33 @@
+/* adapters.h - default adapters
+ *
+ * Copyright (C) 2005 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_ADAPTERS_H
+#define PYSQLITE_ADAPTERS_H
+#include "Python.h"
+#include "pythread.h"
+#include "sqlite3.h"
+
+PyObject* adapt_date(PyObject* self, PyObject* args, PyObject* kwargs);
+PyObject* adapt_datetime(PyObject* self, PyObject* args, PyObject* kwargs);
+
+#endif
diff --git a/Modules/_sqlite/cache.c b/Modules/_sqlite/cache.c
new file mode 100644
index 0000000..0c7d4a3
--- /dev/null
+++ b/Modules/_sqlite/cache.c
@@ -0,0 +1,343 @@
+/* cache .c - a LRU cache
+ *
+ * Copyright (C) 2004-2005 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#include "cache.h"
+
+/* only used internally */
+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;
+ }
+
+ Py_INCREF(key);
+ node->key = key;
+
+ Py_INCREF(data);
+ node->data = data;
+
+ node->prev = NULL;
+ node->next = NULL;
+
+ return node;
+}
+
+void node_dealloc(Node* self)
+{
+ Py_DECREF(self->key);
+ Py_DECREF(self->data);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+int cache_init(Cache* self, PyObject* args, PyObject* kwargs)
+{
+ PyObject* factory;
+ int size = 10;
+
+ self->factory = NULL;
+
+ if (!PyArg_ParseTuple(args, "O|i", &factory, &size))
+ {
+ return -1;
+ }
+
+ if (size < 5) {
+ size = 5;
+ }
+ self->size = size;
+ self->first = NULL;
+ self->last = NULL;
+ self->mapping = PyDict_New();
+ Py_INCREF(factory);
+ self->factory = factory;
+
+ self->decref_factory = 1;
+
+ return 0;
+}
+
+void cache_dealloc(Cache* self)
+{
+ Node* node;
+ Node* delete_node;
+
+ if (!self->factory) {
+ /* constructor failed, just get out of here */
+ return;
+ }
+
+ node = self->first;
+ while (node) {
+ delete_node = node;
+ node = node->next;
+ Py_DECREF(delete_node);
+ }
+
+ if (self->decref_factory) {
+ Py_DECREF(self->factory);
+ }
+ Py_DECREF(self->mapping);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+PyObject* cache_get(Cache* self, PyObject* args)
+{
+ PyObject* key;
+ 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++;
+ if (node->prev && node->count > node->prev->count) {
+ ptr = node->prev;
+
+ while (ptr->prev && node->count > ptr->prev->count) {
+ ptr = ptr->prev;
+ }
+
+ if (node->next) {
+ node->next->prev = node->prev;
+ } else {
+ self->last = node->prev;
+ }
+ if (node->prev) {
+ node->prev->next = node->next;
+ }
+ if (ptr->prev) {
+ ptr->prev->next = node;
+ } else {
+ self->first = node;
+ }
+
+ node->next = ptr;
+ node->prev = ptr->prev;
+ if (!node->prev) {
+ self->first = node;
+ }
+ ptr->prev = node;
+ }
+ } else {
+ if (PyDict_Size(self->mapping) == self->size) {
+ if (self->last) {
+ node = self->last;
+ PyDict_DelItem(self->mapping, self->last->key);
+ if (node->prev) {
+ node->prev->next = NULL;
+ }
+ self->last = node->prev;
+ node->prev = NULL;
+
+ Py_DECREF(node);
+ }
+ }
+
+ data = PyObject_CallFunction(self->factory, "O", key);
+
+ if (!data) {
+ return NULL;
+ }
+
+ node = new_node(key, data);
+ node->prev = self->last;
+
+ Py_DECREF(data);
+
+ 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);
+ return node->data;
+}
+
+PyObject* cache_display(Cache* self, PyObject* args)
+{
+ Node* ptr;
+ PyObject* prevkey;
+ PyObject* nextkey;
+ PyObject* fmt_args;
+ PyObject* template;
+ PyObject* display_str;
+
+ ptr = self->first;
+
+ while (ptr) {
+ if (ptr->prev) {
+ prevkey = ptr->prev->key;
+ } else {
+ prevkey = Py_None;
+ }
+ Py_INCREF(prevkey);
+
+ if (ptr->next) {
+ nextkey = ptr->next->key;
+ } else {
+ nextkey = Py_None;
+ }
+ Py_INCREF(nextkey);
+
+ fmt_args = Py_BuildValue("OOO", prevkey, ptr->key, nextkey);
+ template = PyString_FromString("%s <- %s ->%s\n");
+ display_str = PyString_Format(template, fmt_args);
+ Py_DECREF(template);
+ Py_DECREF(fmt_args);
+ PyObject_Print(display_str, stdout, Py_PRINT_RAW);
+ Py_DECREF(display_str);
+
+ Py_DECREF(prevkey);
+ Py_DECREF(nextkey);
+
+ ptr = ptr->next;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyMethodDef cache_methods[] = {
+ {"get", (PyCFunction)cache_get, METH_VARARGS,
+ PyDoc_STR("Gets an entry from the cache.")},
+ {"display", (PyCFunction)cache_display, METH_NOARGS,
+ PyDoc_STR("For debugging only.")},
+ {NULL, NULL}
+};
+
+PyTypeObject NodeType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "pysqlite2.dbapi2.Node", /* tp_name */
+ sizeof(Node), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)node_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */
+ 0, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0 /* tp_free */
+};
+
+PyTypeObject CacheType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "pysqlite2.dbapi2.Cache", /* tp_name */
+ sizeof(Cache), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)cache_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */
+ 0, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ cache_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)cache_init, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0 /* tp_free */
+};
+
+extern int cache_setup_types(void)
+{
+ int rc;
+
+ NodeType.tp_new = PyType_GenericNew;
+ CacheType.tp_new = PyType_GenericNew;
+
+ rc = PyType_Ready(&NodeType);
+ if (rc < 0) {
+ return rc;
+ }
+
+ rc = PyType_Ready(&CacheType);
+ return rc;
+}
diff --git a/Modules/_sqlite/cache.h b/Modules/_sqlite/cache.h
new file mode 100644
index 0000000..5cc16f3
--- /dev/null
+++ b/Modules/_sqlite/cache.h
@@ -0,0 +1,61 @@
+/* cache.h - definitions for the LRU cache
+ *
+ * Copyright (C) 2004-2005 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#ifndef PYSQLITE_CACHE_H
+#define PYSQLITE_CACHE_H
+#include "Python.h"
+
+typedef struct _Node
+{
+ PyObject_HEAD
+ PyObject* key;
+ PyObject* data;
+ long count;
+ struct _Node* prev;
+ struct _Node* next;
+} Node;
+
+typedef struct
+{
+ PyObject_HEAD
+ int size;
+ PyObject* mapping;
+ PyObject* factory;
+ Node* first;
+ Node* last;
+ int decref_factory;
+} Cache;
+
+extern PyTypeObject NodeType;
+extern PyTypeObject CacheType;
+
+int node_init(Node* self, PyObject* args, PyObject* kwargs);
+void node_dealloc(Node* self);
+
+int cache_init(Cache* self, PyObject* args, PyObject* kwargs);
+void cache_dealloc(Cache* self);
+PyObject* cache_get(Cache* self, PyObject* args);
+
+int cache_setup_types(void);
+
+#endif
diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c
new file mode 100644
index 0000000..e53f0b5
--- /dev/null
+++ b/Modules/_sqlite/connection.c
@@ -0,0 +1,922 @@
+/* connection.c - the connection type
+ *
+ * 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.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#include "cache.h"
+#include "module.h"
+#include "connection.h"
+#include "statement.h"
+#include "cursor.h"
+#include "prepare_protocol.h"
+#include "util.h"
+#include "pythread.h"
+
+static int connection_set_isolation_level(Connection* self, PyObject* isolation_level);
+
+int connection_init(Connection* self, PyObject* args, PyObject* kwargs)
+{
+ static char *kwlist[] = {"database", "timeout", "detect_types", "isolation_level", "check_same_thread", "factory", "cached_statements", NULL, NULL};
+
+ char* database;
+ int detect_types = 0;
+ PyObject* isolation_level = NULL;
+ PyObject* factory = NULL;
+ int check_same_thread = 1;
+ int cached_statements = 100;
+ double timeout = 5.0;
+ int rc;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|diOiOi", kwlist,
+ &database, &timeout, &detect_types, &isolation_level, &check_same_thread, &factory, &cached_statements))
+ {
+ return -1;
+ }
+
+ self->begin_statement = NULL;
+
+ self->statement_cache = NULL;
+
+ Py_INCREF(Py_None);
+ self->row_factory = Py_None;
+
+ Py_INCREF(&PyUnicode_Type);
+ self->text_factory = (PyObject*)&PyUnicode_Type;
+
+ Py_BEGIN_ALLOW_THREADS
+ rc = sqlite3_open(database, &self->db);
+ Py_END_ALLOW_THREADS
+
+ if (rc != SQLITE_OK) {
+ _seterror(self->db);
+ return -1;
+ }
+
+ if (!isolation_level) {
+ isolation_level = PyString_FromString("");
+ } else {
+ Py_INCREF(isolation_level);
+ }
+ self->isolation_level = NULL;
+ connection_set_isolation_level(self, isolation_level);
+ Py_DECREF(isolation_level);
+
+ self->statement_cache = (Cache*)PyObject_CallFunction((PyObject*)&CacheType, "Oi", self, cached_statements);
+ if (PyErr_Occurred()) {
+ return -1;
+ }
+
+ /* By default, the Cache class INCREFs the factory in its initializer, and
+ * decrefs it in its deallocator method. Since this would create a circular
+ * reference here, we're breaking it by decrementing self, and telling the
+ * cache class to not decref the factory (self) in its deallocator.
+ */
+ self->statement_cache->decref_factory = 0;
+ Py_DECREF(self);
+
+ self->inTransaction = 0;
+ self->detect_types = detect_types;
+ self->timeout = timeout;
+ (void)sqlite3_busy_timeout(self->db, (int)(timeout*1000));
+
+ self->thread_ident = PyThread_get_thread_ident();
+ self->check_same_thread = check_same_thread;
+
+ self->function_pinboard = PyDict_New();
+
+ self->Warning = Warning;
+ self->Error = Error;
+ self->InterfaceError = InterfaceError;
+ self->DatabaseError = DatabaseError;
+ self->DataError = DataError;
+ self->OperationalError = OperationalError;
+ self->IntegrityError = IntegrityError;
+ self->InternalError = InternalError;
+ self->ProgrammingError = ProgrammingError;
+ self->NotSupportedError = NotSupportedError;
+
+ return 0;
+}
+
+void flush_statement_cache(Connection* self)
+{
+ Node* node;
+ Statement* statement;
+
+ node = self->statement_cache->first;
+
+ while (node) {
+ statement = (Statement*)(node->data);
+ (void)statement_finalize(statement);
+ node = node->next;
+ }
+
+ Py_DECREF(self->statement_cache);
+ self->statement_cache = (Cache*)PyObject_CallFunction((PyObject*)&CacheType, "O", self);
+ Py_DECREF(self);
+ self->statement_cache->decref_factory = 0;
+}
+
+void reset_all_statements(Connection* self)
+{
+ Node* node;
+ Statement* statement;
+
+ node = self->statement_cache->first;
+
+ while (node) {
+ statement = (Statement*)(node->data);
+ (void)statement_reset(statement);
+ node = node->next;
+ }
+}
+
+void connection_dealloc(Connection* self)
+{
+ Py_XDECREF(self->statement_cache);
+
+ /* Clean up if user has not called .close() explicitly. */
+ if (self->db) {
+ Py_BEGIN_ALLOW_THREADS
+ sqlite3_close(self->db);
+ Py_END_ALLOW_THREADS
+ }
+
+ if (self->begin_statement) {
+ PyMem_Free(self->begin_statement);
+ }
+ Py_XDECREF(self->isolation_level);
+ Py_XDECREF(self->function_pinboard);
+ Py_XDECREF(self->row_factory);
+ Py_XDECREF(self->text_factory);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+PyObject* connection_cursor(Connection* self, PyObject* args, PyObject* kwargs)
+{
+ static char *kwlist[] = {"factory", NULL, NULL};
+ PyObject* factory = NULL;
+ PyObject* cursor;
+
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O", kwlist,
+ &factory)) {
+ return NULL;
+ }
+
+ if (!check_thread(self) || !check_connection(self)) {
+ return NULL;
+ }
+
+ if (factory == NULL) {
+ factory = (PyObject*)&CursorType;
+ }
+
+ cursor = PyObject_CallFunction(factory, "O", self);
+
+ if (cursor && self->row_factory != Py_None) {
+ Py_XDECREF(((Cursor*)cursor)->row_factory);
+ Py_INCREF(self->row_factory);
+ ((Cursor*)cursor)->row_factory = self->row_factory;
+ }
+
+ return cursor;
+}
+
+PyObject* connection_close(Connection* self, PyObject* args)
+{
+ int rc;
+
+ if (!check_thread(self)) {
+ return NULL;
+ }
+
+ flush_statement_cache(self);
+
+ if (self->db) {
+ Py_BEGIN_ALLOW_THREADS
+ rc = sqlite3_close(self->db);
+ Py_END_ALLOW_THREADS
+
+ if (rc != SQLITE_OK) {
+ _seterror(self->db);
+ return NULL;
+ } else {
+ self->db = NULL;
+ }
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/*
+ * Checks if a connection object is usable (i. e. not closed).
+ *
+ * 0 => error; 1 => ok
+ */
+int check_connection(Connection* con)
+{
+ if (!con->db) {
+ PyErr_SetString(ProgrammingError, "Cannot operate on a closed database.");
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+PyObject* _connection_begin(Connection* self)
+{
+ int rc;
+ const char* tail;
+ sqlite3_stmt* statement;
+
+ Py_BEGIN_ALLOW_THREADS
+ rc = sqlite3_prepare(self->db, self->begin_statement, -1, &statement, &tail);
+ Py_END_ALLOW_THREADS
+
+ if (rc != SQLITE_OK) {
+ _seterror(self->db);
+ goto error;
+ }
+
+ rc = _sqlite_step_with_busyhandler(statement, self);
+ if (rc == SQLITE_DONE) {
+ self->inTransaction = 1;
+ } else {
+ _seterror(self->db);
+ }
+
+ Py_BEGIN_ALLOW_THREADS
+ rc = sqlite3_finalize(statement);
+ Py_END_ALLOW_THREADS
+
+ if (rc != SQLITE_OK && !PyErr_Occurred()) {
+ _seterror(self->db);
+ }
+
+error:
+ if (PyErr_Occurred()) {
+ return NULL;
+ } else {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+}
+
+PyObject* connection_commit(Connection* self, PyObject* args)
+{
+ int rc;
+ const char* tail;
+ sqlite3_stmt* statement;
+
+ if (!check_thread(self) || !check_connection(self)) {
+ return NULL;
+ }
+
+ if (self->inTransaction) {
+ Py_BEGIN_ALLOW_THREADS
+ rc = sqlite3_prepare(self->db, "COMMIT", -1, &statement, &tail);
+ Py_END_ALLOW_THREADS
+ if (rc != SQLITE_OK) {
+ _seterror(self->db);
+ goto error;
+ }
+
+ rc = _sqlite_step_with_busyhandler(statement, self);
+ if (rc == SQLITE_DONE) {
+ self->inTransaction = 0;
+ } else {
+ _seterror(self->db);
+ }
+
+ Py_BEGIN_ALLOW_THREADS
+ rc = sqlite3_finalize(statement);
+ Py_END_ALLOW_THREADS
+ if (rc != SQLITE_OK && !PyErr_Occurred()) {
+ _seterror(self->db);
+ }
+
+ }
+
+error:
+ if (PyErr_Occurred()) {
+ return NULL;
+ } else {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+}
+
+PyObject* connection_rollback(Connection* self, PyObject* args)
+{
+ int rc;
+ const char* tail;
+ sqlite3_stmt* statement;
+
+ if (!check_thread(self) || !check_connection(self)) {
+ return NULL;
+ }
+
+ if (self->inTransaction) {
+ reset_all_statements(self);
+
+ Py_BEGIN_ALLOW_THREADS
+ rc = sqlite3_prepare(self->db, "ROLLBACK", -1, &statement, &tail);
+ Py_END_ALLOW_THREADS
+ if (rc != SQLITE_OK) {
+ _seterror(self->db);
+ goto error;
+ }
+
+ rc = _sqlite_step_with_busyhandler(statement, self);
+ if (rc == SQLITE_DONE) {
+ self->inTransaction = 0;
+ } else {
+ _seterror(self->db);
+ }
+
+ Py_BEGIN_ALLOW_THREADS
+ rc = sqlite3_finalize(statement);
+ Py_END_ALLOW_THREADS
+ if (rc != SQLITE_OK && !PyErr_Occurred()) {
+ _seterror(self->db);
+ }
+
+ }
+
+error:
+ if (PyErr_Occurred()) {
+ return NULL;
+ } else {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+}
+
+void _set_result(sqlite3_context* context, PyObject* py_val)
+{
+ long longval;
+ const char* buffer;
+ int buflen;
+ PyObject* stringval;
+
+ if (PyErr_Occurred()) {
+ /* Errors in callbacks are ignored, and we return NULL */
+ PyErr_Clear();
+ sqlite3_result_null(context);
+ } else if (py_val == Py_None) {
+ sqlite3_result_null(context);
+ } else if (PyInt_Check(py_val)) {
+ longval = PyInt_AsLong(py_val);
+ /* TODO: investigate what to do with range overflows - long vs. long long */
+ sqlite3_result_int64(context, (PY_LONG_LONG)longval);
+ } else if (PyFloat_Check(py_val)) {
+ sqlite3_result_double(context, PyFloat_AsDouble(py_val));
+ } else if (PyBuffer_Check(py_val)) {
+ if (PyObject_AsCharBuffer(py_val, &buffer, &buflen) != 0) {
+ PyErr_SetString(PyExc_ValueError, "could not convert BLOB to buffer");
+ }
+ sqlite3_result_blob(context, buffer, buflen, SQLITE_TRANSIENT);
+ } else if (PyString_Check(py_val)) {
+ sqlite3_result_text(context, PyString_AsString(py_val), -1, SQLITE_TRANSIENT);
+ } else if (PyUnicode_Check(py_val)) {
+ stringval = PyUnicode_AsUTF8String(py_val);
+ sqlite3_result_text(context, PyString_AsString(stringval), -1, SQLITE_TRANSIENT);
+ Py_DECREF(stringval);
+ } else {
+ /* TODO: raise error */
+ }
+}
+
+PyObject* _build_py_params(sqlite3_context *context, int argc, sqlite3_value** argv)
+{
+ PyObject* args;
+ int i;
+ sqlite3_value* cur_value;
+ PyObject* cur_py_value;
+ const char* val_str;
+ PY_LONG_LONG val_int;
+ int buflen;
+ void* raw_buffer;
+
+ args = PyTuple_New(argc);
+
+ for (i = 0; i < argc; i++) {
+ cur_value = argv[i];
+ switch (sqlite3_value_type(argv[i])) {
+ case SQLITE_INTEGER:
+ val_int = sqlite3_value_int64(cur_value);
+ cur_py_value = PyInt_FromLong((long)val_int);
+ break;
+ case SQLITE_FLOAT:
+ cur_py_value = PyFloat_FromDouble(sqlite3_value_double(cur_value));
+ break;
+ case SQLITE_TEXT:
+ val_str = (const char*)sqlite3_value_text(cur_value);
+ cur_py_value = PyUnicode_DecodeUTF8(val_str, strlen(val_str), NULL);
+ /* TODO: have a way to show errors here */
+ if (!cur_py_value) {
+ Py_INCREF(Py_None);
+ cur_py_value = Py_None;
+ }
+ break;
+ case SQLITE_BLOB:
+ buflen = sqlite3_value_bytes(cur_value);
+ cur_py_value = PyBuffer_New(buflen);
+ if (!cur_py_value) {
+ /* TODO: error */
+ }
+ if (PyObject_AsWriteBuffer(cur_py_value, &raw_buffer, &buflen)) {
+ /* TODO: error */
+ }
+ memcpy(raw_buffer, sqlite3_value_blob(cur_value), buflen);
+ break;
+ case SQLITE_NULL:
+ default:
+ Py_INCREF(Py_None);
+ cur_py_value = Py_None;
+ }
+ PyTuple_SetItem(args, i, cur_py_value);
+
+ }
+
+ return args;
+}
+
+void _func_callback(sqlite3_context* context, int argc, sqlite3_value** argv)
+{
+ PyObject* args;
+ PyObject* py_func;
+ PyObject* py_retval;
+
+
+ PyGILState_STATE threadstate;
+
+ threadstate = PyGILState_Ensure();
+
+ py_func = (PyObject*)sqlite3_user_data(context);
+
+ args = _build_py_params(context, argc, argv);
+
+ py_retval = PyObject_CallObject(py_func, args);
+ Py_DECREF(args);
+
+ _set_result(context, py_retval);
+ Py_XDECREF(py_retval);
+
+ PyGILState_Release(threadstate);
+}
+
+static void _step_callback(sqlite3_context *context, int argc, sqlite3_value** params)
+{
+ PyObject* args;
+ PyObject* function_result;
+ PyObject* aggregate_class;
+ PyObject** aggregate_instance;
+ PyObject* stepmethod;
+
+ PyGILState_STATE threadstate;
+
+ threadstate = PyGILState_Ensure();
+
+ aggregate_class = (PyObject*)sqlite3_user_data(context);
+
+ aggregate_instance = (PyObject**)sqlite3_aggregate_context(context, sizeof(PyObject*));
+
+ if (*aggregate_instance == 0) {
+ *aggregate_instance = PyObject_CallFunction(aggregate_class, "");
+
+ if (PyErr_Occurred())
+ {
+ PyErr_Clear();
+ *aggregate_instance = 0;
+ PyGILState_Release(threadstate);
+ return;
+ }
+ }
+
+ stepmethod = PyObject_GetAttrString(*aggregate_instance, "step");
+ if (!stepmethod)
+ {
+ PyGILState_Release(threadstate);
+ return;
+ }
+
+ args = _build_py_params(context, argc, params);
+
+ function_result = PyObject_CallObject(stepmethod, args);
+ Py_DECREF(args);
+ Py_DECREF(stepmethod);
+
+ if (function_result == NULL) {
+ PyErr_Clear();
+ } else {
+ Py_DECREF(function_result);
+ }
+
+ PyGILState_Release(threadstate);
+}
+
+void _final_callback(sqlite3_context* context)
+{
+ PyObject* args;
+ PyObject* function_result;
+ PyObject** aggregate_instance;
+ PyObject* aggregate_class;
+ PyObject* finalizemethod;
+
+ PyGILState_STATE threadstate;
+
+ threadstate = PyGILState_Ensure();
+
+ aggregate_class = (PyObject*)sqlite3_user_data(context);
+
+ aggregate_instance = (PyObject**)sqlite3_aggregate_context(context, sizeof(PyObject*));
+ if (!*aggregate_instance) {
+ /* this branch is executed if there was an exception in the aggregate's
+ * __init__ */
+
+ PyGILState_Release(threadstate);
+ return;
+ }
+
+ finalizemethod = PyObject_GetAttrString(*aggregate_instance, "finalize");
+
+ if (!finalizemethod) {
+ /*
+ PyErr_SetString(ProgrammingError, "finalize method missing");
+ goto error;
+ */
+ Py_INCREF(Py_None);
+ function_result = Py_None;
+ } else {
+ args = PyTuple_New(0);
+ function_result = PyObject_CallObject(finalizemethod, args);
+ Py_DECREF(args);
+ Py_DECREF(finalizemethod);
+ }
+
+ _set_result(context, function_result);
+ Py_XDECREF(*aggregate_instance);
+ Py_XDECREF(function_result);
+
+ PyGILState_Release(threadstate);
+}
+
+
+PyObject* connection_create_function(Connection* self, PyObject* args, PyObject* kwargs)
+{
+ static char *kwlist[] = {"name", "narg", "func", NULL, NULL};
+
+ PyObject* func;
+ char* name;
+ int narg;
+ int rc;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "siO", kwlist,
+ &name, &narg, &func))
+ {
+ return NULL;
+ }
+
+ rc = sqlite3_create_function(self->db, name, narg, SQLITE_UTF8, (void*)func, _func_callback, NULL, NULL);
+
+ PyDict_SetItem(self->function_pinboard, func, Py_None);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyObject* connection_create_aggregate(Connection* self, PyObject* args, PyObject* kwargs)
+{
+ PyObject* aggregate_class;
+
+ int n_arg;
+ char* name;
+ static char *kwlist[] = { "name", "n_arg", "aggregate_class", NULL };
+ int rc;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "siO:create_aggregate",
+ kwlist, &name, &n_arg, &aggregate_class)) {
+ return NULL;
+ }
+
+ rc = sqlite3_create_function(self->db, name, n_arg, SQLITE_UTF8, (void*)aggregate_class, 0, &_step_callback, &_final_callback);
+ if (rc != SQLITE_OK) {
+ _seterror(self->db);
+ return NULL;
+ } else {
+ PyDict_SetItem(self->function_pinboard, aggregate_class, Py_None);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+}
+
+int check_thread(Connection* self)
+{
+ if (self->check_same_thread) {
+ if (PyThread_get_thread_ident() != self->thread_ident) {
+ PyErr_Format(ProgrammingError,
+ "SQLite objects created in a thread can only be used in that same thread."
+ "The object was created in thread id %ld and this is thread id %ld",
+ self->thread_ident, PyThread_get_thread_ident());
+ return 0;
+ }
+
+ }
+
+ return 1;
+}
+
+static PyObject* connection_get_isolation_level(Connection* self, void* unused)
+{
+ Py_INCREF(self->isolation_level);
+ return self->isolation_level;
+}
+
+static int connection_set_isolation_level(Connection* self, PyObject* isolation_level)
+{
+ PyObject* empty;
+ PyObject* res;
+ PyObject* begin_statement;
+
+ Py_XDECREF(self->isolation_level);
+
+ if (isolation_level == Py_None) {
+ Py_INCREF(Py_None);
+ self->begin_statement = NULL;
+ self->isolation_level = Py_None;
+
+ empty = PyTuple_New(0);
+ res = connection_commit(self, empty);
+ Py_DECREF(empty);
+ Py_DECREF(res);
+
+ self->inTransaction = 0;
+ } else {
+ Py_INCREF(isolation_level);
+ self->isolation_level = isolation_level;
+
+ begin_statement = PyString_FromString("BEGIN ");
+ if (!begin_statement) {
+ return -1;
+ }
+ PyString_Concat(&begin_statement, isolation_level);
+ if (!begin_statement) {
+ return -1;
+ }
+
+ self->begin_statement = PyMem_Malloc(PyString_Size(begin_statement) + 2);
+ if (!self->begin_statement) {
+ return -1;
+ }
+
+ strcpy(self->begin_statement, PyString_AsString(begin_statement));
+ Py_DECREF(begin_statement);
+ }
+
+ return 0;
+}
+
+PyObject* connection_call(Connection* self, PyObject* args, PyObject* kwargs)
+{
+ PyObject* sql;
+ Statement* statement;
+ int rc;
+
+ if (!PyArg_ParseTuple(args, "O", &sql)) {
+ return NULL;
+ }
+
+ statement = PyObject_New(Statement, &StatementType);
+ if (!statement) {
+ return NULL;
+ }
+
+ rc = statement_create(statement, self, sql);
+
+ if (rc != SQLITE_OK) {
+ if (rc == PYSQLITE_TOO_MUCH_SQL) {
+ PyErr_SetString(Warning, "You can only execute one statement at a time.");
+ } else if (rc == PYSQLITE_SQL_WRONG_TYPE) {
+ PyErr_SetString(Warning, "SQL is of wrong type. Must be string or unicode.");
+ } else {
+ _seterror(self->db);
+ }
+
+ Py_DECREF(statement);
+ statement = 0;
+ }
+
+ return (PyObject*)statement;
+}
+
+PyObject* connection_execute(Connection* self, PyObject* args, PyObject* kwargs)
+{
+ PyObject* cursor = 0;
+ PyObject* result = 0;
+ PyObject* method = 0;
+
+ cursor = PyObject_CallMethod((PyObject*)self, "cursor", "");
+ if (!cursor) {
+ goto error;
+ }
+
+ method = PyObject_GetAttrString(cursor, "execute");
+ if (!method) {
+ Py_DECREF(cursor);
+ cursor = 0;
+ goto error;
+ }
+
+ result = PyObject_CallObject(method, args);
+ if (!result) {
+ Py_DECREF(cursor);
+ cursor = 0;
+ }
+
+error:
+ Py_XDECREF(result);
+ Py_XDECREF(method);
+
+ return cursor;
+}
+
+PyObject* connection_executemany(Connection* self, PyObject* args, PyObject* kwargs)
+{
+ PyObject* cursor = 0;
+ PyObject* result = 0;
+ PyObject* method = 0;
+
+ cursor = PyObject_CallMethod((PyObject*)self, "cursor", "");
+ if (!cursor) {
+ goto error;
+ }
+
+ method = PyObject_GetAttrString(cursor, "executemany");
+ if (!method) {
+ Py_DECREF(cursor);
+ cursor = 0;
+ goto error;
+ }
+
+ result = PyObject_CallObject(method, args);
+ if (!result) {
+ Py_DECREF(cursor);
+ cursor = 0;
+ }
+
+error:
+ Py_XDECREF(result);
+ Py_XDECREF(method);
+
+ return cursor;
+}
+
+PyObject* connection_executescript(Connection* self, PyObject* args, PyObject* kwargs)
+{
+ PyObject* cursor = 0;
+ PyObject* result = 0;
+ PyObject* method = 0;
+
+ cursor = PyObject_CallMethod((PyObject*)self, "cursor", "");
+ if (!cursor) {
+ goto error;
+ }
+
+ method = PyObject_GetAttrString(cursor, "executescript");
+ if (!method) {
+ Py_DECREF(cursor);
+ cursor = 0;
+ goto error;
+ }
+
+ result = PyObject_CallObject(method, args);
+ if (!result) {
+ Py_DECREF(cursor);
+ cursor = 0;
+ }
+
+error:
+ Py_XDECREF(result);
+ Py_XDECREF(method);
+
+ return cursor;
+}
+
+static char connection_doc[] =
+PyDoc_STR("<missing docstring>");
+
+static PyGetSetDef connection_getset[] = {
+ {"isolation_level", (getter)connection_get_isolation_level, (setter)connection_set_isolation_level},
+ {NULL}
+};
+
+static PyMethodDef connection_methods[] = {
+ {"cursor", (PyCFunction)connection_cursor, METH_VARARGS|METH_KEYWORDS,
+ PyDoc_STR("Return a cursor for the connection.")},
+ {"close", (PyCFunction)connection_close, METH_NOARGS,
+ PyDoc_STR("Closes the connection.")},
+ {"commit", (PyCFunction)connection_commit, METH_NOARGS,
+ PyDoc_STR("Commit the current transaction.")},
+ {"rollback", (PyCFunction)connection_rollback, METH_NOARGS,
+ PyDoc_STR("Roll back the current transaction.")},
+ {"create_function", (PyCFunction)connection_create_function, METH_VARARGS|METH_KEYWORDS,
+ PyDoc_STR("Creates a new function. Non-standard.")},
+ {"create_aggregate", (PyCFunction)connection_create_aggregate, METH_VARARGS|METH_KEYWORDS,
+ PyDoc_STR("Creates a new aggregate. Non-standard.")},
+ {"execute", (PyCFunction)connection_execute, METH_VARARGS,
+ PyDoc_STR("Executes a SQL statement. Non-standard.")},
+ {"executemany", (PyCFunction)connection_executemany, METH_VARARGS,
+ 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.")},
+ {NULL, NULL}
+};
+
+static struct PyMemberDef connection_members[] =
+{
+ {"Warning", T_OBJECT, offsetof(Connection, Warning), RO},
+ {"Error", T_OBJECT, offsetof(Connection, Error), RO},
+ {"InterfaceError", T_OBJECT, offsetof(Connection, InterfaceError), RO},
+ {"DatabaseError", T_OBJECT, offsetof(Connection, DatabaseError), RO},
+ {"DataError", T_OBJECT, offsetof(Connection, DataError), RO},
+ {"OperationalError", T_OBJECT, offsetof(Connection, OperationalError), RO},
+ {"IntegrityError", T_OBJECT, offsetof(Connection, IntegrityError), RO},
+ {"InternalError", T_OBJECT, offsetof(Connection, InternalError), RO},
+ {"ProgrammingError", T_OBJECT, offsetof(Connection, ProgrammingError), RO},
+ {"NotSupportedError", T_OBJECT, offsetof(Connection, NotSupportedError), RO},
+ {"row_factory", T_OBJECT, offsetof(Connection, row_factory)},
+ {"text_factory", T_OBJECT, offsetof(Connection, text_factory)},
+ {NULL}
+};
+
+PyTypeObject ConnectionType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "pysqlite2.dbapi2.Connection", /* tp_name */
+ sizeof(Connection), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)connection_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ (ternaryfunc)connection_call, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */
+ connection_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ connection_methods, /* tp_methods */
+ connection_members, /* tp_members */
+ connection_getset, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)connection_init, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0 /* tp_free */
+};
+
+extern int connection_setup_types(void)
+{
+ ConnectionType.tp_new = PyType_GenericNew;
+ return PyType_Ready(&ConnectionType);
+}
diff --git a/Modules/_sqlite/connection.h b/Modules/_sqlite/connection.h
new file mode 100644
index 0000000..ef03bc4
--- /dev/null
+++ b/Modules/_sqlite/connection.h
@@ -0,0 +1,103 @@
+/* connection.h - definitions for the connection type
+ *
+ * Copyright (C) 2004-2005 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#ifndef PYSQLITE_CONNECTION_H
+#define PYSQLITE_CONNECTION_H
+#include "Python.h"
+#include "pythread.h"
+#include "structmember.h"
+
+#include "cache.h"
+#include "module.h"
+
+#include "sqlite3.h"
+
+typedef struct
+{
+ PyObject_HEAD
+ sqlite3* db;
+
+ int inTransaction;
+ int detect_types;
+
+ /* the timeout value in seconds for database locks */
+ double timeout;
+
+ /* for internal use in the timeout handler: when did the timeout handler
+ * first get called with count=0? */
+ double timeout_started;
+
+ /* None for autocommit, otherwise a PyString with the isolation level */
+ PyObject* isolation_level;
+
+ /* NULL for autocommit, otherwise a string with the BEGIN statment; will be
+ * freed in connection destructor */
+ char* begin_statement;
+
+ int check_same_thread;
+ long thread_ident;
+
+ Cache* statement_cache;
+
+ PyObject* row_factory;
+
+ PyObject* text_factory;
+
+ /* remember references to functions/classes used in
+ * create_function/create/aggregate, use these as dictionary keys, so we
+ * can keep the total system refcount constant by clearing that dictionary
+ * in connection_dealloc */
+ PyObject* function_pinboard;
+
+ /* Exception objects */
+ PyObject* Warning;
+ PyObject* Error;
+ PyObject* InterfaceError;
+ PyObject* DatabaseError;
+ PyObject* DataError;
+ PyObject* OperationalError;
+ PyObject* IntegrityError;
+ PyObject* InternalError;
+ PyObject* ProgrammingError;
+ PyObject* NotSupportedError;
+} Connection;
+
+extern PyTypeObject ConnectionType;
+
+PyObject* connection_alloc(PyTypeObject* type, int aware);
+void connection_dealloc(Connection* self);
+PyObject* connection_cursor(Connection* self, PyObject* args, PyObject* kwargs);
+PyObject* connection_close(Connection* self, PyObject* args);
+PyObject* _connection_begin(Connection* self);
+PyObject* connection_begin(Connection* self, PyObject* args);
+PyObject* connection_commit(Connection* self, PyObject* args);
+PyObject* connection_rollback(Connection* self, PyObject* args);
+PyObject* connection_new(PyTypeObject* type, PyObject* args, PyObject* kw);
+int connection_init(Connection* self, PyObject* args, PyObject* kwargs);
+
+int check_thread(Connection* self);
+int check_connection(Connection* con);
+
+int connection_setup_types(void);
+
+#endif
diff --git a/Modules/_sqlite/converters.c b/Modules/_sqlite/converters.c
new file mode 100644
index 0000000..018063a
--- /dev/null
+++ b/Modules/_sqlite/converters.c
@@ -0,0 +1,40 @@
+/* converters.c - default converters
+ *
+ * Copyright (C) 2005 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#include "util.h"
+#include "module.h"
+#include "adapters.h"
+
+/* dummy, will be implemented in a later version */
+
+PyObject* convert_date(PyObject* self, PyObject* args, PyObject* kwargs)
+{
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyObject* convert_timestamp(PyObject* self, PyObject* args, PyObject* kwargs)
+{
+ Py_INCREF(Py_None);
+ return Py_None;
+}
diff --git a/Modules/_sqlite/converters.h b/Modules/_sqlite/converters.h
new file mode 100644
index 0000000..df3768a
--- /dev/null
+++ b/Modules/_sqlite/converters.h
@@ -0,0 +1,33 @@
+/* converters.h - default converters
+ *
+ * Copyright (C) 2005 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_CONVERTERS_H
+#define PYSQLITE_CONVERTERS_H
+#include "Python.h"
+#include "pythread.h"
+#include "sqlite3.h"
+
+PyObject* convert_date(PyObject* self, PyObject* args, PyObject* kwargs);
+PyObject* convert_timestamp(PyObject* self, PyObject* args, PyObject* kwargs);
+
+#endif
diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c
new file mode 100644
index 0000000..c6ab25a
--- /dev/null
+++ b/Modules/_sqlite/cursor.c
@@ -0,0 +1,1067 @@
+/* cursor.c - the cursor type
+ *
+ * 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.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#include "cursor.h"
+#include "module.h"
+#include "util.h"
+#include "microprotocols.h"
+#include "prepare_protocol.h"
+
+/* used to decide wether to call PyInt_FromLong or PyLong_FromLongLong */
+#define INT32_MIN (-2147483647 - 1)
+#define INT32_MAX 2147483647
+
+PyObject* cursor_iternext(Cursor *self);
+
+static StatementKind detect_statement_type(char* statement)
+{
+ char buf[20];
+ char* src;
+ char* dst;
+
+ src = statement;
+ /* skip over whitepace */
+ while (*src == '\r' || *src == '\n' || *src == ' ' || *src == '\t') {
+ src++;
+ }
+
+ if (*src == 0)
+ return STATEMENT_INVALID;
+
+ dst = buf;
+ *dst = 0;
+ while (isalpha(*src) && dst - buf < sizeof(buf) - 2) {
+ *dst++ = tolower(*src++);
+ }
+
+ *dst = 0;
+
+ if (!strcmp(buf, "select")) {
+ return STATEMENT_SELECT;
+ } else if (!strcmp(buf, "insert")) {
+ return STATEMENT_INSERT;
+ } else if (!strcmp(buf, "update")) {
+ return STATEMENT_UPDATE;
+ } else if (!strcmp(buf, "delete")) {
+ return STATEMENT_DELETE;
+ } else if (!strcmp(buf, "replace")) {
+ return STATEMENT_REPLACE;
+ } else {
+ return STATEMENT_OTHER;
+ }
+}
+
+int cursor_init(Cursor* self, PyObject* args, PyObject* kwargs)
+{
+ Connection* connection;
+
+ if (!PyArg_ParseTuple(args, "O!", &ConnectionType, &connection))
+ {
+ return -1;
+ }
+
+ Py_INCREF(connection);
+ self->connection = connection;
+ self->statement = NULL;
+ self->next_row = NULL;
+
+ self->row_cast_map = PyList_New(0);
+
+ Py_INCREF(Py_None);
+ self->description = Py_None;
+
+ Py_INCREF(Py_None);
+ self->lastrowid= Py_None;
+
+ self->arraysize = 1;
+
+ self->rowcount = PyInt_FromLong(-1L);
+
+ Py_INCREF(Py_None);
+ self->row_factory = Py_None;
+
+ if (!check_thread(self->connection)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+void cursor_dealloc(Cursor* self)
+{
+ int rc;
+
+ /* Reset the statement if the user has not closed the cursor */
+ if (self->statement) {
+ rc = statement_reset(self->statement);
+ Py_DECREF(self->statement);
+ }
+
+ Py_XDECREF(self->connection);
+ Py_XDECREF(self->row_cast_map);
+ Py_XDECREF(self->description);
+ Py_XDECREF(self->lastrowid);
+ Py_XDECREF(self->rowcount);
+ Py_XDECREF(self->row_factory);
+ Py_XDECREF(self->next_row);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+void build_row_cast_map(Cursor* self)
+{
+ int i;
+ const char* type_start = (const char*)-1;
+ const char* pos;
+
+ const char* colname;
+ const char* decltype;
+ PyObject* py_decltype;
+ PyObject* converter;
+ PyObject* key;
+
+ if (!self->connection->detect_types) {
+ return;
+ }
+
+ Py_DECREF(self->row_cast_map);
+ self->row_cast_map = PyList_New(0);
+
+ for (i = 0; i < sqlite3_column_count(self->statement->st); i++) {
+ converter = NULL;
+
+ if (self->connection->detect_types | PARSE_COLNAMES) {
+ colname = sqlite3_column_name(self->statement->st, i);
+
+ for (pos = colname; *pos != 0; pos++) {
+ if (*pos == '[') {
+ type_start = pos + 1;
+ } else if (*pos == ']' && type_start != (const char*)-1) {
+ key = PyString_FromStringAndSize(type_start, pos - type_start);
+ converter = PyDict_GetItem(converters, key);
+ Py_DECREF(key);
+ break;
+ }
+
+ }
+ }
+
+ if (!converter && self->connection->detect_types | PARSE_DECLTYPES) {
+ decltype = sqlite3_column_decltype(self->statement->st, i);
+ if (decltype) {
+ for (pos = decltype;;pos++) {
+ if (*pos == ' ' || *pos == 0) {
+ py_decltype = PyString_FromStringAndSize(decltype, pos - decltype);
+ break;
+ }
+ }
+
+ converter = PyDict_GetItem(converters, py_decltype);
+ Py_DECREF(py_decltype);
+ }
+ }
+
+ if (converter) {
+ PyList_Append(self->row_cast_map, converter);
+ } else {
+ PyList_Append(self->row_cast_map, Py_None);
+ }
+ }
+}
+
+int _bind_parameter(Cursor* self, int pos, PyObject* parameter)
+{
+ int rc = SQLITE_OK;
+ long longval;
+#ifdef HAVE_LONG_LONG
+ PY_LONG_LONG longlongval;
+#endif
+ const char* buffer;
+ char* string;
+ int buflen;
+ PyObject* stringval;
+
+ if (parameter == Py_None) {
+ rc = sqlite3_bind_null(self->statement->st, pos);
+ } else if (PyInt_Check(parameter)) {
+ longval = PyInt_AsLong(parameter);
+ rc = sqlite3_bind_int64(self->statement->st, pos, (sqlite_int64)longval);
+#ifdef HAVE_LONG_LONG
+ } else if (PyLong_Check(parameter)) {
+ longlongval = PyLong_AsLongLong(parameter);
+ /* in the overflow error case, longlongval is -1, and an exception is set */
+ rc = sqlite3_bind_int64(self->statement->st, pos, (sqlite_int64)longlongval);
+#endif
+ } else if (PyFloat_Check(parameter)) {
+ rc = sqlite3_bind_double(self->statement->st, pos, PyFloat_AsDouble(parameter));
+ } else if (PyBuffer_Check(parameter)) {
+ if (PyObject_AsCharBuffer(parameter, &buffer, &buflen) == 0) {
+ rc = sqlite3_bind_blob(self->statement->st, pos, buffer, buflen, SQLITE_TRANSIENT);
+ } else {
+ PyErr_SetString(PyExc_ValueError, "could not convert BLOB to buffer");
+ rc = -1;
+ }
+ } else if PyString_Check(parameter) {
+ string = PyString_AsString(parameter);
+ rc = sqlite3_bind_text(self->statement->st, pos, string, -1, SQLITE_TRANSIENT);
+ } else if PyUnicode_Check(parameter) {
+ stringval = PyUnicode_AsUTF8String(parameter);
+ string = PyString_AsString(stringval);
+ rc = sqlite3_bind_text(self->statement->st, pos, string, -1, SQLITE_TRANSIENT);
+ Py_DECREF(stringval);
+ } else {
+ rc = -1;
+ }
+
+ return rc;
+}
+
+PyObject* _build_column_name(const char* colname)
+{
+ const char* pos;
+
+ for (pos = colname;; pos++) {
+ if (*pos == 0 || *pos == ' ') {
+ return PyString_FromStringAndSize(colname, pos - colname);
+ }
+ }
+}
+
+PyObject* unicode_from_string(const char* val_str, int optimize)
+{
+ const char* check;
+ int is_ascii = 0;
+
+ if (optimize) {
+ is_ascii = 1;
+
+ check = val_str;
+ while (*check) {
+ if (*check & 0x80) {
+ is_ascii = 0;
+ break;
+ }
+
+ check++;
+ }
+ }
+
+ if (is_ascii) {
+ return PyString_FromString(val_str);
+ } else {
+ return PyUnicode_DecodeUTF8(val_str, strlen(val_str), NULL);
+ }
+}
+
+/*
+ * Returns a row from the currently active SQLite statement
+ *
+ * Precondidition:
+ * - sqlite3_step() has been called before and it returned SQLITE_ROW.
+ */
+PyObject* _fetch_one_row(Cursor* self)
+{
+ int i, numcols;
+ PyObject* row;
+ PyObject* item = NULL;
+ int coltype;
+ PY_LONG_LONG intval;
+ PyObject* converter;
+ PyObject* converted;
+ int nbytes;
+ PyObject* buffer;
+ void* raw_buffer;
+ const char* val_str;
+ char buf[200];
+
+ Py_BEGIN_ALLOW_THREADS
+ numcols = sqlite3_data_count(self->statement->st);
+ Py_END_ALLOW_THREADS
+
+ row = PyTuple_New(numcols);
+
+ for (i = 0; i < numcols; i++) {
+ if (self->connection->detect_types) {
+ converter = PyList_GetItem(self->row_cast_map, i);
+ if (!converter) {
+ converter = Py_None;
+ }
+ } else {
+ converter = Py_None;
+ }
+
+ if (converter != Py_None) {
+ val_str = (const char*)sqlite3_column_text(self->statement->st, i);
+ if (!val_str) {
+ Py_INCREF(Py_None);
+ converted = Py_None;
+ } else {
+ item = PyString_FromString(val_str);
+ converted = PyObject_CallFunction(converter, "O", item);
+ if (!converted) {
+ /* TODO: have a way to log these errors */
+ Py_INCREF(Py_None);
+ converted = Py_None;
+ PyErr_Clear();
+ }
+ Py_DECREF(item);
+ }
+ } else {
+ Py_BEGIN_ALLOW_THREADS
+ coltype = sqlite3_column_type(self->statement->st, i);
+ Py_END_ALLOW_THREADS
+ if (coltype == SQLITE_NULL) {
+ Py_INCREF(Py_None);
+ converted = Py_None;
+ } else if (coltype == SQLITE_INTEGER) {
+ intval = sqlite3_column_int64(self->statement->st, i);
+ if (intval < INT32_MIN || intval > INT32_MAX) {
+ converted = PyLong_FromLongLong(intval);
+ } else {
+ converted = PyInt_FromLong((long)intval);
+ }
+ } else if (coltype == SQLITE_FLOAT) {
+ converted = PyFloat_FromDouble(sqlite3_column_double(self->statement->st, i));
+ } else if (coltype == SQLITE_TEXT) {
+ val_str = (const char*)sqlite3_column_text(self->statement->st, i);
+ if ((self->connection->text_factory == (PyObject*)&PyUnicode_Type)
+ || (self->connection->text_factory == OptimizedUnicode)) {
+
+ converted = unicode_from_string(val_str,
+ self->connection->text_factory == OptimizedUnicode ? 1 : 0);
+
+ if (!converted) {
+ PyOS_snprintf(buf, sizeof(buf) - 1, "Could not decode to UTF-8 column %s with text %s",
+ sqlite3_column_name(self->statement->st, i), val_str);
+ PyErr_SetString(OperationalError, buf);
+ }
+ } else if (self->connection->text_factory == (PyObject*)&PyString_Type) {
+ converted = PyString_FromString(val_str);
+ } else {
+ converted = PyObject_CallFunction(self->connection->text_factory, "s", val_str);
+ }
+ } else {
+ /* coltype == SQLITE_BLOB */
+ nbytes = sqlite3_column_bytes(self->statement->st, i);
+ buffer = PyBuffer_New(nbytes);
+ if (!buffer) {
+ break;
+ }
+ if (PyObject_AsWriteBuffer(buffer, &raw_buffer, &nbytes)) {
+ break;
+ }
+ memcpy(raw_buffer, sqlite3_column_blob(self->statement->st, i), nbytes);
+ converted = buffer;
+ }
+ }
+
+ PyTuple_SetItem(row, i, converted);
+ }
+
+ if (PyErr_Occurred()) {
+ Py_DECREF(row);
+ row = NULL;
+ }
+
+ return row;
+}
+
+PyObject* _query_execute(Cursor* self, int multiple, PyObject* args)
+{
+ PyObject* operation;
+ PyObject* operation_bytestr = NULL;
+ char* operation_cstr;
+ PyObject* parameters_list = NULL;
+ PyObject* parameters_iter = NULL;
+ PyObject* parameters = NULL;
+ int num_params;
+ int i;
+ int rc;
+ PyObject* func_args;
+ PyObject* result;
+ int numcols;
+ PY_LONG_LONG lastrowid;
+ int statement_type;
+ PyObject* descriptor;
+ PyObject* current_param;
+ PyObject* adapted;
+ PyObject* second_argument = NULL;
+ int num_params_needed;
+ const char* binding_name;
+ long rowcount = 0;
+
+ if (!check_thread(self->connection) || !check_connection(self->connection)) {
+ return NULL;
+ }
+
+ Py_XDECREF(self->next_row);
+ self->next_row = NULL;
+
+ if (multiple) {
+ /* executemany() */
+ if (!PyArg_ParseTuple(args, "OO", &operation, &second_argument)) {
+ return NULL;
+ }
+
+ if (!PyString_Check(operation) && !PyUnicode_Check(operation)) {
+ PyErr_SetString(PyExc_ValueError, "operation parameter must be str or unicode");
+ return NULL;
+ }
+
+ if (PyIter_Check(second_argument)) {
+ /* iterator */
+ Py_INCREF(second_argument);
+ parameters_iter = second_argument;
+ } else {
+ /* sequence */
+ parameters_iter = PyObject_GetIter(second_argument);
+ if (PyErr_Occurred())
+ {
+ return NULL;
+ }
+ }
+ } else {
+ /* execute() */
+ if (!PyArg_ParseTuple(args, "O|O", &operation, &second_argument)) {
+ return NULL;
+ }
+
+ if (!PyString_Check(operation) && !PyUnicode_Check(operation)) {
+ PyErr_SetString(PyExc_ValueError, "operation parameter must be str or unicode");
+ return NULL;
+ }
+
+ parameters_list = PyList_New(0);
+ if (!parameters_list) {
+ return NULL;
+ }
+
+ if (second_argument == NULL) {
+ second_argument = PyTuple_New(0);
+ } else {
+ Py_INCREF(second_argument);
+ }
+ PyList_Append(parameters_list, second_argument);
+ Py_DECREF(second_argument);
+
+ parameters_iter = PyObject_GetIter(parameters_list);
+ }
+
+ if (self->statement != NULL) {
+ /* There is an active statement */
+ rc = statement_reset(self->statement);
+ }
+
+ if (PyString_Check(operation)) {
+ operation_cstr = PyString_AsString(operation);
+ } else {
+ operation_bytestr = PyUnicode_AsUTF8String(operation);
+ if (!operation_bytestr) {
+ goto error;
+ }
+
+ operation_cstr = PyString_AsString(operation_bytestr);
+ }
+
+ /* reset description and rowcount */
+ Py_DECREF(self->description);
+ Py_INCREF(Py_None);
+ self->description = Py_None;
+
+ Py_DECREF(self->rowcount);
+ self->rowcount = PyInt_FromLong(-1L);
+
+ statement_type = detect_statement_type(operation_cstr);
+ if (self->connection->begin_statement) {
+ switch (statement_type) {
+ case STATEMENT_UPDATE:
+ case STATEMENT_DELETE:
+ case STATEMENT_INSERT:
+ case STATEMENT_REPLACE:
+ if (!self->connection->inTransaction) {
+ result = _connection_begin(self->connection);
+ if (!result) {
+ goto error;
+ }
+ Py_DECREF(result);
+ }
+ break;
+ case STATEMENT_OTHER:
+ /* it's a DDL statement or something similar
+ - we better COMMIT first so it works for all cases */
+ if (self->connection->inTransaction) {
+ func_args = PyTuple_New(0);
+ result = connection_commit(self->connection, func_args);
+ Py_DECREF(func_args);
+ if (!result) {
+ goto error;
+ }
+ Py_DECREF(result);
+ }
+ break;
+ case STATEMENT_SELECT:
+ if (multiple) {
+ PyErr_SetString(ProgrammingError,
+ "You cannot execute SELECT statements in executemany().");
+ goto error;
+ }
+ }
+ }
+
+ func_args = PyTuple_New(1);
+ Py_INCREF(operation);
+ PyTuple_SetItem(func_args, 0, operation);
+
+ if (self->statement) {
+ (void)statement_reset(self->statement);
+ Py_DECREF(self->statement);
+ }
+
+ self->statement = (Statement*)cache_get(self->connection->statement_cache, func_args);
+ Py_DECREF(func_args);
+
+ if (!self->statement) {
+ goto error;
+ }
+
+ if (self->statement->in_use) {
+ Py_DECREF(self->statement);
+ self->statement = PyObject_New(Statement, &StatementType);
+ rc = statement_create(self->statement, self->connection, operation);
+ if (rc != SQLITE_OK) {
+ self->statement = 0;
+ goto error;
+ }
+ }
+
+ statement_reset(self->statement);
+ statement_mark_dirty(self->statement);
+
+ Py_BEGIN_ALLOW_THREADS
+ num_params_needed = sqlite3_bind_parameter_count(self->statement->st);
+ Py_END_ALLOW_THREADS
+
+ while (1) {
+ parameters = PyIter_Next(parameters_iter);
+ if (!parameters) {
+ break;
+ }
+
+ statement_mark_dirty(self->statement);
+
+ if (PyDict_Check(parameters)) {
+ /* parameters passed as dictionary */
+ for (i = 1; i <= num_params_needed; i++) {
+ Py_BEGIN_ALLOW_THREADS
+ binding_name = sqlite3_bind_parameter_name(self->statement->st, i);
+ Py_END_ALLOW_THREADS
+ if (!binding_name) {
+ PyErr_Format(ProgrammingError, "Binding %d has no name, but you supplied a dictionary (which has only names).", i);
+ goto error;
+ }
+
+ binding_name++; /* skip first char (the colon) */
+ current_param = PyDict_GetItemString(parameters, binding_name);
+ if (!current_param) {
+ PyErr_Format(ProgrammingError, "You did not supply a value for binding %d.", i);
+ goto error;
+ }
+
+ Py_INCREF(current_param);
+ adapted = microprotocols_adapt(current_param, (PyObject*)&SQLitePrepareProtocolType, NULL);
+ if (adapted) {
+ Py_DECREF(current_param);
+ } else {
+ PyErr_Clear();
+ adapted = current_param;
+ }
+
+ rc = _bind_parameter(self, i, adapted);
+ Py_DECREF(adapted);
+
+ if (rc != SQLITE_OK) {
+ PyErr_Format(InterfaceError, "Error binding parameter :%s - probably unsupported type.", binding_name);
+ goto error;
+ }
+ }
+ } else {
+ /* parameters passed as sequence */
+ num_params = PySequence_Length(parameters);
+ if (num_params != num_params_needed) {
+ PyErr_Format(ProgrammingError, "Incorrect number of bindings supplied. The current statement uses %d, and there are %d supplied.",
+ num_params_needed, num_params);
+ goto error;
+ }
+ for (i = 0; i < num_params; i++) {
+ current_param = PySequence_GetItem(parameters, i);
+ if (!current_param) {
+ goto error;
+ }
+ adapted = microprotocols_adapt(current_param, (PyObject*)&SQLitePrepareProtocolType, NULL);
+
+ if (adapted) {
+ Py_DECREF(current_param);
+ } else {
+ PyErr_Clear();
+ adapted = current_param;
+ }
+
+ rc = _bind_parameter(self, i + 1, adapted);
+ Py_DECREF(adapted);
+
+ if (rc != SQLITE_OK) {
+ PyErr_Format(InterfaceError, "Error binding parameter %d - probably unsupported type.", i);
+ goto error;
+ }
+ }
+ }
+
+ build_row_cast_map(self);
+
+ rc = _sqlite_step_with_busyhandler(self->statement->st, self->connection);
+ if (rc != SQLITE_DONE && rc != SQLITE_ROW) {
+ rc = statement_reset(self->statement);
+ if (rc == SQLITE_SCHEMA) {
+ rc = statement_recompile(self->statement);
+ if (rc == SQLITE_OK) {
+ rc = _sqlite_step_with_busyhandler(self->statement->st, self->connection);
+ } else {
+ _seterror(self->connection->db);
+ goto error;
+ }
+ } else {
+ _seterror(self->connection->db);
+ goto error;
+ }
+ }
+
+ if (rc == SQLITE_ROW || (rc == SQLITE_DONE && statement_type == STATEMENT_SELECT)) {
+ Py_BEGIN_ALLOW_THREADS
+ numcols = sqlite3_column_count(self->statement->st);
+ Py_END_ALLOW_THREADS
+
+ if (self->description == Py_None) {
+ Py_DECREF(self->description);
+ self->description = PyTuple_New(numcols);
+ for (i = 0; i < numcols; i++) {
+ descriptor = PyTuple_New(7);
+ 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);
+ Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 3, Py_None);
+ Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 4, Py_None);
+ Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 5, Py_None);
+ Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 6, Py_None);
+ PyTuple_SetItem(self->description, i, descriptor);
+ }
+ }
+ }
+
+ if (rc == SQLITE_ROW) {
+ if (multiple) {
+ PyErr_SetString(ProgrammingError, "executemany() can only execute DML statements.");
+ goto error;
+ }
+
+ self->next_row = _fetch_one_row(self);
+ } else if (rc == SQLITE_DONE && !multiple) {
+ statement_reset(self->statement);
+ Py_DECREF(self->statement);
+ self->statement = 0;
+ }
+
+ switch (statement_type) {
+ case STATEMENT_UPDATE:
+ case STATEMENT_DELETE:
+ case STATEMENT_INSERT:
+ case STATEMENT_REPLACE:
+ Py_BEGIN_ALLOW_THREADS
+ rowcount += (long)sqlite3_changes(self->connection->db);
+ Py_END_ALLOW_THREADS
+ Py_DECREF(self->rowcount);
+ self->rowcount = PyInt_FromLong(rowcount);
+ }
+
+ Py_DECREF(self->lastrowid);
+ if (statement_type == STATEMENT_INSERT) {
+ Py_BEGIN_ALLOW_THREADS
+ lastrowid = sqlite3_last_insert_rowid(self->connection->db);
+ Py_END_ALLOW_THREADS
+ self->lastrowid = PyInt_FromLong((long)lastrowid);
+ } else {
+ Py_INCREF(Py_None);
+ self->lastrowid = Py_None;
+ }
+
+ if (multiple) {
+ rc = statement_reset(self->statement);
+ }
+ Py_XDECREF(parameters);
+ }
+
+error:
+ Py_XDECREF(operation_bytestr);
+ Py_XDECREF(parameters);
+ Py_DECREF(parameters_iter);
+ Py_XDECREF(parameters_list);
+
+ if (PyErr_Occurred()) {
+ return NULL;
+ } else {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+}
+
+PyObject* cursor_execute(Cursor* self, PyObject* args)
+{
+ return _query_execute(self, 0, args);
+}
+
+PyObject* cursor_executemany(Cursor* self, PyObject* args)
+{
+ return _query_execute(self, 1, args);
+}
+
+PyObject* cursor_executescript(Cursor* self, PyObject* args)
+{
+ PyObject* script_obj;
+ PyObject* script_str = NULL;
+ const char* script_cstr;
+ sqlite3_stmt* statement;
+ int rc;
+ PyObject* func_args;
+ PyObject* result;
+ int statement_completed = 0;
+
+ if (!PyArg_ParseTuple(args, "O", &script_obj)) {
+ return NULL;
+ }
+
+ if (!check_thread(self->connection) || !check_connection(self->connection)) {
+ return NULL;
+ }
+
+ if (PyString_Check(script_obj)) {
+ script_cstr = PyString_AsString(script_obj);
+ } else if (PyUnicode_Check(script_obj)) {
+ script_str = PyUnicode_AsUTF8String(script_obj);
+ if (!script_obj) {
+ return NULL;
+ }
+
+ script_cstr = PyString_AsString(script_str);
+ } else {
+ PyErr_SetString(PyExc_ValueError, "script argument must be unicode or string.");
+ return NULL;
+ }
+
+ /* commit first */
+ func_args = PyTuple_New(0);
+ result = connection_commit(self->connection, func_args);
+ Py_DECREF(func_args);
+ if (!result) {
+ goto error;
+ }
+ Py_DECREF(result);
+
+ while (1) {
+ if (!sqlite3_complete(script_cstr)) {
+ break;
+ }
+ statement_completed = 1;
+
+ rc = sqlite3_prepare(self->connection->db,
+ script_cstr,
+ -1,
+ &statement,
+ &script_cstr);
+ if (rc != SQLITE_OK) {
+ _seterror(self->connection->db);
+ goto error;
+ }
+
+ /* execute statement, and ignore results of SELECT statements */
+ rc = SQLITE_ROW;
+ while (rc == SQLITE_ROW) {
+ rc = _sqlite_step_with_busyhandler(statement, self->connection);
+ }
+
+ if (rc != SQLITE_DONE) {
+ (void)sqlite3_finalize(statement);
+ _seterror(self->connection->db);
+ goto error;
+ }
+
+ rc = sqlite3_finalize(statement);
+ if (rc != SQLITE_OK) {
+ _seterror(self->connection->db);
+ goto error;
+ }
+ }
+
+error:
+ Py_XDECREF(script_str);
+
+ if (!statement_completed) {
+ PyErr_SetString(ProgrammingError, "you did not provide a complete SQL statement");
+ }
+
+ if (PyErr_Occurred()) {
+ return NULL;
+ } else {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+}
+
+PyObject* cursor_getiter(Cursor *self)
+{
+ Py_INCREF(self);
+ return (PyObject*)self;
+}
+
+PyObject* cursor_iternext(Cursor *self)
+{
+ PyObject* next_row_tuple;
+ PyObject* next_row;
+ int rc;
+
+ if (!check_thread(self->connection) || !check_connection(self->connection)) {
+ return NULL;
+ }
+
+ if (!self->next_row) {
+ if (self->statement) {
+ (void)statement_reset(self->statement);
+ Py_DECREF(self->statement);
+ self->statement = NULL;
+ }
+ return NULL;
+ }
+
+ next_row_tuple = self->next_row;
+ self->next_row = NULL;
+
+ if (self->row_factory != Py_None) {
+ next_row = PyObject_CallFunction(self->row_factory, "OO", self, next_row_tuple);
+ Py_DECREF(next_row_tuple);
+ } else {
+ next_row = next_row_tuple;
+ }
+
+ rc = _sqlite_step_with_busyhandler(self->statement->st, self->connection);
+ if (rc != SQLITE_DONE && rc != SQLITE_ROW) {
+ Py_DECREF(next_row);
+ _seterror(self->connection->db);
+ return NULL;
+ }
+
+ if (rc == SQLITE_ROW) {
+ self->next_row = _fetch_one_row(self);
+ }
+
+ return next_row;
+}
+
+PyObject* cursor_fetchone(Cursor* self, PyObject* args)
+{
+ PyObject* row;
+
+ row = cursor_iternext(self);
+ if (!row && !PyErr_Occurred()) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ return row;
+}
+
+PyObject* cursor_fetchmany(Cursor* self, PyObject* args)
+{
+ PyObject* row;
+ PyObject* list;
+ int maxrows = self->arraysize;
+ int counter = 0;
+
+ if (!PyArg_ParseTuple(args, "|i", &maxrows)) {
+ return NULL;
+ }
+
+ list = PyList_New(0);
+
+ /* just make sure we enter the loop */
+ row = (PyObject*)1;
+
+ while (row) {
+ row = cursor_iternext(self);
+ if (row) {
+ PyList_Append(list, row);
+ Py_DECREF(row);
+ } else {
+ break;
+ }
+
+ if (++counter == maxrows) {
+ break;
+ }
+ }
+
+ if (PyErr_Occurred()) {
+ Py_DECREF(list);
+ return NULL;
+ } else {
+ return list;
+ }
+}
+
+PyObject* cursor_fetchall(Cursor* self, PyObject* args)
+{
+ PyObject* row;
+ PyObject* list;
+
+ list = PyList_New(0);
+
+ /* just make sure we enter the loop */
+ row = (PyObject*)1;
+
+ while (row) {
+ row = cursor_iternext(self);
+ if (row) {
+ PyList_Append(list, row);
+ Py_DECREF(row);
+ }
+ }
+
+ if (PyErr_Occurred()) {
+ Py_DECREF(list);
+ return NULL;
+ } else {
+ return list;
+ }
+}
+
+PyObject* pysqlite_noop(Connection* self, PyObject* args)
+{
+ /* don't care, return None */
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyObject* cursor_close(Cursor* self, PyObject* args)
+{
+ if (!check_thread(self->connection) || !check_connection(self->connection)) {
+ return NULL;
+ }
+
+ if (self->statement) {
+ (void)statement_reset(self->statement);
+ Py_DECREF(self->statement);
+ self->statement = 0;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyMethodDef cursor_methods[] = {
+ {"execute", (PyCFunction)cursor_execute, METH_VARARGS,
+ PyDoc_STR("Executes a SQL statement.")},
+ {"executemany", (PyCFunction)cursor_executemany, METH_VARARGS,
+ PyDoc_STR("Repeatedly executes a SQL statement.")},
+ {"executescript", (PyCFunction)cursor_executescript, METH_VARARGS,
+ PyDoc_STR("Executes a multiple SQL statements at once. Non-standard.")},
+ {"fetchone", (PyCFunction)cursor_fetchone, METH_NOARGS,
+ PyDoc_STR("Fetches several rows from the resultset.")},
+ {"fetchmany", (PyCFunction)cursor_fetchmany, METH_VARARGS,
+ PyDoc_STR("Fetches all rows from the resultset.")},
+ {"fetchall", (PyCFunction)cursor_fetchall, METH_NOARGS,
+ PyDoc_STR("Fetches one row from the resultset.")},
+ {"close", (PyCFunction)cursor_close, METH_NOARGS,
+ PyDoc_STR("Closes the cursor.")},
+ {"setinputsizes", (PyCFunction)pysqlite_noop, METH_VARARGS,
+ PyDoc_STR("Required by DB-API. Does nothing in pysqlite.")},
+ {"setoutputsize", (PyCFunction)pysqlite_noop, METH_VARARGS,
+ PyDoc_STR("Required by DB-API. Does nothing in pysqlite.")},
+ {NULL, NULL}
+};
+
+static struct PyMemberDef cursor_members[] =
+{
+ {"connection", T_OBJECT, offsetof(Cursor, connection), RO},
+ {"description", T_OBJECT, offsetof(Cursor, description), RO},
+ {"arraysize", T_INT, offsetof(Cursor, arraysize), 0},
+ {"lastrowid", T_OBJECT, offsetof(Cursor, lastrowid), RO},
+ {"rowcount", T_OBJECT, offsetof(Cursor, rowcount), RO},
+ {"row_factory", T_OBJECT, offsetof(Cursor, row_factory), 0},
+ {NULL}
+};
+
+PyTypeObject CursorType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "pysqlite2.dbapi2.Cursor", /* tp_name */
+ sizeof(Cursor), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)cursor_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_ITER|Py_TPFLAGS_BASETYPE, /* tp_flags */
+ 0, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)cursor_getiter, /* tp_iter */
+ (iternextfunc)cursor_iternext, /* tp_iternext */
+ cursor_methods, /* tp_methods */
+ cursor_members, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)cursor_init, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0 /* tp_free */
+};
+
+extern int cursor_setup_types(void)
+{
+ CursorType.tp_new = PyType_GenericNew;
+ return PyType_Ready(&CursorType);
+}
diff --git a/Modules/_sqlite/cursor.h b/Modules/_sqlite/cursor.h
new file mode 100644
index 0000000..7f56799
--- /dev/null
+++ b/Modules/_sqlite/cursor.h
@@ -0,0 +1,71 @@
+/* cursor.h - definitions for the cursor type
+ *
+ * Copyright (C) 2004-2005 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#ifndef PYSQLITE_CURSOR_H
+#define PYSQLITE_CURSOR_H
+#include "Python.h"
+
+#include "statement.h"
+#include "connection.h"
+#include "module.h"
+
+typedef struct
+{
+ PyObject_HEAD
+ Connection* connection;
+ PyObject* description;
+ PyObject* row_cast_map;
+ int arraysize;
+ PyObject* lastrowid;
+ PyObject* rowcount;
+ PyObject* row_factory;
+ Statement* statement;
+
+ /* the next row to be returned, NULL if no next row available */
+ PyObject* next_row;
+} Cursor;
+
+typedef enum {
+ STATEMENT_INVALID, STATEMENT_INSERT, STATEMENT_DELETE,
+ STATEMENT_UPDATE, STATEMENT_REPLACE, STATEMENT_SELECT,
+ STATEMENT_OTHER
+} StatementKind;
+
+extern PyTypeObject CursorType;
+
+int cursor_init(Cursor* self, PyObject* args, PyObject* kwargs);
+void cursor_dealloc(Cursor* self);
+PyObject* cursor_execute(Cursor* self, PyObject* args);
+PyObject* cursor_executemany(Cursor* self, PyObject* args);
+PyObject* cursor_getiter(Cursor *self);
+PyObject* cursor_iternext(Cursor *self);
+PyObject* cursor_fetchone(Cursor* self, PyObject* args);
+PyObject* cursor_fetchmany(Cursor* self, PyObject* args);
+PyObject* cursor_fetchall(Cursor* self, PyObject* args);
+PyObject* pysqlite_noop(Connection* self, PyObject* args);
+PyObject* cursor_close(Cursor* self, PyObject* args);
+
+int cursor_setup_types(void);
+
+#define UNKNOWN (-1)
+#endif
diff --git a/Modules/_sqlite/microprotocols.c b/Modules/_sqlite/microprotocols.c
new file mode 100644
index 0000000..5df41a1
--- /dev/null
+++ b/Modules/_sqlite/microprotocols.c
@@ -0,0 +1,141 @@
+/* microprotocols.c - minimalist and non-validating protocols implementation
+ *
+ * Copyright (C) 2003-2004 Federico Di Gregorio <fog@debian.org>
+ *
+ * This file is part of psycopg and was adapted for pysqlite. Federico Di
+ * Gregorio gave the permission to use it within pysqlite under the following
+ * license:
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#include <Python.h>
+#include <structmember.h>
+
+#include "cursor.h"
+#include "microprotocols.h"
+#include "prepare_protocol.h"
+
+
+/** the adapters registry **/
+
+PyObject *psyco_adapters;
+
+/* microprotocols_init - initialize the adapters dictionary */
+
+int
+microprotocols_init(PyObject *dict)
+{
+ /* create adapters dictionary and put it in module namespace */
+ if ((psyco_adapters = PyDict_New()) == NULL) {
+ return -1;
+ }
+
+ PyDict_SetItemString(dict, "adapters", psyco_adapters);
+
+ return 0;
+}
+
+
+/* microprotocols_add - add a reverse type-caster to the dictionary */
+
+int
+microprotocols_add(PyTypeObject *type, PyObject *proto, PyObject *cast)
+{
+ PyObject* key;
+
+ if (proto == NULL) proto = (PyObject*)&SQLitePrepareProtocolType;
+
+ /*
+ Dprintf("microprotocols_add: cast %p for (%s, ?)",
+ cast, type->tp_name);
+ */
+
+ key = Py_BuildValue("(OO)", (PyObject*)type, proto);
+ PyDict_SetItem(psyco_adapters, key, cast);
+ Py_DECREF(key);
+
+ return 0;
+}
+
+/* microprotocols_adapt - adapt an object to the built-in protocol */
+
+PyObject *
+microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
+{
+ PyObject *adapter, *key;
+
+ /* we don't check for exact type conformance as specified in PEP 246
+ because the SQLitePrepareProtocolType type is abstract and there is no
+ way to get a quotable object to be its instance */
+
+ /* look for an adapter in the registry */
+ key = Py_BuildValue("(OO)", (PyObject*)obj->ob_type, proto);
+ adapter = PyDict_GetItem(psyco_adapters, key);
+ Py_DECREF(key);
+ if (adapter) {
+ PyObject *adapted = PyObject_CallFunctionObjArgs(adapter, obj, NULL);
+ return adapted;
+ }
+
+ /* try to have the protocol adapt this object*/
+ if (PyObject_HasAttrString(proto, "__adapt__")) {
+ PyObject *adapted = PyObject_CallMethod(proto, "__adapt__", "O", obj);
+ if (adapted) {
+ if (adapted != Py_None) {
+ return adapted;
+ } else {
+ Py_DECREF(adapted);
+ }
+ }
+
+ if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError))
+ return NULL;
+ }
+
+ /* and finally try to have the object adapt itself */
+ if (PyObject_HasAttrString(obj, "__conform__")) {
+ PyObject *adapted = PyObject_CallMethod(obj, "__conform__","O", proto);
+ if (adapted) {
+ if (adapted != Py_None) {
+ return adapted;
+ } else {
+ Py_DECREF(adapted);
+ }
+ }
+
+ if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError)) {
+ return NULL;
+ }
+ }
+
+ /* else set the right exception and return NULL */
+ PyErr_SetString(ProgrammingError, "can't adapt");
+ return NULL;
+}
+
+/** module-level functions **/
+
+PyObject *
+psyco_microprotocols_adapt(Cursor *self, PyObject *args)
+{
+ PyObject *obj, *alt = NULL;
+ PyObject *proto = (PyObject*)&SQLitePrepareProtocolType;
+
+ if (!PyArg_ParseTuple(args, "O|OO", &obj, &proto, &alt)) return NULL;
+ return microprotocols_adapt(obj, proto, alt);
+}
diff --git a/Modules/_sqlite/microprotocols.h b/Modules/_sqlite/microprotocols.h
new file mode 100644
index 0000000..d2d9b65
--- /dev/null
+++ b/Modules/_sqlite/microprotocols.h
@@ -0,0 +1,59 @@
+/* microprotocols.c - definitions for minimalist and non-validating protocols
+ *
+ * Copyright (C) 2003-2004 Federico Di Gregorio <fog@debian.org>
+ *
+ * This file is part of psycopg and was adapted for pysqlite. Federico Di
+ * Gregorio gave the permission to use it within pysqlite under the following
+ * license:
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#ifndef PSYCOPG_MICROPROTOCOLS_H
+#define PSYCOPG_MICROPROTOCOLS_H 1
+
+#include <Python.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** adapters registry **/
+
+extern PyObject *psyco_adapters;
+
+/** the names of the three mandatory methods **/
+
+#define MICROPROTOCOLS_GETQUOTED_NAME "getquoted"
+#define MICROPROTOCOLS_GETSTRING_NAME "getstring"
+#define MICROPROTOCOLS_GETBINARY_NAME "getbinary"
+
+/** exported functions **/
+
+/* used by module.c to init the microprotocols system */
+extern int microprotocols_init(PyObject *dict);
+extern int microprotocols_add(
+ PyTypeObject *type, PyObject *proto, PyObject *cast);
+extern PyObject *microprotocols_adapt(
+ PyObject *obj, PyObject *proto, PyObject *alt);
+
+extern PyObject *
+ psyco_microprotocols_adapt(Cursor* self, PyObject *args);
+#define psyco_microprotocols_adapt_doc \
+ "adapt(obj, protocol, alternate) -> adapt obj to given protocol"
+
+#endif /* !defined(PSYCOPG_MICROPROTOCOLS_H) */
diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c
new file mode 100644
index 0000000..70993d0
--- /dev/null
+++ b/Modules/_sqlite/module.c
@@ -0,0 +1,290 @@
+/* module.c - the module itself
+ *
+ * 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.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#include "connection.h"
+#include "statement.h"
+#include "cursor.h"
+#include "cache.h"
+#include "prepare_protocol.h"
+#include "microprotocols.h"
+#include "row.h"
+
+#if SQLITE_VERSION_NUMBER >= 3003003
+#define HAVE_SHARED_CACHE
+#endif
+
+/* static objects at module-level */
+
+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*
+ kwargs)
+{
+ /* Python seems to have no way of extracting a single keyword-arg at
+ * C-level, so this code is redundant with the one in connection_init in
+ * connection.c and must always be copied from there ... */
+
+ static char *kwlist[] = {"database", "timeout", "detect_types", "isolation_level", "check_same_thread", "factory", "cached_statements", NULL, NULL};
+ char* database;
+ int detect_types = 0;
+ PyObject* isolation_level;
+ PyObject* factory = NULL;
+ int check_same_thread = 1;
+ int cached_statements;
+ double timeout = 5.0;
+
+ PyObject* result;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|diOiOi", kwlist,
+ &database, &timeout, &detect_types, &isolation_level, &check_same_thread, &factory, &cached_statements))
+ {
+ return NULL;
+ }
+
+ if (factory == NULL) {
+ factory = (PyObject*)&ConnectionType;
+ }
+
+ result = PyObject_Call(factory, args, kwargs);
+
+ return result;
+}
+
+static PyObject* module_complete(PyObject* self, PyObject* args, PyObject*
+ kwargs)
+{
+ static char *kwlist[] = {"statement", NULL, NULL};
+ char* statement;
+
+ PyObject* result;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &statement))
+ {
+ return NULL;
+ }
+
+ if (sqlite3_complete(statement)) {
+ result = Py_True;
+ } else {
+ result = Py_False;
+ }
+
+ Py_INCREF(result);
+
+ return result;
+}
+
+#ifdef HAVE_SHARED_CACHE
+static PyObject* module_enable_shared_cache(PyObject* self, PyObject* args, PyObject*
+ kwargs)
+{
+ static char *kwlist[] = {"do_enable", NULL, NULL};
+ int do_enable;
+ int rc;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &do_enable))
+ {
+ return NULL;
+ }
+
+ rc = sqlite3_enable_shared_cache(do_enable);
+
+ if (rc != SQLITE_OK) {
+ PyErr_SetString(OperationalError, "Changing the shared_cache flag failed");
+ return NULL;
+ } else {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+}
+#endif /* HAVE_SHARED_CACHE */
+
+static PyObject* module_register_adapter(PyObject* self, PyObject* args, PyObject* kwargs)
+{
+ PyTypeObject* type;
+ PyObject* caster;
+
+ if (!PyArg_ParseTuple(args, "OO", &type, &caster)) {
+ return NULL;
+ }
+
+ microprotocols_add(type, (PyObject*)&SQLitePrepareProtocolType, caster);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject* module_register_converter(PyObject* self, PyObject* args, PyObject* kwargs)
+{
+ PyObject* name;
+ PyObject* callable;
+
+ if (!PyArg_ParseTuple(args, "OO", &name, &callable)) {
+ return NULL;
+ }
+
+ PyDict_SetItem(converters, name, callable);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+void converters_init(PyObject* dict)
+{
+ converters = PyDict_New();
+
+ PyDict_SetItemString(dict, "converters", converters);
+}
+
+static PyMethodDef module_methods[] = {
+ {"connect", (PyCFunction)module_connect, METH_VARARGS|METH_KEYWORDS, PyDoc_STR("Creates a connection.")},
+ {"complete_statement", (PyCFunction)module_complete, METH_VARARGS|METH_KEYWORDS, PyDoc_STR("Checks if a string contains a complete SQL statement.")},
+#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.")},
+ {"adapt", (PyCFunction)psyco_microprotocols_adapt, METH_VARARGS, psyco_microprotocols_adapt_doc},
+ {NULL, NULL}
+};
+
+PyMODINIT_FUNC init_sqlite3(void)
+{
+ PyObject *module, *dict;
+ PyObject* time_module;
+
+ module = Py_InitModule("_sqlite3", module_methods);
+
+ if (
+ (row_setup_types() < 0) ||
+ (cursor_setup_types() < 0) ||
+ (connection_setup_types() < 0) ||
+ (cache_setup_types() < 0) ||
+ (statement_setup_types() < 0) ||
+ (prepare_protocol_setup_types() < 0)
+ ) {
+ return;
+ }
+
+ Py_INCREF(&ConnectionType);
+ PyModule_AddObject(module, "Connection", (PyObject*) &ConnectionType);
+ Py_INCREF(&CursorType);
+ PyModule_AddObject(module, "Cursor", (PyObject*) &CursorType);
+ Py_INCREF(&CacheType);
+ PyModule_AddObject(module, "Statement", (PyObject*)&StatementType);
+ Py_INCREF(&StatementType);
+ PyModule_AddObject(module, "Cache", (PyObject*) &CacheType);
+ Py_INCREF(&SQLitePrepareProtocolType);
+ PyModule_AddObject(module, "PrepareProtocol", (PyObject*) &SQLitePrepareProtocolType);
+ Py_INCREF(&RowType);
+ PyModule_AddObject(module, "Row", (PyObject*) &RowType);
+
+ if (!(dict = PyModule_GetDict(module)))
+ {
+ goto error;
+ }
+
+ /*** Create DB-API Exception hierarchy */
+
+ Error = PyErr_NewException("sqlite3.Error", PyExc_StandardError, NULL);
+ PyDict_SetItemString(dict, "Error", Error);
+
+ Warning = PyErr_NewException("sqlite3.Warning", PyExc_StandardError, NULL);
+ PyDict_SetItemString(dict, "Warning", Warning);
+
+ /* Error subclasses */
+
+ InterfaceError = PyErr_NewException("sqlite3.InterfaceError", Error, NULL);
+ PyDict_SetItemString(dict, "InterfaceError", InterfaceError);
+
+ DatabaseError = PyErr_NewException("sqlite3.DatabaseError", Error, NULL);
+ PyDict_SetItemString(dict, "DatabaseError", DatabaseError);
+
+ /* DatabaseError subclasses */
+
+ InternalError = PyErr_NewException("sqlite3.InternalError", DatabaseError, NULL);
+ PyDict_SetItemString(dict, "InternalError", InternalError);
+
+ OperationalError = PyErr_NewException("sqlite3.OperationalError", DatabaseError, NULL);
+ PyDict_SetItemString(dict, "OperationalError", OperationalError);
+
+ ProgrammingError = PyErr_NewException("sqlite3.ProgrammingError", DatabaseError, NULL);
+ PyDict_SetItemString(dict, "ProgrammingError", ProgrammingError);
+
+ IntegrityError = PyErr_NewException("sqlite3.IntegrityError", DatabaseError,NULL);
+ PyDict_SetItemString(dict, "IntegrityError", IntegrityError);
+
+ DataError = PyErr_NewException("sqlite3.DataError", DatabaseError, NULL);
+ PyDict_SetItemString(dict, "DataError", DataError);
+
+ NotSupportedError = PyErr_NewException("sqlite3.NotSupportedError", DatabaseError, NULL);
+ PyDict_SetItemString(dict, "NotSupportedError", NotSupportedError);
+
+ 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));
+
+ PyDict_SetItemString(dict, "version", PyString_FromString(PYSQLITE_VERSION));
+ PyDict_SetItemString(dict, "sqlite_version", PyString_FromString(sqlite3_libversion()));
+
+ /* initialize microprotocols layer */
+ microprotocols_init(dict);
+
+ /* 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.
+ *
+ * PyEval_InitThreads is called here due to a quirk in python 1.5
+ * - 2.2.1 (at least) according to Russell Williamson <merel@wt.net>:
+ * The global interepreter lock is not initialized until the first
+ * thread is created using thread.start_new_thread() or fork() is
+ * called. that would cause the ALLOW_THREADS here to segfault due
+ * to a null pointer reference if no threads or child processes
+ * have been created. This works around that and is a no-op if
+ * threads have already been initialized.
+ * (see pybsddb-users mailing list post on 2002-08-07)
+ */
+ PyEval_InitThreads();
+
+error:
+ if (PyErr_Occurred())
+ {
+ PyErr_SetString(PyExc_ImportError, "_sqlite3: init failed");
+ }
+}
diff --git a/Modules/_sqlite/module.h b/Modules/_sqlite/module.h
new file mode 100644
index 0000000..75fe29d
--- /dev/null
+++ b/Modules/_sqlite/module.h
@@ -0,0 +1,53 @@
+/* module.h - definitions for the module
+ *
+ * Copyright (C) 2004-2005 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#ifndef PYSQLITE_MODULE_H
+#define PYSQLITE_MODULE_H
+#include "Python.h"
+
+extern PyObject* Error;
+extern PyObject* Warning;
+extern PyObject* InterfaceError;
+extern PyObject* DatabaseError;
+extern PyObject* InternalError;
+extern PyObject* OperationalError;
+extern PyObject* ProgrammingError;
+extern PyObject* IntegrityError;
+extern PyObject* DataError;
+extern PyObject* NotSupportedError;
+
+extern PyObject* OptimizedUnicode;
+
+/* the functions time.time() and time.sleep() */
+extern PyObject* time_time;
+extern PyObject* time_sleep;
+
+/* A dictionary, mapping colum types (INTEGER, VARCHAR, etc.) to converter
+ * functions, that convert the SQL value to the appropriate Python value.
+ * The key is uppercase.
+ */
+extern PyObject* converters;
+
+#define PARSE_DECLTYPES 1
+#define PARSE_COLNAMES 2
+#endif
diff --git a/Modules/_sqlite/prepare_protocol.c b/Modules/_sqlite/prepare_protocol.c
new file mode 100644
index 0000000..2e8349c
--- /dev/null
+++ b/Modules/_sqlite/prepare_protocol.c
@@ -0,0 +1,84 @@
+/* prepare_protocol.c - the protocol for preparing values for SQLite
+ *
+ * Copyright (C) 2005 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#include "prepare_protocol.h"
+
+int prepare_protocol_init(SQLitePrepareProtocol* self, PyObject* args, PyObject* kwargs)
+{
+ return 0;
+}
+
+void prepare_protocol_dealloc(SQLitePrepareProtocol* self)
+{
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+PyTypeObject SQLitePrepareProtocolType= {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "pysqlite2.dbapi2.PrepareProtocol", /* tp_name */
+ sizeof(SQLitePrepareProtocol), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)prepare_protocol_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ 0, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)prepare_protocol_init, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0 /* tp_free */
+};
+
+extern int prepare_protocol_setup_types(void)
+{
+ SQLitePrepareProtocolType.tp_new = PyType_GenericNew;
+ SQLitePrepareProtocolType.ob_type= &PyType_Type;
+ return PyType_Ready(&SQLitePrepareProtocolType);
+}
diff --git a/Modules/_sqlite/prepare_protocol.h b/Modules/_sqlite/prepare_protocol.h
new file mode 100644
index 0000000..2fc4f61
--- /dev/null
+++ b/Modules/_sqlite/prepare_protocol.h
@@ -0,0 +1,41 @@
+/* prepare_protocol.h - the protocol for preparing values for SQLite
+ *
+ * Copyright (C) 2005 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#ifndef PYSQLITE_PREPARE_PROTOCOL_H
+#define PYSQLITE_PREPARE_PROTOCOL_H
+#include "Python.h"
+
+typedef struct
+{
+ PyObject_HEAD
+} SQLitePrepareProtocol;
+
+extern PyTypeObject SQLitePrepareProtocolType;
+
+int prepare_protocol_init(SQLitePrepareProtocol* self, PyObject* args, PyObject* kwargs);
+void prepare_protocol_dealloc(SQLitePrepareProtocol* self);
+
+int prepare_protocol_setup_types(void);
+
+#define UNKNOWN (-1)
+#endif
diff --git a/Modules/_sqlite/row.c b/Modules/_sqlite/row.c
new file mode 100644
index 0000000..97b538d
--- /dev/null
+++ b/Modules/_sqlite/row.c
@@ -0,0 +1,195 @@
+/* row.c - an enhanced tuple for database rows
+ *
+ * Copyright (C) 2005 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#include "row.h"
+#include "cursor.h"
+
+void row_dealloc(Row* self)
+{
+ Py_XDECREF(self->data);
+ Py_XDECREF(self->description);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+int row_init(Row* self, PyObject* args, PyObject* kwargs)
+{
+ PyObject* data;
+ Cursor* cursor;
+
+ self->data = 0;
+ self->description = 0;
+
+ if (!PyArg_ParseTuple(args, "OO", &cursor, &data)) {
+ return -1;
+ }
+
+ if (!PyObject_IsInstance((PyObject*)cursor, (PyObject*)&CursorType)) {
+ PyErr_SetString(PyExc_TypeError, "instance of cursor required for first argument");
+ return -1;
+ }
+
+ if (!PyTuple_Check(data)) {
+ PyErr_SetString(PyExc_TypeError, "tuple required for second argument");
+ return -1;
+ }
+
+ Py_INCREF(data);
+ self->data = data;
+
+ Py_INCREF(cursor->description);
+ self->description = cursor->description;
+
+ return 0;
+}
+
+PyObject* row_subscript(Row* self, PyObject* idx)
+{
+ long _idx;
+ char* key;
+ int nitems, i;
+ char* compare_key;
+
+ char* p1;
+ char* p2;
+
+ PyObject* item;
+
+ if (PyInt_Check(idx)) {
+ _idx = PyInt_AsLong(idx);
+ item = PyTuple_GetItem(self->data, _idx);
+ if (item) {
+ Py_INCREF(item);
+ }
+ return item;
+ } else if (PyString_Check(idx)) {
+ key = PyString_AsString(idx);
+
+ nitems = PyTuple_Size(self->description);
+
+ for (i = 0; i < nitems; i++) {
+ compare_key = PyString_AsString(PyTuple_GET_ITEM(PyTuple_GET_ITEM(self->description, i), 0));
+
+ p1 = key;
+ p2 = compare_key;
+
+ while (1) {
+ if ((*p1 == (char)0) || (*p2 == (char)0)) {
+ break;
+ }
+
+ if ((*p1 | 0x20) != (*p2 | 0x20)) {
+ break;
+ }
+
+ p1++;
+ p2++;
+ }
+
+ if ((*p1 == (char)0) && (*p2 == (char)0)) {
+ /* found item */
+ item = PyTuple_GetItem(self->data, i);
+ Py_INCREF(item);
+ return item;
+ }
+
+ }
+
+ PyErr_SetString(PyExc_IndexError, "No item with that key");
+ return NULL;
+ } else if (PySlice_Check(idx)) {
+ PyErr_SetString(PyExc_ValueError, "slices not implemented, yet");
+ return NULL;
+ } else {
+ PyErr_SetString(PyExc_IndexError, "Index must be int or string");
+ return NULL;
+ }
+}
+
+int row_length(Row* self, PyObject* args, PyObject* kwargs)
+{
+ return PyTuple_GET_SIZE(self->data);
+}
+
+static int row_print(Row* self, FILE *fp, int flags)
+{
+ return (&PyTuple_Type)->tp_print(self->data, fp, flags);
+}
+
+
+PyMappingMethods row_as_mapping = {
+ /* mp_length */ (inquiry)row_length,
+ /* mp_subscript */ (binaryfunc)row_subscript,
+ /* mp_ass_subscript */ (objobjargproc)0,
+};
+
+
+PyTypeObject RowType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "pysqlite2.dbapi2.Row", /* tp_name */
+ sizeof(Row), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)row_dealloc, /* tp_dealloc */
+ (printfunc)row_print, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */
+ 0, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)row_init, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0 /* tp_free */
+};
+
+extern int row_setup_types(void)
+{
+ RowType.tp_new = PyType_GenericNew;
+ RowType.tp_as_mapping = &row_as_mapping;
+ return PyType_Ready(&RowType);
+}
diff --git a/Modules/_sqlite/row.h b/Modules/_sqlite/row.h
new file mode 100644
index 0000000..c6e083c
--- /dev/null
+++ b/Modules/_sqlite/row.h
@@ -0,0 +1,39 @@
+/* row.h - an enhanced tuple for database rows
+ *
+ * Copyright (C) 2005 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#ifndef PYSQLITE_ROW_H
+#define PYSQLITE_ROW_H
+#include "Python.h"
+
+typedef struct _Row
+{
+ PyObject_HEAD
+ PyObject* data;
+ PyObject* description;
+} Row;
+
+extern PyTypeObject RowType;
+
+int row_setup_types(void);
+
+#endif
diff --git a/Modules/_sqlite/statement.c b/Modules/_sqlite/statement.c
new file mode 100644
index 0000000..a2c0f13
--- /dev/null
+++ b/Modules/_sqlite/statement.c
@@ -0,0 +1,285 @@
+/* statement.c - the statement type
+ *
+ * Copyright (C) 2005-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.
+ */
+
+#include "statement.h"
+#include "connection.h"
+
+/* prototypes */
+int check_remaining_sql(const char* tail);
+
+typedef enum {
+ LINECOMMENT_1,
+ IN_LINECOMMENT,
+ COMMENTSTART_1,
+ IN_COMMENT,
+ COMMENTEND_1,
+ NORMAL
+} parse_remaining_sql_state;
+
+int statement_create(Statement* self, Connection* connection, PyObject* sql)
+{
+ const char* tail;
+ int rc;
+ PyObject* sql_str;
+ char* sql_cstr;
+
+ self->st = NULL;
+
+ self->st = NULL;
+ self->in_use = 0;
+
+ if (PyString_Check(sql)) {
+ sql_str = sql;
+ Py_INCREF(sql_str);
+ } else if (PyUnicode_Check(sql)) {
+ sql_str = PyUnicode_AsUTF8String(sql);
+ if (!sql_str) {
+ rc = PYSQLITE_SQL_WRONG_TYPE;
+ return rc;
+ }
+ } else {
+ rc = PYSQLITE_SQL_WRONG_TYPE;
+ return rc;
+ }
+
+ self->sql = sql_str;
+
+ sql_cstr = PyString_AsString(sql_str);
+
+ rc = sqlite3_prepare(connection->db,
+ sql_cstr,
+ -1,
+ &self->st,
+ &tail);
+
+ self->db = connection->db;
+
+ if (rc == SQLITE_OK && check_remaining_sql(tail)) {
+ (void)sqlite3_finalize(self->st);
+ rc = PYSQLITE_TOO_MUCH_SQL;
+ }
+
+ return rc;
+}
+
+int statement_recompile(Statement* self)
+{
+ const char* tail;
+ int rc;
+ char* sql_cstr;
+ sqlite3_stmt* new_st;
+
+ sql_cstr = PyString_AsString(self->sql);
+
+ rc = sqlite3_prepare(self->db,
+ sql_cstr,
+ -1,
+ &new_st,
+ &tail);
+
+ if (rc == SQLITE_OK) {
+ (void)sqlite3_transfer_bindings(self->st, new_st);
+
+ (void)sqlite3_finalize(self->st);
+ self->st = new_st;
+ }
+
+ return rc;
+}
+
+int statement_finalize(Statement* self)
+{
+ int rc;
+
+ rc = SQLITE_OK;
+ if (self->st) {
+ Py_BEGIN_ALLOW_THREADS
+ rc = sqlite3_finalize(self->st);
+ Py_END_ALLOW_THREADS
+ self->st = NULL;
+ }
+
+ self->in_use = 0;
+
+ return rc;
+}
+
+int statement_reset(Statement* self)
+{
+ int rc;
+
+ rc = SQLITE_OK;
+
+ if (self->in_use && self->st) {
+ Py_BEGIN_ALLOW_THREADS
+ rc = sqlite3_reset(self->st);
+ Py_END_ALLOW_THREADS
+
+ if (rc == SQLITE_OK) {
+ self->in_use = 0;
+ }
+ }
+
+ return rc;
+}
+
+void statement_mark_dirty(Statement* self)
+{
+ self->in_use = 1;
+}
+
+void statement_dealloc(Statement* self)
+{
+ int rc;
+
+ if (self->st) {
+ Py_BEGIN_ALLOW_THREADS
+ rc = sqlite3_finalize(self->st);
+ Py_END_ALLOW_THREADS
+ }
+
+ self->st = NULL;
+
+ Py_XDECREF(self->sql);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+/*
+ * Checks if there is anything left in an SQL string after SQLite compiled it.
+ * This is used to check if somebody tried to execute more than one SQL command
+ * with one execute()/executemany() command, which the DB-API and we don't
+ * allow.
+ *
+ * Returns 1 if there is more left than should be. 0 if ok.
+ */
+int check_remaining_sql(const char* tail)
+{
+ const char* pos = tail;
+
+ parse_remaining_sql_state state = NORMAL;
+
+ for (;;) {
+ switch (*pos) {
+ case 0:
+ return 0;
+ case '-':
+ if (state == NORMAL) {
+ state = LINECOMMENT_1;
+ } else if (state == LINECOMMENT_1) {
+ state = IN_LINECOMMENT;
+ }
+ break;
+ case ' ':
+ case '\t':
+ break;
+ case '\n':
+ case 13:
+ if (state == IN_LINECOMMENT) {
+ state = NORMAL;
+ }
+ break;
+ case '/':
+ if (state == NORMAL) {
+ state = COMMENTSTART_1;
+ } else if (state == COMMENTEND_1) {
+ state = NORMAL;
+ } else if (state == COMMENTSTART_1) {
+ return 1;
+ }
+ break;
+ case '*':
+ if (state == NORMAL) {
+ return 1;
+ } else if (state == LINECOMMENT_1) {
+ return 1;
+ } else if (state == COMMENTSTART_1) {
+ state = IN_COMMENT;
+ } else if (state == IN_COMMENT) {
+ state = COMMENTEND_1;
+ }
+ break;
+ default:
+ if (state == COMMENTEND_1) {
+ state = IN_COMMENT;
+ } else if (state == IN_LINECOMMENT) {
+ } else if (state == IN_COMMENT) {
+ } else {
+ return 1;
+ }
+ }
+
+ pos++;
+ }
+
+ return 0;
+}
+
+PyTypeObject StatementType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "pysqlite2.dbapi2.Statement", /* tp_name */
+ sizeof(Statement), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)statement_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ 0, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0 /* tp_free */
+};
+
+extern int statement_setup_types(void)
+{
+ StatementType.tp_new = PyType_GenericNew;
+ return PyType_Ready(&StatementType);
+}
diff --git a/Modules/_sqlite/statement.h b/Modules/_sqlite/statement.h
new file mode 100644
index 0000000..8cf52eb
--- /dev/null
+++ b/Modules/_sqlite/statement.h
@@ -0,0 +1,55 @@
+/* statement.h - definitions for the statement type
+ *
+ * Copyright (C) 2005 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#ifndef PYSQLITE_STATEMENT_H
+#define PYSQLITE_STATEMENT_H
+#include "Python.h"
+
+#include "connection.h"
+#include "sqlite3.h"
+
+#define PYSQLITE_TOO_MUCH_SQL (-100)
+#define PYSQLITE_SQL_WRONG_TYPE (-101)
+
+typedef struct
+{
+ PyObject_HEAD
+ sqlite3* db;
+ sqlite3_stmt* st;
+ PyObject* sql;
+ int in_use;
+} Statement;
+
+extern PyTypeObject StatementType;
+
+int statement_create(Statement* self, Connection* connection, PyObject* sql);
+void statement_dealloc(Statement* self);
+
+int statement_recompile(Statement* self);
+int statement_finalize(Statement* self);
+int statement_reset(Statement* self);
+void statement_mark_dirty(Statement* self);
+
+int statement_setup_types(void);
+
+#endif
diff --git a/Modules/_sqlite/util.c b/Modules/_sqlite/util.c
new file mode 100644
index 0000000..ec400a1
--- /dev/null
+++ b/Modules/_sqlite/util.c
@@ -0,0 +1,147 @@
+/* util.c - various utility functions
+ *
+ * Copyright (C) 2005 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#include "module.h"
+#include "connection.h"
+
+/*
+ * 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
+)
+{
+ int rc;
+
+ Py_BEGIN_ALLOW_THREADS
+ rc = sqlite3_step(statement);
+ Py_END_ALLOW_THREADS
+
+ return rc;
+}
+
+/**
+ * Checks the SQLite error code and sets the appropriate DB-API exception.
+ * Returns the error code (0 means no error occured).
+ */
+int _seterror(sqlite3* db)
+{
+ int errorcode;
+
+ errorcode = sqlite3_errcode(db);
+
+ switch (errorcode)
+ {
+ case SQLITE_OK:
+ PyErr_Clear();
+ break;
+ case SQLITE_ERROR:
+ PyErr_SetString(OperationalError, sqlite3_errmsg(db));
+ break;
+ case SQLITE_INTERNAL:
+ PyErr_SetString(InternalError, sqlite3_errmsg(db));
+ break;
+ case SQLITE_PERM:
+ PyErr_SetString(OperationalError, sqlite3_errmsg(db));
+ break;
+ 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_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;
+ case SQLITE_MISUSE:
+ PyErr_SetString(ProgrammingError, sqlite3_errmsg(db));
+ break;
+ default:
+ PyErr_SetString(DatabaseError, sqlite3_errmsg(db));
+ }
+
+ return errorcode;
+}
+
diff --git a/Modules/_sqlite/util.h b/Modules/_sqlite/util.h
new file mode 100644
index 0000000..6e74b2d
--- /dev/null
+++ b/Modules/_sqlite/util.h
@@ -0,0 +1,40 @@
+/* util.h - various utility functions
+ *
+ * Copyright (C) 2005 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#ifndef PYSQLITE_UTIL_H
+#define PYSQLITE_UTIL_H
+#include "Python.h"
+#include "pythread.h"
+#include "sqlite3.h"
+#include "connection.h"
+
+void pysqlite_sleep(double seconds);
+
+int _sqlite_step_with_busyhandler(sqlite3_stmt* statement, Connection* connection);
+
+/**
+ * Checks the SQLite error code and sets the appropriate DB-API exception.
+ * Returns the error code (0 means no error occured).
+ */
+int _seterror(sqlite3* db);
+#endif
diff --git a/setup.py b/setup.py
index 9272c82..e29e82c 100644
--- a/setup.py
+++ b/setup.py
@@ -689,6 +689,81 @@ class PyBuildExt(build_ext):
dblibs = []
dblib_dir = None
+ # The sqlite interface
+
+ # We hunt for "#define SQLITE_VERSION_NUMBER nnnnn"
+ # We need to find a version >= 3002002 (> sqlite version 3.2.2)
+ sqlite_incdir = sqlite_libdir = None
+ sqlite_inc_paths = [ '/usr/include',
+ '/usr/include/sqlite',
+ '/usr/include/sqlite3',
+ '/usr/local/include',
+ '/usr/local/include/sqlite',
+ '/usr/local/include/sqlite3',
+ ]
+ MIN_SQLITE_VERSION = 3002002
+ for d in sqlite_inc_paths + inc_dirs:
+ f = os.path.join(d, "sqlite3.h")
+ if os.path.exists(f):
+ if db_setup_debug: print "found %s"%f
+ f = open(f).read()
+ m = re.search(r"#define\WSQLITE_VERSION_NUMBER\W(\d+)", f)
+ if m:
+ sqlite_version = int(m.group(1))
+ if sqlite_version >= MIN_SQLITE_VERSION:
+ # we win!
+ print "%s/sqlite3.h: version %s"%(d, sqlite_version)
+ sqlite_incdir = d
+ break
+ else:
+ if db_setup_debug:
+ print "%s: version %d is too old, need >= %s"%(d,
+ sqlite_version, MIN_SQLITE_VERSION)
+
+ if sqlite_incdir:
+ sqlite_dirs_to_check = [
+ os.path.join(sqlite_incdir, '..', 'lib64'),
+ os.path.join(sqlite_incdir, '..', 'lib'),
+ os.path.join(sqlite_incdir, '..', '..', 'lib64'),
+ os.path.join(sqlite_incdir, '..', '..', 'lib'),
+ ]
+ sqlite_libfile = self.compiler.find_library_file(
+ sqlite_dirs_to_check + lib_dirs, 'sqlite3')
+ sqlite_libdir = [os.path.abspath(os.path.dirname(sqlite_libfile))]
+
+ if sqlite_incdir and sqlite_libdir:
+ sqlite_srcs = ['_sqlite/adapters.c',
+ '_sqlite/cache.c',
+ '_sqlite/connection.c',
+ '_sqlite/converters.c',
+ '_sqlite/cursor.c',
+ '_sqlite/microprotocols.c',
+ '_sqlite/module.c',
+ '_sqlite/prepare_protocol.c',
+ '_sqlite/row.c',
+ '_sqlite/statement.c',
+ '_sqlite/util.c', ]
+
+ PYSQLITE_VERSION = "2.1.3"
+ sqlite_defines = []
+ if sys.platform != "win32":
+ sqlite_defines.append(('PYSQLITE_VERSION',
+ '"%s"' % PYSQLITE_VERSION))
+ else:
+ sqlite_defines.append(('PYSQLITE_VERSION',
+ '\\"'+PYSQLITE_VERSION+'\\"'))
+ sqlite_defines.append(('PY_MAJOR_VERSION',
+ str(sys.version_info[0])))
+ sqlite_defines.append(('PY_MINOR_VERSION',
+ str(sys.version_info[1])))
+
+ exts.append(Extension('_sqlite3', sqlite_srcs,
+ define_macros=sqlite_defines,
+ include_dirs=["Modules/_sqlite",
+ sqlite_incdir],
+ library_dirs=sqlite_libdir,
+ runtime_library_dirs=sqlite_libdir,
+ libraries=["sqlite3",]))
# Look for Berkeley db 1.85. Note that it is built as a different
# module name so it can be included even when later versions are