Skip to content

Comments

fix: SQLSTATE42P01 errors BED-7470#35

Open
urangel wants to merge 2 commits intomainfrom
BED-7470
Open

fix: SQLSTATE42P01 errors BED-7470#35
urangel wants to merge 2 commits intomainfrom
BED-7470

Conversation

@urangel
Copy link

@urangel urangel commented Feb 20, 2026

Queries that contain bound right nodes that are referenced in a different frame result in translations that surface the SQLSTATE42P01 error upon query execution. Selectivity optimization rewrites that flip query directions seem to be part of the underlying issue.

This fix mitigates these errors in some queries that are experiencing the issue by increasing the selectivity measure for bound nodes which decreases the chance of a query rewrite.

Each query noted in the issues has been added as a test case and were verified to execute without error.

Summary by CodeRabbit

Release Notes

  • Tests

    • Updated pattern matching test cases for multi-hop path traversals
    • Added new test cases for complex recursive path patterns with depth-bounded constraints
    • Enhanced test coverage for path filtering and aggregation scenarios
  • Performance

    • Refined query selectivity calculation to optimize query planning and execution efficiency

@coderabbitai
Copy link

coderabbitai bot commented Feb 20, 2026

Walkthrough

This PR updates test translation cases in Cypher/PostgreSQL patterns, replacing domain/name-based filtering with objectid-based predicates in multipart patterns, adding two new pattern binding test cases with recursive traversals, and adjusting a selectivity weight constant for bound identifier optimization.

Changes

Cohort / File(s) Summary
Test Translation Cases
cypher/models/pgsql/test/translation_cases/multipart.sql, cypher/models/pgsql/test/translation_cases/pattern_binding.sql
Updated multipart test case to use objectid-based filtering instead of domain/name patterns; removed one complex edge traversal case. Added two new pattern binding test cases: one with multi-hop path and system_tags filter, and another with samaccountname filter and depth-bounded recursive traversal (1..3).
Selectivity Optimization
cypher/models/pgsql/translate/selectivity.go
Introduced new constant selectivityWeightBoundIdentifier = 700 and updated MeasureSelectivity to apply this weight instead of selectivityWeightNarrowSearch when owningIdentifierBound is true, altering selectivity bias for bound identifiers.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Suggested reviewers

  • zinic
  • kpom-specter

Poem

