cloud-fan commented on code in PR #58584:
URL: https://github.com/apache/spark/pull/58584#discussion_r4036387252


##########
sql/hive/src/main/scala/org/apache/spark/sql/hive/hiveUDFEvaluators.scala:
##########
@@ -111,34 +111,36 @@ class HiveSimpleUDFEvaluator(
   }
 }
 
-class HiveGenericUDFEvaluator(
-    funcWrapper: HiveFunctionWrapper, children: Seq[Expression])
-  extends HiveUDFEvaluatorBase[GenericUDF](funcWrapper, children) {
-
-  // SPARK-58792: copied expression nodes (e.g. via withNewChildrenInternal) 
share one
-  // HiveFunctionWrapper, whose cached GenericUDF instance is mutable: 
initialize()
-  // rewrites its converters and output holders based on the arguments of 
whichever
-  // copy initialized it last. Give every evaluator its own clone so copied 
nodes
-  // cannot corrupt each other.
-  @transient
-  override lazy val function: GenericUDF =
-    
HiveFunctionRegistryUtils.cloneGenericUDF(funcWrapper.createFunction[GenericUDF]())
-
-  @transient
-  private lazy val argumentInspectors = children.map(toInspector).toArray
+private[hive] object HiveGenericUDFEvaluator extends HiveInspectors {
+
+  /**
+   * Driver-side Hive initialize for `SELECT hive_udf(...)`. Returns the 
Catalyst type

Review Comment:
   **Nit (P3):** This should say `Driver-side Hive initialization` or 
explicitly name the `initialize` call. As written, the opening lifecycle 
sentence uses the verb as a noun and is unclear.



##########
sql/core/src/main/scala/org/apache/spark/sql/execution/BaseScriptTransformationExec.scala:
##########
@@ -201,7 +211,15 @@ trait BaseScriptTransformationExec extends UnaryExecNode {
   private lazy val outputFieldWriters: Seq[String => Any] = output.map { attr 
=>
     val converter = 
CatalystTypeConverters.createToCatalystConverter(attr.dataType)
     attr.dataType match {
-      case StringType => wrapperConvertException(data => data, converter)
+      case _: CharType | _: VarcharType =>
+        // First-class CHAR/VARCHAR must not use Hive LazySimpleSerde's 
null-on-error path.

Review Comment:
   **Nit (P3):** Please spell this `LazySimpleSerDe`, matching the concrete 
Hive class and the name used elsewhere in the implementation and tests.



##########
sql/core/src/main/scala/org/apache/spark/sql/execution/BaseScriptTransformationExec.scala:
##########
@@ -241,6 +259,31 @@ trait BaseScriptTransformationExec extends UnaryExecNode {
         data => IntervalUtils.microsToDuration(
           IntervalUtils.castStringToDTInterval(UTF8String.fromString(data), 
start, end)),
         converter)
+      case dt @ (_: ArrayType | _: MapType | _: StructType)
+          if CharVarcharUtils.hasCharVarchar(dt) =>
+        val physicalType = 
ScriptTransformationIOSchema.toUnboundedStringType(dt)
+        // JSON object keys are strings. Cast them to the declared map key 
type after parsing.
+        val jsonType = 
ScriptTransformationIOSchema.toJsonMapKeyType(physicalType)
+        val complexTypeFactory = JsonToStructs(
+          jsonType,
+          ioschema.outputSerdeProps.toMap,
+          Literal(null),
+          Some(conf.sessionLocalTimeZone))
+        val parsedToPhysical = if (jsonType.sameType(physicalType)) {
+          identity[Any] _
+        } else {
+          val cast = Cast(

Review Comment:
   **Non-blocking (P2):** This directly evaluates a whole-map Cast after 
parsing every JSON object key as STRING. For a non-ANSI `MAP<INT, CHAR(4)>`, 
`{"not-an-int":"ab"}` can produce a null key, while `{"1":"a","01":"b"}` can 
produce duplicate integer keys. Because this bypasses the analyzer's map-key 
nullability check and the resulting MapData performs no validation, malformed 
script output can escape as invalid internal data.
   
   **Recommended change:** Restore non-string JSON map keys recursively through 
a validated Catalyst map-construction path, treat failed or colliding key 
conversions as malformed script fields, and add focused valid, invalid, 
duplicate-producing, and nested-key coverage.
   
   **Why this works:** After JsonToStructs parses string object keys, 
recursively convert each map's key array to the declared physical key type and 
rebuild the map through a constructor that enforces non-null and deduplicated 
keys. Evaluate this restoration inside the existing malformed-field wrapper, 
then run the existing declared CHAR/VARCHAR conversion outside it so length 
errors retain their specific signal.
   
   **Scope:** Make the new no-SerDe nested CHAR/VARCHAR path restore JSON map 
keys without bypassing Catalyst map-key validity invariants.
   
   **Compatibility:** Preserve valid no-SerDe complex TRANSFORM output, legacy 
null-on-parse-error handling, first-class CHAR/VARCHAR padding and length 
enforcement, and Hive SerDe behavior.
   
   **Risks:** Moving CHAR/VARCHAR checks inside the generic malformed-field 
wrapper could accidentally convert required EXCEED_LIMIT_LENGTH failures to 
null. Rebuilding nested maps at only the outer level could leave invalid keys 
in maps nested under arrays, structs, or map values.
   
   **Constraints:** Valid non-string map keys in CHAR/VARCHAR-bearing nested 
outputs must remain supported. CHAR/VARCHAR overflow must continue to raise 
EXCEED_LIMIT_LENGTH rather than being swallowed as a generic malformed field. 
Malformed non-CHAR/VARCHAR parse or key-conversion failures must retain the 
established null-on-error behavior where applicable.
   
   **Success:** A failed non-string JSON map-key conversion cannot produce a 
null key in Catalyst MapData. Distinct JSON keys that convert to one declared 
key cannot produce undefined duplicate-key MapData. Valid top-level and nested 
non-string map keys retain their declared Catalyst types and values. Nested 
CHAR/VARCHAR padding and overflow behavior remains unchanged.



##########
sql/hive/src/main/scala/org/apache/spark/sql/hive/HiveInspectors.scala:
##########
@@ -897,6 +953,19 @@ private[hive] trait HiveInspectors {
         (value: Any, row: InternalRow, ordinal: Int) => row(ordinal) = 
unwrapper(value)
     }
 
+  /**
+   * Builds an in-place unwrapper that also honors target-type-specific 
conversions.

Review Comment:
   Confirmed. In the current source, CHAR/VARCHAR-bearing and 
nanosecond-timestamp targets use the DataType-aware path, while unrelated 
primitive targets retain their specialized setters, so the comment and 
implementation now align. Thanks.
   
   <!-- SPARK_DEV_REVIEW_REPLY 
{"feedback_id":"inline:4008642276","thread_id":"inline:4008642276","verdict_sha256":"f604aded9a0ed9de1cf7f06c3acfeb8e4e13defa3c50d17e3ed7b9e2910309fe"}
 -->



##########
sql/hive/src/test/scala/org/apache/spark/sql/hive/HiveInspectorSuite.scala:
##########
@@ -292,6 +301,240 @@ class HiveInspectorSuite extends SparkFunSuite with 
HiveInspectors {
     assert(typeInfo2.scale() === 10)
   }
 
+  test("SPARK-59277: Hive object inspectors preserve CHAR/VARCHAR type 
information") {
+    withFirstClassCharVarchar(enabled = true) {
+      Seq[DataType](CharType(5), VarcharType(7)).foreach { dataType =>
+        val inspector = 
toInspector(dataType).asInstanceOf[PrimitiveObjectInspector]
+        assert(inspectorToDataType(inspector) === dataType)
+        dataType match {
+          case c: CharType =>
+            assert(inspector.getTypeInfo.asInstanceOf[CharTypeInfo].getLength 
=== c.length)
+          case v: VarcharType =>
+            
assert(inspector.getTypeInfo.asInstanceOf[VarcharTypeInfo].getLength === 
v.length)
+        }
+      }
+    }
+  }
+
+  test("SPARK-59277: Hive object inspectors accept collated CHAR/VARCHAR 
values") {
+    withFirstClassCharVarchar(enabled = true) {
+      Seq[DataType](
+        CharType(5, "UTF8_LCASE"),
+        VarcharType(7, "UNICODE_CI")).foreach { dataType =>
+        val inspector = toInspector(dataType)
+        val value = UTF8String.fromString(dataType match {
+          case _: CharType => "ab"
+          case _: VarcharType => "abc"
+        })
+        val expectedValue = dataType match {
+          case _: CharType => UTF8String.fromString("ab   ")
+          case _: VarcharType => value
+        }
+        val expectedType = dataType match {
+          case c: CharType => CharType(c.length)
+          case v: VarcharType => VarcharType(v.length)
+        }
+        assert(inspectorToDataType(inspector) === expectedType)
+        assert(unwrap(wrap(value, inspector, dataType), inspector) === 
expectedValue)
+      }
+    }
+  }
+
+  test("SPARK-59277: writable CHAR/VARCHAR inspectors round-trip values and 
nulls") {
+    withFirstClassCharVarchar(enabled = true) {
+      val charType = CharType(5)
+      val charInspector = 
PrimitiveObjectInspectorFactory.getPrimitiveWritableObjectInspector(
+        new CharTypeInfo(charType.length))
+      val charValue = UTF8String.fromString("abc")
+      val wrappedChar = wrap(charValue, charInspector, charType)
+      assert(wrappedChar.isInstanceOf[HiveCharWritable])
+      
assert(wrappedChar.asInstanceOf[HiveCharWritable].getHiveChar.getPaddedValue 
=== "abc  ")
+      assert(unwrapperFor(charInspector, charType)(wrappedChar) ===
+        UTF8String.fromString("abc  "))
+      assert(wrap(null, charInspector, charType) === null)
+      assert(unwrapperFor(charInspector, charType)(null) === null)
+      checkError(
+        exception = intercept[SparkRuntimeException] {
+          wrap(UTF8String.fromString("abcdef"), charInspector, charType)
+        },
+        condition = "EXCEED_LIMIT_LENGTH",
+        parameters = Map("limit" -> "5"))
+
+      val varcharType = VarcharType(7)
+      val varcharInspector = 
PrimitiveObjectInspectorFactory.getPrimitiveWritableObjectInspector(
+        new VarcharTypeInfo(varcharType.length))
+      val varcharValue = UTF8String.fromString("abc")
+      val wrappedVarchar = wrap(varcharValue, varcharInspector, varcharType)
+      assert(wrappedVarchar.isInstanceOf[HiveVarcharWritable])
+      
assert(wrappedVarchar.asInstanceOf[HiveVarcharWritable].getHiveVarchar.getValue 
=== "abc")
+      assert(unwrapperFor(varcharInspector, varcharType)(wrappedVarchar) === 
varcharValue)
+      assert(wrap(null, varcharInspector, varcharType) === null)
+      assert(unwrapperFor(varcharInspector, varcharType)(null) === null)
+      checkError(
+        exception = intercept[SparkRuntimeException] {
+          wrap(UTF8String.fromString("abcdefgh"), varcharInspector, 
varcharType)
+        },
+        condition = "EXCEED_LIMIT_LENGTH",
+        parameters = Map("limit" -> "7"))
+    }
+  }
+
+  test("SPARK-59277: Hive object inspectors support nested CHAR/VARCHAR") {
+    withFirstClassCharVarchar(enabled = true) {
+      val dataType = StructType(Seq(
+        StructField("chars", ArrayType(CharType(4))),
+        StructField("varchars", MapType(IntegerType, VarcharType(8)))))
+      val inspector = toInspector(dataType)
+      assert(inspectorToDataType(inspector) === dataType)
+
+      val input = InternalRow(
+        new GenericArrayData(Array[Any](UTF8String.fromString("a"))),
+        ArrayBasedMapData(
+          Array[Any](1),
+          Array[Any](UTF8String.fromString("value"))))
+      val result = unwrapperFor(inspector, dataType)(
+        wrap(input, inspector, dataType)).asInstanceOf[InternalRow]
+      assert(result.getArray(0).getUTF8String(0) === UTF8String.fromString("a  
 "))
+      assert(result.getMap(1).valueArray().getUTF8String(0) === 
UTF8String.fromString("value"))
+
+      val outerType = StructType(Seq(StructField("nested", dataType)))
+      val outerInspector = 
toInspector(outerType).asInstanceOf[StructObjectInspector]
+      val field = outerInspector.getAllStructFieldRefs.get(0)
+      val targetRow = new SpecificInternalRow(Seq(dataType))
+      unwrapperFor(field, dataType)(wrap(input, inspector, dataType), 
targetRow, 0)
+      val nestedResult = targetRow.getStruct(0, dataType.length)
+      assert(nestedResult.getArray(0).getUTF8String(0) === 
UTF8String.fromString("a   "))
+      assert(
+        nestedResult.getMap(1).valueArray().getUTF8String(0) === 
UTF8String.fromString("value"))
+    }
+  }
+
+  test("SPARK-59277: typed field unwrappers preserve nanosecond timestamps") {
+    val value = Timestamp.valueOf("2026-09-16 12:34:56.123456789")
+    Seq(
+      TimestampNTZNanosType(9) ->
+        DateTimeUtils.localDateTimeToTimestampNanos(value.toLocalDateTime, 9),
+      TimestampLTZNanosType(9) ->
+        DateTimeUtils.instantToTimestampNanos(value.toInstant, 9)).foreach {
+      case (dataType, expected) =>
+        val inspector = 
ObjectInspectorFactory.getStandardStructObjectInspector(
+          util.Arrays.asList("value"),
+          
util.Arrays.asList(PrimitiveObjectInspectorFactory.javaTimestampObjectInspector))
+        val field = inspector.getAllStructFieldRefs.get(0)
+        val targetRow = new SpecificInternalRow(Seq(dataType))
+        unwrapperFor(field, dataType)(value, targetRow, 0)
+        assert(targetRow.get(0, dataType) === expected)
+    }
+  }
+
+  test("SPARK-59277: Hive constant inspectors preserve CHAR/VARCHAR type 
information") {
+    withFirstClassCharVarchar(enabled = true) {
+      Seq[DataType](CharType(5), VarcharType(7)).foreach { dataType =>
+        val value = UTF8String.fromString("abc")
+        val inspector = toInspector(Literal.create(value, dataType))
+        assert(inspector.isInstanceOf[ConstantObjectInspector])
+        assert(inspectorToDataType(inspector) === dataType)
+        val expected = dataType match {
+          case _: CharType => UTF8String.fromString("abc  ")
+          case _: VarcharType => value
+        }
+        assert(unwrapperFor(inspector, dataType)(
+          
inspector.asInstanceOf[ConstantObjectInspector].getWritableConstantValue) === 
expected)
+
+        val nullInspector = toInspector(Literal.create(null, dataType))
+        assert(nullInspector.isInstanceOf[ConstantObjectInspector])
+        assert(inspectorToDataType(nullInspector) === dataType)
+        assert(unwrapperFor(nullInspector, dataType)(
+          
nullInspector.asInstanceOf[ConstantObjectInspector].getWritableConstantValue) 
=== null)
+      }
+    }
+  }
+
+  test("SPARK-59277: Hive CHAR/VARCHAR inspectors remain STRING under legacy 
semantics") {
+    withFirstClassCharVarchar(enabled = false) {
+      Seq[DataType](CharType(5), VarcharType(7)).foreach { dataType =>
+        val inspector = toInspector(dataType)
+        assert(inspectorToDataType(inspector) === StringType)
+        assert(inspectorToDataType(inspector, preserveCharVarchar = true) === 
dataType)
+      }
+    }
+  }
+
+  test("SPARK-59277: Hive return type compatibility allows only STRING 
boundary drift") {
+    checkCompatibleHiveReturnType(StringType, CharType(5))
+    checkCompatibleHiveReturnType(CharType(5), StringType)
+    checkCompatibleHiveReturnType(ArrayType(StringType), 
ArrayType(VarcharType(7)))
+    checkCompatibleHiveReturnType(CharType(5), CharType(5))
+    checkCompatibleHiveReturnType(VarcharType(3), VarcharType(3))
+
+    Seq[(DataType, DataType)](
+      VarcharType(3) -> CharType(5),
+      CharType(4) -> CharType(5),
+      VarcharType(4) -> VarcharType(5),
+      IntegerType -> CharType(5)).foreach { case (runtimeType, expectedType) =>
+      intercept[SparkException] {
+        checkCompatibleHiveReturnType(runtimeType, expectedType)
+      }
+    }
+  }
+
+  test("SPARK-59277: Hive CHAR/VARCHAR boundaries enforce Spark length 
semantics") {
+    withFirstClassCharVarchar(enabled = true) {
+      val varchar = VarcharType(3)
+      val varcharInspector = toInspector(varchar)
+      checkError(
+        exception = intercept[SparkRuntimeException] {
+          wrap(UTF8String.fromString("abcd"), varcharInspector, varchar)
+        },
+        condition = "EXCEED_LIMIT_LENGTH",
+        parameters = Map("limit" -> "3"))
+
+      val char = CharType(5)
+      val charInspector = toInspector(char)
+      checkError(
+        exception = intercept[SparkRuntimeException] {
+          wrap(UTF8String.fromString("abcdef"), charInspector, char)
+        },
+        condition = "EXCEED_LIMIT_LENGTH",
+        parameters = Map("limit" -> "5"))
+
+      val varcharUnwrapper =
+        
unwrapperFor(PrimitiveObjectInspectorFactory.javaStringObjectInspector, varchar)
+      checkError(
+        exception = intercept[SparkRuntimeException] {
+          varcharUnwrapper("abcd")
+        },
+        condition = "EXCEED_LIMIT_LENGTH",
+        parameters = Map("limit" -> "3"))
+
+      val charUnwrapper =
+        
unwrapperFor(PrimitiveObjectInspectorFactory.javaStringObjectInspector, char)
+      assert(charUnwrapper("ab") === UTF8String.fromString("ab   "))
+      checkError(
+        exception = intercept[SparkRuntimeException] {
+          charUnwrapper("abcdef")
+        },
+        condition = "EXCEED_LIMIT_LENGTH",
+        parameters = Map("limit" -> "5"))
+    }
+  }
+
+  test("SPARK-59277: Hive object inspectors reject unsupported CHAR/VARCHAR 
lengths") {
+    withFirstClassCharVarchar(enabled = true) {
+      Seq[DataType](CharType(0), CharType(256), VarcharType(65536)).foreach { 
dataType =>

Review Comment:
   **Non-blocking (P2):** Please add `VarcharType(0)` to both the inspector and 
type-info invalid-length checks. `CharType(0)` does not exercise the 
independent VARCHAR lower-bound helper, so that new rejection can currently 
regress without a test failure.



##########
sql/hive/src/main/scala/org/apache/spark/sql/hive/HiveInspectors.scala:
##########
@@ -1122,6 +1244,51 @@ private[hive] trait HiveInspectors {
     case _: JavaVoidObjectInspector => NullType
   }
 
+  /**
+   * Analysis snapshots the Catalyst return type, but runtime inspectors are 
rebuilt from the
+   * current children (including foldability and session CHAR/VARCHAR 
settings). STRING may drift
+   * to or from a bounded string type across that boundary. Two bounded types 
must match exactly:
+   * accepting a different kind or length would apply the snapshotted 
conversion to an incompatible
+   * runtime value.
+   */
+  def checkCompatibleHiveReturnType(
+      inspector: ObjectInspector,
+      expectedType: DataType): Unit = {
+    checkCompatibleHiveReturnType(
+      inspectorToDataType(inspector, preserveCharVarchar = true),
+      expectedType)
+  }
+
+  def checkCompatibleHiveReturnType(
+      runtimeType: DataType,
+      expectedType: DataType): Unit = {
+    if (!compatibleHiveReturnType(runtimeType, expectedType)) {
+      throw SparkException.internalError(
+        s"Hive function runtime type ${runtimeType.catalogString} is 
incompatible " +
+          s"with analysis type ${expectedType.catalogString}.")
+    }
+  }
+
+  private def compatibleHiveReturnType(
+      runtimeType: DataType,
+      expectedType: DataType): Boolean = {
+    (runtimeType, expectedType) match {
+      case (rt: CharType, et: CharType) => rt == et
+      case (rt: VarcharType, et: VarcharType) => rt == et
+      case (_: CharType | _: VarcharType, _: CharType | _: VarcharType) => 
false
+      case (_: StringType, _: CharType | _: VarcharType) => true
+      case (_: CharType | _: VarcharType, _: StringType) => true
+      case (ArrayType(rt, _), ArrayType(et, _)) => 
compatibleHiveReturnType(rt, et)
+      case (MapType(rk, rv, _), MapType(ek, ev, _)) =>

Review Comment:
   **Non-blocking (P2):** The focused compatibility test reaches only scalar 
and array cases, so neither the new MapType key/value recursion nor the 
StructType arity/field recursion has a regression-sensitive assertion. Please 
add positive and negative map and struct cases; otherwise a runtime inspector 
mismatch in either branch can pass the changed suite.



##########
sql/hive/src/test/scala/org/apache/spark/sql/hive/execution/HiveUDAFSuite.scala:
##########
@@ -200,6 +201,25 @@ class HiveUDAFSuite extends QueryTest
     }
   }
 
+  test("SPARK-59277: Hive UDAF supports first-class CHAR/VARCHAR") {

Review Comment:
   Confirmed. The new UDAF uses STRING for its partial inspector and CHAR(5) 
for the final inspector, and the repartitioned test exercises the ordered pair 
across shuffle. Thanks.
   
   <!-- SPARK_DEV_REVIEW_REPLY 
{"feedback_id":"inline:4008642285","thread_id":"inline:4008642285","verdict_sha256":"f604aded9a0ed9de1cf7f06c3acfeb8e4e13defa3c50d17e3ed7b9e2910309fe"}
 -->



##########
sql/hive/src/test/scala/org/apache/spark/sql/hive/HiveInspectorSuite.scala:
##########
@@ -292,6 +300,140 @@ class HiveInspectorSuite extends SparkFunSuite with 
HiveInspectors {
     assert(typeInfo2.scale() === 10)
   }
 
+  test("SPARK-59277: Hive object inspectors preserve CHAR/VARCHAR type 
information") {
+    withFirstClassCharVarchar(enabled = true) {
+      Seq[DataType](CharType(5), VarcharType(7)).foreach { dataType =>
+        val inspector = 
toInspector(dataType).asInstanceOf[PrimitiveObjectInspector]
+        assert(inspectorToDataType(inspector) === dataType)
+        dataType match {
+          case c: CharType =>
+            assert(inspector.getTypeInfo.asInstanceOf[CharTypeInfo].getLength 
=== c.length)
+          case v: VarcharType =>
+            
assert(inspector.getTypeInfo.asInstanceOf[VarcharTypeInfo].getLength === 
v.length)
+        }
+      }
+    }
+  }
+
+  test("SPARK-59277: Hive object inspectors accept collated CHAR/VARCHAR 
values") {
+    withFirstClassCharVarchar(enabled = true) {
+      Seq[DataType](
+        CharType(5, "UTF8_LCASE"),
+        VarcharType(7, "UNICODE_CI")).foreach { dataType =>
+        val inspector = toInspector(dataType)
+        val value = UTF8String.fromString(dataType match {
+          case _: CharType => "ab"
+          case _: VarcharType => "abc"
+        })
+        val expectedValue = dataType match {
+          case _: CharType => UTF8String.fromString("ab   ")
+          case _: VarcharType => value
+        }
+        val expectedType = dataType match {
+          case c: CharType => CharType(c.length)
+          case v: VarcharType => VarcharType(v.length)
+        }
+        assert(inspectorToDataType(inspector) === expectedType)
+        assert(unwrap(wrap(value, inspector, dataType), inspector) === 
expectedValue)
+      }
+    }
+  }
+
+  test("SPARK-59277: Hive object inspectors support nested CHAR/VARCHAR") {
+    withFirstClassCharVarchar(enabled = true) {
+      val dataType = StructType(Seq(
+        StructField("chars", ArrayType(CharType(4))),
+        StructField("varchars", MapType(IntegerType, VarcharType(8)))))
+      val inspector = toInspector(dataType)
+      assert(inspectorToDataType(inspector) === dataType)
+
+      val input = InternalRow(
+        new GenericArrayData(Array[Any](UTF8String.fromString("a"))),
+        ArrayBasedMapData(
+          Array[Any](1),
+          Array[Any](UTF8String.fromString("value"))))
+      val result = unwrapperFor(inspector, dataType)(
+        wrap(input, inspector, dataType)).asInstanceOf[InternalRow]
+      assert(result.getArray(0).getUTF8String(0) === UTF8String.fromString("a  
 "))
+      assert(result.getMap(1).valueArray().getUTF8String(0) === 
UTF8String.fromString("value"))
+
+      val outerType = StructType(Seq(StructField("nested", dataType)))
+      val outerInspector = 
toInspector(outerType).asInstanceOf[StructObjectInspector]
+      val field = outerInspector.getAllStructFieldRefs.get(0)
+      val targetRow = new SpecificInternalRow(Seq(dataType))
+      unwrapperFor(field, dataType)(wrap(input, inspector, dataType), 
targetRow, 0)
+      val nestedResult = targetRow.getStruct(0, dataType.length)
+      assert(nestedResult.getArray(0).getUTF8String(0) === 
UTF8String.fromString("a   "))
+      assert(
+        nestedResult.getMap(1).valueArray().getUTF8String(0) === 
UTF8String.fromString("value"))
+    }
+  }
+
+  test("SPARK-59277: Hive constant inspectors preserve CHAR/VARCHAR type 
information") {

Review Comment:
   Confirmed. The constant-inspector loop now exercises null CHAR and VARCHAR 
literals through both inspector creation and typed unwrapping. Thanks.
   
   <!-- SPARK_DEV_REVIEW_REPLY 
{"feedback_id":"inline:4008642290","thread_id":"inline:4008642290","verdict_sha256":"f604aded9a0ed9de1cf7f06c3acfeb8e4e13defa3c50d17e3ed7b9e2910309fe"}
 -->



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to