Skip to content

Commit ba82098

Browse files
gh-103489: Add get/set config methods to sqlite3.Connection
1 parent 2b6e877 commit ba82098

File tree

7 files changed

+366
-1
lines changed

7 files changed

+366
-1
lines changed

Doc/library/sqlite3.rst

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,35 @@ Module constants
573573
package, a third-party library which used to upstream changes to
574574
:mod:`!sqlite3`. Today, it carries no meaning or practical value.
575575

576+
.. _sqlite3-dbconfig-constants
577+
578+
.. data:: SQLITE_DBCONFIG_DEFENSIVE
579+
SQLITE_DBCONFIG_DQS_DDL
580+
SQLITE_DBCONFIG_DQS_DML
581+
SQLITE_DBCONFIG_ENABLE_FKEY
582+
SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER
583+
SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION
584+
SQLITE_DBCONFIG_ENABLE_QPSG
585+
SQLITE_DBCONFIG_ENABLE_TRIGGER
586+
SQLITE_DBCONFIG_ENABLE_VIEW
587+
SQLITE_DBCONFIG_LEGACY_ALTER_TABLE
588+
SQLITE_DBCONFIG_LEGACY_FILE_FORMAT
589+
SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE
590+
SQLITE_DBCONFIG_RESET_DATABASE
591+
SQLITE_DBCONFIG_TRIGGER_EQP
592+
SQLITE_DBCONFIG_TRUSTED_SCHEMA
593+
SQLITE_DBCONFIG_WRITABLE_SCHEMA
594+
595+
These constants are used for the :meth:`Connection.setconfig`
596+
and :meth:`~Connection.getconfig` methods.
597+
598+
..versionadded:: 3.12
599+
600+
..seealso::
601+
602+
https://www.sqlite.org/c3ref/c_dbconfig_defensive.html
603+
SQLite docs: Database Connection Configuration Options
604+
576605

577606
.. _sqlite3-connection-objects:
578607

@@ -1201,6 +1230,30 @@ Connection objects
12011230
.. _SQLite limit category: https://www.sqlite.org/c3ref/c_limit_attached.html
12021231

12031232

1233+
.. method:: getconfig(op, /)
1234+
1235+
Query a boolean connection configuration option.
1236+
1237+
:param int op:
1238+
A :ref:`SQLITE_DBCONFIG code <sqlite3-dbconfig-constants>`.
1239+
1240+
:rtype: bool
1241+
1242+
.. versionadded:: 3.12
1243+
1244+
.. method:: setconfig(op, enable, /)
1245+
1246+
Set a boolean connection configuration option.
1247+
1248+
:param int op:
1249+
A :ref:`SQLITE_DBCONFIG code <sqlite3-dbconfig-constants>`.
1250+
1251+
:param bool enable:
1252+
``True`` if the configuration option should be enabled;
1253+
``False`` if it should be disabled.
1254+
1255+
.. versionadded:: 3.12
1256+
12041257
.. method:: serialize(*, name="main")
12051258

12061259
Serialize a database into a :class:`bytes` object. For an

Doc/whatsnew/3.12.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,11 @@ sqlite3
384384
:ref:`transaction handling <sqlite3-transaction-control-autocommit>`.
385385
(Contributed by Erlend E. Aasland in :gh:`83638`.)
386386

387+
* Add :meth:`~sqlite3.Connection.getconfig` and
388+
:meth:`~sqlite3.Connection.setconfig` to :class:`~sqlite3.Connection`
389+
to make configuration changes to a database connection.
390+
(Contributed by Erlend E. Aasland in :gh:`103489`.)
391+
387392
threading
388393
---------
389394

Lib/test/test_sqlite3/test_dbapi.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,21 @@ def test_connection_bad_reinit(self):
577577
cx.executemany, "insert into t values(?)",
578578
((v,) for v in range(3)))
579579

580+
def test_connection_config(self):
581+
op = sqlite.SQLITE_DBCONFIG_ENABLE_FKEY
582+
with memory_database() as cx:
583+
with self.assertRaisesRegex(ValueError, "unknown"):
584+
cx.getconfig(-1)
585+
586+
old = cx.getconfig(op)
587+
new = not old
588+
589+
cx.setconfig(op, new)
590+
self.assertEqual(cx.getconfig(op), new)
591+
592+
cx.setconfig(op, old)
593+
self.assertEqual(cx.getconfig(op), old)
594+
580595

581596
class UninitialisedConnectionTests(unittest.TestCase):
582597
def setUp(self):
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Add :meth:`~sqlite3.Connection.getconfig` and
2+
:meth:`~sqlite3.Connection.setconfig` to :class:`~sqlite3.Connection` to
3+
make configuration changes to a database connection. Patch by Erlend E.
4+
Aasland.

Modules/_sqlite/clinic/connection.c.h

Lines changed: 133 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/_sqlite/connection.c

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include "prepare_protocol.h"
3131
#include "util.h"
3232

33+
#include <stdbool.h>
34+
3335
#if SQLITE_VERSION_NUMBER >= 3014000
3436
#define HAVE_TRACE_V2
3537
#endif
@@ -2340,6 +2342,115 @@ getlimit_impl(pysqlite_Connection *self, int category)
23402342
return setlimit_impl(self, category, -1);
23412343
}
23422344

