diff --git a/fuzz/fuzz_seeds/postgres_alter_extension.sql b/fuzz/fuzz_seeds/postgres_alter_extension.sql index 593de1f0f1..596c32be97 100644 --- a/fuzz/fuzz_seeds/postgres_alter_extension.sql +++ b/fuzz/fuzz_seeds/postgres_alter_extension.sql @@ -26,3 +26,4 @@ ALTER EXTENSION citext ADD ROLE role_name; ALTER EXTENSION citext DROP STATISTICS public.st; ALTER EXTENSION citext DROP SUBSCRIPTION sub; ALTER EXTENSION citext ADD TABLESPACE ts; +ALTER EXTENSION citext ADD FUNCTION public.f(IN a int, text); diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index c1fe3cd604..830d7a8769 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -4946,6 +4946,8 @@ pub enum AlterExtensionMember { ForeignDataWrapper(Ident), /// `FOREIGN TABLE object_name` ForeignTable(ObjectName), + /// `FUNCTION function_name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ]` + Function(FunctionDesc), /// `INDEX object_name` Index(ObjectName), /// `[ PROCEDURAL ] LANGUAGE object_name` @@ -5019,6 +5021,7 @@ impl fmt::Display for AlterExtensionMember { write!(f, "FOREIGN DATA WRAPPER {name}") } AlterExtensionMember::ForeignTable(name) => write!(f, "FOREIGN TABLE {name}"), + AlterExtensionMember::Function(function) => write!(f, "FUNCTION {function}"), AlterExtensionMember::Index(name) => write!(f, "INDEX {name}"), AlterExtensionMember::Language { procedural, name } => { if *procedural { diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 19eec492a9..f321d18457 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -12563,6 +12563,8 @@ impl<'a> Parser<'a> { Ok(AlterExtensionMember::ForeignTable( self.parse_object_name(false)?, )) + } else if self.parse_keyword(Keyword::FUNCTION) { + Ok(AlterExtensionMember::Function(self.parse_function_desc()?)) } else if self.parse_keyword(Keyword::INDEX) { Ok(AlterExtensionMember::Index(self.parse_object_name(false)?)) } else if self.parse_keywords(&[Keyword::PROCEDURAL, Keyword::LANGUAGE]) { diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index b8d2598472..c91c42cd90 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -13538,6 +13538,10 @@ fn alter_extension_member_round_trip_forms(action: &str) { format!("ALTER EXTENSION ext_name {action} FOREIGN DATA WRAPPER fdw"), format!("ALTER EXTENSION ext_name {action} FOREIGN TABLE ft"), format!("ALTER EXTENSION ext_name {action} FOREIGN TABLE schema.ft"), + format!("ALTER EXTENSION ext_name {action} FUNCTION f"), + format!("ALTER EXTENSION ext_name {action} FUNCTION f()"), + format!("ALTER EXTENSION ext_name {action} FUNCTION f(INT)"), + format!("ALTER EXTENSION ext_name {action} FUNCTION f(INT, TEXT)"), format!("ALTER EXTENSION ext_name {action} INDEX idx"), format!("ALTER EXTENSION ext_name {action} LANGUAGE plpgsql"), format!("ALTER EXTENSION ext_name {action} PROCEDURAL LANGUAGE plpgsql"), @@ -13697,6 +13701,20 @@ fn parse_alter_extension_add_member() { other => panic!("unexpected statement: {other:?}"), } + match pg_and_generic().verified_stmt("ALTER EXTENSION ext_name ADD FUNCTION f(INT)") { + Statement::AlterExtension(AlterExtension { + operation: AlterExtensionOperation::Add(AlterExtensionMember::Function(function)), + .. + }) => { + assert_eq!(function.name, ObjectName::from(Ident::new("f"))); + assert_eq!( + function.args, + Some(vec![OperateFunctionArg::unnamed(DataType::Int(None))]) + ); + } + other => panic!("unexpected statement: {other:?}"), + } + match pg_and_generic().verified_stmt("ALTER EXTENSION ext_name ADD PROCEDURAL LANGUAGE plpgsql") { Statement::AlterExtension(AlterExtension { @@ -13723,6 +13741,10 @@ fn parse_alter_extension_add_member() { "ALTER EXTENSION ext_name ADD SCHEMA a.b", "Expected: end of statement, found: .", ), + ( + "ALTER EXTENSION ext_name ADD FUNCTION 1", + "Expected: identifier, found: 1", + ), ] { assert_eq!( pg_and_generic().parse_sql_statements(sql).unwrap_err(),