Conversation
WalkthroughThe PR introduces comprehensive PostgreSQL translation support for Cypher's Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
drivers/pg/query/sql/schema_up.sql (1)
461-464: Preferbigintcounters for frontier sizes.
count(*)is bigint in PostgreSQL; storing inint4can overflow on large traversals. Consider wideningremaining,forward_front_size, andbackward_front_sizetoint8.Also applies to: 493-496, 700-702
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@drivers/pg/query/sql/schema_up.sql` around lines 461 - 464, The procedure/function currently declares and returns 32-bit ints (e.g. "returns int4 as" and variables remaining, forward_front_size, backward_front_size) which can overflow for large counts; change the function return type from int4 to int8 and widen the local variables remaining, forward_front_size, and backward_front_size to int8 (bigint) and update any related casts/usages of count(*) to use bigint to match; apply the same change to the other occurrences noted (the other function blocks around the other ranges).
🤖 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/unwind.sql`:
- Around line 32-33: The test's expected SQL preserves duplicates because it
uses "select i1 as x from s0, unnest(i0) as i1" but the Cypher was "return
distinct x"; update the expected SQL to enforce distinctness by using SELECT
DISTINCT (e.g., "select distinct i1 as x ...") so the fixture asserts
duplicate-removal; locate the clause around s0 / i0 / i1 and change the SELECT
to DISTINCT to match the original "return distinct x" semantics.
In `@cypher/models/pgsql/translate/projection.go`:
- Around line 443-453: The unnest/UNWIND sources are being appended to
sqlSelect.From only at projection time (in the block that uses
part.unwindClauses, pgsql.FunctionUnnest, pgsql.AliasedExpression and
unwind.Binding.Identifier), which is too late for downstream clause translation;
instead, materialize those unnest sources into the query part's FROM pipeline so
later MATCH/WHERE/SUBPART translation can bind to the new aliases. Move the
logic that converts part.unwindClauses -> pgsql.FromClause (the
pgsql.FunctionCall(FunctionUnnest) wrapped in pgsql.AliasedExpression with
unwind.Binding.Identifier) into the earlier FROM-building stage for the query
part (the same pipeline that produces sqlSelect.From for other sources), and
apply the same change for the other occurrence noted (the similar block around
lines 493-503) so UNWINDs are present before subsequent clause translation.
In `@cypher/models/pgsql/translate/unwind.go`:
- Around line 18-26: The current PushFrame call in unwind.go creates a real
Frame that downstream tail-projection treats as having a FROM source; change the
synthetic UNWIND bookkeeping frame so it cannot be mistaken for a real SQL
source: when you create the frame in the s.scope.PushFrame() branch, mark it as
synthetic/bookkeeping (e.g., set a Frame.Synthetic or Frame.IsBookkeeping flag
or keep its Source/Relation nil) and update the tail-projection logic (the code
that inspects s.query.CurrentPart().Frame to decide to synthesize a FROM/CTE) to
skip creating a relation when that flag is set (or when Frame.Source is nil).
Ensure references include the PushFrame call site and
s.query.CurrentPart().Frame so the synthetic frame is recognized and not emitted
as a SQL source.
In `@drivers/pg/query/sql/schema_up.sql`:
- Around line 577-583: The deduplication and visited-tracking currently collapse
paths across different roots because deduped uses DISTINCT ON
(next_front.next_id) and visited is keyed by next_id alone; modify the dedupe
and all visited table usages to include root_id so they operate on (root_id,
next_id) pairs: update the CTE deduped to use DISTINCT ON (next_front.root_id,
next_front.next_id) and adjust all references/insertions/SELECTs against the
visited table and any joins with next_front (the visited-marking logic around
visited, and any EXISTS/NOT EXISTS checks) to include root_id alongside next_id
so visitation and pruning are scoped per root_id. Ensure any ORDER BY or GROUP
BY that relied on next_id is updated to include root_id where appropriate.
---
Nitpick comments:
In `@drivers/pg/query/sql/schema_up.sql`:
- Around line 461-464: The procedure/function currently declares and returns
32-bit ints (e.g. "returns int4 as" and variables remaining, forward_front_size,
backward_front_size) which can overflow for large counts; change the function
return type from int4 to int8 and widen the local variables remaining,
forward_front_size, and backward_front_size to int8 (bigint) and update any
related casts/usages of count(*) to use bigint to match; apply the same change
to the other occurrences noted (the other function blocks around the other
ranges).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 10ed1a64-19ad-4bfb-ade7-f60dec38327c
📒 Files selected for processing (9)
cmd/benchmark/main.gocypher/models/pgsql/test/translation_cases/unwind.sqlcypher/models/pgsql/translate/model.gocypher/models/pgsql/translate/projection.gocypher/models/pgsql/translate/translator.gocypher/models/pgsql/translate/unwind.gocypher/models/pgsql/translate/with.godrivers/pg/query/sql/schema_up.sqlintegration/cypher_test.go
💤 Files with no reviewable changes (1)
- integration/cypher_test.go
There was a problem hiding this comment.
🧹 Nitpick comments (1)
cypher/models/pgsql/translate/model.go (1)
683-697: Minor typo in method name:AddIdentifershould beAddIdentifier.The method name has a typo (
Identiferinstead ofIdentifier). While this doesn't affect functionality, it could cause confusion and inconsistency with standard naming.✏️ Suggested fix
-// AddIdentifer appends a from clause for frameID if it has not been seen before. -func (s *FromClauseBuilder) AddIdentifer(frameID pgsql.Identifier) { +// AddIdentifier appends a from clause for frameID if it has not been seen before. +func (s *FromClauseBuilder) AddIdentifier(frameID pgsql.Identifier) {Note: Also update the call site in
AddBinding(line 702) to useAddIdentifier.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cypher/models/pgsql/translate/model.go` around lines 683 - 697, Rename the method AddIdentifer to AddIdentifier to fix the typo: update the function declaration on the FromClauseBuilder receiver and all call sites (notably the call from AddBinding) to use the new AddIdentifier name; ensure the identifier's visibility/signature remains the same (receiver type, parameter pgsql.Identifier) so no other behavior changes are introduced and run tests/compile to catch any missed references.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@cypher/models/pgsql/translate/model.go`:
- Around line 683-697: Rename the method AddIdentifer to AddIdentifier to fix
the typo: update the function declaration on the FromClauseBuilder receiver and
all call sites (notably the call from AddBinding) to use the new AddIdentifier
name; ensure the identifier's visibility/signature remains the same (receiver
type, parameter pgsql.Identifier) so no other behavior changes are introduced
and run tests/compile to catch any missed references.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: c66defab-d361-4923-9f44-e766b8bf889d
📒 Files selected for processing (10)
cypher/models/pgsql/format/format.gocypher/models/pgsql/model.gocypher/models/pgsql/test/translation_cases/create.sqlcypher/models/pgsql/translate/create.gocypher/models/pgsql/translate/model.gocypher/models/pgsql/translate/node.gocypher/models/pgsql/translate/projection.gocypher/models/pgsql/translate/relationship.gocypher/models/pgsql/translate/translator.gointegration/testdata/cases/create_inline.json
✅ Files skipped from review due to trivial changes (1)
- integration/testdata/cases/create_inline.json
🚧 Files skipped from review as they are similar to previous changes (1)
- cypher/models/pgsql/translate/projection.go
|
@CodeRabbit yo, blast me and review this again |
|
✅ Actions performedFull review triggered. |
|
Update unit tests to use a default graph ID so that query forms match integration tests |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
cypher/models/pgsql/translate/model.go (1)
709-714: Nil-guardbindinginAddBinding.
binding.LastProjectionwill panic ifbindingis nil. Callers most likely always pass a resolved binding, but a cheap guard avoids a confusing NPE if an upstream lookup ever returns nil (e.g., partially failed translation paths).🛡️ Suggested guard
func (s *FromClauseBuilder) AddBinding(binding *BoundIdentifier) { - if binding.LastProjection != nil { + if binding != nil && binding.LastProjection != nil { s.AddIdentifer(binding.LastProjection.Binding.Identifier) } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cypher/models/pgsql/translate/model.go` around lines 709 - 714, The AddBinding method on FromClauseBuilder will panic if the passed binding is nil because it dereferences binding.LastProjection; add a nil-guard at the start of FromClauseBuilder.AddBinding to return early if binding == nil (or handle accordingly) before accessing binding.LastProjection, then proceed to check LastProjection and call s.AddIdentifer(binding.LastProjection.Binding.Identifier) only when non-nil; this protects BoundIdentifier consumers from NPEs when upstream lookups return nil.
🤖 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/testcase.go`:
- Line 208: In AssertLive, translate.Translate is incorrectly called with
context.Background(), which ignores the test-provided ctx; replace
context.Background() with the passed-in ctx so the translation respects
cancellation/deadlines and context values (i.e., change the translate.Translate
call in AssertLive to use ctx when invoking translate.Translate(ctx,
regularQuery, driver.KindMapper(), s.CypherParams, defaultGraph.ID)).
In `@cypher/models/pgsql/test/translation_cases/multipart.sql`:
- Line 39: The s2 CTE multiplies traversal rows because its FROM clause includes
"s1, s3" which is uncorrelated to s3.root_id; this repeats each s3 row for every
s1 row and inflates the count(s2.n1). Fix by either removing s1 from the s2 FROM
when it isn't referenced, or correlate s1 to s3 (e.g., join s1 ON (s1.n0).id =
s3.root_id) so the carried frame (n0) is bound per traversal root_id; update the
s2 select/from to use the correlated join instead of the cross join.
In `@cypher/models/pgsql/translate/model.go`:
- Around line 693-714: Rename the exported method FromClauseBuilder.AddIdentifer
to AddIdentifier everywhere (update the method declaration and all call sites
such as the usage in projection.go where fromClauseBuilder.AddIdentifer(...) is
invoked, and update the related comment text), and in
FromClauseBuilder.AddBinding add a defensive nil check for the binding parameter
before dereferencing binding.LastProjection (i.e., return early if binding ==
nil) while keeping the existing check for binding.LastProjection != nil.
---
Nitpick comments:
In `@cypher/models/pgsql/translate/model.go`:
- Around line 709-714: The AddBinding method on FromClauseBuilder will panic if
the passed binding is nil because it dereferences binding.LastProjection; add a
nil-guard at the start of FromClauseBuilder.AddBinding to return early if
binding == nil (or handle accordingly) before accessing binding.LastProjection,
then proceed to check LastProjection and call
s.AddIdentifer(binding.LastProjection.Binding.Identifier) only when non-nil;
this protects BoundIdentifier consumers from NPEs when upstream lookups return
nil.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 18334c8b-c9cc-4e99-8015-e31cbe59f0d8
📒 Files selected for processing (26)
cmd/benchmark/main.gocypher/models/pgsql/format/format.gocypher/models/pgsql/model.gocypher/models/pgsql/test/query_test.gocypher/models/pgsql/test/testcase.gocypher/models/pgsql/test/translation_cases/create.sqlcypher/models/pgsql/test/translation_cases/multipart.sqlcypher/models/pgsql/test/translation_cases/nodes.sqlcypher/models/pgsql/test/translation_cases/stepwise_traversal.sqlcypher/models/pgsql/test/translation_cases/unwind.sqlcypher/models/pgsql/translate/create.gocypher/models/pgsql/translate/format.gocypher/models/pgsql/translate/model.gocypher/models/pgsql/translate/node.gocypher/models/pgsql/translate/pattern.gocypher/models/pgsql/translate/projection.gocypher/models/pgsql/translate/relationship.gocypher/models/pgsql/translate/tracking.gocypher/models/pgsql/translate/translator.gocypher/models/pgsql/translate/unwind.gocypher/models/pgsql/translate/with.gocypher/models/pgsql/visualization/visualizer_test.godrivers/pg/transaction.gointegration/cypher_test.gointegration/testdata/cases/create_inline.jsonintegration/testdata/cases/unwind_inline.json
💤 Files with no reviewable changes (1)
- integration/cypher_test.go
✅ Files skipped from review due to trivial changes (4)
- cmd/benchmark/main.go
- integration/testdata/cases/create_inline.json
- integration/testdata/cases/unwind_inline.json
- cypher/models/pgsql/test/translation_cases/create.sql
🚧 Files skipped from review as they are similar to previous changes (8)
- cypher/models/pgsql/model.go
- cypher/models/pgsql/format/format.go
- cypher/models/pgsql/translate/with.go
- cypher/models/pgsql/test/translation_cases/unwind.sql
- cypher/models/pgsql/translate/relationship.go
- cypher/models/pgsql/translate/projection.go
- cypher/models/pgsql/translate/node.go
- cypher/models/pgsql/translate/create.go
| t.Fatalf("Driver has no default graph set") | ||
| } | ||
|
|
||
| if translation, err := translate.Translate(context.Background(), regularQuery, driver.KindMapper(), s.CypherParams, defaultGraph.ID); err != nil { |
There was a problem hiding this comment.
Use the passed ctx instead of context.Background() in AssertLive.
AssertLive accepts a ctx context.Context and correctly threads it into driver.Run on Line 213, but translate.Translate here is invoked with context.Background(). If the test's context carries a deadline/cancellation or test-scoped values (e.g., for the context-aware kind mapper), they are ignored during translation. The pre-existing WriteTo/Assert paths have no caller-provided context, but AssertLive does.
🛠️ Proposed fix
- if translation, err := translate.Translate(context.Background(), regularQuery, driver.KindMapper(), s.CypherParams, defaultGraph.ID); err != nil {
+ if translation, err := translate.Translate(ctx, regularQuery, driver.KindMapper(), s.CypherParams, defaultGraph.ID); err != nil {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if translation, err := translate.Translate(context.Background(), regularQuery, driver.KindMapper(), s.CypherParams, defaultGraph.ID); err != nil { | |
| if translation, err := translate.Translate(ctx, regularQuery, driver.KindMapper(), s.CypherParams, defaultGraph.ID); err != nil { |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@cypher/models/pgsql/test/testcase.go` at line 208, In AssertLive,
translate.Translate is incorrectly called with context.Background(), which
ignores the test-provided ctx; replace context.Background() with the passed-in
ctx so the translation respects cancellation/deadlines and context values (i.e.,
change the translate.Translate call in AssertLive to use ctx when invoking
translate.Translate(ctx, regularQuery, driver.KindMapper(), s.CypherParams,
defaultGraph.ID)).
|
|
||
| -- case: match (n:NodeKind1) where n.hasspn = true and n.enabled = true and not n.objectid ends with '-502' and not coalesce(n.gmsa, false) = true and not coalesce(n.msa, false) = true match (n)-[:EdgeKind1|EdgeKind2*1..]->(c:NodeKind2) with distinct n, count(c) as adminCount return n order by adminCount desc limit 100 | ||
| with s0 as (with s1 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'hasspn'))::bool = true and ((n0.properties ->> 'enabled'))::bool = true and not coalesce((n0.properties ->> 'objectid'), '')::text like '%-502' and not coalesce(((n0.properties ->> 'gmsa'))::bool, false)::bool = true and not coalesce(((n0.properties ->> 'msa'))::bool, false)::bool = true) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]), s2 as (with recursive s3(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.start_id, e0.end_id, 1, n1.kind_ids operator (pg_catalog.@>) array [2]::int2[], e0.start_id = e0.end_id, array [e0.id] from s1 join edge e0 on e0.start_id = (s1.n0).id join node n1 on n1.id = e0.end_id where e0.kind_id = any (array [3, 4]::int2[]) union select s3.root_id, e0.end_id, s3.depth + 1, n1.kind_ids operator (pg_catalog.@>) array [2]::int2[], e0.id = any (s3.path), s3.path || e0.id from s3 join edge e0 on e0.start_id = s3.next_id join node n1 on n1.id = e0.end_id where e0.kind_id = any (array [3, 4]::int2[]) and s3.depth < 15 and not s3.is_cycle) select (select array_agg((e0.id, e0.start_id, e0.end_id, e0.kind_id, e0.properties)::edgecomposite) from edge e0 where e0.id = any (s3.path)) as e0, s3.path as ep0, (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s1, s3 join node n0 on n0.id = s3.root_id join node n1 on n1.id = s3.next_id where s3.satisfied) select s2.n0 as n0, count(s2.n1)::int8 as i0 from s2 group by n0) select s0.n0 as n from s0 order by s0.i0 desc limit 100; | ||
| with s0 as (with s1 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'hasspn'))::bool = true and ((n0.properties ->> 'enabled'))::bool = true and not coalesce((n0.properties ->> 'objectid'), '')::text like '%-502' and not coalesce(((n0.properties ->> 'gmsa'))::bool, false)::bool = true and not coalesce(((n0.properties ->> 'msa'))::bool, false)::bool = true) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]), s2 as (with recursive s3(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.start_id, e0.end_id, 1, n1.kind_ids operator (pg_catalog.@>) array [2]::int2[], e0.start_id = e0.end_id, array [e0.id] from s1 join edge e0 on e0.start_id = (s1.n0).id join node n1 on n1.id = e0.end_id where e0.kind_id = any (array [3, 4]::int2[]) union select s3.root_id, e0.end_id, s3.depth + 1, n1.kind_ids operator (pg_catalog.@>) array [2]::int2[], e0.id = any (s3.path), s3.path || e0.id from s3 join edge e0 on e0.start_id = s3.next_id join node n1 on n1.id = e0.end_id where e0.kind_id = any (array [3, 4]::int2[]) and s3.depth < 15 and not s3.is_cycle) select (select array_agg((e0.id, e0.start_id, e0.end_id, e0.kind_id, e0.properties)::edgecomposite) from edge e0 where e0.id = any (s3.path)) as e0, s3.path as ep0, (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s1, s3 join node n0 on n0.id = s3.root_id join node n1 on n1.id = s3.next_id where s3.satisfied) select distinct s2.n0 as n0, count(s2.n1)::int8 as i0 from s2 group by n0) select s0.n0 as n from s0 order by s0.i0 desc limit 100; |
There was a problem hiding this comment.
Avoid multiplying recursive traversal rows before count(c).
Inside s2, from s1, s3 join ... is uncorrelated with s3.root_id, so every traversal row is repeated once per s1 row. Since this case counts s2.n1, adminCount is inflated before ordering/limiting; select distinct after aggregation does not fix that. Correlate the carried frame to s3.root_id, or omit s1 from this FROM when it is not referenced.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@cypher/models/pgsql/test/translation_cases/multipart.sql` at line 39, The s2
CTE multiplies traversal rows because its FROM clause includes "s1, s3" which is
uncorrelated to s3.root_id; this repeats each s3 row for every s1 row and
inflates the count(s2.n1). Fix by either removing s1 from the s2 FROM when it
isn't referenced, or correlate s1 to s3 (e.g., join s1 ON (s1.n0).id =
s3.root_id) so the carried frame (n0) is bound per traversal root_id; update the
s2 select/from to use the correlated join instead of the cross join.
| // AddIdentifer appends a from clause for frameID if it has not been seen before. | ||
| func (s *FromClauseBuilder) AddIdentifer(frameID pgsql.Identifier) { | ||
| if frameID == "" { | ||
| return | ||
| } | ||
|
|
||
| if _, already := s.seen[frameID]; !already { | ||
| s.seen[frameID] = struct{}{} | ||
| s.fromClauses = append(s.fromClauses, pgsql.FromClause{ | ||
| Source: pgsql.TableReference{ | ||
| Name: pgsql.CompoundIdentifier{frameID}, | ||
| }, | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| // AddBinding adds the frame in which binding was last materialized, if any. | ||
| func (s *FromClauseBuilder) AddBinding(binding *BoundIdentifier) { | ||
| if binding.LastProjection != nil { | ||
| s.AddIdentifer(binding.LastProjection.Binding.Identifier) | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Enumerate all call sites so the rename can be applied cleanly.
rg -nP --type=go '\bAddIdentifer\b'Repository: SpecterOps/DAWGS
Length of output: 649
Fix the misspelled exported method name AddIdentifer → AddIdentifier.
Newly introduced exported API that appears across multiple files (model.go and projection.go). Once call sites proliferate, the rename gets costly and misspellings propagate into linter/IDE suggestions. Additionally, AddBinding on line 712 dereferences binding.LastProjection without null-checking binding itself—add a defensive check.
✏️ Proposed changes
-// AddIdentifer appends a from clause for frameID if it has not been seen before.
-func (s *FromClauseBuilder) AddIdentifer(frameID pgsql.Identifier) {
+// AddIdentifier appends a from clause for frameID if it has not been seen before.
+func (s *FromClauseBuilder) AddIdentifier(frameID pgsql.Identifier) {
if frameID == "" {
return
}
@@
func (s *FromClauseBuilder) AddBinding(binding *BoundIdentifier) {
+ if binding == nil {
+ return
+ }
if binding.LastProjection != nil {
- s.AddIdentifer(binding.LastProjection.Binding.Identifier)
+ s.AddIdentifier(binding.LastProjection.Binding.Identifier)
}
}Update call site in cypher/models/pgsql/translate/projection.go line 523 (fromClauseBuilder.AddIdentifer(...) → fromClauseBuilder.AddIdentifier(...)).
Also update comment reference in cypher/models/pgsql/translate/model.go line 681.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@cypher/models/pgsql/translate/model.go` around lines 693 - 714, Rename the
exported method FromClauseBuilder.AddIdentifer to AddIdentifier everywhere
(update the method declaration and all call sites such as the usage in
projection.go where fromClauseBuilder.AddIdentifer(...) is invoked, and update
the related comment text), and in FromClauseBuilder.AddBinding add a defensive
nil check for the binding parameter before dereferencing binding.LastProjection
(i.e., return early if binding == nil) while keeping the existing check for
binding.LastProjection != nil.
Description
Implement Cypher-to-PgSQL translation for UNWIND, mapping it to PostgreSQL's
unnest()function. This supports UNWIND as a reading clause in both multi-part queries (preceded by WITH/MATCH) and standalone single-part queries.UnwindClausemodel and tracking onQueryPartprepareUnwind/translateUnwindhandlers in the AST translatorunnest() FROMclauses in both inline and tail projectionsprepareUnwindwhen none exists to prevent nil pointer dereference on standalone UNWIND queriesResolves: <TICKET_OR_ISSUE_NUMBER>
Type of Change
Testing
go test -tags manual_integration ./integration/...)Driver Impact
drivers/pg)drivers/neo4j)Checklist
go.mod/go.sumare up to date if dependencies changedSummary by CodeRabbit
Release Notes
New Features
Tests