| /* |
| * 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> |
| #include <thread> |
| |
| using namespace ignite; |
| |
| #define TEST_TABLE_NAME "tbl1" |
| |
| /** |
| * Test suite. |
| */ |
| class transactions_test : public ignite_runner_suite { |
| protected: |
| /** |
| * Set up for every test. |
| */ |
| void SetUp() override { |
| clear_table1(); |
| |
| ignite_client_configuration cfg{get_node_addrs()}; |
| cfg.set_logger(get_logger()); |
| |
| m_client = ignite_client::start(cfg, std::chrono::seconds(30)); |
| } |
| |
| /** |
| * Tear down for every test. |
| */ |
| void TearDown() override { clear_table1(); } |
| |
| /** Ignite client. */ |
| ignite_client m_client; |
| }; |
| |
| TEST_F(transactions_test, empty_transaction_rollback) { |
| auto api = m_client.get_transactions(); |
| |
| auto tx = api.begin(); |
| tx.rollback(); |
| } |
| |
| TEST_F(transactions_test, empty_transaction_commit) { |
| auto api = m_client.get_transactions(); |
| |
| auto tx = api.begin(); |
| tx.commit(); |
| } |
| |
| TEST_F(transactions_test, commit_updates_data) { |
| auto record_view = m_client.get_tables().get_table(TABLE_1)->get_record_binary_view(); |
| |
| auto tx = m_client.get_transactions().begin(); |
| |
| auto expected = get_tuple(42, "Lorem ipsum"); |
| record_view.upsert(&tx, expected); |
| |
| tx.commit(); |
| |
| auto actual = record_view.get(nullptr, get_tuple(42)); |
| |
| ASSERT_TRUE(actual.has_value()); |
| EXPECT_EQ(2, actual->column_count()); |
| EXPECT_EQ(expected.get<int64_t>("key"), actual->get<int64_t>("key")); |
| EXPECT_EQ(expected.get<std::string>("val"), actual->get<std::string>("val")); |
| } |
| |
| TEST_F(transactions_test, rollback_does_not_update_data) { |
| auto record_view = m_client.get_tables().get_table(TABLE_1)->get_record_binary_view(); |
| |
| auto tx = m_client.get_transactions().begin(); |
| |
| auto value0 = get_tuple(42, "Lorem ipsum"); |
| record_view.upsert(&tx, value0); |
| |
| tx.rollback(); |
| |
| auto actual = record_view.get(nullptr, get_tuple(42)); |
| |
| ASSERT_FALSE(actual.has_value()); |
| } |
| |
| // TODO https://issues.apache.org/jira/browse/IGNITE-22057 |
| TEST_F(transactions_test, DISABLED_destruction_does_not_update_data) { |
| auto record_view = m_client.get_tables().get_table(TABLE_1)->get_record_binary_view(); |
| |
| { |
| auto tx = m_client.get_transactions().begin(); |
| |
| auto value0 = get_tuple(42, "Lorem ipsum"); |
| record_view.upsert(&tx, value0); |
| } |
| |
| auto actual = record_view.get(nullptr, get_tuple(42)); |
| |
| ASSERT_FALSE(actual.has_value()); |
| } |
| |
| TEST_F(transactions_test, non_committed_data_visible_for_tx) { |
| auto record_view = m_client.get_tables().get_table(TABLE_1)->get_record_binary_view(); |
| |
| auto tx = m_client.get_transactions().begin(); |
| |
| auto value0 = get_tuple(42, "Lorem ipsum"); |
| record_view.upsert(&tx, value0); |
| |
| auto value_in = record_view.get(&tx, get_tuple(42)); |
| |
| ASSERT_TRUE(value_in.has_value()); |
| EXPECT_EQ(2, value_in->column_count()); |
| EXPECT_EQ(value0.get<int64_t>("key"), value_in->get<int64_t>("key")); |
| EXPECT_EQ(value0.get<std::string>("val"), value_in->get<std::string>("val")); |
| } |
| |
| TEST_F(transactions_test, sql_commit) { |
| auto record_view = m_client.get_tables().get_table(TABLE_1)->get_record_binary_view(); |
| |
| auto tx = m_client.get_transactions().begin(); |
| |
| m_client.get_sql().execute(&tx, nullptr, {"INSERT INTO " + std::string(TABLE_1) + " VALUES (?, ?)"}, |
| {std::int64_t(42), std::string("Lorem ipsum")}); |
| |
| tx.commit(); |
| |
| auto value = record_view.get(nullptr, get_tuple(42)); |
| |
| ASSERT_TRUE(value.has_value()); |
| EXPECT_EQ(2, value->column_count()); |
| EXPECT_EQ(42, value->get<int64_t>("key")); |
| EXPECT_EQ("Lorem ipsum", value->get<std::string>("val")); |
| } |
| |
| TEST_F(transactions_test, sql_rollback) { |
| auto record_view = m_client.get_tables().get_table(TABLE_1)->get_record_binary_view(); |
| |
| auto tx = m_client.get_transactions().begin(); |
| |
| m_client.get_sql().execute(&tx, nullptr, {"INSERT INTO " + std::string(TABLE_1) + " VALUES (?, ?)"}, |
| {std::int64_t(42), std::string("Lorem ipsum")}); |
| |
| tx.rollback(); |
| |
| auto value = record_view.get(nullptr, get_tuple(42)); |
| |
| ASSERT_FALSE(value.has_value()); |
| } |
| |
| TEST_F(transactions_test, rollback_after_commit_works) { |
| auto tx = m_client.get_transactions().begin(); |
| |
| tx.commit(); |
| tx.rollback(); |
| } |
| |
| TEST_F(transactions_test, commit_after_commit_works) { |
| auto tx = m_client.get_transactions().begin(); |
| |
| tx.commit(); |
| tx.commit(); |
| } |
| |
| TEST_F(transactions_test, commit_after_rollback_works) { |
| auto tx = m_client.get_transactions().begin(); |
| |
| tx.rollback(); |
| tx.commit(); |
| } |
| |
| TEST_F(transactions_test, rollback_after_rollback_works) { |
| auto tx = m_client.get_transactions().begin(); |
| |
| tx.rollback(); |
| tx.rollback(); |
| } |
| |
| TEST_F(transactions_test, record_view_upsert_all) { |
| auto record_view = m_client.get_tables().get_table(TABLE_1)->get_record_binary_view(); |
| |
| auto tx = m_client.get_transactions().begin(); |
| |
| auto value0 = get_tuple(42, "Lorem ipsum"); |
| record_view.upsert_all(&tx, {value0}); |
| |
| auto values1 = record_view.get_all(&tx, {get_tuple(42)}); |
| |
| ASSERT_EQ(1, values1.size()); |
| EXPECT_EQ(2, values1.front()->column_count()); |
| EXPECT_EQ(value0.get<int64_t>("key"), values1.front()->get<int64_t>("key")); |
| EXPECT_EQ(value0.get<std::string>("val"), values1.front()->get<std::string>("val")); |
| |
| tx.rollback(); |
| |
| auto values2 = record_view.get_all(nullptr, {get_tuple(42)}); |
| |
| ASSERT_EQ(1, values2.size()); |
| EXPECT_FALSE(values2.front().has_value()); |
| } |
| |
| TEST_F(transactions_test, record_view_get_and_upsert) { |
| auto record_view = m_client.get_tables().get_table(TABLE_1)->get_record_binary_view(); |
| |
| auto tx = m_client.get_transactions().begin(); |
| |
| auto value0 = get_tuple(42, "Lorem ipsum"); |
| auto value1 = record_view.get_and_upsert(&tx, value0); |
| |
| ASSERT_FALSE(value1.has_value()); |
| |
| auto value2 = record_view.get_and_upsert(&tx, get_tuple(42, "12")); |
| |
| ASSERT_TRUE(value2.has_value()); |
| EXPECT_EQ(2, value2->column_count()); |
| EXPECT_EQ(value0.get<int64_t>("key"), value2->get<int64_t>("key")); |
| EXPECT_EQ(value0.get<std::string>("val"), value2->get<std::string>("val")); |
| |
| tx.rollback(); |
| |
| auto value3 = record_view.get_and_upsert(nullptr, value0); |
| |
| ASSERT_FALSE(value3.has_value()); |
| } |
| |
| TEST_F(transactions_test, record_view_insert) { |
| auto record_view = m_client.get_tables().get_table(TABLE_1)->get_record_binary_view(); |
| |
| auto tx = m_client.get_transactions().begin(); |
| |
| auto value0 = get_tuple(42, "Lorem ipsum"); |
| record_view.insert(&tx, value0); |
| |
| auto value1 = record_view.get(&tx, get_tuple(42)); |
| |
| ASSERT_TRUE(value1.has_value()); |
| EXPECT_EQ(2, value1->column_count()); |
| EXPECT_EQ(value0.get<int64_t>("key"), value1->get<int64_t>("key")); |
| EXPECT_EQ(value0.get<std::string>("val"), value1->get<std::string>("val")); |
| |
| tx.rollback(); |
| |
| auto value2 = record_view.get(nullptr, get_tuple(42)); |
| |
| ASSERT_FALSE(value2.has_value()); |
| } |
| |
| TEST_F(transactions_test, record_view_insert_all) { |
| auto record_view = m_client.get_tables().get_table(TABLE_1)->get_record_binary_view(); |
| |
| auto tx = m_client.get_transactions().begin(); |
| |
| auto value0 = get_tuple(42, "Lorem ipsum"); |
| record_view.insert_all(&tx, {value0}); |
| |
| auto values1 = record_view.get_all(&tx, {get_tuple(42)}); |
| |
| ASSERT_EQ(1, values1.size()); |
| EXPECT_EQ(2, values1.front()->column_count()); |
| EXPECT_EQ(value0.get<int64_t>("key"), values1.front()->get<int64_t>("key")); |
| EXPECT_EQ(value0.get<std::string>("val"), values1.front()->get<std::string>("val")); |
| |
| tx.rollback(); |
| |
| auto values2 = record_view.get_all(nullptr, {get_tuple(42)}); |
| |
| ASSERT_EQ(1, values2.size()); |
| EXPECT_FALSE(values2.front().has_value()); |
| } |
| |
| TEST_F(transactions_test, record_view_replace) { |
| auto record_view = m_client.get_tables().get_table(TABLE_1)->get_record_binary_view(); |
| |
| auto tx = m_client.get_transactions().begin(); |
| |
| auto value0 = get_tuple(42, "Lorem ipsum"); |
| record_view.insert(&tx, value0); |
| |
| auto success1 = record_view.replace(&tx, get_tuple(42, "13")); |
| |
| ASSERT_TRUE(success1); |
| |
| tx.rollback(); |
| |
| auto success2 = record_view.replace(nullptr, get_tuple(42, "13")); |
| |
| ASSERT_FALSE(success2); |
| } |
| |
| TEST_F(transactions_test, record_view_replace_exact) { |
| auto record_view = m_client.get_tables().get_table(TABLE_1)->get_record_binary_view(); |
| |
| auto tx = m_client.get_transactions().begin(); |
| |
| auto value0 = get_tuple(42, "Lorem ipsum"); |
| record_view.insert(&tx, value0); |
| |
| auto success1 = record_view.replace(&tx, value0, get_tuple(42, "13")); |
| |
| ASSERT_TRUE(success1); |
| |
| tx.rollback(); |
| |
| auto success2 = record_view.replace(nullptr, value0, get_tuple(42, "13")); |
| |
| ASSERT_FALSE(success2); |
| } |
| |
| TEST_F(transactions_test, record_view_get_and_replace) { |
| auto record_view = m_client.get_tables().get_table(TABLE_1)->get_record_binary_view(); |
| |
| auto tx = m_client.get_transactions().begin(); |
| |
| auto value0 = get_tuple(42, "Lorem ipsum"); |
| record_view.insert(&tx, value0); |
| |
| auto value1 = record_view.get_and_replace(&tx, get_tuple(42, "12")); |
| |
| ASSERT_TRUE(value1.has_value()); |
| EXPECT_EQ(2, value1->column_count()); |
| EXPECT_EQ(value0.get<int64_t>("key"), value1->get<int64_t>("key")); |
| EXPECT_EQ(value0.get<std::string>("val"), value1->get<std::string>("val")); |
| |
| tx.rollback(); |
| |
| auto value2 = record_view.get_and_replace(nullptr, value0); |
| |
| ASSERT_FALSE(value2.has_value()); |
| } |
| |
| TEST_F(transactions_test, record_view_remove) { |
| auto record_view = m_client.get_tables().get_table(TABLE_1)->get_record_binary_view(); |
| |
| auto value0 = get_tuple(42, "Lorem ipsum"); |
| record_view.insert(nullptr, value0); |
| |
| auto tx = m_client.get_transactions().begin(); |
| auto success1 = record_view.remove(&tx, get_tuple(42)); |
| |
| ASSERT_TRUE(success1); |
| tx.rollback(); |
| |
| auto success2 = record_view.remove(nullptr, get_tuple(42)); |
| ASSERT_TRUE(success2); |
| } |
| |
| TEST_F(transactions_test, record_view_remove_exact) { |
| auto record_view = m_client.get_tables().get_table(TABLE_1)->get_record_binary_view(); |
| |
| auto value0 = get_tuple(42, "Lorem ipsum"); |
| record_view.insert(nullptr, value0); |
| |
| auto tx = m_client.get_transactions().begin(); |
| auto success1 = record_view.remove(&tx, value0); |
| |
| ASSERT_TRUE(success1); |
| tx.rollback(); |
| |
| auto success2 = record_view.remove(nullptr, value0); |
| ASSERT_TRUE(success2); |
| } |
| |
| TEST_F(transactions_test, record_view_get_and_remove) { |
| auto record_view = m_client.get_tables().get_table(TABLE_1)->get_record_binary_view(); |
| |
| auto value0 = get_tuple(42, "Lorem ipsum"); |
| record_view.insert(nullptr, value0); |
| |
| auto tx = m_client.get_transactions().begin(); |
| auto value1 = record_view.get_and_remove(&tx, get_tuple(42)); |
| |
| ASSERT_TRUE(value1.has_value()); |
| EXPECT_EQ(2, value1->column_count()); |
| EXPECT_EQ(value0.get<int64_t>("key"), value1->get<int64_t>("key")); |
| EXPECT_EQ(value0.get<std::string>("val"), value1->get<std::string>("val")); |
| |
| tx.rollback(); |
| |
| auto value2 = record_view.get_and_remove(nullptr, get_tuple(42)); |
| |
| ASSERT_TRUE(value2.has_value()); |
| EXPECT_EQ(2, value2->column_count()); |
| EXPECT_EQ(value0.get<int64_t>("key"), value2->get<int64_t>("key")); |
| EXPECT_EQ(value0.get<std::string>("val"), value2->get<std::string>("val")); |
| } |
| |
| TEST_F(transactions_test, record_view_remove_all) { |
| auto record_view = m_client.get_tables().get_table(TABLE_1)->get_record_binary_view(); |
| |
| auto value0 = get_tuple(42, "Lorem ipsum"); |
| record_view.insert(nullptr, value0); |
| |
| auto tx = m_client.get_transactions().begin(); |
| auto values1 = record_view.remove_all(&tx, {get_tuple(42)}); |
| |
| ASSERT_TRUE(values1.empty()); |
| tx.rollback(); |
| |
| auto values2 = record_view.remove_all(nullptr, {get_tuple(42)}); |
| ASSERT_TRUE(values2.empty()); |
| } |
| |
| TEST_F(transactions_test, record_view_remove_all_exact) { |
| auto record_view = m_client.get_tables().get_table(TABLE_1)->get_record_binary_view(); |
| |
| auto value0 = get_tuple(42, "Lorem ipsum"); |
| record_view.insert(nullptr, value0); |
| |
| auto tx = m_client.get_transactions().begin(); |
| auto values1 = record_view.remove_all(&tx, {value0}); |
| |
| ASSERT_TRUE(values1.empty()); |
| tx.rollback(); |
| |
| auto values2 = record_view.remove_all(nullptr, {value0}); |
| ASSERT_TRUE(values2.empty()); |
| } |
| |
| TEST_F(transactions_test, tx_successful_when_timeout_does_not_exceed) { |
| auto record_view = m_client.get_tables().get_table(TABLE_1)->get_record_binary_view(); |
| |
| int64_t key = 42; |
| std::string val = "Lorem ipsum"; |
| |
| auto tx_opts = transaction_options().set_timeout_millis(10'000L).set_read_only(false); |
| auto tx = m_client.get_transactions().begin(tx_opts); |
| |
| record_view.insert(&tx, get_tuple(key, val)); |
| |
| tx.commit(); |
| |
| auto value = record_view.get(nullptr, get_tuple(42)); |
| |
| ASSERT_TRUE(value.has_value()); |
| EXPECT_EQ(key, value->get<int64_t>(KEY_COLUMN)); |
| EXPECT_EQ(val, value->get<std::string>(VAL_COLUMN)); |
| } |
| |
| |
| TEST_F(transactions_test, tx_failed_when_timeout_exceeds) { |
| auto record_view = m_client.get_tables().get_table(TABLE_1)->get_record_binary_view(); |
| |
| auto timeout_ms = 1'000L; |
| |
| int64_t key = 42; |
| std::string val = "Lorem ipsum"; |
| auto tx_opts = transaction_options() |
| .set_timeout_millis(timeout_ms) |
| .set_read_only(false); |
| |
| auto tx = m_client.get_transactions().begin(tx_opts); |
| |
| record_view.insert(&tx, get_tuple(key, val)); |
| |
| std::this_thread::sleep_for(std::chrono::milliseconds{timeout_ms * 2}); |
| |
| EXPECT_THROW( |
| { |
| try { |
| // TODO change to check tx.commit when IGNITE-24233 is implemented |
| // tx.commit(); |
| auto rec = record_view.get(&tx, get_tuple(key)); |
| } catch (const ignite_error& e) { |
| EXPECT_EQ(e.get_status_code(), error::code::TX_ALREADY_FINISHED_WITH_TIMEOUT); |
| throw; |
| } |
| }, |
| ignite_error); |
| } |