Skip to content
133 changes: 124 additions & 9 deletions src/ast/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ use crate::ast::{
UniqueConstraint,
},
ArgMode, AttachedToken, CommentDef, ConditionalStatements, CreateFunctionBody,
CreateFunctionUsing, CreateTableLikeKind, CreateTableOptions, CreateViewParams, DataType, Expr,
FileFormat, FunctionBehavior, FunctionCalledOnNull, FunctionDefinitionSetParam, FunctionDesc,
FunctionDeterminismSpecifier, FunctionParallel, FunctionSecurity, HiveDistributionStyle,
HiveFormat, HiveIOFormat, HiveRowFormat, HiveSetLocation, Ident, InitializeKind,
MySQLColumnPosition, ObjectName, OnCommit, OneOrManyWithParens, OperateFunctionArg,
OrderByExpr, ProjectionSelect, Query, RefreshModeKind, ResetConfig, RowAccessPolicy,
SequenceOptions, Spanned, SqlOption, StorageLifecyclePolicy, StorageSerializationPolicy,
TableVersion, Tag, TriggerEvent, TriggerExecBody, TriggerObject, TriggerPeriod,
TriggerReferencing, Value, ValueWithSpan, WrappedCollection,
CreateFunctionUsing, CreateServerOption, CreateTableLikeKind, CreateTableOptions,
CreateViewParams, DataType, Expr, FileFormat, FunctionBehavior, FunctionCalledOnNull,
FunctionDefinitionSetParam, FunctionDesc, FunctionDeterminismSpecifier, FunctionParallel,
FunctionSecurity, HiveDistributionStyle, HiveFormat, HiveIOFormat, HiveRowFormat,
HiveSetLocation, Ident, InitializeKind, MySQLColumnPosition, ObjectName, OnCommit,
OneOrManyWithParens, OperateFunctionArg, OrderByExpr, ProjectionSelect, Query, RefreshModeKind,
ResetConfig, RowAccessPolicy, SequenceOptions, Spanned, SqlOption, StorageLifecyclePolicy,
StorageSerializationPolicy, TableVersion, Tag, TriggerEvent, TriggerExecBody, TriggerObject,
TriggerPeriod, TriggerReferencing, Value, ValueWithSpan, WrappedCollection,
};
use crate::display_utils::{DisplayCommaSeparated, Indent, NewLine, SpaceOrNewline};
use crate::keywords::Keyword;
Expand Down Expand Up @@ -5757,3 +5757,118 @@ impl From<AlterPolicy> for crate::ast::Statement {
crate::ast::Statement::AlterPolicy(v)
}
}

/// The handler/validator clause of a `CREATE FOREIGN DATA WRAPPER` statement.
///
/// The function-or-absence portion of a `HANDLER` or `VALIDATOR` clause on a
/// foreign data wrapper.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FdwRoutineClause {
/// A named function, e.g. `HANDLER myhandler` or `VALIDATOR myvalidator`.
Function(ObjectName),
/// The `NO HANDLER` / `NO VALIDATOR` form.
Absent,
}

impl FdwRoutineClause {
fn fmt_with_label(&self, f: &mut fmt::Formatter<'_>, label: &str) -> fmt::Result {
match self {
FdwRoutineClause::Function(name) => write!(f, " {label} {name}"),
FdwRoutineClause::Absent => write!(f, " NO {label}"),
}
}
}

/// A `CREATE FOREIGN DATA WRAPPER` statement.
///
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createforeigndatawrapper.html)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct CreateForeignDataWrapper {
/// The name of the foreign-data wrapper. Can be schema-qualified.
pub name: ObjectName,
/// Optional `HANDLER handler_function` or `NO HANDLER` clause.
pub handler: Option<FdwRoutineClause>,
/// Optional `VALIDATOR validator_function` or `NO VALIDATOR` clause.
pub validator: Option<FdwRoutineClause>,
/// Optional `OPTIONS (key 'value', ...)` clause.
pub options: Option<Vec<CreateServerOption>>,
}

impl fmt::Display for CreateForeignDataWrapper {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "CREATE FOREIGN DATA WRAPPER {}", self.name)?;
if let Some(handler) = &self.handler {
handler.fmt_with_label(f, "HANDLER")?;
}
if let Some(validator) = &self.validator {
validator.fmt_with_label(f, "VALIDATOR")?;
}
if let Some(options) = &self.options {
write!(f, " OPTIONS ({})", display_comma_separated(options))?;
}
Ok(())
}
}

impl From<CreateForeignDataWrapper> for crate::ast::Statement {
fn from(v: CreateForeignDataWrapper) -> Self {
crate::ast::Statement::CreateForeignDataWrapper(v)
}
}

