Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e32c372
sqlite3_file_control
hashbrowncipher Jan 5, 2025
3f9c1e2
sqlite3_file_control
hashbrowncipher Jan 5, 2025
f9eb73d
sqlite3_file_control
hashbrowncipher Jan 5, 2025
dc60055
sqlite3_file_control
hashbrowncipher Jan 5, 2025
4867672
sqlite3_file_control
hashbrowncipher Jan 5, 2025
4bcb82c
sqlite3_file_control
hashbrowncipher Jan 6, 2025
c42bbd5
change arg
hashbrowncipher Jan 7, 2025
466401c
remove unsupported opcodes
hashbrowncipher Jan 7, 2025
cc3bed9
update docstring
hashbrowncipher Jan 7, 2025
773dc5f
add reference docs
hashbrowncipher Jan 7, 2025
e6d299b
Pull in main
erlend-aasland Feb 10, 2025
bd2a2a9
gh-129928: Rework sqlite3 error helpers
erlend-aasland Feb 10, 2025
b376f84
Merge remote-tracking branch 'erlend-aasland/sqlite/set-error' into s…
hashbrowncipher Feb 10, 2025
b17f832
use set_error_from_code
hashbrowncipher Feb 10, 2025
8bb3c57
fix test
hashbrowncipher Feb 10, 2025
91da077
Merge remote-tracking branch 'origin/main' into sqlite3_file_control
qris Mar 23, 2026
98c2d1d
Merge branch 'main' into sqlite3_file_control
qris Apr 22, 2026
b47206a
pysqlite_connection_file_control: update generated code for 8614f86b7163
qris Apr 22, 2026
9703cf7
test_wal_preservation: add debugging for macOS and iOS failures
qris Apr 22, 2026
de6f341
test_wal_preservation: more debugging
qris Apr 22, 2026
5ddea3b
Merge branch 'main' into sqlite3_file_control
qris Jul 18, 2026
fa1d5d0
test_wal_preservation: fix test on Darwin (MacOS and iPhone)
qris Jul 19, 2026
e7bc25d
Merge remote-tracking branch 'upstream/main' into sqlite3_file_control
qris Oct 29, 2026
251b17c
Merge branch 'main' into sqlite3_file_control
qris Oct 29, 2026
9af79c1
Fix comment about default value of SQLITE_FCNTL_PERSIST_WAL on Darwin
qris Oct 29, 2026
49576d4
More debugging for test failure on iOS
qris Oct 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions Doc/library/sqlite3.rst
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,61 @@ Module constants
.. deprecated-removed:: 3.12 3.14
The :data:`!version` and :data:`!version_info` constants.

.. _sqlite3-fcntl-constants:

.. data:: SQLITE_FCNTL_LOCKSTATE
SQLITE_FCNTL_GET_LOCKPROXYFILE
SQLITE_FCNTL_SET_LOCKPROXYFILE
SQLITE_FCNTL_LAST_ERRNO
SQLITE_FCNTL_SIZE_HINT
SQLITE_FCNTL_CHUNK_SIZE
SQLITE_FCNTL_FILE_POINTER
SQLITE_FCNTL_SYNC_OMITTED
SQLITE_FCNTL_WIN32_AV_RETRY
SQLITE_FCNTL_PERSIST_WAL
SQLITE_FCNTL_OVERWRITE
SQLITE_FCNTL_POWERSAFE_OVERWRITE
SQLITE_FCNTL_PRAGMA
SQLITE_FCNTL_BUSYHANDLER
SQLITE_FCNTL_MMAP_SIZE
SQLITE_FCNTL_TRACE
SQLITE_FCNTL_HAS_MOVED
SQLITE_FCNTL_SYNC
SQLITE_FCNTL_COMMIT_PHASETWO
SQLITE_FCNTL_WIN32_SET_HANDLE
SQLITE_FCNTL_WAL_BLOCK
SQLITE_FCNTL_ZIPVFS
SQLITE_FCNTL_RBU
SQLITE_FCNTL_VFS_POINTER
SQLITE_FCNTL_JOURNAL_POINTER
SQLITE_FCNTL_WIN32_GET_HANDLE
SQLITE_FCNTL_PDB
SQLITE_FCNTL_BEGIN_ATOMIC_WRITE
SQLITE_FCNTL_COMMIT_ATOMIC_WRITE
SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE
SQLITE_FCNTL_LOCK_TIMEOUT
SQLITE_FCNTL_DATA_VERSION
SQLITE_FCNTL_SIZE_LIMIT
SQLITE_FCNTL_CKPT_DONE
SQLITE_FCNTL_RESERVE_BYTES
SQLITE_FCNTL_CKPT_START
SQLITE_FCNTL_EXTERNAL_READER
SQLITE_FCNTL_CKSM_FILE
SQLITE_FCNTL_RESET_CACHE
SQLITE_FCNTL_NULL_IO

