├── .dockerignore ├── .github ├── dependabot.yml └── workflows │ └── github_actions.yml ├── .gitignore ├── .mvn ├── modernizer │ └── violations.xml └── wrapper │ ├── MavenWrapperDownloader.java │ └── maven-wrapper.properties ├── Dockerfile ├── LICENSE ├── README.md ├── docker ├── Dockerfile-dev ├── entrypoint.sh └── etc │ ├── catalog │ └── tiledb.properties │ ├── config.properties │ ├── jvm.config │ ├── log.properties │ └── node.properties ├── docs ├── Configuration.md ├── Examples.md ├── Limitations.md └── SQL.md ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main ├── java │ └── io │ │ └── trino │ │ └── plugin │ │ └── tiledb │ │ ├── TileDBClient.java │ │ ├── TileDBColumn.java │ │ ├── TileDBColumnHandle.java │ │ ├── TileDBColumnProperties.java │ │ ├── TileDBConfig.java │ │ ├── TileDBConnector.java │ │ ├── TileDBConnectorFactory.java │ │ ├── TileDBConnectorId.java │ │ ├── TileDBErrorCode.java │ │ ├── TileDBMetadata.java │ │ ├── TileDBModule.java │ │ ├── TileDBOutputTableHandle.java │ │ ├── TileDBPageSink.java │ │ ├── TileDBPageSinkProvider.java │ │ ├── TileDBPlugin.java │ │ ├── TileDBRecordCursor.java │ │ ├── TileDBRecordSet.java │ │ ├── TileDBRecordSetProvider.java │ │ ├── TileDBSessionProperties.java │ │ ├── TileDBSplit.java │ │ ├── TileDBSplitManager.java │ │ ├── TileDBTable.java │ │ ├── TileDBTableHandle.java │ │ ├── TileDBTableProperties.java │ │ ├── TileDBTransactionHandle.java │ │ └── util │ │ ├── StringPartitioner.java │ │ └── Util.java └── resources │ └── services │ └── io.trino.spi.Plugin ├── modernizer └── violations.xml └── test ├── java └── io │ └── trino │ └── plugin │ └── tiledb │ ├── TestTileDBConfig.java │ ├── TestTileDBIntegrationSmokeTest.java │ ├── TestTileDBPlugin.java │ ├── TestTileDBQueries.java │ └── TileDBQueryRunner.java └── resources └── tiledb_arrays ├── dense_global ├── __array_schema.tdb ├── __c2076894fd614aac824cfc6cc0e1b754_1538424663019 │ ├── __fragment_metadata.tdb │ └── a.tdb └── __lock.tdb └── sparse_global ├── __5d43f0e2b2f34675abaa642ef0f6128e_1538424648583 ├── __coords.tdb ├── __fragment_metadata.tdb └── a.tdb ├── __array_schema.tdb └── __lock.tdb /.dockerignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | - package-ecosystem: "maven" 8 | directory: "/" 9 | groups: 10 | trino: 11 | patterns: 12 | - "io.trino*" 13 | schedule: 14 | interval: "daily" 15 | allow: 16 | - dependency-name: "io.trino:trino-testing" 17 | - dependency-name: "io.trino:trino-root" 18 | - dependency-name: "io.tiledb:tiledb-java" 19 | reviewers: 20 | - "DimitrisStaratzis" 21 | -------------------------------------------------------------------------------- /.github/workflows/github_actions.yml: -------------------------------------------------------------------------------- 1 | name: TileDB-Trino 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | tags: 7 | - '*' 8 | branches: 9 | - 'master' 10 | pull_request: 11 | 12 | jobs: 13 | Test: 14 | runs-on: ${{ matrix.os }} 15 | strategy: 16 | matrix: 17 | os: [ ubuntu-latest, macos-11 ] 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | 22 | - name: Set up Java 23 | uses: actions/setup-java@v4 24 | with: 25 | distribution: 'adopt' 26 | java-version: '21' 27 | check-latest: true 28 | 29 | - run: | 30 | mvn checkstyle:checkstyle 31 | shell: bash 32 | name: checkStyle 33 | 34 | - run: | 35 | mvn clean package -e -DskipTests 36 | shell: bash 37 | name: checkFormat 38 | 39 | - run: | 40 | mvn test 41 | shell: bash 42 | name: test 43 | 44 | Release: 45 | if: startsWith(github.ref, 'refs/tags/') 46 | needs: [Test] 47 | name: Create-Release 48 | runs-on: ubuntu-latest 49 | steps: 50 | - name: Set-up Java 51 | uses: actions/setup-java@v4 52 | with: 53 | distribution: 'adopt' 54 | java-version: '21' 55 | check-latest: true 56 | 57 | - name: Checkout 58 | uses: actions/checkout@v4 59 | 60 | - name: Create-Jars 61 | run: set +e; mvn clean package -DskipTests; mkdir jars/; cp ./target/*.jar jars/ 62 | 63 | - name: Create Release 64 | id: create_release 65 | uses: softprops/action-gh-release@v1 66 | env: 67 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 68 | with: 69 | files: jars/*.jar 70 | tag_name: ${{ github.event.release.tag_name }} 71 | name: ${{ github.event.release.tag_name }} 72 | draft: false 73 | prerelease: false 74 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | .idea 25 | 26 | *.iml 27 | *.ipr 28 | *.iws 29 | target/ 30 | /var 31 | /*/var/ 32 | /presto-product-tests/**/var/ 33 | pom.xml.versionsBackup 34 | test-output/ 35 | test-reports/ 36 | /atlassian-ide-plugin.xml 37 | .idea 38 | .DS_Store 39 | .classpath 40 | .settings 41 | .project 42 | temp-testng-customsuite.xml 43 | test-output 44 | .externalToolBuilders 45 | *~ 46 | benchmark_outputs 47 | *.pyc 48 | *.class 49 | .checkstyle 50 | .mvn/timing.properties 51 | .editorconfig 52 | -------------------------------------------------------------------------------- /.mvn/modernizer/violations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | java/lang/Class.newInstance:()Ljava/lang/Object; 5 | 1.1 6 | Prefer Class.getConstructor().newInstance() 7 | 8 | 9 | 10 | java/lang/String.toLowerCase:()Ljava/lang/String; 11 | 1.1 12 | Prefer String.toLowerCase(java.util.Locale) 13 | 14 | 15 | 16 | com/google/common/primitives/Ints.checkedCast:(J)I 17 | 1.8 18 | Prefer Math.toIntExact(long) 19 | 20 | 21 | 22 | org/testng/Assert.assertEquals:(Ljava/lang/Iterable;Ljava/lang/Iterable;)V 23 | 1.8 24 | Use com.facebook.presto.testing.assertions.Assert.assertEquals due to TestNG #543 25 | 26 | 27 | 28 | org/testng/Assert.assertEquals:(Ljava/lang/Iterable;Ljava/lang/Iterable;Ljava/lang/String;)V 29 | 1.8 30 | Use com.facebook.presto.testing.assertions.Assert.assertEquals due to TestNG #543 31 | 32 | 33 | 34 | java/util/TimeZone.getTimeZone:(Ljava/lang/String;)Ljava/util/TimeZone; 35 | 1.8 36 | Avoid TimeZone.getTimeZone as it returns GMT for a zone not supported by the JVM. Use TimeZone.getTimeZone(ZoneId.of(..)) instead, or TimeZone.getTimeZone(..., false). 37 | 38 | 39 | 40 | org/joda/time/DateTimeZone.toTimeZone:()Ljava/util/TimeZone; 41 | 1.8 42 | Avoid DateTimeZone.toTimeZone as it returns GMT for a zone not supported by the JVM. Use TimeZone.getTimeZone(ZoneId.of(dtz.getId())) instead. 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | 20 | import java.net.*; 21 | import java.io.*; 22 | import java.nio.channels.*; 23 | import java.util.Properties; 24 | 25 | public class MavenWrapperDownloader { 26 | 27 | /** 28 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 29 | */ 30 | private static final String DEFAULT_DOWNLOAD_URL = 31 | "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"; 32 | 33 | /** 34 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 35 | * use instead of the default one. 36 | */ 37 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 38 | ".mvn/wrapper/maven-wrapper.properties"; 39 | 40 | /** 41 | * Path where the maven-wrapper.jar will be saved to. 42 | */ 43 | private static final String MAVEN_WRAPPER_JAR_PATH = 44 | ".mvn/wrapper/maven-wrapper.jar"; 45 | 46 | /** 47 | * Name of the property which should be used to override the default download url for the wrapper. 48 | */ 49 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 50 | 51 | public static void main(String args[]) { 52 | System.out.println("- Downloader started"); 53 | File baseDirectory = new File(args[0]); 54 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 55 | 56 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 57 | // wrapperUrl parameter. 58 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 59 | String url = DEFAULT_DOWNLOAD_URL; 60 | if(mavenWrapperPropertyFile.exists()) { 61 | FileInputStream mavenWrapperPropertyFileInputStream = null; 62 | try { 63 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 64 | Properties mavenWrapperProperties = new Properties(); 65 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 66 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 67 | } catch (IOException e) { 68 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 69 | } finally { 70 | try { 71 | if(mavenWrapperPropertyFileInputStream != null) { 72 | mavenWrapperPropertyFileInputStream.close(); 73 | } 74 | } catch (IOException e) { 75 | // Ignore ... 76 | } 77 | } 78 | } 79 | System.out.println("- Downloading from: : " + url); 80 | 81 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 82 | if(!outputFile.getParentFile().exists()) { 83 | if(!outputFile.getParentFile().mkdirs()) { 84 | System.out.println( 85 | "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 86 | } 87 | } 88 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 89 | try { 90 | downloadFileFromURL(url, outputFile); 91 | System.out.println("Done"); 92 | System.exit(0); 93 | } catch (Throwable e) { 94 | System.out.println("- Error downloading"); 95 | e.printStackTrace(); 96 | System.exit(1); 97 | } 98 | } 99 | 100 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 101 | URL website = new URL(urlString); 102 | ReadableByteChannel rbc; 103 | rbc = Channels.newChannel(website.openStream()); 104 | FileOutputStream fos = new FileOutputStream(destination); 105 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 106 | fos.close(); 107 | rbc.close(); 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | MAINTAINER help@tiledb.io 3 | 4 | ENV TRINO_VERSION=433 5 | ENV TRINO_HOME=/opt/trino 6 | ENV TRINO_CONF_DIR=${TRINO_HOME}/etc 7 | 8 | # Install necessary packages including curl, ca-certificates, wget, Python 3, and Java 17 9 | RUN apt-get update && \ 10 | apt-get install -y --no-install-recommends curl ca-certificates wget python-is-python3 openjdk-17-jdk && \ 11 | rm -rf /var/lib/apt/lists/* 12 | 13 | 14 | # Download trino cluster 15 | RUN curl -L https://repo1.maven.org/maven2/io/trino/trino-server/${TRINO_VERSION}/trino-server-${TRINO_VERSION}.tar.gz -o /tmp/trino-server.tgz && \ 16 | tar -xzf /tmp/trino-server.tgz -C /opt && \ 17 | ln -s /opt/trino-server-${TRINO_VERSION} ${TRINO_HOME} && \ 18 | mkdir -p ${TRINO_HOME}/data && \ 19 | rm -f /tmp/trino-server.tgz 20 | 21 | # Download trino CLI 22 | ADD https://repo1.maven.org/maven2/io/trino/trino-cli/${TRINO_VERSION}/trino-cli-${TRINO_VERSION}-executable.jar ${TRINO_HOME}/bin/ 23 | 24 | RUN chmod +x ${TRINO_HOME}/bin/trino-cli-${TRINO_VERSION}-executable.jar 25 | 26 | ARG TRINO_TILEDB_VERSION=1.17.2 27 | 28 | # Download latest trino release 29 | RUN mkdir ${TRINO_HOME}/plugin/tiledb && \ 30 | cd ${TRINO_HOME}/plugin/tiledb && \ 31 | curl -s https://api.github.com/repos/TileDB-Inc/TileDB-Trino/releases/tags/${TRINO_TILEDB_VERSION} \ 32 | | grep "browser_download_url.*jar" \ 33 | | cut -d : -f 2,3 \ 34 | | tr -d \" \ 35 | | wget -i - 36 | 37 | # Add entry script to start trino server and cli 38 | ADD docker/entrypoint.sh ${TRINO_HOME}/bin/ 39 | 40 | RUN chmod +x ${TRINO_HOME}/bin/entrypoint.sh 41 | 42 | # Add example arrays 43 | ADD src/test/resources/tiledb_arrays /opt/tiledb_example_arrays 44 | 45 | WORKDIR ${TRINO_HOME} 46 | 47 | # Add configuration parameters 48 | COPY docker/etc ${TRINO_HOME}/etc 49 | 50 | # Expose port for trino ui 51 | EXPOSE 8080 52 | 53 | ENV PATH=${PATH}:"${TRINO_HOME}/bin" 54 | 55 | # Volumes for config and data (used for stats) 56 | VOLUME ["${TRINO_HOME}/etc", "${TRINO_HOME}/data"] 57 | 58 | # Set default command to entry point script 59 | CMD ["./bin/entrypoint.sh"] 60 | 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 TileDB, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

This repository is currently archived, please get in touch via hello@tiledb.com or the TileDB Forum if you have a use-case to discuss.

