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 82d46f420f IGNITE-20346 ODBC: Implement column metadata fetching 
(#2650)
82d46f420f is described below

commit 82d46f420f98665191110aefd0add421bddec161
Author: Igor Sapego <isap...@apache.org>
AuthorDate: Tue Oct 3 14:26:08 2023 +0400

    IGNITE-20346 ODBC: Implement column metadata fetching (#2650)
---
 modules/platforms/cpp/CMakeLists.txt               |   1 -
 modules/platforms/cpp/DEVNOTES.md                  | 143 +++++----
 modules/platforms/cpp/ignite/odbc/CMakeLists.txt   |   1 +
 .../cpp/ignite/odbc/app/application_data_buffer.h  |  18 ++
 .../ignite/odbc/query/column_metadata_query.cpp    | 324 +++++++++++++++++++++
 .../cpp/ignite/odbc/query/column_metadata_query.h  | 185 ++++++++++++
 modules/platforms/cpp/ignite/odbc/query/query.h    |   3 +
 .../cpp/ignite/odbc/query/type_info_query.cpp      |   2 +-
 .../platforms/cpp/ignite/odbc/sql_statement.cpp    |  11 +-
 modules/platforms/cpp/ignite/odbc/type_traits.cpp  |  41 ++-
 modules/platforms/cpp/ignite/odbc/type_traits.h    |  36 +--
 .../cpp/ignite/protocol/client_operation.h         |   3 +
 .../cpp/tests/odbc-test/api_robustness_test.cpp    |  18 +-
 .../cpp/tests/odbc-test/meta_queries_test.cpp      |  34 +--
 14 files changed, 705 insertions(+), 115 deletions(-)

diff --git a/modules/platforms/cpp/CMakeLists.txt 
b/modules/platforms/cpp/CMakeLists.txt
index 2d75fcb554..fac1523dcf 100644
--- a/modules/platforms/cpp/CMakeLists.txt
+++ b/modules/platforms/cpp/CMakeLists.txt
@@ -26,7 +26,6 @@ set(CMAKE_PROJECT_VERSION ${PROJECT_VERSION})
 option(ENABLE_CONAN "Use Conan package manager to get dependencies" ON)
 option(ENABLE_CLIENT "Build Ignite.C++ Client module" ON)
 option(ENABLE_ODBC "Build Ignite ODBC driver module" OFF)
-option(ENABLE_ODBC_MSI "Build Ignite ODBC driver installer for Windows" OFF)
 option(ENABLE_TESTS "Build Ignite.C++ tests" OFF)
 option(ENABLE_ADDRESS_SANITIZER "If address sanitizer is enabled" OFF)
 option(ENABLE_UB_SANITIZER "If undefined behavior sanitizer is enabled" OFF)
diff --git a/modules/platforms/cpp/DEVNOTES.md 
b/modules/platforms/cpp/DEVNOTES.md
index a81bef9d4f..39a267ed72 100644
--- a/modules/platforms/cpp/DEVNOTES.md
+++ b/modules/platforms/cpp/DEVNOTES.md
@@ -1,16 +1,18 @@
 ## Build C++
 
 ### Prerequisites
-* C++ compiler supporting C++17
-* One of build systems: make, ninja, MS Visual Studio, etc
-* Conan C/C++ package manager 1.X (optional)
-* CMake 3.10+
+* C++ compiler supporting C++17;
+* One of build systems: make, ninja, MS Visual Studio, etc;
+* Conan C/C++ package manager 1.X (optional);
+* CMake 3.10+;
+* To build the ODBC driver, it is required to have an ODBC driver manager with 
headers on your system. On Windows, it 
+  comes with your OS, but on Unix-like systems you may need to install one, 
for example, unixODBC;
 
 ### Installing Conan Package Manager
 
 The Conan package manager can be obtained from [its website](https://conan.io).
 
-Currently we support Conan versions 1.X. The version 2.0+ is **not** supported 
yet.
+Currently, we support Conan versions 1.X. The version 2.0+ is **not** 
supported yet.
 
 One way to install Conan is as follows (need python in your system):
 
@@ -18,7 +20,7 @@ One way to install Conan is as follows (need python in your 
system):
 pip install conan==1.59.0
 ```
 
-Also before use it might be required to configure the default conan profile:
+Also, before use, it might be required to configure the default conan profile:
 
 ```
 conan profile new --detect default
@@ -29,10 +31,8 @@ conan profile update settings.compiler.libcxx=libstdc++11 
default
 
 It is possible to build the project without Conan if all the dependencies are 
installed on the system manually.
 The project dependencies include the following libraries:
-
-       - msgpack-c 4.0.0
-       - gtest 1.12.1
-       - unixodbc 
+- msgpack-c 4.0.0
+- gtest 1.12.1
 
 When the project is configured with the `-DENABLE_CONAN=OFF` CMake option, the 
Conan machinery is turned off and
 the dependencies are resolved by using the standard means of the build 
platform. For example, the project can be
@@ -40,100 +40,143 @@ configured like this:
 
 ```shell
 ...
-cmake .. -DENABLE_CONAN=0 -DCMAKE_BUILD_TYPE=Release
+cmake .. -DENABLE_CONAN=OFF -DCMAKE_BUILD_TYPE=Release
 ...
 ```
 
-However Conan is enabled by default and so all the build examples below use it.
+However, Conan is enabled by default, and so all the build examples below use 
it.
 
-### Linux Build
+### CMake options and examples of typical build configurations
 
-#### Building in debug mode with tests
+There are multiple configuration options supported by the CMake project that 
can be used to choose what exactly and how
+should be built. You can check the whole list of available cmake options with 
the command `cmake -LAH`. Below, you can
+see project-specific options only:
+- ENABLE_CONAN={ON|OFF}. ON by default. The effect of this exact option is 
described in a section above;
+- ENABLE_CLIENT={ON|OFF}. ON by default. Indicates whether the C++ client 
should be built;
+- ENABLE_ODBC={ON|OFF}. OFF by default. Indicates whether the ODBC driver 
should be built;
+- ENABLE_TESTS={ON|OFF}. OFF by default. Indicates whether the tests for the 
selected components should be built;
+- WARNINGS_AS_ERRORS={ON|OFF}. OFF by default. If enabled, compiler will treat 
warnings as errors. It may be a good idea
+  to enable this option if you are planning on submitting a PR, but if you 
just want to build a project just keep it 
+  disabled;
 
-In this dir:
+There are also general CMake options that you should specify. They are build 
type options. There are two types of build
+available - `Release` and `Debug`. The choice here depends on how are you 
going to use resulting artifacts. If you are
+going to use them in production, it is probably a good idea to use `Release` 
build type. If you are just an Ignite
+developer or planning to submit a patch for the project, use Debug.
+
+You should ALWAYS specify a build type.
+
+There are two options for this:
+- CMAKE_BUILD_TYPE={Debug|Release}. Used on single-configuration generators, 
like Unix Makefile generator or Ninja. This
+  is a parameter that you should most likely use if you are using Unix-like OS;
+- CMAKE_CONFIGURATION_TYPES={Debug|Release}. Used on multi-configuration 
generators like Visual Studio. If you are using
+  Visual Studio generator, you should specify this option, as it ignores 
CMAKE_BUILD_TYPE.
+
+So, basically, if you are an Apache Ignite developer, you would want to 
configure your project like this:
 
 ```shell
-mkdir cmake-build-debug
-cd cmake-build-debug
-cmake .. -DENABLE_TESTS=ON -DENABLE_ODBC=OFF -DCMAKE_BUILD_TYPE=Debug
-cmake --build . -j8
+cmake .. -DENABLE_ODBC=ON -DENABLE_TESTS=ON -DWARNINGS_AS_ERRORS=ON 
-DCMAKE_BUILD_TYPE=Debug
 ```
 
-#### Building in release mode without tests
+But if you just want to compile the client to use in your project, the 
following configuration is more fitting for you,
+considering default options:
 
-In this dir:
+```shell
+cmake .. -DCMAKE_BUILD_TYPE=Release
+```
+
+If you don't need the client and just want to compile the ODBC driver, just 
add the following options:
 
 ```shell
-mkdir cmake-build-release
-cd cmake-build-release
-cmake .. -DENABLE_TESTS=OFF -DENABLE_ODBC=OFF -DCMAKE_BUILD_TYPE=Release
-cmake --build . -j8
+cmake .. -DENABLE_CLIENT=OFF -DENABLE_ODBC=ON -DCMAKE_BUILD_TYPE=Release
 ```
 
-### MacOS Build
+And do not forget to replace `CMAKE_BUILD_TYPE` with 
`CMAKE_CONFIGURATION_TYPES` if you are using Visual Studio
+generator:
 
-On macOS it is typically required to use the C++ standard library from the 
LLVM project:
+```shell
+cmake .. -DCMAKE_CONFIGURATION_TYPES=Release
+```
+
+Below, you can find more detailed line-by-line instructions and configurations 
for different platforms and use-cases.
+
+
+### Linux and macOS Builds
+
+On macOS, it is typically required to specify the C++ standard library from 
the LLVM project if you are using Conan:
 
 ```
 conan profile update settings.compiler.libcxx=libc++11 default
 ```
 
-#### Building in debug mode with tests.
+#### Building in debug mode with tests and ODBC
 
 In this dir:
 
 ```shell
 mkdir cmake-build-debug
 cd cmake-build-debug
-cmake .. -DENABLE_TESTS=ON -DENABLE_ODBC=OFF -DCMAKE_BUILD_TYPE=Debug
-cmake --build . -j8
+cmake .. -DENABLE_TESTS=ON -DENABLE_ODBC=ON -DCMAKE_BUILD_TYPE=Debug
+cmake --build .
+```
+
+#### Building only the client in release mode
+
+In this dir:
+
+```shell
+mkdir cmake-build-release
+cd cmake-build-release
+cmake .. -DCMAKE_BUILD_TYPE=Release
+cmake --build .
 ```
 
-#### Building in release mode without tests.
+#### Building only the ODBC driver in release mode
 
 In this dir:
 
 ```shell
 mkdir cmake-build-release
 cd cmake-build-release
-cmake .. -DENABLE_TESTS=OFF -DENABLE_ODBC=OFF -DCMAKE_BUILD_TYPE=Release
-cmake --build . -j8
+cmake .. -DENABLE_CLIENT=OFF -DENABLE_ODBC=ON -DCMAKE_BUILD_TYPE=Release
+cmake --build .
 ```
 
 ### Windows Build
 
-#### Building in debug mode with tests
+#### Building in debug mode with tests and ODBC using single-config generator
 
-In this dir (using the ninja build system, other single-config systems can be 
used too):
+In this dir (using Ninja, but any other single-config generator can be used):
 
 ```shell
 mkdir cmake-build-debug
 cd cmake-build-debug
-cmake .. -DENABLE_TESTS=ON -DENABLE_ODBC=OFF -DCMAKE_BUILD_TYPE=Debug -GNinja
-cmake --build . -j8
+cmake .. -DENABLE_TESTS=ON -DENABLE_ODBC=ON -DCMAKE_BUILD_TYPE=Debug -GNinja
+cmake --build .
 ```
 
-#### Building in release mode without tests
+#### Building only the client in release mode using single-config generator
 
-In this dir (using the ninja build system, other single-config systems can be 
used too):
+In this dir (using Ninja, but any other single-config generator can be used):
 
 ```shell
 mkdir cmake-build-release
 cd cmake-build-release
-cmake .. -DENABLE_TESTS=OFF -DENABLE_ODBC=OFF -DCMAKE_BUILD_TYPE=Release 
-GNinja
-cmake --build . -j8
+cmake .. -DCMAKE_BUILD_TYPE=Release -GNinja
+cmake --build .
 ```
 
 #### Building with Visual Studio in multi-config mode
 
 Run in this dir from, for example, [VS developer 
PowerShell](https://learn.microsoft.com/en-us/visualstudio/ide/reference/command-prompt-powershell?view=vs-2022):
+We are using Visual Studio 17 2022 in this example, but any other multi-config 
generator can be used.
 
 ```shell
 mkdir cmake-build
 cd cmake-build
-cmake .. -DENABLE_TESTS=ON
-cmake --build . --config Debug -j8
-cmake --build . --config Release -j8
+cmake .. -DENABLE_TESTS=ON -DENABLE_ODBC=ON 
-DCMAKE_CONFIGURATION_TYPES="Debug;Release" -G "Visual Studio 17 2022"
+cmake --build . --config Debug
+cmake --build . --config Release
 ```
 
 ## Run Tests
@@ -144,29 +187,29 @@ cmake --build . --config Release -j8
 
 ### Starting Java Test Node
 
-Tests require a running Java node. You don't need to start it separately, if 
there is no running test nodes, tests will
-start one internally. So prior to running tests you will obviously need to 
build a Java part of the product. To do that
-the following command can be used from the root of the repo:
+Tests require a running Java node. You don't need to start it separately, if 
there are no running test nodes, tests will
+start one internally. So prior to running tests, you will obviously need to 
build a Java part of the product. To do
+that, the following command can be used from the root of the repo:
 `./gradlew assemble compileIntegrationTestJava`
 
 Or a faster variant:
 `./gradlew assemble compileIntegrationTestJava -x check -x assembleDist -x 
distTar -x distZip --parallel`
 
 You can start a Test Node separately in the root repo. Tests will detect that 
there is a running node and will not start
-another one. This can be useful for debugging. To start node from the console 
you can use the following command prompt:
+another one. This can be useful for debugging. To start node from the console, 
you can use the following command prompt:
 `./gradlew :ignite-runner:runnerPlatformTest --no-daemon`
 
 You can also run 
`org.apache.ignite.internal.runner.app.PlatformTestNodeRunner` class in IDEA 
with a debugger or
 profiler, then run Client tests as usual.
 
-### Starting tests in Windows
+### Starting tests on Windows
 In modules/platforms/cpp dir:
 `./cmake-build-debug/bin/ignite-client-test.exe`
 
 To run a specific test:
 `./cmake-build-debug/bin/ignite-client-test.exe --gtest_filter=Test_Cases1*`
 
-### Starting tests in Linux
+### Starting tests on Linux
 In modules/platforms/cpp dir:
 `./cmake-build-debug/bin/ignite-client-test`
 
diff --git a/modules/platforms/cpp/ignite/odbc/CMakeLists.txt 
b/modules/platforms/cpp/ignite/odbc/CMakeLists.txt
index ea20e78785..309ee4ce75 100644
--- a/modules/platforms/cpp/ignite/odbc/CMakeLists.txt
+++ b/modules/platforms/cpp/ignite/odbc/CMakeLists.txt
@@ -35,6 +35,7 @@ set(SOURCES
     diagnostic/diagnostic_record_storage.cpp
     meta/column_meta.cpp
     meta/table_meta.cpp
+    query/column_metadata_query.cpp
     query/data_query.cpp
     query/table_metadata_query.cpp
     query/type_info_query.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 fd7ea6ee8d..96b4b57b12 100644
--- a/modules/platforms/cpp/ignite/odbc/app/application_data_buffer.h
+++ b/modules/platforms/cpp/ignite/odbc/app/application_data_buffer.h
@@ -150,6 +150,24 @@ public:
      */
     conversion_result put_bool(bool value);
 
+    /**
+     * Put in buffer value of type string.
+     *
+     * @param value Value.
+     * @return Conversion result.
+     */
+    conversion_result put_string(const std::optional<std::string> &value) {
+        return value ? put_string(*value) : put_null();
+    }
+
+    /**
+     * Put in buffer value of type string.
+     *
+     * @param value Value.
+     * @return Conversion result.
+     */
+    conversion_result put_string(const char *value) { return 
put_string(std::string(value)); }
+
     /**
      * Put in buffer value of type string.
      *
diff --git a/modules/platforms/cpp/ignite/odbc/query/column_metadata_query.cpp 
b/modules/platforms/cpp/ignite/odbc/query/column_metadata_query.cpp
new file mode 100644
index 0000000000..fb31c62911
--- /dev/null
+++ b/modules/platforms/cpp/ignite/odbc/query/column_metadata_query.cpp
@@ -0,0 +1,324 @@
+/*
+ * 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 <utility>
+
+#include "ignite/odbc/log.h"
+#include "ignite/odbc/odbc_error.h"
+#include "ignite/odbc/query/column_metadata_query.h"
+#include "ignite/odbc/sql_connection.h"
+#include "ignite/odbc/type_traits.h"
+
+namespace {
+
+enum class result_column {
+    /** Catalog name. NULL if not applicable to the data source. */
+    TABLE_CAT = 1,
+
+    /** Schema name. NULL if not applicable to the data source. */
+    TABLE_SCHEM,
+
+    /** Table name. */
+    TABLE_NAME,
+
+    /** Column name. */
+    COLUMN_NAME,
+
+    /** SQL data type. */
+    DATA_TYPE,
+
+    /** Data source-dependent data type name. */
+    TYPE_NAME,
+
+    /** Column size. */
+    COLUMN_SIZE,
+
+    /** The length in bytes of data transferred on fetch. */
+    BUFFER_LENGTH,
+
+    /** The total number of significant digits to the right of the decimal 
point. */
+    DECIMAL_DIGITS,
+
+    /** Precision. */
+    NUM_PREC_RADIX,
+
+    /** Nullability of the data in column. */
+    NULLABLE,
+
+    /** A description of the column. */
+    REMARKS
+};
+
+using namespace ignite;
+
+/**
+ * Reads result set metadata.
+ *
+ * @param reader Reader.
+ * @return Result set meta columns.
+ */
+std::vector<odbc_column_meta> read_meta(protocol::reader &reader) {
+    auto size = reader.read_int32();
+
+    std::vector<odbc_column_meta> columns;
+    columns.reserve(size);
+
+    for (std::int32_t column_idx = 0; column_idx < size; ++column_idx) {
+        auto has_data = reader.read_bool();
+        assert(has_data);
+
+        auto status = reader.read_int32();
+        assert(status == 0);
+
+        auto err_msg = reader.read_string_nullable();
+        assert(!err_msg);
+
+        odbc_column_meta column{};
+        column.label = reader.read_string();
+        column.schema = reader.read_string_nullable();
+        column.table = reader.read_string_nullable();
+        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();
+
+        columns.emplace_back(std::move(column));
+    }
+
+    return columns;
+}
+
+} // anonymous namespace
+
+namespace ignite {
+
+column_metadata_query::column_metadata_query(
+    diagnosable_adapter &diag, sql_connection &connection, std::string schema, 
std::string table, std::string column)
+    : query(diag, query_type::COLUMN_METADATA)
+    , m_connection(connection)
+    , m_schema(std::move(schema))
+    , m_table(std::move(table))
+    , m_column(std::move(column)) {
+    m_columns_meta.reserve(12);
+
+    const std::string sch;
+    const std::string tbl;
+
+    m_columns_meta.emplace_back(sch, tbl, "TABLE_CAT", ignite_type::STRING);
+    m_columns_meta.emplace_back(sch, tbl, "TABLE_SCHEM", ignite_type::STRING);
+    m_columns_meta.emplace_back(sch, tbl, "TABLE_NAME", ignite_type::STRING);
+    m_columns_meta.emplace_back(sch, tbl, "COLUMN_NAME", ignite_type::STRING);
+    m_columns_meta.emplace_back(sch, tbl, "DATA_TYPE", ignite_type::INT16);
+    m_columns_meta.emplace_back(sch, tbl, "TYPE_NAME", ignite_type::STRING);
+    m_columns_meta.emplace_back(sch, tbl, "COLUMN_SIZE", ignite_type::INT32);
+    m_columns_meta.emplace_back(sch, tbl, "BUFFER_LENGTH", ignite_type::INT32);
+    m_columns_meta.emplace_back(sch, tbl, "DECIMAL_DIGITS", 
ignite_type::INT16);
+    m_columns_meta.emplace_back(sch, tbl, "NUM_PREC_RADIX", 
ignite_type::INT16);
+    m_columns_meta.emplace_back(sch, tbl, "NULLABLE", ignite_type::INT16);
+    m_columns_meta.emplace_back(sch, tbl, "REMARKS", ignite_type::STRING);
+}
+
+sql_result column_metadata_query::execute() {
+    if (m_executed)
+        close();
+
+    sql_result result = make_request_get_columns_meta();
+
+    if (result == sql_result::AI_SUCCESS) {
+        m_executed = true;
+        m_fetched = false;
+
+        m_cursor = m_meta.begin();
+    }
+
+    return result;
+}
+
+sql_result column_metadata_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_meta.end())
+        return sql_result::AI_NO_DATA;
+
+    column_binding_map::iterator it;
+
+    for (it = column_bindings.begin(); it != column_bindings.end(); ++it)
+        get_column(it->first, it->second);
+
+    return sql_result::AI_SUCCESS;
+}
+
+sql_result column_metadata_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_meta.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;
+    }
+
+    const auto &current_column = *m_cursor;
+    switch (result_column(column_idx)) {
+        case result_column::TABLE_CAT: {
+            buffer.put_null();
+            break;
+        }
+
+        case result_column::TABLE_SCHEM: {
+            buffer.put_string(current_column.schema);
+            break;
+        }
+
+        case result_column::TABLE_NAME: {
+            buffer.put_string(current_column.table);
+            break;
+        }
+
+        case result_column::COLUMN_NAME: {
+            buffer.put_string(current_column.column);
+            break;
+        }
+
+        case result_column::DATA_TYPE: {
+            
buffer.put_int16(ignite_type_to_sql_type(current_column.data_type));
+            break;
+        }
+
+        case result_column::TYPE_NAME: {
+            buffer.put_string(current_column.data_type_name);
+            break;
+        }
+
+        case result_column::COLUMN_SIZE: {
+            if (current_column.data_type == ignite_type::DECIMAL || 
current_column.data_type == ignite_type::NUMBER) {
+                buffer.put_int16(std::int16_t(current_column.precision));
+                break;
+            }
+
+            std::int32_t column_size = 
ignite_type_max_column_size(current_column.data_type);
+            if (column_size < 0)
+                buffer.put_null();
+            else
+                buffer.put_int32(column_size);
+            break;
+        }
+
+        case result_column::BUFFER_LENGTH: {
+            buffer.put_null();
+            break;
+        }
+
+        case result_column::DECIMAL_DIGITS: {
+            std::int32_t dec_digits = 
ignite_type_decimal_digits(current_column.data_type, current_column.scale);
+            if (dec_digits < 0)
+                buffer.put_null();
+            else
+                buffer.put_int16(std::int16_t(dec_digits));
+            break;
+        }
+
+        case result_column::NUM_PREC_RADIX: {
+            auto radix = 
std::int16_t(ignite_type_num_precision_radix(current_column.data_type));
+            if (radix)
+                buffer.put_int16(radix);
+            else
+                buffer.put_null();
+            break;
+        }
+
+        case result_column::NULLABLE: {
+            buffer.put_int16(std::int16_t(current_column.nullable ? 
SQL_NULLABLE : SQL_NO_NULLS));
+            break;
+        }
+
+        case result_column::REMARKS: {
+            buffer.put_string(current_column.label);
+            break;
+        }
+
+        default:
+            break;
+    }
+
+    return sql_result::AI_SUCCESS;
+}
+
+sql_result column_metadata_query::close() {
+    m_meta.clear();
+
+    m_executed = false;
+
+    return sql_result::AI_SUCCESS;
+}
+
+sql_result column_metadata_query::make_request_get_columns_meta() {
+    auto success = m_diag.catch_errors([&] {
+        auto response =
+            
m_connection.sync_request(protocol::client_operation::JDBC_COLUMN_META, 
[&](protocol::writer &writer) {
+                writer.write(m_schema);
+                writer.write(m_table);
+                writer.write(m_column);
+            });
+
+        protocol::reader reader{response.get_bytes_view()};
+        m_has_result_set = reader.read_bool();
+
+        auto status = reader.read_int32();
+        auto err_msg = reader.read_string_nullable();
+        if (err_msg)
+            throw odbc_error(response_status_to_sql_state(status), *err_msg);
+
+        if (m_has_result_set) {
+            m_meta = read_meta(reader);
+        }
+
+        m_executed = true;
+    });
+
+    if (!success)
+        return sql_result::AI_ERROR;
+
+    size_t i = 0;
+    for (const auto &meta : m_meta) {
+        LOG_MSG("\n[" << i << "] SchemaName:     " << (meta.schema ? 
*meta.schema : "NULL") << "\n[" << i
+                      << "] TableName:      " << (meta.table ? *meta.table : 
"NULL") << "\n[" << i
+                      << "] ColumnName:     " << (meta.column ? *meta.column : 
"NULL") << "\n[" << i
+                      << "] ColumnType:     " << 
static_cast<std::int32_t>(meta.data_type));
+        ++i;
+    }
+
+    return sql_result::AI_SUCCESS;
+}
+
+} // namespace ignite
diff --git a/modules/platforms/cpp/ignite/odbc/query/column_metadata_query.h 
b/modules/platforms/cpp/ignite/odbc/query/column_metadata_query.h
new file mode 100644
index 0000000000..50dab7ac62
--- /dev/null
+++ b/modules/platforms/cpp/ignite/odbc/query/column_metadata_query.h
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "ignite/odbc/meta/column_meta.h"
+#include "ignite/odbc/query/query.h"
+
+namespace ignite {
+
+/** Connection forward-declaration. */
+class sql_connection;
+
+/**
+ * Column metadata.
+ */
+struct odbc_column_meta {
+    /**
+     * Default constructor.
+     */
+    odbc_column_meta() = default;
+
+    /** Label. */
+    std::string label;
+
+    /** Schema name. */
+    std::optional<std::string> schema;
+
+    /** Table name. */
+    std::optional<std::string> table;
+
+    /** Column name. */
+    std::optional<std::string> column;
+
+    /** Data type. */
+    ignite_type data_type{ignite_type::UNDEFINED};
+
+    /** Data type name. */
+    std::string data_type_name;
+
+    /** Nullability. */
+    bool nullable{false};
+
+    /** Precision. */
+    std::int32_t precision{0};
+
+    /** Scale. */
+    std::int32_t scale{0};
+};
+
+/**
+ * Query.
+ */
+class column_metadata_query : public query {
+public:
+    /**
+     * Constructor.
+     *
+     * @param diag Diagnostics collector.
+     * @param connection Associated connection.
+     * @param schema Schema search pattern.
+     * @param table Table search pattern.
+     * @param column Column search pattern.
+     */
+    column_metadata_query(diagnosable_adapter &diag, sql_connection 
&connection, std::string schema, std::string table,
+        std::string column);
+
+    /**
+     * Destructor.
+     */
+    ~column_metadata_query() override = default;
+
+    /**
+     * Execute query.
+     *
+     * @return True on success.
+     */
+    sql_result execute() override;
+
+    /**
+     * Get column metadata.
+     *
+     * @return Column metadata.
+     */
+    const column_meta_vector *get_meta() override { return &m_columns_meta; }
+
+    /**
+     * Fetch next result row to application buffers.
+     *
+     * @param column_bindings Column bindings.
+     * @return Operation result.
+     */
+    sql_result fetch_next_row(column_binding_map &column_bindings) override;
+
+    /**
+     * Get data of the specified column in the result set.
+     *
+     * @param column_idx Column index.
+     * @param buffer Buffer to put column data to.
+     * @return Operation result.
+     */
+    sql_result get_column(std::uint16_t column_idx, application_data_buffer 
&buffer) override;
+
+    /**
+     * Close query.
+     *
+     * @return True on success.
+     */
+    sql_result close() override;
+
+    /**
+     * Check if data is available.
+     *
+     * @return True if data is available.
+     */
+    bool is_data_available() const override { return m_cursor != m_meta.end(); 
}
+
+    /**
+     * Get number of rows affected by the statement.
+     *
+     * @return Number of rows affected by the statement.
+     */
+    std::int64_t affected_rows() const override { return 0; }
+
+    /**
+     * Move to the next result set.
+     *
+     * @return Operation result.
+     */
+    sql_result next_result_set() override { return sql_result::AI_NO_DATA; }
+
+private:
+    /**
+     * Make get columns metadata requests and use response to set internal 
state.
+     *
+     * @return Operation result.
+     */
+    sql_result make_request_get_columns_meta();
+
+    /** Connection associated with the statement. */
+    sql_connection &m_connection;
+
+    /** Schema search pattern. */
+    std::string m_schema;
+
+    /** Table search pattern. */
+    std::string m_table;
+
+    /** Column search pattern. */
+    std::string m_column;
+
+    /** Query executed. */
+    bool m_executed{false};
+
+    /** Fetched flag. */
+    bool m_fetched{false};
+
+    /** Result set flag. */
+    bool m_has_result_set{false};
+
+    /** Fetched metadata. */
+    std::vector<odbc_column_meta> m_meta;
+
+    /** Metadata cursor. */
+    std::vector<odbc_column_meta>::iterator m_cursor;
+
+    /** Columns metadata. */
+    column_meta_vector m_columns_meta;
+};
+
+} // namespace ignite
diff --git a/modules/platforms/cpp/ignite/odbc/query/query.h 
b/modules/platforms/cpp/ignite/odbc/query/query.h
index ce596eff70..49fe78d270 100644
--- a/modules/platforms/cpp/ignite/odbc/query/query.h
+++ b/modules/platforms/cpp/ignite/odbc/query/query.h
@@ -34,6 +34,9 @@ enum class query_type {
     /** Table metadata. */
     TABLE_METADATA,
 
+    /** Column metadata. */
+    COLUMN_METADATA,
+
     /** Type info. */
     TYPE_INFO,
 };
