Skip to content

Commit e05dad1

Browse files
committed
Adds support for evaluation of CASE WHEN THEN
1 parent ff60c72 commit e05dad1

File tree

3 files changed

+136
-0
lines changed

3 files changed

+136
-0
lines changed

partiql-eval/src/main/kotlin/org/partiql/eval/internal/Compiler.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import org.partiql.eval.internal.operator.rel.RelJoinInner
66
import org.partiql.eval.internal.operator.rel.RelJoinLeft
77
import org.partiql.eval.internal.operator.rel.RelProject
88
import org.partiql.eval.internal.operator.rel.RelScan
9+
import org.partiql.eval.internal.operator.rex.ExprCase
910
import org.partiql.eval.internal.operator.rex.ExprCollection
1011
import org.partiql.eval.internal.operator.rex.ExprLiteral
1112
import org.partiql.eval.internal.operator.rex.ExprPathKey
@@ -113,6 +114,14 @@ internal object Compiler {
113114
}
114115
}
115116

117+
override fun visitRexOpCase(node: Rex.Op.Case, ctx: Unit): Operator {
118+
val branches = node.branches.map { branch ->
119+
visitRex(branch.condition, ctx) to visitRex(branch.rex, ctx)
120+
}
121+
val default = visitRex(node.default, ctx)
122+
return ExprCase(branches, default)
123+
}
124+
116125
// TODO: Re-look at
117126
override fun visitPartiQLPlan(node: PartiQLPlan, ctx: Unit): Operator.Expr {
118127
return visitStatement(node.statement, ctx) as Operator.Expr
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package org.partiql.eval.internal.operator.rex
2+
3+
import org.partiql.eval.internal.Record
4+
import org.partiql.eval.internal.operator.Operator
5+
import org.partiql.value.BoolValue
6+
import org.partiql.value.PartiQLValue
7+
import org.partiql.value.PartiQLValueExperimental
8+
9+
internal class ExprCase(
10+
private val branches: List<Pair<Operator.Expr, Operator.Expr>>,
11+
private val default: Operator.Expr
12+
) : Operator.Expr {
13+
14+
@OptIn(PartiQLValueExperimental::class)
15+
override fun eval(record: Record): PartiQLValue {
16+
branches.forEach { branch ->
17+
val condition = branch.first.eval(record)
18+
if (condition.isTrue()) {
19+
return branch.second.eval(record)
20+
}
21+
}
22+
return default.eval(record)
23+
}
24+
25+
@OptIn(PartiQLValueExperimental::class)
26+
private fun PartiQLValue.isTrue(): Boolean {
27+
return this is BoolValue && this.value == true
28+
}
29+
}

partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEngineDefaultTest.kt

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.partiql.eval.internal
22

3+
import org.junit.jupiter.api.Disabled
34
import org.junit.jupiter.api.Test
45
import org.partiql.eval.PartiQLEngine
56
import org.partiql.eval.PartiQLResult
@@ -12,6 +13,7 @@ import org.partiql.value.bagValue
1213
import org.partiql.value.boolValue
1314
import org.partiql.value.int32Value
1415
import org.partiql.value.nullValue
16+
import org.partiql.value.stringValue
1517
import org.partiql.value.structValue
1618
import kotlin.test.assertEquals
1719

@@ -98,4 +100,100 @@ class PartiQLEngineDefaultTest {
98100
val expected = bagValue(sequenceOf(structValue(sequenceOf("a" to int32Value(1), "b" to nullValue()))))
99101
assertEquals(expected, output)
100102
}
103+
104+
@OptIn(PartiQLValueExperimental::class)
105+
@Test
106+
fun testCaseLiteral00() {
107+
val source = """
108+
CASE
109+
WHEN NULL THEN 'isNull'
110+
WHEN MISSING THEN 'isMissing'
111+
WHEN FALSE THEN 'isFalse'
112+
WHEN TRUE THEN 'isTrue'
113+
END
114+
;
115+
""".trimIndent()
116+
val statement = parser.parse(source).root
117+
val session = PartiQLPlanner.Session("q", "u")
118+
val plan = planner.plan(statement, session)
119+
120+
val prepared = engine.prepare(plan.plan)
121+
val result = engine.execute(prepared) as PartiQLResult.Value
122+
val output = result.value
123+
124+
val expected = stringValue("isTrue")
125+
assertEquals(expected, output)
126+
}
127+
128+
@OptIn(PartiQLValueExperimental::class)
129+
@Test
130+
fun testCaseLiteral01() {
131+
val source = """
132+
CASE
133+
WHEN NULL THEN 'isNull'
134+
WHEN MISSING THEN 'isMissing'
135+
WHEN FALSE THEN 'isFalse'
136+
END
137+
;
138+
""".trimIndent()
139+
val statement = parser.parse(source).root
140+
val session = PartiQLPlanner.Session("q", "u")
141+
val plan = planner.plan(statement, session)
142+
143+
val prepared = engine.prepare(plan.plan)
144+
val result = engine.execute(prepared) as PartiQLResult.Value
145+
val output = result.value
146+
147+
val expected = nullValue()
148+
assertEquals(expected, output)
149+
}
150+
151+
@Disabled("This is disabled because FN EQUALS is not yet implemented.")
152+
@OptIn(PartiQLValueExperimental::class)
153+
@Test
154+
fun testCaseLiteral02() {
155+
val source = """
156+
CASE (1)
157+
WHEN NULL THEN 'isNull'
158+
WHEN MISSING THEN 'isMissing'
159+
WHEN 2 THEN 'isTwo'
160+
WHEN 1 THEN 'isOne'
161+
END
162+
;
163+
""".trimIndent()
164+
val statement = parser.parse(source).root
165+
val session = PartiQLPlanner.Session("q", "u")
166+
val plan = planner.plan(statement, session)
167+
168+
val prepared = engine.prepare(plan.plan)
169+
val result = engine.execute(prepared) as PartiQLResult.Value
170+
val output = result.value
171+
172+
val expected = stringValue("isOne")
173+
assertEquals(expected, output)
174+
}
175+
176+
@Disabled("This is disabled because FN EQUALS is not yet implemented.")
177+
@OptIn(PartiQLValueExperimental::class)
178+
@Test
179+
fun testCaseLiteral03() {
180+
val source = """
181+
CASE (1)
182+
WHEN NULL THEN 'isNull'
183+
WHEN MISSING THEN 'isMissing'
184+
WHEN 2 THEN 'isTwo'
185+
END
186+
;
187+
""".trimIndent()
188+
val statement = parser.parse(source).root
189+
val session = PartiQLPlanner.Session("q", "u")
190+
val plan = planner.plan(statement, session)
191+
192+
val prepared = engine.prepare(plan.plan)
193+
val result = engine.execute(prepared) as PartiQLResult.Value
194+
val output = result.value
195+
196+
val expected = nullValue()
197+
assertEquals(expected, output)
198+
}
101199
}

0 commit comments

Comments
 (0)