diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index 4a6f090..6c9868c 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -1,5 +1,6 @@ //! CLI subcommands. pub mod parse; +pub mod run; pub mod sql_test; pub mod transpile; diff --git a/src/cmd/run.rs b/src/cmd/run.rs new file mode 100644 index 0000000..7af351b --- /dev/null +++ b/src/cmd/run.rs @@ -0,0 +1,45 @@ +use std::path::PathBuf; + +use clap::Parser; +use tracing::instrument; + +use crate::{ + ast::parse_sql, drivers, errors::Result, infer::InferTypes, known_files::KnownFiles, + scope::Scope, +}; + +/// Run an SQL file using the specified database. +#[derive(Debug, Parser)] +pub struct RunOpt { + /// An SQL file to transpile. + sql_path: PathBuf, + + /// A database locator to run tests against. (For now, this must be a + /// an actual database locator, and not the name of a dialect.) + #[clap(long, visible_alias = "db", default_value = "sqlite3::memory:")] + database: String, +} + +/// Run our SQL test suite. +/// +/// TODO: Deduplicate this with the transpile command. +#[instrument(skip(opt))] +pub async fn cmd_run(files: &mut KnownFiles, opt: &RunOpt) -> Result<()> { + // Get a database driver for our target. + let locator = opt.database.parse::>()?; + let mut driver = locator.driver().await?; + + // Parse our SQL. + let file_id = files.add(&opt.sql_path)?; + let mut ast = parse_sql(files, file_id)?; + + // Run the type checker, but do not fail on errors. + let scope = Scope::root(); + if let Err(err) = ast.infer_types(&scope) { + err.emit(files); + eprintln!("\nType checking failed. Manual fixes will probably be required!"); + } + + // Run our AST. + driver.execute_ast(&ast).await +} diff --git a/src/main.rs b/src/main.rs index 8217f78..b016d8c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,6 +18,7 @@ mod util; use cmd::{ parse::{cmd_parse, ParseOpt}, + run::{cmd_run, RunOpt}, sql_test::{cmd_sql_test, SqlTestOpt}, transpile::{cmd_transpile, TranspileOpt}, }; @@ -28,6 +29,8 @@ use tracing::info_span; enum Opt { /// Parse SQL from a CSV file containing `id` and `query` columns. Parse(ParseOpt), + /// Run an SQL file. + Run(RunOpt), /// Run SQL tests from a directory. SqlTest(SqlTestOpt), /// Transpile BigQuery SQL to another dialect. @@ -44,6 +47,7 @@ async fn main() { let mut files = KnownFiles::new(); let result = match opt { Opt::Parse(parse_opt) => cmd_parse(&mut files, &parse_opt), + Opt::Run(run_opt) => cmd_run(&mut files, &run_opt).await, Opt::SqlTest(sql_test_opt) => cmd_sql_test(&mut files, &sql_test_opt).await, Opt::Transpile(transpile_opt) => cmd_transpile(&mut files, &transpile_opt).await, };