diff --git a/modules/platforms/cpp/ignite/odbc/query/type_info_query.cpp 
b/modules/platforms/cpp/ignite/odbc/query/type_info_query.cpp
index 0fb7fe3677..b428055385 100644
--- a/modules/platforms/cpp/ignite/odbc/query/type_info_query.cpp
+++ b/modules/platforms/cpp/ignite/odbc/query/type_info_query.cpp
@@ -293,7 +293,7 @@ sql_result type_info_query::get_column(std::uint16_t 
column_idx, application_dat
 
         case result_column::MINIMUM_SCALE:
         case result_column::MAXIMUM_SCALE: {
-            
buffer.put_int16(std::int16_t(ignite_type_decimal_digits(current_type)));
+            
buffer.put_int16(std::int16_t(ignite_type_decimal_digits(current_type, -1)));
 
             break;
         }
diff --git a/modules/platforms/cpp/ignite/odbc/sql_statement.cpp 
b/modules/platforms/cpp/ignite/odbc/sql_statement.cpp
index e29512ae98..4a3284de49 100644
--- a/modules/platforms/cpp/ignite/odbc/sql_statement.cpp
+++ b/modules/platforms/cpp/ignite/odbc/sql_statement.cpp
@@ -19,6 +19,7 @@
 
 #include "ignite/odbc/log.h"
 #include "ignite/odbc/odbc_error.h"
+#include "ignite/odbc/query/column_metadata_query.h"
 #include "ignite/odbc/query/data_query.h"
 #include "ignite/odbc/query/table_metadata_query.h"
 #include "ignite/odbc/query/type_info_query.h"
@@ -554,16 +555,12 @@ void sql_statement::execute_get_columns_meta_query(
 
 sql_result sql_statement::internal_execute_get_columns_meta_query(
     const std::string &schema, const std::string &table, const std::string 
&column) {
-    UNUSED_VALUE schema;
-    UNUSED_VALUE table;
-    UNUSED_VALUE column;
 
     if (m_current_query)
         m_current_query->close();
 
-    // TODO: IGNITE-20346 Implement table column metadata fetching
-    add_status_record(sql_state::SHYC00_OPTIONAL_FEATURE_NOT_IMPLEMENTED, 
"Column metadata is not supported.");
-    return sql_result::AI_ERROR;
+    m_current_query = std::make_unique<column_metadata_query>(*this, 
m_connection, schema, table, column);
+    return m_current_query->execute();
 }
 
 void sql_statement::execute_get_tables_meta_query(
@@ -1013,7 +1010,7 @@ sql_result sql_statement::internal_describe_param(
 
     // TODO: IGNITE-19854 Implement meta fetching for a parameter
     if (decimal_digits)
-        *decimal_digits = int16_t(ignite_type_decimal_digits(type));
+        *decimal_digits = int16_t(ignite_type_decimal_digits(type, -1));
 
     // TODO: IGNITE-19854 Implement meta fetching for a parameter
     if (nullable)
diff --git a/modules/platforms/cpp/ignite/odbc/type_traits.cpp 
b/modules/platforms/cpp/ignite/odbc/type_traits.cpp
index bdafc1c767..7e41d42387 100644
--- a/modules/platforms/cpp/ignite/odbc/type_traits.cpp
+++ b/modules/platforms/cpp/ignite/odbc/type_traits.cpp
@@ -596,16 +596,47 @@ std::int32_t ignite_type_num_precision_radix(ignite_type 
typ) {
     return sql_type_num_precision_radix(sql_type);
 }
 
-std::int32_t sql_type_decimal_digits(std::int16_t) {
-    // TODO: IGNITE-19854 Remove once parameters meta fetching is implemented
+std::int32_t sql_type_decimal_digits(std::int16_t type, std::int32_t scale) {
+    switch (type) {
+        case SQL_BIT:
+        case SQL_TINYINT:
+        case SQL_SMALLINT:
+        case SQL_INTEGER:
+        case SQL_BIGINT:
+        case SQL_REAL:
+        case SQL_FLOAT:
+        case SQL_DOUBLE:
+            return sql_type_column_size(type);
+
+        case SQL_TYPE_TIME:
+        case SQL_TYPE_TIMESTAMP:
+            return 9;
+
+        case SQL_DECIMAL:
+        case SQL_NUMERIC:
+            if (scale >= 0)
+                return scale;
+            break;
+
+        case SQL_GUID:
+        case SQL_TYPE_DATE:
+        case SQL_VARCHAR:
+        case SQL_CHAR:
+        case SQL_WCHAR:
+        case SQL_LONGVARBINARY:
+        case SQL_BINARY:
+        case SQL_VARBINARY:
+        case SQL_LONGVARCHAR:
+        default:
+            break;
+    }
     return -1;
 }
 
-std::int32_t ignite_type_decimal_digits(ignite_type typ) {
-    // TODO: IGNITE-19854 Remove once parameters meta fetching is implemented
+std::int32_t ignite_type_decimal_digits(ignite_type typ, std::int32_t scale) {
     std::int16_t sql_type = ignite_type_to_sql_type(typ);
 
-    return sql_type_decimal_digits(sql_type);
+    return sql_type_decimal_digits(sql_type, scale);
 }
 
 bool is_sql_type_unsigned(std::int16_t type) {
diff --git a/modules/platforms/cpp/ignite/odbc/type_traits.h 
b/modules/platforms/cpp/ignite/odbc/type_traits.h
index aa9b65cc7b..04e83e8564 100644
--- a/modules/platforms/cpp/ignite/odbc/type_traits.h
+++ b/modules/platforms/cpp/ignite/odbc/type_traits.h
@@ -123,7 +123,7 @@ const std::string &ignite_type_to_sql_type_name(ignite_type 
typ);
  * @param type Application type.
  * @return True if the type is supported.
  */
-bool is_sql_type_supported(int16_t type);
+bool is_sql_type_supported(std::int16_t type);
 
 /**
  * Get corresponding binary type for ODBC SQL type.
@@ -131,7 +131,7 @@ bool is_sql_type_supported(int16_t type);
  * @param sql_type SQL type.
  * @return Binary type.
  */
-ignite_type sql_type_to_ignite_type(int16_t sql_type);
+ignite_type sql_type_to_ignite_type(std::int16_t sql_type);
 
 /**
  * Convert ODBC type to driver type alias.
@@ -139,7 +139,7 @@ ignite_type sql_type_to_ignite_type(int16_t sql_type);
  * @param type ODBC type;
  * @return Internal driver type.
  */
-odbc_native_type to_driver_type(int16_t type);
+odbc_native_type to_driver_type(std::int16_t type);
 
 /**
  * Convert Ignite data type to SQL data type.
@@ -147,7 +147,7 @@ odbc_native_type to_driver_type(int16_t type);
  * @param typ Data type.
  * @return SQL data type.
  */
-int16_t ignite_type_to_sql_type(ignite_type typ);
+std::int16_t ignite_type_to_sql_type(ignite_type typ);
 
 /**
  * Get Ignite type SQL nullability.
@@ -158,7 +158,7 @@ int16_t ignite_type_to_sql_type(ignite_type typ);
  *         SQL_NULLABLE_UNKNOWN if it is not known whether the
  *         column accepts NULL values.
  */
-int16_t ignite_type_nullability(ignite_type typ);
+std::int16_t ignite_type_nullability(ignite_type typ);
 
 /**
  * Get SQL type display size.
@@ -166,7 +166,7 @@ int16_t ignite_type_nullability(ignite_type typ);
  * @param type SQL type.
  * @return Display size.
  */
-int32_t sql_type_display_size(int16_t type);
+std::int32_t sql_type_display_size(std::int16_t type);
 
 /**
  * Get Ignite type display size.
@@ -174,7 +174,7 @@ int32_t sql_type_display_size(int16_t type);
  * @param typ Ignite type.
  * @return Display size.
  */
-int32_t ignite_type_display_size(ignite_type typ);
+std::int32_t ignite_type_display_size(ignite_type typ);
 
 /**
  * Get SQL type column size.
@@ -182,7 +182,7 @@ int32_t ignite_type_display_size(ignite_type typ);
  * @param type SQL type.
  * @return Column size.
  */
-int32_t sql_type_column_size(int16_t type);
+std::int32_t sql_type_column_size(std::int16_t type);
 
 /**
  * Get Ignite type column size.
@@ -190,7 +190,7 @@ int32_t sql_type_column_size(int16_t type);
  * @param typ Ignite type.
  * @return Column size.
  */
-int32_t ignite_type_max_column_size(ignite_type typ);
+std::int32_t ignite_type_max_column_size(ignite_type typ);
 
 /**
  * Get SQL type transfer octet length.
@@ -198,7 +198,7 @@ int32_t ignite_type_max_column_size(ignite_type typ);
  * @param type SQL type.
  * @return Transfer octet length.
  */
-int32_t sql_type_transfer_length(int16_t type);
+std::int32_t sql_type_transfer_length(std::int16_t type);
 
 /**
  * Get Ignite type transfer octet length.
@@ -206,7 +206,7 @@ int32_t sql_type_transfer_length(int16_t type);
  * @param typ Ignite type.
  * @return Transfer octet length.
  */
-int32_t ignite_type_transfer_length(ignite_type typ);
+std::int32_t ignite_type_transfer_length(ignite_type typ);
 
 /**
  * Get SQL type numeric precision radix.
@@ -214,7 +214,7 @@ int32_t ignite_type_transfer_length(ignite_type typ);
  * @param type SQL type.
  * @return Numeric precision radix.
  */
-int32_t sql_type_num_precision_radix(int8_t type);
+std::int32_t sql_type_num_precision_radix(int8_t type);
 
 /**
  * Get Ignite type numeric precision radix.
@@ -222,23 +222,25 @@ int32_t sql_type_num_precision_radix(int8_t type);
  * @param typ Ignite type.
  * @return Numeric precision radix.
  */
-int32_t ignite_type_num_precision_radix(ignite_type typ);
+std::int32_t ignite_type_num_precision_radix(ignite_type typ);
 
 /**
  * Get SQL type decimal digits.
  *
  * @param type SQL type.
+ * @param scale Scale if applies. Negative value means scale is not available.
  * @return big_decimal digits.
  */
-int32_t sql_type_decimal_digits(int16_t type);
+std::int32_t sql_type_decimal_digits(std::int16_t type, std::int32_t scale);
 
 /**
  * Get Ignite type decimal digits.
  *
- * @param typ Ignite type.
+* @param typ Ignite type.
+* @param scale Scale if applies. Negative value means scale is not available.
  * @return big_decimal digits.
  */
-int32_t ignite_type_decimal_digits(ignite_type typ);
+std::int32_t ignite_type_decimal_digits(ignite_type typ, std::int32_t scale);
 
 /**
  * Checks if the SQL type is unsigned.
@@ -246,7 +248,7 @@ int32_t ignite_type_decimal_digits(ignite_type typ);
  * @param type SQL type.
  * @return True if unsigned or non-numeric.
  */
-bool is_sql_type_unsigned(int16_t type);
+bool is_sql_type_unsigned(std::int16_t type);
 
 /**
  * Checks if the Ignite type is unsigned.
diff --git a/modules/platforms/cpp/ignite/protocol/client_operation.h 
b/modules/platforms/cpp/ignite/protocol/client_operation.h
index 99a1b70be7..1652977482 100644
--- a/modules/platforms/cpp/ignite/protocol/client_operation.h
+++ b/modules/platforms/cpp/ignite/protocol/client_operation.h
@@ -83,6 +83,9 @@ enum class client_operation {
     /** Get table metadata. */
     JDBC_TABLE_META = 38,
 
+    /** Get column metadata. */
+    JDBC_COLUMN_META = 39,
+
     /** Begin transaction. */
     TX_BEGIN = 43,
 
diff --git a/modules/platforms/cpp/tests/odbc-test/api_robustness_test.cpp 
b/modules/platforms/cpp/tests/odbc-test/api_robustness_test.cpp
index 7fd7d73cd9..29114df565 100644
--- a/modules/platforms/cpp/tests/odbc-test/api_robustness_test.cpp
+++ b/modules/platforms/cpp/tests/odbc-test/api_robustness_test.cpp
@@ -245,9 +245,7 @@ TEST_F(api_robustness_test, sql_columns) {
     SQLRETURN ret = SQLColumns(m_statement, catalogName, sizeof(catalogName), 
schemaName, sizeof(schemaName), tableName,
         sizeof(tableName), columnName, sizeof(columnName));
 
-    UNUSED_VALUE ret;
-    // TODO IGNITE-20346: Uncomment once column metadata is implemented.
-    // ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement);
+    ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement);
 
     // Sizes are nulls.
     SQLColumns(m_conn, catalogName, 0, schemaName, 0, tableName, 0, 
columnName, 0);
@@ -397,17 +395,11 @@ TEST_F(api_robustness_test, sql_col_attribute) {
 
     // Everything is ok. Character attribute.
     ret = SQLColAttribute(m_statement, 1, SQL_COLUMN_TABLE_NAME, buffer, 
sizeof(buffer), &resLen, &numericAttr);
-
-    UNUSED_VALUE ret;
-    // TODO IGNITE-20346: Uncomment once column metadata is implemented.
-    // ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement);
+    ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement);
 
     // Everything is ok. Numeric attribute.
     ret = SQLColAttribute(m_statement, 1, SQL_DESC_COUNT, buffer, 
sizeof(buffer), &resLen, &numericAttr);
-
-    UNUSED_VALUE ret;
-    // TODO IGNITE-20346: Uncomment once column metadata is implemented.
-    // ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement);
+    ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement);
 
     SQLColAttribute(m_statement, 1, SQL_COLUMN_TABLE_NAME, buffer, 
sizeof(buffer), &resLen, 0);
     SQLColAttribute(m_statement, 1, SQL_COLUMN_TABLE_NAME, buffer, 
sizeof(buffer), 0, &numericAttr);
@@ -444,9 +436,7 @@ TEST_F(api_robustness_test, sql_describe_col) {
     ret = SQLDescribeCol(m_statement, 1, columnName, sizeof(columnName), 
&columnNameLen, &dataType, &columnSize,
         &decimalDigits, &nullable);
 
-    UNUSED_VALUE ret;
-    // TODO IGNITE-20346: Uncomment once column metadata is implemented.
-    // ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement);
+    ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement);
 
     SQLDescribeCol(
         m_statement, 1, 0, sizeof(columnName), &columnNameLen, &dataType, 
&columnSize, &decimalDigits, &nullable);
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 ed07bd9e5a..11af9248b4 100644
--- a/modules/platforms/cpp/tests/odbc-test/meta_queries_test.cpp
+++ b/modules/platforms/cpp/tests/odbc-test/meta_queries_test.cpp
@@ -546,23 +546,20 @@ TEST_F(meta_queries_test, get_data_with_tables) {
     check_single_row_result_set_with_get_data(m_statement);
 }
 
-// TODO: IGNITE-20346 Implement column metadata fetching
-#ifdef MUTED
 TEST_F(meta_queries_test, get_data_with_columns) {
     odbc_connect(get_basic_connection_string());
 
-    SQLCHAR empty[] = "";
-    SQLCHAR table[] = "TestType";
-    SQLCHAR column[] = "str";
+    SQLCHAR any[] = "%";
+    SQLCHAR table[] = "META_QUERIES_TEST";
+    SQLCHAR column[] = "STR";
 
-    SQLRETURN ret = SQLColumns(m_statement, empty, SQL_NTS, empty, SQL_NTS, 
table, SQL_NTS, column, SQL_NTS);
+    SQLRETURN ret = SQLColumns(m_statement, any, SQL_NTS, any, SQL_NTS, table, 
SQL_NTS, column, SQL_NTS);
 
     if (!SQL_SUCCEEDED(ret))
         FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement));
 
     check_single_row_result_set_with_get_data(m_statement);
 }
-#endif // MUTED
 
 TEST_F(meta_queries_test, get_data_with_select_query) {
     odbc_connect(get_basic_connection_string());
@@ -723,19 +720,17 @@ TEST_F(meta_queries_test, tables_meta) {
     EXPECT_TRUE(ret == SQL_NO_DATA);
 }
 
-// TODO: IGNITE-20346 Implement table column metadata fetching
-#ifdef MUTED
 TEST_F(meta_queries_test, ddl_columns_meta) {
     odbc_connect(get_basic_connection_string());
 
-    SQLCHAR create_table[] = "create table TestTable(id int primary key, 
testColumn varchar)";
+    SQLCHAR create_table[] = "create table if not exists DDL_COLUMNS_META(ID 
int primary key, TEST_COLUMN varchar)";
     SQLRETURN ret = SQLExecDirect(m_statement, create_table, SQL_NTS);
 
     if (!SQL_SUCCEEDED(ret))
         FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement));
 
     SQLCHAR any[] = "%";
-    SQLCHAR table[] = "TestTable";
+    SQLCHAR table[] = "DDL_COLUMNS_META";
 
     ret = SQLColumns(m_statement, any, SQL_NTS, any, SQL_NTS, table, SQL_NTS, 
any, SQL_NTS);
 
@@ -748,8 +743,8 @@ TEST_F(meta_queries_test, ddl_columns_meta) {
         FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement));
 
     check_string_column(m_statement, 1, "");
-    check_string_column(m_statement, 2, "\"PUBLIC\"");
-    check_string_column(m_statement, 3, "TESTTABLE");
+    check_string_column(m_statement, 2, "PUBLIC");
+    check_string_column(m_statement, 3, "DDL_COLUMNS_META");
     check_string_column(m_statement, 4, "ID");
 
     ret = SQLFetch(m_statement);
@@ -758,9 +753,9 @@ TEST_F(meta_queries_test, ddl_columns_meta) {
         FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement));
 
     check_string_column(m_statement, 1, "");
