Skip to content

Commit bc1fb61

Browse files
solontseviffyio
authored andcommitted
Support for Postgres CREATE SERVER (apache#1914)
Co-authored-by: Ifeanyi Ubah <ify1992@yahoo.com>
1 parent ae87e50 commit bc1fb61

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
@@ -3347,6 +3347,8 @@ pub enum Statement {
33473347
secret_type: Ident,
33483348
options: Vec<SecretOption>,
33493349
},
3350+
/// A `CREATE SERVER` statement.
3351+
CreateServer(CreateServerStatement),
33503352
/// ```sql
33513353
/// CREATE POLICY
33523354
/// ```
@@ -5207,6 +5209,9 @@ impl fmt::Display for Statement {
52075209
write!(f, " )")?;
52085210
Ok(())
52095211
}
5212+
Statement::CreateServer(stmt) => {
5213+
write!(f, "{stmt}")
5214+
}
52105215
Statement::CreatePolicy {
52115216
name,
52125217
table_name,
@@ -8005,6 +8010,70 @@ impl fmt::Display for SecretOption {
80058010
}
80068011
}
80078012

8013+
/// A `CREATE SERVER` statement.
8014+
///
8015+
/// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-createserver.html)
8016+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
8017+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
8018+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
8019+
pub struct CreateServerStatement {
8020+
pub name: ObjectName,
8021+
pub if_not_exists: bool,
8022+
pub server_type: Option<Ident>,
8023+
pub version: Option<Ident>,
8024+
pub foreign_data_wrapper: ObjectName,
8025+
pub options: Option<Vec<CreateServerOption>>,
8026+
}
8027+
8028+
impl fmt::Display for CreateServerStatement {
8029+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
8030+
let CreateServerStatement {
8031+
name,
8032+
if_not_exists,
8033+
server_type,
8034+
version,
8035+
foreign_data_wrapper,
8036+
options,
8037+
} = self;
8038+
8039+
write!(
8040+
f,
8041+
"CREATE SERVER {if_not_exists}{name} ",
8042+
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
8043+
)?;
8044+
8045+
if let Some(st) = server_type {
8046+
write!(f, "TYPE {st} ")?;
8047+
}
8048+
8049+
if let Some(v) = version {
8050+
write!(f, "VERSION {v} ")?;
8051+
}
8052+
8053+
write!(f, "FOREIGN DATA WRAPPER {foreign_data_wrapper}")?;
8054+
8055+
if let Some(o) = options {
8056+
write!(f, " OPTIONS ({o})", o = display_comma_separated(o))?;
8057+
}
8058+
8059+
Ok(())
8060+
}
8061+
}
8062+
8063+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
8064+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
8065+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
8066+
pub struct CreateServerOption {
8067+
pub key: Ident,
8068+
pub value: Ident,
8069+
}
8070+
8071+
impl fmt::Display for CreateServerOption {
8072+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
8073+
write!(f, "{} {}", self.key, self.value)
8074+
}
8075+
}
8076+
80088077
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
80098078
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
80108079
#[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
@@ -4705,6 +4705,8 @@ impl<'a> Parser<'a> {
47054705
self.parse_create_procedure(or_alter)
47064706
} else if self.parse_keyword(Keyword::CONNECTOR) {
47074707
self.parse_create_connector()
4708+
} else if self.parse_keyword(Keyword::SERVER) {
4709+
self.parse_pg_create_server()
47084710
} else {
47094711
self.expected("an object type after CREATE", self.peek_token())
47104712
}
@@ -16041,6 +16043,49 @@ impl<'a> Parser<'a> {
1604116043
Ok(sequence_options)
1604216044
}
1604316045

16046+
/// Parse a `CREATE SERVER` statement.
16047+
///
16048+
/// See [Statement::CreateServer]
16049+
pub fn parse_pg_create_server(&mut self) -> Result<Statement, ParserError> {
16050+
let ine = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
16051+
let name = self.parse_object_name(false)?;
16052+
16053+
let server_type = if self.parse_keyword(Keyword::TYPE) {
16054+
Some(self.parse_identifier()?)
16055+
} else {
16056+
None
16057+
};
16058+
16059+
let version = if self.parse_keyword(Keyword::VERSION) {
16060+
Some(self.parse_identifier()?)
16061+
} else {
16062+
None
16063+
};
16064+
16065+
self.expect_keywords(&[Keyword::FOREIGN, Keyword::DATA, Keyword::WRAPPER])?;
16066+
let foreign_data_wrapper = self.parse_object_name(false)?;
16067+
16068+
let mut options = None;
16069+
if self.parse_keyword(Keyword::OPTIONS) {
16070+
self.expect_token(&Token::LParen)?;
16071+
options = Some(self.parse_comma_separated(|p| {
16072+
let key = p.parse_identifier()?;
16073+
let value = p.parse_identifier()?;
16074+
Ok(CreateServerOption { key, value })
16075+
})?);
16076+
self.expect_token(&Token::RParen)?;
16077+
}
16078+
16079+
Ok(Statement::CreateServer(CreateServerStatement {
16080+
name,
16081+
if_not_exists: ine,
16082+
server_type,
16083+
version,
16084+
foreign_data_wrapper,
16085+
options,
16086+
}))
16087+
}
16088+
1604416089
/// The index of the first unprocessed token.
1604516090
pub fn index(&self) -> usize {
1604616091
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)