Skip to content

Commit 207c321

Browse files
author
Erlend Egeberg Aasland
authored
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 <[email protected]> Co-written-by: Sergey Fedoseev <[email protected]>
1 parent 22748a8 commit 207c321

File tree

12 files changed

+29
-94
lines changed

12 files changed

+29
-94
lines changed

Doc/library/sqlite3.rst

+2-6
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ application using SQLite and then port the code to a larger database such as
1818
PostgreSQL or Oracle.
1919

2020
The sqlite3 module was written by Gerhard Häring. It provides a SQL interface
21-
compliant with the DB-API 2.0 specification described by :pep:`249`.
21+
compliant with the DB-API 2.0 specification described by :pep:`249`, and
22+
requires SQLite 3.7.3 or newer.
2223

2324
To use the module, you must first create a :class:`Connection` object that
2425
represents the database. Here the data will be stored in the
@@ -591,8 +592,6 @@ Connection Objects
591592
dest = sqlite3.connect(':memory:')
592593
source.backup(dest)
593594

594-
Availability: SQLite 3.6.11 or higher
595-
596595
.. versionadded:: 3.7
597596

598597

@@ -701,9 +700,6 @@ Cursor Objects
701700
statements because we cannot determine the number of rows a query produced
702701
until all rows were fetched.
703702

704-
With SQLite versions before 3.6.5, :attr:`rowcount` is set to 0 if
705-
you make a ``DELETE FROM table`` without any condition.
706-
707703
.. attribute:: lastrowid
708704

709705
This read-only attribute provides the rowid of the last modified row. It is

Doc/whatsnew/3.10.rst

+5
Original file line numberDiff line numberDiff line change
@@ -191,10 +191,15 @@ that may require changes to your code.
191191
Build Changes
192192
=============
193193

194+
194195
* The C99 functions :c:func:`snprintf` and :c:func:`vsnprintf` are now required
195196
to build Python.
196197
(Contributed by Victor Stinner in :issue:`36020`.)
197198

199+
* :mod:`sqlite3` requires SQLite 3.7.3 or higher.
200+
(Contributed by Sergey Fedoseev and Erlend E. Aasland :issue:`40744`.)
201+
202+
198203

199204
C API Changes
200205
=============

Lib/sqlite3/test/backup.py

-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import unittest
33

44

5-
@unittest.skipIf(sqlite.sqlite_version_info < (3, 6, 11), "Backup API not supported")
65
class BackupTests(unittest.TestCase):
76
def setUp(self):
87
cx = self.cx = sqlite.connect(":memory:")

Lib/sqlite3/test/dbapi.py

-6
Original file line numberDiff line numberDiff line change
@@ -185,12 +185,6 @@ def CheckOpenUri(self):
185185
with self.assertRaises(sqlite.OperationalError):
186186
cx.execute('insert into test(id) values(1)')
187187

188-
@unittest.skipIf(sqlite.sqlite_version_info >= (3, 3, 1),
189-
'needs sqlite versions older than 3.3.1')
190-
def CheckSameThreadErrorOnOldVersion(self):
191-
with self.assertRaises(sqlite.NotSupportedError) as cm:
192-
sqlite.connect(':memory:', check_same_thread=False)
193-
self.assertEqual(str(cm.exception), 'shared connections not available')
194188

195189
class CursorTests(unittest.TestCase):
196190
def setUp(self):

Lib/sqlite3/test/hooks.py

-6
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,6 @@ def upper(self):
6161
self.assertEqual(result[0][0], 'b')
6262
self.assertEqual(result[1][0], 'a')
6363

64-
@unittest.skipIf(sqlite.sqlite_version_info < (3, 2, 1),
65-
'old SQLite versions crash on this test')
6664
def CheckCollationIsUsed(self):
6765
def mycoll(x, y):
6866
# reverse order
@@ -240,16 +238,12 @@ def trace(statement):
240238
traced_statements.append(statement)
241239
con.set_trace_callback(trace)
242240
con.execute("create table foo(x)")
243-
# Can't execute bound parameters as their values don't appear
244-
# in traced statements before SQLite 3.6.21
245-
# (cf. http://www.sqlite.org/draft/releaselog/3_6_21.html)
246241
con.execute('insert into foo(x) values ("%s")' % unicode_value)
247242
con.commit()
248243
self.assertTrue(any(unicode_value in stmt for stmt in traced_statements),
249244
"Unicode data %s garbled in trace callback: %s"
250245
% (ascii(unicode_value), ', '.join(map(ascii, traced_statements))))
251246

