Skip to content

Instantly share code, notes, and snippets.

@wmertens
Last active October 6, 2022 09:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wmertens/4df207197074f9cb93f003c1cb723b4c to your computer and use it in GitHub Desktop.
Save wmertens/4df207197074f9cb93f003c1cb723b4c to your computer and use it in GitHub Desktop.
Wrap a command with an SQLite read lock on given files so they can safely be backed up
#!/usr/bin/env bash
#
# This script wraps a command with a read lock on given SQLite databases
# Example: wrap-sqlite-backup.sh *.sqlite3 -- tar cvzf backup.tgz *.sqlite3
#
# Note that it doesn't check for errors, so it really only works in WAL mode.
# Updates that read from ${COPROC[0]} to check errors welcome.
#
# Wout.Mertens@gmail.com
# https://gist.github.com/wmertens/4df207197074f9cb93f003c1cb723b4c
# exit on any error
set -e
DBs=
while [ -n "$1" ] && [ "$1" != "--" ]; do
DBs="$DBs $1"
shift
done
if [ "$1" != "--" ]; then
echo "Usage: $0 sqliteDbFiles... -- command args..." >&2
exit 1
fi
shift
# We run the sqlite CLI in a coprocess and acquire read locks on all DBs
coproc sqlite3
# We can send command on this fd. We're ignoring the output fd.
I=${COPROC[1]}
function close() {
echo ".quit" >&$I
wait
}
# Try to close coproc on script exit
trap close 0
for i in $DBs; do
n=$(basename "$i")
echo "ATTACH DATABASE '$i' AS \"$n\"; PRAGMA \"$n\".wal_checkpoint(FULL);" >&$I
done
echo "BEGIN;" >&$I
for i in $DBs; do
n=$(basename "$i")
# This is enough to acquire a read lock
echo "SELECT 1 FROM \"$n\".sqlite_master;" >&$I
done
# Now wait for the command to complete
# Since this is the last command, it will determine the exit code
"${@}"
# The trap above will close sqlite3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment