Skip to content

Commit 11f549b

Browse files
committed
feat(ast): add CreateCast, CreateConversion, CreateLanguage, CreateRule AST nodes
Add AST structs and Display impls for four PostgreSQL catalog DDL statements. Each follows the established fork pattern: struct in ddl.rs with Debug/Clone/PartialEq/etc derives, Statement enum variant, Display arm, From impl, and Span::empty() arm in spans.rs. New keywords: ALSO, ASSIGNMENT, CONVERSION, IMPLICIT, INLINE, PROCEDURAL, TRUSTED. Closes pgmold-89, pgmold-91, pgmold-92, pgmold-93.
1 parent 4c4d773 commit 11f549b

6 files changed

Lines changed: 706 additions & 6 deletions

File tree

src/ast/ddl.rs

Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6512,3 +6512,310 @@ impl From<CreateSubscription> for crate::ast::Statement {
65126512
crate::ast::Statement::CreateSubscription(v)
65136513
}
65146514
}
6515+
6516+
/// The function binding kind for a `CREATE CAST` statement.
6517+
///
6518+
/// Note: this is a PostgreSQL-specific construct.
6519+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
6520+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6521+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
6522+
pub enum CastFunctionKind {
6523+
/// `WITH FUNCTION function_name(arg_types)`
6524+
WithFunction {
6525+
function_name: ObjectName,
6526+
argument_types: Vec<DataType>,
6527+
},
6528+
/// `WITHOUT FUNCTION`
6529+
WithoutFunction,
6530+
/// `WITH INOUT`
6531+
WithInout,
6532+
}
6533+
6534+
impl fmt::Display for CastFunctionKind {
6535+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
6536+
match self {
6537+
CastFunctionKind::WithFunction {
6538+
function_name,
6539+
argument_types,
6540+
} => {
6541+
write!(f, "WITH FUNCTION {function_name}")?;
6542+
if !argument_types.is_empty() {
6543+
write!(f, "({})", display_comma_separated(argument_types))?;
6544+
}
6545+
Ok(())
6546+
}
6547+
CastFunctionKind::WithoutFunction => write!(f, "WITHOUT FUNCTION"),
6548+
CastFunctionKind::WithInout => write!(f, "WITH INOUT"),
6549+
}
6550+
}
6551+
}
6552+
6553+
/// The context in which a cast may be invoked automatically.
6554+
///
6555+
/// Note: this is a PostgreSQL-specific construct.
6556+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
6557+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6558+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
6559+
pub enum CastContext {
6560+
/// No `AS` clause — explicit cast only (default).
6561+
Explicit,
6562+
/// `AS ASSIGNMENT`
6563+
Assignment,
6564+
/// `AS IMPLICIT`
6565+
Implicit,
6566+
}
6567+
6568+
impl fmt::Display for CastContext {
6569+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
6570+
match self {
6571+
CastContext::Explicit => Ok(()),
6572+
CastContext::Assignment => write!(f, " AS ASSIGNMENT"),
6573+
CastContext::Implicit => write!(f, " AS IMPLICIT"),
6574+
}
6575+
}
6576+
}
6577+
6578+
/// A `CREATE CAST` statement.
6579+
///
6580+
/// Note: this is a PostgreSQL-specific statement.
6581+
/// <https://www.postgresql.org/docs/current/sql-createcast.html>
6582+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
6583+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6584+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
6585+
pub struct CreateCast {
6586+
/// The source type.
6587+
pub source_type: DataType,
6588+
/// The target type.
6589+
pub target_type: DataType,
6590+
/// How the cast is implemented.
6591+
pub function_kind: CastFunctionKind,
6592+
/// The cast context (explicit, assignment, or implicit).
6593+
pub cast_context: CastContext,
6594+
}
6595+
6596+
impl fmt::Display for CreateCast {
6597+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
6598+
write!(
6599+
f,
6600+
"CREATE CAST ({source} AS {target}) {function_kind}{context}",
6601+
source = self.source_type,
6602+
target = self.target_type,
6603+
function_kind = self.function_kind,
6604+
context = self.cast_context,
6605+
)
6606+
}
6607+
}
6608+
6609+
impl From<CreateCast> for crate::ast::Statement {
6610+
fn from(v: CreateCast) -> Self {
6611+
crate::ast::Statement::CreateCast(v)
6612+
}
6613+
}
6614+
6615+
/// A `CREATE CONVERSION` statement.
6616+
///
6617+
/// Note: this is a PostgreSQL-specific statement.
6618+
/// <https://www.postgresql.org/docs/current/sql-createconversion.html>
6619+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
6620+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6621+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
6622+
pub struct CreateConversion {
6623+
/// The conversion name.
6624+
pub name: ObjectName,
6625+
/// Whether this is a `DEFAULT` conversion.
6626+
pub is_default: bool,
6627+
/// The source encoding name (a string literal like `'LATIN1'`).
6628+
pub source_encoding: String,
6629+
/// The destination encoding name (a string literal like `'UTF8'`).
6630+
pub destination_encoding: String,
6631+
/// The conversion function name.
6632+
pub function_name: ObjectName,
6633+
}
6634+
6635+
impl fmt::Display for CreateConversion {
6636+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
6637+
write!(f, "CREATE")?;
6638+
if self.is_default {
6639+
write!(f, " DEFAULT")?;
6640+
}
6641+
write!(
6642+
f,
6643+
" CONVERSION {name} FOR '{source}' TO '{destination}' FROM {function}",
6644+
name = self.name,
6645+
source = self.source_encoding,
6646+
destination = self.destination_encoding,
6647+
function = self.function_name,
6648+
)
6649+
}
6650+
}
6651+
6652+
impl From<CreateConversion> for crate::ast::Statement {
6653+
fn from(v: CreateConversion) -> Self {
6654+
crate::ast::Statement::CreateConversion(v)
6655+
}
6656+
}
6657+
6658+
/// A `CREATE LANGUAGE` statement.
6659+
///
6660+
/// Note: this is a PostgreSQL-specific statement.
6661+
/// <https://www.postgresql.org/docs/current/sql-createlanguage.html>
6662+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
6663+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6664+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
6665+
pub struct CreateLanguage {
6666+
/// The language name.
6667+
pub name: Ident,
6668+
/// Whether `OR REPLACE` was specified.
6669+
pub or_replace: bool,
6670+
/// Whether `TRUSTED` was specified.
6671+
pub trusted: bool,
6672+
/// Whether `PROCEDURAL` was specified.
6673+
pub procedural: bool,
6674+
/// Optional `HANDLER handler_function` clause.
6675+
pub handler: Option<ObjectName>,
6676+
/// Optional `INLINE inline_function` clause.
6677+
pub inline_handler: Option<ObjectName>,
6678+
/// Optional `VALIDATOR validator_function` clause.
6679+
pub validator: Option<ObjectName>,
6680+
}
6681+
6682+
impl fmt::Display for CreateLanguage {
6683+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
6684+
write!(f, "CREATE")?;
6685+
if self.or_replace {
6686+
write!(f, " OR REPLACE")?;
6687+
}
6688+
if self.trusted {
6689+
write!(f, " TRUSTED")?;
6690+
}
6691+
if self.procedural {
6692+
write!(f, " PROCEDURAL")?;
6693+
}
6694+
write!(f, " LANGUAGE {}", self.name)?;
6695+
if let Some(handler) = &self.handler {
6696+
write!(f, " HANDLER {handler}")?;
6697+
}
6698+
if let Some(inline) = &self.inline_handler {
6699+
write!(f, " INLINE {inline}")?;
6700+
}
6701+
if let Some(validator) = &self.validator {
6702+
write!(f, " VALIDATOR {validator}")?;
6703+
}
6704+
Ok(())
6705+
}
6706+
}
6707+
6708+
impl From<CreateLanguage> for crate::ast::Statement {
6709+
fn from(v: CreateLanguage) -> Self {
6710+
crate::ast::Statement::CreateLanguage(v)
6711+
}
6712+
}
6713+
6714+
/// The event that triggers a rule.
6715+
///
6716+
/// Note: this is a PostgreSQL-specific construct.
6717+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
6718+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6719+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
6720+
pub enum RuleEvent {
6721+
Select,
6722+
Insert,
6723+
Update,
6724+
Delete,
6725+
}
6726+
6727+
impl fmt::Display for RuleEvent {
6728+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
6729+
match self {
6730+
RuleEvent::Select => write!(f, "SELECT"),
6731+
RuleEvent::Insert => write!(f, "INSERT"),
6732+
RuleEvent::Update => write!(f, "UPDATE"),
6733+
RuleEvent::Delete => write!(f, "DELETE"),
6734+
}
6735+
}
6736+
}
6737+
6738+
/// The action performed by a rule.
6739+
///
6740+
/// Note: this is a PostgreSQL-specific construct.
6741+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
6742+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6743+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
6744+
pub enum RuleAction {
6745+
/// `NOTHING`
6746+
Nothing,
6747+
/// One or more statements (parenthesized when more than one).
6748+
Statements(Vec<crate::ast::Statement>),
6749+
}
6750+
6751+
impl fmt::Display for RuleAction {
6752+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
6753+
match self {
6754+
RuleAction::Nothing => write!(f, "NOTHING"),
6755+
RuleAction::Statements(stmts) => {
6756+
if stmts.len() == 1 {
6757+
write!(f, "{}", stmts[0])
6758+
} else {
6759+
write!(f, "(")?;
6760+
for (i, stmt) in stmts.iter().enumerate() {
6761+
if i > 0 {
6762+
write!(f, "; ")?;
6763+
}
6764+
write!(f, "{stmt}")?;
6765+
}
6766+
write!(f, ")")
6767+
}
6768+
}
6769+
}
6770+
}
6771+
}
6772+
6773+
/// A `CREATE RULE` statement.
6774+
///
6775+
/// Note: this is a PostgreSQL-specific statement.
6776+
/// <https://www.postgresql.org/docs/current/sql-createrule.html>
6777+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
6778+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6779+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
6780+
pub struct CreateRule {
6781+
/// The rule name.
6782+
pub name: Ident,
6783+
/// The event that triggers the rule.
6784+
pub event: RuleEvent,
6785+
/// The table the rule applies to.
6786+
pub table: ObjectName,
6787+
/// Optional `WHERE condition` clause.
6788+
pub condition: Option<Expr>,
6789+
/// Whether the rule is `INSTEAD` (true) or `ALSO` (false).
6790+
pub instead: bool,
6791+
/// The action(s) taken by the rule.
6792+
pub action: RuleAction,
6793+
}
6794+
6795+
impl fmt::Display for CreateRule {
6796+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
6797+
write!(
6798+
f,
6799+
"CREATE RULE {name} AS ON {event} TO {table}",
6800+
name = self.name,
6801+
event = self.event,
6802+
table = self.table,
6803+
)?;
6804+
if let Some(condition) = &self.condition {
6805+
write!(f, " WHERE {condition}")?;
6806+
}
6807+
write!(f, " DO")?;
6808+
if self.instead {
6809+
write!(f, " INSTEAD")?;
6810+
} else {
6811+
write!(f, " ALSO")?;
6812+
}
6813+
write!(f, " {}", self.action)
6814+
}
6815+
}
6816+
6817+
impl From<CreateRule> for crate::ast::Statement {
6818+
fn from(v: CreateRule) -> Self {
6819+
crate::ast::Statement::CreateRule(v)
6820+
}
6821+
}

