diff --git a/core/trino-spi/src/main/java/io/trino/spi/Experimental.java b/core/trino-spi/src/main/java/io/trino/spi/Experimental.java
new file mode 100644
index 000000000000..3867e67245ad
--- /dev/null
+++ b/core/trino-spi/src/main/java/io/trino/spi/Experimental.java
@@ -0,0 +1,40 @@
+/*
+ * 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;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Signifies that a public API (public class, method or field) is subject to incompatible changes,
+ * or even removal, in a future release. An API bearing this annotation is exempt from any
+ * compatibility guarantees made by its containing library. Note that the presence of this
+ * annotation implies nothing about the quality or performance of the API in question, only the fact
+ * that it has not been finalized.
+ */
+@Retention(RUNTIME)
+@Target({TYPE, FIELD, METHOD, CONSTRUCTOR})
+public @interface Experimental
+{
+ /**
+ * When the community expects the SPI will be finalized, but this date may be extended.
+ */
+ String eta();
+}
diff --git a/core/trino-spi/src/main/java/io/trino/spi/ptf/AbstractConnectorTableFunction.java b/core/trino-spi/src/main/java/io/trino/spi/ptf/AbstractConnectorTableFunction.java
index 3d65d97a57a4..e23ecadd7219 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/ptf/AbstractConnectorTableFunction.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/ptf/AbstractConnectorTableFunction.java
@@ -13,6 +13,7 @@
*/
package io.trino.spi.ptf;
+import io.trino.spi.Experimental;
import io.trino.spi.connector.ConnectorSession;
import io.trino.spi.connector.ConnectorTransactionHandle;
@@ -21,6 +22,7 @@
import static java.util.Objects.requireNonNull;
+@Experimental(eta = "2022-10-31")
public abstract class AbstractConnectorTableFunction
implements ConnectorTableFunction
{
diff --git a/core/trino-spi/src/main/java/io/trino/spi/ptf/Argument.java b/core/trino-spi/src/main/java/io/trino/spi/ptf/Argument.java
index bc02ecac922c..ee2371cc911d 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/ptf/Argument.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/ptf/Argument.java
@@ -15,6 +15,7 @@
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import io.trino.spi.Experimental;
import io.trino.spi.expression.ConnectorExpression;
/**
@@ -32,6 +33,7 @@
@JsonSubTypes.Type(value = ScalarArgument.class, name = "scalar"),
@JsonSubTypes.Type(value = TableArgument.class, name = "table"),
})
+@Experimental(eta = "2022-10-31")
public abstract class Argument
{
}
diff --git a/core/trino-spi/src/main/java/io/trino/spi/ptf/ArgumentSpecification.java b/core/trino-spi/src/main/java/io/trino/spi/ptf/ArgumentSpecification.java
index 19644b3ab608..30374afa720e 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/ptf/ArgumentSpecification.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/ptf/ArgumentSpecification.java
@@ -13,6 +13,8 @@
*/
package io.trino.spi.ptf;
+import io.trino.spi.Experimental;
+
import javax.annotation.Nullable;
import static io.trino.spi.ptf.Preconditions.checkArgument;
@@ -28,6 +30,7 @@
*
* Default values are allowed for all arguments except Table arguments.
*/
+@Experimental(eta = "2022-10-31")
public abstract class ArgumentSpecification
{
private final String name;
diff --git a/core/trino-spi/src/main/java/io/trino/spi/ptf/ConnectorTableFunction.java b/core/trino-spi/src/main/java/io/trino/spi/ptf/ConnectorTableFunction.java
index 519cf987d02f..dddd662f86ef 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/ptf/ConnectorTableFunction.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/ptf/ConnectorTableFunction.java
@@ -13,12 +13,14 @@
*/
package io.trino.spi.ptf;
+import io.trino.spi.Experimental;
import io.trino.spi.connector.ConnectorSession;
import io.trino.spi.connector.ConnectorTransactionHandle;
import java.util.List;
import java.util.Map;
+@Experimental(eta = "2022-10-31")
public interface ConnectorTableFunction
{
String getSchema();
diff --git a/core/trino-spi/src/main/java/io/trino/spi/ptf/ConnectorTableFunctionHandle.java b/core/trino-spi/src/main/java/io/trino/spi/ptf/ConnectorTableFunctionHandle.java
index 5c6aececd1fe..47df2b836356 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/ptf/ConnectorTableFunctionHandle.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/ptf/ConnectorTableFunctionHandle.java
@@ -13,9 +13,12 @@
*/
package io.trino.spi.ptf;
+import io.trino.spi.Experimental;
+
/**
* An area to store all information necessary to execute the table function, gathered at analysis time
*/
+@Experimental(eta = "2022-10-31")
public interface ConnectorTableFunctionHandle
{
}
diff --git a/core/trino-spi/src/main/java/io/trino/spi/ptf/Descriptor.java b/core/trino-spi/src/main/java/io/trino/spi/ptf/Descriptor.java
index fa299773600f..11456c2d4e65 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/ptf/Descriptor.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/ptf/Descriptor.java
@@ -15,6 +15,7 @@
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
+import io.trino.spi.Experimental;
import io.trino.spi.type.Type;
import java.util.ArrayList;
@@ -27,6 +28,7 @@
import static io.trino.spi.ptf.Preconditions.checkNotNullOrEmpty;
import static java.util.Objects.requireNonNull;
+@Experimental(eta = "2022-10-31")
public class Descriptor
{
private final List fields;
diff --git a/core/trino-spi/src/main/java/io/trino/spi/ptf/DescriptorArgument.java b/core/trino-spi/src/main/java/io/trino/spi/ptf/DescriptorArgument.java
index 01cb5525b69b..85fca6e2b4ef 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/ptf/DescriptorArgument.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/ptf/DescriptorArgument.java
@@ -15,6 +15,7 @@
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
+import io.trino.spi.Experimental;
import io.trino.spi.expression.ConnectorExpression;
import java.util.Optional;
@@ -27,6 +28,7 @@
* This representation should be considered experimental. Eventually, {@link ConnectorExpression}
* should be extended to include this kind of argument.
*/
+@Experimental(eta = "2022-10-31")
public class DescriptorArgument
extends Argument
{
diff --git a/core/trino-spi/src/main/java/io/trino/spi/ptf/DescriptorArgumentSpecification.java b/core/trino-spi/src/main/java/io/trino/spi/ptf/DescriptorArgumentSpecification.java
index b447d28b7d86..8c9f250bbf35 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/ptf/DescriptorArgumentSpecification.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/ptf/DescriptorArgumentSpecification.java
@@ -13,6 +13,9 @@
*/
package io.trino.spi.ptf;
+import io.trino.spi.Experimental;
+
+@Experimental(eta = "2022-10-31")
public class DescriptorArgumentSpecification
extends ArgumentSpecification
{
diff --git a/core/trino-spi/src/main/java/io/trino/spi/ptf/DescriptorMapping.java b/core/trino-spi/src/main/java/io/trino/spi/ptf/DescriptorMapping.java
index 2fb5d2460649..3a910300c721 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/ptf/DescriptorMapping.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/ptf/DescriptorMapping.java
@@ -13,6 +13,8 @@
*/
package io.trino.spi.ptf;
+import io.trino.spi.Experimental;
+
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -23,6 +25,7 @@
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
+@Experimental(eta = "2022-10-31")
public class DescriptorMapping
{
public static final DescriptorMapping EMPTY_MAPPING = new DescriptorMappingBuilder().build();
diff --git a/core/trino-spi/src/main/java/io/trino/spi/ptf/NameAndPosition.java b/core/trino-spi/src/main/java/io/trino/spi/ptf/NameAndPosition.java
index f50c35b21cea..59cd944e40f0 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/ptf/NameAndPosition.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/ptf/NameAndPosition.java
@@ -13,6 +13,8 @@
*/
package io.trino.spi.ptf;
+import io.trino.spi.Experimental;
+
import java.util.Objects;
import static io.trino.spi.ptf.Preconditions.checkArgument;
@@ -27,6 +29,7 @@
* The Table Function is supposed to refer to input data using `NameAndPosition`,
* and the engine should provide the requested column.
*/
+@Experimental(eta = "2022-10-31")
public class NameAndPosition
{
private final String name;
diff --git a/core/trino-spi/src/main/java/io/trino/spi/ptf/ReturnTypeSpecification.java b/core/trino-spi/src/main/java/io/trino/spi/ptf/ReturnTypeSpecification.java
index f20990fe0495..73f016f0ae50 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/ptf/ReturnTypeSpecification.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/ptf/ReturnTypeSpecification.java
@@ -13,6 +13,8 @@
*/
package io.trino.spi.ptf;
+import io.trino.spi.Experimental;
+
import static io.trino.spi.ptf.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;
@@ -21,6 +23,7 @@
* These are the columns produced by the table function as opposed to the columns
* of input relations passed through by the table function.
*/
+@Experimental(eta = "2022-10-31")
public abstract class ReturnTypeSpecification
{
/**
diff --git a/core/trino-spi/src/main/java/io/trino/spi/ptf/ScalarArgument.java b/core/trino-spi/src/main/java/io/trino/spi/ptf/ScalarArgument.java
index e2a54b594101..3a981e3f5cbc 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/ptf/ScalarArgument.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/ptf/ScalarArgument.java
@@ -15,6 +15,7 @@
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
+import io.trino.spi.Experimental;
import io.trino.spi.expression.ConnectorExpression;
import io.trino.spi.type.Type;
@@ -29,6 +30,7 @@
* Additionally, only constant values are currently supported. In the future,
* we will add support for different kinds of expressions.
*/
+@Experimental(eta = "2022-10-31")
public class ScalarArgument
extends Argument
{
diff --git a/core/trino-spi/src/main/java/io/trino/spi/ptf/ScalarArgumentSpecification.java b/core/trino-spi/src/main/java/io/trino/spi/ptf/ScalarArgumentSpecification.java
index 74cbe675b14b..a0afc7d26833 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/ptf/ScalarArgumentSpecification.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/ptf/ScalarArgumentSpecification.java
@@ -13,12 +13,14 @@
*/
package io.trino.spi.ptf;
+import io.trino.spi.Experimental;
import io.trino.spi.type.Type;
import static io.trino.spi.ptf.Preconditions.checkArgument;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
+@Experimental(eta = "2022-10-31")
public class ScalarArgumentSpecification
extends ArgumentSpecification
{
diff --git a/core/trino-spi/src/main/java/io/trino/spi/ptf/TableArgument.java b/core/trino-spi/src/main/java/io/trino/spi/ptf/TableArgument.java
index 3f2b21b7b205..7c807eb6001c 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/ptf/TableArgument.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/ptf/TableArgument.java
@@ -15,6 +15,7 @@
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
+import io.trino.spi.Experimental;
import io.trino.spi.expression.ConnectorExpression;
import io.trino.spi.type.RowType;
@@ -30,6 +31,7 @@
* This representation should be considered experimental. Eventually, {@link ConnectorExpression}
* should be extended to include this kind of argument.
*/
+@Experimental(eta = "2022-10-31")
public class TableArgument
extends Argument
{
diff --git a/core/trino-spi/src/main/java/io/trino/spi/ptf/TableArgumentSpecification.java b/core/trino-spi/src/main/java/io/trino/spi/ptf/TableArgumentSpecification.java
index 1b45dc1a1c08..3df865fcd707 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/ptf/TableArgumentSpecification.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/ptf/TableArgumentSpecification.java
@@ -13,6 +13,9 @@
*/
package io.trino.spi.ptf;
+import io.trino.spi.Experimental;
+
+@Experimental(eta = "2022-10-31")
public class TableArgumentSpecification
extends ArgumentSpecification
{
diff --git a/core/trino-spi/src/main/java/io/trino/spi/ptf/TableFunctionAnalysis.java b/core/trino-spi/src/main/java/io/trino/spi/ptf/TableFunctionAnalysis.java
index aac561358480..ae676df25428 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/ptf/TableFunctionAnalysis.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/ptf/TableFunctionAnalysis.java
@@ -13,6 +13,8 @@
*/
package io.trino.spi.ptf;
+import io.trino.spi.Experimental;
+
import java.util.Optional;
import static io.trino.spi.ptf.DescriptorMapping.EMPTY_MAPPING;
@@ -37,6 +39,7 @@
* gathered at analysis time. Typically, these are the values of the constant arguments, and results
* of pre-processing arguments.
*/
+@Experimental(eta = "2022-10-31")
public final class TableFunctionAnalysis
{
private final Optional returnedType;
diff --git a/core/trino-spi/src/test/java/io/trino/spi/TestSpiBackwardCompatibility.java b/core/trino-spi/src/test/java/io/trino/spi/TestSpiBackwardCompatibility.java
index 7ba3992ed0df..399b64d48d29 100644
--- a/core/trino-spi/src/test/java/io/trino/spi/TestSpiBackwardCompatibility.java
+++ b/core/trino-spi/src/test/java/io/trino/spi/TestSpiBackwardCompatibility.java
@@ -22,6 +22,7 @@
import org.testng.annotations.Test;
import java.io.IOException;
+import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@@ -30,6 +31,8 @@
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.time.LocalDate;
+import java.time.format.DateTimeParseException;
import java.util.Set;
import java.util.stream.Stream;
@@ -37,6 +40,7 @@
import static com.google.common.collect.Sets.difference;
import static java.lang.ClassLoader.getPlatformClassLoader;
import static java.lang.ClassLoader.getSystemClassLoader;
+import static java.lang.String.format;
import static java.lang.reflect.Modifier.isPublic;
import static org.assertj.core.api.Assertions.assertThat;
@@ -55,22 +59,12 @@ public class TestSpiBackwardCompatibility
// example
.put("123", "Field: public java.util.List io.trino.spi.predicate.BenchmarkSortedRangeSet$Data.ranges")
.put("377", "Constructor: public io.trino.spi.memory.MemoryPoolInfo(long,long,long,java.util.Map,java.util.Map>,java.util.Map)")
- .put("382", "Method: public io.trino.spi.ptf.TableArgumentSpecification$Builder io.trino.spi.ptf.TableArgumentSpecification$Builder.rowSemantics(boolean)")
- .put("382", "Method: public io.trino.spi.ptf.TableArgumentSpecification$Builder io.trino.spi.ptf.TableArgumentSpecification$Builder.pruneWhenEmpty(boolean)")
- .put("382", "Method: public io.trino.spi.ptf.TableArgumentSpecification$Builder io.trino.spi.ptf.TableArgumentSpecification$Builder.passThroughColumns(boolean)")
- .put("382", "Class: public abstract class io.trino.spi.ptf.ConnectorTableFunction")
- .put("382", "Constructor: public io.trino.spi.ptf.ConnectorTableFunction(java.lang.String,java.lang.String,java.util.List,io.trino.spi.ptf.ReturnTypeSpecification)")
- .put("382", "Method: public java.util.List io.trino.spi.ptf.ConnectorTableFunction.getArguments()")
- .put("382", "Method: public io.trino.spi.ptf.ReturnTypeSpecification io.trino.spi.ptf.ConnectorTableFunction.getReturnTypeSpecification()")
- .put("382", "Method: public java.lang.String io.trino.spi.ptf.ConnectorTableFunction.getName()")
- .put("382", "Method: public java.lang.String io.trino.spi.ptf.ConnectorTableFunction.getSchema()")
.put("383", "Method: public abstract java.lang.String io.trino.spi.function.AggregationState.value()")
.put("383", "Method: public default void io.trino.spi.security.SystemAccessControl.checkCanExecuteFunction(io.trino.spi.security.SystemSecurityContext,io.trino.spi.connector.CatalogSchemaRoutineName)")
.put("383", "Method: public default void io.trino.spi.connector.ConnectorAccessControl.checkCanExecuteFunction(io.trino.spi.connector.ConnectorSecurityContext,io.trino.spi.connector.SchemaRoutineName)")
.put("384", "Constructor: public io.trino.spi.eventlistener.QueryInputMetadata(java.lang.String,java.lang.String,java.lang.String,java.util.List,java.util.Optional,java.util.OptionalLong,java.util.OptionalLong)")
.put("386", "Method: public default java.util.stream.Stream io.trino.spi.connector.ConnectorMetadata.streamTableColumns(io.trino.spi.connector.ConnectorSession,io.trino.spi.connector.SchemaTablePrefix)")
.put("386", "Method: public default boolean io.trino.spi.connector.ConnectorMetadata.isSupportedVersionType(io.trino.spi.connector.ConnectorSession,io.trino.spi.connector.SchemaTableName,io.trino.spi.connector.PointerType,io.trino.spi.type.Type)")
- .put("386", "Method: public static io.trino.spi.ptf.TableArgumentSpecification$Builder io.trino.spi.ptf.TableArgumentSpecification.builder(java.lang.String)")
.put("387", "Constructor: public io.trino.spi.eventlistener.QueryContext(java.lang.String,java.util.Optional,java.util.Set,java.util.Optional,java.util.Optional,java.util.Optional,java.util.Optional,java.util.Set,java.util.Set,java.util.Optional,java.util.Optional,java.util.Optional,java.util.Optional,java.util.Map,io.trino.spi.session.ResourceEstimates,java.lang.String,java.lang.String,java.lang.String,java.util.Optional)")
.put("388", "Method: public abstract java.util.concurrent.CompletableFuture io.trino.spi.connector.ConnectorSplitSource.getNextBatch(io.trino.spi.connector.ConnectorPartitionHandle,int)")
.put("388", "Method: public java.util.concurrent.CompletableFuture io.trino.spi.connector.FixedSplitSource.getNextBatch(io.trino.spi.connector.ConnectorPartitionHandle,int)")
@@ -141,9 +135,19 @@ private static Set getSpiEntities(ClassLoader classLoader, boolean inclu
private static void addClassEntities(ImmutableSet.Builder entities, Class> clazz, boolean includeDeprecated)
{
+ if (isExperimental(clazz, "class " + clazz.getName())) {
+ return;
+ }
+
if (!isPublic(clazz.getModifiers())) {
return;
}
+
+ // TODO remove this after Experimental is released
+ if (isOriginalPtfClass(clazz, includeDeprecated)) {
+ return;
+ }
+
for (Class> nestedClass : clazz.getDeclaredClasses()) {
addClassEntities(entities, nestedClass, includeDeprecated);
}
@@ -152,12 +156,18 @@ private static void addClassEntities(ImmutableSet.Builder entities, Clas
}
entities.add("Class: " + clazz.toGenericString());
for (Constructor> constructor : clazz.getConstructors()) {
+ if (isExperimental(constructor, "constructor " + constructor)) {
+ continue;
+ }
if (!includeDeprecated && constructor.isAnnotationPresent(Deprecated.class)) {
continue;
}
entities.add("Constructor: " + constructor.toGenericString());
}
for (Method method : clazz.getDeclaredMethods()) {
+ if (isExperimental(method, "method " + method)) {
+ continue;
+ }
if (!isPublic(method.getModifiers())) {
continue;
}
@@ -167,6 +177,9 @@ private static void addClassEntities(ImmutableSet.Builder entities, Clas
entities.add("Method: " + method.toGenericString());
}
for (Field field : clazz.getDeclaredFields()) {
+ if (isExperimental(field, "field " + field)) {
+ continue;
+ }
if (!isPublic(field.getModifiers())) {
continue;
}
@@ -176,4 +189,27 @@ private static void addClassEntities(ImmutableSet.Builder entities, Clas
entities.add("Field: " + field.toGenericString());
}
}
+
+ private static boolean isExperimental(AnnotatedElement element, String description)
+ {
+ if (!element.isAnnotationPresent(Experimental.class)) {
+ return false;
+ }
+
+ // validate the annotation while we have access to the annotation
+ String date = element.getAnnotation(Experimental.class).eta();
+ try {
+ LocalDate.parse(date);
+ }
+ catch (DateTimeParseException e) {
+ throw new AssertionError(format("Invalid date '%s' in Experimental annotation on %s", date, description));
+ }
+ return true;
+ }
+
+ // TODO remove this after Experimental is released
+ private static boolean isOriginalPtfClass(Class> clazz, boolean includeDeprecated)
+ {
+ return !includeDeprecated && clazz.getName().startsWith("io.trino.spi.ptf.");
+ }
}