🐰 With objectid's gleam and patterns refined,
New test cases hop through paths well-designed,
Selectivity weights now dance just so,
Our recursive queries run swift and aglow!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main objective of the PR: fixing SQLSTATE42P01 errors referenced in issue BED-7470, which is the primary goal of the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch BED-7470

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cypher/models/pgsql/test/translation_cases/pattern_binding.sql`:
- Around line 56-57: The recursive CTE never applies the terminal-node predicate
because the s2 column "satisfied" is computed but not used and the base case
sets satisfied to false; update the s2 recursive definition in the CTE (symbols:
s1, s2, satisfied, path, ep0) so the base case computes satisfied using the same
expression as the recursive step (e.g. check coalesce((n2.properties ->>
'system_tags'),'')::text like '%admin_tier_0%' and n2.kind_ids @>
array[2]::int2[]) and then add a WHERE filter in the outer select from s2 (or
inside s1) to only emit rows where satisfied = true before constructing p;
ensure the modified logic mirrors the patterns used around lines 10 and 42
(compute satisfied in both base and recursive branches and filter by satisfied
when selecting paths).

Comment on lines +56 to +57
-- case: match p = (:NodeKind1)-[:EdgeKind1]->(:NodeKind2)-[:EdgeKind2*1..]->(t:NodeKind2) where coalesce(t.system_tags, '') contains 'admin_tier_0' return p limit 1000
with s0 as (select (e0.id, e0.start_id, e0.end_id, e0.kind_id, e0.properties)::edgecomposite as e0, (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from edge e0 join node n0 on n0.id = e0.start_id join node n1 on n1.id = e0.end_id where n1.kind_ids operator (pg_catalog.@>) array [2]::int2[] and e0.kind_id = any (array [3]::int2[]) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]), s1 as (with recursive s2(root_id, next_id, depth, satisfied, is_cycle, path) as (select e1.start_id, e1.end_id, 1, false, e1.start_id = e1.end_id, array [e1.id] from s0 join edge e1 on (s0.n1).id = e1.start_id join node n2 on n2.id = e1.end_id where e1.kind_id = any (array [4]::int2[]) union select s2.root_id, e1.end_id, s2.depth + 1, (coalesce((n2.properties ->> 'system_tags'), '')::text like '%admin_tier_0%') and n2.kind_ids operator (pg_catalog.@>) array [2]::int2[], e1.id = any (s2.path), s2.path || e1.id from s2 join edge e1 on e1.start_id = s2.next_id join node n2 on n2.id = e1.end_id where e1.kind_id = any (array [4]::int2[]) and s2.depth <= 15 and not s2.is_cycle) select s0.e0 as e0, (select array_agg((e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite) from edge e1 where e1.id = any (s2.path)) as e1, s2.path as ep0, s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 from s0, s2 join node n1 on n1.id = s2.root_id join node n2 on n2.id = s2.next_id) select edges_to_path(variadic array [(s1.e0).id]::int8[] || s1.ep0)::pathcomposite as p from s1 limit 1000;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's check the file size and read lines around 56-57
wc -l cypher/models/pgsql/test/translation_cases/pattern_binding.sql

Repository: SpecterOps/DAWGS

Length of output: 125


🏁 Script executed:

# Read the file to see the context around lines 56-57
sed -n '50,70p' cypher/models/pgsql/test/translation_cases/pattern_binding.sql | cat -n

Repository: SpecterOps/DAWGS

Length of output: 8025


🏁 Script executed:

# Search for all satisfied-related patterns in the file
rg -n "satisfied" cypher/models/pgsql/test/translation_cases/pattern_binding.sql -A 2 -B 2

Repository: SpecterOps/DAWGS

Length of output: 7644


Missing terminal-node filter in recursive path.
Line 56–57 computes satisfied but never filters on it, so the t.system_tags constraint from the Cypher predicate is not enforced in the SQL. The base case should also compute satisfied correctly rather than hardcoding false. Please add the filter and update the base case to match the pattern used in similar cases (lines 10, 42).

🐛 Suggested fix
- with s0 as (select ...), s1 as (with recursive s2(root_id, next_id, depth, satisfied, is_cycle, path) as (
-   select e1.start_id, e1.end_id, 1, false, e1.start_id = e1.end_id, array [e1.id]
+ with s0 as (select ...), s1 as (with recursive s2(root_id, next_id, depth, satisfied, is_cycle, path) as (
+   select e1.start_id, e1.end_id, 1,
+     (coalesce((n2.properties ->> 'system_tags'), '')::text like '%admin_tier_0%')
+     and n2.kind_ids operator (pg_catalog.@>) array [2]::int2[],
+     e1.start_id = e1.end_id, array [e1.id]
    from s0 join edge e1 on (s0.n1).id = e1.start_id join node n2 on n2.id = e1.end_id
    where e1.kind_id = any (array [4]::int2[])
    union
    select s2.root_id, e1.end_id, s2.depth + 1,
      (coalesce((n2.properties ->> 'system_tags'), '')::text like '%admin_tier_0%')
      and n2.kind_ids operator (pg_catalog.@>) array [2]::int2[],
      e1.id = any (s2.path), s2.path || e1.id
    from s2 join edge e1 on e1.start_id = s2.next_id join node n2 on n2.id = e1.end_id
    where e1.kind_id = any (array [4]::int2[]) and s2.depth <= 15 and not s2.is_cycle
- ) select ... from s0, s2 join node n1 on n1.id = s2.root_id join node n2 on n2.id = s2.next_id) select ...
+ ) select ... from s0, s2 join node n1 on n1.id = s2.root_id join node n2 on n2.id = s2.next_id
+   where s2.satisfied
+ ) select ...
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cypher/models/pgsql/test/translation_cases/pattern_binding.sql` around lines
56 - 57, The recursive CTE never applies the terminal-node predicate because the
s2 column "satisfied" is computed but not used and the base case sets satisfied
to false; update the s2 recursive definition in the CTE (symbols: s1, s2,
satisfied, path, ep0) so the base case computes satisfied using the same
expression as the recursive step (e.g. check coalesce((n2.properties ->>
'system_tags'),'')::text like '%admin_tier_0%' and n2.kind_ids @>
array[2]::int2[]) and then add a WHERE filter in the outer select from s2 (or
inside s1) to only emit rows where satisfied = true before constructing p;
ensure the modified logic mirrors the patterns used around lines 10 and 42
(compute satisfied in both base and recursive branches and filter by satisfied
when selecting paths).

Copy link
Contributor

@seanjSO seanjSO left a comment

Choose a reason for hiding this comment

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

I think this looks good! It's probably still worth getting another set of eyes here because I am still trying to get my brain wrapped around selectivity...

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.

2 participants