Skip to content

Commit

Permalink
Refactor ConnectorTableFunction into interface
Browse files Browse the repository at this point in the history
Introduce an abstract class as base for implementations
  • Loading branch information
kasiafi committed May 24, 2022
1 parent 18bb602 commit 4a7d72a
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,20 @@
import com.google.common.collect.ImmutableMap;
import io.trino.Session;
import io.trino.connector.CatalogName;
import io.trino.spi.ptf.ArgumentSpecification;
import io.trino.spi.ptf.ConnectorTableFunction;
import io.trino.spi.ptf.TableArgumentSpecification;
import io.trino.sql.tree.QualifiedName;

import javax.annotation.concurrent.ThreadSafe;

import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static io.trino.metadata.FunctionResolver.toPath;
import static java.util.Locale.ENGLISH;
Expand All @@ -41,6 +46,9 @@ public void addTableFunctions(CatalogName catalogName, Collection<ConnectorTable
requireNonNull(catalogName, "catalogName is null");
requireNonNull(functions, "functions is null");

functions.stream()
.forEach(TableFunctionRegistry::validateTableFunction);

ImmutableMap.Builder<SchemaFunctionName, TableFunctionMetadata> builder = ImmutableMap.builder();
for (ConnectorTableFunction function : functions) {
builder.put(
Expand Down Expand Up @@ -78,4 +86,29 @@ public TableFunctionMetadata resolve(Session session, QualifiedName qualifiedNam

return null;
}

private static void validateTableFunction(ConnectorTableFunction tableFunction)
{
requireNonNull(tableFunction, "tableFunction is null");
requireNonNull(tableFunction.getName(), "table function name is null");
requireNonNull(tableFunction.getSchema(), "table function schema name is null");
requireNonNull(tableFunction.getArguments(), "table function arguments is null");
requireNonNull(tableFunction.getReturnTypeSpecification(), "table function returnTypeSpecification is null");

checkArgument(!tableFunction.getName().isEmpty(), "table function name is empty");
checkArgument(!tableFunction.getSchema().isEmpty(), "table function schema name is empty");

Set<String> argumentNames = new HashSet<>();
for (ArgumentSpecification specification : tableFunction.getArguments()) {
if (!argumentNames.add(specification.getName())) {
throw new IllegalArgumentException("duplicate argument name: " + specification.getName());
}
}
long tableArgumentsWithRowSemantics = tableFunction.getArguments().stream()
.filter(specification -> specification instanceof TableArgumentSpecification)
.map(TableArgumentSpecification.class::cast)
.filter(TableArgumentSpecification::isRowSemantics)
.count();
checkArgument(tableArgumentsWithRowSemantics <= 1, "more than one table argument with row semantics");
}
}
Original file line number Diff line number Diff line change
@@ -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 io.trino.spi.ptf;

import io.trino.spi.connector.ConnectorSession;
import io.trino.spi.connector.ConnectorTransactionHandle;

import java.util.List;
import java.util.Map;

import static java.util.Objects.requireNonNull;

public abstract class AbstractConnectorTableFunction
implements ConnectorTableFunction
{
private final String schema;
private final String name;
private final List<ArgumentSpecification> arguments;
private final ReturnTypeSpecification returnTypeSpecification;

public AbstractConnectorTableFunction(String schema, String name, List<ArgumentSpecification> arguments, ReturnTypeSpecification returnTypeSpecification)
{
this.schema = requireNonNull(schema, "schema is null");
this.name = requireNonNull(name, "name is null");
this.arguments = List.copyOf(requireNonNull(arguments, "arguments is null"));
this.returnTypeSpecification = requireNonNull(returnTypeSpecification, "returnTypeSpecification is null");
}

@Override
public String getSchema()
{
return schema;
}

@Override
public String getName()
{
return name;
}

@Override
public List<ArgumentSpecification> getArguments()
{
return arguments;
}

@Override
public ReturnTypeSpecification getReturnTypeSpecification()
{
return returnTypeSpecification;
}

@Override
public abstract TableFunctionAnalysis analyze(ConnectorSession session, ConnectorTransactionHandle transaction, Map<String, Argument> arguments);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,62 +16,18 @@
import io.trino.spi.connector.ConnectorSession;
import io.trino.spi.connector.ConnectorTransactionHandle;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static io.trino.spi.ptf.Preconditions.checkArgument;
import static io.trino.spi.ptf.Preconditions.checkNotNullOrEmpty;
import static java.util.Objects.requireNonNull;

public abstract class ConnectorTableFunction
public interface ConnectorTableFunction
{
private final String schema;
private final String name;
private final List<ArgumentSpecification> arguments;
private final ReturnTypeSpecification returnTypeSpecification;

public ConnectorTableFunction(String schema, String name, List<ArgumentSpecification> arguments, ReturnTypeSpecification returnTypeSpecification)
{
this.schema = checkNotNullOrEmpty(schema, "schema");
this.name = checkNotNullOrEmpty(name, "name");
requireNonNull(arguments, "arguments is null");
Set<String> argumentNames = new HashSet<>();
for (ArgumentSpecification specification : arguments) {
if (!argumentNames.add(specification.getName())) {
throw new IllegalArgumentException("duplicate argument name: " + specification.getName());
}
}
long tableArgumentsWithRowSemantics = arguments.stream()
.filter(specification -> specification instanceof TableArgumentSpecification)
.map(TableArgumentSpecification.class::cast)
.filter(TableArgumentSpecification::isRowSemantics)
.count();
checkArgument(tableArgumentsWithRowSemantics <= 1, "more than one table argument with row semantics");
this.arguments = List.copyOf(arguments);
this.returnTypeSpecification = requireNonNull(returnTypeSpecification, "returnTypeSpecification is null");
}

public String getSchema()
{
return schema;
}
String getSchema();

public String getName()
{
return name;
}
String getName();

public List<ArgumentSpecification> getArguments()
{
return arguments;
}
List<ArgumentSpecification> getArguments();

public ReturnTypeSpecification getReturnTypeSpecification()
{
return returnTypeSpecification;
}
ReturnTypeSpecification getReturnTypeSpecification();

/**
* This method is called by the Analyzer. Its main purposes are to:
Expand All @@ -89,5 +45,5 @@ public ReturnTypeSpecification getReturnTypeSpecification()
*
* @param arguments actual invocation arguments, mapped by argument names
*/
public abstract TableFunctionAnalysis analyze(ConnectorSession session, ConnectorTransactionHandle transaction, Map<String, Argument> arguments);
TableFunctionAnalysis analyze(ConnectorSession session, ConnectorTransactionHandle transaction, Map<String, Argument> arguments);
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,13 @@ public class TestSpiBackwardCompatibility
.put("382", ImmutableSet.of(
"Method: public io.trino.spi.ptf.TableArgumentSpecification$Builder io.trino.spi.ptf.TableArgumentSpecification$Builder.rowSemantics(boolean)",
"Method: public io.trino.spi.ptf.TableArgumentSpecification$Builder io.trino.spi.ptf.TableArgumentSpecification$Builder.pruneWhenEmpty(boolean)",
"Method: public io.trino.spi.ptf.TableArgumentSpecification$Builder io.trino.spi.ptf.TableArgumentSpecification$Builder.passThroughColumns(boolean)"))
"Method: public io.trino.spi.ptf.TableArgumentSpecification$Builder io.trino.spi.ptf.TableArgumentSpecification$Builder.passThroughColumns(boolean)",
"Class: public abstract class io.trino.spi.ptf.ConnectorTableFunction",
"Constructor: public io.trino.spi.ptf.ConnectorTableFunction(java.lang.String,java.lang.String,java.util.List<io.trino.spi.ptf.ArgumentSpecification>,io.trino.spi.ptf.ReturnTypeSpecification)",
"Method: public java.util.List<io.trino.spi.ptf.ArgumentSpecification> io.trino.spi.ptf.ConnectorTableFunction.getArguments()",
"Method: public io.trino.spi.ptf.ReturnTypeSpecification io.trino.spi.ptf.ConnectorTableFunction.getReturnTypeSpecification()",
"Method: public java.lang.String io.trino.spi.ptf.ConnectorTableFunction.getName()",
"Method: public java.lang.String io.trino.spi.ptf.ConnectorTableFunction.getSchema()"))
.buildOrThrow();

@Test
Expand Down
5 changes: 3 additions & 2 deletions docs/src/main/sphinx/develop/table-functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ through implementing dedicated interfaces.
Table function declaration
--------------------------

To declare a table function, you need to subclass ``ConnectorTableFunction``.
To declare a table function, you need to implement ``ConnectorTableFunction``.
Subclassing ``AbstractConnectorTableFunction`` is a convenient way to do it.
The connector's ``getTableFunctions()`` method must return a ``Provider`` of
your implementation.

Expand All @@ -24,7 +25,7 @@ The constructor
.. code-block:: java
public class MyFunction
extends ConnectorTableFunction
extends AbstractConnectorTableFunction
{
public MyFunction()
{
Expand Down

0 comments on commit 4a7d72a

Please sign in to comment.