diff --git a/docs/src/main/sphinx/develop/spi-overview.md b/docs/src/main/sphinx/develop/spi-overview.md
index 872619f2dda1..c1367e1cc6a3 100644
--- a/docs/src/main/sphinx/develop/spi-overview.md
+++ b/docs/src/main/sphinx/develop/spi-overview.md
@@ -87,6 +87,41 @@ of a library that Trino uses internally.
For an example `pom.xml` file, see the example HTTP connector in the
`plugin/trino-example-http` directory in the Trino source tree.
+## Building plugins via Maven Archetype
+
+To generate a complete example plugin Maven project, use the Maven Archetype.
+It will require the following parameters:
+
+* `archetypeVersion` should match a released Trino version
+* `groupId` and `artifactId` are chosen by the plugin author
+* `classPrefix` is used as the prefix for all Java classes in the plugin
+* `connectorName` is used as the name of the connector
+
+After creating the project, it also needs to be initialized as a git repository
+and commited, before it can be built. Example:
+
+```bash
+mvn archetype:generate \
+ -DarchetypeGroupId=io.trino \
+ -DarchetypeArtifactId=trino-plugin-archetype \
+ -DarchetypeVersion=$trinoVersion \
+ -DgroupId=$groupId \
+ -DartifactId=$artifactId \
+ -DclassPrefix=$classPrefix \
+ -DconnectorName=$connectorName
+
+cd $artifactId
+git init
+git add --all .
+git commit -m "Initial commit"
+
+mvn clean package
+```
+
+The example connector provides a single table with 3 columns and queries
+for table table return a single row. To change that, start with modifying
+the `ExampleMetadata` and `ExampleRecordSetProvider` classes.
+
## Deploying a custom plugin
Because Trino plugins use the `trino-plugin` packaging type, building
diff --git a/plugin/trino-example-plugin/LICENSE b/plugin/trino-example-plugin/LICENSE
new file mode 100644
index 000000000000..d64569567334
--- /dev/null
+++ b/plugin/trino-example-plugin/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/plugin/trino-example-plugin/README.md b/plugin/trino-example-plugin/README.md
new file mode 100644
index 000000000000..0d6401268822
--- /dev/null
+++ b/plugin/trino-example-plugin/README.md
@@ -0,0 +1,62 @@
+Trino Plugin
+============
+
+This is a [Trino](http://trino.io/) plugin that provides a connector.
+
+## Usage
+
+Download one of the ZIP packages, unzip it and copy the `trino-example-plugin-0.1` directory to the plugin directory on every node in your Trino cluster.
+Create a `example.properties` file in your Trino catalog directory and set all the required properties.
+
+```
+connector.name=example
+```
+
+After reloading Trino, you should be able to connect to the `example` catalog.
+
+## Build
+
+Run all the unit tests:
+```bash
+mvn test
+```
+
+Creates a deployable zip file:
+```bash
+mvn clean package
+```
+
+Unzip the archive from the target directory to use the connector in your Trino cluster.
+```bash
+unzip target/*.zip -d ${PLUGIN_DIRECTORY}/
+mv ${PLUGIN_DIRECTORY}/trino-example-plugin-* ${PLUGIN_DIRECTORY}/trino-example-plugin
+```
+
+## Debug
+
+To test and debug the connector locally, run the `ExampleQueryRunner` class located in tests:
+```bash
+mvn test-compile exec:java -Dexec.mainClass="com.example.ExampleQueryRunner" -Dexec.classpathScope=test
+```
+
+And then run the Trino CLI using `trino --server localhost:8080 --no-progress` and query it:
+```
+trino> show catalogs;
+ Catalog
+---------
+ example
+ system
+(2 rows)
+
+trino> show tables from example.default;
+ Table
+------------
+ single_row
+(1 row)
+
+trino> select * from example.default.single_row;
+ id | type | name
+----+---------------+---------
+ x | default-value | my-name
+(1 row)
+```
diff --git a/plugin/trino-example-plugin/pom.xml b/plugin/trino-example-plugin/pom.xml
new file mode 100644
index 000000000000..48bbe5473c41
--- /dev/null
+++ b/plugin/trino-example-plugin/pom.xml
@@ -0,0 +1,199 @@
+
+
+ 4.0.0
+
+
+ io.airlift
+ airbase
+ 150
+
+
+ io.trino
+ trino-example-plugin
+ 0.1-SNAPSHOT
+ trino-plugin
+
+
+
+ Apache License 2.0
+ http://www.apache.org/licenses/LICENSE-2.0
+ repo
+
+
+
+
+ 21
+
+ \${project.basedir}
+
+ true
+
+ true
+ false
+
+ true
+ false
+
+ 439
+ 240
+ 2.24.1
+ 2.12.7
+
+ -missing
+
+
+
+
+
+ com.google.guava
+ guava
+
+
+
+ com.google.inject
+ guice
+
+
+
+ io.airlift
+ bootstrap
+ ${dep.airlift.version}
+
+
+
+ io.airlift
+ configuration
+ ${dep.airlift.version}
+
+
+
+ javax.validation
+ validation-api
+
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+ provided
+
+
+
+ io.airlift
+ slice
+ provided
+
+
+
+ io.opentelemetry
+ opentelemetry-api
+ provided
+
+
+
+ io.opentelemetry
+ opentelemetry-context
+ provided
+
+
+
+ io.trino
+ trino-spi
+ ${dep.trino.version}
+ provided
+
+
+
+ com.google.errorprone
+ error_prone_annotations
+ ${dep.errorprone.version}
+ runtime
+
+
+
+ io.airlift
+ log
+ ${dep.airlift.version}
+ runtime
+
+
+
+ io.airlift
+ log-manager
+ ${dep.airlift.version}
+ runtime
+
+
+
+ io.airlift
+ units
+ 1.10
+ runtime
+
+
+
+ io.airlift
+ testing
+ ${dep.airlift.version}
+ test
+
+
+
+ io.trino
+ trino-main
+ ${dep.trino.version}
+ test
+
+
+
+ io.trino
+ trino-memory
+ ${dep.trino.version}
+ test
+
+
+
+ io.trino
+ trino-testing
+ ${dep.trino.version}
+ test
+
+
+
+ io.trino
+ trino-tpch
+ ${dep.trino.version}
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+
+
+
+
+
+ io.trino
+ trino-maven-plugin
+ 13
+ true
+
+
+
+ ca.vanzyl.provisio.maven.plugins
+ provisio-maven-plugin
+ 1.0.21
+ true
+
+
+
+
diff --git a/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleColumnHandle.java b/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleColumnHandle.java
new file mode 100644
index 000000000000..3bed79573f40
--- /dev/null
+++ b/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleColumnHandle.java
@@ -0,0 +1,67 @@
+/*
+ * 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.plugin.example;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.trino.spi.connector.ColumnHandle;
+import io.trino.spi.type.Type;
+
+import java.util.Objects;
+
+public class ExampleColumnHandle
+ implements ColumnHandle
+{
+ private final String name;
+ private final Type type;
+
+ @JsonCreator
+ public ExampleColumnHandle(String name, Type type)
+ {
+ this.name = name;
+ this.type = type;
+ }
+
+ @JsonProperty
+ public String getName()
+ {
+ return name;
+ }
+
+ @JsonProperty
+ public Type getType()
+ {
+ return type;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ExampleColumnHandle that = (ExampleColumnHandle) o;
+ return Objects.equals(name, that.name);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return Objects.hash(name);
+ }
+}
diff --git a/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleConfig.java b/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleConfig.java
new file mode 100644
index 000000000000..eb0599972a9e
--- /dev/null
+++ b/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleConfig.java
@@ -0,0 +1,39 @@
+/*
+ * 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.plugin.example;
+
+import io.airlift.configuration.Config;
+import io.airlift.configuration.ConfigDescription;
+
+import javax.validation.constraints.NotNull;
+
+public class ExampleConfig
+{
+ private String defaultType = "default-value";
+
+ @NotNull
+ public String getDefaultType()
+ {
+ return defaultType;
+ }
+
+ @Config("default-type")
+ @ConfigDescription("Default value for the type column")
+ public ExampleConfig setDefaultType(String value)
+ {
+ this.defaultType = value;
+ return this;
+ }
+}
diff --git a/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleConnector.java b/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleConnector.java
new file mode 100644
index 000000000000..7b7dcc8b6f6b
--- /dev/null
+++ b/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleConnector.java
@@ -0,0 +1,70 @@
+/*
+ * 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.plugin.example;
+
+import com.google.inject.Inject;
+import io.trino.spi.connector.Connector;
+import io.trino.spi.connector.ConnectorMetadata;
+import io.trino.spi.connector.ConnectorRecordSetProvider;
+import io.trino.spi.connector.ConnectorSession;
+import io.trino.spi.connector.ConnectorSplitManager;
+import io.trino.spi.connector.ConnectorTransactionHandle;
+import io.trino.spi.transaction.IsolationLevel;
+
+import static io.trino.plugin.example.ExampleTransactionHandle.INSTANCE;
+import static java.util.Objects.requireNonNull;
+
+public class ExampleConnector
+ implements Connector
+{
+ private final ExampleMetadata metadata;
+ private final ExampleSplitManager splitManager;
+ private final ExampleRecordSetProvider recordSetProvider;
+
+ @Inject
+ public ExampleConnector(
+ ExampleMetadata metadata,
+ ExampleSplitManager splitManager,
+ ExampleRecordSetProvider recordSetProvider)
+ {
+ this.metadata = requireNonNull(metadata, "metadata is null");
+ this.splitManager = requireNonNull(splitManager, "splitManager is null");
+ this.recordSetProvider = requireNonNull(recordSetProvider, "recordSetProvider is null");
+ }
+
+ @Override
+ public ConnectorTransactionHandle beginTransaction(IsolationLevel isolationLevel, boolean readOnly, boolean autoCommit)
+ {
+ return INSTANCE;
+ }
+
+ @Override
+ public ConnectorMetadata getMetadata(ConnectorSession session, ConnectorTransactionHandle transaction)
+ {
+ return metadata;
+ }
+
+ @Override
+ public ConnectorSplitManager getSplitManager()
+ {
+ return splitManager;
+ }
+
+ @Override
+ public ConnectorRecordSetProvider getRecordSetProvider()
+ {
+ return recordSetProvider;
+ }
+}
diff --git a/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleConnectorFactory.java b/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleConnectorFactory.java
new file mode 100644
index 000000000000..22137f1e5d9a
--- /dev/null
+++ b/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleConnectorFactory.java
@@ -0,0 +1,56 @@
+/*
+ * 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.plugin.example;
+
+import com.google.inject.Injector;
+import io.airlift.bootstrap.Bootstrap;
+import io.trino.spi.connector.Connector;
+import io.trino.spi.connector.ConnectorContext;
+import io.trino.spi.connector.ConnectorFactory;
+
+import java.util.Map;
+
+import static java.util.Objects.requireNonNull;
+
+public class ExampleConnectorFactory
+ implements ConnectorFactory
+{
+ public static final String CONNECTOR_NAME = "example_connector";
+
+ @Override
+ public String getName()
+ {
+ return CONNECTOR_NAME;
+ }
+
+ @Override
+ public Connector create(String s, Map requiredConfig, ConnectorContext context)
+ {
+ requireNonNull(requiredConfig, "requiredConfig is null");
+
+ // A plugin is not required to use Guice; it is just very convenient
+ Bootstrap app = new Bootstrap(
+ new ExampleModule(
+ context.getNodeManager(),
+ context.getTypeManager()));
+
+ Injector injector = app
+ .doNotInitializeLogging()
+ .setRequiredConfigurationProperties(requiredConfig)
+ .initialize();
+
+ return injector.getInstance(ExampleConnector.class);
+ }
+}
diff --git a/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleMetadata.java b/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleMetadata.java
new file mode 100644
index 000000000000..fe4280884e58
--- /dev/null
+++ b/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleMetadata.java
@@ -0,0 +1,123 @@
+/*
+ * 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.plugin.example;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import io.trino.spi.connector.ColumnHandle;
+import io.trino.spi.connector.ColumnMetadata;
+import io.trino.spi.connector.ConnectorMetadata;
+import io.trino.spi.connector.ConnectorSession;
+import io.trino.spi.connector.ConnectorTableHandle;
+import io.trino.spi.connector.ConnectorTableMetadata;
+import io.trino.spi.connector.ConnectorTableProperties;
+import io.trino.spi.connector.SchemaTableName;
+import io.trino.spi.connector.SchemaTablePrefix;
+import io.trino.spi.connector.TableColumnsMetadata;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static io.trino.spi.type.VarcharType.VARCHAR;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toMap;
+
+public class ExampleMetadata
+ implements ConnectorMetadata
+{
+ public static final String SCHEMA_NAME = "default";
+
+ // TODO replace with the actual tables provided by this connector
+ public static final Map> columns = new ImmutableMap.Builder>()
+ .put("single_row", ImmutableList.of(
+ new ColumnMetadata("id", VARCHAR),
+ new ColumnMetadata("type", VARCHAR),
+ new ColumnMetadata("name", VARCHAR)))
+ .build();
+
+ @Override
+ public List listSchemaNames(ConnectorSession connectorSession)
+ {
+ return List.of(SCHEMA_NAME);
+ }
+
+ @Override
+ public ConnectorTableHandle getTableHandle(ConnectorSession connectorSession, SchemaTableName schemaTableName)
+ {
+ if (!schemaTableName.getSchemaName().equals(SCHEMA_NAME)) {
+ return null;
+ }
+ return new ExampleTableHandle(schemaTableName);
+ }
+
+ @Override
+ public ConnectorTableMetadata getTableMetadata(
+ ConnectorSession connectorSession,
+ ConnectorTableHandle connectorTableHandle)
+ {
+ ExampleTableHandle tableHandle = (ExampleTableHandle) connectorTableHandle;
+ SchemaTableName schemaTableName = tableHandle.getSchemaTableName();
+ return new ConnectorTableMetadata(
+ schemaTableName,
+ columns.get(schemaTableName.getTableName()));
+ }
+
+ @Override
+ public List listTables(ConnectorSession session, Optional schemaName)
+ {
+ return columns
+ .keySet()
+ .stream()
+ .map(table -> new SchemaTableName(SCHEMA_NAME, table))
+ .collect(toList());
+ }
+
+ @Override
+ public Map getColumnHandles(
+ ConnectorSession connectorSession,
+ ConnectorTableHandle connectorTableHandle)
+ {
+ return getTableMetadata(connectorSession, connectorTableHandle).getColumns().stream()
+ .collect(toMap(ColumnMetadata::getName, column -> new ExampleColumnHandle(column.getName(), column.getType())));
+ }
+
+ @Override
+ public ColumnMetadata getColumnMetadata(
+ ConnectorSession connectorSession,
+ ConnectorTableHandle connectorTableHandle,
+ ColumnHandle columnHandle)
+ {
+ ExampleColumnHandle handle = (ExampleColumnHandle) columnHandle;
+ return new ColumnMetadata(handle.getName(), handle.getType());
+ }
+
+ @Override
+ public ConnectorTableProperties getTableProperties(ConnectorSession session, ConnectorTableHandle table)
+ {
+ return new ConnectorTableProperties();
+ }
+
+ @Override
+ public Iterator streamTableColumns(ConnectorSession session, SchemaTablePrefix prefix)
+ {
+ return columns.entrySet().stream()
+ .map(entry -> TableColumnsMetadata.forTable(
+ new SchemaTableName(prefix.getSchema().orElse(""), entry.getKey()),
+ entry.getValue()))
+ .iterator();
+ }
+}
diff --git a/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleModule.java b/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleModule.java
new file mode 100644
index 000000000000..92a8f9564889
--- /dev/null
+++ b/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleModule.java
@@ -0,0 +1,50 @@
+/*
+ * 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.plugin.example;
+
+import com.google.inject.Binder;
+import com.google.inject.Module;
+import com.google.inject.Scopes;
+import io.trino.spi.NodeManager;
+import io.trino.spi.type.TypeManager;
+
+import static io.airlift.configuration.ConfigBinder.configBinder;
+import static java.util.Objects.requireNonNull;
+
+public class ExampleModule
+ implements Module
+{
+ private final NodeManager nodeManager;
+ private final TypeManager typeManager;
+
+ public ExampleModule(NodeManager nodeManager, TypeManager typeManager)
+ {
+ this.nodeManager = requireNonNull(nodeManager, "nodeManager is null");
+ this.typeManager = requireNonNull(typeManager, "typeManager is null");
+ }
+
+ @Override
+ public void configure(Binder binder)
+ {
+ binder.bind(NodeManager.class).toInstance(nodeManager);
+ binder.bind(TypeManager.class).toInstance(typeManager);
+
+ binder.bind(ExampleConnector.class).in(Scopes.SINGLETON);
+ binder.bind(ExampleMetadata.class).in(Scopes.SINGLETON);
+ binder.bind(ExampleSplitManager.class).in(Scopes.SINGLETON);
+ binder.bind(ExampleRecordSetProvider.class).in(Scopes.SINGLETON);
+ configBinder(binder).bindConfig(ExampleConfig.class);
+ }
+}
diff --git a/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExamplePlugin.java b/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExamplePlugin.java
new file mode 100644
index 000000000000..569c7a929e44
--- /dev/null
+++ b/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExamplePlugin.java
@@ -0,0 +1,29 @@
+/*
+ * 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.plugin.example;
+
+import com.google.common.collect.ImmutableList;
+import io.trino.spi.Plugin;
+import io.trino.spi.connector.ConnectorFactory;
+
+public class ExamplePlugin
+ implements Plugin
+{
+ @Override
+ public Iterable getConnectorFactories()
+ {
+ return ImmutableList.of(new ExampleConnectorFactory());
+ }
+}
diff --git a/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleRecordSetProvider.java b/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleRecordSetProvider.java
new file mode 100644
index 000000000000..5932ce55ffcd
--- /dev/null
+++ b/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleRecordSetProvider.java
@@ -0,0 +1,92 @@
+/*
+ * 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.plugin.example;
+
+import com.google.inject.Inject;
+import io.trino.spi.connector.ColumnHandle;
+import io.trino.spi.connector.ColumnMetadata;
+import io.trino.spi.connector.ConnectorRecordSetProvider;
+import io.trino.spi.connector.ConnectorSession;
+import io.trino.spi.connector.ConnectorSplit;
+import io.trino.spi.connector.ConnectorTableHandle;
+import io.trino.spi.connector.ConnectorTableMetadata;
+import io.trino.spi.connector.ConnectorTransactionHandle;
+import io.trino.spi.connector.InMemoryRecordSet;
+import io.trino.spi.connector.RecordSet;
+import io.trino.spi.type.Type;
+
+import java.util.List;
+import java.util.stream.StreamSupport;
+
+import static java.util.stream.Collectors.toList;
+
+public class ExampleRecordSetProvider
+ implements ConnectorRecordSetProvider
+{
+ private final String defaultType;
+ private final ExampleMetadata metadata;
+
+ @Inject
+ public ExampleRecordSetProvider(ExampleConfig config, ExampleMetadata metadata)
+ {
+ this.defaultType = config.getDefaultType();
+ this.metadata = metadata;
+ }
+
+ @Override
+ public RecordSet getRecordSet(
+ ConnectorTransactionHandle connectorTransactionHandle,
+ ConnectorSession connectorSession,
+ ConnectorSplit connectorSplit,
+ ConnectorTableHandle table,
+ List extends ColumnHandle> list)
+ {
+ List columnHandles = list.stream()
+ .map(c -> (ExampleColumnHandle) c)
+ .collect(toList());
+ ConnectorTableMetadata tableMetadata = metadata.getTableMetadata(connectorSession, table);
+
+ List columnIndexes = columnHandles.stream()
+ .map(column -> {
+ int index = 0;
+ for (ColumnMetadata columnMetadata : tableMetadata.getColumns()) {
+ if (columnMetadata.getName().equalsIgnoreCase(column.getName())) {
+ return index;
+ }
+ index++;
+ }
+ throw new IllegalStateException("Unknown column: " + column.getName());
+ })
+ .collect(toList());
+
+ Iterable> rows = getRows();
+ Iterable> mappedRows = StreamSupport.stream(rows.spliterator(), false)
+ .map(row -> columnIndexes.stream()
+ .map(row::get)
+ .collect(toList())).collect(toList());
+
+ List mappedTypes = columnHandles.stream()
+ .map(ExampleColumnHandle::getType)
+ .collect(toList());
+ return new InMemoryRecordSet(mappedTypes, mappedRows);
+ }
+
+ private Iterable> getRows()
+ {
+ // TODO replace the list with an iterable that provides the data read from the data source for this connector
+ return List.of(
+ List.of("x", defaultType, "my-name"));
+ }
+}
diff --git a/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleSplit.java b/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleSplit.java
new file mode 100644
index 000000000000..8d65a09f7a02
--- /dev/null
+++ b/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleSplit.java
@@ -0,0 +1,63 @@
+/*
+ * 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.plugin.example;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.trino.spi.HostAddress;
+import io.trino.spi.connector.ConnectorSplit;
+
+import java.util.List;
+
+public class ExampleSplit
+ implements ConnectorSplit
+{
+ private final ExampleTableHandle tableHandle;
+ private final List addresses;
+
+ @JsonCreator
+ public ExampleSplit(
+ @JsonProperty("tableHandle") ExampleTableHandle tableHandle,
+ @JsonProperty("addresses") List addresses)
+ {
+ this.tableHandle = tableHandle;
+ this.addresses = addresses;
+ }
+
+ @Override
+ public boolean isRemotelyAccessible()
+ {
+ return true;
+ }
+
+ @Override
+ @JsonProperty("addresses")
+ public List getAddresses()
+ {
+ return List.of();
+ }
+
+ @Override
+ public Object getInfo()
+ {
+ return "Example split";
+ }
+
+ @JsonProperty("tableHandle")
+ public ExampleTableHandle getTableHandle()
+ {
+ return tableHandle;
+ }
+}
diff --git a/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleSplitManager.java b/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleSplitManager.java
new file mode 100644
index 000000000000..d392e4cc2c0f
--- /dev/null
+++ b/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleSplitManager.java
@@ -0,0 +1,61 @@
+/*
+ * 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.plugin.example;
+
+import com.google.common.collect.ImmutableList;
+import com.google.inject.Inject;
+import io.trino.spi.HostAddress;
+import io.trino.spi.Node;
+import io.trino.spi.NodeManager;
+import io.trino.spi.connector.ConnectorSession;
+import io.trino.spi.connector.ConnectorSplitManager;
+import io.trino.spi.connector.ConnectorSplitSource;
+import io.trino.spi.connector.ConnectorTableHandle;
+import io.trino.spi.connector.ConnectorTransactionHandle;
+import io.trino.spi.connector.Constraint;
+import io.trino.spi.connector.DynamicFilter;
+import io.trino.spi.connector.FixedSplitSource;
+
+import java.util.List;
+
+import static java.util.stream.Collectors.toList;
+
+public class ExampleSplitManager
+ implements ConnectorSplitManager
+{
+ private final NodeManager nodeManager;
+
+ @Inject
+ public ExampleSplitManager(NodeManager nodeManager)
+ {
+ this.nodeManager = nodeManager;
+ }
+
+ @Override
+ public ConnectorSplitSource getSplits(
+ ConnectorTransactionHandle transaction,
+ ConnectorSession session,
+ ConnectorTableHandle table,
+ DynamicFilter dynamicFilter,
+ Constraint constraint)
+ {
+ List addresses = nodeManager.getRequiredWorkerNodes().stream()
+ .map(Node::getHostAndPort)
+ .collect(toList());
+
+ return new FixedSplitSource(ImmutableList.of(
+ new ExampleSplit((ExampleTableHandle) table, addresses)));
+ }
+}
diff --git a/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleTableHandle.java b/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleTableHandle.java
new file mode 100644
index 000000000000..4f4b9f252126
--- /dev/null
+++ b/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleTableHandle.java
@@ -0,0 +1,44 @@
+/*
+ * 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.plugin.example;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.trino.spi.connector.ConnectorTableHandle;
+import io.trino.spi.connector.SchemaTableName;
+
+public class ExampleTableHandle
+ implements ConnectorTableHandle
+{
+ private final SchemaTableName schemaTableName;
+
+ @JsonCreator
+ public ExampleTableHandle(SchemaTableName schemaTableName)
+ {
+ this.schemaTableName = schemaTableName;
+ }
+
+ @JsonProperty
+ public SchemaTableName getSchemaTableName()
+ {
+ return schemaTableName;
+ }
+
+ @Override
+ public String toString()
+ {
+ return schemaTableName.getTableName();
+ }
+}
diff --git a/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleTransactionHandle.java b/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleTransactionHandle.java
new file mode 100644
index 000000000000..2909a7ace4b5
--- /dev/null
+++ b/plugin/trino-example-plugin/src/main/java/io/trino/plugin/example/ExampleTransactionHandle.java
@@ -0,0 +1,23 @@
+/*
+ * 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.plugin.example;
+
+import io.trino.spi.connector.ConnectorTransactionHandle;
+
+public enum ExampleTransactionHandle
+ implements ConnectorTransactionHandle
+{
+ INSTANCE
+}
diff --git a/plugin/trino-example-plugin/src/test/java/io/trino/plugin/example/ExampleQueryRunner.java b/plugin/trino-example-plugin/src/test/java/io/trino/plugin/example/ExampleQueryRunner.java
new file mode 100644
index 000000000000..8cf75c235036
--- /dev/null
+++ b/plugin/trino-example-plugin/src/test/java/io/trino/plugin/example/ExampleQueryRunner.java
@@ -0,0 +1,71 @@
+/*
+ * 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.plugin.example;
+
+import com.google.common.collect.ImmutableMap;
+import io.airlift.log.Level;
+import io.airlift.log.Logger;
+import io.airlift.log.Logging;
+import io.trino.Session;
+import io.trino.testing.DistributedQueryRunner;
+import io.trino.testing.QueryRunner;
+
+import java.util.Map;
+
+import static io.trino.testing.TestingSession.testSessionBuilder;
+
+public class ExampleQueryRunner
+{
+ private ExampleQueryRunner() {}
+
+ public static QueryRunner createQueryRunner()
+ throws Exception
+ {
+ Session defaultSession = testSessionBuilder()
+ .setCatalog("example")
+ .setSchema("default")
+ .build();
+
+ Map extraProperties = ImmutableMap.builder()
+ .put("http-server.http.port", "8080")
+ .build();
+ QueryRunner queryRunner = DistributedQueryRunner.builder(defaultSession)
+ .setExtraProperties(extraProperties)
+ .setNodeCount(1)
+ .build();
+ queryRunner.installPlugin(new ExamplePlugin());
+
+ queryRunner.createCatalog(
+ "example",
+ "example_connector",
+ Map.of());
+
+ return queryRunner;
+ }
+
+ public static void main(String[] args)
+ throws Exception
+ {
+ Logging logger = Logging.initialize();
+ logger.setLevel("io.trino.plugin.example", Level.DEBUG);
+ logger.setLevel("io.trino", Level.INFO);
+
+ QueryRunner queryRunner = createQueryRunner();
+
+ Logger log = Logger.get(ExampleQueryRunner.class);
+ log.info("======== SERVER STARTED ========");
+ log.info("\n====\n%s\n====", ((DistributedQueryRunner) queryRunner).getCoordinator().getBaseUrl());
+ }
+}
diff --git a/plugin/trino-example-plugin/src/test/java/io/trino/plugin/example/TestExampleQueries.java b/plugin/trino-example-plugin/src/test/java/io/trino/plugin/example/TestExampleQueries.java
new file mode 100644
index 000000000000..a48cd4114df2
--- /dev/null
+++ b/plugin/trino-example-plugin/src/test/java/io/trino/plugin/example/TestExampleQueries.java
@@ -0,0 +1,44 @@
+/*
+ * 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.plugin.example;
+
+import io.trino.testing.AbstractTestQueryFramework;
+import io.trino.testing.QueryRunner;
+import org.junit.jupiter.api.Test;
+
+public class TestExampleQueries
+ extends AbstractTestQueryFramework
+{
+ @Override
+ protected QueryRunner createQueryRunner()
+ throws Exception
+ {
+ return ExampleQueryRunner.createQueryRunner();
+ }
+
+ @Test
+ public void showTables()
+ {
+ assertQuery("SHOW SCHEMAS FROM example", "VALUES 'default', 'information_schema'");
+ assertQuery("SHOW TABLES FROM example.default", "VALUES 'single_row'");
+ }
+
+ @Test
+ public void selectFromTable()
+ {
+ assertQuery("SELECT name FROM single_row WHERE id = 'x'",
+ "VALUES ('my-name')");
+ }
+}
diff --git a/plugin/trino-plugin-archetype-builder/.gitignore b/plugin/trino-plugin-archetype-builder/.gitignore
new file mode 100644
index 000000000000..0e13eebbeaad
--- /dev/null
+++ b/plugin/trino-plugin-archetype-builder/.gitignore
@@ -0,0 +1,11 @@
+target/
+pom.xml.tag
+pom.xml.releaseBackup
+pom.xml.versionsBackup
+pom.xml.next
+release.properties
+dependency-reduced-pom.xml
+buildNumber.properties
+.mvn/timing.properties
+# https://github.com/takari/maven-wrapper#usage-without-binary-jar
+.mvn/wrapper/maven-wrapper.jar
diff --git a/plugin/trino-plugin-archetype-builder/archetype.properties b/plugin/trino-plugin-archetype-builder/archetype.properties
new file mode 100644
index 000000000000..828b7852ad71
--- /dev/null
+++ b/plugin/trino-plugin-archetype-builder/archetype.properties
@@ -0,0 +1,3 @@
+excludePatterns=*.iml,interpolated-pom.xml,build.log
+connectorName=example_connector
+classPrefix=Example
diff --git a/plugin/trino-plugin-archetype-builder/pom.xml b/plugin/trino-plugin-archetype-builder/pom.xml
new file mode 100644
index 000000000000..79843d06d683
--- /dev/null
+++ b/plugin/trino-plugin-archetype-builder/pom.xml
@@ -0,0 +1,162 @@
+
+
+ 4.0.0
+
+
+ io.trino
+ trino-root
+ 440-SNAPSHOT
+ ../../pom.xml
+
+
+ trino-plugin-archetype-builder
+ pom
+ Trino - Maven Archetype Builder for Trino Plugins
+
+
+ ${project.parent.basedir}
+ UTF-8
+
+
+
+
+ io.trino
+ trino-example-plugin
+ 0.1-SNAPSHOT
+
+
+
+
+
+
+ maven-resources-plugin
+
+
+ copy-sources
+
+ copy-resources
+
+ generate-sources
+
+ ${project.build.directory}/trino-example-plugin
+
+
+ ${project.basedir}/../trino-example-plugin
+
+ **/*.iml
+ target
+
+ false
+
+
+
+
+
+ overwrite-generated-meta
+
+
+ copy-resources
+
+ process-sources
+
+ ${project.build.directory}/trino-example-plugin/target/generated-sources/archetype/src/main/resources/META-INF/maven
+ true
+
+
+ ${project.basedir}/src/main/resources/META-INF/maven
+ false
+
+
+
+
+
+ overwrite-generated-pom
+
+
+ copy-resources
+
+ process-sources
+
+ ${project.build.directory}/trino-example-plugin/target/generated-sources/archetype
+ true
+
+
+ ${project.basedir}/src/main/resources
+ true
+
+ pom.xml
+
+
+
+
+
+
+
+
+ maven-invoker-plugin
+ 3.3.0
+
+
+ pom.xml
+
+ ${project.build.directory}/trino-example-plugin/target/generated-sources/archetype
+ false
+ true
+
+
+
+ generate-archetype
+
+ run
+
+ generate-sources
+
+ ${project.build.directory}/trino-example-plugin
+
+
+ org.apache.maven.plugins:maven-archetype-plugin:3.2.1:create-from-project -Darchetype.properties=${project.basedir}/archetype.properties -Darchetype.postPhase=validate
+
+
+
+
+ package-archetype
+
+ run
+
+ package
+
+
+
+ package -Dgib.disable
+
+
+
+
+ install-archetype
+
+ run
+
+ install
+
+
+ install -Dgib.disable
+
+
+
+
+ deploy-archetype
+
+ run
+
+ deploy
+
+
+ deploy -Dgib.disable
+
+
+
+
+
+
+
+
diff --git a/plugin/trino-plugin-archetype-builder/src/main/resources/META-INF/maven/archetype-metadata.xml b/plugin/trino-plugin-archetype-builder/src/main/resources/META-INF/maven/archetype-metadata.xml
new file mode 100644
index 000000000000..b0ce8d909351
--- /dev/null
+++ b/plugin/trino-plugin-archetype-builder/src/main/resources/META-INF/maven/archetype-metadata.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+ .gitignore
+
+
+
+
+
+
+ README.md
+ LICENSE
+
+
+
+ src/main/java
+
+
+ src/test/java
+
+
+
diff --git a/plugin/trino-plugin-archetype-builder/src/main/resources/pom.xml b/plugin/trino-plugin-archetype-builder/src/main/resources/pom.xml
new file mode 100644
index 000000000000..97608c76895d
--- /dev/null
+++ b/plugin/trino-plugin-archetype-builder/src/main/resources/pom.xml
@@ -0,0 +1,46 @@
+
+
+ 4.0.0
+
+
+ io.trino
+ trino-root
+ ${project.version}
+
+ ../../../../../../../pom.xml
+
+
+ trino-plugin-archetype
+ maven-archetype
+ Trino - Maven Archetype for Trino Plugins
+
+
+ ${project.parent.basedir}
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-archetype-plugin
+ 3.2.1
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-archetype-plugin
+ 3.2.1
+
+
+
+
+ org.apache.maven.archetype
+ archetype-packaging
+ 3.2.1
+
+
+
+
diff --git a/plugin/trino-plugin-archetype-builder/src/test/resources/projects/basic/archetype.properties b/plugin/trino-plugin-archetype-builder/src/test/resources/projects/basic/archetype.properties
new file mode 100644
index 000000000000..a6368b9c4673
--- /dev/null
+++ b/plugin/trino-plugin-archetype-builder/src/test/resources/projects/basic/archetype.properties
@@ -0,0 +1,7 @@
+package=it.pkg
+groupId=com.it
+artifactId=basic
+version=0.1-SNAPSHOT
+keepParent=false
+connectorName=example_connector
+classPrefix=Example
diff --git a/plugin/trino-plugin-archetype-builder/src/test/resources/projects/basic/goal.txt b/plugin/trino-plugin-archetype-builder/src/test/resources/projects/basic/goal.txt
new file mode 100644
index 000000000000..0b5987362fe3
--- /dev/null
+++ b/plugin/trino-plugin-archetype-builder/src/test/resources/projects/basic/goal.txt
@@ -0,0 +1 @@
+verify
diff --git a/pom.xml b/pom.xml
index f8a83363d4c9..3ede0b5e467e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -71,6 +71,7 @@
plugin/trino-elasticsearch
plugin/trino-example-http
plugin/trino-example-jdbc
+ plugin/trino-example-plugin
plugin/trino-exchange-filesystem
plugin/trino-exchange-hdfs
plugin/trino-geospatial
@@ -97,6 +98,7 @@
plugin/trino-password-authenticators
plugin/trino-phoenix5
plugin/trino-pinot
+ plugin/trino-plugin-archetype-builder
plugin/trino-postgresql
plugin/trino-prometheus
plugin/trino-raptor-legacy