Skip to content

Commit 96eb232

Browse files
committed
support call operator
1 parent e215e7f commit 96eb232

3 files changed

Lines changed: 83 additions & 0 deletions

File tree

src/ast/query.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2717,6 +2717,12 @@ pub enum PipeOperator {
27172717
set_quantifier: SetQuantifier,
27182718
queries: Vec<Box<Query>>,
27192719
},
2720+
/// Calls a table function or procedure that returns a table.
2721+
///
2722+
/// Syntax: `|> CALL function_name(args) [AS alias]`
2723+
///
2724+
/// See more at <https://cloud.google.com/bigquery/docs/reference/standard-sql/pipe-syntax#call_pipe_operator>
2725+
Call { function: Function, alias: Option<Ident> },
27202726
}
27212727

27222728
impl fmt::Display for PipeOperator {
@@ -2841,6 +2847,13 @@ impl fmt::Display for PipeOperator {
28412847
}
28422848
Ok(())
28432849
}
2850+
PipeOperator::Call { function, alias } => {
2851+
write!(f, "CALL {}", function)?;
2852+
if let Some(alias) = alias {
2853+
write!(f, " AS {}", alias)?;
2854+
}
2855+
Ok(())
2856+
}
28442857
}
28452858
}
28462859
}

src/parser/mod.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11087,6 +11087,7 @@ impl<'a> Parser<'a> {
1108711087
Keyword::UNION,
1108811088
Keyword::INTERSECT,
1108911089
Keyword::EXCEPT,
11090+
Keyword::CALL,
1109011091
])?;
1109111092
match kw {
1109211093
Keyword::SELECT => {
@@ -11212,6 +11213,24 @@ impl<'a> Parser<'a> {
1121211213
})?;
1121311214
pipe_operators.push(PipeOperator::Except { set_quantifier, queries });
1121411215
}
11216+
Keyword::CALL => {
11217+
let function_name = self.parse_object_name(false)?;
11218+
let function_expr = self.parse_function(function_name)?;
11219+
// Extract Function from Expr::Function
11220+
if let Expr::Function(function) = function_expr {
11221+
// Parse optional alias
11222+
let alias = if self.parse_keyword(Keyword::AS) {
11223+
Some(self.parse_identifier()?)
11224+
} else {
11225+
None
11226+
};
11227+
pipe_operators.push(PipeOperator::Call { function, alias });
11228+
} else {
11229+
return Err(ParserError::ParserError(
11230+
"Expected function call after CALL".to_string()
11231+
));
11232+
}
11233+
}
1121511234
unhandled => {
1121611235
return Err(ParserError::ParserError(format!(
1121711236
"`expect_one_of_keywords` further up allowed unhandled keyword: {unhandled:?}"

tests/sqlparser_common.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15243,6 +15243,32 @@ fn parse_pipeline_operator() {
1524315243
// except pipe operator with BY NAME and multiple queries
1524415244
dialects.verified_stmt("SELECT * FROM users |> EXCEPT DISTINCT BY NAME (SELECT * FROM admins), (SELECT * FROM guests)");
1524515245

15246+
// call pipe operator
15247+
dialects.verified_stmt("SELECT * FROM users |> CALL my_function()");
15248+
dialects.verified_stmt("SELECT * FROM users |> CALL process_data(5, 'test')");
15249+
dialects.verified_stmt("SELECT * FROM users |> CALL namespace.function_name(col1, col2, 'literal')");
15250+
15251+
// call pipe operator with complex arguments
15252+
dialects.verified_stmt("SELECT * FROM users |> CALL transform_data(col1 + col2)");
15253+
dialects.verified_stmt("SELECT * FROM users |> CALL analyze_data('param1', 100, true)");
15254+
15255+
// call pipe operator with aliases
15256+
dialects.verified_stmt("SELECT * FROM input_table |> CALL tvf1(arg1) AS al");
15257+
dialects.verified_stmt("SELECT * FROM users |> CALL process_data(5) AS result_table");
15258+
dialects.verified_stmt("SELECT * FROM users |> CALL namespace.func() AS my_alias");
15259+
15260+
// multiple call pipe operators in sequence
15261+
dialects.verified_stmt("SELECT * FROM input_table |> CALL tvf1(arg1) |> CALL tvf2(arg2, arg3)");
15262+
dialects.verified_stmt("SELECT * FROM data |> CALL transform(col1) |> CALL validate() |> CALL process(param)");
15263+
15264+
// multiple call pipe operators with aliases
15265+
dialects.verified_stmt("SELECT * FROM input_table |> CALL tvf1(arg1) AS step1 |> CALL tvf2(arg2) AS step2");
15266+
dialects.verified_stmt("SELECT * FROM data |> CALL preprocess() AS clean_data |> CALL analyze(mode) AS results");
15267+
15268+
// call pipe operators mixed with other pipe operators
15269+
dialects.verified_stmt("SELECT * FROM users |> CALL transform() |> WHERE status = 'active' |> CALL process(param)");
15270+
dialects.verified_stmt("SELECT * FROM data |> CALL preprocess() AS clean |> SELECT col1, col2 |> CALL validate()");
15271+
1524615272
// many pipes
1524715273
dialects.verified_stmt(
1524815274
"SELECT * FROM CustomerOrders |> AGGREGATE SUM(cost) AS total_cost GROUP BY customer_id, state, item_type |> EXTEND COUNT(*) OVER (PARTITION BY customer_id) AS num_orders |> WHERE num_orders > 1 |> AGGREGATE AVG(total_cost) AS average GROUP BY state DESC, item_type ASC",
@@ -15300,6 +15326,31 @@ fn parse_pipeline_operator_negative_tests() {
1530015326
ParserError::ParserError("INTERSECT pipe operator requires DISTINCT modifier".to_string()),
1530115327
dialects.parse_sql_statements("SELECT * FROM users |> INTERSECT ALL BY NAME (SELECT * FROM admins)").unwrap_err()
1530215328
);
15329+
15330+
// Test that CALL without function name fails
15331+
assert!(
15332+
dialects.parse_sql_statements("SELECT * FROM users |> CALL").is_err()
15333+
);
15334+
15335+
// Test that CALL without parentheses fails
15336+
assert!(
15337+
dialects.parse_sql_statements("SELECT * FROM users |> CALL my_function").is_err()
15338+
);
15339+
15340+
// Test that CALL with invalid function syntax fails
15341+
assert!(
15342+
dialects.parse_sql_statements("SELECT * FROM users |> CALL 123invalid").is_err()
15343+
);
15344+
15345+
// Test that CALL with malformed arguments fails
15346+
assert!(
15347+
dialects.parse_sql_statements("SELECT * FROM users |> CALL my_function(,)").is_err()
15348+
);
15349+
15350+
// Test that CALL with invalid alias syntax fails
15351+
assert!(
15352+
dialects.parse_sql_statements("SELECT * FROM users |> CALL my_function() AS").is_err()
15353+
);
1530315354
}
1530415355

1530515356
#[test]

0 commit comments

Comments
 (0)