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 ae3800ea82 IGNITE-22588 Fix performance of JDBC driver (#4012)
ae3800ea82 is described below

commit ae3800ea82dbaa8c0d0050912d4e62723c257ea6
Author: korlov42 <[email protected]>
AuthorDate: Fri Jun 28 16:57:13 2024 +0300

    IGNITE-22588 Fix performance of JDBC driver (#4012)
    
    Co-authored-by: Igor Sapego <[email protected]>
---
 .../ignite/internal/client/proto/ClientOp.java     |   3 -
 .../jdbc/proto/JdbcQueryCursorHandler.java         |  10 -
 .../internal/jdbc/proto/JdbcStatementType.java     |   2 +-
 .../internal/jdbc/proto/event/JdbcColumnMeta.java  | 235 ++++------
 .../jdbc/proto/event/JdbcQuerySingleResult.java    |  46 +-
 .../handler/ClientInboundMessageHandler.java       |   4 -
 .../ignite/client/handler/JdbcHandlerBase.java     |  50 +-
 .../client/handler/JdbcQueryCursorHandlerImpl.java |  65 ---
 .../jdbc/ClientJdbcQueryMetadataRequest.java       |  49 --
 .../handler/requests/jdbc/JdbcMetadataCatalog.java |   2 +-
 .../apache/ignite/internal/client/ClientUtils.java |   3 -
 .../org/apache/ignite/client/RetryPolicyTest.java  |   2 +-
 .../internal/jdbc/ItJdbcMetadataSelfTest.java      |  42 +-
 .../jdbc/JdbcClientQueryCursorHandler.java         |  14 -
 .../ignite/internal/jdbc/JdbcDatabaseMetadata.java | 503 +++++++++++----------
 .../apache/ignite/internal/jdbc/JdbcResultSet.java |  77 +---
 .../apache/ignite/internal/jdbc/JdbcStatement.java |  14 +-
 .../ignite/internal/jdbc/JdbcResultSetTest.java    |   2 +-
 .../ignite/odbc/query/column_metadata_query.cpp    |  32 +-
 .../cpp/ignite/odbc/query/column_metadata_query.h  |   3 -
 .../ignite/internal/sql/engine/util/Commons.java   |  22 -
 21 files changed, 475 insertions(+), 705 deletions(-)

diff --git 
a/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientOp.java
 
b/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientOp.java
index 4d85005e53..45706d23a7 100644
--- 
a/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientOp.java
+++ 
b/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientOp.java
@@ -105,9 +105,6 @@ public class ClientOp {
     /** Get primary key metadata. */
     public static final int JDBC_PK_META = 41;
 
-    /** Get query metadata. */
-    public static final int JDBC_QUERY_META = 42;
-
     /** Begin transaction. */
     public static final int TX_BEGIN = 43;
 
diff --git 
a/modules/client-common/src/main/java/org/apache/ignite/internal/jdbc/proto/JdbcQueryCursorHandler.java
 
b/modules/client-common/src/main/java/org/apache/ignite/internal/jdbc/proto/JdbcQueryCursorHandler.java
index 69eae806e4..2f841322e9 100644
--- 
a/modules/client-common/src/main/java/org/apache/ignite/internal/jdbc/proto/JdbcQueryCursorHandler.java
+++ 
b/modules/client-common/src/main/java/org/apache/ignite/internal/jdbc/proto/JdbcQueryCursorHandler.java
@@ -20,11 +20,9 @@ package org.apache.ignite.internal.jdbc.proto;
 import java.sql.Statement;
 import java.util.concurrent.CompletableFuture;
 import 
org.apache.ignite.internal.jdbc.proto.event.JdbcFetchQueryResultsRequest;
-import org.apache.ignite.internal.jdbc.proto.event.JdbcMetaColumnsResult;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcQueryCloseRequest;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcQueryCloseResult;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcQueryFetchResult;
-import org.apache.ignite.internal.jdbc.proto.event.JdbcQueryMetadataRequest;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcQuerySingleResult;
 
 /**
@@ -54,12 +52,4 @@ public interface JdbcQueryCursorHandler {
      * @return Result future.
      */
     CompletableFuture<JdbcQueryCloseResult> closeAsync(JdbcQueryCloseRequest 
req);
-
-    /**
-     * {@link JdbcQueryMetadataRequest} command handler.
-     *
-     * @param req Jdbc query metadata request.
-     * @return Result future.
-     */
-    CompletableFuture<JdbcMetaColumnsResult> 
queryMetadataAsync(JdbcQueryMetadataRequest req);
 }
diff --git 
a/modules/client-common/src/main/java/org/apache/ignite/internal/jdbc/proto/JdbcStatementType.java
 
b/modules/client-common/src/main/java/org/apache/ignite/internal/jdbc/proto/JdbcStatementType.java
index 69097a6942..33eca32496 100644
--- 
a/modules/client-common/src/main/java/org/apache/ignite/internal/jdbc/proto/JdbcStatementType.java
+++ 
b/modules/client-common/src/main/java/org/apache/ignite/internal/jdbc/proto/JdbcStatementType.java
@@ -53,7 +53,7 @@ public enum JdbcStatementType {
     public static JdbcStatementType getStatement(byte id) {
         JdbcStatementType value = STATEMENT_TYPE_IDX.get(id);
 
-        Objects.requireNonNull(value, String.format("Unknown jdbcStatementType 
%s", id));
+        Objects.requireNonNull(value, () -> String.format("Unknown 
jdbcStatementType %s", id));
 
         return value;
     }
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 df1d31bfcf..d31c9dbfe2 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
@@ -33,16 +33,14 @@ import static java.sql.Types.TINYINT;
 import static java.sql.Types.VARBINARY;
 import static java.sql.Types.VARCHAR;
 
-import java.math.BigDecimal;
-import java.sql.Time;
-import java.sql.Timestamp;
-import java.sql.Types;
-import java.util.Date;
 import java.util.Objects;
-import java.util.UUID;
 import org.apache.ignite.internal.client.proto.ClientMessagePacker;
 import org.apache.ignite.internal.client.proto.ClientMessageUnpacker;
+import org.apache.ignite.internal.jdbc.JdbcConverterUtils;
+import org.apache.ignite.internal.lang.IgniteInternalException;
 import org.apache.ignite.internal.tostring.S;
+import org.apache.ignite.lang.ErrorGroups.Common;
+import org.apache.ignite.sql.ColumnType;
 
 /**
  * JDBC column metadata.
@@ -64,10 +62,7 @@ public class JdbcColumnMeta extends Response {
     private String colName;
 
     /** Data type. */
-    private int dataType;
-
-    /** Data type name. */
-    private String dataTypeName;
+    private ColumnType columnType;
 
     /** Precision. */
     private int precision;
@@ -75,9 +70,6 @@ public class JdbcColumnMeta extends Response {
     /** Scale. */
     private int scale;
 
-    /** Data type class. */
-    private String dataTypeCls;
-
     /**
      * Default constructor is used for serialization.
      */
@@ -88,10 +80,10 @@ public class JdbcColumnMeta extends Response {
      * Constructor.
      *
      * @param label Column label.
-     * @param cls   Type.
+     * @param columnType Type.
      */
-    public JdbcColumnMeta(String label, Class<?> cls) {
-        this(label, null, null, null, cls, -1, -1, true);
+    public JdbcColumnMeta(String label, ColumnType columnType) {
+        this(label, null, null, null, columnType, -1, -1, true);
     }
 
     /**
@@ -101,58 +93,22 @@ public class JdbcColumnMeta extends Response {
      * @param schemaName Schema.
      * @param tblName    Table.
      * @param colName    Column.
-     * @param cls        Type.
+     * @param columnType Type.
      * @param nullable   Nullable flag.
      * @param precision  Column precision.
      * @param scale      Column scale.
      */
-    public JdbcColumnMeta(String label, String schemaName, String tblName, 
String colName, Class<?> cls, int precision, int scale,
-            boolean nullable) {
-        this(label, schemaName, tblName, colName, cls.getName(), precision, 
scale, nullable);
-    }
-
-    /**
-     * Constructor with nullable flag.
-     *
-     * @param label        Column label.
-     * @param schemaName   Schema.
-     * @param tblName      Table.
-     * @param colName      Column.
-     * @param javaTypeName Java type name.
-     * @param nullable     Nullable flag.
-     * @param precision    Column precision.
-     * @param scale        Column scale.
-     */
-    public JdbcColumnMeta(String label, String schemaName, String tblName, 
String colName, String javaTypeName, int precision, int scale,
-            boolean nullable) {
-        this(label, schemaName, tblName, colName, type(javaTypeName), 
typeName(javaTypeName), javaTypeName, precision, scale, nullable);
-    }
-
-    /**
-     * Constructor.
-     *
-     * @param label        Column label.
-     * @param schemaName   Schema.
-     * @param tblName      Table.
-     * @param colName      Column.
-     * @param dataType     JDBC datatype id.
-     * @param dataTypeName JDBC datatype name.
-     * @param javaTypeName Java type name.
-     * @param precision    Column precision.
-     * @param scale        Column scale.
-     * @param nullable     Nullable flag.
-     */
-    public JdbcColumnMeta(String label, String schemaName, String tblName, 
String colName, int dataType, String dataTypeName,
-            String javaTypeName, int precision, int scale, boolean nullable) {
+    public JdbcColumnMeta(
+            String label, String schemaName, String tblName, String colName, 
ColumnType columnType,
+            int precision, int scale, boolean nullable
+    ) {
         this.label = label;
         this.schemaName = schemaName;
         this.tblName = tblName;
         this.colName = colName;
         this.nullable = nullable;
 
-        this.dataType = dataType;
-        this.dataTypeName = dataTypeName;
-        this.dataTypeCls = javaTypeName;
+        this.columnType = columnType;
         this.precision = precision;
         this.scale = scale;
     }
@@ -193,13 +149,18 @@ public class JdbcColumnMeta extends Response {
         return colName != null ? colName : label;
     }
 
+    /** Gets column type. */
+    public ColumnType columnType() {
+        return columnType;
+    }
+
     /**
      * Gets data type id.
      *
      * @return Column's data type.
      */
     public int dataType() {
-        return dataType;
+        return typeId(columnType);
     }
 
     /**
@@ -208,7 +169,7 @@ public class JdbcColumnMeta extends Response {
      * @return Column's data type name.
      */
     public String dataTypeName() {
-        return dataTypeName;
+        return typeName(columnType);
     }
 
     /**
@@ -253,7 +214,7 @@ public class JdbcColumnMeta extends Response {
      * @return Data type class.
      */
     public String dataTypeClass() {
-        return dataTypeCls;
+        return JdbcConverterUtils.columnTypeToJdbcClass(columnType).getName();
     }
 
     /** {@inheritDoc} */
@@ -270,9 +231,7 @@ public class JdbcColumnMeta extends Response {
         ClientMessageUtils.writeStringNullable(packer, tblName);
         ClientMessageUtils.writeStringNullable(packer, colName);
 
-        packer.packInt(dataType);
-        packer.packString(dataTypeName);
-        packer.packString(dataTypeCls);
+        packer.packInt(columnType.id());
         packer.packBoolean(nullable);
         packer.packInt(precision);
         packer.packInt(scale);
@@ -292,9 +251,7 @@ public class JdbcColumnMeta extends Response {
         tblName = ClientMessageUtils.readStringNullable(unpacker);
         colName = ClientMessageUtils.readStringNullable(unpacker);
 
-        dataType = unpacker.unpackInt();
-        dataTypeName = unpacker.unpackString();
-        dataTypeCls = unpacker.unpackString();
+        columnType = ColumnType.getById(unpacker.unpackInt());
         nullable = unpacker.unpackBoolean();
         precision = unpacker.unpackInt();
         scale = unpacker.unpackInt();
@@ -313,14 +270,12 @@ public class JdbcColumnMeta extends Response {
 
         JdbcColumnMeta meta = (JdbcColumnMeta) o;
         return nullable == meta.nullable
-                && dataType == meta.dataType
+                && columnType == meta.columnType
                 && precision == meta.precision
                 && scale == meta.scale
                 && Objects.equals(schemaName, meta.schemaName)
                 && Objects.equals(tblName, meta.tblName)
-                && Objects.equals(colName, meta.colName)
-                && Objects.equals(dataTypeCls, meta.dataTypeCls)
-                && Objects.equals(dataTypeName, meta.dataTypeName);
+                && Objects.equals(colName, meta.colName);
     }
 
     /** {@inheritDoc} */
@@ -330,9 +285,7 @@ public class JdbcColumnMeta extends Response {
         result = 31 * result + (schemaName != null ? schemaName.hashCode() : 
0);
         result = 31 * result + (tblName != null ? tblName.hashCode() : 0);
         result = 31 * result + (colName != null ? colName.hashCode() : 0);
-        result = 31 * result + (dataTypeCls != null ? dataTypeCls.hashCode() : 
0);
-        result = 31 * result + dataType;
-        result = 31 * result + (dataTypeName != null ? dataTypeName.hashCode() 
: 0);
+        result = 31 * result + columnType.id();
         result = 31 * result + precision;
         result = 31 * result + scale;
         return result;
@@ -345,88 +298,68 @@ public class JdbcColumnMeta extends Response {
     }
 
     /**
-     * Converts Java class name to type from {@link Types}.
+     * Converts column type to SQL type name.
      *
-     * @param cls Java class name.
-     * @return Type from {@link Types}.
+     * @param columnType Column type.
+     * @return SQL type name.
      */
-    private static int type(String cls) {
-        if (Boolean.class.getName().equals(cls) || 
boolean.class.getName().equals(cls)) {
-            return BOOLEAN;
-        } else if (Byte.class.getName().equals(cls) || 
byte.class.getName().equals(cls)) {
-            return TINYINT;
-        } else if (Short.class.getName().equals(cls) || 
short.class.getName().equals(cls)) {
-            return SMALLINT;
-        } else if (Integer.class.getName().equals(cls) || 
int.class.getName().equals(cls)) {
-            return INTEGER;
-        } else if (Long.class.getName().equals(cls) || 
long.class.getName().equals(cls)) {
-            return BIGINT;
-        } else if (Float.class.getName().equals(cls) || 
float.class.getName().equals(cls)) {
-            return REAL;
-        } else if (Double.class.getName().equals(cls) || 
double.class.getName().equals(cls)) {
-            return DOUBLE;
-        } else if (String.class.getName().equals(cls)) {
-            return VARCHAR;
-        } else if (byte[].class.getName().equals(cls)) {
-            return VARBINARY;
-        } else if (Time.class.getName().equals(cls)) {
-            return TIME;
-        } else if (Timestamp.class.getName().equals(cls)) {
-            return TIMESTAMP;
-        } else if (Date.class.getName().equals(cls) || 
java.sql.Date.class.getName().equals(cls)) {
-            return DATE;
-        } else if (BigDecimal.class.getName().equals(cls)) {
-            return DECIMAL;
-        } else if (Void.class.getName().equals(cls)) {
-            return NULL;
-        } else {
-            // IgniteCustomType: All custom data types are Types.OTHER.
-            // But every custom type may have a designated name (see typeName 
method).
-            return OTHER;
+    private static String typeName(ColumnType columnType) {
+        switch (columnType) {
+            case BOOLEAN: return "BOOLEAN";
+            case INT8: return "TINYINT";
+            case INT16: return "SMALLINT";
+            case INT32: return "INTEGER";
+            case INT64: return "BIGINT";
+            case FLOAT: return "REAL";
+            case DOUBLE: return "DOUBLE";
+            case STRING: return "VARCHAR";
+            case BYTE_ARRAY: return "VARBINARY";
+            case TIME: return "TIME";
+            case DATETIME: return "TIMESTAMP";
+            case TIMESTAMP: return "TIMESTAMP WITH LOCAL TIME ZONE";
+            case DATE: return "DATE";
+            case DECIMAL: return "DECIMAL";
+            case NULL: return "NULL";
+            case UUID: return "UUID";
+            case NUMBER:
+            case BITMASK:
+            case PERIOD:
+            case DURATION:
+                // IgniteCustomType: JDBC spec allows database dependent type 
name. See DatabaseMetadata::getColumns (TYPE_NAME column);
+                // So include JDBC TYPE_NAME of your type otherwise its type 
name is going to be OTHER.
+                return "OTHER";
+            default:
+                throw new IgniteInternalException(Common.INTERNAL_ERR, 
"Unknown column type: " + columnType);
         }
     }
 
-    /**
-     * Converts Java class name to SQL type name.
-     *
-     * @param cls Java class name.
-     * @return SQL type name.
-     */
-    private static String typeName(String cls) {
-        if (Boolean.class.getName().equals(cls) || 
boolean.class.getName().equals(cls)) {
-            return "BOOLEAN";
-        } else if (Byte.class.getName().equals(cls) || 
byte.class.getName().equals(cls)) {
-            return "TINYINT";
-        } else if (Short.class.getName().equals(cls) || 
short.class.getName().equals(cls)) {
-            return "SMALLINT";
-        } else if (Integer.class.getName().equals(cls) || 
int.class.getName().equals(cls)) {
-            return "INTEGER";
-        } else if (Long.class.getName().equals(cls) || 
long.class.getName().equals(cls)) {
-            return "BIGINT";
-        } else if (Float.class.getName().equals(cls) || 
float.class.getName().equals(cls)) {
-            return "REAL";
-        } else if (Double.class.getName().equals(cls) || 
double.class.getName().equals(cls)) {
-            return "DOUBLE";
-        } else if (String.class.getName().equals(cls)) {
-            return "VARCHAR";
-        } else if (byte[].class.getName().equals(cls)) {
-            return "VARBINARY";
-        } else if (Time.class.getName().equals(cls)) {
-            return "TIME";
-        } else if (Timestamp.class.getName().equals(cls)) {
-            return "TIMESTAMP";
-        } else if (Date.class.getName().equals(cls) || 
java.sql.Date.class.getName().equals(cls)) {
-            return "DATE";
-        } else if (BigDecimal.class.getName().equals(cls)) {
-            return "DECIMAL";
-        } else if (Void.class.getName().equals(cls)) {
-            return "NULL";
-        } else if (UUID.class.getName().equals(cls)) {
-            return "UUID";
-        } else {
-            // IgniteCustomType: JDBC spec allows database dependent type 
name. See DatabaseMetadata::getColumns (TYPE_NAME column);
-            // So include JDBC TYPE_NAME of your type otherwise its type name 
is going to be OTHER.
-            return "OTHER";
+    private static int typeId(ColumnType columnType) {
+        switch (columnType) {
+            case BOOLEAN: return BOOLEAN;
+            case INT8: return TINYINT;
+            case INT16: return SMALLINT;
+            case INT32: return INTEGER;
+            case INT64: return BIGINT;
+            case FLOAT: return REAL;
+            case DOUBLE: return DOUBLE;
+            case STRING: return VARCHAR;
+            case BYTE_ARRAY: return VARBINARY;
+            case TIME: return TIME;
+            case DATETIME: return TIMESTAMP;
+            case DATE: return DATE;
+            case DECIMAL: return DECIMAL;
+            case NULL: return NULL;
+            case UUID:
+            case NUMBER:
+            case BITMASK:
+            case PERIOD:
+            case DURATION:
+            case TIMESTAMP:
+                // IgniteCustomType: JDBC spec allows database dependent type 
name. See DatabaseMetadata::getColumns (TYPE_NAME column);
+                // So include JDBC TYPE_NAME of your type otherwise its type 
name is going to be OTHER.
+                return OTHER;
+            default:
+                throw new IgniteInternalException(Common.INTERNAL_ERR, 
"Unknown column type: " + columnType);
         }
     }
 }
diff --git 
a/modules/client-common/src/main/java/org/apache/ignite/internal/jdbc/proto/event/JdbcQuerySingleResult.java
 
b/modules/client-common/src/main/java/org/apache/ignite/internal/jdbc/proto/event/JdbcQuerySingleResult.java
index e1701c080b..07512d0a12 100644
--- 
a/modules/client-common/src/main/java/org/apache/ignite/internal/jdbc/proto/event/JdbcQuerySingleResult.java
+++ 
b/modules/client-common/src/main/java/org/apache/ignite/internal/jdbc/proto/event/JdbcQuerySingleResult.java
@@ -24,7 +24,6 @@ import 
org.apache.ignite.internal.binarytuple.BinaryTupleReader;
 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.sql.ColumnType;
 import org.jetbrains.annotations.Nullable;
 
 /**
@@ -50,10 +49,7 @@ public class JdbcQuerySingleResult extends Response {
     private boolean hasMoreData;
 
     /** Ordered list of types of columns in serialized rows. Null only when 
result has no resultSet. */
-    private @Nullable List<ColumnType> columnTypes;
-
-    /** Decimal scales in appearance order. Can be empty in case no any 
decimal columns, or null when result has no resultSet. */
-    private int @Nullable [] decimalScales;
+    private @Nullable List<JdbcColumnMeta> meta;
 
     // === Attributes of response without result set ===
 
@@ -80,8 +76,7 @@ public class JdbcQuerySingleResult extends Response {
      *
      * @param cursorId Id of the cursor in case it was registered on server.
      * @param rowTuples Serialized SQL result rows.
-     * @param columnTypes Ordered list of types of columns in serialized rows.
-     * @param decimalScales Decimal scales in appearance order.
+     * @param meta List of columns-related metadata.
      * @param hasMoreData Flag indicates the query has un-fetched results.
      * @param hasNextResult Flag indicates that current result is part of 
multi-statement query, there is at least one more result.
      */
@@ -89,8 +84,7 @@ public class JdbcQuerySingleResult extends Response {
     public JdbcQuerySingleResult(
             @Nullable Long cursorId,
             List<BinaryTupleReader> rowTuples,
-            List<ColumnType> columnTypes,
-            int[] decimalScales,
+            List<JdbcColumnMeta> meta,
             boolean hasMoreData,
             boolean hasNextResult
     ) {
@@ -98,15 +92,12 @@ public class JdbcQuerySingleResult extends Response {
 
         this.cursorId = cursorId;
         this.rowTuples = rowTuples;
-        this.columnTypes = columnTypes;
-        this.decimalScales = decimalScales;
+        this.meta = meta;
 
         this.hasMoreData = hasMoreData;
         this.hasNextResult = hasNextResult;
 
         hasResultSet = true;
-
-        assert decimalScales != null;
     }
 
     /**
@@ -132,14 +123,8 @@ public class JdbcQuerySingleResult extends Response {
         return rowTuples;
     }
 
-    /** Return types of columns in serialized rows if result has result set, 
return null otherwise. */
-    public @Nullable List<ColumnType> columnTypes() {
-        return columnTypes;
-    }
-
-    /** Return decimal scales in appearance order in columns if result has 
result set, return null otherwise. */
-    public int @Nullable [] decimalScales() {
-        return decimalScales;
+    public @Nullable List<JdbcColumnMeta> meta() {
+        return meta;
     }
 
     /** Returns {@code true} if there is more data available in current result 
set. */
@@ -186,16 +171,14 @@ public class JdbcQuerySingleResult extends Response {
             return;
         }
 
-        assert decimalScales != null;
-        assert columnTypes != null;
         assert rowTuples != null;
+        assert meta != null;
 
         packer.packBoolean(hasMoreData);
-        packer.packIntArray(decimalScales);
 
-        packer.packInt(this.columnTypes.size());
-        for (ColumnType columnType : this.columnTypes) {
-            packer.packInt(columnType.id());
+        packer.packInt(meta.size());
+        for (JdbcColumnMeta columnMeta : meta) {
+            columnMeta.writeBinary(packer);
         }
 
         packer.packInt(rowTuples.size());
@@ -230,13 +213,16 @@ public class JdbcQuerySingleResult extends Response {
         }
 
         hasMoreData = unpacker.unpackBoolean();
-        decimalScales = unpacker.unpackIntArray();
 
         int count = unpacker.unpackInt();
-        columnTypes = new ArrayList<>(count);
+        meta = new ArrayList<>(count);
 
         for (int i = 0; i < count; i++) {
-            columnTypes.add(ColumnType.getById(unpacker.unpackInt()));
+            var columnMeta = new JdbcColumnMeta();
+
+            columnMeta.readBinary(unpacker);
+
+            meta.add(columnMeta);
         }
 
         int size = unpacker.unpackInt();
diff --git 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientInboundMessageHandler.java
 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientInboundMessageHandler.java
index 0a2c8154e1..a1f40a502f 100644
--- 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientInboundMessageHandler.java
+++ 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientInboundMessageHandler.java
@@ -59,7 +59,6 @@ import 
org.apache.ignite.client.handler.requests.jdbc.ClientJdbcFinishTxRequest;
 import org.apache.ignite.client.handler.requests.jdbc.ClientJdbcHasMoreRequest;
 import 
org.apache.ignite.client.handler.requests.jdbc.ClientJdbcPreparedStmntBatchRequest;
 import 
org.apache.ignite.client.handler.requests.jdbc.ClientJdbcPrimaryKeyMetadataRequest;
-import 
org.apache.ignite.client.handler.requests.jdbc.ClientJdbcQueryMetadataRequest;
 import 
org.apache.ignite.client.handler.requests.jdbc.ClientJdbcSchemasMetadataRequest;
 import 
org.apache.ignite.client.handler.requests.jdbc.ClientJdbcTableMetadataRequest;
 import org.apache.ignite.client.handler.requests.jdbc.JdbcMetadataCatalog;
@@ -726,9 +725,6 @@ public class ClientInboundMessageHandler extends 
ChannelInboundHandlerAdapter im
             case ClientOp.JDBC_PK_META:
                 return ClientJdbcPrimaryKeyMetadataRequest.process(in, out, 
jdbcQueryEventHandler);
 
-            case ClientOp.JDBC_QUERY_META:
-                return ClientJdbcQueryMetadataRequest.process(in, out, 
jdbcQueryCursorHandler);
-
             case ClientOp.TX_BEGIN:
                 return ClientTransactionBeginRequest.process(in, out, 
igniteTransactions, resources, metrics);
 
diff --git 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/JdbcHandlerBase.java
 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/JdbcHandlerBase.java
index 42a9fa3ec5..6d75096261 100644
--- 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/JdbcHandlerBase.java
+++ 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/JdbcHandlerBase.java
@@ -20,10 +20,10 @@ package org.apache.ignite.client.handler;
 import static 
org.apache.ignite.internal.jdbc.proto.IgniteQueryErrorCode.UNSUPPORTED_OPERATION;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.CompletionStage;
 import org.apache.ignite.internal.binarytuple.BinaryTupleReader;
+import org.apache.ignite.internal.jdbc.proto.event.JdbcColumnMeta;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcQuerySingleResult;
 import org.apache.ignite.internal.jdbc.proto.event.Response;
 import org.apache.ignite.internal.lang.IgniteInternalCheckedException;
@@ -80,9 +80,8 @@ abstract class JdbcHandlerBase {
             switch (cur.queryType()) {
                 case EXPLAIN:
                 case QUERY: {
-                    if (cursorId == null) {
-                        // for queries with result set we still need to save 
cursor to resources, so later we can
-                        // derive result's metadata which is loaded on demand 
by driver
+                    if (cursorId == null && batch.hasMore()) {
+                        // more fetches are expected, so let's keep the cursor 
in resources
                         try {
                             cursorId = resources.put(new ClientResource(cur, 
cur::closeAsync));
                         } catch (IgniteInternalCheckedException e) {
@@ -129,19 +128,13 @@ abstract class JdbcHandlerBase {
             rows.add(item.asBinaryTuple());
         }
 
-        int[] decimalScales = new int[columns.size()];
-        List<ColumnType> schema = new ArrayList<>(columns.size());
+        List<JdbcColumnMeta> meta = new ArrayList<>(columns.size());
 
-        int countOfDecimal = 0;
         for (ColumnMetadata column : columns) {
-            schema.add(column.type());
-            if (column.type() == ColumnType.DECIMAL) {
-                decimalScales[countOfDecimal++] = column.scale();
-            }
+            meta.add(createColumnMetadata(column));
         }
-        decimalScales = Arrays.copyOf(decimalScales, countOfDecimal);
 
-        return new JdbcQuerySingleResult(cursorId, rows, schema, 
decimalScales, batch.hasMore(), hasNextResult);
+        return new JdbcQuerySingleResult(cursorId, rows, meta, 
batch.hasMore(), hasNextResult);
     }
 
     JdbcQuerySingleResult createErrorResult(String logMessage, Throwable 
origin, @Nullable String errMessagePrefix) {
@@ -189,4 +182,35 @@ abstract class JdbcHandlerBase {
         Throwable cause = ExceptionUtils.unwrapCause(t);
         return cause.getMessage();
     }
+
+    /**
+     * Create Jdbc representation of column metadata from given origin and 
RelDataTypeField field.
+     *
+     * @param fldMeta field metadata contains info about column.
+     * @return JdbcColumnMeta object.
+     */
+    private static JdbcColumnMeta createColumnMetadata(ColumnMetadata fldMeta) 
{
+        ColumnMetadata.ColumnOrigin origin = fldMeta.origin();
+
+        String schemaName = null;
+        String tblName = null;
+        String colName = null;
+
+        if (origin != null) {
+            schemaName = origin.schemaName();
+            tblName = origin.tableName();
+            colName = origin.columnName();
+        }
+
+        return new JdbcColumnMeta(
+                fldMeta.name(),
+                schemaName,
+                tblName,
+                colName,
+                fldMeta.type(),
+                fldMeta.precision(),
+                fldMeta.scale(),
+                fldMeta.nullable()
+        );
+    }
 }
diff --git 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/JdbcQueryCursorHandlerImpl.java
 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/JdbcQueryCursorHandlerImpl.java
index 4cc235aa76..d02c7a0f16 100644
--- 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/JdbcQueryCursorHandlerImpl.java
+++ 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/JdbcQueryCursorHandlerImpl.java
@@ -24,23 +24,16 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
 import java.util.function.Function;
-import java.util.stream.Collectors;
-import org.apache.ignite.internal.jdbc.JdbcConverterUtils;
 import org.apache.ignite.internal.jdbc.proto.JdbcQueryCursorHandler;
-import org.apache.ignite.internal.jdbc.proto.event.JdbcColumnMeta;
 import 
org.apache.ignite.internal.jdbc.proto.event.JdbcFetchQueryResultsRequest;
-import org.apache.ignite.internal.jdbc.proto.event.JdbcMetaColumnsResult;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcQueryCloseRequest;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcQueryCloseResult;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcQueryFetchResult;
-import org.apache.ignite.internal.jdbc.proto.event.JdbcQueryMetadataRequest;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcQuerySingleResult;
 import org.apache.ignite.internal.jdbc.proto.event.Response;
 import org.apache.ignite.internal.lang.IgniteInternalCheckedException;
 import org.apache.ignite.internal.sql.engine.AsyncSqlCursor;
 import org.apache.ignite.internal.sql.engine.InternalSqlRow;
-import org.apache.ignite.sql.ColumnMetadata;
-import org.apache.ignite.sql.ResultSetMetadata;
 
 /**
  * Jdbc query event handler implementation.
@@ -169,64 +162,6 @@ public class JdbcQueryCursorHandlerImpl extends 
JdbcHandlerBase implements JdbcQ
         });
     }
 
-    /** {@inheritDoc} */
-    @Override
-    public CompletableFuture<JdbcMetaColumnsResult> 
queryMetadataAsync(JdbcQueryMetadataRequest req) {
-        AsyncSqlCursor<List<Object>> asyncSqlCursor;
-        try {
-            asyncSqlCursor = 
resources.get(req.cursorId()).get(AsyncSqlCursor.class);
-        } catch (IgniteInternalCheckedException e) {
-            StringWriter sw = getWriterWithStackTrace(e);
-
-            return CompletableFuture.completedFuture(new 
JdbcMetaColumnsResult(Response.STATUS_FAILED,
-                    "Failed to find query cursor [curId=" + req.cursorId() + 
"]. Error message:" + sw));
-        }
-
-        ResultSetMetadata metadata = asyncSqlCursor.metadata();
-
-        if (metadata == null) {
-            return CompletableFuture.completedFuture(new 
JdbcMetaColumnsResult(Response.STATUS_FAILED,
-                    "Failed to get query metadata for cursor [curId=" + 
req.cursorId() + ']'));
-        }
-
-        List<JdbcColumnMeta> meta = metadata.columns().stream()
-                .map(this::createColumnMetadata)
-                .collect(Collectors.toList());
-
-        return CompletableFuture.completedFuture(new 
JdbcMetaColumnsResult(meta));
-    }
-
-    /**
-     * Create Jdbc representation of column metadata from given origin and 
RelDataTypeField field.
-     *
-     * @param fldMeta field metadata contains info about column.
-     * @return JdbcColumnMeta object.
-     */
-    private JdbcColumnMeta createColumnMetadata(ColumnMetadata fldMeta) {
-        ColumnMetadata.ColumnOrigin origin = fldMeta.origin();
-
-        String schemaName = null;
-        String tblName = null;
-        String colName = null;
-
-        if (origin != null) {
-            schemaName = origin.schemaName();
-            tblName = origin.tableName();
-            colName = origin.columnName();
-        }
-
-        return new JdbcColumnMeta(
-            fldMeta.name(),
-            schemaName,
-            tblName,
-            colName,
-            JdbcConverterUtils.columnTypeToJdbcClass(fldMeta.type()),
-            fldMeta.precision(),
-            fldMeta.scale(),
-            fldMeta.nullable()
-        );
-    }
-
     /**
      * Serializes the stack trace of given exception for further sending to 
the client.
      *
diff --git 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/jdbc/ClientJdbcQueryMetadataRequest.java
 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/jdbc/ClientJdbcQueryMetadataRequest.java
deleted file mode 100644
index 047f2c59e3..0000000000
--- 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/jdbc/ClientJdbcQueryMetadataRequest.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.client.handler.requests.jdbc;
-
-import java.util.concurrent.CompletableFuture;
-import org.apache.ignite.internal.client.proto.ClientMessagePacker;
-import org.apache.ignite.internal.client.proto.ClientMessageUnpacker;
-import org.apache.ignite.internal.jdbc.proto.JdbcQueryCursorHandler;
-import org.apache.ignite.internal.jdbc.proto.event.JdbcQueryMetadataRequest;
-
-/**
- * Client jdbc query metadata request handler.
- */
-public class ClientJdbcQueryMetadataRequest {
-    /**
-     * Processes remote {@code JdbcQueryMetadataRequest}.
-     *
-     * @param in      Client message unpacker.
-     * @param out     Client message packer.
-     * @param handler Query event handler.
-     * @return Operation future.
-     */
-    public static CompletableFuture<Void> process(
-            ClientMessageUnpacker in,
-            ClientMessagePacker out,
-            JdbcQueryCursorHandler handler
-    ) {
-        var req = new JdbcQueryMetadataRequest();
-
-        req.readBinary(in);
-
-        return handler.queryMetadataAsync(req).thenAccept(res -> 
res.writeBinary(out));
-    }
-}
diff --git 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/jdbc/JdbcMetadataCatalog.java
 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/jdbc/JdbcMetadataCatalog.java
index 9df0a27c62..9ae753c488 100644
--- 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/jdbc/JdbcMetadataCatalog.java
+++ 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/jdbc/JdbcMetadataCatalog.java
@@ -234,7 +234,7 @@ public class JdbcMetadataCatalog {
                 SqlCommon.DEFAULT_SCHEMA_NAME,
                 tblName,
                 col.name(),
-                Commons.nativeTypeToJdbcClass(colType),
+                colType.spec().asColumnType(), 
                 Commons.nativeTypePrecision(colType),
                 Commons.nativeTypeScale(colType),
                 col.nullable()
diff --git 
a/modules/client/src/main/java/org/apache/ignite/internal/client/ClientUtils.java
 
b/modules/client/src/main/java/org/apache/ignite/internal/client/ClientUtils.java
index db6c93cdfa..ff886ef002 100644
--- 
a/modules/client/src/main/java/org/apache/ignite/internal/client/ClientUtils.java
+++ 
b/modules/client/src/main/java/org/apache/ignite/internal/client/ClientUtils.java
@@ -200,9 +200,6 @@ public class ClientUtils {
             case ClientOp.JDBC_PK_META:
                 return null;
 
-            case ClientOp.JDBC_QUERY_META:
-                return null;
-
             case ClientOp.TX_BEGIN:
             case ClientOp.TX_COMMIT:
             case ClientOp.TX_ROLLBACK:
diff --git 
a/modules/client/src/test/java/org/apache/ignite/client/RetryPolicyTest.java 
b/modules/client/src/test/java/org/apache/ignite/client/RetryPolicyTest.java
index b87b85fb38..99c36e8df0 100644
--- a/modules/client/src/test/java/org/apache/ignite/client/RetryPolicyTest.java
+++ b/modules/client/src/test/java/org/apache/ignite/client/RetryPolicyTest.java
@@ -221,7 +221,7 @@ public class RetryPolicyTest extends BaseIgniteAbstractTest 
{
             }
         }
 
-        long expectedNullCount = 22;
+        long expectedNullCount = 21;
 
         String msg = nullOpFields.size()
                 + " operation codes do not have public equivalent. When adding 
new codes, update ClientOperationType too. Missing ops: "
diff --git 
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/internal/jdbc/ItJdbcMetadataSelfTest.java
 
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/internal/jdbc/ItJdbcMetadataSelfTest.java
index f6bac123ea..967500f7b2 100644
--- 
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/internal/jdbc/ItJdbcMetadataSelfTest.java
+++ 
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/internal/jdbc/ItJdbcMetadataSelfTest.java
@@ -135,9 +135,7 @@ public class ItJdbcMetadataSelfTest extends 
AbstractJdbcSelfTest {
                             rs.getString("TABLE_SCHEM"),
                             rs.getString("TABLE_NAME"),
                             rs.getString("COLUMN_NAME"),
-                            rs.getInt("DATA_TYPE"),
-                            rs.getString("TYPE_NAME"),
-                            dataTypeToJavaCls(rs.getInt("DATA_TYPE"), 
rs.getString("TYPE_NAME")),
+                            dataTypeToColumnType(rs.getInt("DATA_TYPE"), 
rs.getString("TYPE_NAME")),
                             rs.getShort("COLUMN_SIZE"),
                             rs.getShort("DECIMAL_DIGITS"),
                             "YES".equals(rs.getString("IS_NULLABLE"))
@@ -153,61 +151,63 @@ public class ItJdbcMetadataSelfTest extends 
AbstractJdbcSelfTest {
         }
     }
 
-    private String dataTypeToJavaCls(int dataType, String typeName) {
-        Class cls = null;
+    private ColumnType dataTypeToColumnType(int dataType, String typeName) {
+        ColumnType type = null;
 
         switch (dataType) {
             case Types.BOOLEAN:
-                cls = Boolean.class;
+                type = ColumnType.BOOLEAN;
                 break;
             case Types.TINYINT:
-                cls = Byte.class;
+                type = ColumnType.INT8;
                 break;
             case Types.SMALLINT:
-                cls = Short.class;
+                type = ColumnType.INT16;
                 break;
             case Types.INTEGER:
-                cls = Integer.class;
+                type = ColumnType.INT32;
                 break;
             case Types.BIGINT:
-                cls = Long.class;
+                type = ColumnType.INT64;
                 break;
             case Types.REAL:
-                cls = Float.class;
+                type = ColumnType.FLOAT;
                 break;
             case Types.DOUBLE:
-                cls = Double.class;
+                type = ColumnType.DOUBLE;
                 break;
             case Types.DECIMAL:
-                cls = BigDecimal.class;
+                type = ColumnType.DECIMAL;
                 break;
             case Types.DATE:
-                cls = java.sql.Date.class;
+                type = ColumnType.DATE;
                 break;
             case Types.TIME:
-                cls = java.sql.Time.class;
+                type = ColumnType.TIME;
                 break;
             case Types.TIMESTAMP:
-                cls = java.sql.Timestamp.class;
+                type = ColumnType.DATETIME;
                 break;
             case Types.OTHER:
                 if (typeName.equals("UUID")) {
-                    cls = UUID.class;
+                    type = ColumnType.UUID;
+                } else if (typeName.equals("TIMESTAMP WITH LOCAL TIME ZONE")) {
+                    type = ColumnType.TIMESTAMP;
                 }
                 break;
             case Types.VARCHAR:
-                cls = String.class;
+                type = ColumnType.STRING;
                 break;
             case Types.VARBINARY:
-                cls = byte[].class;
+                type = ColumnType.BYTE_ARRAY;
                 break;
             default:
                 break;
         }
 
-        assertNotNull(cls, "Not supported type " + dataType + " " + typeName);
+        assertNotNull(type, "Not supported type " + dataType + " " + typeName);
 
-        return cls.getName();
+        return type;
     }
 
     @Test
diff --git 
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcClientQueryCursorHandler.java
 
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcClientQueryCursorHandler.java
index dc6a10baa1..8fd352a116 100644
--- 
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcClientQueryCursorHandler.java
+++ 
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcClientQueryCursorHandler.java
@@ -22,11 +22,9 @@ import org.apache.ignite.internal.client.ClientChannel;
 import org.apache.ignite.internal.client.proto.ClientOp;
 import org.apache.ignite.internal.jdbc.proto.JdbcQueryCursorHandler;
 import 
org.apache.ignite.internal.jdbc.proto.event.JdbcFetchQueryResultsRequest;
-import org.apache.ignite.internal.jdbc.proto.event.JdbcMetaColumnsResult;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcQueryCloseRequest;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcQueryCloseResult;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcQueryFetchResult;
-import org.apache.ignite.internal.jdbc.proto.event.JdbcQueryMetadataRequest;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcQuerySingleResult;
 
 /**
@@ -80,16 +78,4 @@ public class JdbcClientQueryCursorHandler implements 
JdbcQueryCursorHandler {
             return res;
         });
     }
-
-    /** {@inheritDoc} */
-    @Override
-    public CompletableFuture<JdbcMetaColumnsResult> 
queryMetadataAsync(JdbcQueryMetadataRequest req) {
-        return channel.serviceAsync(ClientOp.JDBC_QUERY_META, w -> 
req.writeBinary(w.out()), r -> {
-            JdbcMetaColumnsResult res = new JdbcMetaColumnsResult();
-
-            res.readBinary(r.in());
-
-            return res;
-        });
-    }
 }
diff --git 
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcDatabaseMetadata.java
 
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcDatabaseMetadata.java
index f72b2edf82..bc9da6794b 100644
--- 
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcDatabaseMetadata.java
+++ 
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcDatabaseMetadata.java
@@ -52,6 +52,7 @@ import 
org.apache.ignite.internal.jdbc.proto.event.JdbcMetaTablesResult;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcPrimaryKeyMeta;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcTableMeta;
 import org.apache.ignite.sql.ColumnMetadata;
+import org.apache.ignite.sql.ColumnType;
 
 /**
  * JDBC database metadata implementation.
@@ -792,12 +793,12 @@ public class JdbcDatabaseMetadata implements 
DatabaseMetaData {
     public ResultSet getProcedures(String catalog, String schemaPtrn,
             String procedureNamePtrn) throws SQLException {
         return new JdbcResultSet(Collections.emptyList(), asList(
-                new JdbcColumnMeta("PROCEDURE_CAT", String.class),
-                new JdbcColumnMeta("PROCEDURE_SCHEM", String.class),
-                new JdbcColumnMeta("PROCEDURE_NAME", String.class),
-                new JdbcColumnMeta("REMARKS", String.class),
-                new JdbcColumnMeta("PROCEDURE_TYPE", String.class),
-                new JdbcColumnMeta("SPECIFIC_NAME", String.class)
+                new JdbcColumnMeta("PROCEDURE_CAT", ColumnType.STRING),
+                new JdbcColumnMeta("PROCEDURE_SCHEM", ColumnType.STRING),
+                new JdbcColumnMeta("PROCEDURE_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("REMARKS", ColumnType.STRING),
+                new JdbcColumnMeta("PROCEDURE_TYPE", ColumnType.STRING),
+                new JdbcColumnMeta("SPECIFIC_NAME", ColumnType.STRING)
         ));
     }
 
@@ -806,26 +807,26 @@ public class JdbcDatabaseMetadata implements 
DatabaseMetaData {
     public ResultSet getProcedureColumns(String catalog, String schemaPtrn, 
String procedureNamePtrn,
             String colNamePtrn) throws SQLException {
         return new JdbcResultSet(Collections.emptyList(), asList(
-                new JdbcColumnMeta("PROCEDURE_CAT", String.class),
-                new JdbcColumnMeta("PROCEDURE_SCHEM", String.class),
-                new JdbcColumnMeta("PROCEDURE_NAME", String.class),
-                new JdbcColumnMeta("COLUMN_NAME", String.class),
-                new JdbcColumnMeta("COLUMN_TYPE", Short.class),
-                new JdbcColumnMeta("COLUMN_TYPE", Integer.class),
-                new JdbcColumnMeta("TYPE_NAME", String.class),
-                new JdbcColumnMeta("PRECISION", Integer.class),
-                new JdbcColumnMeta("LENGTH", Integer.class),
-                new JdbcColumnMeta("SCALE", Short.class),
-                new JdbcColumnMeta("RADIX", Short.class),
-                new JdbcColumnMeta("NULLABLE", Short.class),
-                new JdbcColumnMeta("REMARKS", String.class),
-                new JdbcColumnMeta("COLUMN_DEF", String.class),
-                new JdbcColumnMeta("SQL_DATA_TYPE", Integer.class),
-                new JdbcColumnMeta("SQL_DATETIME_SUB", Integer.class),
-                new JdbcColumnMeta("CHAR_OCTET_LENGTH", Integer.class),
-                new JdbcColumnMeta("ORDINAL_POSITION", Integer.class),
-                new JdbcColumnMeta("IS_NULLABLE", String.class),
-                new JdbcColumnMeta("SPECIFIC_NAME", String.class)
+                new JdbcColumnMeta("PROCEDURE_CAT", ColumnType.STRING),
+                new JdbcColumnMeta("PROCEDURE_SCHEM", ColumnType.STRING),
+                new JdbcColumnMeta("PROCEDURE_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("COLUMN_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("COLUMN_TYPE", ColumnType.INT16),
+                new JdbcColumnMeta("COLUMN_TYPE", ColumnType.INT32),
+                new JdbcColumnMeta("TYPE_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("PRECISION", ColumnType.INT32),
+                new JdbcColumnMeta("LENGTH", ColumnType.INT32),
+                new JdbcColumnMeta("SCALE", ColumnType.INT16),
+                new JdbcColumnMeta("RADIX", ColumnType.INT16),
+                new JdbcColumnMeta("NULLABLE", ColumnType.INT16),
+                new JdbcColumnMeta("REMARKS", ColumnType.STRING),
+                new JdbcColumnMeta("COLUMN_DEF", ColumnType.STRING),
+                new JdbcColumnMeta("SQL_DATA_TYPE", ColumnType.INT32),
+                new JdbcColumnMeta("SQL_DATETIME_SUB", ColumnType.INT32),
+                new JdbcColumnMeta("CHAR_OCTET_LENGTH", ColumnType.INT32),
+                new JdbcColumnMeta("ORDINAL_POSITION", ColumnType.INT32),
+                new JdbcColumnMeta("IS_NULLABLE", ColumnType.STRING),
+                new JdbcColumnMeta("SPECIFIC_NAME", ColumnType.STRING)
         ));
     }
 
@@ -836,16 +837,16 @@ public class JdbcDatabaseMetadata implements 
DatabaseMetaData {
         conn.ensureNotClosed();
 
         final List<JdbcColumnMeta> meta = asList(
-                new JdbcColumnMeta("TABLE_CAT", String.class),
-                new JdbcColumnMeta("TABLE_SCHEM", String.class),
-                new JdbcColumnMeta("TABLE_NAME", String.class),
-                new JdbcColumnMeta("TABLE_TYPE", String.class),
-                new JdbcColumnMeta("REMARKS", String.class),
-                new JdbcColumnMeta("TYPE_CAT", String.class),
-                new JdbcColumnMeta("TYPE_SCHEM", String.class),
-                new JdbcColumnMeta("TYPE_NAME", String.class),
-                new JdbcColumnMeta("SELF_REFERENCING_COL_NAME", String.class),
-                new JdbcColumnMeta("REF_GENERATION", String.class));
+                new JdbcColumnMeta("TABLE_CAT", ColumnType.STRING),
+                new JdbcColumnMeta("TABLE_SCHEM", ColumnType.STRING),
+                new JdbcColumnMeta("TABLE_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("TABLE_TYPE", ColumnType.STRING),
+                new JdbcColumnMeta("REMARKS", ColumnType.STRING),
+                new JdbcColumnMeta("TYPE_CAT", ColumnType.STRING),
+                new JdbcColumnMeta("TYPE_SCHEM", ColumnType.STRING),
+                new JdbcColumnMeta("TYPE_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("SELF_REFERENCING_COL_NAME", 
ColumnType.STRING),
+                new JdbcColumnMeta("REF_GENERATION", ColumnType.STRING));
 
         boolean tblTypeMatch = false;
 
@@ -901,8 +902,8 @@ public class JdbcDatabaseMetadata implements 
DatabaseMetaData {
         conn.ensureNotClosed();
 
         List<JdbcColumnMeta> meta = asList(
-                new JdbcColumnMeta("TABLE_SCHEM", String.class),
-                new JdbcColumnMeta("TABLE_CATALOG", String.class)
+                new JdbcColumnMeta("TABLE_SCHEM", ColumnType.STRING),
+                new JdbcColumnMeta("TABLE_CATALOG", ColumnType.STRING)
         );
 
         if (!isValidCatalog(catalog)) {
@@ -942,7 +943,7 @@ public class JdbcDatabaseMetadata implements 
DatabaseMetaData {
     @Override
     public ResultSet getCatalogs() throws SQLException {
         return new JdbcResultSet(singletonList(singletonList(CATALOG_NAME)),
-                asList(new JdbcColumnMeta("TABLE_CAT", String.class)));
+                asList(new JdbcColumnMeta("TABLE_CAT", ColumnType.STRING)));
     }
 
     /** {@inheritDoc} */
@@ -951,7 +952,7 @@ public class JdbcDatabaseMetadata implements 
DatabaseMetaData {
     public ResultSet getTableTypes() throws SQLException {
         return new JdbcResultSet(
                 asList(singletonList(TYPE_TABLE)),
-                asList(new JdbcColumnMeta("TABLE_TYPE", String.class)));
+                asList(new JdbcColumnMeta("TABLE_TYPE", ColumnType.STRING)));
     }
 
     /** {@inheritDoc} */
@@ -960,30 +961,30 @@ public class JdbcDatabaseMetadata implements 
DatabaseMetaData {
         conn.ensureNotClosed();
 
         final List<JdbcColumnMeta> meta = asList(
-                new JdbcColumnMeta("TABLE_CAT", String.class),      // 1
-                new JdbcColumnMeta("TABLE_SCHEM", String.class),    // 2
-                new JdbcColumnMeta("TABLE_NAME", String.class),     // 3
-                new JdbcColumnMeta("COLUMN_NAME", String.class),    // 4
-                new JdbcColumnMeta("DATA_TYPE", Short.class),       // 5
-                new JdbcColumnMeta("TYPE_NAME", String.class),      // 6
-                new JdbcColumnMeta("COLUMN_SIZE", Integer.class),   // 7
-                new JdbcColumnMeta("BUFFER_LENGTH", Integer.class), // 8
-                new JdbcColumnMeta("DECIMAL_DIGITS", Integer.class), // 9
-                new JdbcColumnMeta("NUM_PREC_RADIX", Short.class),  // 10
-                new JdbcColumnMeta("NULLABLE", Short.class),        // 11
-                new JdbcColumnMeta("REMARKS", String.class),        // 12
-                new JdbcColumnMeta("COLUMN_DEF", String.class),     // 13
-                new JdbcColumnMeta("SQL_DATA_TYPE", Integer.class), // 14
-                new JdbcColumnMeta("SQL_DATETIME_SUB", Integer.class), // 15
-                new JdbcColumnMeta("CHAR_OCTET_LENGTH", Integer.class), // 16
-                new JdbcColumnMeta("ORDINAL_POSITION", Integer.class), // 17
-                new JdbcColumnMeta("IS_NULLABLE", String.class),    // 18
-                new JdbcColumnMeta("SCOPE_CATLOG", String.class),   // 19
-                new JdbcColumnMeta("SCOPE_SCHEMA", String.class),   // 20
-                new JdbcColumnMeta("SCOPE_TABLE", String.class),    // 21
-                new JdbcColumnMeta("SOURCE_DATA_TYPE", Short.class), // 22
-                new JdbcColumnMeta("IS_AUTOINCREMENT", String.class), // 23
-                new JdbcColumnMeta("IS_GENERATEDCOLUMN", String.class) // 24
+                new JdbcColumnMeta("TABLE_CAT", ColumnType.STRING),      // 1
+                new JdbcColumnMeta("TABLE_SCHEM", ColumnType.STRING),    // 2
+                new JdbcColumnMeta("TABLE_NAME", ColumnType.STRING),     // 3
+                new JdbcColumnMeta("COLUMN_NAME", ColumnType.STRING),    // 4
+                new JdbcColumnMeta("DATA_TYPE", ColumnType.INT16),       // 5
+                new JdbcColumnMeta("TYPE_NAME", ColumnType.STRING),      // 6
+                new JdbcColumnMeta("COLUMN_SIZE", ColumnType.INT32),   // 7
+                new JdbcColumnMeta("BUFFER_LENGTH", ColumnType.INT32), // 8
+                new JdbcColumnMeta("DECIMAL_DIGITS", ColumnType.INT32), // 9
+                new JdbcColumnMeta("NUM_PREC_RADIX", ColumnType.INT16),  // 10
+                new JdbcColumnMeta("NULLABLE", ColumnType.INT16),        // 11
+                new JdbcColumnMeta("REMARKS", ColumnType.STRING),        // 12
+                new JdbcColumnMeta("COLUMN_DEF", ColumnType.STRING),     // 13
+                new JdbcColumnMeta("SQL_DATA_TYPE", ColumnType.INT32), // 14
+                new JdbcColumnMeta("SQL_DATETIME_SUB", ColumnType.INT32), // 15
+                new JdbcColumnMeta("CHAR_OCTET_LENGTH", ColumnType.INT32), // 
16
+                new JdbcColumnMeta("ORDINAL_POSITION", ColumnType.INT32), // 17
+                new JdbcColumnMeta("IS_NULLABLE", ColumnType.STRING),    // 18
+                new JdbcColumnMeta("SCOPE_CATLOG", ColumnType.STRING),   // 19
+                new JdbcColumnMeta("SCOPE_SCHEMA", ColumnType.STRING),   // 20
+                new JdbcColumnMeta("SCOPE_TABLE", ColumnType.STRING),    // 21
+                new JdbcColumnMeta("SOURCE_DATA_TYPE", ColumnType.INT16), // 22
+                new JdbcColumnMeta("IS_AUTOINCREMENT", ColumnType.STRING), // 
23
+                new JdbcColumnMeta("IS_GENERATEDCOLUMN", ColumnType.STRING) // 
24
         );
 
         if (!isValidCatalog(catalog)) {
@@ -1019,14 +1020,14 @@ public class JdbcDatabaseMetadata implements 
DatabaseMetaData {
     public ResultSet getColumnPrivileges(String catalog, String schema, String 
tbl,
             String colNamePtrn) throws SQLException {
         return new JdbcResultSet(Collections.emptyList(), asList(
-                new JdbcColumnMeta("TABLE_CAT", String.class),
-                new JdbcColumnMeta("TABLE_SCHEM", String.class),
-                new JdbcColumnMeta("TABLE_NAME", String.class),
-                new JdbcColumnMeta("COLUMN_NAME", String.class),
-                new JdbcColumnMeta("GRANTOR", String.class),
-                new JdbcColumnMeta("GRANTEE", String.class),
-                new JdbcColumnMeta("PRIVILEGE", String.class),
-                new JdbcColumnMeta("IS_GRANTABLE", String.class)
+                new JdbcColumnMeta("TABLE_CAT", ColumnType.STRING),
+                new JdbcColumnMeta("TABLE_SCHEM", ColumnType.STRING),
+                new JdbcColumnMeta("TABLE_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("COLUMN_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("GRANTOR", ColumnType.STRING),
+                new JdbcColumnMeta("GRANTEE", ColumnType.STRING),
+                new JdbcColumnMeta("PRIVILEGE", ColumnType.STRING),
+                new JdbcColumnMeta("IS_GRANTABLE", ColumnType.STRING)
         ));
     }
 
@@ -1035,13 +1036,13 @@ public class JdbcDatabaseMetadata implements 
DatabaseMetaData {
     public ResultSet getTablePrivileges(String catalog, String schemaPtrn,
             String tblNamePtrn) throws SQLException {
         return new JdbcResultSet(Collections.emptyList(), asList(
-                new JdbcColumnMeta("TABLE_CAT", String.class),
-                new JdbcColumnMeta("TABLE_SCHEM", String.class),
-                new JdbcColumnMeta("TABLE_NAME", String.class),
-                new JdbcColumnMeta("GRANTOR", String.class),
-                new JdbcColumnMeta("GRANTEE", String.class),
-                new JdbcColumnMeta("PRIVILEGE", String.class),
-                new JdbcColumnMeta("IS_GRANTABLE", String.class)
+                new JdbcColumnMeta("TABLE_CAT", ColumnType.STRING),
+                new JdbcColumnMeta("TABLE_SCHEM", ColumnType.STRING),
+                new JdbcColumnMeta("TABLE_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("GRANTOR", ColumnType.STRING),
+                new JdbcColumnMeta("GRANTEE", ColumnType.STRING),
+                new JdbcColumnMeta("PRIVILEGE", ColumnType.STRING),
+                new JdbcColumnMeta("IS_GRANTABLE", ColumnType.STRING)
         ));
     }
 
@@ -1050,14 +1051,14 @@ public class JdbcDatabaseMetadata implements 
DatabaseMetaData {
     public ResultSet getBestRowIdentifier(String catalog, String schema, 
String tbl, int scope,
             boolean nullable) throws SQLException {
         return new JdbcResultSet(Collections.emptyList(), asList(
-                new JdbcColumnMeta("SCOPE", Short.class),
-                new JdbcColumnMeta("COLUMN_NAME", String.class),
-                new JdbcColumnMeta("DATA_TYPE", Integer.class),
-                new JdbcColumnMeta("TYPE_NAME", String.class),
-                new JdbcColumnMeta("COLUMN_SIZE", Integer.class),
-                new JdbcColumnMeta("BUFFER_LENGTH", Integer.class),
-                new JdbcColumnMeta("DECIMAL_DIGITS", Short.class),
-                new JdbcColumnMeta("PSEUDO_COLUMN", Short.class)
+                new JdbcColumnMeta("SCOPE", ColumnType.INT16),
+                new JdbcColumnMeta("COLUMN_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("DATA_TYPE", ColumnType.INT32),
+                new JdbcColumnMeta("TYPE_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("COLUMN_SIZE", ColumnType.INT32),
+                new JdbcColumnMeta("BUFFER_LENGTH", ColumnType.INT32),
+                new JdbcColumnMeta("DECIMAL_DIGITS", ColumnType.INT16),
+                new JdbcColumnMeta("PSEUDO_COLUMN", ColumnType.INT16)
         ));
     }
 
@@ -1065,14 +1066,14 @@ public class JdbcDatabaseMetadata implements 
DatabaseMetaData {
     @Override
     public ResultSet getVersionColumns(String catalog, String schema, String 
tbl) throws SQLException {
         return new JdbcResultSet(Collections.emptyList(), asList(
-                new JdbcColumnMeta("SCOPE", Short.class),
-                new JdbcColumnMeta("COLUMN_NAME", String.class),
-                new JdbcColumnMeta("DATA_TYPE", Integer.class),
-                new JdbcColumnMeta("TYPE_NAME", String.class),
-                new JdbcColumnMeta("COLUMN_SIZE", Integer.class),
-                new JdbcColumnMeta("BUFFER_LENGTH", Integer.class),
-                new JdbcColumnMeta("DECIMAL_DIGITS", Short.class),
-                new JdbcColumnMeta("PSEUDO_COLUMN", Short.class)
+                new JdbcColumnMeta("SCOPE", ColumnType.INT16),
+                new JdbcColumnMeta("COLUMN_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("DATA_TYPE", ColumnType.INT32),
+                new JdbcColumnMeta("TYPE_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("COLUMN_SIZE", ColumnType.INT32),
+                new JdbcColumnMeta("BUFFER_LENGTH", ColumnType.INT32),
+                new JdbcColumnMeta("DECIMAL_DIGITS", ColumnType.INT16),
+                new JdbcColumnMeta("PSEUDO_COLUMN", ColumnType.INT16)
         ));
     }
 
@@ -1082,12 +1083,12 @@ public class JdbcDatabaseMetadata implements 
DatabaseMetaData {
         conn.ensureNotClosed();
 
         final List<JdbcColumnMeta> meta = asList(
-                new JdbcColumnMeta("TABLE_CAT", String.class),
-                new JdbcColumnMeta("TABLE_SCHEM", String.class),
-                new JdbcColumnMeta("TABLE_NAME", String.class),
-                new JdbcColumnMeta("COLUMN_NAME", String.class),
-                new JdbcColumnMeta("KEY_SEQ", Short.class),
-                new JdbcColumnMeta("PK_NAME", String.class));
+                new JdbcColumnMeta("TABLE_CAT", ColumnType.STRING),
+                new JdbcColumnMeta("TABLE_SCHEM", ColumnType.STRING),
+                new JdbcColumnMeta("TABLE_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("COLUMN_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("KEY_SEQ", ColumnType.INT16),
+                new JdbcColumnMeta("PK_NAME", ColumnType.STRING));
 
         if (!isValidCatalog(catalog)) {
             return new JdbcResultSet(Collections.emptyList(), meta);
@@ -1120,20 +1121,20 @@ public class JdbcDatabaseMetadata implements 
DatabaseMetaData {
     @Override
     public ResultSet getImportedKeys(String catalog, String schema, String 
tbl) throws SQLException {
         return new JdbcResultSet(Collections.emptyList(), asList(
-                new JdbcColumnMeta("PKTABLE_CAT", String.class),
-                new JdbcColumnMeta("PKTABLE_SCHEM", String.class),
-                new JdbcColumnMeta("PKTABLE_NAME", String.class),
-                new JdbcColumnMeta("PKCOLUMN_NAME", String.class),
-                new JdbcColumnMeta("FKTABLE_CAT", String.class),
-                new JdbcColumnMeta("FKTABLE_SCHEM", String.class),
-                new JdbcColumnMeta("FKTABLE_NAME", String.class),
-                new JdbcColumnMeta("FKCOLUMN_NAME", String.class),
-                new JdbcColumnMeta("KEY_SEQ", Short.class),
-                new JdbcColumnMeta("UPDATE_RULE", Short.class),
-                new JdbcColumnMeta("DELETE_RULE", Short.class),
-                new JdbcColumnMeta("FK_NAME", String.class),
-                new JdbcColumnMeta("PK_NAME", String.class),
-                new JdbcColumnMeta("DEFERRABILITY", Short.class)
+                new JdbcColumnMeta("PKTABLE_CAT", ColumnType.STRING),
+                new JdbcColumnMeta("PKTABLE_SCHEM", ColumnType.STRING),
+                new JdbcColumnMeta("PKTABLE_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("PKCOLUMN_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("FKTABLE_CAT", ColumnType.STRING),
+                new JdbcColumnMeta("FKTABLE_SCHEM", ColumnType.STRING),
+                new JdbcColumnMeta("FKTABLE_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("FKCOLUMN_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("KEY_SEQ", ColumnType.INT16),
+                new JdbcColumnMeta("UPDATE_RULE", ColumnType.INT16),
+                new JdbcColumnMeta("DELETE_RULE", ColumnType.INT16),
+                new JdbcColumnMeta("FK_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("PK_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("DEFERRABILITY", ColumnType.INT16)
         ));
     }
 
@@ -1141,20 +1142,20 @@ public class JdbcDatabaseMetadata implements 
DatabaseMetaData {
     @Override
     public ResultSet getExportedKeys(String catalog, String schema, String 
tbl) throws SQLException {
         return new JdbcResultSet(Collections.emptyList(), asList(
-                new JdbcColumnMeta("PKTABLE_CAT", String.class),
-                new JdbcColumnMeta("PKTABLE_SCHEM", String.class),
-                new JdbcColumnMeta("PKTABLE_NAME", String.class),
-                new JdbcColumnMeta("PKCOLUMN_NAME", String.class),
-                new JdbcColumnMeta("FKTABLE_CAT", String.class),
-                new JdbcColumnMeta("FKTABLE_SCHEM", String.class),
-                new JdbcColumnMeta("FKTABLE_NAME", String.class),
-                new JdbcColumnMeta("FKCOLUMN_NAME", String.class),
-                new JdbcColumnMeta("KEY_SEQ", Short.class),
-                new JdbcColumnMeta("UPDATE_RULE", Short.class),
-                new JdbcColumnMeta("DELETE_RULE", Short.class),
-                new JdbcColumnMeta("FK_NAME", String.class),
-                new JdbcColumnMeta("PK_NAME", String.class),
-                new JdbcColumnMeta("DEFERRABILITY", Short.class)
+                new JdbcColumnMeta("PKTABLE_CAT", ColumnType.STRING),
+                new JdbcColumnMeta("PKTABLE_SCHEM", ColumnType.STRING),
+                new JdbcColumnMeta("PKTABLE_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("PKCOLUMN_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("FKTABLE_CAT", ColumnType.STRING),
+                new JdbcColumnMeta("FKTABLE_SCHEM", ColumnType.STRING),
+                new JdbcColumnMeta("FKTABLE_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("FKCOLUMN_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("KEY_SEQ", ColumnType.INT16),
+                new JdbcColumnMeta("UPDATE_RULE", ColumnType.INT16),
+                new JdbcColumnMeta("DELETE_RULE", ColumnType.INT16),
+                new JdbcColumnMeta("FK_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("PK_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("DEFERRABILITY", ColumnType.INT16)
         ));
     }
 
@@ -1163,20 +1164,20 @@ public class JdbcDatabaseMetadata implements 
DatabaseMetaData {
     public ResultSet getCrossReference(String parentCatalog, String 
parentSchema, String parentTbl,
             String foreignCatalog, String foreignSchema, String foreignTbl) 
throws SQLException {
         return new JdbcResultSet(Collections.emptyList(), asList(
-                new JdbcColumnMeta("PKTABLE_CAT", String.class),
-                new JdbcColumnMeta("PKTABLE_SCHEM", String.class),
-                new JdbcColumnMeta("PKTABLE_NAME", String.class),
-                new JdbcColumnMeta("PKCOLUMN_NAME", String.class),
-                new JdbcColumnMeta("FKTABLE_CAT", String.class),
-                new JdbcColumnMeta("FKTABLE_SCHEM", String.class),
-                new JdbcColumnMeta("FKTABLE_NAME", String.class),
-                new JdbcColumnMeta("FKCOLUMN_NAME", String.class),
-                new JdbcColumnMeta("KEY_SEQ", Short.class),
-                new JdbcColumnMeta("UPDATE_RULE", Short.class),
-                new JdbcColumnMeta("DELETE_RULE", Short.class),
-                new JdbcColumnMeta("FK_NAME", String.class),
-                new JdbcColumnMeta("PK_NAME", String.class),
-                new JdbcColumnMeta("DEFERRABILITY", Short.class)
+                new JdbcColumnMeta("PKTABLE_CAT", ColumnType.STRING),
+                new JdbcColumnMeta("PKTABLE_SCHEM", ColumnType.STRING),
+                new JdbcColumnMeta("PKTABLE_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("PKCOLUMN_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("FKTABLE_CAT", ColumnType.STRING),
+                new JdbcColumnMeta("FKTABLE_SCHEM", ColumnType.STRING),
+                new JdbcColumnMeta("FKTABLE_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("FKCOLUMN_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("KEY_SEQ", ColumnType.INT16),
+                new JdbcColumnMeta("UPDATE_RULE", ColumnType.INT16),
+                new JdbcColumnMeta("DELETE_RULE", ColumnType.INT16),
+                new JdbcColumnMeta("FK_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("PK_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("DEFERRABILITY", ColumnType.INT16)
         ));
     }
 
@@ -1276,24 +1277,24 @@ public class JdbcDatabaseMetadata implements 
DatabaseMetaData {
                 Types.ARRAY, 0, null));
 
         return new JdbcResultSet(types, asList(
-                new JdbcColumnMeta("TYPE_NAME", String.class),
-                new JdbcColumnMeta("DATA_TYPE", Integer.class),
-                new JdbcColumnMeta("PRECISION", Integer.class),
-                new JdbcColumnMeta("LITERAL_PREFIX", String.class),
-                new JdbcColumnMeta("LITERAL_SUFFIX", String.class),
-                new JdbcColumnMeta("CREATE_PARAMS", String.class),
-                new JdbcColumnMeta("NULLABLE", Short.class),
-                new JdbcColumnMeta("CASE_SENSITIVE", Boolean.class),
-                new JdbcColumnMeta("SEARCHABLE", Short.class),
-                new JdbcColumnMeta("UNSIGNED_ATTRIBUTE", Boolean.class),
-                new JdbcColumnMeta("FIXED_PREC_SCALE", Boolean.class),
-                new JdbcColumnMeta("AUTO_INCREMENT", Boolean.class),
-                new JdbcColumnMeta("LOCAL_TYPE_NAME", String.class),
-                new JdbcColumnMeta("MINIMUM_SCALE", Short.class),
-                new JdbcColumnMeta("MAXIMUM_SCALE", Short.class),
-                new JdbcColumnMeta("SQL_DATA_TYPE", Integer.class),
-                new JdbcColumnMeta("SQL_DATETIME_SUB", Integer.class),
-                new JdbcColumnMeta("NUM_PREC_RADIX", Integer.class)
+                new JdbcColumnMeta("TYPE_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("DATA_TYPE", ColumnType.INT32),
+                new JdbcColumnMeta("PRECISION", ColumnType.INT32),
+                new JdbcColumnMeta("LITERAL_PREFIX", ColumnType.STRING),
+                new JdbcColumnMeta("LITERAL_SUFFIX", ColumnType.STRING),
+                new JdbcColumnMeta("CREATE_PARAMS", ColumnType.STRING),
+                new JdbcColumnMeta("NULLABLE", ColumnType.INT16),
+                new JdbcColumnMeta("CASE_SENSITIVE", ColumnType.BOOLEAN),
+                new JdbcColumnMeta("SEARCHABLE", ColumnType.INT16),
+                new JdbcColumnMeta("UNSIGNED_ATTRIBUTE", ColumnType.BOOLEAN),
+                new JdbcColumnMeta("FIXED_PREC_SCALE", ColumnType.BOOLEAN),
+                new JdbcColumnMeta("AUTO_INCREMENT", ColumnType.BOOLEAN),
+                new JdbcColumnMeta("LOCAL_TYPE_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("MINIMUM_SCALE", ColumnType.INT16),
+                new JdbcColumnMeta("MAXIMUM_SCALE", ColumnType.INT16),
+                new JdbcColumnMeta("SQL_DATA_TYPE", ColumnType.INT32),
+                new JdbcColumnMeta("SQL_DATETIME_SUB", ColumnType.INT32),
+                new JdbcColumnMeta("NUM_PREC_RADIX", ColumnType.INT32)
         ));
     }
 
@@ -1304,19 +1305,19 @@ public class JdbcDatabaseMetadata implements 
DatabaseMetaData {
         conn.ensureNotClosed();
 
         final List<JdbcColumnMeta> meta = asList(
-                new JdbcColumnMeta("TABLE_CAT", String.class),
-                new JdbcColumnMeta("TABLE_SCHEM", String.class),
-                new JdbcColumnMeta("TABLE_NAME", String.class),
-                new JdbcColumnMeta("NON_UNIQUE", Boolean.class),
-                new JdbcColumnMeta("INDEX_QUALIFIER", String.class),
-                new JdbcColumnMeta("INDEX_NAME", String.class),
-                new JdbcColumnMeta("TYPE", Short.class),
-                new JdbcColumnMeta("ORDINAL_POSITION", Short.class),
-                new JdbcColumnMeta("COLUMN_NAME", String.class),
-                new JdbcColumnMeta("ASC_OR_DESC", String.class),
-                new JdbcColumnMeta("CARDINALITY", Integer.class),
-                new JdbcColumnMeta("PAGES", Integer.class),
-                new JdbcColumnMeta("FILTER_CONDITION", String.class));
+                new JdbcColumnMeta("TABLE_CAT", ColumnType.STRING),
+                new JdbcColumnMeta("TABLE_SCHEM", ColumnType.STRING),
+                new JdbcColumnMeta("TABLE_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("NON_UNIQUE", ColumnType.BOOLEAN),
+                new JdbcColumnMeta("INDEX_QUALIFIER", ColumnType.STRING),
+                new JdbcColumnMeta("INDEX_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("TYPE", ColumnType.INT16),
+                new JdbcColumnMeta("ORDINAL_POSITION", ColumnType.INT16),
+                new JdbcColumnMeta("COLUMN_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("ASC_OR_DESC", ColumnType.STRING),
+                new JdbcColumnMeta("CARDINALITY", ColumnType.INT32),
+                new JdbcColumnMeta("PAGES", ColumnType.INT32),
+                new JdbcColumnMeta("FILTER_CONDITION", ColumnType.STRING));
 
         if (!isValidCatalog(catalog)) {
             return new JdbcResultSet(Collections.emptyList(), meta);
@@ -1402,13 +1403,13 @@ public class JdbcDatabaseMetadata implements 
DatabaseMetaData {
     public ResultSet getUDTs(String catalog, String schemaPtrn, String 
typeNamePtrn,
             int[] types) throws SQLException {
         return new JdbcResultSet(Collections.emptyList(), asList(
-                new JdbcColumnMeta("TYPE_CAT", String.class),
-                new JdbcColumnMeta("TYPE_SCHEM", String.class),
-                new JdbcColumnMeta("TYPE_NAME", String.class),
-                new JdbcColumnMeta("CLASS_NAME", String.class),
-                new JdbcColumnMeta("DATA_TYPE", Integer.class),
-                new JdbcColumnMeta("REMARKS", String.class),
-                new JdbcColumnMeta("BASE_TYPE", Short.class)
+                new JdbcColumnMeta("TYPE_CAT", ColumnType.STRING),
+                new JdbcColumnMeta("TYPE_SCHEM", ColumnType.STRING),
+                new JdbcColumnMeta("TYPE_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("CLASS_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("DATA_TYPE", ColumnType.INT32),
+                new JdbcColumnMeta("REMARKS", ColumnType.STRING),
+                new JdbcColumnMeta("BASE_TYPE", ColumnType.INT16)
         ));
     }
 
@@ -1447,12 +1448,12 @@ public class JdbcDatabaseMetadata implements 
DatabaseMetaData {
     public ResultSet getSuperTypes(String catalog, String schemaPtrn,
             String typeNamePtrn) throws SQLException {
         return new JdbcResultSet(Collections.emptyList(), asList(
-                new JdbcColumnMeta("TYPE_CAT", String.class),
-                new JdbcColumnMeta("TYPE_SCHEM", String.class),
-                new JdbcColumnMeta("TYPE_NAME", String.class),
-                new JdbcColumnMeta("SUPERTYPE_CAT", String.class),
-                new JdbcColumnMeta("SUPERTYPE_SCHEM", String.class),
-                new JdbcColumnMeta("SUPERTYPE_NAME", String.class)
+                new JdbcColumnMeta("TYPE_CAT", ColumnType.STRING),
+                new JdbcColumnMeta("TYPE_SCHEM", ColumnType.STRING),
+                new JdbcColumnMeta("TYPE_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("SUPERTYPE_CAT", ColumnType.STRING),
+                new JdbcColumnMeta("SUPERTYPE_SCHEM", ColumnType.STRING),
+                new JdbcColumnMeta("SUPERTYPE_NAME", ColumnType.STRING)
         ));
     }
 
@@ -1461,10 +1462,10 @@ public class JdbcDatabaseMetadata implements 
DatabaseMetaData {
     public ResultSet getSuperTables(String catalog, String schemaPtrn,
             String tblNamePtrn) throws SQLException {
         return new JdbcResultSet(Collections.emptyList(), asList(
-                new JdbcColumnMeta("TABLE_CAT", String.class),
-                new JdbcColumnMeta("TABLE_SCHEM", String.class),
-                new JdbcColumnMeta("TABLE_NAME", String.class),
-                new JdbcColumnMeta("SUPERTABLE_NAME", String.class)
+                new JdbcColumnMeta("TABLE_CAT", ColumnType.STRING),
+                new JdbcColumnMeta("TABLE_SCHEM", ColumnType.STRING),
+                new JdbcColumnMeta("TABLE_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("SUPERTABLE_NAME", ColumnType.STRING)
         ));
     }
 
@@ -1473,27 +1474,27 @@ public class JdbcDatabaseMetadata implements 
DatabaseMetaData {
     public ResultSet getAttributes(String catalog, String schemaPtrn, String 
typeNamePtrn,
             String attributeNamePtrn) throws SQLException {
         return new JdbcResultSet(Collections.emptyList(), asList(
-                new JdbcColumnMeta("TYPE_CAT", String.class),
-                new JdbcColumnMeta("TYPE_SCHEM", String.class),
-                new JdbcColumnMeta("TYPE_NAME", String.class),
-                new JdbcColumnMeta("ATTR_NAME", String.class),
-                new JdbcColumnMeta("DATA_TYPE", Integer.class),
-                new JdbcColumnMeta("ATTR_TYPE_NAME", String.class),
-                new JdbcColumnMeta("ATTR_SIZE", Integer.class),
-                new JdbcColumnMeta("DECIMAL_DIGITS", Integer.class),
-                new JdbcColumnMeta("NUM_PREC_RADIX", Integer.class),
-                new JdbcColumnMeta("NULLABLE", Integer.class),
-                new JdbcColumnMeta("REMARKS", String.class),
-                new JdbcColumnMeta("ATTR_DEF", String.class),
-                new JdbcColumnMeta("SQL_DATA_TYPE", Integer.class),
-                new JdbcColumnMeta("SQL_DATETIME_SUB", Integer.class),
-                new JdbcColumnMeta("CHAR_OCTET_LENGTH", Integer.class),
-                new JdbcColumnMeta("ORDINAL_POSITION", Integer.class),
-                new JdbcColumnMeta("IS_NULLABLE", String.class),
-                new JdbcColumnMeta("SCOPE_CATALOG", String.class),
-                new JdbcColumnMeta("SCOPE_SCHEMA", String.class),
-                new JdbcColumnMeta("SCOPE_TABLE", String.class),
-                new JdbcColumnMeta("SOURCE_DATA_TYPE", Short.class)
+                new JdbcColumnMeta("TYPE_CAT", ColumnType.STRING),
+                new JdbcColumnMeta("TYPE_SCHEM", ColumnType.STRING),
+                new JdbcColumnMeta("TYPE_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("ATTR_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("DATA_TYPE", ColumnType.INT32),
+                new JdbcColumnMeta("ATTR_TYPE_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("ATTR_SIZE", ColumnType.INT32),
+                new JdbcColumnMeta("DECIMAL_DIGITS", ColumnType.INT32),
+                new JdbcColumnMeta("NUM_PREC_RADIX", ColumnType.INT32),
+                new JdbcColumnMeta("NULLABLE", ColumnType.INT32),
+                new JdbcColumnMeta("REMARKS", ColumnType.STRING),
+                new JdbcColumnMeta("ATTR_DEF", ColumnType.STRING),
+                new JdbcColumnMeta("SQL_DATA_TYPE", ColumnType.INT32),
+                new JdbcColumnMeta("SQL_DATETIME_SUB", ColumnType.INT32),
+                new JdbcColumnMeta("CHAR_OCTET_LENGTH", ColumnType.INT32),
+                new JdbcColumnMeta("ORDINAL_POSITION", ColumnType.INT32),
+                new JdbcColumnMeta("IS_NULLABLE", ColumnType.STRING),
+                new JdbcColumnMeta("SCOPE_CATALOG", ColumnType.STRING),
+                new JdbcColumnMeta("SCOPE_SCHEMA", ColumnType.STRING),
+                new JdbcColumnMeta("SCOPE_TABLE", ColumnType.STRING),
+                new JdbcColumnMeta("SOURCE_DATA_TYPE", ColumnType.INT16)
         ));
     }
 
@@ -1573,10 +1574,10 @@ public class JdbcDatabaseMetadata implements 
DatabaseMetaData {
     @Override
     public ResultSet getClientInfoProperties() throws SQLException {
         return new JdbcResultSet(Collections.emptyList(), asList(
-                new JdbcColumnMeta("NAME", String.class),
-                new JdbcColumnMeta("MAX_LEN", Integer.class),
-                new JdbcColumnMeta("DEFAULT_VALUE", String.class),
-                new JdbcColumnMeta("DESCRIPTION", String.class)
+                new JdbcColumnMeta("NAME", ColumnType.STRING),
+                new JdbcColumnMeta("MAX_LEN", ColumnType.INT32),
+                new JdbcColumnMeta("DEFAULT_VALUE", ColumnType.STRING),
+                new JdbcColumnMeta("DESCRIPTION", ColumnType.STRING)
         ));
     }
 
@@ -1587,12 +1588,12 @@ public class JdbcDatabaseMetadata implements 
DatabaseMetaData {
     public ResultSet getFunctions(String catalog, String schemaPtrn,
             String functionNamePtrn) throws SQLException {
         return new JdbcResultSet(Collections.emptyList(), asList(
-                new JdbcColumnMeta("FUNCTION_CAT", String.class),
-                new JdbcColumnMeta("FUNCTION_SCHEM", String.class),
-                new JdbcColumnMeta("FUNCTION_NAME", String.class),
-                new JdbcColumnMeta("REMARKS", String.class),
-                new JdbcColumnMeta("FUNCTION_TYPE", String.class),
-                new JdbcColumnMeta("SPECIFIC_NAME", String.class)
+                new JdbcColumnMeta("FUNCTION_CAT", ColumnType.STRING),
+                new JdbcColumnMeta("FUNCTION_SCHEM", ColumnType.STRING),
+                new JdbcColumnMeta("FUNCTION_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("REMARKS", ColumnType.STRING),
+                new JdbcColumnMeta("FUNCTION_TYPE", ColumnType.STRING),
+                new JdbcColumnMeta("SPECIFIC_NAME", ColumnType.STRING)
         ));
     }
 
@@ -1601,23 +1602,23 @@ public class JdbcDatabaseMetadata implements 
DatabaseMetaData {
     public ResultSet getFunctionColumns(String catalog, String schemaPtrn, 
String functionNamePtrn,
             String colNamePtrn) throws SQLException {
         return new JdbcResultSet(Collections.emptyList(), asList(
-                new JdbcColumnMeta("FUNCTION_CAT", String.class),
-                new JdbcColumnMeta("FUNCTION_SCHEM", String.class),
-                new JdbcColumnMeta("FUNCTION_NAME", String.class),
-                new JdbcColumnMeta("COLUMN_NAME", String.class),
-                new JdbcColumnMeta("COLUMN_TYPE", Short.class),
-                new JdbcColumnMeta("DATA_TYPE", Integer.class),
-                new JdbcColumnMeta("TYPE_NAME", String.class),
-                new JdbcColumnMeta("PRECISION", Integer.class),
-                new JdbcColumnMeta("LENGTH", Integer.class),
-                new JdbcColumnMeta("SCALE", Short.class),
-                new JdbcColumnMeta("RADIX", Short.class),
-                new JdbcColumnMeta("NULLABLE", Short.class),
-                new JdbcColumnMeta("REMARKS", String.class),
-                new JdbcColumnMeta("CHAR_OCTET_LENGTH", Integer.class),
-                new JdbcColumnMeta("ORDINAL_POSITION", Integer.class),
-                new JdbcColumnMeta("IS_NULLABLE", String.class),
-                new JdbcColumnMeta("SPECIFIC_NAME", String.class)
+                new JdbcColumnMeta("FUNCTION_CAT", ColumnType.STRING),
+                new JdbcColumnMeta("FUNCTION_SCHEM", ColumnType.STRING),
+                new JdbcColumnMeta("FUNCTION_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("COLUMN_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("COLUMN_TYPE", ColumnType.INT16),
+                new JdbcColumnMeta("DATA_TYPE", ColumnType.INT32),
+                new JdbcColumnMeta("TYPE_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("PRECISION", ColumnType.INT32),
+                new JdbcColumnMeta("LENGTH", ColumnType.INT32),
+                new JdbcColumnMeta("SCALE", ColumnType.INT16),
+                new JdbcColumnMeta("RADIX", ColumnType.INT16),
+                new JdbcColumnMeta("NULLABLE", ColumnType.INT16),
+                new JdbcColumnMeta("REMARKS", ColumnType.STRING),
+                new JdbcColumnMeta("CHAR_OCTET_LENGTH", ColumnType.INT32),
+                new JdbcColumnMeta("ORDINAL_POSITION", ColumnType.INT32),
+                new JdbcColumnMeta("IS_NULLABLE", ColumnType.STRING),
+                new JdbcColumnMeta("SPECIFIC_NAME", ColumnType.STRING)
         ));
     }
 
@@ -1642,18 +1643,18 @@ public class JdbcDatabaseMetadata implements 
DatabaseMetaData {
     public ResultSet getPseudoColumns(String catalog, String schemaPtrn, 
String tblNamePtrn,
             String colNamePtrn) throws SQLException {
         return new JdbcResultSet(Collections.emptyList(), asList(
-                new JdbcColumnMeta("TABLE_CAT", String.class),
-                new JdbcColumnMeta("TABLE_SCHEM", String.class),
-                new JdbcColumnMeta("TABLE_NAME", String.class),
-                new JdbcColumnMeta("COLUMN_NAME", String.class),
-                new JdbcColumnMeta("DATA_TYPE", Integer.class),
-                new JdbcColumnMeta("COLUMN_SIZE", Integer.class),
-                new JdbcColumnMeta("DECIMAL_DIGITS", Integer.class),
-                new JdbcColumnMeta("NUM_PREC_RADIX", Integer.class),
-                new JdbcColumnMeta("COLUMN_USAGE", Integer.class),
-                new JdbcColumnMeta("REMARKS", String.class),
-                new JdbcColumnMeta("CHAR_OCTET_LENGTH", Integer.class),
-                new JdbcColumnMeta("IS_NULLABLE", String.class)
+                new JdbcColumnMeta("TABLE_CAT", ColumnType.STRING),
+                new JdbcColumnMeta("TABLE_SCHEM", ColumnType.STRING),
+                new JdbcColumnMeta("TABLE_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("COLUMN_NAME", ColumnType.STRING),
+                new JdbcColumnMeta("DATA_TYPE", ColumnType.INT32),
+                new JdbcColumnMeta("COLUMN_SIZE", ColumnType.INT32),
+                new JdbcColumnMeta("DECIMAL_DIGITS", ColumnType.INT32),
+                new JdbcColumnMeta("NUM_PREC_RADIX", ColumnType.INT32),
+                new JdbcColumnMeta("COLUMN_USAGE", ColumnType.INT32),
+                new JdbcColumnMeta("REMARKS", ColumnType.STRING),
+                new JdbcColumnMeta("CHAR_OCTET_LENGTH", ColumnType.INT32),
+                new JdbcColumnMeta("IS_NULLABLE", ColumnType.STRING)
         ));
     }
 
diff --git 
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcResultSet.java 
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcResultSet.java
index d84abc6a75..c694b0f3b8 100644
--- 
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcResultSet.java
+++ 
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcResultSet.java
@@ -66,11 +66,9 @@ import 
org.apache.ignite.internal.jdbc.proto.JdbcQueryCursorHandler;
 import org.apache.ignite.internal.jdbc.proto.SqlStateCode;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcColumnMeta;
 import 
org.apache.ignite.internal.jdbc.proto.event.JdbcFetchQueryResultsRequest;
-import org.apache.ignite.internal.jdbc.proto.event.JdbcMetaColumnsResult;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcQueryCloseRequest;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcQueryCloseResult;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcQueryFetchResult;
-import org.apache.ignite.internal.jdbc.proto.event.JdbcQueryMetadataRequest;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcQuerySingleResult;
 import org.apache.ignite.internal.util.TransformingIterator;
 import org.apache.ignite.sql.ColumnType;
@@ -103,6 +101,9 @@ public class JdbcResultSet implements ResultSet {
     private final boolean hasResultSet;
     private final boolean hasNextResult;
 
+    /** Jdbc metadata. */
+    private final @Nullable JdbcResultSetMetadata jdbcMeta;
+
     /** Column order map. */
     private @Nullable Map<String, Integer> colOrder;
 
@@ -142,9 +143,6 @@ public class JdbcResultSet implements ResultSet {
     /** Query request handler. */
     private JdbcQueryCursorHandler cursorHandler;
 
-    /** Jdbc metadata. */
-    private @Nullable JdbcResultSetMetadata jdbcMeta;
-
     /** Count of columns in resultSet row. */
     private int columnCount;
 
@@ -174,6 +172,7 @@ public class JdbcResultSet implements ResultSet {
             int fetchSize,
             boolean finished,
             @Nullable List<BinaryTupleReader> rows,
+            @Nullable List<JdbcColumnMeta> meta,
             boolean hasResultSet,
             boolean hasNextResult,
             long updCnt,
@@ -197,10 +196,12 @@ public class JdbcResultSet implements ResultSet {
         if (this.hasResultSet) {
             this.transformer = Objects.requireNonNull(transformer);
             this.rows = Objects.requireNonNull(rows);
+            this.jdbcMeta = new 
JdbcResultSetMetadata(Objects.requireNonNull(meta));
 
             rowsIter = new TransformingIterator<>(rows.iterator(), 
transformer);
         } else {
             this.updCnt = updCnt;
+            this.jdbcMeta = null;
         }
 
         holdsResource = cursorId != null;
@@ -226,7 +227,7 @@ public class JdbcResultSet implements ResultSet {
         this.rowsIter = rows.iterator();
         this.jdbcMeta = new JdbcResultSetMetadata(meta);
 
-        initColumnOrder();
+        initColumnOrder(jdbcMeta);
     }
 
     boolean holdResults() {
@@ -251,17 +252,16 @@ public class JdbcResultSet implements ResultSet {
 
                 Long newCursorId = res.cursorId();
 
-                List<ColumnType> columnTypes = res.columnTypes();
-                int[] decimalScales = res.decimalScales();
+                List<JdbcColumnMeta> meta = res.meta();
 
                 rows = List.of();
 
-                Function<BinaryTupleReader, List<Object>> transformer = 
createTransformer(columnTypes, decimalScales);
+                Function<BinaryTupleReader, List<Object>> transformer = meta 
!= null ? createTransformer(meta) : null;
 
-                int colCount = columnTypes == null ? 0 : columnTypes.size();
+                int colCount = meta != null ? meta.size() : 0;
 
                 return new JdbcResultSet(cursorHandler, stmt, newCursorId, 
fetchSize, !res.hasMoreData(), res.items(),
-                        res.hasResultSet(), res.hasNextResult(), 
res.updateCount(), closeStmt, colCount, transformer);
+                        meta, res.hasResultSet(), res.hasNextResult(), 
res.updateCount(), closeStmt, colCount, transformer);
             } else {
                 // cursor doesn't have next result, thus let's just close 
current one
                 close0(true);
@@ -957,11 +957,7 @@ public class JdbcResultSet implements ResultSet {
     public ResultSetMetaData getMetaData() throws SQLException {
         ensureNotClosed();
 
-        if (jdbcMeta == null) {
-            initMeta();
-        }
-
-        return jdbcMeta;
+        return metaOrThrow();
     }
 
     /** {@inheritDoc} */
@@ -2261,11 +2257,7 @@ public class JdbcResultSet implements ResultSet {
             return colOrder;
         }
 
-        if (jdbcMeta == null) {
-            initMeta();
-        }
-
-        initColumnOrder();
+        initColumnOrder(metaOrThrow());
 
         return colOrder;
     }
@@ -2273,7 +2265,7 @@ public class JdbcResultSet implements ResultSet {
     /**
      * Init column order map.
      */
-    private void initColumnOrder() throws SQLException {
+    private void initColumnOrder(JdbcResultSetMetadata jdbcMeta) throws 
SQLException {
         colOrder = new HashMap<>(jdbcMeta.getColumnCount());
 
         for (int i = 0; i < jdbcMeta.getColumnCount(); ++i) {
@@ -2285,47 +2277,28 @@ public class JdbcResultSet implements ResultSet {
         }
     }
 
-    /**
-     * Initialize metadata if it's not initialized yet.
-     *
-     * @throws SQLException On error.
-     */
-    private void initMeta() throws SQLException {
-        if (finished && !hasResultSet) {
-            throw new SQLException("Server cursor is already closed.", 
SqlStateCode.INVALID_CURSOR_STATE);
+    private JdbcResultSetMetadata metaOrThrow() throws SQLException {
+        if (jdbcMeta == null) {
+            throw new SQLException("Result doesn't have metadata");
         }
 
-        try {
-            if (jdbcMeta == null) {
-                assert cursorId != null : "Unable to call meta() method for 
non QUERY result set.";
-
-                JdbcMetaColumnsResult res = 
cursorHandler.queryMetadataAsync(new JdbcQueryMetadataRequest(cursorId)).get();
-
-                jdbcMeta = new JdbcResultSetMetadata(res.meta());
-            }
-        } catch (InterruptedException e) {
-            throw new SQLException("Thread was interrupted.", e);
-        } catch (ExecutionException e) {
-            throw new SQLException("Metadata request failed.", e);
-        } catch (CancellationException e) {
-            throw new SQLException("Metadata request canceled.", 
SqlStateCode.QUERY_CANCELLED);
-        }
+        return jdbcMeta;
     }
 
-    static Function<BinaryTupleReader, List<Object>> 
createTransformer(List<ColumnType> columnTypes, int[] decimalScales) {
+    static Function<BinaryTupleReader, List<Object>> 
createTransformer(List<JdbcColumnMeta> meta) {
         return (tuple) -> {
-            int columnCount = columnTypes.size();
+            int columnCount = meta.size();
             List<Object> row = new ArrayList<>(columnCount);
-            int decimalIdx = 0;
             int currentDecimalScale = -1;
 
-            for (int colIdx = 0; colIdx < columnCount; colIdx++) {
-                ColumnType type = columnTypes.get(colIdx);
+            int idx = 0;
+            for (JdbcColumnMeta columnMeta : meta) {
+                ColumnType type = columnMeta.columnType();
                 if (type == ColumnType.DECIMAL) {
-                    currentDecimalScale = decimalScales[decimalIdx++];
+                    currentDecimalScale = columnMeta.scale();
                 }
 
-                row.add(JdbcConverterUtils.deriveValueFromBinaryTuple(type, 
tuple, colIdx, currentDecimalScale));
+                row.add(JdbcConverterUtils.deriveValueFromBinaryTuple(type, 
tuple, idx++, currentDecimalScale));
             }
 
             return row;
diff --git 
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcStatement.java 
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcStatement.java
index aa8c48fdfb..d1995b85f8 100644
--- 
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcStatement.java
+++ 
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcStatement.java
@@ -43,11 +43,11 @@ import 
org.apache.ignite.internal.jdbc.proto.JdbcStatementType;
 import org.apache.ignite.internal.jdbc.proto.SqlStateCode;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcBatchExecuteRequest;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcBatchExecuteResult;
+import org.apache.ignite.internal.jdbc.proto.event.JdbcColumnMeta;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcQueryExecuteRequest;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcQuerySingleResult;
 import org.apache.ignite.internal.util.ArrayUtils;
 import org.apache.ignite.internal.util.CollectionUtils;
-import org.apache.ignite.sql.ColumnType;
 import org.jetbrains.annotations.Nullable;
 
 /**
@@ -160,15 +160,15 @@ public class JdbcStatement implements Statement {
 
         JdbcQueryCursorHandler handler = new 
JdbcClientQueryCursorHandler(res.getChannel());
 
-        List<ColumnType> columnTypes = executeResult.columnTypes();
-        columnTypes = columnTypes == null ? List.of() : columnTypes;
-        int[] decimalScales = executeResult.decimalScales();
+        List<JdbcColumnMeta> meta = executeResult.meta();
 
-        Function<BinaryTupleReader, List<Object>> transformer = 
createTransformer(columnTypes, decimalScales);
+        Function<BinaryTupleReader, List<Object>> transformer = meta != null ? 
createTransformer(meta) : null;
+
+        int colCount = meta != null ? meta.size() : 0;
 
         resSets.add(new JdbcResultSet(handler, this, executeResult.cursorId(), 
pageSize, !executeResult.hasMoreData(),
-                executeResult.items(), executeResult.hasResultSet(), 
executeResult.hasNextResult(),
-                executeResult.updateCount(), closeOnCompletion, 
columnTypes.size(), transformer));
+                executeResult.items(), meta, executeResult.hasResultSet(), 
executeResult.hasNextResult(),
+                executeResult.updateCount(), closeOnCompletion, colCount, 
transformer));
     }
 
     /** {@inheritDoc} */
diff --git 
a/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/JdbcResultSetTest.java
 
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/JdbcResultSetTest.java
index d62f5bfb28..8db7319be8 100644
--- 
a/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/JdbcResultSetTest.java
+++ 
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc/JdbcResultSetTest.java
@@ -141,6 +141,6 @@ public class JdbcResultSetTest extends 
BaseIgniteAbstractTest {
             JdbcStatement statement,
             boolean hasNextResult
     ) {
-        return new JdbcResultSet(handler, statement, 1L, 1, true, List.of(), 
true, hasNextResult, 0, false, 1, r -> List.of());
+        return new JdbcResultSet(handler, statement, 1L, 1, true, List.of(), 
List.of(), true, hasNextResult, 0, false, 1, r -> List.of());
     }
 }
diff --git a/modules/platforms/cpp/ignite/odbc/query/column_metadata_query.cpp 
b/modules/platforms/cpp/ignite/odbc/query/column_metadata_query.cpp
index 6bc60b75cf..6627b97298 100644
--- a/modules/platforms/cpp/ignite/odbc/query/column_metadata_query.cpp
+++ b/modules/platforms/cpp/ignite/odbc/query/column_metadata_query.cpp
@@ -65,6 +65,34 @@ enum class result_column {
 
 using namespace ignite;
 
+/**
+ * Convert data type to string.
+ * @param data_type Data type.
+ * @return String representation.
+ */
+std::string_view data_type_to_string(ignite_type data_type) {
+    switch (data_type) {
+        case ignite_type::BOOLEAN: return "BOOLEAN";
+        case ignite_type::INT8: return "TINYINT";
+        case ignite_type::INT16: return "SMALLINT";
+        case ignite_type::INT32: return "INTEGER";
+        case ignite_type::INT64: return "BIGINT";
+        case ignite_type::FLOAT: return "REAL";
+        case ignite_type::DOUBLE: return "DOUBLE";
+        case ignite_type::STRING: return "VARCHAR";
+        case ignite_type::BYTE_ARRAY: return "VARBINARY";
+        case ignite_type::TIME: return "TIME";
+        case ignite_type::DATETIME: return "TIMESTAMP";
+        case ignite_type::TIMESTAMP: return "TIMESTAMP WITH LOCAL TIME ZONE";
+        case ignite_type::DATE: return "DATE";
+        case ignite_type::DECIMAL: return "DECIMAL";
+        case ignite_type::NIL: return "NULL";
+        case ignite_type::UUID: return "UUID";
+        default:
+            return "OTHER";
+    }
+}
+
 /**
  * Reads result set metadata.
  *
@@ -91,8 +119,6 @@ std::vector<odbc_column_meta> 
read_column_meta(protocol::reader &reader) {
         column.column = reader.read_string_nullable();
 
         column.data_type = ignite_type(reader.read_int32());
-        column.data_type_name = reader.read_string();
-        reader.skip(); // data_type_class
         column.nullable = reader.read_bool();
         column.precision = reader.read_int32();
         column.scale = reader.read_int32();
@@ -211,7 +237,7 @@ sql_result column_metadata_query::get_column(std::uint16_t 
column_idx, applicati
         }
 
         case result_column::TYPE_NAME: {
-            buffer.put_string(current_column.data_type_name);
+            
buffer.put_string(std::string(data_type_to_string(current_column.data_type)));
             break;
         }
 
diff --git a/modules/platforms/cpp/ignite/odbc/query/column_metadata_query.h 
b/modules/platforms/cpp/ignite/odbc/query/column_metadata_query.h
index 0fa4411018..56c8d95dcc 100644
--- a/modules/platforms/cpp/ignite/odbc/query/column_metadata_query.h
+++ b/modules/platforms/cpp/ignite/odbc/query/column_metadata_query.h
@@ -49,9 +49,6 @@ struct odbc_column_meta {
     /** Data type. */
     ignite_type data_type{ignite_type::UNDEFINED};
 
-    /** Data type name. */
-    std::string data_type_name;
-
     /** Nullability. */
     bool nullable{false};
 
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Commons.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Commons.java
index b63180efc5..bd5938c23d 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Commons.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Commons.java
@@ -600,28 +600,6 @@ public final class Commons {
         }
     }
 
-    /**
-     * Provide mapping Native types to JDBC classes.
-     *
-     * @param type Native type
-     * @return JDBC corresponding class.
-     */
-    public static Class<?> nativeTypeToJdbcClass(NativeType type) {
-        assert type != null;
-
-        switch (type.spec()) {
-            case DATE:
-                return java.sql.Date.class;
-            case TIME:
-                return java.sql.Time.class;
-            case DATETIME:
-            case TIMESTAMP:
-                return java.sql.Timestamp.class;
-            default:
-                return nativeTypeToClass(type);
-        }
-    }
-
     /**
      * Gets the precision of this type. Returns {@link 
ColumnMetadata#UNDEFINED_PRECISION} if
      * precision is not applicable for this type.

Reply via email to