Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 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 @@ -1894,7 +1894,7 @@ class Analyzer(
def apply(plan: LogicalPlan): LogicalPlan = plan.resolveOperatorsUp {
// Resolve functions with concrete relations from v2 catalog.
case UnresolvedFunc(multipartIdent) =>
val funcIdent = parseSessionCatalogFunctionIdentifier(multipartIdent, "function lookup")
val funcIdent = parseSessionCatalogFunctionIdentifier(multipartIdent)
Comment thread
cloud-fan marked this conversation as resolved.
ResolvedFunc(Identifier.of(funcIdent.database.toArray, funcIdent.funcName))

case q: LogicalPlan =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3595,7 +3595,7 @@ class AstBuilder(conf: SQLConf) extends SqlBaseBaseVisitor[AnyRef] with Logging
} else {
Seq(describeFuncName.getText)
}
DescribeFunctionStatement(functionName, EXTENDED != null)
DescribeFunction(UnresolvedFunc(functionName), EXTENDED != null)
}

/**
Expand All @@ -3610,8 +3610,10 @@ class AstBuilder(conf: SQLConf) extends SqlBaseBaseVisitor[AnyRef] with Logging
case Some(x) => throw new ParseException(s"SHOW $x FUNCTIONS not supported", ctx)
}
val pattern = Option(ctx.pattern).map(string(_))
val functionName = Option(ctx.multipartIdentifier).map(visitMultipartIdentifier)
ShowFunctionsStatement(userScope, systemScope, pattern, functionName)
val functionName = Option(ctx.multipartIdentifier)

@cloud-fan cloud-fan Jul 24, 2020

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: maybe unresolvedFunc? it's not a name anymore.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

or keep it functionName, and do

ShowFunctions(UnresolvedFunc(functionName), userScope, systemScope, pattern)

@imback82 imback82 Jul 24, 2020

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Changed it to unresolvedFuncOpt since functionName in the second suggestion will be Option[Seq[String]].

.map(visitMultipartIdentifier)
.map(UnresolvedFunc(_))
ShowFunctions(functionName, userScope, systemScope, pattern)
}

/**
Expand All @@ -3624,8 +3626,8 @@ class AstBuilder(conf: SQLConf) extends SqlBaseBaseVisitor[AnyRef] with Logging
*/
override def visitDropFunction(ctx: DropFunctionContext): LogicalPlan = withOrigin(ctx) {
val functionName = visitMultipartIdentifier(ctx.multipartIdentifier)
DropFunctionStatement(
functionName,
DropFunction(
UnresolvedFunc(functionName),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

will analyzer give a nice error if UnresolvedFunc is left to CheckAnalysis?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nvm, this won't happen for now.

ctx.EXISTS != null,
ctx.TEMPORARY != null)
}
Expand All @@ -3650,12 +3652,24 @@ class AstBuilder(conf: SQLConf) extends SqlBaseBaseVisitor[AnyRef] with Logging
}
}

