diff --git a/src/cache_invalidation_optimizer.cpp b/src/cache_invalidation_optimizer.cpp index 075eadf..6a98fae 100644 --- a/src/cache_invalidation_optimizer.cpp +++ b/src/cache_invalidation_optimizer.cpp @@ -19,6 +19,17 @@ CacheInvalidationOptimizer::CacheInvalidationOptimizer() { optimize_function = OptimizeFunction; } +static bool MergeCanRewriteRowGroups(const LogicalMergeInto &merge) { + for (const auto &entry : merge.actions) { + for (const auto &action : entry.second) { + if (action->action_type == MergeActionType::MERGE_DELETE || action->update_is_del_and_insert) { + return true; + } + } + } + return false; +} + void CacheInvalidationOptimizer::WalkPlanForDML(ClientContext &context, unique_ptr &op) { // Recurse into children first for (auto &child : op->children) { @@ -30,10 +41,7 @@ void CacheInvalidationOptimizer::WalkPlanForDML(ClientContext &context, unique_p auto &del = op->Cast(); auto table_oid = del.table.oid; - // Copy the row_id expression; it will be resolved during column binding resolution - auto row_id_expr = del.expressions[0]->Copy(); - - auto invalidator = make_uniq(table_oid, std::move(row_id_expr)); + auto invalidator = make_uniq(table_oid, CacheInvalidatorMode::CLEAR_TABLE); invalidator->children = std::move(del.children); del.children.clear(); del.children.push_back(std::move(invalidator)); @@ -72,9 +80,13 @@ void CacheInvalidationOptimizer::WalkPlanForDML(ClientContext &context, unique_p auto &duck_table = merge.table.Cast(); auto pre_insert_rows = duck_table.GetStorage().GetTotalRows(); - auto row_id_col = merge.row_id_start; - - auto invalidator = make_uniq(table_oid, row_id_col, pre_insert_rows); + unique_ptr invalidator; + if (MergeCanRewriteRowGroups(merge)) { + invalidator = make_uniq(table_oid, CacheInvalidatorMode::CLEAR_TABLE); + } else { + auto row_id_col = merge.row_id_start; + invalidator = make_uniq(table_oid, row_id_col, pre_insert_rows); + } invalidator->children = std::move(merge.children); merge.children.clear(); merge.children.push_back(std::move(invalidator)); diff --git a/src/include/logical_cache_invalidator.hpp b/src/include/logical_cache_invalidator.hpp index 5b3f92f..a091d51 100644 --- a/src/include/logical_cache_invalidator.hpp +++ b/src/include/logical_cache_invalidator.hpp @@ -11,7 +11,11 @@ struct LogicalCacheInvalidator : public LogicalExtensionOperator { idx_t row_id_column_index; // for ROW_ID mode idx_t pre_insert_row_count; // for INSERT/MERGE modes - // For DELETE/UPDATE: pass the row_id expression to be resolved during column binding. + // For DELETE/TRUNCATE-style rewrites: clear the entire table cache. + LogicalCacheInvalidator(idx_t table_oid, CacheInvalidatorMode mode); + + // For row-id-based invalidation (e.g. UPDATE): pass the row_id expression to be + // resolved during column binding. LogicalCacheInvalidator(idx_t table_oid, unique_ptr row_id_expr); // For INSERT: count rows and compute affected range. diff --git a/src/include/physical_cache_invalidator.hpp b/src/include/physical_cache_invalidator.hpp index 7759f9a..04a29cb 100644 --- a/src/include/physical_cache_invalidator.hpp +++ b/src/include/physical_cache_invalidator.hpp @@ -5,8 +5,10 @@ namespace duckdb { enum class CacheInvalidatorMode : uint8_t { - // DELETE/UPDATE: observe row IDs at row_id_column_index + // UPDATE-style invalidation: observe row IDs at row_id_column_index ROW_ID, + // DELETE/TRUNCATE: clear all cache entries for the table + CLEAR_TABLE, // INSERT: count rows and compute affected range from pre_insert_row_count INSERT, // MERGE: hybrid — track row IDs for matched rows (UPDATE/DELETE) and count diff --git a/src/include/query_condition_cache_state.hpp b/src/include/query_condition_cache_state.hpp index 1abc017..7376fe8 100644 --- a/src/include/query_condition_cache_state.hpp +++ b/src/include/query_condition_cache_state.hpp @@ -116,6 +116,9 @@ class ConditionCacheStore : public ObjectCacheEntry { idx_t RemoveRowGroupsForTable(ClientContext &context, idx_t table_oid, const unordered_set &row_group_indices); + // Remove all cache entries for a table. Returns count of entries removed. + idx_t RemoveEntriesForTable(ClientContext &context, idx_t table_oid); + // Check if any entries exist for a given table OID bool HasEntriesForTable(ClientContext &context, idx_t table_oid); diff --git a/src/logical_cache_invalidator.cpp b/src/logical_cache_invalidator.cpp index bf7df77..84dbd9f 100644 --- a/src/logical_cache_invalidator.cpp +++ b/src/logical_cache_invalidator.cpp @@ -7,7 +7,11 @@ namespace duckdb { -// DELETE/UPDATE mode: row_id expression stored in expressions[0], resolved during column binding +LogicalCacheInvalidator::LogicalCacheInvalidator(idx_t table_oid, CacheInvalidatorMode mode) + : table_oid(table_oid), mode(mode), row_id_column_index(0), pre_insert_row_count(0) { +} + +// Row-id mode: row_id expression stored in expressions[0], resolved during column binding LogicalCacheInvalidator::LogicalCacheInvalidator(idx_t table_oid, unique_ptr row_id_expr) : table_oid(table_oid), mode(CacheInvalidatorMode::ROW_ID), row_id_column_index(0), pre_insert_row_count(0) { expressions.push_back(std::move(row_id_expr)); @@ -88,6 +92,9 @@ unique_ptr CacheInvalidatorOperatorExtension::Deserial unique_ptr result; switch (mode) { + case CacheInvalidatorMode::CLEAR_TABLE: + result = make_uniq(oid, mode); + break; case CacheInvalidatorMode::ROW_ID: { unique_ptr row_id_expr; if (!exprs.empty()) { diff --git a/src/physical_cache_invalidator.cpp b/src/physical_cache_invalidator.cpp index 82b9b45..cf9a45f 100644 --- a/src/physical_cache_invalidator.cpp +++ b/src/physical_cache_invalidator.cpp @@ -44,6 +44,8 @@ OperatorResultType PhysicalCacheInvalidator::Execute(ExecutionContext &context, auto &invalidator_state = gstate.Cast(); switch (mode) { + case CacheInvalidatorMode::CLEAR_TABLE: + break; case CacheInvalidatorMode::ROW_ID: CollectRowGroups(input.data[row_id_column_index], input.size(), invalidator_state, /*track_nulls=*/false); break; @@ -66,6 +68,12 @@ OperatorFinalResultType PhysicalCacheInvalidator::OperatorFinalize(Pipeline &pip OperatorFinalizeInput &input) const { auto &invalidator_state = input.global_state.Cast(); + if (mode == CacheInvalidatorMode::CLEAR_TABLE) { + auto store = ConditionCacheStore::GetOrCreate(context); + store->RemoveEntriesForTable(context, table_oid); + return OperatorFinalResultType::FINISHED; + } + // For INSERT and MERGE modes: compute row groups from the inserted row range if (invalidator_state.inserted_row_count > 0) { idx_t first_rg = pre_insert_row_count / DEFAULT_ROW_GROUP_SIZE; @@ -98,6 +106,9 @@ InsertionOrderPreservingMap PhysicalCacheInvalidator::ParamsToString() c InsertionOrderPreservingMap result; result["Table OID"] = to_string(table_oid); switch (mode) { + case CacheInvalidatorMode::CLEAR_TABLE: + result["Mode"] = "CLEAR_TABLE"; + break; case CacheInvalidatorMode::ROW_ID: result["Mode"] = "ROW_ID"; result["Row ID Column"] = to_string(row_id_column_index); diff --git a/src/query_condition_cache_optimizer.cpp b/src/query_condition_cache_optimizer.cpp index ac8914b..39bccf2 100644 --- a/src/query_condition_cache_optimizer.cpp +++ b/src/query_condition_cache_optimizer.cpp @@ -48,8 +48,7 @@ void QueryConditionCacheOptimizer::PreOptimizeFunction(OptimizerExtensionInput & if (!IsSettingEnabled(input.context)) { return; } - auto query_state = - input.context.registered_state->GetOrCreate("qcc_optimizer_state"); + auto query_state = input.context.registered_state->GetOrCreate("qcc_optimizer_state"); query_state->cache_apply_pending.clear(); try { PreOptimizeWalk(input.context, plan, /*inside_dml=*/false, *query_state); diff --git a/src/query_condition_cache_state.cpp b/src/query_condition_cache_state.cpp index db14627..59102e3 100644 --- a/src/query_condition_cache_state.cpp +++ b/src/query_condition_cache_state.cpp @@ -40,8 +40,6 @@ void RowGroupFilter::MergeFrom(const RowGroupFilter &other) { // ------- CONDITION_CACHE_ENTRY ------- optional_idx ConditionCacheEntry::GetEstimatedCacheMemory() const { - // Rough estimate: each RowGroupFilter is ~BITVECTOR_ARRAY_SIZE * 8 bytes - // Plus overhead for the map structure idx_t estimated_size = sizeof(ConditionCacheEntry); estimated_size += bitvectors.size() * (sizeof(idx_t) + sizeof(RowGroupFilter) + 32); // map overhead return optional_idx(estimated_size); @@ -133,6 +131,28 @@ idx_t ConditionCacheStore::RemoveRowGroupsForTable(ClientContext &context, idx_t return removed_count; } +idx_t ConditionCacheStore::RemoveEntriesForTable(ClientContext &context, idx_t table_oid) { + auto &cache = ObjectCache::GetObjectCache(context); + + auto index = cache.Get(MakeFilterKeyIndexKey(table_oid)); + if (!index) { + return 0; + } + + auto filter_keys = index->GetAll(); + idx_t removed_count = 0; + for (auto &filter_key : filter_keys) { + CacheKey key {table_oid, filter_key}; + string cache_key = MakeCacheKeyString(key); + if (cache.Get(cache_key)) { + cache.Delete(cache_key); + ++removed_count; + } + } + cache.Delete(MakeFilterKeyIndexKey(table_oid)); + return removed_count; +} + bool ConditionCacheStore::HasEntriesForTable(ClientContext &context, idx_t table_oid) { auto &cache = ObjectCache::GetObjectCache(context); auto index = cache.Get(MakeFilterKeyIndexKey(table_oid)); diff --git a/test/sql/condition_cache_invalidation.test b/test/sql/condition_cache_invalidation.test index 4d0f9e8..0dc8a49 100644 --- a/test/sql/condition_cache_invalidation.test +++ b/test/sql/condition_cache_invalidation.test @@ -20,15 +20,15 @@ SELECT * FROM condition_cache_info('t', 'val = 42'); ---- 5 -# DELETE a row in RG0 should invalidate only RG0 +# DELETE is treated as a delete-like rewrite, so the whole table cache is cleared statement ok DELETE FROM t WHERE id = 42; -# Only 4 row groups remain cached (RG0 invalidated) +# No cache entry remains for this predicate query I SELECT * FROM condition_cache_info('t', 'val = 42'); ---- -4 +0 # Rebuild restores all 5 row groups query I @@ -57,7 +57,7 @@ SELECT status FROM condition_cache_build('t', 'val = 42'); Cache Built: 245/245 vectors, 5/5 row groups # INSERT appends to the last row group — only that RG is invalidated -# Build a predicate that only hits RG0 +# Build a predicate that only matches RG0, but caches all row groups query I SELECT status FROM condition_cache_build('t', 'id < 3000'); ---- @@ -71,7 +71,8 @@ SELECT * FROM condition_cache_info('t', 'id < 3000'); statement ok INSERT INTO t VALUES (999999, 0); -# RG0 cache (for 'id < 3000') should be preserved since insert only affects the last RG +# The known-empty last row group is invalidated, but RG0 and the other known-empty +# row groups remain cached query I SELECT * FROM condition_cache_info('t', 'id < 3000'); ---- @@ -109,7 +110,7 @@ SELECT * FROM condition_cache_info('t2', 'val = 5'); 0 # ============================================================================ -# TRUNCATE should not crash and stale entries should be harmless +# TRUNCATE should clear the table cache and remain correct # ============================================================================ statement ok @@ -128,8 +129,13 @@ SELECT * FROM condition_cache_info('t_trunc', 'val = 42'); statement ok TRUNCATE t_trunc; -# After truncate, cache entry may still exist but queries must still be correct -# (empty table returns 0 rows regardless of stale cache) +# Cache entry is cleared +query I +SELECT * FROM condition_cache_info('t_trunc', 'val = 42'); +---- +0 + +# Empty table still returns 0 rows query I SELECT count(*) FROM t_trunc WHERE val = 42; ---- @@ -158,3 +164,36 @@ query I SELECT count(*) FROM t_drop WHERE val = 5; ---- 100 + +# ============================================================================ +# CHECKPOINT vacuum must not leave stale cache entries that prune moved rows +# ============================================================================ + +statement ok +SET use_query_condition_cache = true; + +statement ok +CREATE TABLE t_checkpoint AS +SELECT i AS id, CASE WHEN i >= 245760 THEN 1 ELSE 0 END AS val +FROM range(368640) t(i); + +query I +SELECT status FROM condition_cache_build('t_checkpoint', 'val=1'); +---- +Cache Built: 60/180 vectors, 1/3 row groups + +query I +SELECT count(*) FROM t_checkpoint WHERE val = 1; +---- +122880 + +statement ok +DELETE FROM t_checkpoint WHERE id < 122880; + +statement ok +CHECKPOINT; + +query I +SELECT count(*) FROM t_checkpoint WHERE val = 1; +---- +122880 diff --git a/test/unittest/test_bitvector.cpp b/test/unittest/test_bitvector.cpp index 638fd67..d5756e6 100644 --- a/test/unittest/test_bitvector.cpp +++ b/test/unittest/test_bitvector.cpp @@ -45,5 +45,16 @@ TEST_CASE("RowGroupFilter - basic operations", "[bitvector]") { REQUIRE(bv.VectorHasRows(5)); REQUIRE_FALSE(bv.VectorHasRows(6)); } + + SECTION("merge combines matching vectors") { + RowGroupFilter lhs({2}); + RowGroupFilter rhs({5}); + + lhs.MergeFrom(rhs); + + REQUIRE(lhs.VectorHasRows(2)); + REQUIRE(lhs.VectorHasRows(5)); + REQUIRE_FALSE(lhs.VectorHasRows(3)); + } } } // namespace duckdb diff --git a/test/unittest/test_cache_invalidation.cpp b/test/unittest/test_cache_invalidation.cpp index aa08ee5..2842588 100644 --- a/test/unittest/test_cache_invalidation.cpp +++ b/test/unittest/test_cache_invalidation.cpp @@ -64,6 +64,26 @@ TEST_CASE("RemoveRowGroupsForTable - basic operations", "[invalidation]") { REQUIRE(found2->bitvectors.count(0) == 1); } + SECTION("removes all entries for a table") { + auto entry1 = make_shared_ptr(); + entry1->bitvectors[0]; + store->Upsert(context, {1, "val > 5"}, entry1); + + auto entry2 = make_shared_ptr(); + entry2->bitvectors[0].SetVector(0); + store->Upsert(context, {1, "val < 10"}, entry2); + + auto entry3 = make_shared_ptr(); + entry3->bitvectors[0].SetVector(0); + store->Upsert(context, {2, "val = 42"}, entry3); + + auto removed = store->RemoveEntriesForTable(context, 1); + REQUIRE(removed == 2); + REQUIRE(store->Lookup(context, {1, "val > 5"}) == nullptr); + REQUIRE(store->Lookup(context, {1, "val < 10"}) == nullptr); + REQUIRE(store->Lookup(context, {2, "val = 42"}) != nullptr); + } + SECTION("removes from multiple entries for the same table") { auto entry1 = make_shared_ptr(); entry1->bitvectors[0].SetVector(0); diff --git a/test/unittest/test_logical_cache_invalidator.cpp b/test/unittest/test_logical_cache_invalidator.cpp index 55fdd00..d438cba 100644 --- a/test/unittest/test_logical_cache_invalidator.cpp +++ b/test/unittest/test_logical_cache_invalidator.cpp @@ -5,6 +5,16 @@ namespace duckdb { +TEST_CASE("LogicalCacheInvalidator - CLEAR_TABLE mode constructor", "[logical_invalidator]") { + LogicalCacheInvalidator op(7, CacheInvalidatorMode::CLEAR_TABLE); + + REQUIRE(op.table_oid == 7); + REQUIRE(op.mode == CacheInvalidatorMode::CLEAR_TABLE); + REQUIRE(op.row_id_column_index == 0); + REQUIRE(op.pre_insert_row_count == 0); + REQUIRE(op.expressions.empty()); +} + TEST_CASE("LogicalCacheInvalidator - ROW_ID mode constructor", "[logical_invalidator]") { auto row_id_expr = make_uniq(LogicalType::BIGINT, 3); LogicalCacheInvalidator op(42, std::move(row_id_expr)); diff --git a/test/unittest/test_optimizer_invalidation.cpp b/test/unittest/test_optimizer_invalidation.cpp index 3640c02..e0a2894 100644 --- a/test/unittest/test_optimizer_invalidation.cpp +++ b/test/unittest/test_optimizer_invalidation.cpp @@ -9,7 +9,7 @@ namespace duckdb { -TEST_CASE("Optimizer invalidation - DELETE removes affected row groups from cache", "[invalidation][optimizer]") { +TEST_CASE("Optimizer invalidation - DELETE clears table cache", "[invalidation][optimizer]") { DuckDB db(nullptr); db.LoadStaticExtension(); Connection con(db); @@ -35,19 +35,13 @@ TEST_CASE("Optimizer invalidation - DELETE removes affected row groups from cach auto del_result = con.Query("DELETE FROM t WHERE id < 100"); REQUIRE_FALSE(del_result->HasError()); - // The selective entry (only had RG0) should be fully removed + // DELETE-style rewrites clear all cache entries for the table. auto after_del = LookupEntry(con, table_oid, "id < 3000"); REQUIRE(after_del == nullptr); - // The broad entry should have RG0 removed but RGs 1-4 preserved + // The broad entry should also be cleared. auto broad_after = LookupEntry(con, table_oid, "val = 42"); - REQUIRE(broad_after != nullptr); - REQUIRE(broad_after->bitvectors.size() == 4); - REQUIRE(broad_after->bitvectors.count(0) == 0); - REQUIRE(broad_after->bitvectors.count(1) == 1); - REQUIRE(broad_after->bitvectors.count(2) == 1); - REQUIRE(broad_after->bitvectors.count(3) == 1); - REQUIRE(broad_after->bitvectors.count(4) == 1); + REQUIRE(broad_after == nullptr); } TEST_CASE("Optimizer invalidation - UPDATE removes affected row groups from cache", "[invalidation][optimizer]") { @@ -100,11 +94,15 @@ TEST_CASE("Optimizer invalidation - INSERT only invalidates affected row groups" auto ins_result = con.Query("INSERT INTO t VALUES (999999, 0)"); REQUIRE_FALSE(ins_result->HasError()); - // Selective entry (RG0 only) should be preserved — INSERT didn't touch RG0 + // Selective entry preserves RG0 and the known-empty middle row groups. auto selective_after = LookupEntry(con, table_oid, "id < 3000"); REQUIRE(selective_after != nullptr); REQUIRE(selective_after->bitvectors.size() == 1); REQUIRE(selective_after->bitvectors.count(0) == 1); + REQUIRE(selective_after->bitvectors.count(1) == 1); + REQUIRE(selective_after->bitvectors.count(2) == 1); + REQUIRE(selective_after->bitvectors.count(3) == 1); + REQUIRE(selective_after->bitvectors.count(4) == 0); // Broad entry should have RG4 invalidated, RGs 0-3 preserved auto broad_after = LookupEntry(con, table_oid, "val = 42"); @@ -196,7 +194,8 @@ TEST_CASE("Optimizer invalidation - cross-table isolation", "[invalidation][opti REQUIRE(t1_after->bitvectors.size() == 5); } -TEST_CASE("Optimizer invalidation - DELETE across multiple row groups", "[invalidation][optimizer]") { +TEST_CASE("Optimizer invalidation - DELETE across multiple row groups clears table cache", + "[invalidation][optimizer]") { DuckDB db(nullptr); db.LoadStaticExtension(); Connection con(db); @@ -214,15 +213,9 @@ TEST_CASE("Optimizer invalidation - DELETE across multiple row groups", "[invali auto del_result = con.Query("DELETE FROM t WHERE id < 100 OR (id >= 250000 AND id < 250100)"); REQUIRE_FALSE(del_result->HasError()); - // RG0 and RG2 should be invalidated + // DELETE-style rewrites clear all cache entries for the table. auto after = LookupEntry(con, table_oid, "val = 42"); - REQUIRE(after != nullptr); - REQUIRE(after->bitvectors.size() == 3); - REQUIRE(after->bitvectors.count(0) == 0); - REQUIRE(after->bitvectors.count(1) == 1); - REQUIRE(after->bitvectors.count(2) == 0); - REQUIRE(after->bitvectors.count(3) == 1); - REQUIRE(after->bitvectors.count(4) == 1); + REQUIRE(after == nullptr); } TEST_CASE("Optimizer invalidation - MERGE invalidates matched and inserted row groups", "[invalidation][optimizer]") { @@ -262,7 +255,28 @@ TEST_CASE("Optimizer invalidation - MERGE invalidates matched and inserted row g REQUIRE(after->bitvectors.count(4) == 0); } -TEST_CASE("Optimizer invalidation - TRUNCATE does not crash with stale cache", "[invalidation][optimizer]") { +TEST_CASE("Optimizer invalidation - MERGE DELETE clears table cache", "[invalidation][optimizer]") { + DuckDB db(nullptr); + db.LoadStaticExtension(); + Connection con(db); + + con.Query("CREATE TABLE dst AS SELECT i AS id, i % 100 AS val FROM range(500000) t(i)"); + auto table_oid = GetTableOid(con, "dst"); + + BuildCache(con, "dst", "val = 42"); + auto entry = LookupEntry(con, table_oid, "val = 42"); + REQUIRE(entry != nullptr); + REQUIRE(entry->bitvectors.size() == 5); + + con.Query("CREATE TABLE src (id INTEGER)"); + con.Query("INSERT INTO src VALUES (200)"); + + auto merge_result = con.Query("MERGE INTO dst USING src ON dst.id = src.id WHEN MATCHED THEN DELETE"); + REQUIRE_FALSE(merge_result->HasError()); + REQUIRE(LookupEntry(con, table_oid, "val = 42") == nullptr); +} + +TEST_CASE("Optimizer invalidation - TRUNCATE clears table cache", "[invalidation][optimizer]") { DuckDB db(nullptr); db.LoadStaticExtension(); Connection con(db); @@ -275,11 +289,12 @@ TEST_CASE("Optimizer invalidation - TRUNCATE does not crash with stale cache", " REQUIRE(entry != nullptr); REQUIRE(entry->bitvectors.size() == 5); - // TRUNCATE removes all rows — stale cache entries remain but are harmless + // TRUNCATE lowers to DELETE, so the whole table cache should be cleared. auto trunc_result = con.Query("TRUNCATE t"); REQUIRE_FALSE(trunc_result->HasError()); + REQUIRE(LookupEntry(con, table_oid, "val = 42") == nullptr); - // Query on empty table should return 0 rows regardless of stale cache + // Query on empty table should still return 0 rows. auto count_result = con.Query("SELECT count(*) FROM t WHERE val = 42"); REQUIRE_FALSE(count_result->HasError()); auto chunk = count_result->Fetch(); @@ -346,7 +361,7 @@ TEST_CASE("Optimizer invalidation - INSERT into partial tail row group", "[inval // --- Plan injection tests (verify optimizer injects PhysicalCacheInvalidator with correct fields) --- -TEST_CASE("Optimizer invalidation - injects ROW_ID invalidator for DELETE", "[invalidation][optimizer]") { +TEST_CASE("Optimizer invalidation - injects CLEAR_TABLE invalidator for DELETE", "[invalidation][optimizer]") { DuckDB db(nullptr); db.LoadStaticExtension(); Connection con(db); @@ -358,7 +373,7 @@ TEST_CASE("Optimizer invalidation - injects ROW_ID invalidator for DELETE", "[in REQUIRE_FALSE(prepared->HasError()); auto *invalidator = FindInvalidator(prepared->data->physical_plan->Root()); REQUIRE(invalidator); - REQUIRE(invalidator->mode == CacheInvalidatorMode::ROW_ID); + REQUIRE(invalidator->mode == CacheInvalidatorMode::CLEAR_TABLE); REQUIRE(invalidator->table_oid == table_oid); } diff --git a/test/unittest/test_physical_cache_invalidator.cpp b/test/unittest/test_physical_cache_invalidator.cpp index f47e178..7d080e1 100644 --- a/test/unittest/test_physical_cache_invalidator.cpp +++ b/test/unittest/test_physical_cache_invalidator.cpp @@ -36,7 +36,7 @@ TEST_CASE("PhysicalCacheInvalidator - ParamsToString contains mode and table OID REQUIRE(invalidator); auto params = invalidator->ParamsToString(); REQUIRE(params["Table OID"] == to_string(table_oid)); - REQUIRE(params["Mode"] == "ROW_ID"); + REQUIRE(params["Mode"] == "CLEAR_TABLE"); } TEST_CASE("PhysicalCacheInvalidator - ParallelOperator returns true", "[physical_invalidator]") {