diff --git a/pom.xml b/pom.xml index 0a395518b0438..ec53d04cf9b71 100644 --- a/pom.xml +++ b/pom.xml @@ -138,6 +138,7 @@ presto-proxy presto-kudu presto-elasticsearch + presto-sql-function @@ -424,6 +425,12 @@ ${project.version} + + com.facebook.presto + presto-sql-function + ${project.version} + + com.fasterxml.jackson.module jackson-module-afterburner diff --git a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestBingTileFunctions.java b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestBingTileFunctions.java index 8a3450460805e..6b10db2a6bf4a 100644 --- a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestBingTileFunctions.java +++ b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestBingTileFunctions.java @@ -60,8 +60,8 @@ protected void registerFunctions() for (Type type : plugin.getTypes()) { functionAssertions.getTypeRegistry().addType(type); } - functionAssertions.getMetadata().addFunctions(extractFunctions(plugin.getFunctions())); - functionAssertions.getMetadata().addFunctions(ImmutableList.of(APPLY_FUNCTION)); + functionAssertions.getMetadata().registerBuiltInFunctions(extractFunctions(plugin.getFunctions())); + functionAssertions.getMetadata().registerBuiltInFunctions(ImmutableList.of(APPLY_FUNCTION)); FunctionManager functionManager = functionAssertions.getMetadata().getFunctionManager(); approxDistinct = functionManager.getAggregateFunctionImplementation( functionManager.lookupFunction("approx_distinct", fromTypes(BING_TILE))); diff --git a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestSphericalGeoFunctions.java b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestSphericalGeoFunctions.java index 4ea2936825d8f..7f8d2e5aa811a 100644 --- a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestSphericalGeoFunctions.java +++ b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestSphericalGeoFunctions.java @@ -47,7 +47,7 @@ protected void registerFunctions() for (Type type : plugin.getTypes()) { functionAssertions.getTypeRegistry().addType(type); } - functionAssertions.getMetadata().addFunctions(extractFunctions(plugin.getFunctions())); + functionAssertions.getMetadata().registerBuiltInFunctions(extractFunctions(plugin.getFunctions())); } @Test diff --git a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/aggregation/AbstractTestGeoAggregationFunctions.java b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/aggregation/AbstractTestGeoAggregationFunctions.java index 7dfaaf80c0be1..cb870e03ffc73 100644 --- a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/aggregation/AbstractTestGeoAggregationFunctions.java +++ b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/aggregation/AbstractTestGeoAggregationFunctions.java @@ -48,7 +48,7 @@ public void registerFunctions() for (Type type : plugin.getTypes()) { functionAssertions.getTypeRegistry().addType(type); } - functionAssertions.getMetadata().addFunctions(extractFunctions(plugin.getFunctions())); + functionAssertions.getMetadata().registerBuiltInFunctions(extractFunctions(plugin.getFunctions())); FunctionManager functionManager = functionAssertions.getMetadata().getFunctionManager(); function = functionManager.getAggregateFunctionImplementation( functionManager.lookupFunction(getFunctionName(), fromTypes(GEOMETRY))); diff --git a/presto-main/pom.xml b/presto-main/pom.xml index 51a190c05b569..3db93634f6d9c 100644 --- a/presto-main/pom.xml +++ b/presto-main/pom.xml @@ -71,6 +71,11 @@ presto-memory-context + + com.facebook.presto + presto-sql-function + + com.google.code.findbugs jsr305 diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/BuiltInFunction.java b/presto-main/src/main/java/com/facebook/presto/metadata/BuiltInFunction.java new file mode 100644 index 0000000000000..da402d101d808 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/metadata/BuiltInFunction.java @@ -0,0 +1,26 @@ +/* + * 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.metadata; + +import com.facebook.presto.spi.function.SqlFunction; + +public abstract class BuiltInFunction + implements SqlFunction +{ + @Override + public boolean isCalledOnNullInput() + { + return false; + } +} 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 e7eace859da5c..235ee37a6e117 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 @@ -151,12 +151,12 @@ import com.facebook.presto.operator.window.SqlWindowFunction; import com.facebook.presto.operator.window.WindowFunctionSupplier; import com.facebook.presto.spi.PrestoException; -import com.facebook.presto.spi.QueryId; import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.block.BlockEncodingSerde; import com.facebook.presto.spi.function.FunctionHandle; import com.facebook.presto.spi.function.FunctionMetadata; import com.facebook.presto.spi.function.FunctionNamespaceManager; +import com.facebook.presto.spi.function.FunctionNamespaceTransactionHandle; import com.facebook.presto.spi.function.OperatorType; import com.facebook.presto.spi.function.Signature; import com.facebook.presto.spi.function.SqlFunction; @@ -349,9 +349,10 @@ @ThreadSafe public class BuiltInFunctionNamespaceManager - implements FunctionNamespaceManager + implements FunctionNamespaceManager { public static final FullyQualifiedName.Prefix DEFAULT_NAMESPACE = FullyQualifiedName.of("presto.default.foo").getPrefix(); + public static final String NAME = "_builtin"; private final TypeManager typeManager; private final LoadingCache specializedFunctionKeyCache; @@ -664,11 +665,10 @@ public BuiltInFunctionNamespaceManager( builder.scalar(LegacyLogFunction.class); } - addFunctions(builder.getFunctions()); + registerBuiltInFunctions(builder.getFunctions()); } - @Override - public final synchronized void addFunctions(List functions) + public synchronized void registerBuiltInFunctions(List functions) { for (SqlFunction function : functions) { for (SqlFunction existingFunction : this.functions.list()) { @@ -679,19 +679,46 @@ public final synchronized void addFunctions(List function } @Override - public List listFunctions() + public void createFunction(BuiltInFunction function, boolean replace) + { + throw new UnsupportedOperationException("createFunction cannot be called on BuiltInFunctionNamespaceManager"); + } + + public String getName() + { + return NAME; + } + + @Override + public FunctionNamespaceTransactionHandle beginTransaction() + { + return new EmptyTransactionHandle(); + } + + @Override + public void commit(FunctionNamespaceTransactionHandle transactionHandle) + { + } + + @Override + public void rollback(FunctionNamespaceTransactionHandle transactionHandle) + { + } + + @Override + public Collection listFunctions() { return functions.list(); } @Override - public Collection getCandidates(QueryId queryId, FullyQualifiedName name) + public Collection getFunctions(Optional transactionHandle, FullyQualifiedName functionName) { - return functions.get(name); + return functions.get(functionName); } @Override - public FunctionHandle getFunctionHandle(QueryId queryId, Signature signature) + public FunctionHandle getFunctionHandle(Optional transactionHandle, Signature signature) { return new BuiltInFunctionHandle(signature); } @@ -709,7 +736,7 @@ public FunctionMetadata getFunctionMetadata(FunctionHandle functionHandle) throwIfInstanceOf(e.getCause(), PrestoException.class); throw e; } - SqlFunction function = functionKey.getFunction(); + BuiltInFunction function = functionKey.getFunction(); Optional operatorType = tryGetOperatorType(signature.getName()); if (operatorType.isPresent()) { return new FunctionMetadata( @@ -796,11 +823,11 @@ private SpecializedFunctionKey getSpecializedFunctionKey(Signature signature) private SpecializedFunctionKey doGetSpecializedFunctionKey(Signature signature) { - Iterable candidates = getCandidates(null, signature.getName()); + Iterable candidates = getFunctions(null, signature.getName()); // search for exact match Type returnType = typeManager.getType(signature.getReturnType()); List argumentTypeSignatureProviders = fromTypeSignatures(signature.getArgumentTypes()); - for (SqlFunction candidate : candidates) { + for (BuiltInFunction candidate : candidates) { Optional boundVariables = new SignatureBinder(typeManager, candidate.getSignature(), false) .bindVariables(argumentTypeSignatureProviders, returnType); if (boundVariables.isPresent()) { @@ -811,7 +838,7 @@ private SpecializedFunctionKey doGetSpecializedFunctionKey(Signature signature) // TODO: hack because there could be "type only" coercions (which aren't necessarily included as implicit casts), // so do a second pass allowing "type only" coercions List argumentTypes = resolveTypes(signature.getArgumentTypes(), typeManager); - for (SqlFunction candidate : candidates) { + for (BuiltInFunction candidate : candidates) { SignatureBinder binder = new SignatureBinder(typeManager, candidate.getSignature(), true); Optional boundVariables = binder.bindVariables(argumentTypeSignatureProviders, returnType); if (!boundVariables.isPresent()) { @@ -863,25 +890,30 @@ private SpecializedFunctionKey doGetSpecializedFunctionKey(Signature signature) throw new PrestoException(FUNCTION_IMPLEMENTATION_MISSING, format("%s not found", signature)); } + private static class EmptyTransactionHandle + implements FunctionNamespaceTransactionHandle + { + } + private static class FunctionMap { - private final Multimap functions; + private final Multimap functions; public FunctionMap() { functions = ImmutableListMultimap.of(); } - public FunctionMap(FunctionMap map, Iterable functions) + public FunctionMap(FunctionMap map, Iterable functions) { - this.functions = ImmutableListMultimap.builder() + this.functions = ImmutableListMultimap.builder() .putAll(map.functions) .putAll(Multimaps.index(functions, function -> function.getSignature().getName())) .build(); // Make sure all functions with the same name are aggregations or none of them are - for (Map.Entry> entry : this.functions.asMap().entrySet()) { - Collection values = entry.getValue(); + for (Map.Entry> entry : this.functions.asMap().entrySet()) { + Collection values = entry.getValue(); long aggregations = values.stream() .map(function -> function.getSignature().getKind()) .filter(kind -> kind == AGGREGATE) @@ -890,12 +922,12 @@ public FunctionMap(FunctionMap map, Iterable functions) } } - public List list() + public List list() { return ImmutableList.copyOf(functions.values()); } - public Collection get(FullyQualifiedName name) + public Collection get(FullyQualifiedName name) { return functions.get(name); } diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/FunctionExtractor.java b/presto-main/src/main/java/com/facebook/presto/metadata/FunctionExtractor.java index 28bf03507942c..5c40e63704318 100644 --- a/presto-main/src/main/java/com/facebook/presto/metadata/FunctionExtractor.java +++ b/presto-main/src/main/java/com/facebook/presto/metadata/FunctionExtractor.java @@ -18,7 +18,6 @@ import com.facebook.presto.spi.function.AggregationFunction; import com.facebook.presto.spi.function.ScalarFunction; import com.facebook.presto.spi.function.ScalarOperator; -import com.facebook.presto.spi.function.SqlFunction; import com.facebook.presto.spi.function.WindowFunction; import java.util.Collection; @@ -30,7 +29,7 @@ public final class FunctionExtractor { private FunctionExtractor() {} - public static List extractFunctions(Collection> classes) + public static List extractFunctions(Collection> classes) { return classes.stream() .map(FunctionExtractor::extractFunctions) @@ -38,7 +37,7 @@ public static List extractFunctions(Collection> .collect(toImmutableList()); } - public static List extractFunctions(Class clazz) + public static List extractFunctions(Class clazz) { if (WindowFunction.class.isAssignableFrom(clazz)) { @SuppressWarnings("unchecked") diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/FunctionListBuilder.java b/presto-main/src/main/java/com/facebook/presto/metadata/FunctionListBuilder.java index 5dd73d711cd01..d250be11f33b0 100644 --- a/presto-main/src/main/java/com/facebook/presto/metadata/FunctionListBuilder.java +++ b/presto-main/src/main/java/com/facebook/presto/metadata/FunctionListBuilder.java @@ -15,7 +15,6 @@ import com.facebook.presto.operator.scalar.annotations.ScalarFromAnnotationsParser; import com.facebook.presto.operator.window.WindowAnnotationsParser; -import com.facebook.presto.spi.function.SqlFunction; import com.facebook.presto.spi.function.WindowFunction; import com.google.common.collect.ImmutableList; @@ -26,7 +25,7 @@ public class FunctionListBuilder { - private final List functions = new ArrayList<>(); + private final List functions = new ArrayList<>(); public FunctionListBuilder window(Class clazz) { @@ -58,22 +57,22 @@ public FunctionListBuilder scalars(Class clazz) return this; } - public FunctionListBuilder functions(SqlFunction... sqlFunctions) + public FunctionListBuilder functions(BuiltInFunction... sqlFunctions) { - for (SqlFunction sqlFunction : sqlFunctions) { + for (BuiltInFunction sqlFunction : sqlFunctions) { function(sqlFunction); } return this; } - public FunctionListBuilder function(SqlFunction sqlFunction) + public FunctionListBuilder function(BuiltInFunction sqlFunction) { requireNonNull(sqlFunction, "parametricFunction is null"); functions.add(sqlFunction); return this; } - public List getFunctions() + public List getFunctions() { return ImmutableList.copyOf(functions); } 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 e2f153b1b861b..32c508c9039c7 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 @@ -18,7 +18,6 @@ import com.facebook.presto.operator.scalar.ScalarFunctionImplementation; import com.facebook.presto.operator.window.WindowFunctionSupplier; import com.facebook.presto.spi.PrestoException; -import com.facebook.presto.spi.QueryId; import com.facebook.presto.spi.block.BlockEncodingSerde; import com.facebook.presto.spi.function.FunctionHandle; import com.facebook.presto.spi.function.FunctionKind; @@ -26,6 +25,7 @@ import com.facebook.presto.spi.function.FunctionMetadataManager; import com.facebook.presto.spi.function.FunctionNamespaceManager; import com.facebook.presto.spi.function.FunctionNamespaceManagerFactory; +import com.facebook.presto.spi.function.FunctionNamespaceTransactionHandle; import com.facebook.presto.spi.function.OperatorType; import com.facebook.presto.spi.function.Signature; import com.facebook.presto.spi.function.SqlFunction; @@ -36,6 +36,7 @@ import com.facebook.presto.sql.analyzer.FeaturesConfig; import com.facebook.presto.sql.analyzer.TypeSignatureProvider; import com.facebook.presto.sql.tree.QualifiedName; +import com.facebook.presto.transaction.TransactionManager; import com.facebook.presto.type.TypeRegistry; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; @@ -65,6 +66,7 @@ import static com.facebook.presto.sql.analyzer.TypeSignatureProvider.fromTypeSignatures; import static com.facebook.presto.sql.planner.LiteralEncoder.MAGIC_LITERAL_FUNCTION_PREFIX; import static com.facebook.presto.sql.planner.LiteralEncoder.getMagicLiteralFunctionSignature; +import static com.facebook.presto.transaction.InMemoryTransactionManager.createTestTransactionManager; import static com.facebook.presto.type.TypeUtils.resolveTypes; import static com.facebook.presto.type.UnknownType.UNKNOWN; import static com.google.common.base.MoreObjects.toStringHelper; @@ -83,40 +85,51 @@ public class FunctionManager implements FunctionMetadataManager { private final TypeManager typeManager; - private final BuiltInFunctionNamespaceManager builtInFunctionNamespace; + private final TransactionManager transactionManager; + private final BuiltInFunctionNamespaceManager builtInFunctionNamespaceManager; private final FunctionInvokerProvider functionInvokerProvider; - private final Map functionNamespaceFactories = new ConcurrentHashMap<>(); + private final Map functionNamespaceManagerFactories = new ConcurrentHashMap<>(); private final HandleResolver handleResolver; - private final Map functionNamespaces = new ConcurrentHashMap<>(); + private final Map> functionNamespaces = new ConcurrentHashMap<>(); @Inject - public FunctionManager(TypeManager typeManager, BlockEncodingSerde blockEncodingSerde, FeaturesConfig featuresConfig, HandleResolver handleResolver) + public FunctionManager( + TypeManager typeManager, + TransactionManager transactionManager, + BlockEncodingSerde blockEncodingSerde, + FeaturesConfig featuresConfig, + HandleResolver handleResolver) { this.typeManager = requireNonNull(typeManager, "typeManager is null"); - this.builtInFunctionNamespace = new BuiltInFunctionNamespaceManager(typeManager, blockEncodingSerde, featuresConfig, this); - this.functionNamespaces.put(DEFAULT_NAMESPACE, builtInFunctionNamespace); + this.transactionManager = requireNonNull(transactionManager, "transactionManager is null"); + this.builtInFunctionNamespaceManager = new BuiltInFunctionNamespaceManager(typeManager, blockEncodingSerde, featuresConfig, this); + this.functionNamespaces.put(DEFAULT_NAMESPACE, builtInFunctionNamespaceManager); this.functionInvokerProvider = new FunctionInvokerProvider(this); this.handleResolver = handleResolver; if (typeManager instanceof TypeRegistry) { ((TypeRegistry) typeManager).setFunctionManager(this); } + // TODO: Provide a more encapsulated way for TransactionManager to register FunctionNamespaceManager + transactionManager.registerFunctionNamespaceManager(BuiltInFunctionNamespaceManager.NAME, builtInFunctionNamespaceManager); } @VisibleForTesting public FunctionManager(TypeManager typeManager, BlockEncodingSerde blockEncodingSerde, FeaturesConfig featuresConfig) { - this(typeManager, blockEncodingSerde, featuresConfig, new HandleResolver()); + // TODO: Convert this constructor to a function in the testing package + this(typeManager, createTestTransactionManager(), blockEncodingSerde, featuresConfig, new HandleResolver()); } public void loadFunctionNamespaces(String functionNamespaceManagerName, List functionNamespacePrefixes, Map properties) { requireNonNull(functionNamespaceManagerName, "connectorName is null"); - FunctionNamespaceManagerFactory factory = functionNamespaceFactories.get(functionNamespaceManagerName); + FunctionNamespaceManagerFactory factory = functionNamespaceManagerFactories.get(functionNamespaceManagerName); checkState(factory != null, "No function namespace manager for %s", functionNamespaceManagerName); - FunctionNamespaceManager manager = factory.create(properties); + FunctionNamespaceManager functionNamespaceManager = factory.create(properties); + transactionManager.registerFunctionNamespaceManager(functionNamespaceManagerName, functionNamespaceManager); for (String functionNamespacePrefix : functionNamespacePrefixes) { - if (functionNamespaces.putIfAbsent(FullyQualifiedName.Prefix.of(functionNamespacePrefix), manager) != null) { + if (functionNamespaces.putIfAbsent(FullyQualifiedName.Prefix.of(functionNamespacePrefix), functionNamespaceManager) != null) { throw new IllegalArgumentException(format("Function namespace manager '%s' already registered to handle function namespace '%s'", factory.getName(), functionNamespacePrefix)); } } @@ -129,20 +142,20 @@ public FunctionInvokerProvider getFunctionInvokerProvider() public void addFunctionNamespaceFactory(FunctionNamespaceManagerFactory factory) { - if (functionNamespaceFactories.putIfAbsent(factory.getName(), factory) != null) { + if (functionNamespaceManagerFactories.putIfAbsent(factory.getName(), factory) != null) { throw new IllegalArgumentException(format("Resource group configuration manager '%s' is already registered", factory.getName())); } handleResolver.addFunctionNamespace(factory.getName(), factory.getHandleResolver()); } - public void addFunctions(List functions) + public void registerBuiltInFunctions(List functions) { - builtInFunctionNamespace.addFunctions(functions); + builtInFunctionNamespaceManager.registerBuiltInFunctions(functions); } public List listFunctions() { - return builtInFunctionNamespace.listFunctions().stream() + return builtInFunctionNamespaceManager.listFunctions().stream() .filter(function -> !function.isHidden()) .collect(toImmutableList()); } @@ -157,24 +170,30 @@ public List listFunctions() */ public FunctionHandle resolveFunction(Session session, QualifiedName name, List parameterTypes) { - FullyQualifiedName fullyQualifiedName; + return resolveFunction(Optional.of(session), name, parameterTypes); + } + + private FunctionHandle resolveFunction(Optional session, QualifiedName name, List parameterTypes) + { + FullyQualifiedName functionName; if (!name.getPrefix().isPresent()) { - fullyQualifiedName = FullyQualifiedName.of(DEFAULT_NAMESPACE, name.getSuffix()); + functionName = FullyQualifiedName.of(DEFAULT_NAMESPACE, name.getSuffix()); } else { - fullyQualifiedName = FullyQualifiedName.of(name.getOriginalParts()); + functionName = FullyQualifiedName.of(name.getOriginalParts()); } - Optional servingNamespaceManager = getServingNamespaceManager(fullyQualifiedName); - if (!servingNamespaceManager.isPresent()) { + Optional> functionNamespaceManager = getServingFunctionNamespaceManager(functionName); + if (!functionNamespaceManager.isPresent()) { throw new PrestoException(FUNCTION_NOT_FOUND, format("Cannot find function namespace for function %s", name)); } - QueryId queryId = session == null ? null : session.getQueryId(); - Collection allCandidates = servingNamespaceManager.get().getCandidates(queryId, fullyQualifiedName); + Optional transactionHandle = session.flatMap(Session::getTransactionId) + .map(transactionId -> transactionManager.getFunctionNamespaceTransaction(transactionId, functionNamespaceManager.get().getName())); + Collection candidates = functionNamespaceManager.get().getFunctions(transactionHandle, functionName); try { - return lookupFunction(servingNamespaceManager.get(), queryId, fullyQualifiedName, parameterTypes, allCandidates); + return lookupFunction(functionNamespaceManager.get(), transactionHandle, functionName, parameterTypes, candidates); } catch (PrestoException e) { if (e.getErrorCode().getCode() != FUNCTION_NOT_FOUND.toErrorCode().getCode()) { @@ -182,9 +201,9 @@ public FunctionHandle resolveFunction(Session session, QualifiedName name, List< } } - Optional match = matchFunctionWithCoercion(allCandidates, parameterTypes); + Optional match = matchFunctionWithCoercion(candidates, parameterTypes); if (match.isPresent()) { - return servingNamespaceManager.get().getFunctionHandle(queryId, match.get()); + return functionNamespaceManager.get().getFunctionHandle(transactionHandle, match.get()); } if (name.getSuffix().startsWith(MAGIC_LITERAL_FUNCTION_PREFIX)) { @@ -200,7 +219,7 @@ public FunctionHandle resolveFunction(Session session, QualifiedName name, List< return new BuiltInFunctionHandle(getMagicLiteralFunctionSignature(type)); } - throw new PrestoException(FUNCTION_NOT_FOUND, constructFunctionNotFoundErrorMessage(name.toString(), parameterTypes, allCandidates)); + throw new PrestoException(FUNCTION_NOT_FOUND, constructFunctionNotFoundErrorMessage(name.toString(), parameterTypes, candidates)); } @Override @@ -211,17 +230,17 @@ public FunctionMetadata getFunctionMetadata(FunctionHandle functionHandle) public WindowFunctionSupplier getWindowFunctionImplementation(FunctionHandle functionHandle) { - return builtInFunctionNamespace.getWindowFunctionImplementation(functionHandle); + return builtInFunctionNamespaceManager.getWindowFunctionImplementation(functionHandle); } public InternalAggregationFunction getAggregateFunctionImplementation(FunctionHandle functionHandle) { - return builtInFunctionNamespace.getAggregateFunctionImplementation(functionHandle); + return builtInFunctionNamespaceManager.getAggregateFunctionImplementation(functionHandle); } public ScalarFunctionImplementation getScalarFunctionImplementation(FunctionHandle functionHandle) { - return builtInFunctionNamespace.getScalarFunctionImplementation(functionHandle); + return builtInFunctionNamespaceManager.getScalarFunctionImplementation(functionHandle); } @VisibleForTesting @@ -231,7 +250,7 @@ public List listOperators() .map(OperatorType::getFunctionName) .collect(toImmutableSet()); - return builtInFunctionNamespace.listFunctions().stream() + return builtInFunctionNamespaceManager.listFunctions().stream() .filter(function -> operatorNames.contains(function.getSignature().getName())) .collect(toImmutableList()); } @@ -239,7 +258,7 @@ public List listOperators() public FunctionHandle resolveOperator(OperatorType operatorType, List argumentTypes) { try { - return resolveFunction(null, QualifiedName.of(operatorType.getFunctionName().getParts()), argumentTypes); + return resolveFunction(Optional.empty(), QualifiedName.of(operatorType.getFunctionName().getParts()), argumentTypes); } catch (PrestoException e) { if (e.getErrorCode().getCode() == FUNCTION_NOT_FOUND.toErrorCode().getCode()) { @@ -263,9 +282,9 @@ public FunctionHandle resolveOperator(OperatorType operatorType, List parameterTypes) { - FullyQualifiedName fullyQualifiedName = FullyQualifiedName.of(DEFAULT_NAMESPACE, name); - Collection allCandidates = builtInFunctionNamespace.getCandidates(null, fullyQualifiedName); - return lookupFunction(builtInFunctionNamespace, null, fullyQualifiedName, parameterTypes, allCandidates); + FullyQualifiedName functionName = FullyQualifiedName.of(DEFAULT_NAMESPACE, name); + Collection candidates = builtInFunctionNamespaceManager.getFunctions(Optional.empty(), functionName); + return lookupFunction(builtInFunctionNamespaceManager, Optional.empty(), functionName, parameterTypes, candidates); } public FunctionHandle lookupCast(CastType castType, TypeSignature fromType, TypeSignature toType) @@ -273,7 +292,7 @@ public FunctionHandle lookupCast(CastType castType, TypeSignature fromType, Type Signature signature = new Signature(castType.getCastName(), SCALAR, emptyList(), emptyList(), toType, singletonList(fromType), false); try { - builtInFunctionNamespace.getScalarFunctionImplementation(signature); + builtInFunctionNamespaceManager.getScalarFunctionImplementation(signature); } catch (PrestoException e) { if (castType.isOperatorType() && e.getErrorCode().getCode() == FUNCTION_IMPLEMENTATION_MISSING.toErrorCode().getCode()) { @@ -281,52 +300,57 @@ public FunctionHandle lookupCast(CastType castType, TypeSignature fromType, Type } throw e; } - return builtInFunctionNamespace.getFunctionHandle(null, signature); + return builtInFunctionNamespaceManager.getFunctionHandle(Optional.empty(), signature); } - private FunctionHandle lookupFunction(FunctionNamespaceManager functionNamespaceManager, QueryId queryId, FullyQualifiedName name, List parameterTypes, Collection allCandidates) + private FunctionHandle lookupFunction( + FunctionNamespaceManager functionNamespaceManager, + Optional transactionHandle, + FullyQualifiedName functionName, + List parameterTypes, + Collection candidates) { - List exactCandidates = allCandidates.stream() + List exactCandidates = candidates.stream() .filter(function -> function.getSignature().getTypeVariableConstraints().isEmpty()) .collect(Collectors.toList()); Optional match = matchFunctionExact(exactCandidates, parameterTypes); if (match.isPresent()) { - return functionNamespaceManager.getFunctionHandle(queryId, match.get()); + return functionNamespaceManager.getFunctionHandle(transactionHandle, match.get()); } - List genericCandidates = allCandidates.stream() + List genericCandidates = candidates.stream() .filter(function -> !function.getSignature().getTypeVariableConstraints().isEmpty()) .collect(Collectors.toList()); match = matchFunctionExact(genericCandidates, parameterTypes); if (match.isPresent()) { - return functionNamespaceManager.getFunctionHandle(queryId, match.get()); + return functionNamespaceManager.getFunctionHandle(transactionHandle, match.get()); } - throw new PrestoException(FUNCTION_NOT_FOUND, constructFunctionNotFoundErrorMessage(name.toString(), parameterTypes, allCandidates)); + throw new PrestoException(FUNCTION_NOT_FOUND, constructFunctionNotFoundErrorMessage(functionName.toString(), parameterTypes, candidates)); } - private Optional getServingNamespaceManager(FullyQualifiedName name) + private Optional> getServingFunctionNamespaceManager(FullyQualifiedName functionName) { - FullyQualifiedName.Prefix functionPrefix = name.getPrefix(); + FullyQualifiedName.Prefix functionPrefix = functionName.getPrefix(); if (functionPrefix.equals(DEFAULT_NAMESPACE)) { - return Optional.of(builtInFunctionNamespace); + return Optional.of(builtInFunctionNamespaceManager); } FullyQualifiedName.Prefix bestMatchNamespace = null; - FunctionNamespaceManager servingNamespaceManager = null; + FunctionNamespaceManager servingFunctionNamespaceManager = null; - for (Map.Entry functionNamespace : functionNamespaces.entrySet()) { + for (Map.Entry> functionNamespace : functionNamespaces.entrySet()) { if (functionNamespace.getKey().contains(functionPrefix) && (bestMatchNamespace == null || bestMatchNamespace.contains(functionNamespace.getKey()))) { bestMatchNamespace = functionNamespace.getKey(); - servingNamespaceManager = functionNamespace.getValue(); + servingFunctionNamespaceManager = functionNamespace.getValue(); } } - return Optional.ofNullable(servingNamespaceManager); + return Optional.ofNullable(servingFunctionNamespaceManager); } - private String constructFunctionNotFoundErrorMessage(String name, List parameterTypes, Collection candidates) + private String constructFunctionNotFoundErrorMessage(String name, List parameterTypes, Collection candidates) { List expectedParameters = new ArrayList<>(); for (SqlFunction function : candidates) { @@ -349,12 +373,12 @@ private Optional matchFunctionExact(List candidates, Lis return matchFunction(candidates, actualParameters, false); } - private Optional matchFunctionWithCoercion(Collection candidates, List actualParameters) + private Optional matchFunctionWithCoercion(Collection candidates, List actualParameters) { return matchFunction(candidates, actualParameters, true); } - private Optional matchFunction(Collection candidates, List parameters, boolean coercionAllowed) + private Optional matchFunction(Collection candidates, List parameters, boolean coercionAllowed) { List applicableFunctions = identifyApplicableFunctions(candidates, parameters, coercionAllowed); if (applicableFunctions.isEmpty()) { @@ -381,7 +405,7 @@ private Optional matchFunction(Collection candidates, Li throw new PrestoException(AMBIGUOUS_FUNCTION_CALL, errorMessageBuilder.toString()); } - private List identifyApplicableFunctions(Collection candidates, List actualParameters, boolean allowCoercion) + private List identifyApplicableFunctions(Collection candidates, List actualParameters, boolean allowCoercion) { ImmutableList.Builder applicableFunctions = ImmutableList.builder(); for (SqlFunction function : candidates) { @@ -551,13 +575,13 @@ private static class ApplicableFunction { private final Signature declaredSignature; private final Signature boundSignature; - private final boolean isCalledOnNullInput; + private final boolean calledOnNullInput; - private ApplicableFunction(Signature declaredSignature, Signature boundSignature, boolean isCalledOnNullInput) + private ApplicableFunction(Signature declaredSignature, Signature boundSignature, boolean calledOnNullInput) { this.declaredSignature = declaredSignature; this.boundSignature = boundSignature; - this.isCalledOnNullInput = isCalledOnNullInput; + this.calledOnNullInput = calledOnNullInput; } public Signature getDeclaredSignature() @@ -572,7 +596,7 @@ public Signature getBoundSignature() public boolean isCalledOnNullInput() { - return isCalledOnNullInput; + return calledOnNullInput; } @Override @@ -581,6 +605,7 @@ public String toString() return toStringHelper(this) .add("declaredSignature", declaredSignature) .add("boundSignature", boundSignature) + .add("calledOnNullInput", calledOnNullInput) .toString(); } } diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/Metadata.java b/presto-main/src/main/java/com/facebook/presto/metadata/Metadata.java index 2742dbdb7ba30..e9dd24fa64d67 100644 --- a/presto-main/src/main/java/com/facebook/presto/metadata/Metadata.java +++ b/presto-main/src/main/java/com/facebook/presto/metadata/Metadata.java @@ -60,7 +60,7 @@ public interface Metadata List listFunctions(); - void addFunctions(List functions); + void registerBuiltInFunctions(List functions); boolean schemaExists(Session session, CatalogSchemaName schema); diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/MetadataManager.java b/presto-main/src/main/java/com/facebook/presto/metadata/MetadataManager.java index be1f836f8405b..f8fb5dedf1a47 100644 --- a/presto-main/src/main/java/com/facebook/presto/metadata/MetadataManager.java +++ b/presto-main/src/main/java/com/facebook/presto/metadata/MetadataManager.java @@ -155,7 +155,7 @@ public MetadataManager( columnPropertyManager, analyzePropertyManager, transactionManager, - new FunctionManager(typeManager, blockEncodingSerde, featuresConfig)); + new FunctionManager(typeManager, transactionManager, blockEncodingSerde, featuresConfig, new HandleResolver())); } @Inject @@ -271,10 +271,9 @@ public List listFunctions() } @Override - public void addFunctions(List functionInfos) + public void registerBuiltInFunctions(List functionInfos) { - // TODO: transactional when FunctionManager is made transactional - functions.addFunctions(functionInfos); + functions.registerBuiltInFunctions(functionInfos); } @Override diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/SpecializedFunctionKey.java b/presto-main/src/main/java/com/facebook/presto/metadata/SpecializedFunctionKey.java index 6fc00fb7bd33f..17c506b4be101 100644 --- a/presto-main/src/main/java/com/facebook/presto/metadata/SpecializedFunctionKey.java +++ b/presto-main/src/main/java/com/facebook/presto/metadata/SpecializedFunctionKey.java @@ -13,26 +13,24 @@ */ package com.facebook.presto.metadata; -import com.facebook.presto.spi.function.SqlFunction; - import java.util.Objects; import static java.util.Objects.requireNonNull; public class SpecializedFunctionKey { - private final SqlFunction function; + private final BuiltInFunction function; private final BoundVariables boundVariables; private final int arity; - public SpecializedFunctionKey(SqlFunction function, BoundVariables boundVariables, int arity) + public SpecializedFunctionKey(BuiltInFunction function, BoundVariables boundVariables, int arity) { this.function = requireNonNull(function, "function is null"); this.boundVariables = requireNonNull(boundVariables, "boundVariables is null"); this.arity = arity; } - public SqlFunction getFunction() + public BuiltInFunction getFunction() { return function; } diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/SqlAggregationFunction.java b/presto-main/src/main/java/com/facebook/presto/metadata/SqlAggregationFunction.java index bcb34163df113..cbe3a29b34f98 100644 --- a/presto-main/src/main/java/com/facebook/presto/metadata/SqlAggregationFunction.java +++ b/presto-main/src/main/java/com/facebook/presto/metadata/SqlAggregationFunction.java @@ -18,7 +18,6 @@ import com.facebook.presto.spi.function.FunctionKind; import com.facebook.presto.spi.function.LongVariableConstraint; import com.facebook.presto.spi.function.Signature; -import com.facebook.presto.spi.function.SqlFunction; import com.facebook.presto.spi.function.TypeVariableConstraint; import com.facebook.presto.spi.relation.FullyQualifiedName; import com.facebook.presto.spi.type.TypeManager; @@ -34,7 +33,7 @@ import static java.util.Objects.requireNonNull; public abstract class SqlAggregationFunction - implements SqlFunction + extends BuiltInFunction { private final Signature signature; private final boolean hidden; diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/SqlScalarFunction.java b/presto-main/src/main/java/com/facebook/presto/metadata/SqlScalarFunction.java index 14f856fc8a322..7596cf08ed870 100644 --- a/presto-main/src/main/java/com/facebook/presto/metadata/SqlScalarFunction.java +++ b/presto-main/src/main/java/com/facebook/presto/metadata/SqlScalarFunction.java @@ -16,7 +16,6 @@ import com.facebook.presto.operator.scalar.ScalarFunctionImplementation; import com.facebook.presto.spi.function.OperatorType; import com.facebook.presto.spi.function.Signature; -import com.facebook.presto.spi.function.SqlFunction; import com.facebook.presto.spi.type.TypeManager; import static com.facebook.presto.spi.function.FunctionKind.SCALAR; @@ -24,7 +23,7 @@ import static java.util.Objects.requireNonNull; public abstract class SqlScalarFunction - implements SqlFunction + extends BuiltInFunction { private final Signature signature; diff --git a/presto-main/src/main/java/com/facebook/presto/operator/window/SqlWindowFunction.java b/presto-main/src/main/java/com/facebook/presto/operator/window/SqlWindowFunction.java index 63fab1b98f3a1..4795c2e64674d 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/window/SqlWindowFunction.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/window/SqlWindowFunction.java @@ -14,15 +14,15 @@ package com.facebook.presto.operator.window; import com.facebook.presto.metadata.BoundVariables; +import com.facebook.presto.metadata.BuiltInFunction; import com.facebook.presto.metadata.FunctionManager; import com.facebook.presto.spi.function.Signature; -import com.facebook.presto.spi.function.SqlFunction; import com.facebook.presto.spi.type.TypeManager; import static java.util.Objects.requireNonNull; public class SqlWindowFunction - implements SqlFunction + extends BuiltInFunction { private final WindowFunctionSupplier supplier; diff --git a/presto-main/src/main/java/com/facebook/presto/server/PluginManager.java b/presto-main/src/main/java/com/facebook/presto/server/PluginManager.java index 01261d36ab3b5..d199b6e4fe46e 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/PluginManager.java +++ b/presto-main/src/main/java/com/facebook/presto/server/PluginManager.java @@ -208,7 +208,7 @@ public void installPlugin(Plugin plugin) for (Class functionClass : plugin.getFunctions()) { log.info("Registering functions from %s", functionClass.getName()); - metadata.addFunctions(extractFunctions(functionClass)); + metadata.registerBuiltInFunctions(extractFunctions(functionClass)); } for (FunctionNamespaceManagerFactory functionNamespaceManagerFactory : plugin.getFunctionNamespaceManagerFactories()) { 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 new file mode 100644 index 0000000000000..e3b3e5d8ace6a --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/testing/InMemoryFunctionNamespaceManager.java @@ -0,0 +1,108 @@ +/* + * 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.testing; + +import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.function.FunctionMetadata; +import com.facebook.presto.spi.relation.FullyQualifiedName; +import com.facebook.presto.sqlfunction.AbstractSqlInvokedFunctionNamespaceManager; +import com.facebook.presto.sqlfunction.SqlFunctionId; +import com.facebook.presto.sqlfunction.SqlInvokedFunctionNamespaceManagerConfig; +import com.facebook.presto.sqlfunction.SqlInvokedRegularFunction; +import com.facebook.presto.sqlfunction.SqlInvokedRegularFunctionHandle; + +import javax.annotation.concurrent.ThreadSafe; + +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import static com.facebook.presto.spi.StandardErrorCode.GENERIC_USER_ERROR; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.common.collect.MoreCollectors.onlyElement; +import static java.lang.String.format; + +@ThreadSafe +public class InMemoryFunctionNamespaceManager + extends AbstractSqlInvokedFunctionNamespaceManager +{ + private static final String NAME = "_in_memory"; + private final Map latestFunctions = new ConcurrentHashMap<>(); + + public InMemoryFunctionNamespaceManager(SqlInvokedFunctionNamespaceManagerConfig config) + { + super(config); + } + + @Override + public String getName() + { + return NAME; + } + + @Override + public synchronized void createFunction(SqlInvokedRegularFunction function, boolean replace) + { + SqlFunctionId functionId = new SqlFunctionId(function.getSignature().getName(), function.getSignature().getArgumentTypes()); + if (!replace && latestFunctions.containsKey(functionId)) { + throw new PrestoException(GENERIC_USER_ERROR, format("Function '%s' already exists", functionId.getName())); + } + + SqlInvokedRegularFunction replacedFunction = latestFunctions.get(functionId); + long version = 1; + if (replacedFunction != null) { + checkArgument(replacedFunction.getVersion().isPresent(), "missing version in replaced function"); + version = replacedFunction.getVersion().get() + 1; + } + function = SqlInvokedRegularFunction.versioned(function, version); + latestFunctions.put(functionId, function); + } + + @Override + public Collection listFunctions() + { + return latestFunctions.values(); + } + + @Override + public Collection fetchFunctionsDirect(FullyQualifiedName name) + { + return latestFunctions.values().stream() + .filter(function -> function.getSignature().getName().equals(name)) + .map(InMemoryFunctionNamespaceManager::copyFunction) + .collect(toImmutableList()); + } + + @Override + public FunctionMetadata fetchFunctionMetadataDirect(SqlInvokedRegularFunctionHandle functionHandle) + { + return fetchFunctionsDirect(functionHandle.getName()).stream() + .filter(function -> function.getRequiredFunctionHandle().equals(functionHandle)) + .map(AbstractSqlInvokedFunctionNamespaceManager::sqlInvokedFunctionToMetadata) + .collect(onlyElement()); + } + + private static SqlInvokedRegularFunction copyFunction(SqlInvokedRegularFunction function) + { + return new SqlInvokedRegularFunction( + function.getSignature().getName(), + function.getParameters(), + function.getSignature().getReturnType(), + function.getComment(), + function.getRoutineCharacteristics(), + function.getBody(), + function.getVersion()); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/transaction/InMemoryTransactionManager.java b/presto-main/src/main/java/com/facebook/presto/transaction/InMemoryTransactionManager.java index f41e40a0de863..74c24c9158afa 100644 --- a/presto-main/src/main/java/com/facebook/presto/transaction/InMemoryTransactionManager.java +++ b/presto-main/src/main/java/com/facebook/presto/transaction/InMemoryTransactionManager.java @@ -21,6 +21,8 @@ import com.facebook.presto.spi.connector.Connector; import com.facebook.presto.spi.connector.ConnectorMetadata; import com.facebook.presto.spi.connector.ConnectorTransactionHandle; +import com.facebook.presto.spi.function.FunctionNamespaceManager; +import com.facebook.presto.spi.function.FunctionNamespaceTransactionHandle; import com.facebook.presto.spi.transaction.IsolationLevel; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -86,6 +88,8 @@ public class InMemoryTransactionManager private final CatalogManager catalogManager; private final Executor finishingExecutor; + private final Map> functionNamespaceManagers = new HashMap<>(); + private InMemoryTransactionManager(Duration idleTimeout, int maxFinishingConcurrency, CatalogManager catalogManager, Executor finishingExecutor) { this.catalogManager = catalogManager; @@ -176,7 +180,7 @@ public TransactionId beginTransaction(IsolationLevel isolationLevel, boolean rea { TransactionId transactionId = TransactionId.create(); BoundedExecutor executor = new BoundedExecutor(finishingExecutor, maxFinishingConcurrency); - TransactionMetadata transactionMetadata = new TransactionMetadata(transactionId, isolationLevel, readOnly, autoCommitContext, catalogManager, executor); + TransactionMetadata transactionMetadata = new TransactionMetadata(transactionId, isolationLevel, readOnly, autoCommitContext, catalogManager, executor, functionNamespaceManagers); checkState(transactions.put(transactionId, transactionMetadata) == null, "Duplicate transaction ID: %s", transactionId); return transactionId; } @@ -227,6 +231,19 @@ public ConnectorTransactionHandle getConnectorTransaction(TransactionId transact return getCatalogMetadata(transactionId, connectorId).getTransactionHandleFor(connectorId); } + @Override + public synchronized void registerFunctionNamespaceManager(String functionNamespaceManagerName, FunctionNamespaceManager functionNamespaceManager) + { + checkArgument(!functionNamespaceManagers.containsKey(functionNamespaceManagerName), "FunctionNamespaceManager %s is already registered", functionNamespaceManagerName); + functionNamespaceManagers.put(functionNamespaceManagerName, functionNamespaceManager); + } + + @Override + public FunctionNamespaceTransactionHandle getFunctionNamespaceTransaction(TransactionId transactionId, String functionNamespaceManagerName) + { + return getTransactionMetadata(transactionId).getFunctionNamespaceTransaction(functionNamespaceManagerName); + } + private void checkConnectorWrite(TransactionId transactionId, ConnectorId connectorId) { getTransactionMetadata(transactionId).checkConnectorWrite(connectorId); @@ -323,13 +340,18 @@ private static class TransactionMetadata @GuardedBy("this") private final Map catalogMetadata = new ConcurrentHashMap<>(); + private final Map> functionNamespaceManagers; + @GuardedBy("this") + private final Map functionNamespaceTransactions = new ConcurrentHashMap<>(); + public TransactionMetadata( TransactionId transactionId, IsolationLevel isolationLevel, boolean readOnly, boolean autoCommitContext, CatalogManager catalogManager, - Executor finishingExecutor) + Executor finishingExecutor, + Map> functionNamespaceManagers) { this.transactionId = requireNonNull(transactionId, "transactionId is null"); this.isolationLevel = requireNonNull(isolationLevel, "isolationLevel is null"); @@ -337,6 +359,7 @@ public TransactionMetadata( this.autoCommitContext = autoCommitContext; this.catalogManager = requireNonNull(catalogManager, "catalogManager is null"); this.finishingExecutor = listeningDecorator(ExecutorServiceAdapter.from(requireNonNull(finishingExecutor, "finishingExecutor is null"))); + this.functionNamespaceManagers = requireNonNull(functionNamespaceManagers, "functionNamespaceManagers is null"); } public void setActive() @@ -437,6 +460,13 @@ private synchronized CatalogMetadata getTransactionCatalogMetadata(ConnectorId c return catalogMetadata; } + private synchronized FunctionNamespaceTransactionHandle getFunctionNamespaceTransaction(String functionNamespaceManagerName) + { + checkOpenTransaction(); + + return functionNamespaceTransactions.computeIfAbsent(functionNamespaceManagerName, name -> functionNamespaceManagers.get(name).beginTransaction()); + } + public synchronized ConnectorTransactionMetadata createConnectorTransactionMetadata(ConnectorId connectorId, Catalog catalog) { Connector connector = catalog.getConnector(connectorId); diff --git a/presto-main/src/main/java/com/facebook/presto/transaction/NoOpTransactionManager.java b/presto-main/src/main/java/com/facebook/presto/transaction/NoOpTransactionManager.java index 89700104fe238..4d3d318cb83b6 100644 --- a/presto-main/src/main/java/com/facebook/presto/transaction/NoOpTransactionManager.java +++ b/presto-main/src/main/java/com/facebook/presto/transaction/NoOpTransactionManager.java @@ -16,6 +16,8 @@ import com.facebook.presto.metadata.CatalogMetadata; import com.facebook.presto.spi.ConnectorId; import com.facebook.presto.spi.connector.ConnectorTransactionHandle; +import com.facebook.presto.spi.function.FunctionNamespaceManager; +import com.facebook.presto.spi.function.FunctionNamespaceTransactionHandle; import com.facebook.presto.spi.transaction.IsolationLevel; import com.google.common.util.concurrent.ListenableFuture; @@ -95,6 +97,17 @@ public ConnectorTransactionHandle getConnectorTransaction(TransactionId transact throw new UnsupportedOperationException(); } + @Override + public void registerFunctionNamespaceManager(String functionNamespaceManagerName, FunctionNamespaceManager functionNamespaceManager) + { + } + + @Override + public FunctionNamespaceTransactionHandle getFunctionNamespaceTransaction(TransactionId transactionId, String functionNamespaceManagerName) + { + throw new UnsupportedOperationException(); + } + @Override public void checkAndSetActive(TransactionId transactionId) { diff --git a/presto-main/src/main/java/com/facebook/presto/transaction/TransactionManager.java b/presto-main/src/main/java/com/facebook/presto/transaction/TransactionManager.java index bc99b944d6789..6cc40c23eeb14 100644 --- a/presto-main/src/main/java/com/facebook/presto/transaction/TransactionManager.java +++ b/presto-main/src/main/java/com/facebook/presto/transaction/TransactionManager.java @@ -18,6 +18,8 @@ import com.facebook.presto.security.AccessControl; import com.facebook.presto.spi.ConnectorId; import com.facebook.presto.spi.connector.ConnectorTransactionHandle; +import com.facebook.presto.spi.function.FunctionNamespaceManager; +import com.facebook.presto.spi.function.FunctionNamespaceTransactionHandle; import com.facebook.presto.spi.transaction.IsolationLevel; import com.google.common.util.concurrent.ListenableFuture; @@ -57,6 +59,10 @@ default boolean isAutoCommit(TransactionId transactionId) ConnectorTransactionHandle getConnectorTransaction(TransactionId transactionId, ConnectorId connectorId); + void registerFunctionNamespaceManager(String functionNamespaceManagerName, FunctionNamespaceManager functionNamespaceManager); + + FunctionNamespaceTransactionHandle getFunctionNamespaceTransaction(TransactionId transactionId, String functionNamespaceManagerName); + void checkAndSetActive(TransactionId transactionId); void trySetActive(TransactionId transactionId); diff --git a/presto-main/src/test/java/com/facebook/presto/metadata/AbstractMockMetadata.java b/presto-main/src/test/java/com/facebook/presto/metadata/AbstractMockMetadata.java index a44d2023aff8e..4a380ca74cedf 100644 --- a/presto-main/src/test/java/com/facebook/presto/metadata/AbstractMockMetadata.java +++ b/presto-main/src/test/java/com/facebook/presto/metadata/AbstractMockMetadata.java @@ -75,7 +75,7 @@ public List listFunctions() } @Override - public void addFunctions(List functions) + public void registerBuiltInFunctions(List functions) { throw new UnsupportedOperationException(); } diff --git a/presto-main/src/test/java/com/facebook/presto/metadata/SqlInvokedRegularFunctionTestUtils.java b/presto-main/src/test/java/com/facebook/presto/metadata/SqlInvokedRegularFunctionTestUtils.java new file mode 100644 index 0000000000000..8d017df4cbfe0 --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/metadata/SqlInvokedRegularFunctionTestUtils.java @@ -0,0 +1,66 @@ +/* + * 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.metadata; + +import com.facebook.presto.spi.relation.FullyQualifiedName; +import com.facebook.presto.sqlfunction.RoutineCharacteristics; +import com.facebook.presto.sqlfunction.SqlInvokedRegularFunction; +import com.facebook.presto.sqlfunction.SqlParameter; +import com.google.common.collect.ImmutableList; + +import java.util.Optional; + +import static com.facebook.presto.spi.type.StandardTypes.DOUBLE; +import static com.facebook.presto.spi.type.StandardTypes.INTEGER; +import static com.facebook.presto.spi.type.TypeSignature.parseTypeSignature; +import static com.facebook.presto.sqlfunction.RoutineCharacteristics.Determinism.DETERMINISTIC; +import static com.facebook.presto.sqlfunction.RoutineCharacteristics.Language.SQL; +import static com.facebook.presto.sqlfunction.RoutineCharacteristics.NullCallClause.CALLED_ON_NULL_INPUT; +import static com.facebook.presto.sqlfunction.RoutineCharacteristics.NullCallClause.RETURNS_NULL_ON_NULL_INPUT; + +public class SqlInvokedRegularFunctionTestUtils +{ + private SqlInvokedRegularFunctionTestUtils() + { + } + + public static final FullyQualifiedName POWER_TOWER = FullyQualifiedName.of("unittest.memory.power_tower"); + + public static final SqlInvokedRegularFunction FUNCTION_POWER_TOWER_DOUBLE = new SqlInvokedRegularFunction( + POWER_TOWER, + ImmutableList.of(new SqlParameter("x", parseTypeSignature(DOUBLE))), + parseTypeSignature(DOUBLE), + Optional.of("power tower"), + new RoutineCharacteristics(SQL, DETERMINISTIC, CALLED_ON_NULL_INPUT), + "pow(x, x)", + Optional.empty()); + + public static final SqlInvokedRegularFunction FUNCTION_POWER_TOWER_DOUBLE_UPDATED = new SqlInvokedRegularFunction( + POWER_TOWER, + ImmutableList.of(new SqlParameter("x", parseTypeSignature(DOUBLE))), + parseTypeSignature(DOUBLE), + Optional.of("power tower"), + new RoutineCharacteristics(SQL, DETERMINISTIC, RETURNS_NULL_ON_NULL_INPUT), + "pow(x, x)", + Optional.empty()); + + public static final SqlInvokedRegularFunction FUNCTION_POWER_TOWER_INT = new SqlInvokedRegularFunction( + POWER_TOWER, + ImmutableList.of(new SqlParameter("x", parseTypeSignature(INTEGER))), + parseTypeSignature(INTEGER), + Optional.of("power tower"), + new RoutineCharacteristics(SQL, DETERMINISTIC, RETURNS_NULL_ON_NULL_INPUT), + "pow(x, x)", + Optional.empty()); +} diff --git a/presto-main/src/test/java/com/facebook/presto/metadata/TestFunctionManager.java b/presto-main/src/test/java/com/facebook/presto/metadata/TestFunctionManager.java index 23dbe18735e2a..87d73d60beafc 100644 --- a/presto-main/src/test/java/com/facebook/presto/metadata/TestFunctionManager.java +++ b/presto-main/src/test/java/com/facebook/presto/metadata/TestFunctionManager.java @@ -117,7 +117,7 @@ public void testMagicLiteralFunction() @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "\\QFunction already registered: presto.default.custom_add(bigint,bigint):bigint\\E") public void testDuplicateFunctions() { - List functions = new FunctionListBuilder() + List functions = new FunctionListBuilder() .scalars(CustomFunctions.class) .getFunctions() .stream() @@ -126,20 +126,20 @@ public void testDuplicateFunctions() TypeRegistry typeManager = new TypeRegistry(); FunctionManager functionManager = createFunctionManager(typeManager); - functionManager.addFunctions(functions); - functionManager.addFunctions(functions); + functionManager.registerBuiltInFunctions(functions); + functionManager.registerBuiltInFunctions(functions); } @Test(expectedExceptions = IllegalStateException.class, expectedExceptionsMessageRegExp = "'presto.default.sum' is both an aggregation and a scalar function") public void testConflictingScalarAggregation() { - List functions = new FunctionListBuilder() + List functions = new FunctionListBuilder() .scalars(ScalarSum.class) .getFunctions(); TypeRegistry typeManager = new TypeRegistry(); FunctionManager functionManager = createFunctionManager(typeManager); - functionManager.addFunctions(functions); + functionManager.registerBuiltInFunctions(functions); } @Test @@ -399,13 +399,13 @@ private FunctionHandle resolveFunctionHandle() { FeaturesConfig featuresConfig = new FeaturesConfig(); FunctionManager functionManager = new FunctionManager(typeRegistry, blockEncoding, featuresConfig); - functionManager.addFunctions(createFunctionsFromSignatures()); + functionManager.registerBuiltInFunctions(createFunctionsFromSignatures()); return functionManager.resolveFunction(TEST_SESSION, QualifiedName.of("presto", "default", TEST_FUNCTION_NAME), fromTypeSignatures(parameterTypes)); } - private List createFunctionsFromSignatures() + private List createFunctionsFromSignatures() { - ImmutableList.Builder functions = ImmutableList.builder(); + ImmutableList.Builder functions = ImmutableList.builder(); for (SignatureBuilder functionSignature : functionSignatures) { Signature signature = functionSignature.name(TEST_FUNCTION_NAME).build(); functions.add(new SqlScalarFunction(signature) 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 new file mode 100644 index 0000000000000..79fdca16728f3 --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/metadata/TestFunctionNamespaceManager.java @@ -0,0 +1,166 @@ +/* + * 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.metadata; + +import com.facebook.presto.spi.ErrorCodeSupplier; +import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.function.FunctionMetadata; +import com.facebook.presto.spi.function.FunctionNamespaceTransactionHandle; +import com.facebook.presto.sqlfunction.SqlInvokedFunctionNamespaceManagerConfig; +import com.facebook.presto.sqlfunction.SqlInvokedRegularFunction; +import com.facebook.presto.testing.InMemoryFunctionNamespaceManager; +import com.google.common.collect.ImmutableSet; +import io.airlift.units.Duration; +import org.testng.annotations.Test; + +import java.util.Collection; +import java.util.Optional; + +import static com.facebook.presto.metadata.SqlInvokedRegularFunctionTestUtils.FUNCTION_POWER_TOWER_DOUBLE; +import static com.facebook.presto.metadata.SqlInvokedRegularFunctionTestUtils.FUNCTION_POWER_TOWER_DOUBLE_UPDATED; +import static com.facebook.presto.metadata.SqlInvokedRegularFunctionTestUtils.FUNCTION_POWER_TOWER_INT; +import static com.facebook.presto.metadata.SqlInvokedRegularFunctionTestUtils.POWER_TOWER; +import static com.facebook.presto.spi.StandardErrorCode.GENERIC_USER_ERROR; +import static com.facebook.presto.sqlfunction.SqlInvokedRegularFunction.versioned; +import static com.facebook.presto.testing.assertions.Assert.assertEquals; +import static com.google.common.collect.Iterables.getOnlyElement; +import static java.lang.String.format; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.testng.Assert.assertNotSame; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +public class TestFunctionNamespaceManager +{ + @Test + public void testCreateFunction() + { + InMemoryFunctionNamespaceManager functionNamespaceManager = createFunctionNamespaceManager(); + functionNamespaceManager.createFunction(FUNCTION_POWER_TOWER_DOUBLE, false); + assertEquals(functionNamespaceManager.listFunctions(), ImmutableSet.of(versioned(FUNCTION_POWER_TOWER_DOUBLE, 1))); + + functionNamespaceManager.createFunction(FUNCTION_POWER_TOWER_INT, false); + assertEquals( + ImmutableSet.copyOf(functionNamespaceManager.listFunctions()), + ImmutableSet.of(versioned(FUNCTION_POWER_TOWER_DOUBLE, 1), versioned(FUNCTION_POWER_TOWER_INT, 1))); + + functionNamespaceManager.createFunction(FUNCTION_POWER_TOWER_DOUBLE_UPDATED, true); + assertEquals( + ImmutableSet.copyOf(functionNamespaceManager.listFunctions()), + ImmutableSet.of(versioned(FUNCTION_POWER_TOWER_DOUBLE_UPDATED, 2), versioned(FUNCTION_POWER_TOWER_INT, 1))); + + System.out.println(FUNCTION_POWER_TOWER_DOUBLE); + } + + @Test + public void testCreateFunctionFailed() + { + InMemoryFunctionNamespaceManager functionNamespaceManager = createFunctionNamespaceManager(); + functionNamespaceManager.createFunction(FUNCTION_POWER_TOWER_DOUBLE, false); + assertPrestoException( + () -> functionNamespaceManager.createFunction(FUNCTION_POWER_TOWER_DOUBLE_UPDATED, false), + GENERIC_USER_ERROR, + ".*Function 'unittest.memory.power_tower' already exists"); + } + + @Test + public void testTransactionalGetFunction() + { + InMemoryFunctionNamespaceManager functionNamespaceManager = new InMemoryFunctionNamespaceManager( + new SqlInvokedFunctionNamespaceManagerConfig() + .setFunctionCacheExpiration(new Duration(0, MILLISECONDS)) + .setMetadataCacheExpiration(new Duration(0, MILLISECONDS))); + + // begin first transaction + FunctionNamespaceTransactionHandle transaction1 = functionNamespaceManager.beginTransaction(); + assertEquals(functionNamespaceManager.getFunctions(Optional.of(transaction1), POWER_TOWER).size(), 0); + + // create function, first transaction still sees no functions + functionNamespaceManager.createFunction(FUNCTION_POWER_TOWER_DOUBLE, false); + assertEquals(functionNamespaceManager.getFunctions(Optional.of(transaction1), POWER_TOWER).size(), 0); + + // second transaction sees newly created function + FunctionNamespaceTransactionHandle transaction2 = functionNamespaceManager.beginTransaction(); + Collection functions2 = functionNamespaceManager.getFunctions(Optional.of(transaction2), POWER_TOWER); + assertEquals(functions2.size(), 1); + assertEquals(getOnlyElement(functions2), versioned(FUNCTION_POWER_TOWER_DOUBLE, 1)); + + // update the function, second transaction still sees the old functions + functionNamespaceManager.createFunction(FUNCTION_POWER_TOWER_DOUBLE_UPDATED, true); + functions2 = functionNamespaceManager.getFunctions(Optional.of(transaction2), POWER_TOWER); + assertEquals(functions2.size(), 1); + assertEquals(getOnlyElement(functions2), versioned(FUNCTION_POWER_TOWER_DOUBLE, 1)); + + // third transaction sees the updated function + FunctionNamespaceTransactionHandle transaction3 = functionNamespaceManager.beginTransaction(); + Collection functions3 = functionNamespaceManager.getFunctions(Optional.of(transaction3), POWER_TOWER); + assertEquals(functions3.size(), 1); + assertEquals(getOnlyElement(functions3), versioned(FUNCTION_POWER_TOWER_DOUBLE_UPDATED, 2)); + + functionNamespaceManager.commit(transaction1); + functionNamespaceManager.commit(transaction2); + functionNamespaceManager.commit(transaction3); + } + + @Test + public void testCaching() + { + InMemoryFunctionNamespaceManager functionNamespaceManager = createFunctionNamespaceManager(); + functionNamespaceManager.createFunction(FUNCTION_POWER_TOWER_DOUBLE, false); + + // fetchFunctionsDirect does not produce the same function reference + SqlInvokedRegularFunction function1 = getOnlyElement(functionNamespaceManager.fetchFunctionsDirect(POWER_TOWER)); + SqlInvokedRegularFunction function2 = getOnlyElement(functionNamespaceManager.fetchFunctionsDirect(POWER_TOWER)); + assertEquals(function1, function2); + assertNotSame(function1, function2); + + // fetchFunctionMetadataDirect does not produce the same metdata reference + FunctionMetadata metadata1 = functionNamespaceManager.fetchFunctionMetadataDirect(function1.getRequiredFunctionHandle()); + FunctionMetadata metadata2 = functionNamespaceManager.fetchFunctionMetadataDirect(function2.getRequiredFunctionHandle()); + assertEquals(metadata1, metadata2); + assertNotSame(metadata1, metadata2); + + // getFunctionMetadata produces the same metadata reference + metadata1 = functionNamespaceManager.getFunctionMetadata(function1.getRequiredFunctionHandle()); + metadata2 = functionNamespaceManager.getFunctionMetadata(function2.getRequiredFunctionHandle()); + assertSame(metadata1, metadata2); + + // getFunctions produces the same function collection reference + functionNamespaceManager.createFunction(FUNCTION_POWER_TOWER_INT, false); + FunctionNamespaceTransactionHandle transaction1 = functionNamespaceManager.beginTransaction(); + FunctionNamespaceTransactionHandle transaction2 = functionNamespaceManager.beginTransaction(); + Collection functions1 = functionNamespaceManager.getFunctions(Optional.of(transaction1), POWER_TOWER); + Collection functions2 = functionNamespaceManager.getFunctions(Optional.of(transaction2), POWER_TOWER); + assertEquals(functions1.size(), 2); + assertSame(functions1, functions2); + } + + private static InMemoryFunctionNamespaceManager createFunctionNamespaceManager() + { + return new InMemoryFunctionNamespaceManager(new SqlInvokedFunctionNamespaceManagerConfig()); + } + + private static void assertPrestoException(Runnable runnable, ErrorCodeSupplier expectedErrorCode, String expectedMessageRegex) + { + try { + runnable.run(); + fail(format("Expected PrestoException with error code '%s', but not Exception is thrown", expectedErrorCode.toErrorCode().getName())); + } + catch (PrestoException e) { + assertEquals(e.getErrorCode(), expectedErrorCode.toErrorCode()); + assertTrue(e.getMessage().matches(expectedMessageRegex)); + } + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestScanFilterAndProjectOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/TestScanFilterAndProjectOperator.java index ccc457d709b87..a67fd55fad441 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestScanFilterAndProjectOperator.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestScanFilterAndProjectOperator.java @@ -274,7 +274,7 @@ public void testPageYield() } Metadata metadata = functionAssertions.getMetadata(); FunctionManager functionManager = metadata.getFunctionManager(); - functionManager.addFunctions(functions.build()); + functionManager.registerBuiltInFunctions(functions.build()); // match each column with a projection ExpressionCompiler expressionCompiler = new ExpressionCompiler(metadata, new PageFunctionCompiler(metadata, 0)); @@ -338,7 +338,7 @@ public void testRecordCursorYield() // set up generic long function with a callback to force yield Metadata metadata = functionAssertions.getMetadata(); FunctionManager functionManager = metadata.getFunctionManager(); - functionManager.addFunctions(ImmutableList.of(new GenericLongFunction("record_cursor", value -> { + functionManager.registerBuiltInFunctions(ImmutableList.of(new GenericLongFunction("record_cursor", value -> { driverContext.getYieldSignal().forceYieldForTesting(); return value; }))); diff --git a/presto-main/src/test/java/com/facebook/presto/operator/aggregation/AbstractTestAggregationFunction.java b/presto-main/src/test/java/com/facebook/presto/operator/aggregation/AbstractTestAggregationFunction.java index 1776ca116984c..47980932fdd0f 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/aggregation/AbstractTestAggregationFunction.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/aggregation/AbstractTestAggregationFunction.java @@ -64,7 +64,7 @@ public final void destroyTestAggregationFunction() protected void registerFunctions(Plugin plugin) { - functionManager.addFunctions(extractFunctions(plugin.getFunctions())); + functionManager.registerBuiltInFunctions(extractFunctions(plugin.getFunctions())); } protected void registerTypes(Plugin plugin) diff --git a/presto-main/src/test/java/com/facebook/presto/operator/aggregation/TestCountNullAggregation.java b/presto-main/src/test/java/com/facebook/presto/operator/aggregation/TestCountNullAggregation.java index b6974ed793087..5da964cae6afc 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/aggregation/TestCountNullAggregation.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/aggregation/TestCountNullAggregation.java @@ -39,7 +39,7 @@ public class TestCountNullAggregation @BeforeClass public void setup() { - functionManager.addFunctions(new FunctionListBuilder().aggregates(CountNull.class).getFunctions()); + functionManager.registerBuiltInFunctions(new FunctionListBuilder().aggregates(CountNull.class).getFunctions()); } @Override diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/AbstractTestFunctions.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/AbstractTestFunctions.java index 9a7fc859a29c2..b522a8bc2215b 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/AbstractTestFunctions.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/AbstractTestFunctions.java @@ -14,6 +14,7 @@ package com.facebook.presto.operator.scalar; import com.facebook.presto.Session; +import com.facebook.presto.metadata.BuiltInFunction; import com.facebook.presto.metadata.FunctionListBuilder; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.SqlScalarFunction; @@ -22,7 +23,6 @@ import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.StandardErrorCode; import com.facebook.presto.spi.function.OperatorType; -import com.facebook.presto.spi.function.SqlFunction; import com.facebook.presto.spi.type.DecimalParseResult; import com.facebook.presto.spi.type.Decimals; import com.facebook.presto.spi.type.SqlDecimal; @@ -186,30 +186,30 @@ protected void tryEvaluateWithAll(String projection, Type expectedType) protected void registerScalarFunction(SqlScalarFunction sqlScalarFunction) { Metadata metadata = functionAssertions.getMetadata(); - metadata.getFunctionManager().addFunctions(ImmutableList.of(sqlScalarFunction)); + metadata.getFunctionManager().registerBuiltInFunctions(ImmutableList.of(sqlScalarFunction)); } protected void registerScalar(Class clazz) { Metadata metadata = functionAssertions.getMetadata(); - List functions = new FunctionListBuilder() + List functions = new FunctionListBuilder() .scalars(clazz) .getFunctions(); - metadata.getFunctionManager().addFunctions(functions); + metadata.getFunctionManager().registerBuiltInFunctions(functions); } protected void registerParametricScalar(Class clazz) { Metadata metadata = functionAssertions.getMetadata(); - List functions = new FunctionListBuilder() + List functions = new FunctionListBuilder() .scalar(clazz) .getFunctions(); - metadata.getFunctionManager().addFunctions(functions); + metadata.getFunctionManager().registerBuiltInFunctions(functions); } protected void registerFunctions(Plugin plugin) { - functionAssertions.getMetadata().addFunctions(extractFunctions(plugin.getFunctions())); + functionAssertions.getMetadata().registerBuiltInFunctions(extractFunctions(plugin.getFunctions())); } protected void registerTypes(Plugin plugin) diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayDistinct.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayDistinct.java index 2fbe911217918..80797553cb785 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayDistinct.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayDistinct.java @@ -107,7 +107,7 @@ public void setup() { MetadataManager metadata = MetadataManager.createTestMetadataManager(); FunctionManager functionManager = metadata.getFunctionManager(); - metadata.addFunctions(extractFunctions(BenchmarkArrayDistinct.class)); + metadata.registerBuiltInFunctions(extractFunctions(BenchmarkArrayDistinct.class)); ExpressionCompiler compiler = new ExpressionCompiler(metadata, new PageFunctionCompiler(metadata, 0)); ImmutableList.Builder projectionsBuilder = ImmutableList.builder(); Block[] blocks = new Block[TYPES.size()]; diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayFilter.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayFilter.java index 1706d157c8ac2..7aa66b60e6cd8 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayFilter.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayFilter.java @@ -126,7 +126,7 @@ public void setup() { MetadataManager metadata = MetadataManager.createTestMetadataManager(); FunctionManager functionManager = metadata.getFunctionManager(); - metadata.addFunctions(new FunctionListBuilder().function(EXACT_ARRAY_FILTER_FUNCTION).getFunctions()); + metadata.registerBuiltInFunctions(new FunctionListBuilder().function(EXACT_ARRAY_FILTER_FUNCTION).getFunctions()); ExpressionCompiler compiler = new ExpressionCompiler(metadata, new PageFunctionCompiler(metadata, 0)); ImmutableList.Builder projectionsBuilder = ImmutableList.builder(); Block[] blocks = new Block[TYPES.size()]; diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayHashCodeOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayHashCodeOperator.java index 6609734ba92a7..da4d41934e793 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayHashCodeOperator.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayHashCodeOperator.java @@ -117,8 +117,8 @@ public void setup() { MetadataManager metadata = MetadataManager.createTestMetadataManager(); FunctionManager functionManager = metadata.getFunctionManager(); - metadata.addFunctions(new FunctionListBuilder().scalar(BenchmarkOldArrayHash.class).getFunctions()); - metadata.addFunctions(new FunctionListBuilder().scalar(BenchmarkAnotherArrayHash.class).getFunctions()); + metadata.registerBuiltInFunctions(new FunctionListBuilder().scalar(BenchmarkOldArrayHash.class).getFunctions()); + metadata.registerBuiltInFunctions(new FunctionListBuilder().scalar(BenchmarkAnotherArrayHash.class).getFunctions()); ExpressionCompiler compiler = new ExpressionCompiler(metadata, new PageFunctionCompiler(metadata, 0)); ImmutableList.Builder projectionsBuilder = ImmutableList.builder(); Block[] blocks = new Block[1]; diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArraySort.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArraySort.java index 43567151df376..47e8f93ebc776 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArraySort.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArraySort.java @@ -105,7 +105,7 @@ public static class BenchmarkData public void setup() { MetadataManager metadata = MetadataManager.createTestMetadataManager(); - metadata.addFunctions(extractFunctions(BenchmarkArraySort.class)); + metadata.registerBuiltInFunctions(extractFunctions(BenchmarkArraySort.class)); ExpressionCompiler compiler = new ExpressionCompiler(metadata, new PageFunctionCompiler(metadata, 0)); ImmutableList.Builder projectionsBuilder = ImmutableList.builder(); Block[] blocks = new Block[TYPES.size()]; diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/FunctionAssertions.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/FunctionAssertions.java index 310ef1efb665a..13385b3187758 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/FunctionAssertions.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/FunctionAssertions.java @@ -15,6 +15,7 @@ import com.facebook.presto.Session; import com.facebook.presto.execution.warnings.WarningCollector; +import com.facebook.presto.metadata.BuiltInFunction; import com.facebook.presto.metadata.FunctionListBuilder; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.Split; @@ -47,7 +48,6 @@ import com.facebook.presto.spi.TableHandle; import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.connector.ConnectorTransactionHandle; -import com.facebook.presto.spi.function.SqlFunction; import com.facebook.presto.spi.plan.PlanNodeId; import com.facebook.presto.spi.predicate.Utils; import com.facebook.presto.spi.relation.RowExpression; @@ -219,15 +219,15 @@ public Metadata getMetadata() return metadata; } - public FunctionAssertions addFunctions(List functionInfos) + public FunctionAssertions addFunctions(List functionInfos) { - metadata.addFunctions(functionInfos); + metadata.registerBuiltInFunctions(functionInfos); return this; } public FunctionAssertions addScalarFunctions(Class clazz) { - metadata.addFunctions(new FunctionListBuilder().scalars(clazz).getFunctions()); + metadata.registerBuiltInFunctions(new FunctionListBuilder().scalars(clazz).getFunctions()); return this; } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestLambdaExpression.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestLambdaExpression.java index 72ca397d55a87..a2e0fdd33bd11 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestLambdaExpression.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestLambdaExpression.java @@ -50,7 +50,7 @@ private TestLambdaExpression(Session session) @BeforeClass public void setUp() { - functionAssertions.getMetadata().addFunctions(ImmutableList.of(APPLY_FUNCTION, INVOKE_FUNCTION)); + functionAssertions.getMetadata().registerBuiltInFunctions(ImmutableList.of(APPLY_FUNCTION, INVOKE_FUNCTION)); } @Test diff --git a/presto-main/src/test/java/com/facebook/presto/sql/TestExpressionInterpreter.java b/presto-main/src/test/java/com/facebook/presto/sql/TestExpressionInterpreter.java index b2a850d459382..98cdc672e4d2f 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/TestExpressionInterpreter.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/TestExpressionInterpreter.java @@ -144,7 +144,7 @@ public class TestExpressionInterpreter @BeforeClass public void setup() { - METADATA.getFunctionManager().addFunctions(ImmutableList.of(APPLY_FUNCTION)); + METADATA.getFunctionManager().registerBuiltInFunctions(ImmutableList.of(APPLY_FUNCTION)); } @Test diff --git a/presto-main/src/test/java/com/facebook/presto/sql/analyzer/TestAnalyzer.java b/presto-main/src/test/java/com/facebook/presto/sql/analyzer/TestAnalyzer.java index 5eb3079a85822..649c7c09ffa3f 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/analyzer/TestAnalyzer.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/analyzer/TestAnalyzer.java @@ -1562,7 +1562,7 @@ public void setup() new AnalyzePropertyManager(), transactionManager); - metadata.getFunctionManager().addFunctions(ImmutableList.of(APPLY_FUNCTION)); + metadata.getFunctionManager().registerBuiltInFunctions(ImmutableList.of(APPLY_FUNCTION)); Catalog tpchTestCatalog = createTestingCatalog(TPCH_CATALOG, TPCH_CONNECTOR_ID); catalogManager.registerCatalog(tpchTestCatalog); diff --git a/presto-ml/src/test/java/com/facebook/presto/ml/AbstractTestMLFunctions.java b/presto-ml/src/test/java/com/facebook/presto/ml/AbstractTestMLFunctions.java index 1206bd70f9da0..dffbefd9189aa 100644 --- a/presto-ml/src/test/java/com/facebook/presto/ml/AbstractTestMLFunctions.java +++ b/presto-ml/src/test/java/com/facebook/presto/ml/AbstractTestMLFunctions.java @@ -25,7 +25,7 @@ abstract class AbstractTestMLFunctions @BeforeClass protected void registerFunctions() { - functionAssertions.getMetadata().addFunctions( + functionAssertions.getMetadata().registerBuiltInFunctions( extractFunctions(new MLPlugin().getFunctions())); } } diff --git a/presto-ml/src/test/java/com/facebook/presto/ml/TestEvaluateClassifierPredictions.java b/presto-ml/src/test/java/com/facebook/presto/ml/TestEvaluateClassifierPredictions.java index 4f8b007a901e6..cb59c7300ae4c 100644 --- a/presto-ml/src/test/java/com/facebook/presto/ml/TestEvaluateClassifierPredictions.java +++ b/presto-ml/src/test/java/com/facebook/presto/ml/TestEvaluateClassifierPredictions.java @@ -42,7 +42,7 @@ public class TestEvaluateClassifierPredictions @Test public void testEvaluateClassifierPredictions() { - metadata.addFunctions(extractFunctions(new MLPlugin().getFunctions())); + metadata.registerBuiltInFunctions(extractFunctions(new MLPlugin().getFunctions())); InternalAggregationFunction aggregation = functionManager.getAggregateFunctionImplementation( functionManager.lookupFunction("evaluate_classifier_predictions", fromTypes(BIGINT, BIGINT))); Accumulator accumulator = aggregation.bind(ImmutableList.of(0, 1), Optional.empty()).createAccumulator(); diff --git a/presto-ml/src/test/java/com/facebook/presto/ml/TestMLQueries.java b/presto-ml/src/test/java/com/facebook/presto/ml/TestMLQueries.java index 6162e9a981e35..6fa261f686a83 100644 --- a/presto-ml/src/test/java/com/facebook/presto/ml/TestMLQueries.java +++ b/presto-ml/src/test/java/com/facebook/presto/ml/TestMLQueries.java @@ -71,7 +71,7 @@ private static LocalQueryRunner createLocalQueryRunner() for (ParametricType parametricType : plugin.getParametricTypes()) { localQueryRunner.getTypeManager().addParametricType(parametricType); } - localQueryRunner.getMetadata().addFunctions(extractFunctions(new MLPlugin().getFunctions())); + localQueryRunner.getMetadata().registerBuiltInFunctions(extractFunctions(new MLPlugin().getFunctions())); return localQueryRunner; } 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 795cbb85426c6..d5e8bccded73e 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 @@ -56,8 +56,10 @@ statement | CREATE (OR REPLACE)? VIEW qualifiedName AS query #createView | DROP VIEW (IF EXISTS)? qualifiedName #dropView | CREATE (OR REPLACE)? FUNCTION functionName=qualifiedName - '(' (sqlParameterDeclaration (',' sqlParameterDeclaration)*)? ')' - RETURNS returnType=type routineCharacteristics routineBody #createFunction + '(' (sqlParameterDeclaration (',' sqlParameterDeclaration)*)? ')' + RETURNS returnType=type + (COMMENT string)? + routineCharacteristics routineBody #createFunction | CALL qualifiedName '(' (callArgument (',' callArgument)*)? ')' #call | CREATE ROLE name=identifier (WITH ADMIN grantor)? #createRole 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 10421a12165ff..766791d26987f 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 @@ -561,8 +561,12 @@ protected Void visitCreateFunction(CreateFunction node, Integer indent) .append(formatSqlParameterDeclarations(node.getParameters())) .append("\nRETURNS ") .append(node.getReturnType()) - .append("\n") - .append(formatRoutineCharacteristics(node.getCharacteristics())) + .append("\n"); + if (node.getComment().isPresent()) { + builder.append("\nCOMMENT ") + .append(formatStringLiteral(node.getComment().get())); + } + builder.append(formatRoutineCharacteristics(node.getCharacteristics())) .append("\nRETURN ") .append(formatExpression(node.getBody(), parameters)); 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 013f01c54684c..1380a51327898 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 @@ -404,6 +404,11 @@ public Node visitCreateView(SqlBaseParser.CreateViewContext context) @Override public Node visitCreateFunction(SqlBaseParser.CreateFunctionContext context) { + Optional comment = Optional.empty(); + if (context.COMMENT() != null) { + comment = Optional.of(((StringLiteral) visit(context.string())).getValue()); + } + return new CreateFunction( getQualifiedName(context.functionName), context.REPLACE() != null, @@ -411,6 +416,7 @@ public Node visitCreateFunction(SqlBaseParser.CreateFunctionContext context) .map(this::getParameterDeclarations) .collect(toImmutableList()), getType(context.returnType), + comment, getRoutineCharacteristics(context.routineCharacteristics()), (Expression) visit(context.routineBody())); } diff --git a/presto-parser/src/main/java/com/facebook/presto/sql/tree/CreateFunction.java b/presto-parser/src/main/java/com/facebook/presto/sql/tree/CreateFunction.java index 046b38234e5ad..2031da7b45db7 100644 --- a/presto-parser/src/main/java/com/facebook/presto/sql/tree/CreateFunction.java +++ b/presto-parser/src/main/java/com/facebook/presto/sql/tree/CreateFunction.java @@ -29,26 +29,28 @@ public class CreateFunction private final boolean replace; private final List parameters; private final String returnType; + private final Optional comment; private final RoutineCharacteristics characteristics; private final Expression body; - public CreateFunction(QualifiedName functionName, boolean replace, List parameters, String returnType, RoutineCharacteristics characteristics, Expression body) + public CreateFunction(QualifiedName functionName, boolean replace, List parameters, String returnType, Optional comment, RoutineCharacteristics characteristics, Expression body) { - this(Optional.empty(), replace, functionName, parameters, returnType, characteristics, body); + this(Optional.empty(), replace, functionName, parameters, returnType, comment, characteristics, body); } - public CreateFunction(NodeLocation location, boolean replace, QualifiedName functionName, List parameters, String returnType, RoutineCharacteristics characteristics, Expression body) + public CreateFunction(NodeLocation location, boolean replace, QualifiedName functionName, List parameters, String returnType, Optional comment, RoutineCharacteristics characteristics, Expression body) { - this(Optional.of(location), replace, functionName, parameters, returnType, characteristics, body); + this(Optional.of(location), replace, functionName, parameters, returnType, comment, characteristics, body); } - private CreateFunction(Optional location, boolean replace, QualifiedName functionName, List parameters, String returnType, RoutineCharacteristics characteristics, Expression body) + private CreateFunction(Optional location, boolean replace, QualifiedName functionName, List parameters, String returnType, Optional comment, RoutineCharacteristics characteristics, Expression body) { super(location); this.functionName = requireNonNull(functionName, "functionName is null"); this.replace = replace; this.parameters = ImmutableList.copyOf(requireNonNull(parameters, "parameters is null")); this.returnType = requireNonNull(returnType, "returnType is null"); + this.comment = requireNonNull(comment, "comment is null"); this.characteristics = requireNonNull(characteristics, "routineCharacteristics is null"); this.body = requireNonNull(body, "body is null"); } @@ -73,6 +75,11 @@ public String getReturnType() return returnType; } + public Optional getComment() + { + return comment; + } + public RoutineCharacteristics getCharacteristics() { return characteristics; @@ -100,7 +107,7 @@ public List getChildren() @Override public int hashCode() { - return Objects.hash(functionName, parameters, returnType, characteristics, body); + return Objects.hash(functionName, parameters, returnType, comment, characteristics, body); } @Override @@ -116,6 +123,7 @@ public boolean equals(Object obj) return Objects.equals(functionName, o.functionName) && Objects.equals(parameters, o.parameters) && Objects.equals(returnType, o.returnType) && + Objects.equals(comment, o.comment) && Objects.equals(characteristics, o.characteristics) && Objects.equals(body, o.body); } @@ -127,6 +135,7 @@ public String toString() .add("functionName", functionName) .add("parameters", parameters) .add("returnType", returnType) + .add("comment", comment) .add("characteristics", characteristics) .add("body", body) .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 aa836083fb3b3..9d4aaf0eefba8 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 @@ -1428,6 +1428,7 @@ public void testCreateFunction() assertStatement( "CREATE FUNCTION tan (x double)\n" + "RETURNS double\n" + + "COMMENT 'tangent trigonometric function'\n" + "LANGUAGE SQL\n" + "DETERMINISTIC\n" + "RETURNS NULL ON NULL INPUT\n" + @@ -1437,6 +1438,7 @@ public void testCreateFunction() false, ImmutableList.of(new SqlParameterDeclaration(identifier("x"), "double")), "double", + Optional.of("tangent trigonometric function"), new RoutineCharacteristics(SQL, DETERMINISTIC, RETURNS_NULL_ON_NULL_INPUT), new ArithmeticBinaryExpression( DIVIDE, @@ -1448,6 +1450,7 @@ public void testCreateFunction() true, ImmutableList.of(), "double", + Optional.empty(), new RoutineCharacteristics(SQL, NOT_DETERMINISTIC, CALLED_ON_NULL_INPUT), new FunctionCall(QualifiedName.of("rand"), ImmutableList.of())); assertStatement( 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 bc50cb7e7d8d1..df723bbaf057d 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 @@ -13,36 +13,49 @@ */ package com.facebook.presto.spi.function; -import com.facebook.presto.spi.QueryId; import com.facebook.presto.spi.api.Experimental; import com.facebook.presto.spi.relation.FullyQualifiedName; import java.util.Collection; -import java.util.List; +import java.util.Optional; @Experimental -public interface FunctionNamespaceManager +public interface FunctionNamespaceManager { - void addFunctions(List functions); + String getName(); - List listFunctions(); + /** + * Start a transaction. + */ + FunctionNamespaceTransactionHandle beginTransaction(); + + /** + * Commit the transaction. Will be called at most once and will not be called if + * {@link #rollback(FunctionNamespaceTransactionHandle)} is called. + */ + void commit(FunctionNamespaceTransactionHandle transactionHandle); /** - * Ideally function namespaces should support transactions like connectors do, and getCandidates should be transaction-aware. - * queryId serves as a transaction ID before proper support for transaction is introduced. - * TODO Support transaction in function namespaces + * Rollback the transaction. Will be called at most once and will not be called if + * {@link #commit(FunctionNamespaceTransactionHandle)} is called. */ - Collection getCandidates(QueryId queryId, FullyQualifiedName name); + void rollback(FunctionNamespaceTransactionHandle transactionHandle); /** - * If a SqlFunction for a given signature is returned from {@link #getCandidates(QueryId, FullyQualifiedName)} - * for a given queryId, getFunctionHandle with the same queryId should return a valid FunctionHandle, even if the function - * is deleted. Multiple calls of this function with the same parameters should return the same FunctionHandle. - * queryId serves as a transaction ID before proper support for transaction is introduced. - * TODO Support transaction in function namespaces - * @return FunctionHandle or null if the namespace manager does not manage any function with the given signature. + * Create or replace the specified function. + * TODO: Support transaction */ - FunctionHandle getFunctionHandle(QueryId queryId, Signature signature); + void createFunction(F function, boolean replace); + + /** + * List all functions managed by the {@link FunctionNamespaceManager}. + * TODO: Support transaction + */ + Collection listFunctions(); + + Collection getFunctions(Optional transactionHandle, FullyQualifiedName functionName); + + FunctionHandle getFunctionHandle(Optional transactionHandle, Signature signature); FunctionMetadata getFunctionMetadata(FunctionHandle functionHandle); } diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/function/FunctionNamespaceManagerFactory.java b/presto-spi/src/main/java/com/facebook/presto/spi/function/FunctionNamespaceManagerFactory.java index 496cb6ebd0261..fb388ca9264ce 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/function/FunctionNamespaceManagerFactory.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/function/FunctionNamespaceManagerFactory.java @@ -21,5 +21,5 @@ public interface FunctionNamespaceManagerFactory FunctionHandleResolver getHandleResolver(); - FunctionNamespaceManager create(Map config); + FunctionNamespaceManager create(Map config); } diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/function/FunctionNamespaceTransactionHandle.java b/presto-spi/src/main/java/com/facebook/presto/spi/function/FunctionNamespaceTransactionHandle.java new file mode 100644 index 0000000000000..69df8227f8758 --- /dev/null +++ b/presto-spi/src/main/java/com/facebook/presto/spi/function/FunctionNamespaceTransactionHandle.java @@ -0,0 +1,18 @@ +/* + * 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.spi.function; + +public interface FunctionNamespaceTransactionHandle +{ +} diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/function/SqlFunction.java b/presto-spi/src/main/java/com/facebook/presto/spi/function/SqlFunction.java index 0e7676f56f6e1..a21c07a122623 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/function/SqlFunction.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/function/SqlFunction.java @@ -21,10 +21,7 @@ public interface SqlFunction boolean isDeterministic(); - default boolean isCalledOnNullInput() - { - return false; - }; + boolean isCalledOnNullInput(); String getDescription(); } diff --git a/presto-sql-function/pom.xml b/presto-sql-function/pom.xml new file mode 100644 index 0000000000000..04f922c7bf6da --- /dev/null +++ b/presto-sql-function/pom.xml @@ -0,0 +1,57 @@ + + + + presto-root + com.facebook.presto + 0.228-SNAPSHOT + + 4.0.0 + + presto-sql-function + presto-sql-function + + + ${project.parent.basedir} + + + + + com.facebook.presto + presto-spi + + + + com.fasterxml.jackson.core + jackson-annotations + + + + com.google.code.findbugs + jsr305 + + + + com.google.guava + guava + + + + io.airlift + configuration + + + + io.airlift + units + + + + + org.testng + testng + test + + + diff --git a/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/AbstractSqlInvokedFunctionNamespaceManager.java b/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/AbstractSqlInvokedFunctionNamespaceManager.java new file mode 100644 index 0000000000000..60b2650b71222 --- /dev/null +++ b/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/AbstractSqlInvokedFunctionNamespaceManager.java @@ -0,0 +1,159 @@ +/* + * 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.sqlfunction; + +import com.facebook.presto.spi.function.FunctionHandle; +import com.facebook.presto.spi.function.FunctionMetadata; +import com.facebook.presto.spi.function.FunctionNamespaceManager; +import com.facebook.presto.spi.function.FunctionNamespaceTransactionHandle; +import com.facebook.presto.spi.function.Signature; +import com.facebook.presto.spi.relation.FullyQualifiedName; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +import javax.annotation.concurrent.GuardedBy; + +import java.util.Collection; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import static com.facebook.presto.spi.function.FunctionKind.SCALAR; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.ImmutableMap.toImmutableMap; +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +public abstract class AbstractSqlInvokedFunctionNamespaceManager + implements FunctionNamespaceManager +{ + private final ConcurrentMap transactions = new ConcurrentHashMap<>(); + + private final LoadingCache> functions; + private final LoadingCache metadataByHandle; + + public AbstractSqlInvokedFunctionNamespaceManager(SqlInvokedFunctionNamespaceManagerConfig config) + { + this.functions = CacheBuilder.newBuilder() + .expireAfterWrite(config.getFunctionCacheExpiration().toMillis(), MILLISECONDS) + .build(new CacheLoader>() + { + @Override + public Collection load(FullyQualifiedName functionName) + { + Collection functions = fetchFunctionsDirect(functionName); + for (SqlInvokedRegularFunction function : functions) { + metadataByHandle.put(function.getRequiredFunctionHandle(), sqlInvokedFunctionToMetadata(function)); + } + return functions; + } + }); + this.metadataByHandle = CacheBuilder.newBuilder() + .expireAfterWrite(config.getMetadataCacheExpiration().toMillis(), MILLISECONDS) + .build(new CacheLoader() + { + @Override + public FunctionMetadata load(SqlInvokedRegularFunctionHandle functionHandle) + { + return fetchFunctionMetadataDirect(functionHandle); + } + }); + } + + protected abstract Collection fetchFunctionsDirect(FullyQualifiedName functionName); + + protected abstract FunctionMetadata fetchFunctionMetadataDirect(SqlInvokedRegularFunctionHandle functionHandle); + + @Override + public final FunctionNamespaceTransactionHandle beginTransaction() + { + UuidFunctionNamespaceTransactionHandle transactionHandle = UuidFunctionNamespaceTransactionHandle.create(); + transactions.put(transactionHandle, new FunctionCollection()); + return transactionHandle; + } + + @Override + public final void commit(FunctionNamespaceTransactionHandle transactionHandle) + { + // Transactional commit is not supported yet. + transactions.remove(transactionHandle); + } + + @Override + public final void rollback(FunctionNamespaceTransactionHandle transactionHandle) + { + // Transactional rollback is not supported yet. + transactions.remove(transactionHandle); + } + + @Override + public final Collection getFunctions(Optional transactionHandle, FullyQualifiedName functionName) + { + checkArgument(transactionHandle.isPresent(), "missing transactionHandle"); + return transactions.get(transactionHandle.get()).loadAndGetFunctionsTransactional(functionName); + } + + @Override + public final FunctionHandle getFunctionHandle(Optional transactionHandle, Signature signature) + { + checkArgument(transactionHandle.isPresent(), "missing transactionHandle"); + SqlFunctionId functionId = new SqlFunctionId(signature.getName(), signature.getArgumentTypes()); + return transactions.get(transactionHandle.get()).getFunctionHandle(functionId); + } + + @Override + public final FunctionMetadata getFunctionMetadata(FunctionHandle functionHandle) + { + checkArgument(functionHandle instanceof SqlInvokedRegularFunctionHandle, "Unsupported FunctionHandle type '%s'", functionHandle.getClass().getSimpleName()); + return metadataByHandle.getUnchecked((SqlInvokedRegularFunctionHandle) functionHandle); + } + + protected static FunctionMetadata sqlInvokedFunctionToMetadata(SqlInvokedRegularFunction function) + { + return new FunctionMetadata( + function.getSignature().getName(), + function.getSignature().getArgumentTypes(), + function.getSignature().getReturnType(), + SCALAR, + function.isDeterministic(), + function.isCalledOnNullInput()); + } + + private Collection fetchFunctions(FullyQualifiedName functionName) + { + return functions.getUnchecked(functionName); + } + + private class FunctionCollection + { + @GuardedBy("this") + private final Map> functions = new ConcurrentHashMap<>(); + + @GuardedBy("this") + private final Map functionHandles = new ConcurrentHashMap<>(); + + public synchronized Collection loadAndGetFunctionsTransactional(FullyQualifiedName functionName) + { + Collection functions = this.functions.computeIfAbsent(functionName, AbstractSqlInvokedFunctionNamespaceManager.this::fetchFunctions); + functionHandles.putAll(functions.stream().collect(toImmutableMap(SqlInvokedRegularFunction::getFunctionId, SqlInvokedRegularFunction::getRequiredFunctionHandle))); + return functions; + } + + public synchronized FunctionHandle getFunctionHandle(SqlFunctionId functionId) + { + return functionHandles.get(functionId); + } + } +} diff --git a/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/RoutineCharacteristics.java b/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/RoutineCharacteristics.java new file mode 100644 index 0000000000000..64a6540d1a3b3 --- /dev/null +++ b/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/RoutineCharacteristics.java @@ -0,0 +1,97 @@ +/* + * 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.sqlfunction; + +import java.util.Objects; + +import static com.facebook.presto.sqlfunction.RoutineCharacteristics.Determinism.DETERMINISTIC; +import static com.facebook.presto.sqlfunction.RoutineCharacteristics.NullCallClause.CALLED_ON_NULL_INPUT; +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; + +public class RoutineCharacteristics +{ + public enum Language + { + SQL; + } + + public enum Determinism + { + DETERMINISTIC, + NOT_DETERMINISTIC; + } + + public enum NullCallClause + { + RETURNS_NULL_ON_NULL_INPUT, + CALLED_ON_NULL_INPUT; + } + + private final Language language; + private final Determinism determinism; + private final NullCallClause nullCallClause; + + public RoutineCharacteristics( + Language language, + Determinism determinism, + NullCallClause nullCallClause) + { + this.language = requireNonNull(language, "language is null"); + this.determinism = requireNonNull(determinism, "determinism is null"); + this.nullCallClause = requireNonNull(nullCallClause, "nullCallClause is null"); + } + + public Language getLanguage() + { + return language; + } + + public boolean isDeterministic() + { + return determinism == DETERMINISTIC; + } + + public boolean isCalledOnNullInput() + { + return nullCallClause == CALLED_ON_NULL_INPUT; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + RoutineCharacteristics that = (RoutineCharacteristics) o; + return language == that.language + && determinism == that.determinism + && nullCallClause == that.nullCallClause; + } + + @Override + public int hashCode() + { + return Objects.hash(language, determinism, nullCallClause); + } + + @Override + public String toString() + { + return format("(%s, %s, %s)", language, determinism, nullCallClause); + } +} diff --git a/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/SqlFunctionId.java b/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/SqlFunctionId.java new file mode 100644 index 0000000000000..5d03ec25e07ef --- /dev/null +++ b/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/SqlFunctionId.java @@ -0,0 +1,77 @@ +/* + * 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.sqlfunction; + +import com.facebook.presto.spi.relation.FullyQualifiedName; +import com.facebook.presto.spi.type.TypeSignature; + +import java.util.List; +import java.util.Objects; + +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; +import static java.util.stream.Collectors.joining; + +public class SqlFunctionId +{ + private final FullyQualifiedName name; + private final List argumentTypes; + + public SqlFunctionId( + FullyQualifiedName name, + List argumentTypes) + { + this.name = requireNonNull(name, "name is null"); + this.argumentTypes = requireNonNull(argumentTypes, "argumentTypes is null"); + } + + public FullyQualifiedName getName() + { + return name; + } + + public List getArgumentTypes() + { + return argumentTypes; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + SqlFunctionId o = (SqlFunctionId) obj; + return Objects.equals(name, o.name) + && Objects.equals(argumentTypes, o.argumentTypes); + } + + @Override + public int hashCode() + { + return Objects.hash(name, argumentTypes); + } + + @Override + public String toString() + { + String arguments = argumentTypes.stream() + .map(Object::toString) + .collect(joining(", ")); + return format("%s(%s)", name, arguments); + } +} diff --git a/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/SqlInvokedFunctionNamespaceManagerConfig.java b/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/SqlInvokedFunctionNamespaceManagerConfig.java new file mode 100644 index 0000000000000..c7d1f3d3deedd --- /dev/null +++ b/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/SqlInvokedFunctionNamespaceManagerConfig.java @@ -0,0 +1,53 @@ +/* + * 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.sqlfunction; + +import io.airlift.configuration.Config; +import io.airlift.units.Duration; +import io.airlift.units.MinDuration; + +import static java.util.concurrent.TimeUnit.HOURS; +import static java.util.concurrent.TimeUnit.MINUTES; + +public class SqlInvokedFunctionNamespaceManagerConfig +{ + private Duration functionCacheExpiration = new Duration(5, MINUTES); + private Duration metadataCacheExpiration = new Duration(8, HOURS); + + @MinDuration("0ns") + public Duration getFunctionCacheExpiration() + { + return functionCacheExpiration; + } + + @Config("function-cache-expiration") + public SqlInvokedFunctionNamespaceManagerConfig setFunctionCacheExpiration(Duration functionCacheExpiration) + { + this.functionCacheExpiration = functionCacheExpiration; + return this; + } + + @MinDuration("0ns") + public Duration getMetadataCacheExpiration() + { + return metadataCacheExpiration; + } + + @Config("metadata-cache-expiration") + public SqlInvokedFunctionNamespaceManagerConfig setMetadataCacheExpiration(Duration metadataCacheExpiration) + { + this.metadataCacheExpiration = metadataCacheExpiration; + return this; + } +} diff --git a/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/SqlInvokedRegularFunction.java b/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/SqlInvokedRegularFunction.java new file mode 100644 index 0000000000000..061de88e0bc39 --- /dev/null +++ b/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/SqlInvokedRegularFunction.java @@ -0,0 +1,188 @@ +/* + * 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.sqlfunction; + +import com.facebook.presto.spi.function.Signature; +import com.facebook.presto.spi.function.SqlFunction; +import com.facebook.presto.spi.relation.FullyQualifiedName; +import com.facebook.presto.spi.type.TypeSignature; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static com.facebook.presto.spi.function.FunctionKind.SCALAR; +import static com.google.common.base.Preconditions.checkState; +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; +import static java.util.stream.Collectors.collectingAndThen; +import static java.util.stream.Collectors.joining; +import static java.util.stream.Collectors.toList; + +public class SqlInvokedRegularFunction + implements SqlFunction +{ + private final List parameters; + private final Optional comment; + private final RoutineCharacteristics routineCharacteristics; + private final String body; + + private final Signature signature; + private final SqlFunctionId functionId; + private final Optional functionHandle; + + public SqlInvokedRegularFunction( + FullyQualifiedName functionName, + List parameters, + TypeSignature returnType, + Optional comment, + RoutineCharacteristics routineCharacteristics, + String body, + Optional version) + { + this.parameters = requireNonNull(parameters, "parameters is null"); + this.comment = requireNonNull(comment, "comment is null"); + this.routineCharacteristics = requireNonNull(routineCharacteristics, "routineCharacteristics is null"); + this.body = requireNonNull(body, "body is null"); + + List argumentTypes = parameters.stream() + .map(SqlParameter::getType) + .collect(collectingAndThen(toList(), Collections::unmodifiableList)); + this.signature = new Signature(functionName, SCALAR, returnType, argumentTypes); + this.functionId = new SqlFunctionId(functionName, argumentTypes); + this.functionHandle = version.map(v -> new SqlInvokedRegularFunctionHandle(functionName, argumentTypes, v)); + } + + public static SqlInvokedRegularFunction versioned(SqlInvokedRegularFunction function, long version) + { + if (function.getVersion().isPresent()) { + throw new IllegalArgumentException(format("function %s is already versioned", function.getVersion().get())); + } + return new SqlInvokedRegularFunction( + function.getSignature().getName(), + function.getParameters(), + function.getSignature().getReturnType(), + function.comment, + function.getRoutineCharacteristics(), + function.getBody(), + Optional.of(version)); + } + + @Override + public Signature getSignature() + { + return signature; + } + + @Override + public boolean isHidden() + { + return false; + } + + @Override + public boolean isDeterministic() + { + return routineCharacteristics.isDeterministic(); + } + + @Override + public boolean isCalledOnNullInput() + { + return routineCharacteristics.isCalledOnNullInput(); + } + + @Override + public String getDescription() + { + return comment.orElse(""); + } + + public List getParameters() + { + return parameters; + } + + public Optional getComment() + { + return comment; + } + + public RoutineCharacteristics getRoutineCharacteristics() + { + return routineCharacteristics; + } + + public String getBody() + { + return body; + } + + public SqlFunctionId getFunctionId() + { + return functionId; + } + + public SqlInvokedRegularFunctionHandle getRequiredFunctionHandle() + { + checkState(functionHandle.isPresent(), "missing function handle"); + return functionHandle.get(); + } + + public Optional getVersion() + { + return functionHandle.map(SqlInvokedRegularFunctionHandle::getVersion); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + SqlInvokedRegularFunction o = (SqlInvokedRegularFunction) obj; + return Objects.equals(parameters, o.parameters) + && Objects.equals(comment, o.comment) + && Objects.equals(routineCharacteristics, o.routineCharacteristics) + && Objects.equals(body, o.body) + && Objects.equals(signature, o.signature) + && Objects.equals(functionId, o.functionId) + && Objects.equals(functionHandle, o.functionHandle); + } + + @Override + public int hashCode() + { + return Objects.hash(parameters, comment, routineCharacteristics, body, signature, functionId, functionHandle); + } + + @Override + public String toString() + { + return format( + "%s(%s):%s%s {%s} %s", + signature.getName(), + parameters.stream() + .map(Object::toString) + .collect(joining(",")), + signature.getReturnType(), + getVersion().map(version -> ":" + version).orElse(""), + body, + routineCharacteristics); + } +} diff --git a/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/SqlInvokedRegularFunctionHandle.java b/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/SqlInvokedRegularFunctionHandle.java new file mode 100644 index 0000000000000..b505aa385c2f6 --- /dev/null +++ b/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/SqlInvokedRegularFunctionHandle.java @@ -0,0 +1,99 @@ +/* + * 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.sqlfunction; + +import com.facebook.presto.spi.function.FunctionHandle; +import com.facebook.presto.spi.relation.FullyQualifiedName; +import com.facebook.presto.spi.type.TypeSignature; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; +import java.util.Objects; + +import static java.util.Objects.requireNonNull; +import static java.util.stream.Collectors.joining; + +public class SqlInvokedRegularFunctionHandle + implements FunctionHandle +{ + private final FullyQualifiedName name; + private final List argumentTypes; + private final long version; + + @JsonCreator + public SqlInvokedRegularFunctionHandle( + @JsonProperty("name") FullyQualifiedName name, + @JsonProperty("argumentTypes") List argumentTypes, + @JsonProperty("version") long version) + { + this.name = requireNonNull(name, "name is null"); + this.argumentTypes = requireNonNull(argumentTypes, "argumentTypes is null"); + this.version = version; + } + + @JsonProperty + public FullyQualifiedName getName() + { + return name; + } + + @JsonProperty + public List getArgumentTypes() + { + return argumentTypes; + } + + @JsonProperty + public long getVersion() + { + return version; + } + + @Override + public FullyQualifiedName.Prefix getFunctionNamespace() + { + return name.getPrefix(); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + SqlInvokedRegularFunctionHandle o = (SqlInvokedRegularFunctionHandle) obj; + return Objects.equals(name, o.name) + && Objects.equals(argumentTypes, o.argumentTypes) + && Objects.equals(version, o.version); + } + + @Override + public int hashCode() + { + return Objects.hash(name, argumentTypes, version); + } + + @Override + public String toString() + { + String arguments = argumentTypes.stream() + .map(Object::toString) + .collect(joining(", ")); + return String.format("%s(%s):%s", name, arguments, version); + } +} diff --git a/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/SqlParameter.java b/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/SqlParameter.java new file mode 100644 index 0000000000000..535ee31472b85 --- /dev/null +++ b/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/SqlParameter.java @@ -0,0 +1,68 @@ +/* + * 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.sqlfunction; + +import com.facebook.presto.spi.type.TypeSignature; + +import java.util.Objects; + +import static java.util.Objects.requireNonNull; + +public class SqlParameter +{ + private final String name; + private final TypeSignature type; + + public SqlParameter(String name, TypeSignature type) + { + this.name = requireNonNull(name, "name is null"); + this.type = requireNonNull(type, "type is null"); + } + + public String getName() + { + return name; + } + + public TypeSignature getType() + { + return type; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + SqlParameter o = (SqlParameter) obj; + return Objects.equals(name, o.name) + && Objects.equals(type, o.type); + } + + @Override + public int hashCode() + { + return Objects.hash(name, type); + } + + @Override + public String toString() + { + return String.format("%s %s", name, type); + } +} diff --git a/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/UuidFunctionNamespaceTransactionHandle.java b/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/UuidFunctionNamespaceTransactionHandle.java new file mode 100644 index 0000000000000..609b338b45ed6 --- /dev/null +++ b/presto-sql-function/src/main/java/com/facebook/presto/sqlfunction/UuidFunctionNamespaceTransactionHandle.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.sqlfunction; + +import com.facebook.presto.spi.function.FunctionNamespaceTransactionHandle; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Objects; +import java.util.UUID; + +import static java.util.Objects.requireNonNull; + +public class UuidFunctionNamespaceTransactionHandle + implements FunctionNamespaceTransactionHandle +{ + private final UUID uuid; + + private UuidFunctionNamespaceTransactionHandle(UUID uuid) + { + this.uuid = requireNonNull(uuid, "uuid is null"); + } + + public static UuidFunctionNamespaceTransactionHandle create() + { + return new UuidFunctionNamespaceTransactionHandle(UUID.randomUUID()); + } + + @JsonCreator + public static UuidFunctionNamespaceTransactionHandle valueOf(String value) + { + return new UuidFunctionNamespaceTransactionHandle(UUID.fromString(value)); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final UuidFunctionNamespaceTransactionHandle o = (UuidFunctionNamespaceTransactionHandle) obj; + return Objects.equals(uuid, o.uuid); + } + + @Override + public int hashCode() + { + return Objects.hash(uuid); + } + + @Override + @JsonValue + public String toString() + { + return uuid.toString(); + } +} diff --git a/presto-sql-function/src/test/java/com/facebook/presto/sqlfunction/TestSqlInvokedFunctionNamespaceManagerConfig.java b/presto-sql-function/src/test/java/com/facebook/presto/sqlfunction/TestSqlInvokedFunctionNamespaceManagerConfig.java new file mode 100644 index 0000000000000..4edbb180b6e5f --- /dev/null +++ b/presto-sql-function/src/test/java/com/facebook/presto/sqlfunction/TestSqlInvokedFunctionNamespaceManagerConfig.java @@ -0,0 +1,51 @@ +/* + * 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.sqlfunction; + +import com.google.common.collect.ImmutableMap; +import io.airlift.units.Duration; +import org.testng.annotations.Test; + +import java.util.Map; + +import static io.airlift.configuration.testing.ConfigAssertions.assertFullMapping; +import static io.airlift.configuration.testing.ConfigAssertions.assertRecordedDefaults; +import static io.airlift.configuration.testing.ConfigAssertions.recordDefaults; +import static java.util.concurrent.TimeUnit.HOURS; +import static java.util.concurrent.TimeUnit.MINUTES; + +public class TestSqlInvokedFunctionNamespaceManagerConfig +{ + @Test + public void testDefault() + { + assertRecordedDefaults(recordDefaults(SqlInvokedFunctionNamespaceManagerConfig.class) + .setFunctionCacheExpiration(new Duration(5, MINUTES)) + .setMetadataCacheExpiration(new Duration(8, HOURS))); + } + + @Test + public void testExplicitPropertyMappings() + { + Map properties = new ImmutableMap.Builder() + .put("function-cache-expiration", "10m") + .put("metadata-cache-expiration", "4h") + .build(); + SqlInvokedFunctionNamespaceManagerConfig expected = new SqlInvokedFunctionNamespaceManagerConfig() + .setFunctionCacheExpiration(new Duration(10, MINUTES)) + .setMetadataCacheExpiration(new Duration(4, HOURS)); + + assertFullMapping(properties, expected); + } +} 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 b68c99b9c4ba3..7d342a5b2d494 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 @@ -15,9 +15,9 @@ import com.facebook.presto.Session; import com.facebook.presto.SystemSessionProperties; +import com.facebook.presto.metadata.BuiltInFunction; import com.facebook.presto.metadata.FunctionListBuilder; import com.facebook.presto.spi.PrestoException; -import com.facebook.presto.spi.function.SqlFunction; import com.facebook.presto.spi.session.PropertyMetadata; import com.facebook.presto.spi.type.Decimals; import com.facebook.presto.spi.type.SqlTimestampWithTimeZone; @@ -103,7 +103,7 @@ public abstract class AbstractTestQueries extends AbstractTestQueryFramework { // We can just use the default type registry, since we don't use any parametric types - protected static final List CUSTOM_FUNCTIONS = new FunctionListBuilder() + protected static final List CUSTOM_FUNCTIONS = new FunctionListBuilder() .aggregates(CustomSum.class) .window(CustomRank.class) .scalars(CustomAdd.class) @@ -4107,7 +4107,7 @@ public void testWindowNoChannels() public void testInvalidWindowFunction() { assertQueryFails("SELECT abs(x) OVER ()\n" + - "FROM (VALUES (1), (2), (3)) t(x)", + "FROM (VALUES (1), (2), (3)) t(x)", "line 1:1: Not a window function: abs"); } diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/DistributedQueryRunner.java b/presto-tests/src/main/java/com/facebook/presto/tests/DistributedQueryRunner.java index 1f9bc39929669..e8deca804c2e3 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/DistributedQueryRunner.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/DistributedQueryRunner.java @@ -167,7 +167,7 @@ private DistributedQueryRunner( start = System.nanoTime(); for (TestingPrestoServer server : servers) { - server.getMetadata().addFunctions(AbstractTestQueries.CUSTOM_FUNCTIONS); + server.getMetadata().registerBuiltInFunctions(AbstractTestQueries.CUSTOM_FUNCTIONS); } log.info("Added functions in %s", nanosSince(start).convertToMostSuccinctTimeUnit()); diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/StandaloneQueryRunner.java b/presto-tests/src/main/java/com/facebook/presto/tests/StandaloneQueryRunner.java index 876de3376e56a..3d6285e2ff614 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/StandaloneQueryRunner.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/StandaloneQueryRunner.java @@ -75,7 +75,7 @@ public StandaloneQueryRunner(Session defaultSession) refreshNodes(); - server.getMetadata().addFunctions(AbstractTestQueries.CUSTOM_FUNCTIONS); + server.getMetadata().registerBuiltInFunctions(AbstractTestQueries.CUSTOM_FUNCTIONS); SessionPropertyManager sessionPropertyManager = server.getMetadata().getSessionPropertyManager(); sessionPropertyManager.addSystemSessionProperties(TEST_SYSTEM_PROPERTIES); diff --git a/presto-tests/src/test/java/com/facebook/presto/tests/TestLocalQueries.java b/presto-tests/src/test/java/com/facebook/presto/tests/TestLocalQueries.java index 6a0bb87757902..1a3d62158eb0f 100644 --- a/presto-tests/src/test/java/com/facebook/presto/tests/TestLocalQueries.java +++ b/presto-tests/src/test/java/com/facebook/presto/tests/TestLocalQueries.java @@ -70,7 +70,7 @@ public static LocalQueryRunner createLocalQueryRunner() new TpchConnectorFactory(1), ImmutableMap.of()); - localQueryRunner.getMetadata().addFunctions(CUSTOM_FUNCTIONS); + localQueryRunner.getMetadata().registerBuiltInFunctions(CUSTOM_FUNCTIONS); SessionPropertyManager sessionPropertyManager = localQueryRunner.getMetadata().getSessionPropertyManager(); sessionPropertyManager.addSystemSessionProperties(TEST_SYSTEM_PROPERTIES); diff --git a/presto-tests/src/test/java/com/facebook/presto/tests/TestQueryPlanDeterminism.java b/presto-tests/src/test/java/com/facebook/presto/tests/TestQueryPlanDeterminism.java index 05fcd17b83d55..3c72675416d1a 100644 --- a/presto-tests/src/test/java/com/facebook/presto/tests/TestQueryPlanDeterminism.java +++ b/presto-tests/src/test/java/com/facebook/presto/tests/TestQueryPlanDeterminism.java @@ -71,7 +71,7 @@ private static LocalQueryRunner createLocalQueryRunner() new TpchConnectorFactory(1), ImmutableMap.of()); - localQueryRunner.getMetadata().addFunctions(CUSTOM_FUNCTIONS); + localQueryRunner.getMetadata().registerBuiltInFunctions(CUSTOM_FUNCTIONS); SessionPropertyManager sessionPropertyManager = localQueryRunner.getMetadata().getSessionPropertyManager(); sessionPropertyManager.addSystemSessionProperties(TEST_SYSTEM_PROPERTIES); diff --git a/presto-verifier/pom.xml b/presto-verifier/pom.xml index 95fb05ba063b6..979619358ff0a 100644 --- a/presto-verifier/pom.xml +++ b/presto-verifier/pom.xml @@ -94,6 +94,11 @@ bootstrap + + io.airlift + concurrent + + io.airlift configuration diff --git a/presto-verifier/src/main/java/com/facebook/presto/verifier/framework/Column.java b/presto-verifier/src/main/java/com/facebook/presto/verifier/framework/Column.java index d27aded5acac2..c00dd65f5540e 100644 --- a/presto-verifier/src/main/java/com/facebook/presto/verifier/framework/Column.java +++ b/presto-verifier/src/main/java/com/facebook/presto/verifier/framework/Column.java @@ -13,14 +13,10 @@ */ package com.facebook.presto.verifier.framework; -import com.facebook.presto.block.BlockEncodingManager; -import com.facebook.presto.metadata.FunctionManager; -import com.facebook.presto.metadata.HandleResolver; import com.facebook.presto.spi.type.ArrayType; import com.facebook.presto.spi.type.Type; -import com.facebook.presto.sql.analyzer.FeaturesConfig; +import com.facebook.presto.spi.type.TypeManager; import com.facebook.presto.sql.tree.Identifier; -import com.facebook.presto.type.TypeRegistry; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableSet; @@ -48,11 +44,6 @@ public enum Category } private static final Set FLOATING_POINT_TYPES = ImmutableSet.of(DOUBLE, REAL); - private static final TypeRegistry typeRegistry = new TypeRegistry(); - - static { - new FunctionManager(typeRegistry, new BlockEncodingManager(typeRegistry), new FeaturesConfig(), new HandleResolver()); - } private final String name; private final Category category; @@ -86,10 +77,10 @@ public Type getType() return type; } - public static Column fromResultSet(ResultSet resultSet) + public static Column fromResultSet(TypeManager typeManager, ResultSet resultSet) throws SQLException { - Type type = typeRegistry.getType(parseTypeSignature(resultSet.getString("Type"))); + Type type = typeManager.getType(parseTypeSignature(resultSet.getString("Type"))); Category category; if (FLOATING_POINT_TYPES.contains(type)) { category = FLOATING_POINT; diff --git a/presto-verifier/src/main/java/com/facebook/presto/verifier/framework/DataVerification.java b/presto-verifier/src/main/java/com/facebook/presto/verifier/framework/DataVerification.java index 6279dcaa5afb9..3059bf28336a4 100644 --- a/presto-verifier/src/main/java/com/facebook/presto/verifier/framework/DataVerification.java +++ b/presto-verifier/src/main/java/com/facebook/presto/verifier/framework/DataVerification.java @@ -13,6 +13,7 @@ */ package com.facebook.presto.verifier.framework; +import com.facebook.presto.spi.type.TypeManager; import com.facebook.presto.sql.tree.QualifiedName; import com.facebook.presto.sql.tree.Query; import com.facebook.presto.sql.tree.ShowColumns; @@ -52,6 +53,7 @@ public class DataVerification extends AbstractVerification { + private final TypeManager typeManager; private final ChecksumValidator checksumValidator; private final LimitQueryDeterminismAnalyzer limitQueryDeterminismAnalyzer; @@ -63,10 +65,12 @@ public DataVerification( FailureResolverManager failureResolverManager, VerificationContext verificationContext, VerifierConfig verifierConfig, + TypeManager typeManager, ChecksumValidator checksumValidator, LimitQueryDeterminismAnalyzer limitQueryDeterminismAnalyzer) { super(verificationResubmitter, prestoAction, sourceQuery, queryRewriter, failureResolverManager, verificationContext, verifierConfig); + this.typeManager = requireNonNull(typeManager, "typeManager is null"); this.checksumValidator = requireNonNull(checksumValidator, "checksumValidator is null"); this.limitQueryDeterminismAnalyzer = requireNonNull(limitQueryDeterminismAnalyzer, "limitQueryDeterminismAnalyzer is null"); } @@ -192,7 +196,7 @@ private DeterminismAnalysis matchResultToDeterminism(MatchResult matchResult) private List getColumns(QualifiedName tableName) { return getPrestoAction() - .execute(new ShowColumns(tableName), DESCRIBE, Column::fromResultSet) + .execute(new ShowColumns(tableName), DESCRIBE, resultSet -> Column.fromResultSet(typeManager, resultSet)) .getResults(); } diff --git a/presto-verifier/src/main/java/com/facebook/presto/verifier/framework/VerificationFactory.java b/presto-verifier/src/main/java/com/facebook/presto/verifier/framework/VerificationFactory.java index b04a1839fdcf6..1b60d5f1cf17a 100644 --- a/presto-verifier/src/main/java/com/facebook/presto/verifier/framework/VerificationFactory.java +++ b/presto-verifier/src/main/java/com/facebook/presto/verifier/framework/VerificationFactory.java @@ -13,6 +13,7 @@ */ package com.facebook.presto.verifier.framework; +import com.facebook.presto.spi.type.TypeManager; import com.facebook.presto.sql.parser.SqlParser; import com.facebook.presto.verifier.annotation.ForTest; import com.facebook.presto.verifier.checksum.ChecksumValidator; @@ -41,6 +42,7 @@ public class VerificationFactory private final PrestoResourceClient testResourceClient; private final ChecksumValidator checksumValidator; private final VerifierConfig verifierConfig; + private final TypeManager typeManager; private final FailureResolverConfig failureResolverConfig; @Inject @@ -52,6 +54,7 @@ public VerificationFactory( @ForTest PrestoResourceClient testResourceClient, ChecksumValidator checksumValidator, VerifierConfig verifierConfig, + TypeManager typeManager, FailureResolverConfig failureResolverConfig) { this.sqlParser = requireNonNull(sqlParser, "sqlParser is null"); @@ -61,6 +64,7 @@ public VerificationFactory( this.testResourceClient = requireNonNull(testResourceClient, "testResourceClient is null"); this.checksumValidator = requireNonNull(checksumValidator, "checksumValidator is null"); this.verifierConfig = requireNonNull(verifierConfig, "config is null"); + this.typeManager = requireNonNull(typeManager, "typeManager is null"); this.failureResolverConfig = requireNonNull(failureResolverConfig, "failureResolverConfig is null"); } @@ -86,6 +90,7 @@ public Verification get(VerificationResubmitter verificationResubmitter, SourceQ failureResolverManager, verificationContext, verifierConfig, + typeManager, checksumValidator, limitQueryDeterminismAnalyzer); default: diff --git a/presto-verifier/src/main/java/com/facebook/presto/verifier/framework/VerifierModule.java b/presto-verifier/src/main/java/com/facebook/presto/verifier/framework/VerifierModule.java index 195a7270b9f08..18444d5aa37d9 100644 --- a/presto-verifier/src/main/java/com/facebook/presto/verifier/framework/VerifierModule.java +++ b/presto-verifier/src/main/java/com/facebook/presto/verifier/framework/VerifierModule.java @@ -13,9 +13,23 @@ */ package com.facebook.presto.verifier.framework; +import com.facebook.presto.block.BlockEncodingManager; +import com.facebook.presto.metadata.CatalogManager; +import com.facebook.presto.metadata.FunctionManager; +import com.facebook.presto.metadata.HandleJsonModule; +import com.facebook.presto.spi.block.BlockEncoding; +import com.facebook.presto.spi.block.BlockEncodingSerde; +import com.facebook.presto.spi.type.Type; +import com.facebook.presto.spi.type.TypeManager; +import com.facebook.presto.sql.analyzer.FeaturesConfig; import com.facebook.presto.sql.parser.SqlParser; import com.facebook.presto.sql.parser.SqlParserOptions; import com.facebook.presto.sql.tree.Property; +import com.facebook.presto.transaction.ForTransactionManager; +import com.facebook.presto.transaction.InMemoryTransactionManager; +import com.facebook.presto.transaction.TransactionManager; +import com.facebook.presto.transaction.TransactionManagerConfig; +import com.facebook.presto.type.TypeRegistry; import com.facebook.presto.verifier.annotation.ForControl; import com.facebook.presto.verifier.annotation.ForTest; import com.facebook.presto.verifier.checksum.ChecksumValidator; @@ -35,17 +49,26 @@ import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Key; +import com.google.inject.Provides; +import com.google.inject.Scopes; import com.google.inject.TypeLiteral; import io.airlift.configuration.AbstractConfigurationAwareModule; import javax.inject.Provider; +import javax.inject.Singleton; import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ScheduledExecutorService; import java.util.function.Predicate; import static com.google.inject.Scopes.SINGLETON; +import static com.google.inject.multibindings.Multibinder.newSetBinder; +import static io.airlift.concurrent.Threads.daemonThreadsNamed; import static io.airlift.configuration.ConfigBinder.configBinder; import static java.util.Objects.requireNonNull; +import static java.util.concurrent.Executors.newCachedThreadPool; +import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; public class VerifierModule extends AbstractConfigurationAwareModule @@ -85,12 +108,35 @@ protected final void setup(Binder binder) binder.bind(customQueryFilterClass).in(SINGLETON); } - install(new VerificationPrestoActionModule(exceptionClassifier)); - install(new VerificationQueryRewriterModule()); - install(new FailureResolverModule(failureResolverFactories)); + // block encoding + binder.bind(BlockEncodingSerde.class).to(BlockEncodingManager.class).in(Scopes.SINGLETON); + newSetBinder(binder, BlockEncoding.class); + + // catalog + binder.bind(CatalogManager.class).in(Scopes.SINGLETON); + + // function + binder.bind(FunctionManager.class).in(SINGLETON); + + // handle resolver + binder.install(new HandleJsonModule()); + // parser binder.bind(SqlParserOptions.class).toInstance(sqlParserOptions); binder.bind(SqlParser.class).in(SINGLETON); + + // transaction + configBinder(binder).bindConfig(TransactionManagerConfig.class); + + // type + configBinder(binder).bindConfig(FeaturesConfig.class); + binder.bind(TypeManager.class).to(TypeRegistry.class).in(SINGLETON); + newSetBinder(binder, Type.class); + + // verifier + install(new VerificationPrestoActionModule(exceptionClassifier)); + install(new VerificationQueryRewriterModule()); + install(new FailureResolverModule(failureResolverFactories)); binder.bind(VerificationManager.class).in(SINGLETON); binder.bind(VerificationFactory.class).in(SINGLETON); binder.bind(ChecksumValidator.class).in(SINGLETON); @@ -128,4 +174,31 @@ public List> get() return customVerificationFilters.build(); } } + + @Provides + @Singleton + @ForTransactionManager + public static ScheduledExecutorService createTransactionIdleCheckExecutor() + { + return newSingleThreadScheduledExecutor(daemonThreadsNamed("transaction-idle-check")); + } + + @Provides + @Singleton + @ForTransactionManager + public static ExecutorService createTransactionFinishingExecutor() + { + return newCachedThreadPool(daemonThreadsNamed("transaction-finishing-%s")); + } + + @Provides + @Singleton + public static TransactionManager createTransactionManager( + TransactionManagerConfig config, + CatalogManager catalogManager, + @ForTransactionManager ScheduledExecutorService idleCheckExecutor, + @ForTransactionManager ExecutorService finishingExecutor) + { + return InMemoryTransactionManager.create(config, idleCheckExecutor, catalogManager, finishingExecutor); + } } diff --git a/presto-verifier/src/test/java/com/facebook/presto/verifier/framework/TestDataVerification.java b/presto-verifier/src/test/java/com/facebook/presto/verifier/framework/TestDataVerification.java index 089e60c2ccc58..d239a41c1b154 100644 --- a/presto-verifier/src/test/java/com/facebook/presto/verifier/framework/TestDataVerification.java +++ b/presto-verifier/src/test/java/com/facebook/presto/verifier/framework/TestDataVerification.java @@ -17,6 +17,7 @@ import com.facebook.presto.sql.parser.SqlParserOptions; import com.facebook.presto.sql.tree.QualifiedName; import com.facebook.presto.tests.StandaloneQueryRunner; +import com.facebook.presto.type.TypeRegistry; import com.facebook.presto.verifier.checksum.ChecksumValidator; import com.facebook.presto.verifier.checksum.FloatingPointColumnValidator; import com.facebook.presto.verifier.checksum.OrderableArrayColumnValidator; @@ -109,6 +110,7 @@ private DataVerification createVerification(String controlQuery, String testQuer new FailureResolverManager(ImmutableList.of()), verificationContext, verifierConfig, + new TypeRegistry(), checksumValidator, new LimitQueryDeterminismAnalyzer(prestoAction, verifierConfig)); } diff --git a/presto-verifier/src/test/java/com/facebook/presto/verifier/framework/TestVerificationManager.java b/presto-verifier/src/test/java/com/facebook/presto/verifier/framework/TestVerificationManager.java index 8487771f284de..9ca5f6fb495ea 100644 --- a/presto-verifier/src/test/java/com/facebook/presto/verifier/framework/TestVerificationManager.java +++ b/presto-verifier/src/test/java/com/facebook/presto/verifier/framework/TestVerificationManager.java @@ -19,6 +19,7 @@ import com.facebook.presto.sql.parser.SqlParserOptions; import com.facebook.presto.sql.tree.QualifiedName; import com.facebook.presto.sql.tree.Statement; +import com.facebook.presto.type.TypeRegistry; import com.facebook.presto.verifier.checksum.ChecksumValidator; import com.facebook.presto.verifier.checksum.FloatingPointColumnValidator; import com.facebook.presto.verifier.checksum.OrderableArrayColumnValidator; @@ -137,6 +138,7 @@ private static VerificationManager getVerificationManager(List sour new MockPrestoResourceClient(), new ChecksumValidator(new SimpleColumnValidator(), new FloatingPointColumnValidator(verifierConfig), new OrderableArrayColumnValidator()), verifierConfig, + new TypeRegistry(), new FailureResolverConfig().setEnabled(false)), SQL_PARSER, ImmutableSet.of(),