gh-103489: Add get/set config methods to sqlite3.Connection (#103506)

This commit is contained in:
Erlend E. Aasland 2023-04-26 21:57:48 +02:00 committed by GitHub
parent 222c63fc6b
commit bb8aa7a2b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 329 additions and 1 deletions

View file

@ -1568,6 +1568,85 @@ exit:
return return_value;
}
PyDoc_STRVAR(setconfig__doc__,
"setconfig($self, op, enable=True, /)\n"
"--\n"
"\n"
"Set a boolean connection configuration option.\n"
"\n"
" op\n"
" The configuration verb; one of the sqlite3.SQLITE_DBCONFIG codes.");
#define SETCONFIG_METHODDEF \
{"setconfig", _PyCFunction_CAST(setconfig), METH_FASTCALL, setconfig__doc__},
static PyObject *
setconfig_impl(pysqlite_Connection *self, int op, int enable);
static PyObject *
setconfig(pysqlite_Connection *self, PyObject *const *args, Py_ssize_t nargs)
{
PyObject *return_value = NULL;
int op;
int enable = 1;
if (!_PyArg_CheckPositional("setconfig", nargs, 1, 2)) {
goto exit;
}
op = _PyLong_AsInt(args[0]);
if (op == -1 && PyErr_Occurred()) {
goto exit;
}
if (nargs < 2) {
goto skip_optional;
}
enable = PyObject_IsTrue(args[1]);
if (enable < 0) {
goto exit;
}
skip_optional:
return_value = setconfig_impl(self, op, enable);
exit:
return return_value;
}
PyDoc_STRVAR(getconfig__doc__,
"getconfig($self, op, /)\n"
"--\n"
"\n"
"Query a boolean connection configuration option.\n"
"\n"
" op\n"
" The configuration verb; one of the sqlite3.SQLITE_DBCONFIG codes.");
#define GETCONFIG_METHODDEF \
{"getconfig", (PyCFunction)getconfig, METH_O, getconfig__doc__},
static int
getconfig_impl(pysqlite_Connection *self, int op);
static PyObject *
getconfig(pysqlite_Connection *self, PyObject *arg)
{
PyObject *return_value = NULL;
int op;
int _return_value;
op = _PyLong_AsInt(arg);
if (op == -1 && PyErr_Occurred()) {
goto exit;
}
_return_value = getconfig_impl(self, op);
if ((_return_value == -1) && PyErr_Occurred()) {
goto exit;
}
return_value = PyBool_FromLong((long)_return_value);
exit:
return return_value;
}
#ifndef CREATE_WINDOW_FUNCTION_METHODDEF
#define CREATE_WINDOW_FUNCTION_METHODDEF
#endif /* !defined(CREATE_WINDOW_FUNCTION_METHODDEF) */
@ -1587,4 +1666,4 @@ exit:
#ifndef DESERIALIZE_METHODDEF
#define DESERIALIZE_METHODDEF
#endif /* !defined(DESERIALIZE_METHODDEF) */
/*[clinic end generated code: output=833f0e27cd3a4560 input=a9049054013a1b77]*/
/*[clinic end generated code: output=8b03149c115ee6da input=a9049054013a1b77]*/

View file

