From f4405fe90547503d8debd6129050a1f58583c5d6 Mon Sep 17 00:00:00 2001 From: George Stagg Date: Fri, 20 Mar 2026 13:48:18 +0000 Subject: [PATCH 1/2] Allow for other readers in cli --- src/cli.rs | 136 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 90 insertions(+), 46 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 23c173c9..ec706a55 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -5,14 +5,11 @@ Provides commands for executing ggsql queries with various data sources and outp */ use clap::{Parser, Subcommand}; +use ggsql::reader::{Reader, Spec}; +use ggsql::validate::validate; use ggsql::{parser, VERSION}; use std::path::PathBuf; -#[cfg(feature = "duckdb")] -use ggsql::reader::{DuckDBReader, Reader}; -#[cfg(feature = "duckdb")] -use ggsql::validate::validate; - #[cfg(feature = "vegalite")] use ggsql::writer::{VegaLiteWriter, Writer}; @@ -153,23 +150,75 @@ fn cmd_exec(query: String, reader: String, writer: String, output: Option r, + Err(e) => { + eprintln!("Failed to create reader: {}", e); + std::process::exit(1); + } + }; + exec_with_reader(&query, &r, &writer, output, verbose); + } + #[cfg(not(feature = "duckdb"))] + { + eprintln!("DuckDB reader not compiled in. Rebuild with --features duckdb"); + std::process::exit(1); + } + } else if reader.starts_with("polars://") { + #[cfg(feature = "polars-sql")] + { + let r = match ggsql::reader::PolarsReader::from_connection_string(&reader) { + Ok(r) => r, + Err(e) => { + eprintln!("Failed to create reader: {}", e); + std::process::exit(1); + } + }; + exec_with_reader(&query, &r, &writer, output, verbose); + } + #[cfg(not(feature = "polars-sql"))] + { + eprintln!("Polars reader not compiled in. Rebuild with --features polars-sql"); + std::process::exit(1); + } + } else if reader.starts_with("sqlite://") { + #[cfg(feature = "sqlite")] + { + let r = match ggsql::reader::SqliteReader::from_connection_string(&reader) { + Ok(r) => r, + Err(e) => { + eprintln!("Failed to create reader: {}", e); + std::process::exit(1); + } + }; + exec_with_reader(&query, &r, &writer, output, verbose); + } + #[cfg(not(feature = "sqlite"))] + { + eprintln!("SQLite reader not compiled in. Rebuild with --features sqlite"); + std::process::exit(1); + } + } else if reader.starts_with("postgres://") || reader.starts_with("postgresql://") { + eprintln!("PostgreSQL reader is not yet implemented"); std::process::exit(1); - } - - let db_reader = DuckDBReader::from_connection_string(&reader); - if let Err(e) = db_reader { - eprintln!("Failed to create DuckDB reader: {}", e); + } else { + eprintln!("Unsupported connection string: {}", reader); std::process::exit(1); } - let db_reader = db_reader.unwrap(); +} +fn exec_with_reader( + query: &str, + reader: &R, + writer: &str, + output: Option, + verbose: bool, +) { // Use validate() to check if query has visualization - let validated = match validate(&query) { + let validated = match validate(query) { Ok(v) => v, Err(e) => { eprintln!("Failed to validate query: {}", e); @@ -181,12 +230,12 @@ fn cmd_exec(query: String, reader: String, writer: String, output: Option s, Err(e) => { eprintln!("Failed to execute query: {}", e); @@ -194,6 +243,10 @@ fn cmd_exec(query: String, reader: String, writer: String, output: Option, verbose: bool) { if verbose { let metadata = spec.metadata(); eprintln!("\nQuery executed:"); @@ -292,41 +345,32 @@ fn cmd_parse(query: String, format: String) { } fn cmd_validate(query: String, _reader: Option) { - #[cfg(feature = "duckdb")] - { - match validate(&query) { - Ok(validated) if validated.valid() => { - println!("✓ Query syntax is valid"); + match validate(&query) { + Ok(validated) if validated.valid() => { + println!("✓ Query syntax is valid"); + } + Ok(validated) => { + println!("✗ Validation errors:"); + for err in validated.errors() { + println!(" - {}", err.message); } - Ok(validated) => { - println!("✗ Validation errors:"); - for err in validated.errors() { - println!(" - {}", err.message); + if !validated.warnings().is_empty() { + println!("\nWarnings:"); + for warning in validated.warnings() { + println!(" - {}", warning.message); } - if !validated.warnings().is_empty() { - println!("\nWarnings:"); - for warning in validated.warnings() { - println!(" - {}", warning.message); - } - } - std::process::exit(1); - } - Err(e) => { - eprintln!("Error during validation: {}", e); - std::process::exit(1); } + std::process::exit(1); + } + Err(e) => { + eprintln!("Error during validation: {}", e); + std::process::exit(1); } - } - - #[cfg(not(feature = "duckdb"))] - { - eprintln!("Validation requires the duckdb feature"); - std::process::exit(1); } } // Prints a CSV-like output to stdout with aligned columns -fn print_table_fallback(query: &str, reader: &DuckDBReader, max_rows: usize) { +fn print_table_fallback(query: &str, reader: &R, max_rows: usize) { let source_tree = match parser::SourceTree::new(query) { Ok(st) => st, Err(e) => { From 7aed38ef86dd06d54984fd15b7f880fb07cc4033 Mon Sep 17 00:00:00 2001 From: George Stagg Date: Fri, 20 Mar 2026 14:04:18 +0000 Subject: [PATCH 2/2] Update doc strings for help output --- src/cli.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index ec706a55..b6974ad3 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -29,11 +29,11 @@ pub enum Commands { /// The ggsql query to execute query: String, - /// Data source connection string + /// Data source connection string (duckdb://, sqlite://, polars://) #[arg(long, default_value = "duckdb://memory")] reader: String, - /// Output format + /// Output format (vegalite) #[arg(long, default_value = "vegalite")] writer: String, @@ -51,11 +51,11 @@ pub enum Commands { /// Path to .sql file containing ggsql query file: PathBuf, - /// Data source connection string + /// Data source connection string (duckdb://, sqlite://, polars://) #[arg(long, default_value = "duckdb://memory")] reader: String, - /// Output format + /// Output format (vegalite) #[arg(long, default_value = "vegalite")] writer: String, @@ -83,7 +83,7 @@ pub enum Commands { /// The ggsql query to validate query: String, - /// Data source connection string (needed for column validation) + /// Data source connection string for column validation (duckdb://, sqlite://, polars://) #[arg(long)] reader: Option, },