Skip to content

Commit 51f4977

Browse files
committed
Address CREATE AGGREGATE review feedback
- Replace em-dashes in CreateAggregateOption doc comments - Add FunctionParallel::as_str and reuse it in Display and the PARALLEL option - Support old-syntax BASETYPE option (parse and Display round-trip) - Clarify the CreateAggregate args field doc - Test the unknown-option error path and the old BASETYPE syntax
1 parent 7671da0 commit 51f4977

4 files changed

Lines changed: 98 additions & 29 deletions

File tree

src/ast/ddl.rs

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5833,7 +5833,7 @@ pub struct CreateAggregate {
58335833
pub or_replace: bool,
58345834
/// The aggregate name (can be schema-qualified).
58355835
pub name: ObjectName,
5836-
/// Input arguments. Empty for zero-argument aggregates or `(*)`.
5836+
/// Input arguments. Empty when `star_args` is true or when the argument list is empty.
58375837
pub args: Vec<OperateFunctionArg>,
58385838
/// True if the argument list was the wildcard form `(*)` (used by
58395839
/// zero-argument aggregates such as `count(*)`).
@@ -5850,10 +5850,16 @@ impl fmt::Display for CreateAggregate {
58505850
write!(f, " OR REPLACE")?;
58515851
}
58525852
write!(f, " AGGREGATE {}", self.name)?;
5853-
if self.star_args && self.args.is_empty() {
5854-
write!(f, " (*)")?;
5855-
} else {
5856-
write!(f, " ({})", display_comma_separated(&self.args))?;
5853+
let is_old_syntax = self
5854+
.options
5855+
.iter()
5856+
.any(|option| matches!(option, CreateAggregateOption::BaseType(_)));
5857+
if !is_old_syntax {
5858+
if self.star_args && self.args.is_empty() {
5859+
write!(f, " (*)")?;
5860+
} else {
5861+
write!(f, " ({})", display_comma_separated(&self.args))?;
5862+
}
58575863
}
58585864
write!(f, " ({})", display_comma_separated(&self.options))
58595865
}
@@ -5880,7 +5886,7 @@ pub enum CreateAggregateOption {
58805886
StateDataSize(u64),
58815887
/// `FINALFUNC = final_function`
58825888
FinalFunction(ObjectName),
5883-
/// `FINALFUNC_EXTRA` — pass extra dummy arguments to the final function.
5889+
/// `FINALFUNC_EXTRA`. Passes extra dummy arguments to the final function.
58845890
FinalFunctionExtra,
58855891
/// `FINALFUNC_MODIFY = { READ_ONLY | SHAREABLE | READ_WRITE }`
58865892
FinalFunctionModify(AggregateModifyKind),
@@ -5912,8 +5918,10 @@ pub enum CreateAggregateOption {
59125918
SortOperator(ObjectName),
59135919
/// `PARALLEL = { SAFE | RESTRICTED | UNSAFE }`
59145920
Parallel(FunctionParallel),
5915-
/// `HYPOTHETICAL` — marks the aggregate as hypothetical-set.
5921+
/// `HYPOTHETICAL`. Marks the aggregate as hypothetical-set.
59165922
Hypothetical,
5923+
/// `BASETYPE = base_type` (old aggregate syntax).
5924+
BaseType(DataType),
59175925
}
59185926

59195927
impl fmt::Display for CreateAggregateOption {
@@ -5938,15 +5946,9 @@ impl fmt::Display for CreateAggregateOption {
59385946
Self::MovingFinalFunctionModify(kind) => write!(f, "MFINALFUNC_MODIFY = {kind}"),
59395947
Self::MovingInitialCondition(cond) => write!(f, "MINITCOND = {cond}"),
59405948
Self::SortOperator(name) => write!(f, "SORTOP = {name}"),
5941-
Self::Parallel(parallel) => {
5942-
let kind = match parallel {
5943-
FunctionParallel::Safe => "SAFE",
5944-
FunctionParallel::Restricted => "RESTRICTED",
5945-
FunctionParallel::Unsafe => "UNSAFE",
5946-
};
5947-
write!(f, "PARALLEL = {kind}")
5948-
}
5949+
Self::Parallel(parallel) => write!(f, "PARALLEL = {}", parallel.as_str()),
59495950
Self::Hypothetical => write!(f, "HYPOTHETICAL"),
5951+
Self::BaseType(data_type) => write!(f, "BASETYPE = {data_type}"),
59505952
}
59515953
}
59525954
}

src/ast/mod.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10069,16 +10069,23 @@ pub enum FunctionParallel {
1006910069
Safe,
1007010070
}
1007110071

10072-
impl fmt::Display for FunctionParallel {
10073-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
10072+
impl FunctionParallel {
10073+
/// Returns the bare keyword for this parallel mode, without the `PARALLEL` prefix.
10074+
pub fn as_str(&self) -> &'static str {
1007410075
match self {
10075-
FunctionParallel::Unsafe => write!(f, "PARALLEL UNSAFE"),
10076-
FunctionParallel::Restricted => write!(f, "PARALLEL RESTRICTED"),
10077-
FunctionParallel::Safe => write!(f, "PARALLEL SAFE"),
10076+
FunctionParallel::Unsafe => "UNSAFE",
10077+
FunctionParallel::Restricted => "RESTRICTED",
10078+
FunctionParallel::Safe => "SAFE",
1007810079
}
1007910080
}
1008010081
}
1008110082

10083+
impl fmt::Display for FunctionParallel {
10084+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
10085+
write!(f, "PARALLEL {}", self.as_str())
10086+
}
10087+
}
10088+
1008210089
/// [BigQuery] Determinism specifier used in a UDF definition.
1008310090
///
1008410091
/// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#syntax_11

