Qing Fu created SPARK-59598:
-------------------------------

             Summary: GetMapValue and ElementAt must not match a null map key
                 Key: SPARK-59598
                 URL: https://issues.apache.org/jira/browse/SPARK-59598
             Project: Spark
          Issue Type: Bug
          Components: SQL
    Affects Versions: 5.0.0
            Reporter: Qing Fu


{{GetMapValue}} ({{m[k]}}) and {{ElementAt}} ({{element_at(m, k)}}) share their 
key lookup in {{GetMapValueUtil}}. When the map's key array contains a null, 
the lookup matches that null key against the key type's zero value and returns 
its value instead of NULL.

For a map {{{}{null: 10, 1: 20}{}}}:
{code}
m[0]  ==>  10     -- should be NULL
m[1]  ==>  20     -- correct
{code}

Two independent mechanisms cause this, so it reproduces with codegen both on 
and off:

1. Interpreted linear scan. {{ordering.equiv(keys.get(i, keyType), ordinal)}} 
compares using the key type's natural ordering. {{keys.get}} is null-aware and 
correctly returns null for a null slot, but for a primitive key type the 
ordering unboxes its arguments, and Scala's {{BoxesRunTime.unboxToInt(null)}} 
is 0. So {{equiv(null, 0)}} is true.

2. Generated code. The candidate key is read with a primitive getter 
({{CodeGenerator.getValue}} emits {{keys.getInt(i)}}), which is not null-aware 
and returns 0 for a null slot. This affects the generated linear scan, and also 
the hash probe added by SPARK-55959: {{buildHashBuckets}} hashes the unboxed 
null into the same bucket as 0, so the probe finds the null key and then 
compares 0 == 0.

Maps with null keys are reachable. {{ArrayBasedMapBuilder}} rejects them, but 
the file-format readers construct {{ArrayBasedMapData}} directly and do not. 
{{ParquetRowConverter}} says so in the tree today:

{code:scala}
override def end(): Unit = {
  // The parquet map may contains null or duplicated map keys. When it happens, 
the behavior is
  // undefined.
  // TODO (SPARK-26174): disallow it with a config.
{code}

{{OrcDeserializer}} and {{AvroDeserializer}} likewise build the key array 
without a runtime null check, and the Hive {{MapObjectInspector}} unwrapper in 
{{HiveInspectors}} applies the key unwrapper with no null check at all. So a 
null key can reach the lookup from Parquet, ORC, Avro or Hive data.

The lookup key itself is never null (both expressions are null-safe on the 
ordinal), so a null map key can never be the key being looked up. Skipping null 
keys makes the result well-defined and consistent across all paths, and changes 
nothing for maps without null keys.




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to