Skip to content

Commit

Permalink
[SPARK-31224][SQL] Add view support to SHOW CREATE TABLE
Browse files Browse the repository at this point in the history
### What changes were proposed in this pull request?

For now `SHOW CREATE TABLE` command doesn't support views, but `SHOW CREATE TABLE AS SERDE` supports it. Since the views syntax are the same between Hive DDL and Spark DDL, we should be able to support views in both two commands.

This is Hive syntax for creating views:

```
CREATE VIEW [IF NOT EXISTS] [db_name.]view_name [(column_name [COMMENT column_comment], ...) ]
  [COMMENT view_comment]
  [TBLPROPERTIES (property_name = property_value, ...)]
  AS SELECT ...;
```

https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL#LanguageManualDDL-CreateView

This is Spark syntax for creating views:

```
CREATE [OR REPLACE] [[GLOBAL] TEMPORARY] VIEW [IF NOT EXISTS [db_name.]view_name
    create_view_clauses
    AS query;
```
https://spark.apache.org/docs/3.0.0-preview/sql-ref-syntax-ddl-create-view.html

Looks like it is the same. We could support views in both commands.

This patch proposes to add views support to `SHOW CREATE TABLE`.

### Why are the changes needed?

To extend the view support of `SHOW CREATE TABLE`, so users can use `SHOW CREATE TABLE` to show Spark DDL for views.

### Does this PR introduce any user-facing change?

Yes. `SHOW CREATE TABLE` can be used to show Spark DDL for views.

### How was this patch tested?

Unit tests.

Closes apache#27984 from viirya/spark-view.

Authored-by: Liang-Chi Hsieh <[email protected]>
Signed-off-by: Wenchen Fan <[email protected]>
  • Loading branch information
viirya authored and Seongjin Cho committed Apr 14, 2020
1 parent 13f3247 commit 98fcdf6
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 133 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1057,6 +1057,42 @@ trait ShowCreateTableCommandBase {
protected def concatByMultiLines(iter: Iterable[String]): String = {
iter.mkString("(\n ", ",\n ", ")\n")
}

protected def showCreateView(metadata: CatalogTable, builder: StringBuilder): Unit = {
showViewDataColumns(metadata, builder)
showTableComment(metadata, builder)
showViewProperties(metadata, builder)
showViewText(metadata, builder)
}

private def showViewDataColumns(metadata: CatalogTable, builder: StringBuilder): Unit = {
if (metadata.schema.nonEmpty) {
val viewColumns = metadata.schema.map { f =>
val comment = f.getComment()
.map(escapeSingleQuotedString)
.map(" COMMENT '" + _ + "'")

// view columns shouldn't have data type info
s"${quoteIdentifier(f.name)}${comment.getOrElse("")}"
}
builder ++= concatByMultiLines(viewColumns)
}
}

private def showViewProperties(metadata: CatalogTable, builder: StringBuilder): Unit = {
val viewProps = metadata.properties.filterKeys(!_.startsWith(CatalogTable.VIEW_PREFIX))
if (viewProps.nonEmpty) {
val props = viewProps.map { case (key, value) =>
s"'${escapeSingleQuotedString(key)}' = '${escapeSingleQuotedString(value)}'"
}

builder ++= s"TBLPROPERTIES ${concatByMultiLines(props)}"
}
}

private def showViewText(metadata: CatalogTable, builder: StringBuilder): Unit = {
builder ++= metadata.viewText.mkString("AS ", "", "\n")
}
}

