// Copyright (c) 2023-2026 ParadeDB, Inc.
//
// This file is part of ParadeDB - Postgres for Search and Analytics
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
// Copyright (c) 2023-2025 ParadeDB, Inc.
//
// This file is part of ParadeDB - Postgres for Search and Analytics
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#![allow(clippy::mutable_key_type)]
#![allow(clippy::type_complexity)]
mod auto;
mod cli;
mod csv;
mod graph;
mod headless;
mod metrics;
mod runner;
mod sqlscanner;
mod suite;
mod table_helper;
mod tui;
use crate::auto::{setup_server, ServerHandler};
use crate::cli::{Cli, Command};
use crate::runner::SuiteRunner;
use crate::suite::{PgConfigStyle, PgVersion, ServerStyle, Suite, SuiteDefinition};
use anyhow::Context;
use clap::Parser;
use parking_lot::Mutex;
use serde::{Deserialize, Serialize};
use shutdown_hooks::add_shutdown_hook;
use std::path::Path;
use std::time::Duration;
#[derive(Debug, Serialize, Deserialize)]
pub struct MetricsLine {
pub duration: Duration,
pub job_title: String,
pub server_name: String,
pub metrics: serde_json::Map,
}
/// Main entry point using subcommands.
fn main() -> anyhow::Result<()> {
let cli = Cli::parse();
match cli.command {
// When using the "ui" subcommand.
Command::Ui(args) => {
let suite = load_suite(&args.suite_path, args.pgversion).with_context(|| {
format!("Failed to load suite file: {}", args.suite_path.display())
})?;
let suite_runner = SuiteRunner::new(suite, args.paused)?;
tui::run(suite_runner)?;
}
// When using the "headless" subcommand.
Command::Headless(args) => {
let suite = load_suite(&args.suite_path, args.pgversion).with_context(|| {
format!("Failed to load suite file: {}", args.suite_path.display())
})?;
let suite_runner = SuiteRunner::new(suite, false)?;
let mut log_file = args.log_file.clone();
if let Some(path) = log_file.as_ref() {
if path.display().to_string() == "-" {
log_file = None
}
}
headless::run(
suite_runner,
log_file,
args.log_interval_ms,
Some(args.runtime),
)?;
}
// When using the "graph" subcommand.
Command::Graph(graph_args) => {
graph::run(&graph_args)?;
}
Command::Csv(csv_args) => {
csv::run(&csv_args)?;
}
other => panic!("Unrecognized command: {:?}", other),
}
Ok(())
}
/// Loads the Suite (TOML) from the provided path.
fn load_suite>(path: P, pgversion: Option) -> anyhow::Result {
eprintln!("Loading Suite: {}", path.as_ref().display());
let file = std::fs::read_to_string(path.as_ref())?;
let mut definition = toml::from_str::(&file)?;
definition.path = Some(path.as_ref().to_path_buf());
// Override server configurations with the provided pgversion if specified
if let Some(version) = pgversion {
for server in &mut definition.servers {
match &mut server.style {
ServerStyle::Pgrx(_) => {
server.style = ServerStyle::Pgrx(version.clone());
}
ServerStyle::Automatic { pg_config, .. } => {
if let PgConfigStyle::Pgrx(_) = pg_config {
*pg_config = PgConfigStyle::Pgrx(version.clone());
}
}
_ => {
// For other server styles, we don't override
}
}
}
}
eprintln!("{definition:#?}");
static RUNNING_POSTGRES_INSTANCES: Mutex