This is an automated email from the ASF dual-hosted git repository.

dtenedor pushed a commit to branch branch-4.x
in repository https://gitbox.apache.org/repos/asf/spark.git


The following commit(s) were added to refs/heads/branch-4.x by this push:
     new 00d66a2e2eb5 [SPARK-59683][SQL][HIVE] Support first-class CHAR/VARCHAR 
in script TRANSFORM output
00d66a2e2eb5 is described below

commit 00d66a2e2eb558343e61f4d773d4848a644ea280
Author: Serge Rielau <[email protected]>
AuthorDate: Tue Oct 6 22:44:27 2026 -0700

    [SPARK-59683][SQL][HIVE] Support first-class CHAR/VARCHAR in script 
TRANSFORM output
    
    ### What changes were proposed in this pull request?
    
    Support first-class CHAR/VARCHAR output from script TRANSFORM when 
first-class semantics are enabled. Split out from 
[#58584](https://github.com/apache/spark/pull/58584) (Hive inspector conversion 
+ UDF/UDAF/UDTF) per review feedback.
    
    - **No-SerDe path:** CHAR/VARCHAR scalar, nested, and complex output with 
padding, overflow (`EXCEED_LIMIT_LENGTH`), null-token, and malformed-field 
handling.
    - **No-SerDe nested maps:** Restore JSON string keys to the declared 
physical key type through a per-row `ArrayBasedMapBuilder` that validates null 
and duplicate converted keys.
    - **SerDe path:** Rewrite `LazySimpleSerDe` CHAR/VARCHAR output types to 
STRING so Spark applies first-class length checks. Non-`LazySimpleSerDe` SerDes 
keep the declared CHAR/VARCHAR schema.
    - **Legacy:** Legacy flag-off behavior remains unchanged.
    
    This PR depends on [#58584](https://github.com/apache/spark/pull/58584) for 
Hive inspector CHAR/VARCHAR conversion. JIRA: 
https://issues.apache.org/jira/browse/SPARK-59683
    
    ### Why are the changes needed?
    
    With `spark.sql.charVarchar.standardSemantics.enabled=true`, script 
TRANSFORM output was not aware of CHAR/VARCHAR types. Bounded-string values 
passed through without padding or length enforcement, and nested output 
containing non-string map keys was not restored from the JSON string 
representation.
    
    ### Does this PR introduce _any_ user-facing change?
    
    Yes. When first-class CHAR/VARCHAR semantics are enabled, script TRANSFORM 
now pads CHAR output, enforces length limits on both CHAR and VARCHAR, and 
correctly handles nested CHAR/VARCHAR in arrays, maps, and structs. Legacy 
flag-off behavior remains unchanged.
    
    ### How was this patch tested?
    
    Added focused coverage for:
    
    - Scalar CHAR/VARCHAR output without SerDe (padding, overflow).
    - Nested CHAR/VARCHAR (arrays, maps with non-string keys, structs) without 
SerDe.
    - Malformed JSON and colliding map-key restoration without SerDe.
    - Colliding row followed by valid row in the same partition (builder-leak 
regression).
    - Nested collated CHAR/VARCHAR with Hive SerDe.
    - Scalar CHAR/VARCHAR overflow with Hive SerDe.
    - `LazySimpleSerDe`-only CHAR/VARCHAR type rewrite verification.
    
    Ran:
    
    ```
    sbt -java-home /usr/lib/jvm/java-17-openjdk-amd64 \
      -Dsbt.override.build.repos=true \
      'sql/testOnly 
org.apache.spark.sql.execution.SparkScriptTransformationSuite -- -z SPARK-59277'
    
    sbt -java-home /usr/lib/jvm/java-17-openjdk-amd64 \
      -Dsbt.override.build.repos=true \
      'hive/testOnly 
org.apache.spark.sql.hive.execution.HiveScriptTransformationSuite -- -z 
SPARK-59277'
    ```
    
    ### Was this patch authored or co-authored using generative AI tooling?
    
    Generated-by: Cursor Auto
    
    Closes #58936 from srielau/serge-rielau_data/SPARK-59277-transform.
    
    Authored-by: Serge Rielau <[email protected]>
    Signed-off-by: Daniel Tenedorio <[email protected]>
    (cherry picked from commit 6d020ca9295ea5121a831e98ded48dc60061b5f5)
    Signed-off-by: Daniel Tenedorio <[email protected]>
---
 .../src/main/resources/error/error-conditions.json |  10 +-
 .../spark/sql/catalyst/parser/AstBuilder.scala     |   3 +-
 .../plans/logical/ScriptTransformation.scala       |   4 +-
 .../spark/sql/errors/QueryExecutionErrors.scala    |  13 +-
 .../execution/BaseScriptTransformationExec.scala   | 187 +++++++++-
 .../spark/sql/execution/SparkSqlParser.scala       |   3 +-
 .../sql-tests/analyzer-results/transform.sql.out   |  86 ++---
 .../execution/BaseScriptTransformationSuite.scala  | 375 ++++++++++++++++++++-
 .../execution/SparkScriptTransformationSuite.scala |  56 +++
 .../org/apache/spark/sql/hive/HiveInspectors.scala |  18 +
 .../execution/HiveScriptTransformationExec.scala   |  30 +-
 .../execution/HiveScriptTransformationSuite.scala  | 257 +++++++++++++-
 12 files changed, 969 insertions(+), 73 deletions(-)

diff --git a/common/utils/src/main/resources/error/error-conditions.json 
b/common/utils/src/main/resources/error/error-conditions.json
index e1ed46a58c14..30eeaf2a2ab0 100644
--- a/common/utils/src/main/resources/error/error-conditions.json
+++ b/common/utils/src/main/resources/error/error-conditions.json
@@ -9443,6 +9443,11 @@
           "TRANSFORM with SERDE is only supported in hive mode."
         ]
       },
+      "TRANSFORM_WITHOUT_SERDE_TYPE" : {
+        "message" : [
+          "TRANSFORM without SERDE does not support the output data type 
<dataType>."
+        ]
+      },
       "TRIM_COLLATION" : {
         "message" : [
           "TRIM specifier in the collation."
@@ -11737,11 +11742,6 @@
       "Subprocess exited with status <exitCode>. Error: <stderrBuffer>."
     ]
   },
