bpo-40744: Drop support for SQLite pre 3.7.3 (GH-20909)

Remove code required to support SQLite pre 3.7.3.

Co-written-by: Berker Peksag <berker.peksag@gmail.com>
Co-written-by: Sergey Fedoseev <fedoseev.sergey@gmail.com>
This commit is contained in:
Erlend Egeberg Aasland 2020-09-07 23:26:54 +02:00 committed by GitHub
parent 22748a83d9
commit 207c321f13
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 29 additions and 94 deletions

View File

@ -18,7 +18,8 @@ application using SQLite and then port the code to a larger database such as
PostgreSQL or Oracle.
The sqlite3 module was written by Gerhard Häring. It provides a SQL interface
compliant with the DB-API 2.0 specification described by :pep:`249`.
compliant with the DB-API 2.0 specification described by :pep:`249`, and
requires SQLite 3.7.3 or newer.
To use the module, you must first create a :class:`Connection` object that
represents the database. Here the data will be stored in the
@ -591,8 +592,6 @@ Connection Objects
dest = sqlite3.connect(':memory:')
source.backup(dest)
Availability: SQLite 3.6.11 or higher
.. versionadded:: 3.7
@ -701,9 +700,6 @@ Cursor Objects
statements because we cannot determine the number of rows a query produced
until all rows were fetched.
With SQLite versions before 3.6.5, :attr:`rowcount` is set to 0 if
you make a ``DELETE FROM table`` without any condition.
.. attribute:: lastrowid
This read-only attribute provides the rowid of the last modified row. It is

View File

@ -191,10 +191,15 @@ that may require changes to your code.
Build Changes
=============
* The C99 functions :c:func:`snprintf` and :c:func:`vsnprintf` are now required
to build Python.
(Contributed by Victor Stinner in :issue:`36020`.)
* :mod:`sqlite3` requires SQLite 3.7.3 or higher.
(Contributed by Sergey Fedoseev and Erlend E. Aasland :issue:`40744`.)
C API Changes
=============

View File

@ -2,7 +2,6 @@ import sqlite3 as sqlite
import unittest
@unittest.skipIf(sqlite.sqlite_version_info < (3, 6, 11), "Backup API not supported")
class BackupTests(unittest.TestCase):
def setUp(self):
cx = self.cx = sqlite.connect(":memory:")

View File

@ -185,12 +185,6 @@ class ConnectionTests(unittest.TestCase):
with self.assertRaises(sqlite.OperationalError):
cx.execute('insert into test(id) values(1)')
@unittest.skipIf(sqlite.sqlite_version_info >= (3, 3, 1),
'needs sqlite versions older than 3.3.1')
def CheckSameThreadErrorOnOldVersion(self):
with self.assertRaises(sqlite.NotSupportedError) as cm:
sqlite.connect(':memory:', check_same_thread=False)
self.assertEqual(str(cm.exception), 'shared connections not available')
class CursorTests(unittest.TestCase):
def setUp(self):

View File

@ -61,8 +61,6 @@ class CollationTests(unittest.TestCase):
self.assertEqual(result[0][0], 'b')
self.assertEqual(result[1][0], 'a')
@unittest.skipIf(sqlite.sqlite_version_info < (3, 2, 1),
'old SQLite versions crash on this test')
def CheckCollationIsUsed(self):
def mycoll(x, y):
# reverse order
@ -240,16 +238,12 @@ class TraceCallbackTests(unittest.TestCase):
traced_statements.append(statement)
con.set_trace_callback(trace)
con.execute("create table foo(x)")
# Can't execute bound parameters as their values don't appear
# in traced statements before SQLite 3.6.21
# (cf. http://www.sqlite.org/draft/releaselog/3_6_21.html)
con.execute('insert into foo(x) values ("%s")' % unicode_value)
con.commit()
self.assertTrue(any(unicode_value in stmt for stmt in traced_statements),
"Unicode data %s garbled in trace callback: %s"
% (ascii(unicode_value), ', '.join(map(ascii, traced_statements))))
@unittest.skipIf(sqlite.sqlite_version_info < (3, 3, 9), "sqlite3_prepare_v2 is not available")
def CheckTraceCallbackContent(self):
# set_trace_callback() shouldn't produce duplicate content (bpo-26187)
traced_statements = []

View File

