Skip to content

Commit ceb1cf3

Browse files
committed
feat(ast): add CreateStatistics, CreateAccessMethod, CreateEventTrigger, CreateTransform AST nodes
Add structs and enums for four PostgreSQL-specific DDL statements: - CreateStatistics with StatisticsKind (NDistinct, Dependencies, Mcv) - CreateAccessMethod with AccessMethodType (Index, Table) - CreateEventTrigger with EventTriggerEvent (DdlCommandStart, DdlCommandEnd, TableRewrite, SqlDrop) - CreateTransform / TransformElement with OR REPLACE support Adds TRANSFORM keyword to keywords.rs. Closes pgmold-103, pgmold-104, pgmold-105, pgmold-106.
1 parent 4c4d773 commit ceb1cf3

4 files changed

Lines changed: 301 additions & 0 deletions

File tree

src/ast/ddl.rs

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6512,3 +6512,269 @@ impl From<CreateSubscription> for crate::ast::Statement {
65126512
crate::ast::Statement::CreateSubscription(v)
65136513
}
65146514
}
6515+
6516+
/// A kind of extended statistics collected by `CREATE STATISTICS`.
6517+
///
6518+
/// Note: this is a PostgreSQL-specific concept.
6519+
/// <https://www.postgresql.org/docs/current/sql-createstatistics.html>
6520+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
6521+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6522+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
6523+
pub enum StatisticsKind {
6524+
/// `ndistinct` — n-distinct statistics
6525+
NDistinct,
6526+
/// `dependencies` — functional dependency statistics
6527+
Dependencies,
6528+
/// `mcv` — most-common-values statistics
6529+
Mcv,
6530+
}
6531+
6532+
impl fmt::Display for StatisticsKind {
6533+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
6534+
match self {
6535+
StatisticsKind::NDistinct => write!(f, "ndistinct"),
6536+
StatisticsKind::Dependencies => write!(f, "dependencies"),
6537+
StatisticsKind::Mcv => write!(f, "mcv"),
6538+
}
6539+
}
6540+
}
6541+
6542+
/// A `CREATE STATISTICS` statement.
6543+
///
6544+
/// Note: this is a PostgreSQL-specific statement.
6545+
/// <https://www.postgresql.org/docs/current/sql-createstatistics.html>
6546+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
6547+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6548+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
6549+
pub struct CreateStatistics {
6550+
/// Optional `IF NOT EXISTS` clause.
6551+
pub if_not_exists: bool,
6552+
/// The statistics object name, e.g. `public.s`.
6553+
pub name: ObjectName,
6554+
/// Optional `(ndistinct, dependencies, mcv)` kind list.
6555+
pub kinds: Vec<StatisticsKind>,
6556+
/// The expressions (columns or arbitrary expressions) to collect statistics on.
6557+
pub on: Vec<Expr>,
6558+
/// The table to collect statistics from.
6559+
pub from: ObjectName,
6560+
}
6561+
6562+
impl fmt::Display for CreateStatistics {
6563+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
6564+
write!(f, "CREATE STATISTICS")?;
6565+
if self.if_not_exists {
6566+
write!(f, " IF NOT EXISTS")?;
6567+
}
6568+
write!(f, " {}", self.name)?;
6569+
if !self.kinds.is_empty() {
6570+
write!(f, " ({})", display_comma_separated(&self.kinds))?;
6571+
}
6572+
write!(f, " ON {}", display_comma_separated(&self.on))?;
6573+
write!(f, " FROM {}", self.from)?;
6574+
Ok(())
6575+
}
6576+
}
6577+
6578+
impl From<CreateStatistics> for crate::ast::Statement {
6579+
fn from(v: CreateStatistics) -> Self {
6580+
crate::ast::Statement::CreateStatistics(v)
6581+
}
6582+
}
6583+
6584+
/// The type of access method in `CREATE ACCESS METHOD`.
6585+
///
6586+
/// Note: this is a PostgreSQL-specific concept.
6587+
/// <https://www.postgresql.org/docs/current/sql-create-access-method.html>
6588+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
6589+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6590+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
6591+
pub enum AccessMethodType {
6592+
/// `INDEX` — an index access method
6593+
Index,
6594+
/// `TABLE` — a table access method
6595+
Table,
6596+
}
6597+
6598+
impl fmt::Display for AccessMethodType {
6599+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
6600+
match self {
6601+
AccessMethodType::Index => write!(f, "INDEX"),
6602+
AccessMethodType::Table => write!(f, "TABLE"),
6603+
}
6604+
}
6605+
}
6606+
6607+
/// A `CREATE ACCESS METHOD` statement.
6608+
///
6609+
/// Note: this is a PostgreSQL-specific statement.
6610+
/// <https://www.postgresql.org/docs/current/sql-create-access-method.html>
6611+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
6612+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6613+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
6614+
pub struct CreateAccessMethod {
6615+
/// The access method name.
6616+
pub name: Ident,
6617+
/// `TYPE INDEX | TABLE`
6618+
pub method_type: AccessMethodType,
6619+
/// `HANDLER handler_function`
6620+
pub handler: ObjectName,
6621+
}
6622+
6623+
impl fmt::Display for CreateAccessMethod {
6624+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
6625+
write!(
6626+
f,
6627+
"CREATE ACCESS METHOD {name} TYPE {method_type} HANDLER {handler}",
6628+
name = self.name,
6629+
method_type = self.method_type,
6630+
handler = self.handler,
6631+
)
6632+
}
6633+
}
6634+
6635+
impl From<CreateAccessMethod> for crate::ast::Statement {
6636+
fn from(v: CreateAccessMethod) -> Self {
6637+
crate::ast::Statement::CreateAccessMethod(v)
6638+
}
6639+
}
6640+
6641+
/// An event name for `CREATE EVENT TRIGGER`.
6642+
///
6643+
/// Note: this is a PostgreSQL-specific concept.
6644+
/// <https://www.postgresql.org/docs/current/sql-createeventtrigger.html>
6645+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
6646+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6647+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
6648+
pub enum EventTriggerEvent {
6649+
/// `ddl_command_start`
6650+
DdlCommandStart,
6651+
/// `ddl_command_end`
6652+
DdlCommandEnd,
6653+
/// `table_rewrite`
6654+
TableRewrite,
6655+
/// `sql_drop`
6656+
SqlDrop,
6657+
}
6658+
6659+
impl fmt::Display for EventTriggerEvent {
6660+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
6661+
match self {
6662+
EventTriggerEvent::DdlCommandStart => write!(f, "ddl_command_start"),
6663+
EventTriggerEvent::DdlCommandEnd => write!(f, "ddl_command_end"),
6664+
EventTriggerEvent::TableRewrite => write!(f, "table_rewrite"),
6665+
EventTriggerEvent::SqlDrop => write!(f, "sql_drop"),
6666+
}
6667+
}
6668+
}
6669+
6670+
/// A `CREATE EVENT TRIGGER` statement.
6671+
///
6672+
/// Note: this is a PostgreSQL-specific statement.
6673+
/// <https://www.postgresql.org/docs/current/sql-createeventtrigger.html>
6674+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
6675+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6676+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
6677+
pub struct CreateEventTrigger {
6678+
/// The trigger name.
6679+
pub name: Ident,
6680+
/// The event that fires the trigger.
6681+
pub event: EventTriggerEvent,
6682+
/// Optional `WHEN TAG IN ('tag', ...)` filter.
6683+
pub when_tags: Option<Vec<Value>>,
6684+
/// The handler function name (from `EXECUTE FUNCTION name()`).
6685+
pub execute: ObjectName,
6686+
/// Whether `PROCEDURE` was used instead of `FUNCTION` (older alias).
6687+
pub is_procedure: bool,
6688+
}
6689+
6690+
impl fmt::Display for CreateEventTrigger {
6691+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
6692+
write!(f, "CREATE EVENT TRIGGER {} ON {}", self.name, self.event)?;
6693+
if let Some(tags) = &self.when_tags {
6694+
write!(f, " WHEN TAG IN ({})", display_comma_separated(tags))?;
6695+
}
6696+
let func_kw = if self.is_procedure {
6697+
"PROCEDURE"
6698+
} else {
6699+
"FUNCTION"
6700+
};
6701+
write!(f, " EXECUTE {func_kw} {}()", self.execute)?;
6702+
Ok(())
6703+
}
6704+
}
6705+
6706+
impl From<CreateEventTrigger> for crate::ast::Statement {
6707+
fn from(v: CreateEventTrigger) -> Self {
6708+
crate::ast::Statement::CreateEventTrigger(v)
6709+
}
6710+
}
6711+
6712+
/// A single element in a `CREATE TRANSFORM` transform list.
6713+
///
6714+
/// Either `FROM SQL WITH FUNCTION name(arg_types)` or `TO SQL WITH FUNCTION name(arg_types)`.
6715+
///
6716+
/// Note: this is a PostgreSQL-specific concept.
6717+
/// <https://www.postgresql.org/docs/current/sql-createtransform.html>
6718+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
6719+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6720+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
6721+
pub struct TransformElement {
6722+
/// `true` = FROM SQL, `false` = TO SQL
6723+
pub is_from: bool,
6724+
/// The function name.
6725+
pub function: ObjectName,
6726+
/// The argument type list (may be empty).
6727+
pub arg_types: Vec<DataType>,
6728+
}
6729+
6730+
impl fmt::Display for TransformElement {
6731+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
6732+
let direction = if self.is_from { "FROM" } else { "TO" };
6733+
write!(
6734+
f,
6735+
"{direction} SQL WITH FUNCTION {}({})",
6736+
self.function,
6737+
display_comma_separated(&self.arg_types),
6738+
)
6739+
}
6740+
}
6741+
6742+
/// A `CREATE TRANSFORM` statement.
6743+
///
6744+
/// Note: this is a PostgreSQL-specific statement.
6745+
/// <https://www.postgresql.org/docs/current/sql-createtransform.html>
6746+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
6747+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6748+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
6749+
pub struct CreateTransform {
6750+
/// Whether `OR REPLACE` was specified.
6751+
pub or_replace: bool,
6752+
/// The data type being transformed.
6753+
pub type_name: DataType,
6754+
/// The procedural language name.
6755+
pub language: Ident,
6756+
/// The list of transform elements (FROM SQL and/or TO SQL).
6757+
pub elements: Vec<TransformElement>,
6758+
}
6759+
6760+
impl fmt::Display for CreateTransform {
6761+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
6762+
write!(f, "CREATE")?;
6763+
if self.or_replace {
6764+
write!(f, " OR REPLACE")?;
6765+
}
6766+
write!(
6767+
f,
6768+
" TRANSFORM FOR {} LANGUAGE {} ({})",
6769+
self.type_name,
6770+
self.language,
6771+
display_comma_separated(&self.elements),
6772+
)
6773+
}
6774+
}
6775+
6776+
impl From<CreateTransform> for crate::ast::Statement {
6777+
fn from(v: CreateTransform) -> Self {
6778+
crate::ast::Statement::CreateTransform(v)
6779+
}
6780+
}

