Skip to content

Commit 48d3ba3

Browse files
authored
Fix same-table subquery dependency deduplication (#4579)
This PR fixes the bugs with where clauses such as `id IN (SELECT a FROM rel ...) OR id IN (SELECT b FROM rel ...)` where both subqueries are treated as the same even though there selected columns differ, as raised by [vibl](#3675 (comment)) ## Summary - include explicitly selected columns in shape comparability so same-table subqueries projecting different PK columns remain distinct dependencies - bump the shape status version because shape hash semantics changed
1 parent 8750fa7 commit 48d3ba3

4 files changed

Lines changed: 42 additions & 1 deletion

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@core/sync-service": patch
3+
---
4+
5+
Fix subquery dependency deduplication for same-table subqueries that project different columns, preventing plain snapshots from silently dropping one arm of an `OR` filter.

packages/sync-service/lib/electric/shape_cache/shape_status.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ defmodule Electric.ShapeCache.ShapeStatus do
3333
}
3434

3535
# MUST be updated when Shape.comparable/1 changes.
36-
@version 8
36+
@version 9
3737

3838
# Tuple format: {handle, hash, snapshot_started, last_read_time, generation}
3939
@shape_last_used_time_pos 4

packages/sync-service/lib/electric/shapes/shape.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ defmodule Electric.Shapes.Shape do
113113
def comparable(%__MODULE__{} = shape) do
114114
{:shape, {shape.root_table_id, shape.root_table}, shape.root_pk,
115115
Comparable.comparable(shape.where), shape.selected_columns,
116+
shape.explicitly_selected_columns,
116117
Enum.flat_map(shape.flags, fn {k, v} -> if(v, do: [k], else: []) end) |> Enum.sort(),
117118
shape.replica, shape.log_mode}
118119
end

packages/sync-service/test/electric/shapes/shape_test.exs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -983,6 +983,41 @@ defmodule Electric.Shapes.ShapeTest do
983983
end
984984
end
985985

986+
describe "subquery dependency construction" do
987+
test "does not deduplicate subqueries with different projected columns" do
988+
inspector =
989+
Support.StubInspector.new(%{
990+
"item" => [
991+
%{name: "id", pk_position: 0}
992+
],
993+
"rel" => [
994+
%{name: "a", pk_position: 0},
995+
%{name: "b", pk_position: 1},
996+
%{name: "kind", pk_position: 2}
997+
]
998+
})
999+
1000+
assert {:ok, %Shape{where: where, shape_dependencies: dependencies}} =
1001+
Shape.new("item",
1002+
inspector: inspector,
1003+
feature_flags: ["allow_subqueries"],
1004+
where:
1005+
"id IN (SELECT a FROM rel WHERE kind = 'k') OR id IN (SELECT b FROM rel WHERE kind = 'k')"
1006+
)
1007+
1008+
assert Enum.map(dependencies, & &1.explicitly_selected_columns) == [["a"], ["b"]]
1009+
1010+
assert where.query ==
1011+
"id IN (SELECT a FROM public.rel WHERE kind = 'k') OR id IN (SELECT b FROM public.rel WHERE kind = 'k')"
1012+
1013+
assert where.used_refs == %{
1014+
["id"] => :text,
1015+
["$sublink", "0"] => {:array, :text},
1016+
["$sublink", "1"] => {:array, :text}
1017+
}
1018+
end
1019+
end
1020+
9861021
describe "new!/2" do
9871022
import Support.DbSetup
9881023
import Support.DbStructureSetup

0 commit comments

Comments
 (0)