Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ statement
rowFormat? createFileFormat? locationSpec?
(TBLPROPERTIES tablePropertyList)?
(AS? query)? #createTable
| CREATE TABLE (IF NOT EXISTS)? target=tableIdentifier
LIKE source=tableIdentifier #createTableLike
| ANALYZE TABLE tableIdentifier partitionSpec? COMPUTE STATISTICS
(identifier | FOR COLUMNS identifierSeq?)? #analyze
| ALTER (TABLE | VIEW) from=tableIdentifier
Expand Down Expand Up @@ -136,10 +138,7 @@ statement
;

hiveNativeCommands
: createTableHeader LIKE tableIdentifier
rowFormat? createFileFormat? locationSpec?
(TBLPROPERTIES tablePropertyList)?
| DELETE FROM tableIdentifier (WHERE booleanExpression)?
: DELETE FROM tableIdentifier (WHERE booleanExpression)?
| TRUNCATE TABLE tableIdentifier partitionSpec?
(COLUMNS identifierList)?
| SHOW COLUMNS (FROM | IN) tableIdentifier ((FROM|IN) identifier)?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,45 @@

package org.apache.spark.sql.execution.command

import org.apache.spark.sql.{Row, SQLContext}
import org.apache.spark.sql.{AnalysisException, Row, SQLContext}
import org.apache.spark.sql.catalyst.TableIdentifier
import org.apache.spark.sql.catalyst.catalog.CatalogTable
import org.apache.spark.sql.catalyst.catalog.{CatalogTable, CatalogTableType}

/**
* A command to create a table with the same definition of the given existing table.
*
* The syntax of using this command in SQL is:
* {{{
* CREATE TABLE [IF NOT EXISTS] [db_name.]table_name
* LIKE [other_db_name.]existing_table_name
* }}}
*/
case class CreateTableLike(
targetTable: TableIdentifier,
sourceTable: TableIdentifier,
ifNotExists: Boolean) extends RunnableCommand {

override def run(sqlContext: SQLContext): Seq[Row] = {
val catalog = sqlContext.sessionState.catalog
if (!catalog.tableExists(sourceTable)) {
throw new AnalysisException(
s"Source table in CREATE TABLE LIKE does not exist: '$sourceTable'")
}
if (catalog.isTemporaryTable(sourceTable)) {
throw new AnalysisException(
s"Source table in CREATE TABLE LIKE cannot be temporary: '$sourceTable'")
}

val tableToCreate = catalog.getTableMetadata(sourceTable).copy(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of doing a copy and change some variables that we need to update, how about we create a new CatalogTable and put the stuff that we need to retain from the source table? Then it's more clear that what we retained for the CREATE TABLE LIKE command.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. But I do this to address previous comment. The main concern is that we may add more fields into CatalogTable. If so, we will need to revisit here to add these fields.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I am neutral on this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My concern is that we may add more fields into CatalogTable which we don't want to retain, like lastAccessTime, I'm not sure which way is safer too, cc @andrewor14

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your concern is correct. But I think most fields in source table should be retained. Only few exceptions like lastAccessTime.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dilipbiswal and I also hit the same issue when doing the Describe Table. At the end, we decided to avoid adding more fields into CatalogTable. Will follow the discussion in this thread. Thanks!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think things like lastAccessTime are exceptions. If we don't do copy here then we might forget to copy a new field that we add to CatalogTable.

identifier = targetTable,
tableType = CatalogTableType.MANAGED_TABLE,
createTime = System.currentTimeMillis,
lastAccessTime = -1).withNewStorage(locationUri = None)

catalog.createTable(tableToCreate, ifNotExists)
Seq.empty[Row]
}
}


// TODO: move the rest of the table commands from ddl.scala to this file
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,9 @@ class HiveCompatibilitySuite extends HiveQueryFileTest with BeforeAndAfter {
"skewjoinopt18",
"skewjoinopt9",

// This test tries to create a table like with TBLPROPERTIES clause, which we don't support.
"create_like_tbl_props",

// Index commands are not supported
"drop_index",
"drop_index_removes_partition_dirs",
Expand Down Expand Up @@ -537,7 +540,6 @@ class HiveCompatibilitySuite extends HiveQueryFileTest with BeforeAndAfter {
"count",
"cp_mj_rc",
"create_insert_outputformat",
"create_like_tbl_props",
"create_nested_type",
"create_struct_table",
"create_view_translate",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import org.apache.spark.sql.catalyst.parser._
import org.apache.spark.sql.catalyst.parser.SqlBaseParser._
import org.apache.spark.sql.catalyst.plans.logical.LogicalPlan
import org.apache.spark.sql.execution.SparkSqlAstBuilder
import org.apache.spark.sql.execution.command.CreateTable
import org.apache.spark.sql.execution.command.{CreateTable, CreateTableLike}
import org.apache.spark.sql.hive.{CreateTableAsSelect => CTAS, CreateViewAsSelect => CreateView}
import org.apache.spark.sql.hive.{HiveGenericUDTF, HiveMetastoreTypes, HiveSerDe}
import org.apache.spark.sql.hive.HiveShim.HiveFunctionWrapper
Expand Down Expand Up @@ -236,6 +236,15 @@ class HiveSqlAstBuilder extends SparkSqlAstBuilder {
}
}

/**
* Create a [[CreateTableLike]] command.
*/
override def visitCreateTableLike(ctx: CreateTableLikeContext): LogicalPlan = withOrigin(ctx) {
val targetTable = visitTableIdentifier(ctx.target)
val sourceTable = visitTableIdentifier(ctx.source)
CreateTableLike(targetTable, sourceTable, ctx.EXISTS != null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

much better!

}

/**
* Create or replace a view. This creates a [[CreateViewAsSelect]] command.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import org.apache.spark.sql.catalyst.expressions.JsonTuple
import org.apache.spark.sql.catalyst.parser.ParseException
import org.apache.spark.sql.catalyst.plans.PlanTest
import org.apache.spark.sql.catalyst.plans.logical.{Generate, ScriptTransformation}
import org.apache.spark.sql.execution.command.CreateTable
import org.apache.spark.sql.execution.command.{CreateTable, CreateTableLike}
import org.apache.spark.sql.hive.execution.{HiveNativeCommand, HiveSqlParser}

class HiveDDLCommandSuite extends PlanTest {
Expand Down Expand Up @@ -564,4 +564,26 @@ class HiveDDLCommandSuite extends PlanTest {
assertUnsupported("MSCK REPAIR TABLE tab1")
}

test("create table like") {
val v1 = "CREATE TABLE table1 LIKE table2"
val (target, source, exists) = parser.parsePlan(v1).collect {
case CreateTableLike(t, s, allowExisting) => (t, s, allowExisting)
}.head
assert(exists == false)
assert(target.database.isEmpty)
assert(target.table == "table1")
assert(source.database.isEmpty)
assert(source.table == "table2")

val v2 = "CREATE TABLE IF NOT EXISTS table1 LIKE table2"
val (target2, source2, exists2) = parser.parsePlan(v2).collect {
case CreateTableLike(t, s, allowExisting) => (t, s, allowExisting)
}.head
assert(exists2)
assert(target2.database.isEmpty)
assert(target2.table == "table1")
assert(source2.database.isEmpty)
assert(source2.table == "table2")
}

}