src/parser/mod.rs

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7235,22 +7235,31 @@ impl<'a> Parser<'a> {
72357235
) -> Result<CreateAggregate, ParserError> {
72367236
let name = self.parse_object_name(false)?;
72377237

7238-
// Argument list: `(input_arg [, ...])` or `(*)` for zero-arg.
7238+
// The old syntax has a single parenthesized list that carries both
7239+
// `BASETYPE` and the remaining options. The modern syntax has a
7240+
// separate `(input_arg [, ...])` argument list followed by the
7241+
// options list. They are distinguished by whether a second
7242+
// parenthesized list follows the first.
7243+
let checkpoint = self.index;
72397244
self.expect_token(&Token::LParen)?;
7240-
let star_args = self.consume_token(&Token::Mul);
7241-
let args = if star_args || self.peek_token().token == Token::RParen {
7245+
let mut star_args = self.consume_token(&Token::Mul);
7246+
let mut args = if star_args || self.peek_token().token == Token::RParen {
72427247
vec![]
72437248
} else {
72447249
self.parse_comma_separated(Parser::parse_function_arg)?
72457250
};
72467251
self.expect_token(&Token::RParen)?;
72477252

7248-
// Options block: `( SFUNC = ..., STYPE = ..., ... )`.
7249-
self.expect_token(&Token::LParen)?;
7250-
let options = self.parse_comma_separated(|parser| {
7251-
let key = parser.parse_identifier()?;
7252-
parser.parse_create_aggregate_option(&key.value.to_uppercase())
7253-
})?;
7253+
// The first list is the modern argument-type list only when a second
7254+
// list follows. Otherwise it was the old-syntax options list, so rewind,
7255+
// discard the speculatively parsed args, and re-parse it as options.
7256+
if !self.consume_token(&Token::LParen) {
7257+
self.index = checkpoint;
7258+
args = vec![];
7259+
star_args = false;
7260+
self.expect_token(&Token::LParen)?;
7261+
}
7262+
let options = self.parse_comma_separated(Parser::parse_create_aggregate_option_entry)?;
72547263
self.expect_token(&Token::RParen)?;
72557264

72567265
Ok(CreateAggregate {
@@ -7275,6 +7284,13 @@ impl<'a> Parser<'a> {
72757284
}
72767285
}
72777286

7287+
fn parse_create_aggregate_option_entry(
7288+
&mut self,
7289+
) -> Result<CreateAggregateOption, ParserError> {
7290+
let key = self.parse_identifier()?;
7291+
self.parse_create_aggregate_option(&key.value.to_uppercase())
7292+
}
7293+
72787294
fn parse_create_aggregate_option(
72797295
&mut self,
72807296
key: &str,
@@ -7387,6 +7403,10 @@ impl<'a> Parser<'a> {
73877403
))
73887404
}
73897405
"HYPOTHETICAL" => Ok(CreateAggregateOption::Hypothetical),
7406+
"BASETYPE" => {
7407+
self.expect_token(&Token::Eq)?;
7408+
Ok(CreateAggregateOption::BaseType(self.parse_data_type()?))
7409+
}
73907410
other => Err(ParserError::ParserError(format!(
73917411
"Unknown CREATE AGGREGATE option: {other}"
73927412
))),

tests/sqlparser_postgres.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9348,3 +9348,43 @@ fn parse_create_aggregate_additional_options() {
93489348
"CREATE AGGREGATE my_sum (INT) (SFUNC = my_sfunc, STYPE = internal, COMBINEFUNC = my_combine, SERIALFUNC = my_serial, DESERIALFUNC = my_deserial)",
93499349
);
93509350
}
9351+
9352+
#[test]
9353+
fn parse_create_aggregate_old_syntax_basetype() {
9354+
let stmt = pg_and_generic()
9355+
.verified_stmt("CREATE AGGREGATE my_avg (BASETYPE = INT, SFUNC = my_sfunc, STYPE = INT)");
9356+
match stmt {
9357+
Statement::CreateAggregate(agg) => {
9358+
assert!(agg.args.is_empty());
9359+
assert!(!agg.star_args);
9360+
assert_eq!(agg.options.len(), 3);
9361+
assert_eq!(
9362+
agg.options[0],
9363+
CreateAggregateOption::BaseType(DataType::Int(None))
9364+
);
9365+
}
9366+
_ => panic!("Expected CreateAggregate, got: {stmt:?}"),
9367+
}
9368+
9369+
let stmt = pg_and_generic()
9370+
.verified_stmt("CREATE AGGREGATE my_avg (SFUNC = my_sfunc, BASETYPE = INT, STYPE = INT)");
9371+
match stmt {
9372+
Statement::CreateAggregate(agg) => {
9373+
assert!(agg.args.is_empty());
9374+
assert!(!agg.star_args);
9375+
assert_eq!(agg.options.len(), 3);
9376+
assert_eq!(
9377+
agg.options[1],
9378+
CreateAggregateOption::BaseType(DataType::Int(None))
9379+
);
9380+
}
9381+
_ => panic!("Expected CreateAggregate, got: {stmt:?}"),
9382+
}
9383+
}
9384+
9385+
#[test]
9386+
fn parse_create_aggregate_unknown_option() {
9387+
assert!(pg_and_generic()
9388+
.parse_sql_statements("CREATE AGGREGATE foo (INT) (UNKNOWN_OPTION = bar)")
9389+
.is_err());
9390+
}

0 commit comments

Comments
 (0)