Skip to content

Commit 0e6fbad

Browse files
pdabre12Pratik Joseph Dabre
authored andcommitted
Introduce getSqlInvokedFunctions SPI and BuiltInPluginFunctionNamespaceManager for registering sql invoked functions
1 parent 84ae840 commit 0e6fbad

File tree

13 files changed

+511
-74
lines changed

13 files changed

+511
-74
lines changed

presto-main-base/src/main/java/com/facebook/presto/metadata/BuiltInFunctionHandle.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import com.facebook.presto.common.CatalogSchemaName;
1717
import com.facebook.presto.common.type.TypeSignature;
18+
import com.facebook.presto.spi.function.BuiltInFunctionKind;
1819
import com.facebook.presto.spi.function.FunctionHandle;
1920
import com.facebook.presto.spi.function.FunctionKind;
2021
import com.facebook.presto.spi.function.Signature;
@@ -24,18 +25,26 @@
2425
import java.util.List;
2526
import java.util.Objects;
2627

28+
import static com.facebook.presto.spi.function.BuiltInFunctionKind.ENGINE;
2729
import static java.util.Objects.requireNonNull;
2830

2931
public class BuiltInFunctionHandle
3032
implements FunctionHandle
3133
{
3234
private final Signature signature;
35+
private final BuiltInFunctionKind builtInFunctionKind;
3336

3437
@JsonCreator
3538
public BuiltInFunctionHandle(@JsonProperty("signature") Signature signature)
39+
{
40+
this(signature, ENGINE);
41+
}
42+
43+
public BuiltInFunctionHandle(Signature signature, BuiltInFunctionKind builtInFunctionKind)
3644
{
3745
this.signature = requireNonNull(signature, "signature is null");
3846
checkArgument(signature.getTypeVariableConstraints().isEmpty(), "%s has unbound type parameters", signature);
47+
this.builtInFunctionKind = requireNonNull(builtInFunctionKind, "builtInFunctionKind is null");
3948
}
4049

4150
@JsonProperty
@@ -68,6 +77,12 @@ public CatalogSchemaName getCatalogSchemaName()
6877
return signature.getName().getCatalogSchemaName();
6978
}
7079

80+
@JsonProperty
81+
public BuiltInFunctionKind getBuiltInFunctionKind()
82+
{
83+
return builtInFunctionKind;
84+
}
85+
7186
@Override
7287
public boolean equals(Object o)
7388
{
@@ -78,13 +93,14 @@ public boolean equals(Object o)
7893
return false;
7994
}
8095
BuiltInFunctionHandle that = (BuiltInFunctionHandle) o;
81-
return Objects.equals(signature, that.signature);
96+
return Objects.equals(signature, that.signature)
97+
&& Objects.equals(builtInFunctionKind, that.builtInFunctionKind);
8298
}
8399

84100
@Override
85101
public int hashCode()
86102
{
87-
return Objects.hash(signature);
103+
return Objects.hash(signature, builtInFunctionKind);
88104
}
89105

