srielau commented on code in PR #58317:
URL: https://github.com/apache/spark/pull/58317#discussion_r3960964836


##########
sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/FileFormat.scala:
##########
@@ -266,6 +295,25 @@ object FileFormat {
    */
   val OPTION_RETURNING_BATCH = "returning_batch"
 
+  /**
+   * Engine-private Hadoop configuration entry that transports the analyzed 
CHAR/VARCHAR scan mode
+   * across the legacy [[FileFormat.buildReaderWithPartitionValues]] 
signature. It is written by the
+   * mode-aware overload and read by formats that honor first-class 
CHAR/VARCHAR types. This is not
+   * a public option; the authoritative state is the typed plan field and 
overload parameter.
+   */
+  val CHAR_VARCHAR_SCAN_MODE = "__spark_sql_char_varchar_scan_mode"
+
+  /** Writes the CHAR/VARCHAR scan mode into `conf` under 
[[CHAR_VARCHAR_SCAN_MODE]]. */
+  private[sql] def setCharVarcharScanMode(
+      conf: Configuration, mode: CharVarcharScanMode): Unit = {
+    conf.set(CHAR_VARCHAR_SCAN_MODE, mode.toString)
+  }
+
+  /** Reads the CHAR/VARCHAR scan mode from `conf`, or `None` if no mode was 
bridged in. */
+  private[sql] def charVarcharScanMode(conf: Configuration): 
Option[CharVarcharScanMode] = {
+    Option(conf.get(CHAR_VARCHAR_SCAN_MODE)).map(CharVarcharScanMode.fromName)

Review Comment:
   Fixed in 50940524276. FileSourceScanExec now removes the reserved bridge 
entry from the Hadoop configuration immediately after relation options are 
copied. A bound mode is then added only by 
FileFormat.buildReaderWithPartitionValues typed overload on its cloned per-call 
configuration, preserving virtual dispatch and delegating super calls. Added an 
unbound V1 ORC regression for invalid, SparkStandard, and PreserveNative caller 
values; all are ignored and the focused ORC tests pass.



##########
sql/catalyst/src/main/scala/org/apache/spark/sql/execution/datasources/v2/DataSourceV2Relation.scala:
##########
@@ -116,7 +116,10 @@ case class DataSourceV2Relation(
     catalog: Option[CatalogPlugin],
     identifier: Option[Identifier],
     options: CaseInsensitiveStringMap,
-    timeTravelSpec: Option[TimeTravelSpec] = None)
+    timeTravelSpec: Option[TimeTravelSpec] = None,
+    // Bound at analysis so sameResult / cache reuse distinguish preserve-only 
vs standard
+    // CHAR/VARCHAR scans. None means the relation was not analyzed under 
first-class types.
+    charVarcharScanMode: Option[CharVarcharScanMode] = None)

Review Comment:
   Fixed in 50940524276. Added CacheManager.recacheByV2Relation for the 
catalog-less mutation path. It compares the written and cached 
DataSourceV2Relation with only charVarcharScanMode cleared, preserving all 
existing sameResult identity while invalidating both bound variants. 
DataSourceV2Strategy now uses it only for the TableProvider branch. Added an 
end-to-end SimpleWritableDataSource append/recache regression for 
PreserveNative and SparkStandard; the focused DataSourceV2Suite test passes.



##########
sql/core/src/test/scala/org/apache/spark/sql/execution/datasources/SaveIntoDataSourceCommandSuite.scala:
##########
@@ -69,7 +70,29 @@ class SaveIntoDataSourceCommandSuite extends 
SharedSparkSession {
     saveIntoDataSource(2)
     checkAnswer(loadData, Row(0) :: Row(1) :: Nil)
 
+    spark.catalog.clearCache()
     FakeV1DataSource.data = null
+
+    // Bound modes store Some(false)/Some(true) on LogicalRelation, so recache 
must match

Review Comment:
   Fixed in 50940524276. The test comment now refers to the two bound scan 
modes instead of the obsolete Some(false)/Some(true) representation.



##########
sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/ApplyCharTypePadding.scala:
##########
@@ -51,34 +51,74 @@ object ApplyCharTypePadding extends Rule[LogicalPlan] {
   }
 
   override def apply(plan: LogicalPlan): LogicalPlan = {
+    val standardSemantics = conf.charVarcharStandardSemantics
+    val scanMode = CharVarcharScanMode(standardSemantics)
+
+    // Bind into case-class state, not a TreeNodeTag: `TreeNode.makeCopy` 
calls `copyTagsFrom`,
+    // so a tag survives canonicalization, but it does not participate in 
structural plan

Review Comment:
   Fixed in 50940524276. The comment now states only the stable reason: 
TreeNodeTag values do not participate in structural equality / sameResult, 
while the case-class field does.



-- 
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