├── .github
└── workflows
│ └── maven.yml
├── .gitignore
├── .mvn
└── modernizer
│ └── violations.xml
├── DEVELOPMENT.md
├── Dockerfile
├── LICENSE
├── README.md
├── pom.xml
└── src
├── license
└── LICENSE-HEADER.txt
├── main
└── java
│ └── io
│ └── trino
│ └── plugin
│ └── db2
│ ├── DB2Client.java
│ ├── DB2ClientModule.java
│ ├── DB2Config.java
│ └── DB2Plugin.java
└── test
└── java
└── io
└── trino
└── plugin
└── db2
├── TestDB2Config.java
└── TestDB2Plugin.java
/.github/workflows/maven.yml:
--------------------------------------------------------------------------------
1 | name: Java CI
2 |
3 | on:
4 | push:
5 | paths-ignore:
6 | - '**.md'
7 | pull_request:
8 | paths-ignore:
9 | - '**.md'
10 |
11 | jobs:
12 | build:
13 | runs-on: ubuntu-latest
14 | strategy:
15 | fail-fast: false
16 | matrix:
17 | java-version:
18 | - 17
19 | env:
20 | MAVEN_SKIP_CHECKS_AND_DOCS: -Dair.check.skip-all=true -Dmaven.javadoc.skip=true
21 | MAVEN_FAST_INSTALL: -DskipTests -Dair.check.skip-all=true -Dmaven.javadoc.skip=true -B -q -T C1
22 | steps:
23 | - uses: actions/checkout@v1
24 |
25 | - uses: actions/setup-java@v1
26 | with:
27 | java-version: ${{ matrix.java-version }}
28 |
29 | - name: mvn install
30 | run: mvn install $MAVEN_FAST_INSTALL
31 |
32 | - name: mvn test
33 | run: mvn test -Dmaven.javadoc.skip=true -B
34 |
35 | - name: Release
36 | uses: softprops/action-gh-release@v1
37 | if: ${{ startsWith(github.ref, 'refs/tags/') && matrix.java-version == '17' }}
38 | with:
39 | files: |
40 | target/trino-db2-*.zip
41 | env:
42 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bin
2 | .classpath
3 | .project
4 | .settings/
5 | target/
6 | .metadata/
7 | .m2/
8 | .idea/
9 | *.iml
10 | .checkstyle
--------------------------------------------------------------------------------
/.mvn/modernizer/violations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/DEVELOPMENT.md:
--------------------------------------------------------------------------------
1 | # Development
2 |
3 | Developers should follow the [development guidelines](https://github.com/trinodb/trino/blob/81e9233eae31f2f3b425aa63a9daee8a00bc8344/DEVELOPMENT.md)
4 | from the Trino community.
5 |
6 | ## Build
7 |
8 | mvn clean install
9 |
10 | ## Release
11 | First update the `main` branch of this repo via PR process. Then, go to https://github.com/IBM/trino-db2/releases to draft your release. Configure the release to create a new branch named after the Trino version (e.g. 372). Before publishing the release, build the plugin locally with `mvn clean install`, and upload the resulting archive `target/trino-db2-[version].zip` to the release binaries. Then, you may click "publish release".
12 |
13 | ## Build a container image including this connector
14 |
15 | It uses multi-stage build and the trinodb container image from community as the
16 | base image.
17 |
18 | docker build -t "/" --build-arg BASE="trinodb/trino:" .
19 |
20 | ## Testing
21 |
22 | So far, it still depends on manual integration testing against an actual Db2
23 | database after coding a feature in Java code.
24 |
25 | I'd recommend following this process to iterate:
26 |
27 | 1. Clone this repo to a local development environment, e.g., IntelliJ IDEA. And
28 | keep code changes in a branch.
29 | 1. Run `mvn clean install` or the Maven tool window of the IDE to build this
30 | connector, while addressing errors/problems from build output.
31 | 1. Config a separate trinodb server with the built connector by creating a file
32 | named `docker-compose.yml`:
33 | ```YAML
34 | # docker-compose.yml
35 | version: "3.7"
36 |
37 | services:
38 | trino-coordinator:
39 | image: trinodb/trino:
40 | container_name: trino-coordinator
41 | volumes:
42 | - source: ./target/trino-db2-
43 | target: /usr/lib/trino/plugin/db2
44 | type: bind
45 | - source: ./conf/trino
46 | target: /etc/trino
47 | type: bind
48 | ports:
49 | - "8080:8080"
50 | ```
51 | 1. Make sure creating a connector config under `./conf/trino/catalog` to connect
52 | to an actual Db2 database. see details from [Connection Configuration](README.md#connection-configuration).
53 | 1. Start this local trinodb server by running `docker-compose up -d`
54 | 1. Connect to this local trinodb server via CLI to perform queries while
55 | capturing server output from container logs by running command `docker logs trino-coordinator`.
56 | 1. If changing Java code, delete this local trinodb server by running command
57 | `docker-compose down` then start from step 2.
58 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | ARG BASE
2 | FROM docker.io/library/maven:3.6.3-openjdk-11 AS builder
3 | WORKDIR /root/trino-db2
4 | COPY . /root/trino-db2
5 | ENV MAVEN_FAST_INSTALL="-DskipTests -Dair.check.skip-all=true -Dmaven.javadoc.skip=true -B -q -T C1"
6 | RUN mvn install $MAVEN_FAST_INSTALL
7 |
8 | FROM $BASE
9 | COPY --from=builder --chown=trino:trino /root/trino-db2/target/trino-db2-*/* /usr/lib/trino/plugin/db2/
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Trino Db2 connector [](https://github.com/IBM/trino-db2/actions)
2 |
3 | This is a plugin for [Trino](https://trino.io/) that allow you to use IBM Db2 Jdbc Connection
4 |
5 | Notice that it requires the connected database to be Db2 10 or Db2 LUW 9.7+ or greater versions to meet the precision need of the timestamp data type.
6 |
7 | See [DEVELOPMENT](DEVELOPMENT.md) for information on development process.
8 |
9 | **Limitation**
10 |
11 | It supports read/write Timestamp data type up to precision `9` while
12 | higher precision will not be preserved.
13 |
14 | ## Connection Configuration
15 |
16 | Create new properties file like `.properties` inside `etc/catalog` dir:
17 |
18 | connector.name=db2
19 | connection-url=jdbc:db2://ip:port/database
20 | connection-user=myuser
21 | connection-password=mypassword
22 |
23 | For a connection with SSL, uses following JDBC URL strings as `connection-url`:
24 |
25 | connection-url=jdbc:db2://ip:port/database:sslConnection=true;
26 |
27 | **Notices**:
28 | * the trailing semi-colon is required. Or it will throw SQLException `Invalid database URL syntax`.
29 | * You can use `db2.iam-api-key` to specify API Key instead of user/password if IAM authentication is supported.
30 |
31 | See the official document of Db2 JDBC details from the article [Connecting programmatically with JDBC](https://www.ibm.com/support/knowledgecenter/en/SS6NHC/com.ibm.swg.im.dashdb.doc/connecting/connect_connecting_jdbc_applications.html).
32 |
33 | ## Configuration Properties
34 |
35 | | Property Name | Description |
36 | |---------------|-------------|
37 | |`db2.varchar-max-length` | max length of VARCHAR type in a CREATE TABLE or ALTER TABLE command. default is `32672`|
38 | |`db2.iam-api-key` | API Key of IBM Cloud IAM. Use this when choosing IAM authentication instead of user/password |
39 |
40 | **Notice**: you may need to customize value of `db2.varchar-max-length` to `32592` when using Db2 warehouse.
41 |
42 | ## _Extra credentials_ Support
43 |
44 | Since release `324`, it starts to support the idea of _extra credentials_ where it allows trino client user to provide Db2 username and password as extra credentials that are passed directly to the backend Db2 server when running a query.
45 |
46 | 1. configure this for the Db2 connector catalog properties file:
47 | ```
48 | user-credential-name=db2_user
49 | password-credential-name=db2_password
50 | ```
51 | 2. passing credentials directly to Db2 server:
52 | ```
53 | trino --extra-credential db2_user=user1 --extra-credential db2_password=secret
54 | ```
55 |
56 | See details from [this answer](https://stackoverflow.com/a/58634432/914967).
57 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 |
7 | io.trino
8 | trino-root
9 | 396
10 |
11 |
12 | trino-db2
13 | Trino - Db2 Connector
14 | trino-plugin
15 |
16 |
17 |
18 | io.trino
19 | trino-base-jdbc
20 |
21 |
22 |
23 | io.airlift
24 | configuration
25 |
26 |
27 |
28 | com.google.guava
29 | guava
30 |
31 |
32 |
33 | com.google.inject
34 | guice
35 |
36 |
37 |
38 | com.ibm.db2.jcc
39 | db2jcc
40 | db2jcc4
41 |
42 |
43 |
44 | javax.inject
45 | javax.inject
46 |
47 |
48 |
49 | javax.validation
50 | validation-api
51 |
52 |
53 |
54 |
55 | io.trino
56 | trino-spi
57 | provided
58 |
59 |
60 |
61 | io.airlift
62 | slice
63 | provided
64 |
65 |
66 |
67 | com.fasterxml.jackson.core
68 | jackson-annotations
69 | provided
70 |
71 |
72 |
73 | org.openjdk.jol
74 | jol-core
75 | provided
76 |
77 |
78 |
79 |
80 | io.trino
81 | trino-main
82 | test
83 |
84 |
85 |
86 | io.trino
87 | trino-tests
88 | test
89 |
90 |
91 |
92 | io.trino
93 | trino-tpch
94 | test
95 |
96 |
97 |
98 | io.trino.tpch
99 | tpch
100 | test
101 |
102 |
103 |
104 | io.airlift
105 | testing
106 | test
107 |
108 |
109 |
110 | org.testng
111 | testng
112 | test
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/src/license/LICENSE-HEADER.txt:
--------------------------------------------------------------------------------
1 | Licensed under the Apache License, Version 2.0 (the "License");
2 | you may not use this file except in compliance with the License.
3 | You may obtain a copy of the License at
4 |
5 | http://www.apache.org/licenses/LICENSE-2.0
6 |
7 | Unless required by applicable law or agreed to in writing, software
8 | distributed under the License is distributed on an "AS IS" BASIS,
9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | See the License for the specific language governing permissions and
11 | limitations under the License.
12 |
--------------------------------------------------------------------------------
/src/main/java/io/trino/plugin/db2/DB2Client.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.db2;
15 |
16 | import io.trino.plugin.jdbc.BaseJdbcClient;
17 | import io.trino.plugin.jdbc.BaseJdbcConfig;
18 | import io.trino.plugin.jdbc.ColumnMapping;
19 | import io.trino.plugin.jdbc.ConnectionFactory;
20 | import io.trino.plugin.jdbc.JdbcSplit;
21 | import io.trino.plugin.jdbc.JdbcTypeHandle;
22 | import io.trino.plugin.jdbc.LongReadFunction;
23 | import io.trino.plugin.jdbc.ObjectReadFunction;
24 | import io.trino.plugin.jdbc.ObjectWriteFunction;
25 | import io.trino.plugin.jdbc.QueryBuilder;
26 | import io.trino.plugin.jdbc.WriteMapping;
27 | import io.trino.plugin.jdbc.mapping.IdentifierMapping;
28 | import io.trino.spi.TrinoException;
29 | import io.trino.spi.connector.ConnectorSession;
30 | import io.trino.spi.connector.SchemaTableName;
31 | import io.trino.spi.type.CharType;
32 | import io.trino.spi.type.DecimalType;
33 | import io.trino.spi.type.Decimals;
34 | import io.trino.spi.type.LongTimestamp;
35 | import io.trino.spi.type.TimestampType;
36 | import io.trino.spi.type.Type;
37 | import io.trino.spi.type.TypeManager;
38 | import io.trino.spi.type.VarcharType;
39 |
40 | import javax.inject.Inject;
41 |
42 | import java.sql.Connection;
43 | import java.sql.SQLException;
44 | import java.sql.Timestamp;
45 | import java.sql.Types;
46 | import java.util.List;
47 | import java.util.Optional;
48 |
49 | import static com.google.common.base.Preconditions.checkArgument;
50 | import static com.google.common.base.Verify.verify;
51 | import static io.trino.plugin.jdbc.JdbcErrorCode.JDBC_ERROR;
52 | import static io.trino.plugin.jdbc.StandardColumnMappings.bigintColumnMapping;
53 | import static io.trino.plugin.jdbc.StandardColumnMappings.bigintWriteFunction;
54 | import static io.trino.plugin.jdbc.StandardColumnMappings.booleanColumnMapping;
55 | import static io.trino.plugin.jdbc.StandardColumnMappings.booleanWriteFunction;
56 | import static io.trino.plugin.jdbc.StandardColumnMappings.charWriteFunction;
57 | import static io.trino.plugin.jdbc.StandardColumnMappings.dateColumnMappingUsingSqlDate;
58 | import static io.trino.plugin.jdbc.StandardColumnMappings.dateWriteFunctionUsingSqlDate;
59 | import static io.trino.plugin.jdbc.StandardColumnMappings.decimalColumnMapping;
60 | import static io.trino.plugin.jdbc.StandardColumnMappings.defaultCharColumnMapping;
61 | import static io.trino.plugin.jdbc.StandardColumnMappings.defaultVarcharColumnMapping;
62 | import static io.trino.plugin.jdbc.StandardColumnMappings.doubleColumnMapping;
63 | import static io.trino.plugin.jdbc.StandardColumnMappings.doubleWriteFunction;
64 | import static io.trino.plugin.jdbc.StandardColumnMappings.fromLongTrinoTimestamp;
65 | import static io.trino.plugin.jdbc.StandardColumnMappings.integerColumnMapping;
66 | import static io.trino.plugin.jdbc.StandardColumnMappings.integerWriteFunction;
67 | import static io.trino.plugin.jdbc.StandardColumnMappings.longDecimalWriteFunction;
68 | import static io.trino.plugin.jdbc.StandardColumnMappings.realColumnMapping;
69 | import static io.trino.plugin.jdbc.StandardColumnMappings.realWriteFunction;
70 | import static io.trino.plugin.jdbc.StandardColumnMappings.shortDecimalWriteFunction;
71 | import static io.trino.plugin.jdbc.StandardColumnMappings.smallintColumnMapping;
72 | import static io.trino.plugin.jdbc.StandardColumnMappings.smallintWriteFunction;
73 | import static io.trino.plugin.jdbc.StandardColumnMappings.timeColumnMappingUsingSqlTime;
74 | import static io.trino.plugin.jdbc.StandardColumnMappings.timestampWriteFunction;
75 | import static io.trino.plugin.jdbc.StandardColumnMappings.timestampWriteFunctionUsingSqlTimestamp;
76 | import static io.trino.plugin.jdbc.StandardColumnMappings.tinyintColumnMapping;
77 | import static io.trino.plugin.jdbc.StandardColumnMappings.tinyintWriteFunction;
78 | import static io.trino.plugin.jdbc.StandardColumnMappings.toLongTrinoTimestamp;
79 | import static io.trino.plugin.jdbc.StandardColumnMappings.toTrinoTimestamp;
80 | import static io.trino.plugin.jdbc.StandardColumnMappings.varbinaryColumnMapping;
81 | import static io.trino.plugin.jdbc.StandardColumnMappings.varbinaryWriteFunction;
82 | import static io.trino.plugin.jdbc.StandardColumnMappings.varcharColumnMapping;
83 | import static io.trino.plugin.jdbc.StandardColumnMappings.varcharWriteFunction;
84 | import static io.trino.plugin.jdbc.TypeHandlingJdbcSessionProperties.getUnsupportedTypeHandling;
85 | import static io.trino.plugin.jdbc.UnsupportedTypeHandling.CONVERT_TO_VARCHAR;
86 | import static io.trino.spi.StandardErrorCode.NOT_SUPPORTED;
87 | import static io.trino.spi.type.BigintType.BIGINT;
88 | import static io.trino.spi.type.BooleanType.BOOLEAN;
89 | import static io.trino.spi.type.DateType.DATE;
90 | import static io.trino.spi.type.DecimalType.createDecimalType;
91 | import static io.trino.spi.type.DoubleType.DOUBLE;
92 | import static io.trino.spi.type.IntegerType.INTEGER;
93 | import static io.trino.spi.type.RealType.REAL;
94 | import static io.trino.spi.type.SmallintType.SMALLINT;
95 | import static io.trino.spi.type.TimestampType.TIMESTAMP_MILLIS;
96 | import static io.trino.spi.type.TinyintType.TINYINT;
97 | import static io.trino.spi.type.VarbinaryType.VARBINARY;
98 | import static io.trino.spi.type.VarcharType.createUnboundedVarcharType;
99 | import static java.lang.Math.max;
100 | import static java.lang.String.format;
101 | import static java.util.Locale.ENGLISH;
102 | import static java.util.stream.Collectors.joining;
103 |
104 | public class DB2Client
105 | extends BaseJdbcClient
106 | {
107 | private final int varcharMaxLength;
108 | private static final int DB2_MAX_SUPPORTED_TIMESTAMP_PRECISION = 12;
109 | // java.util.LocalDateTime supports up to nanosecond precision
110 | private static final int MAX_LOCAL_DATE_TIME_PRECISION = 9;
111 | private static final String VARCHAR_FORMAT = "VARCHAR(%d)";
112 |
113 | @Inject
114 | public DB2Client(
115 | BaseJdbcConfig config,
116 | DB2Config db2config,
117 | ConnectionFactory connectionFactory,
118 | QueryBuilder queryBuilder,
119 | TypeManager typeManager,
120 | IdentifierMapping identifierMapping)
121 | throws SQLException
122 | {
123 | super(config, "\"", connectionFactory, queryBuilder, identifierMapping);
124 | this.varcharMaxLength = db2config.getVarcharMaxLength();
125 |
126 | // http://stackoverflow.com/questions/16910791/getting-error-code-4220-with-null-sql-state
127 | System.setProperty("db2.jcc.charsetDecoderEncoder", "3");
128 | }
129 |
130 | @Override
131 | public Connection getConnection(ConnectorSession session, JdbcSplit split)
132 | throws SQLException
133 | {
134 | Connection connection = super.getConnection(session, split);
135 | try {
136 | // TRANSACTION_READ_UNCOMMITTED = Uncommitted read
137 | // http://www.ibm.com/developerworks/data/library/techarticle/dm-0509schuetz/
138 | connection.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
139 | }
140 | catch (SQLException e) {
141 | connection.close();
142 | throw e;
143 | }
144 | return connection;
145 | }
146 |
147 | @Override
148 | public Optional toColumnMapping(ConnectorSession session, Connection connection, JdbcTypeHandle typeHandle)
149 | {
150 | Optional mapping = getForcedMappingToVarchar(typeHandle);
151 | if (mapping.isPresent()) {
152 | return mapping;
153 | }
154 |
155 | switch (typeHandle.getJdbcType()) {
156 | case Types.BIT:
157 | case Types.BOOLEAN:
158 | return Optional.of(booleanColumnMapping());
159 |
160 | case Types.TINYINT:
161 | return Optional.of(tinyintColumnMapping());
162 |
163 | case Types.SMALLINT:
164 | return Optional.of(smallintColumnMapping());
165 |
166 | case Types.INTEGER:
167 | return Optional.of(integerColumnMapping());
168 |
169 | case Types.BIGINT:
170 | return Optional.of(bigintColumnMapping());
171 |
172 | case Types.REAL:
173 | return Optional.of(realColumnMapping());
174 |
175 | case Types.FLOAT:
176 | case Types.DOUBLE:
177 | return Optional.of(doubleColumnMapping());
178 |
179 | case Types.NUMERIC:
180 | case Types.DECIMAL:
181 | int decimalDigits = typeHandle.getRequiredDecimalDigits();
182 | int precision = typeHandle.getRequiredColumnSize() + max(-decimalDigits, 0); // Map decimal(p, -s) (negative scale) to decimal(p+s, 0).
183 | if (precision > Decimals.MAX_PRECISION) {
184 | break;
185 | }
186 | return Optional.of(decimalColumnMapping(createDecimalType(precision, max(decimalDigits, 0))));
187 |
188 | case Types.CHAR:
189 | case Types.NCHAR:
190 | return Optional.of(defaultCharColumnMapping(typeHandle.getRequiredColumnSize(), false));
191 |
192 | case Types.VARCHAR:
193 | int columnSize = typeHandle.getRequiredColumnSize();
194 | if (columnSize == -1) {
195 | return Optional.of(varcharColumnMapping(createUnboundedVarcharType(), true));
196 | }
197 | return Optional.of(defaultVarcharColumnMapping(columnSize, true));
198 |
199 | case Types.NVARCHAR:
200 | case Types.LONGVARCHAR:
201 | case Types.LONGNVARCHAR:
202 | return Optional.of(defaultVarcharColumnMapping(typeHandle.getRequiredColumnSize(), false));
203 |
204 | case Types.BINARY:
205 | case Types.VARBINARY:
206 | case Types.LONGVARBINARY:
207 | return Optional.of(varbinaryColumnMapping());
208 |
209 | case Types.DATE:
210 | return Optional.of(dateColumnMappingUsingSqlDate());
211 |
212 | case Types.TIME:
213 | // TODO Consider using `StandardColumnMappings.timeColumnMapping`
214 | return Optional.of(timeColumnMappingUsingSqlTime());
215 |
216 | case Types.TIMESTAMP:
217 | TimestampType timestampType = typeHandle.getDecimalDigits()
218 | .map(TimestampType::createTimestampType)
219 | .orElse(TIMESTAMP_MILLIS);
220 | return Optional.of(timestampColumnMapping(timestampType));
221 | }
222 |
223 | if (getUnsupportedTypeHandling(session) == CONVERT_TO_VARCHAR) {
224 | return mapToUnboundedVarchar(typeHandle);
225 | }
226 | return Optional.empty();
227 | }
228 |
229 | public static ColumnMapping timestampColumnMapping(TimestampType timestampType)
230 | {
231 | if (timestampType.getPrecision() <= TimestampType.MAX_SHORT_PRECISION) {
232 | return ColumnMapping.longMapping(
233 | timestampType,
234 | timestampReadFunction(timestampType),
235 | timestampWriteFunctionUsingSqlTimestamp(timestampType));
236 | }
237 | checkArgument(timestampType.getPrecision() <= MAX_LOCAL_DATE_TIME_PRECISION, "Precision is out of range: %s", timestampType.getPrecision());
238 | return ColumnMapping.objectMapping(
239 | timestampType,
240 | longtimestampReadFunction(timestampType),
241 | longTimestampWriteFunction(timestampType));
242 | }
243 |
244 | public static LongReadFunction timestampReadFunction(TimestampType timestampType)
245 | {
246 | checkArgument(timestampType.getPrecision() <= TimestampType.MAX_SHORT_PRECISION, "Precision is out of range: %s", timestampType.getPrecision());
247 | return (resultSet, columnIndex) -> toTrinoTimestamp(timestampType, resultSet.getTimestamp(columnIndex).toLocalDateTime());
248 | }
249 |
250 | /**
251 | * Customized timestampReadFunction to convert timestamp type to LocalDateTime type.
252 | * Notice that it's because Db2 JDBC driver doesn't support {@link ResetSet#getObject(index, Class)}.
253 | *
254 | * @param timestampType
255 | * @return
256 | */
257 | private static ObjectReadFunction longtimestampReadFunction(TimestampType timestampType)
258 | {
259 | checkArgument(timestampType.getPrecision() <= MAX_LOCAL_DATE_TIME_PRECISION,
260 | "Precision is out of range: %s", timestampType.getPrecision());
261 | return ObjectReadFunction.of(
262 | LongTimestamp.class,
263 | (resultSet, columnIndex) -> toLongTrinoTimestamp(timestampType, resultSet.getTimestamp(columnIndex).toLocalDateTime()));
264 | }
265 |
266 | /**
267 | * Customized timestampWriteFunction.
268 | * Notice that it's because Db2 JDBC driver doesn't support {@link PreparedStatement#setObject(index, LocalDateTime)}.
269 | * @param timestampType
270 | * @return
271 | */
272 | private static ObjectWriteFunction longTimestampWriteFunction(TimestampType timestampType)
273 | {
274 | checkArgument(timestampType.getPrecision() > TimestampType.MAX_SHORT_PRECISION, "Precision is out of range: %s", timestampType.getPrecision());
275 | return ObjectWriteFunction.of(
276 | LongTimestamp.class,
277 | (statement, index, value) -> statement.setTimestamp(index, Timestamp.valueOf(fromLongTrinoTimestamp(value, timestampType.getPrecision()))));
278 | }
279 |
280 | /**
281 | * To map data types when generating SQL.
282 | */
283 | @Override
284 | public WriteMapping toWriteMapping(ConnectorSession session, Type type)
285 | {
286 | if (type instanceof VarcharType) {
287 | VarcharType varcharType = (VarcharType) type;
288 | String dataType;
289 |
290 | if (varcharType.isUnbounded()) {
291 | dataType = format(VARCHAR_FORMAT, this.varcharMaxLength);
292 | }
293 | else if (varcharType.getBoundedLength() > this.varcharMaxLength) {
294 | dataType = format("CLOB(%d)", varcharType.getBoundedLength());
295 | }
296 | else if (varcharType.getBoundedLength() < this.varcharMaxLength) {
297 | dataType = format(VARCHAR_FORMAT, varcharType.getBoundedLength());
298 | }
299 | else {
300 | dataType = format(VARCHAR_FORMAT, this.varcharMaxLength);
301 | }
302 |
303 | return WriteMapping.sliceMapping(dataType, varcharWriteFunction());
304 | }
305 |
306 | if (type instanceof TimestampType) {
307 | TimestampType timestampType = (TimestampType) type;
308 | verify(timestampType.getPrecision() <= DB2_MAX_SUPPORTED_TIMESTAMP_PRECISION);
309 | return WriteMapping.longMapping(format("TIMESTAMP(%s)", timestampType.getPrecision()), timestampWriteFunction(timestampType));
310 | }
311 |
312 | return this.legacyToWriteMapping(session, type);
313 | }
314 |
315 | protected WriteMapping legacyToWriteMapping(ConnectorSession session, Type type)
316 | {
317 | if (type instanceof VarcharType) {
318 | VarcharType varcharType = (VarcharType) type;
319 | String dataType;
320 | if (varcharType.isUnbounded()) {
321 | dataType = "varchar";
322 | }
323 | else {
324 | dataType = "varchar(" + varcharType.getBoundedLength() + ")";
325 | }
326 | return WriteMapping.sliceMapping(dataType, varcharWriteFunction());
327 | }
328 | if (type instanceof CharType) {
329 | return WriteMapping.sliceMapping("char(" + ((CharType) type).getLength() + ")", charWriteFunction());
330 | }
331 | if (type instanceof DecimalType) {
332 | DecimalType decimalType = (DecimalType) type;
333 | String dataType = format("decimal(%s, %s)", decimalType.getPrecision(), decimalType.getScale());
334 | if (decimalType.isShort()) {
335 | return WriteMapping.longMapping(dataType, shortDecimalWriteFunction(decimalType));
336 | }
337 | return WriteMapping.objectMapping(dataType, longDecimalWriteFunction(decimalType));
338 | }
339 |
340 | if (type == BOOLEAN) {
341 | return WriteMapping.booleanMapping("boolean", booleanWriteFunction());
342 | }
343 | if (type == TINYINT) {
344 | return WriteMapping.longMapping("tinyint", tinyintWriteFunction());
345 | }
346 | if (type == SMALLINT) {
347 | return WriteMapping.longMapping("smallint", smallintWriteFunction());
348 | }
349 | if (type == INTEGER) {
350 | return WriteMapping.longMapping("integer", integerWriteFunction());
351 | }
352 | if (type == BIGINT) {
353 | return WriteMapping.longMapping("bigint", bigintWriteFunction());
354 | }
355 | if (type == REAL) {
356 | return WriteMapping.longMapping("real", realWriteFunction());
357 | }
358 | if (type == DOUBLE) {
359 | return WriteMapping.doubleMapping("double precision", doubleWriteFunction());
360 | }
361 | if (type == VARBINARY) {
362 | return WriteMapping.sliceMapping("varbinary", varbinaryWriteFunction());
363 | }
364 | if (type == DATE) {
365 | WriteMapping.longMapping("date", dateWriteFunctionUsingSqlDate());
366 | }
367 | throw new TrinoException(NOT_SUPPORTED, "Unsupported column type: " + type.getDisplayName());
368 | }
369 |
370 | @Override
371 | protected void renameTable(ConnectorSession session, String catalogName, String schemaName, String tableName, SchemaTableName newTable)
372 | {
373 | try (Connection connection = connectionFactory.openConnection(session)) {
374 | String newTableName = newTable.getTableName();
375 | if (connection.getMetaData().storesUpperCaseIdentifiers()) {
376 | newTableName = newTableName.toUpperCase(ENGLISH);
377 | }
378 | // Specifies the new name for the table without a schema name
379 | String sql = format(
380 | "RENAME TABLE %s TO %s",
381 | quoted(catalogName, schemaName, tableName),
382 | quoted(newTableName));
383 | execute(connection, sql);
384 | }
385 | catch (SQLException e) {
386 | throw new TrinoException(JDBC_ERROR, e);
387 | }
388 | }
389 |
390 | @Override
391 | protected void copyTableSchema(Connection connection, String catalogName, String schemaName, String tableName, String newTableName, List columnNames)
392 | {
393 | String sql = format(
394 | "CREATE TABLE %s AS (SELECT %s FROM %s) WITH NO DATA",
395 | quoted(catalogName, schemaName, newTableName),
396 | columnNames.stream()
397 | .map(this::quoted)
398 | .collect(joining(", ")),
399 | quoted(catalogName, schemaName, tableName));
400 | execute(connection, sql);
401 | }
402 | }
403 |
--------------------------------------------------------------------------------
/src/main/java/io/trino/plugin/db2/DB2ClientModule.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.db2;
15 |
16 | import com.google.inject.Binder;
17 | import com.google.inject.Module;
18 | import com.google.inject.Provides;
19 | import com.google.inject.Scopes;
20 | import com.google.inject.Singleton;
21 | import com.ibm.db2.jcc.DB2Driver;
22 | import io.trino.plugin.jdbc.BaseJdbcConfig;
23 | import io.trino.plugin.jdbc.ConnectionFactory;
24 | import io.trino.plugin.jdbc.DecimalModule;
25 | import io.trino.plugin.jdbc.DriverConnectionFactory;
26 | import io.trino.plugin.jdbc.ForBaseJdbc;
27 | import io.trino.plugin.jdbc.JdbcClient;
28 | import io.trino.plugin.jdbc.TypeHandlingJdbcConfig;
29 | import io.trino.plugin.jdbc.credential.CredentialProvider;
30 |
31 | import java.util.Properties;
32 |
33 | import static io.airlift.configuration.ConfigBinder.configBinder;
34 |
35 | public class DB2ClientModule
36 | implements Module
37 | {
38 | @Override
39 | public void configure(Binder binder)
40 | {
41 | binder.bind(JdbcClient.class).annotatedWith(ForBaseJdbc.class).to(DB2Client.class).in(Scopes.SINGLETON);
42 | configBinder(binder).bindConfig(BaseJdbcConfig.class);
43 | configBinder(binder).bindConfig(DB2Config.class);
44 | configBinder(binder).bindConfig(TypeHandlingJdbcConfig.class);
45 | binder.install(new DecimalModule());
46 | }
47 |
48 | @Provides
49 | @Singleton
50 | @ForBaseJdbc
51 | public static ConnectionFactory getConnectionFactory(BaseJdbcConfig config, CredentialProvider credentialProvider, DB2Config db2Config)
52 | {
53 | Properties connectionProperties = new Properties();
54 | // https://www-01.ibm.com/support/knowledgecenter/ssw_ibm_i_72/rzaha/conprop.htm
55 | // block size (a.k.a fetch size), default 32
56 | connectionProperties.setProperty("block size", "512");
57 |
58 | // use IAM authentication when using API key
59 | if (db2Config.getApiKey() != null) {
60 | connectionProperties.setProperty("apiKey", db2Config.getApiKey());
61 | connectionProperties.setProperty("securityMechanism", "15");
62 | connectionProperties.setProperty("pluginName", "IBMIAMauth");
63 | }
64 |
65 | return new DriverConnectionFactory(new DB2Driver(), config.getConnectionUrl(), connectionProperties, credentialProvider);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/java/io/trino/plugin/db2/DB2Config.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.db2;
15 |
16 | import io.airlift.configuration.Config;
17 | import io.airlift.configuration.ConfigDescription;
18 | import io.airlift.configuration.ConfigSecuritySensitive;
19 |
20 | import javax.validation.constraints.Min;
21 |
22 | public class DB2Config
23 | {
24 | // this value comes from the official document
25 | private int varcharMaxLength = 32672;
26 | // this value is for IAM authentication
27 | private String apiKey;
28 |
29 | @Min(1)
30 | public int getVarcharMaxLength()
31 | {
32 | return varcharMaxLength;
33 | }
34 |
35 | @Config("db2.varchar-max-length")
36 | @ConfigDescription("Max length of varchar type in CREATE TABLE statement")
37 | public DB2Config setVarcharMaxLength(int varcharMaxLength)
38 | {
39 | this.varcharMaxLength = varcharMaxLength;
40 | return this;
41 | }
42 |
43 | public String getApiKey()
44 | {
45 | return apiKey;
46 | }
47 |
48 | @Config("db2.iam-api-key")
49 | @ConfigSecuritySensitive
50 | @ConfigDescription("API key for IAM authentication")
51 | public DB2Config setApiKey(String apiKey)
52 | {
53 | this.apiKey = apiKey;
54 | return this;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/io/trino/plugin/db2/DB2Plugin.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.db2;
15 |
16 | import io.trino.plugin.jdbc.JdbcPlugin;
17 |
18 | public class DB2Plugin
19 | extends JdbcPlugin
20 | {
21 | public DB2Plugin()
22 | {
23 | super("db2", new DB2ClientModule());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/test/java/io/trino/plugin/db2/TestDB2Config.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.db2;
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 TestDB2Config
26 | {
27 | @Test
28 | public void testDefaults()
29 | {
30 | assertRecordedDefaults(recordDefaults(DB2Config.class)
31 | .setVarcharMaxLength(32672)
32 | .setApiKey(null));
33 | }
34 |
35 | @Test
36 | public void testExplicitPropertyMappings()
37 | {
38 | int testVarcharLength = 30000;
39 | String testApiKey = "xyz";
40 |
41 | Map properties = new ImmutableMap.Builder()
42 | .put("db2.varchar-max-length", String.valueOf(testVarcharLength))
43 | .put("db2.iam-api-key", testApiKey)
44 | .build();
45 |
46 | DB2Config expected = new DB2Config()
47 | .setVarcharMaxLength(testVarcharLength)
48 | .setApiKey(testApiKey);
49 |
50 | assertFullMapping(properties, expected);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/test/java/io/trino/plugin/db2/TestDB2Plugin.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.db2;
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 static com.google.common.collect.Iterables.getOnlyElement;
23 | import static org.testng.Assert.assertEquals;
24 |
25 | public class TestDB2Plugin
26 | {
27 | @Test
28 | public void testCreateConnector()
29 | {
30 | Plugin plugin = new DB2Plugin();
31 | ConnectorFactory factory = getOnlyElement(plugin.getConnectorFactories());
32 | assertEquals(factory.getName(), "db2");
33 | factory.create("test", ImmutableMap.of("connection-url", "jdbc:db2:test"), new TestingConnectorContext());
34 | }
35 | }
36 |
--------------------------------------------------------------------------------