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

pan3793 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 eb8956e5ac6a [SPARK-58260][SQL] Add config to render Hive DDL in SHOW 
CREATE TABLE for Hive tables
eb8956e5ac6a is described below

commit eb8956e5ac6aa2ee9d9c30632062352a4dd3550d
Author: Cheng Pan <[email protected]>
AuthorDate: Thu Jul 23 23:24:57 2026 +0800

    [SPARK-58260][SQL] Add config to render Hive DDL in SHOW CREATE TABLE for 
Hive tables
    
    ### What changes were proposed in this pull request?
    
    Add a new config `spark.sql.hive.showCreateTableAsSerde` (default `false`). 
When enabled,
    plain `SHOW CREATE TABLE` on a Hive serde table routes to the existing
    `ShowCreateTableAsSerdeCommand` and renders Hive DDL, instead of converting 
to Spark DDL.
    
    ### Why are the changes needed?
    
    We get some reports from customers: they are confused that `CREATE TABLE 
... STORED AS Parquet` but `SHOW CREATE TABLE ...` displays `CREATE TABLE ... 
USING Parquet`.
    
    Background: SPARK-27946 (#24938) deliberately changed plain `SHOW CREATE 
TABLE` to always emit Spark DDL, to push and simplify migration from Hive to 
Spark. `AS SERDE` was introduced in the same PR as the intended escape hatch 
for users who still need Hive DDL, and the failure modes were intentional: 
transactional Hive tables error out by design, and tables whose serde has no 
Spark data source mapping (e.g. RCFILE) are meant to be served by `SHOW CREATE 
TABLE AS SERDE`.
    
    This PR keeps that direction intact: the default behavior is unchanged, and 
`AS SERDE` remains the per-query escape hatch. It only adds a session-level 
toggle that turns "append `AS SERDE` every time" into a config, which helps 
users and integrations (e.g. BI tools, metastore sync jobs) that cannot easily 
rewrite the SQL they issue.
    
    ### Does this PR introduce _any_ user-facing change?
    
    Yes. New config `spark.sql.hive.showCreateTableAsSerde`, default `false` 
(no behavior
    change). When `true`, `SHOW CREATE TABLE` on Hive serde tables returns Hive 
DDL; data
    source tables and views are unaffected.
    
    ### How was this patch tested?
    
    Added tests to `o.a.s.sql.hive.execution.command.ShowCreateTableSuite` 
covering: Hive
    table (matches `AS SERDE` output), data source table and view (unaffected), 
RCFILE and
    transactional Hive tables (Hive DDL rendered instead of erroring).
    
    ### Was this patch authored or co-authored using generative AI tooling?
    
    Generated-by: KIMI K3
    
    Closes #57429 from pan3793/SPARK-58260.
    
    Authored-by: Cheng Pan <[email protected]>
    Signed-off-by: Cheng Pan <[email protected]>
    (cherry picked from commit 0790f4d6a4cc2fce2f3a84e78b43f41856796289)
    Signed-off-by: Cheng Pan <[email protected]>
---
 .../org/apache/spark/sql/internal/SQLConf.scala    | 13 +++
 .../catalyst/analysis/ResolveSessionCatalog.scala  |  7 +-
 .../execution/command/ShowCreateTableSuite.scala   | 93 +++++++++++++++++++++-
 3 files changed, 111 insertions(+), 2 deletions(-)

diff --git 
a/sql/catalyst/src/main/scala/org/apache/spark/sql/internal/SQLConf.scala 
b/sql/catalyst/src/main/scala/org/apache/spark/sql/internal/SQLConf.scala
index ea4c2ffc1559..965b80620968 100644
--- a/sql/catalyst/src/main/scala/org/apache/spark/sql/internal/SQLConf.scala
+++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/internal/SQLConf.scala
@@ -2079,6 +2079,16 @@ object SQLConf {
       .intConf
       .createOptional
 
+  val HIVE_TABLE_SHOW_CREATE_TABLE_AS_SERDE =
+    buildConf("spark.sql.hive.showCreateTableAsSerde")
+      .doc("Whether to render the SHOW CREATE TABLE (without `AS SERDE`) 
statement of Hive table " +
+        "in the Hive DDL format. This is only for display purpose and does not 
affect how Spark " +
+        "reads or writes Hive tables.")
+      .version("4.3.0")
+      .withBindingPolicy(ConfigBindingPolicy.NOT_APPLICABLE)
+      .booleanConf
+      .createWithDefault(false)
+
   val OPTIMIZER_METADATA_ONLY = buildConf("spark.sql.optimizer.metadataOnly")
     .internal()
     .doc("When true, enable the metadata-only query optimization that use the 
table's metadata " +
@@ -8483,6 +8493,9 @@ class SQLConf extends Serializable with Logging with 
SqlApiConf {
 
   def gatherFastStats: Boolean = getConf(GATHER_FASTSTAT)
 
+  def hiveTableShowCreateTableAsSerde: Boolean =
+    getConf(HIVE_TABLE_SHOW_CREATE_TABLE_AS_SERDE)
+
   def optimizerMetadataOnly: Boolean = getConf(OPTIMIZER_METADATA_ONLY)
 
   def wholeStageEnabled: Boolean = getConf(WHOLESTAGE_CODEGEN_ENABLED)
diff --git 
a/sql/core/src/main/scala/org/apache/spark/sql/catalyst/analysis/ResolveSessionCatalog.scala
 
b/sql/core/src/main/scala/org/apache/spark/sql/catalyst/analysis/ResolveSessionCatalog.scala
index 2ed873427e28..01f39e60afd3 100644
--- 
a/sql/core/src/main/scala/org/apache/spark/sql/catalyst/analysis/ResolveSessionCatalog.scala
+++ 
b/sql/core/src/main/scala/org/apache/spark/sql/catalyst/analysis/ResolveSessionCatalog.scala
@@ -455,7 +455,12 @@ class ResolveSessionCatalog(val catalogManager: 
CatalogManager)
         isOverwrite,
         partition)
 
-    case ShowCreateTable(ResolvedV1TableOrViewIdentifier(ident), asSerde, 
output) if asSerde =>
+    case ShowCreateTable(ResolvedTable(catalog, _, t: V1Table, _), asSerde, 
output)
+        if supportsV1Command(catalog) && (asSerde ||
+          (conf.hiveTableShowCreateTableAsSerde && 
DDLUtils.isHiveTable(t.catalogTable))) =>
+      ShowCreateTableAsSerdeCommand(t.catalogTable.identifier, output)
+
+    case ShowCreateTable(ResolvedViewIdentifier(ident), asSerde, output) if 
asSerde =>
       ShowCreateTableAsSerdeCommand(ident, output)
 
     // If target is view, force use v1 command
diff --git 
a/sql/hive/src/test/scala/org/apache/spark/sql/hive/execution/command/ShowCreateTableSuite.scala
 
b/sql/hive/src/test/scala/org/apache/spark/sql/hive/execution/command/ShowCreateTableSuite.scala
index 8e654d28cd03..7502b1815aec 100644
--- 
a/sql/hive/src/test/scala/org/apache/spark/sql/hive/execution/command/ShowCreateTableSuite.scala
+++ 
b/sql/hive/src/test/scala/org/apache/spark/sql/hive/execution/command/ShowCreateTableSuite.scala
@@ -22,7 +22,7 @@ import org.apache.spark.sql.catalyst.TableIdentifier
 import org.apache.spark.sql.catalyst.catalog.{CatalogTable, CatalogUtils}
 import org.apache.spark.sql.catalyst.util.escapeSingleQuotedString
 import org.apache.spark.sql.execution.command.v1
-import org.apache.spark.sql.internal.HiveSerDe
+import org.apache.spark.sql.internal.{HiveSerDe, SQLConf}
 
 /**
  * The class contains tests for the `SHOW CREATE TABLE` command to check V1 
Hive external
@@ -444,4 +444,95 @@ class ShowCreateTableSuite extends 
v1.ShowCreateTableSuiteBase with CommandSuite
       )
     }
   }
+
+  test("plain SHOW CREATE TABLE returns Hive DDL for hive table when the 
config is on") {
+    withNamespaceAndTable(ns, table) { t =>
+      sql(
+        s"""CREATE TABLE $t (
+           |  c1 INT COMMENT 'bla',
+           |  c2 STRING
+           |) STORED AS PARQUET
+         """.stripMargin
+      )
+      val hiveDDL = getShowCreateDDL(t, true)
+      // By default, plain SHOW CREATE TABLE converts the Hive table to Spark 
DDL.
+      assert(getShowCreateDDL(t) !== hiveDDL)
+
+      withSQLConf(SQLConf.HIVE_TABLE_SHOW_CREATE_TABLE_AS_SERDE.key -> "true") 
{
+        assert(getShowCreateDDL(t) === hiveDDL)
+        assert(hiveDDL.mkString(" ").contains(
+          "ROW FORMAT SERDE 
'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe'"))
+      }
+    }
+  }
+
+  test("plain SHOW CREATE TABLE still returns Spark DDL for data source table 
with config on") {
+    withSQLConf(SQLConf.HIVE_TABLE_SHOW_CREATE_TABLE_AS_SERDE.key -> "true") {
+      withNamespaceAndTable(ns, table) { t =>
+        sql(
+          s"""CREATE TABLE $t (
+             |  c1 STRING COMMENT 'bla',
+             |  c2 STRING
+             |) USING orc
+           """.stripMargin
+        )
+        // Should not fail with 
UNSUPPORTED_SHOW_CREATE_TABLE.ON_DATA_SOURCE_TABLE_WITH_AS_SERDE.
+        assert(getShowCreateDDL(t).contains("USING orc"))
+      }
+    }
+  }
+
+  test("plain SHOW CREATE TABLE still returns Spark DDL for view when the 
config is on") {
+    withSQLConf(SQLConf.HIVE_TABLE_SHOW_CREATE_TABLE_AS_SERDE.key -> "true") {
+      withView("v1") {
+        sql("CREATE VIEW v1 AS SELECT 1 AS c1")
+        assert(sql("SHOW CREATE TABLE 
v1").head().getString(0).startsWith("CREATE VIEW"))
+      }
+    }
+  }
+
+  test("plain SHOW CREATE TABLE returns Hive DDL for unsupported serde when 
the config is on") {
+    withSQLConf(SQLConf.HIVE_TABLE_SHOW_CREATE_TABLE_AS_SERDE.key -> "true") {
+      withTable("t1") {
+        sql(
+          s"""
+             |CREATE TABLE t1 (
+             |  c1 INT COMMENT 'bla',
+             |  c2 STRING
+             |) STORED AS RCFILE
+           """.stripMargin
+        )
+        // Without the config, this fails with
+        // UNSUPPORTED_SHOW_CREATE_TABLE.WITH_UNSUPPORTED_SERDE_CONFIGURATION 
because RCFILE
+        // cannot be mapped to a Spark data source.
+        val hiveDDL = getShowCreateDDL("t1", true)
+        assert(getShowCreateDDL("t1") === hiveDDL)
+        assert(hiveDDL.mkString(" ").contains(
+          "INPUTFORMAT 'org.apache.hadoop.hive.ql.io.RCFileInputFormat'"))
+      }
+    }
+  }
+
+  test("plain SHOW CREATE TABLE returns Hive DDL for transactional hive table 
with config on") {
+    withSQLConf(SQLConf.HIVE_TABLE_SHOW_CREATE_TABLE_AS_SERDE.key -> "true") {
+      withTable("t1") {
+        sql(
+          s"""
+             |CREATE TABLE t1 (
+             |  c1 STRING COMMENT 'bla',
+             |  c2 STRING
+             |)
+             |TBLPROPERTIES ('transactional' = 'true')
+             |CLUSTERED BY (c1) INTO 10 BUCKETS
+             |STORED AS ORC
+           """.stripMargin
+        )
+        // Without the config, this fails with
+        // UNSUPPORTED_SHOW_CREATE_TABLE.ON_TRANSACTIONAL_HIVE_TABLE.
+        val showDDL = getShowCreateDDL("t1").mkString(" ")
+        assert(showDDL.contains("ROW FORMAT SERDE"))
+        assert(showDDL.contains("'transactional' = 'true'"))
+      }
+    }
+  }
 }


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

Reply via email to