Optimize AND comparison chains: detect conflicts, prune redundancies by wudidapaopao · Pull Request #99736 · ClickHouse/ClickHouse · GitHub
Skip to content

Optimize AND comparison chains: detect conflicts, prune redundancies#99736

Open
wudidapaopao wants to merge 82 commits into
ClickHouse:masterfrom
wudidapaopao:optimize_and_compare
Open

Optimize AND comparison chains: detect conflicts, prune redundancies#99736
wudidapaopao wants to merge 82 commits into
ClickHouse:masterfrom
wudidapaopao:optimize_and_compare

Conversation

@wudidapaopao

@wudidapaopao wudidapaopao commented Mar 17, 2026

Copy link
Copy Markdown
Contributor

Resolved #90081

Changelog category (leave one):

  • Improvement

Changelog entry (a user-readable short description of the changes that goes into CHANGELOG.md):

Optimize AND chains with multiple comparison conditions on the same expression: detect contradictions (e.g., a < 3 AND a > 5false), prune redundant conditions (e.g., a = 3 AND a < 5a = 3).

Documentation entry for user-facing changes

  • Documentation is written (mandatory for new features)

Summary

Extend LogicalExpressionOptimizerPass to analyze and simplify AND chains of comparison conditions on the same expression (e.g., a = 3 AND a < 5 AND a != 7). The optimizer now detects contradictions (rewrites to false), prunes redundant conditions, and handles cross-type constant comparisons via convertFieldToTypeStrict.

Optimizations and fixes

Category Example Before After
equals + range prune i = 3 AND i < 5 kept i = 3
equals + range conflict i = 3 AND i < 2 kept false
equals + notEquals prune i = 3 AND i != 5 kept i = 3
equals + notEquals conflict i = 3 AND i != 3 kept false
notEquals implied by range i != 3 AND i < 3 kept i < 3
range tightening i < 5 AND i < 3 kept i < 3
empty range detection i < 3 AND i >= 3 kept false
cross-type lossless i = 3 AND i = 3.0 false i = 3
transitive conflict x = 3 AND x = y AND y = 5 kept false
transitive redundant chain x < 3 AND y > 3 AND y < 10 redundant x < 10 added no redundant inference

@clickhouse-gh

clickhouse-gh Bot commented Mar 17, 2026

Copy link
Copy Markdown
Contributor

Workflow [PR], commit [34bd051]

Summary:

job_name test_name status info comment
AST fuzzer (amd_debug, targeted) FAIL
Logical error: Unexpected number of columns in result sample block: A expected B ([C] = [D] + [E] + [F]) (STID: 2980-385f) FAIL cidb, issue
AST fuzzer (amd_msan) FAIL
Logical error: Block structure mismatch in A stream: different columns: (STID: 0993-27f0) FAIL cidb, issue

AI Review

Summary

This PR meaningfully improves AND-chain simplification and it appears to have fixed the earlier non-determinism, NaN, and lossy-conversion wrong-results cases. I still do not think the default-on rewrite is safe because it can drop deterministic subtrees that would raise at execution time, so optimize_redundant_comparisons still changes observable exception behavior.

Findings