val functionIdentifier = visitMultipartIdentifier(ctx.multipartIdentifier)
CreateFunctionStatement(
functionIdentifier,
val nameParts = visitMultipartIdentifier(ctx.multipartIdentifier)
val isTemp = ctx.TEMPORARY != null
val func: LogicalPlan = if (isTemp) {
import org.apache.spark.sql.connector.catalog.CatalogV2Implicits._
// temp func doesn't belong to any catalog and we shouldn't resolve catalog in the name.
if (nameParts.length > 2) {
throw new AnalysisException(s"Unsupported function name '${nameParts.quoted}'")
}
ResolvedFunc(nameParts.asIdentifier)
Comment thread
imback82 marked this conversation as resolved.
Outdated
} else {
UnresolvedFunc(nameParts)
}

CreateFunction(
func,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not sure how to deal with CREATE commands yet. For now, CREATE TABLE and VIEW still use statement plans. We don't need to do lookup for CREATE commands, so UnresolvedFunnc looks weird here.

Can we keep it unchanged?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ok. Will revert this change.

string(ctx.className),
resources.toSeq,
ctx.TEMPORARY != null,
resources,
isTemp,
ctx.EXISTS != null,
ctx.REPLACE != null)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -445,38 +445,3 @@ case class ShowColumnsStatement(
* A SHOW CURRENT NAMESPACE statement, as parsed from SQL
*/
case class ShowCurrentNamespaceStatement() extends ParsedStatement

/**
* A DESCRIBE FUNCTION statement, as parsed from SQL
*/
case class DescribeFunctionStatement(
functionName: Seq[String],
isExtended: Boolean) extends ParsedStatement

/**
* SHOW FUNCTIONS statement, as parsed from SQL
*/
case class ShowFunctionsStatement(
userScope: Boolean,
systemScope: Boolean,
pattern: Option[String],
functionName: Option[Seq[String]]) extends ParsedStatement

/**
* DROP FUNCTION statement, as parsed from SQL
*/
case class DropFunctionStatement(
functionName: Seq[String],
ifExists: Boolean,
isTemp: Boolean) extends ParsedStatement

/**
* CREATE FUNCTION statement, as parsed from SQL
*/
case class CreateFunctionStatement(
functionName: Seq[String],
className: String,
resources: Seq[FunctionResource],
isTemp: Boolean,
ignoreIfExists: Boolean,
replace: Boolean) extends ParsedStatement
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package org.apache.spark.sql.catalyst.plans.logical

import org.apache.spark.sql.catalyst.analysis.{NamedRelation, UnresolvedException}
import org.apache.spark.sql.catalyst.catalog.CatalogTypes.TablePartitionSpec
import org.apache.spark.sql.catalyst.catalog.FunctionResource
import org.apache.spark.sql.catalyst.expressions.{Attribute, AttributeReference, Expression, Unevaluable}
import org.apache.spark.sql.catalyst.plans.DescribeTableSchema
import org.apache.spark.sql.connector.catalog._
Expand Down Expand Up @@ -517,9 +518,50 @@ case class CommentOnTable(child: LogicalPlan, comment: String) extends Command {
override def children: Seq[LogicalPlan] = child :: Nil
}

/**
* The logical plan of the CREATE FUNCTION command that works for v2 catalogs.
*/
case class CreateFunction(
child: LogicalPlan,
className: String,
resources: Seq[FunctionResource],
isTemp: Boolean,
ignoreIfExists: Boolean,
replace: Boolean) extends Command {
override def children: Seq[LogicalPlan] = child :: Nil
}

/**
* The logical plan of the REFRESH FUNCTION command that works for v2 catalogs.
*/
case class RefreshFunction(child: LogicalPlan) extends Command {
override def children: Seq[LogicalPlan] = child :: Nil
}

/**
* The logical plan of the DESCRIBE FUNCTION command that works for v2 catalogs.
*/
case class DescribeFunction(child: LogicalPlan, isExtended: Boolean) extends Command {
override def children: Seq[LogicalPlan] = child :: Nil
}

/**
* The logical plan of the DROP FUNCTION command that works for v2 catalogs.
*/
case class DropFunction(
child: LogicalPlan,
ifExists: Boolean,
isTemp: Boolean) extends Command {
override def children: Seq[LogicalPlan] = child :: Nil
}

/**
* The logical plan of the SHOW FUNCTIONS command that works for v2 catalogs.
*/
case class ShowFunctions(
child: Option[LogicalPlan],
userScope: Boolean,
systemScope: Boolean,
pattern: Option[String]) extends Command {
override def children: Seq[LogicalPlan] = if (child.isDefined) { child.get :: Nil } else { Nil }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: child.toSeq

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

thanks!

}
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,7 @@ private[sql] trait LookupCatalog extends Logging {
}
}

// TODO: move function related v2 statements to the new framework.
def parseSessionCatalogFunctionIdentifier(
nameParts: Seq[String],
sql: String): FunctionIdentifier = {
def parseSessionCatalogFunctionIdentifier(nameParts: Seq[String]): FunctionIdentifier = {
if (nameParts.length == 1 && catalogManager.v1SessionCatalog.isTempFunction(nameParts.head)) {
return FunctionIdentifier(nameParts.head)
}
Expand All @@ -179,7 +176,7 @@ private[sql] trait LookupCatalog extends Logging {
}
}

case _ => throw new AnalysisException(s"$sql is only supported in v1 catalog")
case _ => throw new AnalysisException("function is only supported in v1 catalog")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

function -> function command?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is called from Analyzer.scala except for CREATE FUNCTION, so I thought more general name would be better here. But if you think function command is better here, I am happy to change it. Please let me know!

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ package org.apache.spark.sql.catalyst.parser
import java.util.Locale

import org.apache.spark.sql.AnalysisException
import org.apache.spark.sql.catalyst.analysis.{AnalysisTest, GlobalTempView, LocalTempView, PersistedView, UnresolvedAttribute, UnresolvedFunc, UnresolvedNamespace, UnresolvedRelation, UnresolvedStar, UnresolvedTable, UnresolvedTableOrView}
import org.apache.spark.sql.catalyst.analysis.{AnalysisTest, GlobalTempView, LocalTempView, PersistedView, ResolvedFunc, UnresolvedAttribute, UnresolvedFunc, UnresolvedNamespace, UnresolvedRelation, UnresolvedStar, UnresolvedTable, UnresolvedTableOrView}
import org.apache.spark.sql.catalyst.catalog.{ArchiveResource, BucketSpec, FileResource, FunctionResource, FunctionResourceType, JarResource}
import org.apache.spark.sql.catalyst.expressions.{EqualTo, Literal}
import org.apache.spark.sql.catalyst.plans.logical._
Expand Down Expand Up @@ -2016,92 +2016,101 @@ class DDLParserSuite extends AnalysisTest {
test("DESCRIBE FUNCTION") {
comparePlans(
parsePlan("DESC FUNCTION a"),
DescribeFunctionStatement(Seq("a"), false))
DescribeFunction(UnresolvedFunc(Seq("a")), false))
comparePlans(
parsePlan("DESCRIBE FUNCTION a"),
DescribeFunctionStatement(Seq("a"), false))
DescribeFunction(UnresolvedFunc(Seq("a")), false))
comparePlans(
parsePlan("DESCRIBE FUNCTION a.b.c"),
DescribeFunctionStatement(Seq("a", "b", "c"), false))
DescribeFunction(UnresolvedFunc(Seq("a", "b", "c")), false))
comparePlans(
parsePlan("DESCRIBE FUNCTION EXTENDED a.b.c"),
DescribeFunctionStatement(Seq("a", "b", "c"), true))
DescribeFunction(UnresolvedFunc(Seq("a", "b", "c")), true))
}