252-
@unittest.skipIf(sqlite.sqlite_version_info < (3, 3, 9), "sqlite3_prepare_v2 is not available")
253247
def CheckTraceCallbackContent(self):
254248
# set_trace_callback() shouldn't produce duplicate content (bpo-26187)
255249
traced_statements = []

Lib/sqlite3/test/regression.py

-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ def CheckStatementFinalizationOnCloseDb(self):
8787
cur.execute("select 1 x union select " + str(i))
8888
con.close()
8989

90-
@unittest.skipIf(sqlite.sqlite_version_info < (3, 2, 2), 'needs sqlite 3.2.2 or newer')
9190
def CheckOnConflictRollback(self):
9291
con = sqlite.connect(":memory:")
9392
con.execute("create table foo(x, unique(x) on conflict rollback)")

Lib/sqlite3/test/transactions.py

-4
Original file line numberDiff line numberDiff line change
@@ -111,16 +111,12 @@ def CheckToggleAutoCommit(self):
111111
res = self.cur2.fetchall()
112112
self.assertEqual(len(res), 1)
113113

114-
@unittest.skipIf(sqlite.sqlite_version_info < (3, 2, 2),
115-
'test hangs on sqlite versions older than 3.2.2')
116114
def CheckRaiseTimeout(self):
117115
self.cur1.execute("create table test(i)")
118116
self.cur1.execute("insert into test(i) values (5)")
119117
with self.assertRaises(sqlite.OperationalError):
120118
self.cur2.execute("insert into test(i) values (5)")
121119

