Skip to content

Commit

Permalink
pythongh-105875: Require SQLite 3.15.2 or newer (python#105876)
Browse files Browse the repository at this point in the history
SQLite 3.15.2 was released 2016-11-28.
  • Loading branch information
erlend-aasland authored Jun 18, 2023
1 parent bc07c8f commit 6849acb
Show file tree
Hide file tree
Showing 13 changed files with 61 additions and 202 deletions.
5 changes: 1 addition & 4 deletions Doc/library/sqlite3.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ PostgreSQL or Oracle.

The :mod:`!sqlite3` module was written by Gerhard Häring. It provides an SQL interface
compliant with the DB-API 2.0 specification described by :pep:`249`, and
requires SQLite 3.7.15 or newer.
requires SQLite 3.15.2 or newer.

This document includes four main sections:

Expand Down Expand Up @@ -734,9 +734,6 @@ Connection objects
`deterministic <https://sqlite.org/deterministic.html>`_,
which allows SQLite to perform additional optimizations.

:raises NotSupportedError:
If *deterministic* is used with SQLite versions older than 3.8.3.

.. versionadded:: 3.8
The *deterministic* parameter.

Expand Down
3 changes: 3 additions & 0 deletions Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,9 @@ Build Changes
:file:`!configure`.
(Contributed by Christian Heimes in :gh:`89886`.)

* SQLite 3.15.2 or newer is required to build the :mod:`sqlite3` extension module.
(Contributed by Erlend Aasland in :gh:`105875`.)


C API Changes
=============
Expand Down
2 changes: 0 additions & 2 deletions Lib/test/test_sqlite3/test_backup.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@ def test_bad_target_in_transaction(self):
bck.executemany('INSERT INTO bar (key) VALUES (?)', [(3,), (4,)])
with self.assertRaises(sqlite.OperationalError) as cm:
self.cx.backup(bck)
if sqlite.sqlite_version_info < (3, 8, 8):
self.assertEqual(str(cm.exception), 'target is in transaction')

def test_keyword_only_args(self):
with self.assertRaises(TypeError):
Expand Down
75 changes: 28 additions & 47 deletions Lib/test/test_sqlite3/test_dbapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,20 +165,23 @@ def test_module_constants(self):
"SQLITE_INTERNAL",
"SQLITE_INTERRUPT",
"SQLITE_IOERR",
"SQLITE_LIMIT_WORKER_THREADS",
"SQLITE_LOCKED",
"SQLITE_MISMATCH",
"SQLITE_MISUSE",
"SQLITE_NOLFS",
"SQLITE_NOMEM",
"SQLITE_NOTADB",
"SQLITE_NOTFOUND",
"SQLITE_NOTICE",
"SQLITE_OK",
"SQLITE_PERM",
"SQLITE_PRAGMA",
"SQLITE_PROTOCOL",
"SQLITE_RANGE",
"SQLITE_READ",
"SQLITE_READONLY",
"SQLITE_RECURSIVE",
"SQLITE_REINDEX",
"SQLITE_ROW",
"SQLITE_SAVEPOINT",
Expand All @@ -187,6 +190,7 @@ def test_module_constants(self):
"SQLITE_TOOBIG",
"SQLITE_TRANSACTION",
"SQLITE_UPDATE",
"SQLITE_WARNING",
# Run-time limit categories
"SQLITE_LIMIT_LENGTH",
"SQLITE_LIMIT_SQL_LENGTH",
Expand All @@ -200,32 +204,43 @@ def test_module_constants(self):
"SQLITE_LIMIT_VARIABLE_NUMBER",
"SQLITE_LIMIT_TRIGGER_DEPTH",
]
if sqlite.sqlite_version_info >= (3, 7, 17):
consts += ["SQLITE_NOTICE", "SQLITE_WARNING"]
if sqlite.sqlite_version_info >= (3, 8, 3):
consts.append("SQLITE_RECURSIVE")
if sqlite.sqlite_version_info >= (3, 8, 7):
consts.append("SQLITE_LIMIT_WORKER_THREADS")
consts += ["PARSE_DECLTYPES", "PARSE_COLNAMES"]
# Extended result codes
consts += [
"SQLITE_ABORT_ROLLBACK",
"SQLITE_AUTH_USER",
"SQLITE_BUSY_RECOVERY",
"SQLITE_BUSY_SNAPSHOT",
"SQLITE_CANTOPEN_CONVPATH",
"SQLITE_CANTOPEN_FULLPATH",
"SQLITE_CANTOPEN_ISDIR",
"SQLITE_CANTOPEN_NOTEMPDIR",
"SQLITE_CONSTRAINT_CHECK",
"SQLITE_CONSTRAINT_COMMITHOOK",
"SQLITE_CONSTRAINT_FOREIGNKEY",
"SQLITE_CONSTRAINT_FUNCTION",
"SQLITE_CONSTRAINT_NOTNULL",
"SQLITE_CONSTRAINT_PRIMARYKEY",
"SQLITE_CONSTRAINT_ROWID",
"SQLITE_CONSTRAINT_TRIGGER",
"SQLITE_CONSTRAINT_UNIQUE",
"SQLITE_CONSTRAINT_VTAB",
"SQLITE_CORRUPT_VTAB",
"SQLITE_IOERR_ACCESS",
"SQLITE_IOERR_AUTH",
"SQLITE_IOERR_BLOCKED",
"SQLITE_IOERR_CHECKRESERVEDLOCK",
"SQLITE_IOERR_CLOSE",
"SQLITE_IOERR_CONVPATH",
"SQLITE_IOERR_DELETE",
"SQLITE_IOERR_DELETE_NOENT",
"SQLITE_IOERR_DIR_CLOSE",
"SQLITE_IOERR_DIR_FSYNC",
"SQLITE_IOERR_FSTAT",
"SQLITE_IOERR_FSYNC",
"SQLITE_IOERR_GETTEMPPATH",
"SQLITE_IOERR_LOCK",
"SQLITE_IOERR_MMAP",
"SQLITE_IOERR_NOMEM",
"SQLITE_IOERR_RDLOCK",
"SQLITE_IOERR_READ",
Expand All @@ -237,50 +252,18 @@ def test_module_constants(self):
"SQLITE_IOERR_SHORT_READ",
"SQLITE_IOERR_TRUNCATE",
"SQLITE_IOERR_UNLOCK",
"SQLITE_IOERR_VNODE",
"SQLITE_IOERR_WRITE",
"SQLITE_LOCKED_SHAREDCACHE",
"SQLITE_NOTICE_RECOVER_ROLLBACK",
"SQLITE_NOTICE_RECOVER_WAL",
"SQLITE_OK_LOAD_PERMANENTLY",
"SQLITE_READONLY_CANTLOCK",
"SQLITE_READONLY_DBMOVED",
"SQLITE_READONLY_RECOVERY",
"SQLITE_READONLY_ROLLBACK",
"SQLITE_WARNING_AUTOINDEX",
]
if sqlite.sqlite_version_info >= (3, 7, 16):
consts += [
"SQLITE_CONSTRAINT_CHECK",
"SQLITE_CONSTRAINT_COMMITHOOK",
"SQLITE_CONSTRAINT_FOREIGNKEY",
"SQLITE_CONSTRAINT_FUNCTION",
"SQLITE_CONSTRAINT_NOTNULL",
"SQLITE_CONSTRAINT_PRIMARYKEY",
"SQLITE_CONSTRAINT_TRIGGER",
"SQLITE_CONSTRAINT_UNIQUE",
"SQLITE_CONSTRAINT_VTAB",
"SQLITE_READONLY_ROLLBACK",
]
if sqlite.sqlite_version_info >= (3, 7, 17):
consts += [
"SQLITE_IOERR_MMAP",
"SQLITE_NOTICE_RECOVER_ROLLBACK",
"SQLITE_NOTICE_RECOVER_WAL",
]
if sqlite.sqlite_version_info >= (3, 8, 0):
consts += [
"SQLITE_BUSY_SNAPSHOT",
"SQLITE_IOERR_GETTEMPPATH",
"SQLITE_WARNING_AUTOINDEX",
]
if sqlite.sqlite_version_info >= (3, 8, 1):
consts += ["SQLITE_CANTOPEN_CONVPATH", "SQLITE_IOERR_CONVPATH"]
if sqlite.sqlite_version_info >= (3, 8, 2):
consts.append("SQLITE_CONSTRAINT_ROWID")
if sqlite.sqlite_version_info >= (3, 8, 3):
consts.append("SQLITE_READONLY_DBMOVED")
if sqlite.sqlite_version_info >= (3, 8, 7):
consts.append("SQLITE_AUTH_USER")
if sqlite.sqlite_version_info >= (3, 9, 0):
consts.append("SQLITE_IOERR_VNODE")
if sqlite.sqlite_version_info >= (3, 10, 0):
consts.append("SQLITE_IOERR_AUTH")
if sqlite.sqlite_version_info >= (3, 14, 1):
consts.append("SQLITE_OK_LOAD_PERMANENTLY")
if sqlite.sqlite_version_info >= (3, 21, 0):
consts += [
"SQLITE_IOERR_BEGIN_ATOMIC",
Expand Down Expand Up @@ -330,8 +313,6 @@ def test_error_code_on_exception(self):
self.assertEqual(e.sqlite_errorcode, err_code)
self.assertTrue(e.sqlite_errorname.startswith("SQLITE_CANTOPEN"))

@unittest.skipIf(sqlite.sqlite_version_info <= (3, 7, 16),
"Requires SQLite 3.7.16 or newer")
def test_extended_error_code_on_exception(self):
with memory_database() as con:
with con:
Expand Down
4 changes: 1 addition & 3 deletions Lib/test/test_sqlite3/test_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ def test_trace_expanded_sql(self):
)
def test_trace_too_much_expanded_sql(self):
# If the expanded string is too large, we'll fall back to the
# unexpanded SQL statement (for SQLite 3.14.0 and newer).
# unexpanded SQL statement.
# The resulting string length is limited by the runtime limit
# SQLITE_LIMIT_LENGTH.
template = "select 1 as a where a="
Expand All @@ -334,8 +334,6 @@ def test_trace_too_much_expanded_sql(self):

