mirror of
https://github.com/python/cpython
synced 2024-09-15 23:06:25 +00:00
bpo-45243: Add support for setting/getting sqlite3
connection limits (GH-28463)
This commit is contained in:
parent
e2063d6a1e
commit
b6b38a8226
|
@ -662,6 +662,40 @@ Connection Objects
|
||||||
.. versionadded:: 3.7
|
.. versionadded:: 3.7
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: getlimit(category, /)
|
||||||
|
|
||||||
|
Get a connection run-time limit. *category* 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(category, limit, /)
|
||||||
|
|
||||||
|
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
|
||||||
|
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:
|
.. _sqlite3-cursor-objects:
|
||||||
|
|
||||||
Cursor Objects
|
Cursor Objects
|
||||||
|
|
|
@ -248,6 +248,12 @@ sqlite3
|
||||||
(Contributed by Aviv Palivoda, Daniel Shahaf, and Erlend E. Aasland in
|
(Contributed by Aviv Palivoda, Daniel Shahaf, and Erlend E. Aasland in
|
||||||
:issue:`16379`.)
|
: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`.)
|
||||||
|
|
||||||
|
|
||||||
threading
|
threading
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
|
|
@ -167,11 +167,25 @@ def test_module_constants(self):
|
||||||
"SQLITE_TOOBIG",
|
"SQLITE_TOOBIG",
|
||||||
"SQLITE_TRANSACTION",
|
"SQLITE_TRANSACTION",
|
||||||
"SQLITE_UPDATE",
|
"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",
|
||||||
]
|
]
|
||||||
if sqlite.sqlite_version_info >= (3, 7, 17):
|
if sqlite.sqlite_version_info >= (3, 7, 17):
|
||||||
consts += ["SQLITE_NOTICE", "SQLITE_WARNING"]
|
consts += ["SQLITE_NOTICE", "SQLITE_WARNING"]
|
||||||
if sqlite.sqlite_version_info >= (3, 8, 3):
|
if sqlite.sqlite_version_info >= (3, 8, 3):
|
||||||
consts.append("SQLITE_RECURSIVE")
|
consts.append("SQLITE_RECURSIVE")
|
||||||
|
if sqlite.sqlite_version_info >= (3, 8, 7):
|
||||||
|
consts.append("SQLITE_LIMIT_WORKER_THREADS")
|
||||||
consts += ["PARSE_DECLTYPES", "PARSE_COLNAMES"]
|
consts += ["PARSE_DECLTYPES", "PARSE_COLNAMES"]
|
||||||
for const in consts:
|
for const in consts:
|
||||||
with self.subTest(const=const):
|
with self.subTest(const=const):
|
||||||
|
@ -332,6 +346,28 @@ def test_drop_unused_refs(self):
|
||||||
cu = self.cx.execute(f"select {n}")
|
cu = self.cx.execute(f"select {n}")
|
||||||
self.assertEqual(cu.fetchone()[0], n)
|
self.assertEqual(cu.fetchone()[0], n)
|
||||||
|
|
||||||
|
def test_connection_limits(self):
|
||||||
|
category = sqlite.SQLITE_LIMIT_SQL_LENGTH
|
||||||
|
saved_limit = self.cx.getlimit(category)
|
||||||
|
try:
|
||||||
|
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 saved limit
|
||||||
|
self.cx.setlimit(category, saved_limit)
|
||||||
|
|
||||||
|
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):
|
class UninitialisedConnectionTests(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -767,6 +803,8 @@ def test_check_connection_thread(self):
|
||||||
lambda: self.con.set_trace_callback(None),
|
lambda: self.con.set_trace_callback(None),
|
||||||
lambda: self.con.set_authorizer(None),
|
lambda: self.con.set_authorizer(None),
|
||||||
lambda: self.con.create_collation("foo", 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:
|
for fn in fns:
|
||||||
with self.subTest(fn=fn):
|
with self.subTest(fn=fn):
|
||||||
|
|
|
@ -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.
|
|
@ -750,6 +750,83 @@ exit:
|
||||||
return return_value;
|
return return_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(setlimit__doc__,
|
||||||
|
"setlimit($self, category, limit, /)\n"
|
||||||
|
"--\n"
|
||||||
|
"\n"
|
||||||
|
"Set connection run-time limits.\n"
|
||||||
|
"\n"
|
||||||
|
" category\n"
|
||||||
|
" The limit category to be set.\n"
|
||||||
|
" limit\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 category, int limit);
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
setlimit(pysqlite_Connection *self, PyObject *const *args, Py_ssize_t nargs)
|
||||||
|
{
|
||||||
|
PyObject *return_value = NULL;
|
||||||
|
int category;
|
||||||
|
int limit;
|
||||||
|
|
||||||
|
if (!_PyArg_CheckPositional("setlimit", nargs, 2, 2)) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
category = _PyLong_AsInt(args[0]);
|
||||||
|
if (category == -1 && PyErr_Occurred()) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
limit = _PyLong_AsInt(args[1]);
|
||||||
|
if (limit == -1 && PyErr_Occurred()) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
return_value = setlimit_impl(self, category, limit);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(getlimit__doc__,
|
||||||
|
"getlimit($self, category, /)\n"
|
||||||
|
"--\n"
|
||||||
|
"\n"
|
||||||
|
"Get connection run-time limits.\n"
|
||||||
|
"\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 category);
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
getlimit(pysqlite_Connection *self, PyObject *arg)
|
||||||
|
{
|
||||||
|
PyObject *return_value = NULL;
|
||||||
|
int category;
|
||||||
|
|
||||||
|
category = _PyLong_AsInt(arg);
|
||||||
|
if (category == -1 && PyErr_Occurred()) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
return_value = getlimit_impl(self, category);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef PYSQLITE_CONNECTION_ENABLE_LOAD_EXTENSION_METHODDEF
|
#ifndef PYSQLITE_CONNECTION_ENABLE_LOAD_EXTENSION_METHODDEF
|
||||||
#define PYSQLITE_CONNECTION_ENABLE_LOAD_EXTENSION_METHODDEF
|
#define PYSQLITE_CONNECTION_ENABLE_LOAD_EXTENSION_METHODDEF
|
||||||
#endif /* !defined(PYSQLITE_CONNECTION_ENABLE_LOAD_EXTENSION_METHODDEF) */
|
#endif /* !defined(PYSQLITE_CONNECTION_ENABLE_LOAD_EXTENSION_METHODDEF) */
|
||||||
|
@ -757,4 +834,4 @@ exit:
|
||||||
#ifndef PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF
|
#ifndef PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF
|
||||||
#define PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF
|
#define PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF
|
||||||
#endif /* !defined(PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF) */
|
#endif /* !defined(PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF) */
|
||||||
/*[clinic end generated code: output=7567e5d716309258 input=a9049054013a1b77]*/
|
/*[clinic end generated code: output=0c3901153a3837a5 input=a9049054013a1b77]*/
|
||||||
|
|
|
@ -1896,6 +1896,57 @@ pysqlite_connection_exit_impl(pysqlite_Connection *self, PyObject *exc_type,
|
||||||
Py_RETURN_FALSE;
|
Py_RETURN_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*[clinic input]
|
||||||
|
_sqlite3.Connection.setlimit as setlimit
|
||||||
|
|
||||||
|
category: int
|
||||||
|
The limit category to be set.
|
||||||
|
limit: int
|
||||||
|
The new limit. If the new limit is a negative number, the limit is
|
||||||
|
unchanged.
|
||||||
|
/
|
||||||
|
|
||||||
|
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,
|
||||||
|
the prior value of the limit is returned.
|
||||||
|
[clinic start generated code]*/
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
setlimit_impl(pysqlite_Connection *self, int category, int limit)
|
||||||
|
/*[clinic end generated code: output=0d208213f8d68ccd input=9bd469537e195635]*/
|
||||||
|
{
|
||||||
|
if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
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(old_limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*[clinic input]
|
||||||
|
_sqlite3.Connection.getlimit as getlimit
|
||||||
|
|
||||||
|
category: int
|
||||||
|
The limit category to be queried.
|
||||||
|
/
|
||||||
|
|
||||||
|
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=61e0849fb4fb058f]*/
|
||||||
|
{
|
||||||
|
return setlimit_impl(self, category, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static const char connection_doc[] =
|
static const char connection_doc[] =
|
||||||
PyDoc_STR("SQLite database connection object.");
|
PyDoc_STR("SQLite database connection object.");
|
||||||
|
|
||||||
|
@ -1927,6 +1978,8 @@ static PyMethodDef connection_methods[] = {
|
||||||
PYSQLITE_CONNECTION_SET_AUTHORIZER_METHODDEF
|
PYSQLITE_CONNECTION_SET_AUTHORIZER_METHODDEF
|
||||||
PYSQLITE_CONNECTION_SET_PROGRESS_HANDLER_METHODDEF
|
PYSQLITE_CONNECTION_SET_PROGRESS_HANDLER_METHODDEF
|
||||||
PYSQLITE_CONNECTION_SET_TRACE_CALLBACK_METHODDEF
|
PYSQLITE_CONNECTION_SET_TRACE_CALLBACK_METHODDEF
|
||||||
|
SETLIMIT_METHODDEF
|
||||||
|
GETLIMIT_METHODDEF
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -395,6 +395,21 @@ add_integer_constants(PyObject *module) {
|
||||||
ADD_INT(SQLITE_SAVEPOINT);
|
ADD_INT(SQLITE_SAVEPOINT);
|
||||||
#if SQLITE_VERSION_NUMBER >= 3008003
|
#if SQLITE_VERSION_NUMBER >= 3008003
|
||||||
ADD_INT(SQLITE_RECURSIVE);
|
ADD_INT(SQLITE_RECURSIVE);
|
||||||
|
#endif
|
||||||
|
// Run-time limit categories
|
||||||
|
ADD_INT(SQLITE_LIMIT_LENGTH);
|
||||||
|
ADD_INT(SQLITE_LIMIT_SQL_LENGTH);
|
||||||
|
ADD_INT(SQLITE_LIMIT_COLUMN);
|
||||||
|
ADD_INT(SQLITE_LIMIT_EXPR_DEPTH);
|
||||||
|
ADD_INT(SQLITE_LIMIT_COMPOUND_SELECT);
|
||||||
|
ADD_INT(SQLITE_LIMIT_VDBE_OP);
|
||||||
|
ADD_INT(SQLITE_LIMIT_FUNCTION_ARG);
|
||||||
|
ADD_INT(SQLITE_LIMIT_ATTACHED);
|
||||||
|
ADD_INT(SQLITE_LIMIT_LIKE_PATTERN_LENGTH);
|
||||||
|
ADD_INT(SQLITE_LIMIT_VARIABLE_NUMBER);
|
||||||
|
ADD_INT(SQLITE_LIMIT_TRIGGER_DEPTH);
|
||||||
|
#if SQLITE_VERSION_NUMBER >= 3008007
|
||||||
|
ADD_INT(SQLITE_LIMIT_WORKER_THREADS);
|
||||||
#endif
|
#endif
|
||||||
#undef ADD_INT
|
#undef ADD_INT
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in a new issue