Skip to main content

spark_sql_parser/
lib.rs

1//! Parse SQL into [sqlparser] AST.
2//!
3//! Supports a Spark-style subset: single-statement SELECT, CREATE SCHEMA/DATABASE,
4//! and DROP TABLE/VIEW/SCHEMA.
5
6use sqlparser::ast::Statement;
7use sqlparser::dialect::GenericDialect;
8use sqlparser::parser::Parser;
9use thiserror::Error;
10
11/// Error returned when SQL parsing or validation fails.
12#[derive(Error, Debug)]
13#[error("{0}")]
14pub struct ParseError(String);
15
16/// Parse a single SQL statement (SELECT or DDL: CREATE SCHEMA / CREATE DATABASE / DROP TABLE/VIEW/SCHEMA).
17///
18/// Returns the [sqlparser::ast::Statement] on success. Only one statement per call;
19/// run one statement at a time.
20pub fn parse_sql(query: &str) -> Result<Statement, ParseError> {
21    let dialect = GenericDialect {};
22    let stmts = Parser::parse_sql(&dialect, query).map_err(|e| {
23        ParseError(format!(
24            "SQL parse error: {}. Hint: only SELECT and CREATE SCHEMA/DATABASE/DROP TABLE/VIEW/SCHEMA are supported.",
25            e
26        ))
27    })?;
28    if stmts.len() != 1 {
29        return Err(ParseError(format!(
30            "SQL: expected exactly one statement, got {}. Hint: run one statement at a time.",
31            stmts.len()
32        )));
33    }
34    let stmt = stmts.into_iter().next().unwrap();
35    match &stmt {
36        Statement::Query(_) => {}
37        Statement::CreateSchema { .. } | Statement::CreateDatabase { .. } => {}
38        Statement::Drop {
39            object_type:
40                sqlparser::ast::ObjectType::Table
41                | sqlparser::ast::ObjectType::View
42                | sqlparser::ast::ObjectType::Schema,
43            ..
44        } => {}
45        _ => {
46            return Err(ParseError(format!(
47                "SQL: only SELECT, CREATE SCHEMA/DATABASE, and DROP TABLE/VIEW/SCHEMA are supported, got {:?}.",
48                stmt
49            )));
50        }
51    }
52    Ok(stmt)
53}