Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

StackOverflow when validating 'null.string' but not null. #284

Closed
dlurton opened this issue Aug 10, 2023 · 1 comment · Fixed by #286
Closed

StackOverflow when validating 'null.string' but not null. #284

dlurton opened this issue Aug 10, 2023 · 1 comment · Fixed by #286
Labels
bug Something isn't working

Comments

@dlurton
Copy link

dlurton commented Aug 10, 2023

This might be a duplicate of #169 but I thought I'd file an issue anyway since the situation appears to be slightly different (I'm not specifying any specific Ion or core type here)... I discovered this while attempting to place a constraint on a nullable field that only applied when the value was not null.

The first call to validate() print indicates "null" is not valid for the bat type (unexpected--the type is nullable::, the second call (different only by the typedness of the null value) results in a stack overlfow.

import com.amazon.ion.system.IonSystemBuilder
import com.amazon.ionschema.IonSchemaSystemBuilder
import com.amazon.ionschema.Schema
import org.junit.jupiter.api.Test

class Test {
    val ion = IonSystemBuilder.standard().build()

    @Test
    fun reproduce() {
        val iss = IonSchemaSystemBuilder.standard().withIonSystem(ion).build()

        val schema = iss.newSchema("""
            type::{
                name: bat,
                any_of: [
                 nullable::{valid_values: ["a"]}
                ]
            }
        """.trimIndent())

        validate(schema, "null")
        validate(schema, "null.string")
    }

    private fun validate(schema: Schema, inputText: String) {
        val input = ion.singleValue(inputText)

        val type = schema.getType("bat")!!

        val violations = type.validate(input)
        println("isValid: ${violations.isValid()}")
        println(violations)
    }
}

Output:

isValid: false
Validation failed:
- expected type any
  - value matches none of the types
    - expected type blob, found null
    - expected type bool, found null
    - expected type clob, found null
    - expected type decimal, found null
    - expected type document, found null
    - expected type float, found null
    - expected type int, found null
    - expected type string, found null
    - expected type symbol, found null
    - expected type timestamp, found null
    - expected type list, found null
    - expected type sexp, found null
    - expected type struct, found null


java.lang.StackOverflowError
	at com.amazon.ion.impl.lite.IonStructLite.find_field_helper(IonStructLite.java:431)
	at com.amazon.ion.impl.lite.IonStructLite.get(IonStructLite.java:402)
	at com.amazon.ionschema.internal.TypeImpl.getBaseType(TypeImpl.kt:73)
	at com.amazon.ionschema.internal.TypeImpl.isValidForBaseType(TypeImpl.kt:84)
	at com.amazon.ionschema.internal.TypeBuiltinImpl.isValidForBaseType(TypeBuiltinImpl.kt)
	at com.amazon.ionschema.internal.TypeImpl.isValidForBaseType(TypeImpl.kt:84)

... and so on...
```
@dlurton dlurton added the bug Something isn't working label Aug 10, 2023
@popematt
Copy link
Contributor

You're right that it is both very close to the other issue, but it is slightly different. The other issue was meant to be more illustrative of the problems with nullable:: in general. I think the root cause it the same though.

There are some types for this Ion Schema Kotlin is unable to determine a "base type" to use for the purpose of allowing a typed null. In Ion Schema Kotlin, this results in a SO Error because it naively tries to find the base type of e.g. any (which is a union of all types) and so there is no single Ion type that is the "base type" for any.

You can deal with this in a few different of ways. One would be to make the type explicit in the nullable::-annotated type definition. E.g.:

type::{
  name: bat,
  any_of: [
    nullable::{type:string, valid_values: ["a"]}
  ]
}

If you are specifically dealing with an inline type that has only a valid_values constraint, you can simply add the desired null values to the list instead of using nullable::. E.g.:

type::{
  name: bat,
  any_of: [
   {valid_values: ["a", null, null.string]}
  ]
}

Alternately, you can use Ion Schema 2.0, which drops nullable:: (because it was confusing, and it's sometimes not possible to find the correct "base type") in favor of $null_or:: which is syntactical sugar for the union of the decorated type and the $null type.

Do any of those solutions work for you?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants