use super::super::{
PgColumn, PgCreateTable, PgCreateType, PgCreateTypeDefinition, PgDataType, PgName,
PgScalarDataType, PgSchema,
};
use crate::schema::Srid;
pub(crate) use schema_grammar::schema as parse;
pub(self) enum Definition {
Type(PgCreateType),
Table(PgCreateTable),
}
pub(self) fn group_definitions(
defs: Vec<Definition>,
) -> (Vec<PgCreateType>, Vec<PgCreateTable>) {
let mut types = vec![];
let mut tables = vec![];
for d in defs {
match d {
Definition::Type(ty) => types.push(ty),
Definition::Table(table) => tables.push(table),
}
}
(types, tables)
}
peg::parser! {
grammar schema_grammar() for str {
pub(crate) rule schema() -> PgSchema
= ws()? defs:definition() ** (ws()? ";" ws()?) (";" ws()?)?
{
let (types, tables) = group_definitions(defs);
PgSchema { types, tables }
}
rule definition() -> Definition
= def:create_type() { Definition::Type(def) }
/ def:create_table() { Definition::Table(def) }
rule create_type() -> PgCreateType
= i("CREATE") ws() i("TYPE") ws() name:name() ws() i("AS")
ws() definition:create_type_definition()
{
PgCreateType { name, definition }
}
rule create_type_definition() -> PgCreateTypeDefinition
= create_type_enum_definition()
rule create_type_enum_definition() -> PgCreateTypeDefinition
= i("ENUM") ws() "(" ws()? values:string_constant() ** (ws()? "," ws()?) ws()? ")"
{
PgCreateTypeDefinition::Enum(values)
}
rule create_table() -> PgCreateTable
= i("CREATE") ws() (i("UNLOGGED") ws())? i("TABLE") ws() name:name() ws()? "("
ws()? columns:(column() ** (ws()? "," ws()?)) ws()?
")" ws()? (";" ws()?)?
{
PgCreateTable {
name,
columns,
if_not_exists: false,
temporary: false,
}
}
rule column() -> PgColumn
= name:identifier() ws() data_type:data_type() is_nullable:is_nullable() primary_key()? {
PgColumn {
name,
is_nullable,
data_type,
}
}
rule is_nullable() -> bool
= ws() i("NOT") ws() i("NULL") { false }
/ { true }
rule primary_key()
= ws() i("PRIMARY") ws() i("KEY")
rule data_type() -> PgDataType
= quiet! {
data_type:scalar_data_type() ws()? "[" ws()? "]" {
PgDataType::Array { dimension_count: 1, ty: data_type }
}
/ data_type:scalar_data_type() { PgDataType::Scalar(data_type) }
}
/ expected!("data type")
rule scalar_data_type() -> PgScalarDataType
= i("bigint") { PgScalarDataType::Bigint }
/ i("boolean") { PgScalarDataType::Boolean }
/ i("character") ( ws()? "(" ws()? ['0'..='9']+ ws()? ")" )? { PgScalarDataType::Text }
/ i("citext") { PgScalarDataType::Text }
/ i("date") { PgScalarDataType::Date }
/ i("double") ws() i("precision") { PgScalarDataType::DoublePrecision }
/ i("float") { PgScalarDataType::DoublePrecision }
/ i("public.")? i("geometry") ws()? "(" ws()? identifier() ws()? "," ws()? srid:srid() ws()? ")" {
PgScalarDataType::Geometry(Srid::new(srid))
}
/ i("integer") { PgScalarDataType::Int } / i("int") { PgScalarDataType::Int }
/ i("jsonb") { PgScalarDataType::Jsonb }
/ i("json") { PgScalarDataType::Json }
/ i("numeric") { PgScalarDataType::Numeric }
/ i("real") { PgScalarDataType::Real }
/ i("smallint") { PgScalarDataType::Smallint }
/ i("text") { PgScalarDataType::Text }
/ i("timestamp") ws() i("with") ws() i("time") ws() i("zone") {
PgScalarDataType::TimestampWithTimeZone
}
/ i("timestamp") ws() i("without") ws() i("time") ws() i("zone") {
PgScalarDataType::TimestampWithoutTimeZone
}
/ i("timestamp") {
PgScalarDataType::TimestampWithoutTimeZone
}
/ i("uuid") { PgScalarDataType::Uuid }
/ name:name() { PgScalarDataType::Named(name) }
rule srid() -> u32
= srid:$(['0'..='9']+) { srid.parse().expect("should always parse") }
rule name() -> PgName
= table:identifier() {
PgName::new(None, table)
}
/ schema:identifier() "." table:identifier() {
PgName::new(schema, table)
}
rule identifier() -> String
= quiet! {
id:$(
['A'..='Z' | 'a'..='z' | '_']
['A'..='Z' | 'a'..='z' | '_' | '0'..='9' |'$']*
) { id.to_string() }
/ "\"" quoted:$(( !['"'][_] / "\"\"")*) "\"" {
quoted.replace("\"\"", "\"")
}
}
/ expected!("identifier")
rule string_constant() -> String
= "'" text:$(( !"'" [_] / "''" )*) "'"
{
text.replace("''", "'")
}
rule ws() = quiet! {
([' ' | '\t' | '\r' | '\n'] / ("--" (!['\n'][_])* "\n"))+
}
rule i(literal: &'static str)
= input:$([_]*<{literal.len()}>) {?
if input.eq_ignore_ascii_case(literal) {
Ok(())
} else {
Err(literal)
}
}
}
}