Skip to content

bpo-40744: Drop support for SQLite pre 3.7.3 #20909

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions Doc/library/sqlite3.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -591,8 +592,6 @@ Connection Objects
dest = sqlite3.connect(':memory:')
source.backup(dest)

Availability: SQLite 3.6.11 or higher

.. versionadded:: 3.7


Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions Doc/whatsnew/3.10.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
=============
Expand Down
1 change: 0 additions & 1 deletion Lib/sqlite3/test/backup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
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:")
Expand Down
6 changes: 0 additions & 6 deletions Lib/sqlite3/test/dbapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,12 +185,6 @@ def CheckOpenUri(self):
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):
Expand Down
6 changes: 0 additions & 6 deletions Lib/sqlite3/test/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,6 @@ def upper(self):
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
Expand Down Expand Up @@ -240,16 +238,12 @@ def trace(statement):
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 = []
Expand Down
1 change: 0 additions & 1 deletion Lib/sqlite3/test/regression.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ def CheckStatementFinalizationOnCloseDb(self):
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)")
Expand Down
4 changes: 0 additions & 4 deletions Lib/sqlite3/test/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,16 +111,12 @@ def CheckToggleAutoCommit(self):
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
Expand Down
2 changes: 0 additions & 2 deletions Lib/sqlite3/test/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -401,8 +401,6 @@ def CheckSqliteTimestamp(self):
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)")
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
57 changes: 9 additions & 48 deletions Modules/_sqlite/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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[] = {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
}
Expand All @@ -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:
Expand All @@ -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;

Expand All @@ -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);

Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -1513,7 +1478,6 @@ pysqlite_connection_iterdump(pysqlite_Connection* self, PyObject* args)
return retval;
}

#ifdef HAVE_BACKUP_API
static PyObject *
pysqlite_connection_backup(pysqlite_Connection *self, PyObject *args, PyObject *kwds)
{
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
25 changes: 7 additions & 18 deletions Modules/_sqlite/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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}
};

Expand Down Expand Up @@ -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 ||
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -1452,7 +1452,6 @@ def detect_sqlite(self):
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',
Expand All @@ -1463,7 +1462,8 @@ def detect_sqlite(self):
]
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])

Expand Down