#![no_std]
#![forbid(unsafe_code)]
extern crate alloc;
use alloc::vec::Vec;
use lexer::Token;
use parser::Parser;
mod alter;
mod create;
mod data_type;
mod delete;
mod drop;
mod expression;
mod identifier;
mod insert_replace;
mod issue;
mod keywords;
mod lexer;
mod parser;
mod qualified_name;
mod rename;
mod select;
mod span;
mod sstring;
mod statement;
mod truncate;
mod update;
pub use data_type::{DataType, DataTypeProperty, Type};
pub use identifier::Identifier;
pub use issue::{Issue, Level};
pub use qualified_name::QualifiedName;
pub use span::{OptSpanned, Span, Spanned};
pub use sstring::SString;
pub use statement::{Statement, Union, UnionType, UnionWith};
pub use alter::{
AlterSpecification, AlterTable, ForeignKeyOn, ForeignKeyOnAction, ForeignKeyOnType, IndexCol,
IndexOption, IndexType,
};
pub use create::{
CreateAlgorithm, CreateDefinition, CreateFunction, CreateOption, CreateTable, CreateTrigger,
CreateView, TableOption,
};
pub use delete::{Delete, DeleteFlag};
pub use drop::{
DropDatabase, DropEvent, DropFunction, DropProcedure, DropServer, DropTable, DropTrigger,
DropView,
};
pub use expression::{
BinaryOperator, Expression, Function, IdentifierPart, Is, UnaryOperator, Variable, When,
};
pub use insert_replace::{
InsertReplace, InsertReplaceFlag, InsertReplaceOnDuplicateKeyUpdate, InsertReplaceSet,
InsertReplaceSetPair, InsertReplaceType, OnConflict, OnConflictAction, OnConflictTarget,
};
pub use rename::{RenameTable, TableToTable};
pub use select::{JoinSpecification, JoinType, Select, SelectExpr, SelectFlag, TableReference};
pub use truncate::TruncateTable;
pub use update::{Update, UpdateFlag};
#[derive(Clone, Debug)]
pub enum SQLDialect {
MariaDB,
PostgreSQL,
}
impl SQLDialect {
pub fn is_postgresql(&self) -> bool {
matches!(self, SQLDialect::PostgreSQL)
}
pub fn is_maria(&self) -> bool {
matches!(self, SQLDialect::MariaDB)
}
}
#[derive(Clone, Debug)]
pub enum SQLArguments {
None,
Percent,
QuestionMark,
Dollar,
}
#[derive(Clone, Debug)]
pub struct ParseOptions {
dialect: SQLDialect,
arguments: SQLArguments,
warn_unquoted_identifiers: bool,
warn_none_capital_keywords: bool,
list_hack: bool,
}
impl Default for ParseOptions {
fn default() -> Self {
Self {
dialect: SQLDialect::MariaDB,
arguments: SQLArguments::None,
warn_none_capital_keywords: false,
warn_unquoted_identifiers: false,
list_hack: false,
}
}
}
impl ParseOptions {
pub fn new() -> Self {
Default::default()
}
pub fn dialect(self, dialect: SQLDialect) -> Self {
Self { dialect, ..self }
}
pub fn get_dialect(&self) -> SQLDialect {
self.dialect.clone()
}
pub fn arguments(self, arguments: SQLArguments) -> Self {
Self { arguments, ..self }
}
pub fn warn_unquoted_identifiers(self, warn_unquoted_identifiers: bool) -> Self {
Self {
warn_unquoted_identifiers,
..self
}
}
pub fn warn_none_capital_keywords(self, warn_none_capital_keywords: bool) -> Self {
Self {
warn_none_capital_keywords,
..self
}
}
pub fn list_hack(self, list_hack: bool) -> Self {
Self { list_hack, ..self }
}
}
#[macro_export]
macro_rules! issue_ice {
( $spanned:expr ) => {{
Issue::err(
alloc::format!("Internal compiler error in {}:{}", file!(), line!()),
$spanned,
)
}};
}
#[macro_export]
macro_rules! issue_todo {
( $spanned:expr ) => {{
Issue::err(
alloc::format!("Not yet implemented {}:{}", file!(), line!()),
$spanned,
)
}};
}
pub fn parse_statements<'a>(
src: &'a str,
issues: &mut Vec<Issue>,
options: &ParseOptions,
) -> Vec<Statement<'a>> {
let mut parser = Parser::new(src, issues, options);
statement::parse_statements(&mut parser)
}
pub fn parse_statement<'a>(
src: &'a str,
issues: &mut Vec<Issue>,
options: &ParseOptions,
) -> Option<Statement<'a>> {
let mut parser = Parser::new(src, issues, options);
match statement::parse_statement(&mut parser) {
Ok(Some(v)) => {
if parser.token != Token::Eof {
parser.expected_error("Unexpected token after statement")
}
Some(v)
}
Ok(None) => {
parser.expected_error("Statement");
None
}
Err(_) => None,
}
}
#[test]
pub fn test_parse_alter_sql() {
let sql = "ALTER TABLE `test` ADD COLUMN `test1` VARCHAR (128) NULL DEFAULT NULL";
let options = ParseOptions::new()
.dialect(SQLDialect::MariaDB)
.arguments(SQLArguments::QuestionMark)
.warn_unquoted_identifiers(false);
let mut issues = Vec::new();
parse_statement(sql, &mut issues, &options);
assert!(issues.is_empty(), "Issues: {:#?}", issues);
}
#[test]
pub fn test_parse_delete_sql_with_schema() {
let sql = "DROP TABLE IF EXISTS `test_schema`.`test`";
let options = ParseOptions::new()
.dialect(SQLDialect::MariaDB)
.arguments(SQLArguments::QuestionMark)
.warn_unquoted_identifiers(false);
let mut issues = Vec::new();
parse_statement(sql, &mut issues, &options);
assert!(issues.is_empty(), "Issues: {:#?}", issues);
}
#[test]
pub fn parse_create_index_sql_with_schema() {
let sql = "CREATE INDEX `idx_test` ON test_schema.test(`col_test`)";
let options = ParseOptions::new()
.dialect(SQLDialect::MariaDB)
.arguments(SQLArguments::QuestionMark)
.warn_unquoted_identifiers(false);
let mut issues = Vec::new();
parse_statement(sql, &mut issues, &options);
assert!(issues.is_empty(), "Issues: {:#?}", issues);
}
#[test]
pub fn parse_drop_index_sql_with_schema() {
let sql = "DROP INDEX `idx_test` ON test_schema.test";
let options = ParseOptions::new()
.dialect(SQLDialect::MariaDB)
.arguments(SQLArguments::QuestionMark)
.warn_unquoted_identifiers(false);
let mut issues = Vec::new();
let _result = parse_statement(sql, &mut issues, &options);
assert!(issues.is_empty(), "Issues: {:#?}", issues);
}
#[test]
pub fn parse_create_view_sql_with_schema() {
let sql =
"CREATE OR REPLACE VIEW `test_schema`.`view_test` AS SELECT * FROM `test_schema`.`test`";
let options = ParseOptions::new()
.dialect(SQLDialect::MariaDB)
.arguments(SQLArguments::QuestionMark)
.warn_unquoted_identifiers(false);
let mut issues = Vec::new();
let _result = parse_statement(sql, &mut issues, &options);
assert!(issues.is_empty(), "Issues: {:#?}", issues);
}
#[test]
pub fn parse_drop_view_sql_with_schema() {
let sql = "DROP VIEW `test_schema`.`view_test`";
let options = ParseOptions::new()
.dialect(SQLDialect::MariaDB)
.arguments(SQLArguments::QuestionMark)
.warn_unquoted_identifiers(false);
let mut issues = Vec::new();
let _result = parse_statement(sql, &mut issues, &options);
assert!(issues.is_empty(), "Issues: {:#?}", issues);
}
#[test]
pub fn parse_truncate_table_sql_with_schema() {
let sql = "TRUNCATE TABLE `test_schema`.`table_test`";
let options = ParseOptions::new()
.dialect(SQLDialect::MariaDB)
.arguments(SQLArguments::QuestionMark)
.warn_unquoted_identifiers(false);
let mut issues = Vec::new();
let _result = parse_statement(sql, &mut issues, &options);
assert!(issues.is_empty(), "Issues: {:#?}", issues);
}
#[test]
pub fn parse_rename_table_sql_with_schema() {
let sql = "RENAME TABLE `test_schema`.`table_test` To `test_schema`.`table_new_test`";
let options = ParseOptions::new()
.dialect(SQLDialect::MariaDB)
.arguments(SQLArguments::QuestionMark)
.warn_unquoted_identifiers(false);
let mut issues = Vec::new();
let _result = parse_statement(sql, &mut issues, &options);
assert!(issues.is_empty(), "Issues: {:#?}", issues);
}