-
Notifications
You must be signed in to change notification settings - Fork 28.9k
[SPARK-20383][SQL] Supporting Create [temporary] Function with the keyword 'OR REPLACE' and 'IF NOT EXISTS' #17681
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,13 +31,13 @@ import org.apache.spark.sql.types.{StringType, StructField, StructType} | |
| * The DDL command that creates a function. | ||
| * To create a temporary function, the syntax of using this command in SQL is: | ||
| * {{{ | ||
| * CREATE TEMPORARY FUNCTION functionName | ||
| * CREATE [OR REPLACE] TEMPORARY FUNCTION functionName | ||
| * AS className [USING JAR\FILE 'uri' [, JAR|FILE 'uri']] | ||
| * }}} | ||
| * | ||
| * To create a permanent function, the syntax in SQL is: | ||
| * {{{ | ||
| * CREATE FUNCTION [databaseName.]functionName | ||
| * CREATE [OR REPLACE] FUNCTION [IF NOT EXISTS] [databaseName.]functionName | ||
| * AS className [USING JAR\FILE 'uri' [, JAR|FILE 'uri']] | ||
| * }}} | ||
| */ | ||
|
|
@@ -46,26 +46,46 @@ case class CreateFunctionCommand( | |
| functionName: String, | ||
| className: String, | ||
| resources: Seq[FunctionResource], | ||
| isTemp: Boolean) | ||
| isTemp: Boolean, | ||
| ifNotExists: Boolean, | ||
| replace: Boolean) | ||
| extends RunnableCommand { | ||
|
|
||
| if (ifNotExists && replace) { | ||
| throw new AnalysisException("CREATE FUNCTION with both IF NOT EXISTS and REPLACE" + | ||
| " is not allowed.") | ||
| } | ||
|
|
||
| // Disallow to define a temporary function with `IF NOT EXISTS` | ||
| if (ifNotExists && isTemp) { | ||
| throw new AnalysisException( | ||
| "It is not allowed to define a TEMPORARY function with IF NOT EXISTS.") | ||
| } | ||
|
|
||
| // Temporary function names should not contain database prefix like "database.function" | ||
| if (databaseName.isDefined && isTemp) { | ||
| throw new AnalysisException(s"Specifying a database in CREATE TEMPORARY FUNCTION " + | ||
| s"is not allowed: '${databaseName.get}'") | ||
|
||
| } | ||
|
|
||
| override def run(sparkSession: SparkSession): Seq[Row] = { | ||
| val catalog = sparkSession.sessionState.catalog | ||
| val func = CatalogFunction(FunctionIdentifier(functionName, databaseName), className, resources) | ||
| if (isTemp) { | ||
| if (databaseName.isDefined) { | ||
| throw new AnalysisException(s"Specifying a database in CREATE TEMPORARY FUNCTION " + | ||
| s"is not allowed: '${databaseName.get}'") | ||
| } | ||
| // We first load resources and then put the builder in the function registry. | ||
| catalog.loadFunctionResources(resources) | ||
| catalog.registerFunction(func, overrideIfExists = false) | ||
| catalog.registerFunction(func, overrideIfExists = replace) | ||
| } else { | ||
| // For a permanent, we will store the metadata into underlying external catalog. | ||
| // This function will be loaded into the FunctionRegistry when a query uses it. | ||
| // We do not load it into FunctionRegistry right now. | ||
| // TODO: should we also parse "IF NOT EXISTS"? | ||
| catalog.createFunction(func, ignoreIfExists = false) | ||
| // Handles `CREATE OR REPLACE FUNCTION AS ... USING ...` | ||
| if (replace && catalog.functionExists(func.identifier)) { | ||
| // alter the function in the metastore | ||
| catalog.alterFunction(CatalogFunction(func.identifier, className, resources)) | ||
| } else { | ||
| // For a permanent, we will store the metadata into underlying external catalog. | ||
| // This function will be loaded into the FunctionRegistry when a query uses it. | ||
| // We do not load it into FunctionRegistry right now. | ||
| catalog.createFunction(CatalogFunction(func.identifier, className, resources), ifNotExists) | ||
| } | ||
| } | ||
| Seq.empty[Row] | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1132,6 +1132,15 @@ private[spark] class HiveExternalCatalog(conf: SparkConf, hadoopConf: Configurat | |
| client.dropFunction(db, name) | ||
| } | ||
|
|
||
| override protected def doAlterFunction( | ||
| db: String, funcDefinition: CatalogFunction): Unit = withClient { | ||
| requireDbExists(db) | ||
| val functionName = funcDefinition.identifier.funcName.toLowerCase(Locale.ROOT) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should normalize the function name in
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. LGTM |
||
| requireFunctionExists(db, functionName) | ||
| val functionIdentifier = funcDefinition.identifier.copy(funcName = functionName) | ||
| client.alterFunction(db, funcDefinition.copy(identifier = functionIdentifier)) | ||
| } | ||
|
|
||
| override protected def doRenameFunction( | ||
| db: String, | ||
| oldName: String, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a test case?