diff --git a/cypher/models/pgsql/operators.go b/cypher/models/pgsql/operators.go index 6c90a24..1497039 100644 --- a/cypher/models/pgsql/operators.go +++ b/cypher/models/pgsql/operators.go @@ -1,5 +1,7 @@ package pgsql +import "slices" + type Operator string func (s Operator) IsIn(others ...Operator) bool { @@ -25,13 +27,7 @@ func (s Operator) NodeType() string { } func OperatorIsIn(operator Expression, matchers ...Expression) bool { - for _, matcher := range matchers { - if operator == matcher { - return true - } - } - - return false + return slices.Contains(matchers, operator) } func OperatorIsBoolean(operator Expression) bool { @@ -59,40 +55,41 @@ func OperatorIsComparator(operator Expression) bool { return OperatorIsIn(operator, OperatorEquals, OperatorNotEquals, OperatorGreaterThan, OperatorGreaterThanOrEqualTo, OperatorLessThan, OperatorLessThanOrEqualTo, OperatorArrayOverlap, OperatorLike, OperatorILike, OperatorPGArrayOverlap, - OperatorRegexMatch, OperatorSimilarTo) + OperatorRegexMatch, OperatorSimilarTo, OperatorPGArrayLHSContainsRHS) } const ( - UnsetOperator Operator = "" - OperatorUnion Operator = "union" - OperatorConcatenate Operator = "||" - OperatorArrayOverlap Operator = "&&" - OperatorEquals Operator = "=" - OperatorNotEquals Operator = "!=" - OperatorGreaterThan Operator = ">" - OperatorGreaterThanOrEqualTo Operator = ">=" - OperatorLessThan Operator = "<" - OperatorLessThanOrEqualTo Operator = "<=" - OperatorLike Operator = "like" - OperatorILike Operator = "ilike" - OperatorPGArrayOverlap Operator = "operator (pg_catalog.&&)" - OperatorAnd Operator = "and" - OperatorOr Operator = "or" - OperatorNot Operator = "not" - OperatorJSONBFieldExists Operator = "?" - OperatorJSONField Operator = "->" - OperatorJSONTextField Operator = "->>" - OperatorAdd Operator = "+" - OperatorSubtract Operator = "-" - OperatorMultiply Operator = "*" - OperatorDivide Operator = "/" - OperatorIn Operator = "in" - OperatorIs Operator = "is" - OperatorIsNot Operator = "is not" - OperatorSimilarTo Operator = "similar to" - OperatorRegexMatch Operator = "~" - OperatorAssignment Operator = "=" - OperatorAdditionAssignment Operator = "+=" + UnsetOperator Operator = "" + OperatorUnion Operator = "union" + OperatorConcatenate Operator = "||" + OperatorArrayOverlap Operator = "&&" + OperatorEquals Operator = "=" + OperatorNotEquals Operator = "!=" + OperatorGreaterThan Operator = ">" + OperatorGreaterThanOrEqualTo Operator = ">=" + OperatorLessThan Operator = "<" + OperatorLessThanOrEqualTo Operator = "<=" + OperatorLike Operator = "like" + OperatorILike Operator = "ilike" + OperatorPGArrayOverlap Operator = "operator (pg_catalog.&&)" + OperatorPGArrayLHSContainsRHS Operator = "operator (pg_catalog.@>)" + OperatorAnd Operator = "and" + OperatorOr Operator = "or" + OperatorNot Operator = "not" + OperatorJSONBFieldExists Operator = "?" + OperatorJSONField Operator = "->" + OperatorJSONTextField Operator = "->>" + OperatorAdd Operator = "+" + OperatorSubtract Operator = "-" + OperatorMultiply Operator = "*" + OperatorDivide Operator = "/" + OperatorIn Operator = "in" + OperatorIs Operator = "is" + OperatorIsNot Operator = "is not" + OperatorSimilarTo Operator = "similar to" + OperatorRegexMatch Operator = "~" + OperatorAssignment Operator = "=" + OperatorAdditionAssignment Operator = "+=" OperatorCypherRegexMatch Operator = "=~" OperatorCypherStartsWith Operator = "starts with" diff --git a/cypher/models/pgsql/pgtypes.go b/cypher/models/pgsql/pgtypes.go index dd76933..70748c3 100644 --- a/cypher/models/pgsql/pgtypes.go +++ b/cypher/models/pgsql/pgtypes.go @@ -10,9 +10,7 @@ import ( "github.com/specterops/dawgs/graph" ) -var ( - ErrNoAvailableArrayDataType = errors.New("data type has no direct array representation") -) +var ErrNoAvailableArrayDataType = errors.New("data type has no direct array representation") const ( StringLiteralNull = "null" @@ -118,7 +116,7 @@ func (s DataType) IsKnown() bool { func (s DataType) IsComparable(other DataType, operator Operator) bool { switch operator { - case OperatorPGArrayOverlap, OperatorArrayOverlap: + case OperatorPGArrayOverlap, OperatorArrayOverlap, OperatorPGArrayLHSContainsRHS: if !s.IsArrayType() || !other.IsArrayType() { return false } diff --git a/cypher/models/pgsql/pytypes_test.go b/cypher/models/pgsql/pytypes_test.go index 1de4ee7..0223d83 100644 --- a/cypher/models/pgsql/pytypes_test.go +++ b/cypher/models/pgsql/pytypes_test.go @@ -201,6 +201,20 @@ func TestDataType_Comparable(t *testing.T) { Expected: false, }, + // Array types may use the "LHS contains RHS" operator but only if their base types match + { + LeftTypes: []DataType{IntArray}, + Operators: []Operator{OperatorPGArrayLHSContainsRHS}, + RightTypes: []DataType{IntArray}, + Expected: true, + }, + { + LeftTypes: []DataType{IntArray}, + Operators: []Operator{OperatorPGArrayLHSContainsRHS}, + RightTypes: []DataType{Int}, + Expected: false, + }, + // Catch all for any unsupported operator { LeftTypes: []DataType{Int}, diff --git a/cypher/models/pgsql/test/translation_cases/delete.sql b/cypher/models/pgsql/test/translation_cases/delete.sql index 5b32415..386769e 100644 --- a/cypher/models/pgsql/test/translation_cases/delete.sql +++ b/cypher/models/pgsql/test/translation_cases/delete.sql @@ -1,5 +1,21 @@ +-- Copyright 2026 Specter Ops, Inc. +-- +-- Licensed under the Apache License, Version 2.0 +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- SPDX-License-Identifier: Apache-2.0 + -- case: match (s:NodeKind1) detach delete s -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]), s1 as (delete from node n1 using s0 where (s0.n0).id = n1.id) select 1; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]), s1 as (delete from node n1 using s0 where (s0.n0).id = n1.id) select 1; -- case: match ()-[r:EdgeKind1]->() delete r 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 e0.kind_id = any (array [3]::int2[])), s1 as (delete from edge e1 using s0 where (s0.e0).id = e1.id) select 1; diff --git a/cypher/models/pgsql/test/translation_cases/multipart.sql b/cypher/models/pgsql/test/translation_cases/multipart.sql index 60bbbf9..991bb1d 100644 --- a/cypher/models/pgsql/test/translation_cases/multipart.sql +++ b/cypher/models/pgsql/test/translation_cases/multipart.sql @@ -1,4 +1,4 @@ --- Copyright 2025 Specter Ops, Inc. +-- Copyright 2026 Specter Ops, Inc. -- -- Licensed under the Apache License, Version 2.0 -- you may not use this file except in compliance with the License. @@ -15,59 +15,59 @@ -- SPDX-License-Identifier: Apache-2.0 -- case: with '1' as target match (n:NodeKind1) where n.value = target return n -with s0 as (select '1' as i0), s1 as (select s0.i0 as i0, (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from s0, node n0 where ((n0.properties ->> 'value') = s0.i0) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s1.n0 as n from s1; +with s0 as (select '1' as i0), s1 as (select s0.i0 as i0, (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from s0, node n0 where ((n0.properties ->> 'value') = s0.i0) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s1.n0 as n from s1; -- case: match (n:NodeKind1) where n.value = 1 with n match (b) where id(b) = id(n) return b -with s0 as (with s1 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'value'))::int8 = 1) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s1.n0 as n0 from s1), s2 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where (n1.id = (s0.n0).id)) select s2.n1 as b from s2; +with s0 as (with s1 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'value'))::int8 = 1) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s1.n0 as n0 from s1), s2 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where (n1.id = (s0.n0).id)) select s2.n1 as b from s2; -- case: match (n:NodeKind1) where n.value = 1 with n match (f) where f.name = 'me' with f match (b) where id(b) = id(f) return b -with s0 as (with s1 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'value'))::int8 = 1) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s1.n0 as n0 from s1), s2 as (with s3 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where ((n1.properties ->> 'name') = 'me')) select s3.n1 as n1 from s3), s4 as (select s2.n1 as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 from s2, node n2 where (n2.id = (s2.n1).id)) select s4.n2 as b from s4; +with s0 as (with s1 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'value'))::int8 = 1) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s1.n0 as n0 from s1), s2 as (with s3 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where ((n1.properties ->> 'name') = 'me')) select s3.n1 as n1 from s3), s4 as (select s2.n1 as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 from s2, node n2 where (n2.id = (s2.n1).id)) select s4.n2 as b from s4; -- case: match (n:NodeKind1)-[:EdgeKind1*1..]->(:NodeKind2)-[:EdgeKind2]->(m:NodeKind1) where (n:NodeKind1 or n:NodeKind2) and n.enabled = true with m, collect(distinct(n)) as p where size(p) >= 10 return m -with s0 as (with s1 as (with recursive s2(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 edge e0 join node n0 on n0.id = e0.start_id join node n1 on n1.id = e0.end_id where ((n0.kind_ids operator (pg_catalog.&&) array [1]::int2[] or n0.kind_ids operator (pg_catalog.&&) array [2]::int2[]) and ((n0.properties ->> 'enabled'))::bool = true) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[] and e0.kind_id = any (array [3]::int2[]) union select s2.root_id, e0.end_id, s2.depth + 1, n1.kind_ids operator (pg_catalog.&&) array [2]::int2[], e0.id = any (s2.path), s2.path || e0.id from s2 join edge e0 on e0.start_id = s2.next_id join node n1 on n1.id = e0.end_id where e0.kind_id = any (array [3]::int2[]) and s2.depth <= 15 and not s2.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 (s2.path)) as e0, s2.path as ep0, (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s2 join node n0 on n0.id = s2.root_id join node n1 on n1.id = s2.next_id where s2.satisfied), s3 as (select s1.e0 as e0, (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s1.ep0 as ep0, s1.n0 as n0, s1.n1 as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 from s1 join edge e1 on (s1.n1).id = e1.start_id join node n2 on n2.id = e1.end_id where n2.kind_ids operator (pg_catalog.&&) array [1]::int2[] and e1.kind_id = any (array [4]::int2[])) select s3.n2 as n2, array_remove(coalesce(array_agg(distinct (s3.n0))::nodecomposite[], array []::nodecomposite[])::nodecomposite[], null)::nodecomposite[] as i0 from s3 group by n2) select s0.n2 as m from s0 where (array_length(s0.i0, 1)::int >= 10); +with s0 as (with s1 as (with recursive s2(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.end_id, e0.start_id, 1, ((n0.kind_ids operator (pg_catalog.@>) array [1]::int2[] or n0.kind_ids operator (pg_catalog.@>) array [2]::int2[]) and ((n0.properties ->> 'enabled'))::bool = true) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[], e0.end_id = e0.start_id, array [e0.id] from edge e0 join node n1 on n1.id = e0.end_id join node n0 on n0.id = e0.start_id where n1.kind_ids operator (pg_catalog.@>) array [2]::int2[] and e0.kind_id = any (array [3]::int2[]) union select s2.root_id, e0.start_id, s2.depth + 1, ((n0.kind_ids operator (pg_catalog.@>) array [1]::int2[] or n0.kind_ids operator (pg_catalog.@>) array [2]::int2[]) and ((n0.properties ->> 'enabled'))::bool = true) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[], e0.id = any (s2.path), s2.path || e0.id from s2 join edge e0 on e0.end_id = s2.next_id join node n0 on n0.id = e0.start_id where e0.kind_id = any (array [3]::int2[]) and s2.depth <= 15 and not s2.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 (s2.path)) as e0, s2.path as ep0, (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s2 join node n1 on n1.id = s2.root_id join node n0 on n0.id = s2.next_id where s2.satisfied), s3 as (select s1.e0 as e0, (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s1.ep0 as ep0, s1.n0 as n0, s1.n1 as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 from s1 join edge e1 on (s1.n1).id = e1.start_id join node n2 on n2.id = e1.end_id where n2.kind_ids operator (pg_catalog.@>) array [1]::int2[] and e1.kind_id = any (array [4]::int2[])) select s3.n2 as n2, array_remove(coalesce(array_agg(distinct (s3.n0))::nodecomposite[], array []::nodecomposite[])::nodecomposite[], null)::nodecomposite[] as i0 from s3 group by n2) select s0.n2 as m from s0 where (array_length(s0.i0, 1)::int >= 10); -- case: match (n:NodeKind1)-[:EdgeKind1*1..]->(:NodeKind2)-[:EdgeKind2]->(m:NodeKind1) where (n:NodeKind1 or n:NodeKind2) and n.enabled = true with m, count(distinct(n)) as p where p >= 10 return m -with s0 as (with s1 as (with recursive s2(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 edge e0 join node n0 on n0.id = e0.start_id join node n1 on n1.id = e0.end_id where ((n0.kind_ids operator (pg_catalog.&&) array [1]::int2[] or n0.kind_ids operator (pg_catalog.&&) array [2]::int2[]) and ((n0.properties ->> 'enabled'))::bool = true) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[] and e0.kind_id = any (array [3]::int2[]) union select s2.root_id, e0.end_id, s2.depth + 1, n1.kind_ids operator (pg_catalog.&&) array [2]::int2[], e0.id = any (s2.path), s2.path || e0.id from s2 join edge e0 on e0.start_id = s2.next_id join node n1 on n1.id = e0.end_id where e0.kind_id = any (array [3]::int2[]) and s2.depth <= 15 and not s2.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 (s2.path)) as e0, s2.path as ep0, (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s2 join node n0 on n0.id = s2.root_id join node n1 on n1.id = s2.next_id where s2.satisfied), s3 as (select s1.e0 as e0, (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s1.ep0 as ep0, s1.n0 as n0, s1.n1 as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 from s1 join edge e1 on (s1.n1).id = e1.start_id join node n2 on n2.id = e1.end_id where n2.kind_ids operator (pg_catalog.&&) array [1]::int2[] and e1.kind_id = any (array [4]::int2[])) select s3.n2 as n2, count((s3.n0))::int8 as i0 from s3 group by n2) select s0.n2 as m from s0 where (s0.i0 >= 10); +with s0 as (with s1 as (with recursive s2(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.end_id, e0.start_id, 1, ((n0.kind_ids operator (pg_catalog.@>) array [1]::int2[] or n0.kind_ids operator (pg_catalog.@>) array [2]::int2[]) and ((n0.properties ->> 'enabled'))::bool = true) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[], e0.end_id = e0.start_id, array [e0.id] from edge e0 join node n1 on n1.id = e0.end_id join node n0 on n0.id = e0.start_id where n1.kind_ids operator (pg_catalog.@>) array [2]::int2[] and e0.kind_id = any (array [3]::int2[]) union select s2.root_id, e0.start_id, s2.depth + 1, ((n0.kind_ids operator (pg_catalog.@>) array [1]::int2[] or n0.kind_ids operator (pg_catalog.@>) array [2]::int2[]) and ((n0.properties ->> 'enabled'))::bool = true) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[], e0.id = any (s2.path), s2.path || e0.id from s2 join edge e0 on e0.end_id = s2.next_id join node n0 on n0.id = e0.start_id where e0.kind_id = any (array [3]::int2[]) and s2.depth <= 15 and not s2.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 (s2.path)) as e0, s2.path as ep0, (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s2 join node n1 on n1.id = s2.root_id join node n0 on n0.id = s2.next_id where s2.satisfied), s3 as (select s1.e0 as e0, (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s1.ep0 as ep0, s1.n0 as n0, s1.n1 as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 from s1 join edge e1 on (s1.n1).id = e1.start_id join node n2 on n2.id = e1.end_id where n2.kind_ids operator (pg_catalog.@>) array [1]::int2[] and e1.kind_id = any (array [4]::int2[])) select s3.n2 as n2, count((s3.n0))::int8 as i0 from s3 group by n2) select s0.n2 as m from s0 where (s0.i0 >= 10); -- case: with 365 as max_days match (n:NodeKind1) where n.pwdlastset < (datetime().epochseconds - (max_days * 86400)) and not n.pwdlastset IN [-1.0, 0.0] return n limit 100 -with s0 as (select 365 as i0), s1 as (select s0.i0 as i0, (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from s0, node n0 where (not ((n0.properties ->> 'pwdlastset'))::float8 = any (array [- 1, 0]::float8[]) and ((n0.properties ->> 'pwdlastset'))::numeric < (extract(epoch from now()::timestamp with time zone)::numeric - (s0.i0 * 86400))) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s1.n0 as n from s1 limit 100; +with s0 as (select 365 as i0), s1 as (select s0.i0 as i0, (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from s0, node n0 where (not ((n0.properties ->> 'pwdlastset'))::float8 = any (array [- 1, 0]::float8[]) and ((n0.properties ->> 'pwdlastset'))::numeric < (extract(epoch from now()::timestamp with time zone)::numeric - (s0.i0 * 86400))) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s1.n0 as n from s1 limit 100; -- 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 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; -- case: match (n:NodeKind1) where n.objectid = 'S-1-5-21-1260426776-3623580948-1897206385-23225' match p = (n)-[:EdgeKind1|EdgeKind2*1..]->(c:NodeKind2) return p -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'objectid') = 'S-1-5-21-1260426776-3623580948-1897206385-23225') 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 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 s0 join edge e0 on e0.start_id = (s0.n0).id join node n1 on n1.id = e0.end_id where e0.kind_id = any (array [3, 4]::int2[]) union select s2.root_id, e0.end_id, s2.depth + 1, n1.kind_ids operator (pg_catalog.&&) array [2]::int2[], e0.id = any (s2.path), s2.path || e0.id from s2 join edge e0 on e0.start_id = s2.next_id join node n1 on n1.id = e0.end_id where e0.kind_id = any (array [3, 4]::int2[]) and s2.depth <= 15 and not s2.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 (s2.path)) as e0, s2.path as ep0, (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, s2 join node n0 on n0.id = s2.root_id join node n1 on n1.id = s2.next_id where s2.satisfied) select edges_to_path(variadic ep0)::pathcomposite as p from s1; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'objectid') = 'S-1-5-21-1260426776-3623580948-1897206385-23225') 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 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 s0 join edge e0 on e0.start_id = (s0.n0).id join node n1 on n1.id = e0.end_id where e0.kind_id = any (array [3, 4]::int2[]) union select s2.root_id, e0.end_id, s2.depth + 1, n1.kind_ids operator (pg_catalog.@>) array [2]::int2[], e0.id = any (s2.path), s2.path || e0.id from s2 join edge e0 on e0.start_id = s2.next_id join node n1 on n1.id = e0.end_id where e0.kind_id = any (array [3, 4]::int2[]) and s2.depth <= 15 and not s2.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 (s2.path)) as e0, s2.path as ep0, (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, s2 join node n0 on n0.id = s2.root_id join node n1 on n1.id = s2.next_id where s2.satisfied) select edges_to_path(variadic ep0)::pathcomposite as p from s1; -- case: match (g1:NodeKind1) where g1.name starts with 'test' with collect (g1.domain) as excludes match (d:NodeKind2) where d.name starts with 'other' and not d.name in excludes return d -with s0 as (with s1 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'name') like 'test%') and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select array_remove(coalesce(array_agg(((s1.n0).properties ->> 'domain'))::anyarray, array []::text[])::anyarray, null)::anyarray as i0 from s1), s2 as (select s0.i0 as i0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where (not (n1.properties ->> 'name') = any (s0.i0) and (n1.properties ->> 'name') like 'other%') and n1.kind_ids operator (pg_catalog.&&) array [2]::int2[]) select s2.n1 as d from s2; +with s0 as (with s1 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'name') like 'test%') and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select array_remove(coalesce(array_agg(((s1.n0).properties ->> 'domain'))::anyarray, array []::text[])::anyarray, null)::anyarray as i0 from s1), s2 as (select s0.i0 as i0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where (not (n1.properties ->> 'name') = any (s0.i0) and (n1.properties ->> 'name') like 'other%') and n1.kind_ids operator (pg_catalog.@>) array [2]::int2[]) select s2.n1 as d from s2; -- case: with 'a' as uname match (o:NodeKind1) where o.name starts with uname and o.domain = ' ' return o -with s0 as (select 'a' as i0), s1 as (select s0.i0 as i0, (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from s0, node n0 where ((n0.properties ->> 'domain') = ' ' and (n0.properties ->> 'name') like s0.i0 || '%') and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s1.n0 as o from s1; +with s0 as (select 'a' as i0), s1 as (select s0.i0 as i0, (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from s0, node n0 where ((n0.properties ->> 'domain') = ' ' and (n0.properties ->> 'name') like s0.i0 || '%') and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s1.n0 as o from s1; -- case: match (dc)-[r:EdgeKind1*0..]->(g:NodeKind1) where g.objectid ends with '-516' with collect(dc) as exclude match p = (c:NodeKind2)-[n:EdgeKind2]->(u:NodeKind2)-[:EdgeKind2*1..]->(g:NodeKind1) where g.objectid ends with '-512' and not c in exclude return p limit 100 -with s0 as (with s1 as (with recursive s2(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.end_id, e0.start_id, 1, false, e0.end_id = e0.start_id, array [e0.id] from edge e0 join node n1 on n1.id = e0.end_id join node n0 on n0.id = e0.start_id where ((n1.properties ->> 'objectid') like '%-516') and n1.kind_ids operator (pg_catalog.&&) array [1]::int2[] and e0.kind_id = any (array [3]::int2[]) union select s2.root_id, e0.start_id, s2.depth + 1, false, e0.id = any (s2.path), s2.path || e0.id from s2 join edge e0 on e0.end_id = s2.next_id join node n0 on n0.id = e0.start_id where e0.kind_id = any (array [3]::int2[]) and s2.depth <= 15 and not s2.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 (s2.path)) as e0, s2.path as ep0, (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s2 join node n1 on n1.id = s2.root_id join node n0 on n0.id = s2.next_id) select array_remove(coalesce(array_agg(s1.n0)::nodecomposite[], array []::nodecomposite[])::nodecomposite[], null)::nodecomposite[] as i0 from s1), s3 as (select (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s0.i0 as i0, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2, (n3.id, n3.kind_ids, n3.properties)::nodecomposite as n3 from s0, edge e1 join node n2 on n2.id = e1.start_id join node n3 on n3.id = e1.end_id where n3.kind_ids operator (pg_catalog.&&) array [2]::int2[] and e1.kind_id = any (array [4]::int2[]) and (not n2.id = any ((s0.i0).id)) and n2.kind_ids operator (pg_catalog.&&) array [2]::int2[]), s4 as (with recursive s5(root_id, next_id, depth, satisfied, is_cycle, path) as (select e2.start_id, e2.end_id, 1, false, e2.start_id = e2.end_id, array [e2.id] from s3 join edge e2 on n4.id = e2.end_id join node n3 on (s3.n3).id = e2.start_id where ((n4.properties ->> 'objectid') like '%-512') and n4.kind_ids operator (pg_catalog.&&) array [1]::int2[] and e2.kind_id = any (array [4]::int2[]) union select s5.root_id, e2.end_id, s5.depth + 1, false, e2.id = any (s5.path), s5.path || e2.id from s5 join edge e2 on e2.end_id = s5.next_id join node n3 on (s3.n3).id = e2.start_id where e2.kind_id = any (array [4]::int2[]) and s5.depth <= 15 and not s5.is_cycle) select s3.e1 as e1, (select array_agg((e2.id, e2.start_id, e2.end_id, e2.kind_id, e2.properties)::edgecomposite) from edge e2 where e2.id = any (s5.path)) as e2, s5.path as ep1, s3.i0 as i0, s3.n2 as n2, (n3.id, n3.kind_ids, n3.properties)::nodecomposite as n3, (n4.id, n4.kind_ids, n4.properties)::nodecomposite as n4 from s3, s5 join node n4 on n4.id = s5.root_id join node n3 on n3.id = s5.next_id) select edges_to_path(variadic array [(s4.e1).id]::int8[] || s4.ep1)::pathcomposite as p from s4 limit 100; +with s0 as (with s1 as (with recursive s2(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.end_id, e0.start_id, 1, false, e0.end_id = e0.start_id, array [e0.id] from edge e0 join node n1 on n1.id = e0.end_id join node n0 on n0.id = e0.start_id where ((n1.properties ->> 'objectid') like '%-516') and n1.kind_ids operator (pg_catalog.@>) array [1]::int2[] and e0.kind_id = any (array [3]::int2[]) union select s2.root_id, e0.start_id, s2.depth + 1, false, e0.id = any (s2.path), s2.path || e0.id from s2 join edge e0 on e0.end_id = s2.next_id join node n0 on n0.id = e0.start_id where e0.kind_id = any (array [3]::int2[]) and s2.depth <= 15 and not s2.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 (s2.path)) as e0, s2.path as ep0, (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s2 join node n1 on n1.id = s2.root_id join node n0 on n0.id = s2.next_id) select array_remove(coalesce(array_agg(s1.n0)::nodecomposite[], array []::nodecomposite[])::nodecomposite[], null)::nodecomposite[] as i0 from s1), s3 as (select (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s0.i0 as i0, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2, (n3.id, n3.kind_ids, n3.properties)::nodecomposite as n3 from s0, edge e1 join node n2 on n2.id = e1.start_id join node n3 on n3.id = e1.end_id where n3.kind_ids operator (pg_catalog.@>) array [2]::int2[] and e1.kind_id = any (array [4]::int2[]) and (not n2.id = any ((s0.i0).id)) and n2.kind_ids operator (pg_catalog.@>) array [2]::int2[]), s4 as (with recursive s5(root_id, next_id, depth, satisfied, is_cycle, path) as (select e2.start_id, e2.end_id, 1, false, e2.start_id = e2.end_id, array [e2.id] from s3 join edge e2 on n4.id = e2.end_id join node n3 on (s3.n3).id = e2.start_id where ((n4.properties ->> 'objectid') like '%-512') and n4.kind_ids operator (pg_catalog.@>) array [1]::int2[] and e2.kind_id = any (array [4]::int2[]) union select s5.root_id, e2.end_id, s5.depth + 1, false, e2.id = any (s5.path), s5.path || e2.id from s5 join edge e2 on e2.end_id = s5.next_id join node n3 on (s3.n3).id = e2.start_id where e2.kind_id = any (array [4]::int2[]) and s5.depth <= 15 and not s5.is_cycle) select s3.e1 as e1, (select array_agg((e2.id, e2.start_id, e2.end_id, e2.kind_id, e2.properties)::edgecomposite) from edge e2 where e2.id = any (s5.path)) as e2, s5.path as ep1, s3.i0 as i0, s3.n2 as n2, (n3.id, n3.kind_ids, n3.properties)::nodecomposite as n3, (n4.id, n4.kind_ids, n4.properties)::nodecomposite as n4 from s3, s5 join node n4 on n4.id = s5.root_id join node n3 on n3.id = s5.next_id) select edges_to_path(variadic array [(s4.e1).id]::int8[] || s4.ep1)::pathcomposite as p from s4 limit 100; -- case: match (n:NodeKind1)<-[:EdgeKind1]-(:NodeKind2) where n.objectid ends with '-516' with n, count(n) as dc_count where dc_count = 1 return n -with s0 as (with s1 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.end_id join node n1 on n1.id = e0.start_id where n1.kind_ids operator (pg_catalog.&&) array [2]::int2[] and e0.kind_id = any (array [3]::int2[]) and ((n0.properties ->> 'objectid') like '%-516') and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s1.n0 as n0, count(s1.n0)::int8 as i0 from s1 group by n0) select s0.n0 as n from s0 where (s0.i0 = 1); +with s0 as (with s1 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.end_id join node n1 on n1.id = e0.start_id where n1.kind_ids operator (pg_catalog.@>) array [2]::int2[] and e0.kind_id = any (array [3]::int2[]) and ((n0.properties ->> 'objectid') like '%-516') and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s1.n0 as n0, count(s1.n0)::int8 as i0 from s1 group by n0) select s0.n0 as n from s0 where (s0.i0 = 1); -- case: match (n:NodeKind1)-[:EdgeKind1]->(m:NodeKind2) where n.enabled = true with n, collect(distinct(n)) as p where size(p) >= 100 match p = (n)-[:EdgeKind1]->(m) return p limit 10 -with s0 as (with s1 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.properties ->> 'enabled'))::bool = true) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s1.n0 as n0, array_remove(coalesce(array_agg(distinct (s1.n0))::nodecomposite[], array []::nodecomposite[])::nodecomposite[], null)::nodecomposite[] as i0 from s1 group by n0), s2 as (select (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s0.i0 as i0, s0.n0 as n0, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 from s0 join edge e1 on (s0.n0).id = e1.start_id join node n2 on n2.id = e1.end_id where e1.kind_id = any (array [3]::int2[]) and (array_length(s0.i0, 1)::int >= 100)) select edges_to_path(variadic array [(s2.e1).id]::int8[])::pathcomposite as p from s2 limit 10; +with s0 as (with s1 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.properties ->> 'enabled'))::bool = true) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s1.n0 as n0, array_remove(coalesce(array_agg(distinct (s1.n0))::nodecomposite[], array []::nodecomposite[])::nodecomposite[], null)::nodecomposite[] as i0 from s1 group by n0), s2 as (select (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s0.i0 as i0, s0.n0 as n0, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 from s0 join edge e1 on (s0.n0).id = e1.start_id join node n2 on n2.id = e1.end_id where e1.kind_id = any (array [3]::int2[]) and (array_length(s0.i0, 1)::int >= 100)) select edges_to_path(variadic array [(s2.e1).id]::int8[])::pathcomposite as p from s2 limit 10; -- case: with "a" as check, "b" as ref match p = (u)-[:EdgeKind1]->(g:NodeKind1) where u.name starts with check and u.domain = ref with collect(tolower(g.samaccountname)) as refmembership, tolower(u.samaccountname) as samname return refmembership, samname -with s0 as (select 'a' as i0, 'b' as i1), s1 as (with s2 as (select (e0.id, e0.start_id, e0.end_id, e0.kind_id, e0.properties)::edgecomposite as e0, s0.i0 as i0, s0.i1 as i1, (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, 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 [1]::int2[] and e0.kind_id = any (array [3]::int2[]) and ((n0.properties ->> 'domain') = s0.i1 and (n0.properties ->> 'name') like s0.i0 || '%')) select array_remove(coalesce(array_agg(lower(((s2.n1).properties ->> 'samaccountname'))::text)::anyarray, array []::text[])::anyarray, null)::anyarray as i2, lower(((s2.n0).properties ->> 'samaccountname'))::text as i3 from s2 group by s2.n0) select s1.i2 as refmembership, s1.i3 as samname from s1; +with s0 as (select 'a' as i0, 'b' as i1), s1 as (with s2 as (select (e0.id, e0.start_id, e0.end_id, e0.kind_id, e0.properties)::edgecomposite as e0, s0.i0 as i0, s0.i1 as i1, (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, 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 [1]::int2[] and e0.kind_id = any (array [3]::int2[]) and ((n0.properties ->> 'domain') = s0.i1 and (n0.properties ->> 'name') like s0.i0 || '%')) select array_remove(coalesce(array_agg(lower(((s2.n1).properties ->> 'samaccountname'))::text)::anyarray, array []::text[])::anyarray, null)::anyarray as i2, lower(((s2.n0).properties ->> 'samaccountname'))::text as i3 from s2 group by s2.n0) select s1.i2 as refmembership, s1.i3 as samname from s1; -- case: with "a" as check, "b" as ref match p = (u)-[:EdgeKind1]->(g:NodeKind1) where u.name starts with check and u.domain = ref with collect(tolower(g.samaccountname)) as refmembership, tolower(u.samaccountname) as samname match (u)-[:EdgeKind2]-(g:NodeKind1) where tolower(u.samaccountname) = samname and not tolower(g.samaccountname) IN refmembership return g -with s0 as (select 'a' as i0, 'b' as i1), s1 as (with s2 as (select (e0.id, e0.start_id, e0.end_id, e0.kind_id, e0.properties)::edgecomposite as e0, s0.i0 as i0, s0.i1 as i1, (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, 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 [1]::int2[] and e0.kind_id = any (array [3]::int2[]) and ((n0.properties ->> 'domain') = s0.i1 and (n0.properties ->> 'name') like s0.i0 || '%')) select array_remove(coalesce(array_agg(lower(((s2.n1).properties ->> 'samaccountname'))::text)::anyarray, array []::text[])::anyarray, null)::anyarray as i2, lower(((s2.n0).properties ->> 'samaccountname'))::text as i3 from s2 group by s2.n0), s3 as (select (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s1.i2 as i2, s1.i3 as i3, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2, (n3.id, n3.kind_ids, n3.properties)::nodecomposite as n3 from s1, edge e1 join node n2 on n2.id = e1.end_id or n2.id = e1.start_id join node n3 on n3.id = e1.end_id or n3.id = e1.start_id where (n2.id <> n3.id) and (not lower((n3.properties ->> 'samaccountname'))::text = any (s1.i2)) and n3.kind_ids operator (pg_catalog.&&) array [1]::int2[] and e1.kind_id = any (array [4]::int2[]) and (lower((n2.properties ->> 'samaccountname'))::text = s1.i3)) select s3.n3 as g from s3; +with s0 as (select 'a' as i0, 'b' as i1), s1 as (with s2 as (select (e0.id, e0.start_id, e0.end_id, e0.kind_id, e0.properties)::edgecomposite as e0, s0.i0 as i0, s0.i1 as i1, (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, 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 [1]::int2[] and e0.kind_id = any (array [3]::int2[]) and ((n0.properties ->> 'domain') = s0.i1 and (n0.properties ->> 'name') like s0.i0 || '%')) select array_remove(coalesce(array_agg(lower(((s2.n1).properties ->> 'samaccountname'))::text)::anyarray, array []::text[])::anyarray, null)::anyarray as i2, lower(((s2.n0).properties ->> 'samaccountname'))::text as i3 from s2 group by s2.n0), s3 as (select (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s1.i2 as i2, s1.i3 as i3, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2, (n3.id, n3.kind_ids, n3.properties)::nodecomposite as n3 from s1, edge e1 join node n2 on n2.id = e1.end_id or n2.id = e1.start_id join node n3 on n3.id = e1.end_id or n3.id = e1.start_id where (n2.id <> n3.id) and (not lower((n3.properties ->> 'samaccountname'))::text = any (s1.i2)) and n3.kind_ids operator (pg_catalog.@>) array [1]::int2[] and e1.kind_id = any (array [4]::int2[]) and (lower((n2.properties ->> 'samaccountname'))::text = s1.i3)) select s3.n3 as g from s3; -- case: with "a" as check, "b" as ref match p = (u)-[:EdgeKind1]->(g:NodeKind1) where u.name starts with check and u.domain = ref with collect(tolower(g.samaccountname)) as refmembership, tolower(u.samaccountname) as samname match (u)-[:EdgeKind2]->(g:NodeKind1) where tolower(u.samaccountname) = samname and not tolower(g.samaccountname) IN refmembership return g -with s0 as (select 'a' as i0, 'b' as i1), s1 as (with s2 as (select (e0.id, e0.start_id, e0.end_id, e0.kind_id, e0.properties)::edgecomposite as e0, s0.i0 as i0, s0.i1 as i1, (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, 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 [1]::int2[] and e0.kind_id = any (array [3]::int2[]) and ((n0.properties ->> 'domain') = s0.i1 and (n0.properties ->> 'name') like s0.i0 || '%')) select array_remove(coalesce(array_agg(lower(((s2.n1).properties ->> 'samaccountname'))::text)::anyarray, array []::text[])::anyarray, null)::anyarray as i2, lower(((s2.n0).properties ->> 'samaccountname'))::text as i3 from s2 group by s2.n0), s3 as (select (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s1.i2 as i2, s1.i3 as i3, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2, (n3.id, n3.kind_ids, n3.properties)::nodecomposite as n3 from s1, edge e1 join node n2 on n2.id = e1.start_id join node n3 on n3.id = e1.end_id where (not lower((n3.properties ->> 'samaccountname'))::text = any (s1.i2)) and n3.kind_ids operator (pg_catalog.&&) array [1]::int2[] and e1.kind_id = any (array [4]::int2[]) and (lower((n2.properties ->> 'samaccountname'))::text = s1.i3)) select s3.n3 as g from s3; +with s0 as (select 'a' as i0, 'b' as i1), s1 as (with s2 as (select (e0.id, e0.start_id, e0.end_id, e0.kind_id, e0.properties)::edgecomposite as e0, s0.i0 as i0, s0.i1 as i1, (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, 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 [1]::int2[] and e0.kind_id = any (array [3]::int2[]) and ((n0.properties ->> 'domain') = s0.i1 and (n0.properties ->> 'name') like s0.i0 || '%')) select array_remove(coalesce(array_agg(lower(((s2.n1).properties ->> 'samaccountname'))::text)::anyarray, array []::text[])::anyarray, null)::anyarray as i2, lower(((s2.n0).properties ->> 'samaccountname'))::text as i3 from s2 group by s2.n0), s3 as (select (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s1.i2 as i2, s1.i3 as i3, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2, (n3.id, n3.kind_ids, n3.properties)::nodecomposite as n3 from s1, edge e1 join node n2 on n2.id = e1.start_id join node n3 on n3.id = e1.end_id where (not lower((n3.properties ->> 'samaccountname'))::text = any (s1.i2)) and n3.kind_ids operator (pg_catalog.@>) array [1]::int2[] and e1.kind_id = any (array [4]::int2[]) and (lower((n2.properties ->> 'samaccountname'))::text = s1.i3)) select s3.n3 as g from s3; -- case: match p =(n:NodeKind1)<-[r:EdgeKind1|EdgeKind2*..3]-(u:NodeKind1) where n.domain = 'test' with n, count(r) as incomingCount where incomingCount > 90 with collect(n) as lotsOfAdmins match p =(n:NodeKind1)<-[:EdgeKind1]-() where n in lotsOfAdmins return p -with s0 as (with s1 as (with recursive s2(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.end_id, e0.start_id, 1, n1.kind_ids operator (pg_catalog.&&) array [1]::int2[], e0.end_id = e0.start_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.end_id join node n1 on n1.id = e0.start_id where ((n0.properties ->> 'domain') = 'test') and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[] and e0.kind_id = any (array [3, 4]::int2[]) union select s2.root_id, e0.start_id, s2.depth + 1, n1.kind_ids operator (pg_catalog.&&) array [1]::int2[], e0.id = any (s2.path), s2.path || e0.id from s2 join edge e0 on e0.end_id = s2.next_id join node n1 on n1.id = e0.start_id where e0.kind_id = any (array [3, 4]::int2[]) and s2.depth <= 3 and not s2.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 (s2.path)) as e0, s2.path as ep0, (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s2 join node n0 on n0.id = s2.root_id join node n1 on n1.id = s2.next_id where s2.satisfied) select s1.n0 as n0, count(s1.e0)::int8 as i0 from s1 group by n0), s3 as (select array_remove(coalesce(array_agg(s0.n0)::nodecomposite[], array []::nodecomposite[])::nodecomposite[], null)::nodecomposite[] as i1 from s0 where (s0.i0 > 90)), s4 as (select (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s3.i1 as i1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2, (n3.id, n3.kind_ids, n3.properties)::nodecomposite as n3 from s3, edge e1 join node n2 on n2.id = e1.end_id join node n3 on n3.id = e1.start_id where e1.kind_id = any (array [3]::int2[]) and (n2.id = any ((s3.i1).id)) and n2.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select edges_to_path(variadic array [(s4.e1).id]::int8[])::pathcomposite as p from s4; +with s0 as (with s1 as (with recursive s2(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.end_id, e0.start_id, 1, n1.kind_ids operator (pg_catalog.@>) array [1]::int2[], e0.end_id = e0.start_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.end_id join node n1 on n1.id = e0.start_id where ((n0.properties ->> 'domain') = 'test') and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[] and e0.kind_id = any (array [3, 4]::int2[]) union select s2.root_id, e0.start_id, s2.depth + 1, n1.kind_ids operator (pg_catalog.@>) array [1]::int2[], e0.id = any (s2.path), s2.path || e0.id from s2 join edge e0 on e0.end_id = s2.next_id join node n1 on n1.id = e0.start_id where e0.kind_id = any (array [3, 4]::int2[]) and s2.depth <= 3 and not s2.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 (s2.path)) as e0, s2.path as ep0, (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s2 join node n0 on n0.id = s2.root_id join node n1 on n1.id = s2.next_id where s2.satisfied) select s1.n0 as n0, count(s1.e0)::int8 as i0 from s1 group by n0), s3 as (select array_remove(coalesce(array_agg(s0.n0)::nodecomposite[], array []::nodecomposite[])::nodecomposite[], null)::nodecomposite[] as i1 from s0 where (s0.i0 > 90)), s4 as (select (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s3.i1 as i1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2, (n3.id, n3.kind_ids, n3.properties)::nodecomposite as n3 from s3, edge e1 join node n2 on n2.id = e1.end_id join node n3 on n3.id = e1.start_id where e1.kind_id = any (array [3]::int2[]) and (n2.id = any ((s3.i1).id)) and n2.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select edges_to_path(variadic array [(s4.e1).id]::int8[])::pathcomposite as p from s4; -- case: match (u:NodeKind1)-[:EdgeKind1]->(g:NodeKind2) with g match (g)<-[:EdgeKind1]-(u:NodeKind1) return g -with s0 as (with s1 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[]) select s1.n1 as n1 from s1), s2 as (select (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s0.n1 as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 from s0 join edge e1 on (s0.n1).id = e1.end_id join node n2 on n2.id = e1.start_id where n2.kind_ids operator (pg_catalog.&&) array [1]::int2[] and e1.kind_id = any (array [3]::int2[])) select s2.n1 as g from s2; +with s0 as (with s1 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[]) select s1.n1 as n1 from s1), s2 as (select (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s0.n1 as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 from s0 join edge e1 on (s0.n1).id = e1.end_id join node n2 on n2.id = e1.start_id where n2.kind_ids operator (pg_catalog.@>) array [1]::int2[] and e1.kind_id = any (array [3]::int2[])) select s2.n1 as g from s2; -- case: match (cg:NodeKind1) where cg.name =~ ".*TT" and cg.domain = "MY DOMAIN" with collect (cg.email) as emails match (o:NodeKind1)-[:EdgeKind1]->(g:NodeKind2) where g.name starts with "blah" and not g.email in emails return o -with s0 as (with s1 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'name') ~ '.*TT' and (n0.properties ->> 'domain') = 'MY DOMAIN') and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select array_remove(coalesce(array_agg(((s1.n0).properties ->> 'email'))::anyarray, array []::text[])::anyarray, null)::anyarray as i0 from s1), s2 as (select (e0.id, e0.start_id, e0.end_id, e0.kind_id, e0.properties)::edgecomposite as e0, s0.i0 as i0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 from s0, edge e0 join node n1 on n1.id = e0.start_id join node n2 on n2.id = e0.end_id where (not (n2.properties ->> 'email') = any (s0.i0) and (n2.properties ->> 'name') like 'blah%') and n2.kind_ids operator (pg_catalog.&&) array [2]::int2[] and e0.kind_id = any (array [3]::int2[]) and n1.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s2.n1 as o from s2; +with s0 as (with s1 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'name') ~ '.*TT' and (n0.properties ->> 'domain') = 'MY DOMAIN') and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select array_remove(coalesce(array_agg(((s1.n0).properties ->> 'email'))::anyarray, array []::text[])::anyarray, null)::anyarray as i0 from s1), s2 as (select (e0.id, e0.start_id, e0.end_id, e0.kind_id, e0.properties)::edgecomposite as e0, s0.i0 as i0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 from s0, edge e0 join node n1 on n1.id = e0.start_id join node n2 on n2.id = e0.end_id where (not (n2.properties ->> 'email') = any (s0.i0) and (n2.properties ->> 'name') like 'blah%') and n2.kind_ids operator (pg_catalog.@>) array [2]::int2[] and e0.kind_id = any (array [3]::int2[]) and n1.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s2.n1 as o from s2; diff --git a/cypher/models/pgsql/test/translation_cases/nodes.sql b/cypher/models/pgsql/test/translation_cases/nodes.sql index d060377..6707233 100644 --- a/cypher/models/pgsql/test/translation_cases/nodes.sql +++ b/cypher/models/pgsql/test/translation_cases/nodes.sql @@ -1,3 +1,19 @@ +-- Copyright 2026 Specter Ops, Inc. +-- +-- Licensed under the Apache License, Version 2.0 +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- SPDX-License-Identifier: Apache-2.0 + -- case: match (n) return labels(n) with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0) select (s0.n0).kind_ids from s0; @@ -11,7 +27,7 @@ with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'name') = '1234')) select s0.n0 as n from s0; -- case: match (n:NodeKind1 {name: "SOME NAME"}) return n -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.&&) array [1]::int2[] and (n0.properties ->> 'name') = 'SOME NAME') select s0.n0 as n from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.@>) array [1]::int2[] and (n0.properties ->> 'name') = 'SOME NAME') select s0.n0 as n from s0; -- case: match (n) where n.objectid in $p return n -- cypher_params: {"p":["1","2","3"]} @@ -30,22 +46,22 @@ with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (jsonb_to_text_array((n0.properties -> 'prop'))::int8[] = array [1, 2, 3]::int8[])) select s0.n0 as s from s0; -- case: match (s) where (s:NodeKind1 or s:NodeKind2) return s -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.kind_ids operator (pg_catalog.&&) array [1]::int2[] or n0.kind_ids operator (pg_catalog.&&) array [2]::int2[]))) select s0.n0 as s from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.kind_ids operator (pg_catalog.@>) array [1]::int2[] or n0.kind_ids operator (pg_catalog.@>) array [2]::int2[]))) select s0.n0 as s from s0; -- case: match (n:NodeKind1), (e) where n.name = e.name return n -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]), s1 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where (((s0.n0).properties -> 'name') = (n1.properties -> 'name'))) select s1.n0 as n from s1; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]), s1 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where (((s0.n0).properties -> 'name') = (n1.properties -> 'name'))) select s1.n0 as n from s1; -- case: match (s), (e) where id(s) in e.captured_ids return s, e with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0), s1 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where ((s0.n0).id = any (jsonb_to_text_array((n1.properties -> 'captured_ids'))::int8[]))) select s1.n0 as s, s1.n1 as e from s1; -- case: match (s) where s:NodeKind1 and s:NodeKind2 return s -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (n0.kind_ids operator (pg_catalog.&&) array [1]::int2[] and n0.kind_ids operator (pg_catalog.&&) array [2]::int2[])) select s0.n0 as s from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (n0.kind_ids operator (pg_catalog.@>) array [1]::int2[] and n0.kind_ids operator (pg_catalog.@>) array [2]::int2[])) select s0.n0 as s from s0; -- case: match (s) where s.name = '1234' return s with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'name') = '1234')) select s0.n0 as s from s0; -- case: match (s:NodeKind1), (e:NodeKind2) where s.selected or s.tid = e.tid and e.enabled return s, e -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]), s1 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where ((((s0.n0).properties ->> 'selected'))::bool or ((s0.n0).properties -> 'tid') = (n1.properties -> 'tid') and ((n1.properties ->> 'enabled'))::bool) and n1.kind_ids operator (pg_catalog.&&) array [2]::int2[]) select s1.n0 as s, s1.n1 as e from s1; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]), s1 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where ((((s0.n0).properties ->> 'selected'))::bool or ((s0.n0).properties -> 'tid') = (n1.properties -> 'tid') and ((n1.properties ->> 'enabled'))::bool) and n1.kind_ids operator (pg_catalog.@>) array [2]::int2[]) select s1.n0 as s, s1.n1 as e from s1; -- case: match (s) where s.value + 2 / 3 > 10 return s with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'value'))::int8 + 2 / 3 > 10)) select s0.n0 as s from s0; @@ -60,10 +76,10 @@ with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (lower((n0.properties ->> 'name'))::text = '1234')) select s0.n0 as s from s0; -- case: match (s:NodeKind1), (e:NodeKind2) where s.name = e.name return s, e -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]), s1 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where (((s0.n0).properties -> 'name') = (n1.properties -> 'name')) and n1.kind_ids operator (pg_catalog.&&) array [2]::int2[]) select s1.n0 as s, s1.n1 as e from s1; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]), s1 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where (((s0.n0).properties -> 'name') = (n1.properties -> 'name')) and n1.kind_ids operator (pg_catalog.@>) array [2]::int2[]) select s1.n0 as s, s1.n1 as e from s1; -- case: match (n) where n.system_tags is not null and not (n:NodeKind1 or n:NodeKind2) return id(n) -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (n0.properties ? 'system_tags' and not (n0.kind_ids operator (pg_catalog.&&) array [1]::int2[] or n0.kind_ids operator (pg_catalog.&&) array [2]::int2[]))) select (s0.n0).id from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (n0.properties ? 'system_tags' and not (n0.kind_ids operator (pg_catalog.@>) array [1]::int2[] or n0.kind_ids operator (pg_catalog.@>) array [2]::int2[]))) select (s0.n0).id from s0; -- case: match (s), (e) where s.name = '1234' and e.other = 1234 return s with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'name') = '1234')), s1 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where (((n1.properties ->> 'other'))::int8 = 1234)) select s1.n0 as s from s1; @@ -162,7 +178,7 @@ with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'name') like ('%' || ((n0.properties ->> 'other')))::text)) select s0.n0 as s from s0; -- case: match (n) where n:NodeKind1 and toLower(n.tenantid) contains 'myid' and n.system_tags contains 'tag' return n -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (n0.kind_ids operator (pg_catalog.&&) array [1]::int2[] and lower((n0.properties ->> 'tenantid'))::text like '%myid%' and (n0.properties ->> 'system_tags') like '%tag%')) select s0.n0 as n from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (n0.kind_ids operator (pg_catalog.@>) array [1]::int2[] and lower((n0.properties ->> 'tenantid'))::text like '%myid%' and (n0.properties ->> 'system_tags') like '%tag%')) select s0.n0 as n from s0; -- case: match (s) where not (s)-[]-() return s with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0) select s0.n0 as s from s0 where (not exists (select 1 from edge e0 where e0.start_id = (s0.n0).id or e0.end_id = (s0.n0).id)); @@ -177,16 +193,16 @@ with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0) select s0.n0 as s from s0 where (not (with s1 as (select (e0.id, e0.start_id, e0.end_id, e0.kind_id, e0.properties)::edgecomposite as e0, s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0 join edge e0 on (s0.n0).id = e0.end_id join node n1 on n1.id = e0.start_id where (n1.properties ->> 'name') = 'n3' and (e0.properties ->> 'prop') = 'a') select count(*) > 0 from s1)); -- case: match (n:NodeKind1) where n.distinguishedname = toUpper('admin') return n -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'distinguishedname') = upper('admin')::text) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s0.n0 as n from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'distinguishedname') = upper('admin')::text) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s0.n0 as n from s0; -- case: match (n:NodeKind1) where n.distinguishedname starts with toUpper('admin') return n -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'distinguishedname') like upper('admin')::text || '%') and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s0.n0 as n from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'distinguishedname') like upper('admin')::text || '%') and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s0.n0 as n from s0; -- case: match (n:NodeKind1) where n.distinguishedname contains toUpper('admin') return n -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'distinguishedname') like '%' || upper('admin')::text || '%') and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s0.n0 as n from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'distinguishedname') like '%' || upper('admin')::text || '%') and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s0.n0 as n from s0; -- case: match (n:NodeKind1) where n.distinguishedname ends with toUpper('admin') return n -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'distinguishedname') like '%' || upper('admin')::text) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s0.n0 as n from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'distinguishedname') like '%' || upper('admin')::text) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s0.n0 as n from s0; -- case: match (s) where not (s)-[{prop: 'a'}]->({name: 'n3'}) return s with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0) select s0.n0 as s from s0 where (not (with s1 as (select (e0.id, e0.start_id, e0.end_id, e0.kind_id, e0.properties)::edgecomposite as e0, s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0 join edge e0 on (s0.n0).id = e0.start_id join node n1 on n1.id = e0.end_id where (n1.properties ->> 'name') = 'n3' and (e0.properties ->> 'prop') = 'a') select count(*) > 0 from s1)); @@ -205,19 +221,19 @@ with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'system_tags') like '%' || ('text')::text)) select s0.n0 as n from s0; -- case: match (n:NodeKind1) where toString(n.functionallevel) in ['2008 R2','2012','2008','2003','2003 Interim','2000 Mixed/Native'] return n -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'functionallevel') = any (array ['2008 R2', '2012', '2008', '2003', '2003 Interim', '2000 Mixed/Native']::text[])) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s0.n0 as n from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'functionallevel') = any (array ['2008 R2', '2012', '2008', '2003', '2003 Interim', '2000 Mixed/Native']::text[])) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s0.n0 as n from s0; -- case: match (n:NodeKind1) where toInt(n.value) in [1, 2, 3, 4] return n -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'value'))::int8 = any (array [1, 2, 3, 4]::int8[])) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s0.n0 as n from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'value'))::int8 = any (array [1, 2, 3, 4]::int8[])) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s0.n0 as n from s0; -- case: match (u:NodeKind1) where u.pwdlastset < (datetime().epochseconds - (365 * 86400)) and not u.pwdlastset IN [-1.0, 0.0] return u limit 100 -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'pwdlastset'))::numeric < (extract(epoch from now()::timestamp with time zone)::numeric - (365 * 86400)) and not ((n0.properties ->> 'pwdlastset'))::float8 = any (array [- 1, 0]::float8[])) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s0.n0 as u from s0 limit 100; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'pwdlastset'))::numeric < (extract(epoch from now()::timestamp with time zone)::numeric - (365 * 86400)) and not ((n0.properties ->> 'pwdlastset'))::float8 = any (array [- 1, 0]::float8[])) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s0.n0 as u from s0 limit 100; -- case: match (u:NodeKind1) where u.pwdlastset < (datetime().epochmillis - 86400000) and not u.pwdlastset IN [-1.0, 0.0] return u limit 100 -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'pwdlastset'))::numeric < (extract(epoch from now()::timestamp with time zone)::numeric * 1000 - 86400000) and not ((n0.properties ->> 'pwdlastset'))::float8 = any (array [- 1, 0]::float8[])) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s0.n0 as u from s0 limit 100; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'pwdlastset'))::numeric < (extract(epoch from now()::timestamp with time zone)::numeric * 1000 - 86400000) and not ((n0.properties ->> 'pwdlastset'))::float8 = any (array [- 1, 0]::float8[])) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s0.n0 as u from s0 limit 100; -- case: match (n:NodeKind1) where size(n.array_value) > 0 return n -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (jsonb_array_length((n0.properties -> 'array_value'))::int > 0) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s0.n0 as n from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (jsonb_array_length((n0.properties -> 'array_value'))::int > 0) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s0.n0 as n from s0; -- case: match (n) where 1 in n.array return n with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (1 = any (jsonb_to_text_array((n0.properties -> 'array'))::int8[]))) select s0.n0 as n from s0; @@ -228,43 +244,43 @@ with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (@pi0::float8 = any (jsonb_to_text_array((n0.properties -> 'array'))::float8[]) or @pi1::text = any (jsonb_to_text_array((n0.properties -> 'array'))::text[]))) select s0.n0 as n from s0; -- case: match (n:NodeKind1) where coalesce(n.system_tags, '') contains 'admin_tier_0' return n -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (coalesce((n0.properties ->> 'system_tags'), '')::text like '%admin_tier_0%') and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s0.n0 as n from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (coalesce((n0.properties ->> 'system_tags'), '')::text like '%admin_tier_0%') and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s0.n0 as n from s0; -- case: match (n:NodeKind1) where coalesce(n.a, n.b, 1) = 1 return n -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (coalesce(((n0.properties ->> 'a'))::int8, ((n0.properties ->> 'b'))::int8, 1)::int8 = 1) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s0.n0 as n from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (coalesce(((n0.properties ->> 'a'))::int8, ((n0.properties ->> 'b'))::int8, 1)::int8 = 1) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s0.n0 as n from s0; -- case: match (n:NodeKind1) where coalesce(n.a, n.b) = 1 return n -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (coalesce((n0.properties ->> 'a'), (n0.properties ->> 'b'))::int8 = 1) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s0.n0 as n from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (coalesce((n0.properties ->> 'a'), (n0.properties ->> 'b'))::int8 = 1) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s0.n0 as n from s0; -- case: match (n:NodeKind1) where 1 = coalesce(n.a, n.b) return n -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (1 = coalesce((n0.properties ->> 'a'), (n0.properties ->> 'b'))::int8) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s0.n0 as n from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (1 = coalesce((n0.properties ->> 'a'), (n0.properties ->> 'b'))::int8) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s0.n0 as n from s0; -- case: match (u:NodeKind1) where u.hasspn = true and u.enabled = true and not '-502' ends with u.objectid and not coalesce(u.gmsa, false) = true and not coalesce(u.msa, false) = true return u limit 10 -with s0 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 '-502' like ('%' || ((n0.properties ->> 'objectid')))::text 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[]) select s0.n0 as u from s0 limit 10; +with s0 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 '-502' like ('%' || ((n0.properties ->> 'objectid')))::text 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[]) select s0.n0 as u from s0 limit 10; -- case: match (n:NodeKind1) where coalesce(n.name, '') = coalesce(n.migrated_name, '') return n -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (coalesce((n0.properties ->> 'name'), '')::text = coalesce((n0.properties ->> 'migrated_name'), '')::text) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s0.n0 as n from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (coalesce((n0.properties ->> 'name'), '')::text = coalesce((n0.properties ->> 'migrated_name'), '')::text) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s0.n0 as n from s0; -- case: match (n:NodeKind1) where '1' in n.array_prop + ['1', '2'] return n -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ('1' = any (jsonb_to_text_array((n0.properties -> 'array_prop'))::text[] || array ['1', '2']::text[])) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s0.n0 as n from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ('1' = any (jsonb_to_text_array((n0.properties -> 'array_prop'))::text[] || array ['1', '2']::text[])) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s0.n0 as n from s0; -- case: match (n:NodeKind1) where ['DES-CBC-CRC', 'DES-CBC-MD5', 'RC4-HMAC-MD5'] in n.arrayProperty return n -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (array ['DES-CBC-CRC', 'DES-CBC-MD5', 'RC4-HMAC-MD5']::text[] operator (pg_catalog.&&) jsonb_to_text_array((n0.properties -> 'arrayProperty'))::text[]) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s0.n0 as n from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (array ['DES-CBC-CRC', 'DES-CBC-MD5', 'RC4-HMAC-MD5']::text[] operator (pg_catalog.&&) jsonb_to_text_array((n0.properties -> 'arrayProperty'))::text[]) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s0.n0 as n from s0; -- case: match (u:NodeKind1) where 'DES-CBC-CRC' in u.arrayProperty or 'DES-CBC-MD5' in u.arrayProperty or 'RC4-HMAC-MD5' in u.arrayProperty return u -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ('DES-CBC-CRC' = any (jsonb_to_text_array((n0.properties -> 'arrayProperty'))::text[]) or 'DES-CBC-MD5' = any (jsonb_to_text_array((n0.properties -> 'arrayProperty'))::text[]) or 'RC4-HMAC-MD5' = any (jsonb_to_text_array((n0.properties -> 'arrayProperty'))::text[])) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s0.n0 as u from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ('DES-CBC-CRC' = any (jsonb_to_text_array((n0.properties -> 'arrayProperty'))::text[]) or 'DES-CBC-MD5' = any (jsonb_to_text_array((n0.properties -> 'arrayProperty'))::text[]) or 'RC4-HMAC-MD5' = any (jsonb_to_text_array((n0.properties -> 'arrayProperty'))::text[])) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s0.n0 as u from s0; -- case: match (n:NodeKind1) match (m:NodeKind2) where m.distinguishedname = 'CN=ADMINSDHOLDER,CN=SYSTEM,' + n.distinguishedname return m -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]), s1 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where ((n1.properties ->> 'distinguishedname') = 'CN=ADMINSDHOLDER,CN=SYSTEM,' || ((s0.n0).properties ->> 'distinguishedname')) and n1.kind_ids operator (pg_catalog.&&) array [2]::int2[]) select s1.n1 as m from s1; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]), s1 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where ((n1.properties ->> 'distinguishedname') = 'CN=ADMINSDHOLDER,CN=SYSTEM,' || ((s0.n0).properties ->> 'distinguishedname')) and n1.kind_ids operator (pg_catalog.@>) array [2]::int2[]) select s1.n1 as m from s1; -- case: match (n:NodeKind1) match (m:NodeKind2) where m.distinguishedname = n.distinguishedname + 'CN=ADMINSDHOLDER,CN=SYSTEM,' return m -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]), s1 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where ((n1.properties ->> 'distinguishedname') = ((s0.n0).properties ->> 'distinguishedname') || 'CN=ADMINSDHOLDER,CN=SYSTEM,') and n1.kind_ids operator (pg_catalog.&&) array [2]::int2[]) select s1.n1 as m from s1; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]), s1 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where ((n1.properties ->> 'distinguishedname') = ((s0.n0).properties ->> 'distinguishedname') || 'CN=ADMINSDHOLDER,CN=SYSTEM,') and n1.kind_ids operator (pg_catalog.@>) array [2]::int2[]) select s1.n1 as m from s1; -- case: match (n:NodeKind1) match (m:NodeKind2) where m.distinguishedname = n.unknown + m.unknown return m -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]), s1 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where ((n1.properties ->> 'distinguishedname') = ((s0.n0).properties -> 'unknown') + (n1.properties -> 'unknown')) and n1.kind_ids operator (pg_catalog.&&) array [2]::int2[]) select s1.n1 as m from s1; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]), s1 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where ((n1.properties ->> 'distinguishedname') = ((s0.n0).properties -> 'unknown') + (n1.properties -> 'unknown')) and n1.kind_ids operator (pg_catalog.@>) array [2]::int2[]) select s1.n1 as m from s1; -- case: match (n:NodeKind1) match (m:NodeKind2) where m.distinguishedname = '1' + '2' return m -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]), s1 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where ((n1.properties ->> 'distinguishedname') = '1' || '2') and n1.kind_ids operator (pg_catalog.&&) array [2]::int2[]) select s1.n1 as m from s1; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]), s1 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where ((n1.properties ->> 'distinguishedname') = '1' || '2') and n1.kind_ids operator (pg_catalog.@>) array [2]::int2[]) select s1.n1 as m from s1; -- case: match (n) where not n.property is not null return n with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (not n0.properties ? 'property')) select s0.n0 as n from s0; @@ -288,10 +304,10 @@ with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ? 'prop' and not (n0.properties ->> 'prop') = any (array ['null', '[]']::text[])))) select s0.n0 as s from s0; -- case: match (n:NodeKind1) optional match (m:NodeKind2) where m.distinguishedname = n.unknown + m.unknown return n, m -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]), s1 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where ((n1.properties ->> 'distinguishedname') = ((s0.n0).properties -> 'unknown') + (n1.properties -> 'unknown')) and n1.kind_ids operator (pg_catalog.&&) array [2]::int2[]), s2 as (select s0.n0 as n0, s1.n1 as n1 from s0 left outer join s1 on (s0.n0 = s1.n0)) select s2.n0 as n, s2.n1 as m from s2; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]), s1 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where ((n1.properties ->> 'distinguishedname') = ((s0.n0).properties -> 'unknown') + (n1.properties -> 'unknown')) and n1.kind_ids operator (pg_catalog.@>) array [2]::int2[]), s2 as (select s0.n0 as n0, s1.n1 as n1 from s0 left outer join s1 on (s0.n0 = s1.n0)) select s2.n0 as n, s2.n1 as m from s2; -- case: optional match (n:NodeKind1) return n -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s0.n0 as n from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s0.n0 as n from s0; -- case: match (n:NodeKind1) optional match (m:NodeKind2) where m.distinguishedname = n.unknown + m.unknown optional match (o:NodeKind2) where o.distinguishedname <> n.otherunknown return n, m, o -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]), s1 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where ((n1.properties ->> 'distinguishedname') = ((s0.n0).properties -> 'unknown') + (n1.properties -> 'unknown')) and n1.kind_ids operator (pg_catalog.&&) array [2]::int2[]), s2 as (select s0.n0 as n0, s1.n1 as n1 from s0 left outer join s1 on (s0.n0 = s1.n0)), s3 as (select s2.n0 as n0, s2.n1 as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 from s2, node n2 where ((n2.properties -> 'distinguishedname') <> ((s2.n0).properties -> 'otherunknown')) and n2.kind_ids operator (pg_catalog.&&) array [2]::int2[]), s4 as (select s2.n0 as n0, s2.n1 as n1, s3.n2 as n2 from s2 left outer join s3 on (s2.n1 = s3.n1) and (s2.n0 = s3.n0)) select s4.n0 as n, s4.n1 as m, s4.n2 as o from s4; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]), s1 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where ((n1.properties ->> 'distinguishedname') = ((s0.n0).properties -> 'unknown') + (n1.properties -> 'unknown')) and n1.kind_ids operator (pg_catalog.@>) array [2]::int2[]), s2 as (select s0.n0 as n0, s1.n1 as n1 from s0 left outer join s1 on (s0.n0 = s1.n0)), s3 as (select s2.n0 as n0, s2.n1 as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 from s2, node n2 where ((n2.properties -> 'distinguishedname') <> ((s2.n0).properties -> 'otherunknown')) and n2.kind_ids operator (pg_catalog.@>) array [2]::int2[]), s4 as (select s2.n0 as n0, s2.n1 as n1, s3.n2 as n2 from s2 left outer join s3 on (s2.n1 = s3.n1) and (s2.n0 = s3.n0)) select s4.n0 as n, s4.n1 as m, s4.n2 as o from s4; diff --git a/cypher/models/pgsql/test/translation_cases/parameters.sql b/cypher/models/pgsql/test/translation_cases/parameters.sql index 6456626..117ec3f 100644 --- a/cypher/models/pgsql/test/translation_cases/parameters.sql +++ b/cypher/models/pgsql/test/translation_cases/parameters.sql @@ -1,12 +1,28 @@ +-- Copyright 2026 Specter Ops, Inc. +-- +-- Licensed under the Apache License, Version 2.0 +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- SPDX-License-Identifier: Apache-2.0 + -- case: match (n) where n.objectid ends with $p0 and not (n:NodeKind1 or n:NodeKind2) return n -- pgsql_params:{"pi0":null} -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'objectid') like '%' || @pi0 and not (n0.kind_ids operator (pg_catalog.&&) array [1]::int2[] or n0.kind_ids operator (pg_catalog.&&) array [2]::int2[]))) select s0.n0 as n from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'objectid') like '%' || @pi0 and not (n0.kind_ids operator (pg_catalog.@>) array [1]::int2[] or n0.kind_ids operator (pg_catalog.@>) array [2]::int2[]))) select s0.n0 as n from s0; -- case: match (n) where n.objectid starts with $p0 and not (n:NodeKind1 or n:NodeKind2) return n -- pgsql_params:{"pi0":null} -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'objectid') like @pi0 || '%' and not (n0.kind_ids operator (pg_catalog.&&) array [1]::int2[] or n0.kind_ids operator (pg_catalog.&&) array [2]::int2[]))) select s0.n0 as n from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'objectid') like @pi0 || '%' and not (n0.kind_ids operator (pg_catalog.@>) array [1]::int2[] or n0.kind_ids operator (pg_catalog.@>) array [2]::int2[]))) select s0.n0 as n from s0; -- case: match (n) where n.objectid contains $p0 and not (n:NodeKind1 or n:NodeKind2) return n -- pgsql_params:{"pi0":null} -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'objectid') like '%' || @pi0 || '%' and not (n0.kind_ids operator (pg_catalog.&&) array [1]::int2[] or n0.kind_ids operator (pg_catalog.&&) array [2]::int2[]))) select s0.n0 as n from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'objectid') like '%' || @pi0 || '%' and not (n0.kind_ids operator (pg_catalog.@>) array [1]::int2[] or n0.kind_ids operator (pg_catalog.@>) array [2]::int2[]))) select s0.n0 as n from s0; diff --git a/cypher/models/pgsql/test/translation_cases/pattern_binding.sql b/cypher/models/pgsql/test/translation_cases/pattern_binding.sql index 6339ff5..4342192 100644 --- a/cypher/models/pgsql/test/translation_cases/pattern_binding.sql +++ b/cypher/models/pgsql/test/translation_cases/pattern_binding.sql @@ -1,14 +1,30 @@ +-- Copyright 2026 Specter Ops, Inc. +-- +-- Licensed under the Apache License, Version 2.0 +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- SPDX-License-Identifier: Apache-2.0 + -- case: match p = (:NodeKind1) return p -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select nodes_to_path(variadic array [(s0.n0).id]::int8[])::pathcomposite as p from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select nodes_to_path(variadic array [(s0.n0).id]::int8[])::pathcomposite as p from s0; -- case: match p = (n:NodeKind1) where n.name contains 'test' return p -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'name') like '%test%') and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select nodes_to_path(variadic array [(s0.n0).id]::int8[])::pathcomposite as p from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'name') like '%test%') and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select nodes_to_path(variadic array [(s0.n0).id]::int8[])::pathcomposite as p from s0; -- case: match p = ()-[]->() return p 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) select edges_to_path(variadic array [(s0.e0).id]::int8[])::pathcomposite as p from s0; -- case: match p=(:NodeKind1)-[r]->(:NodeKind1) where r.isacl return p limit 100 -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 [1]::int2[] and (((e0.properties ->> 'isacl'))::bool) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select edges_to_path(variadic array [(s0.e0).id]::int8[])::pathcomposite as p from s0 limit 100; +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 [1]::int2[] and (((e0.properties ->> 'isacl'))::bool) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select edges_to_path(variadic array [(s0.e0).id]::int8[])::pathcomposite as p from s0 limit 100; -- case: match p = ()-[r1]->()-[r2]->(e) return e 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), s1 as (select s0.e0 as e0, (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s0.n0 as n0, s0.n1 as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 from s0 join edge e1 on (s0.n1).id = e1.start_id join node n2 on n2.id = e1.end_id) select s1.n2 as e from s1; @@ -29,10 +45,11 @@ with s0 as (with recursive s1(root_id, next_id, depth, satisfied, is_cycle, path 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 e0.kind_id = any (array [3]::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 [3]::int2[]) union select s2.root_id, e1.end_id, s2.depth + 1, false, 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 [3]::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 s1.e0 as e, edges_to_path(variadic array [(s1.e0).id]::int8[] || s1.ep0)::pathcomposite as p from s1; -- case: match p = (m:NodeKind1)-[:EdgeKind1]->(c:NodeKind2) where m.objectid ends with "-513" and not toUpper(c.operatingsystem) contains "SERVER" 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 (not upper((n1.properties ->> 'operatingsystem'))::text like '%SERVER%') and n1.kind_ids operator (pg_catalog.&&) array [2]::int2[] and e0.kind_id = any (array [3]::int2[]) and ((n0.properties ->> 'objectid') like '%-513') and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select edges_to_path(variadic array [(s0.e0).id]::int8[])::pathcomposite as p from s0 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 (not upper((n1.properties ->> 'operatingsystem'))::text like '%SERVER%') and n1.kind_ids operator (pg_catalog.@>) array [2]::int2[] and e0.kind_id = any (array [3]::int2[]) and ((n0.properties ->> 'objectid') like '%-513') and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select edges_to_path(variadic array [(s0.e0).id]::int8[])::pathcomposite as p from s0 limit 1000; -- case: match p = (:NodeKind1)-[:EdgeKind1|EdgeKind2]->(e:NodeKind2)-[:EdgeKind2]->(:NodeKind1) where 'a' in e.values or 'b' in e.values or size(e.values) = 0 return p -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 ('a' = any (jsonb_to_text_array((n1.properties -> 'values'))::text[]) or 'b' = any (jsonb_to_text_array((n1.properties -> 'values'))::text[]) or jsonb_array_length((n1.properties -> 'values'))::int = 0) and n1.kind_ids operator (pg_catalog.&&) array [2]::int2[] and e0.kind_id = any (array [3, 4]::int2[]) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]), s1 as (select s0.e0 as e0, (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s0.n0 as n0, s0.n1 as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 from s0 join edge e1 on (s0.n1).id = e1.start_id join node n2 on n2.id = e1.end_id where n2.kind_ids operator (pg_catalog.&&) array [1]::int2[] and e1.kind_id = any (array [4]::int2[])) select edges_to_path(variadic array [(s1.e0).id, (s1.e1).id]::int8[])::pathcomposite as p from s1; +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 ('a' = any (jsonb_to_text_array((n1.properties -> 'values'))::text[]) or 'b' = any (jsonb_to_text_array((n1.properties -> 'values'))::text[]) or jsonb_array_length((n1.properties -> 'values'))::int = 0) and n1.kind_ids operator (pg_catalog.@>) array [2]::int2[] and e0.kind_id = any (array [3, 4]::int2[]) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]), s1 as (select s0.e0 as e0, (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s0.n0 as n0, s0.n1 as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 from s0 join edge e1 on (s0.n1).id = e1.start_id join node n2 on n2.id = e1.end_id where n2.kind_ids operator (pg_catalog.@>) array [1]::int2[] and e1.kind_id = any (array [4]::int2[])) select edges_to_path(variadic array [(s1.e0).id, (s1.e1).id]::int8[])::pathcomposite as p from s1; -- case: match p = (n:NodeKind1)-[r]-(m:NodeKind1) return p -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.end_id or n0.id = e0.start_id join node n1 on n1.id = e0.end_id or n1.id = e0.start_id where (n0.id <> n1.id) and n1.kind_ids operator (pg_catalog.&&) array [1]::int2[] and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select edges_to_path(variadic array [(s0.e0).id]::int8[])::pathcomposite as p from s0; \ No newline at end of file +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.end_id or n0.id = e0.start_id join node n1 on n1.id = e0.end_id or n1.id = e0.start_id where (n0.id <> n1.id) and n1.kind_ids operator (pg_catalog.@>) array [1]::int2[] and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select edges_to_path(variadic array [(s0.e0).id]::int8[])::pathcomposite as p from s0; + diff --git a/cypher/models/pgsql/test/translation_cases/pattern_expansion.sql b/cypher/models/pgsql/test/translation_cases/pattern_expansion.sql index 69231b1..c819721 100644 --- a/cypher/models/pgsql/test/translation_cases/pattern_expansion.sql +++ b/cypher/models/pgsql/test/translation_cases/pattern_expansion.sql @@ -1,3 +1,19 @@ +-- Copyright 2026 Specter Ops, Inc. +-- +-- Licensed under the Apache License, Version 2.0 +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- SPDX-License-Identifier: Apache-2.0 + -- case: match (n)-[*..]->(e) return n, e with s0 as (with recursive s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.start_id, e0.end_id, 1, false, e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.start_id join node n1 on n1.id = e0.end_id union select s1.root_id, e0.end_id, s1.depth + 1, false, e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.start_id = s1.next_id join node n1 on n1.id = e0.end_id where s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id) select s0.n0 as n, s0.n1 as e from s0; @@ -11,52 +27,53 @@ with s0 as (with recursive s1(root_id, next_id, depth, satisfied, is_cycle, path with s0 as (with recursive s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.end_id, e0.start_id, 1, false, e0.end_id = e0.start_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.end_id join node n1 on n1.id = e0.start_id union select s1.root_id, e0.start_id, s1.depth + 1, false, e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.end_id = s1.next_id join node n1 on n1.id = e0.start_id where s1.depth <= 5 and not s1.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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id where s1.depth >= 2) select s0.n0 as n, s0.n1 as e from s0; -- case: match p = (n)-[*..]->(e:NodeKind1) return p -with s0 as (with recursive s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.end_id, e0.start_id, 1, false, e0.end_id = e0.start_id, array [e0.id] from edge e0 join node n1 on n1.id = e0.end_id join node n0 on n0.id = e0.start_id where n1.kind_ids operator (pg_catalog.&&) array [1]::int2[] union select s1.root_id, e0.start_id, s1.depth + 1, false, e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.end_id = s1.next_id join node n0 on n0.id = e0.start_id where s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n1 on n1.id = s1.root_id join node n0 on n0.id = s1.next_id) select edges_to_path(variadic ep0)::pathcomposite as p from s0; +with s0 as (with recursive s1(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 [1]::int2[], e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.start_id join node n1 on n1.id = e0.end_id union select s1.root_id, e0.end_id, s1.depth + 1, n1.kind_ids operator (pg_catalog.@>) array [1]::int2[], e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.start_id = s1.next_id join node n1 on n1.id = e0.end_id where s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id where s1.satisfied) select edges_to_path(variadic ep0)::pathcomposite as p from s0; -- case: match (n)-[*..]->(e:NodeKind1) where n.name = 'n1' return e -with s0 as (with recursive s1(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 [1]::int2[], e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.start_id join node n1 on n1.id = e0.end_id where ((n0.properties ->> 'name') = 'n1') union select s1.root_id, e0.end_id, s1.depth + 1, n1.kind_ids operator (pg_catalog.&&) array [1]::int2[], e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.start_id = s1.next_id join node n1 on n1.id = e0.end_id where s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id where s1.satisfied) select s0.n1 as e from s0; +with s0 as (with recursive s1(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 [1]::int2[], e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.start_id join node n1 on n1.id = e0.end_id where ((n0.properties ->> 'name') = 'n1') union select s1.root_id, e0.end_id, s1.depth + 1, n1.kind_ids operator (pg_catalog.@>) array [1]::int2[], e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.start_id = s1.next_id join node n1 on n1.id = e0.end_id where s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id where s1.satisfied) select s0.n1 as e from s0; -- case: match (n)-[r*..]->(e:NodeKind1) where n.name = 'n1' and r.prop = 'a' return e -with s0 as (with recursive s1(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 [1]::int2[], e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.start_id join node n1 on n1.id = e0.end_id where ((n0.properties ->> 'name') = 'n1') and ((e0.properties ->> 'prop') = 'a') union select s1.root_id, e0.end_id, s1.depth + 1, n1.kind_ids operator (pg_catalog.&&) array [1]::int2[], e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.start_id = s1.next_id join node n1 on n1.id = e0.end_id where ((e0.properties ->> 'prop') = 'a') and s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id where s1.satisfied) select s0.n1 as e from s0; +with s0 as (with recursive s1(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 [1]::int2[], e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.start_id join node n1 on n1.id = e0.end_id where ((n0.properties ->> 'name') = 'n1') and ((e0.properties ->> 'prop') = 'a') union select s1.root_id, e0.end_id, s1.depth + 1, n1.kind_ids operator (pg_catalog.@>) array [1]::int2[], e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.start_id = s1.next_id join node n1 on n1.id = e0.end_id where ((e0.properties ->> 'prop') = 'a') and s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id where s1.satisfied) select s0.n1 as e from s0; -- case: match (n)-[*..]->(e:NodeKind1) where n.name = 'n2' return n -with s0 as (with recursive s1(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 [1]::int2[], e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.start_id join node n1 on n1.id = e0.end_id where ((n0.properties ->> 'name') = 'n2') union select s1.root_id, e0.end_id, s1.depth + 1, n1.kind_ids operator (pg_catalog.&&) array [1]::int2[], e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.start_id = s1.next_id join node n1 on n1.id = e0.end_id where s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id where s1.satisfied) select s0.n0 as n from s0; +with s0 as (with recursive s1(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 [1]::int2[], e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.start_id join node n1 on n1.id = e0.end_id where ((n0.properties ->> 'name') = 'n2') union select s1.root_id, e0.end_id, s1.depth + 1, n1.kind_ids operator (pg_catalog.@>) array [1]::int2[], e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.start_id = s1.next_id join node n1 on n1.id = e0.end_id where s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id where s1.satisfied) select s0.n0 as n from s0; -- case: match (n)-[*..]->(e:NodeKind1)-[]->(l) where n.name = 'n1' return l -with s0 as (with recursive s1(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 [1]::int2[], e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.start_id join node n1 on n1.id = e0.end_id where ((n0.properties ->> 'name') = 'n1') union select s1.root_id, e0.end_id, s1.depth + 1, n1.kind_ids operator (pg_catalog.&&) array [1]::int2[], e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.start_id = s1.next_id join node n1 on n1.id = e0.end_id where s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id where s1.satisfied), s2 as (select s0.e0 as e0, (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s0.ep0 as ep0, s0.n0 as n0, s0.n1 as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 from s0 join edge e1 on (s0.n1).id = e1.start_id join node n2 on n2.id = e1.end_id) select s2.n2 as l from s2; +with s0 as (with recursive s1(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 [1]::int2[], e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.start_id join node n1 on n1.id = e0.end_id where ((n0.properties ->> 'name') = 'n1') union select s1.root_id, e0.end_id, s1.depth + 1, n1.kind_ids operator (pg_catalog.@>) array [1]::int2[], e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.start_id = s1.next_id join node n1 on n1.id = e0.end_id where s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id where s1.satisfied), s2 as (select s0.e0 as e0, (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s0.ep0 as ep0, s0.n0 as n0, s0.n1 as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 from s0 join edge e1 on (s0.n1).id = e1.start_id join node n2 on n2.id = e1.end_id) select s2.n2 as l from s2; -- case: match (n)-[*2..3]->(e:NodeKind1)-[]->(l) where n.name = 'n1' return l -with s0 as (with recursive s1(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 [1]::int2[], e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.start_id join node n1 on n1.id = e0.end_id where ((n0.properties ->> 'name') = 'n1') union select s1.root_id, e0.end_id, s1.depth + 1, n1.kind_ids operator (pg_catalog.&&) array [1]::int2[], e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.start_id = s1.next_id join node n1 on n1.id = e0.end_id where s1.depth <= 3 and not s1.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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id where s1.depth >= 2 and s1.satisfied), s2 as (select s0.e0 as e0, (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s0.ep0 as ep0, s0.n0 as n0, s0.n1 as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 from s0 join edge e1 on (s0.n1).id = e1.start_id join node n2 on n2.id = e1.end_id) select s2.n2 as l from s2; +with s0 as (with recursive s1(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 [1]::int2[], e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.start_id join node n1 on n1.id = e0.end_id where ((n0.properties ->> 'name') = 'n1') union select s1.root_id, e0.end_id, s1.depth + 1, n1.kind_ids operator (pg_catalog.@>) array [1]::int2[], e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.start_id = s1.next_id join node n1 on n1.id = e0.end_id where s1.depth <= 3 and not s1.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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id where s1.depth >= 2 and s1.satisfied), s2 as (select s0.e0 as e0, (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s0.ep0 as ep0, s0.n0 as n0, s0.n1 as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 from s0 join edge e1 on (s0.n1).id = e1.start_id join node n2 on n2.id = e1.end_id) select s2.n2 as l from s2; -- case: match (n)-[]->(e:NodeKind1)-[*2..3]->(l) where n.name = 'n1' return l -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 [1]::int2[] and ((n0.properties ->> 'name') = 'n1')), 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 union select s2.root_id, e1.end_id, s2.depth + 1, false, 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 s2.depth <= 3 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 where s2.depth >= 2) select s1.n2 as l from s1; +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 [1]::int2[] and ((n0.properties ->> 'name') = 'n1')), 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 union select s2.root_id, e1.end_id, s2.depth + 1, false, 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 s2.depth <= 3 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 where s2.depth >= 2) select s1.n2 as l from s1; -- case: match (n)-[*..]->(e)-[:EdgeKind1|EdgeKind2]->()-[*..]->(l) where n.name = 'n1' and e.name = 'n2' return l with s0 as (with recursive s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.start_id, e0.end_id, 1, ((n1.properties ->> 'name') = 'n2'), e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.start_id join node n1 on n1.id = e0.end_id where ((n0.properties ->> 'name') = 'n1') union select s1.root_id, e0.end_id, s1.depth + 1, ((n1.properties ->> 'name') = 'n2'), e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.start_id = s1.next_id join node n1 on n1.id = e0.end_id where s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id where s1.satisfied), s2 as (select s0.e0 as e0, (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s0.ep0 as ep0, s0.n0 as n0, s0.n1 as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 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 [3, 4]::int2[])), s3 as (with recursive s4(root_id, next_id, depth, satisfied, is_cycle, path) as (select e2.start_id, e2.end_id, 1, false, e2.start_id = e2.end_id, array [e2.id] from s2 join edge e2 on (s2.n2).id = e2.start_id join node n3 on n3.id = e2.end_id union select s4.root_id, e2.end_id, s4.depth + 1, false, e2.id = any (s4.path), s4.path || e2.id from s4 join edge e2 on e2.start_id = s4.next_id join node n3 on n3.id = e2.end_id where s4.depth <= 15 and not s4.is_cycle) select s2.e0 as e0, s2.e1 as e1, (select array_agg((e2.id, e2.start_id, e2.end_id, e2.kind_id, e2.properties)::edgecomposite) from edge e2 where e2.id = any (s4.path)) as e2, s2.ep0 as ep0, s4.path as ep1, s2.n0 as n0, s2.n1 as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2, (n3.id, n3.kind_ids, n3.properties)::nodecomposite as n3 from s2, s4 join node n2 on n2.id = s4.root_id join node n3 on n3.id = s4.next_id) select s3.n3 as l from s3; -- case: match p = (:NodeKind1)-[:EdgeKind1*1..]->(n:NodeKind2) where 'admin_tier_0' in split(n.system_tags, ' ') return p limit 1000 -with s0 as (with recursive s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.end_id, e0.start_id, 1, n0.kind_ids operator (pg_catalog.&&) array [1]::int2[], e0.end_id = e0.start_id, array [e0.id] from edge e0 join node n1 on n1.id = e0.end_id join node n0 on n0.id = e0.start_id where ('admin_tier_0' = any (string_to_array((n1.properties ->> 'system_tags'), ' ')::text[])) and n1.kind_ids operator (pg_catalog.&&) array [2]::int2[] and e0.kind_id = any (array [3]::int2[]) union select s1.root_id, e0.start_id, s1.depth + 1, n0.kind_ids operator (pg_catalog.&&) array [1]::int2[], e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.end_id = s1.next_id join node n0 on n0.id = e0.start_id where e0.kind_id = any (array [3]::int2[]) and s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n1 on n1.id = s1.root_id join node n0 on n0.id = s1.next_id where s1.satisfied) select edges_to_path(variadic ep0)::pathcomposite as p from s0 limit 1000; +with s0 as (with recursive s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.end_id, e0.start_id, 1, n0.kind_ids operator (pg_catalog.@>) array [1]::int2[], e0.end_id = e0.start_id, array [e0.id] from edge e0 join node n1 on n1.id = e0.end_id join node n0 on n0.id = e0.start_id where ('admin_tier_0' = any (string_to_array((n1.properties ->> 'system_tags'), ' ')::text[])) and n1.kind_ids operator (pg_catalog.@>) array [2]::int2[] and e0.kind_id = any (array [3]::int2[]) union select s1.root_id, e0.start_id, s1.depth + 1, n0.kind_ids operator (pg_catalog.@>) array [1]::int2[], e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.end_id = s1.next_id join node n0 on n0.id = e0.start_id where e0.kind_id = any (array [3]::int2[]) and s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n1 on n1.id = s1.root_id join node n0 on n0.id = s1.next_id where s1.satisfied) select edges_to_path(variadic ep0)::pathcomposite as p from s0 limit 1000; -- case: match p = (s:NodeKind1)-[*..]->(e:NodeKind2) where s <> e return p -with s0 as (with recursive s1(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 edge e0 join node n0 on n0.id = e0.start_id join node n1 on n1.id = e0.end_id where n0.kind_ids operator (pg_catalog.&&) array [1]::int2[] union select s1.root_id, e0.end_id, s1.depth + 1, n1.kind_ids operator (pg_catalog.&&) array [2]::int2[], e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.start_id = s1.next_id join node n1 on n1.id = e0.end_id where s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id where s1.satisfied and (n0.id <> n1.id)) select edges_to_path(variadic ep0)::pathcomposite as p from s0; +with s0 as (with recursive s1(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 edge e0 join node n0 on n0.id = e0.start_id join node n1 on n1.id = e0.end_id where n0.kind_ids operator (pg_catalog.@>) array [1]::int2[] union select s1.root_id, e0.end_id, s1.depth + 1, n1.kind_ids operator (pg_catalog.@>) array [2]::int2[], e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.start_id = s1.next_id join node n1 on n1.id = e0.end_id where s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id where s1.satisfied and (n0.id <> n1.id)) select edges_to_path(variadic ep0)::pathcomposite as p from s0; -- case: match p = (g:NodeKind1)-[:EdgeKind1|EdgeKind2*]->(target:NodeKind1) where g.objectid ends with '1234' and target.objectid ends with '4567' return p -with s0 as (with recursive s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.start_id, e0.end_id, 1, ((n1.properties ->> 'objectid') like '%4567') and n1.kind_ids operator (pg_catalog.&&) array [1]::int2[], e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.start_id join node n1 on n1.id = e0.end_id where ((n0.properties ->> 'objectid') like '%1234') and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[] and e0.kind_id = any (array [3, 4]::int2[]) union select s1.root_id, e0.end_id, s1.depth + 1, ((n1.properties ->> 'objectid') like '%4567') and n1.kind_ids operator (pg_catalog.&&) array [1]::int2[], e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.start_id = s1.next_id join node n1 on n1.id = e0.end_id where e0.kind_id = any (array [3, 4]::int2[]) and s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id where s1.satisfied) select edges_to_path(variadic ep0)::pathcomposite as p from s0; +with s0 as (with recursive s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.start_id, e0.end_id, 1, ((n1.properties ->> 'objectid') like '%4567') and n1.kind_ids operator (pg_catalog.@>) array [1]::int2[], e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.start_id join node n1 on n1.id = e0.end_id where ((n0.properties ->> 'objectid') like '%1234') and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[] and e0.kind_id = any (array [3, 4]::int2[]) union select s1.root_id, e0.end_id, s1.depth + 1, ((n1.properties ->> 'objectid') like '%4567') and n1.kind_ids operator (pg_catalog.@>) array [1]::int2[], e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.start_id = s1.next_id join node n1 on n1.id = e0.end_id where e0.kind_id = any (array [3, 4]::int2[]) and s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id where s1.satisfied) select edges_to_path(variadic ep0)::pathcomposite as p from s0; -- case: match p = (m:NodeKind2)-[:EdgeKind1*1..]->(n:NodeKind1) where n.objectid = '1234' return p limit 10 -with s0 as (with recursive s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.end_id, e0.start_id, 1, n0.kind_ids operator (pg_catalog.&&) array [2]::int2[], e0.end_id = e0.start_id, array [e0.id] from edge e0 join node n1 on n1.id = e0.end_id join node n0 on n0.id = e0.start_id where ((n1.properties ->> 'objectid') = '1234') and n1.kind_ids operator (pg_catalog.&&) array [1]::int2[] and e0.kind_id = any (array [3]::int2[]) union select s1.root_id, e0.start_id, s1.depth + 1, n0.kind_ids operator (pg_catalog.&&) array [2]::int2[], e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.end_id = s1.next_id join node n0 on n0.id = e0.start_id where e0.kind_id = any (array [3]::int2[]) and s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n1 on n1.id = s1.root_id join node n0 on n0.id = s1.next_id where s1.satisfied) select edges_to_path(variadic ep0)::pathcomposite as p from s0 limit 10; +with s0 as (with recursive s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.end_id, e0.start_id, 1, n0.kind_ids operator (pg_catalog.@>) array [2]::int2[], e0.end_id = e0.start_id, array [e0.id] from edge e0 join node n1 on n1.id = e0.end_id join node n0 on n0.id = e0.start_id where ((n1.properties ->> 'objectid') = '1234') and n1.kind_ids operator (pg_catalog.@>) array [1]::int2[] and e0.kind_id = any (array [3]::int2[]) union select s1.root_id, e0.start_id, s1.depth + 1, n0.kind_ids operator (pg_catalog.@>) array [2]::int2[], e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.end_id = s1.next_id join node n0 on n0.id = e0.start_id where e0.kind_id = any (array [3]::int2[]) and s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n1 on n1.id = s1.root_id join node n0 on n0.id = s1.next_id where s1.satisfied) select edges_to_path(variadic ep0)::pathcomposite as p from s0 limit 10; -- case: match p = (:NodeKind1)<-[:EdgeKind1|EdgeKind2*..]-() return p limit 10 -with s0 as (with recursive s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.end_id, e0.start_id, 1, false, e0.end_id = e0.start_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.end_id join node n1 on n1.id = e0.start_id where n0.kind_ids operator (pg_catalog.&&) array [1]::int2[] and e0.kind_id = any (array [3, 4]::int2[]) union select s1.root_id, e0.start_id, s1.depth + 1, false, e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.end_id = s1.next_id join node n1 on n1.id = e0.start_id where e0.kind_id = any (array [3, 4]::int2[]) and s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id) select edges_to_path(variadic ep0)::pathcomposite as p from s0 limit 10; +with s0 as (with recursive s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.end_id, e0.start_id, 1, false, e0.end_id = e0.start_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.end_id join node n1 on n1.id = e0.start_id where n0.kind_ids operator (pg_catalog.@>) array [1]::int2[] and e0.kind_id = any (array [3, 4]::int2[]) union select s1.root_id, e0.start_id, s1.depth + 1, false, e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.end_id = s1.next_id join node n1 on n1.id = e0.start_id where e0.kind_id = any (array [3, 4]::int2[]) and s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id) select edges_to_path(variadic ep0)::pathcomposite as p from s0 limit 10; -- case: match p = (:NodeKind1)<-[:EdgeKind1|EdgeKind2*..]-(:NodeKind2)<-[:EdgeKind1|EdgeKind2*2..]-(:NodeKind1) return p limit 10 -with s0 as (with recursive s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.end_id, e0.start_id, 1, n1.kind_ids operator (pg_catalog.&&) array [2]::int2[], e0.end_id = e0.start_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.end_id join node n1 on n1.id = e0.start_id where n0.kind_ids operator (pg_catalog.&&) array [1]::int2[] and e0.kind_id = any (array [3, 4]::int2[]) union select s1.root_id, e0.start_id, s1.depth + 1, n1.kind_ids operator (pg_catalog.&&) array [2]::int2[], e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.end_id = s1.next_id join node n1 on n1.id = e0.start_id where e0.kind_id = any (array [3, 4]::int2[]) and s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id where s1.satisfied), s2 as (with recursive s3(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.end_id join node n2 on n2.id = e1.start_id where e1.kind_id = any (array [3, 4]::int2[]) union select s3.root_id, e1.end_id, s3.depth + 1, n2.kind_ids operator (pg_catalog.&&) array [1]::int2[], e1.id = any (s3.path), s3.path || e1.id from s3 join edge e1 on e1.end_id = s3.next_id join node n2 on n2.id = e1.start_id where e1.kind_id = any (array [3, 4]::int2[]) and s3.depth <= 15 and not s3.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 (s3.path)) as e1, s0.ep0 as ep0, s3.path as ep1, 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, s3 join node n1 on n1.id = s3.root_id join node n2 on n2.id = s3.next_id where s3.depth >= 2) select edges_to_path(variadic s2.ep1 || s2.ep0)::pathcomposite as p from s2 limit 10; +with s0 as (with recursive s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.end_id, e0.start_id, 1, n1.kind_ids operator (pg_catalog.@>) array [2]::int2[], e0.end_id = e0.start_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.end_id join node n1 on n1.id = e0.start_id where n0.kind_ids operator (pg_catalog.@>) array [1]::int2[] and e0.kind_id = any (array [3, 4]::int2[]) union select s1.root_id, e0.start_id, s1.depth + 1, n1.kind_ids operator (pg_catalog.@>) array [2]::int2[], e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.end_id = s1.next_id join node n1 on n1.id = e0.start_id where e0.kind_id = any (array [3, 4]::int2[]) and s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id where s1.satisfied), s2 as (with recursive s3(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.end_id join node n2 on n2.id = e1.start_id where e1.kind_id = any (array [3, 4]::int2[]) union select s3.root_id, e1.end_id, s3.depth + 1, n2.kind_ids operator (pg_catalog.@>) array [1]::int2[], e1.id = any (s3.path), s3.path || e1.id from s3 join edge e1 on e1.end_id = s3.next_id join node n2 on n2.id = e1.start_id where e1.kind_id = any (array [3, 4]::int2[]) and s3.depth <= 15 and not s3.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 (s3.path)) as e1, s0.ep0 as ep0, s3.path as ep1, 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, s3 join node n1 on n1.id = s3.root_id join node n2 on n2.id = s3.next_id where s3.depth >= 2) select edges_to_path(variadic s2.ep1 || s2.ep0)::pathcomposite as p from s2 limit 10; -- case: match p = (:NodeKind1)<-[:EdgeKind1|EdgeKind2*..]-(:NodeKind2)<-[:EdgeKind1|EdgeKind2*..]-(:NodeKind1) return p limit 10 -with s0 as (with recursive s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.end_id, e0.start_id, 1, n1.kind_ids operator (pg_catalog.&&) array [2]::int2[], e0.end_id = e0.start_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.end_id join node n1 on n1.id = e0.start_id where n0.kind_ids operator (pg_catalog.&&) array [1]::int2[] and e0.kind_id = any (array [3, 4]::int2[]) union select s1.root_id, e0.start_id, s1.depth + 1, n1.kind_ids operator (pg_catalog.&&) array [2]::int2[], e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.end_id = s1.next_id join node n1 on n1.id = e0.start_id where e0.kind_id = any (array [3, 4]::int2[]) and s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id where s1.satisfied), s2 as (with recursive s3(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.end_id join node n2 on n2.id = e1.start_id where e1.kind_id = any (array [3, 4]::int2[]) union select s3.root_id, e1.end_id, s3.depth + 1, n2.kind_ids operator (pg_catalog.&&) array [1]::int2[], e1.id = any (s3.path), s3.path || e1.id from s3 join edge e1 on e1.end_id = s3.next_id join node n2 on n2.id = e1.start_id where e1.kind_id = any (array [3, 4]::int2[]) and s3.depth <= 15 and not s3.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 (s3.path)) as e1, s0.ep0 as ep0, s3.path as ep1, 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, s3 join node n1 on n1.id = s3.root_id join node n2 on n2.id = s3.next_id) select edges_to_path(variadic s2.ep1 || s2.ep0)::pathcomposite as p from s2 limit 10; +with s0 as (with recursive s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.end_id, e0.start_id, 1, n1.kind_ids operator (pg_catalog.@>) array [2]::int2[], e0.end_id = e0.start_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.end_id join node n1 on n1.id = e0.start_id where n0.kind_ids operator (pg_catalog.@>) array [1]::int2[] and e0.kind_id = any (array [3, 4]::int2[]) union select s1.root_id, e0.start_id, s1.depth + 1, n1.kind_ids operator (pg_catalog.@>) array [2]::int2[], e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.end_id = s1.next_id join node n1 on n1.id = e0.start_id where e0.kind_id = any (array [3, 4]::int2[]) and s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id where s1.satisfied), s2 as (with recursive s3(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.end_id join node n2 on n2.id = e1.start_id where e1.kind_id = any (array [3, 4]::int2[]) union select s3.root_id, e1.end_id, s3.depth + 1, n2.kind_ids operator (pg_catalog.@>) array [1]::int2[], e1.id = any (s3.path), s3.path || e1.id from s3 join edge e1 on e1.end_id = s3.next_id join node n2 on n2.id = e1.start_id where e1.kind_id = any (array [3, 4]::int2[]) and s3.depth <= 15 and not s3.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 (s3.path)) as e1, s0.ep0 as ep0, s3.path as ep1, 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, s3 join node n1 on n1.id = s3.root_id join node n2 on n2.id = s3.next_id) select edges_to_path(variadic s2.ep1 || s2.ep0)::pathcomposite as p from s2 limit 10; -- case: match p = (n:NodeKind1)-[:EdgeKind1|EdgeKind2*1..2]->(r:NodeKind2) where r.name =~ '(?i)Global Administrator.*|User Administrator.*|Cloud Application Administrator.*|Authentication Policy Administrator.*|Exchange Administrator.*|Helpdesk Administrator.*|Privileged Authentication Administrator.*' return p limit 10 -with s0 as (with recursive s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.end_id, e0.start_id, 1, n0.kind_ids operator (pg_catalog.&&) array [1]::int2[], e0.end_id = e0.start_id, array [e0.id] from edge e0 join node n1 on n1.id = e0.end_id join node n0 on n0.id = e0.start_id where ((n1.properties ->> 'name') ~ '(?i)Global Administrator.*|User Administrator.*|Cloud Application Administrator.*|Authentication Policy Administrator.*|Exchange Administrator.*|Helpdesk Administrator.*|Privileged Authentication Administrator.*') and n1.kind_ids operator (pg_catalog.&&) array [2]::int2[] and e0.kind_id = any (array [3, 4]::int2[]) union select s1.root_id, e0.start_id, s1.depth + 1, n0.kind_ids operator (pg_catalog.&&) array [1]::int2[], e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.end_id = s1.next_id join node n0 on n0.id = e0.start_id where e0.kind_id = any (array [3, 4]::int2[]) and s1.depth <= 2 and not s1.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 (s1.path)) as e0, s1.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 join node n1 on n1.id = s1.root_id join node n0 on n0.id = s1.next_id where s1.satisfied) select edges_to_path(variadic ep0)::pathcomposite as p from s0 limit 10; +with s0 as (with recursive s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.end_id, e0.start_id, 1, n0.kind_ids operator (pg_catalog.@>) array [1]::int2[], e0.end_id = e0.start_id, array [e0.id] from edge e0 join node n1 on n1.id = e0.end_id join node n0 on n0.id = e0.start_id where ((n1.properties ->> 'name') ~ '(?i)Global Administrator.*|User Administrator.*|Cloud Application Administrator.*|Authentication Policy Administrator.*|Exchange Administrator.*|Helpdesk Administrator.*|Privileged Authentication Administrator.*') and n1.kind_ids operator (pg_catalog.@>) array [2]::int2[] and e0.kind_id = any (array [3, 4]::int2[]) union select s1.root_id, e0.start_id, s1.depth + 1, n0.kind_ids operator (pg_catalog.@>) array [1]::int2[], e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.end_id = s1.next_id join node n0 on n0.id = e0.start_id where e0.kind_id = any (array [3, 4]::int2[]) and s1.depth <= 2 and not s1.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 (s1.path)) as e0, s1.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 join node n1 on n1.id = s1.root_id join node n0 on n0.id = s1.next_id where s1.satisfied) select edges_to_path(variadic ep0)::pathcomposite as p from s0 limit 10; -- case: match p = (t:NodeKind2)<-[:EdgeKind1*1..]-(a) where (a:NodeKind1 or a:NodeKind2) and t.objectid ends with '-512' return p limit 1000 -with s0 as (with recursive s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.end_id, e0.start_id, 1, ((n1.kind_ids operator (pg_catalog.&&) array [1]::int2[] or n1.kind_ids operator (pg_catalog.&&) array [2]::int2[])), e0.end_id = e0.start_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.end_id join node n1 on n1.id = e0.start_id where ((n0.properties ->> 'objectid') like '%-512') and n0.kind_ids operator (pg_catalog.&&) array [2]::int2[] and e0.kind_id = any (array [3]::int2[]) union select s1.root_id, e0.start_id, s1.depth + 1, ((n1.kind_ids operator (pg_catalog.&&) array [1]::int2[] or n1.kind_ids operator (pg_catalog.&&) array [2]::int2[])), e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.end_id = s1.next_id join node n1 on n1.id = e0.start_id where e0.kind_id = any (array [3]::int2[]) and s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id where s1.satisfied) select edges_to_path(variadic ep0)::pathcomposite as p from s0 limit 1000; +with s0 as (with recursive s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select e0.end_id, e0.start_id, 1, ((n1.kind_ids operator (pg_catalog.@>) array [1]::int2[] or n1.kind_ids operator (pg_catalog.@>) array [2]::int2[])), e0.end_id = e0.start_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.end_id join node n1 on n1.id = e0.start_id where ((n0.properties ->> 'objectid') like '%-512') and n0.kind_ids operator (pg_catalog.@>) array [2]::int2[] and e0.kind_id = any (array [3]::int2[]) union select s1.root_id, e0.start_id, s1.depth + 1, ((n1.kind_ids operator (pg_catalog.@>) array [1]::int2[] or n1.kind_ids operator (pg_catalog.@>) array [2]::int2[])), e0.id = any (s1.path), s1.path || e0.id from s1 join edge e0 on e0.end_id = s1.next_id join node n1 on n1.id = e0.start_id where e0.kind_id = any (array [3]::int2[]) and s1.depth <= 15 and not s1.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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id where s1.satisfied) select edges_to_path(variadic ep0)::pathcomposite as p from s0 limit 1000; + diff --git a/cypher/models/pgsql/test/translation_cases/pattern_rewriting.sql b/cypher/models/pgsql/test/translation_cases/pattern_rewriting.sql index 9853f64..01ee804 100644 --- a/cypher/models/pgsql/test/translation_cases/pattern_rewriting.sql +++ b/cypher/models/pgsql/test/translation_cases/pattern_rewriting.sql @@ -1,3 +1,19 @@ +-- Copyright 2026 Specter Ops, Inc. +-- +-- Licensed under the Apache License, Version 2.0 +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- SPDX-License-Identifier: Apache-2.0 + -- case: match (s:NodeKind1) return s -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s0.n0 as s from s0; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s0.n0 as s from s0; diff --git a/cypher/models/pgsql/test/translation_cases/quantifiers.sql b/cypher/models/pgsql/test/translation_cases/quantifiers.sql index 8c18fd3..74a1b43 100644 --- a/cypher/models/pgsql/test/translation_cases/quantifiers.sql +++ b/cypher/models/pgsql/test/translation_cases/quantifiers.sql @@ -1,31 +1,40 @@ --- Tests the ANY quantifier +-- Copyright 2026 Specter Ops, Inc. +-- +-- Licensed under the Apache License, Version 2.0 +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- SPDX-License-Identifier: Apache-2.0 + -- case: MATCH (n:NodeKind1) WHERE n.usedeskeyonly OR ANY(type IN n.supportedencryptiontypes WHERE type CONTAINS 'DES') RETURN n LIMIT 100 -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'usedeskeyonly'))::bool or ((select count(*)::int from unnest(jsonb_to_text_array((n0.properties -> 'supportedencryptiontypes'))) as i0 where (i0 like '%DES%')) >= 1)::bool) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s0.n0 as n from s0 limit 100; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'usedeskeyonly'))::bool or ((select count(*)::int from unnest(jsonb_to_text_array((n0.properties -> 'supportedencryptiontypes'))) as i0 where (i0 like '%DES%')) >= 1)::bool) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s0.n0 as n from s0 limit 100; --- Tests the ALL quantifier -- case: MATCH (n:NodeKind1) WHERE n.usedeskeyonly OR ALL(type IN n.supportedencryptiontypes WHERE type CONTAINS 'DES') RETURN n LIMIT 100 -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'usedeskeyonly'))::bool or ((select count(*)::int from unnest(jsonb_to_text_array((n0.properties -> 'supportedencryptiontypes'))) as i0 where (i0 like '%DES%')) = array_length(jsonb_to_text_array((n0.properties -> 'supportedencryptiontypes')), 1))::bool) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s0.n0 as n from s0 limit 100; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'usedeskeyonly'))::bool or ((select count(*)::int from unnest(jsonb_to_text_array((n0.properties -> 'supportedencryptiontypes'))) as i0 where (i0 like '%DES%')) = array_length(jsonb_to_text_array((n0.properties -> 'supportedencryptiontypes')), 1))::bool) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s0.n0 as n from s0 limit 100; --- Tests the NONE quantifier -- case: MATCH (n:NodeKind1) WHERE n.usedeskeyonly OR NONE(type IN n.supportedencryptiontypes WHERE type CONTAINS 'DES') RETURN n LIMIT 100 -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'usedeskeyonly'))::bool or ((select count(*)::int from unnest(jsonb_to_text_array((n0.properties -> 'supportedencryptiontypes'))) as i0 where (i0 like '%DES%')) = 0)::bool) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s0.n0 as n from s0 limit 100; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'usedeskeyonly'))::bool or ((select count(*)::int from unnest(jsonb_to_text_array((n0.properties -> 'supportedencryptiontypes'))) as i0 where (i0 like '%DES%')) = 0)::bool) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s0.n0 as n from s0 limit 100; --- Tests the SINGLE quantifier -- case: MATCH (n:NodeKind1) WHERE n.usedeskeyonly OR SINGLE(type IN n.supportedencryptiontypes WHERE type CONTAINS 'DES') RETURN n LIMIT 100 -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'usedeskeyonly'))::bool or ((select count(*)::int from unnest(jsonb_to_text_array((n0.properties -> 'supportedencryptiontypes'))) as i0 where (i0 like '%DES%')) = 1)::bool) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s0.n0 as n from s0 limit 100; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'usedeskeyonly'))::bool or ((select count(*)::int from unnest(jsonb_to_text_array((n0.properties -> 'supportedencryptiontypes'))) as i0 where (i0 like '%DES%')) = 1)::bool) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s0.n0 as n from s0 limit 100; --- Test single part query with multiple quantifiers -- case: match (n:NodeKind1) where n.usedeskeyonly or any(type in n.supportedencryptiontypes where type contains 'DES') or any(type in n.serviceprincipalnames where toLower(type) contains 'mssqlservercluster' or toLower(type) contains 'mssqlserverclustermgmtapi' or toLower(type) contains 'msclustervirtualserver') return n limit 100 -with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'usedeskeyonly'))::bool or ((select count(*)::int from unnest(jsonb_to_text_array((n0.properties -> 'supportedencryptiontypes'))) as i0 where (i0 like '%DES%')) >= 1)::bool or ((select count(*)::int from unnest(jsonb_to_text_array((n0.properties -> 'serviceprincipalnames'))) as i1 where (lower(i1)::text like '%mssqlservercluster%' or lower(i1)::text like '%mssqlserverclustermgmtapi%' or lower(i1)::text like '%msclustervirtualserver%')) >= 1)::bool) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s0.n0 as n from s0 limit 100; +with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'usedeskeyonly'))::bool or ((select count(*)::int from unnest(jsonb_to_text_array((n0.properties -> 'supportedencryptiontypes'))) as i0 where (i0 like '%DES%')) >= 1)::bool or ((select count(*)::int from unnest(jsonb_to_text_array((n0.properties -> 'serviceprincipalnames'))) as i1 where (lower(i1)::text like '%mssqlservercluster%' or lower(i1)::text like '%mssqlserverclustermgmtapi%' or lower(i1)::text like '%msclustervirtualserver%')) >= 1)::bool) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s0.n0 as n from s0 limit 100; --- Test multipart query with single quantifier -- case: MATCH (m:NodeKind1) WHERE m.unconstraineddelegation = true WITH m MATCH (n:NodeKind1)-[:EdgeKind1]->(g:NodeKind2) WHERE g.objectid ENDS WITH '-516' WITH m, COLLECT(n) AS matchingNs WHERE NONE(n IN matchingNs WHERE n.objectid = m.objectid) RETURN m -with s0 as (with s1 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'unconstraineddelegation'))::bool = true) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s1.n0 as n0 from s1), s2 as (with s3 as (select (e0.id, e0.start_id, e0.end_id, e0.kind_id, e0.properties)::edgecomposite as e0, 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, edge e0 join node n1 on n1.id = e0.start_id join node n2 on n2.id = e0.end_id where ((n2.properties ->> 'objectid') like '%-516') and n2.kind_ids operator (pg_catalog.&&) array [2]::int2[] and e0.kind_id = any (array [3]::int2[]) and n1.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s3.n0 as n0, array_remove(coalesce(array_agg(s3.n1)::nodecomposite[], array []::nodecomposite[])::nodecomposite[], null)::nodecomposite[] as i0 from s3 group by n0) select s2.n0 as m from s2 where (((select count(*)::int from unnest(s2.i0) as i1 where ((i1.properties -> 'objectid') = ((s2.n0).properties -> 'objectid'))) = 0)::bool); +with s0 as (with s1 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'unconstraineddelegation'))::bool = true) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s1.n0 as n0 from s1), s2 as (with s3 as (select (e0.id, e0.start_id, e0.end_id, e0.kind_id, e0.properties)::edgecomposite as e0, 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, edge e0 join node n1 on n1.id = e0.start_id join node n2 on n2.id = e0.end_id where ((n2.properties ->> 'objectid') like '%-516') and n2.kind_ids operator (pg_catalog.@>) array [2]::int2[] and e0.kind_id = any (array [3]::int2[]) and n1.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s3.n0 as n0, array_remove(coalesce(array_agg(s3.n1)::nodecomposite[], array []::nodecomposite[])::nodecomposite[], null)::nodecomposite[] as i0 from s3 group by n0) select s2.n0 as m from s2 where (((select count(*)::int from unnest(s2.i0) as i1 where ((i1.properties -> 'objectid') = ((s2.n0).properties -> 'objectid'))) = 0)::bool); --- Test multipart query with single quantifier - ALL (tests to ensure the ALL quantifier's array identifier gets its scope rewritten as well) -- case: MATCH (m:NodeKind1) WHERE m.unconstraineddelegation = true WITH m MATCH (n:NodeKind1)-[:EdgeKind1]->(g:NodeKind2) WHERE g.objectid ENDS WITH '-516' WITH m, COLLECT(n) AS matchingNs WHERE ALL(n IN matchingNs WHERE n.objectid = m.objectid) RETURN m -with s0 as (with s1 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'unconstraineddelegation'))::bool = true) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s1.n0 as n0 from s1), s2 as (with s3 as (select (e0.id, e0.start_id, e0.end_id, e0.kind_id, e0.properties)::edgecomposite as e0, 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, edge e0 join node n1 on n1.id = e0.start_id join node n2 on n2.id = e0.end_id where ((n2.properties ->> 'objectid') like '%-516') and n2.kind_ids operator (pg_catalog.&&) array [2]::int2[] and e0.kind_id = any (array [3]::int2[]) and n1.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s3.n0 as n0, array_remove(coalesce(array_agg(s3.n1)::nodecomposite[], array []::nodecomposite[])::nodecomposite[], null)::nodecomposite[] as i0 from s3 group by n0) select s2.n0 as m from s2 where (((select count(*)::int from unnest(s2.i0) as i1 where ((i1.properties -> 'objectid') = ((s2.n0).properties -> 'objectid'))) = array_length(s2.i0, 1))::bool); +with s0 as (with s1 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((n0.properties ->> 'unconstraineddelegation'))::bool = true) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s1.n0 as n0 from s1), s2 as (with s3 as (select (e0.id, e0.start_id, e0.end_id, e0.kind_id, e0.properties)::edgecomposite as e0, 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, edge e0 join node n1 on n1.id = e0.start_id join node n2 on n2.id = e0.end_id where ((n2.properties ->> 'objectid') like '%-516') and n2.kind_ids operator (pg_catalog.@>) array [2]::int2[] and e0.kind_id = any (array [3]::int2[]) and n1.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s3.n0 as n0, array_remove(coalesce(array_agg(s3.n1)::nodecomposite[], array []::nodecomposite[])::nodecomposite[], null)::nodecomposite[] as i0 from s3 group by n0) select s2.n0 as m from s2 where (((select count(*)::int from unnest(s2.i0) as i1 where ((i1.properties -> 'objectid') = ((s2.n0).properties -> 'objectid'))) = array_length(s2.i0, 1))::bool); --- Test multipart query with multiple quantifiers -- case: MATCH (m:NodeKind1) WHERE ANY(name in m.serviceprincipalnames WHERE name CONTAINS "PHANTOM") WITH m MATCH (n:NodeKind1)-[:EdgeKind1]->(g:NodeKind2) WHERE g.objectid ENDS WITH '-525' WITH m, COLLECT(n) AS matchingNs WHERE NONE(t IN matchingNs WHERE t.objectid = m.objectid) RETURN m -with s0 as (with s1 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((select count(*)::int from unnest(jsonb_to_text_array((n0.properties -> 'serviceprincipalnames'))) as i0 where (i0 like '%PHANTOM%')) >= 1)::bool) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s1.n0 as n0 from s1), s2 as (with s3 as (select (e0.id, e0.start_id, e0.end_id, e0.kind_id, e0.properties)::edgecomposite as e0, 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, edge e0 join node n1 on n1.id = e0.start_id join node n2 on n2.id = e0.end_id where ((n2.properties ->> 'objectid') like '%-525') and n2.kind_ids operator (pg_catalog.&&) array [2]::int2[] and e0.kind_id = any (array [3]::int2[]) and n1.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s3.n0 as n0, array_remove(coalesce(array_agg(s3.n1)::nodecomposite[], array []::nodecomposite[])::nodecomposite[], null)::nodecomposite[] as i1 from s3 group by n0) select s2.n0 as m from s2 where (((select count(*)::int from unnest(s2.i1) as i2 where ((i2.properties -> 'objectid') = ((s2.n0).properties -> 'objectid'))) = 0)::bool); \ No newline at end of file +with s0 as (with s1 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where (((select count(*)::int from unnest(jsonb_to_text_array((n0.properties -> 'serviceprincipalnames'))) as i0 where (i0 like '%PHANTOM%')) >= 1)::bool) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s1.n0 as n0 from s1), s2 as (with s3 as (select (e0.id, e0.start_id, e0.end_id, e0.kind_id, e0.properties)::edgecomposite as e0, 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, edge e0 join node n1 on n1.id = e0.start_id join node n2 on n2.id = e0.end_id where ((n2.properties ->> 'objectid') like '%-525') and n2.kind_ids operator (pg_catalog.@>) array [2]::int2[] and e0.kind_id = any (array [3]::int2[]) and n1.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s3.n0 as n0, array_remove(coalesce(array_agg(s3.n1)::nodecomposite[], array []::nodecomposite[])::nodecomposite[], null)::nodecomposite[] as i1 from s3 group by n0) select s2.n0 as m from s2 where (((select count(*)::int from unnest(s2.i1) as i2 where ((i2.properties -> 'objectid') = ((s2.n0).properties -> 'objectid'))) = 0)::bool); + diff --git a/cypher/models/pgsql/test/translation_cases/scalar_aggregation.sql b/cypher/models/pgsql/test/translation_cases/scalar_aggregation.sql index 6bd7928..c1bb246 100644 --- a/cypher/models/pgsql/test/translation_cases/scalar_aggregation.sql +++ b/cypher/models/pgsql/test/translation_cases/scalar_aggregation.sql @@ -1,3 +1,19 @@ +-- Copyright 2026 Specter Ops, Inc. +-- +-- Licensed under the Apache License, Version 2.0 +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- SPDX-License-Identifier: Apache-2.0 + -- Test cases for scalar aggregation functions with GROUP BY support -- Tests verify the new aggregate functions (sum, avg, min, max) work correctly -- and that GROUP BY clauses are properly generated for mixed scalar/aggregate queries @@ -6,7 +22,7 @@ -- case: MATCH (n) RETURN sum(n.age) with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0) select sum(((s0.n0).properties ->> 'age'))::numeric from s0; --- Simple average aggregate +-- Simple average aggregate -- case: MATCH (n) RETURN avg(n.salary) with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0) select avg(((s0.n0).properties ->> 'salary'))::numeric from s0; @@ -74,4 +90,5 @@ with s0 as (with s1 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposit with s0 as (with s1 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0) select count(s1.n0)::int8 as i0 from s1) select s0.i0 as cnt from s0 where (s0.i0 > 1); -- case: MATCH (n) WITH count(n) as lim MATCH (o) RETURN o -with s0 as (with s1 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0) select count(s1.n0)::int8 as i0 from s1), s2 as (select s0.i0 as i0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1) select s2.n1 as o from s2; \ No newline at end of file +with s0 as (with s1 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0) select count(s1.n0)::int8 as i0 from s1), s2 as (select s0.i0 as i0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1) select s2.n1 as o from s2; + diff --git a/cypher/models/pgsql/test/translation_cases/shortest_paths.sql b/cypher/models/pgsql/test/translation_cases/shortest_paths.sql index 7cc3a2a..a31c1e7 100644 --- a/cypher/models/pgsql/test/translation_cases/shortest_paths.sql +++ b/cypher/models/pgsql/test/translation_cases/shortest_paths.sql @@ -1,25 +1,41 @@ +-- Copyright 2026 Specter Ops, Inc. +-- +-- Licensed under the Apache License, Version 2.0 +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- SPDX-License-Identifier: Apache-2.0 + -- case: match p = allShortestPaths((s:NodeKind1)-[*..]->()) return p --- pgsql_params:{"pi0":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select e0.start_id, e0.end_id, 1, exists (select 1 from edge where end_id = e0.start_id), e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.start_id where n0.kind_ids operator (pg_catalog.\u0026\u0026) array [1]::int2[];","pi1":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select s1.root_id, e0.end_id, s1.depth + 1, exists (select 1 from edge where end_id = e0.start_id), e0.id = any (s1.path), s1.path || e0.id from forward_front s1 join edge e0 on e0.start_id = s1.next_id;"} +-- pgsql_params:{"pi0":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select e0.start_id, e0.end_id, 1, exists (select 1 from edge where end_id = e0.start_id), e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.start_id where n0.kind_ids operator (pg_catalog.@\u003e) array [1]::int2[];","pi1":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select s1.root_id, e0.end_id, s1.depth + 1, exists (select 1 from edge where end_id = e0.start_id), e0.id = any (s1.path), s1.path || e0.id from forward_front s1 join edge e0 on e0.start_id = s1.next_id;"} with s0 as (with s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select * from unidirectional_asp_harness(@pi0::text, @pi1::text, 15)) 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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id) select edges_to_path(variadic ep0)::pathcomposite as p from s0; -- case: match p = allShortestPaths((s:NodeKind1)-[*..]->({name: "123"})) return p --- pgsql_params:{"pi0":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select e0.end_id, e0.start_id, 1, n0.kind_ids operator (pg_catalog.\u0026\u0026) array [1]::int2[], e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n1 on n1.id = e0.end_id join node n0 on n0.id = e0.start_id where (n1.properties -\u003e\u003e 'name') = '123';","pi1":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select s1.root_id, e0.start_id, s1.depth + 1, n0.kind_ids operator (pg_catalog.\u0026\u0026) array [1]::int2[], e0.id = any (s1.path), s1.path || e0.id from forward_front s1 join edge e0 on e0.end_id = s1.next_id join node n0 on n0.id = e0.start_id;","pi2":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select e0.start_id, e0.end_id, 1, (n1.properties -\u003e\u003e 'name') = '123', e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n1 on n1.id = e0.end_id join node n0 on n0.id = e0.start_id where n0.kind_ids operator (pg_catalog.\u0026\u0026) array [1]::int2[];","pi3":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select s1.root_id, e0.end_id, s1.depth + 1, (n1.properties -\u003e\u003e 'name') = '123', e0.id = any (s1.path), e0.id || s1.path from backward_front s1 join edge e0 on e0.start_id = s1.next_id join node n1 on n1.id = e0.end_id;"} +-- pgsql_params:{"pi0":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select e0.end_id, e0.start_id, 1, n0.kind_ids operator (pg_catalog.@\u003e) array [1]::int2[], e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n1 on n1.id = e0.end_id join node n0 on n0.id = e0.start_id where (n1.properties -\u003e\u003e 'name') = '123';","pi1":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select s1.root_id, e0.start_id, s1.depth + 1, n0.kind_ids operator (pg_catalog.@\u003e) array [1]::int2[], e0.id = any (s1.path), s1.path || e0.id from forward_front s1 join edge e0 on e0.end_id = s1.next_id join node n0 on n0.id = e0.start_id;","pi2":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select e0.start_id, e0.end_id, 1, (n1.properties -\u003e\u003e 'name') = '123', e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n1 on n1.id = e0.end_id join node n0 on n0.id = e0.start_id where n0.kind_ids operator (pg_catalog.@\u003e) array [1]::int2[];","pi3":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select s1.root_id, e0.end_id, s1.depth + 1, (n1.properties -\u003e\u003e 'name') = '123', e0.id = any (s1.path), e0.id || s1.path from backward_front s1 join edge e0 on e0.start_id = s1.next_id join node n1 on n1.id = e0.end_id;"} with s0 as (with s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select * from bidirectional_asp_harness(@pi0::text, @pi1::text, @pi2::text, @pi3::text, 15)) 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 (s1.path)) as e0, s1.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 join node n1 on n1.id = s1.root_id join node n0 on n0.id = s1.next_id) select edges_to_path(variadic ep0)::pathcomposite as p from s0; -- case: match p = allShortestPaths((s:NodeKind1)-[*..]->(e)) where e.name = '123' return p --- pgsql_params:{"pi0":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select e0.end_id, e0.start_id, 1, n0.kind_ids operator (pg_catalog.\u0026\u0026) array [1]::int2[], e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n1 on n1.id = e0.end_id join node n0 on n0.id = e0.start_id where ((n1.properties -\u003e\u003e 'name') = '123');","pi1":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select s1.root_id, e0.start_id, s1.depth + 1, n0.kind_ids operator (pg_catalog.\u0026\u0026) array [1]::int2[], e0.id = any (s1.path), s1.path || e0.id from forward_front s1 join edge e0 on e0.end_id = s1.next_id join node n0 on n0.id = e0.start_id;","pi2":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select e0.start_id, e0.end_id, 1, ((n1.properties -\u003e\u003e 'name') = '123'), e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n1 on n1.id = e0.end_id join node n0 on n0.id = e0.start_id where n0.kind_ids operator (pg_catalog.\u0026\u0026) array [1]::int2[];","pi3":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select s1.root_id, e0.end_id, s1.depth + 1, ((n1.properties -\u003e\u003e 'name') = '123'), e0.id = any (s1.path), e0.id || s1.path from backward_front s1 join edge e0 on e0.start_id = s1.next_id join node n1 on n1.id = e0.end_id;"} +-- pgsql_params:{"pi0":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select e0.end_id, e0.start_id, 1, n0.kind_ids operator (pg_catalog.@\u003e) array [1]::int2[], e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n1 on n1.id = e0.end_id join node n0 on n0.id = e0.start_id where ((n1.properties -\u003e\u003e 'name') = '123');","pi1":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select s1.root_id, e0.start_id, s1.depth + 1, n0.kind_ids operator (pg_catalog.@\u003e) array [1]::int2[], e0.id = any (s1.path), s1.path || e0.id from forward_front s1 join edge e0 on e0.end_id = s1.next_id join node n0 on n0.id = e0.start_id;","pi2":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select e0.start_id, e0.end_id, 1, ((n1.properties -\u003e\u003e 'name') = '123'), e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n1 on n1.id = e0.end_id join node n0 on n0.id = e0.start_id where n0.kind_ids operator (pg_catalog.@\u003e) array [1]::int2[];","pi3":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select s1.root_id, e0.end_id, s1.depth + 1, ((n1.properties -\u003e\u003e 'name') = '123'), e0.id = any (s1.path), e0.id || s1.path from backward_front s1 join edge e0 on e0.start_id = s1.next_id join node n1 on n1.id = e0.end_id;"} with s0 as (with s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select * from bidirectional_asp_harness(@pi0::text, @pi1::text, @pi2::text, @pi3::text, 15)) 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 (s1.path)) as e0, s1.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 join node n1 on n1.id = s1.root_id join node n0 on n0.id = s1.next_id) select edges_to_path(variadic ep0)::pathcomposite as p from s0; -- case: match p=shortestPath((n:NodeKind1)-[:EdgeKind1*1..]->(m)) where 'admin_tier_0' in split(m.system_tags, ' ') and n.objectid ends with '-513' and n<>m return p limit 1000 --- pgsql_params:{"pi0":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select e0.start_id, e0.end_id, 1, ('admin_tier_0' = any (string_to_array((n1.properties -\u003e\u003e 'system_tags'), ' ')::text[])), e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.start_id join node n1 on n1.id = e0.end_id where ((n0.properties -\u003e\u003e 'objectid') like '%-513') and n0.kind_ids operator (pg_catalog.\u0026\u0026) array [1]::int2[] and e0.kind_id = any (array [3]::int2[]);","pi1":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select s1.root_id, e0.end_id, s1.depth + 1, ('admin_tier_0' = any (string_to_array((n1.properties -\u003e\u003e 'system_tags'), ' ')::text[])), e0.id = any (s1.path), s1.path || e0.id from forward_front s1 join edge e0 on e0.start_id = s1.next_id join node n1 on n1.id = e0.end_id where e0.kind_id = any (array [3]::int2[]);"} +-- pgsql_params:{"pi0":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select e0.start_id, e0.end_id, 1, ('admin_tier_0' = any (string_to_array((n1.properties -\u003e\u003e 'system_tags'), ' ')::text[])), e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.start_id join node n1 on n1.id = e0.end_id where ((n0.properties -\u003e\u003e 'objectid') like '%-513') and n0.kind_ids operator (pg_catalog.@\u003e) array [1]::int2[] and e0.kind_id = any (array [3]::int2[]);","pi1":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select s1.root_id, e0.end_id, s1.depth + 1, ('admin_tier_0' = any (string_to_array((n1.properties -\u003e\u003e 'system_tags'), ' ')::text[])), e0.id = any (s1.path), s1.path || e0.id from forward_front s1 join edge e0 on e0.start_id = s1.next_id join node n1 on n1.id = e0.end_id where e0.kind_id = any (array [3]::int2[]);"} with s0 as (with s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select * from unidirectional_sp_harness(@pi0::text, @pi1::text, 15)) 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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id) select edges_to_path(variadic ep0)::pathcomposite as p from s0 where ((s0.n0).id <> (s0.n1).id) limit 1000; -- case: match p=shortestPath((n:NodeKind1)-[:EdgeKind1*1..]->(m)) where 'admin_tier_0' in split(m.system_tags, ' ') and n.objectid ends with '-513' and m<>n return p limit 1000 --- pgsql_params:{"pi0":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select e0.start_id, e0.end_id, 1, ('admin_tier_0' = any (string_to_array((n1.properties -\u003e\u003e 'system_tags'), ' ')::text[])), e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.start_id join node n1 on n1.id = e0.end_id where ((n0.properties -\u003e\u003e 'objectid') like '%-513') and n0.kind_ids operator (pg_catalog.\u0026\u0026) array [1]::int2[] and e0.kind_id = any (array [3]::int2[]);","pi1":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select s1.root_id, e0.end_id, s1.depth + 1, ('admin_tier_0' = any (string_to_array((n1.properties -\u003e\u003e 'system_tags'), ' ')::text[])), e0.id = any (s1.path), s1.path || e0.id from forward_front s1 join edge e0 on e0.start_id = s1.next_id join node n1 on n1.id = e0.end_id where e0.kind_id = any (array [3]::int2[]);"} +-- pgsql_params:{"pi0":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select e0.start_id, e0.end_id, 1, ('admin_tier_0' = any (string_to_array((n1.properties -\u003e\u003e 'system_tags'), ' ')::text[])), e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.start_id join node n1 on n1.id = e0.end_id where ((n0.properties -\u003e\u003e 'objectid') like '%-513') and n0.kind_ids operator (pg_catalog.@\u003e) array [1]::int2[] and e0.kind_id = any (array [3]::int2[]);","pi1":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select s1.root_id, e0.end_id, s1.depth + 1, ('admin_tier_0' = any (string_to_array((n1.properties -\u003e\u003e 'system_tags'), ' ')::text[])), e0.id = any (s1.path), s1.path || e0.id from forward_front s1 join edge e0 on e0.start_id = s1.next_id join node n1 on n1.id = e0.end_id where e0.kind_id = any (array [3]::int2[]);"} with s0 as (with s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select * from unidirectional_sp_harness(@pi0::text, @pi1::text, 15)) 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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id) select edges_to_path(variadic ep0)::pathcomposite as p from s0 where ((s0.n1).id <> (s0.n0).id) limit 1000; -- case: match p=shortestPath((t:NodeKind1)<-[:EdgeKind1|EdgeKind2*1..]-(s:NodeKind2)) where coalesce(t.system_tags, '') contains 'admin_tier_0' and t.name =~ 'name.*' and s<>t return p limit 1000 --- pgsql_params:{"pi0":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select e0.end_id, e0.start_id, 1, n1.kind_ids operator (pg_catalog.\u0026\u0026) array [2]::int2[], e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.end_id join node n1 on n1.id = e0.start_id where (coalesce((n0.properties -\u003e\u003e 'system_tags'), '')::text like '%admin_tier_0%' and (n0.properties -\u003e\u003e 'name') ~ 'name.*') and n0.kind_ids operator (pg_catalog.\u0026\u0026) array [1]::int2[] and e0.kind_id = any (array [3, 4]::int2[]);","pi1":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select s1.root_id, e0.start_id, s1.depth + 1, n1.kind_ids operator (pg_catalog.\u0026\u0026) array [2]::int2[], e0.id = any (s1.path), s1.path || e0.id from forward_front s1 join edge e0 on e0.end_id = s1.next_id join node n1 on n1.id = e0.start_id where e0.kind_id = any (array [3, 4]::int2[]);"} +-- pgsql_params:{"pi0":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select e0.end_id, e0.start_id, 1, n1.kind_ids operator (pg_catalog.@\u003e) array [2]::int2[], e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.end_id join node n1 on n1.id = e0.start_id where (coalesce((n0.properties -\u003e\u003e 'system_tags'), '')::text like '%admin_tier_0%' and (n0.properties -\u003e\u003e 'name') ~ 'name.*') and n0.kind_ids operator (pg_catalog.@\u003e) array [1]::int2[] and e0.kind_id = any (array [3, 4]::int2[]);","pi1":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select s1.root_id, e0.start_id, s1.depth + 1, n1.kind_ids operator (pg_catalog.@\u003e) array [2]::int2[], e0.id = any (s1.path), s1.path || e0.id from forward_front s1 join edge e0 on e0.end_id = s1.next_id join node n1 on n1.id = e0.start_id where e0.kind_id = any (array [3, 4]::int2[]);"} with s0 as (with s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select * from unidirectional_sp_harness(@pi0::text, @pi1::text, 15)) 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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id) select edges_to_path(variadic ep0)::pathcomposite as p from s0 where ((s0.n1).id <> (s0.n0).id) limit 1000; -- case: match p=shortestPath((a)-[:EdgeKind1*]->(b)) where id(a) = 1 and id(b) = 2 return p @@ -27,11 +43,11 @@ with s0 as (with s1(root_id, next_id, depth, satisfied, is_cycle, path) as (sele with s0 as (with s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select * from unidirectional_sp_harness(@pi0::text, @pi1::text, 15)) 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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id) select edges_to_path(variadic ep0)::pathcomposite as p from s0; -- case: match p=shortestPath((a)-[:EdgeKind1*]->(b:NodeKind1)) where a <> b return p --- pgsql_params:{"pi0":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select e0.end_id, e0.start_id, 1, exists (select 1 from edge where end_id = e0.end_id), e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n1 on n1.id = e0.end_id where n1.kind_ids operator (pg_catalog.\u0026\u0026) array [1]::int2[] and e0.kind_id = any (array [3]::int2[]);","pi1":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select s1.root_id, e0.start_id, s1.depth + 1, exists (select 1 from edge where end_id = e0.end_id), e0.id = any (s1.path), s1.path || e0.id from forward_front s1 join edge e0 on e0.end_id = s1.next_id where e0.kind_id = any (array [3]::int2[]);"} -with s0 as (with s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select * from unidirectional_sp_harness(@pi0::text, @pi1::text, 15)) 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 (s1.path)) as e0, s1.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 join node n1 on n1.id = s1.root_id join node n0 on n0.id = s1.next_id) select edges_to_path(variadic ep0)::pathcomposite as p from s0 where ((s0.n0).id <> (s0.n1).id); +-- pgsql_params:{"pi0":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select e0.start_id, e0.end_id, 1, n1.kind_ids operator (pg_catalog.@\u003e) array [1]::int2[], e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n1 on n1.id = e0.end_id where e0.kind_id = any (array [3]::int2[]);","pi1":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select s1.root_id, e0.end_id, s1.depth + 1, n1.kind_ids operator (pg_catalog.@\u003e) array [1]::int2[], e0.id = any (s1.path), s1.path || e0.id from forward_front s1 join edge e0 on e0.start_id = s1.next_id join node n1 on n1.id = e0.end_id where e0.kind_id = any (array [3]::int2[]);"} +with s0 as (with s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select * from unidirectional_sp_harness(@pi0::text, @pi1::text, 15)) 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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id) select edges_to_path(variadic ep0)::pathcomposite as p from s0 where ((s0.n0).id <> (s0.n1).id); -- case: match p=shortestPath((a:NodeKind2)-[:EdgeKind1*]->(b)) where a <> b return p --- pgsql_params:{"pi0":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select e0.start_id, e0.end_id, 1, exists (select 1 from edge where end_id = e0.start_id), e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.start_id where n0.kind_ids operator (pg_catalog.\u0026\u0026) array [2]::int2[] and e0.kind_id = any (array [3]::int2[]);","pi1":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select s1.root_id, e0.end_id, s1.depth + 1, exists (select 1 from edge where end_id = e0.start_id), e0.id = any (s1.path), s1.path || e0.id from forward_front s1 join edge e0 on e0.start_id = s1.next_id where e0.kind_id = any (array [3]::int2[]);"} +-- pgsql_params:{"pi0":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select e0.start_id, e0.end_id, 1, exists (select 1 from edge where end_id = e0.start_id), e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n0 on n0.id = e0.start_id where n0.kind_ids operator (pg_catalog.@\u003e) array [2]::int2[] and e0.kind_id = any (array [3]::int2[]);","pi1":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select s1.root_id, e0.end_id, s1.depth + 1, exists (select 1 from edge where end_id = e0.start_id), e0.id = any (s1.path), s1.path || e0.id from forward_front s1 join edge e0 on e0.start_id = s1.next_id where e0.kind_id = any (array [3]::int2[]);"} with s0 as (with s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select * from unidirectional_sp_harness(@pi0::text, @pi1::text, 15)) 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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id) select edges_to_path(variadic ep0)::pathcomposite as p from s0 where ((s0.n0).id <> (s0.n1).id); -- case: match p=shortestPath((b)<-[:EdgeKind1*]-(a)) where id(a) = 1 and id(b) = 2 return p @@ -39,5 +55,6 @@ with s0 as (with s1(root_id, next_id, depth, satisfied, is_cycle, path) as (sele with s0 as (with s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select * from unidirectional_sp_harness(@pi0::text, @pi1::text, 15)) 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 (s1.path)) as e0, s1.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 join node n0 on n0.id = s1.root_id join node n1 on n1.id = s1.next_id) select edges_to_path(variadic ep0)::pathcomposite as p from s0; -- case: match p = allShortestPaths((m:NodeKind1)<-[:EdgeKind1*..]-(n)) where coalesce(m.system_tags, '') contains 'admin_tier_0' and n.name = '123' and n <> m return p --- pgsql_params:{"pi0":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select e0.start_id, e0.end_id, 1, (coalesce((n0.properties -\u003e\u003e 'system_tags'), '')::text like '%admin_tier_0%') and n0.kind_ids operator (pg_catalog.\u0026\u0026) array [1]::int2[], e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n1 on n1.id = e0.start_id join node n0 on n0.id = e0.end_id where ((n1.properties -\u003e\u003e 'name') = '123') and e0.kind_id = any (array [3]::int2[]);","pi1":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select s1.root_id, e0.end_id, s1.depth + 1, (coalesce((n0.properties -\u003e\u003e 'system_tags'), '')::text like '%admin_tier_0%') and n0.kind_ids operator (pg_catalog.\u0026\u0026) array [1]::int2[], e0.id = any (s1.path), s1.path || e0.id from forward_front s1 join edge e0 on e0.start_id = s1.next_id join node n0 on n0.id = e0.end_id where e0.kind_id = any (array [3]::int2[]);","pi2":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select e0.end_id, e0.start_id, 1, ((n1.properties -\u003e\u003e 'name') = '123'), e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n1 on n1.id = e0.start_id join node n0 on n0.id = e0.end_id where (coalesce((n0.properties -\u003e\u003e 'system_tags'), '')::text like '%admin_tier_0%') and n0.kind_ids operator (pg_catalog.\u0026\u0026) array [1]::int2[] and e0.kind_id = any (array [3]::int2[]);","pi3":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select s1.root_id, e0.start_id, s1.depth + 1, ((n1.properties -\u003e\u003e 'name') = '123'), e0.id = any (s1.path), e0.id || s1.path from backward_front s1 join edge e0 on e0.end_id = s1.next_id join node n1 on n1.id = e0.start_id where e0.kind_id = any (array [3]::int2[]);"} +-- pgsql_params:{"pi0":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select e0.start_id, e0.end_id, 1, (coalesce((n0.properties -\u003e\u003e 'system_tags'), '')::text like '%admin_tier_0%') and n0.kind_ids operator (pg_catalog.@\u003e) array [1]::int2[], e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n1 on n1.id = e0.start_id join node n0 on n0.id = e0.end_id where ((n1.properties -\u003e\u003e 'name') = '123') and e0.kind_id = any (array [3]::int2[]);","pi1":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select s1.root_id, e0.end_id, s1.depth + 1, (coalesce((n0.properties -\u003e\u003e 'system_tags'), '')::text like '%admin_tier_0%') and n0.kind_ids operator (pg_catalog.@\u003e) array [1]::int2[], e0.id = any (s1.path), s1.path || e0.id from forward_front s1 join edge e0 on e0.start_id = s1.next_id join node n0 on n0.id = e0.end_id where e0.kind_id = any (array [3]::int2[]);","pi2":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select e0.end_id, e0.start_id, 1, ((n1.properties -\u003e\u003e 'name') = '123'), e0.start_id = e0.end_id, array [e0.id] from edge e0 join node n1 on n1.id = e0.start_id join node n0 on n0.id = e0.end_id where (coalesce((n0.properties -\u003e\u003e 'system_tags'), '')::text like '%admin_tier_0%') and n0.kind_ids operator (pg_catalog.@\u003e) array [1]::int2[] and e0.kind_id = any (array [3]::int2[]);","pi3":"insert into next_front (root_id, next_id, depth, satisfied, is_cycle, path) select s1.root_id, e0.start_id, s1.depth + 1, ((n1.properties -\u003e\u003e 'name') = '123'), e0.id = any (s1.path), e0.id || s1.path from backward_front s1 join edge e0 on e0.end_id = s1.next_id join node n1 on n1.id = e0.start_id where e0.kind_id = any (array [3]::int2[]);"} with s0 as (with s1(root_id, next_id, depth, satisfied, is_cycle, path) as (select * from bidirectional_asp_harness(@pi0::text, @pi1::text, @pi2::text, @pi3::text, 15)) 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 (s1.path)) as e0, s1.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 join node n1 on n1.id = s1.root_id join node n0 on n0.id = s1.next_id) select edges_to_path(variadic ep0)::pathcomposite as p from s0 where ((s0.n1).id <> (s0.n0).id); + diff --git a/cypher/models/pgsql/test/translation_cases/stepwise_traversal.sql b/cypher/models/pgsql/test/translation_cases/stepwise_traversal.sql index c4d2ebf..b535290 100644 --- a/cypher/models/pgsql/test/translation_cases/stepwise_traversal.sql +++ b/cypher/models/pgsql/test/translation_cases/stepwise_traversal.sql @@ -1,3 +1,19 @@ +-- Copyright 2026 Specter Ops, Inc. +-- +-- Licensed under the Apache License, Version 2.0 +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- SPDX-License-Identifier: Apache-2.0 + -- case: match ()-[r]->() return r 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) select s0.e0 as r from s0; @@ -14,7 +30,7 @@ with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from 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), s1 as (select s0.e0 as e0, (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s0.n0 as n0, s0.n1 as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2, (n3.id, n3.kind_ids, n3.properties)::nodecomposite as n3 from s0, edge e1 join node n2 on n2.id = e1.start_id join node n3 on n3.id = e1.end_id) select s1.e0 as r, s1.e1 as e from s1; -- case: match p = (:NodeKind1)-[:EdgeKind1|EdgeKind2]->(c:NodeKind2) where '123' in c.prop2 or '243' in c.prop2 or size(c.prop2) = 0 return p limit 10 -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 ('123' = any (jsonb_to_text_array((n1.properties -> 'prop2'))::text[]) or '243' = any (jsonb_to_text_array((n1.properties -> 'prop2'))::text[]) or jsonb_array_length((n1.properties -> 'prop2'))::int = 0) and n1.kind_ids operator (pg_catalog.&&) array [2]::int2[] and e0.kind_id = any (array [3, 4]::int2[]) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select edges_to_path(variadic array [(s0.e0).id]::int8[])::pathcomposite as p from s0 limit 10; +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 ('123' = any (jsonb_to_text_array((n1.properties -> 'prop2'))::text[]) or '243' = any (jsonb_to_text_array((n1.properties -> 'prop2'))::text[]) or jsonb_array_length((n1.properties -> 'prop2'))::int = 0) and n1.kind_ids operator (pg_catalog.@>) array [2]::int2[] and e0.kind_id = any (array [3, 4]::int2[]) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select edges_to_path(variadic array [(s0.e0).id]::int8[])::pathcomposite as p from s0 limit 10; -- case: match ()-[r:EdgeKind1]->() return count(r) as the_count 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 e0.kind_id = any (array [3]::int2[])) select count(s0.e0)::int8 as the_count from s0; @@ -28,7 +44,7 @@ with s0 as (select (e0.id, e0.start_id, e0.end_id, e0.kind_id, e0.properties)::e 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 (not ((n0.properties ->> 'objectid') like '%' || @pi2::text or (n1.properties ->> 'objectid') like '%' || @pi3::text) and n1.id = @pi0::float8) and ((e0.kind_id = any (array [3]::int2[]) or e0.kind_id = any (array [4]::int2[]))) and (not (n0.id = @pi1::float8))) select (s0.n0).id, (s0.e0).id, (s0.n1).id from s0; -- case: match (s)-[r]->(e) where s.name = '123' and e:NodeKind1 and not r.property return s, r, e -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 [1]::int2[]) and (not ((e0.properties ->> 'property'))::bool) and ((n0.properties ->> 'name') = '123')) select s0.n0 as s, s0.e0 as r, s0.n1 as e from s0; +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 [1]::int2[]) and (not ((e0.properties ->> 'property'))::bool) and ((n0.properties ->> 'name') = '123')) select s0.n0 as s, s0.e0 as r, s0.n1 as e from s0; -- case: match ()-[r]->() where r.value = 42 return r 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 (((e0.properties ->> 'value'))::int8 = 42)) select s0.e0 as r from s0; @@ -40,7 +56,7 @@ with s0 as (select (e0.id, e0.start_id, e0.end_id, e0.kind_id, e0.properties)::e 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 ((n0.properties ->> 'name') = '123')) select s0.n0 as n, s0.e0 as r from s0; -- case: match (n:NodeKind1)-[r]->() where n.name = '123' or n.name = '321' or n.name = '222' or n.name = '333' return n, r -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 ((n0.properties ->> 'name') = '123' or (n0.properties ->> 'name') = '321' or (n0.properties ->> 'name') = '222' or (n0.properties ->> 'name') = '333') and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select s0.n0 as n, s0.e0 as r from s0; +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 ((n0.properties ->> 'name') = '123' or (n0.properties ->> 'name') = '321' or (n0.properties ->> 'name') = '222' or (n0.properties ->> 'name') = '333') and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select s0.n0 as n, s0.e0 as r from s0; -- case: match (s)-[r]->(e) where s.name = '123' and e.name = '321' return s, r, e 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.properties ->> 'name') = '321') and ((n0.properties ->> 'name') = '123')) select s0.n0 as s, s0.e0 as r, s0.n1 as e from s0; @@ -64,7 +80,7 @@ with s0 as (select (e0.id, e0.start_id, e0.end_id, e0.kind_id, e0.properties)::e 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 e0.kind_id = any (array [3, 4]::int2[])), s1 as (select s0.e0 as e0, (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s0.n0 as n0, s0.n1 as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 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 [3]::int2[])) select ((s1.n0).properties -> 'name') as s_name, ((s1.n1).properties -> 'name') as e_name from s1; -- case: match (s:NodeKind1)-[r:EdgeKind1|EdgeKind2]->(e:NodeKind2) return s.name, e.name -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, 4]::int2[]) and n0.kind_ids operator (pg_catalog.&&) array [1]::int2[]) select ((s0.n0).properties -> 'name'), ((s0.n1).properties -> 'name') from s0; +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, 4]::int2[]) and n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]) select ((s0.n0).properties -> 'name'), ((s0.n1).properties -> 'name') from s0; -- case: match (s)-[r:EdgeKind1]->() where (s)-[r {prop: 'a'}]->() return s 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 (e0.properties ->> 'prop') = 'a' and e0.kind_id = any (array [3]::int2[])) select s0.n0 as s from s0 where ((with s1 as (select s0.e0 as e0, s0.n0 as n0, s0.n1 as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 from s0 join edge e0 on (s0.n0).id = (s0.e0).start_id join node n2 on n2.id = (s0.e0).end_id) select count(*) > 0 from s1)); @@ -73,7 +89,7 @@ with s0 as (select (e0.id, e0.start_id, e0.end_id, e0.kind_id, e0.properties)::e 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.id = 1) and e0.kind_id = any (array [3]::int2[]) and (not (coalesce((n0.properties ->> 'system_tags'), '')::text like '%admin\_tier\_0%'))) select (s0.n0).id, (s0.n0).kind_ids, (s0.e0).id, (s0.e0).kind_id from s0; -- case: match (s)-[r]->(e) where s:NodeKind1 and toLower(s.name) starts with 'test' and r:EdgeKind1 and id(e) in [1, 2] return r limit 1 -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.id = any (array [1, 2]::int8[])) and (e0.kind_id = any (array [3]::int2[])) and (n0.kind_ids operator (pg_catalog.&&) array [1]::int2[] and lower((n0.properties ->> 'name'))::text like 'test%')) select s0.e0 as r from s0 limit 1; +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.id = any (array [1, 2]::int8[])) and (e0.kind_id = any (array [3]::int2[])) and (n0.kind_ids operator (pg_catalog.@>) array [1]::int2[] and lower((n0.properties ->> 'name'))::text like 'test%')) select s0.e0 as r from s0 limit 1; -- case: match (n1)-[]->(n2) where n1 <> n2 return n2 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 (n0.id <> n1.id)) select s0.n1 as n2 from s0; @@ -84,3 +100,6 @@ with s0 as (select (e0.id, e0.start_id, e0.end_id, e0.kind_id, e0.properties)::e -- case: match ()-[r]->()-[e]->(n) where r <> e return n 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), s1 as (select s0.e0 as e0, (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s0.n0 as n0, s0.n1 as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 from s0 join edge e1 on (s0.n1).id = e1.start_id join node n2 on n2.id = e1.end_id where ((s0.e0).id <> e1.id)) select s1.n2 as n from s1; +-- case: match (s:NodeKind1:NodeKind2)-[r:EdgeKind1|EdgeKind2]->(e:NodeKind2:NodeKind1) return s.name, e.name +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, 1]::int2[] and e0.kind_id = any (array [3, 4]::int2[]) and n0.kind_ids operator (pg_catalog.@>) array [1, 2]::int2[]) select ((s0.n0).properties -> 'name'), ((s0.n1).properties -> 'name') from s0; + diff --git a/cypher/models/pgsql/test/translation_cases/update.sql b/cypher/models/pgsql/test/translation_cases/update.sql index 47c33c8..cbd5cde 100644 --- a/cypher/models/pgsql/test/translation_cases/update.sql +++ b/cypher/models/pgsql/test/translation_cases/update.sql @@ -1,3 +1,19 @@ +-- Copyright 2026 Specter Ops, Inc. +-- +-- Licensed under the Apache License, Version 2.0 +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- SPDX-License-Identifier: Apache-2.0 + -- case: match (n) set n.other = 1 set n.prop = '1' return n with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0), s1 as (update node n1 set properties = n1.properties || jsonb_build_object('other', 1, 'prop', '1')::jsonb from s0 where (s0.n0).id = n1.id returning (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n0) select s1.n0 as n from s1; @@ -29,10 +45,10 @@ with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0), s1 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1), s2 as (update node n2 set properties = n2.properties || jsonb_build_object('target', true)::jsonb from s1 where (s1.n0).id = n2.id returning (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n0, s1.n1 as n1), s3 as (update node n3 set properties = n3.properties || jsonb_build_object('target', true)::jsonb from s2 where (s2.n1).id = n3.id returning s2.n0 as n0, (n3.id, n3.kind_ids, n3.properties)::nodecomposite as n1) select s3.n0 as n1, s3.n1 as n3 from s3; -- case: match ()-[r]->(:NodeKind1) set r.is_special_outbound = true -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 [1]::int2[]), s1 as (update edge e1 set properties = e1.properties || jsonb_build_object('is_special_outbound', true)::jsonb from s0 where (s0.e0).id = e1.id returning (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e0, s0.n0 as n0, s0.n1 as n1) select 1; +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 [1]::int2[]), s1 as (update edge e1 set properties = e1.properties || jsonb_build_object('is_special_outbound', true)::jsonb from s0 where (s0.e0).id = e1.id returning (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e0, s0.n0 as n0, s0.n1 as n1) select 1; -- case: match (a)-[r]->(:NodeKind1) set a.name = '123', r.is_special_outbound = true -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 [1]::int2[]), s1 as (update node n2 set properties = n2.properties || jsonb_build_object('name', '123')::jsonb from s0 where (s0.n0).id = n2.id returning s0.e0 as e0, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n0, s0.n1 as n1), s2 as (update edge e1 set properties = e1.properties || jsonb_build_object('is_special_outbound', true)::jsonb from s1 where (s1.e0).id = e1.id returning (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e0, s1.n0 as n0, s1.n1 as n1) select 1; +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 [1]::int2[]), s1 as (update node n2 set properties = n2.properties || jsonb_build_object('name', '123')::jsonb from s0 where (s0.n0).id = n2.id returning s0.e0 as e0, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n0, s0.n1 as n1), s2 as (update edge e1 set properties = e1.properties || jsonb_build_object('is_special_outbound', true)::jsonb from s1 where (s1.e0).id = e1.id returning (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e0, s1.n0 as n0, s1.n1 as n1) select 1; -- case: match (s) remove s.name with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0), s1 as (update node n1 set properties = n1.properties - array ['name']::text[] from s0 where (s0.n0).id = n1.id returning (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n0) select 1; @@ -47,7 +63,7 @@ with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'name') = 'n1')), s1 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where ((n1.properties ->> 'name') = 'n4')), s2 as (update node n2 set properties = n2.properties || jsonb_build_object('name', ((s1.n1).properties -> 'name'))::jsonb from s1 where (s1.n0).id = n2.id returning (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n0, s1.n1 as n1), s3 as (update node n3 set properties = n3.properties || jsonb_build_object('name', 'RENAMED')::jsonb from s2 where (s2.n1).id = n3.id returning s2.n0 as n0, (n3.id, n3.kind_ids, n3.properties)::nodecomposite as n1) select 1; -- case: match (n)-[r:EdgeKind1]->() where n:NodeKind1 set r.visited = true return r -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 e0.kind_id = any (array [3]::int2[]) and (n0.kind_ids operator (pg_catalog.&&) array [1]::int2[])), s1 as (update edge e1 set properties = e1.properties || jsonb_build_object('visited', true)::jsonb from s0 where (s0.e0).id = e1.id returning (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e0, s0.n0 as n0, s0.n1 as n1) select s1.e0 as r from s1; +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 e0.kind_id = any (array [3]::int2[]) and (n0.kind_ids operator (pg_catalog.@>) array [1]::int2[])), s1 as (update edge e1 set properties = e1.properties || jsonb_build_object('visited', true)::jsonb from s0 where (s0.e0).id = e1.id returning (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e0, s0.n0 as n0, s0.n1 as n1) select s1.e0 as r from s1; -- case: match (n)-[]->()-[r]->() where n.name = 'n1' set r.visited = true return r.name 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 ((n0.properties ->> 'name') = 'n1')), s1 as (select s0.e0 as e0, (e1.id, e1.start_id, e1.end_id, e1.kind_id, e1.properties)::edgecomposite as e1, s0.n0 as n0, s0.n1 as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 from s0 join edge e1 on (s0.n1).id = e1.start_id join node n2 on n2.id = e1.end_id), s2 as (update edge e2 set properties = e2.properties || jsonb_build_object('visited', true)::jsonb from s1 where (s1.e1).id = e2.id returning s1.e0 as e0, (e2.id, e2.start_id, e2.end_id, e2.kind_id, e2.properties)::edgecomposite as e1, s1.n0 as n0, s1.n1 as n1, s1.n2 as n2) select ((s2.e1).properties -> 'name') from s2; diff --git a/cypher/models/pgsql/translate/hinting.go b/cypher/models/pgsql/translate/hinting.go index 7a6bf72..74f8bb7 100644 --- a/cypher/models/pgsql/translate/hinting.go +++ b/cypher/models/pgsql/translate/hinting.go @@ -137,7 +137,7 @@ func InferExpressionType(expression pgsql.Expression) (pgsql.DataType, error) { case pgsql.OperatorAnd, pgsql.OperatorOr, pgsql.OperatorEquals, pgsql.OperatorGreaterThan, pgsql.OperatorGreaterThanOrEqualTo, pgsql.OperatorLessThan, pgsql.OperatorLessThanOrEqualTo, pgsql.OperatorIn, pgsql.OperatorJSONBFieldExists, - pgsql.OperatorLike, pgsql.OperatorILike, pgsql.OperatorPGArrayOverlap: + pgsql.OperatorLike, pgsql.OperatorILike, pgsql.OperatorPGArrayOverlap, pgsql.OperatorPGArrayLHSContainsRHS: return pgsql.Boolean, nil default: diff --git a/cypher/models/pgsql/translate/kind.go b/cypher/models/pgsql/translate/kind.go index cb2bf72..71ae610 100644 --- a/cypher/models/pgsql/translate/kind.go +++ b/cypher/models/pgsql/translate/kind.go @@ -17,7 +17,7 @@ func newPGKindIDMatcher(scope *Scope, treeTranslator *ExpressionTreeTranslator, treeTranslator.PushOperand(pgd.Column(binding.Identifier, pgsql.ColumnKindIDs)) treeTranslator.PushOperand(kindIDsLiteral) - return treeTranslator.CompleteBinaryExpression(scope, pgsql.OperatorPGArrayOverlap) + return treeTranslator.CompleteBinaryExpression(scope, pgsql.OperatorPGArrayLHSContainsRHS) case pgsql.EdgeComposite, pgsql.ExpansionEdge: treeTranslator.PushOperand(pgsql.CompoundIdentifier{binding.Identifier, pgsql.ColumnKindID}) diff --git a/cypher/models/pgsql/translate/node.go b/cypher/models/pgsql/translate/node.go index b585d2d..fcf3a20 100644 --- a/cypher/models/pgsql/translate/node.go +++ b/cypher/models/pgsql/translate/node.go @@ -54,7 +54,7 @@ func (s *Translator) translateNodePatternToStep(nodePattern *cypher.NodePattern, return err } else if err := s.treeTranslator.AddTranslationConstraint(pgsql.NewIdentifierSet().Add(bindingResult.Binding.Identifier), pgsql.NewBinaryExpression( pgsql.CompoundIdentifier{bindingResult.Binding.Identifier, pgsql.ColumnKindIDs}, - pgsql.OperatorPGArrayOverlap, + pgsql.OperatorPGArrayLHSContainsRHS, kindIDsLiteral, )); err != nil { return err