@ -30,6 +30,8 @@
#include "prepare_protocol.h"
#include "util.h"
#include <stdbool.h>
#if SQLITE_VERSION_NUMBER >= 3014000
#define HAVE_TRACE_V2
#endif
@ -2343,6 +2345,119 @@ getlimit_impl(pysqlite_Connection *self, int category)
return setlimit_impl(self, category, -1);
}
static inline bool
is_int_config(const int op)
{
switch (op) {
case SQLITE_DBCONFIG_ENABLE_FKEY:
case SQLITE_DBCONFIG_ENABLE_TRIGGER:
#if SQLITE_VERSION_NUMBER >= 3012002
case SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER:
#endif
#if SQLITE_VERSION_NUMBER >= 3013000
case SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION:
#endif
#if SQLITE_VERSION_NUMBER >= 3016000
case SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE:
#endif
#if SQLITE_VERSION_NUMBER >= 3020000
case SQLITE_DBCONFIG_ENABLE_QPSG:
#endif
#if SQLITE_VERSION_NUMBER >= 3022000
case SQLITE_DBCONFIG_TRIGGER_EQP:
#endif
#if SQLITE_VERSION_NUMBER >= 3024000
case SQLITE_DBCONFIG_RESET_DATABASE:
#endif
#if SQLITE_VERSION_NUMBER >= 3026000
case SQLITE_DBCONFIG_DEFENSIVE:
#endif
#if SQLITE_VERSION_NUMBER >= 3028000
case SQLITE_DBCONFIG_WRITABLE_SCHEMA:
#endif
#if SQLITE_VERSION_NUMBER >= 3029000
case SQLITE_DBCONFIG_DQS_DDL:
case SQLITE_DBCONFIG_DQS_DML:
case SQLITE_DBCONFIG_LEGACY_ALTER_TABLE:
#endif
#if SQLITE_VERSION_NUMBER >= 3030000
case SQLITE_DBCONFIG_ENABLE_VIEW:
#endif
#if SQLITE_VERSION_NUMBER >= 3031000
case SQLITE_DBCONFIG_LEGACY_FILE_FORMAT:
case SQLITE_DBCONFIG_TRUSTED_SCHEMA:
#endif
return true;
default:
return false;
}
}
/*[clinic input]
_sqlite3.Connection.setconfig as setconfig
op: int
The configuration verb; one of the sqlite3.SQLITE_DBCONFIG codes.
enable: bool = True
/
Set a boolean connection configuration option.
[clinic start generated code]*/
static PyObject *
setconfig_impl(pysqlite_Connection *self, int op, int enable)
/*[clinic end generated code: output=c60b13e618aff873 input=a10f1539c2d7da6b]*/
{
if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
return NULL;
}
if (!is_int_config(op)) {
return PyErr_Format(PyExc_ValueError, "unknown config 'op': %d", op);
}
int actual;
int rc = sqlite3_db_config(self->db, op, enable, &actual);
if (rc != SQLITE_OK) {
(void)_pysqlite_seterror(self->state, self->db);
return NULL;
}
if (enable != actual) {
PyErr_SetString(self->state->OperationalError, "Unable to set config");
return NULL;
}
Py_RETURN_NONE;
}
/*[clinic input]
_sqlite3.Connection.getconfig as getconfig -> bool
op: int
The configuration verb; one of the sqlite3.SQLITE_DBCONFIG codes.
/
Query a boolean connection configuration option.
[clinic start generated code]*/
static int
getconfig_impl(pysqlite_Connection *self, int op)
/*[clinic end generated code: output=25ac05044c7b78a3 input=b0526d7e432e3f2f]*/
{
if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
return -1;
}
if (!is_int_config(op)) {
PyErr_Format(PyExc_ValueError, "unknown config 'op': %d", op);
return -1;
}
int current;
int rc = sqlite3_db_config(self->db, op, -1, &current);
if (rc != SQLITE_OK) {
(void)_pysqlite_seterror(self->state, self->db);
return -1;
}
return current;
}
static PyObject *
get_autocommit(pysqlite_Connection *self, void *Py_UNUSED(ctx))
@ -2424,6 +2539,8 @@ static PyMethodDef connection_methods[] = {
DESERIALIZE_METHODDEF
CREATE_WINDOW_FUNCTION_METHODDEF
BLOBOPEN_METHODDEF
SETCONFIG_METHODDEF
GETCONFIG_METHODDEF
{NULL, NULL}
};

View file

@ -499,6 +499,49 @@ add_integer_constants(PyObject *module) {
#if SQLITE_VERSION_NUMBER >= 3008007
ADD_INT(SQLITE_LIMIT_WORKER_THREADS);
#endif
/*
* Database connection configuration options.
* See https://www.sqlite.org/c3ref/c_dbconfig_defensive.html
*/
ADD_INT(SQLITE_DBCONFIG_ENABLE_FKEY);
ADD_INT(SQLITE_DBCONFIG_ENABLE_TRIGGER);
#if SQLITE_VERSION_NUMBER >= 3012002
ADD_INT(SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER);
#endif
#if SQLITE_VERSION_NUMBER >= 3013000
ADD_INT(SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION);
#endif
#if SQLITE_VERSION_NUMBER >= 3016000
ADD_INT(SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE);
#endif
#if SQLITE_VERSION_NUMBER >= 3020000
ADD_INT(SQLITE_DBCONFIG_ENABLE_QPSG);
#endif
#if SQLITE_VERSION_NUMBER >= 3022000
ADD_INT(SQLITE_DBCONFIG_TRIGGER_EQP);
#endif
#if SQLITE_VERSION_NUMBER >= 3024000
ADD_INT(SQLITE_DBCONFIG_RESET_DATABASE);
#endif
#if SQLITE_VERSION_NUMBER >= 3026000
ADD_INT(SQLITE_DBCONFIG_DEFENSIVE);
#endif
#if SQLITE_VERSION_NUMBER >= 3028000
ADD_INT(SQLITE_DBCONFIG_WRITABLE_SCHEMA);
#endif
#if SQLITE_VERSION_NUMBER >= 3029000
ADD_INT(SQLITE_DBCONFIG_DQS_DDL);
ADD_INT(SQLITE_DBCONFIG_DQS_DML);
ADD_INT(SQLITE_DBCONFIG_LEGACY_ALTER_TABLE);
#endif
#if SQLITE_VERSION_NUMBER >= 3030000
ADD_INT(SQLITE_DBCONFIG_ENABLE_VIEW);
#endif
#if SQLITE_VERSION_NUMBER >= 3031000
ADD_INT(SQLITE_DBCONFIG_LEGACY_FILE_FORMAT);
ADD_INT(SQLITE_DBCONFIG_TRUSTED_SCHEMA);
#endif
#undef ADD_INT
return 0;
}