diff --git a/fuzz/fuzz_seeds/postgres_2741_grantee_after_public.sql b/fuzz/fuzz_seeds/postgres_2741_grantee_after_public.sql new file mode 100644 index 0000000000..65847d5056 --- /dev/null +++ b/fuzz/fuzz_seeds/postgres_2741_grantee_after_public.sql @@ -0,0 +1 @@ +REVOKE EXECUTE ON FUNCTION f() FROM PUBLIC, anon diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 22a39f1f2d..8d731893a8 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -18548,6 +18548,9 @@ impl<'a> Parser<'a> { GranteesType::ApplicationRole } else if self.parse_keyword(Keyword::APPLICATION) { GranteesType::Application + } else if grantee_type == GranteesType::Public { + // PUBLIC does not carry over to the next grantee. + GranteesType::None } else { grantee_type.clone() // keep from previous iteraton, if not specified }; diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index fc08f2b327..13b1374870 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -22666,3 +22666,51 @@ fn display_revoke_role_option_for_propagates_write_errors() { assert_eq!(write!(writer, "{statement}"), Err(fmt::Error)); } } + +#[test] +fn parse_grantee_after_public() { + let grantees = + |sql: &str| match all_dialects_except(|d| d.is::()).verified_stmt(sql) { + Statement::Grant(grant) => grant.grantees, + Statement::Revoke(revoke) => revoke.grantees, + stmt => panic!("unexpected statement: {stmt:?}"), + }; + let public = || Grantee { + grantee_type: GranteesType::Public, + name: None, + }; + let named = |grantee_type, name| Grantee { + grantee_type, + name: Some(GranteeName::ObjectName(ObjectName::from(vec![Ident::new( + name, + )]))), + }; + + assert_eq!( + grantees("GRANT SELECT ON t TO PUBLIC, reader"), + vec![public(), named(GranteesType::None, "reader")] + ); + assert_eq!( + grantees("REVOKE SELECT ON t FROM PUBLIC, ROLE r, PUBLIC"), + vec![public(), named(GranteesType::Role, "r"), public()] + ); + assert_eq!( + grantees("GRANT SELECT ON t TO GROUP g, PUBLIC, reader"), + vec![ + named(GranteesType::Group, "g"), + public(), + named(GranteesType::None, "reader") + ] + ); + + // MsSql reads PUBLIC as an ordinary name. + TestedDialects::new(vec![Box::new(MsSqlDialect {})]) + .verified_stmt("GRANT SELECT ON t TO PUBLIC, reader"); + + assert_eq!( + all_dialects() + .parse_sql_statements("GRANT SELECT ON t TO PUBLIC,") + .unwrap_err(), + ParserError::ParserError("Expected: identifier, found: EOF".to_string()) + ); +}