This is an automated email from the ASF dual-hosted git repository. jooger pushed a commit to branch jdbc_over_thin_sql in repository https://gitbox.apache.org/repos/asf/ignite-3.git
commit d7e92ce689d5c7e1e4c67b625378151533b139c4 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); + } +}
