test: cover parsesGroupByGroupingSetsWithAnEmptySet

This commit is contained in:
dualfroz committed 2026-09-09 17:57:56 +02:00
1 parent 33a0168223
commit 8a381f8703
1 file changed
+185
+185
View File
@@ -1976,4 +1976,189 @@ class ParserTest {
assertEquals(listOf("dept", "region"), spec.expressions.map { (it as ColumnExpression).name }) assertEquals(listOf("dept", "region"), spec.expressions.map { (it as ColumnExpression).name })
} }
@Test
fun parsesGroupByGroupingSetsWithAnEmptySet() {
val statement = parseOne(
"SELECT dept, region, SUM(amount) FROM sales GROUP BY GROUPING SETS ((dept, region), (dept), ())"
) as SelectStatement
val spec = statement.groupingSpec as GroupingSetsSpec
assertEquals(3, spec.sets.size)
assertEquals(listOf("dept", "region"), spec.sets[0].map { (it as ColumnExpression).name })
assertEquals(listOf("dept"), spec.sets[1].map { (it as ColumnExpression).name })
assertTrue(spec.sets[2].isEmpty())
assertEquals(listOf("dept", "region"), statement.groupBy.map { (it as ColumnExpression).name })
}
@Test
fun rollupCubeAndGroupingSetsAreNotReservedWordsAsPlainColumnNames() {
val statement = parseOne("SELECT rollup FROM t GROUP BY rollup") as SelectStatement
assertNull(statement.groupingSpec)
assertEquals(listOf("rollup"), statement.groupBy.map { (it as ColumnExpression).name })
}
@Test
fun groupByRollupRequiresAClosingParenthesis() {
assertFailsWith<ParseException> { parse("SELECT 1 FROM t GROUP BY ROLLUP (dept") }
}
@Test
fun groupByGroupingSetsRequiresParenthesizedElements() {
assertFailsWith<ParseException> { parse("SELECT 1 FROM t GROUP BY GROUPING SETS (dept)") }
}
@Test
fun parsesGroupingFunctionCallLikeAnyOtherFunction() {
val statement = parseOne("SELECT dept, GROUPING(dept) FROM sales GROUP BY ROLLUP (dept)") as SelectStatement
val projection = statement.projection as ExpressionProjection
val call = projection.items[1].expression as CallExpression
assertEquals("GROUPING", call.name)
assertEquals(listOf("dept"), call.arguments.map { (it as ColumnExpression).name })
}
@Test
fun plainSelectDefaultsToNotDistinct() {
val statement = parseOne("SELECT dept FROM sales") as SelectStatement
assertFalse(statement.distinct)
assertTrue(statement.distinctOn.isEmpty())
}
@Test
fun selectAllParsesTheSameAsNoModifierAtAll() {
val statement = parseOne("SELECT ALL dept FROM sales") as SelectStatement
assertFalse(statement.distinct)
assertTrue(statement.distinctOn.isEmpty())
}
@Test
fun selectDistinctSetsTheFlagWithNoDistinctOnExpressions() {
val statement = parseOne("SELECT DISTINCT dept, region FROM sales") as SelectStatement
assertTrue(statement.distinct)
assertTrue(statement.distinctOn.isEmpty())
val projection = statement.projection as ExpressionProjection
assertEquals(listOf("dept", "region"), projection.items.map { (it.expression as ColumnExpression).name })
}
@Test
fun selectDistinctOnParsesItsParenthesizedExpressionList() {
val statement = parseOne("SELECT DISTINCT ON (dept, region) dept, region, amount FROM sales") as SelectStatement
assertTrue(statement.distinct)
assertEquals(listOf("dept", "region"), statement.distinctOn.map { (it as ColumnExpression).name })
}
@Test
fun selectDistinctOnAcceptsAnArbitraryExpressionNotJustAColumn() {
val statement = parseOne("SELECT DISTINCT ON (amount + 1) dept FROM sales") as SelectStatement
assertTrue(statement.distinctOn.single() is BinaryExpression)
}
@Test
fun selectDistinctOnRequiresParenthesesAfterOn() {
assertFailsWith<ParseException> { parse("SELECT DISTINCT ON dept FROM sales") }
}
@Test
fun selectDistinctOnRequiresAtLeastOneExpression() {
assertFailsWith<ParseException> { parse("SELECT DISTINCT ON () dept FROM sales") }
}
@Test
fun selectDistinctOnRequiresAClosingParenthesis() {
assertFailsWith<ParseException> { parse("SELECT DISTINCT ON (dept dept FROM sales") }
}
@Test
fun distinctWorksInsideADerivedTableSubquery() {
val statement = parseOne("SELECT * FROM (SELECT DISTINCT dept FROM sales) AS d") as SelectStatement
val source = statement.from as SubquerySource
assertTrue((source.select as SelectStatement).distinct)
}
@Test
fun distinctSurvivesOrderByAndLimitBeingAttachedAfterward() {
val statement = parseOne("SELECT DISTINCT dept FROM sales ORDER BY dept LIMIT 5") as SelectStatement
assertTrue(statement.distinct)
assertNotNull(statement.orderBy)
assertEquals(5, statement.limit)
}
@Test
fun dollarDigitParsesAsAParameterExpression() {
val statement = parseOne("SELECT \$1 FROM sales") as SelectStatement
val projection = statement.projection as ExpressionProjection
assertEquals(ParameterExpression(1), projection.items.single().expression)
}
@Test
fun multiDigitParameterIndexParses() {
val statement = parseOne("SELECT \$12 FROM sales") as SelectStatement
val projection = statement.projection as ExpressionProjection
assertEquals(ParameterExpression(12), projection.items.single().expression)
}
@Test
fun parameterIndexZeroIsAParseError() {
assertFailsWith<ParseException> { parse("SELECT \$0 FROM sales") }
}
@Test
fun prepareParsesTheNameAndWrapsTheTargetStatement() {
val statement = parseOne("PREPARE byId AS SELECT * FROM sales WHERE id = \$1") as PrepareStatement
assertEquals("byId", statement.name)
val target = statement.target as SelectStatement
val where = target.where as BinaryExpression
assertEquals(BinaryOperator.Equal, where.operator)
assertEquals(ParameterExpression(1), where.right)
}
@Test
fun prepareAcceptsInsertUpdateDeleteAndMergeAsItsTarget() {
assertTrue((parseOne("PREPARE p AS INSERT INTO sales VALUES (\$1)") as PrepareStatement).target is InsertStatement)
assertTrue((parseOne("PREPARE p AS UPDATE sales SET dept = \$1") as PrepareStatement).target is UpdateStatement)
assertTrue((parseOne("PREPARE p AS DELETE FROM sales WHERE dept = \$1") as PrepareStatement).target is DeleteStatement)
assertTrue(
(parseOne("PREPARE p AS MERGE INTO sales USING sales2 ON sales.id = sales2.id WHEN MATCHED THEN DELETE") as PrepareStatement)
.target is MergeStatement
)
}
@Test
fun prepareRejectsDdlAndTransactionControlTargets() {
assertFailsWith<ParseException> { parse("PREPARE p AS CREATE TABLE t (id INTEGER)") }
assertFailsWith<ParseException> { parse("PREPARE p AS BEGIN") }
assertFailsWith<ParseException> { parse("PREPARE p AS EXPLAIN SELECT 1") }
}
@Test
fun executeParsesNameAndPositionalArguments() {
val statement = parseOne("EXECUTE byId(1, 'ada')") as ExecuteStatement
assertEquals("byId", statement.name)
assertEquals(listOf(intLit(1), LiteralExpression(Value.TextValue("ada"))), statement.arguments)
}
@Test
fun executeWithNoParenthesesHasNoArguments() {
val statement = parseOne("EXECUTE byId") as ExecuteStatement
assertEquals(emptyList(), statement.arguments)
}
@Test
fun executeWithEmptyParenthesesHasNoArguments() {
val statement = parseOne("EXECUTE byId()") as ExecuteStatement
assertEquals(emptyList(), statement.arguments)
}
@Test
fun deallocateParsesAName() {
assertEquals(DeallocateStatement("byId"), parseOne("DEALLOCATE byId"))
}
@Test
fun deallocatePrepareKeywordIsOptional() {
assertEquals(DeallocateStatement("byId"), parseOne("DEALLOCATE PREPARE byId"))
}
@Test
fun deallocateAllHasNoName() {
assertEquals(DeallocateStatement(null, all = true), parseOne("DEALLOCATE ALL"))
}
} }