BE-482: HashQL: Remove logical not from MIR and fix postgres boolean lowering#8595
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## bm/be-474-hashql-take-into-account-terminators-eligibility-when #8595 +/- ##
===================================================================================================
- Coverage 70.79% 63.80% -6.99%
===================================================================================================
Files 1012 1183 +171
Lines 98707 130156 +31449
Branches 4542 4969 +427
===================================================================================================
+ Hits 69876 83046 +13170
- Misses 28103 46249 +18146
- Partials 728 861 +133 Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| None => { | ||
| self.diagnostics | ||
| .push(ambiguous_integer_type(span, BinOp::BitAnd.as_str())); | ||
| return Expression::Constant(query::Constant::Null); |
Check warning
Code scanning / clippy
unneeded return statement Warning
| None => { | ||
| self.diagnostics | ||
| .push(ambiguous_integer_type(span, BinOp::BitOr.as_str())); | ||
| return Expression::Constant(query::Constant::Null); |
Check warning
Code scanning / clippy
unneeded return statement Warning
| linter_config | ||
| .override_dialect(DialectKind::Postgres) | ||
| .expect("dialect should be loaded"); | ||
| let mut linter = |
Check warning
Code scanning / clippy
variable does not need to be mutable Warning test
| .lint_string(&format!("SELECT {sql}"), None, true) | ||
| .expect("should be valid SQL"); | ||
|
|
||
| let mut fixed = linted.fix_string(); |
Check warning
Code scanning / clippy
variable does not need to be mutable Warning test
| .expect("should be valid SQL"); | ||
|
|
||
| let mut fixed = linted.fix_string(); | ||
| let fixed: String = fixed[7..] |
Check warning
Code scanning / clippy
indexing into a string may panic if the index is within a UTF-8 character Warning test
| let mut fixed = linted.fix_string(); | ||
| let fixed: String = fixed[7..] | ||
| .lines() | ||
| .map(|line| &line[4..]) |
Check warning
Code scanning / clippy
indexing into a string may panic if the index is within a UTF-8 character Warning test
| linter_config | ||
| .override_dialect(DialectKind::Postgres) | ||
| .expect("dialect should be loaded"); | ||
| let mut linter = |
Check warning
Code scanning / clippy
variable does not need to be mutable Warning test
| match env.r#type(r#type).kind.primitive()? { | ||
| PrimitiveType::Boolean => Some(IntegerType::Boolean), | ||
| PrimitiveType::Integer => Some(IntegerType::Integer), | ||
| _ => None, |
Check warning
Code scanning / clippy
wildcard match will also match any future added variants Warning
| Operand::Constant(hashql_mir::body::constant::Constant::Primitive( | ||
| hashql_core::value::Primitive::Integer(_), | ||
| )) => Some(IntegerType::Integer), | ||
| _ => None, |
Check warning
Code scanning / clippy
wildcard matches only a single variant and will also match any future added variants Warning
Merging this PR will not alter performance
Comparing Footnotes
|
Benchmark results
|
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| resolve_policies_for_actor | user: empty, selectivity: high, policies: 2002 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: medium, policies: 1001 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: high, policies: 3314 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: medium, policies: 1526 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: high, policies: 2078 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: medium, policies: 1033 | Flame Graph |
policy_resolution_medium
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| resolve_policies_for_actor | user: empty, selectivity: high, policies: 102 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: medium, policies: 51 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: high, policies: 269 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: medium, policies: 107 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: high, policies: 133 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: medium, policies: 63 | Flame Graph |
policy_resolution_none
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| resolve_policies_for_actor | user: empty, selectivity: high, policies: 2 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: medium, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: high, policies: 8 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: medium, policies: 3 | Flame Graph |
policy_resolution_small
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| resolve_policies_for_actor | user: empty, selectivity: high, policies: 52 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: medium, policies: 25 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: high, policies: 94 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: medium, policies: 26 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: high, policies: 66 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: medium, policies: 29 | Flame Graph |
read_scaling_complete
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| entity_by_id;one_depth | 1 entities | Flame Graph | |
| entity_by_id;one_depth | 10 entities | Flame Graph | |
| entity_by_id;one_depth | 25 entities | Flame Graph | |
| entity_by_id;one_depth | 5 entities | Flame Graph | |
| entity_by_id;one_depth | 50 entities | Flame Graph | |
| entity_by_id;two_depth | 1 entities | Flame Graph | |
| entity_by_id;two_depth | 10 entities | Flame Graph | |
| entity_by_id;two_depth | 25 entities | Flame Graph | |
| entity_by_id;two_depth | 5 entities | Flame Graph | |
| entity_by_id;two_depth | 50 entities | Flame Graph | |
| entity_by_id;zero_depth | 1 entities | Flame Graph | |
| entity_by_id;zero_depth | 10 entities | Flame Graph | |
| entity_by_id;zero_depth | 25 entities | Flame Graph | |
| entity_by_id;zero_depth | 5 entities | Flame Graph | |
| entity_by_id;zero_depth | 50 entities | Flame Graph |
read_scaling_linkless
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| entity_by_id | 1 entities | Flame Graph | |
| entity_by_id | 10 entities | Flame Graph | |
| entity_by_id | 100 entities | Flame Graph | |
| entity_by_id | 1000 entities | Flame Graph | |
| entity_by_id | 10000 entities | Flame Graph |
representative_read_entity
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/block/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/book/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/building/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/organization/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/page/v/2
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/person/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/playlist/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/song/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/uk-address/v/1
|
Flame Graph |
representative_read_entity_type
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| get_entity_type_by_id | Account ID: bf5a9ef5-dc3b-43cf-a291-6210c0321eba
|
Flame Graph |
representative_read_multiple_entities
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| entity_by_property | traversal_paths=0 | 0 | |
| entity_by_property | traversal_paths=255 | 1,resolve_depths=inherit:1;values:255;properties:255;links:127;link_dests:126;type:true | |
| entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:0;link_dests:0;type:false | |
| entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:1;link_dests:0;type:true | |
| entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:2;links:1;link_dests:0;type:true | |
| entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:2;properties:2;links:1;link_dests:0;type:true | |
| link_by_source_by_property | traversal_paths=0 | 0 | |
| link_by_source_by_property | traversal_paths=255 | 1,resolve_depths=inherit:1;values:255;properties:255;links:127;link_dests:126;type:true | |
| link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:0;link_dests:0;type:false | |
| link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:1;link_dests:0;type:true | |
| link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:2;links:1;link_dests:0;type:true | |
| link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:2;properties:2;links:1;link_dests:0;type:true |
scenarios
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| full_test | query-limited | Flame Graph | |
| full_test | query-unlimited | Flame Graph | |
| linked_queries | query-limited | Flame Graph | |
| linked_queries | query-unlimited | Flame Graph |

🌟 What is the purpose of this PR?
Add proper boolean vs integer operator dispatch in the PostgreSQL SQL lowering, with ICE diagnostics for ambiguous cases and snapshot test coverage for all operator paths, as well as remove dedicated
UnOpfor logical not, in favour of bitwise not (similar to logical and and or being replaced).🔍 What does this change?
NOTvs~,ANDvs&,ORvs|). The lowering now inspects the operand type viainteger_type()and emits the correct SQL form.todo!()panics in the filter compiler with a properAmbiguousIntegerTypediagnostic (Severity::Bug). This fires if the operand type cannot be classified as boolean or integer, which is currently unreachable but would surface issues if GVN ever produces union-typed operands.UnOpdocumentation: Added enum-level and per-variant doc comments matching the existingBinOpstyle.op!macro fix: Unary operator arms now producehashql_mir::body::rvalue::UnOpdirectly instead ofhashql_hir::node::operation::UnOp, fixing a type mismatch in the builder.binary_bitand_boolean_and:&on booleans lowers toANDbinary_bitor_boolean_or:|on booleans lowers toORbinary_bitor_bigint_cast:|on integers lowers to|Pre-Merge Checklist 🚀
🚢 Has this modified a publishable library?
This PR:
📜 Does this require a change to the docs?
The changes in this PR:
🕸️ Does this require a change to the Turbo Graph?
The changes in this PR:
A future improvement would be to trace the type via the compiled SQL expression side rather than through the MIR operand type. This would allow disambiguation in cases where
integer_type()cannot resolve (e.g. union types from GVN), since the SQL expression tree could carry its own type information. This is currently not needed as there's no way a union like that could be created.🐾 Next steps
integer_type()for operator dispatch, which would handle ambiguous union types gracefully instead of emitting an ICE.🛡 What tests cover this?
binary_bitand_boolean_and,binary_bitor_boolean_or,binary_bitor_bigint_castunary_not(boolean NOT),unary_bitnot(integer NOT),binary_bitand_bigint_cast(integer AND)❓ How to test this?
cargo test --package hashql-eval -- postgres::filter::tests