#!/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 }