From 39d69ce9ca04d18a8265378926ea5a36ca1fa9db Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 20 Sep 2021 00:37:19 +0200 Subject: [PATCH 01/12] bpo-45243: Add support for setting/getting sqlite3 connection limits --- Lib/sqlite3/test/test_dbapi.py | 30 ++++++++++ Modules/_sqlite/clinic/connection.c.h | 79 ++++++++++++++++++++++++++- Modules/_sqlite/connection.c | 49 +++++++++++++++++ Modules/_sqlite/module.c | 13 +++++ 4 files changed, 170 insertions(+), 1 deletion(-) diff --git a/Lib/sqlite3/test/test_dbapi.py b/Lib/sqlite3/test/test_dbapi.py index 34cadeebfa2627..255aca698be235 100644 --- a/Lib/sqlite3/test/test_dbapi.py +++ b/Lib/sqlite3/test/test_dbapi.py @@ -166,6 +166,19 @@ def test_module_constants(self): "SQLITE_TOOBIG", "SQLITE_TRANSACTION", "SQLITE_UPDATE", + # Run-time limit categories + "SQLITE_LIMIT_LENGTH", + "SQLITE_LIMIT_SQL_LENGTH", + "SQLITE_LIMIT_COLUMN", + "SQLITE_LIMIT_EXPR_DEPTH", + "SQLITE_LIMIT_COMPOUND_SELECT", + "SQLITE_LIMIT_VDBE_OP", + "SQLITE_LIMIT_FUNCTION_ARG", + "SQLITE_LIMIT_ATTACHED", + "SQLITE_LIMIT_LIKE_PATTERN_LENGTH", + "SQLITE_LIMIT_VARIABLE_NUMBER", + "SQLITE_LIMIT_TRIGGER_DEPTH", + "SQLITE_LIMIT_WORKER_THREADS", ] if sqlite.version_info >= (3, 7, 17): consts += ["SQLITE_NOTICE", "SQLITE_WARNING"] @@ -331,6 +344,21 @@ def test_drop_unused_refs(self): cu = self.cx.execute(f"select {n}") self.assertEqual(cu.fetchone()[0], n) + def test_connection_limits(self): + param = sqlite.SQLITE_LIMIT_SQL_LENGTH + setval = 10 + ret1 = self.cx.getlimit(param) + try: + ret2 = self.cx.setlimit(param, setval) + self.assertEqual(ret1, ret2) + self.assertEqual(self.cx.getlimit(param), setval) + msg = "string or blob too big" + self.assertRaisesRegex(sqlite.DataError, msg, + self.cx.execute, "select 1 as '16'") + finally: # restore old limit + print("restoring limit") + self.cx.setlimit(param, ret1) + class UninitialisedConnectionTests(unittest.TestCase): def setUp(self): @@ -766,6 +794,8 @@ def test_check_connection_thread(self): lambda: self.con.set_trace_callback(None), lambda: self.con.set_authorizer(None), lambda: self.con.create_collation("foo", None), + lambda: self.con.setlimit(sqlite.SQLITE_LIMIT_LENGTH, -1), + lambda: self.con.getlimit(sqlite.SQLITE_LIMIT_LENGTH), ] for fn in fns: with self.subTest(fn=fn): diff --git a/Modules/_sqlite/clinic/connection.c.h b/Modules/_sqlite/clinic/connection.c.h index bf5a58d7756c9e..9726b435ed6c54 100644 --- a/Modules/_sqlite/clinic/connection.c.h +++ b/Modules/_sqlite/clinic/connection.c.h @@ -812,6 +812,83 @@ pysqlite_connection_exit(pysqlite_Connection *self, PyObject *const *args, Py_ss return return_value; } +PyDoc_STRVAR(setlimit__doc__, +"setlimit($self, limit, value, /)\n" +"--\n" +"\n" +"Set connection run-time limits. Non-standard.\n" +"\n" +" limit\n" +" The limit category to be set.\n" +" value\n" +" The new limit. If the new limit is a negative number, the limit is\n" +" unchanged.\n" +"\n" +"Attempts to increase a limit above its hard upper bound are silently truncated\n" +"to the hard upper bound. Regardless of whether or not the limit was changed,\n" +"the prior value of the limit is returned."); + +#define SETLIMIT_METHODDEF \ + {"setlimit", (PyCFunction)(void(*)(void))setlimit, METH_FASTCALL, setlimit__doc__}, + +static PyObject * +setlimit_impl(pysqlite_Connection *self, int limit, int value); + +static PyObject * +setlimit(pysqlite_Connection *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + int limit; + int value; + + if (!_PyArg_CheckPositional("setlimit", nargs, 2, 2)) { + goto exit; + } + limit = _PyLong_AsInt(args[0]); + if (limit == -1 && PyErr_Occurred()) { + goto exit; + } + value = _PyLong_AsInt(args[1]); + if (value == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = setlimit_impl(self, limit, value); + +exit: + return return_value; +} + +PyDoc_STRVAR(getlimit__doc__, +"getlimit($self, limit, /)\n" +"--\n" +"\n" +"Get connection run-time limits. Non-standard.\n" +"\n" +" limit\n" +" The limit category to be queried."); + +#define GETLIMIT_METHODDEF \ + {"getlimit", (PyCFunction)getlimit, METH_O, getlimit__doc__}, + +static PyObject * +getlimit_impl(pysqlite_Connection *self, int limit); + +static PyObject * +getlimit(pysqlite_Connection *self, PyObject *arg) +{ + PyObject *return_value = NULL; + int limit; + + limit = _PyLong_AsInt(arg); + if (limit == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = getlimit_impl(self, limit); + +exit: + return return_value; +} + #ifndef PYSQLITE_CONNECTION_ENABLE_LOAD_EXTENSION_METHODDEF #define PYSQLITE_CONNECTION_ENABLE_LOAD_EXTENSION_METHODDEF #endif /* !defined(PYSQLITE_CONNECTION_ENABLE_LOAD_EXTENSION_METHODDEF) */ @@ -819,4 +896,4 @@ pysqlite_connection_exit(pysqlite_Connection *self, PyObject *const *args, Py_ss #ifndef PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF #define PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF #endif /* !defined(PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF) */ -/*[clinic end generated code: output=5b7268875f33c016 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=85f8b7247801658e input=a9049054013a1b77]*/ diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 3b9e42740d4329..f3e0a2387adfd1 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -1892,6 +1892,53 @@ pysqlite_connection_exit_impl(pysqlite_Connection *self, PyObject *exc_type, Py_RETURN_FALSE; } +/*[clinic input] +_sqlite3.Connection.setlimit as setlimit + + limit: int + The limit category to be set. + value: int + The new limit. If the new limit is a negative number, the limit is + unchanged. + / + +Set connection run-time limits. Non-standard. + +Attempts to increase a limit above its hard upper bound are silently truncated +to the hard upper bound. Regardless of whether or not the limit was changed, +the prior value of the limit is returned. +[clinic start generated code]*/ + +static PyObject * +setlimit_impl(pysqlite_Connection *self, int limit, int value) +/*[clinic end generated code: output=18d15e4be8a7a4ec input=e0990e0c90e747ee]*/ +{ + if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { + return NULL; + } + + int previous_value = sqlite3_limit(self->db, limit, value); + return PyLong_FromLong(previous_value); +} + +/*[clinic input] +_sqlite3.Connection.getlimit as getlimit + + limit: int + The limit category to be queried. + / + +Get connection run-time limits. Non-standard. +[clinic start generated code]*/ + +static PyObject * +getlimit_impl(pysqlite_Connection *self, int limit) +/*[clinic end generated code: output=f90c9c8399170536 input=c339eaae3afe367a]*/ +{ + return setlimit_impl(self, limit, -1); +} + + static const char connection_doc[] = PyDoc_STR("SQLite database connection object."); @@ -1923,6 +1970,8 @@ static PyMethodDef connection_methods[] = { PYSQLITE_CONNECTION_SET_AUTHORIZER_METHODDEF PYSQLITE_CONNECTION_SET_PROGRESS_HANDLER_METHODDEF PYSQLITE_CONNECTION_SET_TRACE_CALLBACK_METHODDEF + SETLIMIT_METHODDEF + GETLIMIT_METHODDEF {NULL, NULL} }; diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index 47b1f7a9d0720c..4e635da57215f7 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -392,6 +392,19 @@ static int add_integer_constants(PyObject *module) { #if SQLITE_VERSION_NUMBER >= 3008003 ret += PyModule_AddIntMacro(module, SQLITE_RECURSIVE); #endif + // Run-time limit categories + ret += PyModule_AddIntMacro(module, SQLITE_LIMIT_LENGTH); + ret += PyModule_AddIntMacro(module, SQLITE_LIMIT_SQL_LENGTH); + ret += PyModule_AddIntMacro(module, SQLITE_LIMIT_COLUMN); + ret += PyModule_AddIntMacro(module, SQLITE_LIMIT_EXPR_DEPTH); + ret += PyModule_AddIntMacro(module, SQLITE_LIMIT_COMPOUND_SELECT); + ret += PyModule_AddIntMacro(module, SQLITE_LIMIT_VDBE_OP); + ret += PyModule_AddIntMacro(module, SQLITE_LIMIT_FUNCTION_ARG); + ret += PyModule_AddIntMacro(module, SQLITE_LIMIT_ATTACHED); + ret += PyModule_AddIntMacro(module, SQLITE_LIMIT_LIKE_PATTERN_LENGTH); + ret += PyModule_AddIntMacro(module, SQLITE_LIMIT_VARIABLE_NUMBER); + ret += PyModule_AddIntMacro(module, SQLITE_LIMIT_TRIGGER_DEPTH); + ret += PyModule_AddIntMacro(module, SQLITE_LIMIT_WORKER_THREADS); return ret; } From 6fd1c74f8abaa2657198f15c391a74934ea3c05b Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 20 Sep 2021 01:22:57 +0200 Subject: [PATCH 02/12] Add docs --- Doc/library/sqlite3.rst | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 9c9c7f1eb52573..b7cf3f776662ca 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -624,6 +624,40 @@ Connection Objects .. versionadded:: 3.7 + .. method:: getlimit(limit, /) + + Get a connection run-time limit. *limit* is the limit category to be + queried. + + Example, query the maximum length of an SQL statement:: + + import sqlite3 + con = sqlite3.connect(":memory:") + lim = con.getlimit(sqlite3.SQLITE_LIMIT_SQL_LENGTH) + print(f"SQLITE_LIMIT_SQL_LENGTH={lim}") + + .. versionadded:: 3.11 + + + .. method:: setlimit(limit, value, /) + + Set a connection run-time limit. *limit* is the limit category to be set. + *value* is the new limit. If the new limit is a negative number, the + limit is unchanged. + + Attempts to increase a limit above its hard upper bound are silently + truncated to the hard upper bound. Regardless of whether or not the limit + was changed, the prior value of the limit is returned. + + Example, limit the number of attached databases to 1:: + + import sqlite3 + con = sqlite3.connect(":memory:") + con.setlimit(sqlite3.SQLITE_LIMIT_ATTACHED, 1) + + .. versionadded:: 3.11 + + .. _sqlite3-cursor-objects: Cursor Objects From 3514179f4eeac4681ca7138bdad7fa57484240cb Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 20 Sep 2021 01:25:13 +0200 Subject: [PATCH 03/12] Add NEWS --- .../next/Library/2021-09-20-01-25-09.bpo-45243.0pJf0U.rst | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2021-09-20-01-25-09.bpo-45243.0pJf0U.rst diff --git a/Misc/NEWS.d/next/Library/2021-09-20-01-25-09.bpo-45243.0pJf0U.rst b/Misc/NEWS.d/next/Library/2021-09-20-01-25-09.bpo-45243.0pJf0U.rst new file mode 100644 index 00000000000000..8292e86245f024 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-09-20-01-25-09.bpo-45243.0pJf0U.rst @@ -0,0 +1,4 @@ +Add :meth:`~sqlite3.Connection.setlimit` and +:meth:`~sqlite3.Connection.getlimit` to :class:`sqlite3.Connection` for +setting and getting SQLite limits by connection basis. Patch by Erlend E. +Aasland. From c5abeb0b894ab3643dcb98076fdf73eac5fa5551 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 20 Sep 2021 01:28:36 +0200 Subject: [PATCH 04/12] Add What's New --- Doc/whatsnew/3.11.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index df331ed010f544..157058da56a6ce 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -231,6 +231,12 @@ sqlite3 (Contributed by Aviv Palivoda, Daniel Shahaf, and Erlend E. Aasland in :issue:`16379`.) +* Add :meth:`~sqlite3.Connection.setlimit` and + :meth:`~sqlite3.Connection.getlimit` to :class:`sqlite3.Connection` for + setting and getting SQLite limits by connection basis. + (Contributed by Erlend E. Aasland in :issue:`45243`.) + + time ---- From 76d2c2b0cf688d954bdaebf8f1f397b63f7b8bb7 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 20 Sep 2021 01:33:45 +0200 Subject: [PATCH 05/12] SQLITE_LIMIT_WORKER_THREADS appeared in SQLite 3.8.7 https://sqlite.org/changes.html --- Lib/sqlite3/test/test_dbapi.py | 3 ++- Modules/_sqlite/module.c | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Lib/sqlite3/test/test_dbapi.py b/Lib/sqlite3/test/test_dbapi.py index 255aca698be235..055bb0f72fd264 100644 --- a/Lib/sqlite3/test/test_dbapi.py +++ b/Lib/sqlite3/test/test_dbapi.py @@ -178,12 +178,13 @@ def test_module_constants(self): "SQLITE_LIMIT_LIKE_PATTERN_LENGTH", "SQLITE_LIMIT_VARIABLE_NUMBER", "SQLITE_LIMIT_TRIGGER_DEPTH", - "SQLITE_LIMIT_WORKER_THREADS", ] if sqlite.version_info >= (3, 7, 17): consts += ["SQLITE_NOTICE", "SQLITE_WARNING"] if sqlite.version_info >= (3, 8, 3): consts.append("SQLITE_RECURSIVE") + if sqlite.version_info >= (3, 8, 7): + consts.append("SQLITE_LIMIT_WORKER_THREADS") consts += ["PARSE_DECLTYPES", "PARSE_COLNAMES"] for const in consts: with self.subTest(const=const): diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index 4e635da57215f7..bba31e0373e279 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -404,7 +404,9 @@ static int add_integer_constants(PyObject *module) { ret += PyModule_AddIntMacro(module, SQLITE_LIMIT_LIKE_PATTERN_LENGTH); ret += PyModule_AddIntMacro(module, SQLITE_LIMIT_VARIABLE_NUMBER); ret += PyModule_AddIntMacro(module, SQLITE_LIMIT_TRIGGER_DEPTH); +#if SQLITE_VERSION_NUMBER >= 3008003 ret += PyModule_AddIntMacro(module, SQLITE_LIMIT_WORKER_THREADS); +#endif return ret; } From a51f286cb9a0e12aa4def4666f85a4a99321d96f Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 7 Oct 2021 20:30:35 +0200 Subject: [PATCH 06/12] Remove debug print --- Lib/sqlite3/test/test_dbapi.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/sqlite3/test/test_dbapi.py b/Lib/sqlite3/test/test_dbapi.py index f6a1190d593a87..3750c0f2024570 100644 --- a/Lib/sqlite3/test/test_dbapi.py +++ b/Lib/sqlite3/test/test_dbapi.py @@ -358,7 +358,6 @@ def test_connection_limits(self): self.assertRaisesRegex(sqlite.DataError, msg, self.cx.execute, "select 1 as '16'") finally: # restore old limit - print("restoring limit") self.cx.setlimit(param, ret1) From e093a993244c8d41850b2cace576f61c17b9e882 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 7 Oct 2021 20:36:59 +0200 Subject: [PATCH 07/12] Raise ProgrammingError if limit is out of bounds --- Lib/sqlite3/test/test_dbapi.py | 6 ++++++ Modules/_sqlite/connection.c | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/Lib/sqlite3/test/test_dbapi.py b/Lib/sqlite3/test/test_dbapi.py index 3750c0f2024570..bd309d6135a265 100644 --- a/Lib/sqlite3/test/test_dbapi.py +++ b/Lib/sqlite3/test/test_dbapi.py @@ -360,6 +360,12 @@ def test_connection_limits(self): finally: # restore old limit self.cx.setlimit(param, ret1) + def test_connection_set_bad_limit(self): + self.assertRaisesRegex( + sqlite.ProgrammingError, "'limit' is out of bounds", + self.cx.setlimit, 1111, 0 + ) + class UninitialisedConnectionTests(unittest.TestCase): def setUp(self): diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index b9d3207a0698c3..f1e12aa9a52029 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -1892,6 +1892,10 @@ setlimit_impl(pysqlite_Connection *self, int limit, int value) } int previous_value = sqlite3_limit(self->db, limit, value); + if (previous_value < 0) { + PyErr_SetString(self->ProgrammingError, "'limit' is out of bounds"); + return NULL; + } return PyLong_FromLong(previous_value); } From fced0ce4eb94143024be57e56eb83b3d7e3934a2 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 7 Oct 2021 20:45:55 +0200 Subject: [PATCH 08/12] SQLITE_LIMIT_WORKER_THREADS is defined in SQLite 3.8.7, not 3.8.3 https://sqlite.org/changes.html#version_3_8_7 --- Modules/_sqlite/module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index bba31e0373e279..da23c885a0b0d7 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -404,7 +404,7 @@ static int add_integer_constants(PyObject *module) { ret += PyModule_AddIntMacro(module, SQLITE_LIMIT_LIKE_PATTERN_LENGTH); ret += PyModule_AddIntMacro(module, SQLITE_LIMIT_VARIABLE_NUMBER); ret += PyModule_AddIntMacro(module, SQLITE_LIMIT_TRIGGER_DEPTH); -#if SQLITE_VERSION_NUMBER >= 3008003 +#if SQLITE_VERSION_NUMBER >= 3008007 ret += PyModule_AddIntMacro(module, SQLITE_LIMIT_WORKER_THREADS); #endif return ret; From 4f7c2c03bffbadbe366a37d605fc5d5783b82928 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 7 Oct 2021 20:51:10 +0200 Subject: [PATCH 09/12] SQLite version is found in sqlite3.sqlite_version_info, not sqlite3.version_info --- Lib/sqlite3/test/test_dbapi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/sqlite3/test/test_dbapi.py b/Lib/sqlite3/test/test_dbapi.py index bd309d6135a265..c825f0c08f8ffd 100644 --- a/Lib/sqlite3/test/test_dbapi.py +++ b/Lib/sqlite3/test/test_dbapi.py @@ -184,7 +184,7 @@ def test_module_constants(self): consts += ["SQLITE_NOTICE", "SQLITE_WARNING"] if sqlite.version_info >= (3, 8, 3): consts.append("SQLITE_RECURSIVE") - if sqlite.version_info >= (3, 8, 7): + if sqlite.sqlite_version_info >= (3, 8, 7): consts.append("SQLITE_LIMIT_WORKER_THREADS") consts += ["PARSE_DECLTYPES", "PARSE_COLNAMES"] for const in consts: From 5e2ae979257f6d4324fb4b39a80a909e154a1205 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 8 Oct 2021 09:24:43 +0200 Subject: [PATCH 10/12] Improve argument spec - Use 'category' for limit category - Use 'limit' for the new limit --- Doc/library/sqlite3.rst | 10 ++++---- Lib/sqlite3/test/test_dbapi.py | 18 +++++++------- Modules/_sqlite/clinic/connection.c.h | 36 +++++++++++++-------------- Modules/_sqlite/connection.c | 24 +++++++++--------- 4 files changed, 44 insertions(+), 44 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index a2fdd7fa4ddd7e..60ef4e9c6b56bf 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -624,9 +624,9 @@ Connection Objects .. versionadded:: 3.7 - .. method:: getlimit(limit, /) + .. method:: getlimit(category, /) - Get a connection run-time limit. *limit* is the limit category to be + Get a connection run-time limit. *category* is the limit category to be queried. Example, query the maximum length of an SQL statement:: @@ -639,10 +639,10 @@ Connection Objects .. versionadded:: 3.11 - .. method:: setlimit(limit, value, /) + .. method:: setlimit(category, limit, /) - Set a connection run-time limit. *limit* is the limit category to be set. - *value* is the new limit. If the new limit is a negative number, the + Set a connection run-time limit. *category* is the limit category to be + set. *limit* is the new limit. If the new limit is a negative number, the limit is unchanged. Attempts to increase a limit above its hard upper bound are silently diff --git a/Lib/sqlite3/test/test_dbapi.py b/Lib/sqlite3/test/test_dbapi.py index c825f0c08f8ffd..4210990626453b 100644 --- a/Lib/sqlite3/test/test_dbapi.py +++ b/Lib/sqlite3/test/test_dbapi.py @@ -347,22 +347,22 @@ def test_drop_unused_refs(self): self.assertEqual(cu.fetchone()[0], n) def test_connection_limits(self): - param = sqlite.SQLITE_LIMIT_SQL_LENGTH - setval = 10 - ret1 = self.cx.getlimit(param) + category = sqlite.SQLITE_LIMIT_SQL_LENGTH + saved_limit = self.cx.getlimit(category) try: - ret2 = self.cx.setlimit(param, setval) - self.assertEqual(ret1, ret2) - self.assertEqual(self.cx.getlimit(param), setval) + new_limit = 10 + prev_limit = self.cx.setlimit(category, new_limit) + self.assertEqual(saved_limit, prev_limit) + self.assertEqual(self.cx.getlimit(category), new_limit) msg = "string or blob too big" self.assertRaisesRegex(sqlite.DataError, msg, self.cx.execute, "select 1 as '16'") - finally: # restore old limit - self.cx.setlimit(param, ret1) + finally: # restore saved limit + self.cx.setlimit(category, saved_limit) def test_connection_set_bad_limit(self): self.assertRaisesRegex( - sqlite.ProgrammingError, "'limit' is out of bounds", + sqlite.ProgrammingError, "'category' is out of bounds", self.cx.setlimit, 1111, 0 ) diff --git a/Modules/_sqlite/clinic/connection.c.h b/Modules/_sqlite/clinic/connection.c.h index 9726b435ed6c54..58b3b851c663b6 100644 --- a/Modules/_sqlite/clinic/connection.c.h +++ b/Modules/_sqlite/clinic/connection.c.h @@ -813,14 +813,14 @@ pysqlite_connection_exit(pysqlite_Connection *self, PyObject *const *args, Py_ss } PyDoc_STRVAR(setlimit__doc__, -"setlimit($self, limit, value, /)\n" +"setlimit($self, category, limit, /)\n" "--\n" "\n" "Set connection run-time limits. Non-standard.\n" "\n" -" limit\n" +" category\n" " The limit category to be set.\n" -" value\n" +" limit\n" " The new limit. If the new limit is a negative number, the limit is\n" " unchanged.\n" "\n" @@ -832,58 +832,58 @@ PyDoc_STRVAR(setlimit__doc__, {"setlimit", (PyCFunction)(void(*)(void))setlimit, METH_FASTCALL, setlimit__doc__}, static PyObject * -setlimit_impl(pysqlite_Connection *self, int limit, int value); +setlimit_impl(pysqlite_Connection *self, int category, int limit); static PyObject * setlimit(pysqlite_Connection *self, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; + int category; int limit; - int value; if (!_PyArg_CheckPositional("setlimit", nargs, 2, 2)) { goto exit; } - limit = _PyLong_AsInt(args[0]); - if (limit == -1 && PyErr_Occurred()) { + category = _PyLong_AsInt(args[0]); + if (category == -1 && PyErr_Occurred()) { goto exit; } - value = _PyLong_AsInt(args[1]); - if (value == -1 && PyErr_Occurred()) { + limit = _PyLong_AsInt(args[1]); + if (limit == -1 && PyErr_Occurred()) { goto exit; } - return_value = setlimit_impl(self, limit, value); + return_value = setlimit_impl(self, category, limit); exit: return return_value; } PyDoc_STRVAR(getlimit__doc__, -"getlimit($self, limit, /)\n" +"getlimit($self, category, /)\n" "--\n" "\n" "Get connection run-time limits. Non-standard.\n" "\n" -" limit\n" +" category\n" " The limit category to be queried."); #define GETLIMIT_METHODDEF \ {"getlimit", (PyCFunction)getlimit, METH_O, getlimit__doc__}, static PyObject * -getlimit_impl(pysqlite_Connection *self, int limit); +getlimit_impl(pysqlite_Connection *self, int category); static PyObject * getlimit(pysqlite_Connection *self, PyObject *arg) { PyObject *return_value = NULL; - int limit; + int category; - limit = _PyLong_AsInt(arg); - if (limit == -1 && PyErr_Occurred()) { + category = _PyLong_AsInt(arg); + if (category == -1 && PyErr_Occurred()) { goto exit; } - return_value = getlimit_impl(self, limit); + return_value = getlimit_impl(self, category); exit: return return_value; @@ -896,4 +896,4 @@ getlimit(pysqlite_Connection *self, PyObject *arg) #ifndef PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF #define PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF #endif /* !defined(PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF) */ -/*[clinic end generated code: output=85f8b7247801658e input=a9049054013a1b77]*/ +/*[clinic end generated code: output=ef576e9b40005272 input=a9049054013a1b77]*/ diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index f1e12aa9a52029..e35fb4c06efb8e 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -1869,9 +1869,9 @@ pysqlite_connection_exit_impl(pysqlite_Connection *self, PyObject *exc_type, /*[clinic input] _sqlite3.Connection.setlimit as setlimit - limit: int + category: int The limit category to be set. - value: int + limit: int The new limit. If the new limit is a negative number, the limit is unchanged. / @@ -1884,25 +1884,25 @@ the prior value of the limit is returned. [clinic start generated code]*/ static PyObject * -setlimit_impl(pysqlite_Connection *self, int limit, int value) -/*[clinic end generated code: output=18d15e4be8a7a4ec input=e0990e0c90e747ee]*/ +setlimit_impl(pysqlite_Connection *self, int category, int limit) +/*[clinic end generated code: output=0d208213f8d68ccd input=9d3a8e83f4cc32d7]*/ { if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { return NULL; } - int previous_value = sqlite3_limit(self->db, limit, value); - if (previous_value < 0) { - PyErr_SetString(self->ProgrammingError, "'limit' is out of bounds"); + int old_limit = sqlite3_limit(self->db, category, limit); + if (old_limit < 0) { + PyErr_SetString(self->ProgrammingError, "'category' is out of bounds"); return NULL; } - return PyLong_FromLong(previous_value); + return PyLong_FromLong(old_limit); } /*[clinic input] _sqlite3.Connection.getlimit as getlimit - limit: int + category: int The limit category to be queried. / @@ -1910,10 +1910,10 @@ Get connection run-time limits. Non-standard. [clinic start generated code]*/ static PyObject * -getlimit_impl(pysqlite_Connection *self, int limit) -/*[clinic end generated code: output=f90c9c8399170536 input=c339eaae3afe367a]*/ +getlimit_impl(pysqlite_Connection *self, int category) +/*[clinic end generated code: output=7c3f5d11f24cecb1 input=788e5fb4e2d2b618]*/ { - return setlimit_impl(self, limit, -1); + return setlimit_impl(self, category, -1); } From 1c36d9580d87a06e876dd27a21589cdba6fd952e Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 8 Oct 2021 09:32:46 +0200 Subject: [PATCH 11/12] Drop 'Non-standard' from docstring --- Modules/_sqlite/clinic/connection.c.h | 6 +++--- Modules/_sqlite/connection.c | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Modules/_sqlite/clinic/connection.c.h b/Modules/_sqlite/clinic/connection.c.h index 58b3b851c663b6..6b39744a9a7374 100644 --- a/Modules/_sqlite/clinic/connection.c.h +++ b/Modules/_sqlite/clinic/connection.c.h @@ -816,7 +816,7 @@ PyDoc_STRVAR(setlimit__doc__, "setlimit($self, category, limit, /)\n" "--\n" "\n" -"Set connection run-time limits. Non-standard.\n" +"Set connection run-time limits.\n" "\n" " category\n" " The limit category to be set.\n" @@ -862,7 +862,7 @@ PyDoc_STRVAR(getlimit__doc__, "getlimit($self, category, /)\n" "--\n" "\n" -"Get connection run-time limits. Non-standard.\n" +"Get connection run-time limits.\n" "\n" " category\n" " The limit category to be queried."); @@ -896,4 +896,4 @@ getlimit(pysqlite_Connection *self, PyObject *arg) #ifndef PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF #define PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF #endif /* !defined(PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF) */ -/*[clinic end generated code: output=ef576e9b40005272 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=4d0ff26c99e22885 input=a9049054013a1b77]*/ diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index e35fb4c06efb8e..02d7c09a92bf79 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -1876,7 +1876,7 @@ _sqlite3.Connection.setlimit as setlimit unchanged. / -Set connection run-time limits. Non-standard. +Set connection run-time limits. Attempts to increase a limit above its hard upper bound are silently truncated to the hard upper bound. Regardless of whether or not the limit was changed, @@ -1885,7 +1885,7 @@ the prior value of the limit is returned. static PyObject * setlimit_impl(pysqlite_Connection *self, int category, int limit) -/*[clinic end generated code: output=0d208213f8d68ccd input=9d3a8e83f4cc32d7]*/ +/*[clinic end generated code: output=0d208213f8d68ccd input=9bd469537e195635]*/ { if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { return NULL; @@ -1906,12 +1906,12 @@ _sqlite3.Connection.getlimit as getlimit The limit category to be queried. / -Get connection run-time limits. Non-standard. +Get connection run-time limits. [clinic start generated code]*/ static PyObject * getlimit_impl(pysqlite_Connection *self, int category) -/*[clinic end generated code: output=7c3f5d11f24cecb1 input=788e5fb4e2d2b618]*/ +/*[clinic end generated code: output=7c3f5d11f24cecb1 input=61e0849fb4fb058f]*/ { return setlimit_impl(self, category, -1); } From eb6c23396d051ab27e8443fc7752c0e1096dce71 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 21 Oct 2021 09:42:21 +0200 Subject: [PATCH 12/12] Expand test suite --- Lib/sqlite3/test/test_dbapi.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Lib/sqlite3/test/test_dbapi.py b/Lib/sqlite3/test/test_dbapi.py index 4210990626453b..d020071892b17c 100644 --- a/Lib/sqlite3/test/test_dbapi.py +++ b/Lib/sqlite3/test/test_dbapi.py @@ -360,11 +360,13 @@ def test_connection_limits(self): finally: # restore saved limit self.cx.setlimit(category, saved_limit) - def test_connection_set_bad_limit(self): - self.assertRaisesRegex( - sqlite.ProgrammingError, "'category' is out of bounds", - self.cx.setlimit, 1111, 0 - ) + def test_connection_bad_limit_category(self): + msg = "'category' is out of bounds" + cat = 1111 + self.assertRaisesRegex(sqlite.ProgrammingError, msg, + self.cx.getlimit, cat) + self.assertRaisesRegex(sqlite.ProgrammingError, msg, + self.cx.setlimit, cat, 0) class UninitialisedConnectionTests(unittest.TestCase):