Skip to content

Commit

Permalink
[SPARK-50541][SQL][FOLLOWUP] Migrate DESC TABLE AS JSON to v2 command
Browse files Browse the repository at this point in the history
### What changes were proposed in this pull request?

This is a follow-up of apache#49139 to use v2 command to simplify the code. Now we only need one logical plan and all the implementation is centralized to that logical plan, no need to touch other analyzer/planner rules.

### Why are the changes needed?

code simplification

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

no, this feature is not released yet.

### How was this patch tested?

update tests

### Was this patch authored or co-authored using generative AI tooling?

no

Closes apache#49466 from cloud-fan/as-json.

Authored-by: Wenchen Fan <[email protected]>
Signed-off-by: Dongjoon Hyun <[email protected]>
  • Loading branch information
cloud-fan authored and dongjoon-hyun committed Jan 15, 2025
1 parent d792865 commit 0593ac6
Show file tree
Hide file tree
Showing 14 changed files with 544 additions and 632 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3494,7 +3494,7 @@ class AstBuilder extends DataTypeAstBuilder
/**
* Create an [[UnresolvedTableOrView]] from a multi-part identifier.
*/
private def createUnresolvedTableOrView(
protected def createUnresolvedTableOrView(
ctx: IdentifierReferenceContext,
commandName: String,
allowTempView: Boolean = true): LogicalPlan = withOrigin(ctx) {
Expand Down Expand Up @@ -5198,47 +5198,6 @@ class AstBuilder extends DataTypeAstBuilder
visitLocationSpec(ctx.locationSpec))
}

/**
* Create a [[DescribeColumn]] or [[DescribeRelation]] commands.
*/
override def visitDescribeRelation(ctx: DescribeRelationContext): LogicalPlan = withOrigin(ctx) {
val isExtended = ctx.EXTENDED != null || ctx.FORMATTED != null
val asJson = ctx.JSON != null
if (asJson && !isExtended) {
val tableName = ctx.identifierReference.getText.split("\\.").lastOption.getOrElse("table")
throw QueryCompilationErrors.describeJsonNotExtendedError(tableName)
}
val relation = createUnresolvedTableOrView(ctx.identifierReference, "DESCRIBE TABLE")
if (ctx.describeColName != null) {
if (ctx.partitionSpec != null) {
throw QueryParsingErrors.descColumnForPartitionUnsupportedError(ctx)
} else if (asJson) {
throw QueryCompilationErrors.describeColJsonUnsupportedError()
} else {
DescribeColumn(
relation,
UnresolvedAttribute(ctx.describeColName.nameParts.asScala.map(_.getText).toSeq),
isExtended)
}
} else {
val partitionSpec = if (ctx.partitionSpec != null) {
// According to the syntax, visitPartitionSpec returns `Map[String, Option[String]]`.
visitPartitionSpec(ctx.partitionSpec).map {
case (key, Some(value)) => key -> value
case (key, _) =>
throw QueryParsingErrors.emptyPartitionKeyError(key, ctx.partitionSpec)
}
} else {
Map.empty[String, String]
}
if (asJson) {
DescribeRelationJson(relation, partitionSpec, isExtended)
} else {
DescribeRelation(relation, partitionSpec, isExtended)
}
}
}

/**
* Create an [[AnalyzeTable]], or an [[AnalyzeColumn]].
* Example SQL for analyzing a table or a set of partitions :
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -691,19 +691,6 @@ object DescribeRelation {
def getOutputAttrs: Seq[Attribute] = DescribeCommandSchema.describeTableAttributes()
}

/**
* The logical plan of the DESCRIBE relation_name AS JSON command.
*/
case class DescribeRelationJson(
relation: LogicalPlan,
partitionSpec: TablePartitionSpec,
isExtended: Boolean) extends UnaryCommand {
override val output: Seq[Attribute] = DescribeCommandSchema.describeJsonTableAttributes()
override def child: LogicalPlan = relation
override protected def withNewChildInternal(newChild: LogicalPlan): DescribeRelationJson =
copy(relation = newChild)
}

