Postgres: Scope colon and AS-named function arguments to their functions
diff --git a/derive/src/dialect.rs b/derive/src/dialect.rs
index af17067..aadfaef 100644
--- a/derive/src/dialect.rs
+++ b/derive/src/dialect.rs
@@ -229,7 +229,9 @@
         const _: () = {
             use ::core::iter::Peekable;
             use ::core::str::Chars;
-            use sqlparser::ast::{ColumnOption, Expr, GranteesType, Ident, ObjectNamePart, Statement};
+            use sqlparser::ast::{
+                ColumnOption, Expr, GranteesType, Ident, ObjectName, ObjectNamePart, Statement,
+            };
             use sqlparser::dialect::{Dialect, Precedence};
             use sqlparser::keywords::Keyword;
             use sqlparser::parser::{Parser, ParserError};
diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs
index 2beb565..cbb50bc 100644
--- a/src/dialect/mod.rs
+++ b/src/dialect/mod.rs
@@ -132,7 +132,7 @@
 #[cfg(feature = "derive-dialect")]
 pub use sqlparser_derive::derive_dialect;
 
-use crate::ast::{ColumnOption, Expr, GranteesType, Ident, ObjectNamePart, Statement};
+use crate::ast::{ColumnOption, Expr, GranteesType, Ident, ObjectName, ObjectNamePart, Statement};
 pub use crate::keywords;
 use crate::keywords::Keyword;
 use crate::parser::{Parser, ParserError};
@@ -481,6 +481,14 @@
         false
     }
 
+    /// Returns true if `fn_name` supports named arguments of the form
+    /// `FUN(a : '1', b : '2')`. Defaults to
+    /// [`Dialect::supports_named_fn_args_with_colon_operator`], ignoring `fn_name`.
+    fn supports_named_fn_args_with_colon_operator_for(&self, fn_name: Option<&ObjectName>) -> bool {
+        let _ = fn_name;
+        self.supports_named_fn_args_with_colon_operator()
+    }
+
     /// Returns true if the dialect supports named arguments of the form `FUN(a := '1', b := '2')`.
     fn supports_named_fn_args_with_assignment_operator(&self) -> bool {
         false
@@ -1934,6 +1942,13 @@
         false
     }
 
+    /// Returns true if `fn_name` supports `FUN(a AS x)`-style aliased arguments.
+    /// Defaults to [`Dialect::supports_aliased_function_args`], ignoring `fn_name`.
+    fn supports_aliased_function_args_for(&self, fn_name: Option<&ObjectName>) -> bool {
+        let _ = fn_name;
+        self.supports_aliased_function_args()
+    }
+
     /// Returns true if the dialect supports `USING <format>` in `CREATE TABLE`.
     ///
     /// Example:
diff --git a/src/dialect/postgresql.rs b/src/dialect/postgresql.rs
index 3bec6ce..bc49f4b 100644
--- a/src/dialect/postgresql.rs
+++ b/src/dialect/postgresql.rs
@@ -28,6 +28,7 @@
 // limitations under the License.
 use log::debug;
 
+use crate::ast::ObjectName;
 use crate::dialect::{Dialect, Precedence};
 use crate::keywords::Keyword;
 use crate::parser::{Parser, ParserError};
@@ -261,8 +262,8 @@
     /// ```sql
     /// SELECT json_object('a': 'b')
     /// ```
-    fn supports_named_fn_args_with_colon_operator(&self) -> bool {
-        true
+    fn supports_named_fn_args_with_colon_operator_for(&self, fn_name: Option<&ObjectName>) -> bool {
+        fn_name.is_some_and(|name| Parser::is_simple_unquoted_object_name(name, "json_object"))
     }
 
     /// See <https://www.postgresql.org/docs/current/functions-json.html>
@@ -357,8 +358,12 @@
         true
     }
 
