diff --git a/docs/cli.rst b/docs/cli.rst index 1136a9e..6ac2c2d 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -684,6 +684,8 @@ To remove the FTS tables and triggers you created, use ``disable-fts``:: $ sqlite-utils disable-fts mydb.db documents +.. _cli_vacuum: + Vacuum ====== @@ -691,6 +693,8 @@ You can run VACUUM to optimize your database like so:: $ sqlite-utils vacuum mydb.db +.. _cli_optimize: + Optimize ======== @@ -705,3 +709,18 @@ If you just want to run OPTIMIZE without the VACUUM, use the ``--no-vacuum`` fla # Optimize but skip the VACUUM $ sqlite-utils optimize --no-vacuum mydb.db + +.. _cli_wal: + +WAL mode +======== + +You can enable `Write-Ahead Logging `__ for a database file using the ``enable-wal`` command:: + + $ sqlite-utils enable-wal mydb.db + +You can disable WAL mode using ``disable-wal``:: + + $ sqlite-utils disable-wal mydb.db + +Both of these commands accept one or more database files as arguments. diff --git a/docs/python-api.rst b/docs/python-api.rst index 75ecca0..b9b6013 100644 --- a/docs/python-api.rst +++ b/docs/python-api.rst @@ -1195,6 +1195,8 @@ You can create a unique index by passing ``unique=True``: Use ``if_not_exists=True`` to do nothing if an index with that name already exists. +.. _python_api_vacuum: + Vacuum ====== @@ -1204,6 +1206,31 @@ You can optimize your database by running VACUUM against it like so: Database("my_database.db").vacuum() +.. _python_api_wal: + +WAL mode +======== + +You can enable `Write-Ahead Logging `__ for a database with ``.enable_wal()``: + +.. code-block:: python + + Database("my_database.db").enable_wal() + +You can disable WAL mode using ``.disable_wal()``: + +.. code-block:: python + + Database("my_database.db").disable_wal() + +You can check the current journal mode for a database using the ``journal_mode`` property: + +.. code-block:: python + + journal_mode = Database("my_database.db").journal_mode + +This will usually be ``wal`` or ``delete`` (meaning WAL is disabled), but can have other values - see the `PRAGMA journal_mode `__ documentation. + .. _python_api_suggest_column_types: Suggesting column types diff --git a/sqlite_utils/cli.py b/sqlite_utils/cli.py index ff3f50f..79a8df0 100644 --- a/sqlite_utils/cli.py +++ b/sqlite_utils/cli.py @@ -377,6 +377,32 @@ def disable_fts(path, table): db[table].disable_fts() +@cli.command(name="enable-wal") +@click.argument( + "path", + nargs=-1, + type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False), + required=True, +) +def enable_wal(path): + "Enable WAL for database files" + for path_ in path: + sqlite_utils.Database(path_).enable_wal() + + +@cli.command(name="disable-wal") +@click.argument( + "path", + nargs=-1, + type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False), + required=True, +) +def disable_wal(path): + "Disable WAL for database files" + for path_ in path: + sqlite_utils.Database(path_).disable_wal() + + def insert_upsert_options(fn): for decorator in reversed( ( diff --git a/sqlite_utils/db.py b/sqlite_utils/db.py index 88f5440..a8791c3 100644 --- a/sqlite_utils/db.py +++ b/sqlite_utils/db.py @@ -170,6 +170,18 @@ class Database: ).fetchall() ] + @property + def journal_mode(self): + return self.conn.execute("PRAGMA journal_mode;").fetchone()[0] + + def enable_wal(self): + if self.journal_mode != "wal": + self.conn.execute("PRAGMA journal_mode=wal;") + + def disable_wal(self): + if self.journal_mode != "delete": + self.conn.execute("PRAGMA journal_mode=delete;") + def execute_returning_dicts(self, sql, params=None): cursor = self.conn.execute(sql, params or tuple()) keys = [d[0] for d in cursor.description] diff --git a/tests/test_cli.py b/tests/test_cli.py index 22d42e5..fea89dd 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1230,6 +1230,37 @@ def test_drop_view_error(): assert 'Error: View "t2" does not exist' == result.output.strip() +def test_enable_wal(): + runner = CliRunner() + dbs = ["test.db", "test2.db"] + with runner.isolated_filesystem(): + for dbname in dbs: + db = Database(dbname) + db["t"].create({"pk": int}, pk="pk") + assert db.journal_mode == "delete" + result = runner.invoke(cli.cli, ["enable-wal"] + dbs) + assert 0 == result.exit_code + for dbname in dbs: + db = Database(dbname) + assert db.journal_mode == "wal" + + +def test_disable_wal(): + runner = CliRunner() + dbs = ["test.db", "test2.db"] + with runner.isolated_filesystem(): + for dbname in dbs: + db = Database(dbname) + db["t"].create({"pk": int}, pk="pk") + db.enable_wal() + assert db.journal_mode == "wal" + result = runner.invoke(cli.cli, ["disable-wal"] + dbs) + assert 0 == result.exit_code + for dbname in dbs: + db = Database(dbname) + assert db.journal_mode == "delete" + + @pytest.mark.parametrize( "args,expected", [