diff --git a/lib/philomena/images.ex b/lib/philomena/images.ex index 44403fb8..7f823bf6 100644 --- a/lib/philomena/images.ex +++ b/lib/philomena/images.ex @@ -92,11 +92,6 @@ defmodule Philomena.Images do |> Image.cache_changeset() |> repo.update() end) - |> Multi.run(:source_change, fn repo, %{image: image} -> - %SourceChange{image_id: image.id, initial: true} - |> SourceChange.creation_changeset(attrs, attribution) - |> repo.insert() - end) |> Multi.run(:added_tag_count, fn repo, %{image: image} -> tag_ids = image.added_tags |> Enum.map(& &1.id) tags = Tag |> where([t], t.id in ^tag_ids) @@ -333,29 +328,69 @@ defmodule Philomena.Images do |> Repo.update() end - def update_source(%Image{} = image, attribution, attrs) do - image_changes = - image - |> Image.source_changeset(attrs) - - source_changes = - Ecto.build_assoc(image, :source_changes) - |> SourceChange.creation_changeset(attrs, attribution) + def update_sources(%Image{} = image, attribution, attrs) do + old_sources = attrs["old_source_input"] + new_sources = attrs["source_input"] Multi.new() - |> Multi.update(:image, image_changes) - |> Multi.run(:source_change, fn repo, _changes -> - case image_changes.changes do - %{source_url: _new_source} -> - repo.insert(source_changes) + |> Multi.run(:image, fn repo, _chg -> + image = repo.preload(image, [:sources]) - _ -> - {:ok, nil} + image + |> Image.source_changeset(%{}, old_sources, new_sources) + |> repo.update() + |> case do + {:ok, image} -> + {:ok, {image, image.added_sources, image.removed_sources}} + + error -> + error end end) + |> Multi.run(:added_source_changes, fn repo, %{image: {image, added_sources, _removed}} -> + source_changes = + added_sources + |> Enum.map(&source_change_attributes(attribution, image, &1, true, attribution[:user])) + + {count, nil} = repo.insert_all(SourceChange, source_changes) + + {:ok, count} + end) + |> Multi.run(:removed_source_changes, fn repo, %{image: {image, _added, removed_sources}} -> + source_changes = + removed_sources + |> Enum.map(&source_change_attributes(attribution, image, &1, false, attribution[:user])) + + {count, nil} = repo.insert_all(SourceChange, source_changes) + + {:ok, count} + end) |> Repo.transaction() end + defp source_change_attributes(attribution, image, source, added, user) do + now = DateTime.utc_now() |> DateTime.truncate(:second) + + user_id = + case user do + nil -> nil + user -> user.id + end + + %{ + image_id: image.id, + source: source, + user_id: user_id, + created_at: now, + updated_at: now, + ip: attribution[:ip], + fingerprint: attribution[:fingerprint], + user_agent: attribution[:user_agent], + referrer: attribution[:referrer], + added: added + } + end + def update_locked_tags(%Image{} = image, attrs) do new_tags = Tags.get_or_create_tags(attrs["tag_input"]) @@ -787,6 +822,7 @@ defmodule Philomena.Images do :hiders, :deleter, :gallery_interactions, + :sources, tags: [:aliases, :aliased_tag] ] end diff --git a/lib/philomena/images/elasticsearch_index.ex b/lib/philomena/images/elasticsearch_index.ex index 0936656f..b76912bd 100644 --- a/lib/philomena/images/elasticsearch_index.ex +++ b/lib/philomena/images/elasticsearch_index.ex @@ -119,7 +119,7 @@ defmodule Philomena.Images.ElasticsearchIndex do mime_type: image.image_mime_type, uploader: if(!!image.user and !image.anonymous, do: String.downcase(image.user.name)), true_uploader: if(!!image.user, do: String.downcase(image.user.name)), - source_url: image.source_url |> to_string |> String.downcase(), + source_url: image.sources |> Enum.map(&String.downcase(&1.source)), file_name: image.image_name, original_format: image.image_format, processed: image.processed, diff --git a/lib/philomena/images/image.ex b/lib/philomena/images/image.ex index 34eeb64b..a7ae4f3c 100644 --- a/lib/philomena/images/image.ex +++ b/lib/philomena/images/image.ex @@ -8,6 +8,7 @@ defmodule Philomena.Images.Image do alias Philomena.ImageVotes.ImageVote alias Philomena.ImageFaves.ImageFave alias Philomena.ImageHides.ImageHide + alias Philomena.Images.Source alias Philomena.Images.Subscription alias Philomena.Users.User alias Philomena.Tags.Tag @@ -18,6 +19,7 @@ defmodule Philomena.Images.Image do alias Philomena.Images.Image alias Philomena.Images.TagDiffer + alias Philomena.Images.SourceDiffer alias Philomena.Images.TagValidator alias Philomena.Images.DnpValidator alias Philomena.Repo @@ -42,6 +44,7 @@ defmodule Philomena.Images.Image do many_to_many :locked_tags, Tag, join_through: "image_tag_locks", on_replace: :delete has_one :intensity, ImageIntensity has_many :galleries, through: [:gallery_interactions, :image] + has_many :sources, Source field :image, :string field :image_name, :string @@ -91,6 +94,8 @@ defmodule Philomena.Images.Image do field :removed_tags, {:array, :any}, default: [], virtual: true field :added_tags, {:array, :any}, default: [], virtual: true + field :removed_sources, {:array, :any}, default: [], virtual: true + field :added_sources, {:array, :any}, default: [], virtual: true field :uploaded_image, :string, virtual: true field :removed_image, :string, virtual: true @@ -203,11 +208,10 @@ defmodule Philomena.Images.Image do |> change(image: nil) end - def source_changeset(image, attrs) do + def source_changeset(image, attrs, old_sources, new_sources) do image - |> cast(attrs, [:source_url]) - |> validate_required(:source_url) - |> validate_format(:source_url, ~r/\Ahttps?:\/\//) + |> cast(attrs, []) + |> SourceDiffer.diff_input(old_sources, new_sources) end def tag_changeset(image, attrs, old_tags, new_tags, excluded_tags \\ []) do diff --git a/lib/philomena/images/source.ex b/lib/philomena/images/source.ex index d341a1d4..476872b9 100644 --- a/lib/philomena/images/source.ex +++ b/lib/philomena/images/source.ex @@ -4,9 +4,10 @@ defmodule Philomena.Images.Source do alias Philomena.Images.Image + @primary_key false schema "image_sources" do - belongs_to :image, Image - field :source, :string + belongs_to :image, Image, primary_key: true + field :source, :string, primary_key: true end @doc false diff --git a/lib/philomena/images/source_differ.ex b/lib/philomena/images/source_differ.ex new file mode 100644 index 00000000..19d8bd16 --- /dev/null +++ b/lib/philomena/images/source_differ.ex @@ -0,0 +1,50 @@ +defmodule Philomena.Images.SourceDiffer do + import Ecto.Changeset + alias Philomena.Images.Source + + def diff_input(changeset, old_sources, new_sources) do + old_set = MapSet.new(old_sources) + new_set = MapSet.new(new_sources) + + source_set = MapSet.new(get_field(changeset, :sources), & &1.source) + added_sources = MapSet.difference(new_set, old_set) + removed_sources = MapSet.difference(old_set, new_set) + + {sources, actually_added, actually_removed} = + apply_changes(source_set, added_sources, removed_sources) + + image_id = fetch_field!(changeset, :id) + + changeset + |> put_change(:added_sources, actually_added) + |> put_change(:removed_sources, actually_removed) + |> put_assoc(:sources, source_structs(image_id, sources)) + end + + defp apply_changes(source_set, added_set, removed_set) do + desired_sources = + source_set + |> MapSet.difference(removed_set) + |> MapSet.union(added_set) + + actually_added = + desired_sources + |> MapSet.difference(source_set) + |> Enum.to_list() + + actually_removed = + source_set + |> MapSet.difference(desired_sources) + |> Enum.to_list() + + sources = Enum.to_list(desired_sources) + actually_added = Enum.to_list(actually_added) + actually_removed = Enum.to_list(actually_removed) + + {sources, actually_added, actually_removed} + end + + defp source_structs(image_id, sources) do + Enum.map(sources, &%Source{image_id: image_id, source: &1}) + end +end diff --git a/lib/philomena/source_changes/source_change.ex b/lib/philomena/source_changes/source_change.ex index 6d6840bf..3cff4685 100644 --- a/lib/philomena/source_changes/source_change.ex +++ b/lib/philomena/source_changes/source_change.ex @@ -10,10 +10,10 @@ defmodule Philomena.SourceChanges.SourceChange do field :fingerprint, :string field :user_agent, :string, default: "" field :referrer, :string, default: "" - field :new_value, :string - field :initial, :boolean, default: false + field :value, :string + field :added, :boolean - field :source_url, :string, source: :new_value + field :source_url, :string, source: :value timestamps(inserted_at: :created_at, type: :utc_datetime) end diff --git a/lib/philomena_web/controllers/image/source_controller.ex b/lib/philomena_web/controllers/image/source_controller.ex index f741525c..373dcc10 100644 --- a/lib/philomena_web/controllers/image/source_controller.ex +++ b/lib/philomena_web/controllers/image/source_controller.ex @@ -21,19 +21,18 @@ defmodule PhilomenaWeb.Image.SourceController do plug :load_and_authorize_resource, model: Image, id_name: "image_id", - preload: [:user, tags: :aliases] + preload: [:user, :sources, tags: :aliases] def update(conn, %{"image" => image_params}) do attributes = conn.assigns.attributes image = conn.assigns.image - old_source = image.source_url - case Images.update_source(image, attributes, image_params) do - {:ok, %{image: image}} -> + case Images.update_sources(image, attributes, image_params) do + {:ok, %{image: {image, added_sources, removed_sources}}} -> PhilomenaWeb.Endpoint.broadcast!( "firehose", "image:source_update", - %{image_id: image.id, added: [image.source_url], removed: [old_source]} + %{image_id: image.id, added: [added_sources], removed: [removed_sources]} ) PhilomenaWeb.Endpoint.broadcast!( @@ -49,7 +48,7 @@ defmodule PhilomenaWeb.Image.SourceController do |> where(image_id: ^image.id) |> Repo.aggregate(:count, :id) - if old_source != image.source_url do + if Enum.any?(added_sources) or Enum.any?(removed_sources) do UserStatistics.inc_stat(conn.assigns.current_user, :metadata_updates) end diff --git a/lib/philomena_web/controllers/image_controller.ex b/lib/philomena_web/controllers/image_controller.ex index 3cb2711e..5200deba 100644 --- a/lib/philomena_web/controllers/image_controller.ex +++ b/lib/philomena_web/controllers/image_controller.ex @@ -185,7 +185,7 @@ defmodule PhilomenaWeb.ImageController do _ in fragment("SELECT COUNT(*) FROM source_changes s WHERE s.image_id = ?", i.id), on: true ) - |> preload([:deleter, :locked_tags, user: [awards: :badge], tags: :aliases]) + |> preload([:deleter, :locked_tags, :sources, user: [awards: :badge], tags: :aliases]) |> select([i, t, s], {i, t.count, s.count}) |> Repo.one() |> case do diff --git a/lib/philomena_web/templates/image/_source.html.slime b/lib/philomena_web/templates/image/_source.html.slime index 87b09772..379bd99c 100644 --- a/lib/philomena_web/templates/image/_source.html.slime +++ b/lib/philomena_web/templates/image/_source.html.slime @@ -25,15 +25,15 @@ ' Source: p - = if @image.source_url not in [nil, ""] do - a.js-source-link href=@image.source_url - strong - = @image.source_url + = if Enum.any?(@image.sources) do + = for source <- @image.sources do + a.js-source-link href=source.source + strong= source.source - else em> not provided yet - = if @source_change_count > 1 do + = if @source_change_count > 0 do a.button.button--link.button--separate-left href=Routes.image_source_change_path(@conn, :index, @image) title="Source history" i.fa.fa-history> | History ( diff --git a/lib/philomena_web/templates/source_change/index.html.slime b/lib/philomena_web/templates/source_change/index.html.slime index 77663b20..05971a76 100644 --- a/lib/philomena_web/templates/source_change/index.html.slime +++ b/lib/philomena_web/templates/source_change/index.html.slime @@ -7,10 +7,10 @@ thead tr th colspan=2 Image - th New Source + th Source + th Action th Timestamp th User - th Initial? tbody = for source_change <- @source_changes do @@ -21,8 +21,13 @@ = render PhilomenaWeb.ImageView, "_image_container.html", image: source_change.image, size: :thumb_tiny, conn: @conn td - = source_change.new_value - + = source_change.source_url + + = if source_change.added do + td.success Added + - else + td.danger Removed + td = pretty_time(source_change.created_at) @@ -41,9 +46,5 @@ br ' Ask them before reverting their changes. - td - = if source_change.initial do - ' ✓ - .block__header = @pagination diff --git a/priv/repo/migrations/20211009011024_rewrite_source_changes.exs b/priv/repo/migrations/20211009011024_rewrite_source_changes.exs new file mode 100644 index 00000000..9fa9e3c2 --- /dev/null +++ b/priv/repo/migrations/20211009011024_rewrite_source_changes.exs @@ -0,0 +1,141 @@ +defmodule Philomena.Repo.Migrations.RewriteSourceChanges do + use Ecto.Migration + + def up do + rename table(:source_changes), to: table(:old_source_changes) + + execute( + "alter index index_source_changes_on_image_id rename to index_old_source_changes_on_image_id" + ) + + execute( + "alter index index_source_changes_on_user_id rename to index_old_source_changes_on_user_id" + ) + + execute("alter index index_source_changes_on_ip rename to index_old_source_changes_on_ip") + + execute( + "alter table old_source_changes rename constraint source_changes_pkey to old_source_changes_pkey" + ) + + execute("alter sequence source_changes_id_seq rename to old_source_changes_id_seq") + + create table(:source_changes) do + add :image_id, references(:images, on_update: :update_all, on_delete: :delete_all), + null: false + + add :user_id, references(:users, on_update: :update_all, on_delete: :delete_all) + add :ip, :inet, null: false + timestamps(inserted_at: :created_at) + + add :added, :boolean, null: false + add :fingerprint, :string + add :user_agent, :string, default: "" + add :referrer, :string, default: "" + add :value, :string, null: false + end + + alter table(:image_sources) do + remove :id + modify :source, :string + end + + create index(:image_sources, [:image_id, :source], + name: "index_image_source_on_image_id_and_source", + unique: true + ) + + drop constraint(:image_sources, :length_must_be_valid, + check: "length(source) >= 8 and length(source) <= 1024" + ) + + create constraint(:image_sources, :image_sources_source_check, + check: "substr(source, 1, 7) = 'http://' or substr(source, 1, 8) = 'https://'" + ) + + execute(""" + insert into image_sources (image_id, source) + select id as image_id, substr(source_url, 1, 255) as source from images + where source_url is not null and (substr(source_url, 1, 7) = 'http://' or substr(source_url, 1, 8) = 'https://'); + """) + + # First insert the "added" changes... + execute(""" + with ranked_added_source_changes as ( + select + image_id, user_id, ip, created_at, updated_at, fingerprint, user_agent, + substr(referrer, 1, 255) as referrer, + substr(new_value, 1, 255) as value, true as added, + rank() over (partition by image_id order by created_at asc) + from old_source_changes + where new_value is not null + ) + insert into source_changes + (image_id, user_id, ip, created_at, updated_at, fingerprint, user_agent, referrer, value, added) + select image_id, user_id, ip, created_at, updated_at, fingerprint, user_agent, referrer, value, added + from ranked_added_source_changes + where "rank" > 1; + """) + + # ...then the "removed" changes + execute(""" + with ranked_removed_source_changes as ( + select + image_id, user_id, ip, created_at, updated_at, fingerprint, user_agent, + substr(referrer, 1, 255) as referrer, + substr(new_value, 1, 255) as value, false as added, + rank() over (partition by image_id order by created_at desc) + from old_source_changes + where new_value is not null + ) + insert into source_changes + (image_id, user_id, ip, created_at, updated_at, fingerprint, user_agent, referrer, value, added) + select image_id, user_id, ip, created_at, updated_at, fingerprint, user_agent, referrer, value, added + from ranked_removed_source_changes + where "rank" > 1; + """) + + create index(:source_changes, [:image_id], name: "index_source_changes_on_image_id") + create index(:source_changes, [:user_id], name: "index_source_changes_on_user_id") + create index(:source_changes, [:ip], name: "index_source_changes_on_ip") + end + + def down do + drop table(:source_changes) + rename table(:old_source_changes), to: table(:source_changes) + + execute( + "alter index index_old_source_changes_on_image_id rename to index_source_changes_on_image_id" + ) + + execute( + "alter index index_old_source_changes_on_user_id rename to index_source_changes_on_user_id" + ) + + execute("alter index index_old_source_changes_on_ip rename to index_source_changes_on_ip") + + execute( + "alter table source_changes rename constraint old_source_changes_pkey to source_changes_pkey" + ) + + execute("alter sequence old_source_changes_id_seq rename to source_changes_id_seq") + + execute("truncate image_sources") + + drop constraint(:image_sources, :image_sources_source_check, + check: "substr(source, 1, 7) = 'http://' or substr(source, 1, 8) = 'https://'" + ) + + create constraint(:image_sources, :length_must_be_valid, + check: "length(source) >= 8 and length(source) <= 1024" + ) + + drop index(:image_sources, [:image_id, :source], + name: "index_image_source_on_image_id_and_source" + ) + + alter table(:image_sources) do + modify :source, :text + end + end +end diff --git a/priv/repo/structure.sql b/priv/repo/structure.sql index 1a412576..d41d1d6f 100644 --- a/priv/repo/structure.sql +++ b/priv/repo/structure.sql @@ -842,32 +842,12 @@ ALTER SEQUENCE public.image_intensities_id_seq OWNED BY public.image_intensities -- CREATE TABLE public.image_sources ( - id bigint NOT NULL, image_id bigint NOT NULL, - source text NOT NULL, - CONSTRAINT length_must_be_valid CHECK (((length(source) >= 8) AND (length(source) <= 1024))) + source character varying(255) NOT NULL, + CONSTRAINT image_sources_source_check CHECK (((substr((source)::text, 1, 7) = 'http://'::text) OR (substr((source)::text, 1, 8) = 'https://'::text))) ); --- --- Name: image_sources_id_seq; Type: SEQUENCE; Schema: public; Owner: - --- - -CREATE SEQUENCE public.image_sources_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - --- --- Name: image_sources_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - --- - -ALTER SEQUENCE public.image_sources_id_seq OWNED BY public.image_sources.id; - - -- -- Name: image_subscriptions; Type: TABLE; Schema: public; Owner: - -- @@ -1136,6 +1116,44 @@ CREATE SEQUENCE public.notifications_id_seq ALTER SEQUENCE public.notifications_id_seq OWNED BY public.notifications.id; +-- +-- Name: old_source_changes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.old_source_changes ( + id integer NOT NULL, + ip inet NOT NULL, + fingerprint character varying, + user_agent character varying DEFAULT ''::character varying, + referrer character varying DEFAULT ''::character varying, + new_value character varying, + initial boolean DEFAULT false NOT NULL, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL, + user_id integer, + image_id integer NOT NULL +); + + +-- +-- Name: old_source_changes_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.old_source_changes_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: old_source_changes_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.old_source_changes_id_seq OWNED BY public.old_source_changes.id; + + -- -- Name: poll_options; Type: TABLE; Schema: public; Owner: - -- @@ -1414,17 +1432,17 @@ ALTER SEQUENCE public.site_notices_id_seq OWNED BY public.site_notices.id; -- CREATE TABLE public.source_changes ( - id integer NOT NULL, + id bigint NOT NULL, + image_id bigint NOT NULL, + user_id bigint, ip inet NOT NULL, - fingerprint character varying, - user_agent character varying DEFAULT ''::character varying, - referrer character varying DEFAULT ''::character varying, - new_value character varying, - initial boolean DEFAULT false NOT NULL, - created_at timestamp without time zone NOT NULL, - updated_at timestamp without time zone NOT NULL, - user_id integer, - image_id integer NOT NULL + created_at timestamp(0) without time zone NOT NULL, + updated_at timestamp(0) without time zone NOT NULL, + added boolean NOT NULL, + fingerprint character varying(255), + user_agent character varying(255) DEFAULT ''::character varying, + referrer character varying(255) DEFAULT ''::character varying, + value character varying(255) NOT NULL ); @@ -2265,13 +2283,6 @@ ALTER TABLE ONLY public.image_features ALTER COLUMN id SET DEFAULT nextval('publ ALTER TABLE ONLY public.image_intensities ALTER COLUMN id SET DEFAULT nextval('public.image_intensities_id_seq'::regclass); --- --- Name: image_sources id; Type: DEFAULT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public.image_sources ALTER COLUMN id SET DEFAULT nextval('public.image_sources_id_seq'::regclass); - - -- -- Name: images id; Type: DEFAULT; Schema: public; Owner: - -- @@ -2307,6 +2318,13 @@ ALTER TABLE ONLY public.moderation_logs ALTER COLUMN id SET DEFAULT nextval('pub ALTER TABLE ONLY public.notifications ALTER COLUMN id SET DEFAULT nextval('public.notifications_id_seq'::regclass); +-- +-- Name: old_source_changes id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.old_source_changes ALTER COLUMN id SET DEFAULT nextval('public.old_source_changes_id_seq'::regclass); + + -- -- Name: poll_options id; Type: DEFAULT; Schema: public; Owner: - -- @@ -2627,14 +2645,6 @@ ALTER TABLE ONLY public.image_intensities ADD CONSTRAINT image_intensities_pkey PRIMARY KEY (id); --- --- Name: image_sources image_sources_pkey; Type: CONSTRAINT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public.image_sources - ADD CONSTRAINT image_sources_pkey PRIMARY KEY (id); - - -- -- Name: images images_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -2675,6 +2685,14 @@ ALTER TABLE ONLY public.notifications ADD CONSTRAINT notifications_pkey PRIMARY KEY (id); +-- +-- Name: old_source_changes old_source_changes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.old_source_changes + ADD CONSTRAINT old_source_changes_pkey PRIMARY KEY (id); + + -- -- Name: poll_options poll_options_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -3407,6 +3425,13 @@ CREATE INDEX index_image_hides_on_user_id ON public.image_hides USING btree (use CREATE UNIQUE INDEX index_image_intensities_on_image_id ON public.image_intensities USING btree (image_id); +-- +-- Name: index_image_source_on_image_id_and_source; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX index_image_source_on_image_id_and_source ON public.image_sources USING btree (image_id, source); + + -- -- Name: index_image_subscriptions_on_image_id_and_user_id; Type: INDEX; Schema: public; Owner: - -- @@ -3540,6 +3565,27 @@ CREATE INDEX index_mod_notes_on_notable_type_and_notable_id ON public.mod_notes CREATE UNIQUE INDEX index_notifications_on_actor_id_and_actor_type ON public.notifications USING btree (actor_id, actor_type); +-- +-- Name: index_old_source_changes_on_image_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_old_source_changes_on_image_id ON public.old_source_changes USING btree (image_id); + + +-- +-- Name: index_old_source_changes_on_ip; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_old_source_changes_on_ip ON public.old_source_changes USING btree (ip); + + +-- +-- Name: index_old_source_changes_on_user_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_old_source_changes_on_user_id ON public.old_source_changes USING btree (user_id); + + -- -- Name: index_poll_options_on_poll_id_and_label; Type: INDEX; Schema: public; Owner: - -- @@ -4177,10 +4223,10 @@ ALTER TABLE ONLY public.image_taggings -- --- Name: source_changes fk_rails_10271ec4d0; Type: FK CONSTRAINT; Schema: public; Owner: - +-- Name: old_source_changes fk_rails_10271ec4d0; Type: FK CONSTRAINT; Schema: public; Owner: - -- -ALTER TABLE ONLY public.source_changes +ALTER TABLE ONLY public.old_source_changes ADD CONSTRAINT fk_rails_10271ec4d0 FOREIGN KEY (image_id) REFERENCES public.images(id) ON UPDATE CASCADE ON DELETE CASCADE; @@ -4577,10 +4623,10 @@ ALTER TABLE ONLY public.polls -- --- Name: source_changes fk_rails_8d8cb9cb3b; Type: FK CONSTRAINT; Schema: public; Owner: - +-- Name: old_source_changes fk_rails_8d8cb9cb3b; Type: FK CONSTRAINT; Schema: public; Owner: - -- -ALTER TABLE ONLY public.source_changes +ALTER TABLE ONLY public.old_source_changes ADD CONSTRAINT fk_rails_8d8cb9cb3b FOREIGN KEY (user_id) REFERENCES public.users(id) ON UPDATE CASCADE ON DELETE SET NULL; @@ -4950,6 +4996,19 @@ ALTER TABLE ONLY public.image_tag_locks ALTER TABLE ONLY public.moderation_logs ADD CONSTRAINT moderation_logs_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; +-- Name: source_changes source_changes_image_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.source_changes + ADD CONSTRAINT source_changes_image_id_fkey FOREIGN KEY (image_id) REFERENCES public.images(id) ON UPDATE CASCADE ON DELETE CASCADE; + + +-- +-- Name: source_changes source_changes_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.source_changes + ADD CONSTRAINT source_changes_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON UPDATE CASCADE ON DELETE CASCADE; -- @@ -4988,6 +5047,7 @@ INSERT INTO public."schema_migrations" (version) VALUES (20210912171343); INSERT INTO public."schema_migrations" (version) VALUES (20210917190346); INSERT INTO public."schema_migrations" (version) VALUES (20210921025336); INSERT INTO public."schema_migrations" (version) VALUES (20210929181319); +INSERT INTO public."schema_migrations" (version) VALUES (20211009011024); INSERT INTO public."schema_migrations" (version) VALUES (20211107130226); INSERT INTO public."schema_migrations" (version) VALUES (20211219194836); INSERT INTO public."schema_migrations" (version) VALUES (20220321173359);