================================================================================ Changed-lines coverage summary ================================================================================ Denominator: lines added/modified by this PR in C/C++ source files that LCOV considers coverable (excludes blank lines, braces, comments, header-only declarations, and error-path noise such as `LOGICAL_ERROR`, `UNREACHABLE()`, `abort()`). Numerator: of those coverable lines, the number actually executed by the test suite during this coverage run. PR changed C/C++ lines covered by tests: 92.71% (1768/1907) Uncovered changed code (with context): ================================================================================ src/Interpreters/InterpreterDeleteQuery.cpp ================================================================================ --- uncovered block 94-95 --- 92 | auto uk_metadata = table->getInMemoryMetadataPtr(getContext(), false); 93 | if (uk_metadata->hasUniqueKey()) >> 94 | throw Exception(ErrorCodes::SUPPORT_IS_DISABLED, >> 95 | "DELETE on UNIQUE KEY tables is not supported inside a Replicated database"); 96 | 97 | auto guard = DatabaseCatalog::instance().getDDLGuard(table_id.database_name, table_id.table_name, database.get()); --- uncovered block 118-120 --- 116 | /// explicitly rather than silently ignore it. 117 | if (!delete_query.cluster.empty()) >> 118 | throw Exception(ErrorCodes::BAD_ARGUMENTS, >> 119 | "DELETE ... ON CLUSTER is not supported on UNIQUE KEY tables " >> 120 | "(synchronous single-node path only)."); 121 | 122 | if (auto * merge_tree = dynamic_cast(table.get())) --- uncovered block 129-131 --- 127 | /// ReplicatedMergeTree and other variants: only the non-replicated 128 | /// `StorageMergeTree` synchronous DELETE path is implemented. >> 129 | throw Exception(ErrorCodes::SUPPORT_IS_DISABLED, >> 130 | "DELETE on UNIQUE KEY tables is only supported on StorageMergeTree (got {})", >> 131 | table->getName()); 132 | } 133 | ================================================================================ src/Planner/findParallelReplicasQuery.cpp ================================================================================ --- uncovered block 70-70 --- 68 | 69 | if (storageHasUniqueKey(storage, context)) >> 70 | return false; 71 | 72 | if (!storage->supportsReplication() && !settings[Setting::parallel_replicas_for_non_replicated_merge_tree]) --- uncovered block 99-99 --- 97 | /// so guard the unwrapped MergeTree table against UNIQUE KEY here too. 98 | if (storageHasUniqueKey(underlying_storage, context)) >> 99 | return false; 100 | 101 | return true; ================================================================================ src/Processors/QueryPlan/ReadFromMergeTree.cpp ================================================================================ --- uncovered block 3725-3725 --- 3723 | == result.column_names_to_read.end()) 3724 | { >> 3725 | result.column_names_to_read.insert(result.column_names_to_read.begin(), "_part_offset"); 3726 | } 3727 | } --- uncovered block 4936-4937 --- 4934 | /// distributed case. Drop once the snapshot is serialized into the plan. 4935 | if (getStorageMetadata()->hasUniqueKey()) >> 4936 | throw Exception(ErrorCodes::SUPPORT_IS_DISABLED, >> 4937 | "distributed query plan is not supported on UNIQUE KEY tables"); 4938 | 4939 | /// Serializing the STREAM modifier is not implemented yet, so reject it instead of silently ================================================================================ src/Storages/MergeTree/IMergeTreeDataPart.cpp ================================================================================ --- uncovered block 1581-1581 --- 1579 | /// enumerated); avoids forcing a manifest parse during enumeration. 1580 | if (getDataPartStorage().existsFile(UniqueKeyTxn::UniqueKeyManifest::FILE_NAME)) >> 1581 | result.emplace(UniqueKeyTxn::UniqueKeyManifest::FILE_NAME); 1582 | 1583 | return result; ================================================================================ src/Storages/MergeTree/MergeTreeData.cpp ================================================================================ --- uncovered block 1824-1824 --- 1822 | { 1823 | if (parts.empty()) >> 1824 | return 0; 1825 | 1826 | auto metadata_snapshot = getInMemoryMetadataPtr(local_context, false); --- uncovered block 1874-1874 --- 1872 | res += part.data_part->rows_count; 1873 | if (table_has_uk) >> 1874 | matched_parts.push_back(part.data_part); 1875 | } 1876 | } --- uncovered block 1883-1884 --- 1881 | if (table_has_uk) 1882 | { >> 1883 | size_t dead = getDeadRowsForUniqueKey(matched_parts, uk_partition_snapshots); >> 1884 | res = (dead >= res) ? 0 : res - dead; 1885 | } 1886 | return res; --- uncovered block 2726-2726 --- 2724 | /// `PartitionTxnController::recover` per partition. 2725 | if (supportsReplication()) >> 2726 | return; 2727 | 2728 | /// Each tmp dir is wrapped in a `DataPartStorageOnDiskFull` so all manifest --- uncovered block 2740-2745 --- 2738 | /// needs a write to reconcile, the per-tmp recover() below fails closed. 2739 | if (disk->isBroken()) >> 2740 | throw Exception(ErrorCodes::CORRUPTED_DATA, >> 2741 | "UNIQUE KEY txn-state recovery: disk '{}' is broken and may hold unrecovered " >> 2742 | "tmp marker/bitmap state; aborting startup to stay fail-closed.", >> 2743 | disk->getName()); 2744 | if (!disk->existsDirectory(relative_data_path)) >> 2745 | continue; 2746 | 2747 | for (auto it = disk->iterateDirectory(relative_data_path); it->isValid(); it->next()) --- uncovered block 2754-2766 --- 2752 | 2753 | /// Strip `tmp__` — find the second `_` after `tmp_`. >> 2754 | const auto op_sep = basename.find('_', 4); /// skip "tmp_" >> 2755 | if (op_sep == std::string::npos || op_sep + 1 >= basename.size()) >> 2756 | continue; 2757 | >> 2758 | const std::string part_name = basename.substr(op_sep + 1); >> 2759 | auto info = MergeTreePartInfo::tryParsePartName(part_name, format_version); >> 2760 | if (!info) >> 2761 | continue; 2762 | >> 2763 | auto volume = std::make_shared("volume_uk_recovery_" + disk->getName(), disk); >> 2764 | auto tmp_storage = std::make_shared( >> 2765 | std::move(volume), relative_data_path, basename); >> 2766 | tmp_storages_by_partition[info->getPartitionId()].emplace_back(std::move(tmp_storage)); 2767 | } 2768 | } --- uncovered block 2773-2773 --- 2771 | return; 2772 | >> 2773 | for (auto & [partition_id, tmp_storages] : tmp_storages_by_partition) 2774 | { 2775 | /// Recover each tmp dir in isolation so one bad dir doesn't --- uncovered block 2780-2788 --- 2778 | /// `tmp_insert_`/`tmp_merge_` dir, or a pre-manifest crash with no UK 2779 | /// sidecars to reconcile), so skip it without a fail-closed throw. >> 2780 | for (const auto & tmp_storage : tmp_storages) 2781 | { >> 2782 | if (!UniqueKeyTxn::UniqueKeyManifest::exists(*tmp_storage)) >> 2783 | continue; >> 2784 | try 2785 | { >> 2786 | getOrCreateTxnController(partition_id).recover({tmp_storage}); 2787 | } >> 2788 | catch (...) 2789 | { 2790 | /// Fail closed: a manifest EXISTS but recover() threw. Recovery --- uncovered block 2796-2801 --- 2794 | /// attach/startup fails (we run in the StorageMergeTree ctor) 2795 | /// rather than silently continuing with torn sidecar state. >> 2796 | throw Exception(ErrorCodes::CORRUPTED_DATA, >> 2797 | "UNIQUE KEY txn-state recovery failed for tmp dir '{}' (partition '{}') on table {}: {}. " >> 2798 | "Startup aborted to stay fail-closed. Inspect the tmp dir and its target part's " >> 2799 | "delete_bitmap_.rbm sidecars, remove the stale aborted-commit bitmap, then re-attach.", >> 2800 | tmp_storage->getFullPath(), partition_id, getStorageID().getNameForLogs(), >> 2801 | getCurrentExceptionMessage(/*with_stacktrace=*/false)); 2802 | } 2803 | } --- uncovered block 5208-5212 --- 5206 | 5207 | if (command.type == MutationCommand::DROP_COLUMN && command.clear) >> 5208 | throw Exception(ErrorCodes::SUPPORT_IS_DISABLED, >> 5209 | "ALTER TABLE ... CLEAR COLUMN `{}` is not supported on tables with UNIQUE KEY: " >> 5210 | "it rewrites the part via the full mutation path, dropping the delete-bitmap sidecars " >> 5211 | "and resurrecting deleted rows.", >> 5212 | command.column_name); 5213 | 5214 | /// These commands rebuild whole parts (the full read+rewrite mutation path) and --- uncovered block 6173-6173 --- 6171 | { 6172 | if (!part) >> 6173 | continue; 6174 | const String & partition_id = part->info.getPartitionId(); 6175 | --- uncovered block 6187-6187 --- 6185 | const auto & snapshot = it->second; 6186 | if (!snapshot) >> 6187 | continue; 6188 | 6189 | /// Snapshot-consistency at the pinned csn C: a part newer than C is --- uncovered block 6638-6639 --- 6636 | /// implemented. 6637 | if (supportsReplication()) >> 6638 | throw Exception(ErrorCodes::NOT_IMPLEMENTED, >> 6639 | "SharedMergeTree UNIQUE KEY transaction strategies are not implemented"); 6640 | auto state = UniqueKeyTxn::MakeLocalStrategies(*this, partition_id); 6641 | auto [inserted_it, _] = unique_key_txn_controllers.emplace(partition_id, std::move(state)); ================================================================================ src/Storages/MergeTree/MergeTreeSelectProcessor.cpp ================================================================================ --- uncovered block 280-295 --- 278 | /// regression, not a runtime condition to tolerate. Throw rather than skip. 279 | if (!res.block.has("_part_offset")) >> 280 | throw Exception( 281 | ErrorCodes::LOGICAL_ERROR, >> 282 | "UNIQUE KEY read has a non-empty delete bitmap but `_part_offset` is missing from the " >> 283 | "post-PREWHERE block; the delete-bitmap filter cannot be applied"); 284 | 285 | const auto & part_offset_col = res.block.getByName("_part_offset").column; 286 | const auto * offsets = typeid_cast(part_offset_col.get()); 287 | if (!offsets || offsets->size() != res.row_count) >> 288 | throw Exception( 289 | ErrorCodes::LOGICAL_ERROR, >> 290 | "UNIQUE KEY read has a non-empty delete bitmap but `_part_offset` is malformed " >> 291 | "(type {}, size {}, expected ColumnUInt64 of size {}); the delete-bitmap filter " >> 292 | "cannot be applied", >> 293 | part_offset_col->getName(), >> 294 | part_offset_col->size(), >> 295 | res.row_count); 296 | 297 | const auto & offset_data = offsets->getData(); --- uncovered block 329-329 --- 327 | && !res.unmatched_mark_ranges.empty() 328 | && !current_task.readersChainCanSkipMarksBeforePrewhere()) >> 329 | current_task.addPrewhereUnmatchedMarks(res.unmatched_mark_ranges); 330 | 331 | return {Chunk(), res.num_read_rows, res.num_read_bytes, false, std::move(res.read_mark_ranges)}; ================================================================================ src/Storages/MergeTree/UniqueKey/MergeTreeBitmapStore.cpp ================================================================================ --- uncovered block 246-246 --- 244 | /// Invalidate the cache entry so a later reader does not serve a removed version. 245 | if (cache) >> 246 | cache->remove(DeleteBitmapCache::makeKey(part_id, csn)); 247 | 248 | LOG_TRACE(getLogger("MergeTreeBitmapStore"), --- uncovered block 279-280 --- 277 | if (!resolution_data) 278 | throw Exception(ErrorCodes::LOGICAL_ERROR, >> 279 | "MergeTreeBitmapStore: PartName-keyed bitmap access requires a resolution " >> 280 | "context (data + partition_id); this store was built cache-only"); 281 | 282 | /// Single indexed lookup (`data_parts_by_info.find`, O(log P)). Part names are --- uncovered block 341-341 --- 339 | if (!resolved.storage) 340 | throw Exception(ErrorCodes::LOGICAL_ERROR, >> 341 | "MergeTreeBitmapStore::installBitmap: resolver returned null storage for part '{}'", target); 342 | 343 | installBitmap(*resolved.storage, cacheIdentityOf(resolved.cache_identity, target), target, csn, bitmap); --- uncovered block 348-354 --- 346 | void MergeTreeBitmapStore::removeBitmap(const UniqueKeyTxn::PartName & target, UniqueKeyTxn::CSN csn) 347 | { >> 348 | auto resolved = resolvePart(target); 349 | /// A null target (part already gone from the active+outdated set) is 350 | /// tolerated: nothing to unlink, no index/cache entry to clear. >> 351 | if (!resolved.storage) >> 352 | return; 353 | >> 354 | removeBitmap(*resolved.storage, cacheIdentityOf(resolved.cache_identity, target), csn); 355 | } 356 | ================================================================================ src/Storages/MergeTree/UniqueKey/Txn/LocalStrategies.cpp ================================================================================ --- uncovered block 68-68 --- 66 | auto it = pins.find(csn); 67 | if (it == pins.end()) >> 68 | return; /// double-release guarded 69 | if (--it->second == 0) 70 | pins.erase(it); --- uncovered block 80-80 --- 78 | std::lock_guard lock(pins_mutex); 79 | if (pins.empty()) >> 80 | return MAX_CSN; 81 | CSN floor = MAX_CSN; 82 | for (const auto & [csn, _] : pins) ================================================================================ src/Storages/MergeTree/UniqueKey/Txn/MakeLocalStrategies.cpp ================================================================================ --- uncovered block 32-38 --- 30 | auto shared = data.getActivePartsInPartitionShared(partition_id); 31 | if (!shared) >> 32 | return INVALID_CSN; 33 | 34 | CSN floor = INVALID_CSN; 35 | for (const auto & p : *shared) 36 | { 37 | if (!p) >> 38 | continue; 39 | if (auto meta = p->getUniqueKeyMeta()) 40 | floor = std::max(floor, meta->creation_csn); ================================================================================ src/Storages/MergeTree/UniqueKey/Txn/PartitionTxnController.cpp ================================================================================ --- uncovered block 264-267 --- 262 | /// committed. Propagate so the tmp dir is left for forensics and 263 | /// the caller (runUniqueKeyTxnRecovery) fails the table's startup. >> 264 | tryLogCurrentException(logger, >> 265 | fmt::format("recover: removeBitmap({}, {}) failed for tmp dir '{}'", >> 266 | resolved_target, cs, tmp_storage->getFullPath())); >> 267 | throw; 268 | } 269 | } --- uncovered block 278-279 --- 276 | catch (...) 277 | { >> 278 | tryLogCurrentException(logger, >> 279 | fmt::format("recover: failed to remove tmp dir '{}'", tmp_storage->getFullPath())); 280 | } 281 | } ================================================================================ src/Storages/MergeTree/UniqueKey/Txn/UniqueKeyManifest.cpp ================================================================================ --- uncovered block 58-58 --- 56 | const Poco::Dynamic::Var v = obj->get(key); 57 | if (v.isEmpty() || !v.isString()) >> 58 | throw Exception(ErrorCodes::CORRUPTED_DATA, "field '{}' is missing or not a string", key); 59 | return v.convert(); 60 | } --- uncovered block 105-110 --- 103 | const auto arr = object->getArray(key); 104 | if (!arr) >> 105 | throw Exception(ErrorCodes::CORRUPTED_DATA, "'{}' is not an array", key); 106 | for (unsigned i = 0; i < arr->size(); ++i) 107 | { 108 | const auto entry = arr->getObject(i); 109 | if (!entry) >> 110 | throw Exception(ErrorCodes::CORRUPTED_DATA, "'{}' entry {} is not an object", key, i); 111 | auto target = getStrictString(entry, "target"); 112 | if (target.empty()) ================================================================================ src/Storages/MergeTree/UniqueKey/Txn/tests/gtest_local_snapshot_region.cpp ================================================================================ --- uncovered block 24-26 --- 22 | public: 23 | std::pair >> 24 | readBitmap(const PartName &, CSN) override { return {std::make_shared(), 0}; } >> 25 | void installBitmap(const PartName &, CSN, const DeleteBitmap &) override {} >> 26 | void removeBitmap(const PartName &, CSN) override {} 27 | }; 28 | --- uncovered block 79-79 --- 77 | { 78 | while (reads.load(std::memory_order_acquire) == 0) >> 79 | std::this_thread::yield(); 80 | for (int i = 0; i < N_COMMITS; ++i) 81 | { ================================================================================ src/Storages/MergeTree/UniqueKey/Txn/tests/gtest_recovery_tmp_scan.cpp ================================================================================ --- uncovered block 53-54 --- 51 | 52 | std::pair >> 53 | readBitmap(const PartName & /*part*/, CSN /*csn*/) override { return {std::make_shared(), 0}; } >> 54 | void installBitmap(const PartName & /*target*/, CSN /*csn*/, const DeleteBitmap & /*bitmap*/) override {} 55 | void removeBitmap(const PartName & target, CSN csn) override { unlinks.emplace_back(target, csn); } 56 | }; --- uncovered block 63-74 --- 61 | { 62 | public: >> 63 | PreparedCommitSnapshot readSnapshot() override { return {}; } 64 | /// Never invoked by recovery; stubs to satisfy the interface. >> 65 | CSN attemptCommit(PublishAction) override { return INVALID_CSN; } >> 66 | void withinSnapshotRegion(std::function) override {} >> 67 | std::unique_lock lockForWrite() override { return {}; } 68 | }; 69 | 70 | class StubPinRegistry : public IPinRegistry 71 | { 72 | public: >> 73 | std::shared_ptr acquire(CSN /*csn*/) override { return nullptr; } >> 74 | CSN clusterFloor() override { return MAX_CSN; } 75 | }; 76 | ================================================================================ src/Storages/MergeTree/UniqueKey/Txn/tests/gtest_txn_fakes.h ================================================================================ --- uncovered block 68-68 --- 66 | CSN clusterFloor() override 67 | { >> 68 | return pins_by_csn.empty() ? MAX_CSN : pins_by_csn.begin()->first; 69 | } 70 | --- uncovered block 156-156 --- 154 | if (it != cached_version.end() && csn <= it->second) 155 | throw DB::Exception(DB::ErrorCodes::LOGICAL_ERROR, >> 156 | "RecordingBitmapStore::installBitmap non-monotone csn"); 157 | } 158 | auto owned = std::make_shared(); --- uncovered block 242-242 --- 240 | 241 | /// Tests drive the controller single-threaded; an empty guard suffices. >> 242 | std::unique_lock lockForWrite() override { return {}; } 243 | }; 244 | ================================================================================ src/Storages/MergeTree/UniqueKey/UniqueKeyDelete.cpp ================================================================================ --- uncovered block 86-87 --- 84 | if (!part) 85 | { >> 86 | ++out_skipped_outdated_parts; >> 87 | continue; 88 | } 89 | /// DELETE-vs-merge retargeting: `getActiveContainingPart` returns --- uncovered block 101-106 --- 99 | if (part->name != entry.part_name) 100 | { >> 101 | LOG_DEBUG(log, >> 102 | "UNIQUE KEY DELETE: skipping part '{}' — merged into covering part '{}' " >> 103 | "between SELECT and the writer guard; stale _part_offset values cannot be remapped", >> 104 | entry.part_name, part->name); >> 105 | ++out_skipped_outdated_parts; >> 106 | continue; 107 | } 108 | --- uncovered block 123-123 --- 121 | 122 | if (targets.empty()) >> 123 | return 0; 124 | 125 | const Int64 marker_block_number = marker_block_holder->block.number; --- uncovered block 209-226 --- 207 | /// is the safety boundary (not swallowed); the clone-to-detached only 208 | /// preserves rows for recovery (best-effort). Then rethrow. >> 209 | for (const auto & broken_name : txn_controller.takeOrphanedTargets()) >> 210 | for (const auto & t : targets) >> 211 | if (t.part->name == broken_name) 212 | { >> 213 | storage.removePartsFromWorkingSet(NO_TRANSACTION_RAW, {t.part}, /*clear_without_timeout=*/true); >> 214 | try 215 | { >> 216 | t.part->makeCloneInDetached( >> 217 | "broken-uk-bitmap", t.part->getMetadataSnapshot(), /*disk_transaction=*/{}); 218 | } >> 219 | catch (...) 220 | { >> 221 | tryLogCurrentException(log, >> 222 | "broken UNIQUE KEY part " + broken_name >> 223 | + " retired from working set but clone-to-detached failed"); 224 | } 225 | } >> 226 | throw; 227 | } 228 | chassert(result.csn != UniqueKeyTxn::INVALID_CSN, ================================================================================ src/Storages/MergeTree/UniqueKey/UniqueKeyDeleteRowFinder.cpp ================================================================================ --- uncovered block 149-149 --- 147 | { 148 | if (block.empty()) >> 149 | continue; 150 | 151 | const auto & part_col = block.getByName("_part").column; --- uncovered block 181-182 --- 179 | if (!MergeTreePartInfo::tryParsePartName(e.part_name, storage.format_version)) 180 | throw Exception(ErrorCodes::LOGICAL_ERROR, >> 181 | "UNIQUE KEY DELETE row finder: unparseable part name '{}' from read pipeline", >> 182 | e.part_name); 183 | 184 | return group(pairs, storage.format_version); ================================================================================ src/Storages/MergeTree/UniqueKey/UniqueKeyMarkerPart.cpp ================================================================================ --- uncovered block 38-39 --- 36 | /// from the allocator, but fail loudly rather than stage `all_-1_-1_0`. 37 | if (block_number < 0) >> 38 | throw Exception(ErrorCodes::BAD_ARGUMENTS, >> 39 | "createMarkerPart: block_number must be non-negative, got {}", block_number); 40 | 41 | chassert(meta.is_marker); ================================================================================ src/Storages/MergeTree/UniqueKey/UniqueKeyReadFilter.cpp ================================================================================ --- uncovered block 59-60 --- 57 | if (current_valid) 58 | { >> 59 | out.kept.push_back(current); >> 60 | current_valid = false; 61 | } 62 | } ================================================================================ src/Storages/StorageMergeTree.cpp ================================================================================ --- uncovered block 215-216 --- 213 | "Data directory for table already containing data parts - probably " 214 | "it was unclean DROP table or manual intervention. " >> 215 | "You must either clear directory by hand or use ATTACH TABLE instead " >> 216 | "of CREATE TABLE if you need to use those parts"); 217 | 218 | /// UNIQUE KEY txn-state recovery. Runs AFTER the stale-data guard above so a --- uncovered block 368-368 --- 366 | && !settings[Setting::allow_experimental_analyzer] && local_context->getClientInfo().distributed_depth == 0) 367 | { >> 368 | auto cluster = local_context->getClusterForParallelReplicas(); 369 | if (local_context->canUseParallelReplicasCustomKeyForCluster(*cluster)) 370 | { --- uncovered block 389-389 --- 387 | "Parallel replicas with custom key will not be used because cluster defined by 'cluster_for_parallel_replicas' ('{}') has " 388 | "multiple shards", >> 389 | cluster->getName()); 390 | } 391 | --- uncovered block 417-417 --- 415 | partition_ranges[part->info.getPartitionId()].addPart(part->info.min_block, part->info.max_block); 416 | >> 417 | return constructPromoters(/*committing_block_numbers=*/{}, std::move(partition_ranges)); 418 | } 419 | --- uncovered block 453-453 --- 451 | auto metadata_snapshot = getInMemoryMetadataPtr(local_context, /*bypass_metadata_cache=*/false); 452 | if (metadata_snapshot && metadata_snapshot->hasUniqueKey()) >> 453 | uk_partition_snapshots = captureUniqueKeyPartitionSnapshots(); 454 | auto parts = getVisibleDataPartsVector(local_context); 455 | return totalRowsByPartitionPredicateImpl(filter_actions_dag, local_context, RangesInDataParts(parts), uk_partition_snapshots); --- uncovered block 884-884 --- 882 | const auto & delete_query = query_ptr->as(); 883 | if (!delete_query.predicate) >> 884 | throw Exception(ErrorCodes::BAD_ARGUMENTS, "DELETE on UNIQUE KEY tables requires a WHERE predicate"); 885 | 886 | auto metadata_snapshot = getInMemoryMetadataPtr(query_context, /*bypass_metadata_cache=*/false); === Lost Baseline Coverage: 73 lines === ================================================================================ src/Storages/MergeTree/MergeTreeData.cpp ================================================================================ --- lost coverage block 4541-4541 --- 4539 | { 4540 | if (i > 0) >> 4541 | s += ", "; 4542 | s += backQuoteIfNeed(uk_columns[i]); 4543 | } ================================================================================ src/Storages/StorageMergeTree.cpp ================================================================================ --- lost coverage block 1969-1970 --- 1967 | if (!entry.tid.isNonTransactional()) 1968 | { >> 1969 | end_it = it; >> 1970 | break; 1971 | } 1972 | ================================================================================ src/Storages/StorageReplicatedMergeTree.cpp ================================================================================ --- lost coverage block 1291-1293 --- 1289 | 1290 | if (!zookeeper->tryGet(zookeeper_path + "/replicas", replicas_value, &replicas_stat)) >> 1291 | throw Exception(ErrorCodes::ALL_REPLICAS_LOST, >> 1292 | "Cannot create a replica of the table {}, because the last replica of the table was dropped right now", >> 1293 | zookeeper_path); 1294 | 1295 | /// It is not the first replica, we will mark it as "lost", to immediately repair (clone) from existing replica. --- lost coverage block 3263-3264 --- 3261 | continue; 3262 | >> 3263 | MergeTreePartInfo covering_drop_range; >> 3264 | if (queue.isGoingToBeDropped(part_desc->new_part_info, &covering_drop_range)) 3265 | { 3266 | LOG_WARNING(log, "Will not add part {} (while replacing {}) because it's going to be dropped (DROP_RANGE: {})", --- lost coverage block 3275-3279 --- 3273 | /// However, it's quite dangerous, because part may appear in source table. 3274 | /// So we enqueue it for check only if no replicas of source table have part either. >> 3275 | bool need_check = true; >> 3276 | if (auto * replicated_src_table = typeid_cast(source_table.get())) 3277 | { >> 3278 | String src_replica = replicated_src_table->findReplicaHavingPart(part_desc->src_part_name, false); >> 3279 | if (!src_replica.empty()) 3280 | { 3281 | LOG_DEBUG(log, "Found part {} on replica {} of source table, will not check part {} required for {}", --- lost coverage block 3287-3296 --- 3285 | } 3286 | >> 3287 | if (need_check) 3288 | { >> 3289 | LOG_DEBUG(log, "Will check part {} required for {}, because no replicas have it (including replicas of source table)", >> 3290 | part_desc->new_part_name, entry.znode_name); >> 3291 | enqueuePartForCheck(part_desc->new_part_name); 3292 | } 3293 | >> 3294 | throw Exception(ErrorCodes::NO_REPLICA_HAS_PART, >> 3295 | "Not found part {} (or part covering it) neither source table neither remote replicas", >> 3296 | part_desc->new_part_name); 3297 | } 3298 | --- lost coverage block 4080-4081 --- 4078 | 4079 | if (source_replica.empty()) >> 4080 | throw Exception(ErrorCodes::ALL_REPLICAS_LOST, "All replicas are lost. " >> 4081 | "See SYSTEM DROP REPLICA and SYSTEM RESTORE REPLICA queries, they may help"); 4082 | 4083 | if (is_new_replica) --- lost coverage block 4714-4714 --- 4712 | else 4713 | { >> 4714 | zkutil::KeeperMultiException::check(code, ops, responses); 4715 | } 4716 | --- lost coverage block 4847-4847 --- 4845 | else 4846 | { >> 4847 | part->makeCloneInDetached("covered-by-broken", storage_metadata, /*disk_transaction*/ {}); 4848 | } 4849 | detached_parts.push_back(part->name); --- lost coverage block 7457-7461 --- 7455 | if (likely_converting_ordinary_to_atomic) 7456 | { >> 7457 | LOG_INFO(log, "Table {} should not be renamed, because zookeeper_path contains implicit 'database' or 'table' macro. " >> 7458 | "We cannot rename path in ZooKeeper, so path may become inconsistent with table name. " >> 7459 | "However, we allow renaming while converting Ordinary database to Atomic, because all tables will be renamed back", >> 7460 | old_name.getNameForLogs()); >> 7461 | return; 7462 | } 7463 | --- lost coverage block 7993-8036 --- 7991 | return; 7992 | >> 7993 | auto zookeeper = getZooKeeper(); 7994 | >> 7995 | time_t max_replicas_unprocessed_insert_time = 0; >> 7996 | bool have_replica_with_nothing_unprocessed = false; 7997 | >> 7998 | Strings replicas = zookeeper->getChildren(fs::path(zookeeper_path) / "replicas"); >> 7999 | Strings replica_paths; >> 8000 | replica_paths.reserve(replicas.size() * 2); 8001 | >> 8002 | for (const auto & replica : replicas) 8003 | { >> 8004 | if (replica == replica_name) >> 8005 | continue; 8006 | >> 8007 | replica_paths.push_back(fs::path(zookeeper_path) / "replicas" / replica / "is_active"); >> 8008 | replica_paths.push_back(fs::path(zookeeper_path) / "replicas" / replica / "min_unprocessed_insert_time"); 8009 | } 8010 | >> 8011 | auto replica_result = zookeeper->tryGet(replica_paths); >> 8012 | auto replica_num = 0; 8013 | >> 8014 | for (const auto & replica : replicas) 8015 | { >> 8016 | if (replica == replica_name) 8017 | continue; 8018 | >> 8019 | const auto & is_active_path = replica_paths[replica_num]; 8020 | >> 8021 | const auto & is_active = replica_result[replica_num++]; >> 8022 | const auto & min_unprocessed_insert_time = replica_result[replica_num++]; 8023 | 8024 | /// Skip dead replicas. >> 8025 | if (is_active.error == Coordination::Error::ZNONODE) 8026 | continue; >> 8027 | else if (is_active.error != Coordination::Error::ZOK) 8028 | throw Coordination::Exception::fromPath(is_active.error, is_active_path); 8029 | >> 8030 | if (min_unprocessed_insert_time.error != Coordination::Error::ZOK) 8031 | continue; 8032 | >> 8033 | const auto & value = min_unprocessed_insert_time.data; >> 8034 | time_t replica_time = value.empty() ? 0 : parse(value); 8035 | >> 8036 | if (replica_time == 0) 8037 | { 8038 | /** Note --- lost coverage block 8046-8054 --- 8044 | */ 8045 | >> 8046 | have_replica_with_nothing_unprocessed = true; >> 8047 | break; 8048 | } 8049 | 8050 | max_replicas_unprocessed_insert_time = std::max(replica_time, max_replicas_unprocessed_insert_time); 8051 | } 8052 | >> 8053 | if (have_replica_with_nothing_unprocessed) >> 8054 | out_relative_delay = out_absolute_delay; 8055 | else 8056 | { --- lost coverage block 8394-8394 --- 8392 | { 8393 | if (shutdown_called || partial_shutdown_called) >> 8394 | throw Exception(ErrorCodes::ABORTED, "Cannot assign mutation because shutdown called"); 8395 | 8396 | Coordination::Stat mutations_stat; --- lost coverage block 8430-8439 --- 8428 | break; 8429 | } >> 8430 | if (rc == Coordination::Error::ZBADVERSION) 8431 | { 8432 | /// Cannot retry automatically, because some zookeeper ops were lost on the first attempt. Will retry on DDLWorker-level. >> 8433 | if (query_context->getZooKeeperMetadataTransaction()) 8434 | throw Exception( 8435 | ErrorCodes::CANNOT_ASSIGN_ALTER, 8436 | "Cannot execute alter, because mutations version was suddenly changed due " 8437 | "to concurrent alter"); >> 8438 | LOG_TRACE(log, "Version conflict when trying to create a mutation node, retrying..."); >> 8439 | continue; 8440 | } 8441 | throw Coordination::Exception::fromMessage(rc, "Unable to create a mutation znode"); --- lost coverage block 8841-8845 --- 8839 | else 8840 | { >> 8841 | if (parts_should_be_retried) 8842 | parts_should_be_retried->insert(part_names[i]); 8843 | >> 8844 | if (!Coordination::isHardwareError(response.error)) >> 8845 | LOG_WARNING(log, "Cannot remove part {} from ZooKeeper: {}", part_names[i], Coordination::errorMessage(response.error)); 8846 | } 8847 | } --- lost coverage block 8966-8966 --- 8964 | 8965 | if (result.error == Coordination::Error::ZNONODE) >> 8966 | continue; 8967 | 8968 | ReadBufferFromString buf(result.data); --- lost coverage block 10341-10341 --- 10339 | else 10340 | { >> 10341 | return toString(UUIDHelpers::Nil); 10342 | } 10343 | --- lost coverage block 10877-10877 --- 10875 | LOG_TRACE(logger, "Removed last parent zookeeper lock {} for part {} with id {}", zookeeper_part_uniq_node, part_name, part_id); 10876 | } >> 10877 | else if (error_code == Coordination::Error::ZNOTEMPTY) 10878 | { 10879 | LOG_TRACE( --- lost coverage block 10888-10890 --- 10886 | continue; 10887 | } >> 10888 | else if (error_code == Coordination::Error::ZNONODE) 10889 | { >> 10890 | LOG_TRACE(logger, "Node with parent zookeeper lock {} for part {} with id {} doesn't exist", zookeeper_part_uniq_node, part_name, part_id); 10891 | } 10892 | else --- lost coverage block 10912-10922 --- 10910 | LOG_TRACE(logger, "Removed last parent zookeeper lock {} for part {} (part is finally unlocked)", zookeeper_part_node, part_name); 10911 | } >> 10912 | else if (error_code == Coordination::Error::ZNOTEMPTY) 10913 | { 10914 | LOG_TRACE(logger, "Cannot remove last parent zookeeper lock {} for part {}, another replica locked part concurrently", zookeeper_part_uniq_node, part_name); 10915 | } >> 10916 | else if (error_code == Coordination::Error::ZNONODE) 10917 | { 10918 | /// We don't know what to do, because this part can be mutation part 10919 | /// with hardlinked columns. Since we don't have this information (about blobs not to remove) 10920 | /// we refuse to remove blobs. >> 10921 | LOG_WARNING(logger, "Node with parent zookeeper lock {} for part {} doesn't exist (part was unlocked before), refuse to remove blobs", zookeeper_part_uniq_node, part_name); >> 10922 | return {false, {}}; 10923 | } 10924 | else WARNING: Failed to get start time for [Print Uncovered Code] - start time and duration won't be set --- Coverage counts --- Lines : baseline 850,139/995,793 -> current 851,936/997,992 (delta +1,797 / +2,199) Functions : baseline 940,172/1,015,123 -> current 940,406/1,015,543 (delta +234 / +420) Branches : baseline 271,699/350,170 -> current 272,437/351,102 (delta +738 / +932)