mod common;
use common::pgwire_harness::TestServer;
async fn create_table(server: &TestServer) {
server
.exec(
"CREATE COLLECTION t \
COLUMNS (id TEXT PRIMARY KEY, v INTEGER) \
WITH (engine='document_strict')",
)
.await
.unwrap();
}
#[tokio::test]
async fn count_star_on_empty_collection_returns_one_zero_row() {
let srv = TestServer::start().await;
create_table(&srv).await;
let rows = srv
.query_rows("SELECT COUNT(*) FROM t")
.await
.expect("COUNT(*) over an empty collection must plan and execute");
assert_eq!(
rows.len(),
1,
"scalar COUNT(*) over zero rows must emit exactly one identity row, got {rows:?}"
);
let cell = rows[0]
.first()
.expect("aggregate result must have at least one column");
assert_eq!(
cell, "0",
"COUNT(*) over an empty collection must be 0, got `{cell}`"
);
}
#[tokio::test]
async fn count_star_with_where_matching_nothing_returns_one_zero_row() {
let srv = TestServer::start().await;
create_table(&srv).await;
srv.exec("INSERT INTO t (id, v) VALUES ('a', 1), ('b', 2)")
.await
.unwrap();
let rows = srv
.query_rows("SELECT COUNT(*) FROM t WHERE v > 100")
.await
.expect("COUNT(*) with a non-matching WHERE must plan and execute");
assert_eq!(
rows.len(),
1,
"scalar COUNT(*) with WHERE matching nothing must emit exactly one identity row, got {rows:?}"
);
let cell = rows[0]
.first()
.expect("aggregate result must have at least one column");
assert_eq!(
cell, "0",
"COUNT(*) with WHERE matching nothing must be 0, got `{cell}`"
);
}
#[tokio::test]
async fn count_star_with_matching_rows_returns_true_count() {
let srv = TestServer::start().await;
create_table(&srv).await;
srv.exec("INSERT INTO t (id, v) VALUES ('a', 1), ('b', 2)")
.await
.unwrap();
let rows = srv
.query_rows("SELECT COUNT(*) FROM t")
.await
.expect("COUNT(*) over a non-empty collection must plan and execute");
assert_eq!(
rows.len(),
1,
"scalar COUNT(*) must always emit exactly one row, got {rows:?}"
);
let cell = rows[0]
.first()
.expect("aggregate result must have at least one column");
assert_eq!(
cell, "2",
"COUNT(*) over 2 matching rows must be 2 (no double-count / phantom rows), got `{cell}`"
);
}
#[tokio::test]
async fn group_by_over_empty_collection_returns_zero_rows() {
let srv = TestServer::start().await;
create_table(&srv).await;
let rows = srv
.query_rows("SELECT v, COUNT(*) FROM t GROUP BY v")
.await
.expect("GROUP BY over an empty collection must plan and execute");
assert_eq!(
rows.len(),
0,
"GROUP BY over zero rows must yield zero groups/rows, got {rows:?}"
);
}
#[tokio::test]
async fn count_star_on_empty_columnar_collection_returns_one_zero_row() {
let srv = TestServer::start().await;
srv.exec(
"CREATE COLLECTION c \
COLUMNS (id TEXT PRIMARY KEY, v INTEGER) \
WITH (engine='columnar')",
)
.await
.unwrap();
let rows = srv
.query_rows("SELECT COUNT(*) FROM c")
.await
.expect("COUNT(*) over an empty columnar collection must plan and execute");
assert_eq!(
rows.len(),
1,
"scalar COUNT(*) over an empty columnar collection must emit one identity row, got {rows:?}"
);
assert_eq!(
rows[0].first().map(String::as_str),
Some("0"),
"columnar COUNT(*) over zero rows must be 0, got {rows:?}"
);
}
#[tokio::test]
async fn count_star_on_empty_timeseries_collection_returns_one_zero_row() {
let srv = TestServer::start().await;
srv.exec(
"CREATE COLLECTION ts \
COLUMNS (ts TIMESTAMP TIME_KEY, v INTEGER) \
WITH (engine='timeseries')",
)
.await
.unwrap();
let rows = srv
.query_rows("SELECT COUNT(*) FROM ts")
.await
.expect("COUNT(*) over an empty timeseries collection must plan and execute");
assert_eq!(
rows.len(),
1,
"scalar COUNT(*) over an empty timeseries collection must emit one identity row, got {rows:?}"
);
assert_eq!(
rows[0].first().map(String::as_str),
Some("0"),
"timeseries COUNT(*) over zero rows must be 0, got {rows:?}"
);
}
#[tokio::test]
async fn sum_min_max_on_empty_collection_return_one_null_row() {
let srv = TestServer::start().await;
create_table(&srv).await;
let rows = srv
.query_rows("SELECT COUNT(*), SUM(v), MIN(v), MAX(v) FROM t")
.await
.expect("SUM/MIN/MAX over an empty collection must plan and execute");
assert_eq!(
rows.len(),
1,
"scalar SUM/MIN/MAX over zero rows must emit exactly one identity row, got {rows:?}"
);
let row = &rows[0];
assert_eq!(row.len(), 4, "expected 4 aggregate columns, got {row:?}");
assert_eq!(row[0], "0", "COUNT(*) must be 0, got `{}`", row[0]);
for (name, cell) in ["SUM", "MIN", "MAX"].iter().zip(&row[1..]) {
assert!(
cell.is_empty() || cell.eq_ignore_ascii_case("null"),
"{name}(v) over zero rows must be NULL, got `{cell}`"
);
}
}