test: cover rangeFrameMeasuresDistanceInDaysOnADateColumn

This commit is contained in:
dualfroz committed 2026-09-10 21:21:11 +02:00
1 parent bfd93c3811
commit a46897c93d
1 file changed
+161
@@ -618,3 +618,164 @@ class WindowFunctionTest {
assertEquals(listOf(3L, 3L, 4L, 2L), result.rows.map { (it[1] as Value.IntegerValue).value })
}
@Test
fun rangeFrameMeasuresDistanceInDaysOnADateColumn() {
run("CREATE TABLE events (id INTEGER PRIMARY KEY, day DATE)")
run(
"INSERT INTO events VALUES " +
"(1, CAST('2024-01-01' AS DATE)), (2, CAST('2024-01-02' AS DATE)), " +
"(3, CAST('2024-01-05' AS DATE)), (4, CAST('2024-01-06' AS DATE)), (5, CAST('2024-01-10' AS DATE))"
)
val result = select(
"SELECT id, COUNT(*) OVER (ORDER BY day RANGE BETWEEN 2 PRECEDING AND 2 FOLLOWING) AS c " +
"FROM events ORDER BY id ASC"
)
assertEquals(listOf(2L, 2L, 2L, 2L, 1L), result.rows.map { (it[1] as Value.IntegerValue).value })
}
@Test
fun rangeFrameEntirelyPastThePartitionEdgeAggregatesToNullAndCountsZero() {
seedSales()
val result = select(
"SELECT id, SUM(amount) OVER (ORDER BY amount RANGE BETWEEN 100 FOLLOWING AND 200 FOLLOWING) AS s, " +
"COUNT(*) OVER (ORDER BY amount RANGE BETWEEN 100 FOLLOWING AND 200 FOLLOWING) AS c " +
"FROM sales ORDER BY id ASC"
)
assertEquals(
(1L..5L).map { listOf(int(it), Value.NullValue, int(0)) },
result.rows
)
}
@Test
fun rangeFrameWithAPrecedingBoundRequiresExactlyOneOrderByColumn() {
seedSales()
val error = assertFailsWith<KestrelException> {
run(
"SELECT SUM(amount) OVER (ORDER BY amount, id RANGE BETWEEN 10 PRECEDING AND 10 FOLLOWING) " +
"FROM sales"
)
}
assertTrue(error.message!!.contains("exactly one ORDER BY column"), error.message!!)
}
@Test
fun rangeFrameWithAPrecedingBoundRequiresANumericOrDateOrderColumn() {
seed()
val error = assertFailsWith<KestrelException> {
run("SELECT SUM(salary) OVER (ORDER BY name RANGE BETWEEN 1 PRECEDING AND CURRENT ROW) FROM employees")
}
assertTrue(error.message!!.contains("numeric or DATE"), error.message!!)
}
@Test
fun rangeFrameOffsetMustBeNumeric() {
seedSales()
val error = assertFailsWith<KestrelException> {
run("SELECT SUM(amount) OVER (ORDER BY amount RANGE BETWEEN 'x' PRECEDING AND CURRENT ROW) FROM sales")
}
assertTrue(error.message!!.contains("numeric"), error.message!!)
}
@Test
fun rangeFrameOffsetMustNotBeNegative() {
seedSales()
val error = assertFailsWith<KestrelException> {
run("SELECT SUM(amount) OVER (ORDER BY amount RANGE BETWEEN -1 PRECEDING AND CURRENT ROW) FROM sales")
}
assertTrue(error.message!!.contains("negative"), error.message!!)
}
@Test
fun rangeFrameSurvivesViewDefinitionRoundTrip() {
seedSales()
run(
"CREATE VIEW valueTotals AS SELECT id, " +
"SUM(amount) OVER (ORDER BY amount RANGE BETWEEN 10 PRECEDING AND 10 FOLLOWING) AS s " +
"FROM sales"
)
val result = select("SELECT id, s FROM valueTotals ORDER BY id ASC")
assertEquals(listOf(50L, 80L, 80L, 70L, 50L), result.rows.map { (it[1] as Value.IntegerValue).value })
}
@Test
fun excludeCurrentRowRemovesOnlyTheCurrentRowFromTheWholePartitionFrame() {
seedSales()
val result = select(
"SELECT id, SUM(amount) OVER (" +
"ORDER BY amount ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW" +
") AS s FROM sales ORDER BY id ASC"
)
assertEquals(listOf(120L, 110L, 110L, 100L, 80L), result.rows.map { (it[1] as Value.IntegerValue).value })
}
@Test
fun excludeGroupRemovesTheWholePeerGroupIncludingTheCurrentRow() {
seedSales()
val result = select(
"SELECT id, SUM(amount) OVER (" +
"ORDER BY amount RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE GROUP" +
") AS s FROM sales ORDER BY id ASC"
)
assertEquals(listOf(120L, 90L, 90L, 100L, 80L), result.rows.map { (it[1] as Value.IntegerValue).value })
}
@Test
fun excludeTiesRemovesOnlyTheOtherTiedRowsAndKeepsTheCurrentRow() {
seedSales()
val result = select(
"SELECT id, SUM(amount) OVER (" +
"ORDER BY amount RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE TIES" +
") AS s FROM sales ORDER BY id ASC"
)
assertEquals(listOf(130L, 110L, 110L, 130L, 130L), result.rows.map { (it[1] as Value.IntegerValue).value })
}
@Test
fun excludeCurrentRowInteractsWithABoundedFrameInsteadOfOnlyTheWholePartition() {
seedSales()
val result = select(
"SELECT id, SUM(amount) OVER (" +
"ORDER BY amount ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW" +
") AS s FROM sales ORDER BY id ASC"
)
assertEquals(listOf(20L, 30L, 50L, 70L, 30L), result.rows.map { (it[1] as Value.IntegerValue).value })
}
@Test
fun excludeNoOthersExplicitlyWrittenOutBehavesLikeNoExcludeClauseAtAll() {
seedSales()
val explicit = select(
"SELECT id, SUM(amount) OVER (" +
"ORDER BY amount ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE NO OTHERS" +
") AS s FROM sales ORDER BY id ASC"
)
val implicit = select(
"SELECT id, SUM(amount) OVER (ORDER BY amount ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS s " +
"FROM sales ORDER BY id ASC"
)
assertEquals(implicit.rows, explicit.rows)
}
@Test
fun excludeGroupWithNoOrderByTreatsTheWholePartitionAsOnePeerGroup() {
seedSales()
val result = select(
"SELECT id, COUNT(*) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE GROUP) AS c " +
"FROM sales ORDER BY id ASC"
)
assertEquals(listOf(0L, 0L, 0L, 0L, 0L), result.rows.map { (it[1] as Value.IntegerValue).value })
}
@Test
fun excludeClauseSurvivesViewDefinitionRoundTrip() {
seedSales()
run(
"CREATE VIEW excludingSelf AS SELECT id, " +
"SUM(amount) OVER (ORDER BY amount ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW) AS s " +
"FROM sales"
)
val result = select("SELECT id, s FROM excludingSelf ORDER BY id ASC")
assertEquals(listOf(120L, 110L, 110L, 100L, 80L), result.rows.map { (it[1] as Value.IntegerValue).value })
}