Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions fuzz/fuzz_seeds/create_foreign_data_wrapper.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CREATE FOREIGN DATA WRAPPER postgres_fdw HANDLER postgres_fdw_handler VALIDATOR postgres_fdw_validator
1 change: 1 addition & 0 deletions fuzz/fuzz_seeds/create_foreign_data_wrapper_no_handler.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CREATE FOREIGN DATA WRAPPER myfdw NO HANDLER NO VALIDATOR
1 change: 1 addition & 0 deletions fuzz/fuzz_seeds/create_foreign_data_wrapper_options.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CREATE FOREIGN DATA WRAPPER myfdw HANDLER myhandler VALIDATOR myvalidator OPTIONS (debug 'true')
81 changes: 81 additions & 0 deletions src/ast/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6090,6 +6090,62 @@ impl fmt::Display for CreateForeignTable {
}
}

/// A handler or validator clause of a `CREATE FOREIGN DATA WRAPPER` statement.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum ForeignDataWrapperRoutineClause {
/// `HANDLER handler_function`
Handler(ObjectName),
/// `NO HANDLER`
NoHandler,
/// `VALIDATOR validator_function`
Validator(ObjectName),
/// `NO VALIDATOR`
NoValidator,
}

impl fmt::Display for ForeignDataWrapperRoutineClause {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ForeignDataWrapperRoutineClause::Handler(name) => write!(f, "HANDLER {name}"),
ForeignDataWrapperRoutineClause::NoHandler => f.write_str("NO HANDLER"),
ForeignDataWrapperRoutineClause::Validator(name) => write!(f, "VALIDATOR {name}"),
ForeignDataWrapperRoutineClause::NoValidator => f.write_str("NO VALIDATOR"),
}
}
}

/// 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. Per PostgreSQL this is a bare
/// identifier and cannot be schema-qualified.
pub name: Ident,
/// `HANDLER` and `VALIDATOR` clauses in source order. PostgreSQL's grammar
/// accepts repeated clauses and rejects them only when executing the statement.
pub routines: Vec<ForeignDataWrapperRoutineClause>,
/// 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)?;
for routine in &self.routines {
write!(f, " {routine}")?;
}
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)
Expand All @@ -6112,3 +6168,28 @@ impl Spanned for CreateForeignTable {
)
}
}

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