2345+
static inline bool
2346+
is_int_config(const int op)
2347+
{
2348+
switch (op) {
2349+
case SQLITE_DBCONFIG_ENABLE_FKEY:
2350+
case SQLITE_DBCONFIG_ENABLE_TRIGGER:
2351+
#if SQLITE_VERSION_NUMBER >= 3012002
2352+
case SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER:
2353+
#endif
2354+
#if SQLITE_VERSION_NUMBER >= 3013000
2355+
case SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION:
2356+
#endif
2357+
#if SQLITE_VERSION_NUMBER >= 3016000
2358+
case SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE:
2359+
#endif
2360+
#if SQLITE_VERSION_NUMBER >= 3020000
2361+
case SQLITE_DBCONFIG_ENABLE_QPSG:
2362+
#endif
2363+
#if SQLITE_VERSION_NUMBER >= 3022000
2364+
case SQLITE_DBCONFIG_TRIGGER_EQP:
2365+
#endif
2366+
#if SQLITE_VERSION_NUMBER >= 3024000
2367+
case SQLITE_DBCONFIG_RESET_DATABASE:
2368+
#endif
2369+
#if SQLITE_VERSION_NUMBER >= 3026000
2370+
case SQLITE_DBCONFIG_DEFENSIVE:
2371+
#endif
2372+
#if SQLITE_VERSION_NUMBER >= 3028000
2373+
case SQLITE_DBCONFIG_WRITABLE_SCHEMA:
2374+
#endif
2375+
#if SQLITE_VERSION_NUMBER >= 3029000
2376+
case SQLITE_DBCONFIG_DQS_DDL:
2377+
case SQLITE_DBCONFIG_DQS_DML:
2378+
case SQLITE_DBCONFIG_LEGACY_ALTER_TABLE:
2379+
#endif
2380+
#if SQLITE_VERSION_NUMBER >= 3030000
2381+
case SQLITE_DBCONFIG_ENABLE_VIEW:
2382+
#endif
2383+
#if SQLITE_VERSION_NUMBER >= 3031000
2384+
case SQLITE_DBCONFIG_LEGACY_FILE_FORMAT:
2385+
case SQLITE_DBCONFIG_TRUSTED_SCHEMA:
2386+
#endif
2387+
return true;
2388+
default:
2389+
return false;
2390+
}
2391+
}
2392+
2393+
/*[clinic input]
2394+
_sqlite3.Connection.setconfig as setconfig
2395+
2396+
op: int
2397+
The configuration verb; one of the sqlite3.SQLITE_DBCONFIG codes.
2398+
enable: bool
2399+
2400+
[clinic start generated code]*/
2401+
2402+
static PyObject *
2403+
setconfig_impl(pysqlite_Connection *self, int op, int enable)
2404+
/*[clinic end generated code: output=c60b13e618aff873 input=8c600361bffbfc02]*/
2405+
{
2406+
if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
2407+
return NULL;
2408+
}
2409+
if (!is_int_config(op)) {
2410+
return PyErr_Format(PyExc_ValueError, "unknown config 'op': %d", op);
2411+
}
2412+
2413+
int actual;
2414+
int rc = sqlite3_db_config(self->db, op, enable, &actual);
2415+
if (rc != SQLITE_OK) {
2416+
(void)_pysqlite_seterror(self->state, self->db);
2417+
return NULL;
2418+
}
2419+
if (enable != actual) {
2420+
PyErr_SetString(self->state->OperationalError, "Unable to set config");
2421+
return NULL;
2422+
}
2423+
Py_RETURN_NONE;
2424+
}
2425+
2426+
/*[clinic input]
2427+
_sqlite3.Connection.getconfig as getconfig -> bool
2428+
2429+
op: int
2430+
The configuration verb; one of the sqlite3.SQLITE_DBCONFIG codes.
2431+
2432+
[clinic start generated code]*/
2433+
2434+
static int
2435+
getconfig_impl(pysqlite_Connection *self, int op)
2436+
/*[clinic end generated code: output=25ac05044c7b78a3 input=91d2415857d51d89]*/
2437+
{
2438+
if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
2439+
return -1;
2440+
}
2441+
if (!is_int_config(op)) {
2442+
PyErr_Format(PyExc_ValueError, "unknown config 'op': %d", op);
2443+
return -1;
2444+
}
2445+
2446+
int current;
2447+
int rc = sqlite3_db_config(self->db, op, -1, &current);
2448+
if (rc != SQLITE_OK) {
2449+
(void)_pysqlite_seterror(self->state, self->db);
2450+
return -1;
2451+
}
2452+
return current;
2453+
}
23432454

23442455
static PyObject *
23452456
get_autocommit(pysqlite_Connection *self, void *Py_UNUSED(ctx))
@@ -2421,6 +2532,8 @@ static PyMethodDef connection_methods[] = {
24212532
DESERIALIZE_METHODDEF
24222533
CREATE_WINDOW_FUNCTION_METHODDEF
24232534
BLOBOPEN_METHODDEF
2535+
SETCONFIG_METHODDEF
2536+
GETCONFIG_METHODDEF
24242537
{NULL, NULL}
24252538
};
24262539

0 commit comments

Comments
 (0)