Skip to content

Commit fa08f65

Browse files
committed
Adds support for evaluation of CASE WHEN THEN
1 parent ce8d9be commit fa08f65

File tree

3 files changed

+138
-0
lines changed

3 files changed

+138
-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
@@ -8,6 +8,7 @@ import org.partiql.eval.internal.operator.rel.RelJoinOuterFull
88
import org.partiql.eval.internal.operator.rel.RelJoinRight
99
import org.partiql.eval.internal.operator.rel.RelProject
1010
import org.partiql.eval.internal.operator.rel.RelScan
11+
import org.partiql.eval.internal.operator.rex.ExprCase
1112
import org.partiql.eval.internal.operator.rex.ExprCollection
1213
import org.partiql.eval.internal.operator.rex.ExprLiteral
1314
import org.partiql.eval.internal.operator.rex.ExprSelect
@@ -96,6 +97,14 @@ internal object Compiler {
9697
}
9798
}
9899

100+
override fun visitRexOpCase(node: Rex.Op.Case, ctx: Unit): Operator {
101+
val branches = node.branches.map { branch ->
102+
visitRex(branch.condition, ctx) to visitRex(branch.rex, ctx)
103+
}
104+
val default = visitRex(node.default, ctx)
105+
return ExprCase(branches, default)
106+
}
107+
99108
// TODO: Re-look at
100109
override fun visitPartiQLPlan(node: PartiQLPlan, ctx: Unit): Operator.Expr {
101110
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: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import org.partiql.value.boolValue
1515
import org.partiql.value.int32Value
1616
import org.partiql.value.io.PartiQLValueIonWriterBuilder
1717
import org.partiql.value.nullValue
18+
import org.partiql.value.stringValue
1819
import org.partiql.value.structValue
1920
import java.io.ByteArrayOutputStream
2021
import kotlin.test.assertEquals
@@ -112,10 +113,12 @@ class PartiQLEngineDefaultTest {
112113
fun testJoinOuterFull() {
113114
val statement =
114115
parser.parse("SELECT a, b FROM << { 'a': 1 } >> t FULL OUTER JOIN << { 'b': 2 } >> s ON false;").root
116+
115117
val session = PartiQLPlanner.Session("q", "u")
116118
val plan = planner.plan(statement, session)
117119

118120
val prepared = engine.prepare(plan.plan)
121+
119122
val result = engine.execute(prepared)
120123
if (result is PartiQLResult.Error) {
121124
throw result.cause
@@ -136,6 +139,30 @@ class PartiQLEngineDefaultTest {
136139
assertEquals(expected, output, comparisonString(expected, output))
137140
}
138141

142+
@OptIn(PartiQLValueExperimental::class)
143+
@Test
144+
fun testCaseLiteral00() {
145+
val source = """
146+
CASE
147+
WHEN NULL THEN 'isNull'
148+
WHEN MISSING THEN 'isMissing'
149+
WHEN FALSE THEN 'isFalse'
150+
WHEN TRUE THEN 'isTrue'
151+
END
152+
;
153+
""".trimIndent()
154+
val statement = parser.parse(source).root
155+
val session = PartiQLPlanner.Session("q", "u")
156+
val plan = planner.plan(statement, session)
157+
158+
val prepared = engine.prepare(plan.plan)
159+
val result = engine.execute(prepared) as PartiQLResult.Value
160+
val output = result.value
161+
162+
val expected = stringValue("isTrue")
163+
assertEquals(expected, output)
164+
}
165+
139166
@OptIn(PartiQLValueExperimental::class)
140167
@Test
141168
fun testJoinOuterFullOnTrue() {
@@ -161,6 +188,30 @@ class PartiQLEngineDefaultTest {
161188
assertEquals(expected, output, comparisonString(expected, output))
162189
}
163190

191+
@OptIn(PartiQLValueExperimental::class)
192+
@Test
193+
fun testCaseLiteral01() {
194+
val source = """
195+
CASE
196+
WHEN NULL THEN 'isNull'
197+
WHEN MISSING THEN 'isMissing'
198+
WHEN FALSE THEN 'isFalse'
199+
END
200+
;
201+
""".trimIndent()
202+
val statement = parser.parse(source).root
203+
val session = PartiQLPlanner.Session("q", "u")
204+
val plan = planner.plan(statement, session)
205+
206+
val prepared = engine.prepare(plan.plan)
207+
208+
val result = engine.execute(prepared) as PartiQLResult.Value
209+
val output = result.value
210+
211+
val expected = nullValue()
212+
assertEquals(expected, output)
213+
}
214+
164215
@OptIn(PartiQLValueExperimental::class)
165216
private fun comparisonString(expected: PartiQLValue, actual: PartiQLValue): String {
166217
val expectedBuffer = ByteArrayOutputStream()
@@ -173,4 +224,53 @@ class PartiQLEngineDefaultTest {
173224
appendLine("Actual : $expectedBuffer")
174225
}
175226
}
227+
228+
@Disabled("This is disabled because FN EQUALS is not yet implemented.")
229+
@OptIn(PartiQLValueExperimental::class)
230+
@Test
231+
fun testCaseLiteral02() {
232+
val source = """
233+
CASE (1)
234+
WHEN NULL THEN 'isNull'
235+
WHEN MISSING THEN 'isMissing'
236+
WHEN 2 THEN 'isTwo'
237+
WHEN 1 THEN 'isOne'
238+
END
239+
;
240+
""".trimIndent()
241+
val statement = parser.parse(source).root
242+
val session = PartiQLPlanner.Session("q", "u")
243+
val plan = planner.plan(statement, session)
244+
245+
val prepared = engine.prepare(plan.plan)
246+
val result = engine.execute(prepared) as PartiQLResult.Value
247+
val output = result.value
248+
249+
val expected = stringValue("isOne")
250+
assertEquals(expected, output)
251+
}
252+
253+
@Disabled("This is disabled because FN EQUALS is not yet implemented.")
254+
@OptIn(PartiQLValueExperimental::class)
255+
@Test
256+
fun testCaseLiteral03() {
257+
val source = """
258+
CASE (1)
259+
WHEN NULL THEN 'isNull'
260+
WHEN MISSING THEN 'isMissing'
261+
WHEN 2 THEN 'isTwo'
262+
END
263+
;
264+
""".trimIndent()
265+
val statement = parser.parse(source).root
266+
val session = PartiQLPlanner.Session("q", "u")
267+
val plan = planner.plan(statement, session)
268+
269+
val prepared = engine.prepare(plan.plan)
270+
val result = engine.execute(prepared) as PartiQLResult.Value
271+
val output = result.value
272+
273+
val expected = nullValue()
274+
assertEquals(expected, output)
275+
}
176276
}

0 commit comments

Comments
 (0)