Skip to content

Commit 6fc9124

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

File tree

7 files changed

+311
-1
lines changed

7 files changed

+311
-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: 76 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: 115 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,117 @@ 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+
2401+
[clinic start generated code]*/
2402+
2403+
static PyObject *
2404+
setconfig_impl(pysqlite_Connection *self, int op, int enable)
2405+
/*[clinic end generated code: output=c60b13e618aff873 input=523e31c0dc82f243]*/
2406+
{
2407+
if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
2408+
return NULL;
2409+
}
2410+
if (!is_int_config(op)) {
2411+
return PyErr_Format(PyExc_ValueError, "unknown config 'op': %d", op);
2412+
}
2413+
2414+
int actual;
2415+
int rc = sqlite3_db_config(self->db, op, enable, &actual);
2416+
if (rc != SQLITE_OK) {
2417+
(void)_pysqlite_seterror(self->state, self->db);
2418+
return NULL;
2419+
}
2420+
if (enable != actual) {
2421+
PyErr_SetString(self->state->OperationalError, "Unable to set config");
2422+
return NULL;
2423+
}
2424+
Py_RETURN_NONE;
2425+
}
2426+
2427+
/*[clinic input]
2428+
_sqlite3.Connection.getconfig as getconfig -> bool
2429+
2430+
op: int
2431+
The configuration verb; one of the sqlite3.SQLITE_DBCONFIG codes.
2432+
/
2433+
2434+
[clinic start generated code]*/
2435+
2436+
static int
2437+
getconfig_impl(pysqlite_Connection *self, int op)
2438+
/*[clinic end generated code: output=25ac05044c7b78a3 input=667d2ef05fff2f61]*/
2439+
{
2440+
if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
2441+
return -1;
2442+
}
2443+
if (!is_int_config(op)) {
2444+
PyErr_Format(PyExc_ValueError, "unknown config 'op': %d", op);
2445+
return -1;
2446+
}
2447+
2448+
int current;
2449+
int rc = sqlite3_db_config(self->db, op, -1, &current);
2450+
if (rc != SQLITE_OK) {
2451+
(void)_pysqlite_seterror(self->state, self->db);
2452+
return -1;
2453+
}
2454+
return current;
2455+
}
23432456

23442457
static PyObject *
23452458
get_autocommit(pysqlite_Connection *self, void *Py_UNUSED(ctx))
@@ -2421,6 +2534,8 @@ static PyMethodDef connection_methods[] = {
24212534
DESERIALIZE_METHODDEF
24222535
CREATE_WINDOW_FUNCTION_METHODDEF
24232536
BLOBOPEN_METHODDEF
2537+
SETCONFIG_METHODDEF
2538+
GETCONFIG_METHODDEF
24242539
{NULL, NULL}
24252540
};
24262541

Modules/_sqlite/module.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,49 @@ add_integer_constants(PyObject *module) {
499499
#if SQLITE_VERSION_NUMBER >= 3008007
500500
ADD_INT(SQLITE_LIMIT_WORKER_THREADS);
501501
#endif
502+
503+
/*
504+
* Database connection configuration options.
505+
* See https://www.sqlite.org/c3ref/c_dbconfig_defensive.html
506+
*/
507+
ADD_INT(SQLITE_DBCONFIG_ENABLE_FKEY);
508+
ADD_INT(SQLITE_DBCONFIG_ENABLE_TRIGGER);
509+
#if SQLITE_VERSION_NUMBER >= 3012002
510+
ADD_INT(SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER);
511+
#endif
512+
#if SQLITE_VERSION_NUMBER >= 3013000
513+
ADD_INT(SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION);
514+
#endif
515+
#if SQLITE_VERSION_NUMBER >= 3016000
516+
ADD_INT(SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE);
517+
#endif
518+
#if SQLITE_VERSION_NUMBER >= 3020000
519+
ADD_INT(SQLITE_DBCONFIG_ENABLE_QPSG);
520+
#endif
521+
#if SQLITE_VERSION_NUMBER >= 3022000
522+
ADD_INT(SQLITE_DBCONFIG_TRIGGER_EQP);
523+
#endif
524+
#if SQLITE_VERSION_NUMBER >= 3024000
525+
ADD_INT(SQLITE_DBCONFIG_RESET_DATABASE);
526+
#endif
527+
#if SQLITE_VERSION_NUMBER >= 3026000
528+
ADD_INT(SQLITE_DBCONFIG_DEFENSIVE);
529+
#endif
530+
#if SQLITE_VERSION_NUMBER >= 3028000
531+
ADD_INT(SQLITE_DBCONFIG_WRITABLE_SCHEMA);
532+
#endif
533+
#if SQLITE_VERSION_NUMBER >= 3029000
534+
ADD_INT(SQLITE_DBCONFIG_DQS_DDL);
535+
ADD_INT(SQLITE_DBCONFIG_DQS_DML);
536+
ADD_INT(SQLITE_DBCONFIG_LEGACY_ALTER_TABLE);
537+
#endif
538+
#if SQLITE_VERSION_NUMBER >= 3030000
539+
ADD_INT(SQLITE_DBCONFIG_ENABLE_VIEW);
540+
#endif
541+
#if SQLITE_VERSION_NUMBER >= 3031000
542+
ADD_INT(SQLITE_DBCONFIG_LEGACY_FILE_FORMAT);
543+
ADD_INT(SQLITE_DBCONFIG_TRUSTED_SCHEMA);
544+
#endif
502545
#undef ADD_INT
503546
return 0;
504547
}

0 commit comments

Comments
 (0)