test: cover lagPastThePartitionEdgeFallsBackToTheDefaultExpressionIns...

This commit is contained in:
dualfroz committed 2026-09-10 20:44:11 +02:00
1 parent 5199293348
commit c90e6c12e1
1 file changed
+158
@@ -304,3 +304,161 @@ class WindowFunctionTest {
)
}
@Test
fun lagPastThePartitionEdgeFallsBackToTheDefaultExpressionInsteadOfNull() {
seed()
val result = select(
"SELECT id, LAG(salary, 1, 0) OVER (PARTITION BY deptId ORDER BY salary DESC) AS prev " +
"FROM employees ORDER BY id ASC"
)
assertEquals(
listOf(0L, 400L, 400L, 0L, 200L, 200L),
result.rows.map { (it[1] as Value.IntegerValue).value }
)
}
@Test
fun lagOffsetMustNotBeNegative() {
seed()
val error = assertFailsWith<KestrelException> {
run("SELECT LAG(salary, -1) OVER (ORDER BY id) FROM employees")
}
assertTrue(error.message!!.contains("negative"), error.message!!)
}
@Test
fun lagOffsetMustBeAnInteger() {
seed()
val error = assertFailsWith<KestrelException> {
run("SELECT LAG(salary, 1.5) OVER (ORDER BY id) FROM employees")
}
assertTrue(error.message!!.contains("integer"), error.message!!)
}
@Test
fun firstValueReturnsTheFirstRowsValueForEveryRowInThePartition() {
seed()
val result = select(
"SELECT id, FIRST_VALUE(salary) OVER (PARTITION BY deptId ORDER BY salary DESC) AS top " +
"FROM employees ORDER BY id ASC"
)
assertEquals(listOf(400L, 400L, 400L, 200L, 200L, 200L), result.rows.map { (it[1] as Value.IntegerValue).value })
}
@Test
fun lastValueSharesOneValuePerPeerGroupRatherThanTheWholePartition() {
seed()
val result = select(
"SELECT id, LAST_VALUE(salary) OVER (PARTITION BY deptId ORDER BY salary DESC) AS lv " +
"FROM employees ORDER BY id ASC"
)
assertEquals(listOf(400L, 400L, 300L, 200L, 200L, 100L), result.rows.map { (it[1] as Value.IntegerValue).value })
}
@Test
fun lastValueWithoutOverOrderByUsesTheWholePartitionsLastRow() {
seed()
val result = select(
"SELECT id, LAST_VALUE(salary) OVER (PARTITION BY deptId) AS lv FROM employees ORDER BY id ASC"
)
assertEquals(listOf(300L, 300L, 300L, 100L, 100L, 100L), result.rows.map { (it[1] as Value.IntegerValue).value })
}
@Test
fun ntileSplitsEachPartitionIntoAsEvenBucketsAsPossible() {
seed()
val result = select(
"SELECT id, NTILE(2) OVER (PARTITION BY deptId ORDER BY salary DESC) AS bucket " +
"FROM employees ORDER BY id ASC"
)
assertEquals(listOf(1L, 1L, 2L, 1L, 1L, 2L), result.rows.map { (it[1] as Value.IntegerValue).value })
}
@Test
fun ntileWithMoreBucketsThanRowsLeavesTheHighestBucketsUnused() {
seed()
val result = select(
"SELECT id, NTILE(5) OVER (PARTITION BY deptId ORDER BY salary DESC) AS bucket " +
"FROM employees ORDER BY id ASC"
)
assertEquals(listOf(1L, 2L, 3L, 1L, 2L, 3L), result.rows.map { (it[1] as Value.IntegerValue).value })
}
@Test
fun ntileBucketCountMustBePositive() {
seed()
val error = assertFailsWith<KestrelException> {
run("SELECT NTILE(0) OVER (ORDER BY id) FROM employees")
}
assertTrue(error.message!!.contains("positive"), error.message!!)
}
@Test
fun ntileBucketCountMustBeAnInteger() {
seed()
val error = assertFailsWith<KestrelException> {
run("SELECT NTILE(1.5) OVER (ORDER BY id) FROM employees")
}
assertTrue(error.message!!.contains("integer"), error.message!!)
}
@Test
fun windowFunctionNestedInsideLagsOffsetIsRejected() {
seed()
val error = assertFailsWith<KestrelException> {
run("SELECT LAG(salary, ROW_NUMBER() OVER (ORDER BY id)) OVER (ORDER BY id) FROM employees")
}
assertTrue(error.message!!.contains("nested"), error.message!!)
}
@Test
fun explicitRowsFrameProducesAMovingAverageInsteadOfARunningTotal() {
seed()
val result = select(
"SELECT id, SUM(salary) OVER (PARTITION BY deptId ORDER BY id ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS s " +
"FROM employees ORDER BY id ASC"
)
assertEquals(
listOf(400L, 800L, 700L, 200L, 400L, 300L),
result.rows.map { (it[1] as Value.IntegerValue).value }
)
}
@Test
fun rowsBetweenCurrentRowAndFollowingLooksForward() {
seed()
val result = select(
"SELECT id, SUM(salary) OVER (PARTITION BY deptId ORDER BY id ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING) AS s " +
"FROM employees ORDER BY id ASC"
)
assertEquals(
listOf(800L, 700L, 300L, 400L, 300L, 100L),
result.rows.map { (it[1] as Value.IntegerValue).value }
)
}
@Test
fun rowsBetweenUnboundedPrecedingAndCurrentRowMatchesTheImplicitDefaultOnAUniqueOrderKey() {
seed()
val explicit = select(
"SELECT id, SUM(salary) OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS s " +
"FROM employees ORDER BY id ASC"
)
val implicit = select("SELECT id, SUM(salary) OVER (ORDER BY id) AS s FROM employees ORDER BY id ASC")
assertEquals(implicit.rows, explicit.rows)
}
@Test
fun frameEntirelyPastThePartitionEdgeAggregatesToNullAndCountsZero() {
seed()
val result = select(
"SELECT id, SUM(salary) OVER (PARTITION BY deptId ORDER BY id ROWS BETWEEN 5 FOLLOWING AND 9 FOLLOWING) AS s, " +
"COUNT(*) OVER (PARTITION BY deptId ORDER BY id ROWS BETWEEN 5 FOLLOWING AND 9 FOLLOWING) AS c " +
"FROM employees WHERE deptId = 1 ORDER BY id ASC"
)
assertEquals(
listOf(listOf(int(1), Value.NullValue, int(0)), listOf(int(2), Value.NullValue, int(0)), listOf(int(3), Value.NullValue, int(0))),
result.rows
)
}