Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions crates/rudb-bind/src/binder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7062,6 +7062,9 @@ impl<'a> Binder<'a> {
if parts.frame.unit == WindowUnit::Range && offsets && parts.order.len() != 1 {
return Err(Error::binder("RANGE frames must have only one ORDER BY expression"));
}
if self.semantics.unknown_types() == UnknownTypes::Postgres {
self.pg_aggregate_unknowns(ast, name, args, &mut parts.args)?;
}

let types: Vec<LogicalType> =
parts.args.iter().map(|&arg| self.plan.expr_type(arg).clone()).collect();
Expand Down
6 changes: 5 additions & 1 deletion crates/rudb-kernels/src/pgregression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,8 @@ fn r2(sxx: f64, syy: f64, sxy: f64) -> f64 {
}
false => numerator / denominator,
};
answer.min(1.0)
// A NaN stays a NaN, where `f64::min` would give the one.
if answer > 1.0 { 1.0 } else { answer }
}

/// The `Sx * Sxy / Sxx` that `regr_intercept` takes from the sum of the `y` values, through the
Expand Down Expand Up @@ -344,6 +345,9 @@ mod tests {
state.add(1.0, 1e308);
state.add(1.0, 1e308);
assert!(state.finish().is_err());
let nan: Vec<(f64, f64)> = (1..=3).map(|g| (f64::from(g), f64::NAN)).collect();
let Value::Double(r2) = answer("float8_regr_r2", &nan) else { panic!("a double") };
assert!(r2.is_nan());
let mut infinite = FloatPairs::named("float8_corr").unwrap();
infinite.add(1.0, f64::INFINITY);
infinite.add(2.0, 3.0);
Expand Down
33 changes: 33 additions & 0 deletions crates/rudb-server/tests/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7622,6 +7622,39 @@ fn a_limit_in_a_correlated_subquery_reads_the_outer_row_as_postgres() {
server.stop().unwrap();
}

#[test]
fn an_aggregate_reads_a_string_literal_as_postgres() {
let dirs = Dirs::new("pgaggunknown");
let server = Server::start(dirs.config()).unwrap();
let mut client = Client::unix(&server);
connect(&mut client, PROTOCOL_3_0);
// The answers are the ones that PostgreSQL 19 gives. A string literal takes the type of the
// aggregate that PostgreSQL finds for it, over a group and over a window.
for (sql, expected) in [
(
"select concat_ws('|', corr(g, 'NaN'), regr_r2(g, 'NaN'), min('a'), \
string_agg('x', ',')) from generate_series(1, 3) g",
"NaN|NaN|a|x,x,x",
),
(
"select string_agg(coalesce(c::text, 'null'), ' ') from (select corr(g, '2') \
over (order by g) c from generate_series(1, 3) g) q",
"null null null",
),
] {
assert_eq!(scalar(&mut client, sql), expected, "{sql}");
}
for sql in ["select avg('4') from generate_series(1, 2)", "select sum('x') over ()"] {
let messages = client.query(sql);
let tags = tags(&messages);
assert!(tags.ends_with("EZ"), "{tags}");
let error = &messages[tags.len() - 2];
assert_eq!(error.field(b'C').as_deref(), Some("42725"), "{sql}");
assert_eq!(error.field(b'D').as_deref(), Some("Could not choose a best candidate function."));
}
server.stop().unwrap();
}

#[test]
fn a_pair_statistic_of_a_constant_column_is_null_as_postgres() {
let dirs = Dirs::new("pgpairs");
Expand Down
Loading