Skip to content
56 changes: 56 additions & 0 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3316,6 +3316,18 @@ pub enum Statement {
options: Vec<SecretOption>,
},
/// ```sql
/// CREATE SERVER
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createserver.html)
CreateServer {
name: ObjectName,
if_not_exists: bool,
server_type: Option<Ident>,
version: Option<Ident>,
fdw_name: ObjectName,
options: Option<Vec<ServerOption>>,
},
/// ```sql
/// CREATE POLICY
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createpolicy.html)
Expand Down Expand Up @@ -5175,6 +5187,36 @@ impl fmt::Display for Statement {
write!(f, " )")?;
Ok(())
}
Statement::CreateServer {
name,
if_not_exists,
server_type,
version,
fdw_name,
options,
} => {
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 {fdw_name}")?;

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

Ok(())
}
Statement::CreatePolicy {
name,
table_name,
Expand Down Expand Up @@ -7973,6 +8015,20 @@ impl fmt::Display for SecretOption {
}
}

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

impl fmt::Display for ServerOption {
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
53 changes: 52 additions & 1 deletion src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use IsLateral::*;
use IsOptional::*;

use crate::ast::helpers::stmt_create_table::{CreateTableBuilder, CreateTableConfiguration};
use crate::ast::Statement::CreatePolicy;
use crate::ast::Statement::{CreatePolicy, CreateServer};
use crate::ast::*;
use crate::dialect::*;
use crate::keywords::{Keyword, ALL_KEYWORDS};
Expand Down Expand Up @@ -4662,6 +4662,10 @@ 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)
&& dialect_of!(self is PostgreSqlDialect | GenericDialect)
{
self.parse_pg_create_server()
} else {
self.expected("an object type after CREATE", self.peek_token())
}
Expand Down Expand Up @@ -15806,6 +15810,53 @@ impl<'a> Parser<'a> {
Ok(sequence_options)
}

/// ```sql
/// CREATE SERVER [ IF NOT EXISTS ] server_name [ TYPE 'server_type' ] [ VERSION 'server_version' ]
/// FOREIGN DATA WRAPPER fdw_name
/// [ OPTIONS ( option 'value' [, ... ] ) ]
/// ```
///
/// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-createserver.html)
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 fdw_name = 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(ServerOption { key, value })
})?);
self.expect_token(&Token::RParen)?;
}

Ok(CreateServer {
name,
if_not_exists: ine,
server_type,
version,
fdw_name,
options,
})
}

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

#[test]
fn parse_create_server() {
assert_eq!(
pg_and_generic().verified_stmt("CREATE SERVER myserver FOREIGN DATA WRAPPER postgres_fdw"),
Statement::CreateServer {
name: ObjectName::from(vec!["myserver".into()]),
if_not_exists: false,
server_type: None,
version: None,
fdw_name: ObjectName::from(vec!["postgres_fdw".into()]),
options: None,
}
);

assert_eq!(
pg_and_generic().verified_stmt("CREATE SERVER IF NOT EXISTS myserver TYPE 'server_type' VERSION 'server_version' FOREIGN DATA WRAPPER postgres_fdw"),
Statement::CreateServer {
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(),
}),
fdw_name: ObjectName::from(vec!["postgres_fdw".into()]),
options: None,
}
);

assert_eq!(
pg_and_generic().verified_stmt("CREATE SERVER myserver2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host 'foo', dbname 'foodb', port '5432')"),
Statement::CreateServer {
name: ObjectName::from(vec!["myserver2".into()]),
if_not_exists: false,
server_type: None,
version: None,
fdw_name: ObjectName::from(vec!["postgres_fdw".into()]),
options: Some(vec![
ServerOption {
key: "host".into(),
value: Ident {
value: "foo".to_string(),
quote_style: Some('\''),
span: Span::empty(),
},
},
ServerOption {
key: "dbname".into(),
value: Ident {
value: "foodb".to_string(),
quote_style: Some('\''),
span: Span::empty(),
},
},
ServerOption {
key: "port".into(),
value: Ident {
value: "5432".to_string(),
quote_style: Some('\''),
span: Span::empty(),
},
},
]),
}
)
}