unexpanded_query = template + "?"
expected = [unexpanded_query]
if sqlite.sqlite_version_info < (3, 14, 0):
expected = []
with self.check_stmt_trace(cx, expected):
cx.execute(unexpanded_query, (bad_param,))

Expand Down
1 change: 0 additions & 1 deletion Lib/test/test_sqlite3/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,6 @@ def test_cursor_description_insert(self):
self.assertIsNone(self.cur.description)


@unittest.skipIf(sqlite.sqlite_version_info < (3, 8, 3), "CTEs not supported")
class CommonTableExpressionTests(unittest.TestCase):

def setUp(self):
Expand Down
34 changes: 9 additions & 25 deletions Lib/test/test_sqlite3/test_userfunctions.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,38 +381,22 @@ def append_result(arg):
# Regarding deterministic functions:
#
# Between 3.8.3 and 3.15.0, deterministic functions were only used to
# optimize inner loops, so for those versions we can only test if the
# sqlite machinery has factored out a call or not. From 3.15.0 and onward,
# deterministic functions were permitted in WHERE clauses of partial
# indices, which allows testing based on syntax, iso. the query optimizer.
@unittest.skipIf(sqlite.sqlite_version_info < (3, 8, 3), "Requires SQLite 3.8.3 or higher")
# optimize inner loops. From 3.15.0 and onward, deterministic functions
# were permitted in WHERE clauses of partial indices, which allows testing
# based on syntax, iso. the query optimizer.
def test_func_non_deterministic(self):
mock = Mock(return_value=None)
self.con.create_function("nondeterministic", 0, mock, deterministic=False)
if sqlite.sqlite_version_info < (3, 15, 0):
self.con.execute("select nondeterministic() = nondeterministic()")
self.assertEqual(mock.call_count, 2)
else:
with self.assertRaises(sqlite.OperationalError):
self.con.execute("create index t on test(t) where nondeterministic() is not null")
with self.assertRaises(sqlite.OperationalError):
self.con.execute("create index t on test(t) where nondeterministic() is not null")