122-
@unittest.skipIf(sqlite.sqlite_version_info < (3, 2, 2),
123-
'test hangs on sqlite versions older than 3.2.2')
124120
def CheckLocking(self):
125121
"""
126122
This tests the improved concurrency with pysqlite 2.3.4. You needed

Lib/sqlite3/test/types.py

-2
Original file line numberDiff line numberDiff line change
@@ -401,8 +401,6 @@ def CheckSqliteTimestamp(self):
401401
ts2 = self.cur.fetchone()[0]
402402
self.assertEqual(ts, ts2)
403403

404-
@unittest.skipIf(sqlite.sqlite_version_info < (3, 1),
405-
'the date functions are available on 3.1 or later')
406404
def CheckSqlTimestamp(self):
407405
now = datetime.datetime.utcnow()
408406
self.cur.execute("insert into test(ts) values (current_timestamp)")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
The :mod:`sqlite3` module uses SQLite API functions that require SQLite
2+
v3.7.3 or higher. This patch removes support for older SQLite versions, and
3+
explicitly requires SQLite 3.7.3 both at build, compile and runtime. Patch by
4+
Sergey Fedoseev and Erlend E. Aasland.

Modules/_sqlite/connection.c

+9-48
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,6 @@
3333
#define ACTION_FINALIZE 1
3434
#define ACTION_RESET 2
3535

36-
#if SQLITE_VERSION_NUMBER >= 3003008
37-
#ifndef SQLITE_OMIT_LOAD_EXTENSION
38-
#define HAVE_LOAD_EXTENSION
39-
#endif
40-
#endif
41-
42-
#if SQLITE_VERSION_NUMBER >= 3006011
43-
#define HAVE_BACKUP_API
44-
#endif
45-
4636
#if SQLITE_VERSION_NUMBER >= 3014000
4737
#define HAVE_TRACE_V2
4838
#endif
@@ -61,18 +51,6 @@ static int pysqlite_connection_set_isolation_level(pysqlite_Connection* self, Py
6151
static void _pysqlite_drop_unused_cursor_references(pysqlite_Connection* self);
6252

6353

64-
static void _sqlite3_result_error(sqlite3_context* ctx, const char* errmsg, int len)
65-
{
66-
/* in older SQLite versions, calling sqlite3_result_error in callbacks
67-
* triggers a bug in SQLite that leads either to irritating results or
68-
* segfaults, depending on the SQLite version */
69-
#if SQLITE_VERSION_NUMBER >= 3003003
70-
sqlite3_result_error(ctx, errmsg, len);
71-
#else
72-
PyErr_SetString(pysqlite_OperationalError, errmsg);
73-
#endif
74-
}
75-
7654
int pysqlite_connection_init(pysqlite_Connection* self, PyObject* args, PyObject* kwargs)
7755
{
7856
static char *kwlist[] = {
@@ -182,10 +160,6 @@ int pysqlite_connection_init(pysqlite_Connection* self, PyObject* args, PyObject
182160
self->timeout = timeout;
183161
(void)sqlite3_busy_timeout(self->db, (int)(timeout*1000));
184162
self->thread_ident = PyThread_get_thread_ident();
185-
if (!check_same_thread && sqlite3_libversion_number() < 3003001) {
186-
PyErr_SetString(pysqlite_NotSupportedError, "shared connections not available");
187-
return -1;
188-
}
189163
self->check_same_thread = check_same_thread;
190164

191165
self->function_pinboard_trace_callback = NULL;
@@ -620,7 +594,7 @@ void _pysqlite_func_callback(sqlite3_context* context, int argc, sqlite3_value**
620594
} else {
621595
PyErr_Clear();
622596
}
623-
_sqlite3_result_error(context, "user-defined function raised exception", -1);
597+
sqlite3_result_error(context, "user-defined function raised exception", -1);
624598
}
625599

626600
PyGILState_Release(threadstate);
@@ -652,7 +626,7 @@ static void _pysqlite_step_callback(sqlite3_context *context, int argc, sqlite3_
652626
} else {
653627
PyErr_Clear();
654628
}
655-
_sqlite3_result_error(context, "user-defined aggregate's '__init__' method raised error", -1);
629+
sqlite3_result_error(context, "user-defined aggregate's '__init__' method raised error", -1);
656630
goto error;
657631
}
658632
}
@@ -676,7 +650,7 @@ static void _pysqlite_step_callback(sqlite3_context *context, int argc, sqlite3_
676650
} else {
677651
PyErr_Clear();
678652
}
679-
_sqlite3_result_error(context, "user-defined aggregate's 'step' method raised error", -1);
653+
sqlite3_result_error(context, "user-defined aggregate's 'step' method raised error", -1);
680654
}
681655

682656
error:
@@ -693,7 +667,6 @@ void _pysqlite_final_callback(sqlite3_context* context)
693667
_Py_IDENTIFIER(finalize);
694668
int ok;
695669
PyObject *exception, *value, *tb;
696-
int restore;
697670

698671
PyGILState_STATE threadstate;
699672

@@ -709,7 +682,6 @@ void _pysqlite_final_callback(sqlite3_context* context)
709682

710683
/* Keep the exception (if any) of the last call to step() */
711684
PyErr_Fetch(&exception, &value, &tb);
712-
restore = 1;
713685

714686
function_result = _PyObject_CallMethodIdNoArgs(*aggregate_instance, &PyId_finalize);
715687

@@ -726,19 +698,12 @@ void _pysqlite_final_callback(sqlite3_context* context)
726698
} else {
727699
PyErr_Clear();
728700
}
729-
_sqlite3_result_error(context, "user-defined aggregate's 'finalize' method raised error", -1);
730-
#if SQLITE_VERSION_NUMBER < 3003003
731-
/* with old SQLite versions, _sqlite3_result_error() sets a new Python
732-
exception, so don't restore the previous exception */
733-
restore = 0;
734-
#endif
701+
sqlite3_result_error(context, "user-defined aggregate's 'finalize' method raised error", -1);
735702
}
736703

737-
if (restore) {
738-
/* Restore the exception (if any) of the last call to step(),
739-
but clear also the current exception if finalize() failed */
740-
PyErr_Restore(exception, value, tb);
741-
}
704+
/* Restore the exception (if any) of the last call to step(),
705+
but clear also the current exception if finalize() failed */
706+
PyErr_Restore(exception, value, tb);
742707

743708
error:
744709
PyGILState_Release(threadstate);
@@ -1110,7 +1075,7 @@ static PyObject* pysqlite_connection_set_trace_callback(pysqlite_Connection* sel
11101075
Py_RETURN_NONE;
11111076
}
11121077

1113-
#ifdef HAVE_LOAD_EXTENSION
1078+
#ifndef SQLITE_OMIT_LOAD_EXTENSION
11141079
static PyObject* pysqlite_enable_load_extension(pysqlite_Connection* self, PyObject* args)
11151080
{
11161081
int rc;
@@ -1513,7 +1478,6 @@ pysqlite_connection_iterdump(pysqlite_Connection* self, PyObject* args)
15131478
return retval;
15141479
}
15151480

1516-
#ifdef HAVE_BACKUP_API
15171481
static PyObject *
15181482
pysqlite_connection_backup(pysqlite_Connection *self, PyObject *args, PyObject *kwds)
15191483
{
@@ -1664,7 +1628,6 @@ pysqlite_connection_backup(pysqlite_Connection *self, PyObject *args, PyObject *
16641628
return NULL;
16651629
}
16661630
}
1667-
#endif
16681631

16691632
static PyObject *
16701633
pysqlite_connection_create_collation(pysqlite_Connection* self, PyObject* args)
@@ -1816,7 +1779,7 @@ static PyMethodDef connection_methods[] = {
18161779
PyDoc_STR("Creates a new aggregate. Non-standard.")},
18171780
{"set_authorizer", (PyCFunction)(void(*)(void))pysqlite_connection_set_authorizer, METH_VARARGS|METH_KEYWORDS,
18181781
PyDoc_STR("Sets authorizer callback. Non-standard.")},
1819-
#ifdef HAVE_LOAD_EXTENSION
1782+
#ifndef SQLITE_OMIT_LOAD_EXTENSION
18201783
{"enable_load_extension", (PyCFunction)pysqlite_enable_load_extension, METH_VARARGS,
18211784
PyDoc_STR("Enable dynamic loading of SQLite extension modules. Non-standard.")},
18221785
{"load_extension", (PyCFunction)pysqlite_load_extension, METH_VARARGS,
@@ -1838,10 +1801,8 @@ static PyMethodDef connection_methods[] = {
18381801
PyDoc_STR("Abort any pending database operation. Non-standard.")},
18391802
{"iterdump", (PyCFunction)pysqlite_connection_iterdump, METH_NOARGS,
18401803
PyDoc_STR("Returns iterator to the dump of the database in an SQL text format. Non-standard.")},
1841-
#ifdef HAVE_BACKUP_API
18421804
{"backup", (PyCFunction)(void(*)(void))pysqlite_connection_backup, METH_VARARGS | METH_KEYWORDS,
18431805
PyDoc_STR("Makes a backup of the database. Non-standard.")},
1844-
#endif
18451806
{"__enter__", (PyCFunction)pysqlite_connection_enter, METH_NOARGS,
18461807
PyDoc_STR("For context manager. Non-standard.")},
18471808
{"__exit__", (PyCFunction)pysqlite_connection_exit, METH_VARARGS,

Modules/_sqlite/module.c

+7-18
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
#include "microprotocols.h"
3030
#include "row.h"
3131

32-
#if SQLITE_VERSION_NUMBER >= 3003003
33-
#define HAVE_SHARED_CACHE
32+
#if SQLITE_VERSION_NUMBER < 3007003
33+
#error "SQLite 3.7.3 or higher required"
3434
#endif
3535

3636
/* static objects at module-level */
@@ -131,7 +131,6 @@ PyDoc_STRVAR(module_complete_doc,
131131
\n\
132132
Checks if a string contains a complete SQL statement. Non-standard.");
133133

134-
#ifdef HAVE_SHARED_CACHE
135134
static PyObject* module_enable_shared_cache(PyObject* self, PyObject* args, PyObject*
136135
kwargs)
137136
{
@@ -159,7 +158,6 @@ PyDoc_STRVAR(module_enable_shared_cache_doc,
159158
\n\
160159
Enable or disable shared cache mode for the calling thread.\n\
161160
Experimental/Non-standard.");
162-
#endif /* HAVE_SHARED_CACHE */
163161

164162
static PyObject* module_register_adapter(PyObject* self, PyObject* args)
165163
{
@@ -253,10 +251,8 @@ static PyMethodDef module_methods[] = {
253251
METH_VARARGS | METH_KEYWORDS, module_connect_doc},
254252
{"complete_statement", (PyCFunction)(void(*)(void))module_complete,
255253
METH_VARARGS | METH_KEYWORDS, module_complete_doc},
256-
#ifdef HAVE_SHARED_CACHE
257254
{"enable_shared_cache", (PyCFunction)(void(*)(void))module_enable_shared_cache,
258255
METH_VARARGS | METH_KEYWORDS, module_enable_shared_cache_doc},
259-
#endif
260256
{"register_adapter", (PyCFunction)module_register_adapter,
261257
METH_VARARGS, module_register_adapter_doc},
262258
{"register_converter", (PyCFunction)module_register_converter,
@@ -307,29 +303,17 @@ static const IntConstantPair _int_constants[] = {
307303
{"SQLITE_UPDATE", SQLITE_UPDATE},
308304
{"SQLITE_ATTACH", SQLITE_ATTACH},
309305
{"SQLITE_DETACH", SQLITE_DETACH},
310-
#if SQLITE_VERSION_NUMBER >= 3002001
311306
{"SQLITE_ALTER_TABLE", SQLITE_ALTER_TABLE},
312307
{"SQLITE_REINDEX", SQLITE_REINDEX},
313-
#endif
314-
#if SQLITE_VERSION_NUMBER >= 3003000
315308
{"SQLITE_ANALYZE", SQLITE_ANALYZE},
316-
#endif
317-
#if SQLITE_VERSION_NUMBER >= 3003007
318309
{"SQLITE_CREATE_VTABLE", SQLITE_CREATE_VTABLE},
319310
{"SQLITE_DROP_VTABLE", SQLITE_DROP_VTABLE},
320-
#endif
321-
#if SQLITE_VERSION_NUMBER >= 3003008
322311
{"SQLITE_FUNCTION", SQLITE_FUNCTION},
323-
#endif
324-
#if SQLITE_VERSION_NUMBER >= 3006008
325312
{"SQLITE_SAVEPOINT", SQLITE_SAVEPOINT},
326-
#endif
327313
#if SQLITE_VERSION_NUMBER >= 3008003
328314
{"SQLITE_RECURSIVE", SQLITE_RECURSIVE},
329315
#endif
330-
#if SQLITE_VERSION_NUMBER >= 3006011
331316
{"SQLITE_DONE", SQLITE_DONE},
332-
#endif
333317
{(char*)NULL, 0}
334318
};
335319

@@ -360,6 +344,11 @@ PyMODINIT_FUNC PyInit__sqlite3(void)
360344
PyObject *tmp_obj;
361345
int i;
362346

347+
if (sqlite3_libversion_number() < 3007003) {
348+
PyErr_SetString(PyExc_ImportError, MODULE_NAME ": SQLite 3.7.3 or higher required");
349+
return NULL;
350+
}
351+
363352
module = PyModule_Create(&_sqlite3module);
364353

365354
if (!module ||

setup.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1452,7 +1452,6 @@ def detect_sqlite(self):
14521452
sqlite_setup_debug = False # verbose debug prints from this script?
14531453

14541454
# We hunt for #define SQLITE_VERSION "n.n.n"
1455-
# We need to find >= sqlite version 3.3.9, for sqlite3_prepare_v2
14561455
sqlite_incdir = sqlite_libdir = None
14571456
sqlite_inc_paths = [ '/usr/include',
14581457
'/usr/include/sqlite',
@@ -1463,7 +1462,8 @@ def detect_sqlite(self):
14631462
]
14641463
if CROSS_COMPILING:
14651464
sqlite_inc_paths = []
1466-
MIN_SQLITE_VERSION_NUMBER = (3, 7, 2)
1465+
# We need to find >= sqlite version 3.7.3, for sqlite3_create_function_v2()
1466+
MIN_SQLITE_VERSION_NUMBER = (3, 7, 3)
14671467
MIN_SQLITE_VERSION = ".".join([str(x)
14681468
for x in MIN_SQLITE_VERSION_NUMBER])
14691469

0 commit comments

Comments
 (0)