Skip to content

Commit 5de181c

Browse files
authored
Merge pull request #219 from modelix/bugfix/cce-nullIfEmpty
fix(modelql): .nullIfEmpty() caused a CCE in some cases
2 parents 45f6358 + c7f02cf commit 5de181c

File tree

3 files changed

+28
-7
lines changed

3 files changed

+28
-7
lines changed

modelql-core/src/commonMain/kotlin/org/modelix/modelql/core/NullIfEmpty.kt

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,30 @@
1313
*/
1414
package org.modelix.modelql.core
1515

16+
import kotlinx.coroutines.flow.map
1617
import kotlinx.coroutines.flow.onEmpty
1718
import kotlinx.serialization.KSerializer
1819
import kotlinx.serialization.SerialName
1920
import kotlinx.serialization.Serializable
20-
import kotlinx.serialization.builtins.nullable
2121
import kotlinx.serialization.modules.SerializersModule
2222

2323
class NullIfEmpty<E>() : MonoTransformingStep<E, E?>() {
2424

2525
override fun getOutputSerializer(serializersModule: SerializersModule): KSerializer<out IStepOutput<E?>> {
26-
val serializer: KSerializer<IStepOutput<E>> = getProducer().getOutputSerializer(serializersModule).upcast()
27-
val valueSerializer = (serializer as SimpleStepOutputSerializer<E>).valueSerializer
28-
val nullableValueSerializer = (valueSerializer as KSerializer<Any>).nullable as KSerializer<E?>
29-
return nullableValueSerializer.stepOutputSerializer(this)
26+
return MultiplexedOutputSerializer(
27+
this,
28+
listOf(
29+
getProducer().getOutputSerializer(serializersModule).upcast(),
30+
nullSerializer<E?>().stepOutputSerializer(this).upcast(),
31+
),
32+
)
3033
}
3134

3235
override fun createFlow(input: StepFlow<E>, context: IFlowInstantiationContext): StepFlow<E?> {
3336
val downcast: StepFlow<E?> = input
34-
return downcast.onEmpty { emit((null as E?).asStepOutput(this@NullIfEmpty)) }
37+
return downcast.map { MultiplexedOutput(0, it) }.onEmpty {
38+
emit(MultiplexedOutput(1, null.asStepOutput(this@NullIfEmpty)))
39+
}
3540
}
3641

3742
override fun transform(evaluationContext: QueryEvaluationContext, input: E): E? {

modelql-core/src/commonTest/kotlin/org/modelix/modelql/core/ModelQLTest.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,22 @@ class ModelQLTest {
279279
assertEquals(flowSize, sequenceSize)
280280
}
281281

282+
@Test
283+
fun test_firstOrNull_nullIfEmpty() = runTestWithTimeout {
284+
val result = remoteProductDatabaseQuery { db ->
285+
db.products.firstOrNull().nullIfEmpty()
286+
}
287+
assertEquals(testDatabase.products.firstOrNull(), result)
288+
}
289+
290+
@Test
291+
fun test_nullIfEmpty() = runTestWithTimeout {
292+
val result = remoteProductDatabaseQuery { db ->
293+
db.products.nullIfEmpty().toList()
294+
}
295+
assertEquals(testDatabase.products, result)
296+
}
297+
282298
// @Test
283299
// fun testIndexLookup() {
284300
// val result = remoteProductDatabaseQuery { db ->

modelql-core/src/jvmTest/kotlin/org/modelix/modelql/core/PerformanceTests.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class PerformanceTests {
4747
val query = buildMonoQuery<Int, Int> { it.filter { it.equalTo(0) } }
4848
val intRange = 1..100000
4949

50-
compareBenchmark(100, 10.0, {
50+
compareBenchmark(100, 20.0, {
5151
query.asSequence(QueryEvaluationContext.EMPTY, intRange.asSequence()).count()
5252
}, {
5353
intRange.asSequence().filter { it == 0 }.count()

0 commit comments

Comments
 (0)