@unittest.skipIf(sqlite.sqlite_version_info < (3, 8, 3), "Requires SQLite 3.8.3 or higher")
def test_func_deterministic(self):
mock = Mock(return_value=None)
self.con.create_function("deterministic", 0, mock, deterministic=True)
if sqlite.sqlite_version_info < (3, 15, 0):
self.con.execute("select deterministic() = deterministic()")
self.assertEqual(mock.call_count, 1)
else:
try:
self.con.execute("create index t on test(t) where deterministic() is not null")
except sqlite.OperationalError:
self.fail("Unexpected failure while creating partial index")

@unittest.skipIf(sqlite.sqlite_version_info >= (3, 8, 3), "SQLite < 3.8.3 needed")
def test_func_deterministic_not_supported(self):
with self.assertRaises(sqlite.NotSupportedError):
self.con.create_function("deterministic", 0, int, deterministic=True)
try:
self.con.execute("create index t on test(t) where deterministic() is not null")
except sqlite.OperationalError:
self.fail("Unexpected failure while creating partial index")

def test_func_deterministic_keyword_only(self):
with self.assertRaises(TypeError):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
SQLite 3.15.2 or newer is required to build the :mod:`sqlite3` extension
module. Patch by Erlend Aasland.
8 changes: 0 additions & 8 deletions Modules/_sqlite/blob.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,6 @@ static void
blob_seterror(pysqlite_Blob *self, int rc)
{
assert(self->connection != NULL);
#if SQLITE_VERSION_NUMBER < 3008008
// SQLite pre 3.8.8 does not set this blob error on the connection
if (rc == SQLITE_ABORT) {
PyErr_SetString(self->connection->OperationalError,
"Cannot operate on an expired blob handle");
return;
}
#endif
_pysqlite_seterror(self->connection->state, self->connection->db);
}

Expand Down
Loading

0 comments on commit 6849acb

Please sign in to comment.