2 | 3 | # TileDB Trino Connector 4 | 5 | ![TileDB-Trino CI](https://github.com/TileDB-Inc/TileDB-Trino/actions/workflows/github_actions.yml/badge.svg) 6 | 7 | TileDB is an efficient library for managing large-scale, 8 | multi-dimensional dense and sparse array data introducing a novel array format. For more information about TileDB 9 | see the [official TileDB documentation](https://docs.tiledb.io/en/latest/introduction.html) 10 | 11 | This connector allows running SQL on TileDB arrays via Trino. The TileDB-Trino interface supports column subselection on attributes and predicate pushdown on dimension fields, leading to superb performance for 12 | projection and range queries. 13 | 14 | ## Docker 15 | 16 | A quickstart Docker image is available. The docker image will start a single-node 17 | Trino cluster and open the CLI Trino interface where SQL can be run. 18 | The Docker image includes two example tiledb arrays 19 | `/opt/tiledb_example_arrays/dense_global` and `/opt/tiledb_example_arrays/sparse_global`. 20 | Simply build and run: 21 | 22 | ``` 23 | docker build -t tiledb-trino . 24 | 25 | docker run -it --rm tiledb-trino 26 | 27 | ``` 28 | 29 | or mount a local array into the Docker container with the `-v` option: 30 | 31 | ``` 32 | docker run -it --rm -v /local/array/path:/data/local_array tiledb-trino 33 | ``` 34 | 35 | In the above example, replace `/local/array/path` with the path to the 36 | array folder on your local machine. The `/data/local_array` path is the 37 | path you will use within the Docker image to access `/local/array/path` 38 | (you can replace it with another path of your choice). 39 | 40 | 41 | ## Installation 42 | 43 | Currently, this connector is built as a plugin. It must be packaged and 44 | installed on the TrinoDB instances. 45 | 46 | ### Latest Release 47 | 48 | Download the [latest release](https://github.com/TileDB-Inc/TileDB-Trino/releases/latest) 49 | and skip to the section 50 | [Installation on existing Trino instance](#Installation-on-existing-Trino-instance). 51 | 52 | ### Building Connector From Source 53 | 54 | The TileDB connector can be built using the following command from the 55 | top level directory of the Trino source. 56 | ``` 57 | ./mvnw package 58 | ``` 59 | 60 | Tests can be skipped by adding `-DskipTests` 61 | 62 | ``` 63 | ./mvnw package -DskipTests 64 | ``` 65 | 66 | ### Installation on a Trino instance 67 | 68 | First clone Trino 69 | ``` 70 | git clone https://github.com/trinodb/trino.git 71 | ``` 72 | Install Trino 73 | ``` 74 | ./mvnw clean install -DskipTests 75 | ``` 76 | 77 | Create a TileDB directory 78 | ``` 79 | mkdir trino/core/trino-server/target/trino-server-***-SNAPSHOT/plugin/tiledb 80 | ``` 81 | Build and copy the TileDB-Trino jars to the TileDB directory 82 | ``` 83 | cp TileDB-Trino/target/*.jar trino/core/trino-server/target/trino-server-***-SNAPSHOT/plugin/tiledb 84 | ``` 85 | Create two nested directories "etc/catalog" which include the tiledb.properties file and move them to: 86 | ``` 87 | trino/core/trino-server/target/trino-server-***-SNAPSHOT/ 88 | ``` 89 | Launch the Trino Server 90 | ``` 91 | trino/core/trino-server/target/trino-server-***-SNAPSHOT/bin/launcher run 92 | ``` 93 | Launch the Trino-CLI with the TileDB plugin 94 | ``` 95 | ./trino/client/trino-cli/target/trino-cli-***-SNAPSHOT-executable.jar --schema tiledb --catalog tiledb 96 | ``` 97 | 98 | ### Configuration 99 | 100 | See [docs/Configuration.md](docs/Configuration.md). 101 | 102 | ## Limitations 103 | 104 | See [docs/Limitations.md](docs/Limitations.md). 105 | 106 | ## Arrays as SQL Tables 107 | 108 | When a multi-dimensional array is queried in Trino, the dimensions are converted 109 | to table columns for the result set. TileDB array attributes are also returned as columns. 110 | 111 | ### Dense Arrays 112 | 113 | Consider the following example 2D `4x2` dense array with `dim1` and `dim2` 114 | as the dimensions and a single attribute `a`: 115 | 116 | ``` 117 | +-------+-------+ 118 | | | | 119 | | a:1 | a:2 | 120 | | | | 121 | +---------------+ 122 | | | | 123 | | a:3 | a:4 | 124 | | | | 125 | +---------------+ 126 | | | | 127 | | a:5 | a:6 | 128 | | | | 129 | +---------------+ 130 | | | | 131 | | a:7 | a:8 | 132 | | | | 133 | +-------+-------+ 134 | ```` 135 | 136 | When queried via Trino the results are mapped to the following table: 137 | 138 | ``` 139 | dim1 | dim2 | a 140 | ------+------+--- 141 | 1 | 1 | 1 142 | 1 | 2 | 2 143 | 2 | 1 | 3 144 | 2 | 2 | 4 145 | 3 | 1 | 5 146 | 3 | 2 | 6 147 | 4 | 1 | 7 148 | 4 | 2 | 8 149 | ``` 150 | 151 | ### Sparse Arrays 152 | 153 | A sparse array is materialized similarly to dense arrays. The following example 154 | depicts a 2D `4x4` sparse array with dimensions `dim1`, `dim2` and 155 | a single attribute `a`. Notice that this array has mostly empty cells. 156 | 157 | ``` 158 | +-------+-------+-------+-------+ 159 | | | | | | 160 | | a:1 | | | | 161 | | | | | | 162 | +-------------------------------+ 163 | | | | | | 164 | | | | a:3 | a:2 | 165 | | | | | | 166 | +-------------------------------+ 167 | | | | | | 168 | | | | | | 169 | | | | | | 170 | +-------------------------------+ 171 | | | | | | 172 | | | | | | 173 | | | | | | 174 | +-------+-------+-------+-------+ 175 | ``` 176 | 177 | For sparse arrays only non-empty cells are materialized and returned. 178 | The above array is modeled in Trino as a table of the form: 179 | 180 | ``` 181 | dim1 | dim2 | a 182 | ------+------+--- 183 | 1 | 1 | 1 184 | 2 | 4 | 2 185 | 2 | 3 | 3 186 | ``` 187 | -------------------------------------------------------------------------------- /docker/Dockerfile-dev: -------------------------------------------------------------------------------- 1 | FROM openjdk:8 2 | MAINTAINER help@tiledb.io 3 | 4 | ENV PRESTO_VERSION=315 5 | ENV PRESTO_HOME=/opt/presto 6 | ENV PRESTO_CONF_DIR=${PRESTO_HOME}/etc 7 | 8 | # Add less for pagenation 9 | RUN apt-get update && apt-get install -y --no-install-recommends \ 10 | less && \ 11 | rm -rf /var/lib/apt/lists/ 12 | 13 | # Download presto cluster 14 | RUN curl -L https://repo1.maven.org/maven2/io/trino/presto-server/${PRESTO_VERSION}/presto-server-${PRESTO_VERSION}.tar.gz -o /tmp/presto-server.tgz && \ 15 | tar -xzf /tmp/presto-server.tgz -C /opt && \ 16 | ln -s /opt/presto-server-${PRESTO_VERSION} ${PRESTO_HOME} && \ 17 | mkdir -p ${PRESTO_HOME}/data && \ 18 | rm -f /tmp/presto-server.tgz 19 | 20 | # Download presto CLI 21 | ADD https://repo1.maven.org/maven2/io/trino/presto-cli/${PRESTO_VERSION}/presto-cli-${PRESTO_VERSION}-executable.jar ${PRESTO_HOME}/bin/ 22 | 23 | RUN chmod +x ${PRESTO_HOME}/bin/presto-cli-${PRESTO_VERSION}-executable.jar 24 | 25 | # Copy plugin to build 26 | COPY . /tmp/presto-build 27 | 28 | WORKDIR /tmp/presto-build 29 | 30 | # Add entry script to start presto server and cli 31 | ADD docker/entrypoint.sh ${PRESTO_HOME}/bin/ 32 | 33 | RUN chmod +x ${PRESTO_HOME}/bin/entrypoint.sh 34 | 35 | # Add example arrays 36 | ADD src/test/resources/tiledb_arrays /opt/tiledb_example_arrays 37 | 38 | # Build presto and copy package to PRESTO_HOME and purge build 39 | RUN ./mvnw package -DskipTests && \ 40 | mkdir ${PRESTO_HOME}/plugin/tiledb && \ 41 | cp target/presto-tiledb-${PRESTO_VERSION}.jar ${PRESTO_HOME}/plugin/tiledb/presto-tiledb-${PRESTO_VERSION}.jar && \ 42 | ./mvnw clean && \ 43 | rm -rf ${HOME}/.m2 44 | 45 | WORKDIR ${PRESTO_HOME} 46 | 47 | # Remove plugin sources 48 | RUN rm -r /tmp/presto-build 49 | 50 | # Add configuration parameters 51 | COPY docker/etc ${PRESTO_HOME}/etc 52 | 53 | # Expose port for presto ui 54 | EXPOSE 8080 55 | 56 | ENV PATH=${PATH}:"${PRESTO_HOME}/bin" 57 | 58 | # Volumes for config and data (used for stats) 59 | VOLUME ["${PRESTO_HOME}/etc", "${PRESTO_HOME}/data"] 60 | 61 | # Set default command to entry point script 62 | CMD ["./bin/entrypoint.sh"] 63 | 64 | -------------------------------------------------------------------------------- /docker/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if ! ${TRINO_HOME}/bin/launcher status; then 4 | ${TRINO_HOME}/bin/launcher start; 5 | sleep 2; 6 | fi 7 | 8 | printf "Waiting for trino to initialize.." 9 | until ${TRINO_HOME}/bin/trino-cli-${TRINO_VERSION}-executable.jar --execute 'SELECT * FROM system.runtime.nodes' &> /dev/null ; 10 | do 11 | printf "."; 12 | sleep 1; 13 | printf "."; 14 | sleep 1; 15 | done 16 | printf "\n" 17 | 18 | ${TRINO_HOME}/bin/trino-cli-${TRINO_VERSION}-executable.jar --schema tiledb --catalog tiledb "$@" 19 | -------------------------------------------------------------------------------- /docker/etc/catalog/tiledb.properties: -------------------------------------------------------------------------------- 1 | connector.name=tiledb 2 | # 20 MB buffer 3 | read-buffer-size=20971520 4 | # 20 MB buffer 5 | write-buffer-size=20971520 -------------------------------------------------------------------------------- /docker/etc/config.properties: -------------------------------------------------------------------------------- 1 | coordinator=true 2 | node-scheduler.include-coordinator=true 3 | http-server.http.port=8080 4 | query.max-memory=5GB 5 | query.max-memory-per-node=1GB 6 | discovery-server.enabled=true 7 | discovery.uri=http://localhost:8080 8 | -------------------------------------------------------------------------------- /docker/etc/jvm.config: -------------------------------------------------------------------------------- 1 | -server 2 | -Xmx16G 3 | -XX:+UseG1GC 4 | -XX:G1HeapRegionSize=32M 5 | -XX:+UseGCOverheadLimit 6 | -XX:+ExplicitGCInvokesConcurrent 7 | -XX:+HeapDumpOnOutOfMemoryError 8 | -XX:OnOutOfMemoryError=kill -9 %p 9 | -------------------------------------------------------------------------------- /docker/etc/log.properties: -------------------------------------------------------------------------------- 1 | # 2 | # WARNING 3 | # ^^^^^^^ 4 | # This configuration file is for development only and should NOT be used be 5 | # used in production. For example configuration, see the Trino documentation. 6 | # 7 | 8 | io.trino=DEBUG 9 | com.sun.jersey.guice.spi.container.GuiceComponentProviderFactory=WARN 10 | com.ning.http.client=WARN 11 | io.trino.server.PluginManager=DEBUG 12 | -------------------------------------------------------------------------------- /docker/etc/node.properties: -------------------------------------------------------------------------------- 1 | node.environment=development 2 | node.id=presto-coordinator 3 | node.data-dir=/opt/presto/data 4 | -------------------------------------------------------------------------------- /docs/Configuration.md: -------------------------------------------------------------------------------- 1 | # Configuration 2 | 3 | A single configuration file is needed. The config file should be located in 4 | the catalog folder (`/etc/presto/conf/catalog` on EMR) and named `tiledb.properties`. 5 | 6 | Sample file contents: 7 | ``` 8 | connector.name=tiledb 9 | # Set read buffer to 10M per attribute 10 | read-buffer-size=10485760 11 | ``` 12 | 13 | ## Plugin Configuration Parameters 14 | 15 | The following parameters can be configured in the `tiledb.properties` and are 16 | plugin-wide. 17 | 18 | | Name | Default | Data Type | Purpose | 19 | | ---- | ------- | --------- | ------- | 20 | | array-uris | "" | String (csv list) | List of arrays to preload metadata on | 21 | | read-buffer-size | 10485760 | Integer | Set the max read buffer size per attribute | 22 | | write-buffer-size | 10485760 | Integer | Set the max write buffer size per attribute | 23 | | aws-access-key-id | "" | String | AWS_ACCESS_KEY_ID for s3 access | 24 | | aws-secret-access-key | "" | String | AWS_SECRET_ACCESS_KEY for s3 access | 25 | | tiledb-config | "" | String | TileDB config parameters in key1=value1,key2=value2 form | 26 | 27 | 28 | ## Session Parameters 29 | 30 | The following session parameters can be set via `set session tiledb.X`. 31 | Session parameters which have a default of "plugin config setting" use 32 | the plugin configuration default for the equivalent setting. 33 | 34 | | Name | Default | Data Type | Purpose | 35 | | ---- | ------- | --------- | ------- | 36 | | read_buffer_size | plugin config setting | Integer | Set the max read buffer size per attribute | 37 | | write_buffer_size | plugin config setting | Integer | Set the max write buffer size per attribute | 38 | | aws_access_key_id | plugin config setting | String | AWS_ACCESS_KEY_ID for s3 access | 39 | | aws_secret_access_key | plugin config setting | String | AWS_SECRET_ACCESS_KEY for s3 access | 40 | | splits | -1 | Integer | Set the number of splits to use per query, -1 means splits will be equal to number of workers | 41 | | split_only_predicates | false | Boolean | Split only based on predicates pushed down from where clause. For sparse array splitting evening across all domains can create skewed splits | 42 | | enable_stats | false | Boolean | Enable collecting and dumping of connector stats to presto log | 43 | | tiledb_config | "" | String | TileDB config parameters in key1=value1,key2=value2 form | 44 | -------------------------------------------------------------------------------- /docs/Examples.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | Below are various examples for querying data with the TileDB Trino connector. 4 | 5 | ## SQL Examples 6 | 7 | ### Selecting Data 8 | 9 | Typical select statements work as expected. This include predicate pushdown 10 | for dimension fields. 11 | 12 | Select all columns and all data from an array: 13 | ```sql 14 | SELECT * FROM tiledb.tiledb."file:///opt/tiledb_example_arrays/dense_global" 15 | ``` 16 | 17 | Select subset of columns: 18 | ```sql 19 | SELECT rows, cols FROM tiledb.tiledb."file:///opt/tiledb_example_arrays/dense_global" 20 | ``` 21 | 22 | Select with predicate pushdown: 23 | ```sql 24 | SELECT * FROM tiledb.tiledb."file:///opt/tiledb_example_arrays/dense_global" WHERE rows between 1 and 2 25 | ``` 26 | 27 | ### Showing Query Plans 28 | 29 | Get the query plan without running the query: 30 | ```sql 31 | EXPLAIN SELECT * FROM tiledb.tiledb."file:///opt/tiledb_example_arrays/dense_global" WHERE rows between 1 and 2 32 | ``` 33 | 34 | Analyze the query but running and profiling: 35 | ```sql 36 | EXPLAIN ANALYZE SELECT * FROM tiledb.tiledb."file:///opt/tiledb_example_arrays/dense_global" WHERE rows between 1 and 2 37 | ``` 38 | 39 | ## Creating a TileDB Array 40 | 41 | It is possible to create TileDB array from Trino. Not all array schema 42 | options are currently supported from Trino though (see [Limitations](Limitations.md#create-table) 43 | for more details). 44 | 45 | Minimum create table: 46 | ```sql 47 | CREATE TABLE region( 48 | regionkey bigint WITH (dimension=true), 49 | name varchar, 50 | comment varchar 51 | ) WITH (uri = 's3://bucket/region') 52 | ``` 53 | 54 | Create table with all options specified: 55 | 56 | ```sql 57 | CREATE TABLE region( 58 | regionkey bigint WITH (dimension=true, lower_bound=0L, upper_bound=3000L, extent=50L) 59 | name varchar, 60 | comment varchar 61 | ) WITH (uri = 's3://bucket/region', type = 'SPARSE', cell_order = 'COL_MAJOR', tile_order = 'ROW_MAJOR', capacity = 10L) 62 | ``` 63 | 64 | ## Inserting Data 65 | 66 | Data can be inserted into TileDB arrays through Trino. Inserts can be from 67 | another table or individual values. 68 | 69 | Copy data from one table to another: 70 | 71 | ```sql 72 | INSERT INTO tiledb.tiledb."s3://bucket/region" select * from tpch.tiny.region 73 | ``` 74 | 75 | Data can be inserted using the `VALUES` method for single row inserts. This is 76 | not recommended because each insert will create a new fragment and cause 77 | degraded read performance as the number of fragments increases. 78 | 79 | ```sql 80 | INSERT INTO tiledb.tiledb."s3://bucket/region" VALUES (1, "Test Region", "Example") 81 | ``` -------------------------------------------------------------------------------- /docs/Limitations.md: -------------------------------------------------------------------------------- 1 | # Limitations 2 | 3 | The TileDB connector supports most of Trino functionality. Below is a 4 | list of the features not currently supported. 5 | 6 | ## Encrypted Arrays 7 | 8 | The connector does not currently support creating/writing/reading 9 | encrypted arrays 10 | 11 | ## OpenAt Timestamp 12 | 13 | The connector does not currently support the TileDB `openAt` functionality to 14 | open an array at a specific timestamp. 15 | 16 | ## Datatypes 17 | 18 | TileDB Trino connector supports the following SQL datatypes: 19 | 20 | - BOOLEAN 21 | - TINYINT 22 | - INTEGER 23 | - BIGINT 24 | - REAL 25 | - DOUBLE 26 | - DECIMAL (treated as doubles) 27 | - [STRING*](#variable-length-charvarchar-fields) 28 | - [VARCHAR*](#variable-length-charvarchar-fields) 29 | - [CHAR*](#variable-length-charvarchar-fields) 30 | - VARBINARY 31 | 32 | No other datatypes are supported. 33 | 34 | ### Unsigned Integers 35 | 36 | The TileDB Trino connector does not have full support for unsigned values. 37 | Trino and all connectors are written in Java, and Java does not have unsigned 38 | values. As a result of this Java limitation, an unsigned 64-bit integer can 39 | overflow if it is larger than `2^63 - 1`. Unsigned integers that are 8, 16 or 40 | 32 bits are treated as larger integers. For instance, an unsigned 32-bit value 41 | is read into a Java type of `long`. 42 | 43 | ## Variable-length Char/Varchar fields 44 | 45 | For `varchar`, and `char` datatypes the special case of `char(1)` or `varchar(1)` 46 | is stored on disk as a fixed-sized attribute of size 1. Any `char`/`varchar` greater 47 | than 1 is stored as a variable-length attribute in TileDB. TileDB **will not** enforce 48 | the length parameter but Trino will for inserts. 49 | 50 | ## Decimal Type 51 | 52 | Decimal types are currently treated as doubles. TileDB does not enforce the 53 | precision or scale of the decimal types. 54 | 55 | ## Create Table 56 | 57 | Create table is supported, however only a limited subset of TileDB parameters 58 | is supported. 59 | 60 | - No support for creating encrypted arrays 61 | - No support for setting custom filters on attributes, coordinates or offsets 62 | 63 | 64 | ## Splits 65 | 66 | The current split implementation is naive and splits domains evenly 67 | with user defined predicates (`WHERE` clause) or from the non-empty domains. 68 | This even splitting will likely produce sub optimal splits for sparse 69 | domains. Future work will move splitting into core TileDB where better 70 | heuristics will be used to produce even splits. 71 | 72 | For now, if splits are highly uneven consider increasing the number of splits 73 | via the `tiledb.splits` session parameter or add where clauses to limit the 74 | data set to non-empty regions of the array. -------------------------------------------------------------------------------- /docs/SQL.md: -------------------------------------------------------------------------------- 1 | # SQL 2 | 3 | This document contains all custom SQL options defined by the TileDB Trino connector. 4 | 5 | ## Create Table 6 | 7 | The following properties can be configured for creating a TileDB array in 8 | Trino. 9 | 10 | ### Table properties 11 | 12 | | Property | Description | Default Value | Possible Values | Required | 13 | | -------- | ----------- | ------------- | --------------- | -------- | 14 | | uri | URI for array to be created at | "" | * | Yes | 15 | | cell_order | Cell order for array | ROW_MAJOR | ROW_MAJOR, COL_MAJOR, GLOBAL_ORDER | No | 16 | | tile_order | Tile order for array | ROW_MAJOR | ROW_MAJOR, COL_MAJOR, GLOBAL_ORDER | No | 17 | | capacity | Capacity of sparse array | 10000L | >0 | No | 18 | 19 | ### Column Properties 20 | 21 | | Property | Description | Default Value | Possible Values | Required | 22 | | -------- | ----------- | ------------- | --------------- | -------- | 23 | | dimension | Is column a dimension | False | True, False | No | 24 | | lower_bound | Domain Lower Bound | 0L | Any Long Value | No | 25 | | upper_bound | Domain Upper Bound | Long.MAX_VALUE | Any Long Value | No | 26 | | extent | Dimension Extent | 10L | Any Long Value | No | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | ########################################################################################## 204 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 205 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 206 | ########################################################################################## 207 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 208 | if [ "$MVNW_VERBOSE" = true ]; then 209 | echo "Found .mvn/wrapper/maven-wrapper.jar" 210 | fi 211 | else 212 | if [ "$MVNW_VERBOSE" = true ]; then 213 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 214 | fi 215 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" 216 | while IFS="=" read key value; do 217 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 218 | esac 219 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 220 | if [ "$MVNW_VERBOSE" = true ]; then 221 | echo "Downloading from: $jarUrl" 222 | fi 223 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 224 | 225 | if command -v wget > /dev/null; then 226 | if [ "$MVNW_VERBOSE" = true ]; then 227 | echo "Found wget ... using wget" 228 | fi 229 | wget "$jarUrl" -O "$wrapperJarPath" 230 | elif command -v curl > /dev/null; then 231 | if [ "$MVNW_VERBOSE" = true ]; then 232 | echo "Found curl ... using curl" 233 | fi 234 | curl -o "$wrapperJarPath" "$jarUrl" 235 | else 236 | if [ "$MVNW_VERBOSE" = true ]; then 237 | echo "Falling back to using Java to download" 238 | fi 239 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 240 | if [ -e "$javaClass" ]; then 241 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 242 | if [ "$MVNW_VERBOSE" = true ]; then 243 | echo " - Compiling MavenWrapperDownloader.java ..." 244 | fi 245 | # Compiling the Java class 246 | ("$JAVA_HOME/bin/javac" "$javaClass") 247 | fi 248 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 249 | # Running the downloader 250 | if [ "$MVNW_VERBOSE" = true ]; then 251 | echo " - Running MavenWrapperDownloader.java ..." 252 | fi 253 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 254 | fi 255 | fi 256 | fi 257 | fi 258 | ########################################################################################## 259 | # End of extension 260 | ########################################################################################## 261 | 262 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 263 | if [ "$MVNW_VERBOSE" = true ]; then 264 | echo $MAVEN_PROJECTBASEDIR 265 | fi 266 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 267 | 268 | # For Cygwin, switch paths to Windows format before running java 269 | if $cygwin; then 270 | [ -n "$M2_HOME" ] && 271 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 272 | [ -n "$JAVA_HOME" ] && 273 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 274 | [ -n "$CLASSPATH" ] && 275 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 276 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 277 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 278 | fi 279 | 280 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 281 | 282 | exec "$JAVACMD" \ 283 | $MAVEN_OPTS \ 284 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 285 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 286 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 287 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" 124 | FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( 125 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | echo Found %WRAPPER_JAR% 132 | ) else ( 133 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 134 | echo Downloading from: %DOWNLOAD_URL% 135 | powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" 136 | echo Finished downloading %WRAPPER_JAR% 137 | ) 138 | @REM End of extension 139 | 140 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 141 | if ERRORLEVEL 1 goto error 142 | goto end 143 | 144 | :error 145 | set ERROR_CODE=1 146 | 147 | :end 148 | @endlocal & set ERROR_CODE=%ERROR_CODE% 149 | 150 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 151 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 152 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 153 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 154 | :skipRcPost 155 | 156 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 157 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 158 | 159 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 160 | 161 | exit /B %ERROR_CODE% 162 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | io.trino 6 | trino-root 7 | 439 8 | 9 | 10 | trino-tiledb 11 | Trino - TileDB Connector 12 | 13 | 14 | 15 | 16 | com.fasterxml.jackson.core 17 | jackson-databind 18 | 19 | 20 | 21 | com.github.oshi 22 | oshi-core 23 | 6.4.0 24 | 25 | 26 | org.slf4j 27 | slf4j-api 28 | 29 | 30 | 31 | 32 | 33 | com.google.code.findbugs 34 | jsr305 35 | true 36 | 37 | 38 | 39 | com.google.guava 40 | guava 41 | 42 | 43 | 44 | com.google.inject 45 | guice 46 | 47 | 48 | 49 | 50 | commons-beanutils 51 | commons-beanutils-core 52 | 1.8.3 53 | 54 | 55 | commons-logging 56 | commons-logging 57 | 58 | 59 | 60 | 61 | io.airlift 62 | bootstrap 63 | 64 | 65 | 66 | io.airlift 67 | configuration 68 | 69 | 70 | 71 | io.airlift 72 | json 73 | 74 | 75 | 76 | io.airlift 77 | log 78 | 79 | 80 | 81 | io.tiledb 82 | tiledb-java 83 | 0.22.0 84 | 85 | 86 | 87 | jakarta.inject 88 | jakarta.inject-api 89 | 2.0.1 90 | 91 | 92 | 93 | joda-time 94 | joda-time 95 | 96 | 97 | 98 | com.fasterxml.jackson.core 99 | jackson-annotations 100 | provided 101 | 102 | 103 | 104 | io.airlift 105 | slice 106 | provided 107 | 108 | 109 | 110 | io.airlift 111 | units 112 | provided 113 | 114 | 115 | 116 | 117 | io.trino 118 | trino-spi 119 | provided 120 | 121 | 122 | 123 | commons-beanutils 124 | commons-beanutils-core 125 | 126 | 127 | 128 | 129 | 130 | io.airlift 131 | testing 132 | test 133 | 134 | 135 | 136 | io.airlift.tpch 137 | tpch 138 | 0.10 139 | test 140 | 141 | 142 | 143 | 144 | io.trino 145 | trino-main 146 | test 147 | 148 | 149 | 150 | 151 | io.trino 152 | trino-testing 153 | 439 154 | test 155 | 156 | 157 | 158 | io.trino 159 | trino-tests 160 | test 161 | 162 | 163 | 164 | io.trino 165 | trino-tpch 166 | test 167 | 168 | 169 | 170 | 171 | org.assertj 172 | assertj-core 173 | test 174 | 175 | 176 | 177 | org.gaul 178 | modernizer-maven-annotations 179 | test 180 | 181 | 182 | 183 | org.jetbrains 184 | annotations 185 | test 186 | 187 | 188 | 189 | org.testng 190 | testng 191 | 7.7.0 192 | test 193 | 194 | 195 | 196 | 197 | 198 | 199 | org.apache.maven.plugins 200 | maven-surefire-plugin 201 | 3.0.0 202 | 203 | methods 204 | 1 205 | false 206 | 207 | 208 | 209 | org.apache.maven.plugins 210 | maven-shade-plugin 211 | 3.2.1 212 | 213 | 214 | trino-plugin 215 | 216 | 217 | 218 | META-INF/services/io.trino.spi.Plugin 219 | src/main/resources/services/io.trino.spi.Plugin 220 | 221 | 222 | 223 | 224 | 225 | 226 | shade 227 | 228 | package 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | ci 238 | 239 | 240 | 241 | org.apache.maven.plugins 242 | maven-surefire-plugin 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | -------------------------------------------------------------------------------- /src/main/java/io/trino/plugin/tiledb/TileDBClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package io.trino.plugin.tiledb; 15 | 16 | import com.google.common.collect.ImmutableSet; 17 | import io.tiledb.java.api.Config; 18 | import io.tiledb.java.api.Context; 19 | import io.tiledb.java.api.EncryptionType; 20 | import io.tiledb.java.api.TileDBError; 21 | import io.tiledb.java.api.TileDBObject; 22 | import io.trino.spi.TrinoException; 23 | import io.trino.spi.connector.ConnectorSession; 24 | import jakarta.inject.Inject; 25 | import oshi.hardware.HardwareAbstractionLayer; 26 | 27 | import java.io.File; 28 | import java.net.URI; 29 | import java.net.URISyntaxException; 30 | import java.util.HashMap; 31 | import java.util.Map; 32 | import java.util.Set; 33 | 34 | import static io.trino.plugin.tiledb.TileDBErrorCode.TILEDB_CONFIG_ERROR; 35 | import static io.trino.plugin.tiledb.TileDBErrorCode.TILEDB_CONTEXT_ERROR; 36 | import static io.trino.plugin.tiledb.TileDBErrorCode.TILEDB_DROP_TABLE_ERROR; 37 | import static io.trino.plugin.tiledb.TileDBErrorCode.TILEDB_UNEXPECTED_ERROR; 38 | import static io.trino.plugin.tiledb.TileDBSessionProperties.getAwsAccessKeyId; 39 | import static io.trino.plugin.tiledb.TileDBSessionProperties.getAwsSecretAccessKey; 40 | import static io.trino.plugin.tiledb.TileDBSessionProperties.getTileDBConfig; 41 | import static java.util.Objects.requireNonNull; 42 | 43 | /** 44 | * TileDBClient purpose is to wrap around the client functionality. 45 | * This allows TileDB to have mechanisms to "discover" what tables/schemas exists and report it to prestodb. 46 | * Currently this just reads a single array uri from the config but will eventually support reading a list 47 | * of arrays or other discovery mechanisms. 48 | * 49 | * The client is also responsible for developing the splits. See TileDBSplit for more details 50 | * 51 | * TileDB Client holds a single context for all table, we probably want to change this. 52 | */ 53 | public class TileDBClient 54 | { 55 | /** 56 | * SchemaName -> (TableName -> TableMetadata) 57 | */ 58 | private final Map> schemas; 59 | //TileDBTable tileDBTable; 60 | protected final String connectorId; 61 | protected Context ctx; 62 | 63 | protected TileDBConfig config; 64 | protected oshi.SystemInfo systemInfo; 65 | protected HardwareAbstractionLayer hardwareAbstractionLayer; 66 | 67 | private final String defaultSchema = "tiledb"; 68 | 69 | @Inject 70 | public TileDBClient(TileDBConnectorId connectorId, TileDBConfig config) 71 | { 72 | // Make sure config passed is not empty, since we read array-uri from it 73 | this.config = requireNonNull(config, "config is null"); 74 | try { 75 | // Create context 76 | ctx = new Context(buildConfig()); 77 | //tileDBConfig.close(); 78 | } 79 | catch (TileDBError tileDBError) { 80 | // Print stacktrace, this produces an error client side saying "internal error" 81 | throw new TrinoException(TILEDB_CONTEXT_ERROR, tileDBError); 82 | } 83 | 84 | // connectorId is an internal prestodb identified 85 | this.connectorId = requireNonNull(connectorId, "connectorId is null").toString(); 86 | // Build maps for storing schema and tables 87 | schemas = new HashMap<>(); 88 | if (config.getArrayURIs() != null) { 89 | for (URI arrayUri : config.getArrayURIs()) { 90 | addTableFromURI(ctx, defaultSchema, arrayUri); 91 | } 92 | } 93 | if (!schemas.containsKey(defaultSchema)) { 94 | schemas.put(defaultSchema, new HashMap<>()); 95 | } 96 | 97 | systemInfo = new oshi.SystemInfo(); 98 | 99 | hardwareAbstractionLayer = systemInfo.getHardware(); 100 | } 101 | 102 | public HardwareAbstractionLayer getHardwareAbstractionLayer() 103 | { 104 | return hardwareAbstractionLayer; 105 | } 106 | 107 | /** 108 | * Get plugin configuration 109 | * 110 | * @return TileDB plugin configuration 111 | */ 112 | public TileDBConfig getConfig() 113 | { 114 | return this.config; 115 | } 116 | 117 | /** 118 | * Helper function to add a table to the catalog 119 | * 120 | * @param schema schema to add table to 121 | * @param arrayUri uri of array/table 122 | */ 123 | public TileDBTable addTableFromURI(Context localCtx, String schema, URI arrayUri) 124 | { 125 | // Currently create the "table name" by splitting the path and grabbing the last part. 126 | String path = arrayUri.getPath(); 127 | String tableName = path.substring(path.lastIndexOf('/') + 1); 128 | // Create a table instances 129 | TileDBTable tileDBTable = null; 130 | try { 131 | tileDBTable = new TileDBTable(schema, tableName, arrayUri, localCtx); 132 | Map tableMapping = schemas.get(schema); 133 | if (tableMapping == null) { 134 | tableMapping = new HashMap<>(); 135 | } 136 | tableMapping.put(tableName, tileDBTable); 137 | schemas.put(schema, tableMapping); 138 | } 139 | catch (TileDBError tileDBError) { 140 | if ((new File(arrayUri.toString())).exists()) { 141 | throw new TrinoException(TILEDB_UNEXPECTED_ERROR, tileDBError); 142 | } 143 | } 144 | return tileDBTable; 145 | } 146 | 147 | /** 148 | * Return list of schema's. Since we don't have true discovery this is a static map which we return the keys 149 | * 150 | * @return List of tiledb schema names (currently just "TileDB") 151 | */ 152 | public Set getSchemaNames() 153 | { 154 | return schemas.keySet(); 155 | } 156 | 157 | /** 158 | * Return all "tables" in a schema 159 | * 160 | * @param schema the schema 161 | * @return Set of table names 162 | */ 163 | public Set getTableNames(String schema) 164 | { 165 | requireNonNull(schema, "schema is null"); 166 | Map tables = schemas.get(schema); 167 | if (tables == null) { 168 | return ImmutableSet.of(); 169 | } 170 | return tables.keySet(); 171 | } 172 | 173 | /** 174 | * Fetches a table object given a schema and a table name 175 | * 176 | * @param schema the schema 177 | * @param tableName the table name 178 | * @param encryptionType the encryption type 179 | * @param encryptionKey the encryption key 180 | * @return table object 181 | */ 182 | public TileDBTable getTable(ConnectorSession session, String schema, String tableName, 183 | EncryptionType encryptionType, String encryptionKey) 184 | { 185 | requireNonNull(schema, "schema is null"); 186 | requireNonNull(tableName, "tableName is null"); 187 | Map tables = schemas.get(schema); 188 | // If the table does not already exists we should try to see if the user is passing an array uri 189 | if (tables == null || tables.get(tableName) == null) { 190 | try { 191 | Context localCtx = buildContext(session, encryptionType, encryptionKey); 192 | return addTableFromURI(localCtx, schema, new URI(tableName)); 193 | } 194 | catch (URISyntaxException e) { 195 | throw new TrinoException(TILEDB_UNEXPECTED_ERROR, e); 196 | } 197 | catch (TileDBError tileDBError) { 198 | throw new TrinoException(TILEDB_UNEXPECTED_ERROR, tileDBError); 199 | } 200 | } 201 | return tables.get(tableName); 202 | } 203 | 204 | /** 205 | * Fetches a table object given a schema and a table name 206 | * 207 | * @param schema the schema 208 | * @param tableName the table name 209 | * @return table object 210 | */ 211 | public TileDBTable getTable(ConnectorSession session, String schema, String tableName) 212 | { 213 | String key = TileDBSessionProperties.getEncryptionKey(session); 214 | if (key != null) { 215 | return this.getTable(session, schema, tableName, EncryptionType.TILEDB_AES_256_GCM, key); 216 | } 217 | else { 218 | return this.getTable(session, schema, tableName, null, null); 219 | } 220 | } 221 | 222 | public Context getCtx() 223 | { 224 | return ctx; 225 | } 226 | 227 | /** 228 | * Rollback a create table statement, this just drops the array 229 | * 230 | * @param handle TileDB table handler 231 | */ 232 | public void rollbackCreateTable(TileDBOutputTableHandle handle) 233 | { 234 | try { 235 | TileDBObject.remove(ctx, handle.getURI()); 236 | schemas.get(handle.getSchemaName()).remove(handle.getTableName()); 237 | ctx.close(); 238 | ctx = new Context(buildConfig()); 239 | } 240 | catch (TileDBError tileDBError) { 241 | throw new TrinoException(TILEDB_DROP_TABLE_ERROR, tileDBError); 242 | } 243 | } 244 | 245 | public void dropTable(ConnectorSession session, TileDBTableHandle handle) 246 | { 247 | try { 248 | Context localCtx = buildContext(session, null, null); 249 | TileDBObject.remove(localCtx, handle.getURI()); 250 | schemas.get(handle.getSchemaName()).remove(handle.getTableName()); 251 | ctx.close(); 252 | ctx = new Context(buildConfig()); 253 | } 254 | catch (TileDBError tileDBError) { 255 | throw new TrinoException(TILEDB_DROP_TABLE_ERROR, tileDBError); 256 | } 257 | } 258 | 259 | public Config buildConfig() 260 | { 261 | try { 262 | // Create context 263 | Config tileDBConfig = new Config(); 264 | if (config.getAwsAccessKeyId() != null && !config.getAwsAccessKeyId().isEmpty() 265 | && config.getAwsSecretAccessKey() != null && !config.getAwsSecretAccessKey().isEmpty()) { 266 | tileDBConfig.set("vfs.s3.aws_access_key_id", config.getAwsAccessKeyId()); 267 | tileDBConfig.set("vfs.s3.aws_secret_access_key", config.getAwsSecretAccessKey()); 268 | } 269 | return tileDBConfig; 270 | //tileDBConfig.close(); 271 | } 272 | catch (TileDBError tileDBError) { 273 | // Print stacktrace, this produces an error client side saying "internal error" 274 | throw new TrinoException(TILEDB_CONTEXT_ERROR, tileDBError); 275 | } 276 | } 277 | 278 | public Context buildContext(ConnectorSession session, EncryptionType encryptionType, String encryptionKey) 279 | throws TileDBError 280 | { 281 | Config tileDBConfig = new Config(); 282 | boolean updateCtx = false; 283 | 284 | // Encryption 285 | String currentKey = ctx.getConfig().get("sm.encryption_key"); 286 | boolean currentDifferent = !currentKey.equals(encryptionKey); 287 | // add a key only if the current is different 288 | if (encryptionType != null && encryptionKey != null && currentDifferent) { 289 | tileDBConfig.set("sm.encryption_type", encryptionType.toString().replace("TILEDB_", "")); 290 | tileDBConfig.set("sm.encryption_key", encryptionKey); 291 | updateCtx = true; 292 | } 293 | 294 | if (session != null) { 295 | String configString = getTileDBConfig(session); 296 | 297 | // If the user set any tiledb config parameters, we will set them here 298 | if (configString != null) { 299 | for (String config : configString.split(",")) { 300 | String[] kv = config.split("="); 301 | if (kv.length != 2) { 302 | throw new TrinoException(TILEDB_CONFIG_ERROR, "invalid config for " + config); 303 | } 304 | tileDBConfig.set(kv[0], kv[1]); 305 | updateCtx = true; 306 | } 307 | } 308 | 309 | // We'll deprecate the AWS Access Key parameters and remove in a future version 310 | String awsAccessKeyId = getAwsAccessKeyId(session); 311 | String awsSecretAccessKey = getAwsSecretAccessKey(session); 312 | if (awsAccessKeyId != null && !awsAccessKeyId.isEmpty() 313 | && awsSecretAccessKey != null && !awsSecretAccessKey.isEmpty()) { 314 | tileDBConfig.set("vfs.s3.aws_access_key_id", awsAccessKeyId); 315 | tileDBConfig.set("vfs.s3.aws_secret_access_key", awsSecretAccessKey); 316 | updateCtx = true; 317 | } 318 | if (updateCtx) { 319 | ctx = new Context(tileDBConfig); 320 | } 321 | tileDBConfig.close(); 322 | } 323 | return ctx; 324 | } 325 | } 326 | -------------------------------------------------------------------------------- /src/main/java/io/trino/plugin/tiledb/TileDBColumn.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package io.trino.plugin.tiledb; 15 | 16 | import com.fasterxml.jackson.annotation.JsonCreator; 17 | import com.fasterxml.jackson.annotation.JsonProperty; 18 | import io.tiledb.java.api.Datatype; 19 | import io.trino.spi.type.Type; 20 | 21 | import java.util.Objects; 22 | 23 | import static com.google.common.base.Preconditions.checkArgument; 24 | import static com.google.common.base.Strings.isNullOrEmpty; 25 | import static java.util.Objects.requireNonNull; 26 | 27 | /** 28 | * TileDB Column represents a mapping of tiledb attributes/dimensions to a column type 29 | * We use a custom class so we can track what is a dimension vs an attribute 30 | * 31 | * The json properties are important as this class will be serialized and sent via json to the workers 32 | * Every property needs a getter in the form of getVariable. 33 | * i.e. variable = isDimension, getter = getIsDimension() 34 | */ 35 | public final class TileDBColumn 36 | { 37 | private final String name; 38 | private final Type type; 39 | private final Datatype tileDBType; 40 | private final Boolean isVariableLength; 41 | private final Boolean isDimension; 42 | private final Boolean isNullable; 43 | 44 | @JsonCreator 45 | public TileDBColumn( 46 | @JsonProperty("name") String name, 47 | @JsonProperty("type") Type type, 48 | @JsonProperty("TileDBType") Datatype tileDBType, 49 | @JsonProperty("isVariableLength") Boolean isVariableLength, 50 | @JsonProperty("isDimension") Boolean isDimension, 51 | @JsonProperty("isNullable") Boolean isNullable) 52 | { 53 | checkArgument(!isNullOrEmpty(name), "name is null or is empty"); 54 | this.name = name; 55 | this.type = requireNonNull(type, "type is null"); 56 | this.tileDBType = requireNonNull(tileDBType, "TileDBType is null"); 57 | this.isVariableLength = requireNonNull(isVariableLength, "isVariableLength is null"); 58 | this.isDimension = requireNonNull(isDimension, "isDimension is null"); 59 | if (isNullable == null) { 60 | this.isNullable = false; 61 | } 62 | else { 63 | this.isNullable = isNullable; 64 | } 65 | } 66 | 67 | @JsonProperty 68 | public String getName() 69 | { 70 | return name; 71 | } 72 | 73 | @JsonProperty 74 | public Boolean getNullable() 75 | { 76 | return isNullable; 77 | } 78 | 79 | @JsonProperty 80 | public Datatype getTileDBType() 81 | { 82 | return tileDBType; 83 | } 84 | 85 | @JsonProperty 86 | public Boolean getIsVariableLength() 87 | { 88 | return isVariableLength; 89 | } 90 | 91 | @JsonProperty 92 | public Type getType() 93 | { 94 | return type; 95 | } 96 | 97 | @JsonProperty 98 | public Boolean getIsDimension() 99 | { 100 | return isDimension; 101 | } 102 | 103 | @Override 104 | public int hashCode() 105 | { 106 | return Objects.hash(name, type, isNullable, isDimension); 107 | } 108 | 109 | @Override 110 | public boolean equals(Object obj) 111 | { 112 | if (this == obj) { 113 | return true; 114 | } 115 | if (obj == null || getClass() != obj.getClass()) { 116 | return false; 117 | } 118 | 119 | TileDBColumn other = (TileDBColumn) obj; 120 | return Objects.equals(this.name, other.name) && 121 | Objects.equals(this.type, other.type) && 122 | Objects.equals(this.isNullable, other.isNullable) && 123 | Objects.equals(this.isDimension, other.isDimension); 124 | } 125 | 126 | @Override 127 | public String toString() 128 | { 129 | return name + ":" + type; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/io/trino/plugin/tiledb/TileDBColumnHandle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package io.trino.plugin.tiledb; 15 | 16 | import com.fasterxml.jackson.annotation.JsonCreator; 17 | import com.fasterxml.jackson.annotation.JsonProperty; 18 | import io.tiledb.java.api.Datatype; 19 | import io.trino.spi.connector.ColumnHandle; 20 | import io.trino.spi.connector.ColumnMetadata; 21 | import io.trino.spi.type.Type; 22 | 23 | import java.util.Objects; 24 | 25 | import static com.google.common.base.MoreObjects.toStringHelper; 26 | import static java.util.Objects.requireNonNull; 27 | 28 | /** 29 | * Implements a column handler 30 | * The json properties are important as this class will be serialized and sent via json to the workers 31 | * Every property needs a getter in the form of getVariable. 32 | * i.e. variable = isDimension, getter = getIsDimension() 33 | */ 34 | public final class TileDBColumnHandle 35 | implements ColumnHandle 36 | { 37 | private final String connectorId; 38 | private final String columnName; 39 | private final Type columnType; 40 | private final Datatype columnTileDBType; 41 | private final boolean isVariableLength; 42 | private final boolean isDimension; 43 | private final boolean isNullable; 44 | 45 | @JsonCreator 46 | public TileDBColumnHandle( 47 | @JsonProperty("connectorId") String connectorId, 48 | @JsonProperty("columnName") String columnName, 49 | @JsonProperty("columnType") Type columnType, 50 | @JsonProperty("columnTileDBType") Datatype columnTileDBType, 51 | @JsonProperty("isVariableLength") Boolean isVariableLength, 52 | @JsonProperty("isDimension") Boolean isDimension, 53 | @JsonProperty("isNullable") Boolean isNullable) 54 | { 55 | this.connectorId = requireNonNull(connectorId, "connectorId is null"); 56 | this.columnName = requireNonNull(columnName, "columnName is null"); 57 | this.columnType = requireNonNull(columnType, "columnType is null"); 58 | this.columnTileDBType = requireNonNull(columnTileDBType, "columnTileDBType is null"); 59 | this.isVariableLength = requireNonNull(isVariableLength, "isVariableLength is null"); 60 | this.isDimension = requireNonNull(isDimension, "isDimension is null"); 61 | if (isNullable == null) { 62 | this.isNullable = false; 63 | } 64 | else { 65 | this.isNullable = isNullable; 66 | } 67 | } 68 | 69 | @JsonProperty 70 | public String getConnectorId() 71 | { 72 | return connectorId; 73 | } 74 | 75 | @JsonProperty 76 | public String getColumnName() 77 | { 78 | return columnName; 79 | } 80 | 81 | @JsonProperty 82 | public Type getColumnType() 83 | { 84 | return columnType; 85 | } 86 | 87 | @JsonProperty 88 | public Datatype getColumnTileDBType() 89 | { 90 | return columnTileDBType; 91 | } 92 | 93 | @JsonProperty 94 | public Boolean getIsVariableLength() 95 | { 96 | return isVariableLength; 97 | } 98 | 99 | @JsonProperty 100 | public Boolean getIsDimension() 101 | { 102 | return isDimension; 103 | } 104 | 105 | @JsonProperty 106 | public Boolean getNullable() 107 | { 108 | return isNullable; 109 | } 110 | 111 | public ColumnMetadata getColumnMetadata() 112 | { 113 | return new ColumnMetadata(columnName, columnType); 114 | } 115 | 116 | @Override 117 | public int hashCode() 118 | { 119 | return Objects.hash(connectorId, columnName, columnType, isNullable, isDimension); 120 | } 121 | 122 | @Override 123 | public boolean equals(Object obj) 124 | { 125 | if (this == obj) { 126 | return true; 127 | } 128 | if ((obj == null) || (getClass() != obj.getClass())) { 129 | return false; 130 | } 131 | 132 | TileDBColumnHandle other = (TileDBColumnHandle) obj; 133 | return Objects.equals(this.connectorId, other.connectorId) && 134 | Objects.equals(this.columnName, other.columnName) && 135 | Objects.equals(this.columnType, other.columnType) && 136 | Objects.equals(this.isNullable, other.isNullable) && 137 | Objects.equals(this.isDimension, other.isDimension); 138 | } 139 | 140 | @Override 141 | public String toString() 142 | { 143 | return toStringHelper(this) 144 | .add("connectorId", connectorId) 145 | .add("columnName", columnName) 146 | .add("columnType", columnType) 147 | .add("columnTileDBType", columnTileDBType) 148 | .add("isVariableLength", isVariableLength) 149 | .add("isNullable", isNullable) 150 | .add("isDimension", isDimension) 151 | .toString(); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/main/java/io/trino/plugin/tiledb/TileDBColumnProperties.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package io.trino.plugin.tiledb; 15 | 16 | import com.google.common.collect.ImmutableList; 17 | import io.trino.spi.session.PropertyMetadata; 18 | import io.trino.spi.type.TypeManager; 19 | import jakarta.inject.Inject; 20 | 21 | import java.util.List; 22 | import java.util.Map; 23 | 24 | import static io.trino.spi.session.PropertyMetadata.booleanProperty; 25 | import static io.trino.spi.session.PropertyMetadata.longProperty; 26 | import static io.trino.spi.session.PropertyMetadata.stringProperty; 27 | 28 | public class TileDBColumnProperties 29 | { 30 | public static final String Dimension = "dimension"; 31 | public static final String Nullable = "nullable"; 32 | public static final String LowerBound = "lower_bound"; 33 | public static final String UpperBound = "upper_bound"; 34 | public static final String Extent = "extent"; 35 | public static final String FilterList = "filter_list"; 36 | private final List> columnProperties; 37 | 38 | @Inject 39 | public TileDBColumnProperties(TypeManager typeManager, TileDBConfig config) 40 | { 41 | columnProperties = ImmutableList.of( 42 | booleanProperty( 43 | Dimension, 44 | "Is column a dimension?", 45 | false, 46 | false), 47 | booleanProperty( 48 | Nullable, 49 | "Is column nullable?", 50 | false, 51 | false), 52 | longProperty( 53 | LowerBound, 54 | "Domain Lower Bound", 55 | Long.MIN_VALUE, 56 | false), 57 | longProperty( 58 | UpperBound, 59 | "Domain Upper Bound", 60 | Long.MAX_VALUE, 61 | false), 62 | longProperty( 63 | Extent, 64 | "Dimension Extent", 65 | 10L, 66 | false), 67 | stringProperty(FilterList, 68 | "The filter list", 69 | "", 70 | false)); 71 | } 72 | 73 | public List> getColumnProperties() 74 | { 75 | return columnProperties; 76 | } 77 | 78 | public static Boolean getDimension(Map tableProperties) 79 | { 80 | return (Boolean) tableProperties.get(Dimension); 81 | } 82 | 83 | public static Long getLowerBound(Map tableProperties) 84 | { 85 | return (Long) tableProperties.get(LowerBound); 86 | } 87 | 88 | public static boolean getNullable(Map tableProperties) 89 | { 90 | return (boolean) tableProperties.get(Nullable); 91 | } 92 | 93 | public static Long getUpperBound(Map tableProperties) 94 | { 95 | return (Long) tableProperties.get(UpperBound); 96 | } 97 | 98 | public static String getFilterList(Map tableProperties) 99 | { 100 | if (tableProperties.containsKey(FilterList)) { 101 | return (String) tableProperties.get(FilterList); 102 | } 103 | else { 104 | return ""; 105 | } 106 | } 107 | 108 | public static Long getExtent(Map tableProperties) 109 | { 110 | return (Long) tableProperties.get(Extent); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/io/trino/plugin/tiledb/TileDBConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package io.trino.plugin.tiledb; 15 | 16 | import com.google.common.base.Splitter; 17 | import com.google.common.base.Strings; 18 | import com.google.common.collect.ImmutableSet; 19 | import io.airlift.configuration.Config; 20 | import io.airlift.configuration.ConfigDescription; 21 | import io.trino.spi.TrinoException; 22 | 23 | import java.net.URI; 24 | import java.net.URISyntaxException; 25 | import java.util.Set; 26 | 27 | import static io.trino.plugin.tiledb.TileDBErrorCode.TILEDB_CONFIG_ERROR; 28 | 29 | /** 30 | * TileDBConfig maps directly to a configuration file 31 | */ 32 | public class TileDBConfig 33 | { 34 | private Set arrayURIs; 35 | 36 | private int readBufferSize = 1024 * 1024 * 10; 37 | 38 | private int writeBufferSize = 1024 * 1024 * 1; 39 | 40 | private String awsAccessKeyId; 41 | 42 | private String awsSecretAccessKey; 43 | 44 | private String tileDBConfig; 45 | 46 | public Set getArrayURIs() 47 | { 48 | return arrayURIs; 49 | } 50 | 51 | public int getReadBufferSize() 52 | { 53 | return readBufferSize; 54 | } 55 | 56 | public int getWriteBufferSize() 57 | { 58 | return writeBufferSize; 59 | } 60 | 61 | public String getAwsAccessKeyId() 62 | { 63 | return awsAccessKeyId; 64 | } 65 | 66 | public String getAwsSecretAccessKey() 67 | { 68 | return awsSecretAccessKey; 69 | } 70 | 71 | public String getTileDBConfig() 72 | { 73 | return tileDBConfig; 74 | } 75 | 76 | @ConfigDescription("A comma separated list of array uris") 77 | @Config("array-uris") 78 | public TileDBConfig setArrayURIs(String arrayURIs) 79 | { 80 | if (Strings.isNullOrEmpty(arrayURIs)) { 81 | this.arrayURIs = null; 82 | return this; 83 | } 84 | ImmutableSet.Builder builder = ImmutableSet.builder(); 85 | try { 86 | for (String value : Splitter.on(',').trimResults().omitEmptyStrings().split(arrayURIs)) { 87 | builder.add(new URI(value)); 88 | } 89 | } 90 | catch (URISyntaxException e) { 91 | e.printStackTrace(); 92 | throw new TrinoException(TILEDB_CONFIG_ERROR, e); 93 | } 94 | this.arrayURIs = builder.build(); 95 | return this; 96 | } 97 | 98 | @ConfigDescription("Size in bytes to use for read buffers") 99 | @Config("read-buffer-size") 100 | public TileDBConfig setReadBufferSize(int readBufferSize) 101 | { 102 | this.readBufferSize = readBufferSize; 103 | return this; 104 | } 105 | 106 | @ConfigDescription("Size in bytes to use for write buffers") 107 | @Config("write-buffer-size") 108 | public TileDBConfig setWriteBufferSize(int writeBufferSize) 109 | { 110 | this.writeBufferSize = writeBufferSize; 111 | return this; 112 | } 113 | 114 | @ConfigDescription("AWS Access Key ID used for s3 access") 115 | @Config("aws-access-key-id") 116 | public TileDBConfig setAwsAccessKeyId(String awsAccessKeyId) 117 | { 118 | this.awsAccessKeyId = awsAccessKeyId; 119 | return this; 120 | } 121 | 122 | @ConfigDescription("AWS Secret Access Key used for s3 access") 123 | @Config("aws-secret-access-key") 124 | public TileDBConfig setAwsSecretAccessKey(String awsSecretAccessKey) 125 | { 126 | this.awsSecretAccessKey = awsSecretAccessKey; 127 | return this; 128 | } 129 | 130 | @ConfigDescription("TileDB config parameters in key1=value1,key2=value2 form") 131 | @Config("tiledb-config") 132 | public TileDBConfig setTileDBConfig(String tileDBConfig) 133 | { 134 | this.tileDBConfig = tileDBConfig; 135 | return this; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/io/trino/plugin/tiledb/TileDBConnector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package io.trino.plugin.tiledb; 15 | 16 | import com.google.common.collect.ImmutableList; 17 | import io.airlift.bootstrap.LifeCycleManager; 18 | import io.airlift.log.Logger; 19 | import io.trino.spi.TrinoException; 20 | import io.trino.spi.connector.Connector; 21 | import io.trino.spi.connector.ConnectorMetadata; 22 | import io.trino.spi.connector.ConnectorPageSinkProvider; 23 | import io.trino.spi.connector.ConnectorRecordSetProvider; 24 | import io.trino.spi.connector.ConnectorSession; 25 | import io.trino.spi.connector.ConnectorSplitManager; 26 | import io.trino.spi.connector.ConnectorTransactionHandle; 27 | import io.trino.spi.session.PropertyMetadata; 28 | import io.trino.spi.transaction.IsolationLevel; 29 | import jakarta.inject.Inject; 30 | 31 | import java.util.List; 32 | import java.util.concurrent.ConcurrentHashMap; 33 | import java.util.concurrent.ConcurrentMap; 34 | 35 | import static com.google.common.base.Preconditions.checkArgument; 36 | import static io.trino.plugin.tiledb.TileDBErrorCode.TILEDB_UNEXPECTED_ERROR; 37 | import static java.util.Objects.requireNonNull; 38 | 39 | /** 40 | * TileDBConnector represents a "live" connection, it starts a transaction (which does not exist in tiledb) 41 | * and it stores the record set provider which is used for fetching actual results 42 | */ 43 | public class TileDBConnector 44 | implements Connector 45 | { 46 | private static final Logger log = Logger.get(TileDBConnector.class); 47 | 48 | private final LifeCycleManager lifeCycleManager; 49 | private final TileDBMetadata metadata; 50 | private final TileDBSplitManager splitManager; 51 | private final TileDBRecordSetProvider recordSetProvider; 52 | private final List> sessionProperties; 53 | private final List> tableProperties; 54 | private final List> columnProperties; 55 | private final TileDBPageSinkProvider tileDBPageSinkProvider; 56 | 57 | private final ConcurrentMap transactions = new ConcurrentHashMap<>(); 58 | 59 | @Inject 60 | public TileDBConnector( 61 | LifeCycleManager lifeCycleManager, 62 | TileDBMetadata metadata, 63 | TileDBSplitManager splitManager, 64 | TileDBRecordSetProvider recordSetProvider, 65 | List> sessionProperties, 66 | List> tableProperties, 67 | List> columnProperties, 68 | TileDBPageSinkProvider tileDBPageSinkProvider) 69 | { 70 | this.lifeCycleManager = requireNonNull(lifeCycleManager, "lifeCycleManager is null"); 71 | this.metadata = requireNonNull(metadata, "metadata is null"); 72 | this.splitManager = requireNonNull(splitManager, "splitManager is null"); 73 | this.recordSetProvider = requireNonNull(recordSetProvider, "recordSetProvider is null"); 74 | this.sessionProperties = ImmutableList.copyOf(requireNonNull(sessionProperties, "sessionProperties is null")); 75 | this.tableProperties = ImmutableList.copyOf(requireNonNull(tableProperties, "tableProperties is null")); 76 | this.columnProperties = ImmutableList.copyOf(requireNonNull(columnProperties, "columnProperties is null")); 77 | this.tileDBPageSinkProvider = requireNonNull(tileDBPageSinkProvider, "tileDBPageSinkProvider is null"); 78 | } 79 | 80 | @Override 81 | public ConnectorTransactionHandle beginTransaction(IsolationLevel isolationLevel, boolean readOnly, boolean autoCommit) 82 | { 83 | TileDBTransactionHandle transaction = new TileDBTransactionHandle(); 84 | transactions.put(transaction, metadata); 85 | return transaction; 86 | } 87 | 88 | @Override 89 | public void commit(ConnectorTransactionHandle transactionHandle) 90 | { 91 | checkArgument(transactions.remove(transactionHandle) != null, "no such transaction: %s", transactionHandle); 92 | } 93 | 94 | @Override 95 | public void rollback(ConnectorTransactionHandle transactionHandle) 96 | { 97 | TileDBMetadata metadata = transactions.remove(transactionHandle); 98 | checkArgument(metadata != null, "no such transaction: %s", transactionHandle); 99 | metadata.rollback(); 100 | } 101 | 102 | @Override 103 | public ConnectorMetadata getMetadata(ConnectorSession session, ConnectorTransactionHandle transactionHandle) 104 | { 105 | return metadata; 106 | } 107 | 108 | @Override 109 | public ConnectorSplitManager getSplitManager() 110 | { 111 | return splitManager; 112 | } 113 | 114 | @Override 115 | public ConnectorRecordSetProvider getRecordSetProvider() 116 | { 117 | return recordSetProvider; 118 | } 119 | 120 | @Override 121 | public List> getSessionProperties() 122 | { 123 | return sessionProperties; 124 | } 125 | 126 | @Override 127 | public List> getTableProperties() 128 | { 129 | return tableProperties; 130 | } 131 | 132 | @Override 133 | public List> getColumnProperties() 134 | { 135 | return columnProperties; 136 | } 137 | 138 | @Override 139 | public ConnectorPageSinkProvider getPageSinkProvider() 140 | { 141 | return tileDBPageSinkProvider; 142 | } 143 | 144 | @Override 145 | public final void shutdown() 146 | { 147 | try { 148 | lifeCycleManager.stop(); 149 | } 150 | catch (Exception e) { 151 | log.error(e, "Error shutting down connector"); 152 | throw new TrinoException(TILEDB_UNEXPECTED_ERROR, e); 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/main/java/io/trino/plugin/tiledb/TileDBConnectorFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package io.trino.plugin.tiledb; 15 | 16 | import com.google.inject.Injector; 17 | import io.airlift.bootstrap.Bootstrap; 18 | import io.airlift.bootstrap.LifeCycleManager; 19 | import io.airlift.json.JsonModule; 20 | import io.trino.spi.NodeManager; 21 | import io.trino.spi.TrinoException; 22 | import io.trino.spi.connector.Connector; 23 | import io.trino.spi.connector.ConnectorContext; 24 | import io.trino.spi.connector.ConnectorFactory; 25 | 26 | import java.util.Map; 27 | 28 | import static com.google.common.base.Throwables.throwIfUnchecked; 29 | import static io.trino.plugin.tiledb.TileDBErrorCode.TILEDB_CONNECTOR_ERROR; 30 | import static java.util.Objects.requireNonNull; 31 | 32 | /** 33 | * Factory for creating TileDBConnectors 34 | */ 35 | public class TileDBConnectorFactory 36 | implements ConnectorFactory 37 | { 38 | @Override 39 | public String getName() 40 | { 41 | return "tiledb"; 42 | } 43 | 44 | @Override 45 | public Connector create(String catalogName, Map requiredConfig, ConnectorContext context) 46 | { 47 | requireNonNull(requiredConfig, "requiredConfig is null"); 48 | try { 49 | // A plugin is not required to use Guice; it is just very convenient 50 | Bootstrap app = new Bootstrap( 51 | new JsonModule(), 52 | new TileDBModule(catalogName, context.getTypeManager()), 53 | binder -> { 54 | binder.bind(NodeManager.class).toInstance(context.getNodeManager()); 55 | }); 56 | 57 | Injector injector = app 58 | .doNotInitializeLogging() 59 | .setRequiredConfigurationProperties(requiredConfig) 60 | .initialize(); 61 | 62 | // Create instances for connector 63 | LifeCycleManager lifeCycleManager = injector.getInstance(LifeCycleManager.class); 64 | TileDBMetadata metadata = injector.getInstance(TileDBMetadata.class); 65 | TileDBSplitManager splitManager = injector.getInstance(TileDBSplitManager.class); 66 | TileDBRecordSetProvider recordSetProvider = injector.getInstance(TileDBRecordSetProvider.class); 67 | TileDBSessionProperties tileDBSessionProperties = injector.getInstance(TileDBSessionProperties.class); 68 | 69 | TileDBTableProperties tileDBTableProperties = injector.getInstance(TileDBTableProperties.class); 70 | TileDBColumnProperties tileDBColumnProperties = injector.getInstance(TileDBColumnProperties.class); 71 | TileDBPageSinkProvider pageSinkProvider = injector.getInstance(TileDBPageSinkProvider.class); 72 | return new TileDBConnector( 73 | lifeCycleManager, 74 | metadata, 75 | splitManager, 76 | recordSetProvider, 77 | tileDBSessionProperties.getSessionProperties(), 78 | tileDBTableProperties.getTableProperties(), 79 | tileDBColumnProperties.getColumnProperties(), 80 | pageSinkProvider); 81 | } 82 | catch (Exception e) { 83 | throwIfUnchecked(e); 84 | throw new TrinoException(TILEDB_CONNECTOR_ERROR, e); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/io/trino/plugin/tiledb/TileDBConnectorId.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package io.trino.plugin.tiledb; 15 | 16 | import java.util.Objects; 17 | 18 | import static java.util.Objects.requireNonNull; 19 | 20 | /** 21 | * TileDBConnectorId wraps around unique string identifiers for each TileDB "connection" 22 | * The unique identifiers are passed in from TrinoDB 23 | */ 24 | public final class TileDBConnectorId 25 | { 26 | private final String id; 27 | 28 | public TileDBConnectorId(String id) 29 | { 30 | this.id = requireNonNull(id, "id is null"); 31 | } 32 | 33 | @Override 34 | public String toString() 35 | { 36 | return id; 37 | } 38 | 39 | @Override 40 | public int hashCode() 41 | { 42 | return Objects.hash(id); 43 | } 44 | 45 | @Override 46 | public boolean equals(Object obj) 47 | { 48 | if (this == obj) { 49 | return true; 50 | } 51 | if ((obj == null) || (getClass() != obj.getClass())) { 52 | return false; 53 | } 54 | 55 | TileDBConnectorId other = (TileDBConnectorId) obj; 56 | return Objects.equals(this.id, other.id); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/io/trino/plugin/tiledb/TileDBErrorCode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package io.trino.plugin.tiledb; 15 | 16 | import io.trino.spi.ErrorCode; 17 | import io.trino.spi.ErrorCodeSupplier; 18 | import io.trino.spi.ErrorType; 19 | 20 | import static io.trino.spi.ErrorType.EXTERNAL; 21 | 22 | public enum TileDBErrorCode 23 | implements ErrorCodeSupplier 24 | { 25 | // Thrown when an TileDB error is caught that we were not expecting, 26 | // such as when there is a c_api failure unexpectedly 27 | TILEDB_UNEXPECTED_ERROR(1, EXTERNAL), 28 | 29 | // Thrown when we can not create a tiledb context 30 | TILEDB_CONTEXT_ERROR(2, EXTERNAL), 31 | 32 | // Thrown when a table can not be created 33 | TILEDB_CREATE_TABLE_ERROR(3, EXTERNAL), 34 | 35 | // Thrown when a table errors in being dropped 36 | TILEDB_DROP_TABLE_ERROR(4, EXTERNAL), 37 | 38 | // Thrown when config errors on loading 39 | TILEDB_CONFIG_ERROR(5, EXTERNAL), 40 | 41 | // Thrown connector fails to load array 42 | TILEDB_CONNECTOR_ERROR(6, EXTERNAL), 43 | 44 | // Thrown when records cursor has error with query 45 | TILEDB_RECORD_CURSOR_ERROR(7, EXTERNAL), 46 | 47 | // Throw when records set fails 48 | TILEDB_RECORD_SET_ERROR(8, EXTERNAL), 49 | 50 | // Throw when insert fails 51 | TILEDB_PAGE_SINK_ERROR(9, EXTERNAL), 52 | 53 | // Throw when a split fails 54 | TILEDB_SPLIT_MANAGER_ERROR(10, EXTERNAL); 55 | 56 | private final ErrorCode errorCode; 57 | 58 | TileDBErrorCode(int code, ErrorType type) 59 | { 60 | errorCode = new ErrorCode(code + 0x0600_0000, name(), type); 61 | } 62 | 63 | @Override 64 | public ErrorCode toErrorCode() 65 | { 66 | return errorCode; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/io/trino/plugin/tiledb/TileDBModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package io.trino.plugin.tiledb; 15 | 16 | import com.fasterxml.jackson.databind.DeserializationContext; 17 | import com.fasterxml.jackson.databind.deser.std.FromStringDeserializer; 18 | import com.google.inject.Binder; 19 | import com.google.inject.Module; 20 | import com.google.inject.Scopes; 21 | import io.tiledb.java.api.Datatype; 22 | import io.tiledb.java.api.TileDBError; 23 | import io.trino.spi.type.CharType; 24 | import io.trino.spi.type.Type; 25 | import io.trino.spi.type.TypeManager; 26 | import io.trino.spi.type.TypeSignature; 27 | import io.trino.spi.type.VarbinaryType; 28 | import io.trino.spi.type.VarcharType; 29 | import jakarta.inject.Inject; 30 | 31 | import java.util.ArrayList; 32 | import java.util.List; 33 | 34 | import static io.airlift.configuration.ConfigBinder.configBinder; 35 | import static io.airlift.json.JsonBinder.jsonBinder; 36 | import static io.airlift.json.JsonCodec.listJsonCodec; 37 | import static io.airlift.json.JsonCodecBinder.jsonCodecBinder; 38 | import static io.tiledb.java.api.Datatype.TILEDB_DATETIME_DAY; 39 | import static io.trino.spi.type.BigintType.BIGINT; 40 | import static io.trino.spi.type.DateType.DATE; 41 | import static io.trino.spi.type.DoubleType.DOUBLE; 42 | import static io.trino.spi.type.IntegerType.INTEGER; 43 | import static io.trino.spi.type.RealType.REAL; 44 | import static io.trino.spi.type.SmallintType.SMALLINT; 45 | import static io.trino.spi.type.TimestampType.TIMESTAMP_MILLIS; 46 | import static io.trino.spi.type.TinyintType.TINYINT; 47 | import static io.trino.spi.type.VarcharType.VARCHAR; 48 | import static java.util.Objects.requireNonNull; 49 | 50 | /** 51 | * TileDBModule binds all the class types, declares what is an instance class, and what is a singleton scoped class 52 | */ 53 | public class TileDBModule 54 | implements Module 55 | { 56 | private final String connectorId; 57 | private final TypeManager typeManager; 58 | 59 | public TileDBModule(String connectorId, TypeManager typeManager) 60 | { 61 | this.connectorId = requireNonNull(connectorId, "connector id is null"); 62 | this.typeManager = requireNonNull(typeManager, "typeManager is null"); 63 | } 64 | 65 | @Override 66 | public void configure(Binder binder) 67 | { 68 | binder.bind(TypeManager.class).toInstance(typeManager); 69 | 70 | configBinder(binder).bindConfig(TileDBConfig.class); 71 | binder.bind(TileDBClient.class).in(Scopes.SINGLETON); 72 | binder.bind(TileDBConnectorId.class).toInstance(new TileDBConnectorId(connectorId)); 73 | binder.bind(TileDBMetadata.class).in(Scopes.SINGLETON); 74 | binder.bind(TileDBSplitManager.class).in(Scopes.SINGLETON); 75 | binder.bind(TileDBRecordSetProvider.class).in(Scopes.SINGLETON); 76 | binder.bind(TileDBSessionProperties.class).in(Scopes.SINGLETON); 77 | binder.bind(TileDBTableProperties.class).in(Scopes.SINGLETON); 78 | binder.bind(TileDBColumnProperties.class).in(Scopes.SINGLETON); 79 | binder.bind(TileDBPageSinkProvider.class).in(Scopes.SINGLETON); 80 | 81 | jsonBinder(binder).addDeserializerBinding(Type.class).to(TypeDeserializer.class); 82 | jsonCodecBinder(binder).bindMapJsonCodec(String.class, listJsonCodec(TileDBTable.class)); 83 | } 84 | 85 | public static final class TypeDeserializer 86 | extends FromStringDeserializer 87 | { 88 | private final TypeManager typeManager; 89 | 90 | @Inject 91 | public TypeDeserializer(TypeManager typeManager) 92 | { 93 | super(Type.class); 94 | this.typeManager = requireNonNull(typeManager, "typeManager is null"); 95 | } 96 | 97 | @Override 98 | protected Type _deserialize(String value, DeserializationContext context) 99 | { 100 | return typeManager.getType(new TypeSignature(value)); 101 | } 102 | } 103 | 104 | public static Type prestoTypeFromTileDBType(Datatype type) 105 | throws TileDBError 106 | { 107 | switch (type) { 108 | case TILEDB_INT8: 109 | return TINYINT; 110 | case TILEDB_UINT8: 111 | return SMALLINT; 112 | case TILEDB_INT16: 113 | return SMALLINT; 114 | case TILEDB_UINT16: 115 | return INTEGER; 116 | case TILEDB_INT32: 117 | return INTEGER; 118 | case TILEDB_UINT32: 119 | return BIGINT; 120 | case TILEDB_INT64: 121 | return BIGINT; 122 | case TILEDB_UINT64: 123 | return BIGINT; 124 | case TILEDB_STRING_ASCII: 125 | case TILEDB_CHAR: 126 | case TILEDB_STRING_UTF8: 127 | return VARCHAR; 128 | case TILEDB_FLOAT32: 129 | return REAL; 130 | case TILEDB_FLOAT64: 131 | return DOUBLE; 132 | case TILEDB_DATETIME_AS: 133 | case TILEDB_DATETIME_FS: 134 | case TILEDB_DATETIME_PS: 135 | case TILEDB_DATETIME_NS: 136 | case TILEDB_DATETIME_US: 137 | case TILEDB_DATETIME_MS: 138 | case TILEDB_DATETIME_SEC: 139 | case TILEDB_DATETIME_MIN: 140 | case TILEDB_DATETIME_HR: 141 | return TIMESTAMP_MILLIS; 142 | case TILEDB_DATETIME_DAY: 143 | case TILEDB_DATETIME_WEEK: 144 | case TILEDB_DATETIME_MONTH: 145 | case TILEDB_DATETIME_YEAR: 146 | return DATE; 147 | default: 148 | //TODO: HANDLE ANY and other types 149 | throw new TileDBError("Unknown type: " + type.toString()); 150 | } 151 | } 152 | 153 | public static Datatype tileDBTypeFromTrinoType(Type type) 154 | throws TileDBError 155 | { 156 | type.getJavaType(); 157 | if (type.equals(TINYINT)) { 158 | return Datatype.TILEDB_INT8; 159 | } 160 | else if (type.equals(SMALLINT)) { 161 | return Datatype.TILEDB_INT16; 162 | } 163 | else if (type.equals(INTEGER)) { 164 | return Datatype.TILEDB_INT32; 165 | } 166 | else if (type.equals(BIGINT)) { 167 | return Datatype.TILEDB_INT64; 168 | } 169 | else if (type instanceof CharType) { 170 | return Datatype.TILEDB_CHAR; 171 | } 172 | else if (type instanceof VarcharType) { 173 | VarcharType varcharType = ((VarcharType) type); 174 | // Return TILEDB_CHAR in case the datatype is varchar(1) 175 | if (varcharType.getLength().isPresent() && varcharType.getLength().get().equals(1)) { 176 | return Datatype.TILEDB_CHAR; 177 | } 178 | return Datatype.TILEDB_STRING_ASCII; 179 | } 180 | else if (type instanceof VarbinaryType) { 181 | return Datatype.TILEDB_INT8; 182 | } 183 | else if (type.equals(REAL)) { 184 | return Datatype.TILEDB_FLOAT32; 185 | } 186 | else if (type.equals(DOUBLE)) { 187 | return Datatype.TILEDB_FLOAT64; 188 | } 189 | else if (type.equals(DATE)) { 190 | return TILEDB_DATETIME_DAY; 191 | } 192 | else if (type.equals(TIMESTAMP_MILLIS)) { 193 | return Datatype.TILEDB_DATETIME_MS; 194 | } 195 | //TODO: HANDLE ANY and other types 196 | throw new TileDBError("Unknown type: " + type.toString()); 197 | } 198 | 199 | /** 200 | * This is a helper function to create an ArrayList for a given type 201 | * 202 | * @param type datatype to create list of 203 | * @param isVariableLength if its variable length we will create a list of arrays 204 | * @return List 205 | * @throws TileDBError if the datatype passed is not supported 206 | */ 207 | public static List getJavaListForType(Datatype type, boolean isVariableLength) 208 | throws TileDBError 209 | { 210 | switch (type) { 211 | case TILEDB_FLOAT32: { 212 | if (isVariableLength) { 213 | return new ArrayList(); 214 | } 215 | return new ArrayList(); 216 | } 217 | case TILEDB_FLOAT64: { 218 | if (isVariableLength) { 219 | return new ArrayList(); 220 | } 221 | return new ArrayList(); 222 | } 223 | case TILEDB_INT8: { 224 | if (isVariableLength) { 225 | return new ArrayList(); 226 | } 227 | return new ArrayList(); 228 | } 229 | case TILEDB_INT16: { 230 | if (isVariableLength) { 231 | return new ArrayList(); 232 | } 233 | return new ArrayList(); 234 | } 235 | case TILEDB_INT32: { 236 | if (isVariableLength) { 237 | return new ArrayList(); 238 | } 239 | return new ArrayList(); 240 | } 241 | case TILEDB_DATETIME_MS: 242 | case TILEDB_DATETIME_DAY: 243 | case TILEDB_INT64: { 244 | if (isVariableLength) { 245 | return new ArrayList(); 246 | } 247 | return new ArrayList(); 248 | } 249 | case TILEDB_UINT8: { 250 | if (isVariableLength) { 251 | return new ArrayList(); 252 | } 253 | return new ArrayList(); 254 | } 255 | case TILEDB_UINT16: { 256 | if (isVariableLength) { 257 | return new ArrayList(); 258 | } 259 | return new ArrayList(); 260 | } 261 | case TILEDB_UINT32: { 262 | if (isVariableLength) { 263 | return new ArrayList(); 264 | } 265 | return new ArrayList(); 266 | } 267 | case TILEDB_UINT64: { 268 | if (isVariableLength) { 269 | return new ArrayList(); 270 | } 271 | return new ArrayList(); 272 | } 273 | case TILEDB_CHAR: { 274 | /*if (isVariableLength) { 275 | return new ArrayList(); 276 | }*/ 277 | return new ArrayList(); 278 | } 279 | default: { 280 | throw new TileDBError("Not supported type " + type); 281 | } 282 | } 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /src/main/java/io/trino/plugin/tiledb/TileDBOutputTableHandle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package io.trino.plugin.tiledb; 15 | 16 | import com.fasterxml.jackson.annotation.JsonCreator; 17 | import com.fasterxml.jackson.annotation.JsonProperty; 18 | import io.trino.spi.connector.ConnectorInsertTableHandle; 19 | import io.trino.spi.connector.ConnectorOutputTableHandle; 20 | 21 | import javax.annotation.Nullable; 22 | 23 | import java.util.List; 24 | import java.util.Objects; 25 | 26 | import static java.util.Objects.requireNonNull; 27 | 28 | public class TileDBOutputTableHandle 29 | implements ConnectorOutputTableHandle, ConnectorInsertTableHandle 30 | { 31 | private final String connectorId; 32 | private final String catalogName; 33 | private final String schemaName; 34 | private final String tableName; 35 | private final String uri; 36 | private final List columnHandles; 37 | 38 | @JsonCreator 39 | public TileDBOutputTableHandle( 40 | @JsonProperty("connectorId") String connectorId, 41 | @JsonProperty("catalogName") @Nullable String catalogName, 42 | @JsonProperty("schemaName") @Nullable String schemaName, 43 | @JsonProperty("tableName") String tableName, 44 | @JsonProperty("columnHandles") List columnHandles, 45 | @JsonProperty("uri") String uri) 46 | { 47 | this.connectorId = requireNonNull(connectorId, "connectorId is null"); 48 | this.catalogName = catalogName; 49 | this.schemaName = schemaName; 50 | this.tableName = requireNonNull(tableName, "tableName is null"); 51 | 52 | this.columnHandles = requireNonNull(columnHandles, "columnHandles is null"); 53 | this.uri = requireNonNull(uri, "uri is null"); 54 | } 55 | 56 | @JsonProperty 57 | public String getConnectorId() 58 | { 59 | return connectorId; 60 | } 61 | 62 | @JsonProperty 63 | @Nullable 64 | public String getCatalogName() 65 | { 66 | return catalogName; 67 | } 68 | 69 | @JsonProperty 70 | @Nullable 71 | public String getSchemaName() 72 | { 73 | return schemaName; 74 | } 75 | 76 | @JsonProperty 77 | public String getTableName() 78 | { 79 | return tableName; 80 | } 81 | 82 | @JsonProperty 83 | public List getColumnHandles() 84 | { 85 | return columnHandles; 86 | } 87 | 88 | @JsonProperty 89 | public String getURI() 90 | { 91 | return uri; 92 | } 93 | 94 | @Override 95 | public String toString() 96 | { 97 | return uri; 98 | } 99 | 100 | @Override 101 | public int hashCode() 102 | { 103 | return Objects.hash( 104 | connectorId, 105 | catalogName, 106 | schemaName, 107 | tableName, 108 | columnHandles, 109 | uri); 110 | } 111 | 112 | @Override 113 | public boolean equals(Object obj) 114 | { 115 | if (this == obj) { 116 | return true; 117 | } 118 | if (obj == null || getClass() != obj.getClass()) { 119 | return false; 120 | } 121 | TileDBOutputTableHandle other = (TileDBOutputTableHandle) obj; 122 | /*return Objects.equals(this.connectorId, other.connectorId) && 123 | Objects.equals(this.catalogName, other.catalogName) && 124 | Objects.equals(this.schemaName, other.schemaName) && 125 | Objects.equals(this.tableName, other.tableName) && 126 | Objects.equals(this.columnNames, other.columnNames) && 127 | Objects.equals(this.columnTypes, other.columnTypes) && 128 | Objects.equals(this.uri, other.uri);*/ 129 | return Objects.equals(this.uri, other.uri); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/io/trino/plugin/tiledb/TileDBPageSinkProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package io.trino.plugin.tiledb; 15 | 16 | import io.trino.spi.connector.ConnectorInsertTableHandle; 17 | import io.trino.spi.connector.ConnectorOutputTableHandle; 18 | import io.trino.spi.connector.ConnectorPageSink; 19 | import io.trino.spi.connector.ConnectorPageSinkId; 20 | import io.trino.spi.connector.ConnectorPageSinkProvider; 21 | import io.trino.spi.connector.ConnectorSession; 22 | import io.trino.spi.connector.ConnectorTransactionHandle; 23 | import jakarta.inject.Inject; 24 | 25 | import static java.util.Objects.requireNonNull; 26 | 27 | /** 28 | * Provider for page sink for inserts 29 | */ 30 | public class TileDBPageSinkProvider 31 | implements ConnectorPageSinkProvider 32 | { 33 | private final TileDBClient tileDBClient; 34 | 35 | @Inject 36 | public TileDBPageSinkProvider(TileDBClient tileDBClient) 37 | { 38 | this.tileDBClient = requireNonNull(tileDBClient, "tileDBClient is null"); 39 | } 40 | 41 | @Override 42 | public ConnectorPageSink createPageSink(ConnectorTransactionHandle transactionHandle, ConnectorSession session, ConnectorOutputTableHandle outputTableHandle, ConnectorPageSinkId pageSinkId) 43 | { 44 | return new TileDBPageSink((TileDBOutputTableHandle) outputTableHandle, tileDBClient, session); 45 | } 46 | 47 | @Override 48 | public ConnectorPageSink createPageSink(ConnectorTransactionHandle transactionHandle, ConnectorSession session, ConnectorInsertTableHandle insertTableHandle, ConnectorPageSinkId pageSinkId) 49 | { 50 | return new TileDBPageSink((TileDBOutputTableHandle) insertTableHandle, tileDBClient, session); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/io/trino/plugin/tiledb/TileDBPlugin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package io.trino.plugin.tiledb; 15 | 16 | import com.google.common.collect.ImmutableList; 17 | import io.trino.spi.Plugin; 18 | import io.trino.spi.connector.ConnectorFactory; 19 | 20 | /** 21 | * Plugin is called by prestodb at runtime to create the connector 22 | */ 23 | public class TileDBPlugin 24 | implements Plugin 25 | { 26 | @Override 27 | public Iterable getConnectorFactories() 28 | { 29 | return ImmutableList.of(new TileDBConnectorFactory()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/trino/plugin/tiledb/TileDBRecordSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package io.trino.plugin.tiledb; 15 | 16 | import com.google.common.collect.ImmutableList; 17 | import io.tiledb.java.api.Array; 18 | import io.tiledb.java.api.Context; 19 | import io.tiledb.java.api.EncryptionType; 20 | import io.tiledb.java.api.Layout; 21 | import io.tiledb.java.api.Query; 22 | import io.tiledb.java.api.TileDBError; 23 | import io.trino.spi.TrinoException; 24 | import io.trino.spi.connector.ConnectorSession; 25 | import io.trino.spi.connector.RecordCursor; 26 | import io.trino.spi.connector.RecordSet; 27 | import io.trino.spi.type.Type; 28 | 29 | import java.math.BigInteger; 30 | import java.util.List; 31 | 32 | import static io.tiledb.java.api.QueryType.TILEDB_READ; 33 | import static io.trino.plugin.tiledb.TileDBErrorCode.TILEDB_RECORD_SET_ERROR; 34 | import static io.trino.plugin.tiledb.TileDBSessionProperties.getEncryptionKey; 35 | import static io.trino.plugin.tiledb.TileDBSessionProperties.getTimestamp; 36 | import static java.util.Objects.requireNonNull; 37 | 38 | /** 39 | * TileDBRecordSet is responsible for create the records set to retrieve. Currently it create a query 40 | * and array object then allows the record cursor to do the actual querying/buffer handling 41 | */ 42 | public class TileDBRecordSet 43 | implements RecordSet 44 | { 45 | private final TileDBClient tileDBClient; 46 | private final ConnectorSession session; 47 | private final List columnHandles; 48 | private final List columnTypes; 49 | private final TileDBSplit split; 50 | private Query query; 51 | private Array array; 52 | 53 | private Context localCtx; 54 | 55 | public TileDBRecordSet(TileDBClient tileDBClient, ConnectorSession session, TileDBSplit split, List columnHandles) 56 | { 57 | this.tileDBClient = requireNonNull(tileDBClient, "tileDBClient is null"); 58 | this.session = requireNonNull(session, "session is null"); 59 | this.split = requireNonNull(split, "split is null"); 60 | 61 | this.columnHandles = requireNonNull(columnHandles, "column handles is null"); 62 | ImmutableList.Builder types = ImmutableList.builder(); 63 | for (TileDBColumnHandle column : columnHandles) { 64 | types.add(column.getColumnType()); 65 | } 66 | this.columnTypes = types.build(); 67 | TileDBTable table = tileDBClient.getTable(session, split.getSchemaName(), split.getTableName()); 68 | requireNonNull(table, "Unable to fetch table " + split.getSchemaName() + "." + split.getTableName() + " for record set"); 69 | try { 70 | String key = getEncryptionKey(session); 71 | BigInteger timestamp = getTimestamp(session); 72 | 73 | if (key != null) { 74 | localCtx = tileDBClient.buildContext(session, EncryptionType.TILEDB_AES_256_GCM, key); 75 | } 76 | else { 77 | localCtx = tileDBClient.buildContext(session, null, null); 78 | } 79 | 80 | if (timestamp != null) { 81 | array = new Array(localCtx, table.getURI().toString(), TILEDB_READ, timestamp); 82 | } 83 | else { 84 | array = new Array(localCtx, table.getURI().toString(), TILEDB_READ); 85 | } 86 | query = new Query(array, TILEDB_READ); 87 | if (array.getSchema().isSparse()) { 88 | query.setLayout(Layout.TILEDB_UNORDERED); 89 | } 90 | else { 91 | query.setLayout(array.getSchema().getTileOrder()); 92 | } 93 | } 94 | catch (TileDBError tileDBError) { 95 | throw new TrinoException(TILEDB_RECORD_SET_ERROR, tileDBError); 96 | } 97 | } 98 | 99 | @Override 100 | public List getColumnTypes() 101 | { 102 | return columnTypes; 103 | } 104 | 105 | @Override 106 | public RecordCursor cursor() 107 | { 108 | try { 109 | return new TileDBRecordCursor(tileDBClient, session, split, columnHandles, array, query, localCtx); 110 | } 111 | catch (TileDBError tileDBError) { 112 | tileDBError.printStackTrace(); 113 | } 114 | return null; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/io/trino/plugin/tiledb/TileDBRecordSetProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package io.trino.plugin.tiledb; 15 | 16 | import com.google.common.collect.ImmutableList; 17 | import io.trino.spi.connector.ColumnHandle; 18 | import io.trino.spi.connector.ConnectorRecordSetProvider; 19 | import io.trino.spi.connector.ConnectorSession; 20 | import io.trino.spi.connector.ConnectorSplit; 21 | import io.trino.spi.connector.ConnectorTableHandle; 22 | import io.trino.spi.connector.ConnectorTransactionHandle; 23 | import io.trino.spi.connector.RecordSet; 24 | import jakarta.inject.Inject; 25 | 26 | import java.util.List; 27 | 28 | import static java.util.Objects.requireNonNull; 29 | 30 | /** 31 | * TileDBRecordSetProvider allows a record set to be fetched. Since tiledb does not have transactions, it simply 32 | * passes the split, column handles and a client instance to TileDBRecordSet 33 | */ 34 | public class TileDBRecordSetProvider 35 | implements ConnectorRecordSetProvider 36 | { 37 | //private final String connectorId; 38 | private final TileDBClient tileDBClient; 39 | 40 | @Inject 41 | public TileDBRecordSetProvider(TileDBClient tileDBClient) 42 | { 43 | this.tileDBClient = requireNonNull(tileDBClient, "tileDBClient is null"); 44 | } 45 | 46 | @Override 47 | public RecordSet getRecordSet(ConnectorTransactionHandle transactionHandle, ConnectorSession session, ConnectorSplit split, ConnectorTableHandle table, List columns) 48 | { 49 | TileDBSplit tileDBSplit = (TileDBSplit) split; 50 | 51 | ImmutableList.Builder handles = ImmutableList.builder(); 52 | for (ColumnHandle handle : columns) { 53 | handles.add((TileDBColumnHandle) handle); 54 | } 55 | 56 | return new TileDBRecordSet(tileDBClient, session, tileDBSplit, handles.build()); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/io/trino/plugin/tiledb/TileDBSessionProperties.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package io.trino.plugin.tiledb; 15 | 16 | import com.google.common.collect.ImmutableList; 17 | import io.trino.spi.connector.ConnectorSession; 18 | import io.trino.spi.session.PropertyMetadata; 19 | import jakarta.inject.Inject; 20 | 21 | import java.math.BigInteger; 22 | import java.util.List; 23 | 24 | import static io.trino.spi.session.PropertyMetadata.booleanProperty; 25 | import static io.trino.spi.session.PropertyMetadata.integerProperty; 26 | import static io.trino.spi.session.PropertyMetadata.longProperty; 27 | import static io.trino.spi.session.PropertyMetadata.stringProperty; 28 | 29 | public final class TileDBSessionProperties 30 | { 31 | private static final String READ_BUFFER_SIZE = "read_buffer_size"; 32 | private static final String WRITE_BUFFER_SIZE = "write_buffer_size"; 33 | private static final String AWS_ACCESS_KEY_ID = "aws_access_key_id"; 34 | private static final String AWS_SECRET_ACCESS_KEY = "aws_secret_access_key"; 35 | private static final String SPLITS = "splits"; 36 | private static final String SPLIT_ONLY_PREDICATES = "split_only_predicates"; 37 | private static final String ENABLE_STATS = "enable_stats"; 38 | private static final String TILEDB_CONFIG = "tiledb_config"; 39 | private static final String ENCRYPTION_KEY = "encryption_key"; 40 | private static final String TIMESTAMP = "timestamp"; 41 | 42 | private final List> sessionProperties; 43 | 44 | @Inject 45 | public TileDBSessionProperties(TileDBConfig tileDBConfig, TileDBClient client) 46 | { 47 | sessionProperties = ImmutableList.of( 48 | integerProperty( 49 | READ_BUFFER_SIZE, 50 | "Size in bytes to use for read buffers", 51 | tileDBConfig.getReadBufferSize(), 52 | false), 53 | integerProperty( 54 | WRITE_BUFFER_SIZE, 55 | "Size in bytes to use for read buffers", 56 | tileDBConfig.getWriteBufferSize(), 57 | false), 58 | stringProperty( 59 | AWS_ACCESS_KEY_ID, 60 | "AWS Access Key ID used for s3 access", 61 | tileDBConfig.getAwsAccessKeyId(), 62 | false), 63 | stringProperty( 64 | AWS_SECRET_ACCESS_KEY, 65 | "AWS Secret Access Key used for s3 access", 66 | tileDBConfig.getAwsSecretAccessKey(), 67 | false), 68 | integerProperty( 69 | SPLITS, 70 | "Maximum number of splits to perform, this is best attempt. Connector will always split on dimension ranges, even if this is more than configured max splits. -1 value means split to match number of worker nodes", 71 | -1, 72 | false), 73 | booleanProperty( 74 | SPLIT_ONLY_PREDICATES, 75 | "Split only on query provided predicates", 76 | false, 77 | false), 78 | booleanProperty( 79 | ENABLE_STATS, 80 | "Enable tiledb and connector stats per query", 81 | false, 82 | false), 83 | stringProperty( 84 | TILEDB_CONFIG, 85 | "TileDB config parameters in key1=value1,key2=value2 form", 86 | tileDBConfig.getTileDBConfig(), 87 | false), 88 | stringProperty( 89 | ENCRYPTION_KEY, 90 | "TileDB array encryption key", 91 | null, 92 | false), 93 | longProperty( 94 | TIMESTAMP, 95 | "TileDB array timestamp, for the time travelling feature", 96 | null, 97 | false)); 98 | } 99 | 100 | public List> getSessionProperties() 101 | { 102 | return sessionProperties; 103 | } 104 | 105 | public static int getReadBufferSize(ConnectorSession session) 106 | { 107 | return session.getProperty(READ_BUFFER_SIZE, Integer.class); 108 | } 109 | 110 | public static int getWriteBufferSize(ConnectorSession session) 111 | { 112 | return session.getProperty(WRITE_BUFFER_SIZE, Integer.class); 113 | } 114 | 115 | public static String getAwsAccessKeyId(ConnectorSession session) 116 | { 117 | return session.getProperty(AWS_ACCESS_KEY_ID, String.class); 118 | } 119 | 120 | public static String getAwsSecretAccessKey(ConnectorSession session) 121 | { 122 | return session.getProperty(AWS_SECRET_ACCESS_KEY, String.class); 123 | } 124 | 125 | public static int getSplits(ConnectorSession session) 126 | { 127 | return session.getProperty(SPLITS, Integer.class); 128 | } 129 | 130 | public static boolean getSplitOnlyPredicates(ConnectorSession session) 131 | { 132 | return session.getProperty(SPLIT_ONLY_PREDICATES, Boolean.class); 133 | } 134 | 135 | public static boolean getEnableStats(ConnectorSession session) 136 | { 137 | return session.getProperty(ENABLE_STATS, Boolean.class); 138 | } 139 | 140 | public static String getTileDBConfig(ConnectorSession session) 141 | { 142 | return session.getProperty(TILEDB_CONFIG, String.class); 143 | } 144 | 145 | public static String getEncryptionKey(ConnectorSession session) 146 | { 147 | return session.getProperty(ENCRYPTION_KEY, String.class); 148 | } 149 | 150 | public static BigInteger getTimestamp(ConnectorSession session) 151 | { 152 | if (session.getProperty(TIMESTAMP, Long.class) != null) { 153 | return BigInteger.valueOf(Long.valueOf(session.getProperty(TIMESTAMP, Long.class))); 154 | } 155 | else { 156 | return null; 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/main/java/io/trino/plugin/tiledb/TileDBSplit.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package io.trino.plugin.tiledb; 15 | 16 | import com.fasterxml.jackson.annotation.JsonCreator; 17 | import com.fasterxml.jackson.annotation.JsonProperty; 18 | import com.google.common.collect.ImmutableList; 19 | import io.trino.spi.HostAddress; 20 | import io.trino.spi.connector.ColumnHandle; 21 | import io.trino.spi.connector.ConnectorSplit; 22 | import io.trino.spi.predicate.TupleDomain; 23 | 24 | import java.util.List; 25 | 26 | import static java.util.Objects.requireNonNull; 27 | 28 | /** 29 | * TileDBSplit represents a split, which is a chunk of data to be processed in parallel from a query. Currently this 30 | * is not implemented so only a single split is used per query. 31 | */ 32 | public class TileDBSplit 33 | implements ConnectorSplit 34 | { 35 | private final String connectorId; 36 | private final String schemaName; 37 | private final String tableName; 38 | private final TupleDomain tupleDomain; 39 | 40 | @JsonCreator 41 | public TileDBSplit( 42 | @JsonProperty("connectorId") String connectorId, 43 | @JsonProperty("schemaName") String schemaName, 44 | @JsonProperty("tableName") String tableName, 45 | @JsonProperty("tupleDomain") TupleDomain tupleDomain) 46 | { 47 | this.connectorId = requireNonNull(connectorId, "connector id is null"); 48 | this.schemaName = schemaName; 49 | this.tableName = requireNonNull(tableName, "table name is null"); 50 | this.tupleDomain = requireNonNull(tupleDomain, "tupleDomain is null"); 51 | } 52 | 53 | @JsonProperty 54 | public String getConnectorId() 55 | { 56 | return connectorId; 57 | } 58 | 59 | @JsonProperty 60 | public String getSchemaName() 61 | { 62 | return schemaName; 63 | } 64 | 65 | @JsonProperty 66 | public String getTableName() 67 | { 68 | return tableName; 69 | } 70 | 71 | @JsonProperty 72 | public TupleDomain getTupleDomain() 73 | { 74 | return tupleDomain; 75 | } 76 | 77 | @Override 78 | public boolean isRemotelyAccessible() 79 | { 80 | return true; 81 | } 82 | 83 | @Override 84 | public List getAddresses() 85 | { 86 | return ImmutableList.of(); 87 | } 88 | 89 | @Override 90 | public Object getInfo() 91 | { 92 | return this; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/io/trino/plugin/tiledb/TileDBTable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package io.trino.plugin.tiledb; 15 | 16 | import com.fasterxml.jackson.annotation.JsonCreator; 17 | import com.fasterxml.jackson.annotation.JsonProperty; 18 | import com.google.common.collect.ImmutableList; 19 | import io.tiledb.java.api.ArraySchema; 20 | import io.tiledb.java.api.Attribute; 21 | import io.tiledb.java.api.Context; 22 | import io.tiledb.java.api.Datatype; 23 | import io.tiledb.java.api.Dimension; 24 | import io.tiledb.java.api.Domain; 25 | import io.tiledb.java.api.TileDBError; 26 | import io.trino.spi.connector.ColumnMetadata; 27 | import io.trino.spi.type.Type; 28 | import io.trino.spi.type.VarcharType; 29 | 30 | import java.net.URI; 31 | import java.util.List; 32 | import java.util.Optional; 33 | 34 | import static com.google.common.base.Preconditions.checkArgument; 35 | import static com.google.common.base.Strings.isNullOrEmpty; 36 | import static io.trino.plugin.tiledb.TileDBModule.prestoTypeFromTileDBType; 37 | import static java.lang.Math.toIntExact; 38 | import static java.util.Objects.requireNonNull; 39 | 40 | public class TileDBTable 41 | { 42 | private final String schema; 43 | private final String name; 44 | private final List columns; 45 | private final List columnsMetadata; 46 | private final URI uri; 47 | 48 | @JsonCreator 49 | public TileDBTable( 50 | @JsonProperty("name") String schema, 51 | @JsonProperty("name") String name, 52 | @JsonProperty("uri") URI uri, 53 | Context ctx) 54 | throws TileDBError 55 | { 56 | checkArgument(!isNullOrEmpty(schema), "schema is null or is empty"); 57 | checkArgument(!isNullOrEmpty(name), "name is null or is empty"); 58 | this.schema = requireNonNull(schema, "schema is null"); 59 | this.name = requireNonNull(name, "name is null"); 60 | this.uri = requireNonNull(uri, "uri is null"); 61 | 62 | ImmutableList.Builder columnsMetadata = ImmutableList.builder(); 63 | ImmutableList.Builder columns = ImmutableList.builder(); 64 | ArraySchema s = new ArraySchema(ctx, uri.toString()); 65 | Domain domain = s.getDomain(); 66 | // Add dimensions as a column 67 | for (Dimension dimension : domain.getDimensions()) { 68 | Type type = prestoTypeFromTileDBType(dimension.getType()); 69 | ColumnMetadata columnMetadata = ColumnMetadata.builder() 70 | .setName(dimension.getName()) 71 | .setType(type) 72 | .setExtraInfo(Optional.empty()) 73 | .setComment(Optional.of("Dimension")) 74 | .setHidden(false) 75 | .build(); 76 | 77 | columnsMetadata.add(columnMetadata); 78 | columns.add(new TileDBColumn(dimension.getName(), type, dimension.getType(), dimension.isVar(), true, false)); 79 | dimension.close(); 80 | } 81 | // Add attribute as a column 82 | for (long i = 0; i < s.getAttributeNum(); i++) { 83 | Attribute attribute = s.getAttribute(i); 84 | Type type = prestoTypeFromTileDBType(attribute.getType()); 85 | if (attribute.getType() == Datatype.TILEDB_CHAR && !attribute.isVar()) { 86 | type = VarcharType.createVarcharType(toIntExact(attribute.getCellValNum())); 87 | } 88 | ColumnMetadata columnMetadata = ColumnMetadata.builder() 89 | .setName(attribute.getName()) 90 | .setType(type) 91 | .setExtraInfo(Optional.empty()) 92 | .setComment(Optional.of("Attribute")) 93 | .setHidden(false) 94 | .build(); 95 | columnsMetadata.add(columnMetadata); 96 | columns.add(new TileDBColumn(attribute.getName(), type, attribute.getType(), attribute.isVar(), false, attribute.getNullable())); 97 | attribute.close(); 98 | } 99 | domain.close(); 100 | s.close(); 101 | this.columnsMetadata = columnsMetadata.build(); 102 | this.columns = columns.build(); 103 | } 104 | 105 | @JsonProperty 106 | public String getName() 107 | { 108 | return name; 109 | } 110 | 111 | @JsonProperty 112 | public String getSchema() 113 | { 114 | return schema; 115 | } 116 | 117 | @JsonProperty 118 | public List getColumns() 119 | { 120 | return columns; 121 | } 122 | 123 | @JsonProperty 124 | public URI getURI() 125 | { 126 | return uri; 127 | } 128 | 129 | public List getColumnsMetadata() 130 | { 131 | return columnsMetadata; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/io/trino/plugin/tiledb/TileDBTableHandle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package io.trino.plugin.tiledb; 15 | 16 | import com.fasterxml.jackson.annotation.JsonCreator; 17 | import com.fasterxml.jackson.annotation.JsonProperty; 18 | import com.google.common.base.Joiner; 19 | import io.trino.spi.connector.ConnectorTableHandle; 20 | import io.trino.spi.connector.SchemaTableName; 21 | 22 | import java.util.Objects; 23 | 24 | import static java.util.Objects.requireNonNull; 25 | 26 | /** 27 | * TileDBTableHandle implements the minimum interface for a ConnectorTableHandle it currently holds just the 28 | * table name and schema name. The table name is the last path component of an array uri and schema is hard coded 29 | * to tiledb for now. 30 | */ 31 | public final class TileDBTableHandle 32 | implements ConnectorTableHandle 33 | { 34 | private final String connectorId; 35 | private final String schemaName; 36 | private final String tableName; 37 | private final String uri; 38 | 39 | @JsonCreator 40 | public TileDBTableHandle( 41 | @JsonProperty("connectorId") String connectorId, 42 | @JsonProperty("schemaName") String schemaName, 43 | @JsonProperty("tableName") String tableName, 44 | @JsonProperty("uri") String uri) 45 | { 46 | this.connectorId = requireNonNull(connectorId, "connectorId is null"); 47 | this.schemaName = requireNonNull(schemaName, "schemaName is null"); 48 | this.tableName = requireNonNull(tableName, "tableName is null"); 49 | this.uri = requireNonNull(uri, "uri is null"); 50 | } 51 | 52 | @JsonProperty 53 | public String getConnectorId() 54 | { 55 | return connectorId; 56 | } 57 | 58 | @JsonProperty 59 | public String getSchemaName() 60 | { 61 | return schemaName; 62 | } 63 | 64 | @JsonProperty 65 | public String getTableName() 66 | { 67 | return tableName; 68 | } 69 | 70 | @JsonProperty 71 | public String getURI() 72 | { 73 | return uri; 74 | } 75 | 76 | public SchemaTableName toSchemaTableName() 77 | { 78 | return new SchemaTableName(schemaName, tableName); 79 | } 80 | 81 | @Override 82 | public int hashCode() 83 | { 84 | return Objects.hash(connectorId, schemaName, tableName, uri); 85 | } 86 | 87 | @Override 88 | public boolean equals(Object obj) 89 | { 90 | if (this == obj) { 91 | return true; 92 | } 93 | if ((obj == null) || (getClass() != obj.getClass())) { 94 | return false; 95 | } 96 | 97 | TileDBTableHandle other = (TileDBTableHandle) obj; 98 | return Objects.equals(this.connectorId, other.connectorId) && 99 | Objects.equals(this.schemaName, other.schemaName) && 100 | Objects.equals(this.tableName, other.tableName) && 101 | Objects.equals(this.uri, other.uri); 102 | } 103 | 104 | @Override 105 | public String toString() 106 | { 107 | return Joiner.on(":").join(connectorId, schemaName, tableName); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/io/trino/plugin/tiledb/TileDBTableProperties.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package io.trino.plugin.tiledb; 15 | 16 | import com.google.common.collect.ImmutableList; 17 | import io.trino.spi.session.PropertyMetadata; 18 | import io.trino.spi.type.TypeManager; 19 | import jakarta.inject.Inject; 20 | 21 | import java.util.List; 22 | import java.util.Map; 23 | 24 | import static io.trino.spi.session.PropertyMetadata.longProperty; 25 | import static io.trino.spi.session.PropertyMetadata.stringProperty; 26 | 27 | public class TileDBTableProperties 28 | { 29 | public static final String URI = "uri"; 30 | public static final String CellOrder = "cell_order"; 31 | public static final String TileOrder = "tile_order"; 32 | public static final String Capacity = "capacity"; 33 | public static final String OffsetsFilterList = "offsets_filter_list"; 34 | public static final String EncryptionKey = "encryption_key"; 35 | private final List> tableProperties; 36 | 37 | @Inject 38 | public TileDBTableProperties(TypeManager typeManager, TileDBConfig config) 39 | { 40 | tableProperties = ImmutableList.of( 41 | stringProperty( 42 | URI, 43 | "URI for array to be created at", 44 | null, 45 | false), 46 | stringProperty( 47 | CellOrder, 48 | "Cell order for array [ROW_MAJOR, COL_MAJOR, GLOBAL_ORDER]", 49 | "ROW_MAJOR", 50 | false), 51 | stringProperty( 52 | TileOrder, 53 | "Tile order for array [ROW_MAJOR, COL_MAJOR, GLOBAL_ORDER]", 54 | "ROW_MAJOR", 55 | false), 56 | longProperty( 57 | Capacity, 58 | "Capacity of sparse array", 59 | 10000L, 60 | false), 61 | stringProperty( 62 | OffsetsFilterList, 63 | "The offsets filter list", 64 | "", 65 | false), 66 | stringProperty( 67 | EncryptionKey, 68 | "The offsets filter list", 69 | null, 70 | false)); 71 | } 72 | 73 | public List> getTableProperties() 74 | { 75 | return tableProperties; 76 | } 77 | 78 | public static String getUri(Map tableProperties) 79 | { 80 | return (String) tableProperties.get(URI); 81 | } 82 | 83 | public static String getCellOrder(Map tableProperties) 84 | { 85 | return (String) tableProperties.get(CellOrder); 86 | } 87 | 88 | public static String getTileOrder(Map tableProperties) 89 | { 90 | return (String) tableProperties.get(TileOrder); 91 | } 92 | 93 | public static Long getCapacity(Map tableProperties) 94 | { 95 | return (Long) tableProperties.get(Capacity); 96 | } 97 | 98 | public static String getOffsetsFilterList(Map tableProperties) 99 | { 100 | if (tableProperties.containsKey(OffsetsFilterList)) { 101 | return (String) tableProperties.get(OffsetsFilterList); 102 | } 103 | else { 104 | return ""; 105 | } 106 | } 107 | 108 | public static String getEncryptionKey(Map tableProperties) 109 | { 110 | return (String) tableProperties.get(EncryptionKey); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/io/trino/plugin/tiledb/TileDBTransactionHandle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package io.trino.plugin.tiledb; 15 | 16 | import com.fasterxml.jackson.annotation.JsonCreator; 17 | import com.fasterxml.jackson.annotation.JsonProperty; 18 | import io.trino.spi.connector.ConnectorTransactionHandle; 19 | 20 | import java.util.Objects; 21 | import java.util.UUID; 22 | 23 | import static com.google.common.base.MoreObjects.toStringHelper; 24 | import static java.util.Objects.requireNonNull; 25 | 26 | /** 27 | * TileDBTransactionHandle is a simple transaction handler by declaring a single enum type for a transaction. 28 | * Since tiledb has no transactions we use "instance level" transactions, which I believe just means a transaction 29 | * in presto is mapped to a connector instance's lifespan. 30 | */ 31 | public class TileDBTransactionHandle 32 | implements ConnectorTransactionHandle 33 | { 34 | private final UUID uuid; 35 | 36 | public TileDBTransactionHandle() 37 | { 38 | this(UUID.randomUUID()); 39 | } 40 | 41 | @JsonCreator 42 | public TileDBTransactionHandle(@JsonProperty("uuid") UUID uuid) 43 | { 44 | this.uuid = requireNonNull(uuid, "uuid is null"); 45 | } 46 | 47 | @JsonProperty 48 | public UUID getUuid() 49 | { 50 | return uuid; 51 | } 52 | 53 | @Override 54 | public boolean equals(Object obj) 55 | { 56 | if (this == obj) { 57 | return true; 58 | } 59 | if ((obj == null) || (getClass() != obj.getClass())) { 60 | return false; 61 | } 62 | TileDBTransactionHandle other = (TileDBTransactionHandle) obj; 63 | return Objects.equals(uuid, other.uuid); 64 | } 65 | 66 | @Override 67 | public int hashCode() 68 | { 69 | return Objects.hash(uuid); 70 | } 71 | 72 | @Override 73 | public String toString() 74 | { 75 | return toStringHelper(this) 76 | .add("uuid", uuid) 77 | .toString(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/io/trino/plugin/tiledb/util/StringPartitioner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package io.trino.plugin.tiledb.util; 16 | 17 | import io.tiledb.java.api.Pair; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | public class StringPartitioner 23 | { 24 | private int minChar; 25 | private int maxChar; 26 | 27 | public StringPartitioner() 28 | { 29 | minChar = 0; 30 | maxChar = 127; 31 | } 32 | 33 | public StringPartitioner(int minChar, int maxChar) 34 | { 35 | this.minChar = minChar; 36 | this.maxChar = maxChar; 37 | } 38 | 39 | /** 40 | * Computes the next possible string by incrementing the least possible character. The next string 41 | * is limited to the input string length. For example, if the maximum character provided is 'z', 42 | * then a string like `zzz` cannot be further incremented and it will be returned as it is. On the 43 | * other hand, the next string of `aaz` will be `aba`, the next of `a` `b` and so on. 44 | * 45 | *

