Skip to content

Commit b4844ee

Browse files
viiryacloud-fan
authored andcommitted
[SPARK-29517][SQL] TRUNCATE TABLE should look up catalog/table like v2 commands
### What changes were proposed in this pull request? Add TruncateTableStatement and make TRUNCATE TABLE go through the same catalog/table resolution framework of v2 commands. ### Why are the changes needed? It's important to make all the commands have the same table resolution behavior, to avoid confusing end-users. e.g. ``` USE my_catalog DESC t // success and describe the table t from my_catalog TRUNCATE TABLE t // report table not found as there is no table t in the session catalog ``` ### Does this PR introduce any user-facing change? yes. When running TRUNCATE TABLE, Spark fails the command if the current catalog is set to a v2 catalog, or the table name specified a v2 catalog. ### How was this patch tested? Unit tests. Closes #26174 from viirya/SPARK-29517. Authored-by: Liang-Chi Hsieh <viirya@gmail.com> Signed-off-by: Wenchen Fan <wenchen@databricks.com>
1 parent bb49c80 commit b4844ee

File tree

7 files changed

+61
-16
lines changed

7 files changed

+61
-16
lines changed

sql/catalyst/src/main/antlr4/org/apache/spark/sql/catalyst/parser/SqlBase.g4

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ statement
211211
| CLEAR CACHE #clearCache
212212
| LOAD DATA LOCAL? INPATH path=STRING OVERWRITE? INTO TABLE
213213
tableIdentifier partitionSpec? #loadData
214-
| TRUNCATE TABLE tableIdentifier partitionSpec? #truncateTable
214+
| TRUNCATE TABLE multipartIdentifier partitionSpec? #truncateTable
215215
| MSCK REPAIR TABLE multipartIdentifier #repairTable
216216
| op=(ADD | LIST) identifier .*? #manageResource
217217
| SET ROLE .*? #failNativeCommand

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2728,4 +2728,18 @@ class AstBuilder(conf: SQLConf) extends SqlBaseBaseVisitor[AnyRef] with Logging
27282728
override def visitRepairTable(ctx: RepairTableContext): LogicalPlan = withOrigin(ctx) {
27292729
RepairTableStatement(visitMultipartIdentifier(ctx.multipartIdentifier()))
27302730
}
2731+
2732+
/**
2733+
* Create a [[TruncateTableStatement]] command.
2734+
*
2735+
* For example:
2736+
* {{{
2737+
* TRUNCATE TABLE multi_part_name [PARTITION (partcol1=val1, partcol2=val2 ...)]
2738+
* }}}
2739+
*/
2740+
override def visitTruncateTable(ctx: TruncateTableContext): LogicalPlan = withOrigin(ctx) {
2741+
TruncateTableStatement(
2742+
visitMultipartIdentifier(ctx.multipartIdentifier),
2743+
Option(ctx.partitionSpec).map(visitNonOptionalPartitionSpec))
2744+
}
27312745
}

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/plans/logical/statements.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,3 +316,10 @@ case class AnalyzeColumnStatement(
316316
* A REPAIR TABLE statement, as parsed from SQL
317317
*/
318318
case class RepairTableStatement(tableName: Seq[String]) extends ParsedStatement
319+
320+
/**
321+
* A TRUNCATE TABLE statement, as parsed from SQL
322+
*/
323+
case class TruncateTableStatement(
324+
tableName: Seq[String],
325+
partitionSpec: Option[TablePartitionSpec]) extends ParsedStatement

sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/DDLParserSuite.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -961,6 +961,16 @@ class DDLParserSuite extends AnalysisTest {
961961
RepairTableStatement(Seq("a", "b", "c")))
962962
}
963963