/**
* The logical plan of the DESCRIBE relation_name col_name command.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ class AnalysisExceptionPositionSuite extends AnalysisTest {
}

test("SPARK-34057: UnresolvedTableOrView should retain sql text position") {
verifyTableOrViewPosition("DESCRIBE TABLE unknown", "unknown")
verifyTableOrPermanentViewPosition("ANALYZE TABLE unknown COMPUTE STATISTICS", "unknown")
verifyTableOrViewPosition("ANALYZE TABLE unknown COMPUTE STATISTICS FOR COLUMNS col", "unknown")
verifyTableOrViewPosition("ANALYZE TABLE unknown COMPUTE STATISTICS FOR ALL COLUMNS", "unknown")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,6 @@ class ResolveSessionCatalog(val catalogManager: CatalogManager)
case RenameTable(ResolvedV1TableOrViewIdentifier(oldIdent), newName, isView) =>
AlterTableRenameCommand(oldIdent, newName.asTableIdentifier, isView)

case DescribeRelationJson(
ResolvedV1TableOrViewIdentifier(ident), partitionSpec, isExtended) =>
DescribeTableJsonCommand(ident, partitionSpec, isExtended)

// Use v1 command to describe (temp) view, as v2 catalog doesn't support view yet.
case DescribeRelation(
ResolvedV1TableOrViewIdentifier(ident), partitionSpec, isExtended, output) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import org.antlr.v4.runtime.tree.TerminalNode

import org.apache.spark.SparkException
import org.apache.spark.sql.catalyst.{FunctionIdentifier, TableIdentifier}
import org.apache.spark.sql.catalyst.analysis.{GlobalTempView, LocalTempView, PersistedView, PlanWithUnresolvedIdentifier, SchemaEvolution, SchemaTypeEvolution, UnresolvedFunctionName, UnresolvedIdentifier, UnresolvedNamespace}
import org.apache.spark.sql.catalyst.analysis.{GlobalTempView, LocalTempView, PersistedView, PlanWithUnresolvedIdentifier, SchemaEvolution, SchemaTypeEvolution, UnresolvedAttribute, UnresolvedFunctionName, UnresolvedIdentifier, UnresolvedNamespace}
import org.apache.spark.sql.catalyst.catalog._
import org.apache.spark.sql.catalyst.expressions.{Expression, Literal}
import org.apache.spark.sql.catalyst.parser._
Expand Down Expand Up @@ -1153,4 +1153,46 @@ class SparkSqlAstBuilder extends AstBuilder {
withIdentClause(ctx.identifierReference(), UnresolvedNamespace(_)),
cleanedProperties)
}

/**
* Create a [[DescribeColumn]] or [[DescribeRelation]] or [[DescribeRelationAsJsonCommand]]
* command.
*/
override def visitDescribeRelation(ctx: DescribeRelationContext): LogicalPlan = withOrigin(ctx) {
val isExtended = ctx.EXTENDED != null || ctx.FORMATTED != null
val asJson = ctx.JSON != null
if (asJson && !isExtended) {
val tableName = ctx.identifierReference.getText.split("\\.").lastOption.getOrElse("table")
throw QueryCompilationErrors.describeJsonNotExtendedError(tableName)
}
val relation = createUnresolvedTableOrView(ctx.identifierReference, "DESCRIBE TABLE")
if (ctx.describeColName != null) {
if (ctx.partitionSpec != null) {
throw QueryParsingErrors.descColumnForPartitionUnsupportedError(ctx)
} else if (asJson) {
throw QueryCompilationErrors.describeColJsonUnsupportedError()
} else {
DescribeColumn(
relation,
UnresolvedAttribute(ctx.describeColName.nameParts.asScala.map(_.getText).toSeq),
isExtended)
}
} else {
val partitionSpec = if (ctx.partitionSpec != null) {
// According to the syntax, visitPartitionSpec returns `Map[String, Option[String]]`.
visitPartitionSpec(ctx.partitionSpec).map {
case (key, Some(value)) => key -> value
case (key, _) =>
throw QueryParsingErrors.emptyPartitionKeyError(key, ctx.partitionSpec)
}
} else {
Map.empty[String, String]
}
if (asJson) {
DescribeRelationJsonCommand(relation, partitionSpec, isExtended)
} else {
DescribeRelation(relation, partitionSpec, isExtended)
}
}
}
}
Loading

0 comments on commit 0593ac6

Please sign in to comment.