diff --git a/src/include/main/client_context.h b/src/include/main/client_context.h index d2c859d4b10..008c70fc623 100644 --- a/src/include/main/client_context.h +++ b/src/include/main/client_context.h @@ -164,7 +164,7 @@ class KUZU_API ClientContext { const std::unordered_map>& inputParams); std::unique_ptr executeAndAutoCommitIfNecessaryNoLock( - PreparedStatement* preparedStatement, uint32_t planIdx = 0u, bool requiredNexTx = true, + PreparedStatement* preparedStatement, uint32_t planIdx = 0u, std::optional queryID = std::nullopt); bool canExecuteWriteQuery(); diff --git a/src/main/client_context.cpp b/src/main/client_context.cpp index 64167a06ce0..ed98a1cccc3 100644 --- a/src/main/client_context.cpp +++ b/src/main/client_context.cpp @@ -298,8 +298,8 @@ std::unique_ptr ClientContext::query(std::string_view query, for (auto& statement : parsedStatements) { auto preparedStatement = prepareNoLock(statement, enumerateAllPlans /* enumerate all plans */, encodedJoin, false /*requireNewTx*/); - auto currentQueryResult = executeAndAutoCommitIfNecessaryNoLock(preparedStatement.get(), 0u, - false /*requiredNexTx*/, queryID); + auto currentQueryResult = + executeAndAutoCommitIfNecessaryNoLock(preparedStatement.get(), 0u, queryID); if (!lastResult) { // first result of the query queryResult = std::move(currentQueryResult); @@ -442,7 +442,7 @@ std::unique_ptr ClientContext::executeWithParams(PreparedStatement* KU_ASSERT(preparedStatement->parsedStatement != nullptr); auto rebindPreparedStatement = prepareNoLock(preparedStatement->parsedStatement, false, "", false, preparedStatement->parameterMap); - return executeAndAutoCommitIfNecessaryNoLock(rebindPreparedStatement.get(), 0u, false, queryID); + return executeAndAutoCommitIfNecessaryNoLock(rebindPreparedStatement.get(), 0u, queryID); } void ClientContext::bindParametersNoLock(PreparedStatement* preparedStatement, @@ -461,12 +461,11 @@ void ClientContext::bindParametersNoLock(PreparedStatement* preparedStatement, } std::unique_ptr ClientContext::executeAndAutoCommitIfNecessaryNoLock( - PreparedStatement* preparedStatement, uint32_t planIdx, bool requiredNexTx, - std::optional queryID) { + PreparedStatement* preparedStatement, uint32_t planIdx, std::optional queryID) { if (!preparedStatement->isSuccess()) { return queryResultWithError(preparedStatement->errMsg); } - if (preparedStatement->parsedStatement->requireTx() && requiredNexTx && getTx() == nullptr) { + if (preparedStatement->parsedStatement->requireTx() && getTx() == nullptr) { this->transactionContext->beginAutoTransaction(preparedStatement->isReadOnly()); } this->resetActiveQuery(); @@ -586,7 +585,7 @@ void ClientContext::runQuery(std::string query) { for (auto& statement : parsedStatements) { auto preparedStatement = prepareNoLock(statement, false, "", false); auto currentQueryResult = - executeAndAutoCommitIfNecessaryNoLock(preparedStatement.get(), 0u, false); + executeAndAutoCommitIfNecessaryNoLock(preparedStatement.get(), 0u); if (!currentQueryResult->isSuccess()) { throw ConnectionException(currentQueryResult->errMsg); } diff --git a/src/storage/store/update_info.cpp b/src/storage/store/update_info.cpp index 0b2c4b30cad..f634cc83f09 100644 --- a/src/storage/store/update_info.cpp +++ b/src/storage/store/update_info.cpp @@ -106,7 +106,7 @@ VectorUpdateInfo& UpdateInfo::getOrCreateVectorInfo(const Transaction* transacti // Same transaction. KU_ASSERT(current->version >= Transaction::START_TRANSACTION_ID); info = current; - } else if (current->version >= transaction->getStartTS()) { + } else if (current->version > transaction->getStartTS()) { // Potentially there can be conflicts. `current` can be uncommitted transaction (version // is transaction ID) or committed transaction started after this transaction. for (auto i = 0u; i < current->numRowsUpdated; i++) { diff --git a/test/storage/CMakeLists.txt b/test/storage/CMakeLists.txt index f9fcc5ddc4a..310933923c4 100644 --- a/test/storage/CMakeLists.txt +++ b/test/storage/CMakeLists.txt @@ -3,3 +3,4 @@ add_kuzu_test(compression_test compression_test.cpp) add_kuzu_test(local_hash_index_test local_hash_index_test.cpp) add_kuzu_test(buffer_manager_test buffer_manager_test.cpp) add_kuzu_test(rel_scan_test rel_scan_test.cpp) +add_kuzu_test(node_update_test node_update_test.cpp) \ No newline at end of file diff --git a/test/storage/node_update_test.cpp b/test/storage/node_update_test.cpp new file mode 100644 index 00000000000..08311af8f60 --- /dev/null +++ b/test/storage/node_update_test.cpp @@ -0,0 +1,31 @@ +#include "graph_test/graph_test.h" + +namespace kuzu { +namespace testing { + +class NodeUpdateTest : public EmptyDBTest { +protected: + void SetUp() override { + EmptyDBTest::SetUp(); + createDBAndConn(); + } + + void TearDown() override { EmptyDBTest::TearDown(); } +}; + +TEST_F(NodeUpdateTest, UpdateSameRow) { + ASSERT_TRUE(conn->query("CREATE NODE TABLE IF NOT EXISTS Product (item STRING, price INT64, " + "PRIMARY KEY (item))") + ->isSuccess()); + ASSERT_TRUE(conn->query("CREATE (n:Product {item: 'watch'}) SET n.price = 100")->isSuccess()); + ASSERT_TRUE( + conn->query("MATCH (n:Product) WHERE n.item = 'watch' SET n.price = 200")->isSuccess()); + ASSERT_TRUE( + conn->query("MATCH (n:Product) WHERE n.item = 'watch' SET n.price = 300")->isSuccess()); + const auto res = conn->query("MATCH (n:Product) RETURN n.price"); + ASSERT_TRUE(res->isSuccess() && res->hasNext()); + ASSERT_EQ(res->getNext()->getValue(0)->val.int64Val, 300); +} + +} // namespace testing +} // namespace kuzu