❌ Blockers

  • [src/Analyzer/Passes/LogicalExpressionOptimizerPass.cpp:861] [dismissed by author -- https://github.com/Optimize AND comparison chains: detect conflicts, prune redundancies #99736#discussion_r3418193762] Boundary REDUNDANT folding still drops the only evaluation of a deterministic expression that may throw. toUInt8(s) < 300 AND 1 becomes true with optimize_redundant_comparisons = 1, but throws CANNOT_PARSE_TEXT with it off. I still consider this real because the rewrite happens during analysis, not runtime short-circuiting, so the setting changes whether the throwing subtree is evaluated.
  • [src/Analyzer/Passes/LogicalExpressionOptimizerPass.cpp:1802] [dismissed by author -- https://github.com/Optimize AND comparison chains: detect conflicts, prune redundancies #99736#discussion_r3408627408] Conflict folding still proves a chain false without proving that the dropped operands are safe to skip. That suppresses exceptions both for throwing compared expressions such as toUInt8(s) = 1 AND toUInt8(s) = 2 and for earlier non-comparison siblings such as toUInt8(s) AND i = 1 AND i = 2, which the unoptimized plan evaluates before lazy AND short-circuiting. I still consider this real because it is an analysis-time rewrite controlled by a setting, not an existing runtime short-circuit path.
Final Verdict

Status: ⚠️ Request changes
Minimum required actions: either restrict optimize_redundant_comparisons to operands proven not to throw, or make the exception-suppressing rewrite an explicit non-default behavior instead of a default-on optimization.

@clickhouse-gh clickhouse-gh Bot added the pr-improvement Pull request with some product improvements label Mar 17, 2026
Comment thread src/Analyzer/Passes/LogicalExpressionOptimizerPass.cpp Outdated
Comment thread src/Analyzer/Passes/LogicalExpressionOptimizerPass.cpp Outdated
@wudidapaopao wudidapaopao force-pushed the optimize_and_compare branch from e73fa86 to 37402ba Compare March 17, 2026 14:23
@nihalzp nihalzp self-assigned this Mar 18, 2026

@nihalzp nihalzp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on it!

Let's place the optimization behind a setting which we can enable by default.

After the setting is added, let's duplicate the changed queries in the existing tests once with setting on and once with setting off. This will make sure we do not lose coverage.

@nihalzp

nihalzp commented Mar 18, 2026

Copy link
Copy Markdown
Member

Additionally, you may look at PR #98516 which contains some ideas that can be reused to improve cross-type comparison.

Comment thread src/Analyzer/Passes/LogicalExpressionOptimizerPass.cpp
@wudidapaopao

wudidapaopao commented Mar 21, 2026

Copy link
Copy Markdown
Contributor Author

Hi @nihalzp, thanks for the suggestions!
I've incorporated ideas from PR #98516 and added float-to-int conversions and early pruning when integer values fall outside the range.
I also introduced a new setting optimize_and_compare_chain_pruning (enabled by default) and duplicated the test queries with the setting both on and off.

SELECT 'eq_eq_same';
SELECT * FROM 04032_t WHERE i = 3 AND i = 3 ORDER BY i SETTINGS optimize_and_compare_chain_pruning = 0;
SELECT * FROM 04032_t WHERE i = 3 AND i = 3 ORDER BY i SETTINGS optimize_and_compare_chain_pruning = 1;
EXPLAIN QUERY TREE SELECT * FROM 04032_t WHERE i = 3 AND i = 3 SETTINGS optimize_and_compare_chain_pruning = 0;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using EXPLAIN QUERY TREE, let's use EXPLAIN SYNTAX run_query_tree_passes = 1 which will give us a more concise output while still showing us that optimization is applied.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @nihalzp, I’ve switched to using EXPLAIN SYNTAX run_query_tree_passes = 1 per request. A single CI error is present but not related to this change.

Comment thread src/Analyzer/Passes/LogicalExpressionOptimizerPass.cpp Outdated
The contradictory range c0 <= 1 AND c0 >= 3 must stay intact for EXPLAIN indexes
to exercise primary key analysis; folding it to false breaks the reference.
Settings before -- { echoOn } are not echoed, so the reference stays unchanged.
alexey-milovidov and others added 2 commits June 29, 2026 13:59
…6.7 block

After merging `master`, the current development version rolled from 26.6
to 26.7, so the new setting's `SettingsChangesHistory` entry must live in
the 26.7 block instead of the now-released 26.6 block.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

const auto & not_equals_arguments = not_equals_function->getArguments().getNodes();
if (const auto * rhs_literal = not_equals_arguments[1]->as<ConstantNode>())
node = std::make_shared<ConstantNode>(0u, function_node.getResultType());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This conflict fold only checks for non-convertible comparison filters, but it can also drop non-comparison operands that the original and must evaluate before short-circuiting. For example, WHERE toUInt8(s) AND i = 1 AND i = 2 with s = 'x': and evaluates argument 0 before building the lazy mask, so the query raises CANNOT_PARSE_TEXT. This branch sees the i = 1 / i = 2 conflict and replaces the whole predicate with false, so the exception disappears when optimize_redundant_comparisons = 1.

Please only fold to false after proving no earlier/sibling operand can throw, or restrict this rewrite to cases where all operands being dropped are safe plain comparisons.

wudidapaopao and others added 2 commits June 30, 2026 12:25
…nstants

`tryConvertToColumnType` converts a comparison constant to the column type with a
strict (lossless) conversion and lets the chain pruning fold/prune only when the
conversion succeeds. `strict` is supposed to reject any lossy conversion by returning
a null `Field`, but `convertFieldToType` does not honour that for `DateTime64`/`Time64`
scale reduction: it silently truncates a higher-scale value to a lower scale (e.g.
`1.23` of scale 2 becomes `1.2` of scale 1).

As a result the optimizer folded a comparison chain using a value different from the
original constant, changing query results versus the unoptimized query. For a
`DateTime64(1)` column,
`dt = toDateTime64('1970-01-01 00:00:01.20', 1) AND dt != toDateTime64('1970-01-01 00:00:01.23', 2)`
collapsed both constants to `1.2`, detected `equals` vs `notEquals` as a contradiction,
and folded the whole `AND` to `false` — dropping the row `1.20`, which must be kept
because `1.20 != 1.23`. The setting is enabled by default, so this affected results
silently.

Guard `tryConvertToColumnType` by requiring the strict conversion to be exactly
reversible, checked only for decimal-backed results (`DateTime64`/`Time64`/`Decimal`)
where this truncation can occur, leaving the already-correct string/integer/float
conversions untouched. Lossless finer-scale constants (e.g. `1.20` of scale 2 in a
`DateTime64(1)` column) are still optimized.

Addresses an unresolved review finding on
ClickHouse#99736

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@alexey-milovidov

Copy link
Copy Markdown
Member

Pushed 5e1296309b1 to continue this PR (CI was green on the previous tip; the branch is only a few commits behind master, MERGEABLE, no conflicts, so no master merge was needed).

Fixed (wrong results): the DateTime64/Time64 scale-truncation issue raised at LogicalExpressionOptimizerPass.cpp:396. Strict conversion silently truncates a finer-scale constant onto the column scale, so e.g. dt = toDateTime64('…01.20', 1) AND dt != toDateTime64('…01.23', 2) was folded to false and dropped the valid row 1.20. tryConvertToColumnType now requires the strict conversion to be exactly reversible (round-trip, checked only for decimal-backed results). New test: 04489_redundant_comparisons_datetime64_scale. optimize_redundant_comparisons is on by default, so this was a silent correctness change — verified the new test fails before the fix and passes after, with no impact on the existing 04032_* references (they use no Decimal/DateTime64 columns).

Left for a maintainer design decision (not changed): the remaining open clickhouse-gh findings at lines 1748/822/1748 are all the same axis — folding/pruning a chain can skip evaluating an expression that would throw at runtime (e.g. toUInt8(s) with s = 'x'), so a query that throws with the setting off may succeed with it on. This is exception suppression, not wrong results, and @alexey-milovidov already addressed it on this PR ("we allow all syntactically equivalent expressions to be folded into one. It's explicitly documented. However, the reasoning behind this function is correct - ok to keep it.") and @wudidapaopao demonstrated the behavior pre-exists. Adding conservative canThrow guards would substantially narrow a default-on optimization and contradict that direction, so I have not implemented them — this is a call for the maintainers. The try/catch question at line 763 is in the same hands.

cc @alexey-milovidov @nihalzp

@alexey-milovidov

Copy link
Copy Markdown
Member

Continued this PR. The branch is MERGEABLE, only 13 commits behind master (merge base today), with no conflicts and a fresh CI run in progress on 5e129630, so no master merge was needed. I investigated the two CI failures that appeared on 5e129630 — both are pre-existing, unrelated issues in JOIN code that this PR does not touch:

1. AST fuzzer (amd_debug, targeted)Unexpected number of columns in result sample block (STID 2980-385f)

This is a resurfacing of the closed issue #100422 (comp-query-analyzer, correlated subqueries), not a regression here:

The error recurs disproportionately on this PR only because the targeted AST fuzzer is seeded with this PR's 04032_* tests, which are full of AND-comparison-chain queries on a multi-column table — precisely the shape tryOptimizeAndCompareChain rewrites. It is a seed-selection artifact, not a behavior change introduced here.

2. Stress test (arm_tsan)Invalid number of rows in Chunk (STID 3366-2d1d)

A known, master-wide stress flake with a fix already in progress:

  • The failing query is an ASOF/ANY RIGHT JOIN with EXCEPT ALL and join_algorithm = 'parallel_hash'/'hash' over tables asof_left/asof_right (not from this PR); the abort is in JoiningTransform::transformChunk::checkNumRowsIsConsistent (JoiningTransform.cpp:236). Again, no JOIN code is touched here.
  • This STID appears on master (PR=0) and across ~30 unrelated PRs over the last 90 days. The fix is open PR Fix Invalid number of rows in Chunk in JoiningTransform with additional join filter #106928 ("Fix Invalid number of rows in Chunk in JoiningTransform with additional join filter") by @groeneai — its branch is literally named for this STID (...chunk-3366-2d1d).

Remaining open clickhouse-gh/AI-Review items

The remaining unresolved findings (lines 843/1769) are all the same axis — folding/pruning a chain can skip evaluating an expression that would throw at runtime (e.g. toUInt8(s) with s = 'x'). As discussed above by @alexey-milovidov ("we allow all syntactically equivalent expressions to be folded into one. It's explicitly documented. However, the reasoning behind this function is correct - ok to keep it."), this is exception suppression rather than wrong results, and the behavior pre-exists; it is left as a maintainer design decision. The wrong-results issues raised earlier (the DateTime64/Time64 scale truncation and the non-convertible-constant collapse) are fixed.

In short: both CI reds are pre-existing and unrelated; no code change was made.

@groeneai

Copy link
Copy Markdown
Contributor

@alexey-milovidov you are right. STID 2980-385f is a pre-existing master bug, not caused by this PR, and #104890 did not cover this variant. I reproduced it locally on a clean master debug binary that contains none of this PR's code.

This PR is not involved. The crash reproduces with optimize_and_compare_chain = 0 and optimize_redundant_comparisons = 0 (the crash dump's Changed settings already showed optimize_redundant_comparisons = false). The diff here touches only LogicalExpressionOptimizerPass.{cpp,h}, convertFieldToType, Settings* and tests; HashJoin.cpp / ConcurrentHashJoin.cpp / JoiningTransform.cpp / Planner* / decorrelation are byte-identical to master.

Minimal deterministic trigger (binary-searched from the crash dump's settings):

allow_experimental_correlated_subqueries = 1
convert_query_to_cnf = 1

The trigger is convert_query_to_cnf, not the AND-comparison-chain logic. Same query without convert_query_to_cnf returns a result; with it, abort. Verified on both binaries:

Binary Settings Result
clean master (no PR code) allow_experimental_correlated_subqueries=1, convert_query_to_cnf=1 abort (same 8 expected 9 message)
clean master same, minus convert_query_to_cnf OK
this PR's branch ... convert_query_to_cnf=1, optimize_and_compare_chain=0, optimize_redundant_comparisons=0 abort
this PR's branch ... minus convert_query_to_cnf OK

The clean-master stack is identical to CI: HashJoin::getNonJoinedBlocks (HashJoin.cpp:1860) <- ConcurrentHashJoin::getNonJoinedBlocks (ConcurrentHashJoin.cpp:526) <- NonJoinedBlocksTransform::generate (JoiningTransform.cpp:628). The expected-column formula shows the synthetic exists(__table2) column on both the left block and columns_to_add, so convert_query_to_cnf + correlated-exists decorrelation builds a join whose result block double-counts that column (8 expected 9).

This is the same assertion as #100422 but a distinct upstream rewrite. #104890 fixed the optimize_and_compare_chain PLACEHOLDER-ColumnConst variant we root-caused on #100422; the convert_query_to_cnf path reaches the same overlap through a rewrite the PLACEHOLDER fix does not intercept. So #100422 should be reopened, and it belongs to the AST-fuzzer-internal-path class in #107951. I will reopen #100422 with this repro.

CI report for reference: https://s3.amazonaws.com/clickhouse-test-reports/json.html?PR=99736&sha=5e1296309b1381a48ad8ef8e3f506bf6c424c51a&name_0=PR&name_1=AST%20fuzzer%20%28amd_debug%2C%20targeted%29&name_1=AST%20fuzzer%20%28amd_debug%2C%20targeted%29

Session id: cron:clickhouse-worker-slot-7:20260630-101200

# Conflicts:
#	src/Analyzer/Passes/LogicalExpressionOptimizerPass.cpp
#	src/Core/Settings.cpp
@alexey-milovidov

Copy link
Copy Markdown
Member

Merged the latest master to resolve a conflict that appeared as master advanced (pushed 5a9a9043710, branch was CONFLICTING, now MERGEABLE again).

Two conflicts, both from master newly introducing the optimize_and_compare_chain_max_hash_work setting (a work budget for the existing optimize_and_compare_chain optimization) next to this PR's optimize_redundant_comparisons:

  • src/Core/Settings.cpp — kept both DECLAREs.
  • src/Analyzer/Passes/LogicalExpressionOptimizerPass.cpp — kept both Setting extern declarations; the rest merged cleanly, so master's budget machinery (andCompareChainHashBudgetExceeded) and this PR's pruning coexist, with enterImpl running tryOptimizeAndCompareChain before tryOptimizeAndCompareNotEqualsChain.

Verified both conflict files compile (-fsyntax-only) against the merged tree; the PR's own changes (convertFieldToType strict param, 04489_redundant_comparisons_datetime64_scale, 04032_*) are intact.

Comment thread src/Analyzer/Passes/LogicalExpressionOptimizerPass.cpp Outdated
…s DateTime constants

The comparison-chain pruning converts a comparison constant to the column type with
a strict (lossless) conversion and only folds/prunes when it succeeds. The reversibility
guard added for `DateTime64`/`Time64` scale reduction only checked decimal-backed results,
but `convertFieldToType` also silently truncates `DateTime`/`DateTime64` -> `Date`/`Date32`
(dropping the intra-day part) and `Float64` -> `Float32`, without honouring `strict`.

For a `Date` column, `d = toDate('2024-01-01') AND d != toDateTime('2024-01-01 12:34:56')`
is satisfiable by `d = '2024-01-01'`, because `Date`/`DateTime` comparisons are evaluated
in the `DateTime` domain (`d` promotes to `2024-01-01 00:00:00`, which differs from
`12:34:56`). But the `DateTime` constant truncated to `2024-01-01`, so the optimizer saw
`equals` vs `notEquals` on the same converted value and folded the whole `AND` to `false`,
dropping the row.

Apply the round-trip reversibility check in `tryConvertToColumnType` to all non-string
constants instead of only decimal-backed ones: a lossless conversion round-trips exactly,
so any lossy narrowing is now rejected and the optimization is conservatively skipped
(which only forgoes a fold and never changes results). String constants are excluded
because they are parsed directly at the column resolution (so never carry finer resolution)
and round-tripping them through the string rendering of a number/date would spuriously fail
(e.g. `Float64` `3.0` renders as `"3"`).

Add test `04498_redundant_comparisons_date_datetime_truncation`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@alexey-milovidov

Copy link
Copy Markdown
Member

Pushed 0badf99 to fix the remaining wrong-results case flagged by the AI review.

optimize_redundant_comparisons folded d = toDate('2024-01-01') AND d != toDateTime('2024-01-01 12:34:56') to false on Date/Date32 columns, dropping a row that must be kept: the comparison is evaluated in the DateTime domain (d promotes to 2024-01-01 00:00:00, which differs from 12:34:56), but the DateTime constant was truncated to the day.

convertFieldToType does not honour strict for DateTime/DateTime64 -> Date/Date32 (nor Float64 -> Float32), and the reversibility guard only covered decimal-backed results. tryConvertToColumnType now applies the round-trip reversibility check to all non-string constants; string constants keep their existing path because they are parsed at the column resolution and round-tripping them through the string rendering of a number/date would spuriously break correct folds (e.g. 3.0 -> "3").

Added 04498_redundant_comparisons_date_datetime_truncation; verified 04032_and_comparison_filter_optimization, 04032_and_comparison_filter_type_conversion, 04489_redundant_comparisons_datetime64_scale, 02952_conjunction_optimization, 03285_analyzer_extract_common_expr_bug, 04201, 04316, 04492 still pass locally.

@novikd novikd self-assigned this Jul 2, 2026
@alexey-milovidov

Copy link
Copy Markdown
Member

Merged master to clear a conflict that GitHub had started reporting as master advanced (optimize_and_compare_chain_max_hash_work and the QBit stride refactor in convertFieldToType.cpp landed alongside this PR's changes). The merge was clean — no manual conflict resolution was needed, and the branch is MERGEABLE again. Head is now a6f19603f7afc7935cae0bcab070983f9b6a3529 (was 0badf99316394de7e7d433397c0f6aa7af38cd9e); CI is re-running against current master.

Comment thread src/Analyzer/Passes/LogicalExpressionOptimizerPass.cpp
…_redundant_comparisons

Address the AI review Major on `LogicalExpressionOptimizerPass.cpp:422`: the
round-trip reversibility guard in `tryConvertToColumnType` is the correctness
barrier for several lossy `strict` conversions, but the existing regressions only
cover `DateTime64` scale reduction (`04489`) and `DateTime` -> `Date`/`Date32`
(`04498`). This adds `04499_redundant_comparisons_lossy_conversions` with focused
`optimize_redundant_comparisons = 0/1` equivalence tests for the remaining
guarded surfaces:

- `Time64` scale reduction,
- `DateTime64` -> `Date`/`Date32` truncation,
- `Float64` -> `Float32` narrowing.

Each scenario asserts the same result with the optimization enabled and disabled:
the lossy "keep the row" cases stay at their unoptimized result (the guard skips
the fold), while the lossless cases still fold. Verified locally against a build
of this branch.

Review: ClickHouse#99736 (comment)
@alexey-milovidov

Copy link
Copy Markdown
Member

Addressed the AI review Major on LogicalExpressionOptimizerPass.cpp:422 (round-trip guard test coverage) by adding 04499_redundant_comparisons_lossy_conversions in 7be586d57cf.

It adds focused optimize_redundant_comparisons = 0/1 equivalence tests for the lossy strict-conversion surfaces the round-trip reversibility check in tryConvertToColumnType guards, which previously lacked dedicated coverage (only DateTime64 scale in 04489 and DateTime -> Date/Date32 in 04498 were covered):

  • Time64 scale reduction,
  • DateTime64 -> Date/Date32 truncation,
  • Float64 -> Float32 narrowing.

Each case asserts the same result with the optimization enabled and disabled; verified locally against a Debug build of this branch (every enabled/disabled pair matches).

The remaining AI blockers are the exception-suppression axis (folding a chain skips evaluating a deterministic expression that would otherwise throw), which is the previously-documented maintainer design decision, not wrong results.

@alexey-milovidov

Copy link
Copy Markdown
Member

Merged the latest master (34bd051e8e8) to pick up the fix for the macOS Fast test (arm_darwin) failure.

The only non-flaky red on the previous tip was 04319_skip_unavailable_shards_mode_table_missing hanging in Poco::Net::SocketImpl::pollImpl on the native macOS runner — the known master-wide Darwin flake, unrelated to this PR (which touches only LogicalExpressionOptimizerPass, convertFieldToType, and settings). That test is now skipped on macOS via ci/defs/darwin.skip in #109254 (merged), so re-merging master brings the skip into this branch and clears the red on the next run.

The merge was clean (no conflicts); the two core source files (LogicalExpressionOptimizerPass.cpp, convertFieldToType.cpp) are byte-identical to the previous tip, and both new settings (optimize_redundant_comparisons, optimize_and_compare_chain_max_hash_work) remain present. The AI review verdict on the previous tip was already No new findings.

@clickhouse-gh

clickhouse-gh Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

LLVM Coverage Report

Metric Baseline Current Δ
Lines 85.60% 85.60% +0.00%
Functions 92.70% 92.70% +0.00%
Branches 77.80% 77.80% +0.00%

Changed lines: Changed C/C++ lines covered: 500/521 (95.97%) · Uncovered code

Full report · Diff report

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

can be tested Allows running workflows for external contributors pr-improvement Pull request with some product improvements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Redundant filter removal

6 participants