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 b1fdda2508 IGNITE-21527 C++: Clean up IEP-54 leftovers (#3486)
b1fdda2508 is described below

commit b1fdda2508cdb47e429918432631d2ffc5b5e554
Author: Igor Sapego <[email protected]>
AuthorDate: Wed Mar 27 14:04:52 2024 +0400

    IGNITE-21527 C++: Clean up IEP-54 leftovers (#3486)
---
 .../org/apache/ignite/client/ClientTablesTest.java |  36 ------
 modules/platforms/cpp/ignite/client/CMakeLists.txt |  29 ++++-
 .../ignite/client/detail/compute/compute_impl.h    |   7 +-
 .../cpp/ignite/client/detail/table/schema.h        | 102 ++++++++++++----
 .../cpp/ignite/client/detail/table/table_impl.cpp  | 106 ----------------
 .../platforms/cpp/ignite/client/detail/utils.cpp   |  70 +++++++++--
 modules/platforms/cpp/ignite/client/detail/utils.h |  39 ++++++
 .../cpp/ignite/client/detail/utils_test.cpp        |  92 ++++++++++++++
 .../platforms/cpp/tests/client-test/CMakeLists.txt |   1 +
 .../cpp/tests/client-test/column_order_test.cpp    | 136 +++++++++++++++++++++
 .../cpp/tests/client-test/compute_test.cpp         |   8 +-
 .../platforms/cpp/tests/client-test/sql_test.cpp   |   2 +-
 12 files changed, 444 insertions(+), 184 deletions(-)

diff --git 
a/modules/client/src/test/java/org/apache/ignite/client/ClientTablesTest.java 
b/modules/client/src/test/java/org/apache/ignite/client/ClientTablesTest.java
index 75b82f9fb0..ee03b57143 100644
--- 
a/modules/client/src/test/java/org/apache/ignite/client/ClientTablesTest.java
+++ 
b/modules/client/src/test/java/org/apache/ignite/client/ClientTablesTest.java
@@ -19,17 +19,10 @@ package org.apache.ignite.client;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.util.Comparator;
-import java.util.concurrent.CompletionException;
 import org.apache.ignite.client.fakes.FakeIgniteTables;
-import org.apache.ignite.internal.client.table.ClientTable;
-import org.apache.ignite.internal.table.TableViewInternal;
-import org.apache.ignite.lang.TableAlreadyExistsException;
 import org.apache.ignite.table.Table;
-import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
 /**
@@ -70,33 +63,4 @@ public class ClientTablesTest extends AbstractClientTest {
 
         assertNull(table);
     }
-
-    @Test
-    @Disabled("IGNITE-15179")
-    public void testCreateTable() {
-        var clientTable = ((FakeIgniteTables) 
server.tables()).createTable("t1");
-        assertEquals("t1", clientTable.name());
-
-        var serverTables = server.tables().tables();
-        assertEquals(1, serverTables.size());
-
-        var serverTable = serverTables.get(0);
-        assertEquals("t1", serverTable.name());
-        assertEquals(((TableViewInternal) serverTable).tableId(), 
((ClientTable) clientTable).tableId());
-    }
-
-    @Test
-    @Disabled("IGNITE-15179")
-    public void testCreateTableWhenExists() {
-        ((FakeIgniteTables) server.tables()).createTable(DEFAULT_TABLE);
-
-        var ex = assertThrows(CompletionException.class,
-                () -> ((FakeIgniteTables) 
server.tables()).createTable(DEFAULT_TABLE));
-
-        assertTrue(ex.getMessage().endsWith(FakeIgniteTables.TABLE_EXISTS));
-        assertEquals(TableAlreadyExistsException.class, 
ex.getCause().getClass());
-
-        var serverTables = server.tables().tables();
-        assertEquals(1, serverTables.size());
-    }
 }
diff --git a/modules/platforms/cpp/ignite/client/CMakeLists.txt 
b/modules/platforms/cpp/ignite/client/CMakeLists.txt
index 4b82c9f34b..7da3427464 100644
--- a/modules/platforms/cpp/ignite/client/CMakeLists.txt
+++ b/modules/platforms/cpp/ignite/client/CMakeLists.txt
@@ -65,16 +65,31 @@ set(PUBLIC_HEADERS
     transaction/transactions.h
 )
 
+add_library(${TARGET}-obj OBJECT ${SOURCES})
+target_include_directories(${TARGET}-obj PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+
 add_library(${TARGET} SHARED ${SOURCES})
 
-set_target_properties(${TARGET} PROPERTIES VERSION ${CMAKE_PROJECT_VERSION})
-set_target_properties(${TARGET} PROPERTIES POSITION_INDEPENDENT_CODE 1)
+set(LIBRARIES
+    ignite-common
+    ignite-tuple
+    ignite-network
+    ignite-protocol
+)
 
-if (WIN32)
-    set_target_properties(${TARGET} PROPERTIES OUTPUT_NAME "ignite-client")
-endif()
+set(_target_libs ${TARGET} ${TARGET}-obj)
 
-target_link_libraries(${TARGET} ignite-common ignite-tuple ignite-network 
ignite-protocol)
+foreach(_target_lib IN LISTS _target_libs)
+    set_target_properties(${_target_lib} PROPERTIES VERSION 
${CMAKE_PROJECT_VERSION})
+    set_target_properties(${_target_lib} PROPERTIES POSITION_INDEPENDENT_CODE 
1)
+
+    if (WIN32)
+        set_target_properties(${_target_lib} PROPERTIES OUTPUT_NAME 
"ignite-client")
+    endif()
+
+    target_link_libraries(${_target_lib} ${LIBRARIES})
+endforeach()
+unset(_target_libs)
 
 if (${INSTALL_IGNITE_FILES})
     install(TARGETS ${TARGET}
@@ -85,3 +100,5 @@ if (${INSTALL_IGNITE_FILES})
 
     ignite_install_headers(FILES ${PUBLIC_HEADERS} DESTINATION 
${IGNITE_INCLUDEDIR}/client)
 endif()
+
+ignite_test(utils_test detail/utils_test.cpp LIBS ${TARGET}-obj ${LIBRARIES})
diff --git a/modules/platforms/cpp/ignite/client/detail/compute/compute_impl.h 
b/modules/platforms/cpp/ignite/client/detail/compute/compute_impl.h
index cb259eba66..8fb3e9d468 100644
--- a/modules/platforms/cpp/ignite/client/detail/compute/compute_impl.h
+++ b/modules/platforms/cpp/ignite/client/detail/compute/compute_impl.h
@@ -47,8 +47,8 @@ public:
         , m_tables(std::move(tables)) {}
 
     /**
-     * Submits a compute job represented by the given class for an execution 
on one of the specified nodes asynchronously. If the node
-     * leaves the cluster, it will be restarted on one of the candidate nodes.
+     * Submits a compute job represented by the given class for an execution 
on one of the specified nodes
+     * asynchronously. If the node leaves the cluster, it will be restarted on 
one of the candidate nodes.
      *
      * @param nodes Candidate node to use for the job execution.
      * @param units Deployment units. Can be empty.
@@ -61,7 +61,8 @@ public:
         ignite_callback<std::optional<primitive>> callback);
 
     /**
-     * Submits a compute job represented by the given class for an execution 
on one of the nodes where the given key is located.
+     * Submits a compute job represented by the given class for an execution 
on one of the nodes where the given key is
+     * located.
      *
      * @param table_name Name of the table to be used with @c key to determine 
target node.
      * @param key Table key to be used to determine the target node for job 
execution.
diff --git a/modules/platforms/cpp/ignite/client/detail/table/schema.h 
b/modules/platforms/cpp/ignite/client/detail/table/schema.h
index 9d35828ce7..f15bb0cedb 100644
--- a/modules/platforms/cpp/ignite/client/detail/table/schema.h
+++ b/modules/platforms/cpp/ignite/client/detail/table/schema.h
@@ -23,6 +23,7 @@
 
 #include <msgpack.h>
 
+#include <algorithm>
 #include <array>
 #include <memory>
 #include <string>
@@ -34,11 +35,19 @@ namespace ignite::detail {
  */
 struct column {
     std::string name;
-    ignite_type type{};
+    ignite_type type{ignite_type::UNDEFINED};
     bool nullable{false};
-    bool is_key{false};
-    std::int32_t schema_index{0};
+    std::int32_t colocation_index{-1};
+    std::int32_t key_index{-1};
     std::int32_t scale{0};
+    std::int32_t precision{0};
+
+    /**
+     * Check if the column is the part of the key.
+     *
+     * @return @c true, if the column is the part of the key.
+     */
+    [[nodiscard]] bool is_key() const { return key_index >= 0; }
 
     /**
      * Unpack column from MsgPack object.
@@ -47,18 +56,20 @@ struct column {
      * @return Column value.
      */
     [[nodiscard]] static column read(protocol::reader &reader) {
+        auto constexpr minimum_expected_columns = 7;
+
         auto fields_num = reader.read_int32();
-        assert(fields_num >= 7); // Expect at least six columns.
+        assert(fields_num >= minimum_expected_columns);
 
         column res{};
         res.name = reader.read_string();
         res.type = static_cast<ignite_type>(reader.read_int32());
-        res.is_key = reader.read_int32() >= 0;
+        res.key_index = reader.read_int32();
         res.nullable = reader.read_bool();
-        reader.skip(); // Colocation index.
+        res.colocation_index = reader.read_int32();
         res.scale = reader.read_int32();
-        reader.skip(); // Precision
-        reader.skip(fields_num - 7);
+        res.precision = reader.read_int32();
+        reader.skip(fields_num - minimum_expected_columns);
 
         return res;
     }
@@ -68,9 +79,10 @@ struct column {
  * Schema.
  */
 struct schema {
-    std::int32_t version{-1};
-    std::int32_t key_column_count{0};
-    std::vector<column> columns;
+    const std::int32_t version{-1};
+    const std::vector<column> columns;
+    const std::vector<const column *> key_columns;
+    const std::vector<const column *> val_columns;
 
     // Default
     schema() = default;
@@ -79,13 +91,62 @@ struct schema {
      * Constructor.
      *
      * @param version Version.
-     * @param key_column_count Key column count.
      * @param columns Columns.
+     * @param key_columns Key Columns.
+     * @param val_columns Value Columns.
      */
-    schema(std::int32_t version, std::int32_t key_column_count, 
std::vector<column> &&columns)
+    schema(std::int32_t version, std::vector<column> &&columns, 
std::vector<const column *> &&key_columns,
+        std::vector<const column *> &&val_columns)
         : version(version)
-        , key_column_count(key_column_count)
-        , columns(std::move(columns)) {}
+        , columns(std::move(columns))
+        , key_columns(std::move(key_columns))
+        , val_columns(std::move(val_columns)) {}
+
+    /**
+     * Get column by index.
+     *
+     * @param key_only Key only flag.
+     * @param index Column index.
+     * @return Column reference.
+     */
+    [[nodiscard]] const column &get_column(bool key_only, std::int32_t index) 
const {
+        assert(index >= 0);
+        return key_only ? *key_columns[index] : columns[index];
+    }
+
+    /**
+     * Create schema instance.
+     *
+     * @param version Version.
+     * @param cols Columns.
+     * @return A new schema instance.
+     */
+    static std::shared_ptr<schema> create_instance(std::int32_t version, 
std::vector<column> &&cols) {
+        std::int32_t key_columns_cnt = 0;
+        for (const auto &column : cols) {
+            if (column.is_key())
+                ++key_columns_cnt;
+        }
+        std::int32_t val_columns_cnt = std::int32_t(cols.size()) - 
key_columns_cnt;
+
+        std::vector<const column *> key_columns(key_columns_cnt, nullptr);
+
+        std::vector<const column *> val_columns;
+        val_columns.reserve(val_columns_cnt);
+
+        for (const auto &column : cols) {
+            if (column.is_key()) {
+                assert(column.key_index >= 0 && std::size_t(column.key_index) 
< key_columns.size());
+                assert(key_columns[column.key_index] == nullptr);
+
+                key_columns[column.key_index] = &column;
+            } else {
+                val_columns.push_back(&column);
+            }
+        }
+
+        return std::make_shared<schema>(version, std::move(cols), 
std::move(key_columns), std::move(val_columns));
+    }
 
     /**
      * Read schema using reader.
@@ -94,22 +155,19 @@ struct schema {
      * @return Schema instance.
      */
     static std::shared_ptr<schema> read(protocol::reader &reader) {
-        std::int32_t key_column_count = 0;
         auto schema_version = reader.read_int32();
 
         auto columns_count = reader.read_int32();
-        std::vector<column> columns;
-        columns.reserve(columns_count);
+        std::vector<column> cols;
+        cols.reserve(columns_count);
 
         for (std::int32_t column_idx = 0; column_idx < columns_count; 
++column_idx) {
             auto val = column::read(reader);
-            if (val.is_key)
-                ++key_column_count;
 
-            columns.emplace_back(std::move(val));
+            cols.emplace_back(std::move(val));
         }
 
-        return std::make_shared<schema>(schema_version, key_column_count, 
std::move(columns));
+        return create_instance(schema_version, std::move(cols));
     }
 };
 
diff --git a/modules/platforms/cpp/ignite/client/detail/table/table_impl.cpp 
b/modules/platforms/cpp/ignite/client/detail/table/table_impl.cpp
index 41826d018a..f6cd913803 100644
--- a/modules/platforms/cpp/ignite/client/detail/table/table_impl.cpp
+++ b/modules/platforms/cpp/ignite/client/detail/table/table_impl.cpp
@@ -48,112 +48,6 @@ void write_table_operation_header(protocol::writer &writer, 
std::int32_t id, tra
     writer.write(sch.version);
 }
 
-/**
- * Read tuple.
- *
- * @param reader Reader.
- * @param sch Schema.
- * @return Tuple.
- */
-ignite_tuple read_tuple(protocol::reader &reader, const schema *sch) {
-    auto tuple_data = reader.read_binary();
-
-    auto columns_cnt = std::int32_t(sch->columns.size());
-    ignite_tuple res(columns_cnt);
-    binary_tuple_parser parser(columns_cnt, tuple_data);
-
-    for (std::int32_t i = 0; i < columns_cnt; ++i) {
-        auto &column = sch->columns[i];
-        res.set(column.name, protocol::read_next_column(parser, column.type, 
column.scale));
-    }
-    return res;
-}
-
-/**
- * Read tuple.
- *
- * @param reader Reader.
- * @param sch Schema.
- * @param key_only Should only key fields be read or not.
- * @return Tuple.
- */
-ignite_tuple read_tuple(protocol::reader &reader, const schema *sch, bool 
key_only) {
-    auto tuple_data = reader.read_binary();
-
-    auto columns_cnt = std::int32_t(key_only ? sch->key_column_count : 
sch->columns.size());
-    ignite_tuple res(columns_cnt);
-    binary_tuple_parser parser(columns_cnt, tuple_data);
-
-    for (std::int32_t i = 0; i < columns_cnt; ++i) {
-        auto &column = sch->columns[i];
-        res.set(column.name, protocol::read_next_column(parser, column.type, 
column.scale));
-    }
-    return res;
-}
-
-/**
- * Read tuple.
- *
- * @param reader Reader.
- * @param sch Schema.
- * @return Tuple.
- */
-std::optional<ignite_tuple> read_tuple_opt(protocol::reader &reader, const 
schema *sch) {
-    if (reader.try_read_nil())
-        return std::nullopt;
-
-    return read_tuple(reader, sch);
-}
-
-/**
- * Read tuples.
- *
- * @param reader Reader.
- * @param sch Schema.
- * @param key_only Should only key fields be read or not.
- * @return Tuples.
- */
-std::vector<std::optional<ignite_tuple>> read_tuples_opt(protocol::reader 
&reader, const schema *sch, bool key_only) {
-    if (reader.try_read_nil())
-        return {};
-
-    auto count = reader.read_int32();
-    std::vector<std::optional<ignite_tuple>> res;
-    res.reserve(std::size_t(count));
-
-    for (std::int32_t i = 0; i < count; ++i) {
-        auto exists = reader.read_bool();
-        if (!exists)
-            res.emplace_back(std::nullopt);
-        else
-            res.emplace_back(read_tuple(reader, sch, key_only));
-    }
-
-    return res;
-}
-
-/**
- * Read tuples.
- *
- * @param reader Reader.
- * @param sch Schema.
- * @param key_only Should only key fields be read or not.
- * @return Tuples.
- */
-std::vector<ignite_tuple> read_tuples(protocol::reader &reader, const schema 
*sch, bool key_only) {
-    if (reader.try_read_nil())
-        return {};
-
-    auto count = reader.read_int32();
-    std::vector<ignite_tuple> res;
-    res.reserve(std::size_t(count));
-
-    for (std::int32_t i = 0; i < count; ++i)
-        res.emplace_back(read_tuple(reader, sch, key_only));
-
-    return res;
-}
-
 void 
table_impl::load_latest_schema_async(ignite_callback<std::shared_ptr<schema>> 
callback) {
     auto latest_schema_version = m_latest_schema_version;
 
diff --git a/modules/platforms/cpp/ignite/client/detail/utils.cpp 
b/modules/platforms/cpp/ignite/client/detail/utils.cpp
index c4cd833f87..5881094686 100644
--- a/modules/platforms/cpp/ignite/client/detail/utils.cpp
+++ b/modules/platforms/cpp/ignite/client/detail/utils.cpp
@@ -188,14 +188,16 @@ void append_column(binary_tuple_builder &builder, 
ignite_type typ, const primiti
  */
 std::vector<std::byte> pack_tuple(
     const schema &sch, const ignite_tuple &tuple, bool key_only, 
protocol::bitset_span &no_value) {
-    auto count = std::int32_t(key_only ? sch.key_column_count : 
sch.columns.size());
+    auto count = std::int32_t(key_only ? sch.key_columns.size() : 
sch.columns.size());
     binary_tuple_builder builder{count};
 
     builder.start();
 
+    auto col_indices = reinterpret_cast<std::int32_t *>(alloca(count * 
sizeof(std::int32_t)));
     for (std::int32_t i = 0; i < count; ++i) {
-        const auto &col = sch.columns[i];
+        const auto &col = sch.get_column(key_only, i);
         auto col_idx = tuple.column_ordinal(col.name);
+        col_indices[i] = col_idx;
 
         if (col_idx >= 0)
             claim_column(builder, col.type, tuple.get(col_idx), col.scale);
@@ -206,8 +208,8 @@ std::vector<std::byte> pack_tuple(
     std::int32_t written = 0;
     builder.layout();
     for (std::int32_t i = 0; i < count; ++i) {
-        const auto &col = sch.columns[i];
-        auto col_idx = tuple.column_ordinal(col.name);
+        const auto &col = sch.get_column(key_only, i);
+        auto col_idx = col_indices[i];
 
         if (col_idx >= 0) {
             append_column(builder, col.type, tuple.get(col_idx), col.scale);
@@ -221,8 +223,7 @@ std::vector<std::byte> pack_tuple(
     if (!key_only && written < tuple.column_count()) {
         std::vector<bool> written_ind(tuple.column_count(), false);
         for (std::int32_t i = 0; i < count; ++i) {
-            const auto &col = sch.columns[i];
-            auto col_idx = tuple.column_ordinal(col.name);
+            auto col_idx = col_indices[i];
 
             if (col_idx >= 0)
                 written_ind[col_idx] = true;
@@ -232,6 +233,7 @@ std::vector<std::byte> pack_tuple(
         for (std::int32_t i = 0; i < tuple.column_count(); ++i) {
             if (written_ind[i])
                 continue;
+
             auto &name = tuple.column_name(i);
             unmapped_columns << name << ",";
         }
@@ -265,7 +267,7 @@ ignite_tuple concat(const ignite_tuple &left, const 
ignite_tuple &right) {
 }
 
 void write_tuple(protocol::writer &writer, const schema &sch, const 
ignite_tuple &tuple, bool key_only) {
-    const std::size_t count = key_only ? sch.key_column_count : 
sch.columns.size();
+    const std::size_t count = key_only ? sch.key_columns.size() : 
sch.columns.size();
     const std::size_t bytes_num = bytes_for_bits(count);
 
     auto no_value_bytes = reinterpret_cast<std::byte *>(alloca(bytes_num));
@@ -283,4 +285,58 @@ void write_tuples(protocol::writer &writer, const schema 
&sch, const std::vector
         write_tuple(writer, sch, tuple, key_only);
 }
 
+ignite_tuple read_tuple(protocol::reader &reader, const schema *sch, bool 
key_only) {
+    auto tuple_data = reader.read_binary();
+
+    auto columns_cnt = std::int32_t(key_only ? sch->key_columns.size() : 
sch->columns.size());
+    ignite_tuple res(columns_cnt);
+    binary_tuple_parser parser(columns_cnt, tuple_data);
+
+    for (std::int32_t i = 0; i < columns_cnt; ++i) {
+        auto &column = sch->get_column(key_only, i);
+        res.set(column.name, protocol::read_next_column(parser, column.type, 
column.scale));
+    }
+    return res;
+}
+
+std::optional<ignite_tuple> read_tuple_opt(protocol::reader &reader, const 
schema *sch) {
+    if (reader.try_read_nil())
+        return std::nullopt;
+
+    return read_tuple(reader, sch, false);
+}
+
+std::vector<ignite_tuple> read_tuples(protocol::reader &reader, const schema 
*sch, bool key_only) {
+    if (reader.try_read_nil())
+        return {};
+
+    auto count = reader.read_int32();
+    std::vector<ignite_tuple> res;
+    res.reserve(std::size_t(count));
+
+    for (std::int32_t i = 0; i < count; ++i)
+        res.emplace_back(read_tuple(reader, sch, key_only));
+
+    return res;
+}
+
+std::vector<std::optional<ignite_tuple>> read_tuples_opt(protocol::reader 
&reader, const schema *sch, bool key_only) {
+    if (reader.try_read_nil())
+        return {};
+
+    auto count = reader.read_int32();
+    std::vector<std::optional<ignite_tuple>> res;
+    res.reserve(std::size_t(count));
+
+    for (std::int32_t i = 0; i < count; ++i) {
+        auto exists = reader.read_bool();
+        if (!exists)
+            res.emplace_back(std::nullopt);
+        else
+            res.emplace_back(read_tuple(reader, sch, key_only));
+    }
+
+    return res;
+}
+
 } // namespace ignite::detail
diff --git a/modules/platforms/cpp/ignite/client/detail/utils.h 
b/modules/platforms/cpp/ignite/client/detail/utils.h
index 6a22aa107f..808f490f0b 100644
--- a/modules/platforms/cpp/ignite/client/detail/utils.h
+++ b/modules/platforms/cpp/ignite/client/detail/utils.h
@@ -56,4 +56,43 @@ void write_tuple(protocol::writer &writer, const schema 
&sch, const ignite_tuple
  */
 void write_tuples(protocol::writer &writer, const schema &sch, const 
std::vector<ignite_tuple> &tuples, bool key_only);
 
+/**
+ * Read tuple.
+ *
+ * @param reader Reader.
+ * @param sch Schema.
+ * @param key_only Should only key fields be read or not.
+ * @return Tuple.
+ */
+ignite_tuple read_tuple(protocol::reader &reader, const schema *sch, bool 
key_only);
+
+/**
+ * Read tuple.
+ *
+ * @param reader Reader.
+ * @param sch Schema.
+ * @return Tuple.
+ */
+std::optional<ignite_tuple> read_tuple_opt(protocol::reader &reader, const 
schema *sch);
+
+/**
+ * Read tuples.
+ *
+ * @param reader Reader.
+ * @param sch Schema.
+ * @param key_only Should only key fields be read or not.
+ * @return Tuples.
+ */
+std::vector<ignite_tuple> read_tuples(protocol::reader &reader, const schema 
*sch, bool key_only);
+
+/**
+ * Read tuples.
+ *
+ * @param reader Reader.
+ * @param sch Schema.
+ * @param key_only Should only key fields be read or not.
+ * @return Tuples.
+ */
+std::vector<std::optional<ignite_tuple>> read_tuples_opt(protocol::reader 
&reader, const schema *sch, bool key_only);
+
 } // namespace ignite::detail
diff --git a/modules/platforms/cpp/ignite/client/detail/utils_test.cpp 
b/modules/platforms/cpp/ignite/client/detail/utils_test.cpp
new file mode 100644
index 0000000000..b152ced610
--- /dev/null
+++ b/modules/platforms/cpp/ignite/client/detail/utils_test.cpp
@@ -0,0 +1,92 @@
+/*
+ * 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/client/detail/utils.h"
+
+#include <gtest/gtest.h>
+
+using namespace ignite;
+using namespace detail;
+
+inline column make_column(std::string name, ignite_type type, std::int32_t 
key_index = -1) {
+    column val;
+    val.name = std::move(name);
+    val.type = type;
+    val.key_index = key_index;
+
+    return val;
+}
+
+std::shared_ptr<schema> make_test_schema() {
+    std::vector<column> columns;
+    columns.push_back(make_column("VAL_COL1", ignite_type::INT32));
+    columns.push_back(make_column("KEY_COL1", ignite_type::STRING, 0));
+    columns.push_back(make_column("VAL_COL2", ignite_type::STRING));
+    columns.push_back(make_column("KEY_COL2", ignite_type::INT32, 1));
+
+    return schema::create_instance(0, std::move(columns));
+}
+
+ignite_tuple write_read_tuple(const ignite_tuple &tuple, const 
std::shared_ptr<schema> &sch, bool key_only) {
+    std::vector<std::byte> message;
+    protocol::buffer_adapter buffer(message);
+
+    protocol::writer writer(buffer);
+
+    write_tuple(writer, *sch, tuple, key_only);
+
+    protocol::reader reader(message);
+    reader.skip(); // Skip bitset
+
+    return read_tuple(reader, sch.get(), key_only);
+}
+
+TEST(client_utils, tuple_write_read_random_order_all_columns) {
+    auto sch = make_test_schema();
+
+    ignite_tuple tuple{{"VAL_COL1", std::int32_t(42)}, {"VAL_COL2", 
std::string("Lorem ipsum")},
+        {"KEY_COL2", std::int32_t(1337)}, {"KEY_COL1", std::string("Test 
value")}};
+
+    auto res_tuple = write_read_tuple(tuple, sch, false);
+
+    ASSERT_EQ(4, res_tuple.column_count());
+    EXPECT_EQ("VAL_COL1", res_tuple.column_name(0));
+    EXPECT_EQ("KEY_COL1", res_tuple.column_name(1));
+    EXPECT_EQ("VAL_COL2", res_tuple.column_name(2));
+    EXPECT_EQ("KEY_COL2", res_tuple.column_name(3));
+
+    EXPECT_EQ(std::int32_t(42), res_tuple.get(0));
+    EXPECT_EQ(std::string("Test value"), res_tuple.get(1));
+    EXPECT_EQ(std::string("Lorem ipsum"), res_tuple.get(2));
+    EXPECT_EQ(std::int32_t(1337), res_tuple.get(3));
+}
+
+TEST(client_utils, tuple_write_read_random_order_key_only) {
+    auto sch = make_test_schema();
+
+    ignite_tuple tuple{{"VAL_COL1", std::int32_t(42)}, {"VAL_COL2", 
std::string("Lorem ipsum")},
+        {"KEY_COL2", std::int32_t(1337)}, {"KEY_COL1", std::string("Test 
value")}};
+
+    auto res_tuple = write_read_tuple(tuple, sch, true);
+
+    ASSERT_EQ(2, res_tuple.column_count());
+    EXPECT_EQ("KEY_COL1", res_tuple.column_name(0));
+    EXPECT_EQ("KEY_COL2", res_tuple.column_name(1));
+
+    EXPECT_EQ(std::string("Test value"), res_tuple.get(0));
+    EXPECT_EQ(std::int32_t(1337), res_tuple.get(1));
+}
diff --git a/modules/platforms/cpp/tests/client-test/CMakeLists.txt 
b/modules/platforms/cpp/tests/client-test/CMakeLists.txt
index aafe31f62c..ff944b3191 100644
--- a/modules/platforms/cpp/tests/client-test/CMakeLists.txt
+++ b/modules/platforms/cpp/tests/client-test/CMakeLists.txt
@@ -21,6 +21,7 @@ set(TARGET ${PROJECT_NAME})
 
 set(SOURCES
     basic_authenticator_test.cpp
+    column_order_test.cpp
     compute_test.cpp
     gtest_logger.h
     ignite_client_test.cpp
diff --git a/modules/platforms/cpp/tests/client-test/column_order_test.cpp 
b/modules/platforms/cpp/tests/client-test/column_order_test.cpp
new file mode 100644
index 0000000000..4fdb033bc8
--- /dev/null
+++ b/modules/platforms/cpp/tests/client-test/column_order_test.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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_runner_suite.h"
+
+#include "ignite/client/ignite_client.h"
+#include "ignite/client/ignite_client_configuration.h"
+
+#include <gmock/gmock-matchers.h>
+#include <gtest/gtest.h>
+
+#include <chrono>
+
+using namespace ignite;
+
+#define TEST_TABLE_NAME "column_order_test"
+
+/**
+ * Test suite.
+ */
+class column_order_test : public ignite_runner_suite {
+protected:
+    static const constexpr std::int32_t test_records = 10;
+
+    static void SetUpTestSuite() {
+        ignite_client_configuration cfg{get_node_addrs()};
+        cfg.set_logger(get_logger());
+        auto client = ignite_client::start(cfg, std::chrono::seconds(30));
+
+        client.get_sql().execute(nullptr, {"drop table if exists " 
TEST_TABLE_NAME}, {});
+
+        client.get_sql().execute(nullptr,
+            {"create table column_order_test(val1 varchar, key1 int, val2 
bigint, key2 varchar, primary key(key2, "
+             "key1))"},
+            {});
+
+        for (std::int32_t i = 0; i < test_records; ++i) {
+            auto stri = std::to_string(i);
+            client.get_sql().execute(nullptr, {"insert into " TEST_TABLE_NAME 
" values(?, ?, ?, ?)"},
+                {"test val " + stri, std::int32_t(i), std::int64_t(i * 2), 
"test key " + stri});
+        }
+    }
+
+    static void TearDownTestSuite() {
+        ignite_client_configuration cfg{get_node_addrs()};
+        cfg.set_logger(get_logger());
+        auto client = ignite_client::start(cfg, std::chrono::seconds(30));
+
+        client.get_sql().execute(nullptr, {"drop table if exists " 
TEST_TABLE_NAME}, {});
+    }
+
+    void SetUp() override {
+        ignite_client_configuration cfg{get_node_addrs()};
+        cfg.set_logger(get_logger());
+
+        m_client = ignite_client::start(cfg, std::chrono::seconds(30));
+        auto table = m_client.get_tables().get_table(TEST_TABLE_NAME);
+        ASSERT_TRUE(table.has_value());
+
+        m_table = std::move(*table);
+    }
+
+    void TearDown() override {
+        // remove all
+    }
+
+    /** Ignite client. */
+    ignite_client m_client;
+
+    /** Test table. */
+    table m_table;
+};
+
+ignite_tuple make_test_key_tuple(std::int32_t index) {
+    return {{"key1", std::int32_t(index)}, {"key2", "test key " + 
std::to_string(index)}};
+}
+
+ignite_tuple make_test_full_tuple(std::int32_t index) {
+    auto stri = std::to_string(index);
+    return {{"key1", std::int32_t(index)}, {"key2", "test key " + stri}, 
{"val1", "test val " + stri},
+        {"val2", std::int64_t(index * 2)}};
+}
+
+void check_test_tuple(const ignite_tuple &tuple, std::int32_t index, bool 
key_only) {
+    ASSERT_EQ(key_only ? 2 : 4, tuple.column_count());
+
+    auto ref = make_test_full_tuple(index);
+    EXPECT_EQ(ref.get("key1").get<std::int32_t>(), 
tuple.get("key1").get<std::int32_t>());
+    EXPECT_EQ(ref.get("key2").get<std::string>(), 
tuple.get("key2").get<std::string>());
+
+    if (!key_only) {
+        EXPECT_EQ(ref.get("val1").get<std::string>(), 
tuple.get("val1").get<std::string>());
+        EXPECT_EQ(ref.get("val2").get<std::int64_t>(), 
tuple.get("val2").get<std::int64_t>());
+    }
+}
+
+TEST_F(column_order_test, test_key_columns_nondefault_ordering_get) {
+    auto tuple_view = m_table.get_record_binary_view();
+
+    for (std::int32_t i = 0; i < test_records; ++i) {
+        auto res = tuple_view.get(nullptr, make_test_key_tuple(i));
+
+        ASSERT_TRUE(res.has_value());
+        check_test_tuple(*res, i, false);
+    }
+}
+
+TEST_F(column_order_test, test_key_columns_nondefault_ordering_put_get_remove) 
{
+    auto tuple_view = m_table.get_record_binary_view();
+
+    tuple_view.upsert(nullptr, make_test_full_tuple(13));
+
+    auto res = tuple_view.get(nullptr, make_test_key_tuple(13));
+    ASSERT_TRUE(res.has_value());
+    check_test_tuple(*res, 13, false);
+
+    bool removed = tuple_view.remove(nullptr, make_test_key_tuple(13));
+    ASSERT_TRUE(removed);
+
+    res = tuple_view.get(nullptr, make_test_key_tuple(13));
+    ASSERT_FALSE(res.has_value());
+}
diff --git a/modules/platforms/cpp/tests/client-test/compute_test.cpp 
b/modules/platforms/cpp/tests/client-test/compute_test.cpp
index 53f257f7e3..8c982877ff 100644
--- a/modules/platforms/cpp/tests/client-test/compute_test.cpp
+++ b/modules/platforms/cpp/tests/client-test/compute_test.cpp
@@ -191,14 +191,15 @@ TEST_F(compute_test, unknown_node_execute_throws) {
             try {
                 m_client.get_compute().execute({unknown_node}, {}, ECHO_JOB, 
{"unused"});
             } catch (const ignite_error &e) {
-                EXPECT_THAT(e.what_str(), testing::HasSubstr("None of the 
specified nodes are present in the cluster: [random]"));
+                EXPECT_THAT(e.what_str(),
+                    testing::HasSubstr("None of the specified nodes are 
present in the cluster: [random]"));
                 throw;
             }
         },
         ignite_error);
 }
 
-//TODO https://issues.apache.org/jira/browse/IGNITE-21553
+// TODO https://issues.apache.org/jira/browse/IGNITE-21553
 TEST_F(compute_test, DISABLED_unknown_node_broadcast_throws) {
     auto unknown_node = cluster_node("some", "random", {"127.0.0.1", 1234});
 
@@ -207,7 +208,8 @@ TEST_F(compute_test, 
DISABLED_unknown_node_broadcast_throws) {
             try {
                 m_client.get_compute().execute_broadcast({unknown_node}, {}, 
ECHO_JOB, {"unused"});
             } catch (const ignite_error &e) {
-                EXPECT_THAT(e.what_str(), testing::HasSubstr("None of the 
specified nodes are present in the cluster: [random]"));
+                EXPECT_THAT(e.what_str(),
+                    testing::HasSubstr("None of the specified nodes are 
present in the cluster: [random]"));
                 throw;
             }
         },
diff --git a/modules/platforms/cpp/tests/client-test/sql_test.cpp 
b/modules/platforms/cpp/tests/client-test/sql_test.cpp
index c4f2dd2ffe..23bcd762b3 100644
--- a/modules/platforms/cpp/tests/client-test/sql_test.cpp
+++ b/modules/platforms/cpp/tests/client-test/sql_test.cpp
@@ -83,7 +83,7 @@ protected:
     ignite_client m_client;
 };
 
-void check_columns(
+static void check_columns(
     const result_set_metadata &meta, 
std::initializer_list<std::tuple<std::string, ignite_type>> columns) {
 
     ASSERT_EQ(columns.size(), meta.columns().size());

Reply via email to