From a1c4a5d7cf99247ef9964fb530a6d2d46468dc82 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Fri, 18 Oct 2019 14:11:30 +0300 Subject: [PATCH 1/3] Fix a migration wiping user info of users that don't have any mutes And introduce safe_jsonb_set --- lib/mix/tasks/pleroma/database.ex | 4 ++-- lib/pleroma/object.ex | 4 ++-- lib/pleroma/user.ex | 6 ++--- lib/pleroma/web/activity_pub/utils.ex | 2 +- .../20190711042021_create_safe_jsonb_set.exs | 22 +++++++++++++++++++ ...2024_copy_muted_to_muted_notifications.exs | 2 +- 6 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 priv/repo/migrations/20190711042021_create_safe_jsonb_set.exs diff --git a/lib/mix/tasks/pleroma/database.ex b/lib/mix/tasks/pleroma/database.ex index bcc2052d6..ab7529e08 100644 --- a/lib/mix/tasks/pleroma/database.ex +++ b/lib/mix/tasks/pleroma/database.ex @@ -54,7 +54,7 @@ def run(["remove_embedded_objects" | args]) do Logger.info("Removing embedded objects") Repo.query!( - "update activities set data = jsonb_set(data, '{object}'::text[], data->'object'->'id') where data->'object'->>'id' is not null;", + "update activities set data = safe_jsonb_set(data, '{object}'::text[], data->'object'->'id') where data->'object'->>'id' is not null;", [], timeout: :infinity ) @@ -152,7 +152,7 @@ def run(["fix_likes_collections"]) do set: [ data: fragment( - "jsonb_set(?, '{likes}', '[]'::jsonb, true)", + "safe_jsonb_set(?, '{likes}', '[]'::jsonb, true)", object.data ) ] diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index 3fa407931..bf37b28a7 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -181,7 +181,7 @@ def increase_replies_count(ap_id) do data: fragment( """ - jsonb_set(?, '{repliesCount}', + safe_jsonb_set(?, '{repliesCount}', (coalesce((?->>'repliesCount')::int, 0) + 1)::varchar::jsonb, true) """, o.data, @@ -204,7 +204,7 @@ def decrease_replies_count(ap_id) do data: fragment( """ - jsonb_set(?, '{repliesCount}', + safe_jsonb_set(?, '{repliesCount}', (greatest(0, (?->>'repliesCount')::int - 1))::varchar::jsonb, true) """, o.data, diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 617f160e0..f0912fb10 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -718,7 +718,7 @@ def increase_note_count(%User{} = user) do set: [ info: fragment( - "jsonb_set(?, '{note_count}', ((?->>'note_count')::int + 1)::varchar::jsonb, true)", + "safe_jsonb_set(?, '{note_count}', ((?->>'note_count')::int + 1)::varchar::jsonb, true)", u.info, u.info ) @@ -739,7 +739,7 @@ def decrease_note_count(%User{} = user) do set: [ info: fragment( - "jsonb_set(?, '{note_count}', (greatest(0, (?->>'note_count')::int - 1))::varchar::jsonb, true)", + "safe_jsonb_set(?, '{note_count}', (greatest(0, (?->>'note_count')::int - 1))::varchar::jsonb, true)", u.info, u.info ) @@ -812,7 +812,7 @@ def update_follower_count(%User{} = user) do set: [ info: fragment( - "jsonb_set(?, '{follower_count}', ?::varchar::jsonb, true)", + "safe_jsonb_set(?, '{follower_count}', ?::varchar::jsonb, true)", u.info, s.count ) diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 39a532db3..f22cc2367 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -349,7 +349,7 @@ def update_follow_state_for_all( try do Ecto.Adapters.SQL.query!( Repo, - "UPDATE activities SET data = jsonb_set(data, '{state}', $1) WHERE data->>'type' = 'Follow' AND data->>'actor' = $2 AND data->>'object' = $3 AND data->>'state' = 'pending'", + "UPDATE activities SET data = safe_jsonb_set(data, '{state}', $1) WHERE data->>'type' = 'Follow' AND data->>'actor' = $2 AND data->>'object' = $3 AND data->>'state' = 'pending'", [state, actor, object] ) diff --git a/priv/repo/migrations/20190711042021_create_safe_jsonb_set.exs b/priv/repo/migrations/20190711042021_create_safe_jsonb_set.exs new file mode 100644 index 000000000..d70961431 --- /dev/null +++ b/priv/repo/migrations/20190711042021_create_safe_jsonb_set.exs @@ -0,0 +1,22 @@ +defmodule Pleroma.Repo.Migrations.CreateSafeJsonbSet do + use Ecto.Migration + alias Pleroma.User + + def change do + execute(""" + create or replace function safe_jsonb_set(target jsonb, path text[], new_value jsonb, create_missing boolean default true) returns jsonb as $$ + declare + result jsonb; + begin + result := jsonb_set(target, path, coalesce(new_value, 'null'::jsonb), create_missing); + if result is NULL then + raise 'jsonb_set tried to wipe the object, please report this incindent to Pleroma bug tracker. https://git.pleroma.social/pleroma/pleroma/issues/new'; + return target; + else + return result; + end if; + end; + $$ language plpgsql; + """) + end +end diff --git a/priv/repo/migrations/20190711042024_copy_muted_to_muted_notifications.exs b/priv/repo/migrations/20190711042024_copy_muted_to_muted_notifications.exs index b717cab2e..a3c5b52de 100644 --- a/priv/repo/migrations/20190711042024_copy_muted_to_muted_notifications.exs +++ b/priv/repo/migrations/20190711042024_copy_muted_to_muted_notifications.exs @@ -3,6 +3,6 @@ defmodule Pleroma.Repo.Migrations.CopyMutedToMutedNotifications do alias Pleroma.User def change do - execute("update users set info = jsonb_set(info, '{muted_notifications}', info->'mutes', true) where local = true") + execute("update users set info = safe_jsonb_set(info, '{muted_notifications}', info->'mutes', true) where local = true") end end From 98c9affc485a3d23d53cead9e6e40e837e467eae Mon Sep 17 00:00:00 2001 From: rinpatch Date: Fri, 18 Oct 2019 14:33:51 +0300 Subject: [PATCH 2/3] Add a test for safe_jsonb_set --- test/safe_jsonb_set_test.exs | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 test/safe_jsonb_set_test.exs diff --git a/test/safe_jsonb_set_test.exs b/test/safe_jsonb_set_test.exs new file mode 100644 index 000000000..748540570 --- /dev/null +++ b/test/safe_jsonb_set_test.exs @@ -0,0 +1,12 @@ +defmodule Pleroma.SafeJsonbSetTest do + use Pleroma.DataCase + + test "it doesn't wipe the object when asked to set the value to NULL" do + assert %{rows: [[%{"key" => "value", "test" => nil}]]} = + Ecto.Adapters.SQL.query!( + Pleroma.Repo, + "select safe_jsonb_set('{\"key\": \"value\"}'::jsonb, '{test}', NULL);", + [] + ) + end +end From 41c47639e3eae5d5e2840e9a9bb51cd99660af91 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Fri, 18 Oct 2019 14:40:47 +0300 Subject: [PATCH 3/3] Add a changelog entry for yet another jsonb_set data loss --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb3785a02..3c282514a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [1.1.1] - 2019-10-18 +### Fixed +- One of the migrations between 1.0.0 and 1.1.0 wiping user info of the relay user because of unexpected behavior of postgresql's `jsonb_set`, resulting in inability to post in the default configuration. If you were affected, please run the following query in postgres console, the relay user will be recreated automatically: +``` +delete from users where ap_id = 'https://your.instance.hostname/relay'; +``` + ## [1.1.0] - 2019-10-14 **Breaking:** The stable branch has been changed from `master` to `stable`. If you want to keep using 1.0, the `release/1.0` branch will receive security updates for 6 months after 1.1 release.