@ -87,7 +87,6 @@ class RegressionTests(unittest.TestCase):
cur.execute("select 1 x union select " + str(i))
con.close()
@unittest.skipIf(sqlite.sqlite_version_info < (3, 2, 2), 'needs sqlite 3.2.2 or newer')
def CheckOnConflictRollback(self):
con = sqlite.connect(":memory:")
con.execute("create table foo(x, unique(x) on conflict rollback)")

View File

@ -111,16 +111,12 @@ class TransactionTests(unittest.TestCase):
res = self.cur2.fetchall()
self.assertEqual(len(res), 1)
@unittest.skipIf(sqlite.sqlite_version_info < (3, 2, 2),
'test hangs on sqlite versions older than 3.2.2')
def CheckRaiseTimeout(self):
self.cur1.execute("create table test(i)")
self.cur1.execute("insert into test(i) values (5)")
with self.assertRaises(sqlite.OperationalError):
self.cur2.execute("insert into test(i) values (5)")
@unittest.skipIf(sqlite.sqlite_version_info < (3, 2, 2),
'test hangs on sqlite versions older than 3.2.2')
def CheckLocking(self):
"""
This tests the improved concurrency with pysqlite 2.3.4. You needed

View File

@ -401,8 +401,6 @@ class DateTimeTests(unittest.TestCase):
ts2 = self.cur.fetchone()[0]
self.assertEqual(ts, ts2)
@unittest.skipIf(sqlite.sqlite_version_info < (3, 1),
'the date functions are available on 3.1 or later')
def CheckSqlTimestamp(self):
now = datetime.datetime.utcnow()
self.cur.execute("insert into test(ts) values (current_timestamp)")

View File

@ -0,0 +1,4 @@
The :mod:`sqlite3` module uses SQLite API functions that require SQLite
v3.7.3 or higher. This patch removes support for older SQLite versions, and
explicitly requires SQLite 3.7.3 both at build, compile and runtime. Patch by
Sergey Fedoseev and Erlend E. Aasland.

View File

@ -33,16 +33,6 @@
#define ACTION_FINALIZE 1
#define ACTION_RESET 2
#if SQLITE_VERSION_NUMBER >= 3003008
#ifndef SQLITE_OMIT_LOAD_EXTENSION
#define HAVE_LOAD_EXTENSION
#endif
#endif
#if SQLITE_VERSION_NUMBER >= 3006011
#define HAVE_BACKUP_API
#endif
#if SQLITE_VERSION_NUMBER >= 3014000
#define HAVE_TRACE_V2
#endif
@ -61,18 +51,6 @@ static int pysqlite_connection_set_isolation_level(pysqlite_Connection* self, Py
static void _pysqlite_drop_unused_cursor_references(pysqlite_Connection* self);
static void _sqlite3_result_error(sqlite3_context* ctx, const char* errmsg, int len)
{
/* in older SQLite versions, calling sqlite3_result_error in callbacks
* triggers a bug in SQLite that leads either to irritating results or
* segfaults, depending on the SQLite version */
#if SQLITE_VERSION_NUMBER >= 3003003
sqlite3_result_error(ctx, errmsg, len);
#else
PyErr_SetString(pysqlite_OperationalError, errmsg);
#endif
}
int pysqlite_connection_init(pysqlite_Connection* self, PyObject* args, PyObject* kwargs)
{
static char *kwlist[] = {
@ -182,10 +160,6 @@ int pysqlite_connection_init(pysqlite_Connection* self, PyObject* args, PyObject
self->timeout = timeout;
(void)sqlite3_busy_timeout(self->db, (int)(timeout*1000));
self->thread_ident = PyThread_get_thread_ident();
if (!check_same_thread && sqlite3_libversion_number() < 3003001) {
PyErr_SetString(pysqlite_NotSupportedError, "shared connections not available");
return -1;
}
self->check_same_thread = check_same_thread;
self->function_pinboard_trace_callback = NULL;
@ -620,7 +594,7 @@ void _pysqlite_func_callback(sqlite3_context* context, int argc, sqlite3_value**
} else {
PyErr_Clear();
}
_sqlite3_result_error(context, "user-defined function raised exception", -1);
sqlite3_result_error(context, "user-defined function raised exception", -1);
}
PyGILState_Release(threadstate);
@ -652,7 +626,7 @@ static void _pysqlite_step_callback(sqlite3_context *context, int argc, sqlite3_
} else {
PyErr_Clear();
}
_sqlite3_result_error(context, "user-defined aggregate's '__init__' method raised error", -1);
sqlite3_result_error(context, "user-defined aggregate's '__init__' method raised error", -1);
goto error;
}
}
@ -676,7 +650,7 @@ static void _pysqlite_step_callback(sqlite3_context *context, int argc, sqlite3_
} else {
PyErr_Clear();
}
_sqlite3_result_error(context, "user-defined aggregate's 'step' method raised error", -1);
sqlite3_result_error(context, "user-defined aggregate's 'step' method raised error", -1);
}
error:
@ -693,7 +667,6 @@ void _pysqlite_final_callback(sqlite3_context* context)
_Py_IDENTIFIER(finalize);
int ok;
PyObject *exception, *value, *tb;
int restore;
PyGILState_STATE threadstate;
@ -709,7 +682,6 @@ void _pysqlite_final_callback(sqlite3_context* context)
/* Keep the exception (if any) of the last call to step() */
PyErr_Fetch(&exception, &value, &tb);
restore = 1;
function_result = _PyObject_CallMethodIdNoArgs(*aggregate_instance, &PyId_finalize);
@ -726,19 +698,12 @@ void _pysqlite_final_callback(sqlite3_context* context)
} else {
PyErr_Clear();
}
_sqlite3_result_error(context, "user-defined aggregate's 'finalize' method raised error", -1);
#if SQLITE_VERSION_NUMBER < 3003003
/* with old SQLite versions, _sqlite3_result_error() sets a new Python
exception, so don't restore the previous exception */
restore = 0;
#endif
sqlite3_result_error(context, "user-defined aggregate's 'finalize' method raised error", -1);
}
if (restore) {
/* Restore the exception (if any) of the last call to step(),
but clear also the current exception if finalize() failed */
PyErr_Restore(exception, value, tb);
}
/* Restore the exception (if any) of the last call to step(),
but clear also the current exception if finalize() failed */
PyErr_Restore(exception, value, tb);
error:
PyGILState_Release(threadstate);
@ -1110,7 +1075,7 @@ static PyObject* pysqlite_connection_set_trace_callback(pysqlite_Connection* sel
Py_RETURN_NONE;
}
#ifdef HAVE_LOAD_EXTENSION
#ifndef SQLITE_OMIT_LOAD_EXTENSION
static PyObject* pysqlite_enable_load_extension(pysqlite_Connection* self, PyObject* args)
{
int rc;
@ -1513,7 +1478,6 @@ finally:
return retval;
}
#ifdef HAVE_BACKUP_API
static PyObject *
pysqlite_connection_backup(pysqlite_Connection *self, PyObject *args, PyObject *kwds)
{
@ -1664,7 +1628,6 @@ pysqlite_connection_backup(pysqlite_Connection *self, PyObject *args, PyObject *
return NULL;
}
}
#endif
static PyObject *
pysqlite_connection_create_collation(pysqlite_Connection* self, PyObject* args)
@ -1816,7 +1779,7 @@ static PyMethodDef connection_methods[] = {
PyDoc_STR("Creates a new aggregate. Non-standard.")},
{"set_authorizer", (PyCFunction)(void(*)(void))pysqlite_connection_set_authorizer, METH_VARARGS|METH_KEYWORDS,
PyDoc_STR("Sets authorizer callback. Non-standard.")},
#ifdef HAVE_LOAD_EXTENSION
#ifndef SQLITE_OMIT_LOAD_EXTENSION
{"enable_load_extension", (PyCFunction)pysqlite_enable_load_extension, METH_VARARGS,
PyDoc_STR("Enable dynamic loading of SQLite extension modules. Non-standard.")},
{"load_extension", (PyCFunction)pysqlite_load_extension, METH_VARARGS,
@ -1838,10 +1801,8 @@ static PyMethodDef connection_methods[] = {
PyDoc_STR("Abort any pending database operation. Non-standard.")},
{"iterdump", (PyCFunction)pysqlite_connection_iterdump, METH_NOARGS,
PyDoc_STR("Returns iterator to the dump of the database in an SQL text format. Non-standard.")},
#ifdef HAVE_BACKUP_API
{"backup", (PyCFunction)(void(*)(void))pysqlite_connection_backup, METH_VARARGS | METH_KEYWORDS,
PyDoc_STR("Makes a backup of the database. Non-standard.")},
#endif
{"__enter__", (PyCFunction)pysqlite_connection_enter, METH_NOARGS,
PyDoc_STR("For context manager. Non-standard.")},
{"__exit__", (PyCFunction)pysqlite_connection_exit, METH_VARARGS,

View File

@ -29,8 +29,8 @@
#include "microprotocols.h"
#include "row.h"
#if SQLITE_VERSION_NUMBER >= 3003003
#define HAVE_SHARED_CACHE
#if SQLITE_VERSION_NUMBER < 3007003
#error "SQLite 3.7.3 or higher required"
#endif
/* static objects at module-level */
@ -131,7 +131,6 @@ PyDoc_STRVAR(module_complete_doc,
\n\
Checks if a string contains a complete SQL statement. Non-standard.");
#ifdef HAVE_SHARED_CACHE
static PyObject* module_enable_shared_cache(PyObject* self, PyObject* args, PyObject*
kwargs)
{
@ -159,7 +158,6 @@ PyDoc_STRVAR(module_enable_shared_cache_doc,
\n\
Enable or disable shared cache mode for the calling thread.\n\
Experimental/Non-standard.");
#endif /* HAVE_SHARED_CACHE */
static PyObject* module_register_adapter(PyObject* self, PyObject* args)
{
@ -253,10 +251,8 @@ static PyMethodDef module_methods[] = {
METH_VARARGS | METH_KEYWORDS, module_connect_doc},
{"complete_statement", (PyCFunction)(void(*)(void))module_complete,
METH_VARARGS | METH_KEYWORDS, module_complete_doc},
#ifdef HAVE_SHARED_CACHE
{"enable_shared_cache", (PyCFunction)(void(*)(void))module_enable_shared_cache,
METH_VARARGS | METH_KEYWORDS, module_enable_shared_cache_doc},
#endif
{"register_adapter", (PyCFunction)module_register_adapter,
METH_VARARGS, module_register_adapter_doc},
{"register_converter", (PyCFunction)module_register_converter,
@ -307,29 +303,17 @@ static const IntConstantPair _int_constants[] = {
{"SQLITE_UPDATE", SQLITE_UPDATE},
{"SQLITE_ATTACH", SQLITE_ATTACH},
{"SQLITE_DETACH", SQLITE_DETACH},
#if SQLITE_VERSION_NUMBER >= 3002001
{"SQLITE_ALTER_TABLE", SQLITE_ALTER_TABLE},
{"SQLITE_REINDEX", SQLITE_REINDEX},
#endif
#if SQLITE_VERSION_NUMBER >= 3003000
{"SQLITE_ANALYZE", SQLITE_ANALYZE},
#endif
#if SQLITE_VERSION_NUMBER >= 3003007
{"SQLITE_CREATE_VTABLE", SQLITE_CREATE_VTABLE},
{"SQLITE_DROP_VTABLE", SQLITE_DROP_VTABLE},
#endif
#if SQLITE_VERSION_NUMBER >= 3003008
{"SQLITE_FUNCTION", SQLITE_FUNCTION},
#endif
#if SQLITE_VERSION_NUMBER >= 3006008
{"SQLITE_SAVEPOINT", SQLITE_SAVEPOINT},
#endif
#if SQLITE_VERSION_NUMBER >= 3008003
{"SQLITE_RECURSIVE", SQLITE_RECURSIVE},
#endif
#if SQLITE_VERSION_NUMBER >= 3006011
{"SQLITE_DONE", SQLITE_DONE},
#endif
{(char*)NULL, 0}
};
@ -360,6 +344,11 @@ PyMODINIT_FUNC PyInit__sqlite3(void)
PyObject *tmp_obj;
int i;
if (sqlite3_libversion_number() < 3007003) {
PyErr_SetString(PyExc_ImportError, MODULE_NAME ": SQLite 3.7.3 or higher required");
return NULL;
}
module = PyModule_Create(&_sqlite3module);
if (!module ||

View File

@ -1452,7 +1452,6 @@ class PyBuildExt(build_ext):
sqlite_setup_debug = False # verbose debug prints from this script?
# We hunt for #define SQLITE_VERSION "n.n.n"
# We need to find >= sqlite version 3.3.9, for sqlite3_prepare_v2
sqlite_incdir = sqlite_libdir = None
sqlite_inc_paths = [ '/usr/include',
'/usr/include/sqlite',
@ -1463,7 +1462,8 @@ class PyBuildExt(build_ext):
]
if CROSS_COMPILING:
sqlite_inc_paths = []
MIN_SQLITE_VERSION_NUMBER = (3, 7, 2)
# We need to find >= sqlite version 3.7.3, for sqlite3_create_function_v2()
MIN_SQLITE_VERSION_NUMBER = (3, 7, 3)
MIN_SQLITE_VERSION = ".".join([str(x)
for x in MIN_SQLITE_VERSION_NUMBER])