-    fn supports_aliased_function_args(&self) -> bool {
-        true
+    fn supports_aliased_function_args_for(&self, fn_name: Option<&ObjectName>) -> bool {
+        fn_name.is_some_and(|name| {
+            ["XMLFOREST", "XMLELEMENT", "XMLTABLE"]
+                .iter()
+                .any(|f| Parser::is_simple_unquoted_object_name(name, f))
+        })
     }
 
     /// Postgres supports query optimizer hints via the `pg_hint_plan` extension,
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 1a46c6e..fb4f51a 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -1745,7 +1745,7 @@
 
     /// Returns true if the given [ObjectName] is a single unquoted
     /// identifier matching `expected` (case-insensitive).
-    fn is_simple_unquoted_object_name(name: &ObjectName, expected: &str) -> bool {
+    pub(crate) fn is_simple_unquoted_object_name(name: &ObjectName, expected: &str) -> bool {
         if let [ObjectNamePart::Identifier(ident)] = name.0.as_slice() {
             ident.quote_style.is_none() && ident.value.eq_ignore_ascii_case(expected)
         } else {
@@ -2585,7 +2585,7 @@
         {
             self.parse_xmlparse_argument_list()?
         } else {
-            self.parse_function_argument_list()?
+            self.parse_function_argument_list(&name)?
         };
         let mut parameters = FunctionArguments::None;
         // ClickHouse aggregations support parametric functions like `HISTOGRAM(0.5, 0.6)(x, y)`
@@ -2594,7 +2594,7 @@
             && self.consume_token(&Token::LParen)
         {
             parameters = FunctionArguments::List(args);
-            args = self.parse_function_argument_list()?;
+            args = self.parse_function_argument_list(&name)?;
         }
 
         let within_group = if self.parse_keywords(&[Keyword::WITHIN, Keyword::GROUP]) {
@@ -2673,7 +2673,7 @@
     /// Parse time-related function `name` possibly followed by `(...)` arguments.
     pub fn parse_time_functions(&mut self, name: ObjectName) -> Result<Expr, ParserError> {
         let args = if self.consume_token(&Token::LParen) {
-            FunctionArguments::List(self.parse_function_argument_list()?)
+            FunctionArguments::List(self.parse_function_argument_list(&name)?)
         } else {
             FunctionArguments::None
         };
@@ -19027,6 +19027,15 @@
 
     /// Parse a single function argument, handling named and unnamed variants.
     pub fn parse_function_args(&mut self) -> Result<FunctionArg, ParserError> {
+        self.parse_function_args_for(None)
+    }
+
+    /// Like [`Parser::parse_function_args`], but `fn_name`, when known, scopes
+    /// dialect-specific named-argument syntax to the functions that define it.
+    fn parse_function_args_for(
+        &mut self,
+        fn_name: Option<&ObjectName>,
+    ) -> Result<FunctionArg, ParserError> {
         // Parse the argument expression once, then check for a named-arg
         // operator. Parsing it speculatively and re-parsing on the unnamed
         // path is O(2^depth) on nested calls like `CAST(CASE (CAST(CASE (…`.
@@ -19035,7 +19044,7 @@
             // A wildcard is never a named-arg name; only the unnamed form applies.
             if !matches!(expr, Expr::Wildcard(_) | Expr::QualifiedWildcard(..)) {
                 if let Some(operator) =
-                    self.maybe_parse(|p| p.parse_function_named_arg_operator())?
+                    self.maybe_parse(|p| p.parse_function_named_arg_operator(fn_name))?
                 {
                     let arg = self.parse_wildcard_expr()?.into();
                     return Ok(FunctionArg::ExprNamed {
@@ -19047,13 +19056,13 @@
             }
             let arg_expr = self.function_arg_expr_from_wildcard(expr)?;
             return Ok(FunctionArg::Unnamed(
-                self.maybe_parse_aliased_function_arg(arg_expr)?,
+                self.maybe_parse_aliased_function_arg(arg_expr, fn_name)?,
             ));
         }
 
         let arg = self.maybe_parse(|p| {
             let name = p.parse_identifier()?;
-            let operator = p.parse_function_named_arg_operator()?;
+            let operator = p.parse_function_named_arg_operator(fn_name)?;
             let arg = p.parse_wildcard_expr()?.into();
             Ok(FunctionArg::Named {
                 name,
@@ -19067,7 +19076,7 @@
         let wildcard_expr = self.parse_wildcard_expr()?;
         let arg_expr = self.function_arg_expr_from_wildcard(wildcard_expr)?;
         Ok(FunctionArg::Unnamed(
-            self.maybe_parse_aliased_function_arg(arg_expr)?,
+            self.maybe_parse_aliased_function_arg(arg_expr, fn_name)?,
         ))
     }
 
@@ -19101,10 +19110,11 @@
     fn maybe_parse_aliased_function_arg(
         &mut self,
         arg_expr: FunctionArgExpr,
+        fn_name: Option<&ObjectName>,
     ) -> Result<FunctionArgExpr, ParserError> {
         Ok(match arg_expr {
             FunctionArgExpr::Expr(expr)
-                if self.dialect.supports_aliased_function_args()
+                if self.dialect.supports_aliased_function_args_for(fn_name)
                     && self.parse_keyword(Keyword::AS) =>
             {
                 FunctionArgExpr::Expr(Expr::Named {
@@ -19116,7 +19126,10 @@
         })
     }
 
-    fn parse_function_named_arg_operator(&mut self) -> Result<FunctionArgOperator, ParserError> {
+    fn parse_function_named_arg_operator(
+        &mut self,
+        fn_name: Option<&ObjectName>,
+    ) -> Result<FunctionArgOperator, ParserError> {
         if self.parse_keyword(Keyword::VALUE) {
             return Ok(FunctionArgOperator::Value);
         }
@@ -19135,7 +19148,11 @@
             {
                 Ok(FunctionArgOperator::Assignment)
             }
-            Token::Colon if self.dialect.supports_named_fn_args_with_colon_operator() => {
+            Token::Colon
+                if self
+                    .dialect
+                    .supports_named_fn_args_with_colon_operator_for(fn_name) =>
+            {
                 Ok(FunctionArgOperator::Colon)
             }
             _ => {
@@ -19185,7 +19202,10 @@
     /// FIRST_VALUE(x ORDER BY 1,2,3);
     /// FIRST_VALUE(x IGNORE NULL);
     /// ```
-    fn parse_function_argument_list(&mut self) -> Result<FunctionArgumentList, ParserError> {
+    fn parse_function_argument_list(
+        &mut self,
+        fn_name: &ObjectName,
+    ) -> Result<FunctionArgumentList, ParserError> {
         let mut clauses = vec![];
 
         // Handle clauses that may exist with an empty argument list
@@ -19209,7 +19229,7 @@
         }
 
         let duplicate_treatment = self.parse_duplicate_treatment()?;
-        let args = self.parse_comma_separated(Parser::parse_function_args)?;
+        let args = self.parse_comma_separated(|p| p.parse_function_args_for(Some(fn_name)))?;
 
         if self.parse_keyword(Keyword::WHERE) {
             clauses.push(FunctionArgumentClause::Where(self.parse_expr()?));
diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs
index fb99851..09809e3 100644
--- a/tests/sqlparser_postgres.rs
+++ b/tests/sqlparser_postgres.rs
@@ -4145,6 +4145,25 @@
 }
 
 #[test]
+fn parse_named_fn_args_scoped_to_their_functions() {
+    assert_eq!(
+        pg().parse_sql_statements("SELECT abs(a:b) FROM t")
+            .unwrap_err()
+            .to_string(),
+        "sql parser error: Expected: ), found: :"
+    );
+    assert_eq!(
+        pg().parse_sql_statements("SELECT abs(a AS x) FROM t")
+            .unwrap_err()
+            .to_string(),
+        "sql parser error: Expected: ), found: AS"
+    );
+
+    pg().verified_expr("JSON_OBJECT('a' : 'b')");
+    pg_and_generic().verified_only_select("SELECT XMLELEMENT(a AS x, b)");
+}
+
+#[test]
 fn parse_xmlparse() {
     // The parser only distinguishes the two modes, so the corpus covers those
     // plus a non-literal argument.