2026-03-13 05:15:02 +01:00

80 lines
2.6 KiB
Bash

#!/usr/bin/env bash
# Copyright © 2012-2026 ScriptFanix
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# A copy of the GNU General Public License v3 is includded in the LICENSE file
# at the root of the project.
openDatabase() {
local \
populate_db
# If the DB file doesn't exist yet, mark it for schema population
[[ -f "$database" ]] || populate_db=1
# Create named FIFOs for bidirectional communication with sqlite3
rm -f "$tempdir"/sqlite.{in,out}
mkfifo "$tempdir"/sqlite.{in,out}
# Start sqlite3 in background:
# - '-newline ::AtOM:SQL:EOL::' makes each row end with our custom
# marker. Allows storing newlines.
# - pipe through sed to convert the custom EOL to NUL bytes
# (for 'read -d $'\0'')
# - sed also removes the trailing newline that follows each NUL
stdbuf -o0 sqlite3 -bail \
-newline $'::AtOM:SQL:EOL::\n' \
"$database" \
< "$tempdir/sqlite.in" \
| stdbuf -o0 \
sed 's/::AtOM:SQL:EOL::/\x0/g;s/\(\x0\)\xA/\1/g' \
> "$tempdir/sqlite.out" &
# Store the PID of the background sqlite3 process so we can wait for it
# to exit
db_pid=$!
# Open FD 3 as the write end (send SQL commands to sqlite3)
exec 3> "$tempdir"/sqlite.in
# Open FD 4 as the read end (receive query results from sqlite3)
exec 4< "$tempdir"/sqlite.out
# FIFOs can be deleted immediately after opening; the fds keep them
# alive
rm "$tempdir"/sqlite.{in,out}
# At debug level > 2, tee all SQL to a debug log file (FD 5 = original FD 3)
if (( debug > 2 ))
then
exec 5>&3
exec 3> >(tee -a "$tempdir/debug.log" >&5)
fi
# If new database, populate schema from the SQL schema file
(( populate_db )) && cat $schema >&3
# Configure sqlite3 output separator to match what we parse with ::AtOM:SQL:Sep::
echo '.separator ::AtOM:SQL:Sep::' >&3
# Enforce referential integrity
echo 'PRAGMA foreign_keys = ON;' >&3
# Allow trigger chains
echo 'PRAGMA recursive_triggers = ON;' >&3
# Keep temp tables in memory
echo 'PRAGMA temp_store = 2;' >&3
# We don't handle concurrent writes, lock the database for exclusive
# access to prevent corruption
echo 'PRAGMA locking_mode = EXCLUSIVE;' >&3
# Drain the initial empty result sqlite3 sends on startup
read -u4 -r -d $'\0'
unset REPLY
checkDatabaseVersion
}