Repository navigation
Conversation
Parse from_csv and the CSV datasource as unbounded STRING, then apply write-side CHAR/VARCHAR assignment so overflow is a whole-row parse-mode error, matching JSON after SPARK-59722.
HyukjinKwon
left a comment
There was a problem hiding this comment.
Review summary
The CSV change is small and is applied consistently in the V1 reader, the V2 scan builder, and the parser. Every excluded filter is still evaluated after the scan, so the change cannot drop rows.
The main open point is whether the problem it fixes can actually happen. Reading the source, CAST('ab' AS CHAR(4)) is folded to the padded literal before filter translation, so the pushed filter already seems to match the padded value, and the end-to-end test may pass without the change. I've raised this as a question in the decision challenge below rather than as a defect.
There is also one code-level nit: the new end-to-end test registers a temp view that is never dropped from the shared session.
Findings
1 total: 0 P0, 0 P1, 0 P2, 1 P3.
Nit (P3)
- New CSV CHAR test leaks the
csv_chartemp view —sql/core/src/test/scala/org/apache/spark/sql/CharVarcharTestSuite.scala:2906— see inline.
Decision challenges
Does the CSV CHAR filter mismatch reproduce, and does the e2e test guard it?
Could you confirm that the row loss this PR fixes actually reproduces on master? Reading the pinned source, I couldn't find a path where the pushed filter and the post-scan predicate disagree:
DataSourceStrategy.translateLeafNodeFilteronly translatescolumn = Literaland copies the literal value unchanged (DataSourceStrategy.scala:640-643). SoCAST('ab' AS CHAR(4))must already be folded byConstantFoldingby the time a sourceFilterexists.- Evaluating that cast pads to
'ab 'on both CHAR branches ofToStringBase.castToString(charTypeCastandcharTypeWriteSideCheck). - With a first-class
c CHAR(4)schema, the attribute carries no raw-type metadata, soApplyCharTypePaddingadds no read-side projection or literal rewrite. OrderedFiltersthen evaluatesEqualTo("c", "ab ")against the same padded'ab 'that the post-scan filter sees.
If that is right, the new end-to-end assertions pass with or without the exclusion, and the change only gives up parser-level row skipping for every filter on a CHAR column. The only mismatch the tests demonstrate is the hand-built EqualTo("c", "ab") in UnivocityParserSuite, and Spark's own c = 'ab' would also reject 'ab ' after the scan.
I didn't build Spark or run the tests, so I may be missing an analyzer or optimizer step. Could you check whether SPARK-60102: CSV CHAR filters are not pushed down fails with the three excludeFiltersOnCharColumns calls reverted, or point to a query that loses rows on master? If such a query exists, using it in the end-to-end test would make the test guard the fix.
Generated by Omnigent on Databricks.
|
Thanks for the careful read. I reverted the three
So the analysis is right: the end-to-end test does not guard a SQL-path bug, and Spark's own I wrapped the temp view as suggested. Happy to drop the CHAR-column exclusion (and keep only the parser unit test, or close the PR) if that cost is not worth a synthetic mismatch. |
|
Closing this PR.
The remaining CHAR-column filter skip does not fix a Spark-SQL-generated mismatch. Spark's own equality is either already padded ( |
What changes were proposed in this pull request?
When CSV uses a first-class
CHARschema, do not push filters that reference a top-levelCHARcolumn intoUnivocityParser. Filters on other columns in the same schema continue to push down.The exclusion is applied consistently in the V1 CSV file format, the V2 CSV scan builder, and
UnivocityParser.VARCHARfilters are unchanged becauseVARCHARvalues are not padded.CSV value conversion remains unchanged: CHAR/VARCHAR semantics continue to run inside the STRING converter, matching
from_jsonvalues. This PR does not introduce post-parse assignment or change PERMISSIVE/FAILFAST behavior.JIRA: https://issues.apache.org/jira/browse/SPARK-60102
Related: #59325
Why are the changes needed?
The CSV converter pads a parsed
CHAR(n)value to lengthn. A translated source filter can contain the corresponding unpadded literal and is compiled as STRING equality. Evaluating that filter inside the parser can therefore reject a row that satisfies the CHAR predicate after the scan.For example, a CSV row containing
abunder schemac CHAR(4)must satisfy:Skipping only filters that reference
cpreserves correctness while retaining pushdown for independent non-CHAR columns.Does this PR introduce any user-facing change?
Yes, relative to current unreleased
master, when first-class CHAR/VARCHAR semantics are enabled. CSV queries with a pushed filter on a CHAR column no longer lose matching rows. CSV parsing, CHAR padding, VARCHAR length checks, parse modes, and filters on non-CHAR columns are unchanged.How was this patch tested?
Added coverage that:
./dev/lint-scalaalso passed.Was this patch authored or co-authored using generative AI tooling?
Generated-by: Cursor