Skip to content

fix(spp_cel_domain): handle relational fields and SQL JOINs in CEL#41

Open
jeremi wants to merge 2 commits into19.0from
feat/cel-domain-fixes
Open

fix(spp_cel_domain): handle relational fields and SQL JOINs in CEL#41
jeremi wants to merge 2 commits into19.0from
feat/cel-domain-fixes

Conversation

@jeremi
Copy link
Member

@jeremi jeremi commented Feb 17, 2026

Summary

  • Handle one2many/many2many fields as bare CEL predicates instead of erroring
  • Use query.select() for SQL with JOINs instead of raw field access, fixing incorrect query generation
  • Add tests for relational predicate handling

Origin

Cherry-picked from openspp-modules-v2 branch claude/global-alliance-policy-basket.

Test plan

  • Existing CEL domain tests pass
  • New relational predicate tests pass
  • CEL expressions using x2many fields evaluate correctly

Note

Medium Risk
Touches core query translation/execution paths (SQL generation and predicate compilation), which could change result sets or performance for existing expressions; mitigated by added regression tests.

Overview
Fixes CEL compilation and SQL fast-path generation for relational lookups.

Domains converted to SQL now use query.select() (in both CelExecutor._domain_to_id_sql and SQLBuilder.select_ids_from_domain) so the generated subquery includes required FROM/JOIN clauses for related-field domains (e.g. gender_id.uri) and still applies record rules even for empty domains.

CEL translation now treats bare one2many/many2many fields as truthy predicates by compiling them to [(field, '!=', False)], and variable CRUD now invalidates all CEL caches via spp.cel.service.invalidate_caches(); adds targeted tests covering relational predicates and cache invalidation behavior.

Written by Cursor Bugbot for commit dbf4c17. This will update automatically on new commits. Configure here.

@gemini-code-assist
Copy link

Summary of Changes

Hello @jeremi, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the Common Expression Language (CEL) domain functionality by improving its ability to handle relational fields and generate accurate SQL queries. It allows 'one2many' and 'many2many' fields to be used intuitively as bare predicates, simplifying complex expressions. The changes also ensure that SQL queries involving related fields are correctly constructed with necessary JOINs, preventing data retrieval issues. Furthermore, the cache invalidation mechanism for CEL variables has been made more robust, guaranteeing that changes to variables are immediately reflected across all CEL components.

Highlights

  • Relational Field Predicates: Enabled 'one2many' and 'many2many' fields to be used as bare predicates in CEL expressions, interpreting them as checks for the presence of related records.
  • Improved SQL Generation: Switched to 'query.select()' for SQL generation in CEL domain conversions, ensuring correct handling of SQL JOINs for related field lookups and preventing incorrect query generation.
  • Centralized Cache Invalidation: Consolidated CEL cache invalidation logic to clear all relevant caches (resolver, translation, profile) in a coordinated manner when CEL variables are modified or deleted.
  • Comprehensive Testing: Introduced new dedicated tests for relational predicate handling and expanded existing tests to cover various scenarios of CEL variable cache invalidation.
Changelog
  • spp_cel_domain/models/cel_executor.py
    • Updated SQL generation for domain conversion to use 'query.select()' for better JOIN handling.
    • Adjusted a 'round()' function call for coverage calculation formatting.
  • spp_cel_domain/models/cel_sql_builder.py
    • Refactored 'select_ids_from_domain' to streamline SQL generation and incorporate 'query.select()' for relational field support.
  • spp_cel_domain/models/cel_translator.py
    • Modified '_flatten_attr' to support 'one2many' and 'many2many' fields as bare predicates, treating them as "not empty" checks.
  • spp_cel_domain/models/cel_variable.py
    • Revised '_invalidate_resolver_cache' to call a centralized 'cel_service.invalidate_caches()' method, ensuring all CEL caches are cleared.
  • spp_cel_domain/tests/init.py
    • Added 'test_cel_relational_predicate' to the test suite.
  • spp_cel_domain/tests/test_cel_relational_predicate.py
    • Introduced new tests for 'one2many' and 'many2many' fields as bare CEL predicates, verifying compilation and correct domain generation.
  • spp_cel_domain/tests/test_cel_variable.py
    • Added a new test class 'TestCELVariableCacheInvalidation' with multiple tests covering variable creation, modification, deletion, and their impact on various CEL caches.
