test: cover groupsBetweenPrecedingAndFollowingCountsPeerGroupsNotPhys...
This commit is contained in:
1 parent
a46897c93d
commit
b4524fcffb
1 file changed
+156
@@ -779,3 +779,159 @@ class WindowFunctionTest {
|
|||||||
assertEquals(listOf(120L, 110L, 110L, 100L, 80L), result.rows.map { (it[1] as Value.IntegerValue).value })
|
assertEquals(listOf(120L, 110L, 110L, 100L, 80L), result.rows.map { (it[1] as Value.IntegerValue).value })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- GROUPS frame unit ----
|
||||||
|
// sales (ordered by amount): id1=10, {id2=20, id3=20} tied, id4=30, id5=50 -> 4 distinct peer
|
||||||
|
// groups. GROUPS counts THESE groups, unlike ROWS (physical rows) or RANGE (value distance).
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun groupsBetweenPrecedingAndFollowingCountsPeerGroupsNotPhysicalRows() {
|
||||||
|
seedSales()
|
||||||
|
val groups = select(
|
||||||
|
"SELECT id, SUM(amount) OVER (ORDER BY amount GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS s " +
|
||||||
|
"FROM sales ORDER BY id ASC"
|
||||||
|
)
|
||||||
|
val rows = select(
|
||||||
|
"SELECT id, SUM(amount) OVER (ORDER BY amount ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS s " +
|
||||||
|
"FROM sales ORDER BY id ASC"
|
||||||
|
)
|
||||||
|
// id2/id3's tied peer group makes the GROUPS frame span more physical rows than ROWS for
|
||||||
|
// their neighbours (id1 and id4), even though both ask for "1 PRECEDING AND 1 FOLLOWING".
|
||||||
|
assertEquals(listOf(50L, 80L, 80L, 120L, 80L), groups.rows.map { (it[1] as Value.IntegerValue).value })
|
||||||
|
assertEquals(listOf(30L, 50L, 70L, 100L, 80L), rows.rows.map { (it[1] as Value.IntegerValue).value })
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun groupsFrameBehavesExactlyLikeRowsFrameWhenNoRowsAreTied() {
|
||||||
|
run("CREATE TABLE prices (id INTEGER PRIMARY KEY, price DECIMAL(10, 2))")
|
||||||
|
run("INSERT INTO prices VALUES (1, 1.00), (2, 1.40), (3, 1.50), (4, 2.00)")
|
||||||
|
val groups = select(
|
||||||
|
"SELECT id, COUNT(*) OVER (ORDER BY price GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS c " +
|
||||||
|
"FROM prices ORDER BY id ASC"
|
||||||
|
)
|
||||||
|
val rows = select(
|
||||||
|
"SELECT id, COUNT(*) OVER (ORDER BY price ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS c " +
|
||||||
|
"FROM prices ORDER BY id ASC"
|
||||||
|
)
|
||||||
|
assertEquals(rows.rows, groups.rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun groupsCurrentRowExpandsToTheWholePeerGroupJustLikeRangeCurrentRow() {
|
||||||
|
seedSales()
|
||||||
|
val groups = select(
|
||||||
|
"SELECT id, SUM(amount) OVER (ORDER BY amount GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS s " +
|
||||||
|
"FROM sales ORDER BY id ASC"
|
||||||
|
)
|
||||||
|
val range = select(
|
||||||
|
"SELECT id, SUM(amount) OVER (ORDER BY amount RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS s " +
|
||||||
|
"FROM sales ORDER BY id ASC"
|
||||||
|
)
|
||||||
|
val rows = select(
|
||||||
|
"SELECT id, SUM(amount) OVER (ORDER BY amount ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS s " +
|
||||||
|
"FROM sales ORDER BY id ASC"
|
||||||
|
)
|
||||||
|
assertEquals(listOf(10L, 50L, 50L, 80L, 130L), groups.rows.map { (it[1] as Value.IntegerValue).value })
|
||||||
|
assertEquals(groups.rows, range.rows)
|
||||||
|
assertEquals(listOf(10L, 30L, 50L, 80L, 130L), rows.rows.map { (it[1] as Value.IntegerValue).value })
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun groupsFrameWorksWithAMultiColumnNonNumericOrderByUnlikeRange() {
|
||||||
|
seed()
|
||||||
|
val result = select(
|
||||||
|
"SELECT id, COUNT(*) OVER (ORDER BY deptId, name GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS c " +
|
||||||
|
"FROM employees ORDER BY deptId ASC, name ASC"
|
||||||
|
)
|
||||||
|
// ada, grace, linus (dept 1) then kay, meg, sam (dept 2) - every row is its own peer group
|
||||||
|
// since no two rows share the same (deptId, name), so this is exactly the sliding-window-of-3
|
||||||
|
// pattern (2 at each edge, 3 in the middle).
|
||||||
|
assertEquals(
|
||||||
|
listOf(int(1), int(3), int(2), int(5), int(6), int(4)),
|
||||||
|
result.rows.map { it[0] }
|
||||||
|
)
|
||||||
|
assertEquals(listOf(2L, 3L, 3L, 3L, 3L, 2L), result.rows.map { (it[1] as Value.IntegerValue).value })
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun groupsFrameEntirelyPastThePartitionEdgeAggregatesToNullAndCountsZero() {
|
||||||
|
seedSales()
|
||||||
|
val result = select(
|
||||||
|
"SELECT id, SUM(amount) OVER (ORDER BY amount GROUPS BETWEEN 100 FOLLOWING AND 200 FOLLOWING) AS s, " +
|
||||||
|
"COUNT(*) OVER (ORDER BY amount GROUPS 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 groupsFrameOffsetMustBeAnInteger() {
|
||||||
|
seedSales()
|
||||||
|
val error = assertFailsWith<KestrelException> {
|
||||||
|
run("SELECT SUM(amount) OVER (ORDER BY amount GROUPS BETWEEN 1.5 PRECEDING AND CURRENT ROW) FROM sales")
|
||||||
|
}
|
||||||
|
assertTrue(error.message!!.contains("integer"), error.message!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun groupsFrameOffsetMustNotBeNegative() {
|
||||||
|
seedSales()
|
||||||
|
val error = assertFailsWith<KestrelException> {
|
||||||
|
run("SELECT SUM(amount) OVER (ORDER BY amount GROUPS BETWEEN -1 PRECEDING AND CURRENT ROW) FROM sales")
|
||||||
|
}
|
||||||
|
assertTrue(error.message!!.contains("negative"), error.message!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun groupsFrameWithUnboundedPrecedingAndFollowingCoversTheWholePartitionJustLikeRowsAndRange() {
|
||||||
|
seedSales()
|
||||||
|
val groups = select(
|
||||||
|
"SELECT id, SUM(amount) OVER (ORDER BY amount GROUPS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS s " +
|
||||||
|
"FROM sales ORDER BY id ASC"
|
||||||
|
)
|
||||||
|
assertEquals(listOf(130L, 130L, 130L, 130L, 130L), groups.rows.map { (it[1] as Value.IntegerValue).value })
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun groupsFrameIsRejectedOnRankingAndOffsetWindowFunctions() {
|
||||||
|
seed()
|
||||||
|
val error = assertFailsWith<KestrelException> {
|
||||||
|
run("SELECT ROW_NUMBER() OVER (ORDER BY id GROUPS BETWEEN 1 PRECEDING AND CURRENT ROW) FROM employees")
|
||||||
|
}
|
||||||
|
assertTrue(error.message!!.contains("GROUPS"), error.message!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun excludeGroupCombinesWithAGroupsFrameJustLikeItDoesWithRange() {
|
||||||
|
seedSales()
|
||||||
|
val groups = select(
|
||||||
|
"SELECT id, SUM(amount) OVER (" +
|
||||||
|
"ORDER BY amount GROUPS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE GROUP" +
|
||||||
|
") AS s FROM sales ORDER BY id ASC"
|
||||||
|
)
|
||||||
|
assertEquals(listOf(120L, 90L, 90L, 100L, 80L), groups.rows.map { (it[1] as Value.IntegerValue).value })
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun nestedWindowFunctionInAGroupsFrameOffsetIsRejected() {
|
||||||
|
seedSales()
|
||||||
|
val error = assertFailsWith<KestrelException> {
|
||||||
|
run(
|
||||||
|
"SELECT SUM(amount) OVER (ORDER BY amount GROUPS BETWEEN ROW_NUMBER() OVER (ORDER BY amount) PRECEDING AND CURRENT ROW) " +
|
||||||
|
"FROM sales"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
assertTrue(error.message!!.contains("nested"), error.message!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun groupsFrameSurvivesViewDefinitionRoundTrip() {
|
||||||
|
seedSales()
|
||||||
|
run(
|
||||||
|
"CREATE VIEW groupTotals AS SELECT id, " +
|
||||||
|
"SUM(amount) OVER (ORDER BY amount GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS s " +
|
||||||
|
"FROM sales"
|
||||||
|
)
|
||||||
|
val result = select("SELECT id, s FROM groupTotals ORDER BY id ASC")
|
||||||
|
assertEquals(listOf(50L, 80L, 80L, 120L, 80L), result.rows.map { (it[1] as Value.IntegerValue).value })
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user