You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by lu...@apache.org on 2026/10/07 16:27:59 UTC
(datafusion-sqlparser-rs) 01/01: Generic: Parse schema-qualified calls to keyword functions like floor
This is an automated email from the ASF dual-hosted git repository.
LucaCappelletti94 pushed a commit to branch upstream/qualified-keyword-functions
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git
commit b61158cd871a430a2afba6d2f5c155a340fd409c
Author: LucaCappelletti94 <ca...@gmail.com>
AuthorDate: Wed Oct 7 18:27:38 2026 +0200
Generic: Parse schema-qualified calls to keyword functions like floor
---
src/parser/mod.rs | 16 +++++++-----
tests/sqlparser_common.rs | 62 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 72 insertions(+), 6 deletions(-)
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 1a46c6e5..5e0943ac 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -2172,13 +2172,17 @@ impl<'a> Parser<'a> {
Some(expr) => {
chain.push(AccessExpr::Dot(expr));
}
- // If the expression is not a valid suffix, fall back to
- // parsing as an identifier. This handles cases like `T.interval`
- // where `interval` is a keyword but should be treated as an identifier.
+ // Falls back to identifier parsing for keyword suffixes
+ // like `T.interval`; a following `(` makes it a
+ // qualified call instead, e.g. `pg_catalog.floor(3)`.
None => {
- chain.push(AccessExpr::Dot(Expr::Identifier(
- self.parse_identifier()?,
- )));
+ let ident = self.parse_identifier()?;
+ let field = if self.peek_token_ref().token == Token::LParen {
+ self.parse_function(ObjectName::from(vec![ident]))?
+ } else {
+ Expr::Identifier(ident)
+ };
+ chain.push(AccessExpr::Dot(field));
}
}
}
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index 8e08c657..bcf69522 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -16090,6 +16090,68 @@ fn test_keywords_as_column_names_after_dot() {
}
}
+#[test]
+fn test_qualified_keyword_function_calls() {
+ let pg = TestedDialects::new(vec![Box::new(PostgreSqlDialect {})]);
+
+ for (qualifier, func) in [
+ ("public", "floor"),
+ ("pg_catalog", "floor"),
+ ("pg_catalog", "ceil"),
+ ] {
+ let sql = format!("SELECT {qualifier}.{func}(3)");
+ match expr_from_projection(&pg.verified_only_select(&sql).projection[0]) {
+ Expr::Function(Function {
+ name,
+ args: FunctionArguments::List(FunctionArgumentList { args, .. }),
+ ..
+ }) => {
+ assert_eq!(
+ name,
+ &ObjectName::from(vec![Ident::new(qualifier), Ident::new(func)])
+ );
+ assert_eq!(
+ args,
+ &[FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::value(
+ number("3")
+ )))]
+ );
+ }
+ other => panic!("Expected a qualified function call, got: {other:?}"),
+ }
+ }
+
+ // EXTRACT has its own `EXTRACT(field FROM source)` grammar; schema-qualified
+ // it is an ordinary function call like any other qualified name.
+ match expr_from_projection(
+ &pg.verified_only_select("SELECT pg_catalog.extract(3)")
+ .projection[0],
+ ) {
+ Expr::Function(Function { name, .. }) => assert_eq!(
+ name,
+ &ObjectName::from(vec![Ident::new("pg_catalog"), Ident::new("extract")])
+ ),
+ other => panic!("Expected a qualified function call, got: {other:?}"),
+ }
+
+ // Unqualified calls still parse as the dedicated FLOOR/CEIL expression.
+ match expr_from_projection(&pg.verified_only_select("SELECT FLOOR(3)").projection[0]) {
+ Expr::Floor { .. } => {}
+ other => panic!("Expected Expr::Floor, got: {other:?}"),
+ }
+
+ // Keyword column identifiers after a dot, without parentheses, are untouched.
+ match expr_from_projection(
+ &pg.verified_only_select("SELECT t.interval FROM tbl t")
+ .projection[0],
+ ) {
+ Expr::CompoundIdentifier(idents) => {
+ assert_eq!(idents, &vec![Ident::new("t"), Ident::new("interval")])
+ }
+ other => panic!("Expected CompoundIdentifier, got: {other:?}"),
+ }
+}
+
#[test]
fn parse_create_table_with_bit_types() {
let sql = "CREATE TABLE t (a BIT, b BIT VARYING, c BIT(42), d BIT VARYING(43))";
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org