LucaCappelletti94 commented on code in PR #2317:
URL:
https://github.com/apache/datafusion-sqlparser-rs/pull/2317#discussion_r4294232999
##########
src/ast/ddl.rs:
##########
@@ -6056,6 +6056,60 @@ impl fmt::Display for CreateForeignTable {
}
}
+/// The handler/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 {
+ /// A named function, e.g. `HANDLER myhandler` or `VALIDATOR myvalidator`.
+ Function(ObjectName),
+ /// The `NO HANDLER` / `NO VALIDATOR` form.
+ Absent,
+}
+
+impl ForeignDataWrapperRoutineClause {
+ fn fmt_with_label(&self, f: &mut fmt::Formatter<'_>, label: &str) ->
fmt::Result {
+ match self {
+ ForeignDataWrapperRoutineClause::Function(name) => write!(f, "
{label} {name}"),
+ ForeignDataWrapperRoutineClause::Absent => write!(f, " NO
{label}"),
+ }
+ }
+}
Review Comment:
You should make each clause a variant, mirroring the four `fdw_option`
productions, so a list can hold repeats and either order.
```suggestion
/// 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"),
}
}
}
```
##########
src/parser/mod.rs:
##########
@@ -20756,6 +20763,53 @@ 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()?;
+
+ // PostgreSQL accepts HANDLER and VALIDATOR in either order, so they
are
+ // consumed in a loop rather than positionally.
+ let mut handler = None;
+ let mut validator = None;
+ loop {
+ let loc = self.peek_token_ref().span.start;
+ let (slot, clause) = if self.parse_keyword(Keyword::HANDLER) {
+ (
+ &mut handler,
+
ForeignDataWrapperRoutineClause::Function(self.parse_object_name(false)?),
+ )
+ } else if self.parse_keywords(&[Keyword::NO, Keyword::HANDLER]) {
+ (&mut handler, ForeignDataWrapperRoutineClause::Absent)
+ } else if self.parse_keyword(Keyword::VALIDATOR) {
+ (
+ &mut validator,
+
ForeignDataWrapperRoutineClause::Function(self.parse_object_name(false)?),
+ )
+ } else if self.parse_keywords(&[Keyword::NO, Keyword::VALIDATOR]) {
+ (&mut validator, ForeignDataWrapperRoutineClause::Absent)
+ } else {
+ break;
+ };
+ if slot.is_some() {
+ return parser_err!("conflicting or redundant options", loc);
+ }
+ *slot = Some(clause);
+ }
+
+ let options = self.parse_pg_options_clause()?;
+
+ Ok(CreateForeignDataWrapper {
+ name,
+ handler,
+ validator,
+ options,
+ })
Review Comment:
You should collect the clauses in source order instead of rejecting repeats.
PostgreSQL's grammar takes `fdw_options` as a plain list and raises
"conflicting or redundant options" only in `parse_func_options` when the
statement executes. `pg_query` 6.2 accepts `CREATE FOREIGN DATA WRAPPER w
HANDLER h1 HANDLER h2`, which this loop rejects, so `fuzz_postgres_accepts`
panics on it and on any mutation of the new seeds that repeats a clause.
```suggestion
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,
})
```
##########
src/ast/ddl.rs:
##########
@@ -6056,6 +6056,60 @@ impl fmt::Display for CreateForeignTable {
}
}
+/// The handler/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 {
+ /// A named function, e.g. `HANDLER myhandler` or `VALIDATOR myvalidator`.
+ Function(ObjectName),
+ /// The `NO HANDLER` / `NO VALIDATOR` form.
+ Absent,
+}
+
+impl ForeignDataWrapperRoutineClause {
+ fn fmt_with_label(&self, f: &mut fmt::Formatter<'_>, label: &str) ->
fmt::Result {
+ match self {
+ ForeignDataWrapperRoutineClause::Function(name) => write!(f, "
{label} {name}"),
+ ForeignDataWrapperRoutineClause::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. Per PostgreSQL this is a bare
+ /// identifier and cannot be schema-qualified.
+ pub name: Ident,
+ /// Optional `HANDLER handler_function` or `NO HANDLER` clause.
+ pub handler: Option<ForeignDataWrapperRoutineClause>,
+ /// Optional `VALIDATOR validator_function` or `NO VALIDATOR` clause.
+ pub validator: Option<ForeignDataWrapperRoutineClause>,
Review Comment:
```suggestion
/// `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>,
```
##########
src/ast/ddl.rs:
##########
@@ -6078,3 +6132,29 @@ 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 {
+ let routine_span = |clause: &Option<ForeignDataWrapperRoutineClause>|
match clause {
+ Some(ForeignDataWrapperRoutineClause::Function(name)) =>
Some(name.span()),
+ _ => None,
+ };
+ Span::union_iter(
+ core::iter::once(self.name.span)
+ .chain(routine_span(&self.handler))
+ .chain(routine_span(&self.validator))
Review Comment:
You should take the spans from the list.
```suggestion
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,
}))
```
##########
tests/sqlparser_postgres.rs:
##########
@@ -10154,3 +10154,112 @@ fn parse_bitstring_literal_escaping() {
pg_and_generic().verified_stmt("SELECT B''''");
pg_and_generic().verified_stmt("SELECT B'it''s'");
}
+
+#[test]
+fn parse_create_foreign_data_wrapper() {
+ let sql = "CREATE FOREIGN DATA WRAPPER myfdw";
+ let Statement::CreateForeignDataWrapper(stmt) =
pg_and_generic().verified_stmt(sql) else {
+ unreachable!()
+ };
+ assert_eq!(stmt.name.to_string(), "myfdw");
+ assert!(stmt.handler.is_none());
+ assert!(stmt.validator.is_none());
+ assert!(stmt.options.is_none());
+
+ let sql = "CREATE FOREIGN DATA WRAPPER myfdw HANDLER myhandler";
+ let Statement::CreateForeignDataWrapper(stmt) =
pg_and_generic().verified_stmt(sql) else {
+ unreachable!()
+ };
+ assert_eq!(
+ stmt.handler,
+ Some(ForeignDataWrapperRoutineClause::Function(ObjectName::from(
+ vec!["myhandler".into()]
+ )))
+ );
+
+ let sql = "CREATE FOREIGN DATA WRAPPER myfdw NO HANDLER";
+ let Statement::CreateForeignDataWrapper(stmt) =
pg_and_generic().verified_stmt(sql) else {
+ unreachable!()
+ };
+ assert_eq!(stmt.handler, Some(ForeignDataWrapperRoutineClause::Absent));
+
+ let sql = "CREATE FOREIGN DATA WRAPPER myfdw NO VALIDATOR";
+ let Statement::CreateForeignDataWrapper(stmt) =
pg_and_generic().verified_stmt(sql) else {
+ unreachable!()
+ };
+ assert_eq!(
+ stmt.validator,
+ Some(ForeignDataWrapperRoutineClause::Absent)
+ );
+
+ let sql = "CREATE FOREIGN DATA WRAPPER myfdw HANDLER myhandler VALIDATOR
myvalidator OPTIONS (debug 'true')";
+ let Statement::CreateForeignDataWrapper(stmt) =
pg_and_generic().verified_stmt(sql) else {
+ unreachable!()
+ };
+ assert_eq!(
+ stmt.options,
+ Some(vec![CreateServerOption {
+ key: "debug".into(),
+ value: Ident {
+ value: "true".to_string(),
+ quote_style: Some('\''),
+ span: Span::empty(),
+ },
+ }])
+ );
+
+ let sql = "CREATE FOREIGN DATA WRAPPER myfdw NO HANDLER NO VALIDATOR";
+ let Statement::CreateForeignDataWrapper(stmt) =
pg_and_generic().verified_stmt(sql) else {
+ unreachable!()
+ };
+ assert_eq!(stmt.handler, Some(ForeignDataWrapperRoutineClause::Absent));
+ assert_eq!(
+ stmt.validator,
+ Some(ForeignDataWrapperRoutineClause::Absent)
+ );
+
+ // A schema-qualified name is not valid: FDW names are bare identifiers.
+ assert!(matches!(
+ pg_and_generic().parse_sql_statements("CREATE FOREIGN DATA WRAPPER
myschema.myfdw"),
+ Err(ParserError::ParserError(_))
+ ));
+
+ for sql in [
+ "CREATE TEMPORARY FOREIGN DATA WRAPPER myfdw",
+ "CREATE OR ALTER FOREIGN DATA WRAPPER myfdw",
+ "CREATE GLOBAL TEMPORARY FOREIGN DATA WRAPPER myfdw",
+ ] {
+ let err = pg_and_generic().parse_sql_statements(sql).unwrap_err();
+ assert!(
+ err.to_string()
+ .contains("CREATE FOREIGN DATA WRAPPER does not accept this
modifier"),
+ "unexpected error for {sql}: {err}"
+ );
+ }
+}
+
+#[test]
+fn parse_create_foreign_data_wrapper_clause_order() {
+ // PostgreSQL takes these in either order; Display normalizes to HANDLER
first.
+ let canonical = "CREATE FOREIGN DATA WRAPPER myfdw HANDLER myhandler
VALIDATOR myvalidator";
+ pg_and_generic().one_statement_parses_to(
+ "CREATE FOREIGN DATA WRAPPER myfdw VALIDATOR myvalidator HANDLER
myhandler",
+ canonical,
+ );
+ pg_and_generic().one_statement_parses_to(
+ "CREATE FOREIGN DATA WRAPPER myfdw NO VALIDATOR NO HANDLER",
+ "CREATE FOREIGN DATA WRAPPER myfdw NO HANDLER NO VALIDATOR",
+ );
+
+ for sql in [
+ "CREATE FOREIGN DATA WRAPPER myfdw HANDLER h1 HANDLER h2",
+ "CREATE FOREIGN DATA WRAPPER myfdw NO HANDLER HANDLER h",
+ "CREATE FOREIGN DATA WRAPPER myfdw VALIDATOR v NO VALIDATOR",
+ ] {
+ let err = pg_and_generic().parse_sql_statements(sql).unwrap_err();
+ assert!(
+ err.to_string().contains("conflicting or redundant options"),
+ "unexpected error for {sql}: {err}"
+ );
+ }
+}
Review Comment:
You should pin that repeated and reordered clauses round-trip as `pg_query`
accepts all five of these.
```suggestion
fn parse_create_foreign_data_wrapper_clause_order() {
for sql in [
"CREATE FOREIGN DATA WRAPPER myfdw VALIDATOR myvalidator HANDLER
myhandler",
"CREATE FOREIGN DATA WRAPPER myfdw NO VALIDATOR NO HANDLER",
"CREATE FOREIGN DATA WRAPPER myfdw HANDLER h1 HANDLER h2",
"CREATE FOREIGN DATA WRAPPER myfdw NO HANDLER HANDLER h",
"CREATE FOREIGN DATA WRAPPER myfdw VALIDATOR v NO VALIDATOR",
] {
pg_and_generic().verified_stmt(sql);
}
}
```
##########
src/ast/ddl.rs:
##########
@@ -6056,6 +6056,60 @@ impl fmt::Display for CreateForeignTable {
}
}
+/// The handler/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 {
+ /// A named function, e.g. `HANDLER myhandler` or `VALIDATOR myvalidator`.
+ Function(ObjectName),
+ /// The `NO HANDLER` / `NO VALIDATOR` form.
+ Absent,
+}
+
+impl ForeignDataWrapperRoutineClause {
+ fn fmt_with_label(&self, f: &mut fmt::Formatter<'_>, label: &str) ->
fmt::Result {
+ match self {
+ ForeignDataWrapperRoutineClause::Function(name) => write!(f, "
{label} {name}"),
+ ForeignDataWrapperRoutineClause::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. Per PostgreSQL this is a bare
+ /// identifier and cannot be schema-qualified.
+ pub name: Ident,
+ /// Optional `HANDLER handler_function` or `NO HANDLER` clause.
+ pub handler: Option<ForeignDataWrapperRoutineClause>,
+ /// Optional `VALIDATOR validator_function` or `NO VALIDATOR` clause.
+ pub validator: Option<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)?;
+ if let Some(handler) = &self.handler {
+ handler.fmt_with_label(f, "HANDLER")?;
+ }
+ if let Some(validator) = &self.validator {
+ validator.fmt_with_label(f, "VALIDATOR")?;
+ }
Review Comment:
You should render the clauses in the order they were parsed, which also
makes every accepted statement round-trip unchanged.
```suggestion
for routine in &self.routines {
write!(f, " {routine}")?;
}
```
##########
tests/sqlparser_postgres.rs:
##########
@@ -10154,3 +10154,112 @@ fn parse_bitstring_literal_escaping() {
pg_and_generic().verified_stmt("SELECT B''''");
pg_and_generic().verified_stmt("SELECT B'it''s'");
}
+
+#[test]
+fn parse_create_foreign_data_wrapper() {
+ let sql = "CREATE FOREIGN DATA WRAPPER myfdw";
+ let Statement::CreateForeignDataWrapper(stmt) =
pg_and_generic().verified_stmt(sql) else {
+ unreachable!()
+ };
+ assert_eq!(stmt.name.to_string(), "myfdw");
+ assert!(stmt.handler.is_none());
+ assert!(stmt.validator.is_none());
+ assert!(stmt.options.is_none());
+
+ let sql = "CREATE FOREIGN DATA WRAPPER myfdw HANDLER myhandler";
+ let Statement::CreateForeignDataWrapper(stmt) =
pg_and_generic().verified_stmt(sql) else {
+ unreachable!()
+ };
+ assert_eq!(
+ stmt.handler,
+ Some(ForeignDataWrapperRoutineClause::Function(ObjectName::from(
+ vec!["myhandler".into()]
+ )))
+ );
+
+ let sql = "CREATE FOREIGN DATA WRAPPER myfdw NO HANDLER";
+ let Statement::CreateForeignDataWrapper(stmt) =
pg_and_generic().verified_stmt(sql) else {
+ unreachable!()
+ };
+ assert_eq!(stmt.handler, Some(ForeignDataWrapperRoutineClause::Absent));
+
+ let sql = "CREATE FOREIGN DATA WRAPPER myfdw NO VALIDATOR";
+ let Statement::CreateForeignDataWrapper(stmt) =
pg_and_generic().verified_stmt(sql) else {
+ unreachable!()
+ };
+ assert_eq!(
+ stmt.validator,
+ Some(ForeignDataWrapperRoutineClause::Absent)
+ );
+
+ let sql = "CREATE FOREIGN DATA WRAPPER myfdw HANDLER myhandler VALIDATOR
myvalidator OPTIONS (debug 'true')";
+ let Statement::CreateForeignDataWrapper(stmt) =
pg_and_generic().verified_stmt(sql) else {
+ unreachable!()
+ };
+ assert_eq!(
+ stmt.options,
+ Some(vec![CreateServerOption {
+ key: "debug".into(),
+ value: Ident {
+ value: "true".to_string(),
+ quote_style: Some('\''),
+ span: Span::empty(),
+ },
+ }])
+ );
+
+ let sql = "CREATE FOREIGN DATA WRAPPER myfdw NO HANDLER NO VALIDATOR";
+ let Statement::CreateForeignDataWrapper(stmt) =
pg_and_generic().verified_stmt(sql) else {
+ unreachable!()
+ };
+ assert_eq!(stmt.handler, Some(ForeignDataWrapperRoutineClause::Absent));
+ assert_eq!(
+ stmt.validator,
+ Some(ForeignDataWrapperRoutineClause::Absent)
+ );
Review Comment:
You should assert on `routines` here.
```suggestion
assert!(stmt.routines.is_empty());
assert!(stmt.options.is_none());
let sql = "CREATE FOREIGN DATA WRAPPER myfdw HANDLER myhandler";
let Statement::CreateForeignDataWrapper(stmt) =
pg_and_generic().verified_stmt(sql) else {
unreachable!()
};
assert_eq!(
stmt.routines,
vec![ForeignDataWrapperRoutineClause::Handler(ObjectName::from(
vec!["myhandler".into()]
))]
);
let sql = "CREATE FOREIGN DATA WRAPPER myfdw NO HANDLER";
let Statement::CreateForeignDataWrapper(stmt) =
pg_and_generic().verified_stmt(sql) else {
unreachable!()
};
assert_eq!(
stmt.routines,
vec![ForeignDataWrapperRoutineClause::NoHandler]
);
let sql = "CREATE FOREIGN DATA WRAPPER myfdw NO VALIDATOR";
let Statement::CreateForeignDataWrapper(stmt) =
pg_and_generic().verified_stmt(sql) else {
unreachable!()
};
assert_eq!(
stmt.routines,
vec![ForeignDataWrapperRoutineClause::NoValidator]
);
let sql = "CREATE FOREIGN DATA WRAPPER myfdw HANDLER myhandler VALIDATOR
myvalidator OPTIONS (debug 'true')";
let Statement::CreateForeignDataWrapper(stmt) =
pg_and_generic().verified_stmt(sql) else {
unreachable!()
};
assert_eq!(
stmt.options,
Some(vec![CreateServerOption {
key: "debug".into(),
value: Ident {
value: "true".to_string(),
quote_style: Some('\''),
span: Span::empty(),
},
}])
);
let sql = "CREATE FOREIGN DATA WRAPPER myfdw NO HANDLER NO VALIDATOR";
let Statement::CreateForeignDataWrapper(stmt) =
pg_and_generic().verified_stmt(sql) else {
unreachable!()
};
assert_eq!(
stmt.routines,
vec![
ForeignDataWrapperRoutineClause::NoHandler,
ForeignDataWrapperRoutineClause::NoValidator
]
);
```
##########
tests/sqlparser_postgres.rs:
##########
@@ -10154,3 +10154,112 @@ fn parse_bitstring_literal_escaping() {
pg_and_generic().verified_stmt("SELECT B''''");
pg_and_generic().verified_stmt("SELECT B'it''s'");
}
+
+#[test]
+fn parse_create_foreign_data_wrapper() {
+ let sql = "CREATE FOREIGN DATA WRAPPER myfdw";
+ let Statement::CreateForeignDataWrapper(stmt) =
pg_and_generic().verified_stmt(sql) else {
+ unreachable!()
+ };
+ assert_eq!(stmt.name.to_string(), "myfdw");
+ assert!(stmt.handler.is_none());
+ assert!(stmt.validator.is_none());
+ assert!(stmt.options.is_none());
+
+ let sql = "CREATE FOREIGN DATA WRAPPER myfdw HANDLER myhandler";
+ let Statement::CreateForeignDataWrapper(stmt) =
pg_and_generic().verified_stmt(sql) else {
+ unreachable!()
+ };
+ assert_eq!(
+ stmt.handler,
+ Some(ForeignDataWrapperRoutineClause::Function(ObjectName::from(
+ vec!["myhandler".into()]
+ )))
+ );
+
+ let sql = "CREATE FOREIGN DATA WRAPPER myfdw NO HANDLER";
+ let Statement::CreateForeignDataWrapper(stmt) =
pg_and_generic().verified_stmt(sql) else {
+ unreachable!()
+ };
+ assert_eq!(stmt.handler, Some(ForeignDataWrapperRoutineClause::Absent));
+
+ let sql = "CREATE FOREIGN DATA WRAPPER myfdw NO VALIDATOR";
+ let Statement::CreateForeignDataWrapper(stmt) =
pg_and_generic().verified_stmt(sql) else {
+ unreachable!()
+ };
+ assert_eq!(
+ stmt.validator,
+ Some(ForeignDataWrapperRoutineClause::Absent)
+ );
+
+ let sql = "CREATE FOREIGN DATA WRAPPER myfdw HANDLER myhandler VALIDATOR
myvalidator OPTIONS (debug 'true')";
+ let Statement::CreateForeignDataWrapper(stmt) =
pg_and_generic().verified_stmt(sql) else {
+ unreachable!()
+ };
+ assert_eq!(
+ stmt.options,
+ Some(vec![CreateServerOption {
+ key: "debug".into(),
+ value: Ident {
+ value: "true".to_string(),
+ quote_style: Some('\''),
+ span: Span::empty(),
+ },
+ }])
+ );
+
+ let sql = "CREATE FOREIGN DATA WRAPPER myfdw NO HANDLER NO VALIDATOR";
+ let Statement::CreateForeignDataWrapper(stmt) =
pg_and_generic().verified_stmt(sql) else {
+ unreachable!()
+ };
+ assert_eq!(stmt.handler, Some(ForeignDataWrapperRoutineClause::Absent));
+ assert_eq!(
+ stmt.validator,
+ Some(ForeignDataWrapperRoutineClause::Absent)
+ );
+
+ // A schema-qualified name is not valid: FDW names are bare identifiers.
+ assert!(matches!(
+ pg_and_generic().parse_sql_statements("CREATE FOREIGN DATA WRAPPER
myschema.myfdw"),
+ Err(ParserError::ParserError(_))
+ ));
Review Comment:
You should pin the exact error, as #2628 does for the other PostgreSQL
negative tests. `matches!(.., Err(ParserError::ParserError(_)))` still passes
if the statement starts failing earlier for an unrelated reason.
```suggestion
assert_eq!(
pg_and_generic().parse_sql_statements("CREATE FOREIGN DATA WRAPPER
myschema.myfdw"),
Err(ParserError::ParserError(
"Expected: end of statement, found: .".to_string()
))
);
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]