Skip to content

Commit b55df96

Browse files
authored
fix error on complex delete_all (#255)
* reproduce * fix?
1 parent c14350d commit b55df96

File tree

5 files changed

+156
-3
lines changed

5 files changed

+156
-3
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
- Fix `delete_all` with subqueries https://github.com/plausible/ecto_ch/pull/255
6+
37
## 0.8.2 (2025-08-26)
48

59
- Add `Ecto.Query.API.splice/1` support https://github.com/plausible/ecto_ch/pull/240

lib/ecto/adapters/clickhouse/connection.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ defmodule Ecto.Adapters.ClickHouse.Connection do
125125
where =
126126
case query.wheres do
127127
[] -> " WHERE 1"
128-
_ -> where(query, {{nil, nil, nil}}, params)
128+
_ -> where(query, {{nil, nil, nil}, []}, params)
129129
end
130130

131131
["ALTER TABLE ", quote_table(prefix, table), " UPDATE ", fields, where]
@@ -196,7 +196,7 @@ defmodule Ecto.Adapters.ClickHouse.Connection do
196196
where =
197197
case query.wheres do
198198
[] -> " WHERE 1"
199-
_ -> where(query, {{nil, nil, nil}}, params)
199+
_ -> where(query, {{nil, nil, nil}, []}, params)
200200
end
201201

202202
["DELETE FROM ", quote_table(prefix, table) | where]

test/ecto/adapters/clickhouse/connection_test.exs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1472,6 +1472,28 @@ defmodule Ecto.Adapters.ClickHouse.ConnectionTest do
14721472
assert delete_all(query) == ~s{DELETE FROM "first"."schema" WHERE 1}
14731473
end
14741474

1475+
# https://github.com/plausible/ecto_ch/issues/247
1476+
test "delete all with subquery" do
1477+
hashes =
1478+
from uim in "user_id_map",
1479+
where: uim.account_id == ^91241 and like(uim.user_id, "anon:%"),
1480+
select: uim.user_id_hash
1481+
1482+
query =
1483+
from rup in "recent_user_profiles",
1484+
where: rup.account_id == ^91241 and rup.user_id_hash in subquery(hashes)
1485+
1486+
assert delete_all(query) == """
1487+
DELETE FROM "recent_user_profiles" \
1488+
WHERE (\
1489+
("account_id" = {$0:Int64}) AND \
1490+
("user_id_hash" IN (\
1491+
SELECT su0."user_id_hash" FROM "user_id_map" AS su0 \
1492+
WHERE (\
1493+
(su0."account_id" = {$1:Int64}) AND (su0."user_id" LIKE 'anon:%')))))\
1494+
"""
1495+
end
1496+
14751497
test "CTE alter_update_all" do
14761498
cte_query =
14771499
from(x in Schema,
@@ -1513,6 +1535,28 @@ defmodule Ecto.Adapters.ClickHouse.ConnectionTest do
15131535
fn -> alter_update_all(query) end
15141536
end
15151537

1538+
test "alter_update_all with subquery" do
1539+
hashes =
1540+
from uim in "user_id_map",
1541+
where: uim.account_id == ^91241 and like(uim.user_id, "anon:%"),
1542+
select: uim.user_id_hash
1543+
1544+
query =
1545+
from rup in "recent_user_profiles",
1546+
where: rup.account_id == ^91241 and rup.user_id_hash in subquery(hashes),
1547+
update: [set: [timestamp: ^0]]
1548+
1549+
assert alter_update_all(query) == """
1550+
ALTER TABLE "recent_user_profiles" UPDATE "timestamp"={$0:Int64} \
1551+
WHERE (\
1552+
("account_id" = {$1:Int64}) AND \
1553+
("user_id_hash" IN (\
1554+
SELECT su0."user_id_hash" FROM "user_id_map" AS su0 \
1555+
WHERE (\
1556+
(su0."account_id" = {$2:Int64}) AND (su0."user_id" LIKE 'anon:%')))))\
1557+
"""
1558+
end
1559+
15161560
# TODO alter_delete_all
15171561

15181562
describe "windows" do
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
defmodule Ecto.Integration.DeleteAllTest do
2+
use Ecto.Integration.Case
3+
import Ecto.Query
4+
alias Ecto.Integration.TestRepo
5+
6+
@moduletag :lightweight_delete
7+
8+
# based on https://github.com/plausible/ecto_ch/issues/247
9+
10+
setup do
11+
TestRepo.query!("""
12+
CREATE TABLE user_id_map (
13+
account_id UInt64,
14+
user_id_hash UInt64,
15+
user_id String
16+
) ENGINE ReplacingMergeTree() ORDER BY (account_id, user_id_hash)
17+
""")
18+
19+
on_exit(fn -> TestRepo.query!("DROP TABLE user_id_map") end)
20+
21+
TestRepo.query!("""
22+
CREATE TABLE recent_user_profiles (
23+
account_id UInt64,
24+
user_id_hash UInt64,
25+
timestamp DateTime64(3)
26+
) ENGINE ReplacingMergeTree(timestamp)
27+
ORDER BY (account_id, user_id_hash)
28+
SETTINGS enable_block_number_column = 1, enable_block_offset_column = 1
29+
""")
30+
31+
on_exit(fn -> TestRepo.query!("DROP TABLE recent_user_profiles") end)
32+
33+
:ok
34+
end
35+
36+
defmodule UserIdMap do
37+
use Ecto.Schema
38+
39+
@primary_key false
40+
schema "user_id_map" do
41+
field :account_id, Ch, type: "UInt64"
42+
field :user_id_hash, Ch, type: "UInt64"
43+
field :user_id, :string
44+
end
45+
end
46+
47+
defmodule RecentUserProfiles do
48+
use Ecto.Schema
49+
50+
@primary_key false
51+
schema "recent_user_profiles" do
52+
field :account_id, Ch, type: "UInt64"
53+
field :user_id_hash, Ch, type: "UInt64"
54+
field :timestamp, Ch, type: "DateTime64(3)"
55+
end
56+
end
57+
58+
test "delete_all with subqeury" do
59+
TestRepo.insert!(%UserIdMap{account_id: 91241, user_id_hash: 100, user_id: "anon:123"})
60+
TestRepo.insert!(%UserIdMap{account_id: 91241, user_id_hash: 200, user_id: "registered:bob"})
61+
62+
TestRepo.insert!(%RecentUserProfiles{
63+
account_id: 91241,
64+
user_id_hash: 100,
65+
timestamp: ~N[2023-01-01 10:00:00.000000]
66+
})
67+
68+
TestRepo.insert!(%RecentUserProfiles{
69+
account_id: 91241,
70+
user_id_hash: 200,
71+
timestamp: ~N[2023-01-01 12:00:00.000000]
72+
})
73+
74+
list_recent_user_profiles = fn ->
75+
TestRepo.all(
76+
from rup in RecentUserProfiles,
77+
inner_join: uim in UserIdMap,
78+
on: uim.user_id_hash == rup.user_id_hash,
79+
order_by: [asc: :timestamp],
80+
select: uim.user_id
81+
)
82+
end
83+
84+
assert list_recent_user_profiles.() == ["anon:123", "registered:bob"]
85+
86+
hashes =
87+
from uim in UserIdMap,
88+
where: uim.account_id == ^91241 and like(uim.user_id, "anon:%"),
89+
select: uim.user_id_hash
90+
91+
query =
92+
from rup in RecentUserProfiles,
93+
where: rup.account_id == ^91241 and rup.user_id_hash in subquery(hashes)
94+
95+
TestRepo.delete_all(query,
96+
settings: [
97+
lightweight_deletes_sync: 0,
98+
lightweight_delete_mode: "lightweight_update_force",
99+
alter_update_mode: "lightweight"
100+
]
101+
)
102+
103+
assert list_recent_user_profiles.() == ["registered:bob"]
104+
end
105+
end

test/test_helper.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ exclude =
5252
[]
5353
else
5454
# these types are not supported in older ClickHouse versions we have in the CI
55-
[:time, :variant, :json, :dynamic]
55+
[:time, :variant, :json, :dynamic, :lightweight_delete]
5656
end
5757

5858
{:ok, _} = Ecto.Adapters.ClickHouse.ensure_all_started(TestRepo.config(), :temporary)

0 commit comments

Comments
 (0)