src/ast/mod.rs

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,16 @@ pub use self::ddl::{
6969
AlterTableLock, AlterTableOperation, AlterTableType, AlterTrigger, AlterTriggerOperation,
7070
AlterType, AlterTypeAddValue,
7171
AlterTypeAddValuePosition, AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue,
72-
AggregateModifyKind, ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnOptions,
72+
AggregateModifyKind, CastContext, CastFunctionKind, ClusteredBy, ColumnDef, ColumnOption,
73+
ColumnOptionDef, ColumnOptions,
7374
ColumnPolicy, ColumnPolicyProperty, ConstraintCharacteristics, CreateAggregate,
74-
CreateAggregateOption, CreateCollation, CreateCollationDefinition, CreateConnector,
75-
CreateDomain, CreateExtension, CreateForeignDataWrapper, CreateForeignTable, CreateFunction,
76-
CreateIndex, CreateOperator, CreateOperatorClass, CreateOperatorFamily, CreatePolicy,
77-
CreatePolicyCommand, CreatePolicyType, CreatePublication, CreateSubscription, CreateTable,
75+
CreateAggregateOption, CreateCast, CreateCollation, CreateCollationDefinition, CreateConnector,
76+
CreateConversion, CreateDomain, CreateExtension, CreateForeignDataWrapper, CreateForeignTable,
77+
CreateFunction, CreateIndex, CreateLanguage, CreateOperator, CreateOperatorClass,
78+
CreateOperatorFamily, CreatePolicy, CreatePolicyCommand, CreatePolicyType, CreatePublication,
79+
CreateRule, CreateSubscription, CreateTable,
7880
CreateTextSearchConfiguration, CreateTextSearchDictionary, CreateTextSearchParser,
79-
CreateTextSearchTemplate, CreateTrigger, PublicationTarget,
81+
CreateTextSearchTemplate, CreateTrigger, PublicationTarget, RuleAction, RuleEvent,
8082
CreateView, Deduplicate, DeferrableInitial, DistStyle, DropBehavior, DropExtension,
8183
DropFunction, DropOperator, DropOperatorClass, DropOperatorFamily, DropOperatorSignature,
8284
DropPolicy, DropTrigger, FdwRoutineClause, ForValues, FunctionReturnType, GeneratedAs,
@@ -4049,6 +4051,32 @@ pub enum Statement {
40494051
/// <https://www.postgresql.org/docs/current/sql-createsubscription.html>
40504052
CreateSubscription(CreateSubscription),
40514053
/// ```sql
4054+
/// CREATE CAST (source_type AS target_type) WITH FUNCTION func_name [(arg_types)] [AS ASSIGNMENT | AS IMPLICIT]
4055+
/// CREATE CAST (source_type AS target_type) WITHOUT FUNCTION [AS ASSIGNMENT | AS IMPLICIT]
4056+
/// CREATE CAST (source_type AS target_type) WITH INOUT [AS ASSIGNMENT | AS IMPLICIT]
4057+
/// ```
4058+
/// Note: this is a PostgreSQL-specific statement.
4059+
/// <https://www.postgresql.org/docs/current/sql-createcast.html>
4060+
CreateCast(CreateCast),
4061+
/// ```sql
4062+
/// CREATE [DEFAULT] CONVERSION name FOR 'source_encoding' TO 'dest_encoding' FROM function_name
4063+
/// ```
4064+
/// Note: this is a PostgreSQL-specific statement.
4065+
/// <https://www.postgresql.org/docs/current/sql-createconversion.html>
4066+
CreateConversion(CreateConversion),
4067+
/// ```sql
4068+
/// CREATE [OR REPLACE] [TRUSTED] [PROCEDURAL] LANGUAGE name [HANDLER handler_func] [INLINE inline_func] [VALIDATOR validator_func | NO VALIDATOR]
4069+
/// ```
4070+
/// Note: this is a PostgreSQL-specific statement.
4071+
/// <https://www.postgresql.org/docs/current/sql-createlanguage.html>
4072+
CreateLanguage(CreateLanguage),
4073+
/// ```sql
4074+
/// CREATE RULE name AS ON event TO table [WHERE condition] DO [ALSO | INSTEAD] { NOTHING | command | (command ; ...) }
4075+
/// ```
4076+
/// Note: this is a PostgreSQL-specific statement.
4077+
/// <https://www.postgresql.org/docs/current/sql-createrule.html>
4078+
CreateRule(CreateRule),
4079+
/// ```sql
40524080
/// DROP EXTENSION [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ]
40534081
/// ```
40544082
/// Note: this is a PostgreSQL-specific statement.
@@ -5537,6 +5565,10 @@ impl fmt::Display for Statement {
55375565
Statement::CreateTextSearchTemplate(v) => write!(f, "{v}"),
55385566
Statement::CreatePublication(v) => write!(f, "{v}"),
55395567
Statement::CreateSubscription(v) => write!(f, "{v}"),
5568+
Statement::CreateCast(v) => write!(f, "{v}"),
5569+
Statement::CreateConversion(v) => write!(f, "{v}"),
5570+
Statement::CreateLanguage(v) => write!(f, "{v}"),
5571+
Statement::CreateRule(v) => write!(f, "{v}"),
55405572
Statement::DropExtension(drop_extension) => write!(f, "{drop_extension}"),
55415573
Statement::DropOperator(drop_operator) => write!(f, "{drop_operator}"),
55425574
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::CreateCast(_) => Span::empty(),
394+
Statement::CreateConversion(_) => Span::empty(),
395+
Statement::CreateLanguage(_) => Span::empty(),
396+
Statement::CreateRule(_) => 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(),

0 commit comments

Comments
 (0)