90106
@Override
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
package com.facebook.presto.metadata;
15+
16+
import com.facebook.presto.common.Page;
17+
import com.facebook.presto.common.QualifiedObjectName;
18+
import com.facebook.presto.common.block.BlockEncodingSerde;
19+
import com.facebook.presto.common.function.SqlFunctionResult;
20+
import com.facebook.presto.common.type.TypeManager;
21+
import com.facebook.presto.common.type.UserDefinedType;
22+
import com.facebook.presto.spi.PrestoException;
23+
import com.facebook.presto.spi.function.AlterRoutineCharacteristics;
24+
import com.facebook.presto.spi.function.FunctionHandle;
25+
import com.facebook.presto.spi.function.FunctionMetadata;
26+
import com.facebook.presto.spi.function.FunctionNamespaceManager;
27+
import com.facebook.presto.spi.function.FunctionNamespaceTransactionHandle;
28+
import com.facebook.presto.spi.function.Parameter;
29+
import com.facebook.presto.spi.function.ScalarFunctionImplementation;
30+
import com.facebook.presto.spi.function.Signature;
31+
import com.facebook.presto.spi.function.SqlFunction;
32+
import com.facebook.presto.spi.function.SqlInvokedFunction;
33+
import com.facebook.presto.spi.function.SqlInvokedScalarFunctionImplementation;
34+
import com.google.common.base.Supplier;
35+
import com.google.common.base.Suppliers;
36+
import com.google.common.cache.CacheBuilder;
37+
import com.google.common.cache.CacheLoader;
38+
import com.google.common.cache.LoadingCache;
39+
import com.google.common.util.concurrent.UncheckedExecutionException;
40+
41+
import java.util.Collection;
42+
import java.util.List;
43+
import java.util.Optional;
44+
import java.util.concurrent.CompletableFuture;
45+
46+
import static com.facebook.presto.spi.function.BuiltInFunctionKind.PLUGIN;
47+
import static com.facebook.presto.spi.function.FunctionImplementationType.SQL;
48+
import static com.facebook.presto.spi.function.FunctionKind.SCALAR;
49+
import static com.google.common.base.Preconditions.checkArgument;
50+
import static com.google.common.base.Throwables.throwIfInstanceOf;
51+
import static com.google.common.collect.ImmutableList.toImmutableList;
52+
import static java.util.Collections.emptyList;
53+
import static java.util.Objects.requireNonNull;
54+
import static java.util.concurrent.TimeUnit.HOURS;
55+
56+
public class BuiltInPluginFunctionNamespaceManager
57+
implements FunctionNamespaceManager<SqlFunction>
58+
{
59+
private volatile FunctionMap functions = new FunctionMap();
60+
private final FunctionAndTypeManager functionAndTypeManager;
61+
private final Supplier<FunctionMap> cachedFunctions =
62+
Suppliers.memoize(this::checkForNamingConflicts);
63+
private final LoadingCache<Signature, SpecializedFunctionKey> specializedFunctionKeyCache;
64+
private final LoadingCache<SpecializedFunctionKey, ScalarFunctionImplementation> specializedScalarCache;
65+
66+
public BuiltInPluginFunctionNamespaceManager(FunctionAndTypeManager functionAndTypeManager)
67+
{
68+
this.functionAndTypeManager = requireNonNull(functionAndTypeManager, "functionAndTypeManager is null");
69+
specializedFunctionKeyCache = CacheBuilder.newBuilder()
70+
.maximumSize(1000)
71+
.expireAfterWrite(1, HOURS)
72+
.build(CacheLoader.from(this::doGetSpecializedFunctionKey));
73+
specializedScalarCache = CacheBuilder.newBuilder()
74+
.maximumSize(1000)
75+
.expireAfterWrite(1, HOURS)
76+
.build(CacheLoader.from(key -> {
77+
checkArgument(
78+
key.getFunction() instanceof SqlInvokedFunction,
79+
"Unsupported scalar function class: %s",
80+
key.getFunction().getClass());
81+
return new SqlInvokedScalarFunctionImplementation(((SqlInvokedFunction) key.getFunction()).getBody());
82+
}));
83+
}
84+
85+
public synchronized void registerPluginFunctions(List<? extends SqlFunction> functions)
86+
{
87+
checkForNamingConflicts(functions);
88+
this.functions = new FunctionMap(this.functions, functions);
89+
}
90+
91+
@Override
92+
public FunctionHandle getFunctionHandle(Optional<? extends FunctionNamespaceTransactionHandle> transactionHandle, Signature signature)
93+
{
94+
return new BuiltInFunctionHandle(signature, PLUGIN);
95+
}
96+
97+
@Override
98+
public Collection<SqlFunction> getFunctions(Optional<? extends FunctionNamespaceTransactionHandle> transactionHandle, QualifiedObjectName functionName)
99+
{
100+
if (functions.list().isEmpty() ||
101+
(!functionName.getCatalogSchemaName().equals(functionAndTypeManager.getDefaultNamespace()))) {
102+
return emptyList();
103+
}
104+
return cachedFunctions.get().get(functionName);
105+
}
106+
107+
/**
108+
* likePattern / escape is not used for optimization, returning all functions.
109+
*/
110+
@Override
111+
public Collection<SqlFunction> listFunctions(Optional<String> likePattern, Optional<String> escape)
112+
{
113+
return cachedFunctions.get().list();
114+
}
115+
116+
public FunctionMetadata getFunctionMetadata(FunctionHandle functionHandle)
117+
{
118+
checkArgument(functionHandle instanceof BuiltInFunctionHandle, "Expect BuiltInFunctionHandle");
119+
Signature signature = ((BuiltInFunctionHandle) functionHandle).getSignature();
120+
SpecializedFunctionKey functionKey;
121+
try {
122+
functionKey = specializedFunctionKeyCache.getUnchecked(signature);
123+
}
124+
catch (UncheckedExecutionException e) {
125+
throwIfInstanceOf(e.getCause(), PrestoException.class);
126+
throw e;
127+
}
128+
SqlFunction function = functionKey.getFunction();
129+
checkArgument(function instanceof SqlInvokedFunction, "BuiltInPluginFunctionNamespaceManager only support SqlInvokedFunctions");
130+
SqlInvokedFunction sqlFunction = (SqlInvokedFunction) function;
131+
List<String> argumentNames = sqlFunction.getParameters().stream().map(Parameter::getName).collect(toImmutableList());
132+
return new FunctionMetadata(
133+
signature.getName(),
134+
signature.getArgumentTypes(),
135+
argumentNames,
136+
signature.getReturnType(),
137+
signature.getKind(),
138+
sqlFunction.getRoutineCharacteristics().getLanguage(),
139+
SQL,
140+
function.isDeterministic(),
141+
function.isCalledOnNullInput(),
142+
sqlFunction.getVersion(),
143+
sqlFunction.getComplexTypeFunctionDescriptor());
144+
}
145+
146+
public ScalarFunctionImplementation getScalarFunctionImplementation(FunctionHandle functionHandle)
147+
{
148+
checkArgument(functionHandle instanceof BuiltInFunctionHandle, "Expect BuiltInFunctionHandle");
149+
return getScalarFunctionImplementation(((BuiltInFunctionHandle) functionHandle).getSignature());
150+
}
151+
152+
@Override
153+
public void setBlockEncodingSerde(BlockEncodingSerde blockEncodingSerde)
154+
{
155+
throw new UnsupportedOperationException("BuiltInPluginFunctionNamespaceManager does not support setting block encoding");
156+
}
157+
158+
@Override
159+
public FunctionNamespaceTransactionHandle beginTransaction()
160+
{
161+
throw new UnsupportedOperationException("BuiltInPluginFunctionNamespaceManager does not support setting block encoding");
162+
}
163+
164+
@Override
165+
public void commit(FunctionNamespaceTransactionHandle transactionHandle)
166+
{
167+
throw new UnsupportedOperationException("BuiltInPluginFunctionNamespaceManager does not support setting block encoding");
168+
}
169+
170+
@Override
171+
public void abort(FunctionNamespaceTransactionHandle transactionHandle)
172+
{
173+
throw new UnsupportedOperationException("BuiltInPluginFunctionNamespaceManager does not support setting block encoding");
174+
}
175+
176+
@Override
177+
public void createFunction(SqlInvokedFunction function, boolean replace)
178+
{
179+
throw new UnsupportedOperationException("BuiltInPluginFunctionNamespaceManager does not support setting block encoding");
180+
}
181+
182+
@Override
183+
public void dropFunction(QualifiedObjectName functionName, Optional parameterTypes, boolean exists)
184+
{
185+
throw new UnsupportedOperationException("BuiltInPluginFunctionNamespaceManager does not support drop function");
186+
}
187+
188+
@Override
189+
public void alterFunction(QualifiedObjectName functionName, Optional parameterTypes, AlterRoutineCharacteristics alterRoutineCharacteristics)
190+
{
191+
throw new UnsupportedOperationException("BuiltInPluginFunctionNamespaceManager does not alter function");
192+
}
193+
194+
@Override
195+
public void addUserDefinedType(UserDefinedType userDefinedType)
196+
{
197+
throw new UnsupportedOperationException("BuiltInPluginFunctionNamespaceManager does not support adding user defined types");
198+
}
199+
200+
@Override
201+
public Optional<UserDefinedType> getUserDefinedType(QualifiedObjectName typeName)
202+
{
203+
throw new UnsupportedOperationException("BuiltInPluginFunctionNamespaceManager does not support getting user defined types");
204+
}
205+
206+
@Override
207+
public CompletableFuture<SqlFunctionResult> executeFunction(String source, FunctionHandle functionHandle, Page input, List channels, TypeManager typeManager)
208+
{
209+
throw new UnsupportedOperationException("BuiltInPluginFunctionNamespaceManager does not execute function");
210+
}
211+
212+
private ScalarFunctionImplementation getScalarFunctionImplementation(Signature signature)
213+
{
214+
checkArgument(signature.getKind() == SCALAR, "%s is not a scalar function", signature);
215+
checkArgument(signature.getTypeVariableConstraints().isEmpty(), "%s has unbound type parameters", signature);
216+
217+
try {
218+
return specializedScalarCache.getUnchecked(getSpecializedFunctionKey(signature));
219+
}
220+
catch (UncheckedExecutionException e) {
221+
throwIfInstanceOf(e.getCause(), PrestoException.class);
222+
throw e;
223+
}
224+
}
225+
226+
private synchronized FunctionMap checkForNamingConflicts()
227+
{
228+
Optional<FunctionNamespaceManager<?>> functionNamespaceManager =
229+
functionAndTypeManager.getServingFunctionNamespaceManager(functionAndTypeManager.getDefaultNamespace());
230+
checkArgument(functionNamespaceManager.isPresent(), "Cannot find function namespace for catalog '%s'", functionAndTypeManager.getDefaultNamespace().getCatalogName());
231+
checkForNamingConflicts(functionNamespaceManager.get().listFunctions(Optional.empty(), Optional.empty()));
232+
return functions;
233+
}
234+
235+
private synchronized void checkForNamingConflicts(Collection<? extends SqlFunction> functions)
236+
{
237+
for (SqlFunction function : functions) {
238+
for (SqlFunction existingFunction : this.functions.list()) {
239+
checkArgument(!function.getSignature().equals(existingFunction.getSignature()), "Function already registered: %s", function.getSignature());
240+
}
241+
}
242+
}
243+
244+
private SpecializedFunctionKey doGetSpecializedFunctionKey(Signature signature)
245+
{
246+
return functionAndTypeManager.getSpecializedFunctionKey(signature, getFunctions(Optional.empty(), signature.getName()));
247+
}
248+
249+
private SpecializedFunctionKey getSpecializedFunctionKey(Signature signature)
250+
{
251+
try {
252+
return specializedFunctionKeyCache.getUnchecked(signature);
253+
}
254+
catch (UncheckedExecutionException e) {
255+
throwIfInstanceOf(e.getCause(), PrestoException.class);
256+
throw e;
257+
}
258+
}
259+
}