-    check_string_column(m_statement, 2, "\"PUBLIC\"");
-    check_string_column(m_statement, 3, "TESTTABLE");
-    check_string_column(m_statement, 4, "TESTCOLUMN");
+    check_string_column(m_statement, 2, "PUBLIC");
+    check_string_column(m_statement, 3, "DDL_COLUMNS_META");
+    check_string_column(m_statement, 4, "TEST_COLUMN");
 
     ret = SQLFetch(m_statement);
 
@@ -770,7 +765,7 @@ TEST_F(meta_queries_test, ddl_columns_meta) {
 TEST_F(meta_queries_test, ddl_columns_meta_escaped) {
     odbc_connect(get_basic_connection_string());
 
-    SQLCHAR create_table[] = "create table ESG_FOCUS(id int primary key, 
TEST_COLUMN varchar)";
+    SQLCHAR create_table[] = "create table if not exists ESG_FOCUS(id int 
primary key, TEST_COLUMN varchar)";
     SQLRETURN ret = SQLExecDirect(m_statement, create_table, SQL_NTS);
 
     if (!SQL_SUCCEEDED(ret))
@@ -790,7 +785,7 @@ TEST_F(meta_queries_test, ddl_columns_meta_escaped) {
         FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement));
 
     check_string_column(m_statement, 1, "");
-    check_string_column(m_statement, 2, "\"PUBLIC\"");
+    check_string_column(m_statement, 2, "PUBLIC");
     check_string_column(m_statement, 3, "ESG_FOCUS");
     check_string_column(m_statement, 4, "ID");
 
@@ -800,7 +795,7 @@ TEST_F(meta_queries_test, ddl_columns_meta_escaped) {
         FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement));
 
     check_string_column(m_statement, 1, "");
-    check_string_column(m_statement, 2, "\"PUBLIC\"");
+    check_string_column(m_statement, 2, "PUBLIC");
     check_string_column(m_statement, 3, "ESG_FOCUS");
     check_string_column(m_statement, 4, "TEST_COLUMN");
 
@@ -808,7 +803,6 @@ TEST_F(meta_queries_test, ddl_columns_meta_escaped) {
 
     ASSERT_EQ(ret, SQL_NO_DATA);
 }
-#endif // MUTED
 
 // TODO: IGNITE-19854 Implement metadata fetching for the non-executed query.
 #ifdef MUTED


Reply via email to