src/ast/mod.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ pub use self::ddl::{
7777
CreatePolicyCommand, CreatePolicyType, CreatePublication, CreateSubscription, CreateTable,
7878
CreateTextSearchConfiguration, CreateTextSearchDictionary, CreateTextSearchParser,
7979
CreateTextSearchTemplate, CreateTrigger, PublicationTarget,
80+
AccessMethodType, CreateAccessMethod, CreateEventTrigger, CreateStatistics, CreateTransform,
81+
EventTriggerEvent, StatisticsKind, TransformElement,
8082
CreateView, Deduplicate, DeferrableInitial, DistStyle, DropBehavior, DropExtension,
8183
DropFunction, DropOperator, DropOperatorClass, DropOperatorFamily, DropOperatorSignature,
8284
DropPolicy, DropTrigger, FdwRoutineClause, ForValues, FunctionReturnType, GeneratedAs,
@@ -4049,6 +4051,30 @@ pub enum Statement {
40494051
/// <https://www.postgresql.org/docs/current/sql-createsubscription.html>
40504052
CreateSubscription(CreateSubscription),
40514053
/// ```sql
4054+
/// CREATE STATISTICS [ IF NOT EXISTS ] name [ ( kind [, ...] ) ] ON expr [, ...] FROM table_name
4055+
/// ```
4056+
/// Note: this is a PostgreSQL-specific statement.
4057+
/// <https://www.postgresql.org/docs/current/sql-createstatistics.html>
4058+
CreateStatistics(CreateStatistics),
4059+
/// ```sql
4060+
/// CREATE ACCESS METHOD name TYPE INDEX | TABLE HANDLER handler_function
4061+
/// ```
4062+
/// Note: this is a PostgreSQL-specific statement.
4063+
/// <https://www.postgresql.org/docs/current/sql-create-access-method.html>
4064+
CreateAccessMethod(CreateAccessMethod),
4065+
/// ```sql
4066+
/// CREATE EVENT TRIGGER name ON event [ WHEN TAG IN ( 'tag' [, ...] ) ] EXECUTE FUNCTION | PROCEDURE function_name()
4067+
/// ```
4068+
/// Note: this is a PostgreSQL-specific statement.
4069+
/// <https://www.postgresql.org/docs/current/sql-createeventtrigger.html>
4070+
CreateEventTrigger(CreateEventTrigger),
4071+
/// ```sql
4072+
/// CREATE [ OR REPLACE ] TRANSFORM FOR type_name LANGUAGE lang_name ( transform_element_list )
4073+
/// ```
4074+
/// Note: this is a PostgreSQL-specific statement.
4075+
/// <https://www.postgresql.org/docs/current/sql-createtransform.html>
4076+
CreateTransform(CreateTransform),
4077+
/// ```sql
40524078
/// DROP EXTENSION [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ]
40534079
/// ```
40544080
/// Note: this is a PostgreSQL-specific statement.
@@ -5537,6 +5563,10 @@ impl fmt::Display for Statement {
55375563
Statement::CreateTextSearchTemplate(v) => write!(f, "{v}"),
55385564
Statement::CreatePublication(v) => write!(f, "{v}"),
55395565
Statement::CreateSubscription(v) => write!(f, "{v}"),
5566+
Statement::CreateStatistics(v) => write!(f, "{v}"),
5567+
Statement::CreateAccessMethod(v) => write!(f, "{v}"),
5568+
Statement::CreateEventTrigger(v) => write!(f, "{v}"),
5569+
Statement::CreateTransform(v) => write!(f, "{v}"),
55405570
Statement::DropExtension(drop_extension) => write!(f, "{drop_extension}"),
55415571
Statement::DropOperator(drop_operator) => write!(f, "{drop_operator}"),
55425572
Statement::DropOperatorFamily(drop_operator_family) => {

src/ast/spans.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,10 @@ impl Spanned for Statement {
390390
Statement::CreateTextSearchTemplate(_) => Span::empty(),
391391
Statement::CreatePublication(_) => Span::empty(),
392392
Statement::CreateSubscription(_) => Span::empty(),
393+
Statement::CreateStatistics(_) => Span::empty(),
394+
Statement::CreateAccessMethod(_) => Span::empty(),
395+
Statement::CreateEventTrigger(_) => Span::empty(),
396+
Statement::CreateTransform(_) => Span::empty(),
393397
Statement::DropExtension(drop_extension) => drop_extension.span(),
394398
Statement::DropOperator(drop_operator) => drop_operator.span(),
395399
Statement::DropOperatorFamily(drop_operator_family) => drop_operator_family.span(),

src/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,6 +1074,7 @@ define_keywords!(
10741074
TRAN,
10751075
TRANSACTION,
10761076
TRANSIENT,
1077+
TRANSFORM,
10771078
TRANSLATE,
10781079
TRANSLATE_REGEX,
10791080
TRANSLATION,

0 commit comments

Comments
 (0)