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

RSQL =ini= cannot handle kotlin enums #3109

Open
manosbatsis opened this issue Nov 3, 2023 · 1 comment
Open

RSQL =ini= cannot handle kotlin enums #3109

manosbatsis opened this issue Nov 3, 2023 · 1 comment

Comments

@manosbatsis
Copy link

Consider a (kotlin) enum:

enum class Symbol { FOO, BAR, BAZ }

and it's use in some entity:

@Entity
@Include(rootLevel = true, name = "myentity")
class MyEntity(
    // ...
    @Enumerated(EnumType.STRING)
    @Column(length = 3, nullable = false, updatable = false)
    val symbol: Symbol,
    // ...
)

Current Behavior

This works:

/api/reserve?filter[myentity]=symbol==FOO

but this:

/api/reserve?filter[myentity]=symbol=ini=FOO*

thows the exception bellow. Seems the query visitor makes a string-type assumption because of the operator:

java.lang.ClassCastException: class example.Symbol cannot be cast to class java.lang.String (example.Symbol is in unnamed module of loader 'app'; java.lang.String is in module java.base of loader 'bootstrap')
at org.hibernate.type.descriptor.java.StringJavaType.unwrap(StringJavaType.java:27) ~[hibernate-core-6.2.13.Final.jar:6.2.13.Final]
at org.hibernate.type.descriptor.jdbc.VarcharJdbcType$1.doBind(VarcharJdbcType.java:108) ~[hibernate-core-6.2.13.Final.jar:6.2.13.Final]
at org.hibernate.type.descriptor.jdbc.BasicBinder.bind(BasicBinder.java:61) ~[hibernate-core-6.2.13.Final.jar:6.2.13.Final]
at org.hibernate.sql.exec.internal.AbstractJdbcParameter.bindParameterValue(AbstractJdbcParameter.java:118) ~[hibernate-core-6.2.13.Final.jar:6.2.13.Final]
at org.hibernate.sql.exec.internal.AbstractJdbcParameter.bindParameterValue(AbstractJdbcParameter.java:108) ~[hibernate-core-6.2.13.Final.jar:6.2.13.Final]
at org.hibernate.sql.results.jdbc.internal.DeferredResultSetAccess.bindParameters(DeferredResultSetAccess.java:199) ~[hibernate-core-6.2.13.Final.jar:6.2.13.Final]
at org.hibernate.sql.results.jdbc.internal.DeferredResultSetAccess.executeQuery(DeferredResultSetAccess.java:228) ~[hibernate-core-6.2.13.Final.jar:6.2.13.Final]
at org.hibernate.sql.results.jdbc.internal.DeferredResultSetAccess.getResultSet(DeferredResultSetAccess.java:163) ~[hibernate-core-6.2.13.Final.jar:6.2.13.Final]
at org.hibernate.sql.results.jdbc.internal.JdbcValuesResultSetImpl.advanceNext(JdbcValuesResultSetImpl.java:254) ~[hibernate-core-6.2.13.Final.jar:6.2.13.Final]
at org.hibernate.sql.results.jdbc.internal.JdbcValuesResultSetImpl.processNext(JdbcValuesResultSetImpl.java:134) ~[hibernate-core-6.2.13.Final.jar:6.2.13.Final]
at org.hibernate.sql.results.jdbc.internal.AbstractJdbcValues.next(AbstractJdbcValues.java:19) ~[hibernate-core-6.2.13.Final.jar:6.2.13.Final]
at org.hibernate.sql.results.internal.RowProcessingStateStandardImpl.next(RowProcessingStateStandardImpl.java:66) ~[hibernate-core-6.2.13.Final.jar:6.2.13.Final]
at org.hibernate.internal.ScrollableResultsImpl.next(ScrollableResultsImpl.java:51) ~[hibernate-core-6.2.13.Final.jar:6.2.13.Final]
at org.hibernate.query.internal.ScrollableResultsIterator.hasNext(ScrollableResultsIterator.java:33) ~[hibernate-core-6.2.13.Final.jar:6.2.13.Final]
at java.base/java.util.Spliterators$IteratorSpliterator.tryAdvance(Spliterators.java:1855) ~[na:na]
at java.base/java.util.Spliterators$1Adapter.hasNext(Spliterators.java:681) ~[na:na]
at com.yahoo.elide.datastores.jpql.porting.ScrollableIteratorBase.<init>(ScrollableIteratorBase.java:39) ~[elide-datastore-hibernate-7.0.0-pr6.jar:na]
at com.yahoo.elide.datastores.jpa.ScrollableIterator.<init>(ScrollableIterator.java:19) ~[elide-datastore-jpa-7.0.0-pr6.jar:na]
at com.yahoo.elide.datastores.jpa.porting.QueryWrapper.scroll(QueryWrapper.java:62) ~[elide-datastore-jpa-7.0.0-pr6.jar:na]
at com.yahoo.elide.datastores.jpql.JPQLTransaction.lambda$loadObjects$1(JPQLTransaction.java:130) ~[elide-datastore-hibernate-7.0.0-pr6.jar:na]
at com.yahoo.elide.core.utils.TimedFunction.get(TimedFunction.java:33) ~[elide-core-7.0.0-pr6.jar:na]
at com.yahoo.elide.datastores.jpql.JPQLTransaction.loadObjects(JPQLTransaction.java:131) ~[elide-datastore-hibernate-7.0.0-pr6.jar:na]
at com.yahoo.elide.datastores.multiplex.MultiplexTransaction.loadObjects(MultiplexTransaction.java:68) ~[elide-datastore-multiplex-7.0.0-pr6.jar:na]
at com.yahoo.elide.core.datastore.inmemory.InMemoryStoreTransaction.lambda$loadObjects$2(InMemoryStoreTransaction.java:117) ~[elide-core-7.0.0-pr6.jar:na]
at com.yahoo.elide.core.datastore.inmemory.InMemoryStoreTransaction.fetchData(InMemoryStoreTransaction.java:262) ~[elide-core-7.0.0-pr6.jar:na]
at com.yahoo.elide.core.datastore.inmemory.InMemoryStoreTransaction.loadObjects(InMemoryStoreTransaction.java:123) ~[elide-core-7.0.0-pr6.jar:na]
at com.yahoo.elide.core.PersistentResource.loadRecords(PersistentResource.java:374) ~[elide-core-7.0.0-pr6.jar:na]
at com.yahoo.elide.jsonapi.parser.state.CollectionTerminalState.getResourceCollection(CollectionTerminalState.java:148) ~[elide-core-7.0.0-pr6.jar:na]
at com.yahoo.elide.jsonapi.parser.state.CollectionTerminalState.handleGet(CollectionTerminalState.java:77) ~[elide-core-7.0.0-pr6.jar:na]
at com.yahoo.elide.jsonapi.parser.state.StateContext.handleGet(StateContext.java:108) ~[elide-core-7.0.0-pr6.jar:na]
at com.yahoo.elide.jsonapi.parser.GetVisitor.visitQuery(GetVisitor.java:31) ~[elide-core-7.0.0-pr6.jar:na]
at com.yahoo.elide.jsonapi.parser.GetVisitor.visitQuery(GetVisitor.java:18) ~[elide-core-7.0.0-pr6.jar:na]
at com.yahoo.elide.generated.parsers.CoreParser$QueryContext.accept(CoreParser.java:582) ~[elide-core-7.0.0-pr6.jar:na]
at org.antlr.v4.runtime.tree.AbstractParseTreeVisitor.visitChildren(AbstractParseTreeVisitor.java:46) ~[antlr4-runtime-4.13.0.jar:4.13.0]
at com.yahoo.elide.generated.parsers.CoreBaseVisitor.visitStart(CoreBaseVisitor.java:21) ~[elide-core-7.0.0-pr6.jar:na]
at com.yahoo.elide.jsonapi.parser.BaseVisitor.visitStart(BaseVisitor.java:47) ~[elide-core-7.0.0-pr6.jar:na]
at com.yahoo.elide.jsonapi.parser.BaseVisitor.visitStart(BaseVisitor.java:33) ~[elide-core-7.0.0-pr6.jar:na]
at com.yahoo.elide.generated.parsers.CoreParser$StartContext.accept(CoreParser.java:118) ~[elide-core-7.0.0-pr6.jar:na]
at org.antlr.v4.runtime.tree.AbstractParseTreeVisitor.visit(AbstractParseTreeVisitor.java:18) ~[antlr4-runtime-4.13.0.jar:4.13.0]
at com.yahoo.elide.Elide.visit(Elide.java:572) ~[elide-core-7.0.0-pr6.jar:na]
at com.yahoo.elide.Elide.lambda$get$1(Elide.java:270) ~[elide-core-7.0.0-pr6.jar:na]
at com.yahoo.elide.Elide.handleRequest(Elide.java:596) ~[elide-core-7.0.0-pr6.jar:na]
at com.yahoo.elide.Elide.get(Elide.java:263) ~[elide-core-7.0.0-pr6.jar:na]
at com.yahoo.elide.spring.controllers.JsonApiController$1.call(JsonApiController.java:85) ~[elide-spring-boot-autoconfigure-7.0.0-pr6.jar:na]
at com.yahoo.elide.spring.controllers.JsonApiController$1.call(JsonApiController.java:82) ~[elide-spring-boot-autoconfigure-7.0.0-pr6.jar:na]
at org.springframework.web.context.request.async.WebAsyncManager.lambda$startCallableProcessing$4(WebAsyncManager.java:341) ~[spring-web-6.0.13.jar:6.0.13]
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) ~[na:na]
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[na:na]
at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]

Your Environment

  • Elide version used: 7.0.0-pr6
  • Environment name and version: OpenJDK Runtime Environment (build 17.0.8.1+1-Ubuntu-0ubuntu122.04)
@aklish
Copy link
Member

aklish commented Nov 10, 2023

The asterisk is treated by the FIQL parser as a wildcard (and the operator becomes a prefix query). For in-memory evaluation of the filter, Elide attempts to coerce any argument compared against a wildcard to a String.

The other place the wildcard is evaluated is in the JPQL generation for filtering directly against the database:

        GLOBAL_OPERATOR_GENERATORS.put(PREFIX, new CaseAwareJPQLGenerator(
                "%s LIKE CONCAT(%s, '%%')",
                CaseAwareJPQLGenerator.Case.NONE,
                CaseAwareJPQLGenerator.ArgumentCount.ONE,
                true)
        );

That looks like where the problematic String conversion is happening. You can override the JPQL generation for any model field to work around this:

https://elide.io/pages/guide/v7/16-performance.html#jpql-fragment-override

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants