From 615f6254f872c2673127afa795ed709f227b6b0a Mon Sep 17 00:00:00 2001 From: Daira-Emma Hopwood Date: Sun, 7 Jul 2024 02:07:41 +0100 Subject: [PATCH] Add utility methods on `TestState` to dump the contents of a database table, or to run an sqlite3 command. The latter is marked `unsafe`. The name of the table must be a static string containing only `[a-ZA-Z_]` characters. These are only usable if both `#[cfg(test)]` and the "unstable" feature are enabled. Signed-off-by: Daira-Emma Hopwood --- zcash_client_sqlite/src/testing.rs | 61 ++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/zcash_client_sqlite/src/testing.rs b/zcash_client_sqlite/src/testing.rs index 53d2b6c19..d5dbd72e0 100644 --- a/zcash_client_sqlite/src/testing.rs +++ b/zcash_client_sqlite/src/testing.rs @@ -1,3 +1,4 @@ +use std::ffi::OsStr; use std::fmt; use std::num::NonZeroU32; use std::{collections::BTreeMap, convert::Infallible}; @@ -1227,6 +1228,66 @@ impl TestState { Ok(results) } + + /// Dump the schema and contents of the given database table, in + /// sqlite3 ".dump" format. The name of the table must be a static + /// string. This assumes that `sqlite3` is on your path and that it + /// invokes a compatible version of sqlite3. + /// + /// # Panics + /// + /// Panics if `name` contains characters outside `[a-zA-Z_]`. + #[allow(dead_code)] + #[cfg(feature = "unstable")] + pub(crate) fn dump_table(&self, name: &'static str) { + assert!(name.chars().all(|c| c.is_ascii_alphabetic() || c == '_')); + unsafe { + run_sqlite3(self._data_file.path(), &format!(r#".dump "{name}""#)); + } + } + + /// Print the results of an arbitrary sqlite3 command (with "-safe" + /// and "-readonly" flags) to stderr. This is completely insecure and + /// should not be exposed in production. Use of the "-safe" and + /// "-readonly" flags is intended only to limit *accidental* misuse. + /// The output is unfiltered, and control codes could mess up your + /// terminal. This assumes that `sqlite3` is on your path and that it + /// invokes a compatible version of sqlite3. + #[allow(dead_code)] + #[cfg(feature = "unstable")] + pub(crate) unsafe fn run_sqlite3(&self, command: &str) { + run_sqlite3(self._data_file.path(), command) + } +} + +// See the doc comment for `TestState::run_sqlite3` above. +// +// - `db_path` is the path to the database file. +// - `command` may contain newlines. +#[allow(dead_code)] +#[cfg(feature = "unstable")] +unsafe fn run_sqlite3>(db_path: S, command: &str) { + use std::process::Command; + let output = Command::new("sqlite3") + .arg(db_path) + .arg("-safe") + .arg("-readonly") + .arg(command) + .output() + .expect("failed to execute sqlite3 process"); + + eprintln!( + "{}\n------\n{}", + command, + String::from_utf8_lossy(&output.stdout) + ); + if !output.stderr.is_empty() { + eprintln!( + "------ stderr:\n{}", + String::from_utf8_lossy(&output.stderr) + ); + } + eprintln!("------"); } pub(crate) struct TransactionSummary {