-  "_LEGACY_ERROR_TEMP_2265" : {
-    "message" : [
-      "<nodeName> without serde does not support <dt> as output data type."
-    ]
-  },
   "_LEGACY_ERROR_TEMP_2266" : {
     "message" : [
       "Invalid `startIndex` provided for generating iterator over the array. 
Total elements: <numRows>, requested `startIndex`: <startIndex>."
diff --git 
a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala
 
b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala
index ec894bc34916..3f0a83c51c46 100644
--- 
a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala
+++ 
b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala
@@ -2050,7 +2050,8 @@ class AstBuilder extends DataTypeAstBuilder
       inSerdeClass, outSerdeClass,
       inSerdeProps, outSerdeProps,
       reader, writer,
-      schemaLess)
+      schemaLess,
+      conf.charVarcharStandardSemantics)
   }
 
   /**
diff --git 
a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/plans/logical/ScriptTransformation.scala
 
b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/plans/logical/ScriptTransformation.scala
index 3a2cb2cd7309..5532803eafc5 100644
--- 
a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/plans/logical/ScriptTransformation.scala
+++ 
b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/plans/logical/ScriptTransformation.scala
@@ -49,4 +49,6 @@ case class ScriptInputOutputSchema(
     outputSerdeProps: Seq[(String, String)],
     recordReaderClass: Option[String],
     recordWriterClass: Option[String],
-    schemaLess: Boolean)
+    schemaLess: Boolean,
+    // Bound while the script is parsed under the direct-query or 
persisted-view SQLConf.
+    standardCharVarcharSemantics: Boolean = false)
diff --git 
a/sql/catalyst/src/main/scala/org/apache/spark/sql/errors/QueryExecutionErrors.scala
 
b/sql/catalyst/src/main/scala/org/apache/spark/sql/errors/QueryExecutionErrors.scala
index 79b979b54f32..f80cfecf7aa8 100644
--- 
a/sql/catalyst/src/main/scala/org/apache/spark/sql/errors/QueryExecutionErrors.scala
+++ 
b/sql/catalyst/src/main/scala/org/apache/spark/sql/errors/QueryExecutionErrors.scala
@@ -2438,14 +2438,11 @@ private[sql] object QueryExecutionErrors extends 
QueryErrorsBase with ExecutionE
       cause = cause)
   }
 
-  def outputDataTypeUnsupportedByNodeWithoutSerdeError(
-      nodeName: String, dt: DataType): Throwable = {
-    new SparkException(
-      errorClass = "_LEGACY_ERROR_TEMP_2265",
-      messageParameters = Map(
-        "nodeName" -> nodeName,
-        "dt" -> dt.getClass.getSimpleName),
-      cause = null)
+  def scriptTransformWithoutSerdeUnsupportedTypeError(
+      dt: DataType): SparkUnsupportedOperationException = {
+    new SparkUnsupportedOperationException(
+      errorClass = "UNSUPPORTED_FEATURE.TRANSFORM_WITHOUT_SERDE_TYPE",
+      messageParameters = Map("dataType" -> toSQLType(dt)))
   }
 
   def invalidStartIndexError(
diff --git 
a/sql/core/src/main/scala/org/apache/spark/sql/execution/BaseScriptTransformationExec.scala
 
b/sql/core/src/main/scala/org/apache/spark/sql/execution/BaseScriptTransformationExec.scala
index a313e2c671be..535db0d82fec 100644
--- 
a/sql/core/src/main/scala/org/apache/spark/sql/execution/BaseScriptTransformationExec.scala
+++ 
b/sql/core/src/main/scala/org/apache/spark/sql/execution/BaseScriptTransformationExec.scala
@@ -31,10 +31,28 @@ import org.apache.spark.internal.Logging
 import org.apache.spark.internal.LogKeys._
 import org.apache.spark.rdd.RDD
 import org.apache.spark.sql.catalyst.{CatalystTypeConverters, InternalRow}
-import org.apache.spark.sql.catalyst.expressions.{Attribute, AttributeSet, 
Cast, Expression, GenericInternalRow, JsonToStructs, Literal, StructsToJson, 
UnsafeProjection}
+import org.apache.spark.sql.catalyst.expressions.{
+  Attribute,
+  AttributeSet,
+  BoundReference,
+  Cast,
+  Expression,
+  GenericInternalRow,
+  JsonToStructs,
+  Literal,
+  StructsToJson,
+  UnsafeProjection}
 import org.apache.spark.sql.catalyst.plans.logical.ScriptInputOutputSchema
 import org.apache.spark.sql.catalyst.plans.physical.Partitioning
-import org.apache.spark.sql.catalyst.util.{DateTimeUtils, IntervalUtils}
+import org.apache.spark.sql.catalyst.util.{
+  ArrayBasedMapBuilder,
+  ArrayData,
+  CharVarcharCodegenUtils,
+  CharVarcharUtils,
+  DateTimeUtils,
+  GenericArrayData,
+  IntervalUtils,
+  MapData}
 import org.apache.spark.sql.errors.QueryExecutionErrors
 import org.apache.spark.sql.internal.SQLConf
 import org.apache.spark.sql.types._
@@ -62,6 +80,11 @@ trait BaseScriptTransformationExec extends UnaryExecNode {
 
   override def outputPartitioning: Partitioning = child.outputPartitioning
 
+  // Bound at parse into the I/O schema under the query or persisted-view 
SQLConf.
+  // Do not re-read SQLConf: planning and task conf can differ from that 
captured mode.
+  private def standardCharVarcharSemantics: Boolean =
+    ioschema.standardCharVarcharSemantics
+
   override def doExecute(): RDD[InternalRow] = {
     val broadcastedHadoopConf =
       new SerializableConfiguration(session.sessionState.newHadoopConf())
@@ -201,7 +224,20 @@ 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 dt @ (_: CharType | _: VarcharType) =>
+        // Without standard semantics, no-SerDe scalar CHAR/VARCHAR stays 
unsupported
+        // (they extend StringType, so this case must come before `_: 
StringType`).
+        if (!standardCharVarcharSemantics) {
+          throw 
QueryExecutionErrors.scriptTransformWithoutSerdeUnsupportedTypeError(dt)
+        }
+        // Do not use the SerDe null-on-error wrapper; that would hide 
EXCEED_LIMIT_LENGTH.
+        (data: String) =>
+          if (data == ioschema.outputRowFormatMap("TOK_TABLEROWFORMATNULL")) {
+            null
+          } else {
+            converter(data)
+          }
+      case _: StringType => wrapperConvertException(data => data, converter)
       case BooleanType => wrapperConvertException(data => data.toBoolean, 
converter)
       case ByteType => wrapperConvertException(data => data.toByte, converter)
       case BinaryType =>
@@ -241,6 +277,34 @@ trait BaseScriptTransformationExec extends UnaryExecNode {
         data => IntervalUtils.microsToDuration(
           IntervalUtils.castStringToDTInterval(UTF8String.fromString(data), 
start, end)),
         converter)
+      case dt @ (_: ArrayType | _: MapType | _: StructType)
+          if standardCharVarcharSemantics && 
CharVarcharUtils.hasCharVarchar(dt) =>
+        val physicalType = 
ScriptTransformationIOSchema.toUnboundedStringType(dt)
+        // JSON object keys are strings. Restore non-string keys inside the 
malformed
+        // wrapper, then apply CHAR/VARCHAR (and CHAR/VARCHAR map-key dedup) 
outside it.
+        val jsonType = 
ScriptTransformationIOSchema.toJsonMapKeyType(physicalType)
+        val tz = Some(conf.sessionLocalTimeZone)
+        val complexTypeFactory = JsonToStructs(
+          jsonType,
+          ioschema.outputSerdeProps.toMap,
+          Literal(null),
+          tz)
+        val restoreJsonKeys = if (jsonType.sameType(physicalType)) {
+          identity[Any] _
+        } else {
+          ScriptTransformationIOSchema.makeJsonToTargetRestorer(
+            jsonType, physicalType, tz)
+        }
+        val toDeclaredType = 
ScriptTransformationIOSchema.makeJsonToTargetRestorer(
+          physicalType, dt, tz)
+        val parser = wrapperConvertException(
+          data => restoreJsonKeys(
+            complexTypeFactory.nullSafeEval(UTF8String.fromString(data))),
+          identity)
+        (data: String) => {
+          val parsed = parser(data)
+          if (parsed == null) null else toDeclaredType(parsed)
+        }
       case _: ArrayType | _: MapType | _: StructType =>
         val complexTypeFactory = JsonToStructs(attr.dataType,
           ioschema.outputSerdeProps.toMap, Literal(null), 
Some(conf.sessionLocalTimeZone))
@@ -249,11 +313,11 @@ trait BaseScriptTransformationExec extends UnaryExecNode {
       case udt: UserDefinedType[_] =>
         wrapperConvertException(data => udt.deserialize(data), converter)
       case dt =>
-        throw 
QueryExecutionErrors.outputDataTypeUnsupportedByNodeWithoutSerdeError(nodeName, 
dt)
+        throw 
QueryExecutionErrors.scriptTransformWithoutSerdeUnsupportedTypeError(dt)
     }
   }
 
-  // Keep consistent with Hive `LazySimpleSerde`, when there is a type case 
error, return null
+  // Match Hive `LazySimpleSerDe`: return null when a type cast fails.
   private val wrapperConvertException: (String => Any, Any => Any) => String 
=> Any =
     (f: String => Any, converter: Any => Any) =>
       (data: String) => converter {
@@ -368,7 +432,8 @@ case class ScriptTransformationIOSchema(
     outputSerdeProps: Seq[(String, String)],
     recordReaderClass: Option[String],
     recordWriterClass: Option[String],
-    schemaLess: Boolean) extends Serializable {
+    schemaLess: Boolean,
+    standardCharVarcharSemantics: Boolean = false) extends Serializable {
   import ScriptTransformationIOSchema._
 
   val inputRowFormatMap = inputRowFormat.toMap.withDefault((k) => 
defaultFormat(k))
@@ -376,13 +441,115 @@ case class ScriptTransformationIOSchema(
 }
 
 object ScriptTransformationIOSchema {
+  private[sql] def toUnboundedStringType(dataType: DataType): DataType = {
+    dataType.transformRecursively {
+      case c: CharType => c.toStringType
+      case v: VarcharType => v.toStringType
+    }
+  }
+
+  // JSON object keys are always strings. Rewrite every map key, including 
nested maps.
+  // `transformRecursively` would stop at the first matching MapType and skip 
children.
+  private[sql] def toJsonMapKeyType(dataType: DataType): DataType = dataType 
match {
+    case ArrayType(et, n) => ArrayType(toJsonMapKeyType(et), n)
+    case MapType(kt, vt, n) =>
+      val jsonKey = if (kt.isInstanceOf[StringType]) kt else StringType
+      MapType(jsonKey, toJsonMapKeyType(vt), n)
+    case StructType(fields) =>
+      StructType(fields.map(f => f.copy(dataType = 
toJsonMapKeyType(f.dataType))))
+    case other => other
+  }
+
+  /**
+   * Convert a Catalyst value from `sourceType` to `targetType`. `sourceType` 
is
+   * the parsed Catalyst type (unbounded STRING in place of CHAR/VARCHAR, and
+   * STRING map keys where JSON cannot preserve the declared key type).
+   * CHAR/VARCHAR leaves use write-side length checks. Maps rebuild through a
+   * fresh [[ArrayBasedMapBuilder]] when the source and target map types differ
+   * or the target contains CHAR/VARCHAR, so CHAR/VARCHAR key collisions follow
+   * MAP_KEY_DEDUP_POLICY. Same-typed maps without CHAR/VARCHAR are unchanged.
+   */
+  private[sql] def makeJsonToTargetRestorer(
+      sourceType: DataType,
+      targetType: DataType,
+      timeZoneId: Option[String]): Any => Any = {
+
+    def make(jt: DataType, tt: DataType): Any => Any = {
+      if (jt.sameType(tt) && !CharVarcharUtils.hasCharVarchar(tt)) {
+        identity
+      } else (jt, tt) match {
+        case (_, c: CharType) =>
+          (input: Any) =>
+            CharVarcharCodegenUtils.charTypeWriteSideCheck(
+              input.asInstanceOf[UTF8String], c.length)
+        case (_, v: VarcharType) =>
+          (input: Any) =>
+            CharVarcharCodegenUtils.varcharTypeWriteSideCheck(
+              input.asInstanceOf[UTF8String], v.length)
+        case (ArrayType(jet, _), ArrayType(tet, _)) =>
+          val elem = make(jet, tet)
+          (input: Any) => {
+            val arr = input.asInstanceOf[ArrayData]
+            val n = arr.numElements()
+            val out = new Array[Any](n)
+            var i = 0
+            while (i < n) {
+              out(i) = if (arr.isNullAt(i)) null
+                else elem(arr.get(i, jet))
+              i += 1
+            }
+            new GenericArrayData(out)
+          }
+        case (MapType(jkt, jvt, _), MapType(tkt, tvt, _)) =>
+          val keyConvert = make(jkt, tkt)
+          val valRestore = make(jvt, tvt)
+          (input: Any) => {
+            val map = input.asInstanceOf[MapData]
+            val n = map.numElements()
+            val builder = new ArrayBasedMapBuilder(tkt, tvt)
+            var i = 0
+            while (i < n) {
+              val k = keyConvert(map.keyArray().get(i, jkt))
+              val v = if (map.valueArray().isNullAt(i)) null
+                else valRestore(map.valueArray().get(i, jvt))
+              builder.put(k, v)
+              i += 1
+            }
+            builder.build()
+          }
+        case (js: StructType, ts: StructType) =>
+          val restorers = js.fields.zip(ts.fields).map {
+            case (jf, tf) => make(jf.dataType, tf.dataType)
+          }
+          (input: Any) => {
+            val row = input.asInstanceOf[InternalRow]
+            val out = new GenericInternalRow(ts.length)
+            var i = 0
+            while (i < ts.length) {
+              if (row.isNullAt(i)) out.setNullAt(i)
+              else out.update(i,
+                restorers(i)(row.get(i, js(i).dataType)))
+              i += 1
+            }
+            out
+          }
+        case _ if !jt.sameType(tt) =>
+          val c = Cast(BoundReference(0, jt, nullable = false), tt, timeZoneId)
+          (input: Any) => c.eval(InternalRow(input))
+        case _ => identity
+      }
+    }
+
+    make(sourceType, targetType)
+  }
+
   val defaultFormat = Map(
     ("TOK_TABLEROWFORMATFIELD", "\u0001"),
     ("TOK_TABLEROWFORMATLINES", "\n"),
     ("TOK_TABLEROWFORMATNULL" -> "\\N")
   )
 
-  val defaultIOSchema = ScriptTransformationIOSchema(
+  def defaultIOSchema: ScriptTransformationIOSchema = 
ScriptTransformationIOSchema(
     inputRowFormat = Seq.empty,
     outputRowFormat = Seq.empty,
     inputSerdeClass = None,
@@ -391,7 +558,8 @@ object ScriptTransformationIOSchema {
     outputSerdeProps = Seq.empty,
     recordReaderClass = None,
     recordWriterClass = None,
-    schemaLess = false
+    schemaLess = false,
+    standardCharVarcharSemantics = SQLConf.get.charVarcharStandardSemantics
   )
 
   def apply(input: ScriptInputOutputSchema): ScriptTransformationIOSchema = {
@@ -404,6 +572,7 @@ object ScriptTransformationIOSchema {
       input.outputSerdeProps,
       input.recordReaderClass,
       input.recordWriterClass,
-      input.schemaLess)
+      input.schemaLess,
+      input.standardCharVarcharSemantics)
   }
 }
diff --git 
a/sql/core/src/main/scala/org/apache/spark/sql/execution/SparkSqlParser.scala 
b/sql/core/src/main/scala/org/apache/spark/sql/execution/SparkSqlParser.scala
index 7747c94b584d..87213a1619ab 100644
--- 
a/sql/core/src/main/scala/org/apache/spark/sql/execution/SparkSqlParser.scala
+++ 
b/sql/core/src/main/scala/org/apache/spark/sql/execution/SparkSqlParser.scala
@@ -1347,7 +1347,8 @@ class SparkSqlAstBuilder extends AstBuilder {
         inSerdeClass, outSerdeClass,
         inSerdeProps, outSerdeProps,
         reader, writer,
-        schemaLess)
+        schemaLess,
+        conf.charVarcharStandardSemantics)
     }
   }
 