/**
Expand Down Expand Up @@ -1100,21 +1136,33 @@ case class ShowCreateTableCommand(table: TableIdentifier)
)
}

if (tableMetadata.tableType == VIEW) {
throw new AnalysisException("Hive view isn't supported by SHOW CREATE TABLE")
}

if ("true".equalsIgnoreCase(tableMetadata.properties.getOrElse("transactional", "false"))) {
throw new AnalysisException(
"SHOW CREATE TABLE doesn't support transactional Hive table. " +
s"Please use `SHOW CREATE TABLE ${tableMetadata.identifier} AS SERDE` " +
"to show Hive DDL instead.")
}

convertTableMetadata(tableMetadata)
if (tableMetadata.tableType == VIEW) {
tableMetadata
} else {
convertTableMetadata(tableMetadata)
}
}

val stmt = showCreateDataSourceTable(metadata)
val builder = StringBuilder.newBuilder

val stmt = if (tableMetadata.tableType == VIEW) {
builder ++= s"CREATE VIEW ${table.quotedString} "
showCreateView(metadata, builder)

builder.toString()
} else {
builder ++= s"CREATE TABLE ${table.quotedString} "

showCreateDataSourceTable(metadata, builder)
builder.toString()
}

Seq(Row(stmt))
}
Expand Down Expand Up @@ -1194,18 +1242,13 @@ case class ShowCreateTableCommand(table: TableIdentifier)
}
}

private def showCreateDataSourceTable(metadata: CatalogTable): String = {
val builder = StringBuilder.newBuilder

builder ++= s"CREATE TABLE ${table.quotedString} "
private def showCreateDataSourceTable(metadata: CatalogTable, builder: StringBuilder): Unit = {
showDataSourceTableDataColumns(metadata, builder)
showDataSourceTableOptions(metadata, builder)
showDataSourceTableNonDataColumns(metadata, builder)
showTableComment(metadata, builder)
showTableLocation(metadata, builder)
showTableProperties(metadata, builder)

builder.toString()
}
}

Expand Down Expand Up @@ -1264,10 +1307,7 @@ case class ShowCreateTableAsSerdeCommand(table: TableIdentifier)
builder ++= s"CREATE$tableTypeString ${table.quotedString}"

if (metadata.tableType == VIEW) {
showViewDataColumns(metadata, builder)
showTableComment(metadata, builder)
showViewProperties(metadata, builder)
showViewText(metadata, builder)
showCreateView(metadata, builder)
} else {
showHiveTableHeader(metadata, builder)
showTableComment(metadata, builder)
Expand All @@ -1280,35 +1320,6 @@ case class ShowCreateTableAsSerdeCommand(table: TableIdentifier)
builder.toString()
}

private def showViewDataColumns(metadata: CatalogTable, builder: StringBuilder): Unit = {
if (metadata.schema.nonEmpty) {
val viewColumns = metadata.schema.map { f =>
val comment = f.getComment()
.map(escapeSingleQuotedString)
.map(" COMMENT '" + _ + "'")

// view columns shouldn't have data type info
s"${quoteIdentifier(f.name)}${comment.getOrElse("")}"
}
builder ++= concatByMultiLines(viewColumns)
}
}

private def showViewProperties(metadata: CatalogTable, builder: StringBuilder): Unit = {
val viewProps = metadata.properties.filterKeys(!_.startsWith(CatalogTable.VIEW_PREFIX))
if (viewProps.nonEmpty) {
val props = viewProps.map { case (key, value) =>
s"'${escapeSingleQuotedString(key)}' = '${escapeSingleQuotedString(value)}'"
}

builder ++= s"TBLPROPERTIES ${concatByMultiLines(props)}"
}
}

private def showViewText(metadata: CatalogTable, builder: StringBuilder): Unit = {
builder ++= metadata.viewText.mkString("AS ", "", "\n")
}

private def showHiveTableHeader(metadata: CatalogTable, builder: StringBuilder): Unit = {
val columns = metadata.schema.filterNot { column =>
metadata.partitionColumnNames.contains(column.name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ CREATE VIEW view_SPARK_30302 (aaa, bbb)
AS SELECT a, b FROM tbl;

SHOW CREATE TABLE view_SPARK_30302 AS SERDE;

SHOW CREATE TABLE view_SPARK_30302;

DROP VIEW view_SPARK_30302;


Expand All @@ -83,6 +86,9 @@ COMMENT 'This is a comment with \'quoted text\' for view'
AS SELECT a, b FROM tbl;

SHOW CREATE TABLE view_SPARK_30302 AS SERDE;

SHOW CREATE TABLE view_SPARK_30302;

DROP VIEW view_SPARK_30302;


Expand All @@ -92,13 +98,9 @@ TBLPROPERTIES ('a' = '1', 'b' = '2')
AS SELECT a, b FROM tbl;

SHOW CREATE TABLE view_SPARK_30302 AS SERDE;
DROP VIEW view_SPARK_30302;

-- SHOW CREATE TABLE does not support view
CREATE VIEW view_SPARK_30302 (aaa, bbb)
AS SELECT a, b FROM tbl;

SHOW CREATE TABLE view_SPARK_30302;

DROP VIEW view_SPARK_30302;

DROP TABLE tbl;
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,17 @@ CREATE VIEW `default`.`view_SPARK_30302`(
AS SELECT a, b FROM tbl


-- !query
SHOW CREATE TABLE view_SPARK_30302
-- !query schema
struct<createtab_stmt:string>
-- !query output
CREATE VIEW `default`.`view_SPARK_30302` (
`aaa`,
`bbb`)
AS SELECT a, b FROM tbl


-- !query
DROP VIEW view_SPARK_30302
-- !query schema
Expand Down Expand Up @@ -331,6 +342,18 @@ COMMENT 'This is a comment with \'quoted text\' for view'
AS SELECT a, b FROM tbl


-- !query
SHOW CREATE TABLE view_SPARK_30302
-- !query schema
struct<createtab_stmt:string>
-- !query output
CREATE VIEW `default`.`view_SPARK_30302` (
`aaa` COMMENT 'comment with \'quoted text\' for aaa',
`bbb`)
COMMENT 'This is a comment with \'quoted text\' for view'
AS SELECT a, b FROM tbl


-- !query
DROP VIEW view_SPARK_30302
-- !query schema
Expand Down Expand Up @@ -363,30 +386,18 @@ TBLPROPERTIES (
AS SELECT a, b FROM tbl


-- !query
DROP VIEW view_SPARK_30302
-- !query schema
struct<>
-- !query output



-- !query
CREATE VIEW view_SPARK_30302 (aaa, bbb)
AS SELECT a, b FROM tbl
-- !query schema
struct<>
-- !query output



-- !query
SHOW CREATE TABLE view_SPARK_30302
-- !query schema
struct<>
struct<createtab_stmt:string>
-- !query output
org.apache.spark.sql.AnalysisException
Hive view isn't supported by SHOW CREATE TABLE;
CREATE VIEW `default`.`view_SPARK_30302` (
`aaa`,
`bbb`)
TBLPROPERTIES (
'a' = '1',
'b' = '2')
AS SELECT a, b FROM tbl


-- !query
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,18 +188,26 @@ abstract class ShowCreateTableSuite extends QueryTest with SQLTestUtils {
if (result.length > 1) result(0) + result(1) else result.head
}

protected def checkCreateTable(table: String): Unit = {
checkCreateTableOrView(TableIdentifier(table, Some("default")), "TABLE")
protected def checkCreateTable(table: String, serde: Boolean = false): Unit = {
checkCreateTableOrView(TableIdentifier(table, Some("default")), "TABLE", serde)
}

protected def checkCreateView(table: String): Unit = {
checkCreateTableOrView(TableIdentifier(table, Some("default")), "VIEW")
protected def checkCreateView(table: String, serde: Boolean = false): Unit = {
checkCreateTableOrView(TableIdentifier(table, Some("default")), "VIEW", serde)
}

private def checkCreateTableOrView(table: TableIdentifier, checkType: String): Unit = {
protected def checkCreateTableOrView(
table: TableIdentifier,
checkType: String,
serde: Boolean): Unit = {
val db = table.database.getOrElse("default")
val expected = spark.sharedState.externalCatalog.getTable(db, table.table)
val shownDDL = sql(s"SHOW CREATE TABLE ${table.quotedString}").head().getString(0)
val shownDDL = if (serde) {
sql(s"SHOW CREATE TABLE ${table.quotedString} AS SERDE").head().getString(0)
} else {
sql(s"SHOW CREATE TABLE ${table.quotedString}").head().getString(0)
}

sql(s"DROP $checkType ${table.quotedString}")

try {
Expand Down
Loading

0 comments on commit 98fcdf6

Please sign in to comment.