presto-main-base/src/main/java/com/facebook/presto/metadata/BuiltInTypeAndFunctionNamespaceManager.java

Lines changed: 0 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -292,9 +292,6 @@
292292
import com.google.common.cache.CacheLoader;
293293
import com.google.common.cache.LoadingCache;
294294
import com.google.common.collect.ImmutableList;
295-
import com.google.common.collect.ImmutableListMultimap;
296-
import com.google.common.collect.Multimap;
297-
import com.google.common.collect.Multimaps;
298295
import com.google.common.util.concurrent.UncheckedExecutionException;
299296
import io.airlift.slice.Slice;
300297

@@ -1396,44 +1393,6 @@ private static class EmptyTransactionHandle
13961393
{
13971394
}
13981395

1399-
private static class FunctionMap
1400-
{
1401-
private final Multimap<QualifiedObjectName, SqlFunction> functions;
1402-
1403-
public FunctionMap()
1404-
{
1405-
functions = ImmutableListMultimap.of();
1406-
}
1407-
1408-
public FunctionMap(FunctionMap map, Iterable<? extends SqlFunction> functions)
1409-
{
1410-
this.functions = ImmutableListMultimap.<QualifiedObjectName, SqlFunction>builder()
1411-
.putAll(map.functions)
1412-
.putAll(Multimaps.index(functions, function -> function.getSignature().getName()))
1413-
.build();
1414-
1415-
// Make sure all functions with the same name are aggregations or none of them are
1416-
for (Map.Entry<QualifiedObjectName, Collection<SqlFunction>> entry : this.functions.asMap().entrySet()) {
1417-
Collection<SqlFunction> values = entry.getValue();
1418-
long aggregations = values.stream()
1419-
.map(function -> function.getSignature().getKind())
1420-
.filter(kind -> kind == AGGREGATE)
1421-
.count();
1422-
checkState(aggregations == 0 || aggregations == values.size(), "'%s' is both an aggregation and a scalar function", entry.getKey());
1423-
}
1424-
}
1425-
1426-
public List<SqlFunction> list()
1427-
{
1428-
return ImmutableList.copyOf(functions.values());
1429-
}
1430-
1431-
public Collection<SqlFunction> get(QualifiedObjectName name)
1432-
{
1433-
return functions.get(name);
1434-
}
1435-
}
1436-
14371396
/**
14381397
* TypeSignature but has overridden equals(). Here, we compare exact signature of any underlying distinct
14391398
* types. Some distinct types may have extra information on their lazily loaded parents, and same parent

0 commit comments

Comments
 (0)