/// A `CREATE FOREIGN TABLE` statement.
///
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createforeigntable.html)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct CreateForeignTable {
/// The foreign table name.
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
pub name: ObjectName,
/// Whether `IF NOT EXISTS` was specified.
pub if_not_exists: bool,
/// Column definitions.
pub columns: Vec<ColumnDef>,
/// Table-level constraints (e.g. `CHECK (...)`, composite `FOREIGN KEY`).
/// PostgreSQL accepts these in `CREATE FOREIGN TABLE` column lists.
pub constraints: Vec<TableConstraint>,
/// The `SERVER server_name` clause.
pub server_name: Ident,
/// Optional `OPTIONS (key 'value', ...)` clause at the table level.
pub options: Option<Vec<CreateServerOption>>,
}

impl fmt::Display for CreateForeignTable {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"CREATE FOREIGN TABLE {if_not_exists}{name} ({columns}",
if_not_exists = if self.if_not_exists {
"IF NOT EXISTS "
} else {
""
},
name = self.name,
columns = display_comma_separated(&self.columns),
)?;
if !self.constraints.is_empty() {
write!(f, ", {}", display_comma_separated(&self.constraints))?;
}
write!(f, ") SERVER {}", self.server_name)?;
if let Some(options) = &self.options {
write!(f, " OPTIONS ({})", display_comma_separated(options))?;
}
Ok(())
}
}