impl Spanned for CreateForeignDataWrapper {
fn span(&self) -> Span {
Span::union_iter(
core::iter::once(self.name.span)
.chain(self.routines.iter().filter_map(|routine| match routine {
ForeignDataWrapperRoutineClause::Handler(name)
| ForeignDataWrapperRoutineClause::Validator(name) => Some(name.span()),
_ => None,
}))
.chain(
self.options
.iter()
.flatten()
.flat_map(|option| [option.key.span, option.value.span]),
),
)
}
}
19 changes: 13 additions & 6 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,13 @@ pub use self::ddl::{
AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue, ClusteredBy, ColumnDef,
ColumnOption, ColumnOptionDef, ColumnOptions, ColumnPolicy, ColumnPolicyProperty,
ConstraintCharacteristics, CreateCollation, CreateCollationDefinition, CreateConnector,
CreateDomain, CreateExtension, CreateForeignTable, CreateFunction, CreateIndex, CreateOperator,
CreateOperatorClass, CreateOperatorFamily, CreatePolicy, CreatePolicyCommand, CreatePolicyType,
CreateTable, CreateTextSearch, CreateTrigger, CreateView, Deduplicate, DeferrableInitial,
DistStyle, DropBehavior, DropExtension, DropFunction, DropOperator, DropOperatorClass,
DropOperatorFamily, DropOperatorSignature, DropPolicy, DropTrigger, ForValues,
FunctionReturnType, GeneratedAs, GeneratedExpressionMode, IdentityParameters, IdentityProperty,
CreateDomain, CreateExtension, CreateForeignDataWrapper, CreateForeignTable, CreateFunction,
CreateIndex, CreateOperator, CreateOperatorClass, CreateOperatorFamily, CreatePolicy,
CreatePolicyCommand, CreatePolicyType, CreateTable, CreateTextSearch, CreateTrigger,
CreateView, Deduplicate, DeferrableInitial, DistStyle, DropBehavior, DropExtension,
DropFunction, DropOperator, DropOperatorClass, DropOperatorFamily, DropOperatorSignature,
DropPolicy, DropTrigger, ForValues, ForeignDataWrapperRoutineClause, FunctionReturnType,
GeneratedAs, GeneratedExpressionMode, IdentityParameters, IdentityProperty,
IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder, IndexColumn,
IndexOption, IndexType, KeyOrIndexDisplay, Msck, NullsDistinctOption, OperatorArgTypes,
OperatorClassItem, OperatorFamilyDropItem, OperatorFamilyItem, OperatorOption, OperatorPurpose,
Expand Down Expand Up @@ -3821,6 +3822,11 @@ pub enum Statement {
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createforeigntable.html)
CreateForeignTable(CreateForeignTable),
/// ```sql
/// CREATE FOREIGN DATA WRAPPER
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createforeigndatawrapper.html)
CreateForeignDataWrapper(CreateForeignDataWrapper),
/// ```sql
/// CREATE POLICY
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createpolicy.html)
Expand Down Expand Up @@ -5684,6 +5690,7 @@ impl fmt::Display for Statement {
write!(f, "{stmt}")
}
Statement::CreateForeignTable(stmt) => write!(f, "{stmt}"),
Statement::CreateForeignDataWrapper(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
1 change: 1 addition & 0 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,7 @@ impl Spanned for Statement {
Statement::CreateSecret { .. } => Span::empty(),
Statement::CreateServer { .. } => Span::empty(),
Statement::CreateForeignTable(stmt) => stmt.span(),
Statement::CreateForeignDataWrapper(stmt) => stmt.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 @@ -485,6 +485,7 @@ define_keywords!(
GROUPING,
GROUPS,
GZIP,
HANDLER,
HASH,
HASHES,
HAVING,
Expand Down Expand Up @@ -1146,6 +1147,7 @@ define_keywords!(
VALID,
VALIDATE,
VALIDATION_MODE,
VALIDATOR,
VALUE,
VALUES,
VALUE_OF,
Expand Down
64 changes: 52 additions & 12 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5371,6 +5371,16 @@ impl<'a> Parser<'a> {
let persistent = dialect_of!(self is DuckDbDialect)
&& self.parse_one_of_keywords(&[Keyword::PERSISTENT]).is_some();
let create_view_params = self.parse_create_view_params()?;
// `or_replace` is caught by an earlier arm. It stays so that reordering cannot bypass it.
let has_modifier = or_replace
|| or_alter
|| temporary
|| global.is_some()
|| transient
|| volatile
|| multiset.is_some()
|| persistent
|| create_view_params.is_some();
if self.peek_keywords(&[Keyword::SNAPSHOT, Keyword::TABLE]) {
self.parse_create_snapshot_table().map(Into::into)
} else if self.peek_keywords(&[Keyword::TEXT, Keyword::SEARCH]) {
Expand Down Expand Up @@ -5452,24 +5462,21 @@ impl<'a> Parser<'a> {
} else if self.parse_keyword(Keyword::SERVER) {
self.parse_pg_create_server()
} else if self.parse_keywords(&[Keyword::FOREIGN, Keyword::TABLE]) {
// `or_replace` cannot reach here today, since the arm above catches it.
// It stays so that reordering the arms cannot make it fall through.
if or_replace
|| or_alter
|| temporary
|| global.is_some()
|| transient
|| volatile
|| multiset.is_some()
|| persistent
|| create_view_params.is_some()
{
if has_modifier {
return parser_err!(
"CREATE FOREIGN TABLE does not accept this modifier",
modifier_loc
);
}
self.parse_create_foreign_table().map(Into::into)
} else if self.parse_keywords(&[Keyword::FOREIGN, Keyword::DATA, Keyword::WRAPPER]) {
if has_modifier {
return parser_err!(
"CREATE FOREIGN DATA WRAPPER does not accept this modifier",
modifier_loc
);
}
self.parse_create_foreign_data_wrapper().map(Into::into)
Comment thread
fmguerreiro marked this conversation as resolved.
} else {
self.expected_ref("an object type after CREATE", self.peek_token_ref())
}
Expand Down Expand Up @@ -20800,6 +20807,39 @@ impl<'a> Parser<'a> {
})
}

/// 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_identifier()?;

let mut routines = Vec::new();
loop {
let routine = if self.parse_keyword(Keyword::HANDLER) {
ForeignDataWrapperRoutineClause::Handler(self.parse_object_name(false)?)
} else if self.parse_keywords(&[Keyword::NO, Keyword::HANDLER]) {
ForeignDataWrapperRoutineClause::NoHandler
} else if self.parse_keyword(Keyword::VALIDATOR) {
ForeignDataWrapperRoutineClause::Validator(self.parse_object_name(false)?)
} else if self.parse_keywords(&[Keyword::NO, Keyword::VALIDATOR]) {
ForeignDataWrapperRoutineClause::NoValidator
} else {
break;
};
routines.push(routine);
}

let options = self.parse_pg_options_clause()?;

Ok(CreateForeignDataWrapper {
name,
routines,
options,
})
}

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