diff --git a/presto-main/src/main/java/com/facebook/presto/execution/CreateFunctionTask.java b/presto-main/src/main/java/com/facebook/presto/execution/CreateFunctionTask.java index 4920e92e56d8b..55aa1ba6e78f5 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/CreateFunctionTask.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/CreateFunctionTask.java @@ -15,8 +15,6 @@ import com.facebook.presto.metadata.Metadata; import com.facebook.presto.security.AccessControl; -import com.facebook.presto.spi.CatalogSchemaName; -import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.function.QualifiedFunctionName; import com.facebook.presto.spi.function.RoutineCharacteristics; import com.facebook.presto.spi.function.SqlInvokedFunction; @@ -34,12 +32,10 @@ import java.util.List; import java.util.Optional; -import static com.facebook.presto.spi.StandardErrorCode.GENERIC_USER_ERROR; import static com.facebook.presto.spi.type.TypeSignature.parseTypeSignature; import static com.facebook.presto.sql.SqlFormatter.formatSql; import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.util.concurrent.Futures.immediateFuture; -import static java.lang.String.format; import static java.util.Locale.ENGLISH; import static java.util.Objects.requireNonNull; @@ -77,14 +73,7 @@ public ListenableFuture execute(CreateFunction statement, TransactionManager private SqlInvokedFunction createSqlInvokedFunction(CreateFunction statement) { - List parts = statement.getFunctionName().getParts(); - if (parts.size() != 3) { - throw new PrestoException(GENERIC_USER_ERROR, format("Invalid function name: %s, require exactly 3 parts", statement.getFunctionName())); - } - - QualifiedFunctionName functionName = QualifiedFunctionName.of( - new CatalogSchemaName(parts.get(0), parts.get(1)), - parts.get(2)); + QualifiedFunctionName functionName = QualifiedFunctionName.of(statement.getFunctionName().toString()); List parameters = statement.getParameters().stream() .map(parameter -> new SqlParameter(parameter.getName().toString().toLowerCase(ENGLISH), parseTypeSignature(parameter.getType()))) .collect(toImmutableList()); diff --git a/presto-main/src/main/java/com/facebook/presto/execution/DropFunctionTask.java b/presto-main/src/main/java/com/facebook/presto/execution/DropFunctionTask.java new file mode 100644 index 0000000000000..566bf6b2fb4e4 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/execution/DropFunctionTask.java @@ -0,0 +1,71 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.execution; + +import com.facebook.presto.metadata.Metadata; +import com.facebook.presto.security.AccessControl; +import com.facebook.presto.spi.function.QualifiedFunctionName; +import com.facebook.presto.spi.type.TypeSignature; +import com.facebook.presto.sql.analyzer.Analyzer; +import com.facebook.presto.sql.parser.SqlParser; +import com.facebook.presto.sql.tree.DropFunction; +import com.facebook.presto.sql.tree.Expression; +import com.facebook.presto.transaction.TransactionManager; +import com.google.common.util.concurrent.ListenableFuture; + +import javax.inject.Inject; + +import java.util.List; +import java.util.Optional; + +import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.common.util.concurrent.Futures.immediateFuture; +import static java.util.Objects.requireNonNull; + +public class DropFunctionTask + implements DataDefinitionTask +{ + private final SqlParser sqlParser; + + @Inject + public DropFunctionTask(SqlParser sqlParser) + { + this.sqlParser = requireNonNull(sqlParser, "sqlParser is null"); + } + + @Override + public String getName() + { + return "DROP FUNCTION"; + } + + @Override + public String explain(DropFunction statement, List parameters) + { + return "DROP FUNCTION " + statement.getFunctionName(); + } + + @Override + public ListenableFuture execute(DropFunction statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + { + Analyzer analyzer = new Analyzer(stateMachine.getSession(), metadata, sqlParser, accessControl, Optional.empty(), parameters, stateMachine.getWarningCollector()); + analyzer.analyze(statement); + + metadata.getFunctionManager().dropFunction( + QualifiedFunctionName.of(statement.getFunctionName().toString()), + statement.getParameterTypes().map(types -> types.stream().map(TypeSignature::parseTypeSignature).collect(toImmutableList())), + statement.isExists()); + return immediateFuture(null); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/BuiltInFunctionNamespaceManager.java b/presto-main/src/main/java/com/facebook/presto/metadata/BuiltInFunctionNamespaceManager.java index e9bf5199d71be..3f05b71a1d311 100644 --- a/presto-main/src/main/java/com/facebook/presto/metadata/BuiltInFunctionNamespaceManager.java +++ b/presto-main/src/main/java/com/facebook/presto/metadata/BuiltInFunctionNamespaceManager.java @@ -697,6 +697,12 @@ public void createFunction(SqlInvokedFunction function, boolean replace) throw new PrestoException(GENERIC_USER_ERROR, format("Cannot create function in built-in function namespace: %s", function.getSignature().getName())); } + @Override + public void dropFunction(QualifiedFunctionName functionName, Optional> parameterTypes, boolean exists) + { + throw new PrestoException(GENERIC_USER_ERROR, format("Cannot drop function in built-in function namespace: %s", functionName)); + } + public String getName() { return NAME; diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/FunctionManager.java b/presto-main/src/main/java/com/facebook/presto/metadata/FunctionManager.java index f895099f3a6cb..e48e6495576c7 100644 --- a/presto-main/src/main/java/com/facebook/presto/metadata/FunctionManager.java +++ b/presto-main/src/main/java/com/facebook/presto/metadata/FunctionManager.java @@ -174,6 +174,17 @@ public void createFunction(SqlInvokedFunction function, boolean replace) functionNamespaceManager.get().createFunction(function, replace); } + public void dropFunction(QualifiedFunctionName functionName, Optional> parameterTypes, boolean exists) + { + Optional> functionNamespaceManager = getServingFunctionNamespaceManager(functionName.getFunctionNamespace()); + if (functionNamespaceManager.isPresent()) { + functionNamespaceManager.get().dropFunction(functionName, parameterTypes, exists); + } + else if (!exists) { + throw new PrestoException(FUNCTION_NOT_FOUND, format("Function not found: %s", functionName.getFunctionNamespace())); + } + } + /** * Resolves a function using implicit type coercions. We enforce explicit naming for dynamic function namespaces. * All unqualified function names will only be resolved against the built-in static function namespace. While it is diff --git a/presto-main/src/main/java/com/facebook/presto/server/CoordinatorModule.java b/presto-main/src/main/java/com/facebook/presto/server/CoordinatorModule.java index ee41e4a1fd9b4..77a0c435fd092 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/CoordinatorModule.java +++ b/presto-main/src/main/java/com/facebook/presto/server/CoordinatorModule.java @@ -38,6 +38,7 @@ import com.facebook.presto.execution.DataDefinitionTask; import com.facebook.presto.execution.DeallocateTask; import com.facebook.presto.execution.DropColumnTask; +import com.facebook.presto.execution.DropFunctionTask; import com.facebook.presto.execution.DropRoleTask; import com.facebook.presto.execution.DropSchemaTask; import com.facebook.presto.execution.DropTableTask; @@ -106,6 +107,7 @@ import com.facebook.presto.sql.tree.CreateView; import com.facebook.presto.sql.tree.Deallocate; import com.facebook.presto.sql.tree.DropColumn; +import com.facebook.presto.sql.tree.DropFunction; import com.facebook.presto.sql.tree.DropRole; import com.facebook.presto.sql.tree.DropSchema; import com.facebook.presto.sql.tree.DropTable; @@ -307,6 +309,7 @@ protected void setup(Binder binder) bindDataDefinitionTask(binder, executionBinder, CreateView.class, CreateViewTask.class); bindDataDefinitionTask(binder, executionBinder, DropView.class, DropViewTask.class); bindDataDefinitionTask(binder, executionBinder, CreateFunction.class, CreateFunctionTask.class); + bindDataDefinitionTask(binder, executionBinder, DropFunction.class, DropFunctionTask.class); bindDataDefinitionTask(binder, executionBinder, Use.class, UseTask.class); bindDataDefinitionTask(binder, executionBinder, SetSession.class, SetSessionTask.class); bindDataDefinitionTask(binder, executionBinder, ResetSession.class, ResetSessionTask.class); diff --git a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/SemanticErrorCode.java b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/SemanticErrorCode.java index 5ce3780c9a77a..012f766445ec2 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/SemanticErrorCode.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/SemanticErrorCode.java @@ -49,6 +49,7 @@ public enum SemanticErrorCode INVALID_LITERAL, FUNCTION_NOT_FOUND, + INVALID_FUNCTION_NAME, DUPLICATE_PARAMETER_NAME, ORDER_BY_MUST_BE_IN_SELECT, diff --git a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java index 0ad25a58005a0..c56874247f887 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java @@ -60,6 +60,7 @@ import com.facebook.presto.sql.tree.Delete; import com.facebook.presto.sql.tree.DereferenceExpression; import com.facebook.presto.sql.tree.DropColumn; +import com.facebook.presto.sql.tree.DropFunction; import com.facebook.presto.sql.tree.DropSchema; import com.facebook.presto.sql.tree.DropTable; import com.facebook.presto.sql.tree.DropView; @@ -173,6 +174,7 @@ import static com.facebook.presto.sql.analyzer.SemanticErrorCode.DUPLICATE_PARAMETER_NAME; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.DUPLICATE_PROPERTY; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.DUPLICATE_RELATION; +import static com.facebook.presto.sql.analyzer.SemanticErrorCode.INVALID_FUNCTION_NAME; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.INVALID_ORDINAL; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.INVALID_PROCEDURE_ARGUMENTS; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.INVALID_WINDOW_FRAME; @@ -552,6 +554,9 @@ protected Scope visitCreateFunction(CreateFunction node, Optional scope) { analysis.setUpdateType("CREATE FUNCTION"); + // Check function name + checkFunctionName(node, node.getFunctionName()); + // Check parameter List duplicateParameters = node.getParameters().stream() .map(SqlParameterDeclaration::getName) @@ -586,6 +591,13 @@ protected Scope visitCreateFunction(CreateFunction node, Optional scope) return createAndAssignScope(node, scope); } + @Override + protected Scope visitDropFunction(DropFunction node, Optional scope) + { + checkFunctionName(node, node.getFunctionName()); + return createAndAssignScope(node, scope); + } + @Override protected Scope visitSetSession(SetSession node, Optional scope) { @@ -1543,6 +1555,13 @@ else if (column.getExpression() instanceof Identifier) { return assignments.build(); } + private void checkFunctionName(Statement node, QualifiedName functionName) + { + if (functionName.getParts().size() != 3) { + throw new SemanticException(INVALID_FUNCTION_NAME, node, format("Function name should be in the form of catalog.schema.function_name, found: %s", functionName)); + } + } + private class OrderByExpressionRewriter extends ExpressionRewriter { diff --git a/presto-main/src/main/java/com/facebook/presto/testing/InMemoryFunctionNamespaceManager.java b/presto-main/src/main/java/com/facebook/presto/testing/InMemoryFunctionNamespaceManager.java index 69e83db3922a1..1b46b28e25b03 100644 --- a/presto-main/src/main/java/com/facebook/presto/testing/InMemoryFunctionNamespaceManager.java +++ b/presto-main/src/main/java/com/facebook/presto/testing/InMemoryFunctionNamespaceManager.java @@ -20,16 +20,20 @@ import com.facebook.presto.spi.function.SqlFunctionHandle; import com.facebook.presto.spi.function.SqlFunctionId; import com.facebook.presto.spi.function.SqlInvokedFunction; +import com.facebook.presto.spi.type.TypeSignature; import com.facebook.presto.sqlfunction.AbstractSqlInvokedFunctionNamespaceManager; import com.facebook.presto.sqlfunction.SqlInvokedFunctionNamespaceManagerConfig; import javax.annotation.concurrent.ThreadSafe; import java.util.Collection; +import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import static com.facebook.presto.spi.StandardErrorCode.GENERIC_USER_ERROR; +import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.MoreCollectors.onlyElement; import static java.lang.String.format; @@ -69,6 +73,12 @@ public synchronized void createFunction(SqlInvokedFunction function, boolean rep latestFunctions.put(functionId, function.withVersion(version)); } + @Override + public synchronized void dropFunction(QualifiedFunctionName functionName, Optional> parameterTypes, boolean exists) + { + throw new PrestoException(NOT_SUPPORTED, "Drop Function is not supported in InMemoryFunctionNamespaceManager"); + } + @Override public Collection listFunctions() { diff --git a/presto-main/src/main/java/com/facebook/presto/testing/LocalQueryRunner.java b/presto-main/src/main/java/com/facebook/presto/testing/LocalQueryRunner.java index 181274aed2a5b..90d8ef187eb12 100644 --- a/presto-main/src/main/java/com/facebook/presto/testing/LocalQueryRunner.java +++ b/presto-main/src/main/java/com/facebook/presto/testing/LocalQueryRunner.java @@ -45,6 +45,7 @@ import com.facebook.presto.execution.CreateViewTask; import com.facebook.presto.execution.DataDefinitionTask; import com.facebook.presto.execution.DeallocateTask; +import com.facebook.presto.execution.DropFunctionTask; import com.facebook.presto.execution.DropTableTask; import com.facebook.presto.execution.DropViewTask; import com.facebook.presto.execution.Lifespan; @@ -156,6 +157,7 @@ import com.facebook.presto.sql.tree.CreateTable; import com.facebook.presto.sql.tree.CreateView; import com.facebook.presto.sql.tree.Deallocate; +import com.facebook.presto.sql.tree.DropFunction; import com.facebook.presto.sql.tree.DropTable; import com.facebook.presto.sql.tree.DropView; import com.facebook.presto.sql.tree.Explain; @@ -431,6 +433,7 @@ private LocalQueryRunner(Session defaultSession, FeaturesConfig featuresConfig, .put(CreateTable.class, new CreateTableTask()) .put(CreateView.class, new CreateViewTask(jsonCodec(ViewDefinition.class), sqlParser, new FeaturesConfig())) .put(CreateFunction.class, new CreateFunctionTask(sqlParser)) + .put(DropFunction.class, new DropFunctionTask(sqlParser)) .put(DropTable.class, new DropTableTask()) .put(DropView.class, new DropViewTask()) .put(RenameColumn.class, new RenameColumnTask()) diff --git a/presto-main/src/main/java/com/facebook/presto/util/StatementUtils.java b/presto-main/src/main/java/com/facebook/presto/util/StatementUtils.java index 627bddda22694..410e88d6f58ae 100644 --- a/presto-main/src/main/java/com/facebook/presto/util/StatementUtils.java +++ b/presto-main/src/main/java/com/facebook/presto/util/StatementUtils.java @@ -29,6 +29,7 @@ import com.facebook.presto.sql.tree.DescribeInput; import com.facebook.presto.sql.tree.DescribeOutput; import com.facebook.presto.sql.tree.DropColumn; +import com.facebook.presto.sql.tree.DropFunction; import com.facebook.presto.sql.tree.DropRole; import com.facebook.presto.sql.tree.DropSchema; import com.facebook.presto.sql.tree.DropTable; @@ -111,6 +112,7 @@ private StatementUtils() {} builder.put(CreateView.class, QueryType.DATA_DEFINITION); builder.put(DropView.class, QueryType.DATA_DEFINITION); builder.put(CreateFunction.class, QueryType.DATA_DEFINITION); + builder.put(DropFunction.class, QueryType.DATA_DEFINITION); builder.put(Use.class, QueryType.DATA_DEFINITION); builder.put(SetSession.class, QueryType.DATA_DEFINITION); builder.put(ResetSession.class, QueryType.DATA_DEFINITION); diff --git a/presto-main/src/test/java/com/facebook/presto/metadata/TestFunctionNamespaceManager.java b/presto-main/src/test/java/com/facebook/presto/metadata/TestFunctionNamespaceManager.java index 00a379e516279..60f9438c0e290 100644 --- a/presto-main/src/test/java/com/facebook/presto/metadata/TestFunctionNamespaceManager.java +++ b/presto-main/src/test/java/com/facebook/presto/metadata/TestFunctionNamespaceManager.java @@ -59,8 +59,6 @@ public void testCreateFunction() assertEquals( ImmutableSet.copyOf(functionNamespaceManager.listFunctions()), ImmutableSet.of(FUNCTION_POWER_TOWER_DOUBLE_UPDATED.withVersion(2), FUNCTION_POWER_TOWER_INT.withVersion(1))); - - System.out.println(FUNCTION_POWER_TOWER_DOUBLE); } @Test diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/TestRowExpressionVariableInliner.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/TestRowExpressionVariableInliner.java index 8bacb88ed8502..41e105f3d31e5 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/TestRowExpressionVariableInliner.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/TestRowExpressionVariableInliner.java @@ -34,7 +34,7 @@ private static class TestFunctionHandle @Override public CatalogSchemaName getFunctionNamespace() { - return QualifiedFunctionName.of("a.b.c").getFunctionNamespace(); + return QualifiedFunctionName.of(new CatalogSchemaName("a", "b"), "c").getFunctionNamespace(); } } diff --git a/presto-parser/src/main/antlr4/com/facebook/presto/sql/parser/SqlBase.g4 b/presto-parser/src/main/antlr4/com/facebook/presto/sql/parser/SqlBase.g4 index 536fcead83eb7..016f089486ec0 100644 --- a/presto-parser/src/main/antlr4/com/facebook/presto/sql/parser/SqlBase.g4 +++ b/presto-parser/src/main/antlr4/com/facebook/presto/sql/parser/SqlBase.g4 @@ -60,6 +60,7 @@ statement RETURNS returnType=type (COMMENT string)? routineCharacteristics routineBody #createFunction + | DROP FUNCTION (IF EXISTS)? qualifiedName types? #dropFunction | CALL qualifiedName '(' (callArgument (',' callArgument)*)? ')' #call | CREATE ROLE name=identifier (WITH ADMIN grantor)? #createRole @@ -394,6 +395,10 @@ normalForm : NFD | NFC | NFKD | NFKC ; +types + : '(' (type (',' type)*)? ')' + ; + type : type ARRAY | ARRAY '<' type '>' diff --git a/presto-parser/src/main/java/com/facebook/presto/sql/SqlFormatter.java b/presto-parser/src/main/java/com/facebook/presto/sql/SqlFormatter.java index 766791d26987f..04c75a44f5f06 100644 --- a/presto-parser/src/main/java/com/facebook/presto/sql/SqlFormatter.java +++ b/presto-parser/src/main/java/com/facebook/presto/sql/SqlFormatter.java @@ -33,6 +33,7 @@ import com.facebook.presto.sql.tree.DescribeInput; import com.facebook.presto.sql.tree.DescribeOutput; import com.facebook.presto.sql.tree.DropColumn; +import com.facebook.presto.sql.tree.DropFunction; import com.facebook.presto.sql.tree.DropRole; import com.facebook.presto.sql.tree.DropSchema; import com.facebook.presto.sql.tree.DropTable; @@ -573,6 +574,26 @@ protected Void visitCreateFunction(CreateFunction node, Integer indent) return null; } + @Override + protected Void visitDropFunction(DropFunction node, Integer indent) + { + builder.append("DROP FUNCTION "); + if (node.isExists()) { + builder.append("IF EXISTS "); + } + builder.append(formatName(node.getFunctionName())); + if (node.getParameterTypes().isPresent()) { + String elementIndent = indentString(indent + 1); + builder.append("(\n") + .append(node.getParameterTypes().get().stream() + .map(type -> elementIndent + type) + .collect(joining(",\n"))) + .append(")\n"); + } + + return null; + } + @Override protected Void visitDropView(DropView node, Integer context) { diff --git a/presto-parser/src/main/java/com/facebook/presto/sql/parser/AstBuilder.java b/presto-parser/src/main/java/com/facebook/presto/sql/parser/AstBuilder.java index 1380a51327898..047fccb4ed059 100644 --- a/presto-parser/src/main/java/com/facebook/presto/sql/parser/AstBuilder.java +++ b/presto-parser/src/main/java/com/facebook/presto/sql/parser/AstBuilder.java @@ -50,6 +50,7 @@ import com.facebook.presto.sql.tree.DescribeOutput; import com.facebook.presto.sql.tree.DoubleLiteral; import com.facebook.presto.sql.tree.DropColumn; +import com.facebook.presto.sql.tree.DropFunction; import com.facebook.presto.sql.tree.DropRole; import com.facebook.presto.sql.tree.DropSchema; import com.facebook.presto.sql.tree.DropTable; @@ -421,6 +422,13 @@ public Node visitCreateFunction(SqlBaseParser.CreateFunctionContext context) (Expression) visit(context.routineBody())); } + @Override + public Node visitDropFunction(SqlBaseParser.DropFunctionContext context) + { + Optional> parameterTypes = context.types() == null ? Optional.empty() : Optional.of(getTypes(context.types())); + return new DropFunction(getLocation(context), getQualifiedName(context.qualifiedName()), parameterTypes, context.EXISTS() != null); + } + @Override public Node visitRoutineBody(SqlBaseParser.RoutineBodyContext context) { @@ -2149,6 +2157,13 @@ private static QuantifiedComparisonExpression.Quantifier getComparisonQuantifier throw new IllegalArgumentException("Unsupported quantifier: " + symbol.getText()); } + private List getTypes(SqlBaseParser.TypesContext types) + { + return types.type().stream() + .map(this::getType) + .collect(toImmutableList()); + } + private String getType(SqlBaseParser.TypeContext type) { if (type.baseType() != null) { diff --git a/presto-parser/src/main/java/com/facebook/presto/sql/tree/AstVisitor.java b/presto-parser/src/main/java/com/facebook/presto/sql/tree/AstVisitor.java index d4d488174079e..7451f88426fca 100644 --- a/presto-parser/src/main/java/com/facebook/presto/sql/tree/AstVisitor.java +++ b/presto-parser/src/main/java/com/facebook/presto/sql/tree/AstVisitor.java @@ -587,6 +587,16 @@ protected R visitCreateFunction(CreateFunction node, C context) return visitStatement(node, context); } + protected R visitDropFunction(CreateFunction node, C context) + { + return visitStatement(node, context); + } + + protected R visitDropFunction(DropFunction node, C context) + { + return visitStatement(node, context); + } + protected R visitInsert(Insert node, C context) { return visitStatement(node, context); diff --git a/presto-parser/src/main/java/com/facebook/presto/sql/tree/DropFunction.java b/presto-parser/src/main/java/com/facebook/presto/sql/tree/DropFunction.java new file mode 100644 index 0000000000000..5522c29d2d99b --- /dev/null +++ b/presto-parser/src/main/java/com/facebook/presto/sql/tree/DropFunction.java @@ -0,0 +1,107 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.sql.tree; + +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +public class DropFunction + extends Statement +{ + private final QualifiedName functionName; + private final Optional> parameterTypes; + private final boolean exists; + + public DropFunction(QualifiedName functionName, Optional> parameterTypes, boolean exists) + { + this(Optional.empty(), functionName, parameterTypes, exists); + } + + public DropFunction(NodeLocation location, QualifiedName functionName, Optional> parameterTypes, boolean exists) + { + this(Optional.of(location), functionName, parameterTypes, exists); + } + + private DropFunction(Optional location, QualifiedName functionName, Optional> parameterTypes, boolean exists) + { + super(location); + this.functionName = requireNonNull(functionName, "functionName is null"); + this.parameterTypes = requireNonNull(parameterTypes, "parameterTypes is null").map(ImmutableList::copyOf); + this.exists = exists; + } + + public QualifiedName getFunctionName() + { + return functionName; + } + + public Optional> getParameterTypes() + { + return parameterTypes; + } + + public boolean isExists() + { + return exists; + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitDropFunction(this, context); + } + + @Override + public List getChildren() + { + return ImmutableList.of(); + } + + @Override + public int hashCode() + { + return Objects.hash(functionName, parameterTypes, exists); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if ((obj == null) || (getClass() != obj.getClass())) { + return false; + } + DropFunction o = (DropFunction) obj; + return Objects.equals(functionName, o.functionName) + && Objects.equals(parameterTypes, o.parameterTypes) + && (exists == o.exists); + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("functionName", functionName) + .add("parameterTypes", parameterTypes) + .add("exists", exists) + .toString(); + } +} diff --git a/presto-parser/src/test/java/com/facebook/presto/sql/parser/TestSqlParser.java b/presto-parser/src/test/java/com/facebook/presto/sql/parser/TestSqlParser.java index 9d4aaf0eefba8..4a3f6d5e10743 100644 --- a/presto-parser/src/test/java/com/facebook/presto/sql/parser/TestSqlParser.java +++ b/presto-parser/src/test/java/com/facebook/presto/sql/parser/TestSqlParser.java @@ -47,6 +47,7 @@ import com.facebook.presto.sql.tree.DescribeOutput; import com.facebook.presto.sql.tree.DoubleLiteral; import com.facebook.presto.sql.tree.DropColumn; +import com.facebook.presto.sql.tree.DropFunction; import com.facebook.presto.sql.tree.DropRole; import com.facebook.presto.sql.tree.DropSchema; import com.facebook.presto.sql.tree.DropTable; @@ -1337,6 +1338,22 @@ public void testDropView() assertStatement("DROP VIEW IF EXISTS a.b.c", new DropView(QualifiedName.of("a", "b", "c"), true)); } + @Test + public void testDropFunction() + { + assertStatement("DROP FUNCTION a", new DropFunction(QualifiedName.of("a"), Optional.empty(), false)); + assertStatement("DROP FUNCTION a.b", new DropFunction(QualifiedName.of("a", "b"), Optional.empty(), false)); + assertStatement("DROP FUNCTION a.b.c", new DropFunction(QualifiedName.of("a", "b", "c"), Optional.empty(), false)); + + assertStatement("DROP FUNCTION a()", new DropFunction(QualifiedName.of("a"), Optional.of(ImmutableList.of()), false)); + assertStatement("DROP FUNCTION a.b()", new DropFunction(QualifiedName.of("a", "b"), Optional.of(ImmutableList.of()), false)); + assertStatement("DROP FUNCTION a.b.c()", new DropFunction(QualifiedName.of("a", "b", "c"), Optional.of(ImmutableList.of()), false)); + + assertStatement("DROP FUNCTION IF EXISTS a.b.c(int)", new DropFunction(QualifiedName.of("a", "b", "c"), Optional.of(ImmutableList.of("int")), true)); + assertStatement("DROP FUNCTION IF EXISTS a.b.c(bigint, double)", new DropFunction(QualifiedName.of("a", "b", "c"), Optional.of(ImmutableList.of("bigint", "double")), true)); + assertStatement("DROP FUNCTION IF EXISTS a.b.c(ARRAY(string), MAP(int,double))", new DropFunction(QualifiedName.of("a", "b", "c"), Optional.of(ImmutableList.of("ARRAY(string)", "MAP(int,double)")), true)); + } + @Test public void testInsertInto() { diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/function/FunctionNamespaceManager.java b/presto-spi/src/main/java/com/facebook/presto/spi/function/FunctionNamespaceManager.java index ad0740f2f29bb..a70c93ac1fab3 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/function/FunctionNamespaceManager.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/function/FunctionNamespaceManager.java @@ -14,8 +14,10 @@ package com.facebook.presto.spi.function; import com.facebook.presto.spi.api.Experimental; +import com.facebook.presto.spi.type.TypeSignature; import java.util.Collection; +import java.util.List; import java.util.Optional; @Experimental @@ -46,6 +48,12 @@ public interface FunctionNamespaceManager */ void createFunction(SqlInvokedFunction function, boolean replace); + /** + * Drop the specified function. + * TODO: Support transaction + */ + void dropFunction(QualifiedFunctionName functionName, Optional> parameterTypes, boolean exists); + /** * List all functions managed by the {@link FunctionNamespaceManager}. * TODO: Support transaction diff --git a/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/testing/SqlInvokedFunctionTestUtils.java b/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/testing/SqlInvokedFunctionTestUtils.java index 4ddd26621cbf2..36e3a565a6d0a 100644 --- a/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/testing/SqlInvokedFunctionTestUtils.java +++ b/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/testing/SqlInvokedFunctionTestUtils.java @@ -13,6 +13,7 @@ */ package com.facebook.presto.sqlfunction.testing; +import com.facebook.presto.spi.CatalogSchemaName; import com.facebook.presto.spi.function.QualifiedFunctionName; import com.facebook.presto.spi.function.RoutineCharacteristics; import com.facebook.presto.spi.function.SqlInvokedFunction; @@ -35,7 +36,7 @@ private SqlInvokedFunctionTestUtils() { } - public static final QualifiedFunctionName POWER_TOWER = QualifiedFunctionName.of("unittest.memory.power_tower"); + public static final QualifiedFunctionName POWER_TOWER = QualifiedFunctionName.of(new CatalogSchemaName("unittest", "memory"), "power_tower"); public static final SqlInvokedFunction FUNCTION_POWER_TOWER_DOUBLE = new SqlInvokedFunction( POWER_TOWER, diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueries.java b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueries.java index eb48c47c3de0b..e01070dfef2e0 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueries.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueries.java @@ -4653,6 +4653,7 @@ public void testExplainDdl() assertExplainDdl("CREATE TABLE foo (pk bigint)", "CREATE TABLE foo"); assertExplainDdl("CREATE VIEW foo AS SELECT * FROM orders", "CREATE VIEW foo"); assertExplainDdl("CREATE OR REPLACE FUNCTION testing.default.tan (x int) RETURNS double COMMENT 'tangent trigonometric function' LANGUAGE SQL DETERMINISTIC CALLED ON NULL INPUT RETURN sin(x) / cos(x)", "CREATE FUNCTION testing.default.tan"); + assertExplainDdl("DROP FUNCTION IF EXISTS testing.default.tan (int)", "DROP FUNCTION testing.default.tan"); assertExplainDdl("DROP TABLE orders"); assertExplainDdl("DROP VIEW view"); assertExplainDdl("ALTER TABLE orders RENAME TO new_name"); @@ -7981,6 +7982,7 @@ public void testDescribeOutputNonSelect() assertDescribeOutputEmpty("DROP TABLE foo"); assertDescribeOutputEmpty("CREATE VIEW foo AS SELECT * FROM nation"); assertDescribeOutputEmpty("CREATE FUNCTION testing.default.tan (x int) RETURNS double COMMENT 'tangent trigonometric function' LANGUAGE SQL DETERMINISTIC CALLED ON NULL INPUT RETURN sin(x) / cos(x)"); + assertDescribeOutputEmpty("DROP FUNCTION IF EXISTS testing.default.tan (int)"); assertDescribeOutputEmpty("DROP VIEW foo"); assertDescribeOutputEmpty("PREPARE test FROM SELECT * FROM orders"); diff --git a/presto-tests/src/test/java/com/facebook/presto/tests/TestSqlFunctions.java b/presto-tests/src/test/java/com/facebook/presto/tests/TestSqlFunctions.java index 61679d90abc3d..9a14d7f5c4265 100644 --- a/presto-tests/src/test/java/com/facebook/presto/tests/TestSqlFunctions.java +++ b/presto-tests/src/test/java/com/facebook/presto/tests/TestSqlFunctions.java @@ -29,7 +29,7 @@ public void testCreateFunctionInvalidFunctionName() { assertQueryFails( "CREATE FUNCTION testing.tan (x int) RETURNS double COMMENT 'tangent trigonometric function' RETURN sin(x) / cos(x)", - "Invalid function name: testing\\.tan, require exactly 3 parts"); + ".*Function name should be in the form of catalog\\.schema\\.function_name, found: testing\\.tan"); assertQueryFails( "CREATE FUNCTION presto.default.tan (x int) RETURNS double COMMENT 'tangent trigonometric function' RETURN sin(x) / cos(x)", "Cannot create function in built-in function namespace: presto\\.default\\.tan"); @@ -48,4 +48,15 @@ public void testCreateFunctionInvalidSemantics() "CREATE FUNCTION testing.default.tan (x double) RETURNS double COMMENT 'tangent trigonometric function' RETURN sum(x)", ".*CREATE FUNCTION body cannot contain aggregations, window functions or grouping operations:.*"); } + + @Test + public void testDropFunctionInvalidFunctionName() + { + assertQueryFails( + "DROP FUNCTION IF EXISTS testing.tan", + ".*Function name should be in the form of catalog\\.schema\\.function_name, found: testing\\.tan"); + assertQueryFails( + "DROP FUNCTION presto.default.tan (double)", + "Cannot drop function in built-in function namespace: presto\\.default\\.tan"); + } }