diff --git a/src/ast/data_type.rs b/src/ast/data_type.rs index 02aa6cc9f..1f2b6be97 100644 --- a/src/ast/data_type.rs +++ b/src/ast/data_type.rs @@ -45,6 +45,10 @@ pub enum EnumMember { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub enum DataType { + /// Table type in [postgresql]. e.g. CREATE FUNCTION RETURNS TABLE(...) + /// + /// [postgresql]: https://www.postgresql.org/docs/15/sql-createfunction.html + Table(Vec), /// Fixed-length character type e.g. CHARACTER(10) Character(Option), /// Fixed-length char type e.g. CHAR(10) @@ -630,6 +634,7 @@ impl fmt::Display for DataType { DataType::Unspecified => Ok(()), DataType::Trigger => write!(f, "TRIGGER"), DataType::AnyType => write!(f, "ANY TYPE"), + DataType::Table(fields) => write!(f, "TABLE({})", display_comma_separated(fields)), } } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index ca858c42e..4cb90b05e 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -8867,6 +8867,10 @@ impl<'a> Parser<'a> { let _ = self.parse_keyword(Keyword::TYPE); Ok(DataType::AnyType) } + Keyword::TABLE => { + let columns = self.parse_returns_table_columns()?; + Ok(DataType::Table(columns)) + } _ => { self.prev_token(); let type_name = self.parse_object_name(false)?; @@ -8894,6 +8898,24 @@ impl<'a> Parser<'a> { Ok((data, trailing_bracket)) } + fn parse_returns_table_column(&mut self) -> Result { + let name = self.parse_identifier()?; + let data_type = self.parse_data_type()?; + Ok(ColumnDef { + name, + data_type, + collation: None, + options: Vec::new(), // No constraints expected here + }) + } + + fn parse_returns_table_columns(&mut self) -> Result, ParserError> { + self.expect_token(&Token::LParen)?; + let columns = self.parse_comma_separated(Parser::parse_returns_table_column)?; + self.expect_token(&Token::RParen)?; + Ok(columns) + } + pub fn parse_string_values(&mut self) -> Result, ParserError> { self.expect_token(&Token::LParen)?; let mut values = Vec::new(); diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index ee4aa2a0a..62da0f574 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -3803,6 +3803,7 @@ fn parse_create_function_detailed() { pg_and_generic().verified_stmt("CREATE OR REPLACE FUNCTION add(a INTEGER, IN b INTEGER = 1) RETURNS INTEGER LANGUAGE SQL STABLE CALLED ON NULL INPUT PARALLEL UNSAFE RETURN a + b"); pg_and_generic().verified_stmt(r#"CREATE OR REPLACE FUNCTION increment(i INTEGER) RETURNS INTEGER LANGUAGE plpgsql AS $$ BEGIN RETURN i + 1; END; $$"#); pg_and_generic().verified_stmt(r#"CREATE OR REPLACE FUNCTION no_arg() RETURNS VOID LANGUAGE plpgsql AS $$ BEGIN DELETE FROM my_table; END; $$"#); + pg_and_generic().verified_stmt(r#"CREATE OR REPLACE FUNCTION return_table(i INTEGER) RETURNS TABLE(id UUID, is_active BOOLEAN) LANGUAGE plpgsql AS $$ BEGIN RETURN QUERY SELECT NULL::UUID, NULL::BOOLEAN; END; $$"#); } #[test] fn parse_incorrect_create_function_parallel() {