use crate::ast::NoExt;
use crate::ast::dialect::FeatureSet;
use crate::parser::Dialect;
use crate::render::RenderDialect;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct Postgres;
impl Dialect for Postgres {
type Ext = NoExt;
fn features(&self) -> &FeatureSet {
&FeatureSet::POSTGRES
}
}
impl RenderDialect for Postgres {
fn render_features(&self) -> FeatureSet {
FeatureSet::POSTGRES
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::DataType;
use crate::ast::dialect::{Casing, FeatureSet};
use crate::dialect::Ansi;
use crate::dialect::test_support::{REPRESENTATIVE, assert_full_grammar, first_column_type};
use crate::parse_with;
use crate::parser::Parsed;
#[test]
fn postgres_parses_the_full_m1_select_grammar() {
assert_full_grammar(Postgres);
}
#[test]
fn features_return_their_presets_and_differ_in_dialect_specific_fields() {
assert_eq!(std::mem::size_of::<Ansi>(), 0);
assert_eq!(std::mem::size_of::<Postgres>(), 0);
assert_eq!(Ansi.features(), &FeatureSet::ANSI, "ANSI preset");
assert_eq!(
Postgres.features(),
&FeatureSet::POSTGRES,
"PostgreSQL preset"
);
assert_eq!(Ansi.features().identifier_casing, Casing::Upper);
assert_eq!(Postgres.features().identifier_casing, Casing::Lower);
assert_ne!(
Ansi.features().identifier_casing,
Postgres.features().identifier_casing,
);
assert!(!Ansi.features().string_literals.escape_strings);
assert!(!Ansi.features().string_literals.dollar_quoted_strings);
assert!(Postgres.features().string_literals.escape_strings);
assert!(Postgres.features().string_literals.dollar_quoted_strings);
}
#[test]
fn ansi_and_postgres_render_identically() {
use squonk_ast::render::{RenderConfig, RenderCtx, RenderExt, RenderMode};
let render = |parsed: &Parsed| {
let config = RenderConfig {
mode: RenderMode::Canonical,
..RenderConfig::default()
};
let ctx = RenderCtx::new(parsed.resolver(), parsed.source(), &config);
parsed.statements()[0].displayed(&ctx).to_string()
};
let ansi =
parse_with(REPRESENTATIVE, crate::ParseConfig::new(Ansi)).expect("parses under ANSI");
let postgres = parse_with(REPRESENTATIVE, crate::ParseConfig::new(Postgres))
.expect("parses under PostgreSQL");
assert_eq!(
render(&ansi),
render(&postgres),
"M1 has no ANSI/PostgreSQL rendering divergence",
);
assert_eq!(render(&ansi), REPRESENTATIVE);
}
#[test]
fn ansi_and_postgres_reject_mysql_only_surface() {
for sql in [
"SELECT a FROM t LIMIT 5, 10", "SELECT `c` FROM t", ] {
assert!(
parse_with(sql, crate::ParseConfig::new(Ansi)).is_err(),
"ANSI rejects {sql:?}"
);
assert!(
parse_with(sql, crate::ParseConfig::new(Postgres)).is_err(),
"PostgreSQL rejects {sql:?}"
);
}
}
#[test]
fn postgres_reads_a_national_string_spelling_as_a_typed_literal() {
use crate::ast::{CastSyntax, Expr, SelectItem, SetExpr, Statement};
assert!(!Postgres.features().string_literals.national_strings);
let parsed = parse_with("SELECT N'x'", crate::ParseConfig::new(Postgres))
.expect("parses under PostgreSQL");
let Statement::Query { query, .. } = &parsed.statements()[0] else {
panic!("expected a query statement");
};
let SetExpr::Select { select, .. } = &query.body else {
panic!("expected a SELECT body");
};
let [
SelectItem::Expr {
expr:
Expr::Cast {
syntax: CastSyntax::PrefixTyped,
..
},
alias: None,
..
},
] = select.projection.as_slice()
else {
panic!("expected the typed-literal cast projection");
};
assert!(parse_with("DO LANGUAGE N'p'", crate::ParseConfig::new(Postgres)).is_ok());
}
#[test]
fn ansi_rejects_the_general_operator_surface() {
for sql in [
"SELECT a && b", "SELECT a ~ b", "SELECT a <-> b", "SELECT 2 ^ 3", "SELECT @ b", ] {
assert!(
parse_with(sql, crate::ParseConfig::new(Ansi)).is_err(),
"ANSI rejects {sql:?}"
);
assert!(
parse_with(sql, crate::ParseConfig::new(Postgres)).is_ok(),
"PostgreSQL accepts {sql:?}"
);
}
}
#[test]
fn ansi_and_postgres_do_not_recognize_mysql_type_names() {
for sql in [
"CREATE TABLE t (c TINYINT)",
"CREATE TABLE t (c MEDIUMINT)",
"CREATE TABLE t (c MEDIUMTEXT)",
"CREATE TABLE t (c DATETIME)",
] {
assert!(
matches!(first_column_type(Ansi, sql), DataType::UserDefined { .. }),
"ANSI reads {sql:?} as a user-defined type",
);
assert!(
matches!(
first_column_type(Postgres, sql),
DataType::UserDefined { .. }
),
"PostgreSQL reads {sql:?} as a user-defined type",
);
}
for sql in [
"CREATE TABLE t (c ENUM('a', 'b'))",
"CREATE TABLE t (c SET('a', 'b'))",
] {
assert!(
parse_with(sql, crate::ParseConfig::new(Ansi)).is_err(),
"ANSI rejects {sql:?}"
);
assert!(
parse_with(sql, crate::ParseConfig::new(Postgres)).is_err(),
"PostgreSQL rejects {sql:?}",
);
}
}
}