From a0183844306f0bf98ac9c412dc6d61bbee2688f9 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 2 Sep 2021 00:19:24 +0200 Subject: [PATCH 01/14] Use expanded SQL when tracing using v2 API --- Doc/library/sqlite3.rst | 6 +++++ Doc/whatsnew/3.11.rst | 4 ++++ Lib/sqlite3/test/hooks.py | 27 +++++++++++++++++++++- Modules/_sqlite/connection.c | 43 ++++++++++++++++++++++++++++++------ 4 files changed, 72 insertions(+), 8 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 7c60188bc70b59..7b981571331508 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -468,8 +468,14 @@ Connection Objects Passing :const:`None` as *trace_callback* will disable the trace callback. + For SQLite 3.14.0 and newer, bound parameters are expanded in the passed + statement string. + .. versionadded:: 3.3 + .. versionchanged:: 3.11 + Added support for expanded SQL statements. + .. method:: enable_load_extension(enabled) diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 60ef953d2ec165..232faf4363075a 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -232,6 +232,10 @@ sqlite3 (Contributed by Aviv Palivoda, Daniel Shahaf, and Erlend E. Aasland in :issue:`16379`.) +* For SQLite 3.14.0 and newer, bound parameters are expanded in the statement + string passed to the trace callback. See :meth:`~sqlite3.Connection.set_trace_callback`. + (Contributed by Erlend E. Aasland in :issue:`45138`.) + Removed ======= diff --git a/Lib/sqlite3/test/hooks.py b/Lib/sqlite3/test/hooks.py index 43e3810d13df18..f18038745b2971 100644 --- a/Lib/sqlite3/test/hooks.py +++ b/Lib/sqlite3/test/hooks.py @@ -20,8 +20,8 @@ # misrepresented as being the original software. # 3. This notice may not be removed or altered from any source distribution. -import unittest import sqlite3 as sqlite +import unittest from test.support.os_helper import TESTFN, unlink from .userfunctions import with_tracebacks @@ -289,6 +289,31 @@ def trace(statement): con2.close() self.assertEqual(traced_statements, queries) + @unittest.skipIf(sqlite.sqlite_version_info < (3, 14, 0), + "Requires SQLite 3.14.0 or newer") + def test_trace_expanded_sql(self): + cx = sqlite.connect(":memory:") + traced = [] + cx.set_trace_callback(lambda stmt: traced.append(stmt)) + with cx: + cx.execute("create table t(t)") + cx.executemany("insert into t values(?)", ((v,) for v in range(3))) + expected = [ + "create table t(t)", + "BEGIN ", + "insert into t values(0)", + "insert into t values(1)", + "insert into t values(2)", + "COMMIT", + ] + self.assertEqual(traced, expected) + + @with_tracebacks(["ZeroDivisionError", "5/0"]) + def test_trace_bad_handler(self): + cx = sqlite.connect(":memory:") + cx.set_trace_callback(lambda stmt: 5/0) + cx.execute("select 1") + def suite(): tests = [ diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index bf803370c05719..4b7109e20a5e55 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -1026,11 +1026,10 @@ progress_callback(void *ctx) * to ensure future compatibility. */ static int -trace_callback(unsigned int type, void *ctx, void *prepared_statement, - void *statement_string) +trace_callback(unsigned int type, void *ctx, void *stmt, void *Py_UNUSED(sql)) #else static void -trace_callback(void *ctx, const char *statement_string) +trace_callback(void *ctx, const char *sql) #endif { #ifdef HAVE_TRACE_V2 @@ -1041,11 +1040,33 @@ trace_callback(void *ctx, const char *statement_string) PyGILState_STATE gilstate = PyGILState_Ensure(); + assert(ctx != NULL); PyObject *py_statement = NULL; +#ifdef HAVE_TRACE_V2 + assert(stmt != NULL); + const char *expanded_sql = sqlite3_expanded_sql((sqlite3_stmt *)stmt); + if (expanded_sql == NULL) { + sqlite3 *db = sqlite3_db_handle((sqlite3_stmt *)stmt); + if (sqlite3_errcode(db) == SQLITE_NOMEM) { + (void)PyErr_NoMemory(); + goto exit; + } + else { + pysqlite_state *state = ((callback_context *)ctx)->state; + assert(state != NULL); + PyErr_SetString(state->DataError, + "Expanded SQL string exceeds the maximum string " + "length"); + } + } + else { + py_statement = PyUnicode_FromString(expanded_sql); + sqlite3_free((void *)expanded_sql); + } +#else + py_statement = PyUnicode_FromString(sql); +#endif PyObject *ret = NULL; - py_statement = PyUnicode_DecodeUTF8(statement_string, - strlen(statement_string), "replace"); - assert(ctx != NULL); if (py_statement) { PyObject *callable = ((callback_context *)ctx)->callable; ret = PyObject_CallOneArg(callable, py_statement); @@ -1056,9 +1077,17 @@ trace_callback(void *ctx, const char *statement_string) Py_DECREF(ret); } else { - print_or_clear_traceback(ctx); + pysqlite_state *state = ((callback_context *)ctx)->state; + assert(state != NULL); + if (state->enable_callback_tracebacks) { + PyErr_Print(); + } + else { + PyErr_Clear(); + } } +exit: PyGILState_Release(gilstate); #ifdef HAVE_TRACE_V2 return 0; From 0d59fd9dc7234d966d56b58da4c2925c71d485b5 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 8 Sep 2021 16:12:10 +0200 Subject: [PATCH 02/14] Fall back to unexpanded sql if expanded sql is too long --- Modules/_sqlite/connection.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 4b7109e20a5e55..826788948e8bd4 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -1026,7 +1026,7 @@ progress_callback(void *ctx) * to ensure future compatibility. */ static int -trace_callback(unsigned int type, void *ctx, void *stmt, void *Py_UNUSED(sql)) +trace_callback(unsigned int type, void *ctx, void *stmt, void *sql) #else static void trace_callback(void *ctx, const char *sql) @@ -1054,9 +1054,14 @@ trace_callback(void *ctx, const char *sql) else { pysqlite_state *state = ((callback_context *)ctx)->state; assert(state != NULL); - PyErr_SetString(state->DataError, - "Expanded SQL string exceeds the maximum string " - "length"); + if (state->enable_callback_tracebacks) { + PyErr_SetString(state->DataError, + "Expanded SQL string exceeds the maximum " + "string length"); + PyErr_Print(); + } + // Fall back to unexpanded sql + py_statement = PyUnicode_FromString((const char *)sql); } } else { From 654b89f954effbac44ac687758a02fd33c058d1b Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 8 Sep 2021 16:19:03 +0200 Subject: [PATCH 03/14] Remove unneeded indent --- Modules/_sqlite/connection.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 826788948e8bd4..7f77fdd9c427ea 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -1051,18 +1051,17 @@ trace_callback(void *ctx, const char *sql) (void)PyErr_NoMemory(); goto exit; } - else { - pysqlite_state *state = ((callback_context *)ctx)->state; - assert(state != NULL); - if (state->enable_callback_tracebacks) { - PyErr_SetString(state->DataError, - "Expanded SQL string exceeds the maximum " - "string length"); - PyErr_Print(); - } - // Fall back to unexpanded sql - py_statement = PyUnicode_FromString((const char *)sql); + + pysqlite_state *state = ((callback_context *)ctx)->state; + assert(state != NULL); + if (state->enable_callback_tracebacks) { + PyErr_SetString(state->DataError, + "Expanded SQL string exceeds the maximum string " + "length"); + PyErr_Print(); } + // Fall back to unexpanded sql + py_statement = PyUnicode_FromString((const char *)sql); } else { py_statement = PyUnicode_FromString(expanded_sql); From d7dcbd47b50a82649cfdee598b3ab9f462d85b1a Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 8 Sep 2021 16:21:08 +0200 Subject: [PATCH 04/14] Add NEWS --- .../next/Library/2021-09-08-16-21-03.bpo-45138.yghUrK.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2021-09-08-16-21-03.bpo-45138.yghUrK.rst diff --git a/Misc/NEWS.d/next/Library/2021-09-08-16-21-03.bpo-45138.yghUrK.rst b/Misc/NEWS.d/next/Library/2021-09-08-16-21-03.bpo-45138.yghUrK.rst new file mode 100644 index 00000000000000..7b0b4402aeacea --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-09-08-16-21-03.bpo-45138.yghUrK.rst @@ -0,0 +1,3 @@ +For SQLite 3.14.0 and newer, bound parameters are expanded in the statement +string passed to the :mod:`sqlite3` trace callback. Patch by Erlend E. +Aasland. From bc71d9913273d9c91f6527f1d13f3d6a3bf8a2e5 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 8 Sep 2021 16:24:12 +0200 Subject: [PATCH 05/14] Revert spurious print traceback change --- Modules/_sqlite/connection.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 7f77fdd9c427ea..f20d95fcd6a3c9 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -1081,14 +1081,7 @@ trace_callback(void *ctx, const char *sql) Py_DECREF(ret); } else { - pysqlite_state *state = ((callback_context *)ctx)->state; - assert(state != NULL); - if (state->enable_callback_tracebacks) { - PyErr_Print(); - } - else { - PyErr_Clear(); - } + print_or_clear_traceback((callback_context *)ctx); } exit: From b91fc81849479a4368e21cdff413e7949cd944a4 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 6 Oct 2021 23:06:47 +0200 Subject: [PATCH 06/14] Chain exceptions if multiple exceptions happen --- Modules/_sqlite/connection.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 271093393c3939..052d214e0a3e65 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -1061,12 +1061,10 @@ trace_callback(void *ctx, const char *sql) pysqlite_state *state = ((callback_context *)ctx)->state; assert(state != NULL); - if (state->enable_callback_tracebacks) { - PyErr_SetString(state->DataError, - "Expanded SQL string exceeds the maximum string " - "length"); - PyErr_Print(); - } + PyErr_SetString(state->DataError, + "Expanded SQL string exceeds the maximum string " + "length"); + // Fall back to unexpanded sql py_statement = PyUnicode_FromString((const char *)sql); } @@ -1077,17 +1075,19 @@ trace_callback(void *ctx, const char *sql) #else py_statement = PyUnicode_FromString(sql); #endif - PyObject *ret = NULL; if (py_statement) { + PyObject *exc, *val, *tb; + PyErr_Fetch(&exc, &val, &tb); + PyObject *callable = ((callback_context *)ctx)->callable; - ret = PyObject_CallOneArg(callable, py_statement); + PyObject *ret = PyObject_CallOneArg(callable, py_statement); Py_DECREF(py_statement); - } + Py_XDECREF(ret); - if (ret) { - Py_DECREF(ret); + _PyErr_ChainExceptions(exc, val, tb); } - else { + + if (PyErr_Occurred()) { print_or_clear_traceback((callback_context *)ctx); } From ec19cf7ee23ee119b386924e5b257449a9bd7fa6 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 3 Nov 2021 23:45:13 +0100 Subject: [PATCH 07/14] Improve coverage --- Lib/test/test_sqlite3/test_dbapi.py | 14 ++++++++++ Lib/test/test_sqlite3/test_hooks.py | 40 +++++++++++++++++++++-------- 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/Lib/test/test_sqlite3/test_dbapi.py b/Lib/test/test_sqlite3/test_dbapi.py index ba3652a04a2d87..12534c9308c540 100644 --- a/Lib/test/test_sqlite3/test_dbapi.py +++ b/Lib/test/test_sqlite3/test_dbapi.py @@ -48,6 +48,20 @@ def managed_connect(*args, in_mem=False, **kwargs): unlink(TESTFN) +def memory_database(): + cx = sqlite.connect(":memory:") + return contextlib.closing(cx) + + +@contextlib.contextmanager +def cx_limit(cx, category=sqlite.SQLITE_LIMIT_LENGTH, limit=128): + try: + _prev = cx.setlimit(category, limit) + yield limit + finally: + cx.setlimit(category, _prev) + + class ModuleTests(unittest.TestCase): def test_api_level(self): self.assertEqual(sqlite.apilevel, "2.0", diff --git a/Lib/test/test_sqlite3/test_hooks.py b/Lib/test/test_sqlite3/test_hooks.py index 4042b2dd14df4e..782ddb70ca2b03 100644 --- a/Lib/test/test_sqlite3/test_hooks.py +++ b/Lib/test/test_sqlite3/test_hooks.py @@ -20,12 +20,25 @@ # misrepresented as being the original software. # 3. This notice may not be removed or altered from any source distribution. +import contextlib import sqlite3 as sqlite import unittest from test.support.os_helper import TESTFN, unlink +from .test_dbapi import memory_database, cx_limit from .test_userfunctions import with_tracebacks + +@contextlib.contextmanager +def check_trace(self, cx, expected): + try: + traced = [] + cx.set_trace_callback(lambda stmt: traced.append(stmt)) + yield + finally: + self.assertEqual(traced, expected) + + class CollationTests(unittest.TestCase): def test_create_collation_not_string(self): con = sqlite.connect(":memory:") @@ -292,12 +305,6 @@ def trace(statement): @unittest.skipIf(sqlite.sqlite_version_info < (3, 14, 0), "Requires SQLite 3.14.0 or newer") def test_trace_expanded_sql(self): - cx = sqlite.connect(":memory:") - traced = [] - cx.set_trace_callback(lambda stmt: traced.append(stmt)) - with cx: - cx.execute("create table t(t)") - cx.executemany("insert into t values(?)", ((v,) for v in range(3))) expected = [ "create table t(t)", "BEGIN ", @@ -306,13 +313,26 @@ def test_trace_expanded_sql(self): "insert into t values(2)", "COMMIT", ] - self.assertEqual(traced, expected) + with memory_database() as cx, check_trace(self, cx, expected): + with cx: + cx.execute("create table t(t)") + cx.executemany("insert into t values(?)", ((v,) for v in range(3))) + + @with_tracebacks([ + "DataError", + "Expanded SQL string exceeds the maximum string length" + ], traceback=False) + def test_trace_too_much_expanded_sql(self): + query = "select * from sqlite_schema where type=?" + with memory_database() as cx, cx_limit(cx) as lim: + with check_trace(self, cx, [query]): + cx.execute(query, ("a"*lim,)) @with_tracebacks(["ZeroDivisionError", "5/0"]) def test_trace_bad_handler(self): - cx = sqlite.connect(":memory:") - cx.set_trace_callback(lambda stmt: 5/0) - cx.execute("select 1") + with memory_database() as cx: + cx.set_trace_callback(lambda stmt: 5/0) + cx.execute("select 1") if __name__ == "__main__": From f773a859ca4af20bb462b75933f6dff6a452ec18 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 10 Nov 2021 21:01:19 +0100 Subject: [PATCH 08/14] Expand test --- Lib/test/test_sqlite3/test_hooks.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_sqlite3/test_hooks.py b/Lib/test/test_sqlite3/test_hooks.py index 782ddb70ca2b03..aa049d958da26b 100644 --- a/Lib/test/test_sqlite3/test_hooks.py +++ b/Lib/test/test_sqlite3/test_hooks.py @@ -30,13 +30,14 @@ @contextlib.contextmanager -def check_trace(self, cx, expected): +def check_stmt_trace(self, cx, expected): try: traced = [] cx.set_trace_callback(lambda stmt: traced.append(stmt)) yield finally: self.assertEqual(traced, expected) + cx.set_trace_callback(None) class CollationTests(unittest.TestCase): @@ -313,7 +314,7 @@ def test_trace_expanded_sql(self): "insert into t values(2)", "COMMIT", ] - with memory_database() as cx, check_trace(self, cx, expected): + with memory_database() as cx, check_stmt_trace(self, cx, expected): with cx: cx.execute("create table t(t)") cx.executemany("insert into t values(?)", ((v,) for v in range(3))) @@ -323,10 +324,23 @@ def test_trace_expanded_sql(self): "Expanded SQL string exceeds the maximum string length" ], traceback=False) def test_trace_too_much_expanded_sql(self): - query = "select * from sqlite_schema where type=?" - with memory_database() as cx, cx_limit(cx) as lim: - with check_trace(self, cx, [query]): - cx.execute(query, ("a"*lim,)) + # If the expanded string is too large, we'll fall back to the + # unexpanded SQL statement. The resulting string length is limited by + # SQLITE_LIMIT_LENGTH. + template = "select * from sqlite_schema where type=" + category=sqlite.SQLITE_LIMIT_LENGTH + with memory_database() as cx, cx_limit(cx, category=category) as lim: + nextra = lim - (len(template) + 2) - 1 + ok_param = "a" * nextra + bad_param = "a" * (nextra + 1) + + unexpanded_query = template + "?" + with check_stmt_trace(self, cx, [unexpanded_query]): + cx.execute(unexpanded_query, (bad_param,)) + + expanded_query = f"{template}'{ok_param}'" + with check_stmt_trace(self, cx, [expanded_query]): + cx.execute(unexpanded_query, (ok_param,)) @with_tracebacks(["ZeroDivisionError", "5/0"]) def test_trace_bad_handler(self): From a83a94571eca602562abddf5f13727c8ec2b34b2 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 10 Nov 2021 21:38:08 +0100 Subject: [PATCH 09/14] We can't rely on sqlite_schema being present --- Lib/test/test_sqlite3/test_hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_sqlite3/test_hooks.py b/Lib/test/test_sqlite3/test_hooks.py index aa049d958da26b..65263a8024c803 100644 --- a/Lib/test/test_sqlite3/test_hooks.py +++ b/Lib/test/test_sqlite3/test_hooks.py @@ -327,7 +327,7 @@ def test_trace_too_much_expanded_sql(self): # If the expanded string is too large, we'll fall back to the # unexpanded SQL statement. The resulting string length is limited by # SQLITE_LIMIT_LENGTH. - template = "select * from sqlite_schema where type=" + template = "select 'b' as \"a\" from sqlite_master where \"a\"=" category=sqlite.SQLITE_LIMIT_LENGTH with memory_database() as cx, cx_limit(cx, category=category) as lim: nextra = lim - (len(template) + 2) - 1 From b016c0c268acca6c773583f03e53122fc7d06e1c Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 29 Nov 2021 17:32:56 +0100 Subject: [PATCH 10/14] Adapt tests to new decorator --- Lib/test/test_sqlite3/test_hooks.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_sqlite3/test_hooks.py b/Lib/test/test_sqlite3/test_hooks.py index 82723fc298b98a..2d0af9554956fd 100644 --- a/Lib/test/test_sqlite3/test_hooks.py +++ b/Lib/test/test_sqlite3/test_hooks.py @@ -319,10 +319,10 @@ def test_trace_expanded_sql(self): cx.execute("create table t(t)") cx.executemany("insert into t values(?)", ((v,) for v in range(3))) - @with_tracebacks([ - "DataError", - "Expanded SQL string exceeds the maximum string length" - ], traceback=False) + @with_tracebacks( + sqlite.DataError, + regex="Expanded SQL string exceeds the maximum string length" + ) def test_trace_too_much_expanded_sql(self): # If the expanded string is too large, we'll fall back to the # unexpanded SQL statement. The resulting string length is limited by @@ -342,7 +342,7 @@ def test_trace_too_much_expanded_sql(self): with check_stmt_trace(self, cx, [expanded_query]): cx.execute(unexpanded_query, (ok_param,)) - @with_tracebacks(["ZeroDivisionError", "5/0"]) + @with_tracebacks(ZeroDivisionError, regex="division by zero") def test_trace_bad_handler(self): with memory_database() as cx: cx.set_trace_callback(lambda stmt: 5/0) From 391c698b2239a4b32a9b23c3ed0ee1b0f95ae561 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 29 Nov 2021 22:04:18 +0100 Subject: [PATCH 11/14] Address review --- Modules/_sqlite/connection.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 39ffa47530be7d..66abe984ac8833 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -1128,6 +1128,7 @@ trace_callback(void *ctx, const char *sql) PyErr_SetString(state->DataError, "Expanded SQL string exceeds the maximum string " "length"); + print_or_clear_traceback((callback_context *)ctx); // Fall back to unexpanded sql py_statement = PyUnicode_FromString((const char *)sql); @@ -1140,22 +1141,16 @@ trace_callback(void *ctx, const char *sql) py_statement = PyUnicode_FromString(sql); #endif if (py_statement) { - PyObject *exc, *val, *tb; - PyErr_Fetch(&exc, &val, &tb); - PyObject *callable = ((callback_context *)ctx)->callable; PyObject *ret = PyObject_CallOneArg(callable, py_statement); Py_DECREF(py_statement); Py_XDECREF(ret); - - _PyErr_ChainExceptions(exc, val, tb); } +exit: if (PyErr_Occurred()) { print_or_clear_traceback((callback_context *)ctx); } - -exit: PyGILState_Release(gilstate); #ifdef HAVE_TRACE_V2 return 0; From beff1d5c7329ab5fbce35cac09eab51307803f06 Mon Sep 17 00:00:00 2001 From: Erlend Egeberg Aasland Date: Thu, 3 Mar 2022 09:27:24 +0100 Subject: [PATCH 12/14] Update Lib/test/test_sqlite3/test_hooks.py Co-authored-by: Jelle Zijlstra --- Lib/test/test_sqlite3/test_hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_sqlite3/test_hooks.py b/Lib/test/test_sqlite3/test_hooks.py index 7d5591d6768931..78b8612b4abd30 100644 --- a/Lib/test/test_sqlite3/test_hooks.py +++ b/Lib/test/test_sqlite3/test_hooks.py @@ -34,7 +34,7 @@ def check_stmt_trace(self, cx, expected): try: traced = [] - cx.set_trace_callback(lambda stmt: traced.append(stmt)) + cx.set_trace_callback(traced.append) yield finally: self.assertEqual(traced, expected) From 992013152a4fc13079947a09df070736f6dd3d8a Mon Sep 17 00:00:00 2001 From: Erlend Egeberg Aasland Date: Thu, 3 Mar 2022 09:27:50 +0100 Subject: [PATCH 13/14] PEP 8 Co-authored-by: Jelle Zijlstra --- Lib/test/test_sqlite3/test_hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_sqlite3/test_hooks.py b/Lib/test/test_sqlite3/test_hooks.py index 78b8612b4abd30..d4cc88b0abb017 100644 --- a/Lib/test/test_sqlite3/test_hooks.py +++ b/Lib/test/test_sqlite3/test_hooks.py @@ -329,7 +329,7 @@ def test_trace_too_much_expanded_sql(self): # unexpanded SQL statement. The resulting string length is limited by # SQLITE_LIMIT_LENGTH. template = "select 'b' as \"a\" from sqlite_master where \"a\"=" - category=sqlite.SQLITE_LIMIT_LENGTH + category = sqlite.SQLITE_LIMIT_LENGTH with memory_database() as cx, cx_limit(cx, category=category) as lim: nextra = lim - (len(template) + 2) - 1 ok_param = "a" * nextra From f086604a0f2c12f91877fc9f8e1ca11e57c5577c Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 3 Mar 2022 09:55:41 +0100 Subject: [PATCH 14/14] Address review: clean up namespace --- Lib/test/test_sqlite3/test_hooks.py | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/Lib/test/test_sqlite3/test_hooks.py b/Lib/test/test_sqlite3/test_hooks.py index d4cc88b0abb017..38126b605469af 100644 --- a/Lib/test/test_sqlite3/test_hooks.py +++ b/Lib/test/test_sqlite3/test_hooks.py @@ -30,17 +30,6 @@ from test.test_sqlite3.test_userfunctions import with_tracebacks -@contextlib.contextmanager -def check_stmt_trace(self, cx, expected): - try: - traced = [] - cx.set_trace_callback(traced.append) - yield - finally: - self.assertEqual(traced, expected) - cx.set_trace_callback(None) - - class CollationTests(unittest.TestCase): def test_create_collation_not_string(self): con = sqlite.connect(":memory:") @@ -239,6 +228,16 @@ def bad_progress(): class TraceCallbackTests(unittest.TestCase): + @contextlib.contextmanager + def check_stmt_trace(self, cx, expected): + try: + traced = [] + cx.set_trace_callback(lambda stmt: traced.append(stmt)) + yield + finally: + self.assertEqual(traced, expected) + cx.set_trace_callback(None) + def test_trace_callback_used(self): """ Test that the trace callback is invoked once it is set. @@ -315,7 +314,7 @@ def test_trace_expanded_sql(self): "insert into t values(2)", "COMMIT", ] - with memory_database() as cx, check_stmt_trace(self, cx, expected): + with memory_database() as cx, self.check_stmt_trace(cx, expected): with cx: cx.execute("create table t(t)") cx.executemany("insert into t values(?)", ((v,) for v in range(3))) @@ -336,11 +335,11 @@ def test_trace_too_much_expanded_sql(self): bad_param = "a" * (nextra + 1) unexpanded_query = template + "?" - with check_stmt_trace(self, cx, [unexpanded_query]): + with self.check_stmt_trace(cx, [unexpanded_query]): cx.execute(unexpanded_query, (bad_param,)) expanded_query = f"{template}'{ok_param}'" - with check_stmt_trace(self, cx, [expanded_query]): + with self.check_stmt_trace(cx, [expanded_query]): cx.execute(unexpanded_query, (ok_param,)) @with_tracebacks(ZeroDivisionError, regex="division by zero")