Skip to content

Commit 239e30a

Browse files
solontseviffyio
andauthored
Support for Postgres CREATE SERVER (#1914)
Co-authored-by: Ifeanyi Ubah <ify1992@yahoo.com>
1 parent 9020385 commit 239e30a

File tree

5 files changed

+196
-0
lines changed

5 files changed

+196
-0
lines changed

src/ast/mod.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3318,6 +3318,8 @@ pub enum Statement {
33183318
secret_type: Ident,
33193319
options: Vec<SecretOption>,
33203320
},
3321+
/// A `CREATE SERVER` statement.
3322+
CreateServer(CreateServerStatement),
33213323
/// ```sql
33223324
/// CREATE POLICY
33233325
/// ```
@@ -5178,6 +5180,9 @@ impl fmt::Display for Statement {
51785180
write!(f, " )")?;
51795181
Ok(())
51805182
}
5183+
Statement::CreateServer(stmt) => {
5184+
write!(f, "{stmt}")
5185+
}
51815186
Statement::CreatePolicy {
51825187
name,
51835188
table_name,
@@ -7976,6 +7981,70 @@ impl fmt::Display for SecretOption {
79767981
}
79777982
}
79787983

7984+
/// A `CREATE SERVER` statement.
7985+
///
7986+
/// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-createserver.html)
7987+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
7988+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
7989+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
7990+
pub struct CreateServerStatement {
7991+
pub name: ObjectName,
7992+
pub if_not_exists: bool,
7993+
pub server_type: Option<Ident>,
7994+
pub version: Option<Ident>,
7995+
pub foreign_data_wrapper: ObjectName,
7996+
pub options: Option<Vec<CreateServerOption>>,
7997+
}
7998+
7999+
impl fmt::Display for CreateServerStatement {
8000+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
8001+
let CreateServerStatement {
8002+
name,
8003+
if_not_exists,
8004+
server_type,
8005+
version,
8006+
foreign_data_wrapper,
8007+
options,
8008+
} = self;
8009+
8010+
write!(
8011+
f,
8012+
"CREATE SERVER {if_not_exists}{name} ",
8013+
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
8014+
)?;
8015+
8016+
if let Some(st) = server_type {
8017+
write!(f, "TYPE {st} ")?;
8018+
}
8019+
8020+
if let Some(v) = version {
8021+
write!(f, "VERSION {v} ")?;
8022+
}
8023+
8024+
write!(f, "FOREIGN DATA WRAPPER {foreign_data_wrapper}")?;
8025+
8026+
if let Some(o) = options {
8027+
write!(f, " OPTIONS ({o})", o = display_comma_separated(o))?;
8028+
}
8029+
8030+
Ok(())
8031+
}
8032+
}
8033+
8034+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
8035+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
8036+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
8037+
pub struct CreateServerOption {
8038+
pub key: Ident,
8039+
pub value: Ident,
8040+
}
8041+
8042+
impl fmt::Display for CreateServerOption {
8043+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
8044+
write!(f, "{} {}", self.key, self.value)
8045+
}
8046+
}
8047+
79798048
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
79808049
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
79818050
#[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,
@@ -1017,6 +1018,7 @@ define_keywords!(
10171018
WITHOUT,
10181019
WITHOUT_ARRAY_WRAPPER,
10191020
WORK,
1021+
WRAPPER,
10201022
WRITE,
10211023
XML,
10221024
XMLNAMESPACES,

src/parser/mod.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4674,6 +4674,8 @@ impl<'a> Parser<'a> {
46744674
self.parse_create_procedure(or_alter)
46754675
} else if self.parse_keyword(Keyword::CONNECTOR) {
46764676
self.parse_create_connector()
4677+
} else if self.parse_keyword(Keyword::SERVER) {
4678+
self.parse_pg_create_server()
46774679
} else {
46784680
self.expected("an object type after CREATE", self.peek_token())
46794681
}
@@ -16009,6 +16011,49 @@ impl<'a> Parser<'a> {
1600916011
Ok(sequence_options)
1601016012
}
1601116013

16014+
/// Parse a `CREATE SERVER` statement.
16015+
///
16016+
/// See [Statement::CreateServer]
16017+
pub fn parse_pg_create_server(&mut self) -> Result<Statement, ParserError> {
16018+
let ine = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
16019+
let name = self.parse_object_name(false)?;
16020+
16021+
let server_type = if self.parse_keyword(Keyword::TYPE) {
16022+
Some(self.parse_identifier()?)
16023+
} else {
16024+
None
16025+
};
16026+
16027+
let version = if self.parse_keyword(Keyword::VERSION) {
16028+
Some(self.parse_identifier()?)
16029+
} else {
16030+
None
16031+
};
16032+
16033+
self.expect_keywords(&[Keyword::FOREIGN, Keyword::DATA, Keyword::WRAPPER])?;
16034+
let foreign_data_wrapper = self.parse_object_name(false)?;
16035+
16036+
let mut options = None;
16037+
if self.parse_keyword(Keyword::OPTIONS) {
16038+
self.expect_token(&Token::LParen)?;
16039+
options = Some(self.parse_comma_separated(|p| {
16040+
let key = p.parse_identifier()?;
16041+
let value = p.parse_identifier()?;
16042+
Ok(CreateServerOption { key, value })
16043+
})?);
16044+
self.expect_token(&Token::RParen)?;
16045+
}
16046+
16047+
Ok(Statement::CreateServer(CreateServerStatement {
16048+
name,
16049+
if_not_exists: ine,
16050+
server_type,
16051+
version,
16052+
foreign_data_wrapper,
16053+
options,
16054+
}))
16055+
}
16056+
1601216057
/// The index of the first unprocessed token.
1601316058
pub fn index(&self) -> usize {
1601416059
self.index

tests/sqlparser_postgres.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6273,3 +6273,82 @@ fn parse_alter_table_validate_constraint() {
62736273
_ => unreachable!(),
62746274
}
62756275
}
6276+
6277+
#[test]
6278+
fn parse_create_server() {
6279+
let test_cases = vec![
6280+
(
6281+
"CREATE SERVER myserver FOREIGN DATA WRAPPER postgres_fdw",
6282+
CreateServerStatement {
6283+
name: ObjectName::from(vec!["myserver".into()]),
6284+
if_not_exists: false,
6285+
server_type: None,
6286+
version: None,
6287+
foreign_data_wrapper: ObjectName::from(vec!["postgres_fdw".into()]),
6288+
options: None,
6289+
},
6290+
),
6291+
(
6292+
"CREATE SERVER IF NOT EXISTS myserver TYPE 'server_type' VERSION 'server_version' FOREIGN DATA WRAPPER postgres_fdw",
6293+
CreateServerStatement {
6294+
name: ObjectName::from(vec!["myserver".into()]),
6295+
if_not_exists: true,
6296+
server_type: Some(Ident {
6297+
value: "server_type".to_string(),
6298+
quote_style: Some('\''),
6299+
span: Span::empty(),
6300+
}),
6301+
version: Some(Ident {
6302+
value: "server_version".to_string(),
6303+
quote_style: Some('\''),
6304+
span: Span::empty(),
6305+
}),
6306+
foreign_data_wrapper: ObjectName::from(vec!["postgres_fdw".into()]),
6307+
options: None,
6308+
}
6309+
),
6310+
(
6311+
"CREATE SERVER myserver2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host 'foo', dbname 'foodb', port '5432')",
6312+
CreateServerStatement {
6313+
name: ObjectName::from(vec!["myserver2".into()]),
6314+
if_not_exists: false,
6315+
server_type: None,
6316+
version: None,
6317+
foreign_data_wrapper: ObjectName::from(vec!["postgres_fdw".into()]),
6318+
options: Some(vec![
6319+
CreateServerOption {
6320+
key: "host".into(),
6321+
value: Ident {
6322+
value: "foo".to_string(),
6323+
quote_style: Some('\''),
6324+
span: Span::empty(),
6325+
},
6326+
},
6327+
CreateServerOption {
6328+
key: "dbname".into(),
6329+
value: Ident {
6330+
value: "foodb".to_string(),
6331+
quote_style: Some('\''),
6332+
span: Span::empty(),
6333+
},
6334+
},
6335+
CreateServerOption {
6336+
key: "port".into(),
6337+
value: Ident {
6338+
value: "5432".to_string(),
6339+
quote_style: Some('\''),
6340+
span: Span::empty(),
6341+
},
6342+
},
6343+
]),
6344+
}
6345+
)
6346+
];
6347+
6348+
for (sql, expected) in test_cases {
6349+
let Statement::CreateServer(stmt) = pg_and_generic().verified_stmt(sql) else {
6350+
unreachable!()
6351+
};
6352+
assert_eq!(stmt, expected);
6353+
}
6354+
}

0 commit comments

Comments
 (0)