test("SHOW FUNCTIONS") {
comparePlans(
parsePlan("SHOW FUNCTIONS"),
ShowFunctionsStatement(true, true, None, None))
ShowFunctions(None, true, true, None))
comparePlans(
parsePlan("SHOW USER FUNCTIONS"),
ShowFunctionsStatement(true, false, None, None))
ShowFunctions(None, true, false, None))
comparePlans(
parsePlan("SHOW user FUNCTIONS"),
ShowFunctionsStatement(true, false, None, None))
ShowFunctions(None, true, false, None))
comparePlans(
parsePlan("SHOW SYSTEM FUNCTIONS"),
ShowFunctionsStatement(false, true, None, None))
ShowFunctions(None, false, true, None))
comparePlans(
parsePlan("SHOW ALL FUNCTIONS"),
ShowFunctionsStatement(true, true, None, None))
ShowFunctions(None, true, true, None))
comparePlans(
parsePlan("SHOW FUNCTIONS LIKE 'funct*'"),
ShowFunctionsStatement(true, true, Some("funct*"), None))
ShowFunctions(None, true, true, Some("funct*")))
comparePlans(
parsePlan("SHOW FUNCTIONS LIKE a.b.c"),
ShowFunctionsStatement(true, true, None, Some(Seq("a", "b", "c"))))
ShowFunctions(Some(UnresolvedFunc(Seq("a", "b", "c"))), true, true, None))
val sql = "SHOW other FUNCTIONS"
intercept(sql, s"$sql not supported")
}

