//! This file contains a [`rust-peg`][peg] grammar. A "PEG" is a "parser
//! expression grammar". It's basically similar to a regular expression,
//! except it can contain recursive rules. See the site for an overview
//! of the basic syntax.
//!
//! `#quiet` and `#expected` are used in a few places to give better
//! error messages. `#quiet` suppresses certain possible tokens (such as
//! whitespace) from "expected ___" errors, and `#expected` allows us to
//! insert descriptive names into those messages.
//!
//! [peg]: https://github.com/kevinmehall/rust-peg
use super::super::{PgColumn, PgCreateTable, PgDataType, PgScalarDataType};
use crate::schema::Srid;
/// A `CREATE TABLE` expression.
pub create_table -> PgCreateTable
= ws? "CREATE"i ws ("UNLOGGED"i ws)? "TABLE"i ws name:identifier ws? "("
ws? columns:(column ** (ws? "," ws?)) ws?
")" ws? (";" ws?)?
{
PgCreateTable {
name,
columns,
if_not_exists: false,
// We don't worry about trying to parse this, which we only use
// internally at the moment.
temporary: false,
}
}
/// A column expression of the form "name type".
column -> PgColumn
= name:identifier ws data_type:data_type is_nullable:is_nullable primary_key? {
PgColumn {
name,
is_nullable,
data_type,
}
}
/// An optional `NOT NULL` expression.
is_nullable -> bool
= ws "NOT"i ws "NULL"i { false }
/ { true }
/// A `PRIMARY KEY` specifier. We can ignore this.
primary_key
= ws "PRIMARY"i ws "KEY"i
/// A Postgres data type.
data_type -> PgDataType
= #quiet<
// Array type.
data_type:scalar_data_type ws? "[" ws? "]" {
PgDataType::Array { dimension_count: 1, ty: data_type }
}
// All other types.
/ data_type:scalar_data_type { PgDataType::Scalar(data_type) }
>
/ #expected("data type")
/// A scalar data type, never an array.
scalar_data_type -> PgScalarDataType
= "bigint"i { PgScalarDataType::Bigint }
/ "boolean"i { PgScalarDataType::Boolean }
/ "date"i { PgScalarDataType::Date }
/ "double"i ws "precision"i { PgScalarDataType::DoublePrecision }
/ "float"i { PgScalarDataType::DoublePrecision }
/ "public."i? "geometry"i ws? "(" ws? identifier ws? "," ws? srid:srid ws? ")" {
PgScalarDataType::Geometry(Srid::new(srid))
}
/ "integer"i { PgScalarDataType::Int } // Longer keyword first!
/ "int"i { PgScalarDataType::Int }
/ "jsonb"i { PgScalarDataType::Jsonb }
/ "json"i { PgScalarDataType::Json }
/ "numeric"i { PgScalarDataType::Numeric }
/ "real"i { PgScalarDataType::Real }
/ "smallint"i { PgScalarDataType::Smallint }
/ "text"i { PgScalarDataType::Text }
/ "timestamp"i ws "with"i ws "time"i ws "zone"i {
PgScalarDataType::TimestampWithTimeZone
}
/ "timestamp"i ws "without"i ws "time"i ws "zone"i {
PgScalarDataType::TimestampWithoutTimeZone
}
/ "timestamp"i {
PgScalarDataType::TimestampWithoutTimeZone
}
/ "uuid"i { PgScalarDataType::Uuid }
/// A GeoJSON SRID number, used to identify a coordinate system.
srid -> u32
= srid:$([0-9]+) { srid.parse().expect("should always parse") }
/// An SQL identifier.
identifier -> String
= #quiet<
// Unquoted identifier.
id:$([A-Za-z_][A-Za-z_0-9$]*) { id.to_string() }
// Double-quoted identifier.
/ "\"" quoted:$(([^"] / "\"\"")*) "\"" {
quoted.replace("\"\"", "\"").to_string()
}
>
/ #expected("identifier")
// One or more characters of whitespace, including comments.
ws = #quiet<
([ \t\r\n] / ("--" [^\n]* "\n"))+
>