The example above is assumes the a-z alphabet for simplicity. In practice, the next string 46 | * depends on the min/max chars defined by the user in the StringPartitioner constructor. The 47 | * default values are the min and max ASCII characters. 48 | * 49 | * @param str The input string 50 | * @return The next string 51 | */ 52 | public String nextStr(String str) 53 | { 54 | int strLen = str.length(); 55 | 56 | char[] charArray = str.toCharArray(); 57 | 58 | int currentCharIdx = strLen - 1; 59 | 60 | while (currentCharIdx > 0 && charArray[currentCharIdx] == maxChar) { 61 | charArray[currentCharIdx] = (char) minChar; 62 | --currentCharIdx; 63 | } 64 | 65 | if (charArray[currentCharIdx] < maxChar) { 66 | charArray[currentCharIdx]++; 67 | } 68 | 69 | return new String(charArray); 70 | } 71 | 72 | /** 73 | * Computes a the n-th next string 74 | * 75 | * @param str The input string 76 | * @param n The n value 77 | * @return The n-th next string 78 | */ 79 | public String nextStr(String str, long n) 80 | { 81 | for (int i = 0; i < n; ++i) { 82 | str = this.nextStr(str); 83 | } 84 | 85 | return str; 86 | } 87 | 88 | /** 89 | * Computes the previous possible string by reducing the least possible character by one. 90 | * For example, if the minimum character provided is 'a', then a string like `aaa` cannot be further reduced 91 | * and it will be returned as it is. On the other hand, the next previous of `abc` will be `abb`, 92 | * the previous of `b` will be `a` and so on. 93 | * 94 | *

The example above is assumes the a-z alphabet for simplicity. In practice, the next string 95 | * depends on the min/max chars defined by the user in the StringPartitioner constructor. The 96 | * default values are the min and max ASCII characters. 97 | * 98 | * @param str The input string 99 | * @return The next string 100 | */ 101 | public String previousStr(String str) 102 | { 103 | int strLen = str.length(); 104 | 105 | char[] charArray = str.toCharArray(); 106 | 107 | int currentCharIdx = strLen - 1; 108 | 109 | while (currentCharIdx > 0 && charArray[currentCharIdx] == minChar) { 110 | charArray[currentCharIdx] = (char) minChar; 111 | --currentCharIdx; 112 | } 113 | 114 | if (charArray[currentCharIdx] > minChar) { 115 | charArray[currentCharIdx]--; 116 | } 117 | 118 | return new String(charArray); 119 | } 120 | 121 | /** 122 | * Returns the distance between two strings. By distance, we mean the maximum possible strings 123 | * that can be created between a1 and a2 in lexicographical order. 124 | * 125 | * @param a1 The left bound 126 | * @param a2 The right bound 127 | * @return The number of possible strings 128 | */ 129 | public int distance(String a1, String a2) 130 | { 131 | String tmp = a1; 132 | int dist = 0; 133 | while (tmp.compareTo(a2) < 0) { 134 | tmp = this.nextStr(tmp); 135 | ++dist; 136 | } 137 | 138 | return dist; 139 | } 140 | 141 | /** 142 | * Adds extra chars to the input string 143 | * 144 | * @param str The input string 145 | * @param c The char to be added to the end of the string 146 | * @param n The occurrences of c to be added 147 | * @return The new string 148 | */ 149 | public static String addExtraChars(String str, char c, int n) 150 | { 151 | char[] newStr = new char[str.length() + n]; 152 | int idx = 0; 153 | 154 | for (char character : str.toCharArray()) { 155 | newStr[idx++] = character; 156 | } 157 | 158 | while (idx < str.length() + n) { 159 | newStr[idx++] = c; 160 | } 161 | 162 | return new String(newStr); 163 | } 164 | 165 | /** 166 | * Splits a String range into equi-width sub-ranges 167 | * 168 | * @param start The left bound 169 | * @param end The right bound 170 | * @param partitions The number of partitions 171 | * @return A list of pairs with the sub-range bounds 172 | */ 173 | public List> split(String start, String end, int partitions) 174 | { 175 | String fixedStart = start; 176 | long width = this.distance(fixedStart, end); 177 | 178 | while (width / partitions < 1) { 179 | // In this case we need to add extra characters to the left string 180 | fixedStart = addExtraChars(fixedStart, (char) minChar, 1); 181 | width = this.distance(fixedStart, end); 182 | } 183 | 184 | long partitionWidth = (width / partitions); 185 | 186 | String tmpStart = fixedStart; 187 | String tmpEnd; 188 | 189 | List> list = new ArrayList<>(); 190 | 191 | for (int i = 0; i < partitions; ++i) { 192 | if (i == partitions - 1) { 193 | tmpEnd = end; 194 | } 195 | else { 196 | tmpEnd = nextStr(tmpStart, partitionWidth); 197 | } 198 | 199 | if (i == 0) { 200 | tmpStart = start; 201 | } 202 | 203 | if (tmpEnd.compareTo(end) > 0) { 204 | tmpEnd = end; 205 | list.add(new Pair(new String(tmpStart.toCharArray()), new String(tmpEnd.toCharArray()))); 206 | break; 207 | } 208 | 209 | list.add(new Pair(new String(tmpStart.toCharArray()), new String(tmpEnd.toCharArray()))); 210 | tmpStart = nextStr(tmpEnd, 1); 211 | } 212 | 213 | return list; 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /src/main/java/io/trino/plugin/tiledb/util/Util.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package io.trino.plugin.tiledb.util; 15 | 16 | import io.tiledb.java.api.BitShuffleFilter; 17 | import io.tiledb.java.api.BitWidthReductionFilter; 18 | import io.tiledb.java.api.ByteShuffleFilter; 19 | import io.tiledb.java.api.Bzip2Filter; 20 | import io.tiledb.java.api.Context; 21 | import io.tiledb.java.api.Datatype; 22 | import io.tiledb.java.api.Dimension; 23 | import io.tiledb.java.api.Domain; 24 | import io.tiledb.java.api.DoubleDeltaFilter; 25 | import io.tiledb.java.api.Filter; 26 | import io.tiledb.java.api.FilterList; 27 | import io.tiledb.java.api.GzipFilter; 28 | import io.tiledb.java.api.LZ4Filter; 29 | import io.tiledb.java.api.NoneFilter; 30 | import io.tiledb.java.api.Pair; 31 | import io.tiledb.java.api.TileDBError; 32 | import io.tiledb.java.api.ZstdFilter; 33 | 34 | import java.util.ArrayList; 35 | import java.util.Arrays; 36 | import java.util.List; 37 | import java.util.Optional; 38 | import java.util.regex.Matcher; 39 | import java.util.regex.Pattern; 40 | 41 | public class Util 42 | { 43 | private Util() {} 44 | 45 | /** 46 | * The String Partitioner 47 | */ 48 | private static final StringPartitioner stringPartitioner = new StringPartitioner(); 49 | 50 | public static Dimension toDimension(Context localCtx, String dimName, Datatype type, Domain domain, Long extent, 51 | Long lowerBound, Long upperBound) 52 | throws TileDBError 53 | { 54 | Class classType = type.javaClass(); 55 | switch (type) { 56 | case TILEDB_INT8: 57 | if (extent > Byte.MAX_VALUE) { 58 | extent = 10000L; 59 | } 60 | if (upperBound > Byte.MAX_VALUE - extent) { 61 | upperBound = (long) Byte.MAX_VALUE - extent; 62 | } 63 | else if (upperBound < Byte.MIN_VALUE) { 64 | upperBound = (long) Byte.MIN_VALUE + extent; 65 | } 66 | if (lowerBound > Byte.MAX_VALUE) { 67 | lowerBound = (long) Byte.MAX_VALUE - extent; 68 | } 69 | else if (lowerBound < Byte.MIN_VALUE) { 70 | lowerBound = (long) Byte.MIN_VALUE; 71 | } 72 | return new Dimension(localCtx, dimName, classType, new Pair(lowerBound.byteValue(), upperBound.byteValue()), extent.byteValue()); 73 | case TILEDB_INT16: 74 | if (extent > Short.MAX_VALUE) { 75 | extent = 10000L; 76 | } 77 | if (upperBound > Short.MAX_VALUE - extent) { 78 | upperBound = (long) Short.MAX_VALUE - extent; 79 | } 80 | else if (upperBound < Short.MIN_VALUE) { 81 | upperBound = (long) Short.MIN_VALUE + extent; 82 | } 83 | if (lowerBound > Short.MAX_VALUE) { 84 | lowerBound = (long) Short.MAX_VALUE - extent; 85 | } 86 | else if (lowerBound < Short.MIN_VALUE) { 87 | lowerBound = (long) Short.MIN_VALUE; 88 | } 89 | return new Dimension(localCtx, dimName, classType, new Pair(lowerBound.shortValue(), upperBound.shortValue()), extent.shortValue()); 90 | case TILEDB_INT32: 91 | if (extent > Integer.MAX_VALUE) { 92 | extent = 10000L; 93 | } 94 | if (upperBound > Integer.MAX_VALUE - extent) { 95 | upperBound = (long) Integer.MAX_VALUE - extent; 96 | } 97 | else if (upperBound < Integer.MIN_VALUE) { 98 | upperBound = (long) Integer.MIN_VALUE + extent; 99 | } 100 | if (lowerBound > Integer.MAX_VALUE) { 101 | lowerBound = (long) Integer.MAX_VALUE - extent; 102 | } 103 | else if (lowerBound < Integer.MIN_VALUE) { 104 | lowerBound = (long) Integer.MIN_VALUE; 105 | } 106 | return new Dimension(localCtx, dimName, classType, new Pair(lowerBound.intValue(), upperBound.intValue()), extent.intValue()); 107 | case TILEDB_DATETIME_AS: 108 | case TILEDB_DATETIME_FS: 109 | case TILEDB_DATETIME_PS: 110 | case TILEDB_DATETIME_NS: 111 | case TILEDB_DATETIME_US: 112 | case TILEDB_DATETIME_MS: 113 | case TILEDB_DATETIME_SEC: 114 | case TILEDB_DATETIME_MIN: 115 | case TILEDB_DATETIME_HR: 116 | case TILEDB_DATETIME_DAY: 117 | case TILEDB_DATETIME_WEEK: 118 | case TILEDB_DATETIME_MONTH: 119 | case TILEDB_DATETIME_YEAR: 120 | case TILEDB_INT64: 121 | if (upperBound > Long.MAX_VALUE - extent) { 122 | upperBound = (long) Long.MAX_VALUE - extent; 123 | } 124 | return new Dimension(localCtx, dimName, type, new Pair(lowerBound, upperBound), extent); 125 | case TILEDB_FLOAT32: 126 | if (upperBound > Float.MAX_VALUE - extent) { 127 | upperBound = (long) Float.MAX_VALUE - extent; 128 | } 129 | else if (upperBound < Float.MIN_VALUE) { 130 | upperBound = (long) Float.MIN_VALUE + extent; 131 | } 132 | if (lowerBound > Float.MAX_VALUE) { 133 | lowerBound = (long) Float.MAX_VALUE - extent; 134 | } 135 | else if (lowerBound < Float.MIN_VALUE) { 136 | lowerBound = (long) Float.MIN_VALUE; 137 | } 138 | if (extent > Float.MAX_VALUE) { 139 | extent = (long) Float.MAX_VALUE; 140 | } 141 | return new Dimension(localCtx, dimName, classType, new Pair(lowerBound.floatValue(), upperBound.floatValue()), extent.floatValue()); 142 | case TILEDB_FLOAT64: 143 | if (upperBound > Double.MAX_VALUE - extent) { 144 | upperBound = (long) Double.MAX_VALUE - extent; 145 | } 146 | else if (upperBound < Double.MIN_VALUE) { 147 | upperBound = (long) Double.MIN_VALUE + extent; 148 | } 149 | if (lowerBound > Double.MAX_VALUE) { 150 | lowerBound = (long) Double.MAX_VALUE - extent; 151 | } 152 | else if (lowerBound < Double.MIN_VALUE) { 153 | lowerBound = (long) Double.MIN_VALUE; 154 | } 155 | if (extent > Double.MAX_VALUE) { 156 | extent = (long) Double.MAX_VALUE; 157 | } 158 | return new Dimension(localCtx, dimName, classType, new Pair(lowerBound.doubleValue(), upperBound.doubleValue()), extent.doubleValue()); 159 | case TILEDB_STRING_ASCII: 160 | return new Dimension(localCtx, dimName, type, null, null); 161 | default: 162 | throw new TileDBError(String.format("Invalid dimension datatype %s, must be one of [TINYINT, SMALLINT, INTEGER, BIGINT, REAL, DOUBLE]", type.toString())); 163 | } 164 | } 165 | 166 | /** 167 | * Parses a comma-separated filter list and returns a list with key-value pairs, 168 | * where the key is the filter name (e.g. bzip2) and the value the filter's value 169 | * (e.g. -1) 170 | * 171 | * @param csvList The csv list 172 | * @return the parsed filter list 173 | * @throws IllegalArgumentException Exception 174 | */ 175 | public static Optional>> tryParseFilterList(String csvList) 176 | throws IllegalArgumentException 177 | { 178 | // filter lists are in the form "(filter, option), (filter, option), etc.") 179 | List> filterResults = new ArrayList<>(); 180 | // String[] splitVals = csvList.split("\\s*,\\s*"); 181 | Pattern filterListRegex = Pattern.compile("\\((.*?)\\)"); 182 | Matcher filterListMatcher = filterListRegex.matcher(csvList); 183 | while (filterListMatcher.find()) { 184 | String filterString = filterListMatcher.group(1); 185 | String[] filterPair = filterString.split("\\s*,\\s*"); 186 | // remove parens 187 | String filterName = filterPair[0]; 188 | List validFilters = Arrays.asList(new String[]{"GZIP", "ZSTD", "LZ4", "RLE", "BZIP2", 189 | "DOUBLE_DELTA", "BIT_WIDTH_REDUCTION", "BITSHUFFLE", "BYTESHUFFLE", "POSITIVE_DELTA"}); 190 | 191 | if (!validFilters.contains(filterName.toUpperCase())) { 192 | throw new IllegalArgumentException("Unknown TileDB filter string value: " + filterName); 193 | } 194 | Integer filterOption = -1; 195 | if (filterPair.length == 2) { 196 | // remove parens 197 | String filterOptionStr = filterPair[1]; 198 | try { 199 | filterOption = Integer.parseInt(filterOptionStr); 200 | } 201 | catch (NumberFormatException err) { 202 | throw new IllegalArgumentException( 203 | "Cannot parse filter option value for " + filterName + ": " + filterOptionStr); 204 | } 205 | } 206 | filterResults.add(new Pair<>(filterName, filterOption)); 207 | } 208 | if (filterResults.isEmpty()) { 209 | return Optional.empty(); 210 | } 211 | return Optional.of(filterResults); 212 | } 213 | 214 | /** 215 | * Returns a TileDB FilterList 216 | * 217 | * @param ctx The context 218 | * @param filterListDesc The filter pairs list extracted with the tryParseFilterList method 219 | * @return The FilterList instance 220 | * @throws TileDBError TileDBError 221 | */ 222 | public static FilterList createTileDBFilterList( 223 | Context ctx, List> filterListDesc) 224 | throws TileDBError 225 | { 226 | FilterList filterList = new FilterList(ctx); 227 | try { 228 | for (Pair filterDesc : filterListDesc) { 229 | String filterName = filterDesc.getFirst(); 230 | Integer filterOption = filterDesc.getSecond(); 231 | if (filterName.equalsIgnoreCase("NONE")) { 232 | try (Filter filter = new NoneFilter(ctx)) { 233 | filterList.addFilter(filter); 234 | } 235 | } 236 | else if (filterName.equalsIgnoreCase("GZIP")) { 237 | try (Filter filter = new GzipFilter(ctx, filterOption)) { 238 | filterList.addFilter(filter); 239 | } 240 | } 241 | else if (filterName.equalsIgnoreCase("ZSTD")) { 242 | try (Filter filter = new ZstdFilter(ctx, filterOption)) { 243 | filterList.addFilter(filter); 244 | } 245 | } 246 | else if (filterName.equalsIgnoreCase("LZ4")) { 247 | try (Filter filter = new LZ4Filter(ctx, filterOption)) { 248 | filterList.addFilter(filter); 249 | } 250 | } 251 | else if (filterName.equalsIgnoreCase("RLE")) { 252 | try (Filter filter = new LZ4Filter(ctx, filterOption)) { 253 | filterList.addFilter(filter); 254 | } 255 | } 256 | else if (filterName.equalsIgnoreCase("BZIP2")) { 257 | try (Filter filter = new Bzip2Filter(ctx, filterOption)) { 258 | filterList.addFilter(filter); 259 | } 260 | } 261 | else if (filterName.equalsIgnoreCase("DOUBLE_DELTA")) { 262 | try (Filter filter = new DoubleDeltaFilter(ctx, filterOption)) { 263 | filterList.addFilter(filter); 264 | } 265 | } 266 | else if (filterName.equalsIgnoreCase("BIT_WIDTH_REDUCTION")) { 267 | try (Filter filter = new BitWidthReductionFilter(ctx, filterOption)) { 268 | filterList.addFilter(filter); 269 | } 270 | } 271 | else if (filterName.equalsIgnoreCase("BITSHUFFLE")) { 272 | try (Filter filter = new BitShuffleFilter(ctx)) { 273 | filterList.addFilter(filter); 274 | } 275 | } 276 | else if (filterName.equalsIgnoreCase("BYTESHUFFLE")) { 277 | try (Filter filter = new ByteShuffleFilter(ctx)) { 278 | filterList.addFilter(filter); 279 | } 280 | } 281 | else if (filterName.equalsIgnoreCase("POSITIVE_DELTA")) { 282 | try (Filter filter = new ByteShuffleFilter(ctx)) { 283 | filterList.addFilter(filter); 284 | } 285 | } 286 | } 287 | } 288 | catch (TileDBError err) { 289 | filterList.close(); 290 | throw err; 291 | } 292 | return filterList; 293 | } 294 | 295 | /** 296 | * Returns v + eps, where eps is the smallest value for the datatype such that v + eps > v. 297 | */ 298 | public static Object addEpsilon(Object value, Datatype type) 299 | throws TileDBError 300 | { 301 | switch (type) { 302 | case TILEDB_CHAR: 303 | case TILEDB_INT8: 304 | return ((byte) value) < Byte.MAX_VALUE ? ((byte) value + 1) : value; 305 | case TILEDB_INT16: 306 | return ((short) value) < Short.MAX_VALUE ? ((short) value + 1) : value; 307 | case TILEDB_INT32: 308 | return ((int) value) < Integer.MAX_VALUE ? ((int) value + 1) : value; 309 | case TILEDB_DATETIME_AS: 310 | case TILEDB_DATETIME_FS: 311 | case TILEDB_DATETIME_PS: 312 | case TILEDB_DATETIME_NS: 313 | case TILEDB_DATETIME_US: 314 | case TILEDB_DATETIME_MS: 315 | case TILEDB_DATETIME_SEC: 316 | case TILEDB_DATETIME_MIN: 317 | case TILEDB_DATETIME_HR: 318 | case TILEDB_DATETIME_DAY: 319 | case TILEDB_DATETIME_WEEK: 320 | case TILEDB_DATETIME_MONTH: 321 | case TILEDB_DATETIME_YEAR: 322 | case TILEDB_INT64: 323 | return ((long) value) < Long.MAX_VALUE ? ((long) value + 1) : value; 324 | case TILEDB_UINT8: 325 | return ((short) value) < ((short) Byte.MAX_VALUE + 1) ? ((short) value + 1) : value; 326 | case TILEDB_UINT16: 327 | return ((int) value) < ((int) Short.MAX_VALUE + 1) ? ((int) value + 1) : value; 328 | case TILEDB_UINT32: 329 | return ((long) value) < ((long) Integer.MAX_VALUE + 1) ? ((long) value + 1) : value; 330 | case TILEDB_UINT64: 331 | return ((long) value) < ((long) Integer.MAX_VALUE + 1) ? ((long) value + 1) : value; 332 | case TILEDB_FLOAT32: 333 | return ((float) value) < Float.MAX_VALUE ? Math.nextUp((float) value) : value; 334 | case TILEDB_FLOAT64: 335 | return ((double) value) < Double.MAX_VALUE ? Math.nextUp((double) value) : value; 336 | case TILEDB_STRING_ASCII: 337 | return stringPartitioner.nextStr((String) value); 338 | default: 339 | throw new TileDBError("Unsupported TileDB Datatype enum: " + type); 340 | } 341 | } 342 | 343 | /** 344 | * Returns v - eps, where eps is the smallest value for the datatype such that v - eps < v. 345 | */ 346 | public static Object subtractEpsilon(Object value, Datatype type) 347 | throws TileDBError 348 | { 349 | switch (type) { 350 | case TILEDB_CHAR: 351 | case TILEDB_INT8: 352 | return ((byte) value) > Byte.MIN_VALUE ? ((byte) value - 1) : value; 353 | case TILEDB_INT16: 354 | return ((short) value) > Short.MIN_VALUE ? ((short) value - 1) : value; 355 | case TILEDB_INT32: 356 | return ((int) value) > Integer.MIN_VALUE ? ((int) value - 1) : value; 357 | case TILEDB_INT64: 358 | return ((long) value) > Long.MIN_VALUE ? ((long) value - 1) : value; 359 | case TILEDB_UINT8: 360 | return ((short) value) > ((short) Byte.MIN_VALUE - 1) ? ((short) value - 1) : value; 361 | case TILEDB_UINT16: 362 | return ((int) value) > ((int) Short.MIN_VALUE - 1) ? ((int) value - 1) : value; 363 | case TILEDB_UINT32: 364 | return ((long) value) > ((long) Integer.MIN_VALUE - 1) ? ((long) value - 1) : value; 365 | case TILEDB_DATETIME_AS: 366 | case TILEDB_DATETIME_FS: 367 | case TILEDB_DATETIME_PS: 368 | case TILEDB_DATETIME_NS: 369 | case TILEDB_DATETIME_US: 370 | case TILEDB_DATETIME_MS: 371 | case TILEDB_DATETIME_SEC: 372 | case TILEDB_DATETIME_MIN: 373 | case TILEDB_DATETIME_HR: 374 | case TILEDB_DATETIME_DAY: 375 | case TILEDB_DATETIME_WEEK: 376 | case TILEDB_DATETIME_MONTH: 377 | case TILEDB_DATETIME_YEAR: 378 | case TILEDB_UINT64: 379 | return ((long) value) > ((long) Integer.MIN_VALUE - 1) ? ((long) value - 1) : value; 380 | case TILEDB_FLOAT32: 381 | return ((float) value) > Float.MIN_VALUE ? Math.nextDown((float) value) : value; 382 | case TILEDB_FLOAT64: 383 | return ((double) value) > Double.MIN_VALUE ? Math.nextDown((double) value) : value; 384 | case TILEDB_STRING_ASCII: 385 | return stringPartitioner.previousStr((String) value); 386 | default: 387 | throw new TileDBError("Unsupported TileDB Datatype enum: " + type); 388 | } 389 | } 390 | } 391 | -------------------------------------------------------------------------------- /src/main/resources/services/io.trino.spi.Plugin: -------------------------------------------------------------------------------- 1 | io.trino.plugin.tiledb.TileDBPlugin -------------------------------------------------------------------------------- /src/modernizer/violations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | java/lang/Class.newInstance:()Ljava/lang/Object; 5 | 1.1 6 | Prefer Class.getConstructor().newInstance() 7 | 8 | 9 | 10 | java/lang/String.toLowerCase:()Ljava/lang/String; 11 | 1.1 12 | Prefer String.toLowerCase(java.util.Locale) 13 | 14 | 15 | 16 | com/google/common/primitives/Ints.checkedCast:(J)I 17 | 1.8 18 | Prefer Math.toIntExact(long) 19 | 20 | 21 | 22 | org/testng/Assert.assertEquals:(Ljava/lang/Iterable;Ljava/lang/Iterable;)V 23 | 1.8 24 | Use io.trino.testing.assertions.Assert.assertEquals due to TestNG #543 25 | 26 | 27 | 28 | org/testng/Assert.assertEquals:(Ljava/lang/Iterable;Ljava/lang/Iterable;Ljava/lang/String;)V 29 | 1.8 30 | Use io.trino.testing.assertions.Assert.assertEquals due to TestNG #543 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/test/java/io/trino/plugin/tiledb/TestTileDBConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package io.trino.plugin.tiledb; 15 | 16 | import com.google.common.collect.ImmutableMap; 17 | import org.testng.annotations.Test; 18 | 19 | import java.util.Map; 20 | 21 | import static io.airlift.configuration.testing.ConfigAssertions.assertFullMapping; 22 | import static io.airlift.configuration.testing.ConfigAssertions.assertRecordedDefaults; 23 | import static io.airlift.configuration.testing.ConfigAssertions.recordDefaults; 24 | 25 | public class TestTileDBConfig 26 | { 27 | @Test 28 | public void testDefaults() 29 | { 30 | assertRecordedDefaults(recordDefaults(TileDBConfig.class) 31 | .setArrayURIs(null) 32 | .setReadBufferSize(10485760) 33 | .setWriteBufferSize(1048576) 34 | .setAwsAccessKeyId(null) 35 | .setAwsSecretAccessKey(null) 36 | .setTileDBConfig(null)); 37 | } 38 | 39 | @Test 40 | public void testExplicitPropertyMappings() 41 | { 42 | Map properties = new ImmutableMap.Builder() 43 | .put("array-uris", "file:///test") 44 | .put("read-buffer-size", "10") 45 | .put("write-buffer-size", "100") 46 | .put("aws-access-key-id", "") 47 | .put("aws-secret-access-key", "") 48 | .put("tiledb-config", "").build(); 49 | 50 | TileDBConfig expected = null; 51 | expected = new TileDBConfig() 52 | .setArrayURIs("file:///test") 53 | .setReadBufferSize(10) 54 | .setWriteBufferSize(100) 55 | .setAwsAccessKeyId("") 56 | .setAwsSecretAccessKey("") 57 | .setTileDBConfig(""); 58 | assertFullMapping(properties, expected); 59 | } 60 | 61 | @Test 62 | public void testExplicitPropertyMappingsWithMultipleArrays() 63 | { 64 | Map properties = new ImmutableMap.Builder() 65 | .put("array-uris", "file:///test,s3:///test-bucket/array") 66 | .put("read-buffer-size", "1048576") 67 | .put("write-buffer-size", "104857") 68 | .put("aws-access-key-id", "123") 69 | .put("aws-secret-access-key", "abc") 70 | .put("tiledb-config", "key1=value1,key2=value2").build(); 71 | 72 | TileDBConfig expected = null; 73 | expected = new TileDBConfig() 74 | .setArrayURIs("file:///test,s3:///test-bucket/array") 75 | .setReadBufferSize(1048576) 76 | .setWriteBufferSize(104857) 77 | .setAwsAccessKeyId("123") 78 | .setAwsSecretAccessKey("abc") 79 | .setTileDBConfig("key1=value1,key2=value2"); 80 | assertFullMapping(properties, expected); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/test/java/io/trino/plugin/tiledb/TestTileDBIntegrationSmokeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package io.trino.plugin.tiledb; 15 | 16 | import io.airlift.tpch.TpchTable; 17 | import io.tiledb.java.api.Context; 18 | import io.tiledb.java.api.TileDBError; 19 | import io.tiledb.java.api.TileDBObject; 20 | import io.trino.spi.TrinoException; 21 | import io.trino.spi.type.VarcharType; 22 | import io.trino.testing.AbstractTestQueries; 23 | import io.trino.testing.MaterializedResult; 24 | import io.trino.testing.QueryRunner; 25 | import org.gaul.modernizer_maven_annotations.SuppressModernizer; 26 | import org.testng.annotations.AfterClass; 27 | import org.testng.annotations.Test; 28 | 29 | import static io.trino.plugin.tiledb.TileDBErrorCode.TILEDB_UNEXPECTED_ERROR; 30 | import static io.trino.spi.type.VarcharType.VARCHAR; 31 | import static org.assertj.core.api.Assertions.assertThat; 32 | import static org.testng.Assert.assertEquals; 33 | 34 | @SuppressModernizer 35 | public class TestTileDBIntegrationSmokeTest 36 | extends AbstractTestQueries 37 | { 38 | public TestTileDBIntegrationSmokeTest() 39 | { 40 | super(); 41 | } 42 | 43 | protected boolean isParameterizedVarcharSupported() 44 | { 45 | return false; 46 | } 47 | 48 | @Override 49 | protected QueryRunner createQueryRunner() 50 | throws Exception 51 | { 52 | return TileDBQueryRunner.createTileDBQueryRunner(); 53 | } 54 | 55 | @Override 56 | public void testShowColumns() 57 | { 58 | MaterializedResult actual = this.computeActual("SHOW COLUMNS FROM orders"); 59 | MaterializedResult expected = MaterializedResult.resultBuilder(this.getSession(), VarcharType.VARCHAR, VarcharType.VARCHAR, VarcharType.VARCHAR, VarcharType.VARCHAR) 60 | .row("orderkey", "bigint", "", "Dimension") 61 | .row("custkey", "bigint", "", "Dimension") 62 | .row("orderstatus", "varchar(1)", "", "Attribute") 63 | .row("totalprice", "double", "", "Attribute") 64 | .row("orderdate", "date", "", "Attribute") 65 | .row("orderpriority", "varchar", "", "Attribute") 66 | .row("clerk", "varchar", "", "Attribute") 67 | .row("shippriority", "integer", "", "Attribute") 68 | .row(new Object[]{"comment", "varchar", "", "Attribute"}).build(); 69 | assertThat(actual.equals(expected)); 70 | } 71 | 72 | @Test 73 | public void testDescribeTable() 74 | { 75 | MaterializedResult actualColumns = computeActual("DESC orders").toTestTypes(); 76 | MaterializedResult expectedColumns = getExpectedOrdersTableDescription(isParameterizedVarcharSupported()); 77 | assertEquals(actualColumns, expectedColumns); 78 | } 79 | 80 | @Test 81 | public void testShowCreateTable() 82 | { 83 | assertThat((String) computeActual("SHOW CREATE TABLE orders").getOnlyValue()) 84 | // If the connector reports additional column properties, the expected value needs to be adjusted in the test subclass 85 | .matches("CREATE TABLE tiledb.tiledb.orders \\Q(\n" + 86 | " orderkey bigint COMMENT 'Dimension',\n" + 87 | " custkey bigint COMMENT 'Dimension',\n" + 88 | " orderstatus varchar(1) COMMENT 'Attribute',\n" + 89 | " totalprice double COMMENT 'Attribute',\n" + 90 | " orderdate date COMMENT 'Attribute',\n" + 91 | " orderpriority varchar COMMENT 'Attribute',\n" + 92 | " clerk varchar COMMENT 'Attribute',\n" + 93 | " shippriority integer COMMENT 'Attribute',\n" + 94 | " comment varchar COMMENT 'Attribute'\n" + 95 | ")"); 96 | } 97 | 98 | @AfterClass(alwaysRun = true) 99 | public final void destroy() 100 | throws TileDBError 101 | { 102 | Context context = new Context(); 103 | for (TpchTable table : TpchTable.getTables()) { 104 | try { 105 | TileDBObject.remove(context, table.getTableName()); 106 | } 107 | catch (TileDBError tileDBError) { 108 | throw new TrinoException(TILEDB_UNEXPECTED_ERROR, tileDBError); 109 | } 110 | } 111 | context.close(); 112 | } 113 | 114 | private MaterializedResult getExpectedOrdersTableDescription(boolean parametrizedVarchar) 115 | { 116 | if (parametrizedVarchar) { 117 | return MaterializedResult.resultBuilder(getQueryRunner().getDefaultSession(), VARCHAR, VARCHAR, VARCHAR, VARCHAR) 118 | .row("orderkey", "bigint", "", "Dimension") 119 | .row("custkey", "bigint", "", "Dimension") 120 | .row("orderstatus", "varchar(1)", "", "Attribute") 121 | .row("totalprice", "double", "", "Attribute") 122 | .row("orderdate", "date", "", "Attribute") 123 | .row("orderpriority", "varchar(15)", "", "Attribute") 124 | .row("clerk", "varchar(15)", "", "Attribute") 125 | .row("shippriority", "integer", "", "Attribute") 126 | .row("comment", "varchar(79)", "", "Attribute") 127 | .build(); 128 | } 129 | else { 130 | return MaterializedResult.resultBuilder(getQueryRunner().getDefaultSession(), VARCHAR, VARCHAR, VARCHAR, VARCHAR) 131 | .row("orderkey", "bigint", "", "Dimension") 132 | .row("custkey", "bigint", "", "Dimension") 133 | .row("orderstatus", "varchar(1)", "", "Attribute") 134 | .row("totalprice", "double", "", "Attribute") 135 | .row("orderdate", "date", "", "Attribute") 136 | .row("orderpriority", "varchar", "", "Attribute") 137 | .row("clerk", "varchar", "", "Attribute") 138 | .row("shippriority", "integer", "", "Attribute") 139 | .row("comment", "varchar", "", "Attribute") 140 | .build(); 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/test/java/io/trino/plugin/tiledb/TestTileDBPlugin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package io.trino.plugin.tiledb; 15 | 16 | import com.google.common.collect.ImmutableMap; 17 | import io.trino.spi.Plugin; 18 | import io.trino.spi.connector.ConnectorFactory; 19 | import io.trino.testing.TestingConnectorContext; 20 | import org.testng.annotations.Test; 21 | 22 | import java.nio.file.Path; 23 | import java.nio.file.Paths; 24 | 25 | import static com.google.common.collect.Iterables.getOnlyElement; 26 | 27 | public class TestTileDBPlugin 28 | { 29 | @Test 30 | public void testCreateConnector() 31 | { 32 | Plugin plugin = new TileDBPlugin(); 33 | ConnectorFactory factory = getOnlyElement(plugin.getConnectorFactories()); 34 | Path testArray = Paths.get("src", "test", "resources", "tiledb_arrays", "dense_global"); 35 | factory.create("test", ImmutableMap.of("array-uris", testArray.toString()), new TestingConnectorContext()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/io/trino/plugin/tiledb/TileDBQueryRunner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package io.trino.plugin.tiledb; 15 | 16 | import com.google.common.collect.ImmutableList; 17 | import com.google.common.collect.ImmutableMap; 18 | import io.airlift.log.Logger; 19 | import io.airlift.tpch.TpchTable; 20 | import io.tiledb.java.api.Context; 21 | import io.tiledb.java.api.TileDBError; 22 | import io.trino.Session; 23 | import io.trino.metadata.QualifiedObjectName; 24 | import io.trino.plugin.tpch.TpchPlugin; 25 | import io.trino.testing.DistributedQueryRunner; 26 | import io.trino.testing.QueryRunner; 27 | import org.intellij.lang.annotations.Language; 28 | 29 | import java.io.File; 30 | import java.util.ArrayList; 31 | import java.util.List; 32 | import java.util.Map; 33 | 34 | import static io.airlift.testing.Closeables.closeAllSuppress; 35 | import static io.airlift.units.Duration.nanosSince; 36 | import static io.tiledb.java.api.Array.exists; 37 | import static io.trino.plugin.tpch.TpchMetadata.TINY_SCHEMA_NAME; 38 | import static io.trino.testing.TestingSession.testSessionBuilder; 39 | import static java.lang.String.format; 40 | import static java.util.concurrent.TimeUnit.SECONDS; 41 | 42 | public final class TileDBQueryRunner 43 | { 44 | private static final Logger LOG = Logger.get(TileDBQueryRunner.class); 45 | private static final String TPCH_SCHEMA = "tiledb"; 46 | private static boolean tpchLoaded; 47 | 48 | private TileDBQueryRunner() {} 49 | 50 | public static DistributedQueryRunner createTileDBQueryRunner() 51 | throws Exception 52 | { 53 | return createTileDBQueryRunner(TpchTable.getTables(), ImmutableMap.of(), ImmutableMap.of(), new Context()); 54 | } 55 | 56 | public static DistributedQueryRunner createTileDBQueryRunner(ImmutableMap sessionProperties) 57 | throws Exception 58 | { 59 | return createTileDBQueryRunner(TpchTable.getTables(), sessionProperties, ImmutableMap.of(), new Context()); 60 | } 61 | 62 | public static DistributedQueryRunner createTileDBQueryRunner(TpchTable... tables) 63 | throws Exception 64 | { 65 | return createTileDBQueryRunner(ImmutableList.copyOf(tables), ImmutableMap.of(), ImmutableMap.of(), 66 | new Context()); 67 | } 68 | 69 | public static DistributedQueryRunner createTileDBQueryRunner(Iterable> tables, 70 | ImmutableMap sessionSystemProperties, 71 | Map extraProperties, Context ctx) 72 | throws Exception 73 | { 74 | Session.SessionBuilder sessionBuilder = testSessionBuilder() 75 | .setCatalog("tiledb") 76 | .setSchema("tiledb"); 77 | 78 | for (Map.Entry entry : sessionSystemProperties.entrySet()) { 79 | sessionBuilder.setSystemProperty(entry.getKey(), entry.getValue()); 80 | } 81 | 82 | Session session = sessionBuilder.build(); 83 | 84 | DistributedQueryRunner queryRunner = DistributedQueryRunner.builder(session).setNodeCount(4) 85 | .setExtraProperties(extraProperties).build(); 86 | 87 | try { 88 | List existingTables = new ArrayList<>(); 89 | List> tablesToCopy = new ArrayList<>(); 90 | for (TpchTable table : tables) { 91 | if ((new File(table.getTableName())).exists()) { 92 | existingTables.add(table.getTableName()); 93 | } 94 | else { 95 | tablesToCopy.add(table); 96 | } 97 | } 98 | 99 | ImmutableMap.Builder propertiesBuilder = ImmutableMap.builder(); 100 | if (!existingTables.isEmpty()) { 101 | propertiesBuilder.put("array-uris", String.join(",", existingTables)); 102 | } 103 | 104 | Map properties = propertiesBuilder.build(); 105 | 106 | queryRunner.installPlugin(new TileDBPlugin()); 107 | queryRunner.createCatalog("tiledb", "tiledb", properties); 108 | 109 | queryRunner.installPlugin(new TpchPlugin()); 110 | queryRunner.createCatalog("tpch", "tpch", ImmutableMap.of()); 111 | 112 | if (!tpchLoaded) { 113 | copyTpchTables(queryRunner, "tpch", TINY_SCHEMA_NAME, createSession(), tablesToCopy, ctx); 114 | tpchLoaded = true; 115 | } 116 | 117 | return queryRunner; 118 | } 119 | catch (Exception e) { 120 | closeAllSuppress(e, queryRunner); 121 | throw e; 122 | } 123 | } 124 | 125 | private static void copyTpchTables( 126 | QueryRunner queryRunner, 127 | String sourceCatalog, 128 | String sourceSchema, 129 | Session session, 130 | Iterable> tables, 131 | Context ctx) 132 | throws TileDBError 133 | { 134 | LOG.info("Loading data from %s.%s...", sourceCatalog, sourceSchema); 135 | long startTime = System.nanoTime(); 136 | for (TpchTable table : tables) { 137 | // Build test tables if they do not exist. Normally we should always rebuild them but for testing 138 | // it is helpful to not destroy them 139 | if (!(new File(table.getTableName())).exists() || !exists(ctx, table.getTableName())) { 140 | copyTable(queryRunner, sourceCatalog, session, sourceSchema, table); 141 | } 142 | } 143 | LOG.info("Loading from %s.%s complete in %s", sourceCatalog, sourceSchema, nanosSince(startTime).toString(SECONDS)); 144 | } 145 | 146 | private static void copyTable( 147 | QueryRunner queryRunner, 148 | String catalog, 149 | Session session, 150 | String schema, 151 | TpchTable table) 152 | { 153 | QualifiedObjectName source = new QualifiedObjectName(catalog, schema, table.getTableName()); 154 | String target = table.getTableName(); 155 | 156 | @Language("SQL") 157 | String createSQL; 158 | String insertSQL; 159 | switch (target) { 160 | case "customer": 161 | createSQL = format("CREATE TABLE %s (\n" + 162 | " custkey bigint WITH (dimension=true),\n" + 163 | " name varchar(25),\n" + 164 | " address varchar(40),\n" + 165 | " nationkey bigint,\n" + 166 | " phone varchar(15),\n" + 167 | " acctbal double,\n" + 168 | " mktsegment varchar(10),\n" + 169 | " comment varchar(117)\n" + 170 | " ) WITH (uri = '%s')", target, target); 171 | insertSQL = format("INSERT INTO %s (custkey, name, nationkey, address, phone, acctbal, mktsegment, comment) SELECT custkey, name, nationkey, address, phone, acctbal, mktsegment, comment FROM %s", target, source); 172 | break; 173 | case "lineitem": 174 | createSQL = format("CREATE TABLE %s(\n" + 175 | " orderkey bigint WITH (dimension=true),\n" + 176 | " partkey bigint WITH (dimension=true),\n" + 177 | " suppkey bigint WITH (dimension=true),\n" + 178 | " linenumber bigint WITH (dimension=true),\n" + 179 | " quantity double,\n" + 180 | " extendedprice double,\n" + 181 | " discount double,\n" + 182 | " tax double,\n" + 183 | " returnflag varchar(1),\n" + 184 | " linestatus varchar(1),\n" + 185 | " shipdate date,\n" + 186 | " commitdate date,\n" + 187 | " receiptdate date,\n" + 188 | " shipinstruct varchar(25),\n" + 189 | " shipmode varchar(10),\n" + 190 | " comment varchar(44)\n" + 191 | " ) WITH (uri = '%s')", target, target); 192 | insertSQL = format("INSERT INTO %s (orderkey, partkey, suppkey, linenumber, quantity, extendedprice, discount, tax, returnflag, linestatus, shipdate, commitdate, receiptdate, shipinstruct, shipmode, comment) SELECT orderkey, partkey, suppkey, linenumber, quantity, extendedprice, discount, tax, returnflag, linestatus, shipdate, commitdate, receiptdate, shipinstruct, shipmode, comment FROM %s", target, source); 193 | break; 194 | case "nation": 195 | createSQL = format("CREATE TABLE %s(\n" + 196 | " nationkey bigint WITH (dimension=true),\n" + 197 | " name varchar(25),\n" + 198 | " regionkey bigint,\n" + 199 | " comment varchar(152)\n" + 200 | " ) WITH (uri = '%s')", target, target); 201 | insertSQL = format("INSERT INTO %s (nationkey, name, regionkey, comment) SELECT nationkey, name, regionkey, comment FROM %s", target, source); 202 | break; 203 | case "orders": 204 | createSQL = format("CREATE TABLE %s(\n" + 205 | " orderkey bigint WITH (dimension=true),\n" + 206 | " custkey bigint WITH (dimension=true),\n" + 207 | " orderstatus varchar(1),\n" + 208 | " totalprice double,\n" + 209 | " orderdate date,\n" + 210 | " orderpriority varchar(15),\n" + 211 | " clerk varchar(15),\n" + 212 | " shippriority integer,\n" + 213 | " comment varchar(79)\n" + 214 | " ) WITH (uri = '%s')", target, target); 215 | insertSQL = format("INSERT INTO %s (orderkey, custkey, orderstatus, totalprice, orderdate, orderpriority, clerk, shippriority, comment) SELECT orderkey, custkey, orderstatus, totalprice, orderdate, orderpriority, clerk, shippriority, comment FROM %s", target, source); 216 | break; 217 | case "part": 218 | createSQL = format("CREATE TABLE %s(\n" + 219 | " partkey bigint WITH (dimension=true),\n" + 220 | " name varchar(55),\n" + 221 | " mfgr varchar(25),\n" + 222 | " brand varchar(10),\n" + 223 | " type varchar(25),\n" + 224 | " size integer,\n" + 225 | " container varchar(10),\n" + 226 | " retailprice double,\n" + 227 | " comment varchar(23)\n" + 228 | " ) WITH (uri = '%s')", target, target); 229 | insertSQL = format("INSERT INTO %s (partkey, name, mfgr, brand, type, size, container, retailprice, comment) SELECT partkey, name, mfgr, brand, type, size, container, retailprice, comment FROM %s", target, source); 230 | break; 231 | case "partsupp": 232 | createSQL = format("CREATE TABLE %s(\n" + 233 | " partkey bigint WITH (dimension=true),\n" + 234 | " suppkey bigint WITH (dimension=true),\n" + 235 | " availqty integer,\n" + 236 | " supplycost double,\n" + 237 | " comment varchar(199)\n" + 238 | " ) WITH (uri = '%s')", target, target); 239 | insertSQL = format("INSERT INTO %s (partkey, suppkey, availqty, supplycost, comment) SELECT partkey, suppkey, availqty, supplycost, comment FROM %s", target, source); 240 | break; 241 | case "region": 242 | createSQL = format("CREATE TABLE %s(\n" + 243 | " regionkey bigint WITH (dimension=true),\n" + 244 | " name varchar(25),\n" + 245 | " comment varchar(152)\n" + 246 | " ) WITH (uri = '%s')", target, target); 247 | insertSQL = format("INSERT INTO %s (regionkey, name, comment) SELECT regionkey, name, comment FROM %s", target, source); 248 | break; 249 | case "supplier": 250 | createSQL = format("CREATE TABLE %s(\n" + 251 | " suppkey bigint WITH (dimension=true),\n" + 252 | " name varchar(25),\n" + 253 | " address varchar(40),\n" + 254 | " nationkey bigint,\n" + 255 | " phone varchar(15),\n" + 256 | " acctbal double,\n" + 257 | " comment varchar(101)\n" + 258 | " ) WITH (uri = '%s')", target, target); 259 | insertSQL = format("INSERT INTO %s (suppkey, name, address, nationkey, phone, acctbal, comment) SELECT suppkey, name, address, nationkey, phone, acctbal, comment FROM %s", target, source); 260 | break; 261 | default: 262 | createSQL = format("CREATE TABLE %s LIKE %s WITH (uri = '%s')", target, source, target); 263 | insertSQL = format("INSERT INTO %s SELECT * FROM %s", target, source); 264 | break; 265 | } 266 | 267 | LOG.info("Running create table for %s", target); 268 | LOG.info("%s", createSQL); 269 | queryRunner.execute(createSQL); 270 | LOG.info("Running import for %s", target); 271 | LOG.info("%s", insertSQL); 272 | long start = System.nanoTime(); 273 | long rows = queryRunner.execute(insertSQL).getUpdateCount().getAsLong(); 274 | LOG.info("Imported %s rows for %s in %s", rows, target, nanosSince(start)); 275 | } 276 | 277 | /*public static void main(String[] args) 278 | throws Exception 279 | { 280 | Logging.initialize(); 281 | DistributedQueryRunner queryRunner = createTileDBQueryRunner(ImmutableList.copyOf(ORDERS), ImmutableMap.of("http-server.http.port", "8080")); 282 | Thread.sleep(10); 283 | Logger log = Logger.get(TileDBQueryRunner.class); 284 | log.info("======== SERVER STARTED ========"); 285 | log.info("\n====\n%s\n====", queryRunner.getCoordinator().getBaseUrl()); 286 | }*/ 287 | 288 | public static Session createSession() 289 | { 290 | return testSessionBuilder() 291 | .setCatalog("tiledb") 292 | .setSchema(TPCH_SCHEMA) 293 | .build(); 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /src/test/resources/tiledb_arrays/dense_global/__array_schema.tdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TileDB-Inc/TileDB-Trino/09454705bfb1fb6313b1af6b07bb2d7bd31ec606/src/test/resources/tiledb_arrays/dense_global/__array_schema.tdb -------------------------------------------------------------------------------- /src/test/resources/tiledb_arrays/dense_global/__c2076894fd614aac824cfc6cc0e1b754_1538424663019/__fragment_metadata.tdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TileDB-Inc/TileDB-Trino/09454705bfb1fb6313b1af6b07bb2d7bd31ec606/src/test/resources/tiledb_arrays/dense_global/__c2076894fd614aac824cfc6cc0e1b754_1538424663019/__fragment_metadata.tdb -------------------------------------------------------------------------------- /src/test/resources/tiledb_arrays/dense_global/__c2076894fd614aac824cfc6cc0e1b754_1538424663019/a.tdb: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /src/test/resources/tiledb_arrays/dense_global/__lock.tdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TileDB-Inc/TileDB-Trino/09454705bfb1fb6313b1af6b07bb2d7bd31ec606/src/test/resources/tiledb_arrays/dense_global/__lock.tdb -------------------------------------------------------------------------------- /src/test/resources/tiledb_arrays/sparse_global/__5d43f0e2b2f34675abaa642ef0f6128e_1538424648583/__coords.tdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TileDB-Inc/TileDB-Trino/09454705bfb1fb6313b1af6b07bb2d7bd31ec606/src/test/resources/tiledb_arrays/sparse_global/__5d43f0e2b2f34675abaa642ef0f6128e_1538424648583/__coords.tdb -------------------------------------------------------------------------------- /src/test/resources/tiledb_arrays/sparse_global/__5d43f0e2b2f34675abaa642ef0f6128e_1538424648583/__fragment_metadata.tdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TileDB-Inc/TileDB-Trino/09454705bfb1fb6313b1af6b07bb2d7bd31ec606/src/test/resources/tiledb_arrays/sparse_global/__5d43f0e2b2f34675abaa642ef0f6128e_1538424648583/__fragment_metadata.tdb -------------------------------------------------------------------------------- /src/test/resources/tiledb_arrays/sparse_global/__5d43f0e2b2f34675abaa642ef0f6128e_1538424648583/a.tdb: -------------------------------------------------------------------------------- 1 |   -------------------------------------------------------------------------------- /src/test/resources/tiledb_arrays/sparse_global/__array_schema.tdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TileDB-Inc/TileDB-Trino/09454705bfb1fb6313b1af6b07bb2d7bd31ec606/src/test/resources/tiledb_arrays/sparse_global/__array_schema.tdb -------------------------------------------------------------------------------- /src/test/resources/tiledb_arrays/sparse_global/__lock.tdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TileDB-Inc/TileDB-Trino/09454705bfb1fb6313b1af6b07bb2d7bd31ec606/src/test/resources/tiledb_arrays/sparse_global/__lock.tdb --------------------------------------------------------------------------------