impl From<CreateForeignTable> for crate::ast::Statement {
fn from(v: CreateForeignTable) -> Self {
crate::ast::Statement::CreateForeignTable(v)
}
}
23 changes: 18 additions & 5 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,12 @@ pub use self::ddl::{
AlterTypeAddValuePosition, AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue,
ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnOptions, ColumnPolicy,
ColumnPolicyProperty, ConstraintCharacteristics, CreateCollation, CreateCollationDefinition,
CreateConnector, CreateDomain, CreateExtension, CreateFunction, CreateIndex, CreateOperator,
CreateOperatorClass, CreateOperatorFamily, CreatePolicy, CreatePolicyCommand, CreatePolicyType,
CreateTable, CreateTrigger, CreateView, Deduplicate, DeferrableInitial, DistStyle,
DropBehavior, DropExtension, DropFunction, DropOperator, DropOperatorClass, DropOperatorFamily,
DropOperatorSignature, DropPolicy, DropTrigger, ForValues, FunctionReturnType, GeneratedAs,
CreateConnector, CreateDomain, CreateExtension, CreateForeignDataWrapper, CreateForeignTable,
CreateFunction, CreateIndex, CreateOperator, CreateOperatorClass, CreateOperatorFamily,
CreatePolicy, CreatePolicyCommand, CreatePolicyType, CreateTable, CreateTrigger, CreateView,
Deduplicate, DeferrableInitial, DistStyle, DropBehavior, DropExtension, DropFunction,
DropOperator, DropOperatorClass, DropOperatorFamily, DropOperatorSignature, DropPolicy,
DropTrigger, FdwRoutineClause, ForValues, FunctionReturnType, GeneratedAs,
GeneratedExpressionMode, IdentityParameters, IdentityProperty, IdentityPropertyFormatKind,
IdentityPropertyKind, IdentityPropertyOrder, IndexColumn, IndexOption, IndexType,
KeyOrIndexDisplay, Msck, NullsDistinctOption, OperatorArgTypes, OperatorClassItem,
Expand Down Expand Up @@ -3737,6 +3738,16 @@ pub enum Statement {
/// A `CREATE SERVER` statement.
CreateServer(CreateServerStatement),
/// ```sql
/// CREATE FOREIGN DATA WRAPPER
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createforeigndatawrapper.html)
CreateForeignDataWrapper(CreateForeignDataWrapper),
/// ```sql
/// CREATE FOREIGN TABLE
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createforeigntable.html)
CreateForeignTable(CreateForeignTable),
/// ```sql
/// CREATE POLICY
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createpolicy.html)
Expand Down Expand Up @@ -5542,6 +5553,8 @@ impl fmt::Display for Statement {
Statement::CreateServer(stmt) => {
write!(f, "{stmt}")
}
Statement::CreateForeignDataWrapper(stmt) => write!(f, "{stmt}"),
Statement::CreateForeignTable(stmt) => write!(f, "{stmt}"),
Statement::CreatePolicy(policy) => write!(f, "{policy}"),
Statement::CreateConnector(create_connector) => create_connector.fmt(f),
Statement::CreateOperator(create_operator) => create_operator.fmt(f),
Expand Down
2 changes: 2 additions & 0 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,8 @@ impl Spanned for Statement {
Statement::DropOperatorClass(drop_operator_class) => drop_operator_class.span(),
Statement::CreateSecret { .. } => Span::empty(),
Statement::CreateServer { .. } => Span::empty(),
Statement::CreateForeignDataWrapper(stmt) => stmt.name.span(),
Statement::CreateForeignTable(stmt) => stmt.name.span(),
Statement::CreateConnector { .. } => Span::empty(),
Statement::CreateOperator(create_operator) => create_operator.span(),
Statement::CreateOperatorFamily(create_operator_family) => {
Expand Down
2 changes: 2 additions & 0 deletions src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,7 @@ define_keywords!(
GROUPING,
GROUPS,
GZIP,
HANDLER,
HASH,
HASHES,
HAVING,
Expand Down Expand Up @@ -1130,6 +1131,7 @@ define_keywords!(
VALID,
VALIDATE,
VALIDATION_MODE,
VALIDATOR,
VALUE,
VALUES,
VALUE_OF,
Expand Down
97 changes: 87 additions & 10 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5212,6 +5212,17 @@ impl<'a> Parser<'a> {
}
} else if self.parse_keyword(Keyword::SERVER) {
self.parse_pg_create_server()
} else if self.parse_keyword(Keyword::FOREIGN) {
if self.parse_keywords(&[Keyword::DATA, Keyword::WRAPPER]) {
self.parse_create_foreign_data_wrapper().map(Into::into)
} else if self.parse_keyword(Keyword::TABLE) {
self.parse_create_foreign_table().map(Into::into)
} else {
self.expected_ref(
"DATA WRAPPER or TABLE after CREATE FOREIGN",
self.peek_token_ref(),
)
}
} else {
self.expected_ref("an object type after CREATE", self.peek_token_ref())
}
Expand Down Expand Up @@ -19695,16 +19706,7 @@ impl<'a> Parser<'a> {
self.expect_keywords(&[Keyword::FOREIGN, Keyword::DATA, Keyword::WRAPPER])?;
let foreign_data_wrapper = self.parse_object_name(false)?;

let mut options = None;
if self.parse_keyword(Keyword::OPTIONS) {
self.expect_token(&Token::LParen)?;
options = Some(self.parse_comma_separated(|p| {
let key = p.parse_identifier()?;
let value = p.parse_identifier()?;
Ok(CreateServerOption { key, value })
})?);
self.expect_token(&Token::RParen)?;
}
let options = self.parse_fdw_options_clause()?;

Ok(Statement::CreateServer(CreateServerStatement {
name,
Expand All @@ -19716,6 +19718,81 @@ impl<'a> Parser<'a> {
}))
}

/// Parse an optional `OPTIONS ( key value [, ...] )` clause shared by
/// `CREATE SERVER`, `CREATE FOREIGN DATA WRAPPER`, and `CREATE FOREIGN TABLE`.
fn parse_fdw_options_clause(&mut self) -> Result<Option<Vec<CreateServerOption>>, ParserError> {
if !self.parse_keyword(Keyword::OPTIONS) {
return Ok(None);
}
self.expect_token(&Token::LParen)?;
let opts = self.parse_comma_separated(|p| {
let key = p.parse_identifier()?;
let value = p.parse_identifier()?;
Ok(CreateServerOption { key, value })
})?;
self.expect_token(&Token::RParen)?;
Ok(Some(opts))
}

/// Parse an optional `HANDLER f | NO HANDLER` / `VALIDATOR f | NO VALIDATOR`
/// clause on `CREATE FOREIGN DATA WRAPPER`. The caller passes the positive
/// keyword (`HANDLER` or `VALIDATOR`); the `NO <keyword>` form is also
/// recognised.
fn parse_fdw_routine_clause(
&mut self,
keyword: Keyword,
) -> Result<Option<FdwRoutineClause>, ParserError> {
if self.parse_keyword(keyword) {
Ok(Some(FdwRoutineClause::Function(
self.parse_object_name(false)?,
)))
} else if self.parse_keywords(&[Keyword::NO, keyword]) {
Ok(Some(FdwRoutineClause::Absent))
} else {
Ok(None)
}
}

/// Parse a `CREATE FOREIGN DATA WRAPPER` statement.
///
/// See <https://www.postgresql.org/docs/current/sql-createforeigndatawrapper.html>
pub fn parse_create_foreign_data_wrapper(
&mut self,
) -> Result<CreateForeignDataWrapper, ParserError> {
let name = self.parse_object_name(false)?;
let handler = self.parse_fdw_routine_clause(Keyword::HANDLER)?;
let validator = self.parse_fdw_routine_clause(Keyword::VALIDATOR)?;
let options = self.parse_fdw_options_clause()?;

Ok(CreateForeignDataWrapper {
name,
handler,
validator,
options,
})
}

/// Parse a `CREATE FOREIGN TABLE` statement.
///
/// See <https://www.postgresql.org/docs/current/sql-createforeigntable.html>
pub fn parse_create_foreign_table(&mut self) -> Result<CreateForeignTable, ParserError> {
let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
let name = self.parse_object_name(false)?;
let (columns, constraints) = self.parse_columns()?;
self.expect_keyword_is(Keyword::SERVER)?;
let server_name = self.parse_identifier()?;
let options = self.parse_fdw_options_clause()?;

Ok(CreateForeignTable {
name,
if_not_exists,
columns,
constraints,
server_name,
options,
})
}

/// The index of the first unprocessed token.
pub fn index(&self) -> usize {
self.index
Expand Down
Loading
Loading