Ivelin Tchangalov created SPARK-60002:
-----------------------------------------

             Summary: CREATE TABLE ... ROW FORMAT DELIMITED silently discards 
NULL DEFINED AS
                 Key: SPARK-60002
                 URL: https://issues.apache.org/jira/browse/SPARK-60002
             Project: Spark
          Issue Type: Bug
          Components: SQL
    Affects Versions: 4.0.0, 3.5.6
            Reporter: Ivelin Tchangalov


Spark's SQL parser accepts the 
{code:java}
NULL DEFINED AS {code}
 clause of 
{code:java}
CREATE TABLE ... ROW FORMAT DELIMITED{code}
but it is never never written to the table's SerDe properties

Hive honours the clause, so the same DDL over the same files returns different 
results in
Hive and in Spark.

Every delimited-text table whose sentinel is not Hive's default {{\N}}
reads that sentinel as literal data instead of NULL. Because the clause is 
accepted
silently, there is no signal at table-creation time. That translates to a data 
issues because data isn't read correctly.
h3. Root cause

{{AstBuilder.visitRowFormatDelimited}} builds the SerDe property map from the 
delimited
clauses but never reads {{{}ctx.nullDefinedAs{}}}. The method carries the 
acknowledgement in a
comment: {{// TODO we need proper support for the NULL format.}}

{{{}field.delim{}}}, {{{}escape.delim{}}}, {{{}colelction.delim{}}}, 
{{mapkey.delim}} and
{{line.delim}} are all stored correctly. The gap is exactly one property,
{{{}serialization.null.format{}}}, which appears nowhere in Spark's main source:
{code:java}
  grep -rn "serialization\.null\.format" --include=*.scala --include=*.java . | 
grep -v /test/
  #   (no output)
  {code}
The grammar already captures the value: {{SqlBaseParser.g4}} binds it to the 
labelled token {{{}nullDefinedAs{}}}. Its only consumer in the codebase is 
{{{}getRowFormatDelimited{}}}, the SCRIPT TRANSFORM path, which maps it to 
{{{}TOK_TABLEROWFORMATNULL{}}}. The table-DDL visitor ignores it.
h3. Reproduction

PySpark with Hive support enabled; {{DESCRIBE FORMATTED}} reports {{{}Provider: 
hive{}}}, so
this is a genuine Hive SerDe table and not a converted datasource table.
{code:sql}
  CREATE EXTERNAL TABLE t (id INT, region STRING)
  ROW FORMAT DELIMITED FIELDS TERMINATED BY '|'
  NULL DEFINED AS 'NA'
  STORED AS TEXTFILE LOCATION '...';
  {code}
Data:
{noformat}
  1|east
  2|NA
  3|west
  {noformat}
{{DESCRIBE FORMATTED t}} reports
{noformat}
  Storage Properties   [serialization.format=|, field.delim=|]
  {noformat}
There is no {{{}serialization.null.format{}}}, and the row with id 2 reads 
{{region}} as the
string {{'NA'}} rather than NULL. Hive returns NULL for the same table and data.

Every sentinel value is affected: {{{}''{}}}, {{{}\N{}}}, {{{}NA{}}}, 
{{{}X{}}}, {{\t}} and {{\001}} all
behave identically. {{\N}} appears to work only because it is the engine 
default.
h3. How the bug was introduced

It was never implemented
h3. Related issues

SPARK-14583 reported the read-side variant of this parity gap: a sentinel 
supplied through {{TBLPROPERTIES}} on a partitioned table that Spark fails to 
apply on read. It was
bulk-closed as Incomplete in 2019. That is a different vector into the same 
Hive parity gap and is not addressed by this ticket.



--
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