Changed-lines coverage: PR changed C/C++ lines covered by tests: 86.33% (663/768) Uncovered changed code (with context): ================================================================================ src/Interpreters/Cache/QueryResultCache.cpp ================================================================================ --- uncovered block 229-234 --- 227 | { 228 | if (data.cannot_verify) >> 229 | return; 230 | 231 | if (const auto * table_expression = node->as()) 232 | { 233 | if (table_expression->table_function) >> 234 | data.cannot_verify = true; 235 | } 236 | else if (const auto * table_identifier = node->as()) --- uncovered block 273-273 --- 271 | /// consistency. 272 | if (finder_data.cannot_verify) >> 273 | return {}; 274 | 275 | std::vector table_hashes; --- uncovered block 290-295 --- 288 | /// Ok to ignore: could not resolve a referenced table (e.g. a CTE), so we cannot verify 289 | /// consistency and conservatively bail out. >> 290 | return {}; 291 | } 292 | 293 | auto storage = DatabaseCatalog::instance().tryGetTable(resolved_id, context); 294 | if (!storage) >> 295 | return {}; 296 | 297 | auto metadata = storage->getInMemoryMetadataPtr(context, false); --- uncovered block 310-320 --- 308 | if (!can_read) 309 | { >> 310 | for (const auto & column : metadata->getColumns()) 311 | { >> 312 | if (access->isGranted(AccessType::SELECT, resolved_id.database_name, resolved_id.table_name, column.name)) 313 | { >> 314 | can_read = true; >> 315 | break; 316 | } 317 | } 318 | } 319 | if (!can_read) >> 320 | return {}; 321 | } 322 | ================================================================================ src/Interpreters/Context.cpp ================================================================================ --- uncovered block 7483-7483 --- 7481 | /// null there so the caller falls back to listing rather than asserting. 7482 | if (!hasQueryContext()) >> 7483 | return nullptr; 7484 | return getQueryContext()->query_consumed_object_sets; 7485 | } --- uncovered block 7494-7494 --- 7492 | bool Context::hasQueryParameters() const 7493 | { >> 7494 | return !query_parameters.empty(); 7495 | } 7496 | ================================================================================ src/Interpreters/QueryConsumedObjectSets.cpp ================================================================================ --- uncovered block 30-30 --- 28 | auto it = objects_by_table.find(table_uuid); 29 | if (it == objects_by_table.end()) >> 30 | return {}; 31 | return it->second; 32 | } ================================================================================ src/Parsers/ASTRefreshStrategy.h ================================================================================ --- uncovered block 32-32 --- 30 | bool if_changed = false; 31 | >> 32 | String getID(char) const override { return "Refresh strategy definition"; } 33 | 34 | ASTPtr clone() const override; ================================================================================ src/Planner/Planner.cpp ================================================================================ --- uncovered block 2188-2190 --- 2186 | if (should_cache && settings[Setting::query_cache_use_only_when_data_was_not_changed]) 2187 | { >> 2188 | referenced_tables_modification_hash = computeQueryReferencedTablesModificationHash(ast, query_context); >> 2189 | if (!referenced_tables_modification_hash) >> 2190 | should_cache = false; 2191 | } 2192 | --- uncovered block 2204-2204 --- 2202 | if (should_cache && referenced_tables_modification_hash.has_value() 2203 | && query_context->hasQueryContext() && !query_context->getQueryConsumedObjectSets()) >> 2204 | query_context->getQueryContext()->setQueryConsumedObjectSets(std::make_shared()); 2205 | 2206 | /// If it is a non-internal SELECT, and passive (read) use of the query cache is enabled, and the cache knows the query, then add a ReadFromQueryResultCacheStep instead of building the rest of the plan. --- uncovered block 2767-2772 --- 2765 | /// executeQuery-level cache for details). 2766 | if (settings[Setting::query_cache_use_only_when_data_was_not_changed]) >> 2767 | query_result_cache_writer->setConsistencyValidator( >> 2768 | [ast, query_context, expected = referenced_tables_modification_hash]() 2769 | { >> 2770 | auto current = computeQueryReferencedTablesModificationHash(ast, query_context); >> 2771 | return current.has_value() && current == expected; >> 2772 | }); 2773 | 2774 | auto stream_into_query_result_cache_step = std::make_unique(query_plan.getRootNode()->step->getOutputHeader(), query_result_cache_writer); ================================================================================ src/Processors/QueryPlan/ReadFromObjectStorageStep.cpp ================================================================================ --- uncovered block 73-74 --- 71 | void setEmitProfileEvents(bool value) override 72 | { >> 73 | emit_profile_events = value; >> 74 | inner->setEmitProfileEvents(value); 75 | } 76 | ================================================================================ src/Storages/MaterializedView/RefreshTask.cpp ================================================================================ --- uncovered block 1238-1238 --- 1236 | out_source_hash = post_read_source_hash; 1237 | else >> 1238 | out_source_hash = std::nullopt; 1239 | } 1240 | ================================================================================ src/Storages/MergeTree/MergeTreeData.cpp ================================================================================ --- uncovered block 11758-11766 --- 11756 | if (snapshot_data && snapshot_data->mutations_snapshot) 11757 | { >> 11758 | const auto & mutations = *snapshot_data->mutations_snapshot; >> 11759 | if (mutations.hasDataMutations() || mutations.hasAlterMutations() || mutations.hasMetadataMutations()) >> 11760 | return {}; 11761 | } 11762 | else 11763 | { 11764 | const auto counters = getMutationCounters(); 11765 | if (counters.num_data > 0 || counters.num_alter > 0 || counters.num_metadata > 0) >> 11766 | return {}; 11767 | } 11768 | --- uncovered block 11773-11774 --- 11771 | if (snapshot_data && snapshot_data->parts) 11772 | { >> 11773 | for (const auto & part : *snapshot_data->parts) >> 11774 | add_part(*part.data_part); 11775 | } 11776 | else --- uncovered block 11787-11787 --- 11785 | hash.update("patch"); 11786 | for (const auto & part : getPatchPartsVectorForInternalUsage()) >> 11787 | add_part(*part); 11788 | 11789 | return hash.get128(); ================================================================================ src/Storages/ObjectStorage/StorageObjectStorage.cpp ================================================================================ --- uncovered block 570-570 --- 568 | auto metadata = object_info->getObjectMetadata(); 569 | if (!metadata) >> 570 | metadata = object_storage->tryGetObjectMetadata(object_info->getPath(), /*with_tags=*/ false); 571 | 572 | QueryConsumedObjectSets::Object object; --- uncovered block 587-587 --- 585 | { 586 | /// Ok to ignore: we could not list the objects, so we conservatively assume the data may have changed. >> 587 | return {}; 588 | } 589 | } --- uncovered block 626-626 --- 624 | /// object could not be probed at all (assume the data may have changed). 625 | if (!object.has_metadata || object.etag.empty() || object.etag.starts_with("W/")) >> 626 | return {}; 627 | 628 | hash.update(object.path); ================================================================================ src/Storages/StorageDistributed.cpp ================================================================================ --- uncovered block 1776-1776 --- 1774 | auto storage = DatabaseCatalog::instance().tryGetTable(table_id, context); 1775 | if (!storage) >> 1776 | return {}; 1777 | /// The local shard can resolve back to the owning `Distributed` table itself (a self-referential 1778 | /// `Distributed` table). The constructor rejects this only on `CREATE`, not when loading existing --- uncovered block 1783-1788 --- 1781 | /// on self-reference. 1782 | if (storage.get() == owner) >> 1783 | return {}; 1784 | auto metadata = storage->getInMemoryMetadataPtr(context, false); 1785 | auto snapshot = storage->getStorageSnapshotWithoutData(metadata, context); 1786 | auto table_hash = storage->getModificationHash(snapshot, context); 1787 | if (!table_hash) >> 1788 | return {}; 1789 | /// Fold the child table's UUID so that a dropped/recreated same-name child is distinguished even 1790 | /// for engines whose own hash does not fold it: URL and object-storage hashes are derived only --- uncovered block 1836-1844 --- 1834 | return {}; /// The shard cannot tell whether its table changed. 1835 | } >> 1836 | SipHash with_identity; >> 1837 | with_identity.update((*uuid_column)[i].safeGet()); >> 1838 | with_identity.update(value.safeGet()); >> 1839 | result = with_identity.get128(); 1840 | } 1841 | } >> 1842 | executor.finish(); 1843 | >> 1844 | return result; /// nullopt if the table was not found on the shard. 1845 | } 1846 | --- uncovered block 1859-1859 --- 1857 | static thread_local std::unordered_set tables_being_hashed; 1858 | if (!tables_being_hashed.insert(this).second) >> 1859 | return {}; 1860 | SCOPE_EXIT({ tables_being_hashed.erase(this); }); 1861 | --- uncovered block 1879-1879 --- 1877 | /// Table functions have no stable identity on the shards, so we cannot ask system.tables about them. 1878 | if (remote_table_function_ptr) >> 1879 | return {}; 1880 | 1881 | /// Without a table UUID (e.g. a table in an `Ordinary` database) we cannot distinguish incarnations of --- uncovered block 1893-1893 --- 1891 | auto cluster = getCluster(); 1892 | if (!cluster) >> 1893 | return {}; 1894 | 1895 | const StorageID remote_table_id{remote_database, remote_table}; --- uncovered block 1934-1934 --- 1932 | /// data than the one we probed. We cannot guarantee consistency in that case, so fail closed. 1933 | if (shard_info.getAllNodeCount() > 1) >> 1934 | return {}; 1935 | 1936 | auto shard_hash = getModificationHashOfRemoteTableInShard(*cluster, shard_info, remote_table_id, this, query_context); --- uncovered block 1952-1952 --- 1950 | { 1951 | /// Ok to ignore: we could not reach a shard, so we conservatively assume the data may have changed. >> 1952 | return {}; 1953 | } 1954 | } ================================================================================ src/Storages/StorageFile.cpp ================================================================================ --- uncovered block 2630-2630 --- 2628 | /// They cannot satisfy the "modification_hash changes whenever the data changes" contract, so we do not 2629 | /// participate in the consistency features rather than risk serving stale results. >> 2630 | return {}; 2631 | } 2632 | ================================================================================ src/Storages/StorageLog.cpp ================================================================================ --- uncovered block 1174-1174 --- 1172 | /// produce the same hash. There is no per-incarnation identity to fold in, so fail closed. 1173 | if (!getStorageID().hasUUID()) >> 1174 | return {}; 1175 | 1176 | /// Sample under the read lock so the hash observes the same committed state a normal read would. ================================================================================ src/Storages/StorageMemory.cpp ================================================================================ --- uncovered block 741-741 --- 739 | 740 | /// Append old blocks with the new ones. >> 741 | Blocks old_and_new_blocks = data.get()->blocks; 742 | old_and_new_blocks.insert(old_and_new_blocks.end(), std::make_move_iterator(new_blocks.begin()), std::make_move_iterator(new_blocks.end())); 743 | ================================================================================ src/Storages/StorageMemory.h ================================================================================ --- uncovered block 41-41 --- 39 | String getName() const override { return "Memory"; } 40 | >> 41 | size_t getSize() const { return data.get()->blocks.size(); } 42 | 43 | /// Snapshot for StorageMemory contains current set of blocks ================================================================================ src/Storages/StorageMerge.cpp ================================================================================ --- uncovered block 464-464 --- 462 | auto base_metadata = IStorage::getInMemoryMetadataPtr(query_context, bypass_metadata_cache); 463 | if (!query_context) >> 464 | return base_metadata; 465 | 466 | /// Resolving the source-table virtuals below recurses into the first source's `getInMemoryMetadataPtr`, --- uncovered block 1924-1924 --- 1922 | { 1923 | if (!table) >> 1924 | return false; 1925 | 1926 | auto metadata = table->getInMemoryMetadataPtr(query_context, false); --- uncovered block 1972-1972 --- 1970 | /// recurse until `checkStackSize` throws `TOO_DEEP_RECURSION` (see `getDatabaseIterators`), so keep 1971 | /// failing closed here as a backstop. >> 1972 | return {}; 1973 | } 1974 | } ================================================================================ src/Storages/StorageStripeLog.cpp ================================================================================ --- uncovered block 596-596 --- 594 | std::optional StorageStripeLog::totalBytes(ContextPtr) const 595 | { >> 596 | return total_bytes; 597 | } 598 | --- uncovered block 605-606 --- 603 | /// restarts `data_version` from the same value, so a recreated table holding different data could 604 | /// produce the same hash. There is no per-incarnation identity to fold in, so fail closed. >> 605 | if (!getStorageID().hasUUID()) >> 606 | return {}; 607 | 608 | /// Sample under the read lock so the hash observes the same committed state a normal read would. --- uncovered block 620-637 --- 618 | /// lock, fail closed (return nullopt): a write is in progress, so we conservatively treat the data as 619 | /// changed rather than risk a stale-but-consistent sample. >> 620 | ReadLock lock{rwlock, std::try_to_lock}; >> 621 | if (!lock) >> 622 | return {}; 623 | >> 624 | SipHash hash; 625 | /// The table UUID distinguishes different incarnations of a table with the same name. The monotonic 626 | /// `data_version` (bumped on every insert, truncate and restore) guarantees the hash changes whenever 627 | /// the data changes, even for TRUNCATE followed by reinserting different rows with the same row/byte 628 | /// counts. The version resets to 0 on restart, which only causes a harmless cache miss. >> 629 | hash.update(getStorageID().uuid); >> 630 | hash.update(storage_snapshot->metadata->getColumns().toString(/*include_comments=*/ false)); 631 | /// Loop-free metadata version: the column string above repeats under a reverted metadata-only `ALTER` 632 | /// and `data_version` does not move (no data changed). See `IStorage::getMetadataVersionForModificationHash`. >> 633 | hash.update(getMetadataVersionForModificationHash()); >> 634 | hash.update(data_version.load(std::memory_order_relaxed)); >> 635 | hash.update(total_rows.load(std::memory_order_relaxed)); >> 636 | hash.update(total_bytes.load(std::memory_order_relaxed)); >> 637 | return hash.get128(); 638 | } 639 | --- uncovered block 770-770 --- 768 | 769 | /// Finish writing. >> 770 | saveIndices(lock); 771 | saveFileSizes(lock); 772 | updateTotalRows(lock); ================================================================================ src/Storages/StorageURL.cpp ================================================================================ --- uncovered block 1593-1615 --- 1591 | return {}; 1592 | >> 1593 | Poco::URI request_uri(uri); >> 1594 | Poco::Net::HTTPBasicCredentials credentials; >> 1595 | StorageURLSource::setCredentials(credentials, request_uri); 1596 | >> 1597 | ReadWriteBufferFromHTTP::HTTPFileInfo file_info; >> 1598 | try 1599 | { >> 1600 | auto buf = BuilderRWBufferFromHTTP(request_uri) >> 1601 | .withConnectionGroup(HTTPConnectionGroupType::STORAGE) >> 1602 | .withSettings(context->getReadSettings()) >> 1603 | .withTimeouts(getHTTPTimeouts(context)) >> 1604 | .withHostFilter(&context->getRemoteHostFilter()) >> 1605 | .withBufSize(settings[Setting::max_read_buffer_size]) >> 1606 | .withRedirects(settings[Setting::max_http_get_redirects]) >> 1607 | .withHeaders(headers) >> 1608 | .create(credentials); 1609 | >> 1610 | file_info = buf->getFileInfo(); 1611 | } >> 1612 | catch (...) 1613 | { 1614 | /// Ok to ignore: we could not reach the server, so we conservatively assume the data may have changed. >> 1615 | return {}; 1616 | } 1617 | --- uncovered block 1622-1641 --- 1620 | /// cannot guarantee detection of changes, so fail closed (return nullopt). A weak ETag (prefixed 1621 | /// with `W/`) is also insufficient. >> 1622 | if (file_info.etag.empty() || file_info.etag.starts_with("W/")) >> 1623 | return {}; 1624 | >> 1625 | SipHash hash; 1626 | /// Table identity distinguishes different incarnations of a same-named table (a DROP + CREATE in an 1627 | /// `Atomic` database gets a fresh UUID) that would otherwise share the same resource `ETag` and columns 1628 | /// under a different read-affecting configuration. >> 1629 | hash.update(getStorageID().uuid); 1630 | /// Loop-free metadata version: the column string below folds by value, so a metadata-only `ALTER` that 1631 | /// is reverted (e.g. an alias/default expression `A -> B -> A`) does not move it and the strong `ETag` 1632 | /// stays the same, yet the middle state B was query-visible. Folding the per-lifetime metadata version 1633 | /// keeps such a round trip from reproducing an earlier hash. See `IStorage::getMetadataVersionForModificationHash`. >> 1634 | hash.update(getMetadataVersionForModificationHash()); >> 1635 | hash.update(storage_snapshot->metadata->getColumns().toString(/*include_comments=*/ false)); >> 1636 | hash.update(uri); >> 1637 | hash.update(file_info.etag); 1638 | /// Mix in size and modification time as well - they only strengthen the strong ETag. >> 1639 | hash.update(file_info.file_size.value_or(0)); >> 1640 | hash.update(file_info.last_modified.value_or(0)); >> 1641 | return hash.get128(); 1642 | } 1643 | ================================================================================ src/Storages/System/StorageSystemTables.cpp ================================================================================ --- uncovered block 1161-1166 --- 1159 | if (!can_read && table && metadata_snapshot) 1160 | { >> 1161 | for (const auto & column : metadata_snapshot->getColumns()) 1162 | { >> 1163 | if (access->isGranted(AccessType::SELECT, database_name, table_name, column.name)) 1164 | { >> 1165 | can_read = true; >> 1166 | break; 1167 | } 1168 | } --- uncovered block 1179-1179 --- 1177 | { 1178 | /// Even if the method throws, it should not prevent querying system.tables. >> 1179 | tryLogCurrentException("StorageSystemTables"); 1180 | } 1181 | WARNING: Failed to get start time for [Print Uncovered Code] - start time and duration won't be set --- Coverage counts --- Lines : baseline 895,398/1,040,448 -> current 896,015/1,041,128 (delta +617 / +680) Functions : baseline 837,845/912,668 -> current 837,804/912,745 (delta -41 / +77) Branches : baseline 289,693/370,650 -> current 290,069/370,940 (delta +376 / +290)