Uh oh!
There was an error while loading. Please reload this page.
[SPARK-58806][SQL][CONNECT] Expose CHAR/VARCHAR metadata and decode result schemas - #58132
[SPARK-58806][SQL][CONNECT] Expose CHAR/VARCHAR metadata and decode result schemas#58132srielau wants to merge 6 commits into
Conversation
Report first-class CHAR/VARCHAR types and lengths through HiveServer2 and Spark Connect JDBC metadata so clients can describe result and catalog columns.
89977ef to
9a499ceCompare
srielau
left a comment
There was a problem hiding this comment.
SQL Language review
Client-metadata review (Connect JDBC + HiveServer2 getColumns). Not Analyzer/Parser.
The Connect JDBC mapping is the real fix: CharType / VarcharType are StringType subclasses, so case StringType => missed them and threw SQLFeatureNotSupportedException. Mapping them to Types.CHAR / VARCHAR with precision n, and widening the other matchers to _: StringType, is the right split.
HS2 getColumns already mapped VARCHAR to Types.VARCHAR; only COLUMN_SIZE was missing. Execute-path CHAR/VARCHAR on HS2 was already wired (TTypeId + CHARACTER_MAXIMUM_LENGTH via defaultSize, which equals length).
Findings
Medium -- Connect
getColumnsstill reportsCHAR_OCTET_LENGTH = 0. Catalog metadata will pick upCOLUMN_SIZE = nviagetDisplaySize->getPrecision, but octet length is hardcoded to 0. HS2 leaves it null as well. Clients that size buffers fromCHAR_OCTET_LENGTHstill see unknown width.Medium --
getTypeInfostill has no CHAR / VARCHAR(n). OnlySTRINGis listed asTypes.VARCHAR. Tools that discover types fromgetTypeInfowill not see CHAR.Medium -- HS2
getColumnstest assertsdefaultSizeinstead of.length. VARCHAR now falls through tocolSize === o.defaultSize. That equals length today, butdefaultSizeis the wrong contract (STRINGis 20). This test also does not enablestandardSemantics, unlike the new result-metadata test.Low -- HS2 result-metadata test does not exercise this diff. Execute-path CHAR/VARCHAR was already implemented. It pins Hive names
char/varchar(no length) vs ConnectCHAR(4)/VARCHAR(6), and does not prove the VARCHARCOLUMN_SIZEfix.Medium -- tests were not run. The PR says Connect and HS2 suites were blocked locally. Please run them (or wait on CI) before merge; Hive type-name assertions are easy to get wrong.
Low -- two metadata mappers stay in lockstep. Connect
getColumnsuses display size forCOLUMN_SIZE; HS2 uses length. They agree for CHAR/VARCHAR only because display size delegates to precision.
Suggested order
- Add Connect
getColumnscoverage for table CHAR/VARCHAR (COLUMN_SIZE, type code, type name). - Decide on
CHAR_OCTET_LENGTHandgetTypeInfo(fix or explicitly out of scope). - Assert HS2 VARCHAR size as
.length, notdefaultSize. - Run the two suites before merge.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Fill JDBC CHAR_OCTET_LENGTH and Connect getTypeInfo for CHAR/VARCHAR, and assert catalog COLUMN_SIZE from declared length rather than defaultSize.
srielau
left a comment
There was a problem hiding this comment.
SQL Language re-review (4673559)
Previous findings are addressed in this commit.
| # | Original | Status |
|---|---|---|
| 1 | Connect CHAR_OCTET_LENGTH hardcoded 0; no table getColumns test | Fixed -- JdbcTypeUtils.getCharOctetLength, table getColumns test |
| 2 | getTypeInfo missing CHAR / VARCHAR(n) | Fixed -- CHAR + VARCHAR with CREATE_PARAMS = length, ordered by DATA_TYPE then TYPE_NAME |
| 3 | HS2 test asserted defaultSize | Fixed -- match .length, explicit 255 / 1024, standardSemantics on |
| 4 | HS2 result-metadata test did not prove getColumnSize | Fixed -- size/octet asserted on c17/c18; CAST test kept as Hive name pin |
| 5 | Tests not run | Open (environment) -- still blocked locally; relying on CI |
| 6 | Dual mappers / display-size vs length | Acceptable -- CHAR/VARCHAR length and octet helpers agree; Connect COLUMN_SIZE still display size, which equals precision for these types |
HS2 scaladoc and CHAR_OCTET_LENGTH are updated. Connect table getColumns covers type code, CHAR(4) / VARCHAR(6) names, COLUMN_SIZE, and octet length.
Remaining
Medium -- still no local run of the Connect/HS2 suites. The new
getColumns/getTypeInfoassertions are the kind that fail on type-name casing. Please confirm GitHub CI forSparkConnectJdbcDataTypeSuite,SparkConnectDatabaseMetaDataSuite, andSparkMetadataOperationSuiteis green before merge.Low -- PR description is stale. It still describes only CAST result metadata and "tests blocked", and does not mention
CHAR_OCTET_LENGTHorgetTypeInfo. Please refresh What/Why/How-tested.Low --
CHAR_OCTET_LENGTHis character lengthn, not UTF-8 octets. Documented in the helper; JDBC's name says octets. Fine if called out; some clients will allocatenbytes forCHAR(n)and truncate multibyte text.
The production mapping looks correct. Not blocking on (2)/(3).
srielau
commented
Aug 19, 2026
Re-review remaining items:
|
A Connect client has no SQLConf, so it always sees DefaultSqlApiConf, where CHAR/VARCHAR are not first class types. A result schema produced by a server running with standard semantics was therefore undecodable: RowEncoder refused to build an encoder for CHAR(n)/VARCHAR(n), and the Arrow layer neither recognized CharEncoder/VarcharEncoder nor accepted the constrained type as a read target. Whether CHAR/VARCHAR are first class is decided by the session that produced the schema, so accept them when building an encoder for an engine-produced schema, while user-supplied schemas (UDF return types, Encoders.row) keep honoring the local configuration.
srielau
left a comment
There was a problem hiding this comment.
SQL Language re-review (b798e6a)
Metadata follow-ups from 4673559 still look in place. The new commit is the right split: engine-produced schemas accept CHAR/VARCHAR (encoderForResultSchema); user-supplied schemas (UDF, Encoders.row, createDataFrame) still honor local charVarcharFirstClassTypes. Arrow treats CHAR/VARCHAR as string vectors and reads via plainStringType, which matches how the server encodes them.
SparkResult only swaps UnboundRowEncoder; typed as[String] keeps StringEncoder and already matched the string-vector case. Nested struct/array CHAR is covered by the recursive flag and the ArrowEncoderSuite round trip.
This decode path is also what makes the existing JDBC CAST getString test runnable on a client with default conf.
Remaining
Medium -- PR description is stale. It still talks only about JDBC/HS2 metadata and local test blockage. Please add the Connect result-decode problem (client
DefaultSqlApiConfcannot see serverstandardSemantics) andencoderForResultSchema.Medium -- missing negative test on the user encoder path. Please assert
RowEncoder.encoderFor(StructType().add("c", CharType(4)))still throwsUNSUPPORTED_DATA_TYPE_FOR_ENCODERwhen first-class types are off, so the newtrueoverride cannot leak into UDF /createDataFrame.Low -- Python Connect is not covered. This is JVM
RowEncoder/ Arrow. If Python already maps proto CHAR to string, say so in the PR; otherwise it is a follow-up.Low (from last review) --
CHAR_OCTET_LENGTHis charactern, not UTF-8 octets; still documented. Confirm CI for JDBC,ArrowEncoderSuite, and HS2.
Architecture of the new entry point looks sound. Not blocking on (3)/(4).
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
cloud-fan
left a comment
There was a problem hiding this comment.
1 blocking, 0 non-blocking, 2 nits.
The result-schema decoding split is sound, but the new octet-length metadata underreports multibyte values and should be corrected before merge.
Correctness (1)
- Blocking: sql/connect/client/jdbc/src/main/scala/org/apache/spark/sql/connect/client/jdbc/util/JdbcTypeUtils.scala:181: Report byte capacity, not character count, in
CHAR_OCTET_LENGTH. -- see inline
Nits: 2 minor items (see inline comments).
Verification
I traced engine-produced schemas from SparkResult.createEncoder through RowEncoder.encoderForResultSchema into the Arrow string reader, while the ordinary encoderFor path still consults the local configuration. I also traced both JDBC getColumns implementations: each now returns the declared character count for CHAR_OCTET_LENGTH, even though the added comments identify byte-buffer sizing clients, so a multibyte UTF-8 value can require more bytes than reported.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
JDBC CHAR_OCTET_LENGTH is a byte capacity. Spark CHAR/VARCHAR n is in characters, so report 4 * n (UTF-8 max), saturating at Int.MaxValue.
cloud-fan
left a comment
There was a problem hiding this comment.
3 addressed, 0 remaining, 1 new to this AI review. (0 newly introduced, 1 late catch, 0 previously raised.)
0 blocking, 0 non-blocking, 1 nit.
The prior correctness issue is fixed; one small Scaladoc clarity nit remains.
Nits: 1 minor item (see inline comments).
Verification
I traced Connect result schemas from SparkResult.createEncoder through RowEncoder.encoderForResultSchema into Arrow deserialization, confirming that ordinary encoderFor remains configuration-gated. I also traced both JDBC getColumns paths and verified that their current helpers report saturated 4 * n byte capacity, with focused tests asserting the corresponding metadata.
Uh oh!
There was an error while loading. Please reload this page.
…/thriftserver/SparkGetColumnsOperation.scala Co-authored-by: Wenchen Fan <cloud0fan@gmail.com>
…esult schemas ### What changes were proposed in this pull request? Covers Connect JDBC mapping ([SPARK-58806](https://issues.apache.org/jira/browse/SPARK-58806)), plus HiveServer2 / JDBC result metadata ([SPARK-58804](https://issues.apache.org/jira/browse/SPARK-58804)) and `DatabaseMetaData.getColumns` `COLUMN_SIZE` ([SPARK-58805](https://issues.apache.org/jira/browse/SPARK-58805)). Parent: [SPARK-58794](https://issues.apache.org/jira/browse/SPARK-58794). Independent of the remaining CHAR/VARCHAR follow-ups. Rebased onto `master` after #58033; can merge without #58080 / #58087 / #58130. Unique vs master: srielau/spark@master...serge-rielau_data/SPARK-58794-clients Expose first-class `CHAR(n)` and `VARCHAR(n)` to SQL clients when `spark.sql.charVarchar.standardSemantics.enabled` is on, and let those clients decode result rows that carry the types. Metadata: - Spark Connect JDBC maps `CharType` / `VarcharType` to `java.sql.Types.CHAR` / `VARCHAR`, with Java string values and precision / display size `n`. - Connect `DatabaseMetaData.getColumns` reports `COLUMN_SIZE` as the declared character length `n`. `CHAR_OCTET_LENGTH` is the UTF-8 maximum byte capacity `4 * n` (saturating at `Int.MaxValue`); unbounded STRING stays 0. - Connect `DatabaseMetaData.getTypeInfo` lists `CHAR` and `VARCHAR` with `CREATE_PARAMS = length`. Unbounded `STRING` remains a `Types.VARCHAR` row without create params. - HiveServer2 `getColumns` reports `COLUMN_SIZE = n` for `VARCHAR(n)` as well as `CHAR(n)`, and `CHAR_OCTET_LENGTH = 4 * n` for both (null for unbounded STRING). - HiveServer2 result metadata coverage pins Hive JDBC names (`char` / `varchar`, no length) and precision `n` for CAST results. Decode (JVM Connect client): - The Connect client process has no engine `SQLConf`, so `RowEncoder.encoderFor` still follows the local `charVarcharFirstClassTypes` flag and rejects CHAR/VARCHAR when first-class types are off. - `RowEncoder.encoderForResultSchema` always accepts CHAR/VARCHAR. `SparkResult.createEncoder` uses it only for `UnboundRowEncoder` (engine-produced result schemas). Other encoder bindings keep `encoderFor`. - Arrow serializer / deserializer treat `CharEncoder` / `VarcharEncoder` like `StringEncoder`, and deserialize via `StringHelper.plainStringType` so STRING Arrow vectors up-cast to CHAR/VARCHAR. - JDBC CAST `ResultSet.next` / `collect` needs this path; metadata-only mapping is not enough. Python Connect already maps proto `char` / `var_char` in `pyspark.sql.connect.types`; a mixed classic vs Connect test covers schema and `collect()`. The existing Connect proto already carries CHAR/VARCHAR and their lengths. ### Why are the changes needed? Spark Connect JDBC rejected first-class `CharType` and `VarcharType` as unsupported because its metadata mapping only recognized the `StringType` singleton. `getColumns` hardcoded `CHAR_OCTET_LENGTH` to 0, and `getTypeInfo` listed only unbounded STRING. HiveServer2 already identified VARCHAR catalog columns but reported `COLUMN_SIZE` as unknown. Clients therefore cannot reliably describe or size CHAR/VARCHAR columns even though Catalyst retains the type and length. Separately, a server with standard semantics sends CHAR/VARCHAR in the result schema. The JVM Connect client then failed at row decode (`UNSUPPORTED_DATA_TYPE_FOR_ENCODER`) because `RowEncoder.encoderFor` read the client's default conf (`charVarcharFirstClassTypes = false`). JDBC CAST collect and Spark Connect `SparkResult` both hit that path. ### Does this PR introduce _any_ user-facing change? Yes, when first-class CHAR/VARCHAR types are present: JDBC and HiveServer2 metadata now report the corresponding JDBC type and declared character length instead of rejecting the type or reporting an unknown size. `CHAR_OCTET_LENGTH` is the UTF-8 maximum byte capacity (`4 * n`), not the character length. Connect clients can also collect CHAR/VARCHAR result columns instead of failing to decode the schema. ### How was this patch tested? - `RowEncoderSuite`: `encoderFor` still raises `UNSUPPORTED_DATA_TYPE_FOR_ENCODER` for CHAR/VARCHAR when both first-class flags are off; `encoderForResultSchema` accepts the same schema. - `ArrowEncoderSuite`: CHAR/VARCHAR Arrow round-trip (top-level, nested struct, array). - `SparkConnectJdbcDataTypeSuite`: CAST result metadata and table `getColumns` (`CHAR(4)` / `VARCHAR(6)`, `COLUMN_SIZE`, `CHAR_OCTET_LENGTH` = 16 / 24); CAST collect after decode. - `SparkConnectDatabaseMetaDataSuite`: `getTypeInfo` rows for CHAR / VARCHAR. - `SparkMetadataOperationSuite`: HiveServer2 `getColumns` / CAST result metadata (`CHAR_OCTET_LENGTH` = `4 * n`). - `pyspark.sql.tests.connect.test_connect_basic.SparkConnectBasicTests.test_char_varchar_result_schema`: classic vs Connect schema and `collect()`. Local compile of `sql-api`, `connect-client-jdbc`, and `hive-thriftserver` passed. HiveServer2 / full Connect JDBC in this environment remain blocked (`CheckReturnValue` while compiling `connect-common`; FIPS Python multiprocessing). Please treat the suites above as the merge gate. ### Was this patch authored or co-authored using generative AI tooling? Yes (Cursor). Closes#58132 from srielau/serge-rielau_data/SPARK-58794-clients. Authored-by: Serge Rielau <serge@rielau.com> Signed-off-by: Wenchen Fan <wenchen@databricks.com> (cherry picked from commit 6af294e) Signed-off-by: Wenchen Fan <wenchen@databricks.com>
cloud-fan
commented
Aug 20, 2026
…tandardSemantics ### What changes were proposed in this pull request? Parent epic: [SPARK-58794](https://issues.apache.org/jira/browse/SPARK-58794). Unique ticket: [SPARK-58807](https://issues.apache.org/jira/browse/SPARK-58807) (CTAS/CREATE VIEW/CTE inherit CHAR/VARCHAR). Also covers [SPARK-58808](https://issues.apache.org/jira/browse/SPARK-58808) (SQL FUNCTION assignment), [SPARK-58809](https://issues.apache.org/jira/browse/SPARK-58809) (session variables), and [SPARK-58811](https://issues.apache.org/jira/browse/SPARK-58811) (ALTER/temp/external). #58080 (SPARK-58798 LCT) is on master. Clients (JDBC / HS2 / Connect) landed in #58132 / SPARK-58806. Language-surface hardening under `spark.sql.charVarchar.standardSemantics.enabled`: **Bug fixes (format write)** - **ORC:** `CharType`/`VarcharType` extend `StringType`, so write used to stamp `spark.sql.catalyst.type=string` and file-only inference lost the constraint. Stamp `char(n)` / `varchar(n)` via `CharVarcharUtils.charVarcharTypeName`. Write stays on ORC `STRING` plus that attribute (not native ORC CHAR/VARCHAR), so ORC `maxLength` does not fight Spark store assignment. Unbounded STRING, including collated STRING, still stamps `string` (same as Avro: collation is not a file-only round-trip in this PR). - **Avro:** same subclass trap on leaves, map keys, and ser/de. Stamp `spark.sql.catalyst.type` on STRING and `spark.sql.catalyst.mapKey.type` on maps. Restore only `StringType` subtypes; a non-string stamp is `IncompatibleSchemaException`. sql/core has no avro data source; ser/de is covered with `DataFileWriter`. **Coverage** - CTAS / CREATE VIEW (CHAR and VARCHAR) / CTE inherit CHAR/VARCHAR - ALTER COLUMN equal-length CHAR/VARCHAR; V2 VARCHAR widen / CHAR to VARCHAR - Session / script variables, FETCH INTO, SQL FUNCTION params and RETURNS - EXTERNAL TABLE scan pad / overflow - CHAR/VARCHAR vs non-string compare and COALESCE - JSON / CSV with a user-specified CHAR/VARCHAR schema - ORC catalog + file-only round-trip, including cross-flag (`standardSemantics` on then off, first-class types off, `preserveCharVarcharTypeInfo` only) - Avro schema conversion, nested struct, CHAR map keys, and serializer/deserializer values ### Why are the changes needed? Without ORC/Avro catalyst-type stamping, Spark-written files cannot re-infer CHAR/VARCHAR under the flag. Language surfaces (CTAS/VIEW/CTE/vars/functions/user schemas) need explicit coverage so schema fidelity does not regress. ### Does this PR introduce _any_ user-facing change? Yes, when the flag is on: ORC and Avro preserve CHAR/VARCHAR logical types across write/read instead of collapsing to STRING. CTAS/VIEW/CTE/vars/functions behavior matches the foundation rules and is now tested. ### How was this patch tested? - `BasicCharVarcharTestSuite` / language surfaces (including Avro ser/de and ORC cross-flag) - Regenerated `charvarchar-standard-semantics.sql` goldens Closes#58087 from srielau/SPARK-58794-surfaces. Authored-by: Serge Rielau <serge@rielau.com> Signed-off-by: Wenchen Fan <wenchen@databricks.com>
…tandardSemantics ### What changes were proposed in this pull request? Parent epic: [SPARK-58794](https://issues.apache.org/jira/browse/SPARK-58794). Unique ticket: [SPARK-58807](https://issues.apache.org/jira/browse/SPARK-58807) (CTAS/CREATE VIEW/CTE inherit CHAR/VARCHAR). Also covers [SPARK-58808](https://issues.apache.org/jira/browse/SPARK-58808) (SQL FUNCTION assignment), [SPARK-58809](https://issues.apache.org/jira/browse/SPARK-58809) (session variables), and [SPARK-58811](https://issues.apache.org/jira/browse/SPARK-58811) (ALTER/temp/external). #58080 (SPARK-58798 LCT) is on master. Clients (JDBC / HS2 / Connect) landed in #58132 / SPARK-58806. Language-surface hardening under `spark.sql.charVarchar.standardSemantics.enabled`: **Bug fixes (format write)** - **ORC:** `CharType`/`VarcharType` extend `StringType`, so write used to stamp `spark.sql.catalyst.type=string` and file-only inference lost the constraint. Stamp `char(n)` / `varchar(n)` via `CharVarcharUtils.charVarcharTypeName`. Write stays on ORC `STRING` plus that attribute (not native ORC CHAR/VARCHAR), so ORC `maxLength` does not fight Spark store assignment. Unbounded STRING, including collated STRING, still stamps `string` (same as Avro: collation is not a file-only round-trip in this PR). - **Avro:** same subclass trap on leaves, map keys, and ser/de. Stamp `spark.sql.catalyst.type` on STRING and `spark.sql.catalyst.mapKey.type` on maps. Restore only `StringType` subtypes; a non-string stamp is `IncompatibleSchemaException`. sql/core has no avro data source; ser/de is covered with `DataFileWriter`. **Coverage** - CTAS / CREATE VIEW (CHAR and VARCHAR) / CTE inherit CHAR/VARCHAR - ALTER COLUMN equal-length CHAR/VARCHAR; V2 VARCHAR widen / CHAR to VARCHAR - Session / script variables, FETCH INTO, SQL FUNCTION params and RETURNS - EXTERNAL TABLE scan pad / overflow - CHAR/VARCHAR vs non-string compare and COALESCE - JSON / CSV with a user-specified CHAR/VARCHAR schema - ORC catalog + file-only round-trip, including cross-flag (`standardSemantics` on then off, first-class types off, `preserveCharVarcharTypeInfo` only) - Avro schema conversion, nested struct, CHAR map keys, and serializer/deserializer values ### Why are the changes needed? Without ORC/Avro catalyst-type stamping, Spark-written files cannot re-infer CHAR/VARCHAR under the flag. Language surfaces (CTAS/VIEW/CTE/vars/functions/user schemas) need explicit coverage so schema fidelity does not regress. ### Does this PR introduce _any_ user-facing change? Yes, when the flag is on: ORC and Avro preserve CHAR/VARCHAR logical types across write/read instead of collapsing to STRING. CTAS/VIEW/CTE/vars/functions behavior matches the foundation rules and is now tested. ### How was this patch tested? - `BasicCharVarcharTestSuite` / language surfaces (including Avro ser/de and ORC cross-flag) - Regenerated `charvarchar-standard-semantics.sql` goldens Closes#58087 from srielau/SPARK-58794-surfaces. Authored-by: Serge Rielau <serge@rielau.com> Signed-off-by: Wenchen Fan <wenchen@databricks.com> (cherry picked from commit db6fc3a) Signed-off-by: Wenchen Fan <wenchen@databricks.com>
What changes were proposed in this pull request?
Covers Connect JDBC mapping (SPARK-58806), plus HiveServer2 / JDBC result metadata (SPARK-58804) and
DatabaseMetaData.getColumnsCOLUMN_SIZE(SPARK-58805). Parent: SPARK-58794.Independent of the remaining CHAR/VARCHAR follow-ups. Rebased onto
masterafter #58033; can merge without #58080 / #58087 / #58130. Unique vs master: srielau/spark@master...serge-rielau_data/SPARK-58794-clientsExpose first-class
CHAR(n)andVARCHAR(n)to SQL clients whenspark.sql.charVarchar.standardSemantics.enabledis on, and let those clients decode result rows that carry the types.Metadata:
CharType/VarcharTypetojava.sql.Types.CHAR/VARCHAR, with Java string values and precision / display sizen.DatabaseMetaData.getColumnsreportsCOLUMN_SIZEas the declared character lengthn.CHAR_OCTET_LENGTHis the UTF-8 maximum byte capacity4 * n(saturating atInt.MaxValue); unbounded STRING stays 0.DatabaseMetaData.getTypeInfolistsCHARandVARCHARwithCREATE_PARAMS = length. UnboundedSTRINGremains aTypes.VARCHARrow without create params.getColumnsreportsCOLUMN_SIZE = nforVARCHAR(n)as well asCHAR(n), andCHAR_OCTET_LENGTH = 4 * nfor both (null for unbounded STRING).char/varchar, no length) and precisionnfor CAST results.Decode (JVM Connect client):
SQLConf, soRowEncoder.encoderForstill follows the localcharVarcharFirstClassTypesflag and rejects CHAR/VARCHAR when first-class types are off.RowEncoder.encoderForResultSchemaalways accepts CHAR/VARCHAR.SparkResult.createEncoderuses it only forUnboundRowEncoder(engine-produced result schemas). Other encoder bindings keepencoderFor.CharEncoder/VarcharEncoderlikeStringEncoder, and deserialize viaStringHelper.plainStringTypeso STRING Arrow vectors up-cast to CHAR/VARCHAR.ResultSet.next/collectneeds this path; metadata-only mapping is not enough.Python Connect already maps proto
char/var_charinpyspark.sql.connect.types; a mixed classic vs Connect test covers schema andcollect().The existing Connect proto already carries CHAR/VARCHAR and their lengths.
Why are the changes needed?
Spark Connect JDBC rejected first-class
CharTypeandVarcharTypeas unsupported because its metadata mapping only recognized theStringTypesingleton.getColumnshardcodedCHAR_OCTET_LENGTHto 0, andgetTypeInfolisted only unbounded STRING. HiveServer2 already identified VARCHAR catalog columns but reportedCOLUMN_SIZEas unknown. Clients therefore cannot reliably describe or size CHAR/VARCHAR columns even though Catalyst retains the type and length.Separately, a server with standard semantics sends CHAR/VARCHAR in the result schema. The JVM Connect client then failed at row decode (
UNSUPPORTED_DATA_TYPE_FOR_ENCODER) becauseRowEncoder.encoderForread the client's default conf (charVarcharFirstClassTypes = false). JDBC CAST collect and Spark ConnectSparkResultboth hit that path.Does this PR introduce any user-facing change?
Yes, when first-class CHAR/VARCHAR types are present: JDBC and HiveServer2 metadata now report the corresponding JDBC type and declared character length instead of rejecting the type or reporting an unknown size.
CHAR_OCTET_LENGTHis the UTF-8 maximum byte capacity (4 * n), not the character length. Connect clients can also collect CHAR/VARCHAR result columns instead of failing to decode the schema.How was this patch tested?
RowEncoderSuite:encoderForstill raisesUNSUPPORTED_DATA_TYPE_FOR_ENCODERfor CHAR/VARCHAR when both first-class flags are off;encoderForResultSchemaaccepts the same schema.ArrowEncoderSuite: CHAR/VARCHAR Arrow round-trip (top-level, nested struct, array).SparkConnectJdbcDataTypeSuite: CAST result metadata and tablegetColumns(CHAR(4)/VARCHAR(6),COLUMN_SIZE,CHAR_OCTET_LENGTH= 16 / 24); CAST collect after decode.SparkConnectDatabaseMetaDataSuite:getTypeInforows for CHAR / VARCHAR.SparkMetadataOperationSuite: HiveServer2getColumns/ CAST result metadata (CHAR_OCTET_LENGTH=4 * n).pyspark.sql.tests.connect.test_connect_basic.SparkConnectBasicTests.test_char_varchar_result_schema: classic vs Connect schema andcollect().Local compile of
sql-api,connect-client-jdbc, andhive-thriftserverpassed. HiveServer2 / full Connect JDBC in this environment remain blocked (CheckReturnValuewhile compilingconnect-common; FIPS Python multiprocessing). Please treat the suites above as the merge gate.Was this patch authored or co-authored using generative AI tooling?
Yes (Cursor).