test("DROP FUNCTION") {
comparePlans(
parsePlan("DROP FUNCTION a"),
DropFunctionStatement(Seq("a"), false, false))
DropFunction(UnresolvedFunc(Seq("a")), false, false))
comparePlans(
parsePlan("DROP FUNCTION a.b.c"),
DropFunctionStatement(Seq("a", "b", "c"), false, false))
DropFunction(UnresolvedFunc(Seq("a", "b", "c")), false, false))
comparePlans(
parsePlan("DROP TEMPORARY FUNCTION a.b.c"),
DropFunctionStatement(Seq("a", "b", "c"), false, true))
DropFunction(UnresolvedFunc(Seq("a", "b", "c")), false, true))
comparePlans(
parsePlan("DROP FUNCTION IF EXISTS a.b.c"),
DropFunctionStatement(Seq("a", "b", "c"), true, false))
DropFunction(UnresolvedFunc(Seq("a", "b", "c")), true, false))
comparePlans(
parsePlan("DROP TEMPORARY FUNCTION IF EXISTS a.b.c"),
DropFunctionStatement(Seq("a", "b", "c"), true, true))
DropFunction(UnresolvedFunc(Seq("a", "b", "c")), true, true))
}

test("CREATE FUNCTION") {
import org.apache.spark.sql.connector.catalog.CatalogV2Implicits._

parseCompare("CREATE FUNCTION a as 'fun'",
CreateFunctionStatement(Seq("a"), "fun", Seq(), false, false, false))
CreateFunction(UnresolvedFunc(Seq("a")), "fun", Seq(), false, false, false))

parseCompare("CREATE FUNCTION a.b.c as 'fun'",
CreateFunctionStatement(Seq("a", "b", "c"), "fun", Seq(), false, false, false))
CreateFunction(UnresolvedFunc(Seq("a", "b", "c")), "fun", Seq(), false, false, false))

parseCompare("CREATE OR REPLACE FUNCTION a.b.c as 'fun'",
CreateFunctionStatement(Seq("a", "b", "c"), "fun", Seq(), false, false, true))
CreateFunction(UnresolvedFunc(Seq("a", "b", "c")), "fun", Seq(), false, false, true))

parseCompare("CREATE TEMPORARY FUNCTION a as 'fun'",
CreateFunction(ResolvedFunc(Seq("a").asIdentifier), "fun", Seq(), true, false, false))

parseCompare("CREATE TEMPORARY FUNCTION a.b.c as 'fun'",
CreateFunctionStatement(Seq("a", "b", "c"), "fun", Seq(), true, false, false))
parseCompare("CREATE TEMPORARY FUNCTION a.b as 'fun'",
CreateFunction(ResolvedFunc(Seq("a", "b").asIdentifier), "fun", Seq(), true, false, false))

val caught = intercept[AnalysisException](
parsePlan("CREATE TEMPORARY FUNCTION a.b.c as 'fun'"))
assert(caught.getMessage.contains("Unsupported function name 'a.b.c'"))

parseCompare("CREATE FUNCTION IF NOT EXISTS a.b.c as 'fun'",
CreateFunctionStatement(Seq("a", "b", "c"), "fun", Seq(), false, true, false))
CreateFunction(UnresolvedFunc(Seq("a", "b", "c")), "fun", Seq(), false, true, false))

parseCompare("CREATE FUNCTION a as 'fun' USING JAR 'j'",
CreateFunctionStatement(Seq("a"), "fun", Seq(FunctionResource(JarResource, "j")),
CreateFunction(UnresolvedFunc(Seq("a")), "fun", Seq(FunctionResource(JarResource, "j")),
false, false, false))

parseCompare("CREATE FUNCTION a as 'fun' USING ARCHIVE 'a'",
CreateFunctionStatement(Seq("a"), "fun", Seq(FunctionResource(ArchiveResource, "a")),
CreateFunction(UnresolvedFunc(Seq("a")), "fun", Seq(FunctionResource(ArchiveResource, "a")),
false, false, false))

parseCompare("CREATE FUNCTION a as 'fun' USING FILE 'f'",
CreateFunctionStatement(Seq("a"), "fun", Seq(FunctionResource(FileResource, "f")),
CreateFunction(UnresolvedFunc(Seq("a")), "fun", Seq(FunctionResource(FileResource, "f")),
false, false, false))

parseCompare("CREATE FUNCTION a as 'fun' USING JAR 'j', ARCHIVE 'a', FILE 'f'",
CreateFunctionStatement(Seq("a"), "fun", Seq(FunctionResource(JarResource, "j"),
CreateFunction(UnresolvedFunc(Seq("a")), "fun", Seq(FunctionResource(JarResource, "j"),
FunctionResource(ArchiveResource, "a"), FunctionResource(FileResource, "f")),
false, false, false))

Expand Down
Loading