Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 14 additions & 15 deletions crates/sqlquack-syntax-coverage/src/cli/_helpers/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@ use std::path::{Path, PathBuf};
use std::process::{Command, ExitCode};
use std::time::Duration;

use clap::ValueEnum;
use serde_json::{Value, json};

use crate::check::main::check_coverage::check_coverage;
use crate::check::models::{
CheckResult, FormResult, LocalFailure, LocalVerdict, ObligationResult, Reason,
};
use crate::cli::_helpers::local::{
frozen_catalogue, parser_tree, postgres_verdict, snowflake_verdict,
};
use crate::cli::_helpers::local::{frozen_catalogue, local_verdict, parser_tree};
use crate::cli::_helpers::output::{print, say};
use crate::cli::_helpers::worker::Worker;
use crate::cli::models::{Common, TranslationArguments};
Expand Down Expand Up @@ -53,10 +52,12 @@ const PARSE_MEMORY_KIB: u64 = 4 * 1024 * 1024;

/// The worker command: this binary, its memory bounded by the shell before it starts.
fn worker_command(binary: PathBuf, local_parser: LocalParser) -> Box<dyn Fn() -> Command> {
let worker: &str = match local_parser {
LocalParser::Postgres => "parse-worker --postgres",
LocalParser::Snowflake | LocalParser::None => "parse-worker",
};
let worker: String = format!(
"parse-worker --parser {}",
local_parser
.to_possible_value()
.map_or_else(String::new, |value| value.get_name().to_owned())
);
Box::new(move || {
let mut command = Command::new("sh");
command
Expand Down Expand Up @@ -101,9 +102,10 @@ fn checked(coverage: &SyntaxCoverage) -> Result<CheckResult, ExitCode> {
worker_command(binary, local_parser),
PARSE_BOUND,
));
let check: CheckResult = check_coverage(coverage, tree.as_deref(), &|sql| match local_parser {
LocalParser::Snowflake | LocalParser::Postgres => worker.borrow_mut().verdict(sql),
LocalParser::None => LocalVerdict::Failed {
let parses: bool = local_verdict(local_parser).is_some();
let check: CheckResult = check_coverage(coverage, tree.as_deref(), &|sql| match parses {
true => worker.borrow_mut().verdict(sql),
false => LocalVerdict::Failed {
failure: LocalFailure::NoParser,
},
});
Expand Down Expand Up @@ -521,11 +523,8 @@ pub(crate) fn diff(
}

/// Reads one JSON string of SQL per line and prints the local verdict on each, flushed.
pub(crate) fn parse_worker(postgres: bool) -> Result<ExitCode, ExitCode> {
let verdict_of: fn(&str) -> LocalVerdict = match postgres {
true => postgres_verdict,
false => snowflake_verdict,
};
pub(crate) fn parse_worker(parser: LocalParser) -> Result<ExitCode, ExitCode> {
let verdict_of: fn(&str) -> LocalVerdict = local_verdict(parser).ok_or(ExitCode::FAILURE)?;
let stdout = std::io::stdout();
for line in std::io::stdin().lock().lines() {
let line: String = line.map_err(|_| ExitCode::FAILURE)?;
Expand Down
25 changes: 20 additions & 5 deletions crates/sqlquack-syntax-coverage/src/cli/_helpers/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

use std::process::Command;

use sqlquack_syntax::{CallMode, Dialect, Expression, ExpressionWalk, ParseOptions};
use sqlquack_syntax::{CallMode, Dialect, Expression, ExpressionWalk, ParseOptions, Parsed};

use crate::check::models::{LocalFailure, LocalVerdict};
use crate::formats::constants::SNOWFLAKE_DIALECT;
use crate::formats::main::content_sha256::content_sha256;
use crate::formats::main::current_dialect::current_dialect;
use crate::formats::models::LocalParser;

/// The code prefix of every complexity-guard error, which is never a syntax verdict.
const GUARD_PREFIX: &str = "E_GUARD_";
Expand All @@ -28,7 +29,7 @@ fn accepted(statements: &[Expression], gapped: bool) -> LocalVerdict {
}

/// SQLQuack's Snowflake verdict; a Command or Raw node anywhere makes the parse opaque.
pub(crate) fn snowflake_verdict(sql: &str) -> LocalVerdict {
fn snowflake_verdict(sql: &str) -> LocalVerdict {
match sqlquack_syntax::parse_statements_with(
sql,
Dialect::Snowflake,
Expand All @@ -45,14 +46,28 @@ pub(crate) fn snowflake_verdict(sql: &str) -> LocalVerdict {
}
}

/// SQLQuack's strict PostgreSQL verdict: a gap makes it opaque, and any refusal rejects it.
pub(crate) fn postgres_verdict(sql: &str) -> LocalVerdict {
match sqlquack_syntax_postgres::parse_strict(sql) {
/// A native dialect parser's strict verdict: a gap makes it opaque, and any refusal rejects it.
fn native_verdict<E>(parsed: Result<Parsed, E>) -> LocalVerdict {
match parsed {
Ok(parsed) => accepted(&parsed.statements, !parsed.gaps.is_empty()),
Err(_) => LocalVerdict::Rejected,
}
}

/// SQLQuack's strict PostgreSQL verdict.
pub(crate) fn postgres_verdict(sql: &str) -> LocalVerdict {
native_verdict(sqlquack_syntax_postgres::parse_strict(sql))
}

/// The verdict of each dialect's local parser, or none when the dialect has no parser yet.
pub(crate) fn local_verdict(parser: LocalParser) -> Option<fn(&str) -> LocalVerdict> {
match parser {
LocalParser::Snowflake => Some(snowflake_verdict),
LocalParser::Postgres => Some(postgres_verdict),
LocalParser::None => None,
}
}

/// Runs git; its exit code, and its trimmed stdout, or why it could not run.
pub(crate) fn git(arguments: &[&str]) -> Result<(i32, String), String> {
match Command::new("git").args(arguments).output() {
Expand Down
2 changes: 1 addition & 1 deletion crates/sqlquack-syntax-coverage/src/cli/main/run_cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub fn run_cli() -> ExitCode {
&common,
),
Command::Diff { base, head, common } => diff(&base, head.as_deref(), &common),
Command::ParseWorker { postgres } => parse_worker(postgres),
Command::ParseWorker { parser } => parse_worker(parser),
};
result.unwrap_or_else(|code| code)
}
7 changes: 4 additions & 3 deletions crates/sqlquack-syntax-coverage/src/cli/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::path::PathBuf;
use clap::{Args, Parser, Subcommand};

use crate::formats::constants::COVERAGE_ROOT;
use crate::formats::models::LocalParser;

#[derive(Debug, Parser)]
#[command(
Expand Down Expand Up @@ -103,9 +104,9 @@ pub enum Command {
/// Internal: one verdict line per SQL line on stdin, for the isolated local parse.
#[command(hide = true)]
ParseWorker {
/// Parse with the PostgreSQL parser instead of the Snowflake one.
#[arg(long)]
postgres: bool,
/// The dialect parser that gives the verdicts.
#[arg(long, value_enum)]
parser: LocalParser,
},
/// What conversion makes of every case Snowflake parsed, by form and family (decision D107).
Translation {
Expand Down
4 changes: 2 additions & 2 deletions crates/sqlquack-syntax-coverage/src/formats/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -634,8 +634,8 @@ pub struct Dialect {
pub syntax_codes: Vec<String>,
}

/// The parser the checker compares receipts with.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
/// The parser the checker compares receipts with; `local_verdict` maps each to its verdict.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize, clap::ValueEnum)]
#[serde(rename_all = "snake_case")]
pub enum LocalParser {
/// `sqlquack_syntax` in the Snowflake dialect, through the isolated worker.
Expand Down
2 changes: 1 addition & 1 deletion crates/sqlquack-syntax-postgres/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ pub use crate::parse::main::format_type::format_type;
pub use crate::parse::main::parse::parse;
pub use crate::parse::main::parse_strict::parse_strict;
pub use crate::parse::main::parse_type::parse_type;
pub use crate::parse::models::{Gap, Parsed};
pub use sqlquack_syntax::{Gap, Parsed};
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use sqlquack_syntax::expressions::names::models::Identifier;
use crate::keywords::models::KeywordCategory;
use crate::parse::constants::{EXPRESSION_KEYWORDS, QUERY_STARTS};
use crate::parse::errors::ParseError;
use crate::parse::models::Gap;
use crate::scan::models::{Token, TokenKind};
use sqlquack_syntax::Gap;

pub(crate) type Parsing<T> = Result<T, ParseError>;

Expand Down
2 changes: 1 addition & 1 deletion crates/sqlquack-syntax-postgres/src/parse/main/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use sqlquack_syntax::Expression;
use crate::parse::_helpers::tree::cursor::Parser;
use crate::parse::_helpers::tree::nodes::command;
use crate::parse::errors::ParseError;
use crate::parse::models::Parsed;
use crate::scan::main::scan::scan;
use sqlquack_syntax::Parsed;

/// Every statement of `sql`; a statement holding a construct the shared tree lacks is a `Command`.
pub fn parse(sql: &str) -> Result<Parsed, ParseError> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

use crate::parse::errors::StrictFailure;
use crate::parse::main::parse::parse;
use crate::parse::models::Parsed;
use crate::scan::main::uncovered::uncovered;
use sqlquack_syntax::Parsed;

/// `parse`'s statements, refused on any parse error, whatever its SQLSTATE, or uncovered byte.
pub fn parse_strict(sql: &str) -> Result<Parsed, StrictFailure> {
Expand Down
1 change: 0 additions & 1 deletion crates/sqlquack-syntax-postgres/src/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ pub(crate) mod _helpers;
pub(crate) mod constants;
pub mod errors;
pub mod main;
pub mod models;

#[cfg(test)]
mod tests;
20 changes: 0 additions & 20 deletions crates/sqlquack-syntax-postgres/src/parse/models.rs

This file was deleted.

2 changes: 2 additions & 0 deletions crates/sqlquack-syntax/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ pub use crate::lossless::models::TokenKind;
pub use crate::modes::errors::StrictFailure;
pub use crate::modes::main::parse_strict::parse_strict;
pub use crate::modes::main::parse_tolerant::parse_tolerant;
pub use crate::modes::models::Gap;
pub use crate::modes::models::Parsed;
pub use crate::modes::models::TolerantParse;
pub use crate::modes::models::TolerantStatement;
pub use crate::parser::main::parse_statements::parse_statements;
Expand Down
17 changes: 17 additions & 0 deletions crates/sqlquack-syntax/src/modes/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,20 @@ pub struct TolerantParse {
/// Each statement between top-level semicolons, in order.
pub statements: Vec<TolerantStatement>,
}

/// A construct a native parser's tree cannot hold: its statement became a `Command` holding its text.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Gap {
/// A stable name for the construct, such as `tablesample` or `merge`.
pub construct: &'static str,
/// Byte range in the input where it starts and where parsing it ended.
pub start: usize,
pub end: usize,
}

/// Every statement a native dialect parser read, in order, and the gaps behind any `Command`.
#[derive(Clone, Debug, PartialEq)]
pub struct Parsed {
pub statements: Vec<Expression>,
pub gaps: Vec<Gap>,
}
Loading