These constants are used for the :meth:`Connection.file_control` method.

The availability of these constants varies depending on the version of SQLite
Python was compiled with.

.. versionadded:: 3.14

.. seealso::

https://www.sqlite.org/c3ref/c_fcntl_begin_atomic_write.html
SQLite docs: Standard File Control Opcodes

.. _sqlite3-connection-objects:

Connection objects
Expand Down Expand Up @@ -1316,6 +1371,24 @@ Connection objects

.. versionadded:: 3.12

.. method:: file_control(op, val, /, name="main")

Invoke a file control method on the database.
Opcodes which take non-integer arguments are not supported.

:param int op:
The :ref:`SQLITE_FCNTL_* constant <sqlite3-fcntl-constants>` to invoke.

:param int arg:
The argument to pass to the operation.

:param str name:
the database name to operate against.

:rtype: int

.. versionadded:: 3.14

.. method:: serialize(*, name="main")

Serialize a database into a :class:`bytes` object. For an
Expand Down
45 changes: 45 additions & 0 deletions Lib/test/test_sqlite3/test_dbapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import sqlite3 as sqlite
import subprocess
import sys
import tempfile
import threading
import unittest
import urllib.parse
Expand Down Expand Up @@ -762,6 +763,50 @@ def test_database_keyword(self):
with contextlib.closing(sqlite.connect(database=":memory:")) as cx:
self.assertEqual(type(cx), sqlite.Connection)

def test_wal_preservation(self):
with tempfile.TemporaryDirectory() as dirname:
path = os.path.join(dirname, "db.sqlite")

# Set flag to 1, check that WAL file is not deleted on close:
with contextlib.closing(sqlite.connect(path)) as cx:
cx.file_control(sqlite.SQLITE_FCNTL_PERSIST_WAL, 1)
# Check that it was set successfully:
rc = cx.file_control(sqlite.SQLITE_FCNTL_PERSIST_WAL, -1)
assert rc == 1, f"cx.file_control(SQLITE_FCNTL_PERSIST_WAL) failed to set flag"

cu = cx.cursor()
result = cu.execute("PRAGMA journal_mode = WAL").fetchall()
assert result == [('wal',)], f"journal_mode could not be set to WAL, is {result}"
cu.execute("CREATE TABLE foo (id int)")
cu.execute("INSERT INTO foo (id) VALUES (1)")
self.assertTrue(os.path.exists(path + "-wal"))
self.assertTrue(os.path.exists(path + "-wal"))

# Set flag to 0, check that WAL file is deleted on close:
with contextlib.closing(sqlite.connect(path)) as cx:
# Check that we can read the default value when we didn't set it explicitly:
rc = cx.file_control(sqlite.SQLITE_FCNTL_PERSIST_WAL, -1)

# The default value should be 0, but on MacOS when compiling with the SDK (not a
# custom build of SQLite) it defaults to 1, perhaps customised by Apple?
default_value = (1 if sys.platform == "darwin" else 0)
assert rc == default_value, f"SQLITE_FCNTL_PERSIST_WAL should be {default_value} " \
f"by default, not {rc}, on {sys.platform}"

# Set it to 0 explicitly so that this test passes on Darwin too:
rc = cx.file_control(sqlite.SQLITE_FCNTL_PERSIST_WAL, 0)
assert rc == 0, f"cx.file_control(SQLITE_FCNTL_PERSIST_WAL) failed to set flag"

cu = cx.cursor()
self.assertTrue(os.path.exists(path + "-wal"))
cu.execute("INSERT INTO foo (id) VALUES (2)")
self.assertFalse(os.path.exists(path + "-wal"))

def test_file_control_raises(self):
with memory_database() as cx:
with self.assertRaises(sqlite.InternalError):
cx.file_control(sqlite.SQLITE_FCNTL_PERSIST_WAL, 1)


class CursorTests(unittest.TestCase):
def setUp(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
sqlite Connection objects now expose a method
:meth:`sqlite3.Connection.file_control`, which is a thin wrapper for
`sqlite3_file_control <https://www.sqlite.org/c3ref/file_control.html>`_.
92 changes: 91 additions & 1 deletion Modules/_sqlite/clinic/connection.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading