This is an automated email from the ASF dual-hosted git repository.

isapego 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 0a1f0215c6 IGNITE-19216 ODBC type info fetching (#2603)
0a1f0215c6 is described below

commit 0a1f0215c6a3f3789c71321ef1ed5e634afa0db3
Author: Igor Sapego <[email protected]>
AuthorDate: Tue Sep 19 13:08:25 2023 +0400

    IGNITE-19216 ODBC type info fetching (#2603)
---
 .../apache/ignite/internal/client/ClientUtils.java |   2 +-
 .../ignite/internal/client/TcpClientChannel.java   |   4 +-
 .../cpp/ignite/client/detail/table/schema.h        |   2 +-
 modules/platforms/cpp/ignite/odbc/CMakeLists.txt   |   1 +
 .../cpp/ignite/odbc/app/application_data_buffer.h  |   2 +-
 modules/platforms/cpp/ignite/odbc/query/query.h    |   3 +
 .../cpp/ignite/odbc/query/table_metadata_query.h   |  20 +-
 .../cpp/ignite/odbc/query/type_info_query.cpp      | 334 +++++++++++++++++++++
 .../odbc/query/{query.h => type_info_query.h}      |  98 +++---
 .../platforms/cpp/ignite/odbc/sql_statement.cpp    |  16 +-
 modules/platforms/cpp/ignite/odbc/type_traits.cpp  | 133 ++++----
 modules/platforms/cpp/ignite/odbc/type_traits.h    |  19 +-
 modules/platforms/cpp/ignite/protocol/utils.cpp    |   4 +-
 .../client-test/key_value_binary_view_test.cpp     |   6 +-
 .../cpp/tests/client-test/key_value_view_test.cpp  |   6 +-
 .../tests/client-test/record_binary_view_test.cpp  |   6 +-
 .../cpp/tests/client-test/record_view_test.cpp     |   6 +-
 .../cpp/tests/odbc-test/meta_queries_test.cpp      |  18 +-
 18 files changed, 521 insertions(+), 159 deletions(-)

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 eccf1b6235..a4a5594743 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
@@ -46,7 +46,7 @@ public class ClientUtils {
 
             throw ExceptionUtils.wrap(e);
         } catch (ExecutionException e) {
-            //TODO: https://issues.apache.org/jira/browse/IGNITE-19539
+            //TODO: https://issues.apache.org/jira/browse/IGNITE-20436
             throw ExceptionUtils.wrap(e);
         }
     }
diff --git 
a/modules/client/src/main/java/org/apache/ignite/internal/client/TcpClientChannel.java
 
b/modules/client/src/main/java/org/apache/ignite/internal/client/TcpClientChannel.java
index 14716bf942..8b8b30c8ee 100644
--- 
a/modules/client/src/main/java/org/apache/ignite/internal/client/TcpClientChannel.java
+++ 
b/modules/client/src/main/java/org/apache/ignite/internal/client/TcpClientChannel.java
@@ -327,7 +327,7 @@ class TcpClientChannel implements ClientChannel, 
ClientMessageHandler, ClientCon
 
             metrics.requestsActiveDecrement();
 
-            // TODO https://issues.apache.org/jira/browse/IGNITE-19539
+            // TODO https://issues.apache.org/jira/browse/IGNITE-20436
             throw ExceptionUtils.wrap(t);
         }
     }
@@ -468,7 +468,7 @@ class TcpClientChannel implements ClientChannel, 
ClientMessageHandler, ClientCon
         }
 
         try {
-            // TODO https://issues.apache.org/jira/browse/IGNITE-19539
+            // TODO https://issues.apache.org/jira/browse/IGNITE-20436
             Class<? extends Throwable> errCls = (Class<? extends Throwable>) 
Class.forName(errClassName);
             if (IgniteException.class.isAssignableFrom(errCls) || 
IgniteCheckedException.class.isAssignableFrom(errCls)) {
                 return copyExceptionWithCause(errCls, traceId, code, errMsg, 
causeWithStackTrace);
diff --git a/modules/platforms/cpp/ignite/client/detail/table/schema.h 
b/modules/platforms/cpp/ignite/client/detail/table/schema.h
index b277e991ee..d521ad5a0f 100644
--- a/modules/platforms/cpp/ignite/client/detail/table/schema.h
+++ b/modules/platforms/cpp/ignite/client/detail/table/schema.h
@@ -93,7 +93,7 @@ struct schema {
      * @param reader Reader to use.
      * @return Schema instance.
      */
-    static std::shared_ptr<schema> read(protocol::reader& reader) {
+    static std::shared_ptr<schema> read(protocol::reader &reader) {
         std::int32_t key_column_count = 0;
         auto schema_version = reader.read_int32();
 
diff --git a/modules/platforms/cpp/ignite/odbc/CMakeLists.txt 
b/modules/platforms/cpp/ignite/odbc/CMakeLists.txt
index 58f02b7ec8..ea20e78785 100644
--- a/modules/platforms/cpp/ignite/odbc/CMakeLists.txt
+++ b/modules/platforms/cpp/ignite/odbc/CMakeLists.txt
@@ -37,6 +37,7 @@ set(SOURCES
     meta/table_meta.cpp
     query/data_query.cpp
     query/table_metadata_query.cpp
+    query/type_info_query.cpp
     odbc.cpp
     entry_points.cpp
     ssl_mode.cpp
diff --git a/modules/platforms/cpp/ignite/odbc/app/application_data_buffer.h 
b/modules/platforms/cpp/ignite/odbc/app/application_data_buffer.h
index 98a2ffcb74..fd7ea6ee8d 100644
--- a/modules/platforms/cpp/ignite/odbc/app/application_data_buffer.h
+++ b/modules/platforms/cpp/ignite/odbc/app/application_data_buffer.h
@@ -522,6 +522,6 @@ private:
 };
 
 /** Column binging map type alias. */
-typedef std::map<uint16_t, application_data_buffer> column_binding_map;
+typedef std::map<std::uint16_t, application_data_buffer> column_binding_map;
 
 } // namespace ignite
diff --git a/modules/platforms/cpp/ignite/odbc/query/query.h 
b/modules/platforms/cpp/ignite/odbc/query/query.h
index 279c36184a..ce596eff70 100644
--- a/modules/platforms/cpp/ignite/odbc/query/query.h
+++ b/modules/platforms/cpp/ignite/odbc/query/query.h
@@ -33,6 +33,9 @@ enum class query_type {
 
     /** Table metadata. */
     TABLE_METADATA,
+
+    /** Type info. */
+    TYPE_INFO,
 };
 
 /**
diff --git a/modules/platforms/cpp/ignite/odbc/query/table_metadata_query.h 
b/modules/platforms/cpp/ignite/odbc/query/table_metadata_query.h
index b9b0b57f10..9ed65d83ab 100644
--- a/modules/platforms/cpp/ignite/odbc/query/table_metadata_query.h
+++ b/modules/platforms/cpp/ignite/odbc/query/table_metadata_query.h
@@ -46,28 +46,28 @@ public:
     /**
      * Destructor.
      */
-    virtual ~table_metadata_query() = default;
+    ~table_metadata_query() override = default;
 
     /**
      * Execute query.
      *
      * @return True on success.
      */
-    virtual sql_result execute();
+    sql_result execute() override;
 
     /**
      * Get column metadata.
      *
      * @return Column metadata.
      */
-    virtual const column_meta_vector *get_meta();
+    const column_meta_vector *get_meta() override;
 
     /**
      * Fetch next result row to application buffers.
      *
      * @return Operation result.
      */
-    virtual sql_result fetch_next_row(column_binding_map &column_bindings);
+    sql_result fetch_next_row(column_binding_map &column_bindings) override;
 
     /**
      * Get data of the specified column in the result set.
@@ -76,39 +76,39 @@ public:
      * @param buffer Buffer to put column data to.
      * @return Operation result.
      */
-    virtual sql_result get_column(std::uint16_t column_idx, 
application_data_buffer &buffer);
+    sql_result get_column(std::uint16_t column_idx, application_data_buffer 
&buffer) override;
 
     /**
      * Close query.
      *
      * @return True on success.
      */
-    virtual sql_result close();
+    sql_result close() override;
 
     /**
      * Check if data is available.
      *
      * @return True if data is available.
      */
-    virtual bool is_data_available() const;
+    bool is_data_available() const override;
 
     /**
      * Get number of rows affected by the statement.
      *
      * @return Number of rows affected by the statement.
      */
-    virtual std::int64_t affected_rows() const;
+    std::int64_t affected_rows() const override;
 
     /**
      * Move to the next result set.
      *
      * @return Operation result.
      */
-    virtual sql_result next_result_set();
+    sql_result next_result_set() override;
 
 private:
     /**
-     * Make get columns metadata requets and use response to set internal 
state.
+     * Make get columns metadata requests and use response to set internal 
state.
      *
      * @return True on success.
      */
diff --git a/modules/platforms/cpp/ignite/odbc/query/type_info_query.cpp 
b/modules/platforms/cpp/ignite/odbc/query/type_info_query.cpp
new file mode 100644
index 0000000000..0fb7fe3677
--- /dev/null
+++ b/modules/platforms/cpp/ignite/odbc/query/type_info_query.cpp
@@ -0,0 +1,334 @@
+/*
+ * 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.
+ */
+
+#include "ignite/odbc/query/type_info_query.h"
+#include "ignite/odbc/system/odbc_constants.h"
+#include "ignite/odbc/type_traits.h"
+
+#include <cassert>
+
+namespace {
+
+enum class result_column {
+    /** Data source-dependent data-type name. */
+    TYPE_NAME = 1,
+
+    /** SQL data type. */
+    DATA_TYPE,
+
+    /** The maximum column size that the server supports for this data type. */
+    COLUMN_SIZE,
+
+    /** Character or characters used to prefix a literal. */
+    LITERAL_PREFIX,
+
+    /** Character or characters used to terminate a literal. */
+    LITERAL_SUFFIX,
+
+    /**
+     * A list of keywords, separated by commas, corresponding to each
+     * parameter that the application may specify in parentheses when using
+     * the name that is returned in the TYPE_NAME field.
+     */
+    CREATE_PARAMS,
+
+    /** Whether the data type accepts a NULL value. */
+    NULLABLE,
+
+    /**
+     * Whether a character data type is case-sensitive in collations and
+     * comparisons.
+     */
+    CASE_SENSITIVE,
+
+    /** How the data type is used in a WHERE clause. */
+    SEARCHABLE,
+
+    /** Whether the data type is unsigned. */
+    UNSIGNED_ATTRIBUTE,
+
+    /** Whether the data type has predefined fixed precision and scale. */
+    FIXED_PREC_SCALE,
+
+    /** Whether the data type is auto-incrementing. */
+    AUTO_UNIQUE_VALUE,
+
+    /**
+     * Localized version of the data source–dependent name of the data
+     * type.
+     */
+    LOCAL_TYPE_NAME,
+
+    /** The minimum scale of the data type on the data source. */
+    MINIMUM_SCALE,
+
+    /** The maximum scale of the data type on the data source. */
+    MAXIMUM_SCALE,
+
+    /**
+     * The value of the SQL data type as it appears in the SQL_DESC_TYPE
+     * field of the descriptor.
+     */
+    SQL_DATA_TYPE,
+
+    /**
+     * When the value of SQL_DATA_TYPE is SQL_DATETIME or SQL_INTERVAL,
+     * this column contains the datetime/interval sub-code.
+     */
+    SQL_DATETIME_SUB,
+
+    /**
+     * If the data type is an approximate numeric type, this column
+     * contains the value 2 to indicate that COLUMN_SIZE specifies a number
+     * of bits.
+     */
+    NUM_PREC_RADIX,
+
+    /**
+     * If the data type is an interval data type, then this column contains
+     * the value of the interval leading precision.
+     */
+    INTERVAL_PRECISION
+};
+
+} // anonymous namespace
+
+namespace ignite {
+
+type_info_query::type_info_query(diagnosable_adapter &diag, std::int16_t 
sql_type)
+    : query(diag, query_type::TYPE_INFO) {
+    m_columns_meta.reserve(19);
+
+    const std::string sch;
+    const std::string tbl;
+
+    m_columns_meta.emplace_back(sch, tbl, "TYPE_NAME", ignite_type::STRING);
+    m_columns_meta.emplace_back(sch, tbl, "DATA_TYPE", ignite_type::INT16);
+    m_columns_meta.emplace_back(sch, tbl, "COLUMN_SIZE", ignite_type::INT32);
+    m_columns_meta.emplace_back(sch, tbl, "LITERAL_PREFIX", 
ignite_type::STRING);
+    m_columns_meta.emplace_back(sch, tbl, "LITERAL_SUFFIX", 
ignite_type::STRING);
+    m_columns_meta.emplace_back(sch, tbl, "CREATE_PARAMS", 
ignite_type::STRING);
+    m_columns_meta.emplace_back(sch, tbl, "NULLABLE", ignite_type::INT16);
+    m_columns_meta.emplace_back(sch, tbl, "CASE_SENSITIVE", 
ignite_type::INT16);
+    m_columns_meta.emplace_back(sch, tbl, "SEARCHABLE", ignite_type::INT16);
+    m_columns_meta.emplace_back(sch, tbl, "UNSIGNED_ATTRIBUTE", 
ignite_type::INT16);
+    m_columns_meta.emplace_back(sch, tbl, "FIXED_PREC_SCALE", 
ignite_type::INT16);
+    m_columns_meta.emplace_back(sch, tbl, "AUTO_UNIQUE_VALUE", 
ignite_type::INT16);
+    m_columns_meta.emplace_back(sch, tbl, "LOCAL_TYPE_NAME", 
ignite_type::STRING);
+    m_columns_meta.emplace_back(sch, tbl, "MINIMUM_SCALE", ignite_type::INT16);
+    m_columns_meta.emplace_back(sch, tbl, "MAXIMUM_SCALE", ignite_type::INT16);
+    m_columns_meta.emplace_back(sch, tbl, "SQL_DATA_TYPE", ignite_type::INT16);
+    m_columns_meta.emplace_back(sch, tbl, "SQL_DATETIME_SUB", 
ignite_type::INT16);
+    m_columns_meta.emplace_back(sch, tbl, "NUM_PREC_RADIX", 
ignite_type::INT32);
+    m_columns_meta.emplace_back(sch, tbl, "INTERVAL_PRECISION", 
ignite_type::INT16);
+
+    assert(is_sql_type_supported(sql_type) || sql_type == SQL_ALL_TYPES);
+
+    if (sql_type == SQL_ALL_TYPES) {
+        m_types.push_back(ignite_type::BOOLEAN);
+        m_types.push_back(ignite_type::INT8);
+        m_types.push_back(ignite_type::INT16);
+        m_types.push_back(ignite_type::INT32);
+        m_types.push_back(ignite_type::INT64);
+        m_types.push_back(ignite_type::FLOAT);
+        m_types.push_back(ignite_type::DOUBLE);
+        m_types.push_back(ignite_type::DECIMAL);
+        m_types.push_back(ignite_type::DATE);
+        m_types.push_back(ignite_type::TIME);
+        m_types.push_back(ignite_type::DATETIME);
+        m_types.push_back(ignite_type::TIMESTAMP);
+        m_types.push_back(ignite_type::UUID);
+        m_types.push_back(ignite_type::BITMASK);
+        m_types.push_back(ignite_type::STRING);
+        m_types.push_back(ignite_type::BYTE_ARRAY);
+        // TODO: IGNITE-19969 implement support for period, duration and 
big_integer
+    } else
+        m_types.push_back(sql_type_to_ignite_type(sql_type));
+}
+
+sql_result type_info_query::execute() {
+    m_cursor = m_types.begin();
+
+    m_executed = true;
+    m_fetched = false;
+
+    return sql_result::AI_SUCCESS;
+}
+
+sql_result type_info_query::fetch_next_row(column_binding_map 
&column_bindings) {
+    if (!m_executed) {
+        m_diag.add_status_record(sql_state::SHY010_SEQUENCE_ERROR, "Query was 
not executed.");
+        return sql_result::AI_ERROR;
+    }
+
+    if (!m_fetched)
+        m_fetched = true;
+    else
+        ++m_cursor;
+
+    if (m_cursor == m_types.end())
+        return sql_result::AI_NO_DATA;
+
+    for (auto &pair : column_bindings)
+        get_column(pair.first, pair.second);
+
+    return sql_result::AI_SUCCESS;
+}
+
+sql_result type_info_query::get_column(std::uint16_t column_idx, 
application_data_buffer &buffer) {
+    if (!m_executed) {
+        m_diag.add_status_record(sql_state::SHY010_SEQUENCE_ERROR, "Query was 
not executed.");
+        return sql_result::AI_ERROR;
+    }
+
+    if (m_cursor == m_types.end()) {
+        m_diag.add_status_record(sql_state::S24000_INVALID_CURSOR_STATE, 
"Cursor has reached end of the result set.");
+        return sql_result::AI_ERROR;
+    }
+
+    auto current_type = *m_cursor;
+
+    switch (result_column(column_idx)) {
+        case result_column::TYPE_NAME: {
+            buffer.put_string(ignite_type_to_sql_type_name(current_type));
+
+            break;
+        }
+
+        case result_column::DATA_TYPE:
+        case result_column::SQL_DATA_TYPE: {
+            buffer.put_int16(ignite_type_to_sql_type(current_type));
+
+            break;
+        }
+
+        case result_column::COLUMN_SIZE: {
+            buffer.put_int32(ignite_type_max_column_size(current_type));
+
+            break;
+        }
+
+        case result_column::LITERAL_PREFIX: {
+            auto prefix = ignite_type_literal_prefix(current_type);
+            if (!prefix)
+                buffer.put_null();
+            else
+                buffer.put_string(*prefix);
+
+            break;
+        }
+
+        case result_column::LITERAL_SUFFIX: {
+            auto suffix = ignite_type_literal_suffix(current_type);
+            if (!suffix)
+                buffer.put_null();
+            else
+                buffer.put_string(*suffix);
+
+            break;
+        }
+
+        case result_column::CREATE_PARAMS: {
+            if (current_type == ignite_type::DECIMAL || current_type == 
ignite_type::NUMBER)
+                buffer.put_string("precision,scale");
+            else
+                buffer.put_null();
+
+            break;
+        }
+
+        case result_column::NULLABLE: {
+            buffer.put_int32(ignite_type_nullability(current_type));
+
+            break;
+        }
+
+        case result_column::CASE_SENSITIVE: {
+            if (current_type == ignite_type::STRING)
+                buffer.put_int16(SQL_TRUE);
+            else
+                buffer.put_int16(SQL_FALSE);
+
+            break;
+        }
+
+        case result_column::SEARCHABLE: {
+            buffer.put_int16(SQL_SEARCHABLE);
+
+            break;
+        }
+
+        case result_column::UNSIGNED_ATTRIBUTE: {
+            buffer.put_int16(is_ignite_type_unsigned(current_type));
+
+            break;
+        }
+
+        case result_column::FIXED_PREC_SCALE:
+        case result_column::AUTO_UNIQUE_VALUE: {
+            buffer.put_int16(SQL_FALSE);
+
+            break;
+        }
+
+        case result_column::LOCAL_TYPE_NAME: {
+            buffer.put_null();
+
+            break;
+        }
+
+        case result_column::MINIMUM_SCALE:
+        case result_column::MAXIMUM_SCALE: {
+            
buffer.put_int16(std::int16_t(ignite_type_decimal_digits(current_type)));
+
+            break;
+        }
+
+        case result_column::SQL_DATETIME_SUB: {
+            buffer.put_null();
+
+            break;
+        }
+
+        case result_column::NUM_PREC_RADIX: {
+            buffer.put_int32(ignite_type_num_precision_radix(current_type));
+
+            break;
+        }
+
+        case result_column::INTERVAL_PRECISION: {
+            buffer.put_null();
+
+            break;
+        }
+
+        default:
+            break;
+    }
+
+    return sql_result::AI_SUCCESS;
+}
+
+sql_result type_info_query::close() {
+    m_cursor = m_types.end();
+
+    m_executed = false;
+
+    return sql_result::AI_SUCCESS;
+}
+
+} // namespace ignite
diff --git a/modules/platforms/cpp/ignite/odbc/query/query.h 
b/modules/platforms/cpp/ignite/odbc/query/type_info_query.h
similarity index 54%
copy from modules/platforms/cpp/ignite/odbc/query/query.h
copy to modules/platforms/cpp/ignite/odbc/query/type_info_query.h
index 279c36184a..2f2eccdd54 100644
--- a/modules/platforms/cpp/ignite/odbc/query/query.h
+++ b/modules/platforms/cpp/ignite/odbc/query/type_info_query.h
@@ -17,48 +17,48 @@
 
 #pragma once
 
-#include "ignite/odbc/common_types.h"
-#include "ignite/odbc/diagnostic/diagnosable_adapter.h"
-#include "ignite/odbc/meta/column_meta.h"
-
-#include <cstdint>
-#include <map>
+#include "ignite/odbc/query/query.h"
 
 namespace ignite {
 
-/** Query type. */
-enum class query_type {
-    /** Data query type. */
-    DATA,
-
-    /** Table metadata. */
-    TABLE_METADATA,
-};
-
 /**
- * Query.
+ * Type info query.
  */
-class query {
+class type_info_query : public query {
 public:
     /**
-     * Virtual destructor
+     * Constructor.
+     *
+     * @param diag Diagnostics collector.
+     * @param sql_type SQL type.
      */
-    virtual ~query() = default;
+    type_info_query(diagnosable_adapter &diag, std::int16_t sql_type);
+
+    /**
+     * Destructor.
+     */
+    ~type_info_query() override = default;
 
     /**
      * Execute query.
      *
-     * @return Execution result.
+     * @return True on success.
+     */
+    sql_result execute() override;
+
+    /**
+     * Get column metadata.
+     *
+     * @return Column metadata.
      */
-    virtual sql_result execute() = 0;
+    const column_meta_vector *get_meta() override { return &m_columns_meta; }
 
     /**
      * Fetch next result row to application buffers.
      *
-     * @param column_bindings Application buffers to put data to.
      * @return Operation result.
      */
-    virtual sql_result fetch_next_row(column_binding_map &m_column_bindings) = 
0;
+    sql_result fetch_next_row(column_binding_map &column_bindings) override;
 
     /**
      * Get data of the specified column in the result set.
@@ -67,67 +67,51 @@ public:
      * @param buffer Buffer to put column data to.
      * @return Operation result.
      */
-    virtual sql_result get_column(std::uint16_t column_idx, 
application_data_buffer &buffer) = 0;
+    sql_result get_column(std::uint16_t column_idx, application_data_buffer 
&buffer) override;
 
     /**
      * Close query.
      *
-     * @return Operation result.
-     */
-    virtual sql_result close() = 0;
-
-    /**
-     * Get column metadata.
-     *
-     * @return Column metadata.
+     * @return True on success.
      */
-    [[nodiscard]] virtual const column_meta_vector *get_meta() {
-        static const column_meta_vector empty;
-
-        return &empty;
-    }
+    sql_result close() override;
 
     /**
      * Check if data is available.
      *
      * @return True if data is available.
      */
-    [[nodiscard]] virtual bool is_data_available() const = 0;
+    bool is_data_available() const override { return m_cursor != 
m_types.end(); }
 
     /**
      * Get number of rows affected by the statement.
      *
      * @return Number of rows affected by the statement.
      */
-    [[nodiscard]] virtual std::int64_t affected_rows() const = 0;
+    std::int64_t affected_rows() const override { return 0; }
 
     /**
      * Move to the next result set.
      *
      * @return Operation result.
      */
-    virtual sql_result next_result_set() = 0;
+    sql_result next_result_set() override { return sql_result::AI_NO_DATA; }
 
-    /**
-     * Get query type.
-     *
-     * @return Query type.
-     */
-    [[nodiscard]] query_type get_type() const { return m_type; }
+private:
+    /** Columns metadata. */
+    column_meta_vector m_columns_meta;
 
-protected:
-    /**
-     * Constructor.
-     */
-    query(diagnosable_adapter &diag, query_type type)
-        : m_diag(diag)
-        , m_type(type) {}
+    /** Executed flag. */
+    bool m_executed{false};
+
+    /** Fetched flag. */
+    bool m_fetched{false};
 
-    /** Diagnostics collector. */
-    diagnosable_adapter &m_diag;
+    /** Requested types. */
+    std::vector<ignite_type> m_types;
 
-    /** Query type. */
-    query_type m_type;
+    /** Query cursor. */
+    std::vector<ignite_type>::const_iterator m_cursor{m_types.end()};
 };
 
 } // namespace ignite
diff --git a/modules/platforms/cpp/ignite/odbc/sql_statement.cpp 
b/modules/platforms/cpp/ignite/odbc/sql_statement.cpp
index 26779ab8bc..e29512ae98 100644
--- a/modules/platforms/cpp/ignite/odbc/sql_statement.cpp
+++ b/modules/platforms/cpp/ignite/odbc/sql_statement.cpp
@@ -21,7 +21,7 @@
 #include "ignite/odbc/odbc_error.h"
 #include "ignite/odbc/query/data_query.h"
 #include "ignite/odbc/query/table_metadata_query.h"
-#include "ignite/odbc/sql_connection.h"
+#include "ignite/odbc/query/type_info_query.h"
 #include "ignite/odbc/sql_statement.h"
 #include "ignite/odbc/system/odbc_constants.h"
 #include "ignite/odbc/utility.h"
@@ -651,11 +651,11 @@ sql_result 
sql_statement::internal_execute_special_columns_query(uint16_t type,
     return sql_result::AI_ERROR;
 }
 
-void sql_statement::execute_get_type_info_query(int16_t sql_type) {
+void sql_statement::execute_get_type_info_query(std::int16_t sql_type) {
     IGNITE_ODBC_API_CALL(internal_execute_get_type_info_query(sql_type));
 }
 
-sql_result sql_statement::internal_execute_get_type_info_query(int16_t 
sql_type) {
+sql_result sql_statement::internal_execute_get_type_info_query(std::int16_t 
sql_type) {
     if (sql_type != SQL_ALL_TYPES && !is_sql_type_supported(sql_type)) {
         std::stringstream builder;
         builder << "Data type is not supported. [typeId=" << sql_type << ']';
@@ -668,9 +668,8 @@ sql_result 
sql_statement::internal_execute_get_type_info_query(int16_t sql_type)
     if (m_current_query)
         m_current_query->close();
 
-    // TODO: IGNITE-19216 Implement type info query
-    add_status_record(sql_state::SHYC00_OPTIONAL_FEATURE_NOT_IMPLEMENTED, 
"Type info query is not supported.");
-    return sql_result::AI_ERROR;
+    m_current_query = std::make_unique<type_info_query>(*this, sql_type);
+    return m_current_query->execute();
 }
 
 void sql_statement::free_resources(uint16_t option) {
@@ -1008,12 +1007,15 @@ sql_result sql_statement::internal_describe_param(
     if (data_type)
         *data_type = ignite_type_to_sql_type(type);
 
+    // TODO: IGNITE-19854 Implement meta fetching for a parameter
     if (param_size)
-        *param_size = ignite_type_column_size(type);
+        *param_size = ignite_type_max_column_size(type);
 
+    // TODO: IGNITE-19854 Implement meta fetching for a parameter
     if (decimal_digits)
         *decimal_digits = int16_t(ignite_type_decimal_digits(type));
 
+    // TODO: IGNITE-19854 Implement meta fetching for a parameter
     if (nullable)
         *nullable = ignite_type_nullability(type);
 
diff --git a/modules/platforms/cpp/ignite/odbc/type_traits.cpp 
b/modules/platforms/cpp/ignite/odbc/type_traits.cpp
index 9fc1075f29..bdafc1c767 100644
--- a/modules/platforms/cpp/ignite/odbc/type_traits.cpp
+++ b/modules/platforms/cpp/ignite/odbc/type_traits.cpp
@@ -76,8 +76,11 @@ const char *statement_attr_id_to_string(long id) {
  */
 namespace sql_type_name {
 
-/** VARCHAR SQL type name constant. */
-const inline std::string VARCHAR("VARCHAR");
+/** BOOLEAN SQL type name constant. */
+const inline std::string BOOLEAN("BOOLEAN");
+
+/** TINYINT SQL type name constant. */
+const inline std::string TINYINT("TINYINT");
 
 /** SMALLINT SQL type name constant. */
 const inline std::string SMALLINT("SMALLINT");
@@ -85,45 +88,45 @@ const inline std::string SMALLINT("SMALLINT");
 /** INTEGER SQL type name constant. */
 const inline std::string INTEGER("INTEGER");
 
-/** DECIMAL SQL type name constant. */
-const inline std::string DECIMAL("DECIMAL");
+/** BIGINT SQL type name constant. */
+const inline std::string BIGINT("BIGINT");
 
 /** FLOAT SQL type name constant. */
-const inline std::string FLOAT("FLOAT");
+const inline std::string REAL("REAL");
 
 /** DOUBLE SQL type name constant. */
 const inline std::string DOUBLE("DOUBLE");
 
-/** BIT SQL type name constant. */
-const inline std::string BIT("BIT");
-
-/** TINYINT SQL type name constant. */
-const inline std::string TINYINT("TINYINT");
-
-/** BIGINT SQL type name constant. */
-const inline std::string BIGINT("BIGINT");
+/** VARCHAR SQL type name constant. */
+const inline std::string VARCHAR("VARCHAR");
 
 /** BINARY SQL type name constant. */
 const inline std::string BINARY("VARBINARY");
 
-/** DATE SQL type name constant. */
-const inline std::string DATE("DATE");
+/** TIME SQL type name constant. */
+const inline std::string TIME("TIME");
 
 /** TIMESTAMP SQL type name constant. */
 const inline std::string TIMESTAMP("TIMESTAMP");
 
-/** TIME SQL type name constant. */
-const inline std::string TIME("TIME");
+/** DATE SQL type name constant. */
+const inline std::string DATE("DATE");
 
-/** GUID SQL type name constant. */
-const inline std::string GUID("GUID");
+/** DECIMAL SQL type name constant. */
+const inline std::string DECIMAL("DECIMAL");
+
+/** UUID SQL type name constant. */
+const inline std::string UUID("UUID");
 
-}; // namespace sql_type_name
+} // namespace sql_type_name
 
 const std::string &ignite_type_to_sql_type_name(ignite_type typ) {
     switch (typ) {
-        case ignite_type::STRING:
-            return sql_type_name::VARCHAR;
+        case ignite_type::BOOLEAN:
+            return sql_type_name::BOOLEAN;
+
+        case ignite_type::INT8:
+            return sql_type_name::TINYINT;
 
         case ignite_type::INT16:
             return sql_type_name::SMALLINT;
@@ -134,24 +137,21 @@ const std::string 
&ignite_type_to_sql_type_name(ignite_type typ) {
         case ignite_type::INT64:
             return sql_type_name::BIGINT;
 
-        case ignite_type::DECIMAL:
-        case ignite_type::NUMBER:
-            return sql_type_name::DECIMAL;
-
         case ignite_type::FLOAT:
-            return sql_type_name::FLOAT;
+            return sql_type_name::REAL;
 
         case ignite_type::DOUBLE:
             return sql_type_name::DOUBLE;
 
-        case ignite_type::BOOLEAN:
-            return sql_type_name::BIT;
+        case ignite_type::STRING:
+            return sql_type_name::VARCHAR;
 
-        case ignite_type::INT8:
-            return sql_type_name::TINYINT;
+        case ignite_type::DECIMAL:
+        case ignite_type::NUMBER:
+            return sql_type_name::DECIMAL;
 
         case ignite_type::UUID:
-            return sql_type_name::GUID;
+            return sql_type_name::UUID;
 
         case ignite_type::DATE:
             return sql_type_name::DATE;
@@ -263,13 +263,13 @@ ignite_type sql_type_to_ignite_type(std::int16_t 
sql_type) {
             return ignite_type::DATE;
 
         case SQL_TYPE_TIMESTAMP:
-            return ignite_type::TIMESTAMP;
+            return ignite_type::DATETIME;
 
         case SQL_TYPE_TIME:
             return ignite_type::TIME;
 
         default:
-            // TODO: Add proper support for all possible types.
+            // TODO: IGNITE-19969 implement support for period, duration and 
big_integer
             break;
     }
 
@@ -405,14 +405,11 @@ std::int16_t ignite_type_to_sql_type(ignite_type typ) {
 }
 
 std::int16_t ignite_type_nullability(ignite_type typ) {
-    // TODO: IGNITE-19854 Remove once parameters meta fetching is implemented
     UNUSED_VALUE(typ);
-
-    return SQL_NULLABLE_UNKNOWN;
+    return SQL_NULLABLE;
 }
 
 std::int32_t sql_type_display_size(std::int16_t type) {
-    // TODO: IGNITE-19854 Remove once parameters meta fetching is implemented
     switch (type) {
         case SQL_VARCHAR:
         case SQL_CHAR:
@@ -465,26 +462,12 @@ std::int32_t sql_type_display_size(std::int16_t type) {
 }
 
 std::int32_t ignite_type_display_size(ignite_type typ) {
-    // TODO: IGNITE-19854 Remove once parameters meta fetching is implemented
     std::int16_t sql_type = ignite_type_to_sql_type(typ);
-
     return sql_type_display_size(sql_type);
 }
 
 std::int32_t sql_type_column_size(std::int16_t type) {
-    // TODO: IGNITE-19854 Remove once parameters meta fetching is implemented
     switch (type) {
-        case SQL_VARCHAR:
-        case SQL_CHAR:
-        case SQL_WCHAR:
-        case SQL_LONGVARBINARY:
-        case SQL_BINARY:
-        case SQL_VARBINARY:
-        case SQL_LONGVARCHAR:
-        case SQL_DECIMAL:
-        case SQL_NUMERIC:
-            return SQL_NO_TOTAL;
-
         case SQL_BIT:
             return 1;
 
@@ -519,20 +502,26 @@ std::int32_t sql_type_column_size(std::int16_t type) {
         case SQL_GUID:
             return 36;
 
+        case SQL_VARCHAR:
+        case SQL_CHAR:
+        case SQL_WCHAR:
+        case SQL_LONGVARBINARY:
+        case SQL_BINARY:
+        case SQL_VARBINARY:
+        case SQL_LONGVARCHAR:
+        case SQL_DECIMAL:
+        case SQL_NUMERIC:
         default:
             return SQL_NO_TOTAL;
     }
 }
 
-std::int32_t ignite_type_column_size(ignite_type typ) {
-    // TODO: IGNITE-19854 Remove once parameters meta fetching is implemented
+std::int32_t ignite_type_max_column_size(ignite_type typ) {
     std::int16_t sql_type = ignite_type_to_sql_type(typ);
-
     return sql_type_column_size(sql_type);
 }
 
 std::int32_t sql_type_transfer_length(std::int16_t type) {
-    // TODO: IGNITE-19854 Remove once parameters meta fetching is implemented
     switch (type) {
         case SQL_VARCHAR:
         case SQL_CHAR:
@@ -582,7 +571,6 @@ std::int32_t ignite_type_transfer_length(ignite_type typ) {
 }
 
 std::int32_t sql_type_num_precision_radix(std::int16_t type) {
-    // TODO: IGNITE-19854 Remove once parameters meta fetching is implemented
     switch (type) {
         case SQL_REAL:
         case SQL_FLOAT:
@@ -642,4 +630,35 @@ bool is_ignite_type_unsigned(ignite_type typ) {
     return is_sql_type_unsigned(sql_type);
 }
 
+std::optional<std::string> ignite_type_literal_prefix(ignite_type typ) {
+    switch (typ) {
+        case ignite_type::STRING:
+            return "'";
+        case ignite_type::BYTE_ARRAY:
+            return "0x";
+        case ignite_type::DATE:
+            return "DATE '";
+        case ignite_type::TIME:
+            return "TIME '";
+        case ignite_type::TIMESTAMP:
+            return "TIMESTAMP '";
+        default:
+            break;
+    }
+    return {};
+}
+
+std::optional<std::string> ignite_type_literal_suffix(ignite_type typ) {
+    switch (typ) {
+        case ignite_type::STRING:
+        case ignite_type::DATE:
+        case ignite_type::TIME:
+        case ignite_type::TIMESTAMP:
+            return "'";
+        default:
+            break;
+    }
+    return {};
+}
+
 } // namespace ignite
diff --git a/modules/platforms/cpp/ignite/odbc/type_traits.h 
b/modules/platforms/cpp/ignite/odbc/type_traits.h
index 727af18d26..aa9b65cc7b 100644
--- a/modules/platforms/cpp/ignite/odbc/type_traits.h
+++ b/modules/platforms/cpp/ignite/odbc/type_traits.h
@@ -20,6 +20,7 @@
 #include <ignite/common/ignite_type.h>
 
 #include <cstdint>
+#include <optional>
 #include <string>
 
 namespace ignite {
@@ -189,7 +190,7 @@ int32_t sql_type_column_size(int16_t type);
  * @param typ Ignite type.
  * @return Column size.
  */
-int32_t ignite_type_column_size(ignite_type typ);
+int32_t ignite_type_max_column_size(ignite_type typ);
 
 /**
  * Get SQL type transfer octet length.
@@ -255,4 +256,20 @@ bool is_sql_type_unsigned(int16_t type);
  */
 bool is_ignite_type_unsigned(ignite_type typ);
 
+/**
+ * Get literal prefix for an ignite type.
+ *
+ * @param typ Type.
+ * @return Prefix.
+ */
+std::optional<std::string> ignite_type_literal_prefix(ignite_type typ);
+
+/**
+ * Get literal suffix for an ignite type.
+ *
+ * @param typ Type.
+ * @return Suffix.
+ */
+std::optional<std::string> ignite_type_literal_suffix(ignite_type typ);
+
 } // namespace ignite
diff --git a/modules/platforms/cpp/ignite/protocol/utils.cpp 
b/modules/platforms/cpp/ignite/protocol/utils.cpp
index c937c9f59d..b3311c2f7c 100644
--- a/modules/platforms/cpp/ignite/protocol/utils.cpp
+++ b/modules/platforms/cpp/ignite/protocol/utils.cpp
@@ -90,8 +90,8 @@ T unpack_int(const msgpack_object &object) {
 
 template<typename T>
 T unpack_uint(const msgpack_object &object) {
-    static_assert(
-        std::numeric_limits<T>::is_integer && 
!std::numeric_limits<T>::is_signed, "Type T is not a unsigned integer type");
+    static_assert(std::numeric_limits<T>::is_integer && 
!std::numeric_limits<T>::is_signed,
+        "Type T is not a unsigned integer type");
 
     auto u64_val = unpack_object<std::uint64_t>(object);
 
diff --git 
a/modules/platforms/cpp/tests/client-test/key_value_binary_view_test.cpp 
b/modules/platforms/cpp/tests/client-test/key_value_binary_view_test.cpp
index 43fe96213f..38fc94e035 100644
--- a/modules/platforms/cpp/tests/client-test/key_value_binary_view_test.cpp
+++ b/modules/platforms/cpp/tests/client-test/key_value_binary_view_test.cpp
@@ -842,7 +842,7 @@ TEST_F(key_value_binary_view_test, 
remove_all_nonexisting_keys_return_all) {
 
     EXPECT_EQ(res.size(), 2);
 
-    // TODO: Key order should be preserved by the server (IGNITE-16004).
+    // TODO: Key order should be preserved by the server (IGNITE-20435).
     EXPECT_EQ(1, res[0].column_count());
     EXPECT_EQ(2, res[0].get<int64_t>("key"));
 
@@ -878,7 +878,7 @@ TEST_F(key_value_binary_view_test, remove_all_overlapped) {
 
     EXPECT_EQ(res.size(), 2);
 
-    // TODO: Key order should be preserved by the server (IGNITE-16004).
+    // TODO: Key order should be preserved by the server (IGNITE-20435).
     EXPECT_EQ(1, res[0].column_count());
     EXPECT_EQ(12, res[0].get<int64_t>("key"));
 
@@ -894,7 +894,7 @@ TEST_F(key_value_binary_view_test, remove_all_empty) {
 TEST_F(key_value_binary_view_test, remove_all_exact_nonexisting) {
     auto res = kv_view.remove_all(nullptr, {{get_tuple(1), get_tuple("foo")}, 
{get_tuple(2), get_tuple("bar")}});
 
-    // TODO: Key order should be preserved by the server (IGNITE-16004).
+    // TODO: Key order should be preserved by the server (IGNITE-20435).
     ASSERT_EQ(2, res.size());
 }
 
diff --git a/modules/platforms/cpp/tests/client-test/key_value_view_test.cpp 
b/modules/platforms/cpp/tests/client-test/key_value_view_test.cpp
index 3d0c5d4bc9..98ee0264b0 100644
--- a/modules/platforms/cpp/tests/client-test/key_value_view_test.cpp
+++ b/modules/platforms/cpp/tests/client-test/key_value_view_test.cpp
@@ -699,7 +699,7 @@ TEST_F(key_value_view_test, 
remove_all_nonexisting_keys_return_all) {
 
     EXPECT_EQ(res.size(), 2);
 
-    // TODO: Key order should be preserved by the server (IGNITE-16004).
+    // TODO: Key order should be preserved by the server (IGNITE-20435).
     EXPECT_EQ(2, res[0].key);
     EXPECT_EQ(1, res[1].key);
 }
@@ -732,7 +732,7 @@ TEST_F(key_value_view_test, remove_all_overlapped) {
 
     EXPECT_EQ(res.size(), 2);
 
-    // TODO: Key order should be preserved by the server (IGNITE-16004).
+    // TODO: Key order should be preserved by the server (IGNITE-20435).
     EXPECT_EQ(12, res[0].key);
     EXPECT_EQ(11, res[1].key);
 }
@@ -746,7 +746,7 @@ TEST_F(key_value_view_test, remove_all_exact_nonexisting) {
     auto res = kv_view.remove_all(
         nullptr, {{test_key_type(1), test_value_type("foo")}, 
{test_key_type(2), test_value_type("bar")}});
 
-    // TODO: Key order should be preserved by the server (IGNITE-16004).
+    // TODO: Key order should be preserved by the server (IGNITE-20435).
     ASSERT_EQ(2, res.size());
 }
 
diff --git 
a/modules/platforms/cpp/tests/client-test/record_binary_view_test.cpp 
b/modules/platforms/cpp/tests/client-test/record_binary_view_test.cpp
index 2c5823dcf5..b77e7e7425 100644
--- a/modules/platforms/cpp/tests/client-test/record_binary_view_test.cpp
+++ b/modules/platforms/cpp/tests/client-test/record_binary_view_test.cpp
@@ -845,7 +845,7 @@ TEST_F(record_binary_view_test, 
remove_all_nonexisting_keys_return_all) {
 
     EXPECT_EQ(res.size(), 2);
 
-    // TODO: Key order should be preserved by the server (IGNITE-16004).
+    // TODO: Key order should be preserved by the server (IGNITE-20435).
     EXPECT_EQ(1, res[0].column_count());
     EXPECT_EQ(2, res[0].get<int64_t>("key"));
 
@@ -880,7 +880,7 @@ TEST_F(record_binary_view_test, remove_all_overlapped) {
 
     EXPECT_EQ(res.size(), 2);
 
-    // TODO: Key order should be preserved by the server (IGNITE-16004).
+    // TODO: Key order should be preserved by the server (IGNITE-20435).
     EXPECT_EQ(1, res[0].column_count());
     EXPECT_EQ(12, res[0].get<int64_t>("key"));
 
@@ -896,7 +896,7 @@ TEST_F(record_binary_view_test, remove_all_empty) {
 TEST_F(record_binary_view_test, remove_all_exact_nonexisting) {
     auto res = tuple_view.remove_all_exact(nullptr, {get_tuple(1, "foo"), 
get_tuple(2, "bar")});
 
-    // TODO: Key order should be preserved by the server (IGNITE-16004).
+    // TODO: Key order should be preserved by the server (IGNITE-20435).
     ASSERT_EQ(2, res.size());
 }
 
diff --git a/modules/platforms/cpp/tests/client-test/record_view_test.cpp 
b/modules/platforms/cpp/tests/client-test/record_view_test.cpp
index 995b2b052b..73935a1331 100644
--- a/modules/platforms/cpp/tests/client-test/record_view_test.cpp
+++ b/modules/platforms/cpp/tests/client-test/record_view_test.cpp
@@ -874,7 +874,7 @@ TEST_F(record_view_test, 
remove_all_nonexisting_keys_return_all) {
 
     EXPECT_EQ(res.size(), 2);
 
-    // TODO: Key order should be preserved by the server (IGNITE-16004).
+    // TODO: Key order should be preserved by the server (IGNITE-20435).
     EXPECT_EQ(2, res[0].key);
     EXPECT_EQ(1, res[1].key);
 }
@@ -906,7 +906,7 @@ TEST_F(record_view_test, remove_all_overlapped) {
 
     EXPECT_EQ(res.size(), 2);
 
-    // TODO: Key order should be preserved by the server (IGNITE-16004).
+    // TODO: Key order should be preserved by the server (IGNITE-20435).
     EXPECT_EQ(12, res[0].key);
     EXPECT_EQ(11, res[1].key);
 }
@@ -919,7 +919,7 @@ TEST_F(record_view_test, remove_all_empty) {
 TEST_F(record_view_test, remove_all_exact_nonexisting) {
     auto res = view.remove_all_exact(nullptr, {test_type(1, "foo"), 
test_type(2, "bar")});
 
-    // TODO: Key order should be preserved by the server (IGNITE-16004).
+    // TODO: Key order should be preserved by the server (IGNITE-20435).
     ASSERT_EQ(2, res.size());
 }
 
diff --git a/modules/platforms/cpp/tests/odbc-test/meta_queries_test.cpp 
b/modules/platforms/cpp/tests/odbc-test/meta_queries_test.cpp
index 30dc1101ba..ed07bd9e5a 100644
--- a/modules/platforms/cpp/tests/odbc-test/meta_queries_test.cpp
+++ b/modules/platforms/cpp/tests/odbc-test/meta_queries_test.cpp
@@ -18,7 +18,6 @@
 #include "odbc_connection.h"
 #include "odbc_suite.h"
 
-#include "ignite/common/config.h"
 #include "ignite/odbc/string_utils.h"
 
 #include <gtest/gtest.h>
@@ -278,8 +277,6 @@ public:
     }
 };
 
-// TODO IGNITE-19216 Implement type info fetching
-#ifdef MUTED
 TEST_F(meta_queries_test, test_get_type_info_all_types) {
     odbc_connect(get_basic_connection_string());
 
@@ -287,8 +284,16 @@ TEST_F(meta_queries_test, test_get_type_info_all_types) {
 
     if (!SQL_SUCCEEDED(ret))
         FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement));
+
+    constexpr auto TYPES_NUM = 16;
+    for (int i = 0; i < TYPES_NUM; ++i) {
+        ret = SQLFetch(m_statement);
+        EXPECT_EQ(ret, SQL_SUCCESS) << "Index " << i;
+    }
+
+    ret = SQLFetch(m_statement);
+    EXPECT_EQ(ret, SQL_NO_DATA);
 }
-#endif // MUTED
 
 TEST_F(meta_queries_test, date_type_column_attribute_curdate) {
     odbc_connect(get_basic_connection_string());
@@ -516,8 +521,6 @@ TEST_F(meta_queries_test, 
col_attributes_column_scale_prepare) {
 }
 #endif // MUTED
 
-// TODO: IGNITE-19216 Implement type info query.
-#ifdef MUTED
 TEST_F(meta_queries_test, get_data_with_get_type_info) {
     odbc_connect(get_basic_connection_string());
 
@@ -528,7 +531,6 @@ TEST_F(meta_queries_test, get_data_with_get_type_info) {
 
     check_single_row_result_set_with_get_data(m_statement);
 }
-#endif // MUTED
 
 TEST_F(meta_queries_test, get_data_with_tables) {
     odbc_connect(get_basic_connection_string());
@@ -660,7 +662,7 @@ TEST_F(meta_queries_test, ddl_tables_meta_table_type_list) {
 }
 
 template<size_t n, size_t k>
-void check_meta(char columns[n][k], SQLLEN columns_len[n], std::string 
table_name) {
+void check_meta(char columns[n][k], SQLLEN columns_len[n], const std::string 
&table_name) {
     std::string catalog(columns[0], columns_len[0]);
     std::string schema(columns[1], columns_len[1]);
     std::string table(columns[2], columns_len[2]);


Reply via email to