Skip to content

Support for Postgres's CREATE SERVER #1914

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
69 changes: 69 additions & 0 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3315,6 +3315,8 @@ pub enum Statement {
secret_type: Ident,
options: Vec<SecretOption>,
},
/// A `CREATE SERVER` statement.
CreateServer(CreateServerStatement),
/// ```sql
/// CREATE POLICY
/// ```
Expand Down Expand Up @@ -5175,6 +5177,9 @@ impl fmt::Display for Statement {
write!(f, " )")?;
Ok(())
}
Statement::CreateServer(stmt) => {
write!(f, "{stmt}")
}
Statement::CreatePolicy {
name,
table_name,
Expand Down Expand Up @@ -7973,6 +7978,70 @@ impl fmt::Display for SecretOption {
}
}

/// A `CREATE SERVER` statement.
///
/// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-createserver.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 CreateServerStatement {
pub name: ObjectName,
pub if_not_exists: bool,
pub server_type: Option<Ident>,
pub version: Option<Ident>,
pub foreign_data_wrapper: ObjectName,
pub options: Option<Vec<CreateServerOption>>,
}

impl fmt::Display for CreateServerStatement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let CreateServerStatement {
name,
if_not_exists,
server_type,
version,
foreign_data_wrapper,
options,
} = self;

write!(
f,
"CREATE SERVER {if_not_exists}{name} ",
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
)?;

if let Some(st) = server_type {
write!(f, "TYPE {st} ")?;
}

if let Some(v) = version {
write!(f, "VERSION {v} ")?;
}

write!(f, "FOREIGN DATA WRAPPER {foreign_data_wrapper}")?;

if let Some(o) = options {
write!(f, " OPTIONS ({o})", o = display_comma_separated(o))?;
}

Ok(())
}
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct CreateServerOption {
pub key: Ident,
pub value: Ident,
}

impl fmt::Display for CreateServerOption {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", self.key, self.value)
}
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
Expand Down
1 change: 1 addition & 0 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,7 @@ impl Spanned for Statement {
Statement::CreateIndex(create_index) => create_index.span(),
Statement::CreateRole { .. } => Span::empty(),
Statement::CreateSecret { .. } => Span::empty(),
Statement::CreateServer { .. } => Span::empty(),
Statement::CreateConnector { .. } => Span::empty(),
Statement::AlterTable {
name,
Expand Down
2 changes: 2 additions & 0 deletions src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -816,6 +816,7 @@ define_keywords!(
SERDE,
SERDEPROPERTIES,
SERIALIZABLE,
SERVER,
SERVICE,
SESSION,
SESSION_USER,
Expand Down Expand Up @@ -1016,6 +1017,7 @@ define_keywords!(
WITHOUT,
WITHOUT_ARRAY_WRAPPER,
WORK,
WRAPPER,
WRITE,
XML,
XMLNAMESPACES,
Expand Down
45 changes: 45 additions & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4662,6 +4662,8 @@ impl<'a> Parser<'a> {
self.parse_create_procedure(or_alter)
} else if self.parse_keyword(Keyword::CONNECTOR) {
self.parse_create_connector()
} else if self.parse_keyword(Keyword::SERVER) {
self.parse_pg_create_server()
} else {
self.expected("an object type after CREATE", self.peek_token())
}
Expand Down Expand Up @@ -15806,6 +15808,49 @@ impl<'a> Parser<'a> {
Ok(sequence_options)
}

/// Parse a `CREATE SERVER` statement.
///
/// See [Statement::CreateServer]
pub fn parse_pg_create_server(&mut self) -> Result<Statement, ParserError> {
let ine = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
let name = self.parse_object_name(false)?;

let server_type = if self.parse_keyword(Keyword::TYPE) {
Some(self.parse_identifier()?)
} else {
None
};

let version = if self.parse_keyword(Keyword::VERSION) {
Some(self.parse_identifier()?)
} else {
None
};

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)?;
}

Ok(Statement::CreateServer(CreateServerStatement {
name,
if_not_exists: ine,
server_type,
version,
foreign_data_wrapper,
options,
}))
}

/// The index of the first unprocessed token.
pub fn index(&self) -> usize {
self.index
Expand Down
79 changes: 79 additions & 0 deletions tests/sqlparser_postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6231,3 +6231,82 @@ fn parse_ts_datatypes() {
_ => unreachable!(),
}
}

#[test]
fn parse_create_server() {
let test_cases = vec![
(
"CREATE SERVER myserver FOREIGN DATA WRAPPER postgres_fdw",
CreateServerStatement {
name: ObjectName::from(vec!["myserver".into()]),
if_not_exists: false,
server_type: None,
version: None,
foreign_data_wrapper: ObjectName::from(vec!["postgres_fdw".into()]),
options: None,
},
),
(
"CREATE SERVER IF NOT EXISTS myserver TYPE 'server_type' VERSION 'server_version' FOREIGN DATA WRAPPER postgres_fdw",
CreateServerStatement {
name: ObjectName::from(vec!["myserver".into()]),
if_not_exists: true,
server_type: Some(Ident {
value: "server_type".to_string(),
quote_style: Some('\''),
span: Span::empty(),
}),
version: Some(Ident {
value: "server_version".to_string(),
quote_style: Some('\''),
span: Span::empty(),
}),
foreign_data_wrapper: ObjectName::from(vec!["postgres_fdw".into()]),
options: None,
}
),
(
"CREATE SERVER myserver2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host 'foo', dbname 'foodb', port '5432')",
CreateServerStatement {
name: ObjectName::from(vec!["myserver2".into()]),
if_not_exists: false,
server_type: None,
version: None,
foreign_data_wrapper: ObjectName::from(vec!["postgres_fdw".into()]),
options: Some(vec![
CreateServerOption {
key: "host".into(),
value: Ident {
value: "foo".to_string(),
quote_style: Some('\''),
span: Span::empty(),
},
},
CreateServerOption {
key: "dbname".into(),
value: Ident {
value: "foodb".to_string(),
quote_style: Some('\''),
span: Span::empty(),
},
},
CreateServerOption {
key: "port".into(),
value: Ident {
value: "5432".to_string(),
quote_style: Some('\''),
span: Span::empty(),
},
},
]),
}
)
];

for (sql, expected) in test_cases {
let Statement::CreateServer(stmt) = pg_and_generic().verified_stmt(sql) else {
unreachable!()
};
assert_eq!(stmt, expected);
}
}