Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor ConnectorTableFunction into interface #12531

Merged
merged 2 commits into from
May 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -15,8 +15,8 @@

import javax.annotation.Nullable;

import static io.trino.spi.ptf.ConnectorTableFunction.checkArgument;
import static io.trino.spi.ptf.ConnectorTableFunction.checkNotNullOrEmpty;
import static io.trino.spi.ptf.Preconditions.checkArgument;
import static io.trino.spi.ptf.Preconditions.checkNotNullOrEmpty;

/**
* Abstract class to capture the three supported argument types for a table function:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,60 +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 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 @@ -87,19 +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);

static String checkNotNullOrEmpty(String value, String name)
{
requireNonNull(value, name + " is null");
checkArgument(!value.isEmpty(), name + " is empty");
return value;
}

static void checkArgument(boolean assertion, String message)
{
if (!assertion) {
throw new IllegalArgumentException(message);
}
}
TableFunctionAnalysis analyze(ConnectorSession session, ConnectorTransactionHandle transaction, Map<String, Argument> arguments);
}
4 changes: 2 additions & 2 deletions core/trino-spi/src/main/java/io/trino/spi/ptf/Descriptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
import java.util.Optional;
import java.util.stream.Collectors;

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

public class Descriptor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
import java.util.Map;
import java.util.Set;

import static io.trino.spi.ptf.ConnectorTableFunction.checkArgument;
import static io.trino.spi.ptf.ConnectorTableFunction.checkNotNullOrEmpty;
import static io.trino.spi.ptf.Preconditions.checkArgument;
import static io.trino.spi.ptf.Preconditions.checkNotNullOrEmpty;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@

import java.util.Objects;

import static io.trino.spi.ptf.Preconditions.checkArgument;
import static io.trino.spi.ptf.Preconditions.checkNotNullOrEmpty;

/**
* This class represents a descriptor field reference.
* `name` is the descriptor argument name, `position` is the zero-based field index.
Expand All @@ -31,8 +34,8 @@ public class NameAndPosition

public NameAndPosition(String name, int position)
{
this.name = ConnectorTableFunction.checkNotNullOrEmpty(name, "name");
ConnectorTableFunction.checkArgument(position >= 0, "position in descriptor must not be negative");
this.name = checkNotNullOrEmpty(name, "name");
checkArgument(position >= 0, "position in descriptor must not be negative");
this.position = position;
}

Expand Down
35 changes: 35 additions & 0 deletions core/trino-spi/src/main/java/io/trino/spi/ptf/Preconditions.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* 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 static java.util.Objects.requireNonNull;

final class Preconditions
{
private Preconditions() {}

public static String checkNotNullOrEmpty(String value, String name)
{
requireNonNull(value, name + " is null");
checkArgument(!value.isEmpty(), name + " is empty");
return value;
}

public static void checkArgument(boolean assertion, String message)
{
if (!assertion) {
throw new IllegalArgumentException(message);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
*/
package io.trino.spi.ptf;

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

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

import io.trino.spi.type.Type;

import static io.trino.spi.ptf.ConnectorTableFunction.checkArgument;
import static io.trino.spi.ptf.Preconditions.checkArgument;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import java.util.List;
import java.util.Optional;

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

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@

import java.util.Optional;

import static io.trino.spi.ptf.ConnectorTableFunction.checkArgument;
import static io.trino.spi.ptf.DescriptorMapping.EMPTY_MAPPING;
import static io.trino.spi.ptf.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;

/**
Expand Down
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