You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ko...@apache.org on 2022/01/14 15:15:12 UTC
[ignite-3] branch main updated: IGNITE-15648 Added JDBC integration tests (#522)
This is an automated email from the ASF dual-hosted git repository.
korlov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 5894611 IGNITE-15648 Added JDBC integration tests (#522)
5894611 is described below
commit 58946113a1977333343d79607da3951041edef08
Author: Vladimir Ermakov <85...@users.noreply.github.com>
AuthorDate: Fri Jan 14 18:15:05 2022 +0300
IGNITE-15648 Added JDBC integration tests (#522)
---
.../proto/query/event/BatchExecuteResult.java | 3 +-
.../client/handler/JdbcQueryEventHandlerImpl.java | 4 +-
.../ignite/internal/jdbc/JdbcConnection.java | 28 +-
.../runner/app/jdbc/AbstractJdbcSelfTest.java | 69 ++-
.../app/jdbc/ItJdbcAbstractStatementSelfTest.java | 55 ++
.../app/jdbc/ItJdbcComplexDmlDdlSelfTest.java | 396 +++++++++++++
.../app/jdbc/ItJdbcComplexQuerySelfTest.java | 231 ++++++++
.../runner/app/jdbc/ItJdbcConnectionSelfTest.java | 88 +--
.../app/jdbc/ItJdbcDeleteStatementSelfTest.java | 68 +++
.../app/jdbc/ItJdbcErrorsAbstractSelfTest.java | 632 +++++++++++++++++++++
.../runner/app/jdbc/ItJdbcErrorsSelfTest.java | 120 ++++
.../app/jdbc/ItJdbcInsertStatementSelfTest.java | 214 +++++++
.../runner/app/jdbc/ItJdbcJoinsSelfTest.java | 164 ++++++
.../jdbc/ItJdbcMetadataPrimaryKeysSelfTest.java | 105 ++++
.../runner/app/jdbc/ItJdbcMetadataSelfTest.java | 318 +++++------
.../app/jdbc/ItJdbcMultiStatementSelfTest.java | 187 ++++++
.../runner/app/jdbc/ItJdbcResultSetSelfTest.java | 238 ++++----
.../app/jdbc/ItJdbcSelectAfterAlterTable.java | 91 +++
.../app/jdbc/ItJdbcStatementCancelSelfTest.java | 157 +++++
.../runner/app/jdbc/ItJdbcStatementSelfTest.java | 163 +++---
.../app/jdbc/ItJdbcUpdateStatementSelfTest.java | 69 +++
21 files changed, 2914 insertions(+), 486 deletions(-)
diff --git a/modules/client-common/src/main/java/org/apache/ignite/client/proto/query/event/BatchExecuteResult.java b/modules/client-common/src/main/java/org/apache/ignite/client/proto/query/event/BatchExecuteResult.java
index 5568c4f..e3dbc75 100644
--- a/modules/client-common/src/main/java/org/apache/ignite/client/proto/query/event/BatchExecuteResult.java
+++ b/modules/client-common/src/main/java/org/apache/ignite/client/proto/query/event/BatchExecuteResult.java
@@ -21,6 +21,7 @@ import java.util.Objects;
import org.apache.ignite.internal.client.proto.ClientMessagePacker;
import org.apache.ignite.internal.client.proto.ClientMessageUnpacker;
import org.apache.ignite.internal.tostring.S;
+import org.apache.ignite.internal.util.ArrayUtils;
/**
* JDBC batch execute result.
@@ -64,7 +65,7 @@ public class BatchExecuteResult extends Response {
* @return Update count for DML queries.
*/
public int[] updateCounts() {
- return updateCnts;
+ return updateCnts == null ? ArrayUtils.INT_EMPTY_ARRAY : updateCnts;
}
/** {@inheritDoc} */
diff --git a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/JdbcQueryEventHandlerImpl.java b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/JdbcQueryEventHandlerImpl.java
index f8681b9..6c9b170 100644
--- a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/JdbcQueryEventHandlerImpl.java
+++ b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/JdbcQueryEventHandlerImpl.java
@@ -290,14 +290,14 @@ public class JdbcQueryEventHandlerImpl implements JdbcQueryEventHandler {
case QUERY:
return new QuerySingleResult(cursorId, fetch, !hasNext);
case DML:
- case DDL: {
if (!validateDmlResult(fetch, hasNext)) {
return new QuerySingleResult(Response.STATUS_FAILED,
"Unexpected result for DML query [" + req.sqlQuery() + "].");
}
return new QuerySingleResult(cursorId, (Long) fetch.get(0).get(0));
- }
+ case DDL:
+ return new QuerySingleResult(cursorId, 0);
default:
return new QuerySingleResult(UNSUPPORTED_OPERATION,
"Query type [" + cur.queryType() + "] is not supported yet.");
diff --git a/modules/client/src/main/java/org/apache/ignite/internal/jdbc/JdbcConnection.java b/modules/client/src/main/java/org/apache/ignite/internal/jdbc/JdbcConnection.java
index 63ee238..857dd2a 100644
--- a/modules/client/src/main/java/org/apache/ignite/internal/jdbc/JdbcConnection.java
+++ b/modules/client/src/main/java/org/apache/ignite/internal/jdbc/JdbcConnection.java
@@ -21,6 +21,7 @@ import static java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT;
import static java.sql.ResultSet.CONCUR_READ_ONLY;
import static java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT;
import static java.sql.ResultSet.TYPE_FORWARD_ONLY;
+import static org.apache.ignite.client.proto.query.SqlStateCode.CLIENT_CONNECTION_FAILED;
import static org.apache.ignite.client.proto.query.SqlStateCode.CONNECTION_CLOSED;
import java.sql.Array;
@@ -134,7 +135,7 @@ public class JdbcConnection implements Connection {
*
* @param props Connection properties.
*/
- public JdbcConnection(ConnectionProperties props) {
+ public JdbcConnection(ConnectionProperties props) throws SQLException {
this.connProps = props;
autoCommit = true;
@@ -148,14 +149,19 @@ public class JdbcConnection implements Connection {
long reconnectThrottlingPeriod = connProps.getReconnectThrottlingPeriod();
int reconnectThrottlingRetries = connProps.getReconnectThrottlingRetries();
- client = ((TcpIgniteClient) IgniteClient
- .builder()
- .addresses(addrs)
- .connectTimeout(netTimeout)
- .retryLimit(retryLimit)
- .reconnectThrottlingPeriod(reconnectThrottlingPeriod)
- .reconnectThrottlingRetries(reconnectThrottlingRetries)
- .build());
+ try {
+ client = ((TcpIgniteClient) IgniteClient
+ .builder()
+ .addresses(addrs)
+ .connectTimeout(netTimeout)
+ .retryLimit(retryLimit)
+ .reconnectThrottlingPeriod(reconnectThrottlingPeriod)
+ .reconnectThrottlingRetries(reconnectThrottlingRetries)
+ .build());
+
+ } catch (Exception e) {
+ throw new SQLException("Failed to connect to server", CLIENT_CONNECTION_FAILED, e);
+ }
this.handler = new JdbcClientQueryEventHandler(client);
@@ -228,7 +234,9 @@ public class JdbcConnection implements Connection {
checkCursorOptions(resSetType, resSetConcurrency);
- Objects.requireNonNull(sql);
+ if (sql == null) {
+ throw new SQLException("SQL string cannot be null.");
+ }
JdbcPreparedStatement stmt = new JdbcPreparedStatement(this, sql, resSetHoldability, schema);
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/AbstractJdbcSelfTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/AbstractJdbcSelfTest.java
index cdb3e40..9e1ba8c 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/AbstractJdbcSelfTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/AbstractJdbcSelfTest.java
@@ -21,14 +21,22 @@ import static org.apache.ignite.internal.testframework.IgniteTestUtils.testNodeN
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.nio.file.Path;
+import java.sql.Connection;
+import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
+import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
+import java.util.stream.Stream;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgnitionManager;
+import org.apache.ignite.internal.util.IgniteUtils;
+import org.apache.ignite.lang.IgniteLogger;
import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.function.Executable;
import org.junit.jupiter.api.io.TempDir;
@@ -38,23 +46,36 @@ import org.junit.jupiter.api.io.TempDir;
*/
public class AbstractJdbcSelfTest {
/** URL. */
- protected static final String URL = "jdbc:ignite:thin://127.0.1.1:10800";
+ protected static final String URL = "jdbc:ignite:thin://127.0.0.1:10800";
/** Cluster nodes. */
protected static final List<Ignite> clusterNodes = new ArrayList<>();
+ /** Connection. */
+ protected static Connection conn;
+
+ /** Statement. */
+ protected Statement stmt;
+
+ /** Logger. */
+ protected IgniteLogger log;
+
/**
* Creates a cluster of three nodes.
*
* @param temp Temporal directory.
*/
@BeforeAll
- public static void beforeAll(@TempDir Path temp, TestInfo testInfo) {
+ public static void beforeAll(@TempDir Path temp, TestInfo testInfo) throws SQLException {
String nodeName = testNodeName(testInfo, 47500);
String configStr = "node.metastorageNodes: [ \"" + nodeName + "\" ]";
clusterNodes.add(IgnitionManager.start(nodeName, configStr, temp.resolve(nodeName)));
+
+ conn = DriverManager.getConnection(URL);
+
+ conn.setSchema("PUBLIC");
}
/**
@@ -64,13 +85,51 @@ public class AbstractJdbcSelfTest {
*/
@AfterAll
public static void afterAll() throws Exception {
- for (Ignite clusterNode : clusterNodes) {
- clusterNode.close();
- }
+ IgniteUtils.closeAll(
+ Stream.concat(
+ Stream.of(conn != null && !conn.isClosed() ? conn : (AutoCloseable) () -> {/* NO-OP */}),
+ clusterNodes.stream()
+ )
+ );
+ conn = null;
clusterNodes.clear();
}
+ @BeforeEach
+ protected void beforeTest() throws Exception {
+ stmt = conn.createStatement();
+
+ assert stmt != null;
+ assert !stmt.isClosed();
+ }
+
+ @AfterEach
+ protected void afterTest() throws Exception {
+ if (stmt != null) {
+ stmt.close();
+
+ assert stmt.isClosed();
+ }
+ }
+
+ /**
+ * Constructor.
+ */
+ @SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod")
+ protected AbstractJdbcSelfTest() {
+ log = IgniteLogger.forClass(getClass());
+ }
+
+ /**
+ * Returns logger.
+ *
+ * @return Logger.
+ */
+ protected IgniteLogger logger() {
+ return log;
+ }
+
/**
* Checks that the function throws SQLException about a closed result set.
*
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcAbstractStatementSelfTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcAbstractStatementSelfTest.java
new file mode 100644
index 0000000..31e33ac
--- /dev/null
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcAbstractStatementSelfTest.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.runner.app.jdbc;
+
+import java.sql.Statement;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+
+/**
+ * Abstract jdbc test.
+ */
+public abstract class ItJdbcAbstractStatementSelfTest extends AbstractJdbcSelfTest {
+ /** SQL query to create a table. */
+ private static final String CREATE_TABLE_SQL = "CREATE TABLE public.person(id INTEGER PRIMARY KEY, sid VARCHAR,"
+ + " firstname VARCHAR NOT NULL, lastname VARCHAR NOT NULL, age INTEGER NOT NULL)";
+
+ /** SQL query to populate table. */
+ private static final String ITEMS_SQL = "INSERT INTO public.person(sid, id, firstname, lastname, age) VALUES "
+ + "('p1', 1, 'John', 'White', 25), "
+ + "('p2', 2, 'Joe', 'Black', 35), "
+ + "('p3', 3, 'Mike', 'Green', 40)";
+
+ /** SQL query to clear table. */
+ private static final String DROP_SQL = "DELETE FROM public.person;";
+
+ @BeforeEach
+ public void refillTable() throws Exception {
+ try (Statement s = conn.createStatement()) {
+ s.executeUpdate(DROP_SQL);
+ s.executeUpdate(ITEMS_SQL);
+ }
+ }
+
+ @BeforeAll
+ public static void createTable() throws Exception {
+ try (Statement s = conn.createStatement()) {
+ s.execute(CREATE_TABLE_SQL);
+ }
+ }
+}
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcComplexDmlDdlSelfTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcComplexDmlDdlSelfTest.java
new file mode 100644
index 0000000..fc08e48
--- /dev/null
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcComplexDmlDdlSelfTest.java
@@ -0,0 +1,396 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.runner.app.jdbc;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Base class for complex SQL tests based on JDBC driver.
+ */
+@Disabled("https://issues.apache.org/jira/browse/IGNITE-16207")
+public class ItJdbcComplexDmlDdlSelfTest extends AbstractJdbcSelfTest {
+ /** Names of companies to use. */
+ private static final List<String> COMPANIES = Arrays.asList("ASF", "GNU", "BSD");
+
+ /** Cities to use. */
+ private static final List<String> CITIES = Arrays.asList("St. Petersburg", "Boston", "Berkeley", "London");
+
+ /**
+ * Test CRD operations.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testCreateSelectDrop() throws Exception {
+ sql(new UpdateChecker(0),
+ "CREATE TABLE person_t (ID int, NAME varchar, AGE int, COMPANY varchar, CITY varchar, "
+ + "primary key (ID, NAME, CITY))");
+
+ sql(new UpdateChecker(0), "CREATE TABLE city (name varchar, population int, primary key (name))");
+
+ sql(new UpdateChecker(3),
+ "INSERT INTO city (name, population) values(?, ?), (?, ?), (?, ?)",
+ "St. Petersburg", 6000000,
+ "Boston", 2000000,
+ "London", 8000000
+ );
+
+ sql(new ResultColumnChecker("id", "name", "age", "comp"),
+ "SELECT id, name, age, company as comp FROM person_t where id < 50");
+
+ for (int i = 0; i < 100; i++) {
+ sql(new UpdateChecker(1),
+ "INSERT INTO person_t (id, name, age, company, city) values (?, ?, ?, ?, ?)",
+ i,
+ "Person " + i,
+ 20 + (i % 10),
+ COMPANIES.get(i % COMPANIES.size()),
+ CITIES.get(i % CITIES.size())
+ );
+ }
+
+ final int[] cnt = {0};
+
+ sql(new ResultPredicateChecker((Object[] objs) -> {
+ int id = ((Integer) objs[0]);
+
+ if (id >= 50) {
+ return false;
+ }
+
+ if (20 + (id % 10) != ((Integer) objs[2])) {
+ return false;
+ }
+
+ if (!("Person " + id).equals(objs[1])) {
+ return false;
+ }
+
+ ++cnt[0];
+
+ return true;
+ }), "SELECT id, name, age FROM person_t where id < 50");
+
+ assertEquals(cnt[0], 50, "Invalid rows count");
+
+ // Berkeley is not present in City table, although 25 people have it specified as their city.
+ sql(new ResultChecker(new Object[][] {{75L}}),
+ "SELECT COUNT(*) from person_t p inner join City c on p.city = c.name");
+
+ sql(new UpdateChecker(34),
+ "UPDATE person_t SET company = 'New Company', age = CASE WHEN MOD(id, 2) <> 0 THEN age + 5 ELSE "
+ + "age + 1 END WHERE company = 'ASF'");
+
+ cnt[0] = 0;
+
+ sql(new ResultPredicateChecker((Object[] objs) -> {
+ int id = ((Integer) objs[0]);
+ int age = ((Integer) objs[2]);
+
+ if (id % 2 == 0) {
+ if (age != 20 + (id % 10) + 1) {
+ return false;
+ }
+ } else {
+ if (age != 20 + (id % 10) + 5) {
+ return false;
+ }
+ }
+
+ ++cnt[0];
+
+ return true;
+ }), "SELECT * FROM person_t where company = 'New Company'");
+
+ assertEquals(cnt[0], 34, "Invalid rows count");
+
+ sql(new UpdateChecker(0), "DROP TABLE city");
+ sql(new UpdateChecker(0), "DROP TABLE person_t");
+ }
+
+ /**
+ * Run sql statement with arguments and check results.
+ *
+ * @param checker Query result's checker.
+ * @param sql SQL statement to execute.
+ * @param args Arguments.
+ * @throws SQLException If failed.
+ */
+ protected void sql(SingleStatementChecker checker, String sql, Object... args) throws SQLException {
+ Statement stmt = null;
+
+ try {
+ if (args.length > 0) {
+ stmt = conn.prepareStatement(sql);
+
+ PreparedStatement pstmt = (PreparedStatement) stmt;
+
+ for (int i = 0; i < args.length; ++i) {
+ pstmt.setObject(i + 1, args[i]);
+ }
+
+ pstmt.execute();
+ } else {
+ stmt = conn.createStatement();
+
+ stmt.execute(sql);
+ }
+
+ checkResults(stmt, checker);
+ } finally {
+ if (stmt != null) {
+ stmt.close();
+ }
+ }
+ }
+
+ /**
+ * Check query results with provided checker.
+ *
+ * @param stmt Statement.
+ * @param checker Checker.
+ * @throws SQLException If failed.
+ */
+ private void checkResults(Statement stmt, SingleStatementChecker checker) throws SQLException {
+ ResultSet rs = stmt.getResultSet();
+
+ if (rs != null) {
+ checker.check(rs);
+ } else {
+ int updCnt = stmt.getUpdateCount();
+
+ assert updCnt != -1 : "Invalid results. Result set is null and update count is -1";
+
+ checker.check(updCnt);
+ }
+ }
+
+ interface SingleStatementChecker {
+ /**
+ * Called when query produces results.
+ *
+ * @param rs Result set.
+ * @throws SQLException On error.
+ */
+ void check(ResultSet rs) throws SQLException;
+
+ /**
+ * Called when query produces any update.
+ *
+ * @param updateCount Update count.
+ */
+ void check(int updateCount);
+ }
+
+ static class UpdateChecker implements SingleStatementChecker {
+ /** Expected update count. */
+ private final int expUpdCnt;
+
+ /**
+ * Constructor.
+ *
+ * @param expUpdCnt Expected Update count.
+ */
+ UpdateChecker(int expUpdCnt) {
+ this.expUpdCnt = expUpdCnt;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void check(ResultSet rs) {
+ fail("Update results are expected. [rs=" + rs + ']');
+ }
+
+ /** {@inheritDoc} */
+ @Override public void check(int updateCount) {
+ assertEquals(expUpdCnt, updateCount);
+ }
+ }
+
+ static class ResultChecker implements SingleStatementChecker {
+ /** Expected update count. */
+ private final Set<Row> expRs = new HashSet<>();
+
+ /**
+ * Constructor.
+ *
+ * @param expRs Expected result set.
+ */
+ ResultChecker(Object[][] expRs) {
+ for (Object[] row : expRs) {
+ this.expRs.add(new Row(row));
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public void check(ResultSet rs) throws SQLException {
+ int cols = rs.getMetaData().getColumnCount();
+
+ while (rs.next()) {
+ Object[] rowObjs = new Object[cols];
+
+ for (int i = 0; i < cols; ++i) {
+ rowObjs[i] = rs.getObject(i + 1);
+ }
+
+ Row row = new Row(rowObjs);
+ var rmv = expRs.remove(row);
+
+ assert rmv : "Invalid row. [row=" + row + ", remainedRows="
+ + printRemainedExpectedResult() + ']';
+ }
+
+ assert expRs.isEmpty() : "Expected results has rows that aren't contained at the result set. [remainedRows="
+ + printRemainedExpectedResult() + ']';
+ }
+
+ /** {@inheritDoc} */
+ @Override public void check(int updateCount) {
+ fail("Results set is expected. [updateCount=" + updateCount + ']');
+ }
+
+ private String printRemainedExpectedResult() {
+ StringBuilder sb = new StringBuilder();
+
+ for (Row r : expRs) {
+ sb.append('\n').append(r.toString());
+ }
+
+ return sb.toString();
+ }
+ }
+
+ static class ResultColumnChecker extends ResultChecker {
+ /** Expected column names. */
+ private final String[] expColLabels;
+
+ /**
+ * Checker column names for rmpty results.
+ *
+ * @param expColLabels Expected column names.
+ */
+ ResultColumnChecker(String... expColLabels) {
+ super(new Object[][]{});
+
+ this.expColLabels = expColLabels;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void check(ResultSet rs) throws SQLException {
+ ResultSetMetaData meta = rs.getMetaData();
+
+ int cols = meta.getColumnCount();
+
+ assertEquals(cols, expColLabels.length, "Invalid columns count: [expected=" + expColLabels.length
+ + ", actual=" + cols + ']');
+
+ for (int i = 0; i < cols; ++i) {
+ assertTrue(expColLabels[i].equalsIgnoreCase(meta.getColumnLabel(i + 1)), expColLabels[i] + ":" + meta.getColumnName(i + 1));
+ }
+
+ super.check(rs);
+ }
+ }
+
+ static class ResultPredicateChecker implements SingleStatementChecker {
+ /** Row predicate. */
+ private final Function<Object[], Boolean> rowPredicate;
+
+ /**
+ * Constructor.
+ *
+ * @param rowPredicate Row predicate to check result set.
+ */
+ ResultPredicateChecker(Function<Object[], Boolean> rowPredicate) {
+ this.rowPredicate = rowPredicate;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void check(ResultSet rs) throws SQLException {
+ int cols = rs.getMetaData().getColumnCount();
+
+ while (rs.next()) {
+ Object[] rowObjs = new Object[cols];
+
+ for (int i = 0; i < cols; ++i) {
+ rowObjs[i] = rs.getObject(i + 1);
+ }
+
+ assert rowPredicate.apply(rowObjs) : "Invalid row. [row=" + Arrays.toString(rowObjs) + ']';
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public void check(int updateCount) {
+ fail("Results set is expected. [updateCount=" + updateCount + ']');
+ }
+ }
+
+ private static class Row {
+ /** Row. */
+ private final Object[] row;
+
+ /**
+ * Conctructor.
+ *
+ * @param row Data row.
+ */
+ private Row(Object[] row) {
+ this.row = row;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Row row1 = (Row) o;
+
+ return Arrays.equals(row, row1.row);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return Arrays.hashCode(row);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return Arrays.toString(row);
+ }
+ }
+}
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcComplexQuerySelfTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcComplexQuerySelfTest.java
new file mode 100644
index 0000000..a36b6b8
--- /dev/null
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcComplexQuerySelfTest.java
@@ -0,0 +1,231 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.runner.app.jdbc;
+
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for complex queries (joins, etc.).
+ */
+@Disabled("https://issues.apache.org/jira/browse/IGNITE-15655")
+public class ItJdbcComplexQuerySelfTest extends AbstractJdbcSelfTest {
+ @BeforeAll
+ public static void createTable() throws Exception {
+ try (Statement s = conn.createStatement()) {
+ s.execute("DROP TABLE IF EXISTS public.person");
+ s.execute("CREATE TABLE public.person (id INTEGER PRIMARY KEY, orgid INTEGER, "
+ + "name VARCHAR NOT NULL, age INTEGER NOT NULL)");
+
+ s.execute("DROP TABLE IF EXISTS public.org");
+ s.execute("CREATE TABLE public.org (id INTEGER PRIMARY KEY, name VARCHAR NOT NULL)");
+
+ s.executeUpdate("INSERT INTO public.person(orgid, id, name, age) VALUES "
+ + "(1, 1, 'John White', 25), "
+ + "(1, 2, 'Joe Black', 35), "
+ + "(2, 3, 'Mike Green', 40)");
+ s.executeUpdate("INSERT INTO public.org(id, name) VALUES "
+ + "(1, 'A'), "
+ + "(2, 'B')");
+ }
+ }
+
+ /**
+ * Join test.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testJoin() throws Exception {
+ ResultSet rs = stmt.executeQuery(
+ "select p.id, p.name, o.name as orgName from PUBLIC.Person p, PUBLIC.Org o where p.orgId = o.id");
+
+ assertNotNull(rs);
+
+ int cnt = 0;
+
+ while (rs.next()) {
+ int id = rs.getInt("id");
+
+ if (id == 1) {
+ assertEquals("John White", rs.getString("name"));
+ assertEquals("A", rs.getString("orgName"));
+ } else if (id == 2) {
+ assertEquals("Joe Black", rs.getString("name"));
+ assertEquals("A", rs.getString("orgName"));
+ } else if (id == 3) {
+ assertEquals("Mike Green", rs.getString("name"));
+ assertEquals("B", rs.getString("orgName"));
+ } else {
+ fail("Wrong ID: " + id);
+ }
+
+ cnt++;
+ }
+
+ assertEquals(3, cnt);
+ }
+
+ /**
+ * Join without alias test.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testJoinWithoutAlias() throws Exception {
+ ResultSet rs = stmt.executeQuery(
+ "select p.id, p.name, o.name from PUBLIC.Person p, PUBLIC.Org o where p.orgId = o.id");
+
+ assertNotNull(rs);
+
+ int cnt = 0;
+
+ while (rs.next()) {
+ int id = rs.getInt(1);
+
+ if (id == 1) {
+ assertEquals("John White", rs.getString("name"));
+ assertEquals("John White", rs.getString(2));
+ assertEquals("A", rs.getString(3));
+ } else if (id == 2) {
+ assertEquals("Joe Black", rs.getString("name"));
+ assertEquals("Joe Black", rs.getString(2));
+ assertEquals("A", rs.getString(3));
+ } else if (id == 3) {
+ assertEquals("Mike Green", rs.getString("name"));
+ assertEquals("Mike Green", rs.getString(2));
+ assertEquals("B", rs.getString(3));
+ } else {
+ fail("Wrong ID: " + id);
+ }
+
+ cnt++;
+ }
+
+ assertEquals(3, cnt);
+ }
+
+ /**
+ * In function test.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testIn() throws Exception {
+ ResultSet rs = stmt.executeQuery("select name from PUBLIC.Person where age in (25, 35)");
+
+ assertNotNull(rs);
+
+ int cnt = 0;
+
+ while (rs.next()) {
+ assertThat(rs.getString("name"), anyOf(is("John White"), is("Joe Black")));
+
+ cnt++;
+ }
+
+ assertEquals(2, cnt);
+ }
+
+ /**
+ * Between func test.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testBetween() throws Exception {
+ ResultSet rs = stmt.executeQuery("select name from PUBLIC.Person where age between 24 and 36");
+
+ assertNotNull(rs);
+
+ int cnt = 0;
+
+ while (rs.next()) {
+ assertThat(rs.getString("name"), anyOf(is("John White"), is("Joe Black")));
+
+ cnt++;
+ }
+
+ assertEquals(2, cnt);
+ }
+
+ /**
+ * Calculated value test.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testCalculatedValue() throws Exception {
+ ResultSet rs = stmt.executeQuery("select age * 2 from PUBLIC.Person");
+
+ assertNotNull(rs);
+
+ int cnt = 0;
+
+ while (rs.next()) {
+ assertThat(rs.getInt(1), anyOf(is(50), is(70), is(80)));
+
+ cnt++;
+ }
+
+ assertEquals(3, cnt);
+ }
+
+ /**
+ * Wrong argument type test.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testWrongArgumentType() throws Exception {
+ try (ResultSet rs = stmt.executeQuery("select * from PUBLIC.Org where name = '2'")) {
+ assertFalse(rs.next());
+ }
+
+ // Check non-indexed field.
+ assertThrows(SQLException.class, () -> {
+ try (ResultSet rs = stmt.executeQuery("select * from PUBLIC.Org where name = 2")) {
+ assertFalse(rs.next());
+ }
+ });
+
+ // Check indexed field.
+ try (ResultSet rs = stmt.executeQuery("select * from PUBLIC.Person where name = '2'")) {
+ assertFalse(rs.next());
+ }
+
+ assertThrows(SQLException.class, () -> {
+ try (ResultSet rs = stmt.executeQuery("select * from PUBLIC.Person where name = 2")) {
+ assertFalse(rs.next());
+ }
+ });
+ }
+}
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcConnectionSelfTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcConnectionSelfTest.java
index 4c2357f..0a8be3c 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcConnectionSelfTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcConnectionSelfTest.java
@@ -62,6 +62,7 @@ import org.junit.jupiter.api.Test;
* Connection test.
*/
@SuppressWarnings("ThrowableNotThrown")
+@Disabled("https://issues.apache.org/jira/browse/IGNITE-15655")
public class ItJdbcConnectionSelfTest extends AbstractJdbcSelfTest {
/**
* Test JDBC loading via ServiceLoader.
@@ -85,7 +86,7 @@ public class ItJdbcConnectionSelfTest extends AbstractJdbcSelfTest {
@SuppressWarnings({"EmptyTryBlock", "unused"})
@Test
public void testDefaults() throws Exception {
- var url = "jdbc:ignite:thin://127.0.1.1:10800";
+ var url = "jdbc:ignite:thin://127.0.0.1:10800";
try (Connection conn = DriverManager.getConnection(url)) {
// No-op.
@@ -97,8 +98,8 @@ public class ItJdbcConnectionSelfTest extends AbstractJdbcSelfTest {
}
@SuppressWarnings({"EmptyTryBlock", "unused"})
+ @Disabled
@Test
- @Disabled("ITDS-1887")
public void testDefaultsIpv6() throws Exception {
var url = "jdbc:ignite:thin://[::1]:10800";
@@ -224,12 +225,11 @@ public class ItJdbcConnectionSelfTest extends AbstractJdbcSelfTest {
}
/**
- * TODO IGNITE-15188.
+ * Test create statement.
*
* @throws Exception If failed.
*/
@Test
- @Disabled
public void testCreateStatement2() throws Exception {
try (Connection conn = DriverManager.getConnection(URL)) {
int[] rsTypes = new int[]{TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.TYPE_SCROLL_SENSITIVE};
@@ -266,12 +266,11 @@ public class ItJdbcConnectionSelfTest extends AbstractJdbcSelfTest {
}
/**
- * TODO IGNITE-15188.
+ * Test create statement.
*
* @throws Exception If failed.
*/
@Test
- @Disabled
public void testCreateStatement3() throws Exception {
try (Connection conn = DriverManager.getConnection(URL)) {
int[] rsTypes = new int[]{ TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.TYPE_SCROLL_SENSITIVE };
@@ -318,8 +317,9 @@ public class ItJdbcConnectionSelfTest extends AbstractJdbcSelfTest {
try (Connection conn = DriverManager.getConnection(URL)) {
// null query text
assertThrows(
- NullPointerException.class,
- () -> conn.prepareStatement(null)
+ SQLException.class,
+ () -> conn.prepareStatement(null),
+ "SQL string cannot be null"
);
final String sqlText = "select * from test where param = ?";
@@ -336,12 +336,11 @@ public class ItJdbcConnectionSelfTest extends AbstractJdbcSelfTest {
}
/**
- * TODO IGNITE-15188.
+ * Test prepare statement.
*
* @throws Exception If failed.
*/
@Test
- @Disabled
public void testPrepareStatement3() throws Exception {
try (Connection conn = DriverManager.getConnection(URL)) {
final String sqlText = "select * from test where param = ?";
@@ -385,12 +384,11 @@ public class ItJdbcConnectionSelfTest extends AbstractJdbcSelfTest {
}
/**
- * TODO IGNITE-15188.
+ * Test prepare statement.
*
* @throws Exception If failed.
*/
@Test
- @Disabled
public void testPrepareStatement4() throws Exception {
try (Connection conn = DriverManager.getConnection(URL)) {
final String sqlText = "select * from test where param = ?";
@@ -515,13 +513,8 @@ public class ItJdbcConnectionSelfTest extends AbstractJdbcSelfTest {
}
}
- /**
- * TODO Enable when transactions are ready.
- *
- * @throws Exception If failed.
- */
@Test
- @Disabled
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-15087")
public void testGetSetAutoCommit() throws Exception {
try (Connection conn = DriverManager.getConnection(URL)) {
boolean ac0 = conn.getAutoCommit();
@@ -582,13 +575,8 @@ public class ItJdbcConnectionSelfTest extends AbstractJdbcSelfTest {
}
}
- /**
- * Enable when transactions are ready.
- *
- * @throws Exception if failed.
- */
@Test
- @Disabled
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-15087")
public void testBeginFailsWhenMvccIsDisabled() throws Exception {
try (Connection conn = DriverManager.getConnection(URL)) {
conn.createStatement().execute("BEGIN");
@@ -599,13 +587,8 @@ public class ItJdbcConnectionSelfTest extends AbstractJdbcSelfTest {
}
}
- /**
- * Enable when transactions are ready.
- *
- * @throws Exception if failed.
- */
@Test
- @Disabled
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-15087")
public void testCommitIgnoredWhenMvccIsDisabled() throws Exception {
try (Connection conn = DriverManager.getConnection(URL)) {
conn.setAutoCommit(false);
@@ -616,13 +599,8 @@ public class ItJdbcConnectionSelfTest extends AbstractJdbcSelfTest {
// assert no exception
}
- /**
- * Enable when transactions are ready.
- *
- * @throws Exception if failed.
- */
@Test
- @Disabled
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-15087")
public void testRollbackIgnoredWhenMvccIsDisabled() throws Exception {
try (Connection conn = DriverManager.getConnection(URL)) {
conn.setAutoCommit(false);
@@ -635,12 +613,11 @@ public class ItJdbcConnectionSelfTest extends AbstractJdbcSelfTest {
}
/**
- * TODO IGNITE-15188.
+ * Test get metadata.
*
* @throws Exception If failed.
*/
@Test
- @Disabled
public void testGetMetaData() throws Exception {
try (Connection conn = DriverManager.getConnection(URL)) {
DatabaseMetaData meta = conn.getMetaData();
@@ -667,13 +644,7 @@ public class ItJdbcConnectionSelfTest extends AbstractJdbcSelfTest {
}
}
- /**
- * TODO IGNITE-15188.
- *
- * @throws Exception If failed.
- */
@Test
- @Disabled
public void testGetSetCatalog() throws Exception {
try (Connection conn = DriverManager.getConnection(URL)) {
assertFalse(conn.getMetaData().supportsCatalogsInDataManipulation());
@@ -788,7 +759,6 @@ public class ItJdbcConnectionSelfTest extends AbstractJdbcSelfTest {
* @throws Exception If failed.
*/
@Test
- @Disabled
public void testGetSetHoldability() throws Exception {
try (Connection conn = DriverManager.getConnection(URL)) {
// default value
@@ -824,13 +794,8 @@ public class ItJdbcConnectionSelfTest extends AbstractJdbcSelfTest {
}
}
- /**
- * TODO IGNITE-15188.
- *
- * @throws Exception If failed.
- */
@Test
- @Disabled
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-15087")
public void testSetSavepoint() throws Exception {
try (Connection conn = DriverManager.getConnection(URL)) {
assertFalse(conn.getMetaData().supportsSavepoints());
@@ -848,13 +813,8 @@ public class ItJdbcConnectionSelfTest extends AbstractJdbcSelfTest {
}
}
- /**
- * TODO IGNITE-15188.
- *
- * @throws Exception If failed.
- */
@Test
- @Disabled
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-15087")
public void testSetSavepointName() throws Exception {
try (Connection conn = DriverManager.getConnection(URL)) {
assertFalse(conn.getMetaData().supportsSavepoints());
@@ -881,13 +841,8 @@ public class ItJdbcConnectionSelfTest extends AbstractJdbcSelfTest {
}
}
- /**
- * TODO IGNITE-15188.
- *
- * @throws Exception If failed.
- */
@Test
- @Disabled
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-15087")
public void testRollbackSavePoint() throws Exception {
try (Connection conn = DriverManager.getConnection(URL)) {
assertFalse(conn.getMetaData().supportsSavepoints());
@@ -914,13 +869,8 @@ public class ItJdbcConnectionSelfTest extends AbstractJdbcSelfTest {
}
}
- /**
- * TODO IGNITE-15188.
- *
- * @throws Exception If failed.
- */
@Test
- @Disabled
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-15087")
public void testReleaseSavepoint() throws Exception {
try (Connection conn = DriverManager.getConnection(URL)) {
assertFalse(conn.getMetaData().supportsSavepoints());
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcDeleteStatementSelfTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcDeleteStatementSelfTest.java
new file mode 100644
index 0000000..786ab71
--- /dev/null
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcDeleteStatementSelfTest.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.runner.app.jdbc;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.sql.SQLException;
+import org.apache.ignite.table.KeyValueView;
+import org.apache.ignite.table.Tuple;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Delete functional statement self test.
+ */
+@Disabled("https://issues.apache.org/jira/browse/IGNITE-15655")
+public class ItJdbcDeleteStatementSelfTest extends ItJdbcAbstractStatementSelfTest {
+ /**
+ * Execute delete query test.
+ *
+ * @throws SQLException If failed.
+ */
+ @Test
+ public void testExecute() throws SQLException {
+ stmt.execute("delete from PUBLIC.PERSON where substring(SID, 2, 1)::int % 2 = 0");
+
+ KeyValueView<Tuple, Tuple> kvView = clusterNodes.get(0).tables().table("PUBLIC.PERSON").keyValueView();
+
+ assertFalse(kvView.contains(null, Tuple.create().set("ID", 2)));
+ assertTrue(kvView.contains(null, Tuple.create().set("ID", 1)));
+ assertTrue(kvView.contains(null, Tuple.create().set("ID", 3)));
+ }
+
+ /**
+ * Execute delete update test.
+ *
+ * @throws SQLException If failed.
+ */
+ @Test
+ public void testExecuteUpdate() throws SQLException {
+ int res = stmt.executeUpdate("delete from PUBLIC.PERSON where substring(SID, 2, 1)::int % 2 = 0");
+
+ assertEquals(1, res);
+
+ KeyValueView<Tuple, Tuple> kvView = clusterNodes.get(0).tables().table("PUBLIC.PERSON").keyValueView();
+
+ assertFalse(kvView.contains(null, Tuple.create().set("ID", 2)));
+ assertTrue(kvView.contains(null, Tuple.create().set("ID", 1)));
+ assertTrue(kvView.contains(null, Tuple.create().set("ID", 3)));
+ }
+}
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcErrorsAbstractSelfTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcErrorsAbstractSelfTest.java
new file mode 100644
index 0000000..55aff1e
--- /dev/null
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcErrorsAbstractSelfTest.java
@@ -0,0 +1,632 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.runner.app.jdbc;
+
+import static org.apache.ignite.client.proto.query.SqlStateCode.CONNECTION_CLOSED;
+import static org.apache.ignite.client.proto.query.SqlStateCode.CONVERSION_FAILED;
+import static org.apache.ignite.client.proto.query.SqlStateCode.INVALID_CURSOR_STATE;
+import static org.apache.ignite.client.proto.query.SqlStateCode.NULL_VALUE;
+import static org.apache.ignite.client.proto.query.SqlStateCode.PARSING_EXCEPTION;
+import static org.apache.ignite.client.proto.query.SqlStateCode.UNSUPPORTED_OPERATION;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.net.URL;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.Date;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.List;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test SQLSTATE codes propagation with (any) Ignite JDBC driver.
+ */
+@Disabled("https://issues.apache.org/jira/browse/IGNITE-15655")
+public abstract class ItJdbcErrorsAbstractSelfTest extends AbstractJdbcSelfTest {
+ /**
+ * Test that parsing-related error codes get propagated to Ignite SQL exceptions.
+ */
+ @Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-15247")
+ public void testParsingErrors() {
+ checkErrorState("gibberish", PARSING_EXCEPTION,
+ "Failed to parse query. Syntax error in SQL statement \"GIBBERISH[*] \"");
+ }
+
+ /**
+ * Test that error codes from tables related DDL operations get propagated to Ignite SQL
+ * exceptions.
+ */
+ @Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-15247")
+ public void testTableErrors() {
+ checkErrorState("DROP TABLE \"PUBLIC\".missing", PARSING_EXCEPTION, "Table doesn't exist: MISSING");
+ }
+
+ /**
+ * Test that error codes from indexes related DDL operations get propagated to Ignite SQL
+ * exceptions.
+ */
+ @Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-15247")
+ public void testIndexErrors() {
+ checkErrorState("DROP INDEX \"PUBLIC\".missing", PARSING_EXCEPTION, "Index doesn't exist: MISSING");
+ }
+
+ /**
+ * Test that error codes from DML operations get propagated to Ignite SQL exceptions.
+ *
+ * @throws SQLException if failed.
+ */
+ @Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-15247")
+ public void testDmlErrors() throws SQLException {
+ stmt.execute("CREATE TABLE INTEGER(KEY INT PRIMARY KEY, VAL INT)");
+
+ try {
+ checkErrorState("INSERT INTO INTEGER(key, val) values(1, null)", NULL_VALUE,
+ "Value for INSERT, COPY, MERGE, or UPDATE must not be null");
+
+ checkErrorState("INSERT INTO INTEGER(key, val) values(1, 'zzz')", CONVERSION_FAILED,
+ "Value conversion failed [column=_VAL, from=java.lang.String, to=java.lang.Integer]");
+ } finally {
+ stmt.execute("DROP TABLE INTEGER;");
+ }
+ }
+
+ /**
+ * Test error code for the case when user attempts to refer a future currently unsupported.
+ *
+ * @throws SQLException if failed.
+ */
+ @Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-15247")
+ public void testUnsupportedSql() throws SQLException {
+ stmt.execute("CREATE TABLE INTEGER(KEY INT PRIMARY KEY, VAL INT)");
+
+ try {
+ checkErrorState("ALTER TABLE INTEGER MODIFY COLUMN KEY CHAR", UNSUPPORTED_OPERATION,
+ "ALTER COLUMN is not supported");
+ } finally {
+ stmt.execute("DROP TABLE INTEGER;");
+ }
+ }
+
+ /**
+ * Test error code for the case when user attempts to use a closed connection.
+ *
+ * @throws SQLException if failed.
+ */
+ @Test
+ public void testConnectionClosed() throws SQLException {
+ Connection conn = getConnection();
+ DatabaseMetaData meta = conn.getMetaData();
+
+ conn.close();
+
+ checkErrorState(() -> conn.prepareStatement("SELECT 1"), CONNECTION_CLOSED, "Connection is closed.");
+ checkErrorState(() -> conn.createStatement(), CONNECTION_CLOSED, "Connection is closed.");
+ checkErrorState(() -> conn.getMetaData(), CONNECTION_CLOSED, "Connection is closed.");
+ checkErrorState(() -> meta.getIndexInfo(null, null, null, false, false), CONNECTION_CLOSED, "Connection is closed.");
+ checkErrorState(() -> meta.getColumns(null, null, null, null), CONNECTION_CLOSED, "Connection is closed.");
+ checkErrorState(() -> meta.getPrimaryKeys(null, null, null), CONNECTION_CLOSED, "Connection is closed.");
+ checkErrorState(() -> meta.getSchemas(null, null), CONNECTION_CLOSED, "Connection is closed.");
+ checkErrorState(() -> meta.getTables(null, null, null, null), CONNECTION_CLOSED, "Connection is closed.");
+ }
+
+ /**
+ * Test error code for the case when user attempts to use a closed result set.
+ */
+ @Test
+ public void testResultSetClosed() {
+ checkErrorState(() -> {
+ try (PreparedStatement stmt = conn.prepareStatement("SELECT 1")) {
+ ResultSet rs = stmt.executeQuery();
+
+ rs.next();
+
+ rs.close();
+
+ rs.getInt(1);
+ }
+ }, INVALID_CURSOR_STATE, "Result set is closed");
+ }
+
+ /**
+ * Test error code for the case when user attempts to get {@code int} value from column whose
+ * value can't be converted to an {@code int}.
+ */
+ @Test
+ public void testInvalidIntFormat() {
+ checkErrorState(() -> {
+ try (PreparedStatement stmt = conn.prepareStatement("SELECT 'zzz'")) {
+ ResultSet rs = stmt.executeQuery();
+
+ rs.next();
+
+ rs.getInt(1);
+ }
+ }, CONVERSION_FAILED, "Cannot convert to int");
+ }
+
+ /**
+ * Test error code for the case when user attempts to get {@code long} value from column whose
+ * value can't be converted to an {@code long}.
+ */
+ @Test
+ public void testInvalidLongFormat() {
+ checkErrorState(() -> {
+ try (PreparedStatement stmt = conn.prepareStatement("SELECT 'zzz'")) {
+ ResultSet rs = stmt.executeQuery();
+
+ rs.next();
+
+ rs.getLong(1);
+ }
+ }, CONVERSION_FAILED, "Cannot convert to long");
+ }
+
+ /**
+ * Test error code for the case when user attempts to get {@code float} value from column whose
+ * value can't be converted to an {@code float}.
+ */
+ @Test
+ public void testInvalidFloatFormat() {
+ checkErrorState(() -> {
+ try (PreparedStatement stmt = conn.prepareStatement("SELECT 'zzz'")) {
+ ResultSet rs = stmt.executeQuery();
+
+ rs.next();
+
+ rs.getFloat(1);
+ }
+ }, CONVERSION_FAILED, "Cannot convert to float");
+ }
+
+ /**
+ * Test error code for the case when user attempts to get {@code double} value from column whose
+ * value can't be converted to an {@code double}.
+ */
+ @Test
+ public void testInvalidDoubleFormat() {
+ checkErrorState(() -> {
+ try (PreparedStatement stmt = conn.prepareStatement("SELECT 'zzz'")) {
+ ResultSet rs = stmt.executeQuery();
+
+ rs.next();
+
+ rs.getDouble(1);
+ }
+ }, CONVERSION_FAILED, "Cannot convert to double");
+ }
+
+ /**
+ * Test error code for the case when user attempts to get {@code byte} value from column whose
+ * value can't be converted to an {@code byte}.
+ */
+ @Test
+ public void testInvalidByteFormat() {
+ checkErrorState(() -> {
+ try (PreparedStatement stmt = conn.prepareStatement("SELECT 'zzz'")) {
+ ResultSet rs = stmt.executeQuery();
+
+ rs.next();
+
+ rs.getByte(1);
+ }
+ }, CONVERSION_FAILED, "Cannot convert to byte");
+ }
+
+ /**
+ * Test error code for the case when user attempts to get {@code short} value from column whose
+ * value can't be converted to an {@code short}.
+ */
+ @Test
+ public void testInvalidShortFormat() {
+ checkErrorState(() -> {
+ try (PreparedStatement stmt = conn.prepareStatement("SELECT 'zzz'")) {
+ ResultSet rs = stmt.executeQuery();
+
+ rs.next();
+
+ rs.getShort(1);
+ }
+ }, CONVERSION_FAILED, "Cannot convert to short");
+ }
+
+ /**
+ * Test error code for the case when user attempts to get {@code BigDecimal} value from column
+ * whose value can't be converted to an {@code BigDecimal}.
+ */
+ @Test
+ public void testInvalidBigDecimalFormat() {
+ checkErrorState(() -> {
+ try (PreparedStatement stmt = conn.prepareStatement("SELECT 'zzz'")) {
+ ResultSet rs = stmt.executeQuery();
+
+ rs.next();
+
+ rs.getBigDecimal(1);
+ }
+ }, CONVERSION_FAILED, "Cannot convert to BigDecimal");
+ }
+
+ /**
+ * Test error code for the case when user attempts to get {@code boolean} value from column
+ * whose value can't be converted to an {@code boolean}.
+ */
+ @Test
+ public void testInvalidBooleanFormat() {
+ checkErrorState(() -> {
+ try (PreparedStatement stmt = conn.prepareStatement("SELECT 'zzz'")) {
+ ResultSet rs = stmt.executeQuery();
+
+ rs.next();
+
+ rs.getBoolean(1);
+ }
+ }, CONVERSION_FAILED, "Cannot convert to boolean");
+ }
+
+ /**
+ * Test error code for the case when user attempts to get {@code boolean} value from column
+ * whose value can't be converted to an {@code boolean}.
+ */
+ @Test
+ public void testInvalidObjectFormat() {
+ checkErrorState(() -> {
+ try (PreparedStatement stmt = conn.prepareStatement("SELECT 'zzz'")) {
+ ResultSet rs = stmt.executeQuery();
+
+ rs.next();
+
+ rs.getObject(1, List.class);
+ }
+ }, CONVERSION_FAILED, "Cannot convert to java.util.List");
+ }
+
+ /**
+ * Test error code for the case when user attempts to get {@link Date} value from column whose
+ * value can't be converted to a {@link Date}.
+ */
+ @Test
+ public void testInvalidDateFormat() {
+ checkErrorState(() -> {
+ try (PreparedStatement stmt = conn.prepareStatement("SELECT 'zzz'")) {
+ ResultSet rs = stmt.executeQuery();
+
+ rs.next();
+
+ rs.getDate(1);
+ }
+ }, CONVERSION_FAILED, "Cannot convert to date");
+ }
+
+ /**
+ * Test error code for the case when user attempts to get {@link Time} value from column whose
+ * value can't be converted to a {@link Time}.
+ */
+ @Test
+ public void testInvalidTimeFormat() {
+ checkErrorState(() -> {
+ try (PreparedStatement stmt = conn.prepareStatement("SELECT 'zzz'")) {
+ ResultSet rs = stmt.executeQuery();
+
+ rs.next();
+
+ rs.getTime(1);
+ }
+ }, CONVERSION_FAILED, "Cannot convert to time");
+ }
+
+ /**
+ * Test error code for the case when user attempts to get {@link Timestamp} value from column
+ * whose value can't be converted to a {@link Timestamp}.
+ */
+ @Test
+ public void testInvalidTimestampFormat() {
+ checkErrorState(() -> {
+ try (PreparedStatement stmt = conn.prepareStatement("SELECT 'zzz'")) {
+ ResultSet rs = stmt.executeQuery();
+
+ rs.next();
+
+ rs.getTimestamp(1);
+ }
+ }, CONVERSION_FAILED, "Cannot convert to timestamp");
+ }
+
+ /**
+ * Test error code for the case when user attempts to get {@link URL} value from column whose
+ * value can't be converted to a {@link URL}.
+ */
+ @Test
+ public void testInvalidUrlFormat() {
+ checkErrorState(() -> {
+ try (PreparedStatement stmt = conn.prepareStatement("SELECT 'zzz'")) {
+ ResultSet rs = stmt.executeQuery();
+
+ rs.next();
+
+ rs.getURL(1);
+ }
+ }, CONVERSION_FAILED, "Cannot convert to URL");
+ }
+
+ /**
+ * Check error code for the case null value is inserted into table field declared as NOT NULL.
+ *
+ * @throws SQLException if failed.
+ */
+ @Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-15247")
+ public void testNotNullViolation() throws SQLException {
+ stmt.execute("CREATE TABLE public.nulltest(id INT PRIMARY KEY, name CHAR NOT NULL)");
+
+ try {
+ checkErrorState(() -> stmt.execute("INSERT INTO public.nulltest(id, name) VALUES (1, NULLIF('a', 'a'))"),
+ NULL_VALUE, "Null value is not allowed for column 'NAME'");
+ } finally {
+ stmt.execute("DROP TABLE public.nulltest");
+ }
+ }
+
+ /**
+ * Checks wrong table name select error message.
+ */
+ @Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-15247")
+ public void testSelectWrongTable() {
+ checkSqlErrorMessage("select from wrong", PARSING_EXCEPTION,
+ "Failed to parse query. Table \"WRONG\" not found");
+ }
+
+ /**
+ * Checks wrong column name select error message.
+ *
+ * @throws SQLException If failed.
+ */
+ @Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-15247")
+ public void testSelectWrongColumnName() throws SQLException {
+ stmt.execute("CREATE TABLE public.test(id INT PRIMARY KEY, name CHAR NOT NULL)");
+
+ try {
+ checkSqlErrorMessage("select wrong from public.test", PARSING_EXCEPTION,
+ "Failed to parse query. Column \"WRONG\" not found");
+ } finally {
+ stmt.execute("DROP TABLE public.test");
+ }
+ }
+
+ /**
+ * Checks wrong syntax select error message.
+ */
+ @Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-15247")
+ public void testSelectWrongSyntax() {
+ checkSqlErrorMessage("select from test where", PARSING_EXCEPTION,
+ "Failed to parse query. Syntax error in SQL statement \"SELECT FROM TEST WHERE[*]");
+ }
+
+ /**
+ * Checks wrong table name DML error message.
+ */
+ @Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-15247")
+ public void testDmlWrongTable() {
+ checkSqlErrorMessage("insert into wrong (id, val) values (3, 'val3')", PARSING_EXCEPTION,
+ "Failed to parse query. Table \"WRONG\" not found");
+
+ checkSqlErrorMessage("merge into wrong (id, val) values (3, 'val3')", PARSING_EXCEPTION,
+ "Failed to parse query. Table \"WRONG\" not found");
+
+ checkSqlErrorMessage("update wrong set val = 'val3' where id = 2", PARSING_EXCEPTION,
+ "Failed to parse query. Table \"WRONG\" not found");
+
+ checkSqlErrorMessage("delete from wrong where id = 2", PARSING_EXCEPTION,
+ "Failed to parse query. Table \"WRONG\" not found");
+ }
+
+ /**
+ * Checks wrong column name DML error message.
+ */
+ @Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-15247")
+ public void testDmlWrongColumnName() {
+ checkSqlErrorMessage("insert into test (id, wrong) values (3, 'val3')", PARSING_EXCEPTION,
+ "Failed to parse query. Column \"WRONG\" not found");
+
+ checkSqlErrorMessage("merge into test (id, wrong) values (3, 'val3')", PARSING_EXCEPTION,
+ "Failed to parse query. Column \"WRONG\" not found");
+
+ checkSqlErrorMessage("update test set wrong = 'val3' where id = 2", PARSING_EXCEPTION,
+ "Failed to parse query. Column \"WRONG\" not found");
+
+ checkSqlErrorMessage("delete from test where wrong = 2", PARSING_EXCEPTION,
+ "Failed to parse query. Column \"WRONG\" not found");
+ }
+
+ /**
+ * Checks wrong syntax DML error message.
+ */
+ @Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-15247")
+ public void testDmlWrongSyntax() {
+ checkSqlErrorMessage("insert test (id, val) values (3, 'val3')", PARSING_EXCEPTION,
+ "Failed to parse query. Syntax error in SQL statement \"INSERT TEST[*] (ID, VAL)");
+
+ checkSqlErrorMessage("merge test (id, val) values (3, 'val3')", PARSING_EXCEPTION,
+ "Failed to parse query. Syntax error in SQL statement \"MERGE TEST[*] (ID, VAL)");
+
+ checkSqlErrorMessage("update test val = 'val3' where id = 2", PARSING_EXCEPTION,
+ "Failed to parse query. Syntax error in SQL statement \"UPDATE TEST VAL =[*] 'val3' WHERE ID = 2");
+
+ checkSqlErrorMessage("delete from test 1where id = 2", PARSING_EXCEPTION,
+ "Failed to parse query. Syntax error in SQL statement \"DELETE FROM TEST 1[*]WHERE ID = 2 ");
+ }
+
+ /**
+ * Checks wrong table name DDL error message.
+ */
+ @Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-15247")
+ public void testDdlWrongTable() {
+ checkSqlErrorMessage("create table test (id int primary key, val varchar)", PARSING_EXCEPTION,
+ "Table already exists: TEST");
+
+ checkSqlErrorMessage("drop table wrong", PARSING_EXCEPTION,
+ "Table doesn't exist: WRONG");
+
+ checkSqlErrorMessage("create index idx1 on wrong (val)", PARSING_EXCEPTION,
+ "Table doesn't exist: WRONG");
+
+ checkSqlErrorMessage("drop index wrong", PARSING_EXCEPTION,
+ "Index doesn't exist: WRONG");
+
+ checkSqlErrorMessage("alter table wrong drop column val", PARSING_EXCEPTION,
+ "Failed to parse query. Table \"WRONG\" not found");
+ }
+
+ /**
+ * Checks wrong column name DDL error message.
+ */
+ @Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-15247")
+ public void testDdlWrongColumnName() {
+ checkSqlErrorMessage("alter table test drop column wrong", PARSING_EXCEPTION,
+ "Failed to parse query. Column \"WRONG\" not found");
+
+ checkSqlErrorMessage("create index idx1 on test (wrong)", PARSING_EXCEPTION,
+ "Column doesn't exist: WRONG");
+
+ checkSqlErrorMessage("create table test(id integer primary key, AgE integer, AGe integer)",
+ PARSING_EXCEPTION,
+ "Duplicate column name: AGE");
+
+ checkSqlErrorMessage(
+ "create table test(\"id\" integer primary key, \"age\" integer, \"age\" integer)",
+ PARSING_EXCEPTION,
+ "Duplicate column name: age");
+
+ checkSqlErrorMessage("create table test(id integer primary key, age integer, age varchar)",
+ PARSING_EXCEPTION,
+ "Duplicate column name: AGE");
+ }
+
+ /**
+ * Checks wrong syntax DDL error message.
+ */
+ @Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-15247")
+ public void testDdlWrongSyntax() {
+ checkSqlErrorMessage("create table test2 (id int wrong key, val varchar)", PARSING_EXCEPTION,
+ "Failed to parse query. Syntax error in SQL statement \"CREATE TABLE TEST2 (ID INT WRONG[*]");
+
+ checkSqlErrorMessage("drop table test on", PARSING_EXCEPTION,
+ "Failed to parse query. Syntax error in SQL statement \"DROP TABLE TEST ON[*]");
+
+ checkSqlErrorMessage("create index idx1 test (val)", PARSING_EXCEPTION,
+ "Failed to parse query. Syntax error in SQL statement \"CREATE INDEX IDX1 TEST[*]");
+
+ checkSqlErrorMessage("drop index", PARSING_EXCEPTION,
+ "Failed to parse query. Syntax error in SQL statement \"DROP INDEX [*]");
+
+ checkSqlErrorMessage("alter table test drop column", PARSING_EXCEPTION,
+ "Failed to parse query. Syntax error in SQL statement \"ALTER TABLE TEST DROP COLUMN [*]");
+ }
+
+ /**
+ * Gets the connection.
+ *
+ * @return Connection to execute statements on.
+ * @throws SQLException if failed.
+ */
+ protected abstract Connection getConnection() throws SQLException;
+
+ /**
+ * Test that running given SQL statement yields expected SQLSTATE code.
+ *
+ * @param sql Statement.
+ * @param expState Expected SQLSTATE code.
+ * @param expMsg Expected message.
+ */
+ private void checkErrorState(final String sql, String expState, String expMsg) {
+ checkErrorState(() -> {
+ try (final PreparedStatement stmt = conn.prepareStatement(sql)) {
+ stmt.execute();
+ }
+ }, expState, expMsg);
+ }
+
+ /**
+ * Test that running given closure yields expected SQLSTATE code.
+ *
+ * @param clo Closure.
+ * @param expState Expected SQLSTATE code.
+ * @param expMsg Expected message.
+ */
+ protected void checkErrorState(final RunnableX clo, String expState, String expMsg) {
+ SQLException ex = assertThrows(SQLException.class, clo::run, expMsg);
+ assertThat(ex.getMessage(), containsString(expMsg));
+
+ assertEquals(expState, ex.getSQLState(), ex.getMessage());
+ }
+
+ /**
+ * Check SQL exception message and error code.
+ *
+ * @param sql Query string.
+ * @param expState Error code.
+ * @param expMsg Error message.
+ */
+ private void checkSqlErrorMessage(final String sql, String expState, String expMsg) {
+ checkErrorState(() -> {
+ stmt.executeUpdate("DROP TABLE IF EXISTS wrong");
+ stmt.executeUpdate("DROP TABLE IF EXISTS test");
+
+ stmt.executeUpdate("CREATE TABLE test (id INT PRIMARY KEY, val VARCHAR)");
+
+ stmt.executeUpdate("INSERT INTO test (id, val) VALUES (1, 'val1')");
+ stmt.executeUpdate("INSERT INTO test (id, val) VALUES (2, 'val2')");
+
+ stmt.execute(sql);
+
+ fail("Exception is expected");
+ }, expState, expMsg);
+ }
+
+ /**
+ * A runnable that can throw an SQLException.
+ */
+ public interface RunnableX {
+ /**
+ * Runs this runnable.
+ */
+ void run() throws SQLException;
+ }
+}
\ No newline at end of file
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcErrorsSelfTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcErrorsSelfTest.java
new file mode 100644
index 0000000..9c18827
--- /dev/null
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcErrorsSelfTest.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.runner.app.jdbc;
+
+import static org.apache.ignite.client.proto.query.SqlStateCode.CLIENT_CONNECTION_FAILED;
+import static org.apache.ignite.client.proto.query.SqlStateCode.INVALID_TRANSACTION_LEVEL;
+import static org.apache.ignite.client.proto.query.SqlStateCode.PARSING_EXCEPTION;
+import static org.apache.ignite.client.proto.query.SqlStateCode.UNSUPPORTED_OPERATION;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.sql.BatchUpdateException;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Statement;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test SQLSTATE codes propagation with thin client driver.
+ */
+@Disabled("https://issues.apache.org/jira/browse/IGNITE-15655")
+public class ItJdbcErrorsSelfTest extends ItJdbcErrorsAbstractSelfTest {
+ /** {@inheritDoc} */
+ @Override protected Connection getConnection() throws SQLException {
+ return DriverManager.getConnection(URL);
+ }
+
+ /**
+ * Test error code for the case when connection string is fine but client can't reach server
+ * due to <b>communication problems</b> (not due to clear misconfiguration).
+ */
+ @Test
+ public void testConnectionError() {
+ checkErrorState(() -> DriverManager.getConnection("jdbc:ignite:thin://unknown.host?connectionTimeout=1000"),
+ CLIENT_CONNECTION_FAILED, "Failed to connect to server");
+ }
+
+ /**
+ * Test error code for the case when connection string is a mess.
+ */
+ @Test
+ public void testInvalidConnectionStringFormat() {
+ checkErrorState(() -> DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1:1000000"),
+ CLIENT_CONNECTION_FAILED, "port range contains invalid port 1000000");
+ }
+
+ /**
+ * Test error code for the case when user attempts to set an invalid isolation level to a connection.
+ */
+ @SuppressWarnings("MagicConstant")
+ @Test
+ public void testInvalidIsolationLevel() {
+ checkErrorState(() -> conn.setTransactionIsolation(1000),
+ INVALID_TRANSACTION_LEVEL, "Invalid transaction isolation level.");
+ }
+
+ /**
+ * Test error code for the case when error is caused on batch execution.
+ *
+ * @throws SQLException if failed.
+ */
+ @Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-16201")
+ public void testBatchUpdateException() throws SQLException {
+ try {
+ stmt.executeUpdate("CREATE TABLE test2 (id int primary key, val varchar)");
+
+ stmt.addBatch("insert into test2 (id, val) values (1, 'val1')");
+ stmt.addBatch("insert into test2 (id, val) values (2, 'val2')");
+ stmt.addBatch("insert into test2 (id1, val1) values (3, 'val3')");
+
+ stmt.executeBatch();
+
+ fail("BatchUpdateException is expected");
+ } catch (BatchUpdateException e) {
+ assertEquals(3, e.getUpdateCounts().length);
+
+ assertArrayEquals(new int[] {1, 1, Statement.EXECUTE_FAILED}, e.getUpdateCounts());
+
+ assertEquals(PARSING_EXCEPTION, e.getSQLState());
+
+ assertTrue(e.getMessage() != null
+ && e.getMessage().contains("Failed to parse query. Column \"ID1\" not found"),
+ "Unexpected error message: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Check that unsupported explain of update operation causes Exception on the driver side with correct code and
+ * message.
+ */
+ @Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-15247")
+ public void testExplainUpdatesUnsupported() {
+ checkErrorState(() -> {
+ stmt.executeUpdate("CREATE TABLE TEST_EXPLAIN (ID INT PRIMARY KEY, VAL INT)");
+
+ stmt.executeUpdate("EXPLAIN INSERT INTO TEST_EXPLAIN VALUES (1, 2)");
+ }, UNSUPPORTED_OPERATION, "Explains of update queries are not supported.");
+ }
+}
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcInsertStatementSelfTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcInsertStatementSelfTest.java
new file mode 100644
index 0000000..0dc6098
--- /dev/null
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcInsertStatementSelfTest.java
@@ -0,0 +1,214 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.runner.app.jdbc;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Statement test.
+ */
+@Disabled("https://issues.apache.org/jira/browse/IGNITE-15655")
+public class ItJdbcInsertStatementSelfTest extends ItJdbcAbstractStatementSelfTest {
+ /** SQL SELECT query for verification. */
+ private static final String SQL_SELECT = "select sid, id, firstName, lastName, age from PUBLIC.PERSON";
+
+ /** SQL query. */
+ private static final String SQL = "insert into PUBLIC.PERSON(sid, id, firstName, lastName, age) values "
+ + "('p1', 1, 'John', 'White', 25), "
+ + "('p2', 2, 'Joe', 'Black', 35), "
+ + "('p3', 3, 'Mike', 'Green', 40)";
+
+ /** SQL query. */
+ private static final String SQL_PREPARED = "insert into PUBLIC.PERSON(sid, id, firstName, lastName, age) values "
+ + "(?, ?, ?, ?, ?), (?, ?, ?, ?, ?), (?, ?, ?, ?, ?)";
+
+ /** Arguments for prepared statement. */
+ private final Object[][] args = new Object[][] {
+ {"p1", 1, "John", "White", 25},
+ {"p3", 3, "Mike", "Green", 40},
+ {"p2", 2, "Joe", "Black", 35}
+ };
+
+ /** SQL query to populate cache. */
+ private static final String DROP_SQL = "DELETE FROM PUBLIC.PERSON;";
+
+ /** Prepared statement. */
+ private PreparedStatement prepStmt;
+
+ @BeforeEach
+ @Override
+ public void refillTable() throws Exception {
+ stmt.execute(DROP_SQL);
+
+ prepStmt = conn.prepareStatement(SQL_PREPARED);
+
+ assertNotNull(prepStmt);
+ assertFalse(prepStmt.isClosed());
+
+ int paramCnt = 1;
+
+ for (Object[] arg : args) {
+ prepStmt.setString(paramCnt++, (String) arg[0]);
+ prepStmt.setInt(paramCnt++, (Integer) arg[1]);
+ prepStmt.setString(paramCnt++, (String) arg[2]);
+ prepStmt.setString(paramCnt++, (String) arg[3]);
+ prepStmt.setInt(paramCnt++, (Integer) arg[4]);
+ }
+ }
+
+ @AfterEach
+ @Override public void afterTest() throws Exception {
+ super.afterTest();
+
+ if (prepStmt != null && !prepStmt.isClosed()) {
+ prepStmt.close();
+
+ assertTrue(prepStmt.isClosed());
+ }
+ }
+
+ private void doCheck() throws Exception {
+ assertTrue(stmt.execute(SQL_SELECT));
+
+ ResultSet rs = stmt.getResultSet();
+
+ assertNotNull(rs);
+
+ while (rs.next()) {
+ int id = rs.getInt("id");
+
+ switch (id) {
+ case 1:
+ assertEquals("p1", rs.getString("sid"));
+ assertEquals("John", rs.getString("firstName"));
+ assertEquals("White", rs.getString("lastName"));
+ assertEquals(25, rs.getInt("age"));
+ break;
+
+ case 2:
+ assertEquals("p2", rs.getString("sid"));
+ assertEquals("Joe", rs.getString("firstName"));
+ assertEquals("Black", rs.getString("lastName"));
+ assertEquals(35, rs.getInt("age"));
+ break;
+
+ case 3:
+ assertEquals("p3", rs.getString("sid"));
+ assertEquals("Mike", rs.getString("firstName"));
+ assertEquals("Green", rs.getString("lastName"));
+ assertEquals(40, rs.getInt("age"));
+ break;
+
+ case 4:
+ assertEquals("p4", rs.getString("sid"));
+ assertEquals("Leah", rs.getString("firstName"));
+ assertEquals("Grey", rs.getString("lastName"));
+ assertEquals(22, rs.getInt("age"));
+ break;
+
+ default:
+ fail("Invalid ID: " + id);
+ }
+ }
+ }
+
+ /**
+ * Execute update test.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testExecuteUpdate() throws Exception {
+ assertEquals(3, stmt.executeUpdate(SQL));
+
+ doCheck();
+ }
+
+ /**
+ * Prepared update execute test.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testPreparedExecuteUpdate() throws Exception {
+ assertEquals(3, prepStmt.executeUpdate());
+
+ doCheck();
+ }
+
+ /**
+ * Test execute.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testExecute() throws Exception {
+ assertFalse(stmt.execute(SQL));
+
+ doCheck();
+ }
+
+ /**
+ * Test prepared execute.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testPreparedExecute() throws Exception {
+ assertFalse(prepStmt.execute());
+
+ doCheck();
+ }
+
+ /**
+ * Test duplicated keys.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testDuplicateKeys() throws Exception {
+ String sql = "insert into PUBLIC.PERSON(sid, id, firstName, lastName, age) values('p1', 1, 'John', 'White', 25)";
+
+ assertFalse(stmt.execute(sql));
+
+ assertThrows(SQLException.class, () -> stmt.execute(SQL), "Failed to INSERT some keys because they are already in cache.");
+
+ stmt.execute("select count(*) from PUBLIC.PERSON;");
+
+ ResultSet resultSet = stmt.getResultSet();
+
+ assertTrue(resultSet.next());
+
+ assertEquals(3, resultSet.getInt(1));
+
+ doCheck();
+ }
+}
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcJoinsSelfTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcJoinsSelfTest.java
new file mode 100644
index 0000000..7416362
--- /dev/null
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcJoinsSelfTest.java
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.runner.app.jdbc;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for complex queries with joins.
+ */
+@Disabled("https://issues.apache.org/jira/browse/IGNITE-15655")
+public class ItJdbcJoinsSelfTest extends AbstractJdbcSelfTest {
+ /**
+ * Check distributed OUTER join of 3 tables (T1 -> T2 -> T3) returns correct result for non-collocated data.
+ *
+ * <ul>
+ * <li>Create tables.</li>
+ * <li>Put data into tables.</li>
+ * <li>Check query with distributedJoin=true returns correct results.</li>
+ * </ul>
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-16219")
+ public void testJoin() throws Exception {
+ stmt.executeUpdate("CREATE TABLE PUBLIC.PERSON"
+ + " (ID INT, NAME VARCHAR(64), AGE INT, CITY_ID DOUBLE, PRIMARY KEY (NAME));");
+ stmt.executeUpdate("CREATE TABLE PUBLIC.MEDICAL_INFO"
+ + " (ID INT, NAME VARCHAR(64), AGE INT, BLOOD_GROUP VARCHAR(64), PRIMARY KEY (ID));");
+ stmt.executeUpdate("CREATE TABLE PUBLIC.BLOOD_GROUP_INFO_PJ"
+ + " (ID INT, BLOOD_GROUP VARCHAR(64), UNIVERSAL_DONOR VARCHAR(64), PRIMARY KEY (ID));");
+ stmt.executeUpdate("CREATE TABLE PUBLIC.BLOOD_GROUP_INFO_P"
+ + " (ID INT, BLOOD_GROUP VARCHAR(64), UNIVERSAL_DONOR VARCHAR(64), PRIMARY KEY (BLOOD_GROUP));");
+
+ populateData();
+
+ checkQueries();
+ }
+
+ /**
+ * Start queries and check query results.
+ *
+ */
+ private void checkQueries() throws SQLException {
+ String res1;
+ String res2;
+
+ // Join on non-primary key.
+ try (final ResultSet resultSet = stmt.executeQuery("SELECT person.id, person.name,"
+ + " medical_info.blood_group, blood_group_info_PJ.universal_donor FROM person "
+ + "LEFT JOIN medical_info ON medical_info.name = person.name "
+ + "LEFT JOIN blood_group_info_PJ ON blood_group_info_PJ.blood_group "
+ + "= medical_info.blood_group;")) {
+
+ res1 = queryResultAsString(resultSet);
+ }
+
+ // Join on primary key.
+ try (final ResultSet resultSet = stmt.executeQuery("SELECT person.id, person.name,"
+ + " medical_info.blood_group, blood_group_info_P.universal_donor FROM person "
+ + "LEFT JOIN medical_info ON medical_info.name = person.name "
+ + "LEFT JOIN blood_group_info_P ON blood_group_info_P.blood_group "
+ + "= medical_info.blood_group;")) {
+
+ res2 = queryResultAsString(resultSet);
+ }
+
+ log.info("Query1 result: \n" + res1);
+ log.info("Query2 result: \n" + res2);
+
+ String expOut = "2001,Shravya,null,null\n"
+ + "2002,Kiran,O+,O+A+B+AB+\n"
+ + "2003,Harika,AB+,AB+\n"
+ + "2004,Srinivas,null,null\n"
+ + "2005,Madhavi,A+,A+AB+\n"
+ + "2006,Deeps,null,null\n"
+ + "2007,Hope,null,null\n";
+
+ assertEquals(expOut, res1, "Wrong result");
+ assertEquals(expOut, res2, "Wrong result");
+ }
+
+ /**
+ * Convert query result to string.
+ *
+ * @param res Query result set.
+ * @return String representation.
+ */
+ private String queryResultAsString(ResultSet res) throws SQLException {
+ List<String> results = new ArrayList<>();
+
+ while (res.next()) {
+ String row = String.valueOf(res.getLong(1))
+ + ',' + res.getString(2)
+ + ',' + res.getString(3)
+ + ',' + res.getString(4);
+
+ results.add(row);
+ }
+
+ results.sort(String::compareTo);
+
+ StringBuilder sb = new StringBuilder();
+
+ for (String result : results) {
+ sb.append(result).append('\n');
+ }
+
+ return sb.toString();
+ }
+
+ private void populateData() throws SQLException {
+ stmt.executeUpdate(" INSERT INTO PUBLIC.PERSON (ID,NAME,AGE,CITY_ID) VALUES "
+ + "(2001,'Shravya',25,1.1), "
+ + "(2002,'Kiran',26,1.1), "
+ + "(2003,'Harika',26,2.4), "
+ + "(2004,'Srinivas',24,3.2), "
+ + "(2005,'Madhavi',23,3.2), "
+ + "(2006,'Deeps',28,1.2), "
+ + "(2007,'Hope',27,1.2);");
+
+ stmt.executeUpdate("INSERT INTO PUBLIC.MEDICAL_INFO (id,name,age,blood_group) VALUES "
+ + "(2001,'Madhavi',23,'A+'), "
+ + "(2002,'Diggi',27,'B+'), "
+ + "(2003,'Kiran',26,'O+'), "
+ + "(2004,'Harika',26,'AB+');");
+
+ stmt.executeUpdate("INSERT INTO PUBLIC.BLOOD_GROUP_INFO_PJ (id,blood_group,universal_donor) VALUES "
+ + "(2001,'A+','A+AB+'), "
+ + "(2002,'O+','O+A+B+AB+'), "
+ + "(2003,'B+','B+AB+'), "
+ + "(2004,'AB+','AB+'), "
+ + "(2005,'O-','EveryOne');");
+
+ stmt.executeUpdate("INSERT INTO PUBLIC.BLOOD_GROUP_INFO_P (id,blood_group,universal_donor) VALUES "
+ + "(2001,'A+','A+AB+'), "
+ + "(2002,'O+','O+A+B+AB+'), "
+ + "(2003,'B+','B+AB+'), "
+ + "(2004,'AB+','AB+'), "
+ + "(2005,'O-','EveryOne');");
+ }
+}
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcMetadataPrimaryKeysSelfTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcMetadataPrimaryKeysSelfTest.java
new file mode 100644
index 0000000..6ee86c7
--- /dev/null
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcMetadataPrimaryKeysSelfTest.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.runner.app.jdbc;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.sql.DatabaseMetaData;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Verifies that primary keys in the metadata are valid.
+ */
+@Disabled("https://issues.apache.org/jira/browse/IGNITE-15655")
+public class ItJdbcMetadataPrimaryKeysSelfTest extends AbstractJdbcSelfTest {
+ /** COLUMN_NAME column index in the metadata table. */
+ private static final int COL_NAME_IDX = 4;
+
+ /** {@inheritDoc} */
+ @AfterEach
+ @Override protected void afterTest() throws Exception {
+ super.afterTest();
+
+ executeUpdate("DROP TABLE IF EXISTS PUBLIC.TEST;");
+ }
+
+ /**
+ * Checks for PK that contains single field.
+ */
+ @Test
+ public void testSingleKey() throws Exception {
+ executeUpdate("CREATE TABLE PUBLIC.TEST (ID INT PRIMARY KEY, NAME VARCHAR);");
+
+ checkPkFields("TEST", "ID");
+ }
+
+ /**
+ * Checks for composite (so implicitly wrapped) primary key.
+ */
+ @Test
+ public void testCompositeKey() throws Exception {
+ executeUpdate("CREATE TABLE PUBLIC.TEST ("
+ + "ID INT, "
+ + "SEC_ID INT, "
+ + "NAME VARCHAR, "
+ + "PRIMARY KEY (ID, SEC_ID));");
+
+ checkPkFields("TEST", "ID", "SEC_ID");
+ }
+
+ /**
+ * Execute update sql operation using new connection.
+ *
+ * @param sql update SQL query.
+ * @return update count.
+ * @throws SQLException on error.
+ */
+ private int executeUpdate(String sql) throws SQLException {
+ try (PreparedStatement stmt = conn.prepareStatement(sql)) {
+ return stmt.executeUpdate();
+ }
+ }
+
+ /**
+ * Checks that field names in the metadata matches specified expected fields.
+ *
+ * @param tabName part of the sql query after CREATE TABLE TESTER.
+ * @param expPkFields Expected primary key fields.
+ */
+ private void checkPkFields(String tabName, String... expPkFields) throws Exception {
+ DatabaseMetaData md = conn.getMetaData();
+
+ ResultSet rs = md.getPrimaryKeys(conn.getCatalog(), null, tabName);
+
+ List<String> colNames = new ArrayList<>();
+
+ while (rs.next()) {
+ colNames.add(rs.getString(COL_NAME_IDX));
+ }
+
+ assertEquals(Arrays.asList(expPkFields), colNames, "Field names in the primary key are not correct");
+ }
+}
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcMetadataSelfTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcMetadataSelfTest.java
index c4396a0..b0691e3 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcMetadataSelfTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcMetadataSelfTest.java
@@ -42,6 +42,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
+import org.apache.ignite.Ignite;
import org.apache.ignite.internal.client.proto.ProtocolVersion;
import org.apache.ignite.internal.schema.configuration.SchemaConfigurationConverter;
import org.apache.ignite.schema.SchemaBuilders;
@@ -56,10 +57,8 @@ import org.junit.jupiter.api.Test;
/**
* Metadata tests.
*/
+@Disabled("https://issues.apache.org/jira/browse/IGNITE-15655")
public class ItJdbcMetadataSelfTest extends AbstractJdbcSelfTest {
- /** URL. */
- protected static final String URL = "jdbc:ignite:thin://127.0.1.1:10800";
-
/** Creates tables. */
@BeforeAll
public static void createTables() {
@@ -127,13 +126,12 @@ public class ItJdbcMetadataSelfTest extends AbstractJdbcSelfTest {
}
@Test
- @Disabled
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-15507")
public void testDecimalAndDateTypeMetaData() throws Exception {
- try (Connection conn = DriverManager.getConnection(URL)) {
- Statement stmt = conn.createStatement();
+ createMetaTable();
- ResultSet rs = stmt.executeQuery(
- "select t.decimal, t.date from \"metaTest\".MetaTest as t");
+ try {
+ ResultSet rs = stmt.executeQuery("SELECT t.DECIMAL_COL, t.DATE FROM PUBLIC.METATEST t;");
assertNotNull(rs);
@@ -144,125 +142,139 @@ public class ItJdbcMetadataSelfTest extends AbstractJdbcSelfTest {
assertEquals(2, meta.getColumnCount());
assertEquals("METATEST", meta.getTableName(1).toUpperCase());
- assertEquals("DECIMAL", meta.getColumnName(1).toUpperCase());
- assertEquals("DECIMAL", meta.getColumnLabel(1).toUpperCase());
+ assertEquals("DECIMAL_COL", meta.getColumnName(1).toUpperCase());
+ assertEquals("DECIMAL_COL", meta.getColumnLabel(1).toUpperCase());
assertEquals(DECIMAL, meta.getColumnType(1));
assertEquals(meta.getColumnTypeName(1), "DECIMAL");
assertEquals(meta.getColumnClassName(1), "java.math.BigDecimal");
assertEquals("METATEST", meta.getTableName(2).toUpperCase());
- assertEquals("DATE", meta.getColumnName(2).toUpperCase());
- assertEquals("DATE", meta.getColumnLabel(2).toUpperCase());
+ assertEquals("DATE_COL", meta.getColumnName(2).toUpperCase());
+ assertEquals("DATE_COL", meta.getColumnLabel(2).toUpperCase());
assertEquals(DATE, meta.getColumnType(2));
assertEquals(meta.getColumnTypeName(2), "DATE");
assertEquals(meta.getColumnClassName(2), "java.sql.Date");
+ } finally {
+ stmt.execute("DROP TABLE METATEST;");
}
}
+ private void createMetaTable() {
+ Ignite ignite = clusterNodes.get(0);
+
+ TableDefinition metaTableDef = SchemaBuilders.tableBuilder("PUBLIC", "METATEST")
+ .columns(
+ SchemaBuilders.column("DECIMAL_COL", ColumnType.decimalOf()).build(),
+ SchemaBuilders.column("DATE_COL", ColumnType.DATE).build(),
+ SchemaBuilders.column("ID", ColumnType.INT32).asNullable(false).build())
+ .withPrimaryKey("ID")
+ .build();
+
+ ignite.tables().createTable(metaTableDef.canonicalName(), (tableChange) -> {
+ SchemaConfigurationConverter.convert(metaTableDef, tableChange).changeReplicas(1).changePartitions(10);
+ });
+ }
+
@Test
public void testGetTables() throws Exception {
- try (Connection conn = DriverManager.getConnection(URL)) {
- DatabaseMetaData meta = conn.getMetaData();
+ DatabaseMetaData meta = conn.getMetaData();
- ResultSet rs = meta.getTables("IGNITE", "PUBLIC", "%", new String[]{"TABLE"});
- assertNotNull(rs);
- assertTrue(rs.next());
- assertEquals("TABLE", rs.getString("TABLE_TYPE"));
- assertEquals("ORGANIZATION", rs.getString("TABLE_NAME"));
- assertTrue(rs.next());
- assertEquals("TABLE", rs.getString("TABLE_TYPE"));
- assertEquals("PERSON", rs.getString("TABLE_NAME"));
-
- rs = meta.getTables("IGNITE", "PUBLIC", "%", null);
- assertNotNull(rs);
- assertTrue(rs.next());
- assertEquals("TABLE", rs.getString("TABLE_TYPE"));
- assertEquals("ORGANIZATION", rs.getString("TABLE_NAME"));
+ ResultSet rs = meta.getTables("IGNITE", "PUBLIC", "%", new String[]{"TABLE"});
+ assertNotNull(rs);
+ assertTrue(rs.next());
+ assertEquals("TABLE", rs.getString("TABLE_TYPE"));
+ assertEquals("ORGANIZATION", rs.getString("TABLE_NAME"));
+ assertTrue(rs.next());
+ assertEquals("TABLE", rs.getString("TABLE_TYPE"));
+ assertEquals("PERSON", rs.getString("TABLE_NAME"));
+
+ rs = meta.getTables("IGNITE", "PUBLIC", "%", null);
+ assertNotNull(rs);
+ assertTrue(rs.next());
+ assertEquals("TABLE", rs.getString("TABLE_TYPE"));
+ assertEquals("ORGANIZATION", rs.getString("TABLE_NAME"));
- rs = meta.getTables("IGNITE", "PUBLIC", "", new String[]{"WRONG"});
- assertFalse(rs.next());
- }
+ rs = meta.getTables("IGNITE", "PUBLIC", "", new String[]{"WRONG"});
+ assertFalse(rs.next());
}
@Test
public void testGetColumns() throws Exception {
- try (Connection conn = DriverManager.getConnection(URL)) {
- DatabaseMetaData meta = conn.getMetaData();
+ DatabaseMetaData meta = conn.getMetaData();
- ResultSet rs = meta.getColumns("IGNITE", "PUBLIC", "PERSON", "%");
+ ResultSet rs = meta.getColumns("IGNITE", "PUBLIC", "PERSON", "%");
- assertNotNull(rs);
+ assertNotNull(rs);
- Collection<String> names = new ArrayList<>(2);
+ Collection<String> names = new ArrayList<>(2);
- names.add("NAME");
- names.add("AGE");
- names.add("ORGID");
+ names.add("NAME");
+ names.add("AGE");
+ names.add("ORGID");
- int cnt = 0;
+ int cnt = 0;
- while (rs.next()) {
- String name = rs.getString("COLUMN_NAME");
+ while (rs.next()) {
+ String name = rs.getString("COLUMN_NAME");
- assertTrue(names.remove(name));
+ assertTrue(names.remove(name));
- if ("NAME".equals(name)) {
- assertEquals(VARCHAR, rs.getInt("DATA_TYPE"));
- assertEquals(rs.getString("TYPE_NAME"), "VARCHAR");
- assertEquals(1, rs.getInt("NULLABLE"));
- } else if ("AGE".equals(name)) {
- assertEquals(INTEGER, rs.getInt("DATA_TYPE"));
- assertEquals(rs.getString("TYPE_NAME"), "INTEGER");
- assertEquals(1, rs.getInt("NULLABLE"));
- } else if ("ORGID".equals(name)) {
- assertEquals(INTEGER, rs.getInt("DATA_TYPE"));
- assertEquals(rs.getString("TYPE_NAME"), "INTEGER");
- assertEquals(0, rs.getInt("NULLABLE"));
+ if ("NAME".equals(name)) {
+ assertEquals(VARCHAR, rs.getInt("DATA_TYPE"));
+ assertEquals(rs.getString("TYPE_NAME"), "VARCHAR");
+ assertEquals(1, rs.getInt("NULLABLE"));
+ } else if ("AGE".equals(name)) {
+ assertEquals(INTEGER, rs.getInt("DATA_TYPE"));
+ assertEquals(rs.getString("TYPE_NAME"), "INTEGER");
+ assertEquals(1, rs.getInt("NULLABLE"));
+ } else if ("ORGID".equals(name)) {
+ assertEquals(INTEGER, rs.getInt("DATA_TYPE"));
+ assertEquals(rs.getString("TYPE_NAME"), "INTEGER");
+ assertEquals(0, rs.getInt("NULLABLE"));
- }
- cnt++;
}
+ cnt++;
+ }
- assertTrue(names.isEmpty());
- assertEquals(3, cnt);
+ assertTrue(names.isEmpty());
+ assertEquals(3, cnt);
- rs = meta.getColumns("IGNITE", "PUBLIC", "ORGANIZATION", "%");
+ rs = meta.getColumns("IGNITE", "PUBLIC", "ORGANIZATION", "%");
- assertNotNull(rs);
+ assertNotNull(rs);
- names.add("ID");
- names.add("NAME");
- names.add("BIGDATA");
-
- cnt = 0;
-
- while (rs.next()) {
- String name = rs.getString("COLUMN_NAME");
-
- assertTrue(names.remove(name));
-
- if ("ID".equals(name)) {
- assertEquals(INTEGER, rs.getInt("DATA_TYPE"));
- assertEquals(rs.getString("TYPE_NAME"), "INTEGER");
- assertEquals(0, rs.getInt("NULLABLE"));
- } else if ("NAME".equals(name)) {
- assertEquals(VARCHAR, rs.getInt("DATA_TYPE"));
- assertEquals(rs.getString("TYPE_NAME"), "VARCHAR");
- assertEquals(1, rs.getInt("NULLABLE"));
- } else if ("BIGDATA".equals(name)) {
- assertEquals(DECIMAL, rs.getInt("DATA_TYPE"));
- assertEquals(rs.getString("TYPE_NAME"), "DECIMAL");
- assertEquals(1, rs.getInt("NULLABLE"));
- assertEquals(10, rs.getInt("DECIMAL_DIGITS"));
- assertEquals(20, rs.getInt("COLUMN_SIZE"));
- }
-
- cnt++;
+ names.add("ID");
+ names.add("NAME");
+ names.add("BIGDATA");
+
+ cnt = 0;
+
+ while (rs.next()) {
+ String name = rs.getString("COLUMN_NAME");
+
+ assertTrue(names.remove(name));
+
+ if ("ID".equals(name)) {
+ assertEquals(INTEGER, rs.getInt("DATA_TYPE"));
+ assertEquals(rs.getString("TYPE_NAME"), "INTEGER");
+ assertEquals(0, rs.getInt("NULLABLE"));
+ } else if ("NAME".equals(name)) {
+ assertEquals(VARCHAR, rs.getInt("DATA_TYPE"));
+ assertEquals(rs.getString("TYPE_NAME"), "VARCHAR");
+ assertEquals(1, rs.getInt("NULLABLE"));
+ } else if ("BIGDATA".equals(name)) {
+ assertEquals(DECIMAL, rs.getInt("DATA_TYPE"));
+ assertEquals(rs.getString("TYPE_NAME"), "DECIMAL");
+ assertEquals(1, rs.getInt("NULLABLE"));
+ assertEquals(10, rs.getInt("DECIMAL_DIGITS"));
+ assertEquals(20, rs.getInt("COLUMN_SIZE"));
}
- assertTrue(names.isEmpty());
- assertEquals(3, cnt);
+ cnt++;
}
+
+ assertTrue(names.isEmpty());
+ assertEquals(3, cnt);
}
/**
@@ -270,135 +282,119 @@ public class ItJdbcMetadataSelfTest extends AbstractJdbcSelfTest {
*/
@Test
public void testCheckSupports() throws SQLException {
- try (Connection conn = DriverManager.getConnection(URL)) {
- DatabaseMetaData meta = conn.getMetaData();
+ DatabaseMetaData meta = conn.getMetaData();
- assertTrue(meta.supportsANSI92EntryLevelSQL());
- assertTrue(meta.supportsAlterTableWithAddColumn());
- assertTrue(meta.supportsAlterTableWithDropColumn());
- assertTrue(meta.nullPlusNonNullIsNull());
- }
+ assertTrue(meta.supportsANSI92EntryLevelSQL());
+ assertTrue(meta.supportsAlterTableWithAddColumn());
+ assertTrue(meta.supportsAlterTableWithDropColumn());
+ assertTrue(meta.nullPlusNonNullIsNull());
}
@Test
public void testVersions() throws Exception {
- try (Connection conn = DriverManager.getConnection(URL)) {
- assertEquals(conn.getMetaData().getDatabaseProductVersion(), ProtocolVersion.LATEST_VER.toString(),
- "Unexpected ignite database product version.");
- assertEquals(conn.getMetaData().getDriverVersion(), ProtocolVersion.LATEST_VER.toString(),
- "Unexpected ignite driver version.");
- }
+ assertEquals(conn.getMetaData().getDatabaseProductVersion(), ProtocolVersion.LATEST_VER.toString(),
+ "Unexpected ignite database product version.");
+ assertEquals(conn.getMetaData().getDriverVersion(), ProtocolVersion.LATEST_VER.toString(),
+ "Unexpected ignite driver version.");
}
@Test
public void testSchemasMetadata() throws Exception {
- try (Connection conn = DriverManager.getConnection(URL)) {
- ResultSet rs = conn.getMetaData().getSchemas();
-
- Set<String> expectedSchemas = new HashSet<>(Arrays.asList("PUBLIC", "PUBLIC"));
+ ResultSet rs = conn.getMetaData().getSchemas();
- Set<String> schemas = new HashSet<>();
+ Set<String> expectedSchemas = new HashSet<>(Arrays.asList("PUBLIC", "PUBLIC"));
- while (rs.next()) {
- schemas.add(rs.getString(1));
- }
+ Set<String> schemas = new HashSet<>();
- assertEquals(schemas, expectedSchemas);
+ while (rs.next()) {
+ schemas.add(rs.getString(1));
}
+
+ assertEquals(schemas, expectedSchemas);
}
@Test
public void testEmptySchemasMetadata() throws Exception {
- try (Connection conn = DriverManager.getConnection(URL)) {
- ResultSet rs = conn.getMetaData().getSchemas(null, "qqq");
+ ResultSet rs = conn.getMetaData().getSchemas(null, "qqq");
- assertFalse(rs.next(), "Empty result set is expected");
- }
+ assertFalse(rs.next(), "Empty result set is expected");
}
@Test
public void testPrimaryKeyMetadata() throws Exception {
- try (Connection conn = DriverManager.getConnection(URL);
- ResultSet rs = conn.getMetaData().getPrimaryKeys(null, "PUBLIC", "PERSON")) {
+ ResultSet rs = conn.getMetaData().getPrimaryKeys(null, "PUBLIC", "PERSON");
- int cnt = 0;
+ int cnt = 0;
- while (rs.next()) {
- assertEquals(rs.getString("COLUMN_NAME"), "ORGID");
-
- cnt++;
- }
+ while (rs.next()) {
+ assertEquals(rs.getString("COLUMN_NAME"), "ORGID");
- assertEquals(1, cnt);
+ cnt++;
}
+
+ assertEquals(1, cnt);
}
@Test
public void testGetAllPrimaryKeys() throws Exception {
- try (Connection conn = DriverManager.getConnection(URL)) {
- ResultSet rs = conn.getMetaData().getPrimaryKeys(null, null, null);
-
- Set<String> expectedPks = new HashSet<>(Arrays.asList(
- "PUBLIC.ORGANIZATION.PK_ORGANIZATION.ID",
- "PUBLIC.PERSON.PK_PERSON.ORGID"));
+ ResultSet rs = conn.getMetaData().getPrimaryKeys(null, null, null);
- Set<String> actualPks = new HashSet<>(expectedPks.size());
+ Set<String> expectedPks = new HashSet<>(Arrays.asList(
+ "PUBLIC.ORGANIZATION.PK_ORGANIZATION.ID",
+ "PUBLIC.PERSON.PK_PERSON.ORGID"));
- while (rs.next()) {
- actualPks.add(rs.getString("TABLE_SCHEM")
- + '.' + rs.getString("TABLE_NAME")
- + '.' + rs.getString("PK_NAME")
- + '.' + rs.getString("COLUMN_NAME"));
- }
+ Set<String> actualPks = new HashSet<>(expectedPks.size());
- assertEquals(expectedPks, actualPks, "Metadata contains unexpected primary keys info.");
+ while (rs.next()) {
+ actualPks.add(rs.getString("TABLE_SCHEM")
+ + '.' + rs.getString("TABLE_NAME")
+ + '.' + rs.getString("PK_NAME")
+ + '.' + rs.getString("COLUMN_NAME"));
}
+
+ assertEquals(expectedPks, actualPks, "Metadata contains unexpected primary keys info.");
}
@Test
public void testInvalidCatalog() throws Exception {
- try (Connection conn = DriverManager.getConnection(URL)) {
- DatabaseMetaData meta = conn.getMetaData();
+ DatabaseMetaData meta = conn.getMetaData();
- ResultSet rs = meta.getSchemas("q", null);
+ ResultSet rs = meta.getSchemas("q", null);
- assertFalse(rs.next(), "Results must be empty");
+ assertFalse(rs.next(), "Results must be empty");
- rs = meta.getTables("q", null, null, null);
+ rs = meta.getTables("q", null, null, null);
- assertFalse(rs.next(), "Results must be empty");
+ assertFalse(rs.next(), "Results must be empty");
- rs = meta.getColumns("q", null, null, null);
+ rs = meta.getColumns("q", null, null, null);
- assertFalse(rs.next(), "Results must be empty");
+ assertFalse(rs.next(), "Results must be empty");
- rs = meta.getIndexInfo("q", null, null, false, false);
+ rs = meta.getIndexInfo("q", null, null, false, false);
- assertFalse(rs.next(), "Results must be empty");
+ assertFalse(rs.next(), "Results must be empty");
- rs = meta.getPrimaryKeys("q", null, null);
+ rs = meta.getPrimaryKeys("q", null, null);
- assertFalse(rs.next(), "Results must be empty");
- }
+ assertFalse(rs.next(), "Results must be empty");
}
@Test
public void testGetTableTypes() throws Exception {
- try (Connection conn = DriverManager.getConnection(URL)) {
- DatabaseMetaData meta = conn.getMetaData();
+ DatabaseMetaData meta = conn.getMetaData();
- ResultSet rs = meta.getTableTypes();
+ ResultSet rs = meta.getTableTypes();
- assertTrue(rs.next());
+ assertTrue(rs.next());
- assertEquals("TABLE", rs.getString("TABLE_TYPE"));
+ assertEquals("TABLE", rs.getString("TABLE_TYPE"));
- assertFalse(rs.next());
- }
+ assertFalse(rs.next());
}
@Test
- @Disabled
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-16203")
public void testParametersMetadata() throws Exception {
// Perform checks few times due to query/plan caching.
for (int i = 0; i < 3; i++) {
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcMultiStatementSelfTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcMultiStatementSelfTest.java
new file mode 100644
index 0000000..4ae7fba
--- /dev/null
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcMultiStatementSelfTest.java
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.runner.app.jdbc;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for ddl queries that contain multiply sql statements, separated by ";".
+ */
+@Disabled("https://issues.apache.org/jira/browse/IGNITE-16204")
+public class ItJdbcMultiStatementSelfTest extends AbstractJdbcSelfTest {
+ /**
+ * Setup tables.
+ */
+ @BeforeEach
+ public void setupTables() throws Exception {
+ execute("DROP TABLE IF EXISTS TEST_TX; "
+ + "DROP TABLE IF EXISTS PUBLIC.TRANSACTIONS; "
+ + "DROP TABLE IF EXISTS ONE;"
+ + "DROP TABLE IF EXISTS TWO;");
+
+ execute("CREATE TABLE TEST_TX (ID INT PRIMARY KEY, AGE INT, NAME VARCHAR) ");
+
+ execute("INSERT INTO TEST_TX VALUES "
+ + "(1, 17, 'James'), "
+ + "(2, 43, 'Valery'), "
+ + "(3, 25, 'Michel'), "
+ + "(4, 19, 'Nick');");
+ }
+
+ /**
+ * Execute sql script using thin driver.
+ */
+ private void execute(String sql) throws Exception {
+ stmt.executeUpdate(sql);
+ }
+
+ /**
+ * Assert that script containing both h2 and non h2 (native) sql statements is handled correctly.
+ */
+ @Test
+ public void testMixedCommands() throws Exception {
+ execute("CREATE TABLE public.transactions (pk INT, id INT, k VARCHAR, v VARCHAR, PRIMARY KEY (pk, id)); "
+ + "CREATE INDEX transactions_id_k_v ON public.transactions (id, k, v) INLINE_SIZE 150; "
+ + "INSERT INTO public.transactions VALUES (1,2,'some', 'word') ; "
+ + "CREATE INDEX transactions_k_v_id ON public.transactions (k, v, id) INLINE_SIZE 150; "
+ + "CREATE INDEX transactions_pk_id ON public.transactions (pk, id) INLINE_SIZE 20;");
+ }
+
+ /**
+ * Sanity test for scripts, containing empty statements are handled correctly.
+ */
+ @Test
+ public void testEmptyStatements() throws Exception {
+ execute(";; ;;;;");
+ execute(" ;; ;;;; ");
+ execute("CREATE TABLE ONE (id INT PRIMARY KEY, VAL VARCHAR);;"
+ + "CREATE INDEX T_IDX ON ONE(val)"
+ + ";;UPDATE ONE SET VAL = 'SOME';;; ");
+ execute("DROP INDEX T_IDX ;; ;;"
+ + "UPDATE ONE SET VAL = 'SOME'");
+ }
+
+ /**
+ * Check multi-statement containing both h2 and native parser statements (having "?" args) works well.
+ */
+ @Test
+ public void testMultiStatementTxWithParams() throws Exception {
+ int leoAge = 28;
+
+ String nickolas = "Nickolas";
+
+ int gabAge = 84;
+ String gabName = "Gab";
+
+ int delYounger = 19;
+
+ String complexQuery =
+ "INSERT INTO TEST_TX VALUES (5, ?, 'Leo'); " // 1
+ + ";;;;"
+ + "BEGIN ; "
+ + "UPDATE TEST_TX SET name = ? WHERE name = 'Nick' ;" // 2
+ + "INSERT INTO TEST_TX VALUES (6, ?, ?); " // 3, 4
+ + "DELETE FROM TEST_TX WHERE age < ?; " // 5
+ + "COMMIT;";
+
+ try (PreparedStatement p = conn.prepareStatement(complexQuery)) {
+ p.setInt(1, leoAge);
+ p.setString(2, nickolas);
+ p.setInt(3, gabAge);
+ p.setString(4, gabName);
+ p.setInt(5, delYounger);
+
+ assertFalse(p.execute(), "Expected, that first result is an update count.");
+
+ assertTrue(p.getUpdateCount() != -1, "Expected update count of the INSERT.");
+ assertTrue(p.getMoreResults(), "More results are expected.");
+
+ assertTrue(p.getUpdateCount() != -1, "Expected update count of an empty statement.");
+ assertTrue(p.getMoreResults(), "More results are expected.");
+ assertTrue(p.getUpdateCount() != -1, "Expected update count of an empty statement.");
+ assertTrue(p.getMoreResults(), "More results are expected.");
+ assertTrue(p.getUpdateCount() != -1, "Expected update count of an empty statement.");
+ assertTrue(p.getMoreResults(), "More results are expected.");
+ assertTrue(p.getUpdateCount() != -1, "Expected update count of an empty statement.");
+ assertTrue(p.getMoreResults(), "More results are expected.");
+
+ assertTrue(p.getUpdateCount() != -1, "Expected update count of the BEGIN");
+ assertTrue(p.getMoreResults(), "More results are expected.");
+ assertTrue(p.getUpdateCount() != -1, "Expected update count of the UPDATE");
+ assertTrue(p.getMoreResults(), "More results are expected.");
+ assertTrue(p.getUpdateCount() != -1, "Expected update count of the INSERT");
+ assertTrue(p.getMoreResults(), "More results are expected.");
+ assertTrue(p.getUpdateCount() != -1, "Expected update count of the DELETE");
+ assertTrue(p.getMoreResults(), "More results are expected.");
+ assertTrue(p.getUpdateCount() != -1, "Expected update count of the COMMIT");
+
+ assertFalse(p.getMoreResults(), "There should have been no results.");
+ assertFalse(p.getUpdateCount() != -1, "There should have been no update results.");
+ }
+
+ try (PreparedStatement sel = conn.prepareStatement("SELECT * FROM TEST_TX ORDER BY ID;")) {
+ try (ResultSet pers = sel.executeQuery()) {
+ assertTrue(pers.next());
+ assertEquals(43, age(pers));
+ assertEquals("Valery", name(pers));
+
+ assertTrue(pers.next());
+ assertEquals(25, age(pers));
+ assertEquals("Michel", name(pers));
+
+ assertTrue(pers.next());
+ assertEquals(19, age(pers));
+ assertEquals("Nickolas", name(pers));
+
+ assertTrue(pers.next());
+ assertEquals(28, age(pers));
+ assertEquals("Leo", name(pers));
+
+ assertTrue(pers.next());
+ assertEquals(84, age(pers));
+ assertEquals("Gab", name(pers));
+
+ assertFalse(pers.next());
+ }
+ }
+ }
+
+ /**
+ * Extract person's name from result set.
+ */
+ private static String name(ResultSet rs) throws SQLException {
+ return rs.getString("NAME");
+ }
+
+ /**
+ * Extract person's age from result set.
+ */
+ private static int age(ResultSet rs) throws SQLException {
+ return rs.getInt("AGE");
+ }
+
+}
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcResultSetSelfTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcResultSetSelfTest.java
index 14789c2..ad1447e 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcResultSetSelfTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcResultSetSelfTest.java
@@ -30,69 +30,95 @@ import java.io.Serializable;
import java.math.BigDecimal;
import java.sql.Blob;
import java.sql.Clob;
-import java.sql.Connection;
import java.sql.Date;
-import java.sql.DriverManager;
import java.sql.NClob;
import java.sql.ResultSet;
import java.sql.SQLException;
-import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.util.ArrayList;
import java.util.GregorianCalendar;
+import java.util.List;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.internal.schema.configuration.SchemaConfigurationConverter;
import org.apache.ignite.internal.tostring.S;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
+import org.apache.ignite.schema.SchemaBuilders;
+import org.apache.ignite.schema.definition.ColumnDefinition;
+import org.apache.ignite.schema.definition.ColumnType;
+import org.apache.ignite.schema.definition.TableDefinition;
+import org.apache.ignite.table.RecordView;
+import org.apache.ignite.table.Tuple;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
/**
* Result set test.
*/
+@Disabled("https://issues.apache.org/jira/browse/IGNITE-15655")
public class ItJdbcResultSetSelfTest extends AbstractJdbcSelfTest {
- /** SQL query. */
- private static final String SQL =
+ /** SQL static query. */
+ private static final String STATIC_SQL =
"SELECT 1::INTEGER as id, true as boolVal, 1::TINYINT as byteVal, 1::SMALLINT as shortVal, 1::INTEGER as intVal, 1::BIGINT "
+ "as longVal, 1.0::FLOAT as floatVal, 1.0::DOUBLE as doubleVal, 1.0::DECIMAL as bigVal, "
+ "'1' as strVal, '1', '1901-02-01'::DATE as dateVal, '01:01:01'::TIME as timeVal, 1::TIMESTAMP as tsVal;";
- /** Statement. */
- private Statement stmt;
-
- /**
- * Create the connection ant statement.
- *
- * @throws Exception if failed.
- */
- @BeforeEach
- public void beforeTest() throws Exception {
- Connection conn = DriverManager.getConnection(URL);
-
- stmt = conn.createStatement();
-
- assertNotNull(stmt);
- assertFalse(stmt.isClosed());
- }
-
- /**
- * Close the connection and statement.
- *
- * @throws Exception if failed.
- */
- @AfterEach
- public void afterTest() throws Exception {
- if (stmt != null) {
- stmt.getConnection().close();
-
- stmt.close();
-
- assertTrue(stmt.isClosed());
- }
+ /** SQL query. */
+ private static final String SQL_SINGLE_RES = "select id, boolVal, byteVal, shortVal, intVal, longVal, floatVal, "
+ + "doubleVal, bigVal, strVal from TEST WHERE id = 1";
+
+ @BeforeAll
+ public static void beforeClass() {
+ Ignite ignite = clusterNodes.get(0);
+
+ List<ColumnDefinition> columns = new ArrayList<>();
+
+ columns.add(SchemaBuilders.column("ID", ColumnType.INT32).build());
+ columns.add(SchemaBuilders.column("BOOLVAL", ColumnType.INT8).asNullable(true).build());
+ columns.add(SchemaBuilders.column("BYTEVAL", ColumnType.INT8).asNullable(true).build());
+ columns.add(SchemaBuilders.column("SHORTVAL", ColumnType.INT16).asNullable(true).build());
+ columns.add(SchemaBuilders.column("INTVAL", ColumnType.INT32).asNullable(true).build());
+ columns.add(SchemaBuilders.column("LONGVAL", ColumnType.INT64).asNullable(true).build());
+ columns.add(SchemaBuilders.column("FLOATVAL", ColumnType.FLOAT).asNullable(true).build());
+ columns.add(SchemaBuilders.column("DOUBLEVAL", ColumnType.DOUBLE).asNullable(true).build());
+ columns.add(SchemaBuilders.column("BIGVAL", ColumnType.decimalOf()).asNullable(true).build());
+ columns.add(SchemaBuilders.column("STRVAL", ColumnType.string()).asNullable(true).build());
+ columns.add(SchemaBuilders.column("ARRVAL", ColumnType.blobOf()).asNullable(true).build());
+ columns.add(SchemaBuilders.column("DATEVAL", ColumnType.DATE).asNullable(true).build());
+ columns.add(SchemaBuilders.column("TIMEVAL", ColumnType.TemporalColumnType.time()).asNullable(true).build());
+ columns.add(SchemaBuilders.column("TSVAL", ColumnType.TemporalColumnType.timestamp()).asNullable(true).build());
+ columns.add(SchemaBuilders.column("URLVAL", ColumnType.blobOf()).asNullable(true).build());
+
+ TableDefinition personTableDef = SchemaBuilders.tableBuilder("PUBLIC", "TEST")
+ .columns(columns)
+ .withPrimaryKey("ID")
+ .build();
+
+ ignite.tables().createTable(personTableDef.canonicalName(), (tableChange) ->
+ SchemaConfigurationConverter.convert(personTableDef, tableChange).changeReplicas(1).changePartitions(10)
+ );
+
+ RecordView<Tuple> tupleRecordView = ignite.tables().table("PUBLIC.TEST").recordView();
+
+ Tuple tuple = Tuple.create();
+
+ tuple.set("BOOLVAL", (byte) 1).set("BYTEVAL", (byte) 1).set("SHORTVAL", (short) 1)
+ .set("INTVAL", 1).set("LONGVAL", 1L).set("FLOATVAL", 1.0f).set("DOUBLEVAL", 1.0d)
+ .set("BIGVAL", new BigDecimal("1")).set("STRVAL", "1")
+ .set("DATEVAL", LocalDate.parse("1901-02-01"))
+ .set("TIMEVAL", LocalTime.parse("01:01:01"))
+ .set("TSVAL", Instant.ofEpochMilli(1));
+
+ tupleRecordView.insert(null, tuple.set("ID", 1));
+ tupleRecordView.insert(null, tuple.set("ID", 2));
}
@Test
public void testBoolean() throws Exception {
- ResultSet rs = stmt.executeQuery(SQL);
+ ResultSet rs = stmt.executeQuery(SQL_SINGLE_RES);
int cnt = 0;
@@ -107,7 +133,7 @@ public class ItJdbcResultSetSelfTest extends AbstractJdbcSelfTest {
assertEquals(1.0, rs.getDouble(2));
assertEquals(1.0f, rs.getFloat(2));
assertEquals(new BigDecimal(1), rs.getBigDecimal(2));
- assertEquals(rs.getString(2), "true");
+ assertEquals(rs.getString(2), "1"); // Because we don't support bool values right now.
assertTrue(rs.getObject(2, Boolean.class));
assertEquals((byte) 1, rs.getObject(2, Byte.class));
@@ -117,7 +143,7 @@ public class ItJdbcResultSetSelfTest extends AbstractJdbcSelfTest {
assertEquals(1.0f, rs.getObject(2, Float.class));
assertEquals(1.0, rs.getObject(2, Double.class));
assertEquals(new BigDecimal(1), rs.getObject(2, BigDecimal.class));
- assertEquals("true", rs.getObject(2, String.class));
+ assertEquals("1", rs.getObject(2, String.class)); // Because we don't support bool values right now.
}
cnt++;
@@ -162,7 +188,7 @@ public class ItJdbcResultSetSelfTest extends AbstractJdbcSelfTest {
@Test
public void testByte() throws Exception {
- ResultSet rs = stmt.executeQuery(SQL);
+ ResultSet rs = stmt.executeQuery(SQL_SINGLE_RES);
int cnt = 0;
@@ -199,7 +225,7 @@ public class ItJdbcResultSetSelfTest extends AbstractJdbcSelfTest {
@Test
public void testShort() throws Exception {
- ResultSet rs = stmt.executeQuery(SQL);
+ ResultSet rs = stmt.executeQuery(SQL_SINGLE_RES);
int cnt = 0;
@@ -237,7 +263,7 @@ public class ItJdbcResultSetSelfTest extends AbstractJdbcSelfTest {
@Test
public void testInteger() throws Exception {
- ResultSet rs = stmt.executeQuery(SQL);
+ ResultSet rs = stmt.executeQuery(SQL_SINGLE_RES);
int cnt = 0;
@@ -275,7 +301,7 @@ public class ItJdbcResultSetSelfTest extends AbstractJdbcSelfTest {
@Test
public void testLong() throws Exception {
- ResultSet rs = stmt.executeQuery(SQL);
+ ResultSet rs = stmt.executeQuery(SQL_SINGLE_RES);
int cnt = 0;
@@ -313,7 +339,7 @@ public class ItJdbcResultSetSelfTest extends AbstractJdbcSelfTest {
@Test
public void testFloat() throws Exception {
- ResultSet rs = stmt.executeQuery(SQL);
+ ResultSet rs = stmt.executeQuery(SQL_SINGLE_RES);
int cnt = 0;
@@ -351,7 +377,7 @@ public class ItJdbcResultSetSelfTest extends AbstractJdbcSelfTest {
@Test
public void testDouble() throws Exception {
- ResultSet rs = stmt.executeQuery(SQL);
+ ResultSet rs = stmt.executeQuery(SQL_SINGLE_RES);
int cnt = 0;
@@ -389,7 +415,7 @@ public class ItJdbcResultSetSelfTest extends AbstractJdbcSelfTest {
@Test
public void testBigDecimal() throws Exception {
- ResultSet rs = stmt.executeQuery(SQL);
+ ResultSet rs = stmt.executeQuery(SQL_SINGLE_RES);
int cnt = 0;
@@ -404,8 +430,8 @@ public class ItJdbcResultSetSelfTest extends AbstractJdbcSelfTest {
assertEquals(1, rs.getLong(9));
assertEquals(1.0, rs.getDouble(9));
assertEquals(1.0f, rs.getFloat(9));
- assertEquals(new BigDecimal("1.0"), rs.getBigDecimal(9));
- assertEquals(rs.getString(9), "1.0");
+ assertEquals(new BigDecimal("1.000"), rs.getBigDecimal(9));
+ assertEquals(rs.getString(9), "1.000");
assertTrue(rs.getObject(9, Boolean.class));
assertEquals((byte) 1, rs.getObject(9, Byte.class));
@@ -414,8 +440,8 @@ public class ItJdbcResultSetSelfTest extends AbstractJdbcSelfTest {
assertEquals(1, rs.getObject(9, Long.class));
assertEquals(1.f, rs.getObject(9, Float.class));
assertEquals(1, rs.getObject(9, Double.class));
- assertEquals(new BigDecimal("1.0"), rs.getObject(9, BigDecimal.class));
- assertEquals(rs.getObject(9, String.class), "1.0");
+ assertEquals(new BigDecimal("1.000"), rs.getObject(9, BigDecimal.class));
+ assertEquals(rs.getObject(9, String.class), "1.000");
}
cnt++;
@@ -430,7 +456,6 @@ public class ItJdbcResultSetSelfTest extends AbstractJdbcSelfTest {
* @throws Exception If failed.
*/
@Test
- @Disabled
public void testBigDecimalScale() throws Exception {
assertEquals(convertStringToBigDecimalViaJdbc("0.1234", 2).toString(), "0.12");
assertEquals(convertStringToBigDecimalViaJdbc("1.0005", 3).toString(), "1.001");
@@ -456,7 +481,7 @@ public class ItJdbcResultSetSelfTest extends AbstractJdbcSelfTest {
@Test
public void testString() throws Exception {
- ResultSet rs = stmt.executeQuery(SQL);
+ ResultSet rs = stmt.executeQuery(SQL_SINGLE_RES);
int cnt = 0;
@@ -492,53 +517,25 @@ public class ItJdbcResultSetSelfTest extends AbstractJdbcSelfTest {
assertEquals(1, cnt);
}
- /**
- * TODO: IGNITE-15163.
- *
- * @throws Exception If failed.
- */
- @Test
- @Disabled
- public void testArray() throws Exception {
- ResultSet rs = stmt.executeQuery(SQL);
-
- int cnt = 0;
-
- while (rs.next()) {
- if (cnt == 0) {
- assertArrayEquals(new byte[]{1}, rs.getBytes("arrVal"));
- assertArrayEquals(new byte[]{1}, rs.getBytes(11));
- }
-
- cnt++;
- }
-
- assertEquals(1, cnt);
- }
-
- /**
- * TODO: IGNITE-15163.
- *
- * @throws Exception If failed.
- */
- @SuppressWarnings("deprecation")
@Test
public void testDate() throws Exception {
- ResultSet rs = stmt.executeQuery(SQL);
+ ResultSet rs = stmt.executeQuery(STATIC_SQL);
int cnt = 0;
+ Date exp = Date.valueOf(LocalDate.parse("1901-02-01"));
+
while (rs.next()) {
if (cnt == 0) {
- assert rs.getDate("dateVal").equals(new Date(1, 1, 1));
+ assertEquals(exp, rs.getDate("dateVal"));
- assertEquals(new Date(1, 1, 1), rs.getDate(12));
- assertEquals(new Time(new Date(1, 1, 1).getTime()), rs.getTime(12));
- assertEquals(new Timestamp(new Date(1, 1, 1).getTime()), rs.getTimestamp(12));
+ assertEquals(exp, rs.getDate(12));
+ assertEquals(new Time(exp.getTime()), rs.getTime(12));
+ assertEquals(new Timestamp(exp.getTime()), rs.getTimestamp(12));
- assertEquals(new Date(1, 1, 1), rs.getObject(12, Date.class));
- assertEquals(new Time(new Date(1, 1, 1).getTime()), rs.getObject(12, Time.class));
- assertEquals(new Timestamp(new Date(1, 1, 1).getTime()), rs.getObject(12, Timestamp.class));
+ assertEquals(exp, rs.getObject(12, Date.class));
+ assertEquals(new Time(exp.getTime()), rs.getObject(12, Time.class));
+ assertEquals(new Timestamp(exp.getTime()), rs.getObject(12, Timestamp.class));
}
cnt++;
@@ -548,14 +545,14 @@ public class ItJdbcResultSetSelfTest extends AbstractJdbcSelfTest {
}
/**
- * TODO: IGNITE-15163.
+ * Test date-time.
*
* @throws Exception If failed.
*/
@SuppressWarnings("deprecation")
@Test
public void testTime() throws Exception {
- ResultSet rs = stmt.executeQuery(SQL);
+ ResultSet rs = stmt.executeQuery(STATIC_SQL);
int cnt = 0;
@@ -585,7 +582,7 @@ public class ItJdbcResultSetSelfTest extends AbstractJdbcSelfTest {
*/
@Test
public void testTimestamp() throws Exception {
- ResultSet rs = stmt.executeQuery(SQL);
+ ResultSet rs = stmt.executeQuery(STATIC_SQL);
int cnt = 0;
@@ -607,46 +604,9 @@ public class ItJdbcResultSetSelfTest extends AbstractJdbcSelfTest {
assertEquals(1, cnt);
}
- /**
- * TODO Enable when sql engine will be fully integrated.
- *
- * @throws Exception If failed.
- */
- @Test
- @Disabled
- public void testObject() throws Exception {
- ResultSet rs = stmt.executeQuery(SQL);
-
- int cnt = 0;
-
- TestObjectField exp = new TestObjectField(100, "AAAA");
-
- while (rs.next()) {
- if (cnt == 0) {
- assertEquals(exp, rs.getObject("objVal"));
-
- assertEquals(exp, rs.getObject(15));
-
- assertEquals(exp, rs.getObject(15, Object.class));
-
- assertEquals(exp, rs.getObject(15, TestObjectField.class));
- }
-
- cnt++;
- }
-
- assertEquals(1, cnt);
- }
-
- /**
- * TODO Enable when sql engine will be fully integrated.
- *
- * @throws Exception If failed.
- */
@Test
- @Disabled
public void testNavigation() throws Exception {
- ResultSet rs = stmt.executeQuery("select id from TestObject where id > 0");
+ ResultSet rs = stmt.executeQuery("SELECT * FROM test where id > 0");
assertTrue(rs.isBeforeFirst());
assertFalse(rs.isAfterLast());
@@ -678,14 +638,14 @@ public class ItJdbcResultSetSelfTest extends AbstractJdbcSelfTest {
assertFalse(rs.isLast());
assertEquals(0, rs.getRow());
- rs = stmt.executeQuery("select id from TestObject where id < 0");
+ rs = stmt.executeQuery("select id from test where id < 0");
assertFalse(rs.isBeforeFirst());
}
@Test
public void testFindColumn() throws Exception {
- final ResultSet rs = stmt.executeQuery(SQL);
+ final ResultSet rs = stmt.executeQuery(SQL_SINGLE_RES);
assertNotNull(rs);
assertTrue(rs.next());
@@ -697,7 +657,7 @@ public class ItJdbcResultSetSelfTest extends AbstractJdbcSelfTest {
@Test
public void testNotSupportedTypes() throws Exception {
- final ResultSet rs = stmt.executeQuery(SQL);
+ final ResultSet rs = stmt.executeQuery(SQL_SINGLE_RES);
assertTrue(rs.next());
@@ -748,7 +708,7 @@ public class ItJdbcResultSetSelfTest extends AbstractJdbcSelfTest {
@Test
public void testUpdateNotSupported() throws Exception {
- final ResultSet rs = stmt.executeQuery(SQL);
+ final ResultSet rs = stmt.executeQuery(SQL_SINGLE_RES);
assertTrue(rs.next());
@@ -921,7 +881,7 @@ public class ItJdbcResultSetSelfTest extends AbstractJdbcSelfTest {
@Test
public void testExceptionOnClosedResultSet() throws Exception {
- final ResultSet rs = stmt.executeQuery(SQL);
+ final ResultSet rs = stmt.executeQuery(SQL_SINGLE_RES);
rs.close();
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcSelectAfterAlterTable.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcSelectAfterAlterTable.java
new file mode 100644
index 0000000..135439d
--- /dev/null
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcSelectAfterAlterTable.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.runner.app.jdbc;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Base class for complex SQL tests based on JDBC driver.
+ */
+@Disabled("https://issues.apache.org/jira/browse/IGNITE-15655")
+public class ItJdbcSelectAfterAlterTable extends AbstractJdbcSelfTest {
+ /** {@inheritDoc} */
+ @BeforeEach
+ @Override protected void beforeTest() throws Exception {
+ super.beforeTest();
+
+ stmt.executeUpdate("CREATE TABLE PUBLIC.PERSON (ID BIGINT, NAME VARCHAR, CITY_ID BIGINT, PRIMARY KEY (ID, CITY_ID))");
+ stmt.executeUpdate("INSERT INTO PUBLIC.PERSON (ID, NAME, CITY_ID) values (1, 'name_1', 11)");
+ }
+
+ /** {@inheritDoc} */
+ @AfterEach
+ @Override protected void afterTest() throws Exception {
+ super.afterTest();
+ clusterNodes.get(0).tables().dropTableAsync("PUBLIC.PERSON").get();
+ }
+
+ /**
+ * Alter table test.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testSelectAfterAlterTableSingleNode() throws Exception {
+ stmt.executeUpdate("alter table PUBLIC.PERSON add AGE int");
+
+ checkNewColumn(stmt);
+ }
+
+ /**
+ * New column check.
+ *
+ * @param stmt Statement to check new column.
+ *
+ * @throws SQLException If failed.
+ */
+ public void checkNewColumn(Statement stmt) throws SQLException {
+ ResultSet rs = stmt.executeQuery("select * from PUBLIC.PERSON");
+
+ ResultSetMetaData meta = rs.getMetaData();
+
+ assertEquals(4, meta.getColumnCount());
+
+ boolean newColExists = false;
+
+ for (int i = 1; i <= meta.getColumnCount(); ++i) {
+ if ("AGE".equalsIgnoreCase(meta.getColumnName(i))) {
+ newColExists = true;
+
+ break;
+ }
+ }
+
+ assertTrue(newColExists);
+ }
+}
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcStatementCancelSelfTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcStatementCancelSelfTest.java
new file mode 100644
index 0000000..19b5cbe
--- /dev/null
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcStatementCancelSelfTest.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.runner.app.jdbc;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Statement cancel test.
+ */
+@Disabled("https://issues.apache.org/jira/browse/IGNITE-16205")
+public class ItJdbcStatementCancelSelfTest extends ItJdbcAbstractStatementSelfTest {
+ /**
+ * Trying to cancel stament without query. In given case cancel is noop, so no exception expected.
+ */
+ @Test
+ public void testCancelingStmtWithoutQuery() {
+ try {
+ stmt.cancel();
+ } catch (Exception e) {
+ log.error("Unexpected exception.", e);
+
+ fail("Unexpected exception");
+ }
+ }
+
+ /**
+ * Trying to retrieve result set of a canceled query.
+ * SQLException with message "The query was cancelled while executing." expected.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testResultSetRetrievalInCanceledStatement() throws Exception {
+ stmt.execute("SELECT 1; SELECT 2; SELECT 3;");
+
+ assertNotNull(stmt.getResultSet());
+
+ stmt.cancel();
+
+ assertThrows(SQLException.class, () -> stmt.getResultSet(), "The query was cancelled while executing.");
+ }
+
+ /**
+ * Trying to cancel already cancelled query.
+ * No exceptions exceped.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testCancelCanceledQuery() throws Exception {
+ stmt.execute("SELECT 1;");
+
+ assertNotNull(stmt.getResultSet());
+
+ stmt.cancel();
+
+ stmt.cancel();
+
+ assertThrows(SQLException.class, () -> stmt.getResultSet(), "The query was cancelled while executing.");
+ }
+
+ /**
+ * Trying to cancel closed query.
+ * SQLException with message "Statement is closed." expected.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testCancelClosedStmt() throws Exception {
+ stmt.close();
+
+ assertThrows(SQLException.class, () -> stmt.cancel(), "Statement is closed.");
+ }
+
+ /**
+ * Trying to call <code>resultSet.next()</code> on a canceled query.
+ * SQLException with message "The query was cancelled while executing." expected.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testResultSetNextAfterCanceling() throws Exception {
+ stmt.setFetchSize(10);
+
+ ResultSet rs = stmt.executeQuery("select * from PUBLIC.PERSON");
+
+ assertTrue(rs.next());
+
+ stmt.cancel();
+
+ assertThrows(SQLException.class, rs::next, "The query was cancelled while executing.");
+ }
+
+ /**
+ * Ensure that it's possible to execute new query on cancelled statement.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testCancelAnotherStmt() throws Exception {
+ stmt.setFetchSize(10);
+
+ ResultSet rs = stmt.executeQuery("select * from PUBLIC.PERSON");
+
+ assertTrue(rs.next());
+
+ stmt.cancel();
+
+ ResultSet rs2 = stmt.executeQuery("select * from PUBLIC.PERSON order by ID asc");
+
+ assertTrue(rs2.next(), "The other cursor mustn't be closed");
+ }
+
+ /**
+ * Ensure that stament cancel doesn't affect another statement workflow, created by the same connection.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testCancelAnotherStmtResultSet() throws Exception {
+ try (Statement anotherStmt = conn.createStatement()) {
+ ResultSet rs1 = stmt.executeQuery("select * from PUBLIC.PERSON WHERE ID % 2 = 0");
+
+ ResultSet rs2 = anotherStmt.executeQuery("select * from PUBLIC.PERSON WHERE ID % 2 <> 0");
+
+ stmt.cancel();
+
+ assertThrows(SQLException.class, rs1::next, "The query was cancelled while executing.");
+
+ assertTrue(rs2.next(), "The other cursor mustn't be closed");
+ }
+ }
+}
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcStatementSelfTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcStatementSelfTest.java
index 794c619..f08c654 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcStatementSelfTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcStatementSelfTest.java
@@ -31,62 +31,33 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.Statement;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
+import java.util.UUID;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
/**
* Statement test.
*/
-@SuppressWarnings({"ThrowableNotThrown"})
-public class ItJdbcStatementSelfTest extends AbstractJdbcSelfTest {
+@Disabled("https://issues.apache.org/jira/browse/IGNITE-15655")
+public class ItJdbcStatementSelfTest extends ItJdbcAbstractStatementSelfTest {
/** SQL query. */
- private static final String SQL =
- "select 1::INTEGER, true, 1::TINYINT, 1::SMALLINT, 1::INTEGER, 1::BIGINT, 1.0::FLOAT, 1.0::DOUBLE, 1.0::DOUBLE, '1';";
+ private static final String SQL = "select * from PERSON where age > 30";
- /** Statement. */
- private Statement stmt;
+ @BeforeAll
+ public static void beforeClass() throws Exception {
+ try (Statement statement = conn.createStatement()) {
+ statement.executeUpdate("create table TEST(ID int primary key, NAME varchar(20));");
- /** Connection. */
- private Connection conn;
+ int stmtCnt = 10;
- /**
- * Create the connection ant statement.
- *
- * @throws Exception if failed.
- */
- @BeforeEach
- protected void beforeTest() throws Exception {
- conn = DriverManager.getConnection(URL);
-
- stmt = conn.createStatement();
-
- assertNotNull(stmt);
- assertFalse(stmt.isClosed());
- }
-
- /**
- * Close the connection and statement.
- *
- * @throws Exception if failed.
- */
- @AfterEach
- protected void afterTest() throws Exception {
- if (stmt != null && !stmt.isClosed()) {
- stmt.close();
-
- assertTrue(stmt.isClosed());
+ for (int i = 0; i < stmtCnt; ++i) {
+ statement.executeUpdate("insert into TEST (ID, NAME) values (" + i + ", 'name_" + i + "'); ");
+ }
}
-
- conn.close();
-
- assertTrue(stmt.isClosed());
- assertTrue(conn.isClosed());
}
@Test
- @Disabled("IGNITE-15108")
public void testExecuteQuery0() throws Exception {
ResultSet rs = stmt.executeQuery(SQL);
@@ -136,7 +107,6 @@ public class ItJdbcStatementSelfTest extends AbstractJdbcSelfTest {
}
@Test
- @Disabled("IGNITE-15108")
public void testExecute() throws Exception {
assertTrue(stmt.execute(SQL));
@@ -172,7 +142,7 @@ public class ItJdbcStatementSelfTest extends AbstractJdbcSelfTest {
}
@Test
- @Disabled("IGNITE-15108")
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-16269")
public void testMaxRows() throws Exception {
stmt.setMaxRows(1);
@@ -262,12 +232,17 @@ public class ItJdbcStatementSelfTest extends AbstractJdbcSelfTest {
@Test
public void testCloseResultSetByConnectionClose() throws Exception {
- ResultSet rs = stmt.executeQuery(SQL);
+ try (
+ Connection conn = DriverManager.getConnection(URL);
+ Statement stmt = conn.createStatement()
+ ) {
+ ResultSet rs = stmt.executeQuery(SQL);
- conn.close();
+ conn.close();
- assertTrue(stmt.isClosed(), "Statement must be implicitly closed after close connection");
- assertTrue(rs.isClosed(), "ResultSet must be implicitly closed after close connection");
+ assertTrue(stmt.isClosed(), "Statement must be implicitly closed after close connection");
+ assertTrue(rs.isClosed(), "ResultSet must be implicitly closed after close connection");
+ }
}
@Test
@@ -326,7 +301,7 @@ public class ItJdbcStatementSelfTest extends AbstractJdbcSelfTest {
@Test
public void testExecuteQueryMultipleOnlyResultSets() throws Exception {
- // assertTrue(conn.getMetaData().supportsMultipleResultSets());
+ assertTrue(conn.getMetaData().supportsMultipleResultSets());
int stmtCnt = 10;
@@ -358,10 +333,7 @@ public class ItJdbcStatementSelfTest extends AbstractJdbcSelfTest {
}
@Test
- @Disabled("IGNITE-15108")
public void testExecuteQueryMultipleOnlyDml() throws Exception {
- conn.setSchema(null);
-
Statement stmt0 = conn.createStatement();
int stmtCnt = 10;
@@ -395,12 +367,8 @@ public class ItJdbcStatementSelfTest extends AbstractJdbcSelfTest {
}
@Test
- @Disabled("IGNITE-15108")
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-16276")
public void testExecuteQueryMultipleMixed() throws Exception {
- conn.setSchema(null);
-
- Statement stmt0 = conn.createStatement();
-
int stmtCnt = 10;
StringBuilder sql = new StringBuilder("drop table if exists test; create table test(ID int primary key, NAME varchar(20)); ");
@@ -413,30 +381,28 @@ public class ItJdbcStatementSelfTest extends AbstractJdbcSelfTest {
}
}
- assertFalse(stmt0.execute(sql.toString()));
+ assertFalse(stmt.execute(sql.toString()));
// DROP TABLE statement
- assertNull(stmt0.getResultSet());
- assertEquals(0, stmt0.getUpdateCount());
+ assertNull(stmt.getResultSet());
+ assertEquals(0, stmt.getUpdateCount());
- assertTrue(stmt0.getMoreResults(), "Result set doesn't have more results.");
+ assertTrue(stmt.getMoreResults(), "Result set doesn't have more results.");
// CREATE TABLE statement
- assertNull(stmt0.getResultSet());
- assertEquals(0, stmt0.getUpdateCount());
-
- boolean notEmptyResult = false;
+ assertNull(stmt.getResultSet());
+ assertEquals(0, stmt.getUpdateCount());
for (int i = 0; i < stmtCnt; ++i) {
- assertTrue(stmt0.getMoreResults());
+ assertTrue(stmt.getMoreResults());
if (i % 2 == 0) {
- assertNull(stmt0.getResultSet());
- assertEquals(1, stmt0.getUpdateCount());
+ assertNull(stmt.getResultSet());
+ assertEquals(1, stmt.getUpdateCount());
} else {
- assertEquals(-1, stmt0.getUpdateCount());
+ assertEquals(-1, stmt.getUpdateCount());
- ResultSet rs = stmt0.getResultSet();
+ ResultSet rs = stmt.getResultSet();
int rowsCnt = 0;
@@ -444,23 +410,16 @@ public class ItJdbcStatementSelfTest extends AbstractJdbcSelfTest {
rowsCnt++;
}
- assertTrue(rowsCnt <= (i + 1) / 2);
-
- if (rowsCnt == (i + 1) / 2) {
- notEmptyResult = true;
- }
-
- assertTrue(notEmptyResult);
-
- assertFalse(stmt0.getMoreResults());
+ assertEquals((i + 1) / 2, rowsCnt);
}
}
+
+ assertFalse(stmt.getMoreResults());
}
@Test
- @Disabled("IGNITE-15108")
public void testExecuteUpdate() throws Exception {
- final String sqlText = "update test set val=1 where _key=1";
+ final String sqlText = "update TEST set NAME='CHANGED_NAME_1' where ID=1;";
assertEquals(1, stmt.executeUpdate(sqlText));
@@ -471,7 +430,7 @@ public class ItJdbcStatementSelfTest extends AbstractJdbcSelfTest {
@Test
public void testExecuteUpdateProducesResultSet() {
- final String sqlText = "select * from test";
+ final String sqlText = "select * from TEST;";
assertThrows(SQLException.class, () -> stmt.executeUpdate(sqlText),
"Given statement type does not match that declared by JDBC driver"
@@ -479,6 +438,23 @@ public class ItJdbcStatementSelfTest extends AbstractJdbcSelfTest {
}
@Test
+ public void testExecuteUpdateOnDdl() throws SQLException {
+ String tableName = "\"test_" + UUID.randomUUID().toString() + "\"";
+
+ stmt.executeUpdate("CREATE TABLE " + tableName + "(id INT PRIMARY KEY, val VARCHAR)");
+
+ ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM " + tableName);
+
+ assertNotNull(rs, "ResultSet expected");
+ assertTrue(rs.next(), "One row expected");
+ assertEquals(0L, rs.getLong(1));
+
+ stmt.executeUpdate("DROP TABLE " + tableName);
+
+ assertThrows(SQLException.class, () -> stmt.executeQuery("SELECT COUNT(*) FROM " + tableName));
+ }
+
+ @Test
public void testClose() throws Exception {
String sqlText = "select 1";
@@ -517,7 +493,7 @@ public class ItJdbcStatementSelfTest extends AbstractJdbcSelfTest {
}
@Test
- @Disabled
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-16269")
public void testGetSetMaxRows() throws Exception {
assertEquals(0, stmt.getMaxRows());
@@ -670,15 +646,7 @@ public class ItJdbcStatementSelfTest extends AbstractJdbcSelfTest {
checkStatementClosed(() -> stmt.getMoreResults(Statement.KEEP_CURRENT_RESULT));
}
- /**
- * TODO Enable when batch query is supported
- *
- * <p>Verifies that empty batch can be performed.
- *
- * @throws Exception If failed.
- */
@Test
- @Disabled
public void testBatchEmpty() throws Exception {
assertTrue(conn.getMetaData().supportsBatchUpdates());
@@ -752,26 +720,23 @@ public class ItJdbcStatementSelfTest extends AbstractJdbcSelfTest {
}
@Test
- @Disabled("IGNITE-15108")
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-16268")
public void testStatementTypeMismatchUpdate() throws Exception {
assertThrows(
SQLException.class,
- () -> stmt.executeQuery("update test set val=28 where _key=1"),
+ () -> stmt.executeQuery("update TEST set NAME='28' where ID=1"),
"Given statement type does not match that declared by JDBC driver"
);
- ResultSet rs = stmt.executeQuery("select val from test where _key=1");
+ ResultSet rs = stmt.executeQuery("select NAME from TEST where ID=1");
boolean next = rs.next();
assertTrue(next);
- assertEquals(
- 1,
- rs.getInt(1),
+ assertEquals(1, rs.getInt(1),
"The data must not be updated. "
+ "Because update statement is executed via 'executeQuery' method."
- + " Data [val=" + rs.getInt(1) + ']'
- );
+ + " Data [val=" + rs.getInt(1) + ']');
}
}
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcUpdateStatementSelfTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcUpdateStatementSelfTest.java
new file mode 100644
index 0000000..baaa1a1
--- /dev/null
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/jdbc/ItJdbcUpdateStatementSelfTest.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.runner.app.jdbc;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.sql.SQLException;
+import org.apache.ignite.table.KeyValueView;
+import org.apache.ignite.table.Tuple;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Update statement self test.
+ */
+@Disabled("https://issues.apache.org/jira/browse/IGNITE-15655")
+public class ItJdbcUpdateStatementSelfTest extends ItJdbcAbstractStatementSelfTest {
+ /**
+ * Execute test.
+ *
+ * @throws SQLException If failed.
+ */
+ @Test
+ public void testExecute() throws SQLException {
+ stmt.execute("update PUBLIC.PERSON set firstName = 'Jack' where substring(SID, 2, 1)::int % 2 = 0");
+
+ KeyValueView<Tuple, Tuple> person = clusterNodes.get(0).tables()
+ .table("PUBLIC.PERSON").keyValueView();
+
+ assertEquals("John", person.get(null, Tuple.create().set("ID", 1)).stringValue("FIRSTNAME"));
+ assertEquals("Jack", person.get(null, Tuple.create().set("ID", 2)).stringValue("FIRSTNAME"));
+ assertEquals("Mike", person.get(null, Tuple.create().set("ID", 3)).stringValue("FIRSTNAME"));
+ }
+
+ /**
+ * Execute update test.
+ *
+ * @throws SQLException If failed.
+ */
+ @Test
+ public void testExecuteUpdate() throws SQLException {
+ int i = stmt.executeUpdate(
+ "update PUBLIC.PERSON set firstName = 'Jack' where substring(SID, 2, 1)::int % 2 = 0");
+
+ assertEquals(1, i);
+
+ KeyValueView<Tuple, Tuple> person = clusterNodes.get(0).tables()
+ .table("PUBLIC.PERSON").keyValueView();
+
+ assertEquals("John", person.get(null, Tuple.create().set("ID", 1)).stringValue("FIRSTNAME"));
+ assertEquals("Jack", person.get(null, Tuple.create().set("ID", 2)).stringValue("FIRSTNAME"));
+ assertEquals("Mike", person.get(null, Tuple.create().set("ID", 3)).stringValue("FIRSTNAME"));
+ }
+}