964+
test("TRUNCATE table") {
965+
comparePlans(
966+
parsePlan("TRUNCATE TABLE a.b.c"),
967+
TruncateTableStatement(Seq("a", "b", "c"), None))
968+
969+
comparePlans(
970+
parsePlan("TRUNCATE TABLE a.b.c PARTITION(ds='2017-06-10')"),
971+
TruncateTableStatement(Seq("a", "b", "c"), Some(Map("ds" -> "2017-06-10"))))
972+
}
973+
964974
private case class TableSpec(
965975
name: Seq[String],
966976
schema: Option[StructType],

sql/core/src/main/scala/org/apache/spark/sql/catalyst/analysis/ResolveSessionCatalog.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import org.apache.spark.sql.catalyst.plans.logical._
2424
import org.apache.spark.sql.catalyst.rules.Rule
2525
import org.apache.spark.sql.connector.catalog.{CatalogManager, CatalogPlugin, LookupCatalog, TableChange, V1Table}
2626
import org.apache.spark.sql.connector.expressions.Transform
27-
import org.apache.spark.sql.execution.command.{AlterTableAddColumnsCommand, AlterTableRecoverPartitionsCommand, AlterTableSetLocationCommand, AlterTableSetPropertiesCommand, AlterTableUnsetPropertiesCommand, AnalyzeColumnCommand, AnalyzePartitionCommand, AnalyzeTableCommand, DescribeColumnCommand, DescribeTableCommand, DropTableCommand, ShowTablesCommand}
27+
import org.apache.spark.sql.execution.command.{AlterTableAddColumnsCommand, AlterTableRecoverPartitionsCommand, AlterTableSetLocationCommand, AlterTableSetPropertiesCommand, AlterTableUnsetPropertiesCommand, AnalyzeColumnCommand, AnalyzePartitionCommand, AnalyzeTableCommand, DescribeColumnCommand, DescribeTableCommand, DropTableCommand, ShowTablesCommand, TruncateTableCommand}
2828
import org.apache.spark.sql.execution.datasources.{CreateTable, DataSource}
2929
import org.apache.spark.sql.execution.datasources.v2.FileDataSourceV2
3030
import org.apache.spark.sql.internal.SQLConf
@@ -282,6 +282,12 @@ class ResolveSessionCatalog(
282282
AlterTableRecoverPartitionsCommand(
283283
v1TableName.asTableIdentifier,
284284
"MSCK REPAIR TABLE")
285+
286+
case TruncateTableStatement(tableName, partitionSpec) =>
287+
val v1TableName = parseV1Table(tableName, "TRUNCATE TABLE")
288+
TruncateTableCommand(
289+
v1TableName.asTableIdentifier,
290+
partitionSpec)
285291
}
286292

287293
private def parseV1Table(tableName: Seq[String], sql: String): Seq[String] = {

sql/core/src/main/scala/org/apache/spark/sql/execution/SparkSqlParser.scala

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -346,20 +346,6 @@ class SparkSqlAstBuilder(conf: SQLConf) extends AstBuilder(conf) {
346346
)
347347
}
348348

349-
/**
350-
* Create a [[TruncateTableCommand]] command.
351-
*
352-
* For example:
353-
* {{{
354-
* TRUNCATE TABLE tablename [PARTITION (partcol1=val1, partcol2=val2 ...)]
355-
* }}}
356-
*/
357-
override def visitTruncateTable(ctx: TruncateTableContext): LogicalPlan = withOrigin(ctx) {
358-
TruncateTableCommand(
359-
visitTableIdentifier(ctx.tableIdentifier),
360-
Option(ctx.partitionSpec).map(visitNonOptionalPartitionSpec))
361-
}
362-
363349
/**
364350
* Create a [[CreateDatabaseCommand]] command.
365351
*

sql/core/src/test/scala/org/apache/spark/sql/connector/DataSourceV2SQLSuite.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,6 +1210,28 @@ class DataSourceV2SQLSuite
12101210
}
12111211
}
12121212

1213+
test("TRUNCATE TABLE") {
1214+
val t = "testcat.ns1.ns2.tbl"
1215+
withTable(t) {
1216+
sql(
1217+
s"""
1218+
|CREATE TABLE $t (id bigint, data string)
1219+
|USING foo
1220+
|PARTITIONED BY (id)
1221+
""".stripMargin)
1222+
1223+
val e1 = intercept[AnalysisException] {
1224+
sql(s"TRUNCATE TABLE $t")
1225+
}
1226+
assert(e1.message.contains("TRUNCATE TABLE is only supported with v1 tables"))
1227+
1228+
val e2 = intercept[AnalysisException] {
1229+
sql(s"TRUNCATE TABLE $t PARTITION(id='1')")
1230+
}
1231+
assert(e2.message.contains("TRUNCATE TABLE is only supported with v1 tables"))
1232+
}
1233+
}
1234+
12131235
private def assertAnalysisError(sqlStatement: String, expectedError: String): Unit = {
12141236
val errMsg = intercept[AnalysisException] {
12151237
sql(sqlStatement)

0 commit comments

Comments
 (0)