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."); + } }