Skip to content

Commit 2e5f356

Browse files
committed
Support for Postgres's CREATE SERVER
1 parent 50c605a commit 2e5f356

5 files changed

Lines changed: 184 additions & 1 deletion

File tree

src/ast/mod.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3316,6 +3316,18 @@ pub enum Statement {
33163316
options: Vec<SecretOption>,
33173317
},
33183318
/// ```sql
3319+
/// CREATE SERVER
3320+
/// ```
3321+
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createserver.html)
3322+
CreateServer {
3323+
name: ObjectName,
3324+
if_not_exists: bool,
3325+
server_type: Option<Ident>,
3326+
version: Option<Ident>,
3327+
fdw_name: ObjectName,
3328+
options: Option<Vec<ServerOption>>,
3329+
},
3330+
/// ```sql
33193331
/// CREATE POLICY
33203332
/// ```
33213333
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createpolicy.html)
@@ -5175,6 +5187,36 @@ impl fmt::Display for Statement {
51755187
write!(f, " )")?;
51765188
Ok(())
51775189
}
5190+
Statement::CreateServer {
5191+
name,
5192+
if_not_exists,
5193+
server_type,
5194+
version,
5195+
fdw_name,
5196+
options
5197+
} => {
5198+
write!(
5199+
f,
5200+
"CREATE SERVER {if_not_exists}{name} ",
5201+
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
5202+
)?;
5203+
5204+
if let Some(st) = server_type {
5205+
write!(f, "TYPE {st} ")?;
5206+
}
5207+
5208+
if let Some(v) = version {
5209+
write!(f, "VERSION {v} ")?;
5210+
}
5211+
5212+
write!(f, "FOREIGN DATA WRAPPER {fdw_name}")?;
5213+
5214+
if let Some(o) = options {
5215+
write!(f, " OPTIONS ({o})", o = display_comma_separated(o))?;
5216+
}
5217+
5218+
Ok(())
5219+
}
51785220
Statement::CreatePolicy {
51795221
name,
51805222
table_name,
@@ -7973,6 +8015,20 @@ impl fmt::Display for SecretOption {
79738015
}
79748016
}
79758017

8018+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
8019+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
8020+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
8021+
pub struct ServerOption {
8022+
pub key: Ident,
8023+
pub value: Ident,
8024+
}
8025+
8026+
impl fmt::Display for ServerOption {
8027+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
8028+
write!(f, "{} {}", self.key, self.value)
8029+
}
8030+
}
8031+
79768032
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
79778033
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
79788034
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]