diff --git 
a/sql/core/src/test/resources/sql-tests/analyzer-results/transform.sql.out 
b/sql/core/src/test/resources/sql-tests/analyzer-results/transform.sql.out
index 1e3ddd1c198d..fe7aa8b4282b 100644
--- a/sql/core/src/test/resources/sql-tests/analyzer-results/transform.sql.out
+++ b/sql/core/src/test/resources/sql-tests/analyzer-results/transform.sql.out
@@ -67,7 +67,7 @@ SELECT TRANSFORM(a)
 USING 'cat' AS (a)
 FROM t
 -- !query analysis
-ScriptTransformation cat, [a#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+ScriptTransformation cat, [a#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
 +- Project [a#x]
    +- SubqueryAlias t
       +- View (`t`, [a#x, b#x, c#x, d#x, e#x, f#x, g#xL, h#x, i#x, j#x, k#x, 
l#x])
@@ -98,7 +98,7 @@ SELECT a, b, decode(c, 'UTF-8'), d, e, f, g, h, i, j, k, l 
FROM (
 -- !query analysis
 Project [a#x, b#x, decode(c#x, UTF-8) AS decode(c, UTF-8)#x, d#x, e#x, f#x, 
g#xL, h#x, i#x, j#x, k#x, l#x]
 +- SubqueryAlias tmp
-   +- ScriptTransformation cat, [a#x, b#x, c#x, d#x, e#x, f#x, g#xL, h#x, i#x, 
j#x, k#x, l#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+   +- ScriptTransformation cat, [a#x, b#x, c#x, d#x, e#x, f#x, g#xL, h#x, i#x, 
j#x, k#x, l#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
       +- Project [a#x, b#x, c#x, d#x, e#x, f#x, g#xL, h#x, i#x, j#x, k#x, l#x]
          +- SubqueryAlias t
             +- View (`t`, [a#x, b#x, c#x, d#x, e#x, f#x, g#xL, h#x, i#x, j#x, 
k#x, l#x])
@@ -129,7 +129,7 @@ SELECT a, b, decode(c, 'UTF-8'), d, e, f, g, h, i, j, k, l 
FROM (
 -- !query analysis
 Project [a#x, b#x, decode(c#x, UTF-8) AS decode(c, UTF-8)#x, d#x, e#x, f#x, 
g#x, h#x, i#x, j#x, k#x, l#x]
 +- SubqueryAlias tmp
-   +- ScriptTransformation cat, [a#x, b#x, c#x, d#x, e#x, f#x, g#x, h#x, i#x, 
j#x, k#x, l#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+   +- ScriptTransformation cat, [a#x, b#x, c#x, d#x, e#x, f#x, g#x, h#x, i#x, 
j#x, k#x, l#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
       +- Project [a#x, b#x, c#x, d#x, e#x, f#x, g#xL, h#x, i#x, j#x, k#x, l#x]
          +- SubqueryAlias t
             +- View (`t`, [a#x, b#x, c#x, d#x, e#x, f#x, g#xL, h#x, i#x, j#x, 
k#x, l#x])
@@ -144,7 +144,7 @@ SELECT TRANSFORM(a)
 USING 'cat'
 FROM t
 -- !query analysis
-ScriptTransformation cat, [key#x, value#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,true)
+ScriptTransformation cat, [key#x, value#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,true,false)
 +- Project [a#x]
    +- SubqueryAlias t
       +- View (`t`, [a#x, b#x, c#x, d#x, e#x, f#x, g#xL, h#x, i#x, j#x, k#x, 
l#x])
@@ -159,7 +159,7 @@ SELECT TRANSFORM(a, b)
 USING 'cat'
 FROM t
 -- !query analysis
-ScriptTransformation cat, [key#x, value#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,true)
+ScriptTransformation cat, [key#x, value#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,true,false)
 +- Project [a#x, b#x]
    +- SubqueryAlias t
       +- View (`t`, [a#x, b#x, c#x, d#x, e#x, f#x, g#xL, h#x, i#x, j#x, k#x, 
l#x])
@@ -174,7 +174,7 @@ SELECT TRANSFORM(a, b, c)
 USING 'cat'
 FROM t
 -- !query analysis
-ScriptTransformation cat, [key#x, value#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,true)
+ScriptTransformation cat, [key#x, value#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,true,false)
 +- Project [a#x, b#x, c#x]
    +- SubqueryAlias t
       +- View (`t`, [a#x, b#x, c#x, d#x, e#x, f#x, g#xL, h#x, i#x, j#x, k#x, 
l#x])
@@ -190,7 +190,7 @@ USING 'cat' AS (a int, b short, c long, d byte, e float, f 
double, g decimal(38,
 FROM VALUES
 ('a','','1231a','a','213.21a','213.21a','0a.21d','2000-04-01123','1997-0102 
00:00:') tmp(a, b, c, d, e, f, g, h, i)
 -- !query analysis
-ScriptTransformation cat, [a#x, b#x, c#xL, d#x, e#x, f#x, g#x, h#x, i#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+ScriptTransformation cat, [a#x, b#x, c#xL, d#x, e#x, f#x, g#x, h#x, i#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
 +- Project [a#x, b#x, c#x, d#x, e#x, f#x, g#x, h#x, i#x]
    +- SubqueryAlias tmp
       +- LocalRelation [a#x, b#x, c#x, d#x, e#x, f#x, g#x, h#x, i#x]
@@ -202,7 +202,7 @@ USING 'cat' AS (a, b)
 FROM t
 GROUP BY b
 -- !query analysis
-ScriptTransformation cat, [a#x, b#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+ScriptTransformation cat, [a#x, b#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
 +- Aggregate [b#x], [b#x, max(a#x) AS max(a)#x, sum(f#x) AS sum(f)#xL]
    +- SubqueryAlias t
       +- View (`t`, [a#x, b#x, c#x, d#x, e#x, f#x, g#xL, h#x, i#x, j#x, k#x, 
l#x])
@@ -215,7 +215,7 @@ ScriptTransformation cat, [a#x, b#x], 
ScriptInputOutputSchema(List(),List(),None
 -- !query
 MAP a, b USING 'cat' AS (a, b) FROM t
 -- !query analysis
-ScriptTransformation cat, [a#x, b#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+ScriptTransformation cat, [a#x, b#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
 +- Project [a#x, b#x]
    +- SubqueryAlias t
       +- View (`t`, [a#x, b#x, c#x, d#x, e#x, f#x, g#xL, h#x, i#x, j#x, k#x, 
l#x])
@@ -228,7 +228,7 @@ ScriptTransformation cat, [a#x, b#x], 
ScriptInputOutputSchema(List(),List(),None
 -- !query
 REDUCE a, b USING 'cat' AS (a, b) FROM t
 -- !query analysis
-ScriptTransformation cat, [a#x, b#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+ScriptTransformation cat, [a#x, b#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
 +- Project [a#x, b#x]
    +- SubqueryAlias t
       +- View (`t`, [a#x, b#x, c#x, d#x, e#x, f#x, g#xL, h#x, i#x, j#x, k#x, 
l#x])
@@ -253,7 +253,7 @@ FROM t
 -- !query analysis
 ScriptTransformation cat, [a#x, b#x, c#x, d#x], 
ScriptInputOutputSchema(List((TOK_TABLEROWFORMATFIELD,@), 
(TOK_TABLEROWFORMATNULL,NULL), (TOK_TABLEROWFORMATLINES,
 )),List((TOK_TABLEROWFORMATFIELD,@), (TOK_TABLEROWFORMATNULL,NULL), 
(TOK_TABLEROWFORMATLINES,
-)),None,None,List(),List(),None,None,false)
+)),None,None,List(),List(),None,None,false,false)
 +- Project [a#x, b#x, c#x, null AS NULL#x]
    +- SubqueryAlias t
       +- View (`t`, [a#x, b#x, c#x, d#x, e#x, f#x, g#xL, h#x, i#x, j#x, k#x, 
l#x])
@@ -278,7 +278,7 @@ FROM t
 -- !query analysis
 ScriptTransformation cat, [d#x], 
ScriptInputOutputSchema(List((TOK_TABLEROWFORMATFIELD,@), 
(TOK_TABLEROWFORMATNULL,NULL), (TOK_TABLEROWFORMATLINES,
 )),List((TOK_TABLEROWFORMATFIELD,@), (TOK_TABLEROWFORMATNULL,NULL), 
(TOK_TABLEROWFORMATLINES,
-)),None,None,List(),List(),None,None,false)
+)),None,None,List(),List(),None,None,false,false)
 +- Project [a#x, b#x, c#x, null AS NULL#x]
    +- SubqueryAlias t
       +- View (`t`, [a#x, b#x, c#x, d#x, e#x, f#x, g#xL, h#x, i#x, j#x, k#x, 
l#x])
@@ -302,7 +302,7 @@ FROM t
 -- !query analysis
 ScriptTransformation cat, [a#x, b#x, c#x, d#x], 
ScriptInputOutputSchema(List((TOK_TABLEROWFORMATFIELD,@), 
(TOK_TABLEROWFORMATLINES,
 )),List((TOK_TABLEROWFORMATFIELD,@), (TOK_TABLEROWFORMATNULL,NULL), 
(TOK_TABLEROWFORMATLINES,
-)),None,None,List(),List(),None,None,false)
+)),None,None,List(),List(),None,None,false,false)
 +- Project [a#x, b#x, c#x, null AS NULL#x]
    +- SubqueryAlias t
       +- View (`t`, [a#x, b#x, c#x, d#x, e#x, f#x, g#xL, h#x, i#x, j#x, k#x, 
l#x])
@@ -325,7 +325,7 @@ FROM t
 -- !query analysis
 ScriptTransformation cat, [a#x, b#x, c#x, d#x], 
ScriptInputOutputSchema(List((TOK_TABLEROWFORMATFIELD,@), 
(TOK_TABLEROWFORMATLINES,
 )),List((TOK_TABLEROWFORMATFIELD,@), (TOK_TABLEROWFORMATLINES,
-)),None,None,List(),List(),None,None,false)
+)),None,None,List(),List(),None,None,false,false)
 +- Project [a#x, b#x, c#x, null AS NULL#x]
    +- SubqueryAlias t
       +- View (`t`, [a#x, b#x, c#x, d#x, e#x, f#x, g#xL, h#x, i#x, j#x, k#x, 
l#x])
@@ -349,7 +349,7 @@ FROM t
 -- !query analysis
 ScriptTransformation cat, [a#x, b#x, c#x, d#x], 
ScriptInputOutputSchema(List((TOK_TABLEROWFORMATFIELD,@), 
(TOK_TABLEROWFORMATNULL,XXXX), (TOK_TABLEROWFORMATLINES,
 )),List((TOK_TABLEROWFORMATFIELD,@), (TOK_TABLEROWFORMATLINES,
-)),None,None,List(),List(),None,None,false)
+)),None,None,List(),List(),None,None,false,false)
 +- Project [a#x, b#x, c#x, null AS NULL#x]
    +- SubqueryAlias t
       +- View (`t`, [a#x, b#x, c#x, d#x, e#x, f#x, g#xL, h#x, i#x, j#x, k#x, 
l#x])
@@ -374,7 +374,7 @@ FROM t
 ScriptTransformation cat, [a#x, b#x, c#x, d#x], 
ScriptInputOutputSchema(List((TOK_TABLEROWFORMATFIELD,@), 
(TOK_TABLEROWFORMATNULL,
 ), (TOK_TABLEROWFORMATLINES,
 )),List((TOK_TABLEROWFORMATFIELD,@), (TOK_TABLEROWFORMATLINES,
-)),None,None,List(),List(),None,None,false)
+)),None,None,List(),List(),None,None,false,false)
 +- Project [a#x, b#x, c#x, null AS NULL#x]
    +- SubqueryAlias t
       +- View (`t`, [a#x, b#x, c#x, d#x, e#x, f#x, g#xL, h#x, i#x, j#x, k#x, 
l#x])
@@ -415,7 +415,7 @@ Project [a#x, b#x, decode(c#x, UTF-8) AS decode(c, 
UTF-8)#x, d#x, e#x, f#x, g#xL
 +- SubqueryAlias tmp
    +- ScriptTransformation cat, [a#x, b#x, c#x, d#x, e#x, f#x, g#xL, h#x, i#x, 
j#x, k#x, l#x], ScriptInputOutputSchema(List((TOK_TABLEROWFORMATFIELD,,), 
(TOK_TABLEROWFORMATNULL,NULL), (TOK_TABLEROWFORMATLINES,
 )),List((TOK_TABLEROWFORMATFIELD,,), (TOK_TABLEROWFORMATNULL,NULL), 
(TOK_TABLEROWFORMATLINES,
-)),None,None,List(),List(),None,None,false)
+)),None,None,List(),List(),None,None,false,false)
       +- Project [a#x, b#x, c#x, d#x, e#x, f#x, g#xL, h#x, i#x, j#x, k#x, l#x]
          +- SubqueryAlias t
             +- View (`t`, [a#x, b#x, c#x, d#x, e#x, f#x, g#xL, h#x, i#x, j#x, 
k#x, l#x])
@@ -456,7 +456,7 @@ Project [a#x, b#xL, decode(c#x, UTF-8) AS decode(c, 
UTF-8)#x, d#x, e#x, f#x, g#x
 +- SubqueryAlias tmp
    +- ScriptTransformation cat, [a#x, b#xL, c#x, d#x, e#x, f#x, g#xL, h#x, 
i#x, j#x, k#x, l#xL], ScriptInputOutputSchema(List((TOK_TABLEROWFORMATFIELD,,), 
(TOK_TABLEROWFORMATNULL,NULL), (TOK_TABLEROWFORMATLINES,
 )),List((TOK_TABLEROWFORMATFIELD,,), (TOK_TABLEROWFORMATNULL,NULL), 
(TOK_TABLEROWFORMATLINES,
-)),None,None,List(),List(),None,None,false)
+)),None,None,List(),List(),None,None,false,false)
       +- Project [a#x, b#x, c#x, d#x, e#x, f#x, g#xL, h#x, i#x, j#x, k#x, l#x]
          +- SubqueryAlias t
             +- View (`t`, [a#x, b#x, c#x, d#x, e#x, f#x, g#xL, h#x, i#x, j#x, 
k#x, l#x])
@@ -516,7 +516,7 @@ SELECT TRANSFORM(b, a, CAST(c AS STRING))
 FROM script_trans
 WHERE a <= 4
 -- !query analysis
-ScriptTransformation cat, [a#x, b#x, c#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+ScriptTransformation cat, [a#x, b#x, c#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
 +- Project [b#x, a#x, cast(c#x as string) AS c#x]
    +- Filter (a#x <= 4)
       +- SubqueryAlias script_trans
@@ -533,7 +533,7 @@ SELECT TRANSFORM(1, 2, 3)
 FROM script_trans
 WHERE a <= 4
 -- !query analysis
-ScriptTransformation cat, [a#x, b#x, c#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+ScriptTransformation cat, [a#x, b#x, c#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
 +- Project [1 AS 1#x, 2 AS 2#x, 3 AS 3#x]
    +- Filter (a#x <= 4)
       +- SubqueryAlias script_trans
@@ -552,7 +552,7 @@ LIMIT 1
 -- !query analysis
 GlobalLimit 1
 +- LocalLimit 1
-   +- ScriptTransformation cat, [a#x, b#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+   +- ScriptTransformation cat, [a#x, b#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
       +- Project [1 AS 1#x, 2 AS 2#x]
          +- SubqueryAlias script_trans
             +- View (`script_trans`, [a#x, b#x, c#x])
@@ -573,7 +573,7 @@ SELECT TRANSFORM(
 FROM script_trans
 WHERE a <= 4
 -- !query analysis
-ScriptTransformation cat, [a#x, b#x, c#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+ScriptTransformation cat, [a#x, b#x, c#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
 +- Project [b#x, a#x, CASE WHEN (c#x > 100) THEN 1 WHEN (c#x < 100) THEN 2 
ELSE 3 END AS CASE WHEN (c > 100) THEN 1 WHEN (c < 100) THEN 2 ELSE 3 END#x]
    +- Filter (a#x <= 4)
       +- SubqueryAlias script_trans
@@ -590,7 +590,7 @@ SELECT TRANSFORM(b, a, c + 1)
 FROM script_trans
 WHERE a <= 4
 -- !query analysis
-ScriptTransformation cat, [a#x, b#x, c#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+ScriptTransformation cat, [a#x, b#x, c#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
 +- Project [b#x, a#x, (c#x + 1) AS (c + 1)#x]
    +- Filter (a#x <= 4)
       +- SubqueryAlias script_trans
@@ -607,7 +607,7 @@ SELECT TRANSFORM(*)
 FROM script_trans
 WHERE a <= 4
 -- !query analysis
-ScriptTransformation cat, [a#x, b#x, c#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+ScriptTransformation cat, [a#x, b#x, c#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
 +- Project [a#x, b#x, c#x]
    +- Filter (a#x <= 4)
       +- SubqueryAlias script_trans
@@ -625,7 +625,7 @@ FROM script_trans
 WHERE a <= 4
 GROUP BY b
 -- !query analysis
-ScriptTransformation cat, [a#x, b#x, c#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+ScriptTransformation cat, [a#x, b#x, c#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
 +- Aggregate [b#x], [b#x, max(a#x) AS max(a)#x, cast(sum(c#x) as string) AS 
CAST(sum(c) AS STRING)#x]
    +- Filter (a#x <= 4)
       +- SubqueryAlias script_trans
@@ -643,7 +643,7 @@ FROM script_trans
 WHERE a <= 4
 GROUP BY b
 -- !query analysis
-ScriptTransformation cat, [a#x, b#x, c#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+ScriptTransformation cat, [a#x, b#x, c#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
 +- Aggregate [b#x], [b#x, max(a#x) FILTER (WHERE (a#x > 3)) AS max(a) FILTER 
(WHERE (a > 3))#x, cast(sum(c#x) as string) AS CAST(sum(c) AS STRING)#x]
    +- Filter (a#x <= 4)
       +- SubqueryAlias script_trans
@@ -661,7 +661,7 @@ FROM script_trans
 WHERE a <= 2
 GROUP BY b
 -- !query analysis
-ScriptTransformation cat, [a#x, b#x, c#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+ScriptTransformation cat, [a#x, b#x, c#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
 +- Aggregate [b#x], [b#x, max(a#x) AS max(a)#x, cast(sum(c#x) as string) AS 
CAST(sum(c) AS STRING)#x]
    +- Filter (a#x <= 2)
       +- SubqueryAlias script_trans
@@ -680,7 +680,7 @@ WHERE a <= 4
 GROUP BY b
 HAVING MAX(a) > 0
 -- !query analysis
-ScriptTransformation cat, [a#x, b#x, c#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+ScriptTransformation cat, [a#x, b#x, c#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
 +- Filter (max(a)#x > 0)
    +- Aggregate [b#x], [b#x, max(a#x) AS max(a)#x, cast(sum(c#x) as string) AS 
CAST(sum(c) AS STRING)#x]
       +- Filter (a#x <= 4)
@@ -700,7 +700,7 @@ WHERE a <= 4
 GROUP BY b
 HAVING MAX(a) > 1
 -- !query analysis
-ScriptTransformation cat, [a#x, b#x, c#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+ScriptTransformation cat, [a#x, b#x, c#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
 +- Filter (max(a)#x > 1)
    +- Aggregate [b#x], [b#x, max(a#x) AS max(a)#x, cast(sum(c#x) as string) AS 
CAST(sum(c) AS STRING)#x]
       +- Filter (a#x <= 4)
@@ -719,7 +719,7 @@ FROM script_trans
 WHERE a <= 4
 WINDOW w AS (PARTITION BY b ORDER BY a)
 -- !query analysis
-ScriptTransformation cat, [a#x, b#x, c#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+ScriptTransformation cat, [a#x, b#x, c#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
 +- Project [b#x, max(a) OVER (PARTITION BY b ORDER BY a ASC NULLS FIRST RANGE 
BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)#x, CAST(sum(c) OVER (PARTITION BY 
b ORDER BY a ASC NULLS FIRST RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) 
AS STRING)#x]
    +- Project [b#x, a#x, c#x, max(a) OVER (PARTITION BY b ORDER BY a ASC NULLS 
FIRST RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)#x, _we1#xL, max(a) 
OVER (PARTITION BY b ORDER BY a ASC NULLS FIRST RANGE BETWEEN UNBOUNDED 
PRECEDING AND CURRENT ROW)#x, cast(_we1#xL as string) AS CAST(sum(c) OVER 
(PARTITION BY b ORDER BY a ASC NULLS FIRST RANGE BETWEEN UNBOUNDED PRECEDING 
AND CURRENT ROW) AS STRING)#x]
       +- Window [max(a#x) windowspecdefinition(b#x, a#x ASC NULLS FIRST, 
specifiedwindowframe(RangeFrame, unboundedpreceding$(), currentrow$())) AS 
max(a) OVER (PARTITION BY b ORDER BY a ASC NULLS FIRST RANGE BETWEEN UNBOUNDED 
PRECEDING AND CURRENT ROW)#x, sum(c#x) windowspecdefinition(b#x, a#x ASC NULLS 
FIRST, specifiedwindowframe(RangeFrame, unboundedpreceding$(), currentrow$())) 
AS _we1#xL], [b#x], [a#x ASC NULLS FIRST]
@@ -743,7 +743,7 @@ WHERE a <= 4
 GROUP BY b, myCol, myCol2
 HAVING max(a) > 1
 -- !query analysis
-ScriptTransformation cat, [a#x, b#x, c#x, d#x, e#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+ScriptTransformation cat, [a#x, b#x, c#x, d#x, e#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
 +- Filter (max(a)#x > 1)
    +- Aggregate [b#x, myCol#x, myCol2#x], [b#x, max(a#x) AS max(a)#x, 
cast(sum(c#x) as string) AS CAST(sum(c) AS STRING)#x, myCol#x, myCol2#x]
       +- Filter (a#x <= 4)
@@ -767,7 +767,7 @@ SELECT a + 1
 -- !query analysis
 Project [(a#x + 1) AS (a + 1)#x]
 +- SubqueryAlias t
-   +- ScriptTransformation cat, [a#x, b#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+   +- ScriptTransformation cat, [a#x, b#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
       +- Project [a#x, b#x]
          +- SubqueryAlias script_trans
             +- View (`script_trans`, [a#x, b#x, c#x])
@@ -790,7 +790,7 @@ ORDER BY result
 Sort [result#xL ASC NULLS FIRST], true
 +- Project [(cast(b#x as bigint) + cast(1 as bigint)) AS result#xL]
    +- SubqueryAlias t
-      +- ScriptTransformation cat, [a#x, b#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+      +- ScriptTransformation cat, [a#x, b#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
          +- Aggregate [a#x], [a#x, sum(b#x) AS sum(b)#xL]
             +- SubqueryAlias script_trans
                +- View (`script_trans`, [a#x, b#x, c#x])
@@ -803,7 +803,7 @@ Sort [result#xL ASC NULLS FIRST], true
 -- !query
 MAP k / 10 USING 'cat' AS (one) FROM (SELECT 10 AS k)
 -- !query analysis
-ScriptTransformation cat, [one#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+ScriptTransformation cat, [one#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
 +- Project [(cast(k#x as double) / cast(10 as double)) AS (k / 10)#x]
    +- SubqueryAlias __auto_generated_subquery_name
       +- Project [10 AS k#x]
@@ -815,7 +815,7 @@ FROM (SELECT 1 AS key, 100 AS value) src
 MAP src.*, src.key, CAST(src.key / 10 AS INT), CAST(src.key % 10 AS INT), 
src.value
   USING 'cat' AS (k, v, tkey, ten, one, tvalue)
 -- !query analysis
-ScriptTransformation cat, [k#x, v#x, tkey#x, ten#x, one#x, tvalue#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+ScriptTransformation cat, [k#x, v#x, tkey#x, ten#x, one#x, tvalue#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
 +- Project [key#x, value#x, key#x, cast((cast(key#x as double) / cast(10 as 
double)) as int) AS CAST((key / 10) AS INT)#x, cast((key#x % 10) as int) AS 
CAST((key % 10) AS INT)#x, value#x]
    +- SubqueryAlias src
       +- Project [1 AS key#x, 100 AS value#x]
@@ -828,7 +828,7 @@ SELECT TRANSFORM(1)
 FROM script_trans
 HAVING true
 -- !query analysis
-ScriptTransformation cat, [a#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+ScriptTransformation cat, [a#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
 +- Filter cast(true as boolean)
    +- Aggregate [1 AS 1#x]
       +- SubqueryAlias script_trans
@@ -851,7 +851,7 @@ SELECT TRANSFORM(1)
 FROM script_trans
 HAVING true
 -- !query analysis
-ScriptTransformation cat, [a#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+ScriptTransformation cat, [a#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
 +- Filter cast(true as boolean)
    +- Project [1 AS 1#x]
       +- SubqueryAlias script_trans
@@ -877,7 +877,7 @@ SELECT t1.b FROM temp t1 JOIN temp t2 ON t1.b = t2.b
 WithCTE
 :- CTERelationDef xxxx, false
 :  +- SubqueryAlias temp
-:     +- ScriptTransformation cat, [b#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+:     +- ScriptTransformation cat, [b#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
 :        +- Project [a#x]
 :           +- SubqueryAlias t
 :              +- View (`t`, [a#x, b#x, c#x, d#x, e#x, f#x, g#xL, h#x, i#x, 
j#x, k#x, l#x])
@@ -999,12 +999,12 @@ FROM (
 SELECT TRANSFORM(a, b)
   USING 'cat' AS (a, b)
 -- !query analysis
-ScriptTransformation cat, [a#x, b#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+ScriptTransformation cat, [a#x, b#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
 +- Project [a#x, b#x]
    +- SubqueryAlias map_output
       +- Sort [a#x ASC NULLS FIRST], false
          +- RepartitionByExpression [a#x]
-            +- ScriptTransformation cat, [a#x, b#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+            +- ScriptTransformation cat, [a#x, b#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
                +- Project [a#x, b#x]
                   +- SubqueryAlias complex_trans
                      +- View (`complex_trans`, [a#x, b#x])
@@ -1024,11 +1024,11 @@ FROM (
 SELECT TRANSFORM(a, b)
   USING 'cat' AS (a, b)
 -- !query analysis
-ScriptTransformation cat, [a#x, b#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+ScriptTransformation cat, [a#x, b#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
 +- Project [a#x, b#x]
    +- SubqueryAlias map_output
       +- Sort [a#x ASC NULLS FIRST], true
-         +- ScriptTransformation cat, [a#x, b#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+         +- ScriptTransformation cat, [a#x, b#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
             +- Project [a#x, b#x]
                +- SubqueryAlias complex_trans
                   +- View (`complex_trans`, [a#x, b#x])
@@ -1043,7 +1043,7 @@ SELECT TRANSFORM (a, b)
   USING 'cat' AS (a CHAR(10), b VARCHAR(10))
 FROM VALUES('apache', 'spark') t(a, b)
 -- !query analysis
-ScriptTransformation cat, [a#x, b#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false)
+ScriptTransformation cat, [a#x, b#x], 
ScriptInputOutputSchema(List(),List(),None,None,List(),List(),None,None,false,false)
 +- Project [a#x, b#x]
    +- SubqueryAlias t
       +- LocalRelation [a#x, b#x]
diff --git 
a/sql/core/src/test/scala/org/apache/spark/sql/execution/BaseScriptTransformationSuite.scala
 
b/sql/core/src/test/scala/org/apache/spark/sql/execution/BaseScriptTransformationSuite.scala
index 62c5f5631776..f7fb05526d96 100644
--- 
a/sql/core/src/test/scala/org/apache/spark/sql/execution/BaseScriptTransformationSuite.scala
+++ 
b/sql/core/src/test/scala/org/apache/spark/sql/execution/BaseScriptTransformationSuite.scala
@@ -26,7 +26,7 @@ import org.json4s.jackson.JsonMethods._
 import org.scalatest.Assertions._
 import org.scalatest.exceptions.TestFailedException
 
-import org.apache.spark.{SparkException, TaskContext, TestUtils}
+import org.apache.spark.{SparkException, SparkThrowable, TaskContext, 
TestUtils}
 import org.apache.spark.rdd.RDD
 import org.apache.spark.sql.{QueryTest, Row}
 import org.apache.spark.sql.catalyst.InternalRow
@@ -70,6 +70,23 @@ abstract class BaseScriptTransformationSuite extends 
QueryTest {
       child: SparkPlan,
       ioschema: ScriptTransformationIOSchema): BaseScriptTransformationExec
 
+  protected def checkTransformWithoutSerdeUnsupportedType(
+      exception: Throwable,
+      sqlType: String): Unit = {
+    val err = Iterator.iterate[Throwable](exception)(_.getCause)
+      .takeWhile(_ != null)
+      .collectFirst {
+        case t: SparkThrowable
+            if t.getCondition == 
"UNSUPPORTED_FEATURE.TRANSFORM_WITHOUT_SERDE_TYPE" => t
+      }.getOrElse {
+        fail(s"expected TRANSFORM_WITHOUT_SERDE_TYPE, got $exception")
+      }
+    checkError(
+      exception = err,
+      condition = "UNSUPPORTED_FEATURE.TRANSFORM_WITHOUT_SERDE_TYPE",
+      parameters = Map("dataType" -> sqlType))
+  }
+
   test("cat without SerDe") {
     assume(TestUtils.testCommandAvailable("/bin/bash"))
 
@@ -86,6 +103,362 @@ abstract class BaseScriptTransformationSuite extends 
QueryTest {
     assert(uncaughtExceptionHandler.exception.isEmpty)
   }
 
+  test("SPARK-59277: TRANSFORM output supports first-class CHAR/VARCHAR 
without SerDe") {
+    assume(TestUtils.testCommandAvailable("/bin/bash"))
+    withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "true") {
+      val input = Seq(("ab", "xyz")).toDF("c", "v")
+      checkAnswer(
+        input,
+        (child: SparkPlan) => createScriptTransformationExec(
+          script = "cat",
+          output = Seq(
+            AttributeReference("c", CharType(4, "UTF8_LCASE"))(),
+            AttributeReference("v", VarcharType(5, "UNICODE_CI"))()),
+          child = child,
+          ioschema = defaultIOSchema),
+        Seq(Row("ab  ", "xyz")))
+    }
+    assert(uncaughtExceptionHandler.exception.isEmpty)
+  }
+
+  test("SPARK-59277: TRANSFORM CHAR/VARCHAR null token without SerDe") {
+    assume(TestUtils.testCommandAvailable("/bin/bash"))
+    withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "true") {
+      val charInput = Seq[String](null, "ab").toDF("c")
+      checkAnswer(
+        charInput,
+        (child: SparkPlan) => createScriptTransformationExec(
+          script = "cat",
+          output = Seq(AttributeReference("c", CharType(4))()),
+          child = child,
+          ioschema = defaultIOSchema),
+        Seq(Row(null), Row("ab  ")))
+      val varcharInput = Seq[String](null, "xy").toDF("v")
+      checkAnswer(
+        varcharInput,
+        (child: SparkPlan) => createScriptTransformationExec(
+          script = "cat",
+          output = Seq(AttributeReference("v", VarcharType(5))()),
+          child = child,
+          ioschema = defaultIOSchema),
+        Seq(Row(null), Row("xy")))
+    }
+    assert(uncaughtExceptionHandler.exception.isEmpty)
+  }
+
+  test("SPARK-59277: TRANSFORM CHAR overflow without SerDe raises 
EXCEED_LIMIT_LENGTH") {
+    assume(TestUtils.testCommandAvailable("/bin/bash"))
+    withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "true") {
+      val input = Seq("abcdef").toDF("c")
+      val exception = intercept[Exception] {
+        QueryTest.executePlan(
+          createScriptTransformationExec(
+            script = "cat",
+            output = Seq(AttributeReference("c", CharType(4))()),
+            child = input.queryExecution.sparkPlan,
+            ioschema = defaultIOSchema),
+          spark.sqlContext)
+      }
+      val runtimeException = exception match {
+        case s: org.apache.spark.SparkRuntimeException => s
+        case other =>
+          other.getCause.asInstanceOf[org.apache.spark.SparkRuntimeException]
+      }
+      checkError(
+        exception = runtimeException,
+        condition = "EXCEED_LIMIT_LENGTH",
+        parameters = Map("limit" -> "4"))
+    }
+  }
+
+  test("SPARK-59277: TRANSFORM VARCHAR overflow without SerDe raises 
EXCEED_LIMIT_LENGTH") {
+    assume(TestUtils.testCommandAvailable("/bin/bash"))
+    withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "true") {
+      val input = Seq("abcdefgh").toDF("v")
+      val exception = intercept[Exception] {
+        QueryTest.executePlan(
+          createScriptTransformationExec(
+            script = "cat",
+            output = Seq(AttributeReference("v", VarcharType(5))()),
+            child = input.queryExecution.sparkPlan,
+            ioschema = defaultIOSchema),
+          spark.sqlContext)
+      }
+      val runtimeException = exception match {
+        case s: org.apache.spark.SparkRuntimeException => s
+        case other =>
+          other.getCause.asInstanceOf[org.apache.spark.SparkRuntimeException]
+      }
+      checkError(
+        exception = runtimeException,
+        condition = "EXCEED_LIMIT_LENGTH",
+        parameters = Map("limit" -> "5"))
+    }
+  }
+
+  test("SPARK-59277: TRANSFORM converts nested CHAR/VARCHAR without SerDe") {
+    assume(TestUtils.testCommandAvailable("/bin/bash"))
+    withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "true") {
+      Seq(
+        ("""["ab"]""", ArrayType(CharType(4)), Row(Seq("ab  "))),
+        ("""["xy"]""", ArrayType(VarcharType(4)), Row(Seq("xy"))),
+        (
+          """{"1":"ab"}""",
+          MapType(IntegerType, CharType(4)),
+          Row(Map(1 -> "ab  "))),
+        (
+          """{"1":{"2":"ab"}}""",
+          MapType(IntegerType, MapType(IntegerType, CharType(4))),
+          Row(Map(1 -> Map(2 -> "ab  ")))),
+        (
+          """[{"1":"ab"}]""",
+          ArrayType(MapType(IntegerType, CharType(4))),
+          Row(Seq(Map(1 -> "ab  ")))),
+        (
+          """{"m":{"1":"ab"}}""",
+          StructType(Seq(StructField("m", MapType(IntegerType, CharType(4))))),
+          Row(Row(Map(1 -> "ab  ")))),
+        (
+          """{"value":"xy"}""",
+          StructType(Seq(StructField("value", CharType(5)))),
+          Row(Row("xy   "))),
+        ("""[null]""", ArrayType(CharType(4)), Row(Seq(null))),
+        (
+          """{"1":null}""",
+          MapType(IntegerType, CharType(4)),
+          Row(Map(1 -> null))),
+        (
+          """{"value":null}""",
+          StructType(Seq(StructField("value", CharType(5)))),
+          Row(Row(null))),
+        (
+          """{"ab":1}""",
+          MapType(CharType(4), IntegerType),
+          Row(Map("ab  " -> 1)))).foreach { case (json, dataType, expected) =>
+        val input = Seq(json).toDF("value")
+        checkAnswer(
+          input,
+          (child: SparkPlan) => createScriptTransformationExec(
+            script = "cat",
+            output = Seq(AttributeReference("value", dataType)()),
+            child = child,
+            ioschema = defaultIOSchema),
+          Seq(expected))
+      }
+    }
+    assert(uncaughtExceptionHandler.exception.isEmpty)
+  }
+
+  test("SPARK-59277: TRANSFORM nested CHAR/VARCHAR overflow without SerDe") {
+    assume(TestUtils.testCommandAvailable("/bin/bash"))
+    withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "true") {
+      Seq(
+        (ArrayType(CharType(4)), """["abcdef"]"""),
+        (MapType(IntegerType, CharType(4)), """{"1":"abcdef"}"""),
+        (
+          MapType(IntegerType, MapType(IntegerType, CharType(4))),
+          """{"1":{"2":"abcdef"}}"""),
+        (
+          StructType(Seq(StructField("value", VarcharType(4)))),
+          """{"value":"abcdef"}""")).foreach { case (dataType, json) =>
+        val input = Seq(json).toDF("value")
+        val exception = intercept[Exception] {
+          QueryTest.executePlan(
+            createScriptTransformationExec(
+              script = "cat",
+              output = Seq(AttributeReference("value", dataType)()),
+              child = input.queryExecution.sparkPlan,
+              ioschema = defaultIOSchema),
+            spark.sqlContext)
+        }
+        val runtimeException = exception match {
+          case s: org.apache.spark.SparkRuntimeException => s
+          case other =>
+            other.getCause.asInstanceOf[org.apache.spark.SparkRuntimeException]
+        }
+        checkError(
+          exception = runtimeException,
+          condition = "EXCEED_LIMIT_LENGTH",
+          parameters = Map("limit" -> "4"))
+      }
+    }
+  }
+
+  test("SPARK-59277: malformed nested CHAR JSON without SerDe returns null") {
+    assume(TestUtils.testCommandAvailable("/bin/bash"))
+    withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "true") {
+      val input = Seq("""{"1":""").toDF("value")
+      checkAnswer(
+        input,
+        (child: SparkPlan) => createScriptTransformationExec(
+          script = "cat",
+          output = Seq(
+            AttributeReference("value", MapType(IntegerType, CharType(4)))()),
+          child = child,
+          ioschema = defaultIOSchema),
+        Seq(Row(null)))
+    }
+    assert(uncaughtExceptionHandler.exception.isEmpty)
+  }
+
+  test("SPARK-59277: TRANSFORM validates restored JSON map keys without 
SerDe") {
+    assume(TestUtils.testCommandAvailable("/bin/bash"))
+    withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "true") {
+      val mapType = MapType(IntegerType, CharType(4))
+      Seq(
+        ("""{"1":"ab"}""", mapType, Row(Map(1 -> "ab  "))),
+        ("""{"not-an-int":"ab"}""", mapType, Row(null)),
+        ("""{"1":"a","01":"b"}""", mapType, Row(null)),
+        (
+          """[{"not-an-int":"ab"}]""",
+          ArrayType(mapType),
+          Row(null)),
+        (
+          """{"m":{"1":"a","01":"b"}}""",
+          StructType(Seq(StructField("m", mapType))),
+          Row(null))).foreach { case (json, dataType, expected) =>
+        val input = Seq(json).toDF("value")
+        checkAnswer(
+          input,
+          (child: SparkPlan) => createScriptTransformationExec(
+            script = "cat",
+            output = Seq(AttributeReference("value", dataType)()),
+            child = child,
+            ioschema = defaultIOSchema),
+          Seq(expected))
+      }
+    }
+    assert(uncaughtExceptionHandler.exception.isEmpty)
+  }
+
+  test("SPARK-59277: colliding map key followed by valid row without SerDe") {
+    assume(TestUtils.testCommandAvailable("/bin/bash"))
+    withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "true") {
+      val mapType = MapType(IntegerType, CharType(4))
+      // Row 1 has duplicate converted keys (1 and 01 both cast to 1).
+      // Row 2 is valid. Both rows are in the same partition.
+      val input = Seq(
+        """{"1":"a","01":"b"}""",
+        """{"2":"cd"}""").toDF("value").coalesce(1)
+      checkAnswer(
+        input,
+        (child: SparkPlan) => createScriptTransformationExec(
+          script = "cat",
+          output = Seq(AttributeReference("value", mapType)()),
+          child = child,
+          ioschema = defaultIOSchema),
+        Seq(Row(null), Row(Map(2 -> "cd  "))))
+    }
+    assert(uncaughtExceptionHandler.exception.isEmpty)
+  }
+
+  test("SPARK-59277: colliding CHAR map keys without SerDe follow 
MAP_KEY_DEDUP_POLICY") {
+    assume(TestUtils.testCommandAvailable("/bin/bash"))
+    val mapType = MapType(CharType(4), IntegerType)
+    val json = """{"a":1,"a ":2}"""
+    withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "true") {
+      val input = Seq(json).toDF("value")
+      val exception = intercept[Exception] {
+        QueryTest.executePlan(
+          createScriptTransformationExec(
+            script = "cat",
+            output = Seq(AttributeReference("value", mapType)()),
+            child = input.queryExecution.sparkPlan,
+            ioschema = defaultIOSchema),
+          spark.sqlContext)
+      }
+      val runtimeException = Iterator.iterate[Throwable](exception)(_.getCause)
+        .takeWhile(_ != null)
+        .collectFirst {
+          case s: org.apache.spark.SparkRuntimeException
+            if s.getCondition == "DUPLICATED_MAP_KEY" => s
+        }.getOrElse {
+          fail(s"expected DUPLICATED_MAP_KEY, got $exception")
+        }
+      checkError(
+        exception = runtimeException,
+        condition = "DUPLICATED_MAP_KEY",
+        parameters = Map(
+          "key" -> "a   ",
+          "mapKeyDedupPolicy" -> "\"spark.sql.mapKeyDedupPolicy\""))
+    }
+    assert(uncaughtExceptionHandler.exception.isEmpty)
+  }
+
+  test("SPARK-59683: preserve-only CHAR/VARCHAR without SerDe stays 
unsupported") {
+    assume(TestUtils.testCommandAvailable("/bin/bash"))
+    withSQLConf(
+        SQLConf.PRESERVE_CHAR_VARCHAR_TYPE_INFO.key -> "true",
+        SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "false") {
+      val input = Seq("ab").toDF("c")
+      val exception = intercept[Exception] {
+        QueryTest.executePlan(
+          createScriptTransformationExec(
+            script = "cat",
+            output = Seq(AttributeReference("c", CharType(4))()),
+            child = input.queryExecution.sparkPlan,
+            ioschema = defaultIOSchema),
+          spark.sqlContext)
+      }
+      checkTransformWithoutSerdeUnsupportedType(exception, "\"CHAR(4)\"")
+    }
+  }
+
+  test("SPARK-59683: bound CHAR/VARCHAR mode survives execution conf change") {
+    assume(TestUtils.testCommandAvailable("/bin/bash"))
+    val padded = Seq("ab").toDF("c")
+    val enabledPlan = withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key 
-> "true") {
+      createScriptTransformationExec(
+        script = "cat",
+        output = Seq(AttributeReference("c", CharType(4))()),
+        child = padded.queryExecution.sparkPlan,
+        ioschema = defaultIOSchema)
+    }
+    withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "false") {
+      assert(QueryTest.executePlan(enabledPlan, spark.sqlContext) === 
Seq(Row("ab  ")))
+    }
+
+    val overflow = Seq("abcdef").toDF("c")
+    val overflowPlan = withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key 
-> "true") {
+      createScriptTransformationExec(
+        script = "cat",
+        output = Seq(AttributeReference("c", CharType(4))()),
+        child = overflow.queryExecution.sparkPlan,
+        ioschema = defaultIOSchema)
+    }
+    withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "false") {
+      val exception = intercept[Exception] {
+        QueryTest.executePlan(overflowPlan, spark.sqlContext)
+      }
+      val runtimeException = exception match {
+        case s: org.apache.spark.SparkRuntimeException => s
+        case other =>
+          other.getCause.asInstanceOf[org.apache.spark.SparkRuntimeException]
+      }
+      checkError(
+        exception = runtimeException,
+        condition = "EXCEED_LIMIT_LENGTH",
+        parameters = Map("limit" -> "4"))
+    }
+
+    val disabledPlan = withSQLConf(
+        SQLConf.PRESERVE_CHAR_VARCHAR_TYPE_INFO.key -> "true",
+        SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "false") {
+      createScriptTransformationExec(
+        script = "cat",
+        output = Seq(AttributeReference("c", CharType(4))()),
+        child = padded.queryExecution.sparkPlan,
+        ioschema = defaultIOSchema)
+    }
+    withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "true") {
+      val exception = intercept[Exception] {
+        QueryTest.executePlan(disabledPlan, spark.sqlContext)
+      }
+      checkTransformWithoutSerdeUnsupportedType(exception, "\"CHAR(4)\"")
+    }
+    assert(uncaughtExceptionHandler.exception.isEmpty)
+  }
+
   test("script transformation should not swallow errors from upstream 
operators (no serde)") {
     assume(TestUtils.testCommandAvailable("/bin/bash"))
 
diff --git 
a/sql/core/src/test/scala/org/apache/spark/sql/execution/SparkScriptTransformationSuite.scala
 
b/sql/core/src/test/scala/org/apache/spark/sql/execution/SparkScriptTransformationSuite.scala
index dbb8e9697089..692302e3af36 100644
--- 
a/sql/core/src/test/scala/org/apache/spark/sql/execution/SparkScriptTransformationSuite.scala
+++ 
b/sql/core/src/test/scala/org/apache/spark/sql/execution/SparkScriptTransformationSuite.scala
@@ -18,8 +18,10 @@
 package org.apache.spark.sql.execution
 
 import org.apache.spark.TestUtils
+import org.apache.spark.sql.Row
 import org.apache.spark.sql.catalyst.expressions.Attribute
 import org.apache.spark.sql.catalyst.parser.ParseException
+import org.apache.spark.sql.internal.SQLConf
 import org.apache.spark.sql.test.SharedSparkSession
 
 class SparkScriptTransformationSuite extends BaseScriptTransformationSuite 
with SharedSparkSession {
@@ -40,6 +42,60 @@ class SparkScriptTransformationSuite extends 
BaseScriptTransformationSuite with
     )
   }
 
+  test("SPARK-59683: TRANSFORM view keeps bound CHAR/VARCHAR mode") {
+    assume(TestUtils.testCommandAvailable("/bin/bash"))
+    withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "true") {
+      withView("v") {
+        sql(
+          """CREATE VIEW v AS
+            |SELECT TRANSFORM('ab') USING 'cat' AS (c CHAR(4))
+            |FROM VALUES (1) input(dummy)""".stripMargin)
+        withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "false") {
+          checkAnswer(sql("SELECT * FROM v"), Row("ab  "))
+        }
+      }
+    }
+    withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "true") {
+      withView("v_overflow") {
+        sql(
+          """CREATE VIEW v_overflow AS
+            |SELECT TRANSFORM('abcdef') USING 'cat' AS (c CHAR(4))
+            |FROM VALUES (1) input(dummy)""".stripMargin)
+        withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "false") {
+          val exception = intercept[Exception] {
+            sql("SELECT * FROM v_overflow").collect()
+          }
+          val runtimeException = exception match {
+            case s: org.apache.spark.SparkRuntimeException => s
+            case other =>
+              
other.getCause.asInstanceOf[org.apache.spark.SparkRuntimeException]
+          }
+          checkError(
+            exception = runtimeException,
+            condition = "EXCEED_LIMIT_LENGTH",
+            parameters = Map("limit" -> "4"))
+        }
+      }
+    }
+    withSQLConf(
+        SQLConf.PRESERVE_CHAR_VARCHAR_TYPE_INFO.key -> "true",
+        SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "false") {
+      withView("v_disabled") {
+        sql(
+          """CREATE VIEW v_disabled AS
+            |SELECT TRANSFORM('ab') USING 'cat' AS (c CHAR(4))
+            |FROM VALUES (1) input(dummy)""".stripMargin)
+        withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "true") {
+          val exception = intercept[Exception] {
+            sql("SELECT * FROM v_disabled").collect()
+          }
+          checkTransformWithoutSerdeUnsupportedType(exception, "\"CHAR(4)\"")
+        }
+      }
+    }
+    assert(uncaughtExceptionHandler.exception.isEmpty)
+  }
+
   test("SPARK-32106: TRANSFORM with serde without hive should throw 
exception") {
     assume(TestUtils.testCommandAvailable("/bin/bash"))
     withTempView("v") {
diff --git 
a/sql/hive/src/main/scala/org/apache/spark/sql/hive/HiveInspectors.scala 
b/sql/hive/src/main/scala/org/apache/spark/sql/hive/HiveInspectors.scala
index b8758407877f..1eb4b48e8338 100644
--- a/sql/hive/src/main/scala/org/apache/spark/sql/hive/HiveInspectors.scala
+++ b/sql/hive/src/main/scala/org/apache/spark/sql/hive/HiveInspectors.scala
@@ -942,6 +942,24 @@ private[hive] trait HiveInspectors {
         unwrapperFor(objectInspector)
     }
 
+  /**
+   * In-place unwrapper that uses DataType-aware conversion when the target
+   * contains CHAR/VARCHAR or nanosecond timestamps. Other fields keep the
+   * specialized primitive setters from the field-only overload.
+   */
+  def unwrapperFor(
+      field: HiveStructField,
+      dataType: DataType): (Any, InternalRow, Int) => Unit = {
+    if (CharVarcharUtils.hasCharVarchar(dataType) ||
+        dataType.existsRecursively(_.isInstanceOf[AnyTimestampNanoType])) {
+      val unwrapper = unwrapperFor(field.getFieldObjectInspector, dataType)
+      (value: Any, row: InternalRow, ordinal: Int) =>
+        row.update(ordinal, unwrapper(value))
+    } else {
+      unwrapperFor(field)
+    }
+  }
+
   /**
    * Builds unwrappers ahead of time according to object inspector
    * types to avoid pattern matching and branching costs per row.
diff --git 
a/sql/hive/src/main/scala/org/apache/spark/sql/hive/execution/HiveScriptTransformationExec.scala
 
b/sql/hive/src/main/scala/org/apache/spark/sql/hive/execution/HiveScriptTransformationExec.scala
index de2d15415837..c53949889611 100644
--- 
a/sql/hive/src/main/scala/org/apache/spark/sql/hive/execution/HiveScriptTransformationExec.scala
+++ 
b/sql/hive/src/main/scala/org/apache/spark/sql/hive/execution/HiveScriptTransformationExec.scala
@@ -27,6 +27,7 @@ import org.apache.hadoop.conf.Configuration
 import org.apache.hadoop.hive.ql.exec.{RecordReader, RecordWriter}
 import org.apache.hadoop.hive.serde.serdeConstants
 import org.apache.hadoop.hive.serde2.AbstractSerDe
+import org.apache.hadoop.hive.serde2.`lazy`.LazySimpleSerDe
 import org.apache.hadoop.hive.serde2.objectinspector._
 import org.apache.hadoop.io.Writable
 
@@ -36,7 +37,7 @@ import org.apache.spark.sql.catalyst.expressions._
 import org.apache.spark.sql.execution._
 import org.apache.spark.sql.hive.HiveInspectors
 import org.apache.spark.sql.hive.HiveShim._
-import org.apache.spark.sql.types.DataType
+import org.apache.spark.sql.types._
 import org.apache.spark.util.{CircularBuffer, Utils}
 
 /**
@@ -75,7 +76,9 @@ private[hive] case class HiveScriptTransformationExec(
       val mutableRow = new SpecificInternalRow(output.map(_.dataType))
 
       @transient
-      lazy val unwrappers = 
outputSoi.getAllStructFieldRefs.asScala.map(unwrapperFor)
+      lazy val unwrappers = 
outputSoi.getAllStructFieldRefs.asScala.zip(output).map {
+        case (field, attr) => unwrapperFor(field, attr.dataType)
+      }
 
       override def hasNext: Boolean = {
         if (completed) {
@@ -256,7 +259,9 @@ object HiveScriptIOSchema extends HiveInspectors {
       output: Seq[Attribute]): Option[(AbstractSerDe, StructObjectInspector)] 
= {
     ioschema.outputSerdeClass.map { serdeClass =>
       val (columns, columnTypes) = parseAttrs(output)
-      val serde = initSerDe(serdeClass, columns, columnTypes, 
ioschema.outputSerdeProps)
+      val serdeTypes = outputTypesForSerDe(
+        serdeClass, columnTypes, ioschema.standardCharVarcharSemantics)
+      val serde = initSerDe(serdeClass, columns, serdeTypes, 
ioschema.outputSerdeProps)
       val structObjectInspector = 
serde.getObjectInspector().asInstanceOf[StructObjectInspector]
       (serde, structObjectInspector)
     }
@@ -268,6 +273,25 @@ object HiveScriptIOSchema extends HiveInspectors {
     (columns, columnTypes)
   }
 
+  /**
+   * Hive LazySimpleSerDe CHAR/VARCHAR types truncate on deserialize. Under 
standard
+   * semantics, map them to STRING so Spark applies length checks. Only 
LazySimpleSerDe
+   * and subclasses get this rewrite; other SerDes keep the declared schema.
+   * preserveCharVarcharTypeInfo without standard semantics is unchanged.
+   */
+  private def outputTypesForSerDe(
+      serdeClassName: String,
+      columnTypes: Seq[DataType],
+      standardCharVarcharSemantics: Boolean): Seq[DataType] = {
+    val serdeClass = Utils.classForName[AbstractSerDe](serdeClassName)
+    if (standardCharVarcharSemantics &&
+        classOf[LazySimpleSerDe].isAssignableFrom(serdeClass)) {
+      columnTypes.map(ScriptTransformationIOSchema.toUnboundedStringType)
+    } else {
+      columnTypes
+    }
+  }
+
   def initSerDe(
       serdeClassName: String,
       columns: Seq[String],
diff --git 
a/sql/hive/src/test/scala/org/apache/spark/sql/hive/execution/HiveScriptTransformationSuite.scala
 
b/sql/hive/src/test/scala/org/apache/spark/sql/hive/execution/HiveScriptTransformationSuite.scala
index b1ff05b8c1b0..a4df01db29f2 100644
--- 
a/sql/hive/src/test/scala/org/apache/spark/sql/hive/execution/HiveScriptTransformationSuite.scala
+++ 
b/sql/hive/src/test/scala/org/apache/spark/sql/hive/execution/HiveScriptTransformationSuite.scala
@@ -20,8 +20,15 @@ package org.apache.spark.sql.hive.execution
 import java.sql.Timestamp
 import java.time.{Duration, Period}
 import java.time.temporal.ChronoUnit
+import java.util.{Arrays, Properties}
 
+import org.apache.hadoop.conf.Configuration
+import org.apache.hadoop.hive.serde.serdeConstants
+import org.apache.hadoop.hive.serde2.{AbstractSerDe, SerDeStats}
 import org.apache.hadoop.hive.serde2.`lazy`.LazySimpleSerDe
+import org.apache.hadoop.hive.serde2.objectinspector.{ObjectInspector, 
ObjectInspectorFactory}
+import 
org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory
+import org.apache.hadoop.io.{Text, Writable}
 import org.scalatest.exceptions.TestFailedException
 
 import org.apache.spark.{SparkException, TestUtils}
@@ -31,6 +38,7 @@ import org.apache.spark.sql.catalyst.util.DateTimeConstants
 import org.apache.spark.sql.execution._
 import org.apache.spark.sql.functions._
 import org.apache.spark.sql.hive.test.TestHiveSingleton
+import org.apache.spark.sql.internal.SQLConf
 import org.apache.spark.sql.types._
 import org.apache.spark.sql.types.DayTimeIntervalType._
 import org.apache.spark.sql.types.YearMonthIntervalType._
@@ -59,7 +67,7 @@ class HiveScriptTransformationSuite extends 
BaseScriptTransformationSuite with T
     )
   }
 
-  private val hiveIOSchema: ScriptTransformationIOSchema = {
+  private def hiveIOSchema: ScriptTransformationIOSchema = {
     defaultIOSchema.copy(
       inputSerdeClass = Some(classOf[LazySimpleSerDe].getCanonicalName),
       outputSerdeClass = Some(classOf[LazySimpleSerDe].getCanonicalName)
@@ -372,6 +380,227 @@ class HiveScriptTransformationSuite extends 
BaseScriptTransformationSuite with T
     }
   }
 
+  test("SPARK-59277: TRANSFORM supports nested collated CHAR/VARCHAR with Hive 
SerDe") {
+    assume(TestUtils.testCommandAvailable("/bin/bash"))
+    withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "true") {
+      val query = sql(
+        """
+          |SELECT TRANSFORM(
+          |  array(CAST('ab' AS CHAR(4) COLLATE UTF8_LCASE)),
+          |  named_struct('value', CAST('xyz' AS VARCHAR(6) COLLATE 
UNICODE_CI)))
+          |USING 'cat'
+          |AS (
+          |  chars ARRAY<CHAR(4) COLLATE UTF8_LCASE>,
+          |  nested STRUCT<value: VARCHAR(6) COLLATE UNICODE_CI>)
+          |FROM VALUES (1) input(dummy)
+          |""".stripMargin)
+      assert(query.schema.map(_.dataType) === Seq(
+        ArrayType(CharType(4, "UTF8_LCASE")),
+        StructType(Seq(StructField("value", VarcharType(6, "UNICODE_CI"))))))
+      checkAnswer(query, Row(Seq("ab  "), Row("xyz")))
+    }
+  }
+
+  test("SPARK-59277: TRANSFORM CHAR overflow with Hive SerDe raises 
EXCEED_LIMIT_LENGTH") {
+    assume(TestUtils.testCommandAvailable("/bin/bash"))
+    withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "true") {
+      val exception = intercept[Exception] {
+        sql(
+          """
+            |SELECT TRANSFORM('abcdef')
+            |USING 'cat'
+            |AS (c CHAR(4))
+            |FROM VALUES (1) input(dummy)
+            |""".stripMargin).collect()
+      }
+      val runtimeException = exception match {
+        case s: org.apache.spark.SparkRuntimeException => s
+        case other =>
+          other.getCause.asInstanceOf[org.apache.spark.SparkRuntimeException]
+      }
+      checkError(
+        exception = runtimeException,
+        condition = "EXCEED_LIMIT_LENGTH",
+        parameters = Map("limit" -> "4"))
+    }
+  }
+
+  test("SPARK-59277: TRANSFORM VARCHAR overflow with Hive SerDe raises 
EXCEED_LIMIT_LENGTH") {
+    assume(TestUtils.testCommandAvailable("/bin/bash"))
+    withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "true") {
+      val exception = intercept[Exception] {
+        sql(
+          """
+            |SELECT TRANSFORM('abcdefgh')
+            |USING 'cat'
+            |AS (v VARCHAR(5))
+            |FROM VALUES (1) input(dummy)
+            |""".stripMargin).collect()
+      }
+      val runtimeException = exception match {
+        case s: org.apache.spark.SparkRuntimeException => s
+        case other =>
+          other.getCause.asInstanceOf[org.apache.spark.SparkRuntimeException]
+      }
+      checkError(
+        exception = runtimeException,
+        condition = "EXCEED_LIMIT_LENGTH",
+        parameters = Map("limit" -> "5"))
+    }
+  }
+
+  test("SPARK-59277: nested CHAR/VARCHAR overflow with Hive SerDe") {
+    assume(TestUtils.testCommandAvailable("/bin/bash"))
+    withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "true") {
+      Seq(
+        """
+          |SELECT TRANSFORM(array('abcdef'))
+          |USING 'cat'
+          |AS (value ARRAY<CHAR(4)>)
+          |FROM VALUES (1) input(dummy)
+          |""".stripMargin,
+        """
+          |SELECT TRANSFORM(named_struct('value', 'abcdef'))
+          |USING 'cat'
+          |AS (value STRUCT<value: VARCHAR(4)>)
+          |FROM VALUES (1) input(dummy)
+          |""".stripMargin).foreach { query =>
+        val exception = intercept[Exception] {
+          sql(query).collect()
+        }
+        val runtimeException = exception match {
+          case s: org.apache.spark.SparkRuntimeException => s
+          case other =>
+            other.getCause.asInstanceOf[org.apache.spark.SparkRuntimeException]
+        }
+        checkError(
+          exception = runtimeException,
+          condition = "EXCEED_LIMIT_LENGTH",
+          parameters = Map("limit" -> "4"))
+      }
+    }
+  }
+
+  test("SPARK-59277: output SerDe CHAR/VARCHAR rewrite is 
LazySimpleSerDe-only") {
+    withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "true") {
+      val output = Seq(
+        AttributeReference("c", CharType(4))(),
+        AttributeReference("v", VarcharType(5))(),
+        AttributeReference("nested", ArrayType(CharType(4)))())
+
+      val (_, lazySoi) = HiveScriptIOSchema.initOutputSerDe(hiveIOSchema, 
output).get
+      
assert(lazySoi.getAllStructFieldRefs.get(0).getFieldObjectInspector.getTypeName 
===
+        "string")
+      
assert(lazySoi.getAllStructFieldRefs.get(1).getFieldObjectInspector.getTypeName 
===
+        "string")
+      
assert(lazySoi.getAllStructFieldRefs.get(2).getFieldObjectInspector.getTypeName 
===
+        "array<string>")
+
+      val subclassSchema = hiveIOSchema.copy(
+        outputSerdeClass = Some(classOf[TestLazySimpleSerDe].getCanonicalName))
+      val (_, subclassSoi) = 
HiveScriptIOSchema.initOutputSerDe(subclassSchema, output).get
+      
assert(subclassSoi.getAllStructFieldRefs.get(0).getFieldObjectInspector.getTypeName
 ===
+        "string")
+
+      SchemaCapturingSerDe.lastColumnTypes = null
+      val customSchema = defaultIOSchema.copy(
+        outputSerdeClass = 
Some(classOf[SchemaCapturingSerDe].getCanonicalName))
+      HiveScriptIOSchema.initOutputSerDe(customSchema, output)
+      val captured = SchemaCapturingSerDe.lastColumnTypes
+      assert(captured.contains("char(4)"))
+      assert(captured.contains("varchar(5)"))
+      assert(captured.contains("array<char(4)>"))
+    }
+  }
+
+  test("SPARK-59683: preserve-only does not rewrite LazySimpleSerDe 
CHAR/VARCHAR") {
+    withSQLConf(
+        SQLConf.PRESERVE_CHAR_VARCHAR_TYPE_INFO.key -> "true",
+        SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "false") {
+      val output = Seq(
+        AttributeReference("c", CharType(4))(),
+        AttributeReference("v", VarcharType(5))(),
+        AttributeReference("nested", ArrayType(CharType(4)))())
+      val (_, soi) = HiveScriptIOSchema.initOutputSerDe(hiveIOSchema, 
output).get
+      
assert(soi.getAllStructFieldRefs.get(0).getFieldObjectInspector.getTypeName ===
+        "char(4)")
+      
assert(soi.getAllStructFieldRefs.get(1).getFieldObjectInspector.getTypeName ===
+        "varchar(5)")
+      
assert(soi.getAllStructFieldRefs.get(2).getFieldObjectInspector.getTypeName ===
+        "array<char(4)>")
+    }
+  }
+
+  test("SPARK-59683: bound Hive SerDe CHAR/VARCHAR mode survives conf change") 
{
+    val output = Seq(
+      AttributeReference("c", CharType(4))(),
+      AttributeReference("v", VarcharType(5))(),
+      AttributeReference("nested", ArrayType(CharType(4)))())
+    val enabledSchema = 
withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "true") {
+      hiveIOSchema
+    }
+    withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "false") {
+      val (_, soi) = HiveScriptIOSchema.initOutputSerDe(enabledSchema, 
output).get
+      
assert(soi.getAllStructFieldRefs.get(0).getFieldObjectInspector.getTypeName ===
+        "string")
+      
assert(soi.getAllStructFieldRefs.get(1).getFieldObjectInspector.getTypeName ===
+        "string")
+      
assert(soi.getAllStructFieldRefs.get(2).getFieldObjectInspector.getTypeName ===
+        "array<string>")
+    }
+    val disabledSchema = withSQLConf(
+        SQLConf.PRESERVE_CHAR_VARCHAR_TYPE_INFO.key -> "true",
+        SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "false") {
+      hiveIOSchema
+    }
+    withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "true") {
+      val (_, soi) = HiveScriptIOSchema.initOutputSerDe(disabledSchema, 
output).get
+      
assert(soi.getAllStructFieldRefs.get(0).getFieldObjectInspector.getTypeName ===
+        "char(4)")
+      
assert(soi.getAllStructFieldRefs.get(1).getFieldObjectInspector.getTypeName ===
+        "varchar(5)")
+      
assert(soi.getAllStructFieldRefs.get(2).getFieldObjectInspector.getTypeName ===
+        "array<char(4)>")
+    }
+  }
+
+  test("SPARK-59683: Hive TRANSFORM view keeps bound CHAR/VARCHAR mode") {
+    assume(TestUtils.testCommandAvailable("/bin/bash"))
+    withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "true") {
+      withView("v") {
+        sql(
+          """CREATE VIEW v AS
+            |SELECT TRANSFORM('ab') USING 'cat' AS (c CHAR(4))
+            |FROM VALUES (1) input(dummy)""".stripMargin)
+        withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "false") {
+          checkAnswer(sql("SELECT * FROM v"), Row("ab  "))
+        }
+      }
+    }
+    withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "true") {
+      withView("v_overflow") {
+        sql(
+          """CREATE VIEW v_overflow AS
+            |SELECT TRANSFORM('abcdef') USING 'cat' AS (c CHAR(4))
+            |FROM VALUES (1) input(dummy)""".stripMargin)
+        withSQLConf(SQLConf.CHAR_VARCHAR_STANDARD_SEMANTICS.key -> "false") {
+          val exception = intercept[Exception] {
+            sql("SELECT * FROM v_overflow").collect()
+          }
+          val runtimeException = exception match {
+            case s: org.apache.spark.SparkRuntimeException => s
+            case other =>
+              
other.getCause.asInstanceOf[org.apache.spark.SparkRuntimeException]
+          }
+          checkError(
+            exception = runtimeException,
+            condition = "EXCEED_LIMIT_LENGTH",
+            parameters = Map("limit" -> "4"))
+        }
+      }
+    }
+  }
+
   test("SPARK-32400: TRANSFORM doesn't support 
CalendarIntervalType/UserDefinedType (hive serde)") {
     assume(TestUtils.testCommandAvailable("/bin/bash"))
     withTempView("v") {
@@ -668,3 +897,29 @@ class HiveScriptTransformationSuite extends 
BaseScriptTransformationSuite with T
     }
   }
 }
+
+class TestLazySimpleSerDe extends LazySimpleSerDe
+
+class SchemaCapturingSerDe extends AbstractSerDe {
+  override def initialize(conf: Configuration, tbl: Properties): Unit = {
+    SchemaCapturingSerDe.lastColumnTypes =
+      tbl.getProperty(serdeConstants.LIST_COLUMN_TYPES)
+  }
+
+  override def getObjectInspector: ObjectInspector =
+    ObjectInspectorFactory.getStandardStructObjectInspector(
+      Arrays.asList("col"),
+      Arrays.asList(PrimitiveObjectInspectorFactory.javaStringObjectInspector))
+
+  override def getSerializedClass: Class[_ <: Writable] = classOf[Text]
+
+  override def getSerDeStats: SerDeStats = null
+
+  override def serialize(obj: Any, inspector: ObjectInspector): Writable = null
+
+  override def deserialize(blob: Writable): AnyRef = null
+}
+
+object SchemaCapturingSerDe {
+  @volatile var lastColumnTypes: String = _
+}


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

Reply via email to