This is an automated email from the ASF dual-hosted git repository.
zstan pushed a commit to branch jdbc_over_thin_sql
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/jdbc_over_thin_sql by this
push:
new ffe62e80175 IGNITE-26276 Sql. Jdbc. Add new implementation for
ResultSetMetadata (#6488)
ffe62e80175 is described below
commit ffe62e80175c69502112cab48970a032892372a2
Author: Max Zhuravkov <[email protected]>
AuthorDate: Thu Aug 28 07:36:36 2025 +0300
IGNITE-26276 Sql. Jdbc. Add new implementation for ResultSetMetadata (#6488)
---
.../internal/jdbc/proto/event/JdbcColumnMeta.java | 10 +-
.../ignite/client/handler/ItClientHandlerTest.java | 1 +
.../internal/jdbc/JdbcResultSetMetadata.java | 37 ++-
.../internal/jdbc2/JdbcResultSetMetadata.java | 223 ++++++++++++++
.../ignite/internal/jdbc/ColumnDefinition.java | 67 +++++
.../jdbc/JdbcResultSetMetadataBaseSelfTest.java | 320 +++++++++++++++++++++
.../jdbc/JdbcResultSetMetadataSelfTest.java | 68 +++++
.../jdbc2/JdbcResultSetMetadata2SelfTest.java | 84 ++++++
8 files changed, 798 insertions(+), 12 deletions(-)
diff --git
a/modules/client-common/src/main/java/org/apache/ignite/internal/jdbc/proto/event/JdbcColumnMeta.java
b/modules/client-common/src/main/java/org/apache/ignite/internal/jdbc/proto/event/JdbcColumnMeta.java
index 5ae1473852b..7fa56b49802 100644
---
a/modules/client-common/src/main/java/org/apache/ignite/internal/jdbc/proto/event/JdbcColumnMeta.java
+++
b/modules/client-common/src/main/java/org/apache/ignite/internal/jdbc/proto/event/JdbcColumnMeta.java
@@ -303,7 +303,7 @@ public class JdbcColumnMeta extends Response {
* @param columnType Column type.
* @return SQL type name.
*/
- private static String typeName(ColumnType columnType) {
+ public static String typeName(ColumnType columnType) {
switch (columnType) {
case BOOLEAN: return "BOOLEAN";
case INT8: return "TINYINT";
@@ -331,7 +331,13 @@ public class JdbcColumnMeta extends Response {
}
}
- private static int typeId(ColumnType columnType) {
+ /**
+ * Converts column type to SQL type id.
+ *
+ * @param columnType Column type.
+ * @return SQL type id.
+ */
+ public static int typeId(ColumnType columnType) {
switch (columnType) {
case BOOLEAN: return BOOLEAN;
case INT8: return TINYINT;
diff --git
a/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/ItClientHandlerTest.java
b/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/ItClientHandlerTest.java
index 883e0687ea8..6358a799ce3 100644
---
a/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/ItClientHandlerTest.java
+++
b/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/ItClientHandlerTest.java
@@ -546,6 +546,7 @@ public class ItClientHandlerTest extends
BaseIgniteAbstractTest {
expected.set(9);
expected.set(10);
expected.set(11);
+ expected.set(12);
assertEquals(expected, supportedFeatures);
var extensionsLen = unpacker.unpackInt();
diff --git
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcResultSetMetadata.java
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcResultSetMetadata.java
index 0cc9b79d324..09abc1f185b 100644
---
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcResultSetMetadata.java
+++
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcResultSetMetadata.java
@@ -52,121 +52,131 @@ public class JdbcResultSetMetadata implements
ResultSetMetaData {
/** {@inheritDoc} */
@Override
public boolean isAutoIncrement(int col) throws SQLException {
+ getColumn(col);
return false;
}
/** {@inheritDoc} */
@Override
public boolean isCaseSensitive(int col) throws SQLException {
+ getColumn(col);
return false;
}
/** {@inheritDoc} */
@Override
public boolean isSearchable(int col) throws SQLException {
+ getColumn(col);
return false;
}
/** {@inheritDoc} */
@Override
public boolean isCurrency(int col) throws SQLException {
+ getColumn(col);
return false;
}
/** {@inheritDoc} */
@Override
public int isNullable(int col) throws SQLException {
- return columnNullable;
+ return getColumn(col).isNullable() ? columnNullable : columnNoNulls;
}
/** {@inheritDoc} */
@Override
public boolean isSigned(int col) throws SQLException {
+ getColumn(col);
return true;
}
/** {@inheritDoc} */
@Override
public int getColumnDisplaySize(int col) throws SQLException {
+ getColumn(col);
return COL_WIDTH;
}
/** {@inheritDoc} */
@Override
public String getColumnLabel(int col) throws SQLException {
- return meta.get(col - 1).columnLabel();
+ return getColumn(col).columnLabel();
}
/** {@inheritDoc} */
@Override
public String getColumnName(int col) throws SQLException {
- return meta.get(col - 1).columnName();
+ return getColumn(col).columnName();
}
/** {@inheritDoc} */
@Override
public String getSchemaName(int col) throws SQLException {
- return meta.get(col - 1).schemaName();
+ return getColumn(col).schemaName();
}
/** {@inheritDoc} */
@Override
public int getPrecision(int col) throws SQLException {
- return meta.get(col - 1).precision();
+ return getColumn(col).precision();
}
/** {@inheritDoc} */
@Override
public int getScale(int col) throws SQLException {
- return meta.get(col - 1).scale();
+ return getColumn(col).scale();
}
/** {@inheritDoc} */
@Override
public String getTableName(int col) throws SQLException {
- return meta.get(col - 1).tableName();
+ return getColumn(col).tableName();
}
/** {@inheritDoc} */
@Override
public String getCatalogName(int col) throws SQLException {
+ getColumn(col);
return "";
}
/** {@inheritDoc} */
@Override
public int getColumnType(int col) throws SQLException {
- return meta.get(col - 1).dataType();
+ return getColumn(col).dataType();
}
/** {@inheritDoc} */
@Override
public String getColumnTypeName(int col) throws SQLException {
- return meta.get(col - 1).dataTypeName();
+ return getColumn(col).dataTypeName();
}
/** {@inheritDoc} */
@Override
public boolean isReadOnly(int col) throws SQLException {
+ getColumn(col);
return true;
}
/** {@inheritDoc} */
@Override
public boolean isWritable(int col) throws SQLException {
+ getColumn(col);
return false;
}
/** {@inheritDoc} */
@Override
public boolean isDefinitelyWritable(int col) throws SQLException {
+ getColumn(col);
return false;
}
/** {@inheritDoc} */
@Override
public String getColumnClassName(int col) throws SQLException {
- return meta.get(col - 1).dataTypeClass();
+ return getColumn(col).dataTypeClass();
}
/** {@inheritDoc} */
@@ -184,4 +194,11 @@ public class JdbcResultSetMetadata implements
ResultSetMetaData {
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return iface != null &&
iface.isAssignableFrom(JdbcResultSetMetadata.class);
}
+
+ private JdbcColumnMeta getColumn(int col) throws SQLException {
+ if (col < 1 || col > meta.size()) {
+ throw new SQLException("Invalid column index: " + col);
+ }
+ return meta.get(col - 1);
+ }
}
diff --git
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSetMetadata.java
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSetMetadata.java
new file mode 100644
index 00000000000..6eba7145a3a
--- /dev/null
+++
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSetMetadata.java
@@ -0,0 +1,223 @@
+/*
+ * 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.jdbc2;
+
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.List;
+import org.apache.ignite.internal.jdbc.JdbcConverterUtils;
+import org.apache.ignite.internal.jdbc.proto.event.JdbcColumnMeta;
+import org.apache.ignite.sql.ColumnMetadata;
+import org.apache.ignite.sql.ColumnMetadata.ColumnOrigin;
+import org.apache.ignite.sql.ColumnType;
+import org.apache.ignite.sql.ResultSetMetadata;
+
+/**
+ * JDBC result set metadata implementation.
+ */
+public class JdbcResultSetMetadata implements ResultSetMetaData {
+ private static final int COLUMN_DISPLAY_SIZE = 30;
+
+ private final List<ColumnMetadata> cols;
+
+ /**
+ * Constructor.
+ *
+ * @param metadata Metadata.
+ */
+ public JdbcResultSetMetadata(ResultSetMetadata metadata) {
+ if (metadata == null) {
+ throw new IllegalArgumentException("metadata");
+ }
+ this.cols = metadata.columns();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getColumnCount() throws SQLException {
+ return cols.size();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isAutoIncrement(int column) throws SQLException {
+ getColumn(column);
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCaseSensitive(int column) throws SQLException {
+ getColumn(column);
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isSearchable(int column) throws SQLException {
+ getColumn(column);
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCurrency(int column) throws SQLException {
+ getColumn(column);
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int isNullable(int column) throws SQLException {
+ return getColumn(column).nullable() ? columnNullable : columnNoNulls;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isSigned(int column) throws SQLException {
+ getColumn(column);
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getColumnDisplaySize(int column) throws SQLException {
+ getColumn(column);
+ return COLUMN_DISPLAY_SIZE;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getColumnLabel(int column) throws SQLException {
+ ColumnMetadata metadata = getColumn(column);
+ return metadata.name();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getColumnName(int column) throws SQLException {
+ ColumnMetadata metadata = getColumn(column);
+ ColumnOrigin origin = metadata.origin();
+ // Compatibility with the existing driver
+ if (origin != null && origin.columnName() != null) {
+ return origin.columnName();
+ } else {
+ return metadata.name();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getSchemaName(int column) throws SQLException {
+ ColumnMetadata.ColumnOrigin origin = getColumn(column).origin();
+ // Compatibility with the existing driver
+ return origin != null ? origin.schemaName() : null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getPrecision(int column) throws SQLException {
+ return getColumn(column).precision();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getScale(int column) throws SQLException {
+ return getColumn(column).scale();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getTableName(int column) throws SQLException {
+ ColumnMetadata.ColumnOrigin origin = getColumn(column).origin();
+ // Compatibility with the existing driver
+ return origin != null ? origin.tableName() : null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getCatalogName(int column) throws SQLException {
+ getColumn(column);
+ return "";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getColumnType(int column) throws SQLException {
+ ColumnType columnType = getColumn(column).type();
+ return JdbcColumnMeta.typeId(columnType);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getColumnTypeName(int column) throws SQLException {
+ ColumnType columnType = getColumn(column).type();
+ return JdbcColumnMeta.typeName(columnType);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isReadOnly(int column) throws SQLException {
+ getColumn(column);
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isWritable(int column) throws SQLException {
+ getColumn(column);
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isDefinitelyWritable(int column) throws SQLException {
+ getColumn(column);
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getColumnClassName(int column) throws SQLException {
+ ColumnType columnType = getColumn(column).type();
+ return JdbcConverterUtils.columnTypeToJdbcClass(columnType).getName();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public <T> T unwrap(Class<T> iface) throws SQLException {
+ if (!isWrapperFor(iface)) {
+ throw new SQLException("Result set meta data is not a wrapper for
" + iface.getName());
+ }
+ return iface.cast(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isWrapperFor(Class<?> iface) throws SQLException {
+ return iface != null &&
iface.isAssignableFrom(JdbcResultSetMetadata.class);
+ }
+
+ private ColumnMetadata getColumn(int idx) throws SQLException {
+ // JDBC columns are 1-based
+ if (idx < 1 || idx > cols.size()) {
+ throw new SQLException("Invalid column index: " + idx);
+ }
+ return cols.get(idx - 1);
+ }
+}
diff --git
a/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/ColumnDefinition.java
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/ColumnDefinition.java
new file mode 100644
index 00000000000..77d09118a96
--- /dev/null
+++
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/ColumnDefinition.java
@@ -0,0 +1,67 @@
+/*
+ * 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.jdbc;
+
+import org.apache.ignite.sql.ColumnType;
+import org.jetbrains.annotations.Nullable;
+
+/** Column definition. */
+public final class ColumnDefinition {
+ public final String label;
+ public final String schema;
+ public final String table;
+ public final String column;
+ public final ColumnType type;
+ public final int precision;
+ public final int scale;
+ public final boolean nullable;
+
+ ColumnDefinition(
+ String label,
+ ColumnType type,
+ int precision,
+ int scale,
+ boolean nullable
+ ) {
+ this(label, null, null, null, type, precision, scale, nullable);
+ }
+
+ private ColumnDefinition(
+ String label,
+ @Nullable String schema,
+ @Nullable String table,
+ @Nullable String column,
+ ColumnType type,
+ int precision,
+ int scale,
+ boolean nullable
+ ) {
+ this.label = label;
+ this.schema = schema;
+ this.table = table;
+ this.column = column;
+ this.type = type;
+ this.precision = precision;
+ this.scale = scale;
+ this.nullable = nullable;
+ }
+
+ ColumnDefinition withOrigin(@Nullable String schema, @Nullable String
table, @Nullable String column) {
+ return new ColumnDefinition(label, schema, table, column, type,
precision, scale, nullable);
+ }
+}
diff --git
a/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/JdbcResultSetMetadataBaseSelfTest.java
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/JdbcResultSetMetadataBaseSelfTest.java
new file mode 100644
index 00000000000..8fa2106e349
--- /dev/null
+++
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/JdbcResultSetMetadataBaseSelfTest.java
@@ -0,0 +1,320 @@
+/*
+ * 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.jdbc;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.List;
+import org.apache.ignite.internal.jdbc.proto.event.JdbcColumnMeta;
+import org.apache.ignite.sql.ColumnType;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+import org.junit.jupiter.params.provider.ValueSource;
+
+/**
+ * Tests for {@link ResultSetMetaData} implementations.
+ */
+public abstract class JdbcResultSetMetadataBaseSelfTest {
+
+ protected static final ColumnDefinition COLUMN = new ColumnDefinition("L",
ColumnType.DECIMAL, 10, 5, true);
+
+ private static final int COLUMN_DISPLAY_SIZE = 30;
+
+ protected abstract ResultSetMetaData createMeta(List<ColumnDefinition>
columns);
+
+ @Test
+ public void columnCount() throws SQLException {
+ {
+ ResultSetMetaData md = createMeta(List.of(COLUMN));
+ assertEquals(1, md.getColumnCount());
+ }
+ {
+ ResultSetMetaData md = createMeta(List.of(COLUMN, COLUMN));
+ assertEquals(2, md.getColumnCount());
+ }
+ }
+
+ @ParameterizedTest
+ @EnumSource(ColumnType.class)
+ public void isAutoIncrement(ColumnType columnType) throws SQLException {
+ ColumnDefinition column = new ColumnDefinition("C", columnType, 0, 0,
true);
+ ResultSetMetaData md = createMeta(List.of(column));
+ assertFalse(md.isAutoIncrement(1));
+ }
+
+ @ParameterizedTest
+ @EnumSource(ColumnType.class)
+ public void isCaseSensitive(ColumnType columnType) throws SQLException {
+ ColumnDefinition column = new ColumnDefinition("C", columnType, 0, 0,
true);
+ ResultSetMetaData md = createMeta(List.of(column));
+ assertFalse(md.isCaseSensitive(1));
+ }
+
+ @ParameterizedTest
+ @EnumSource(ColumnType.class)
+ public void isSearchable(ColumnType columnType) throws SQLException {
+ ColumnDefinition column = new ColumnDefinition("C", columnType, 0, 0,
true);
+ ResultSetMetaData md = createMeta(List.of(column));
+ assertFalse(md.isSearchable(1));
+ }
+
+ @ParameterizedTest
+ @EnumSource(ColumnType.class)
+ public void isCurrency(ColumnType columnType) throws SQLException {
+ ColumnDefinition column = new ColumnDefinition("C", columnType, 0, 0,
true);
+ ResultSetMetaData md = createMeta(List.of(column));
+ assertFalse(md.isCurrency(1));
+ }
+
+ @ParameterizedTest
+ @EnumSource(ColumnType.class)
+ public void getColumnDisplaySize(ColumnType columnType) throws
SQLException {
+ ColumnDefinition column = new ColumnDefinition("C", columnType, 0, 0,
true);
+ ResultSetMetaData md = createMeta(List.of(column));
+ assertEquals(COLUMN_DISPLAY_SIZE, md.getColumnDisplaySize(1));
+ }
+
+ @Test
+ public void getColumnLabel() throws SQLException {
+ ResultSetMetaData md = createMeta(List.of(
+ new ColumnDefinition("LABEL1", ColumnType.INT8, 0, 0, true),
+ new ColumnDefinition("label2", ColumnType.INT8, 0, 0,
true).withOrigin("S", "T", null),
+ new ColumnDefinition("Label3", ColumnType.INT8, 0, 0,
true).withOrigin("S", "T", "COL")
+ ));
+
+ assertEquals("LABEL1", md.getColumnLabel(1));
+ assertEquals("label2", md.getColumnLabel(2));
+ assertEquals("Label3", md.getColumnLabel(3));
+ }
+
+ @Test
+ public void getColumnName() throws SQLException {
+ ResultSetMetaData md = createMeta(List.of(
+ new ColumnDefinition("COLUMN1", ColumnType.INT8, 0, 0, true),
+ new ColumnDefinition("Column2", ColumnType.INT8, 0, 0,
true).withOrigin("S", "T", null),
+ new ColumnDefinition("C3", ColumnType.INT8, 0, 0,
true).withOrigin("S", "T", "Column")
+ ));
+
+ assertEquals("COLUMN1", md.getColumnName(1));
+ assertEquals("Column2", md.getColumnName(2));
+ assertEquals("Column", md.getColumnName(3));
+ }
+
+ @Test
+ public void getSchemaName() throws SQLException {
+ ResultSetMetaData md = createMeta(List.of(
+ new ColumnDefinition("C1", ColumnType.INT8, 0, 0, true),
+ new ColumnDefinition("C2", ColumnType.INT8, 0, 0,
true).withOrigin("S", "T", null),
+ new ColumnDefinition("Schema", ColumnType.INT8, 0, 0,
true).withOrigin("Schema", "T", null)
+ ));
+
+ assertNull(md.getSchemaName(1));
+ assertEquals("S", md.getSchemaName(2));
+ assertEquals("Schema", md.getSchemaName(3));
+ }
+
+ @Test
+ public void getTableName() throws SQLException {
+ ResultSetMetaData md = createMeta(List.of(
+ new ColumnDefinition("C1", ColumnType.INT8, 0, 0, true),
+ new ColumnDefinition("C2", ColumnType.INT8, 0, 0,
true).withOrigin("S", "T", null),
+ new ColumnDefinition("C3", ColumnType.INT8, 0, 0,
true).withOrigin("S", "Table", null)
+ ));
+
+ assertNull(md.getTableName(1));
+ assertEquals("T", md.getTableName(2));
+ assertEquals("Table", md.getTableName(3));
+ }
+
+ @Test
+ public void getCatalogName() throws SQLException {
+ ResultSetMetaData md = createMeta(List.of(COLUMN));
+ assertEquals("", md.getCatalogName(1));
+ }
+
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ public void isNullable(boolean nullable) throws SQLException {
+ ColumnDefinition column = new ColumnDefinition("C", ColumnType.INT8,
0, 0, nullable);
+ ResultSetMetaData md = createMeta(List.of(column));
+
+ int indicator = nullable ? ResultSetMetaData.columnNullable :
ResultSetMetaData.columnNoNulls;
+ assertEquals(indicator, md.isNullable(1));
+ }
+
+ @ParameterizedTest
+ @EnumSource(ColumnType.class)
+ public void isSigned(ColumnType columnType) throws SQLException {
+ ColumnDefinition column = new ColumnDefinition("C", columnType, 0, 0,
true);
+ ResultSetMetaData md = createMeta(List.of(column));
+ assertTrue(md.isSigned(1));
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {-1, 0, 1, 10})
+ public void getPrecision(int precision) throws SQLException {
+ ColumnDefinition column = new ColumnDefinition("C",
ColumnType.DECIMAL, precision, 0, true);
+ ResultSetMetaData md = createMeta(List.of(column));
+
+ assertEquals(column.precision, md.getPrecision(1));
+ }
+
+ @ParameterizedTest
+ @EnumSource(ColumnType.class)
+ public void getColumnType(ColumnType columnType) throws SQLException {
+ ColumnDefinition column = new ColumnDefinition("C", columnType, 10, 5,
true);
+
+ ResultSetMetaData md = createMeta(List.of(column));
+ assertEquals(JdbcColumnMeta.typeId(column.type), md.getColumnType(1));
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {-1, 0, 1, 10})
+ public void getScale(int scale) throws SQLException {
+ ColumnDefinition column = new ColumnDefinition("C",
ColumnType.DECIMAL, 0, scale, true);
+ ResultSetMetaData md = createMeta(List.of(column));
+
+ assertEquals(column.scale, md.getScale(1));
+ }
+
+ @ParameterizedTest
+ @EnumSource(ColumnType.class)
+ public void getColumnTypeName(ColumnType columnType) throws SQLException {
+ ColumnDefinition column = new ColumnDefinition("C", columnType, 0, 0,
true);
+
+ ResultSetMetaData md = createMeta(List.of(column));
+ assertEquals(JdbcColumnMeta.typeName(column.type),
md.getColumnTypeName(1));
+ }
+
+ @Test
+ public void isReadOnly() throws SQLException {
+ ResultSetMetaData md = createMeta(List.of(COLUMN));
+ assertTrue(md.isReadOnly(1));
+ }
+
+ @Test
+ public void isWritable() throws SQLException {
+ ResultSetMetaData md = createMeta(List.of(COLUMN));
+ assertFalse(md.isWritable(1));
+ }
+
+ @Test
+ public void isDefinitelyWritable() throws SQLException {
+ ResultSetMetaData md = createMeta(List.of(COLUMN));
+ assertFalse(md.isDefinitelyWritable(1));
+ }
+
+ @ParameterizedTest
+ @EnumSource(ColumnType.class)
+ public void getColumnClassName(ColumnType columnType) throws SQLException {
+ ColumnDefinition column = new ColumnDefinition("C", columnType, 0, 0,
true);
+ ResultSetMetaData md = createMeta(List.of(column));
+
+ String typeClassName =
JdbcConverterUtils.columnTypeToJdbcClass(column.type).getName();
+ assertEquals(typeClassName, md.getColumnClassName(1));
+ }
+
+ @Test
+ public void unwrapAndIsWrapperFor() throws SQLException {
+ ResultSetMetaData md = createMeta(List.of(COLUMN));
+ assertTrue(md.isWrapperFor(ResultSetMetaData.class));
+ assertDoesNotThrow(() -> md.unwrap(ResultSetMetaData.class));
+ }
+
+ @Test
+ public void methodsExpectValidColumn() {
+ ResultSetMetaData md = createMeta(List.of(COLUMN));
+
+ expectInvalidColumnException(md::isAutoIncrement, 0);
+ expectInvalidColumnException(md::isAutoIncrement, 2);
+
+ expectInvalidColumnException(md::isCaseSensitive, 0);
+ expectInvalidColumnException(md::isCaseSensitive, 2);
+
+ expectInvalidColumnException(md::isSearchable, 0);
+ expectInvalidColumnException(md::isSearchable, 2);
+
+ expectInvalidColumnException(md::isCurrency, 0);
+ expectInvalidColumnException(md::isCurrency, 2);
+
+ expectInvalidColumnException(md::isNullable, 0);
+ expectInvalidColumnException(md::isNullable, 2);
+
+ expectInvalidColumnException(md::isSigned, 0);
+ expectInvalidColumnException(md::isSigned, 2);
+
+ expectInvalidColumnException(md::getColumnDisplaySize, 0);
+ expectInvalidColumnException(md::getColumnDisplaySize, 2);
+
+ expectInvalidColumnException(md::getColumnName, 0);
+ expectInvalidColumnException(md::getColumnName, 2);
+
+ expectInvalidColumnException(md::getSchemaName, 0);
+ expectInvalidColumnException(md::getSchemaName, 2);
+
+ expectInvalidColumnException(md::getPrecision, 0);
+ expectInvalidColumnException(md::getPrecision, 2);
+
+ expectInvalidColumnException(md::getScale, 0);
+ expectInvalidColumnException(md::getScale, 2);
+
+ expectInvalidColumnException(md::getTableName, 0);
+ expectInvalidColumnException(md::getTableName, 2);
+
+ expectInvalidColumnException(md::getCatalogName, 0);
+ expectInvalidColumnException(md::getCatalogName, 2);
+
+ expectInvalidColumnException(md::getColumnType, 0);
+ expectInvalidColumnException(md::getColumnType, 2);
+
+ expectInvalidColumnException(md::getColumnTypeName, 0);
+ expectInvalidColumnException(md::getColumnTypeName, 2);
+
+ expectInvalidColumnException(md::isReadOnly, 0);
+ expectInvalidColumnException(md::isReadOnly, 2);
+
+ expectInvalidColumnException(md::isWritable, 0);
+ expectInvalidColumnException(md::isWritable, 2);
+
+ expectInvalidColumnException(md::isDefinitelyWritable, 0);
+ expectInvalidColumnException(md::isDefinitelyWritable, 2);
+
+ expectInvalidColumnException(md::getColumnClassName, 0);
+ expectInvalidColumnException(md::getColumnClassName, 2);
+ }
+
+ private static void expectInvalidColumnException(ColumnMetadataMethod m,
int column) {
+ SQLException err = assertThrows(SQLException.class, () ->
m.call(column));
+ assertThat(err.getMessage(), containsString("Invalid column index: " +
column));
+ }
+
+ @FunctionalInterface
+ private interface ColumnMetadataMethod {
+ void call(int column) throws SQLException;
+ }
+}
diff --git
a/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/JdbcResultSetMetadataSelfTest.java
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/JdbcResultSetMetadataSelfTest.java
new file mode 100644
index 00000000000..b5cda963204
--- /dev/null
+++
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/JdbcResultSetMetadataSelfTest.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.jdbc;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.ignite.internal.jdbc.proto.event.JdbcColumnMeta;
+import org.apache.ignite.sql.ResultSet;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for {@link JdbcResultSetMetadata}.
+ */
+public class JdbcResultSetMetadataSelfTest extends
JdbcResultSetMetadataBaseSelfTest {
+ @Override
+ protected ResultSetMetaData createMeta(List<ColumnDefinition> columns) {
+ List<JdbcColumnMeta> jdbcColumns = new ArrayList<>();
+ for (ColumnDefinition s : columns) {
+ jdbcColumns.add(new JdbcColumnMeta(s.label, s.schema, s.table,
s.column, s.type, s.precision, s.scale, s.nullable));
+ }
+ return new JdbcResultSetMetadata(jdbcColumns);
+ }
+
+ @Test
+ @Override
+ public void unwrapAndIsWrapperFor() throws SQLException {
+ ResultSetMetaData md = createMeta(List.of(COLUMN));
+ {
+ assertTrue(md.isWrapperFor(ResultSetMetaData.class));
+ assertDoesNotThrow(() -> md.unwrap(ResultSetMetaData.class));
+ }
+
+ {
+ assertTrue(md.isWrapperFor(JdbcResultSetMetadata.class));
+ assertDoesNotThrow(() -> md.unwrap(JdbcResultSetMetadata.class));
+ }
+
+ {
+ assertFalse(md.isWrapperFor(ResultSet.class));
+ SQLException err = assertThrows(SQLException.class, () ->
md.unwrap(ResultSet.class));
+ assertThat(err.getMessage(), containsString("Result set meta data
is not a wrapper for " + ResultSet.class.getName()));
+ }
+ }
+}
diff --git
a/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc2/JdbcResultSetMetadata2SelfTest.java
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc2/JdbcResultSetMetadata2SelfTest.java
new file mode 100644
index 00000000000..775e3d594f1
--- /dev/null
+++
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc2/JdbcResultSetMetadata2SelfTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.jdbc2;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.ignite.internal.jdbc.ColumnDefinition;
+import org.apache.ignite.internal.jdbc.JdbcResultSetMetadataBaseSelfTest;
+import org.apache.ignite.internal.sql.ColumnMetadataImpl;
+import org.apache.ignite.internal.sql.ColumnMetadataImpl.ColumnOriginImpl;
+import org.apache.ignite.internal.sql.ResultSetMetadataImpl;
+import org.apache.ignite.sql.ColumnMetadata;
+import org.apache.ignite.sql.ResultSet;
+import org.apache.ignite.sql.ResultSetMetadata;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for {@link JdbcResultSetMetadata}.
+ */
+public class JdbcResultSetMetadata2SelfTest extends
JdbcResultSetMetadataBaseSelfTest {
+
+ @Test
+ @Override
+ public void unwrapAndIsWrapperFor() throws SQLException {
+ ResultSetMetaData md = createMeta(List.of(COLUMN));
+ {
+ assertTrue(md.isWrapperFor(ResultSetMetaData.class));
+ assertDoesNotThrow(() -> md.unwrap(ResultSetMetaData.class));
+ }
+
+ {
+ assertTrue(md.isWrapperFor(JdbcResultSetMetadata.class));
+ assertDoesNotThrow(() -> md.unwrap(JdbcResultSetMetadata.class));
+ }
+
+ {
+ assertFalse(md.isWrapperFor(ResultSet.class));
+ SQLException err = assertThrows(SQLException.class, () ->
md.unwrap(ResultSet.class));
+ assertThat(err.getMessage(), containsString("Result set meta data
is not a wrapper for " + ResultSet.class.getName()));
+ }
+ }
+
+ @Override
+ protected ResultSetMetaData createMeta(List<ColumnDefinition> columns) {
+ List<ColumnMetadata> columnsMeta = new ArrayList<>();
+
+ for (ColumnDefinition s : columns) {
+ ColumnOriginImpl origin;
+ if (s.schema != null) {
+ origin = new ColumnOriginImpl(s.schema, s.table, s.column);
+ } else {
+ origin = null;
+ }
+
+ columnsMeta.add(new ColumnMetadataImpl(s.label, s.type,
s.precision, s.scale, s.nullable, origin));
+ }
+ ResultSetMetadata apiMeta = new ResultSetMetadataImpl(columnsMeta);
+ return new JdbcResultSetMetadata(apiMeta);
+ }
+}