[SPARK-59273][SQL] Complete CHAR/VARCHAR support at core execution boundaries - #58541
[SPARK-59273][SQL] Complete CHAR/VARCHAR support at core execution boundaries#58541srielau wants to merge 4 commits into
Conversation
…undaries Treat first-class CharType/VarcharType as the string family in JDBC getters/setters, partition decoding, row-to-column conversion, na.fill, and ANALYZE COLUMN stats so standardSemantics no longer fails at these boundaries.
cloud-fan
left a comment
There was a problem hiding this comment.
Review summary
The execution-boundary changes and their direct tests are otherwise consistent, including the corrected JDBC configuration precedence. Before this is ready, the CBO consumers of column statistics need to handle CHAR/VARCHAR as members of the string family so enabling the new ANALYZE path cannot turn valid query planning into a MatchError.
Findings
1 total: 0 P0, 1 P1, 0 P2, 0 P3.
Blocking (P1)
- Handle constrained strings in CBO after collecting their statistics —
sql/core/src/main/scala/org/apache/spark/sql/execution/command/AnalyzeColumnCommand.scala:145— see inline.
cloud-fan
left a comment
There was a problem hiding this comment.
Review summary
The previously reported CBO MatchError after collecting CHAR/VARCHAR statistics is fixed, and the execution-boundary changes are consistent with the shared first-class string-family contract. Two non-blocking follow-ups remain: add CHAR/VARCHAR to the owning join-estimation type matrix so the widened interval and size paths cannot regress untested, and correct one newly added comment that uses MatchError as a verb.
Findings
2 total: 0 P0, 0 P1, 1 P2, 1 P3.
Non-blocking (P2)
- Exercise CHAR/VARCHAR join estimation —
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/plans/logical/statsEstimation/ValueInterval.scala:56— see inline.
Nit (P3)
- Use MatchError as an exception, not a verb —
sql/core/src/test/scala/org/apache/spark/sql/StatisticsCollectionSuite.scala:189— see inline.
Re-review status
Prior AI findings: 1 addressed, 0 still present; additional unresolved findings in this review: 2.
New attribution: 2 newly introduced, 0 late catch, 0 previously raised, 0 unattributed.
Remaining prior AI findings
No prior AI findings remain.
dtenedor
left a comment
There was a problem hiding this comment.
LGTM, merging to master + 4.x
…undaries
### What changes were proposed in this pull request?
When `spark.sql.charVarchar.standardSemantics.enabled` is true, `CharType` and `VarcharType` are first-class `StringType` subtypes. Several execution-boundary matchers still used exact `StringType` (or an explicit CHAR/VARCHAR reject), so those paths failed or skipped constrained string columns.
This patch treats CHAR/VARCHAR as the string family at:
- JDBC getters/setters and JDBC array element conversion
- JDBC schema inference (`CHAR`/`VARCHAR` keep first-class types when standard semantics is on, even if `charVarcharAsString` is also set)
- File partition value decoding
- `RowToColumnConverter`
- `DataFrame.na.fill` for string replacement values
- `ANALYZE TABLE ... FOR COLUMNS` (string-family stats)
Read-side CHAR padding and VARCHAR overflow still come from existing CAST / `ApplyCharTypePadding` paths rather than being reimplemented in each converter.
### Why are the changes needed?
With first-class CHAR/VARCHAR, JDBC scans/writes, file-only partition discovery, columnar conversion, `na.fill("...")`, and column stats currently throw or silently ignore those columns. That blocks enabling standard semantics.
JIRA: https://issues.apache.org/jira/browse/SPARK-59273 (subtask of SPARK-58794)
### Does this PR introduce _any_ user-facing change?
Yes, when `spark.sql.charVarchar.standardSemantics.enabled` is true (still default false):
- JDBC read/write of CHAR/VARCHAR (including arrays) no longer fails with an unsupported JDBC type.
- File partition columns declared as CHAR/VARCHAR can be decoded; CHAR is padded on scan and oversize VARCHAR fails with `EXCEED_LIMIT_LENGTH`.
- Columnar row-to-column conversion accepts CHAR/VARCHAR.
- `df.na.fill("x")` fills null CHAR/VARCHAR columns (CHAR values are padded by CAST).
- `ANALYZE TABLE ... FOR COLUMNS` collects string-family stats on CHAR/VARCHAR instead of rejecting them.
### How was this patch tested?
Added/extended unit tests:
- `JDBCSuite`: read CHAR/VARCHAR and arrays; write CHAR/VARCHAR; standard semantics wins over `charVarcharAsString` in schema inference
- `ParquetV1PartitionDiscoverySuite` / `ParquetV2PartitionDiscoverySuite`: CHAR/VARCHAR partition values and oversize VARCHAR
- `RowToColumnConverterSuite`: CHAR/VARCHAR and nested CHAR arrays
- `DataFrameNaFunctionsSuite`: `na.fill` on CHAR/VARCHAR
- `StatisticsCollectionSuite`: `ANALYZE TABLE ... FOR COLUMNS` on CHAR/VARCHAR
### Was this patch authored or co-authored using generative AI tooling?
Generated-by: Cursor Grok 4.6
Closes #58541 from srielau/serge-rielau_data/SPARK-59273.
Authored-by: Serge Rielau <serge@rielau.com>
Signed-off-by: Daniel Tenedorio <daniel.tenedorio@databricks.com>
(cherry picked from commit 51f54b0)
Signed-off-by: Daniel Tenedorio <daniel.tenedorio@databricks.com>
|
Seems CI did not pass and related to this change: |
|
opened a fix #58646 @dtenedor @srielau @ulysses-you |
…ataFrameNaFunctions.fillValue ### What changes were proposed in this pull request? SPARK-59273 (#58541) widened the fill-value type match in `DataFrameNaFunctions.fillValue` from the exact `StringType` to the whole string family: ```scala - case (StringType, dt) => dt == StringType + case (StringType, _: StringType) => true ``` That pattern is no longer exhaustive for a string fill value: when the schema contains any non-string column, that column falls through to the throwing `case _` and `df.na.fill(<string>)` fails with `IllegalArgumentException: StringType is not matched at fillValue`. This patch keeps the string-family match but restores the skip for non-string columns: ```scala case (StringType, dt) => dt.isInstanceOf[StringType] ``` which mirrors the `NumericType` case above it. ### Why are the changes needed? `na.fill` with a string value must fill only string-family columns and leave other columns untouched; a schema with any non-string column must not throw. The regression breaks basic usage such as: ```scala Seq[(String, Integer)]((null, null)).toDF("name", "age").na.fill("unknown") ``` and reddened master CI in `DataFrameNaFunctionsSuite`, `SparkConnectProtoSuite`, and the PySpark `DataFrameStatTests.test_fillna` / `DataFrameStatParityTests.test_fillna`. SPARK-59273 has not shipped in a release. ### Does this PR introduce _any_ user-facing change? No. It restores on master the pre-SPARK-59273 behavior of `DataFrameNaFunctions.fill` with a string value. ### How was this patch tested? Existing tests already exercise mixed-type schemas and both behaviors, and failed on master after SPARK-59273: - `DataFrameNaFunctionsSuite`: `fill`, `fill with col(*)`, `fill with nested columns` - `SparkConnectProtoSuite`: `SPARK-41128: Test fill na` - PySpark `DataFrameStatTests.test_fillna`, `DataFrameStatParityTests.test_fillna` Ran locally: ``` build/sbt 'sql/testOnly *DataFrameNaFunctionsSuite' build/sbt 'connect/testOnly *SparkConnectProtoSuite' ``` Both suites pass; no new tests added because the failing cases above cover the regression. ### Was this patch authored or co-authored using generative AI tooling? Generated-by: DeepSeek V4 Flash Closes #58646 from pan3793/spark-59273-nafill-followup. Authored-by: Cheng Pan <pan3793@gmail.com> Signed-off-by: Ruifeng Zheng <ruifengz@foxmail.com>
…ataFrameNaFunctions.fillValue ### What changes were proposed in this pull request? SPARK-59273 (#58541) widened the fill-value type match in `DataFrameNaFunctions.fillValue` from the exact `StringType` to the whole string family: ```scala - case (StringType, dt) => dt == StringType + case (StringType, _: StringType) => true ``` That pattern is no longer exhaustive for a string fill value: when the schema contains any non-string column, that column falls through to the throwing `case _` and `df.na.fill(<string>)` fails with `IllegalArgumentException: StringType is not matched at fillValue`. This patch keeps the string-family match but restores the skip for non-string columns: ```scala case (StringType, dt) => dt.isInstanceOf[StringType] ``` which mirrors the `NumericType` case above it. ### Why are the changes needed? `na.fill` with a string value must fill only string-family columns and leave other columns untouched; a schema with any non-string column must not throw. The regression breaks basic usage such as: ```scala Seq[(String, Integer)]((null, null)).toDF("name", "age").na.fill("unknown") ``` and reddened master CI in `DataFrameNaFunctionsSuite`, `SparkConnectProtoSuite`, and the PySpark `DataFrameStatTests.test_fillna` / `DataFrameStatParityTests.test_fillna`. SPARK-59273 has not shipped in a release. ### Does this PR introduce _any_ user-facing change? No. It restores on master the pre-SPARK-59273 behavior of `DataFrameNaFunctions.fill` with a string value. ### How was this patch tested? Existing tests already exercise mixed-type schemas and both behaviors, and failed on master after SPARK-59273: - `DataFrameNaFunctionsSuite`: `fill`, `fill with col(*)`, `fill with nested columns` - `SparkConnectProtoSuite`: `SPARK-41128: Test fill na` - PySpark `DataFrameStatTests.test_fillna`, `DataFrameStatParityTests.test_fillna` Ran locally: ``` build/sbt 'sql/testOnly *DataFrameNaFunctionsSuite' build/sbt 'connect/testOnly *SparkConnectProtoSuite' ``` Both suites pass; no new tests added because the failing cases above cover the regression. ### Was this patch authored or co-authored using generative AI tooling? Generated-by: DeepSeek V4 Flash Closes #58646 from pan3793/spark-59273-nafill-followup. Authored-by: Cheng Pan <pan3793@gmail.com> Signed-off-by: Ruifeng Zheng <ruifengz@foxmail.com> (cherry picked from commit 3d25204) Signed-off-by: Ruifeng Zheng <ruifengz@foxmail.com>
What changes were proposed in this pull request?
When
spark.sql.charVarchar.standardSemantics.enabledis true,CharTypeandVarcharTypeare first-classStringTypesubtypes. Several execution-boundary matchers still used exactStringType(or an explicit CHAR/VARCHAR reject), so those paths failed or skipped constrained string columns.This patch treats CHAR/VARCHAR as the string family at:
CHAR/VARCHARkeep first-class types when standard semantics is on, even ifcharVarcharAsStringis also set)RowToColumnConverterDataFrame.na.fillfor string replacement valuesANALYZE TABLE ... FOR COLUMNS(string-family stats)Read-side CHAR padding and VARCHAR overflow still come from existing CAST /
ApplyCharTypePaddingpaths rather than being reimplemented in each converter.Why are the changes needed?
With first-class CHAR/VARCHAR, JDBC scans/writes, file-only partition discovery, columnar conversion,
na.fill("..."), and column stats currently throw or silently ignore those columns. That blocks enabling standard semantics.JIRA: https://issues.apache.org/jira/browse/SPARK-59273 (subtask of SPARK-58794)
Does this PR introduce any user-facing change?
Yes, when
spark.sql.charVarchar.standardSemantics.enabledis true (still default false):EXCEED_LIMIT_LENGTH.df.na.fill("x")fills null CHAR/VARCHAR columns (CHAR values are padded by CAST).ANALYZE TABLE ... FOR COLUMNScollects string-family stats on CHAR/VARCHAR instead of rejecting them.How was this patch tested?
Added/extended unit tests:
JDBCSuite: read CHAR/VARCHAR and arrays; write CHAR/VARCHAR; standard semantics wins overcharVarcharAsStringin schema inferenceParquetV1PartitionDiscoverySuite/ParquetV2PartitionDiscoverySuite: CHAR/VARCHAR partition values and oversize VARCHARRowToColumnConverterSuite: CHAR/VARCHAR and nested CHAR arraysDataFrameNaFunctionsSuite:na.fillon CHAR/VARCHARStatisticsCollectionSuite:ANALYZE TABLE ... FOR COLUMNSon CHAR/VARCHARWas this patch authored or co-authored using generative AI tooling?
Generated-by: Cursor Grok 4.6