[BUG] in, between, case and coalesce on an object or nested field return 500

Chiusa
#5,856 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

@cnoramut ci sta già lavorando.

Dal 13/10/2026.

  • #5870 di @cnoramut — aperta

Valutazione

Difficoltà
4/5
Tempo stimato
3-5 giorni
Idoneità per principianti
38/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
java, sql
Ambito
backend

Direzione di ricerca

Start with visitIn, visitBetween and visitCase in CalciteRexNodeVisitor, and with EnhancedCoalesceFunction, where the common-type check (leastRestrictive) decides between a 400 and a generated cast. Reproduce with the curl steps in the issue, or with the query where f in ('x', 'y') on an object field, to confirm the 500. Done means object and nested fields get the 400 type error for string lists in in, between, case and coalesce, the same as numeric lists already do, and the existing tests still pass.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

bug PPL

Query Information

PPL Command/Query:

source=objs | where f in ('x', 'y') | fields host

f is an object field with one keyword subfield, so describe objs reports it as struct.

Expected Result: HTTP 400 from the in type check. The same field with an integer list already gets it, source=objs | where f in (1, 2) | fields host.

{
  "error": {
    "context": {
      "stage": "analyzing",
      "stage_description": "Parsing and validating the query"
    },
    "reason": "In expression types are incompatible: fields type STRUCT, values type [INTEGER, INTEGER]",
    "details": "In expression types are incompatible: fields type STRUCT, values type [INTEGER, INTEGER]",
    "location": ["while preparing and validating the query plan"],
    "code": "UNKNOWN",
    "type": "SemanticCheckException"
  },
  "status": 400
}

Actual Result: HTTP 500.

{
  "error": {
    "context": {
      "stage": "executing",
      "stage_description": "Running the query"
    },
    "reason": "Internal error while compiling the query plan.",
    "details": "Line 3, Column 87: Cannot cast \"java.lang.String\" to \"java.util.Map\"",
    "location": ["while running the query"],
    "code": "UNKNOWN",
    "type": "RuntimeException"
  },
  "status": 500
}

between, case and coalesce also return 500, and so do nested fields. between returns a body byte-identical to in. coalesce returns 500 once a row has f missing, so it has to take the string.

Expression object, struct nested, array keyword, string long, bigint
where f in ('x', 'y') 500 500 200 400
where f between 'a' and 'z' 500 500 200 400
eval x = case(host = 'a', f else 'z') 500 500 200 400
eval x = coalesce(f, 'z') 500 500 200 200
where f in (1, 2) 400 400 400 200
where f = 'x' 400 400 200 200

The coalesce 500 is a ClassCastException, class java.lang.String cannot be cast to class java.util.Map. The expected result is the string column that coalesce(f, 1) already returns. A computed array behaves the same way, so eval a = split(host, ',') | where a in ('x') also returns 500.

Dataset Information

Dataset/Schema Type

  • OpenTelemetry (OTEL)
  • Simple Schema for Observability (SS4O)
  • Open Cybersecurity Schema Framework (OCSF)
  • Custom (details below)

Index Mapping

PUT objs { "mappings": { "properties": { "host": { "type": "keyword" }, "f": { "properties": { "a": { "type": "keyword" } } } } } }

Sample Data

POST objs/_doc/1?refresh=true  { "host": "a", "f": { "a": "x" } }

A single index reproduces it. No mapping conflict is involved.

Bug Description

Issue Summary

visitIn, visitBetween and visitCase in CalciteRexNodeVisitor each ask leastRestrictive for a common type of the field and the literals, and raise a 400 when there is none. EnhancedCoalesceFunction takes its return type from the same call and falls back to VARCHAR when there is none. In Calcite 1.42.0, which replaced 1.41.0, SqlTypeUtil.canCastFrom gained two branches that make a cast from a string to an array or a map legal. PPL types object as MAP and nested as ARRAY, and leastRestrictiveByCast relies on canCastFrom, so a string literal now shares a common type with either field and the three guards no longer fire. in and between then cast each literal to the map type and case takes the map as its result type, so the generated code converts a String to a Map, which Janino rejects. coalesce declares a map column and then returns the string in it.

Steps to Reproduce

curl -s -XPUT "localhost:9200/objs" -H 'Content-Type: application/json' \
  -d '{"mappings":{"properties":{"host":{"type":"keyword"},"f":{"properties":{"a":{"type":"keyword"}}}}}}'
curl -s -XPOST "localhost:9200/objs/_doc/1?refresh=true" -H 'Content-Type: application/json' -d '{"host":"a","f":{"a":"x"}}'
curl -s -XPOST "localhost:9200/objs/_doc/2?refresh=true" -H 'Content-Type: application/json' -d '{"host":"b"}'

# 1. Returns HTTP 500 with Cannot cast "java.lang.String" to "java.util.Map"
curl -s -XPOST "localhost:9200/_plugins/_ppl" -H 'Content-Type: application/json' \
  -d '{"query":"source=objs | where f in ('\''x'\'', '\''y'\'') | fields host"}' -w '\nHTTP %{http_code}\n'

# 2. between and case also return 500
curl -s -XPOST "localhost:9200/_plugins/_ppl" -H 'Content-Type: application/json' \
  -d '{"query":"source=objs | where f between '\''a'\'' and '\''z'\'' | fields host"}' -w '\nHTTP %{http_code}\n'
curl -s -XPOST "localhost:9200/_plugins/_ppl" -H 'Content-Type: application/json' \
  -d '{"query":"source=objs | eval x = case(host = '\''a'\'', f else '\''z'\'') | fields x"}' -w '\nHTTP %{http_code}\n'

# 3. coalesce returns 500 with a ClassCastException, since document 2 has no f
curl -s -XPOST "localhost:9200/_plugins/_ppl" -H 'Content-Type: application/json' \
  -d '{"query":"source=objs | eval x = coalesce(f, '\''z'\'') | fields x"}' -w '\nHTTP %{http_code}\n'

# 4. An integer list is still rejected with a 400
curl -s -XPOST "localhost:9200/_plugins/_ppl" -H 'Content-Type: application/json' \
  -d '{"query":"source=objs | where f in (1, 2) | fields host"}' -w '\nHTTP %{http_code}\n'

Impact

A filter written against an object where its leaf was meant, such as where resource in ('checkout') for resource.name, returns a 500 that reports an engine defect instead of a 400 that names the field type. The same field with a numeric list gets that 400 today.

Environment Information

OpenSearch Version: main at 866dafcbd, 3.10.0-SNAPSHOT, one node, with the defaults plugins.calcite.enabled=true, plugins.calcite.pushdown.enabled=true and plugins.calcite.fallback.allowed=false.

Additional Details:

  • The Calcite 1.41.0 to 1.42.0 upgrade fixed CVE-2026-46718. It is on the 3.8 and 3.9 branches.
  • The two canCastFrom branches came from CALCITE-7358, Casts involving MAP and ROW types cause compile-time exceptions.
Lingua principale
Java
Stelle
175
Fork
233
Merge medio
3g 36m
PR unite (30g)
41

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di opensearch-project/sql

Tutte le issue di opensearch-project/sql

Issue simili

Altre issue su Java

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.