[Python-checkins] bpo-40744: Drop support for SQLite pre 3.7.3 (GH-20909)

Erlend Egeberg Aasland webhook-mailer at python.org
Mon Sep 7 17:27:04 EDT 2020


https://github.com/python/cpython/commit/207c321f13cea3fee7f378057864e8c6453f5adf
commit: 207c321f13cea3fee7f378057864e8c6453f5adf
branch: master
author: Erlend Egeberg Aasland <erlend.aasland at innova.no>
committer: GitHub <noreply at github.com>
date: 2020-09-07T22:26:54+01:00
summary:

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 at gmail.com>
Co-written-by: Sergey Fedoseev <fedoseev.sergey at gmail.com>

files:
A Misc/NEWS.d/next/Library/2020-05-30-08-10-23.bpo-40744.jKURVV.rst
M Doc/library/sqlite3.rst
M Doc/whatsnew/3.10.rst
M Lib/sqlite3/test/backup.py
M Lib/sqlite3/test/dbapi.py
M Lib/sqlite3/test/hooks.py
M Lib/sqlite3/test/regression.py
M Lib/sqlite3/test/transactions.py
M Lib/sqlite3/test/types.py
M Modules/_sqlite/connection.c
M Modules/_sqlite/module.c
M setup.py

diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst
index ccb82278bdaa1..13aa8c512d031 100644
--- a/Doc/library/sqlite3.rst
+++ b/Doc/library/sqlite3.rst
@@ -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
diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst
index eb5ae01a7c04d..f6f276a8bfa49 100644
--- a/Doc/whatsnew/3.10.rst
+++ b/Doc/whatsnew/3.10.rst
@@ -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
 =============
diff --git a/Lib/sqlite3/test/backup.py b/Lib/sqlite3/test/backup.py
index 903bacf490301..2752a4db337dd 100644
--- a/Lib/sqlite3/test/backup.py
+++ b/Lib/sqlite3/test/backup.py
@@ -2,7 +2,6 @@
 import unittest
 
 
- at 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:")
diff --git a/Lib/sqlite3/test/dbapi.py b/Lib/sqlite3/test/dbapi.py
index 119da12170331..a8dfeb9b2d693 100644
--- a/Lib/sqlite3/test/dbapi.py
+++ b/Lib/sqlite3/test/dbapi.py
@@ -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):
diff --git a/Lib/sqlite3/test/hooks.py b/Lib/sqlite3/test/hooks.py
index b08adf1d8097b..2e620ecdf864c 100644
--- a/Lib/sqlite3/test/hooks.py
+++ b/Lib/sqlite3/test/hooks.py
@@ -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
@@ -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 = []
diff --git a/Lib/sqlite3/test/regression.py b/Lib/sqlite3/test/regression.py
index cbd46d4978afb..0735a5c129226 100644
--- a/Lib/sqlite3/test/regression.py
+++ b/Lib/sqlite3/test/regression.py
@@ -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)")
diff --git a/Lib/sqlite3/test/transactions.py b/Lib/sqlite3/test/transactions.py
index b8a13de55bc72..c463f7490da57 100644
--- a/Lib/sqlite3/test/transactions.py
+++ b/Lib/sqlite3/test/transactions.py
@@ -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
diff --git a/Lib/sqlite3/test/types.py b/Lib/sqlite3/test/types.py
index d26a9cb93f088..75a9d5601d580 100644
--- a/Lib/sqlite3/test/types.py
+++ b/Lib/sqlite3/test/types.py
@@ -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)")
diff --git a/Misc/NEWS.d/next/Library/2020-05-30-08-10-23.bpo-40744.jKURVV.rst b/Misc/NEWS.d/next/Library/2020-05-30-08-10-23.bpo-40744.jKURVV.rst
new file mode 100644
index 0000000000000..2d1d1f9a20e32
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-05-30-08-10-23.bpo-40744.jKURVV.rst
@@ -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.
diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c
index 1bf9710763a5a..f765ba1df2466 100644
--- a/Modules/_sqlite/connection.c
+++ b/Modules/_sqlite/connection.c
@@ -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 @@ 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)
 {
@@ -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,
diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c
index 71d951ee887e4..82f58eb248026 100644
--- a/Modules/_sqlite/module.c
+++ b/Modules/_sqlite/module.c
@@ -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 ||
diff --git a/setup.py b/setup.py
index 21a5a58981fc1..04b1358bc916e 100644
--- a/setup.py
+++ b/setup.py
@@ -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',
@@ -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])
 



More information about the Python-checkins mailing list