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 70c6382d92 IGNITE-18256 Add RecordView for C++ client (#1891)
70c6382d92 is described below

commit 70c6382d92b870beb4111bb751aa123556651d4e
Author: Igor Sapego <[email protected]>
AuthorDate: Tue Apr 4 13:48:00 2023 +0400

    IGNITE-18256 Add RecordView for C++ client (#1891)
---
 modules/platforms/cpp/ignite/client/CMakeLists.txt |    2 +
 .../cpp/ignite/client/detail/table/table_impl.h    |    2 +-
 .../cpp/ignite/client/table/record_view.h          |  582 ++++++++++-
 .../platforms/cpp/ignite/client/table/table.cpp    |    6 +-
 modules/platforms/cpp/ignite/client/table/table.h  |   19 +-
 modules/platforms/cpp/ignite/client/type_mapping.h |   86 ++
 .../platforms/cpp/tests/client-test/CMakeLists.txt |    1 +
 .../client-test/key_value_binary_view_test.cpp     |    4 +-
 .../tests/client-test/record_binary_view_test.cpp  |    6 +-
 .../cpp/tests/client-test/record_view_test.cpp     | 1083 ++++++++++++++++++++
 .../cpp/tests/client-test/tables_test.cpp          |   12 +-
 .../cpp/tests/client-test/transactions_test.cpp    |   36 +-
 12 files changed, 1782 insertions(+), 57 deletions(-)

diff --git a/modules/platforms/cpp/ignite/client/CMakeLists.txt 
b/modules/platforms/cpp/ignite/client/CMakeLists.txt
index e77c9f1f0f..f29ba9740a 100644
--- a/modules/platforms/cpp/ignite/client/CMakeLists.txt
+++ b/modules/platforms/cpp/ignite/client/CMakeLists.txt
@@ -44,6 +44,8 @@ set(PUBLIC_HEADERS
     ignite_client.h
     ignite_client_configuration.h
     ignite_logger.h
+    primitive.h
+    type_mapping.h
     compute/compute.h
     network/cluster_node.h
     sql/sql.h
diff --git a/modules/platforms/cpp/ignite/client/detail/table/table_impl.h 
b/modules/platforms/cpp/ignite/client/detail/table/table_impl.h
index 4a6395910a..88d0b8ca94 100644
--- a/modules/platforms/cpp/ignite/client/detail/table/table_impl.h
+++ b/modules/platforms/cpp/ignite/client/detail/table/table_impl.h
@@ -232,7 +232,7 @@ public:
      *
      * @param tx Optional transaction. If nullptr implicit transaction for this
      *   single operation is used.
-     * @param key A record with key columns set..
+     * @param key A record with key columns set.
      * @param callback Callback that is called on operation completion. Called 
with
      *   a value indicating whether a record with the specified key was 
deleted.
      */
diff --git a/modules/platforms/cpp/ignite/client/table/record_view.h 
b/modules/platforms/cpp/ignite/client/table/record_view.h
index 5a9d10b298..8c7b3c5db5 100644
--- a/modules/platforms/cpp/ignite/client/table/record_view.h
+++ b/modules/platforms/cpp/ignite/client/table/record_view.h
@@ -17,11 +17,12 @@
 
 #pragma once
 
-#include "ignite/client/table/ignite_tuple.h"
-#include "ignite/client/transaction/transaction.h"
+#include <ignite/client/table/ignite_tuple.h>
+#include <ignite/client/transaction/transaction.h>
+#include <ignite/client/type_mapping.h>
 
-#include "ignite/common/config.h"
-#include "ignite/common/ignite_result.h"
+#include <ignite/common/config.h>
+#include <ignite/common/ignite_result.h>
 
 #include <memory>
 #include <type_traits>
@@ -36,23 +37,8 @@ namespace detail {
 class table_impl;
 }
 
-/**
- * Record view interface provides methods to access table records.
- */
 template<typename T>
-class record_view {
-public:
-    typedef typename std::decay<T>::type value_type;
-
-    // Deleted
-    record_view(const record_view &) = delete;
-    record_view &operator=(const record_view &) = delete;
-
-    // Default
-    record_view() = default;
-    record_view(record_view &&) noexcept = default;
-    record_view &operator=(record_view &&) noexcept = default;
-};
+class record_view;
 
 /**
  * Record view interface provides methods to access table records.
@@ -337,7 +323,7 @@ public:
      *
      * @param tx Optional transaction. If nullptr implicit transaction for this
      *   single operation is used.
-     * @param key A record with key columns set..
+     * @param key A record with key columns set.
      * @param callback Callback that is called on operation completion. Called 
with
      *   a value indicating whether a record with the specified key was 
deleted.
      */
@@ -475,4 +461,558 @@ private:
     std::shared_ptr<detail::table_impl> m_impl;
 };
 
+
+/**
+ * Record view interface provides methods to access table records.
+ */
+template<typename T>
+class record_view {
+    friend class table;
+
+public:
+    typedef typename std::decay<T>::type value_type;
+
+    // Deleted
+    record_view(const record_view &) = delete;
+    record_view &operator=(const record_view &) = delete;
+
+    // Default
+    record_view() = default;
+    record_view(record_view &&) noexcept = default;
+    record_view &operator=(record_view &&) noexcept = default;
+
+    /**
+     * Gets a record by key asynchronously.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param key Key.
+     * @param callback Callback which is called on success with value if it
+     *   exists and @c std::nullopt otherwise
+     */
+    void get_async(transaction *tx, const value_type &key, 
ignite_callback<std::optional<value_type>> callback) {
+        m_delegate.get_async(tx, convert_to_tuple(key), [callback = 
std::move(callback)] (auto res) {
+            callback(convert_result(std::move(res)));
+        });
+    }
+
+    /**
+     * Gets a record by key.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param key Key.
+     * @return Value if exists and @c std::nullopt otherwise.
+     */
+    [[nodiscard]] std::optional<value_type> get(transaction *tx, const 
value_type &key) {
+        return sync<std::optional<value_type>>(
+            [this, tx, &key](auto callback) { get_async(tx, key, 
std::move(callback)); });
+    }
+
+    /**
+     * Gets multiple records by keys asynchronously.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param keys Keys.
+     * @param callback Callback that is called on operation completion. Called 
with
+     *   resulting records with all columns filled from the table. The order of
+     *   elements is guaranteed to be the same as the order of keys. If a 
record
+     *   does not exist, the resulting element of the corresponding order is
+     *   @c std::nullopt.
+     */
+    void get_all_async(transaction *tx, std::vector<value_type> keys,
+        ignite_callback<std::vector<std::optional<value_type>>> callback) {
+        m_delegate.get_all_async(tx, values_to_tuples(std::move(keys)), 
[callback = std::move(callback)] (auto res) {
+            callback(convert_result(std::move(res)));
+        });
+    }
+
+    /**
+     * Gets multiple records by keys.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param keys Keys.
+     * @return Resulting records with all columns filled from the table.
+     *   The order of elements is guaranteed to be the same as the order of
+     *   keys. If a record does not exist, the resulting element of the
+     *   corresponding order is @c std::nullopt.
+     */
+    [[nodiscard]] std::vector<std::optional<value_type>> get_all(transaction 
*tx, std::vector<value_type> keys) {
+        return sync<std::vector<std::optional<value_type>>>([this, tx, keys = 
std::move(keys)](auto callback) mutable {
+            get_all_async(tx, std::move(keys), std::move(callback));
+        });
+    }
+
+    /**
+     * Inserts a record into the table if does not exist or replaces the 
existing one.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *  single operation is used.
+     * @param record A record to insert into the table. The record cannot be 
@c nullptr.
+     * @param callback Callback.
+     */
+    void upsert_async(transaction *tx, const value_type &record, 
ignite_callback<void> callback) {
+        m_delegate.upsert_async(tx, convert_to_tuple(record), 
std::move(callback));
+    }
+
+    /**
+     * Inserts a record into the table if does not exist or replaces the 
existing one.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *  single operation is used.
+     * @param record A record to insert into the table. The record cannot be 
@c nullptr.
+     */
+    void upsert(transaction *tx, const value_type &record) {
+        sync<void>([this, tx, &record](auto callback) { upsert_async(tx, 
record, std::move(callback)); });
+    }
+
+    /**
+     * Inserts multiple records into the table asynchronously, replacing
+     * existing.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param records Records to upsert.
+     * @param callback Callback that is called on operation completion.
+     */
+    void upsert_all_async(transaction *tx, std::vector<value_type> records, 
ignite_callback<void> callback) {
+        m_delegate.upsert_all_async(tx, values_to_tuples(std::move(records)), 
std::move(callback));
+    }
+
+    /**
+     * Inserts multiple records into the table, replacing existing.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param records Records to upsert.
+     */
+    void upsert_all(transaction *tx, std::vector<value_type> records) {
+        sync<void>([this, tx, records = std::move(records)](auto callback) 
mutable {
+            upsert_all_async(tx, std::move(records), std::move(callback));
+        });
+    }
+
+    /**
+     * Inserts a record into the table and returns previous record 
asynchronously.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param record A record to upsert.
+     * @param callback Callback. Called with a value which contains replaced
+     *   record or @c std::nullopt if it did not exist.
+     */
+    void get_and_upsert_async(
+        transaction *tx, const value_type &record, 
ignite_callback<std::optional<value_type>> callback) {
+        m_delegate.get_and_upsert_async(tx, convert_to_tuple(record), 
[callback = std::move(callback)] (auto res) {
+            callback(convert_result(std::move(res)));
+        });
+    }
+
+    /**
+     * Inserts a record into the table and returns previous record.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param record A record to upsert.
+     * @return A replaced record or @c std::nullopt if it did not exist.
+     */
+    [[nodiscard]] std::optional<value_type> get_and_upsert(transaction *tx, 
const value_type &record) {
+        return sync<std::optional<value_type>>(
+            [this, tx, &record](auto callback) { get_and_upsert_async(tx, 
record, std::move(callback)); });
+    }
+
+    /**
+     * Inserts a record into the table if it does not exist asynchronously.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param record A record to insert into the table.
+     * @param callback Callback. Called with a value indicating whether the
+     *   record was inserted. Equals @c false if a record with the same key
+     *   already exists.
+     */
+    void insert_async(transaction *tx, const value_type &record, 
ignite_callback<bool> callback) {
+        m_delegate.insert_async(tx, convert_to_tuple(record), 
std::move(callback));
+    }
+
+    /**
+     * Inserts a record into the table if does not exist.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param record A record to insert into the table.
+     */
+    bool insert(transaction *tx, const value_type &record) {
+        return sync<bool>([this, tx, &record](auto callback) { 
insert_async(tx, record, std::move(callback)); });
+    }
+
+    /**
+     * Inserts multiple records into the table asynchronously, skipping 
existing ones.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param records Records to insert.
+     * @param callback Callback that is called on operation completion. Called 
with
+     *   skipped records.
+     */
+    void insert_all_async(
+        transaction *tx, std::vector<value_type> records, 
ignite_callback<std::vector<value_type>> callback) {
+        m_delegate.insert_all_async(tx, values_to_tuples(std::move(records)),
+            [callback = std::move(callback)] (auto res) {
+                callback(convert_result(std::move(res)));
+            }
+        );
+    }
+
+    /**
+     * Inserts multiple records into the table, skipping existing ones.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param records Records to insert.
+     * @return Skipped records.
+     */
+    std::vector<value_type> insert_all(transaction *tx, 
std::vector<value_type> records) {
+        return sync<std::vector<value_type>>([this, tx, records = 
std::move(records)](auto callback) mutable {
+            insert_all_async(tx, std::move(records), std::move(callback));
+        });
+    }
+
+    /**
+     * Asynchronously replaces a record with the same key columns if it exists,
+     * otherwise does nothing.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param record A record to insert into the table.
+     * @param callback Callback. Called with a value indicating whether a 
record
+     *   with the specified key was replaced.
+     */
+    void replace_async(transaction *tx, const value_type &record, 
ignite_callback<bool> callback) {
+        m_delegate.replace_async(tx, convert_to_tuple(record), 
std::move(callback));
+    }
+
+    /**
+     * Replaces a record with the same key columns if it exists, otherwise does
+     * nothing.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param record A record to insert into the table.
+     * @return A value indicating whether a record with the specified key was
+     *   replaced.
+     */
+    bool replace(transaction *tx, const value_type &record) {
+        return sync<bool>([this, tx, &record](auto callback) { 
replace_async(tx, record, std::move(callback)); });
+    }
+
+    /**
+     * Asynchronously replaces a record with a new one only if all existing
+     * columns have the same values as the specified @c record.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param record Current value of the record to be replaced.
+     * @param new_record A record to replace it with.
+     * @param callback Callback. Called with a value indicating whether a
+     *   specified record was replaced.
+     */
+    void replace_async(
+        transaction *tx, const value_type &record, const value_type 
&new_record, ignite_callback<bool> callback) {
+        m_delegate.replace_async(tx, convert_to_tuple(record), 
convert_to_tuple(new_record), std::move(callback));
+    }
+
+    /**
+     * Replaces a record with a new one only if all existing columns have
+     * the same values as the specified @c record.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param record Current value of the record to be replaced.
+     * @param new_record A record to replace it with.
+     * @return A value indicating whether a specified record was replaced.
+     */
+    bool replace(transaction *tx, const value_type &record, const value_type 
&new_record) {
+        return sync<bool>([this, tx, &record, &new_record] (auto callback) {
+            replace_async(tx, record, new_record, std::move(callback));
+        });
+    }
+
+    /**
+     * Asynchronously replaces a record with the same key columns if it exists
+     * returning previous record value.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param record A record to insert.
+     * @param callback Callback. Called with a previous value for the given 
key,
+     *   or @c std::nullopt if it did not exist.
+     */
+    void get_and_replace_async(
+        transaction *tx, const value_type &record, 
ignite_callback<std::optional<value_type>> callback) {
+        m_delegate.get_and_replace_async(tx, convert_to_tuple(record), 
[callback = std::move(callback)] (auto res) {
+            callback(convert_result(std::move(res)));
+        });
+    }
+
+    /**
+     * Replaces a record with the same key columns if it exists returning
+     * previous record value.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param record A record to insert.
+     * @param callback A previous value for the given key, or @c std::nullopt 
if
+     *   it did not exist.
+     */
+    [[nodiscard]] std::optional<value_type> get_and_replace(transaction *tx, 
const value_type &record) {
+        return sync<std::optional<value_type>>(
+            [this, tx, &record](auto callback) { get_and_replace_async(tx, 
record, std::move(callback)); });
+    }
+
+    /**
+     * Deletes a record with the specified key asynchronously.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param key A record with key columns set.
+     * @param callback Callback that is called on operation completion. Called 
with
+     *   a value indicating whether a record with the specified key was 
deleted.
+     */
+    void remove_async(transaction *tx, const value_type &key, 
ignite_callback<bool> callback) {
+        m_delegate.remove_async(tx, convert_to_tuple(key), 
std::move(callback));
+    }
+
+    /**
+     * Deletes a record with the specified key.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param key A record with key columns set.
+     * @return A value indicating whether a record with the specified key was 
deleted.
+     */
+    bool remove(transaction *tx, const value_type &record) {
+        return sync<bool>([this, tx, &record](auto callback) { 
remove_async(tx, record, std::move(callback)); });
+    }
+
+    /**
+     * Deletes a record only if all existing columns have the same values as
+     * the specified record asynchronously.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param record A record with all columns set.
+     * @param callback Callback that is called on operation completion. Called 
with
+     *   a value indicating whether a record with the specified key was 
deleted.
+     */
+    void remove_exact_async(transaction *tx, const value_type &record, 
ignite_callback<bool> callback) {
+        m_delegate.remove_exact_async(tx, convert_to_tuple(record), 
std::move(callback));
+    }
+
+    /**
+     * Deletes a record only if all existing columns have the same values as
+     * the specified record.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param record A record with all columns set.
+     * @return A value indicating whether a record with the specified key was
+     *   deleted.
+     */
+    bool remove_exact(transaction *tx, const value_type &record) {
+        return sync<bool>([this, tx, &record](auto callback) { 
remove_exact_async(tx, record, std::move(callback)); });
+    }
+
+    /**
+     * Gets and deletes a record with the specified key asynchronously.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param key A record with key columns set.
+     * @param callback Callback that is called on operation completion. Called 
with
+     *   a deleted record or @c std::nullopt if it did not exist.
+     */
+    void get_and_remove_async(
+        transaction *tx, const value_type &key, 
ignite_callback<std::optional<value_type>> callback) {
+        m_delegate.get_and_remove_async(tx, convert_to_tuple(key), [callback = 
std::move(callback)] (auto res) {
+            callback(convert_result(std::move(res)));
+        });
+    }
+
+    /**
+     * Gets and deletes a record with the specified key.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param key A record with key columns set.
+     * @return A deleted record or @c std::nullopt if it did not exist.
+     */
+    std::optional<value_type> get_and_remove(transaction *tx, const value_type 
&key) {
+        return sync<std::optional<value_type>>(
+            [this, tx, &key](auto callback) { get_and_remove_async(tx, key, 
std::move(callback)); });
+    }
+
+    /**
+     * Deletes multiple records from the table asynchronously. If one or more
+     * keys do not exist, other records are still deleted
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param keys Record keys to delete.
+     * @param callback Callback that is called on operation completion. Called 
with
+     *   records from @c keys that did not exist.
+     */
+    void remove_all_async(
+        transaction *tx, std::vector<value_type> keys, 
ignite_callback<std::vector<value_type>> callback) {
+        m_delegate.remove_all_async(tx, values_to_tuples(std::move(keys)), 
[callback = std::move(callback)] (auto res) {
+            callback(convert_result(std::move(res)));
+        });
+    }
+
+    /**
+     * Deletes multiple records from the table If one or more keys do not 
exist,
+     * other records are still deleted
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param keys Record keys to delete.
+     * @return Records from @c keys that did not exist.
+     */
+    std::vector<value_type> remove_all(transaction *tx, 
std::vector<value_type> keys) {
+        return sync<std::vector<value_type>>([this, tx, keys = 
std::move(keys)](auto callback) mutable {
+            remove_all_async(tx, std::move(keys), std::move(callback));
+        });
+    }
+
+    /**
+     * Deletes multiple exactly matching records asynchronously. If one or more
+     * records do not exist, other records are still deleted.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param records Records to delete.
+     * @param callback Callback that is called on operation completion. Called 
with
+     *   records from @c records that did not exist.
+     */
+    void remove_all_exact_async(
+        transaction *tx, std::vector<value_type> records, 
ignite_callback<std::vector<value_type>> callback) {
+        m_delegate.remove_all_exact_async(tx, 
values_to_tuples(std::move(records)),
+            [callback = std::move(callback)] (auto res) {
+                callback(convert_result(std::move(res)));
+            }
+        );
+    }
+
+    /**
+     * Deletes multiple exactly matching records. If one or more records do not
+     * exist, other records are still deleted.
+     *
+     * @param tx Optional transaction. If nullptr implicit transaction for this
+     *   single operation is used.
+     * @param records Records to delete.
+     * @return Records from @c records that did not exist.
+     */
+    std::vector<value_type> remove_all_exact(transaction *tx, 
std::vector<value_type> records) {
+        return sync<std::vector<value_type>>([this, tx, records = 
std::move(records)](auto callback) mutable {
+            remove_all_exact_async(tx, std::move(records), 
std::move(callback));
+        });
+    }
+
+private:
+    /**
+     * Convert values to tuples.
+     * @param vals Values.
+     * @return Tuples.
+     */
+    static std::vector<ignite_tuple> values_to_tuples(std::vector<value_type> 
values) {
+        //TODO: Optimize memory usage (IGNITE-19198)
+        std::vector<ignite_tuple> tuples;
+        tuples.reserve(values.size());
+        for (auto &&value : std::move(values)) {
+            tuples.push_back(convert_to_tuple(std::move(value)));
+        }
+        return tuples;
+    }
+
+    /**
+     * Tuples to values.
+     * @param tuples Tuples.
+     * @return Values.
+     */
+    static std::vector<value_type> tuples_to_values(std::vector<ignite_tuple> 
tuples) {
+        //TODO: Optimize memory usage (IGNITE-19198)
+        std::vector<value_type> values;
+        values.reserve(tuples.size());
+        for (auto &&tuple : std::move(tuples)) {
+            
values.emplace_back(convert_from_tuple<value_type>(std::move(tuple)));
+        }
+        return values;
+    }
+
+    /**
+     * Optional tuples to optional values.
+     * @param tuples Tuples.
+     * @return Values.
+     */
+    static std::vector<std::optional<value_type>> 
tuples_to_values(std::vector<std::optional<ignite_tuple>> tuples) {
+        //TODO: Optimize memory usage (IGNITE-19198)
+        std::vector<std::optional<value_type>> values;
+        values.reserve(tuples.size());
+        for (auto &&tuple : std::move(tuples)) {
+            
values.emplace_back(convert_from_tuple<value_type>(std::move(tuple)));
+        }
+        return values;
+    }
+
+    /**
+     * Convert result from tuple-based type to user type.
+     * @param res Result to convert.
+     * @return Converted result.
+     */
+    static ignite_result<std::optional<value_type>> 
convert_result(ignite_result<std::optional<ignite_tuple>> &&res) {
+        if (res.has_error())
+            return {std::move(res).error()};
+
+        return {convert_from_tuple<value_type>(std::move(res).value())};
+    }
+
+    /**
+     * Convert result from tuple-based type to user type.
+     * @param res Result to convert.
+     * @return Converted result.
+     */
+    static ignite_result<std::vector<std::optional<value_type>>> 
convert_result(
+        ignite_result<std::vector<std::optional<ignite_tuple>>> &&res) {
+        if (res.has_error())
+            return {std::move(res).error()};
+
+        return {tuples_to_values(std::move(res).value())};
+    }
+
+    /**
+     * Convert result from tuple-based type to user type.
+     * @param res Result to convert.
+     * @return Converted result.
+     */
+    static ignite_result<std::vector<value_type>> 
convert_result(ignite_result<std::vector<ignite_tuple>> &&res) {
+        if (res.has_error())
+            return {std::move(res).error()};
+
+        return {tuples_to_values(std::move(res).value())};
+    }
+
+    /**
+     * Constructor
+     *
+     * @param impl Implementation
+     */
+    explicit record_view(record_view<ignite_tuple> delegate)
+        : m_delegate(std::move(delegate)) {}
+
+    /** Delegate. */
+    record_view<ignite_tuple> m_delegate;
+};
+
 } // namespace ignite
diff --git a/modules/platforms/cpp/ignite/client/table/table.cpp 
b/modules/platforms/cpp/ignite/client/table/table.cpp
index 438ae117cd..9c2219d90d 100644
--- a/modules/platforms/cpp/ignite/client/table/table.cpp
+++ b/modules/platforms/cpp/ignite/client/table/table.cpp
@@ -20,15 +20,15 @@
 
 namespace ignite {
 
-const std::string &table::name() const noexcept {
+const std::string &table::get_name() const noexcept {
     return m_impl->name();
 }
 
-record_view<ignite_tuple> table::record_binary_view() const noexcept {
+record_view<ignite_tuple> table::get_record_binary_view() const noexcept {
     return record_view<ignite_tuple>{m_impl};
 }
 
-key_value_view<ignite_tuple, ignite_tuple> table::key_value_binary_view() 
const noexcept {
+key_value_view<ignite_tuple, ignite_tuple> table::get_key_value_binary_view() 
const noexcept {
     return key_value_view<ignite_tuple, ignite_tuple>{m_impl};
 }
 
diff --git a/modules/platforms/cpp/ignite/client/table/table.h 
b/modules/platforms/cpp/ignite/client/table/table.h
index 24721dacde..e1097ebb2c 100644
--- a/modules/platforms/cpp/ignite/client/table/table.h
+++ b/modules/platforms/cpp/ignite/client/table/table.h
@@ -56,21 +56,34 @@ public:
      *
      * @return Table name.
      */
-    [[nodiscard]] IGNITE_API const std::string &name() const noexcept;
+    [[nodiscard]] IGNITE_API const std::string &get_name() const noexcept;
 
     /**
      * Gets the record binary view.
      *
      * @return Record binary view.
      */
-    [[nodiscard]] IGNITE_API record_view<ignite_tuple> record_binary_view() 
const noexcept;
+    [[nodiscard]] IGNITE_API record_view<ignite_tuple> 
get_record_binary_view() const noexcept;
+
+    /**
+     * Gets the record view for the type.
+     *
+     * Template functions @c convert_to_tuple() and @c convert_from_tuple() 
should be specialized for the type T.
+     * @see See type_mapping.h for details.
+     *
+     * @return Record view.
+     */
+    template<typename T>
+    [[nodiscard]] record_view<T> get_record_view() const noexcept {
+        return record_view<T>{get_record_binary_view()};
+    }
 
     /**
      * Gets the key-value binary view.
      *
      * @return Record binary view.
      */
-    [[nodiscard]] IGNITE_API key_value_view<ignite_tuple, ignite_tuple> 
key_value_binary_view() const noexcept;
+    [[nodiscard]] IGNITE_API key_value_view<ignite_tuple, ignite_tuple> 
get_key_value_binary_view() const noexcept;
 
 private:
     /**
diff --git a/modules/platforms/cpp/ignite/client/type_mapping.h 
b/modules/platforms/cpp/ignite/client/type_mapping.h
new file mode 100644
index 0000000000..55eb3a3b64
--- /dev/null
+++ b/modules/platforms/cpp/ignite/client/type_mapping.h
@@ -0,0 +1,86 @@
+/*
+ * 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/client/table/ignite_tuple.h>
+
+namespace ignite {
+
+/**
+ * Function that specifies how a value of a type should be converted to @c 
ignite_tuple.
+ *
+ * @tparam T Type of the value.
+ * @param value Value to convert.
+ * @return An instance of @c ignite_tuple.
+ */
+template<typename T>
+ignite_tuple convert_to_tuple(T &&value);
+
+/**
+ * Function that specifies how an @c ignite_tuple instance should be converted 
to specific type.
+ *
+ * @tparam T Type to convert to.
+ * @param value Instance of the @c ignite_tuple type.
+ * @return A resulting value.
+ */
+template<typename T>
+T convert_from_tuple(ignite_tuple &&value);
+
+/**
+ * Specialisation for const-references.
+ *
+ * @tparam T Type to convert from.
+ * @param value Value.
+ * @return Tuple.
+ */
+template<typename T>
+ignite_tuple convert_to_tuple(const T &value) {
+    return convert_to_tuple(T(value));
+}
+
+/**
+ * Specialisation for optionals.
+ *
+ * @tparam T Type to convert from.
+ * @param value Optional value.
+ * @return Optional tuple.
+ */
+template<typename T>
+std::optional<ignite_tuple> convert_to_tuple(std::optional<T> &&value) {
+    if (!value.has_value())
+        return std::nullopt;
+
+    return {convert_to_tuple<T>(*std::move(value))};
+}
+
+/**
+ * Specialisation for optionals.
+ *
+ * @tparam T Type to convert to.
+ * @param value Optional tuple.
+ * @return Optional value.
+ */
+template<typename T>
+std::optional<T> convert_from_tuple(std::optional<ignite_tuple> &&value) {
+    if (!value.has_value())
+        return std::nullopt;
+
+    return {convert_from_tuple<T>(*std::move(value))};
+}
+
+} // namespace ignite
diff --git a/modules/platforms/cpp/tests/client-test/CMakeLists.txt 
b/modules/platforms/cpp/tests/client-test/CMakeLists.txt
index 2d9611905f..c8772c72e5 100644
--- a/modules/platforms/cpp/tests/client-test/CMakeLists.txt
+++ b/modules/platforms/cpp/tests/client-test/CMakeLists.txt
@@ -27,6 +27,7 @@ set(SOURCES
     key_value_binary_view_test.cpp
     main.cpp
     record_binary_view_test.cpp
+    record_view_test.cpp
     sql_test.cpp
     tables_test.cpp
     transactions_test.cpp
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 c48fca21dd..a0627cc657 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
@@ -39,7 +39,7 @@ protected:
         m_client = ignite_client::start(cfg, std::chrono::seconds(30));
         auto table = m_client.get_tables().get_table(TABLE_1);
 
-        kv_view = table->key_value_binary_view();
+        kv_view = table->get_key_value_binary_view();
     }
 
     void TearDown() override {
@@ -931,7 +931,7 @@ TEST_F(key_value_binary_view_test, remove_all_exact_empty) {
 
 TEST_F(key_value_binary_view_test, types_test) {
     auto table = m_client.get_tables().get_table(TABLE_NAME_ALL_COLUMNS);
-    kv_view = table->key_value_binary_view();
+    kv_view = table->get_key_value_binary_view();
 
     ignite_tuple inserted{
         {"str", "test"},
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 d79f7fb9ea..fd95e93e61 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
@@ -39,7 +39,7 @@ protected:
         m_client = ignite_client::start(cfg, std::chrono::seconds(30));
         auto table = m_client.get_tables().get_table(TABLE_1);
 
-        tuple_view = table->record_binary_view();
+        tuple_view = table->get_record_binary_view();
     }
 
     void TearDown() override {
@@ -774,7 +774,7 @@ TEST_F(record_binary_view_test, remove_exact_empty_throws) {
 }
 
 TEST_F(record_binary_view_test, get_and_remove_nonexisting) {
-    auto res = tuple_view.get_and_replace(nullptr, get_tuple(42, "foo"));
+    auto res = tuple_view.get_and_remove(nullptr, get_tuple(42, "foo"));
     ASSERT_FALSE(res.has_value());
 
     auto res_tuple = tuple_view.get(nullptr, get_tuple(42));
@@ -938,7 +938,7 @@ TEST_F(record_binary_view_test, remove_all_exact_empty) {
 
 TEST_F(record_binary_view_test, types_test) {
     auto table = m_client.get_tables().get_table(TABLE_NAME_ALL_COLUMNS);
-    tuple_view = table->record_binary_view();
+    tuple_view = table->get_record_binary_view();
 
     ignite_tuple inserted{
         {"key", std::int64_t(42)},
diff --git a/modules/platforms/cpp/tests/client-test/record_view_test.cpp 
b/modules/platforms/cpp/tests/client-test/record_view_test.cpp
new file mode 100644
index 0000000000..a8f882eb5c
--- /dev/null
+++ b/modules/platforms/cpp/tests/client-test/record_view_test.cpp
@@ -0,0 +1,1083 @@
+/*
+ * 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 "tests/test-common/test_utils.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;
+
+/**
+ * Test table type mapping (@see ignite_runner_suite::TABLE_1).
+ */
+struct test_type {
+    test_type() = default;
+    explicit test_type(std::int64_t key) : key(key) {}
+    explicit test_type(std::string val) : val(std::move(val)) {}
+    explicit test_type(std::int64_t key, std::string val) : key(key), 
val(std::move(val)) {}
+
+    std::int64_t key{0};
+    std::string val;
+};
+
+struct wrong_mapping_key {
+    wrong_mapping_key() = default;
+    explicit wrong_mapping_key(std::int64_t key) : key(key) {}
+    explicit wrong_mapping_key(std::string val) : val(std::move(val)) {}
+    explicit wrong_mapping_key(std::int64_t key, std::string val) : key(key), 
val(std::move(val)) {}
+
+    std::int64_t key{0};
+    std::string val;
+};
+
+struct wrong_mapping_value {
+    wrong_mapping_value() = default;
+    explicit wrong_mapping_value(std::int64_t key) : key(key) {}
+    explicit wrong_mapping_value(std::string val) : val(std::move(val)) {}
+    explicit wrong_mapping_value(std::int64_t key, std::string val) : 
key(key), val(std::move(val)) {}
+
+    std::int64_t key{0};
+    std::string val;
+};
+
+namespace ignite {
+
+template<>
+ignite_tuple convert_to_tuple(test_type &&value) {
+    ignite_tuple tuple;
+
+    tuple.set("key", value.key);
+    tuple.set("val", value.val);
+
+    return tuple;
+}
+
+template<>
+test_type convert_from_tuple(ignite_tuple&& value) {
+    test_type res;
+
+    res.key = value.get<std::int64_t>("key");
+
+    if (value.column_count() > 1)
+        res.val = value.get<std::string>("val");
+
+    return res;
+}
+
+template<>
+ignite_tuple convert_to_tuple(wrong_mapping_key &&value) {
+    ignite_tuple tuple;
+
+    tuple.set("id", value.key);
+    tuple.set("val", value.val);
+
+    return tuple;
+}
+
+template<>
+wrong_mapping_key convert_from_tuple(ignite_tuple&& value) {
+    wrong_mapping_key res;
+
+    res.key = value.get<std::int64_t>("id");
+
+    if (value.column_count() > 1)
+        res.val = value.get<std::string>("val");
+
+    return res;
+}
+
+template<>
+ignite_tuple convert_to_tuple(wrong_mapping_value &&value) {
+    ignite_tuple tuple;
+
+    tuple.set("key", value.key);
+    tuple.set("str", value.val);
+
+    return tuple;
+}
+
+template<>
+wrong_mapping_value convert_from_tuple(ignite_tuple&& value) {
+    wrong_mapping_value res;
+
+    res.key = value.get<std::int64_t>("key");
+
+    if (value.column_count() > 1)
+        res.val = value.get<std::string>("str");
+
+    return res;
+}
+
+}
+
+/**
+ * Test suite.
+ */
+class record_view_test : public ignite_runner_suite {
+protected:
+    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(TABLE_1);
+
+        view = table->get_record_view<test_type>();
+    }
+
+    void TearDown() override {
+        std::vector<test_type> work_range;
+        work_range.reserve(200);
+        for (std::int64_t i = -100; i < 100; ++i)
+            work_range.emplace_back(i);
+
+        view.remove_all(nullptr, work_range);
+    }
+
+    /** Ignite client. */
+    ignite_client m_client;
+
+    /** Record binary view. */
+    record_view<test_type> view;
+};
+
+TEST_F(record_view_test, wrong_mapped_key_throws) {
+    {
+        test_type val{1, "foo"};
+        view.upsert(nullptr, val);
+    }
+
+    auto table = m_client.get_tables().get_table(TABLE_1);
+    auto wrong_view = table->get_record_view<wrong_mapping_key>();
+
+    wrong_mapping_key key{1};
+    wrong_mapping_key val{1, "foo"};
+
+    EXPECT_THROW(
+        {
+            try {
+                wrong_view.upsert(nullptr, val);
+            } catch (const ignite_error &e) {
+                EXPECT_THAT(e.what_str(), testing::HasSubstr("Missed key 
column: KEY"));
+                throw;
+            }
+        },
+        ignite_error);
+
+    EXPECT_THROW(
+        {
+            try {
+                (void) wrong_view.get(nullptr, key);
+            } catch (const ignite_error &e) {
+                EXPECT_THAT(e.what_str(), testing::HasSubstr("Missed key 
column: KEY"));
+                throw;
+            }
+        },
+        ignite_error);
+}
+
+TEST_F(record_view_test, wrong_mapped_value_throws) {
+    {
+        test_type val{1, "foo"};
+        view.upsert(nullptr, val);
+    }
+
+    auto table = m_client.get_tables().get_table(TABLE_1);
+    auto wrong_view = table->get_record_view<wrong_mapping_value>();
+
+    wrong_mapping_value key{1};
+    wrong_mapping_value val{1, "foo"};
+
+    // Should work just fine. Having additional field and not providing value 
field is not an error.
+    wrong_view.upsert(nullptr, val);
+
+    EXPECT_THROW(
+        {
+            try {
+                (void) wrong_view.get(nullptr, key);
+            } catch (const ignite_error &e) {
+                EXPECT_EQ(e.what_str(), "Can not find column with the name 
'str' in the tuple");
+                throw;
+            }
+        },
+        ignite_error);
+}
+
+TEST_F(record_view_test, upsert_get) {
+    test_type key{1};
+    test_type val{1, "foo"};
+
+    view.upsert(nullptr, val);
+    auto res = view.get(nullptr, key);
+
+    ASSERT_TRUE(res.has_value());
+    EXPECT_EQ(1L, res->key);
+    EXPECT_EQ("foo", res->val);
+}
+
+TEST_F(record_view_test, upsert_get_async) {
+    test_type key{1};
+    test_type val{1, "foo"};
+
+    auto all_done = std::make_shared<std::promise<std::optional<test_type>>>();
+
+    view.upsert_async(nullptr, val, [&](ignite_result<void> &&res) {
+        if (!check_and_set_operation_error(*all_done, res))
+            return;
+
+        view.get_async(nullptr, key, [&](auto res) { 
result_set_promise(*all_done, std::move(res)); });
+    });
+
+    auto res = all_done->get_future().get();
+    ASSERT_TRUE(res.has_value());
+    EXPECT_EQ(val.key, res->key);
+    EXPECT_EQ(val.val, res->val);
+}
+
+
+TEST_F(record_view_test, upsert_overrides_value) {
+    test_type key{1};
+    test_type val{1, "foo"};
+
+    view.upsert(nullptr, val);
+    auto res = view.get(nullptr, key);
+
+    ASSERT_TRUE(res.has_value());
+    EXPECT_EQ(1L, res->key);
+    EXPECT_EQ("foo", res->val);
+
+    val.val = "bar";
+    view.upsert(nullptr, val);
+    res = view.get(nullptr, key);
+
+    ASSERT_TRUE(res.has_value());
+    EXPECT_EQ(1L, res->key);
+    EXPECT_EQ("bar", res->val);
+}
+
+TEST_F(record_view_test, get_all_empty) {
+    auto res = view.get_all(nullptr, {});
+    EXPECT_TRUE(res.empty());
+}
+
+TEST_F(record_view_test, get_all_nonexisting) {
+    auto res = view.get_all(nullptr, {test_type(-42)});
+
+    ASSERT_TRUE(res.empty());
+}
+
+TEST_F(record_view_test, upsert_all_empty_no_throw) {
+    view.upsert_all(nullptr, {});
+}
+
+TEST_F(record_view_test, upsert_all_get_all) {
+    static constexpr std::size_t records_num = 10;
+
+    std::vector<test_type> records;
+    records.reserve(records_num);
+    for (std::int64_t i = 1; i < 1 + std::int64_t(records_num); ++i)
+        records.emplace_back(i, "Val" + std::to_string(i));
+
+    std::vector<test_type> keys;
+    for (std::int64_t i = 9; i < 13; ++i)
+        keys.emplace_back(i);
+
+    view.upsert_all(nullptr, records);
+    auto res = view.get_all(nullptr, keys);
+
+    // TODO: Key order should be preserved by the server (IGNITE-16004).
+    EXPECT_EQ(res.size(), 2);
+
+    ASSERT_TRUE(res[0].has_value());
+    EXPECT_EQ(9, res[0]->key);
+    EXPECT_EQ("Val9", res[0]->val);
+
+    ASSERT_TRUE(res[1].has_value());
+    EXPECT_EQ(10, res[1]->key);
+    EXPECT_EQ("Val10", res[1]->val);
+}
+
+TEST_F(record_view_test, upsert_all_get_all_async) {
+    static constexpr std::size_t records_num = 10;
+
+    std::vector<test_type> records;
+    records.reserve(records_num);
+    for (std::int64_t i = 1; i < 1 + std::int64_t(records_num); ++i)
+        records.emplace_back(i, "Val" + std::to_string(i));
+
+    std::vector<test_type> keys;
+    for (std::int64_t i = 9; i < 13; ++i)
+        keys.emplace_back(i);
+
+    auto all_done = 
std::make_shared<std::promise<std::vector<std::optional<test_type>>>>();
+
+    view.upsert_all_async(nullptr, records, [&](ignite_result<void> &&res) {
+        if (!check_and_set_operation_error(*all_done, res))
+            return;
+
+        // TODO: Key order should be preserved by the server (IGNITE-16004).
+        view.get_all_async(nullptr, keys, [&](auto res) { 
result_set_promise(*all_done, std::move(res)); });
+    });
+
+    auto res = all_done->get_future().get();
+
+    EXPECT_EQ(res.size(), 2);
+
+    ASSERT_TRUE(res[0].has_value());
+    EXPECT_EQ(9, res[0]->key);
+    EXPECT_EQ("Val9", res[0]->val);
+
+    ASSERT_TRUE(res[1].has_value());
+    EXPECT_EQ(10, res[1]->key);
+    EXPECT_EQ("Val10", res[1]->val);
+}
+
+TEST_F(record_view_test, get_and_upsert_new_record) {
+    test_type val{42, "foo"};
+    auto res = view.get_and_upsert(nullptr, val);
+
+    ASSERT_FALSE(res.has_value());
+}
+
+TEST_F(record_view_test, get_and_upsert_existing_record) {
+    test_type val1{42, "foo"};
+    auto res = view.get_and_upsert(nullptr, val1);
+
+    ASSERT_FALSE(res.has_value());
+
+    test_type val2{42, "bar"};
+    res = view.get_and_upsert(nullptr, val2);
+
+    ASSERT_TRUE(res.has_value());
+    EXPECT_EQ(42, res->key);
+    EXPECT_EQ("foo", res->val);
+
+    res = view.get(nullptr, test_type(42));
+
+    ASSERT_TRUE(res.has_value());
+    EXPECT_EQ(42, res->key);
+    EXPECT_EQ("bar", res->val);
+}
+
+TEST_F(record_view_test, get_and_upsert_existing_record_async) {
+    test_type val1{42, "foo"};
+    test_type val2{42, "bar"};
+
+    auto first = std::make_shared<std::promise<std::optional<test_type>>>();
+
+    view.get_and_upsert_async(nullptr, val1, [&](auto res) {
+        if (!check_and_set_operation_error(*first, res))
+            return;
+
+        if (res.value().has_value())
+            
first->set_exception(std::make_exception_ptr(ignite_error("Expected nullopt on 
first insertion")));
+
+        view.get_and_upsert_async(
+            nullptr, val2, [&](auto res) { result_set_promise(*first, 
std::move(res)); });
+    });
+
+    auto res = first->get_future().get();
+    ASSERT_TRUE(res.has_value());
+    EXPECT_EQ(val1.key, res->key);
+    EXPECT_EQ(val1.val, res->val);
+
+    auto second = std::make_shared<std::promise<std::optional<test_type>>>();
+    view.get_async(nullptr, test_type(42), [&](auto res) { 
result_set_promise(*second, std::move(res)); });
+
+    res = second->get_future().get();
+    ASSERT_TRUE(res.has_value());
+    EXPECT_EQ(val2.key, res->key);
+    EXPECT_EQ(val2.val, res->val);
+}
+
+TEST_F(record_view_test, insert_new_record) {
+    test_type val{42, "foo"};
+    auto res = view.insert(nullptr, val);
+
+    ASSERT_TRUE(res);
+}
+
+TEST_F(record_view_test, insert_existing_record) {
+    test_type val1{42, "foo"};
+    auto inserted = view.insert(nullptr, val1);
+    ASSERT_TRUE(inserted);
+
+    test_type val2{42, "bar"};
+    inserted = view.insert(nullptr, val2);
+    ASSERT_FALSE(inserted);
+
+    auto res = view.get(nullptr, test_type(42));
+    ASSERT_TRUE(res.has_value());
+    EXPECT_EQ(42, res->key);
+    EXPECT_EQ("foo", res->val);
+}
+
+TEST_F(record_view_test, insert_existing_record_async) {
+    test_type val1{42, "foo"};
+    test_type val2{42, "bar"};
+
+    auto all_done = std::make_shared<std::promise<std::optional<test_type>>>();
+
+    view.insert_async(nullptr, val1, [&](ignite_result<bool> &&res) {
+        if (!check_and_set_operation_error(*all_done, res))
+            return;
+
+        if (!res.value())
+            
all_done->set_exception(std::make_exception_ptr(ignite_error("Expected true on 
first insertion")));
+
+        view.insert_async(nullptr, val2, [&](ignite_result<bool> &&res) {
+            if (!check_and_set_operation_error(*all_done, res))
+                return;
+
+            if (res.value())
+                
all_done->set_exception(std::make_exception_ptr(ignite_error("Expected false on 
second insertion")));
+
+            view.get_async(
+                nullptr, test_type(42), [&](auto res) { 
result_set_promise(*all_done, std::move(res)); });
+        });
+    });
+
+    auto res = all_done->get_future().get();
+    ASSERT_TRUE(res.has_value());
+    EXPECT_EQ(val1.key, res->key);
+    EXPECT_EQ(val1.val, res->val);
+}
+
+TEST_F(record_view_test, insert_all_new) {
+    static constexpr std::size_t records_num = 10;
+
+    std::vector<test_type> keys;
+    std::vector<test_type> records;
+    for (std::int64_t i = 1; i < 1 + std::int64_t(records_num); ++i) {
+        keys.emplace_back(i);
+        records.emplace_back(i, "Val" + std::to_string(i));
+    }
+
+    auto insert_res = view.insert_all(nullptr, records);
+
+    ASSERT_TRUE(insert_res.empty());
+
+    auto res = view.get_all(nullptr, keys);
+
+    EXPECT_EQ(res.size(), 10);
+    ASSERT_TRUE(std::all_of(res.begin(), res.end(), [](const auto &elem) { 
return elem.has_value(); }));
+}
+
+TEST_F(record_view_test, insert_all_overlapped) {
+    auto res = view.insert_all(nullptr, {test_type(1, "foo"), test_type(2, 
"bar")});
+
+    ASSERT_TRUE(res.empty());
+
+    res = view.insert_all(nullptr, {test_type(2, "baz"), test_type(3, "bar")});
+
+    EXPECT_EQ(res.size(), 1);
+    EXPECT_EQ(2, res.front().key);
+    EXPECT_EQ("baz", res.front().val);
+
+    auto res2 = view.get(nullptr, test_type(2));
+
+    ASSERT_TRUE(res2.has_value());
+    EXPECT_EQ(2, res2->key);
+    EXPECT_EQ("bar", res2->val);
+}
+
+TEST_F(record_view_test, insert_all_overlapped_async) {
+    auto all_done = std::make_shared<std::promise<std::optional<test_type>>>();
+
+    view.insert_all_async(nullptr, {test_type(1, "foo"), test_type(2, "bar")}, 
[&](auto res) {
+        if (!check_and_set_operation_error(*all_done, res))
+            return;
+
+        if (!res.value().empty())
+            
all_done->set_exception(std::make_exception_ptr(ignite_error("Expected empty 
return on first insertion")));
+
+        view.insert_all_async(
+            nullptr, {test_type(1, "foo"), test_type(2, "baz"), test_type(3, 
"bar")}, [&](auto res) {
+                if (!check_and_set_operation_error(*all_done, res))
+                    return;
+
+                if (res.value().size() != 2)
+                    all_done->set_exception(std::make_exception_ptr(
+                        ignite_error("Expected 2 on second insertion but got " 
+ std::to_string(res.value().size()))));
+
+                view.get_async(
+                    nullptr, test_type(2), [&](auto res) { 
result_set_promise(*all_done, std::move(res)); });
+            });
+    });
+
+    auto res = all_done->get_future().get();
+    ASSERT_TRUE(res.has_value());
+    EXPECT_EQ(2, res->key);
+    EXPECT_EQ("bar", res->val);
+}
+
+TEST_F(record_view_test, insert_all_empty) {
+    auto res = view.insert_all(nullptr, {});
+    EXPECT_TRUE(res.empty());
+}
+
+TEST_F(record_view_test, replace_nonexisting) {
+    test_type val{42, "foo"};
+    auto replaced = view.replace(nullptr, val);
+
+    ASSERT_FALSE(replaced);
+
+    auto res = view.get(nullptr, test_type(42));
+    ASSERT_FALSE(res.has_value());
+}
+
+TEST_F(record_view_test, replace_existing) {
+    test_type val1{42, "foo"};
+    auto replaced = view.insert(nullptr, val1);
+    ASSERT_TRUE(replaced);
+
+    test_type val2{42, "bar"};
+    replaced = view.replace(nullptr, val2);
+    ASSERT_TRUE(replaced);
+
+    auto res = view.get(nullptr, test_type(42));
+    ASSERT_TRUE(res.has_value());
+    EXPECT_EQ(42, res->key);
+    EXPECT_EQ("bar", res->val);
+}
+
+TEST_F(record_view_test, replace_existing_async) {
+    test_type val1{42, "foo"};
+    test_type val2{42, "bar"};
+
+    auto all_done = std::make_shared<std::promise<std::optional<test_type>>>();
+
+    view.insert_async(nullptr, val1, [&](ignite_result<bool> &&res) {
+        if (!check_and_set_operation_error(*all_done, res))
+            return;
+
+        if (!res.value())
+            
all_done->set_exception(std::make_exception_ptr(ignite_error("Expected true on 
insertion")));
+
+        view.replace_async(nullptr, val2, [&](ignite_result<bool> &&res) {
+            if (!check_and_set_operation_error(*all_done, res))
+                return;
+
+            if (!res.value())
+                
all_done->set_exception(std::make_exception_ptr(ignite_error("Expected true on 
replace")));
+
+            view.get_async(
+                nullptr, test_type(42), [&](auto res) { 
result_set_promise(*all_done, std::move(res)); });
+        });
+    });
+
+    auto res = all_done->get_future().get();
+    ASSERT_TRUE(res.has_value());
+    EXPECT_EQ(val2.key, res->key);
+    EXPECT_EQ(val2.val, res->val);
+}
+
+TEST_F(record_view_test, replace_exact_nonexisting) {
+    auto replaced = view.replace(nullptr, test_type(42, "foo"), test_type(42, 
"bar"));
+
+    ASSERT_FALSE(replaced);
+
+    auto res = view.get(nullptr, test_type(42));
+    ASSERT_FALSE(res.has_value());
+}
+
+TEST_F(record_view_test, replace_exact_existing_wrong) {
+    auto inserted = view.insert(nullptr, test_type(42, "foo"));
+    ASSERT_TRUE(inserted);
+
+    auto replaced = view.replace(nullptr, test_type(42, "bar"), test_type(42, 
"baz"));
+    ASSERT_FALSE(replaced);
+
+    auto res = view.get(nullptr, test_type(42));
+    ASSERT_TRUE(res.has_value());
+    EXPECT_EQ(42, res->key);
+    EXPECT_EQ("foo", res->val);
+}
+
+TEST_F(record_view_test, replace_exact_existing_right) {
+    auto inserted = view.insert(nullptr, test_type(42, "foo"));
+    ASSERT_TRUE(inserted);
+
+    auto replaced = view.replace(nullptr, test_type(42, "foo"), test_type(42, 
"baz"));
+    ASSERT_TRUE(replaced);
+
+    auto res = view.get(nullptr, test_type(42));
+    ASSERT_TRUE(res.has_value());
+    EXPECT_EQ(42, res->key);
+    EXPECT_EQ("baz", res->val);
+}
+
+TEST_F(record_view_test, replace_exact_existing_right_async) {
+    test_type val1{42, "foo"};
+    test_type val2{42, "bar"};
+
+    auto all_done = std::make_shared<std::promise<std::optional<test_type>>>();
+
+    view.insert_async(nullptr, val1, [&](ignite_result<bool> &&res) {
+        if (!check_and_set_operation_error(*all_done, res))
+            return;
+
+        if (!res.value())
+            
all_done->set_exception(std::make_exception_ptr(ignite_error("Expected true on 
insertion")));
+
+        view.replace_async(nullptr, val1, val2, [&](ignite_result<bool> &&res) 
{
+            if (!check_and_set_operation_error(*all_done, res))
+                return;
+
+            if (!res.value())
+                
all_done->set_exception(std::make_exception_ptr(ignite_error("Expected true on 
replace")));
+
+            view.get_async(
+                nullptr, test_type(42), [&](auto res) { 
result_set_promise(*all_done, std::move(res)); });
+        });
+    });
+
+    auto res = all_done->get_future().get();
+    ASSERT_TRUE(res.has_value());
+    EXPECT_EQ(val2.key, res->key);
+    EXPECT_EQ(val2.val, res->val);
+}
+
+TEST_F(record_view_test, get_and_replace_nonexisting) {
+    test_type val{42, "foo"};
+    auto res1 = view.get_and_replace(nullptr, val);
+
+    ASSERT_FALSE(res1.has_value());
+
+    auto res2 = view.get(nullptr, test_type(42));
+    ASSERT_FALSE(res2.has_value());
+}
+
+TEST_F(record_view_test, get_and_replace_existing) {
+    test_type val1{42, "foo"};
+    auto inserted = view.insert(nullptr, val1);
+    ASSERT_TRUE(inserted);
+
+    test_type val2{42, "bar"};
+    auto res = view.get_and_replace(nullptr, val2);
+
+    ASSERT_TRUE(res.has_value());
+    EXPECT_EQ(42, res->key);
+    EXPECT_EQ("foo", res->val);
+
+    res = view.get(nullptr, test_type(42));
+    ASSERT_TRUE(res.has_value());
+    EXPECT_EQ(42, res->key);
+    EXPECT_EQ("bar", res->val);
+}
+
+TEST_F(record_view_test, get_and_replace_existing_async) {
+    test_type val1{42, "foo"};
+    test_type val2{42, "bar"};
+
+    auto all_done = std::make_shared<std::promise<std::optional<test_type>>>();
+
+    view.insert_async(nullptr, val1, [&](ignite_result<bool> &&res) {
+        if (!check_and_set_operation_error(*all_done, res))
+            return;
+
+        if (!res.value())
+            
all_done->set_exception(std::make_exception_ptr(ignite_error("Expected true on 
insertion")));
+
+        view.get_and_replace_async(
+            nullptr, val2, [&](auto res) { result_set_promise(*all_done, 
std::move(res)); });
+    });
+
+    auto res = all_done->get_future().get();
+    ASSERT_TRUE(res.has_value());
+    EXPECT_EQ(val1.key, res->key);
+    EXPECT_EQ(val1.val, res->val);
+}
+
+TEST_F(record_view_test, remove_nonexisting) {
+    auto removed = view.remove(nullptr, test_type(1));
+    ASSERT_FALSE(removed);
+
+    auto res = view.get(nullptr, test_type(1));
+    ASSERT_FALSE(res.has_value());
+}
+
+TEST_F(record_view_test, remove_existing) {
+    auto inserted = view.insert(nullptr, test_type(1, "foo"));
+    ASSERT_TRUE(inserted);
+
+    auto removed = view.remove(nullptr, test_type(1));
+    ASSERT_TRUE(removed);
+
+    auto res = view.get(nullptr, test_type(1));
+    ASSERT_FALSE(res.has_value());
+}
+
+TEST_F(record_view_test, remove_existing_async) {
+    auto all_done = std::make_shared<std::promise<bool>>();
+
+    view.insert_async(nullptr, test_type(42, "foo"), [&](ignite_result<bool> 
&&res) {
+        if (!check_and_set_operation_error(*all_done, res))
+            return;
+
+        if (!res.value())
+            
all_done->set_exception(std::make_exception_ptr(ignite_error("Expected true on 
insertion")));
+
+        view.remove_async(
+            nullptr, test_type(42), [&](auto res) { 
result_set_promise(*all_done, std::move(res)); });
+    });
+
+    auto res = all_done->get_future().get();
+    ASSERT_TRUE(res);
+}
+
+TEST_F(record_view_test, remove_exact_nonexisting) {
+    auto res = view.remove_exact(nullptr, test_type(1, "foo"));
+    ASSERT_FALSE(res);
+}
+
+TEST_F(record_view_test, remove_exact_existing) {
+    auto inserted = view.insert(nullptr, test_type(1, "foo"));
+    ASSERT_TRUE(inserted);
+
+    auto removed = view.remove_exact(nullptr, test_type(1));
+    ASSERT_FALSE(removed);
+
+    removed = view.remove_exact(nullptr, test_type(1, "bar"));
+    ASSERT_FALSE(removed);
+
+    removed = view.remove_exact(nullptr, test_type(1, "foo"));
+    ASSERT_TRUE(removed);
+
+    auto res = view.get(nullptr, test_type(1));
+    ASSERT_FALSE(res.has_value());
+}
+
+TEST_F(record_view_test, remove_exact_existing_async) {
+    test_type val{42, "foo"};
+
+    auto all_done = std::make_shared<std::promise<bool>>();
+
+    auto inserted = view.insert(nullptr, val);
+    ASSERT_TRUE(inserted);
+
+    view.remove_exact_async(nullptr, test_type(42), [&](auto res) {
+        if (!check_and_set_operation_error(*all_done, res))
+            return;
+
+        if (res.value())
+            
all_done->set_exception(std::make_exception_ptr(ignite_error("Expected false on 
first remove")));
+
+        view.remove_exact_async(nullptr, test_type(42, "bar"), [&](auto res) {
+            if (!check_and_set_operation_error(*all_done, res))
+                return;
+
+            if (res.value())
+                
all_done->set_exception(std::make_exception_ptr(ignite_error("Expected false on 
second remove")));
+
+            view.remove_exact_async(
+                nullptr, val, [&](auto res) { result_set_promise(*all_done, 
std::move(res)); });
+        });
+    });
+
+    auto res = all_done->get_future().get();
+    ASSERT_TRUE(res);
+}
+
+TEST_F(record_view_test, get_and_remove_nonexisting) {
+    auto removed = view.get_and_remove(nullptr, test_type(42, "foo"));
+    ASSERT_FALSE(removed.has_value());
+
+    auto res = view.get(nullptr, test_type(42));
+    ASSERT_FALSE(res.has_value());
+}
+
+TEST_F(record_view_test, get_and_remove_existing) {
+    auto inserted = view.insert(nullptr, test_type(42, "foo"));
+    ASSERT_TRUE(inserted);
+
+    auto removed = view.get_and_remove(nullptr, test_type(42));
+
+    ASSERT_TRUE(removed.has_value());
+    EXPECT_EQ(42, removed->key);
+    EXPECT_EQ("foo", removed->val);
+
+    auto res = view.get(nullptr, test_type(42));
+    ASSERT_FALSE(res.has_value());
+}
+
+TEST_F(record_view_test, get_and_remove_existing_async) {
+    test_type val1{42, "foo"};
+
+    auto all_done = std::make_shared<std::promise<std::optional<test_type>>>();
+
+    view.insert_async(nullptr, val1, [&](ignite_result<bool> &&res) {
+        if (!check_and_set_operation_error(*all_done, res))
+            return;
+
+        if (!res.value())
+            
all_done->set_exception(std::make_exception_ptr(ignite_error("Expected true on 
insertion")));
+
+        view.get_and_remove_async(
+            nullptr, test_type(42), [&](auto res) { 
result_set_promise(*all_done, std::move(res)); });
+    });
+
+    auto res = all_done->get_future().get();
+    ASSERT_TRUE(res.has_value());
+    EXPECT_EQ(val1.key, res->key);
+    EXPECT_EQ(val1.val, res->val);
+}
+
+TEST_F(record_view_test, remove_all_nonexisting_keys_return_all) {
+    std::vector<test_type> non_existing = {test_type(1), test_type(2)};
+    auto res = view.remove_all(nullptr, non_existing);
+
+    EXPECT_EQ(res.size(), 2);
+
+    // TODO: Key order should be preserved by the server (IGNITE-16004).
+    EXPECT_EQ(2, res[0].key);
+    EXPECT_EQ(1, res[1].key);
+}
+
+TEST_F(record_view_test, remove_all_only_existing) {
+    std::vector<test_type> to_insert = {test_type(1, "foo"), test_type(2, 
"bar")};
+    view.upsert_all(nullptr, to_insert);
+
+    auto res = view.remove_all(nullptr, {test_type(1), test_type(2)});
+
+    EXPECT_TRUE(res.empty());
+}
+
+TEST_F(record_view_test, remove_all_overlapped) {
+    static constexpr std::size_t records_num = 10;
+
+    std::vector<test_type> to_insert;
+    to_insert.reserve(records_num);
+    for (std::int64_t i = 1; i < 1 + std::int64_t(records_num); ++i)
+        to_insert.emplace_back(i, "Val" + std::to_string(i));
+
+    view.upsert_all(nullptr, to_insert);
+
+    std::vector<test_type> to_remove;
+    for (std::int64_t i = 9; i < 13; ++i)
+        to_remove.emplace_back(i);
+
+    auto res = view.remove_all(nullptr, to_remove);
+
+    EXPECT_EQ(res.size(), 2);
+
+    // TODO: Key order should be preserved by the server (IGNITE-16004).
+    EXPECT_EQ(12, res[0].key);
+    EXPECT_EQ(11, res[1].key);
+}
+
+TEST_F(record_view_test, remove_all_empty) {
+    auto res = view.remove_all(nullptr, {});
+    EXPECT_TRUE(res.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).
+    ASSERT_EQ(2, res.size());
+}
+
+TEST_F(record_view_test, remove_all_exact_overlapped) {
+    auto res = view.insert_all(nullptr, {test_type(1, "foo"), test_type(2, 
"bar")});
+
+    ASSERT_TRUE(res.empty());
+
+    res = view.remove_all_exact(nullptr, {test_type(1, "baz"), test_type(2, 
"bar")});
+
+    EXPECT_EQ(res.size(), 1);
+    EXPECT_EQ(1, res.front().key);
+    EXPECT_EQ("baz", res.front().val);
+
+    auto res2 = view.get(nullptr, test_type(2));
+
+    ASSERT_FALSE(res2.has_value());
+}
+
+TEST_F(record_view_test, remove_all_exact_overlapped_async) {
+    auto all_done = std::make_shared<std::promise<std::vector<test_type>>>();
+
+    view.insert_all_async(nullptr, {test_type(1, "foo"), test_type(2, "bar")}, 
[&](auto res) {
+        if (!check_and_set_operation_error(*all_done, res))
+            return;
+
+        if (!res.value().empty())
+            
all_done->set_exception(std::make_exception_ptr(ignite_error("Expected empty 
return on first insertion")));
+
+        view.remove_all_exact_async(nullptr, {test_type(1, "baz"), 
test_type(2, "bar")},
+            [&](auto res) { result_set_promise(*all_done, std::move(res)); });
+    });
+
+    auto res = all_done->get_future().get();
+    EXPECT_EQ(res.size(), 1);
+    EXPECT_EQ(1, res.front().key);
+    EXPECT_EQ("baz", res.front().val);
+}
+
+TEST_F(record_view_test, remove_all_exact_empty) {
+    auto res = view.remove_all_exact(nullptr, {});
+    EXPECT_TRUE(res.empty());
+}
+
+/**
+ * All columns type table mapping (@see 
ignite_runner_suite::TABLE_NAME_ALL_COLUMNS).
+ */
+struct all_fields_type {
+    all_fields_type() = default;
+    explicit all_fields_type(std::int64_t key) : m_key(key) {}
+
+    std::int64_t m_key{0};
+    std::string m_str;
+    std::int8_t m_int8{0};
+    std::int16_t m_int16{0};
+    std::int32_t m_int32{0};
+    std::int64_t m_int64{0};
+    float m_float{.0f};
+    double m_double{.0};
+    uuid m_uuid;
+    ignite_date m_date;
+    bit_array m_bitmask;
+    ignite_time m_time;
+    ignite_time m_time2;
+    ignite_date_time m_datetime;
+    ignite_date_time m_datetime2;
+    ignite_timestamp m_timestamp;
+    ignite_timestamp m_timestamp2;
+    std::vector<std::byte> m_blob;
+    big_decimal m_decimal;
+};
+
+namespace ignite {
+
+template<>
+ignite_tuple convert_to_tuple(all_fields_type &&value) {
+    ignite_tuple tuple;
+
+    tuple.set("key", value.m_key);
+    tuple.set("str", value.m_str);
+    tuple.set("int8", value.m_int8);
+    tuple.set("int16", value.m_int16);
+    tuple.set("int32", value.m_int32);
+    tuple.set("int64", value.m_int64);
+    tuple.set("float", value.m_float);
+    tuple.set("double", value.m_double);
+    tuple.set("uuid", value.m_uuid);
+    tuple.set("date", value.m_date);
+    tuple.set("bitmask", value.m_bitmask);
+    tuple.set("time", value.m_time);
+    tuple.set("time2", value.m_time2);
+    tuple.set("datetime", value.m_datetime);
+    tuple.set("datetime2", value.m_datetime2);
+    tuple.set("timestamp", value.m_timestamp);
+    tuple.set("timestamp2", value.m_timestamp2);
+    tuple.set("blob", value.m_blob);
+    tuple.set("decimal", value.m_decimal);
+
+    return tuple;
+}
+
+template<>
+all_fields_type convert_from_tuple(ignite_tuple&& value) {
+    all_fields_type res;
+
+    res.m_key = value.get<std::int64_t>("key");
+
+    if (value.column_count() > 1) {
+        res.m_str = value.get<std::string>("str");
+        res.m_int8 = value.get<std::int8_t>("int8");
+        res.m_int16 = value.get<std::int16_t>("int16");
+        res.m_int32 = value.get<std::int32_t>("int32");
+        res.m_int64 = value.get<std::int64_t>("int64");
+        res.m_float = value.get<float>("float");
+        res.m_double = value.get<double>("double");
+        res.m_uuid = value.get<uuid>("uuid");
+        res.m_date = value.get<ignite_date>("date");
+        res.m_bitmask = value.get<bit_array>("bitmask");
+        res.m_time = value.get<ignite_time>("time");
+        res.m_time2 = value.get<ignite_time>("time2");
+        res.m_datetime = value.get<ignite_date_time>("datetime");
+        res.m_datetime2 = value.get<ignite_date_time>("datetime2");
+        res.m_timestamp = value.get<ignite_timestamp>("timestamp");
+        res.m_timestamp2 = value.get<ignite_timestamp>("timestamp2");
+        res.m_blob = value.get<std::vector<std::byte>>("blob");
+        res.m_decimal = value.get<big_decimal>("decimal");
+    }
+
+    return res;
+}
+
+}
+
+TEST_F(record_view_test, types_test) {
+    auto table = m_client.get_tables().get_table(TABLE_NAME_ALL_COLUMNS);
+    auto view2 = table->get_record_view<all_fields_type>();
+
+    all_fields_type inserted;
+    inserted.m_key = 42;
+    inserted.m_str = "test";
+    inserted.m_int8 = 1;
+    inserted.m_int16 = 2;
+    inserted.m_int32 = 3;
+    inserted.m_int64 = 4;
+    inserted.m_float = .5f;
+    inserted.m_double = .6f;
+    inserted.m_uuid = {0x123e4567e89b12d3, 0x7456426614174000};
+    inserted.m_date = {2023, 2, 7};
+    inserted.m_bitmask = bit_array{16, true};
+    inserted.m_time = {17, 4, 12, 3543634};
+    inserted.m_time2 = {17, 4, 12, 3543634};
+    inserted.m_datetime = {{2020, 7, 28}, {2, 15, 52, 6349879}};
+    inserted.m_datetime2 = {{2020, 7, 28}, {2, 15, 52, 6349879}};
+    inserted.m_timestamp = {3875238472, 248760634};
+    inserted.m_timestamp2 = {3875238472, 248760634};
+    inserted.m_blob = {std::byte(1), std::byte(2), std::byte(42)};
+    inserted.m_decimal = big_decimal{123456789098765};
+
+    view2.upsert(nullptr, inserted);
+    auto res = view2.get(nullptr, all_fields_type{42});
+
+    ASSERT_TRUE(res.has_value());
+
+    EXPECT_EQ(inserted.m_key, res->m_key);
+    EXPECT_EQ(inserted.m_str, res->m_str);
+    EXPECT_EQ(inserted.m_int8, res->m_int8);
+    EXPECT_EQ(inserted.m_int16, res->m_int16);
+    EXPECT_EQ(inserted.m_int32, res->m_int32);
+    EXPECT_EQ(inserted.m_int64, res->m_int64);
+    EXPECT_EQ(inserted.m_float, res->m_float);
+    EXPECT_EQ(inserted.m_double, res->m_double);
+    EXPECT_EQ(inserted.m_uuid, res->m_uuid);
+    EXPECT_EQ(inserted.m_date, res->m_date);
+    EXPECT_EQ(inserted.m_bitmask, res->m_bitmask);
+    EXPECT_EQ(inserted.m_time, res->m_time);
+    EXPECT_EQ(inserted.m_datetime, res->m_datetime);
+    EXPECT_EQ(inserted.m_timestamp, res->m_timestamp);
+    EXPECT_EQ(inserted.m_blob, res->m_blob);
+    EXPECT_EQ(inserted.m_decimal, res->m_decimal);
+
+    EXPECT_EQ(ignite_time(17, 4, 12), res->m_time2);
+    EXPECT_EQ(ignite_date_time({2020, 7, 28}, {2, 15, 52, 6000000}), 
res->m_datetime2);
+    EXPECT_EQ(ignite_timestamp(3875238472, 248700000), res->m_timestamp2);
+}
+
diff --git a/modules/platforms/cpp/tests/client-test/tables_test.cpp 
b/modules/platforms/cpp/tests/client-test/tables_test.cpp
index a56d562fa4..73cf73404d 100644
--- a/modules/platforms/cpp/tests/client-test/tables_test.cpp
+++ b/modules/platforms/cpp/tests/client-test/tables_test.cpp
@@ -45,7 +45,7 @@ TEST_F(tables_test, tables_get_table) {
 
     auto table = tables.get_table("tbl1");
     ASSERT_TRUE(table.has_value());
-    EXPECT_EQ(table->name(), "tbl1");
+    EXPECT_EQ(table->get_name(), "tbl1");
 }
 
 TEST_F(tables_test, tables_get_table_async_promises) {
@@ -70,7 +70,7 @@ TEST_F(tables_test, tables_get_table_async_promises) {
 
     auto table = tablePromise->get_future().get();
     ASSERT_TRUE(table.has_value());
-    EXPECT_EQ(table->name(), "tbl1");
+    EXPECT_EQ(table->get_name(), "tbl1");
 }
 
 TEST_F(tables_test, tables_get_table_async_callbacks) {
@@ -113,9 +113,9 @@ TEST_F(tables_test, tables_get_table_async_callbacks) {
                 
operation2->set_exception(std::make_exception_ptr(ignite_error("Table should 
not be null")));
                 return;
             }
-            if (table->name() != "tbl1") {
+            if (table->get_name() != "tbl1") {
                 operation2->set_exception(
-                    std::make_exception_ptr(ignite_error("Table has unexpected 
name: " + table->name())));
+                    std::make_exception_ptr(ignite_error("Table has unexpected 
name: " + table->get_name())));
                 return;
             }
 
@@ -140,7 +140,7 @@ TEST_F(tables_test, tables_get_tables) {
     auto tables = tablesApi.get_tables();
     ASSERT_GT(tables.size(), 0);
 
-    auto it = std::find_if(tables.begin(), tables.end(), [](auto &table) { 
return table.name() == "TBL1"; });
+    auto it = std::find_if(tables.begin(), tables.end(), [](auto &table) { 
return table.get_name() == "TBL1"; });
 
     ASSERT_NE(it, tables.end());
 }
@@ -159,7 +159,7 @@ TEST_F(tables_test, tables_get_tables_async_promises) {
     auto tables = tablesPromise->get_future().get();
     ASSERT_GT(tables.size(), 0);
 
-    auto it = std::find_if(tables.begin(), tables.end(), [](auto &table) { 
return table.name() == "TBL1"; });
+    auto it = std::find_if(tables.begin(), tables.end(), [](auto &table) { 
return table.get_name() == "TBL1"; });
 
     ASSERT_NE(it, tables.end());
 }
diff --git a/modules/platforms/cpp/tests/client-test/transactions_test.cpp 
b/modules/platforms/cpp/tests/client-test/transactions_test.cpp
index 135d52d1bc..9b70c4b012 100644
--- a/modules/platforms/cpp/tests/client-test/transactions_test.cpp
+++ b/modules/platforms/cpp/tests/client-test/transactions_test.cpp
@@ -68,7 +68,7 @@ TEST_F(transactions_test, empty_transaction_commit) {
 }
 
 TEST_F(transactions_test, commit_updates_data) {
-    auto record_view = 
m_client.get_tables().get_table("tbl1")->record_binary_view();
+    auto record_view = 
m_client.get_tables().get_table("tbl1")->get_record_binary_view();
 
     auto tx = m_client.get_transactions().begin();
 
@@ -86,7 +86,7 @@ TEST_F(transactions_test, commit_updates_data) {
 }
 
 TEST_F(transactions_test, rollback_does_not_update_data) {
-    auto record_view = 
m_client.get_tables().get_table("tbl1")->record_binary_view();
+    auto record_view = 
m_client.get_tables().get_table("tbl1")->get_record_binary_view();
 
     auto tx = m_client.get_transactions().begin();
 
@@ -101,7 +101,7 @@ TEST_F(transactions_test, rollback_does_not_update_data) {
 }
 
 TEST_F(transactions_test, destruction_does_not_update_data) {
-    auto record_view = 
m_client.get_tables().get_table("tbl1")->record_binary_view();
+    auto record_view = 
m_client.get_tables().get_table("tbl1")->get_record_binary_view();
 
     {
         auto tx = m_client.get_transactions().begin();
@@ -116,7 +116,7 @@ TEST_F(transactions_test, destruction_does_not_update_data) 
{
 }
 
 TEST_F(transactions_test, non_committed_data_visible_for_tx) {
-    auto record_view = 
m_client.get_tables().get_table("tbl1")->record_binary_view();
+    auto record_view = 
m_client.get_tables().get_table("tbl1")->get_record_binary_view();
 
     auto tx = m_client.get_transactions().begin();
 
@@ -132,7 +132,7 @@ TEST_F(transactions_test, 
non_committed_data_visible_for_tx) {
 }
 
 TEST_F(transactions_test, sql_commit) {
-    auto record_view = 
m_client.get_tables().get_table("tbl1")->record_binary_view();
+    auto record_view = 
m_client.get_tables().get_table("tbl1")->get_record_binary_view();
 
     auto tx = m_client.get_transactions().begin();
 
@@ -150,7 +150,7 @@ TEST_F(transactions_test, sql_commit) {
 }
 
 TEST_F(transactions_test, sql_rollback) {
-    auto record_view = 
m_client.get_tables().get_table("tbl1")->record_binary_view();
+    auto record_view = 
m_client.get_tables().get_table("tbl1")->get_record_binary_view();
 
     auto tx = m_client.get_transactions().begin();
 
@@ -193,7 +193,7 @@ TEST_F(transactions_test, rollback_after_rollback_works) {
 }
 
 TEST_F(transactions_test, record_view_upsert_all) {
-    auto record_view = 
m_client.get_tables().get_table("tbl1")->record_binary_view();
+    auto record_view = 
m_client.get_tables().get_table("tbl1")->get_record_binary_view();
 
     auto tx = m_client.get_transactions().begin();
 
@@ -215,7 +215,7 @@ TEST_F(transactions_test, record_view_upsert_all) {
 }
 
 TEST_F(transactions_test, record_view_get_and_upsert) {
-    auto record_view = 
m_client.get_tables().get_table("tbl1")->record_binary_view();
+    auto record_view = 
m_client.get_tables().get_table("tbl1")->get_record_binary_view();
 
     auto tx = m_client.get_transactions().begin();
 
@@ -239,7 +239,7 @@ TEST_F(transactions_test, record_view_get_and_upsert) {
 }
 
 TEST_F(transactions_test, record_view_insert) {
-    auto record_view = 
m_client.get_tables().get_table("tbl1")->record_binary_view();
+    auto record_view = 
m_client.get_tables().get_table("tbl1")->get_record_binary_view();
 
     auto tx = m_client.get_transactions().begin();
 
@@ -261,7 +261,7 @@ TEST_F(transactions_test, record_view_insert) {
 }
 
 TEST_F(transactions_test, record_view_insert_all) {
-    auto record_view = 
m_client.get_tables().get_table("tbl1")->record_binary_view();
+    auto record_view = 
m_client.get_tables().get_table("tbl1")->get_record_binary_view();
 
     auto tx = m_client.get_transactions().begin();
 
@@ -283,7 +283,7 @@ TEST_F(transactions_test, record_view_insert_all) {
 }
 
 TEST_F(transactions_test, record_view_replace) {
-    auto record_view = 
m_client.get_tables().get_table("tbl1")->record_binary_view();
+    auto record_view = 
m_client.get_tables().get_table("tbl1")->get_record_binary_view();
 
     auto tx = m_client.get_transactions().begin();
 
@@ -302,7 +302,7 @@ TEST_F(transactions_test, record_view_replace) {
 }
 
 TEST_F(transactions_test, record_view_replace_exact) {
-    auto record_view = 
m_client.get_tables().get_table("tbl1")->record_binary_view();
+    auto record_view = 
m_client.get_tables().get_table("tbl1")->get_record_binary_view();
 
     auto tx = m_client.get_transactions().begin();
 
@@ -321,7 +321,7 @@ TEST_F(transactions_test, record_view_replace_exact) {
 }
 
 TEST_F(transactions_test, record_view_get_and_replace) {
-    auto record_view = 
m_client.get_tables().get_table("tbl1")->record_binary_view();
+    auto record_view = 
m_client.get_tables().get_table("tbl1")->get_record_binary_view();
 
     auto tx = m_client.get_transactions().begin();
 
@@ -343,7 +343,7 @@ TEST_F(transactions_test, record_view_get_and_replace) {
 }
 
 TEST_F(transactions_test, record_view_remove) {
-    auto record_view = 
m_client.get_tables().get_table("tbl1")->record_binary_view();
+    auto record_view = 
m_client.get_tables().get_table("tbl1")->get_record_binary_view();
 
     auto value0 = get_tuple(42, "Lorem ipsum");
     record_view.insert(nullptr, value0);
@@ -359,7 +359,7 @@ TEST_F(transactions_test, record_view_remove) {
 }
 
 TEST_F(transactions_test, record_view_remove_exact) {
-    auto record_view = 
m_client.get_tables().get_table("tbl1")->record_binary_view();
+    auto record_view = 
m_client.get_tables().get_table("tbl1")->get_record_binary_view();
 
     auto value0 = get_tuple(42, "Lorem ipsum");
     record_view.insert(nullptr, value0);
@@ -375,7 +375,7 @@ TEST_F(transactions_test, record_view_remove_exact) {
 }
 
 TEST_F(transactions_test, record_view_get_and_remove) {
-    auto record_view = 
m_client.get_tables().get_table("tbl1")->record_binary_view();
+    auto record_view = 
m_client.get_tables().get_table("tbl1")->get_record_binary_view();
 
     auto value0 = get_tuple(42, "Lorem ipsum");
     record_view.insert(nullptr, value0);
@@ -399,7 +399,7 @@ TEST_F(transactions_test, record_view_get_and_remove) {
 }
 
 TEST_F(transactions_test, record_view_remove_all) {
-    auto record_view = 
m_client.get_tables().get_table("tbl1")->record_binary_view();
+    auto record_view = 
m_client.get_tables().get_table("tbl1")->get_record_binary_view();
 
     auto value0 = get_tuple(42, "Lorem ipsum");
     record_view.insert(nullptr, value0);
@@ -415,7 +415,7 @@ TEST_F(transactions_test, record_view_remove_all) {
 }
 
 TEST_F(transactions_test, record_view_remove_all_exact) {
-    auto record_view = 
m_client.get_tables().get_table("tbl1")->record_binary_view();
+    auto record_view = 
m_client.get_tables().get_table("tbl1")->get_record_binary_view();
 
     auto value0 = get_tuple(42, "Lorem ipsum");
     record_view.insert(nullptr, value0);

Reply via email to