src/ast/spans.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,7 @@ impl Spanned for Statement {
423423
Statement::CreateIndex(create_index) => create_index.span(),
424424
Statement::CreateRole { .. } => Span::empty(),
425425
Statement::CreateSecret { .. } => Span::empty(),
426+
Statement::CreateServer { .. } => Span::empty(),
426427
Statement::CreateConnector { .. } => Span::empty(),
427428
Statement::AlterTable {
428429
name,

src/keywords.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,7 @@ define_keywords!(
816816
SERDE,
817817
SERDEPROPERTIES,
818818
SERIALIZABLE,
819+
SERVER,
819820
SERVICE,
820821
SESSION,
821822
SESSION_USER,
@@ -1016,6 +1017,7 @@ define_keywords!(
10161017
WITHOUT,
10171018
WITHOUT_ARRAY_WRAPPER,
10181019
WORK,
1020+
WRAPPER,
10191021
WRITE,
10201022
XML,
10211023
XMLNAMESPACES,

src/parser/mod.rs

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use IsLateral::*;
3333
use IsOptional::*;
3434

3535
use crate::ast::helpers::stmt_create_table::{CreateTableBuilder, CreateTableConfiguration};
36-
use crate::ast::Statement::CreatePolicy;
36+
use crate::ast::Statement::{CreatePolicy, CreateServer};
3737
use crate::ast::*;
3838
use crate::dialect::*;
3939
use crate::keywords::{Keyword, ALL_KEYWORDS};
@@ -4662,6 +4662,8 @@ impl<'a> Parser<'a> {
46624662
self.parse_create_procedure(or_alter)
46634663
} else if self.parse_keyword(Keyword::CONNECTOR) {
46644664
self.parse_create_connector()
4665+
} else if self.parse_keyword(Keyword::SERVER) && dialect_of!(self is PostgreSqlDialect | GenericDialect) {
4666+
self.parse_pg_create_server()
46654667
} else {
46664668
self.expected("an object type after CREATE", self.peek_token())
46674669
}
@@ -15806,6 +15808,56 @@ impl<'a> Parser<'a> {
1580615808
Ok(sequence_options)
1580715809
}
1580815810

15811+
/// ```sql
15812+
/// CREATE SERVER [ IF NOT EXISTS ] server_name [ TYPE 'server_type' ] [ VERSION 'server_version' ]
15813+
/// FOREIGN DATA WRAPPER fdw_name
15814+
/// [ OPTIONS ( option 'value' [, ... ] ) ]
15815+
/// ```
15816+
///
15817+
/// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-createserver.html)
15818+
pub fn parse_pg_create_server(&mut self) -> Result<Statement, ParserError> {
15819+
let ine = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
15820+
let name = self.parse_object_name(false)?;
15821+
15822+
let server_type = if self.parse_keyword(Keyword::TYPE) {
15823+
Some(self.parse_identifier()?)
15824+
} else {
15825+
None
15826+
};
15827+
15828+
let version = if self.parse_keyword(Keyword::VERSION) {
15829+
Some(self.parse_identifier()?)
15830+
} else {
15831+
None
15832+
};
15833+
15834+
self.expect_keyword_is(Keyword::FOREIGN)?;
15835+
self.expect_keyword_is(Keyword::DATA)?;
15836+
self.expect_keyword_is(Keyword::WRAPPER)?;
15837+
15838+
let fdw_name = self.parse_object_name(false)?;
15839+
15840+
let mut options = None;
15841+
if self.parse_keyword(Keyword::OPTIONS) {
15842+
self.expect_token(&Token::LParen)?;
15843+
options = Some(self.parse_comma_separated(|p| {
15844+
let key = p.parse_identifier()?;
15845+
let value = p.parse_identifier()?;
15846+
Ok(ServerOption { key, value })
15847+
})?);
15848+
self.expect_token(&Token::RParen)?;
15849+
}
15850+
15851+
Ok(CreateServer {
15852+
name,
15853+
if_not_exists: ine,
15854+
server_type,
15855+
version,
15856+
fdw_name,
15857+
options,
15858+
})
15859+
}
15860+
1580915861
/// The index of the first unprocessed token.
1581015862
pub fn index(&self) -> usize {
1581115863
self.index

tests/sqlparser_postgres.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6231,3 +6231,75 @@ fn parse_ts_datatypes() {
62316231
_ => unreachable!(),
62326232
}
62336233
}
6234+
6235+
#[test]
6236+
fn parse_create_server() {
6237+
assert_eq!(
6238+
pg_and_generic().verified_stmt("CREATE SERVER myserver FOREIGN DATA WRAPPER postgres_fdw"),
6239+
Statement::CreateServer {
6240+
name: ObjectName::from(vec!["myserver".into()]),
6241+
if_not_exists: false,
6242+
server_type: None,
6243+
version: None,
6244+
fdw_name: ObjectName::from(vec!["postgres_fdw".into()]),
6245+
options: None,
6246+
}
6247+
);
6248+
6249+
assert_eq!(
6250+
pg_and_generic().verified_stmt("CREATE SERVER IF NOT EXISTS myserver TYPE 'server_type' VERSION 'server_version' FOREIGN DATA WRAPPER postgres_fdw"),
6251+
Statement::CreateServer {
6252+
name: ObjectName::from(vec!["myserver".into()]),
6253+
if_not_exists: true,
6254+
server_type: Some(Ident {
6255+
value: "server_type".to_string(),
6256+
quote_style: Some('\''),
6257+
span: Span::empty(),
6258+
}),
6259+
version: Some(Ident {
6260+
value: "server_version".to_string(),
6261+
quote_style: Some('\''),
6262+
span: Span::empty(),
6263+
}),
6264+
fdw_name: ObjectName::from(vec!["postgres_fdw".into()]),
6265+
options: None,
6266+
}
6267+
);
6268+
6269+
assert_eq!(
6270+
pg_and_generic().verified_stmt("CREATE SERVER myserver2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host 'foo', dbname 'foodb', port '5432')"),
6271+
Statement::CreateServer {
6272+
name: ObjectName::from(vec!["myserver2".into()]),
6273+
if_not_exists: false,
6274+
server_type: None,
6275+
version: None,
6276+
fdw_name: ObjectName::from(vec!["postgres_fdw".into()]),
6277+
options: Some(vec![
6278+
ServerOption {
6279+
key: "host".into(),
6280+
value: Ident {
6281+
value: "foo".to_string(),
6282+
quote_style: Some('\''),
6283+
span: Span::empty(),
6284+
},
6285+
},
6286+
ServerOption {
6287+
key: "dbname".into(),
6288+
value: Ident {
6289+
value: "foodb".to_string(),
6290+
quote_style: Some('\''),
6291+
span: Span::empty(),
6292+
},
6293+
},
6294+
ServerOption {
6295+
key: "port".into(),
6296+
value: Ident {
6297+
value: "5432".to_string(),
6298+
quote_style: Some('\''),
6299+
span: Span::empty(),
6300+
},
6301+
},
6302+
]),
6303+
}
6304+
)
6305+
}

0 commit comments

Comments
 (0)