Activity
  • No specific activity (comments, reviews, progress updates) has been recorded for this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces important fixes and features, including correctly handling SQL generation for domains with related fields using query.select() in cel_executor.py and cel_sql_builder.py, enabling the use of relational fields (one2many, many2many) as bare predicates in CEL expressions via cel_translator.py, and enhancing cache invalidation in cel_variable.py. A security audit confirmed no vulnerabilities, highlighting the use of Odoo's SQL objects and SQL.identifier to prevent SQL injection, and robust security measures in the CEL parser against sandbox escape or denial-of-service attacks. The new tests are comprehensive, though one could be made more specific.

Comment on lines 77 to 82
found = False
for leaf in domain:
if isinstance(leaf, tuple) and leaf[0] == "program_membership_ids" and leaf[1] == "!=":
found = True
break
self.assertTrue(found, f"Expected '!= False' domain for one2many, got: {domain}")

Choose a reason for hiding this comment

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

medium

The current test assertion is a bit weak as it only checks for the presence of the != operator but not the full domain leaf ('program_membership_ids', '!=', False). This could lead to a false positive if the generated domain was, for example, ('program_membership_ids', '!=', True).

To make the test more robust and specific, I suggest checking for the exact expected tuple within the domain. This can be done more concisely using any().

        expected_leaf = ("program_membership_ids", "!=", False)
        # The domain can be complex due to base_domains, so we check if any leaf matches.
        found = any(leaf == expected_leaf for leaf in domain if isinstance(leaf, tuple))
        self.assertTrue(found, f"Expected '{expected_leaf}' to be in domain, but got: {domain}")

@codecov
Copy link

codecov bot commented Feb 18, 2026

Codecov Report

❌ Patch coverage is 93.29268% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 69.00%. Comparing base (5ac7496) to head (3b5c0ce).

Files with missing lines Patch % Lines
..._cel_domain/tests/test_cel_relational_predicate.py 69.69% 10 Missing ⚠️
spp_cel_domain/models/cel_variable.py 75.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             19.0      #41      +/-   ##
==========================================
- Coverage   71.31%   69.00%   -2.32%     
==========================================
  Files         299      404     +105     
  Lines       23618    37746   +14128     
==========================================
+ Hits        16844    26047    +9203     
- Misses       6774    11699    +4925     
Flag Coverage Δ
endpoint_route_handler ?
fastapi ?
spp_alerts ?
spp_api_v2 89.14% <ø> (ø)
spp_api_v2_change_request 66.61% <ø> (ø)
spp_api_v2_cycles 65.45% <ø> (ø)
spp_api_v2_data 48.67% <ø> (ø)
spp_api_v2_entitlements 68.43% <ø> (?)
spp_api_v2_products 64.39% <ø> (?)
spp_api_v2_service_points 63.12% <ø> (?)
spp_api_v2_vocabulary 43.70% <ø> (?)
spp_approval 43.40% <ø> (?)
spp_audit 69.47% <ø> (?)
spp_base_common 92.81% <ø> (ø)
spp_cel_domain 77.05% <93.29%> (?)
spp_cel_event 81.23% <ø> (?)
spp_programs 49.56% <ø> (ø)
spp_security 51.08% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

- Handle one2many/many2many fields as bare CEL predicates
- Use query.select() for SQL with JOINs instead of raw field access
- Add tests for relational predicate handling
Replace weak operator-only check with an exact tuple match to prevent
false positives from incorrect values (e.g. True instead of False).
@jeremi jeremi force-pushed the feat/cel-domain-fixes branch from dbf4c17 to 3b5c0ce Compare February 18, 2026 14:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments