From c55c1395ab4ff16e444bc28729c345eea4b47e1e Mon Sep 17 00:00:00 2001 From: Harald Friessnegger Date: Fri, 23 May 2025 10:32:01 +0200 Subject: [PATCH 1/6] update to schema v30 see https://blog.metabrainz.org/2025/05/20/musicbrainz-database-schema-change-release-2025-05-19-with-upgrade-instructions/ for change notes --- CHANGELOG.rst | 5 +++++ README.rst | 10 +++++++++- mbslave/__init__.py | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 088e3ea..a036bf9 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,8 @@ +Version 30.0.0 +============== + +- Schema change 30. + Version 29.1.0 ============== diff --git a/README.rst b/README.rst index 9e4dce3..211fd8a 100644 --- a/README.rst +++ b/README.rst @@ -8,7 +8,7 @@ MusicBrainz Database Mirror :target: https://badge.fury.io/py/mbslave This repository now contains a collection of scripts for managing a -replica of the MusicBrainz database. +replica of the MusicBrainz database. The main motivation for these scripts is to be able to customize your database. If you don't need such customizations, it might be @@ -82,6 +82,14 @@ When the MusicBrainz database schema changes, the replication will stop working. This is usually announced on the `MusicBrainz blog `__. When it happens, you need to upgrade the database. +Release 2025-05-19 (30) +~~~~~~~~~~~~~~~~~~~~~~~ + +Run the upgrade scripts:: + + mbslave psql -f updates/schema-change/30.all.sql + echo 'UPDATE replication_control SET current_schema_sequence = 30;' | mbslave psql + Release 2024-05-13 (29) ~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/mbslave/__init__.py b/mbslave/__init__.py index 7c2f436..8cb7180 100644 --- a/mbslave/__init__.py +++ b/mbslave/__init__.py @@ -1,4 +1,4 @@ # Copyright (C) 2013 Lukas Lalinsky # Distributed under the MIT license, see the LICENSE file for details. -__version__ = "29.1.0" +__version__ = "30.0.0" From 084990c3d5060bf6146d25e598561b8a2200261b Mon Sep 17 00:00:00 2001 From: Harald Friessnegger Date: Fri, 23 May 2025 13:19:11 +0200 Subject: [PATCH 2/6] update sql scripts (via `./scripts/update_sql.sh`) note that this reverts the adaptions done by https://github.com/acoustid/mbslave/pull/8 as musicbrainz does not ship the scripts with function definitions containing a `SET schema` local adaptions would always lead to merge problems. if users have problems that functions do not use the correct schema for queries and can't find functions or tables, consider to set the schema on session, user or database level: ``` -- session SET search_path to musicbrainz,public; -- user level ALTER USER musicbrainz SET search_path TO musicbrainz, public; -- check user level settings via psql \drds mbdata; -- check user level via query SELECT r.rolname AS role_name, rs.setconfig AS configured_settings FROM pg_roles r LEFT JOIN pg_db_role_setting rs ON r.oid = rs.setrole WHERE r.rolname = 'musicbrainz'; -- db level SELECT setting FROM pg_settings WHERE name = 'search_path'; ALTER DATABASE database_name SET search_path TO musicbrainz, public; ``` --- mbslave/sql/CreateFKConstraints.sql | 5 + mbslave/sql/CreateFunctions.sql | 367 ++++++++-------- mbslave/sql/CreateIndexes.sql | 11 +- mbslave/sql/CreateMirrorOnlyFunctions.sql | 36 +- mbslave/sql/CreateMirrorOnlyTriggers.sql | 21 + mbslave/sql/CreatePrimaryKeys.sql | 1 + mbslave/sql/CreateReplicationTriggers.sql | 4 + mbslave/sql/CreateReplicationTriggers2.sql | 4 + mbslave/sql/CreateTables.sql | 24 +- mbslave/sql/CreateTriggers.sql | 21 + mbslave/sql/DisableLastUpdatedTriggers.sql | 0 mbslave/sql/DropFKConstraints.sql | 1 + mbslave/sql/DropFunctions.sql | 2 + mbslave/sql/DropIndexes.sql | 2 + mbslave/sql/DropMirrorOnlyTriggers.sql | 4 + mbslave/sql/DropPrimaryKeys.sql | 1 + mbslave/sql/DropReplicationTriggers.sql | 1 + mbslave/sql/DropReplicationTriggers2.sql | 1 + mbslave/sql/DropTables.sql | 1 + mbslave/sql/DropTriggers.sql | 4 + mbslave/sql/EnableLastUpdatedTriggers.sql | 0 mbslave/sql/InsertTestData.sql | 14 +- mbslave/sql/TruncateTables.sql | 1 + mbslave/sql/caa/CreateMQTriggers.sql | 148 ------- mbslave/sql/caa/CreateViews.sql | 1 - mbslave/sql/caa/DropMQTriggers.sql | 19 - mbslave/sql/eaa/CreateMQTriggers.sql | 161 ------- mbslave/sql/eaa/CreateViews.sql | 1 - mbslave/sql/eaa/DropMQTriggers.sql | 21 - mbslave/sql/updates/20240221-mbs-13492.sql | 23 + mbslave/sql/updates/20240726-mbs-9373.sql | 9 + .../sql/updates/20241017-mbs-9253-13464.sql | 136 ++++++ ...0241017-mbs-9253-master_and_standalone.sql | 37 ++ .../updates/20241017-mbs-9253-mirror_only.sql | 31 ++ mbslave/sql/updates/20241125-mbs-13832.sql | 48 +++ .../sql/updates/20250320-mbs-13768-fks.sql | 12 + mbslave/sql/updates/20250320-mbs-13768.sql | 31 ++ mbslave/sql/updates/20250408-mbs-13322.sql | 15 + .../sql/updates/20250408-mbs-13964-all.sql | 34 ++ ...250425-mbs-13464-master_and_standalone.sql | 19 + mbslave/sql/updates/20250425-mbs-13464.sql | 81 ++++ mbslave/sql/updates/20250425-mbs-13966.sql | 50 +++ mbslave/sql/updates/schema-change/30.all.sql | 401 ++++++++++++++++++ .../30.master_and_standalone.sql | 74 ++++ .../updates/schema-change/30.mirror_only.sql | 37 ++ 45 files changed, 1355 insertions(+), 560 deletions(-) mode change 100755 => 100644 mbslave/sql/DisableLastUpdatedTriggers.sql mode change 100755 => 100644 mbslave/sql/EnableLastUpdatedTriggers.sql delete mode 100644 mbslave/sql/caa/CreateMQTriggers.sql delete mode 100644 mbslave/sql/caa/DropMQTriggers.sql delete mode 100644 mbslave/sql/eaa/CreateMQTriggers.sql delete mode 100644 mbslave/sql/eaa/DropMQTriggers.sql create mode 100644 mbslave/sql/updates/20240221-mbs-13492.sql create mode 100644 mbslave/sql/updates/20240726-mbs-9373.sql create mode 100644 mbslave/sql/updates/20241017-mbs-9253-13464.sql create mode 100644 mbslave/sql/updates/20241017-mbs-9253-master_and_standalone.sql create mode 100644 mbslave/sql/updates/20241017-mbs-9253-mirror_only.sql create mode 100644 mbslave/sql/updates/20241125-mbs-13832.sql create mode 100644 mbslave/sql/updates/20250320-mbs-13768-fks.sql create mode 100644 mbslave/sql/updates/20250320-mbs-13768.sql create mode 100644 mbslave/sql/updates/20250408-mbs-13322.sql create mode 100644 mbslave/sql/updates/20250408-mbs-13964-all.sql create mode 100644 mbslave/sql/updates/20250425-mbs-13464-master_and_standalone.sql create mode 100644 mbslave/sql/updates/20250425-mbs-13464.sql create mode 100644 mbslave/sql/updates/20250425-mbs-13966.sql create mode 100644 mbslave/sql/updates/schema-change/30.all.sql create mode 100644 mbslave/sql/updates/schema-change/30.master_and_standalone.sql create mode 100644 mbslave/sql/updates/schema-change/30.mirror_only.sql diff --git a/mbslave/sql/CreateFKConstraints.sql b/mbslave/sql/CreateFKConstraints.sql index eef896f..4a601f6 100644 --- a/mbslave/sql/CreateFKConstraints.sql +++ b/mbslave/sql/CreateFKConstraints.sql @@ -3006,6 +3006,11 @@ ALTER TABLE medium_format FOREIGN KEY (parent) REFERENCES medium_format(id); +ALTER TABLE medium_gid_redirect + ADD CONSTRAINT medium_gid_redirect_fk_new_id + FOREIGN KEY (new_id) + REFERENCES medium(id); + ALTER TABLE medium_index ADD CONSTRAINT medium_index_fk_medium FOREIGN KEY (medium) diff --git a/mbslave/sql/CreateFunctions.sql b/mbslave/sql/CreateFunctions.sql index 285d04e..047ccfa 100644 --- a/mbslave/sql/CreateFunctions.sql +++ b/mbslave/sql/CreateFunctions.sql @@ -13,7 +13,7 @@ CREATE OR REPLACE FUNCTION _median(INTEGER[]) RETURNS INTEGER AS $$ LIMIT 1 -- Subtracting (n + 1) % 2 creates a left bias OFFSET greatest(0, floor((select count(*) FROM q) / 2.0) - ((select count(*) + 1 FROM q) % 2)); -$$ LANGUAGE sql SET search_path = musicbrainz, public IMMUTABLE; +$$ LANGUAGE sql IMMUTABLE; CREATE AGGREGATE median(INTEGER) ( SFUNC=array_append, @@ -50,7 +50,7 @@ BEGIN value = value || lpad(to_hex(ceil(random() * 255)::int), 2, '0'); RETURN value::uuid; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION from_hex(t text) RETURNS integer AS $$ @@ -61,7 +61,7 @@ BEGIN RETURN r.hex; END LOOP; END -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public IMMUTABLE STRICT; +$$ LANGUAGE plpgsql IMMUTABLE STRICT; -- NameSpace_URL = '6ba7b8119dad11d180b400c04fd430c8' CREATE OR REPLACE FUNCTION generate_uuid_v3(namespace varchar, name varchar) RETURNS uuid @@ -84,7 +84,7 @@ BEGIN value = value || substr(bytes, 1+2*10, 12); return value::uuid; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public IMMUTABLE STRICT; +$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT; CREATE OR REPLACE FUNCTION inc_ref_count(tbl varchar, row_id integer, val integer) RETURNS void AS $$ @@ -94,7 +94,7 @@ BEGIN EXECUTE 'UPDATE ' || tbl || ' SET ref_count = ref_count + ' || val || ' WHERE id = ' || row_id; RETURN; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION dec_ref_count(tbl varchar, row_id integer, val integer) RETURNS void AS $$ DECLARE @@ -109,7 +109,7 @@ BEGIN EXECUTE 'UPDATE ' || tbl || ' SET ref_count = ref_count - ' || val || ' WHERE id = ' || row_id; RETURN; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION integer_date(year SMALLINT, month SMALLINT, day SMALLINT) RETURNS INTEGER AS $$ @@ -126,7 +126,7 @@ RETURNS INTEGER AS $$ )::INTEGER END ) -$$ LANGUAGE sql SET search_path = musicbrainz, public IMMUTABLE PARALLEL SAFE; +$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE; ----------------------------------------------------------------------- -- area triggers @@ -149,7 +149,7 @@ BEGIN RETURN NEW; END IF; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; ----------------------------------------------------------------------- -- artist triggers @@ -161,7 +161,7 @@ BEGIN INSERT INTO artist_meta (id) VALUES (NEW.id); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; -- Ensure attribute type allows free text if free text is added CREATE OR REPLACE FUNCTION ensure_artist_attribute_type_allows_text() @@ -180,7 +180,7 @@ BEGIN RETURN NEW; END IF; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; ----------------------------------------------------------------------- -- artist_credit triggers @@ -197,7 +197,7 @@ BEGIN -- artist_release_group. RAISE EXCEPTION 'Cannot update artist_credit_name'; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; ----------------------------------------------------------------------- -- editor triggers @@ -211,7 +211,7 @@ BEGIN END IF; RETURN NEW; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; ----------------------------------------------------------------------- -- event triggers @@ -234,7 +234,7 @@ BEGIN RETURN NEW; END IF; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; ----------------------------------------------------------------------- -- event triggers @@ -246,7 +246,7 @@ BEGIN INSERT INTO event_meta (id) VALUES (NEW.id); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; ----------------------------------------------------------------------- -- instrument triggers @@ -261,7 +261,7 @@ BEGIN ) INSERT INTO link_creditable_attribute_type (attribute_type) SELECT id FROM inserted_rows; RETURN NEW; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION a_upd_instrument() RETURNS trigger AS $$ BEGIN @@ -272,7 +272,7 @@ BEGIN RETURN NEW; END IF; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION a_del_instrument() RETURNS trigger AS $$ BEGIN @@ -283,7 +283,7 @@ BEGIN RETURN NEW; END IF; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE plpgsql; -- Ensure attribute type allows free text if free text is added CREATE OR REPLACE FUNCTION ensure_instrument_attribute_type_allows_text() @@ -302,7 +302,7 @@ BEGIN RETURN NEW; END IF; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; ----------------------------------------------------------------------- -- label triggers @@ -313,7 +313,7 @@ BEGIN INSERT INTO label_meta (id) VALUES (NEW.id); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; -- Ensure attribute type allows free text if free text is added CREATE OR REPLACE FUNCTION ensure_label_attribute_type_allows_text() @@ -332,7 +332,7 @@ BEGIN RETURN NEW; END IF; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; ----------------------------------------------------------------------- -- medium triggers @@ -355,7 +355,7 @@ BEGIN RETURN NEW; END IF; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; ----------------------------------------------------------------------- -- place triggers @@ -367,7 +367,7 @@ BEGIN INSERT INTO place_meta (id) VALUES (NEW.id); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; -- Ensure attribute type allows free text if free text is added CREATE OR REPLACE FUNCTION ensure_place_attribute_type_allows_text() @@ -386,7 +386,7 @@ BEGIN RETURN NEW; END IF; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; ----------------------------------------------------------------------- -- recording triggers @@ -395,7 +395,7 @@ $$ LANGUAGE plpgsql SET search_path = musicbrainz, public; CREATE OR REPLACE FUNCTION median_track_length(recording_id integer) RETURNS integer AS $$ SELECT median(track.length) FROM track WHERE recording = $1; -$$ LANGUAGE sql SET search_path = musicbrainz, public; +$$ LANGUAGE SQL; CREATE OR REPLACE FUNCTION b_upd_recording() RETURNS TRIGGER AS $$ BEGIN @@ -409,7 +409,7 @@ BEGIN NEW.last_updated = now(); RETURN NEW; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_ins_recording() RETURNS trigger AS $$ BEGIN @@ -417,7 +417,7 @@ BEGIN INSERT INTO recording_meta (id) VALUES (NEW.id); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_upd_recording() RETURNS trigger AS $$ BEGIN @@ -427,14 +427,14 @@ BEGIN END IF; RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_del_recording() RETURNS trigger AS $$ BEGIN PERFORM dec_ref_count('artist_credit', OLD.artist_credit, 1); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; -- Ensure attribute type allows free text if free text is added CREATE OR REPLACE FUNCTION ensure_recording_attribute_type_allows_text() @@ -453,7 +453,7 @@ BEGIN RETURN NEW; END IF; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; ----------------------------------------------------------------------- -- release triggers @@ -471,7 +471,7 @@ BEGIN INSERT INTO artist_release_group_pending_update VALUES (NEW.release_group); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_upd_release() RETURNS trigger AS $$ BEGIN @@ -516,7 +516,7 @@ BEGIN END IF; RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_del_release() RETURNS trigger AS $$ BEGIN @@ -528,7 +528,40 @@ BEGIN INSERT INTO artist_release_group_pending_update VALUES (OLD.release_group); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION a_upd_release_group_primary_type_mirror() +RETURNS trigger AS $$ +BEGIN + -- DO NOT modify any replicated tables in this function; it's used + -- by a trigger on mirrors. + IF (NEW.child_order IS DISTINCT FROM OLD.child_order) + THEN + INSERT INTO artist_release_group_pending_update ( + SELECT id FROM release_group + WHERE release_group.type = OLD.id + ); + END IF; + RETURN NULL; +END; +$$ LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION a_upd_release_group_secondary_type_mirror() +RETURNS trigger AS $$ +BEGIN + -- DO NOT modify any replicated tables in this function; it's used + -- by a trigger on mirrors. + IF (NEW.child_order IS DISTINCT FROM OLD.child_order) + THEN + INSERT INTO artist_release_group_pending_update ( + SELECT release_group + FROM release_group_secondary_type_join + WHERE secondary_type = OLD.id + ); + END IF; + RETURN NULL; +END; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_ins_release_group_secondary_type_join() RETURNS trigger AS $$ @@ -536,7 +569,7 @@ BEGIN INSERT INTO artist_release_group_pending_update VALUES (NEW.release_group); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_del_release_group_secondary_type_join() RETURNS trigger AS $$ @@ -544,7 +577,7 @@ BEGIN INSERT INTO artist_release_group_pending_update VALUES (OLD.release_group); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_ins_release_label() RETURNS trigger AS $$ @@ -552,7 +585,7 @@ BEGIN INSERT INTO artist_release_pending_update VALUES (NEW.release); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_upd_release_label() RETURNS trigger AS $$ @@ -562,7 +595,7 @@ BEGIN END IF; RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_del_release_label() RETURNS trigger AS $$ @@ -570,7 +603,7 @@ BEGIN INSERT INTO artist_release_pending_update VALUES (OLD.release); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; -- Ensure attribute type allows free text if free text is added CREATE OR REPLACE FUNCTION ensure_release_attribute_type_allows_text() @@ -589,7 +622,7 @@ BEGIN RETURN NEW; END IF; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; ----------------------------------------------------------------------- -- release_group triggers @@ -602,7 +635,7 @@ BEGIN INSERT INTO artist_release_group_pending_update VALUES (NEW.id); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_upd_release_group() RETURNS trigger AS $$ BEGIN @@ -619,7 +652,7 @@ BEGIN END IF; RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_del_release_group() RETURNS trigger AS $$ BEGIN @@ -627,7 +660,7 @@ BEGIN INSERT INTO artist_release_group_pending_update VALUES (OLD.id); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION b_upd_release_group_secondary_type_join() RETURNS trigger AS $$ BEGIN @@ -639,7 +672,7 @@ BEGIN -- artist_release_group up-to-date. RAISE EXCEPTION 'Cannot update release_group_secondary_type_join'; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; -- Ensure attribute type allows free text if free text is added CREATE OR REPLACE FUNCTION ensure_release_group_attribute_type_allows_text() @@ -656,7 +689,7 @@ RETURNS trigger AS $$ ELSE RETURN NEW; END IF; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; ----------------------------------------------------------------------- -- series triggers @@ -679,7 +712,7 @@ BEGIN RETURN NEW; END IF; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; ----------------------------------------------------------------------- -- track triggers @@ -703,7 +736,7 @@ BEGIN ); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_upd_track() RETURNS trigger AS $$ BEGIN @@ -749,7 +782,7 @@ BEGIN PERFORM materialise_recording_length(NEW.recording); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_del_track() RETURNS trigger AS $$ BEGIN @@ -769,7 +802,7 @@ BEGIN ); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; ----------------------------------------------------------------------- -- work triggers @@ -780,7 +813,7 @@ BEGIN INSERT INTO work_meta (id) VALUES (NEW.id); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; -- Ensure attribute type allows free text if free text is added CREATE OR REPLACE FUNCTION ensure_work_attribute_type_allows_text() @@ -798,7 +831,7 @@ BEGIN RETURN NEW; END IF; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; ----------------------------------------------------------------------- -- alternative tracklist triggers @@ -811,7 +844,7 @@ BEGIN END IF; RETURN; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION dec_nullable_artist_credit(row_id integer) RETURNS void AS $$ BEGIN @@ -820,14 +853,14 @@ BEGIN END IF; RETURN; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_ins_alternative_release_or_track() RETURNS trigger AS $$ BEGIN PERFORM inc_nullable_artist_credit(NEW.artist_credit); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_upd_alternative_release_or_track() RETURNS trigger AS $$ BEGIN @@ -837,21 +870,21 @@ BEGIN END IF; RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_del_alternative_release_or_track() RETURNS trigger AS $$ BEGIN PERFORM dec_nullable_artist_credit(OLD.artist_credit); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_ins_alternative_medium_track() RETURNS trigger AS $$ BEGIN PERFORM inc_ref_count('alternative_track', NEW.alternative_track, 1); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_upd_alternative_medium_track() RETURNS trigger AS $$ BEGIN @@ -861,14 +894,14 @@ BEGIN END IF; RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_del_alternative_medium_track() RETURNS trigger AS $$ BEGIN PERFORM dec_ref_count('alternative_track', OLD.alternative_track, 1); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; ----------------------------------------------------------------------- -- lastupdate triggers @@ -879,7 +912,7 @@ BEGIN NEW.last_updated = NOW(); RETURN NEW; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_upd_edit() RETURNS trigger AS $$ BEGIN @@ -889,14 +922,14 @@ BEGIN END IF; RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION b_ins_edit_materialize_status() RETURNS trigger AS $$ BEGIN NEW.status = (SELECT status FROM edit WHERE id = NEW.edit); RETURN NEW; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; ------------------------ -- Collection deletion and hiding triggers @@ -916,7 +949,7 @@ RETURNS trigger AS $$ RETURN NEW; END IF; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION del_collection_sub_on_delete() RETURNS trigger AS $$ @@ -928,7 +961,7 @@ RETURNS trigger AS $$ RETURN OLD; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION del_collection_sub_on_private() RETURNS trigger AS $$ @@ -946,7 +979,7 @@ RETURNS trigger AS $$ RETURN NEW; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION restore_collection_sub_on_public() RETURNS trigger AS $$ @@ -961,7 +994,7 @@ RETURNS trigger AS $$ RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; ------------------------ -- CD Lookup @@ -1005,7 +1038,7 @@ BEGIN RETURN str::cube; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public IMMUTABLE; +$$ LANGUAGE 'plpgsql' IMMUTABLE; CREATE OR REPLACE FUNCTION create_bounding_cube(durations INTEGER[], fuzzy INTEGER) RETURNS cube AS $$ DECLARE @@ -1059,7 +1092,7 @@ BEGIN RETURN str::cube; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public IMMUTABLE; +$$ LANGUAGE 'plpgsql' IMMUTABLE; ------------------------------------------------------------------- -- Maintain musicbrainz.release_first_release_date @@ -1087,7 +1120,7 @@ BEGIN ) ORDER BY release, year NULLS LAST, month NULLS LAST, day NULLS LAST'; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public STRICT; +$$ LANGUAGE 'plpgsql' STRICT; CREATE OR REPLACE FUNCTION set_release_first_release_date(release_id INTEGER) RETURNS VOID AS $$ @@ -1104,7 +1137,7 @@ BEGIN INSERT INTO artist_release_pending_update VALUES (release_id); END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public STRICT; +$$ LANGUAGE 'plpgsql' STRICT; ------------------------------------------------------------------- -- Maintain release_group_meta.first_release_date @@ -1117,9 +1150,10 @@ BEGIN first_release_date_day = first.day FROM ( SELECT rd.year, rd.month, rd.day - FROM release + FROM release_group + LEFT JOIN release ON release.release_group = release_group.id LEFT JOIN release_first_release_date rd ON (rd.release = release.id) - WHERE release.release_group = release_group_id + WHERE release_group.id = release_group_id ORDER BY rd.year NULLS LAST, rd.month NULLS LAST, @@ -1129,7 +1163,7 @@ BEGIN WHERE id = release_group_id; INSERT INTO artist_release_group_pending_update VALUES (release_group_id); END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; ------------------------------------------------------------------- -- Maintain musicbrainz.recording_first_release_date @@ -1149,7 +1183,7 @@ BEGIN rd.month NULLS LAST, rd.day NULLS LAST'; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public STRICT; +$$ LANGUAGE 'plpgsql' STRICT; CREATE OR REPLACE FUNCTION set_recordings_first_release_dates(recording_ids INTEGER[]) RETURNS VOID AS $$ @@ -1164,7 +1198,19 @@ BEGIN format('track.recording = any(%L)', recording_ids) ); END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public STRICT; +$$ LANGUAGE 'plpgsql' STRICT; + +CREATE OR REPLACE FUNCTION set_mediums_recordings_first_release_dates(medium_ids INTEGER[]) +RETURNS VOID AS $$ +BEGIN + PERFORM set_recordings_first_release_dates(( + SELECT array_agg(recording) + FROM track + WHERE track.medium = any(medium_ids) + )); + RETURN; +END; +$$ LANGUAGE 'plpgsql' STRICT; CREATE OR REPLACE FUNCTION set_releases_recordings_first_release_dates(release_ids INTEGER[]) RETURNS VOID AS $$ @@ -1177,7 +1223,19 @@ BEGIN )); RETURN; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public STRICT; +$$ LANGUAGE 'plpgsql' STRICT; + +CREATE OR REPLACE FUNCTION a_upd_medium_mirror() +RETURNS trigger AS $$ +BEGIN + -- DO NOT modify any replicated tables in this function; it's used + -- by a trigger on mirrors. + IF NEW.release IS DISTINCT FROM OLD.release THEN + PERFORM set_mediums_recordings_first_release_dates(ARRAY[OLD.id]); + END IF; + RETURN NULL; +END; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_ins_release_event() RETURNS TRIGGER AS $$ @@ -1196,7 +1254,7 @@ BEGIN RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_upd_release_event() RETURNS TRIGGER AS $$ @@ -1227,7 +1285,7 @@ BEGIN RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_del_release_event() RETURNS TRIGGER AS $$ @@ -1246,13 +1304,13 @@ BEGIN RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION deny_special_purpose_deletion() RETURNS trigger AS $$ BEGIN RAISE EXCEPTION 'Attempted to delete a special purpose row'; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; ------------------------------------------------------------------- -- Ratings @@ -1284,7 +1342,7 @@ BEGIN entity_type::TEXT || '_rating_raw' ) USING entity_id; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION update_aggregate_rating_for_raw_insert() RETURNS trigger AS $$ @@ -1297,7 +1355,7 @@ BEGIN PERFORM update_aggregate_rating(entity_type, new_entity_id); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION update_aggregate_rating_for_raw_update() RETURNS trigger AS $$ @@ -1319,7 +1377,7 @@ BEGIN END IF; RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION update_aggregate_rating_for_raw_delete() RETURNS trigger AS $$ @@ -1332,7 +1390,7 @@ BEGIN PERFORM update_aggregate_rating(entity_type, old_entity_id); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION delete_ratings(enttype TEXT, ids INTEGER[]) RETURNS TABLE(editor INT, rating SMALLINT) AS $$ @@ -1346,7 +1404,7 @@ BEGIN USING ids; RETURN; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; ------------------------------------------------------------------- -- Prevent link attributes being used on links that don't support them @@ -1366,7 +1424,7 @@ BEGIN END IF; RETURN NEW; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; -------------------------------------------------------------------------------- -- Remove unused link rows when a relationship is changed @@ -1390,53 +1448,17 @@ BEGIN RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION delete_unused_url(ids INTEGER[]) RETURNS VOID AS $$ -DECLARE - clear_up INTEGER[]; -BEGIN - SELECT ARRAY( - SELECT id FROM url url_row WHERE id = any(ids) - EXCEPT - SELECT url FROM edit_url JOIN edit ON (edit.id = edit_url.edit) WHERE edit.status = 1 - EXCEPT - SELECT entity1 FROM l_area_url - EXCEPT - SELECT entity1 FROM l_artist_url - EXCEPT - SELECT entity1 FROM l_event_url - EXCEPT - SELECT entity1 FROM l_genre_url - EXCEPT - SELECT entity1 FROM l_instrument_url - EXCEPT - SELECT entity1 FROM l_label_url - EXCEPT - SELECT entity1 FROM l_mood_url - EXCEPT - SELECT entity1 FROM l_place_url - EXCEPT - SELECT entity1 FROM l_recording_url - EXCEPT - SELECT entity1 FROM l_release_url - EXCEPT - SELECT entity1 FROM l_release_group_url - EXCEPT - SELECT entity1 FROM l_series_url - EXCEPT - SELECT entity1 FROM l_url_url - EXCEPT - SELECT entity0 FROM l_url_url - EXCEPT - SELECT entity0 FROM l_url_work - ) INTO clear_up; - - DELETE FROM url_gid_redirect WHERE new_id = any(clear_up); - DELETE FROM url WHERE id = any(clear_up); -END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +BEGIN + DELETE FROM url_gid_redirect WHERE new_id = any(ids); + DELETE FROM url WHERE id = any(ids); +EXCEPTION + WHEN foreign_key_violation THEN RETURN; +END; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION remove_unused_url() RETURNS TRIGGER AS $$ @@ -1455,7 +1477,7 @@ BEGIN RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION simplify_search_hints() RETURNS trigger AS $$ @@ -1474,7 +1496,7 @@ BEGIN END IF; RETURN NEW; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION end_date_implies_ended() RETURNS trigger AS $$ @@ -1487,7 +1509,7 @@ BEGIN END IF; RETURN NEW; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION end_area_implies_ended() RETURNS trigger AS $$ @@ -1498,7 +1520,7 @@ BEGIN END IF; RETURN NEW; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION delete_orphaned_recordings() RETURNS TRIGGER @@ -1559,15 +1581,15 @@ AS $$ RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION padded_by_whitespace(TEXT) RETURNS boolean AS $$ SELECT btrim($1) <> $1; -$$ LANGUAGE sql SET search_path = musicbrainz, public IMMUTABLE; +$$ LANGUAGE SQL IMMUTABLE; CREATE OR REPLACE FUNCTION controlled_for_whitespace(TEXT) RETURNS boolean AS $$ SELECT NOT padded_by_whitespace($1); -$$ LANGUAGE sql IMMUTABLE SET search_path = musicbrainz, public; +$$ LANGUAGE SQL IMMUTABLE SET search_path = musicbrainz, public; CREATE OR REPLACE FUNCTION update_aggregate_tag_count(entity_type taggable_entity_type, entity_id INTEGER, tag_id INTEGER, count_change SMALLINT) RETURNS VOID AS $$ @@ -1583,7 +1605,7 @@ BEGIN entity_type::TEXT ) USING entity_id, tag_id, count_change; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION delete_unused_aggregate_tag(entity_type taggable_entity_type, entity_id INTEGER, tag_id INTEGER) RETURNS VOID AS $$ @@ -1606,7 +1628,7 @@ BEGIN entity_type::TEXT || '_tag_raw' ) USING entity_id, tag_id; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION update_tag_counts_for_raw_insert() RETURNS trigger AS $$ @@ -1620,7 +1642,7 @@ BEGIN UPDATE tag SET ref_count = ref_count + 1 WHERE id = NEW.tag; RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION update_tag_counts_for_raw_update() RETURNS trigger AS $$ @@ -1647,7 +1669,7 @@ BEGIN END IF; RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION update_tag_counts_for_raw_delete() RETURNS trigger AS $$ @@ -1662,7 +1684,7 @@ BEGIN UPDATE tag SET ref_count = ref_count - 1 WHERE id = OLD.tag; RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION delete_unused_tag(tag_id INT) RETURNS void AS $$ @@ -1671,7 +1693,7 @@ RETURNS void AS $$ EXCEPTION WHEN foreign_key_violation THEN RETURN; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION trg_delete_unused_tag() RETURNS trigger AS $$ @@ -1679,7 +1701,7 @@ RETURNS trigger AS $$ PERFORM delete_unused_tag(NEW.id); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION trg_delete_unused_tag_ref() RETURNS trigger AS $$ @@ -1687,7 +1709,7 @@ RETURNS trigger AS $$ PERFORM delete_unused_tag(OLD.tag); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION inserting_edits_requires_confirmed_email_address() RETURNS trigger AS $$ @@ -1702,7 +1724,7 @@ BEGIN RETURN NEW; END IF; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION deny_deprecated_links() RETURNS trigger AS $$ @@ -1713,7 +1735,7 @@ BEGIN END IF; RETURN NEW; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION check_has_dates() RETURNS trigger AS $$ @@ -1731,7 +1753,7 @@ BEGIN END IF; RETURN NEW; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION materialise_recording_length(recording_id INT) RETURNS void as $$ @@ -1741,14 +1763,14 @@ BEGIN WHERE recording.id = recording_id AND recording.length IS DISTINCT FROM track.median; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION track_count_matches_cdtoc(medium, int) RETURNS boolean AS $$ SELECT $1.track_count = $2 + COALESCE( (SELECT count(*) FROM track WHERE medium = $1.id AND (position = 0 OR is_data_track = true) ), 0); -$$ LANGUAGE sql SET search_path = musicbrainz, public IMMUTABLE; +$$ LANGUAGE SQL IMMUTABLE; COMMIT; @@ -1766,7 +1788,7 @@ BEGIN ); RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; ----------------------------------------------------------------------- -- Text search helpers @@ -1774,7 +1796,7 @@ $$ LANGUAGE plpgsql SET search_path = musicbrainz, public; CREATE OR REPLACE FUNCTION mb_lower(input text) RETURNS text AS $$ SELECT lower(input COLLATE musicbrainz.musicbrainz); -$$ LANGUAGE sql SET search_path = musicbrainz, public IMMUTABLE PARALLEL SAFE STRICT; +$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE STRICT; CREATE OR REPLACE FUNCTION mb_simple_tsvector(input text) RETURNS tsvector AS $$ -- The builtin 'simple' dictionary, which the mb_simple text search @@ -1782,7 +1804,7 @@ CREATE OR REPLACE FUNCTION mb_simple_tsvector(input text) RETURNS tsvector AS $$ -- for us, but internally it hardcodes DEFAULT_COLLATION_OID; therefore -- we first lowercase the input string ourselves using mb_lower. SELECT to_tsvector('musicbrainz.mb_simple', musicbrainz.mb_lower(input)); -$$ LANGUAGE sql SET search_path = musicbrainz, public IMMUTABLE PARALLEL SAFE STRICT; +$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE STRICT; ----------------------------------------------------------------------- -- Edit data helpers @@ -1817,7 +1839,7 @@ BEGIN END CASE; RETURN ''; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public IMMUTABLE PARALLEL SAFE STRICT; +$$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE STRICT; ----------------------------------------------------------------------- -- Maintain musicbrainz.artist_release @@ -1844,7 +1866,7 @@ BEGIN (CASE r.barcode WHEN '' THEN '0' ELSE r.barcode END), '[^0-9]+', '', 'g' ), 18)::BIGINT AS barcode, - left(r.name, 1)::CHAR(1) AS sort_character, + r.name, r.id FROM ( SELECT FALSE AS is_track_artist, racn.artist, r.id AS release @@ -1868,7 +1890,7 @@ BEGIN $SQL$ USING release_id; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION apply_artist_release_pending_updates() RETURNS trigger AS $$ @@ -1908,7 +1930,7 @@ BEGIN RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; ----------------------------------------------------------------------- -- Maintain musicbrainz.artist_release_group @@ -1928,7 +1950,12 @@ BEGIN a_rg.artist, -- Withdrawn releases were once official by definition bool_and(r.status IS NOT NULL AND r.status != 1 AND r.status != 5), + rgpt.child_order::SMALLINT, rg.type::SMALLINT, + array_agg( + DISTINCT rgst.child_order ORDER BY rgst.child_order) + FILTER (WHERE rgst.child_order IS NOT NULL + )::SMALLINT[], array_agg( DISTINCT st.secondary_type ORDER BY st.secondary_type) FILTER (WHERE st.secondary_type IS NOT NULL @@ -1938,7 +1965,7 @@ BEGIN rgm.first_release_date_month, rgm.first_release_date_day ), - left(rg.name, 1)::CHAR(1), + rg.name, rg.id FROM ( SELECT FALSE AS is_track_artist, rgacn.artist, rg.id AS release_group @@ -1954,15 +1981,17 @@ BEGIN JOIN release_group rg ON rg.id = a_rg.release_group LEFT JOIN release r ON r.release_group = rg.id JOIN release_group_meta rgm ON rgm.id = rg.id + LEFT JOIN release_group_primary_type rgpt ON rgpt.id = rg.type LEFT JOIN release_group_secondary_type_join st ON st.release_group = rg.id + LEFT JOIN release_group_secondary_type rgst ON rgst.id = st.secondary_type $SQL$ || (CASE WHEN release_group_id IS NULL THEN '' ELSE 'WHERE rg.id = $1' END) || $SQL$ - GROUP BY a_rg.is_track_artist, a_rg.artist, rgm.id, rg.id + GROUP BY a_rg.is_track_artist, a_rg.artist, rgm.id, rg.id, rgpt.child_order ORDER BY a_rg.artist, rg.id, a_rg.is_track_artist $SQL$ USING release_group_id; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION apply_artist_release_group_pending_updates() RETURNS trigger AS $$ @@ -2002,7 +2031,7 @@ BEGIN RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; ----------------------------------------------------------------------- -- Relationship triggers @@ -2019,7 +2048,7 @@ BEGIN END IF; RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION a_upd_l_area_area_mirror() RETURNS trigger AS $$ DECLARE @@ -2043,7 +2072,7 @@ BEGIN END IF; RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION a_del_l_area_area_mirror() RETURNS trigger AS $$ DECLARE @@ -2056,7 +2085,7 @@ BEGIN END IF; RETURN NULL; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION b_upd_link() RETURNS trigger AS $$ BEGIN @@ -2069,28 +2098,28 @@ BEGIN -- area_containment. RAISE EXCEPTION 'link rows are immutable'; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION b_upd_link_attribute() RETURNS trigger AS $$ BEGIN -- Refer to b_upd_link. RAISE EXCEPTION 'link_attribute rows are immutable'; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION b_upd_link_attribute_credit() RETURNS trigger AS $$ BEGIN -- Refer to b_upd_link. RAISE EXCEPTION 'link_attribute_credit rows are immutable'; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION b_upd_link_attribute_text_value() RETURNS trigger AS $$ BEGIN -- Refer to b_upd_link. RAISE EXCEPTION 'link_attribute_text_value rows are immutable'; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; ----------------------------------------------------------------------- -- Maintain musicbrainz.area_containment @@ -2131,7 +2160,7 @@ BEGIN $SQL$ USING part_of_area_link_type_id, descendant_area_ids; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE plpgsql; -- Returns a set of area_containment rows that cover the entire descendant -- hierarchy for parent_area_ids. If NULL is passed, the entire @@ -2168,7 +2197,7 @@ BEGIN $SQL$ USING part_of_area_link_type_id, parent_area_ids; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION update_area_containment_mirror( parent_ids INTEGER[], -- entity0 of area-area "part of" @@ -2216,6 +2245,6 @@ BEGIN ) area_hierarchy ORDER BY descendant, parent, depth; END; -$$ LANGUAGE plpgsql SET search_path = musicbrainz, public; +$$ LANGUAGE plpgsql; -- vi: set ts=4 sw=4 et : diff --git a/mbslave/sql/CreateIndexes.sql b/mbslave/sql/CreateIndexes.sql index b5c0b69..8f48262 100644 --- a/mbslave/sql/CreateIndexes.sql +++ b/mbslave/sql/CreateIndexes.sql @@ -69,16 +69,16 @@ CREATE INDEX artist_rating_raw_idx_editor ON artist_rating_raw (editor); CREATE INDEX artist_tag_raw_idx_tag ON artist_tag_raw (tag); CREATE INDEX artist_tag_raw_idx_editor ON artist_tag_raw (editor); -CREATE INDEX artist_release_nonva_idx_sort ON artist_release_nonva (artist, first_release_date NULLS LAST, catalog_numbers NULLS LAST, country_code NULLS LAST, barcode NULLS LAST, sort_character, release); -CREATE INDEX artist_release_va_idx_sort ON artist_release_va (artist, first_release_date NULLS LAST, catalog_numbers NULLS LAST, country_code NULLS LAST, barcode NULLS LAST, sort_character, release); +CREATE INDEX artist_release_nonva_idx_sort ON artist_release_nonva (artist, first_release_date NULLS LAST, catalog_numbers NULLS LAST, country_code NULLS LAST, barcode NULLS LAST, name, release); +CREATE INDEX artist_release_va_idx_sort ON artist_release_va (artist, first_release_date NULLS LAST, catalog_numbers NULLS LAST, country_code NULLS LAST, barcode NULLS LAST, name, release); CREATE UNIQUE INDEX artist_release_nonva_idx_uniq ON artist_release_nonva (release, artist); CREATE UNIQUE INDEX artist_release_va_idx_uniq ON artist_release_va (release, artist); CREATE INDEX artist_release_pending_update_idx_release ON artist_release_pending_update USING HASH (release); -CREATE INDEX artist_release_group_nonva_idx_sort ON artist_release_group_nonva (artist, unofficial, primary_type NULLS FIRST, secondary_types NULLS FIRST, first_release_date NULLS LAST, sort_character, release_group); -CREATE INDEX artist_release_group_va_idx_sort ON artist_release_group_va (artist, unofficial, primary_type NULLS FIRST, secondary_types NULLS FIRST, first_release_date NULLS LAST, sort_character, release_group); +CREATE INDEX artist_release_group_nonva_idx_sort ON artist_release_group_nonva (artist, unofficial, primary_type_child_order NULLS FIRST, primary_type NULLS FIRST, secondary_type_child_orders NULLS FIRST, secondary_types NULLS FIRST, first_release_date NULLS LAST, name, release_group); +CREATE INDEX artist_release_group_va_idx_sort ON artist_release_group_va (artist, unofficial, primary_type_child_order NULLS FIRST, primary_type NULLS FIRST, secondary_type_child_orders NULLS FIRST, secondary_types NULLS FIRST, first_release_date NULLS LAST, name, release_group); CREATE UNIQUE INDEX artist_release_group_nonva_idx_uniq ON artist_release_group_nonva (release_group, artist); CREATE UNIQUE INDEX artist_release_group_va_idx_uniq ON artist_release_group_va (release_group, artist); @@ -507,6 +507,8 @@ CREATE UNIQUE INDEX editor_collection_type_idx_gid ON editor_collection_type (gi CREATE UNIQUE INDEX cdtoc_idx_discid ON cdtoc (discid); CREATE INDEX cdtoc_idx_freedb_id ON cdtoc (freedb_id); +CREATE UNIQUE INDEX medium_idx_gid ON medium (gid); + CREATE INDEX medium_attribute_idx_medium ON medium_attribute (medium); CREATE UNIQUE INDEX medium_attribute_type_idx_gid ON medium_attribute_type (gid); @@ -731,6 +733,7 @@ CREATE INDEX editor_collection_gid_redirect_idx_new_id ON editor_collection_gid_ CREATE INDEX event_gid_redirect_idx_new_id ON event_gid_redirect (new_id); CREATE INDEX instrument_gid_redirect_idx_new_id ON instrument_gid_redirect (new_id); CREATE INDEX label_gid_redirect_idx_new_id ON label_gid_redirect (new_id); +CREATE INDEX medium_gid_redirect_idx_new_id ON medium_gid_redirect (new_id); CREATE INDEX place_gid_redirect_idx_new_id ON place_gid_redirect (new_id); CREATE INDEX recording_gid_redirect_idx_new_id ON recording_gid_redirect (new_id); CREATE INDEX release_gid_redirect_idx_new_id ON release_gid_redirect (new_id); diff --git a/mbslave/sql/CreateMirrorOnlyFunctions.sql b/mbslave/sql/CreateMirrorOnlyFunctions.sql index 915d6e8..4836bf6 100644 --- a/mbslave/sql/CreateMirrorOnlyFunctions.sql +++ b/mbslave/sql/CreateMirrorOnlyFunctions.sql @@ -9,7 +9,7 @@ BEGIN INSERT INTO artist_release_group_pending_update VALUES (NEW.release_group); RETURN NULL; END; -$$ LANGUAGE 'plpgsql' SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_upd_release_mirror() RETURNS trigger AS $$ @@ -31,7 +31,7 @@ BEGIN END IF; RETURN NULL; END; -$$ LANGUAGE 'plpgsql' SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_del_release_mirror() RETURNS trigger AS $$ @@ -40,7 +40,7 @@ BEGIN INSERT INTO artist_release_group_pending_update VALUES (OLD.release_group); RETURN NULL; END; -$$ LANGUAGE 'plpgsql' SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_ins_release_event_mirror() RETURNS trigger AS $$ @@ -52,7 +52,7 @@ BEGIN END IF; RETURN NULL; END; -$$ LANGUAGE 'plpgsql' SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_upd_release_event_mirror() RETURNS trigger AS $$ @@ -67,7 +67,7 @@ BEGIN END IF; RETURN NULL; END; -$$ LANGUAGE 'plpgsql' SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_del_release_event_mirror() RETURNS trigger AS $$ @@ -79,7 +79,7 @@ BEGIN END IF; RETURN NULL; END; -$$ LANGUAGE 'plpgsql' SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_ins_release_group_mirror() RETURNS trigger AS $$ @@ -87,7 +87,7 @@ BEGIN INSERT INTO artist_release_group_pending_update VALUES (NEW.id); RETURN NULL; END; -$$ LANGUAGE 'plpgsql' SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_upd_release_group_mirror() RETURNS trigger AS $$ @@ -101,7 +101,7 @@ BEGIN END IF; RETURN NULL; END; -$$ LANGUAGE 'plpgsql' SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_del_release_group_mirror() RETURNS trigger AS $$ @@ -109,7 +109,7 @@ BEGIN INSERT INTO artist_release_group_pending_update VALUES (OLD.id); RETURN NULL; END; -$$ LANGUAGE 'plpgsql' SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_upd_release_group_meta_mirror() RETURNS trigger AS $$ @@ -123,7 +123,7 @@ BEGIN END IF; RETURN NULL; END; -$$ LANGUAGE 'plpgsql' SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_ins_release_group_secondary_type_join_mirror() RETURNS trigger AS $$ @@ -131,7 +131,7 @@ BEGIN INSERT INTO artist_release_group_pending_update VALUES (NEW.release_group); RETURN NULL; END; -$$ LANGUAGE 'plpgsql' SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_del_release_group_secondary_type_join_mirror() RETURNS trigger AS $$ @@ -139,7 +139,7 @@ BEGIN INSERT INTO artist_release_group_pending_update VALUES (OLD.release_group); RETURN NULL; END; -$$ LANGUAGE 'plpgsql' SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_ins_release_label_mirror() RETURNS trigger AS $$ @@ -147,7 +147,7 @@ BEGIN INSERT INTO artist_release_pending_update VALUES (NEW.release); RETURN NULL; END; -$$ LANGUAGE 'plpgsql' SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_upd_release_label_mirror() RETURNS trigger AS $$ @@ -157,7 +157,7 @@ BEGIN END IF; RETURN NULL; END; -$$ LANGUAGE 'plpgsql' SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_del_release_label_mirror() RETURNS trigger AS $$ @@ -165,7 +165,7 @@ BEGIN INSERT INTO artist_release_pending_update VALUES (OLD.release); RETURN NULL; END; -$$ LANGUAGE 'plpgsql' SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_ins_track_mirror() RETURNS trigger AS $$ @@ -182,7 +182,7 @@ BEGIN ); RETURN NULL; END; -$$ LANGUAGE 'plpgsql' SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_upd_track_mirror() RETURNS trigger AS $$ @@ -203,7 +203,7 @@ BEGIN END IF; RETURN NULL; END; -$$ LANGUAGE 'plpgsql' SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION a_del_track_mirror() RETURNS trigger AS $$ @@ -220,6 +220,6 @@ BEGIN ); RETURN NULL; END; -$$ LANGUAGE 'plpgsql' SET search_path = musicbrainz, public; +$$ LANGUAGE 'plpgsql'; COMMIT; diff --git a/mbslave/sql/CreateMirrorOnlyTriggers.sql b/mbslave/sql/CreateMirrorOnlyTriggers.sql index 999542a..9875d9d 100644 --- a/mbslave/sql/CreateMirrorOnlyTriggers.sql +++ b/mbslave/sql/CreateMirrorOnlyTriggers.sql @@ -24,6 +24,9 @@ CREATE TRIGGER a_upd_l_area_area_mirror AFTER UPDATE ON l_area_area CREATE TRIGGER a_del_l_area_area_mirror AFTER DELETE ON l_area_area FOR EACH ROW EXECUTE PROCEDURE a_del_l_area_area_mirror(); +CREATE TRIGGER a_upd_medium AFTER UPDATE ON medium + FOR EACH ROW EXECUTE PROCEDURE a_upd_medium_mirror(); + CREATE TRIGGER a_ins_release_mirror AFTER INSERT ON release FOR EACH ROW EXECUTE PROCEDURE a_ins_release_mirror(); @@ -63,6 +66,12 @@ CREATE TRIGGER a_del_release_group_mirror AFTER DELETE ON release_group CREATE TRIGGER a_upd_release_group_meta_mirror AFTER UPDATE ON release_group_meta FOR EACH ROW EXECUTE PROCEDURE a_upd_release_group_meta_mirror(); +CREATE TRIGGER a_upd_release_group_primary_type_mirror AFTER UPDATE ON release_group_primary_type + FOR EACH ROW EXECUTE PROCEDURE a_upd_release_group_primary_type_mirror(); + +CREATE TRIGGER a_upd_release_group_secondary_type_mirror AFTER UPDATE ON release_group_secondary_type + FOR EACH ROW EXECUTE PROCEDURE a_upd_release_group_secondary_type_mirror(); + CREATE TRIGGER a_ins_release_group_secondary_type_join_mirror AFTER INSERT ON release_group_secondary_type_join FOR EACH ROW EXECUTE PROCEDURE a_ins_release_group_secondary_type_join_mirror(); @@ -111,6 +120,18 @@ CREATE CONSTRAINT TRIGGER apply_artist_release_group_pending_updates_mirror AFTER UPDATE ON release_group_meta DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE apply_artist_release_group_pending_updates(); +CREATE CONSTRAINT TRIGGER apply_artist_release_group_pending_updates_mirror + AFTER UPDATE ON release_group_primary_type DEFERRABLE INITIALLY DEFERRED + FOR EACH ROW + WHEN (OLD.child_order IS DISTINCT FROM NEW.child_order) + EXECUTE PROCEDURE apply_artist_release_group_pending_updates(); + +CREATE CONSTRAINT TRIGGER apply_artist_release_group_pending_updates_mirror + AFTER UPDATE ON release_group_secondary_type DEFERRABLE INITIALLY DEFERRED + FOR EACH ROW + WHEN (OLD.child_order IS DISTINCT FROM NEW.child_order) + EXECUTE PROCEDURE apply_artist_release_group_pending_updates(); + CREATE CONSTRAINT TRIGGER apply_artist_release_group_pending_updates_mirror AFTER INSERT OR DELETE ON release_group_secondary_type_join DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE apply_artist_release_group_pending_updates(); diff --git a/mbslave/sql/CreatePrimaryKeys.sql b/mbslave/sql/CreatePrimaryKeys.sql index 8724bab..7b1fb0f 100644 --- a/mbslave/sql/CreatePrimaryKeys.sql +++ b/mbslave/sql/CreatePrimaryKeys.sql @@ -264,6 +264,7 @@ ALTER TABLE medium_attribute_type_allowed_value ADD CONSTRAINT medium_attribute_ ALTER TABLE medium_attribute_type_allowed_value_allowed_format ADD CONSTRAINT medium_attribute_type_allowed_value_allowed_format_pkey PRIMARY KEY (medium_format, medium_attribute_type_allowed_value); ALTER TABLE medium_cdtoc ADD CONSTRAINT medium_cdtoc_pkey PRIMARY KEY (id); ALTER TABLE medium_format ADD CONSTRAINT medium_format_pkey PRIMARY KEY (id); +ALTER TABLE medium_gid_redirect ADD CONSTRAINT medium_gid_redirect_pkey PRIMARY KEY (gid); ALTER TABLE medium_index ADD CONSTRAINT medium_index_pkey PRIMARY KEY (medium); ALTER TABLE mood ADD CONSTRAINT mood_pkey PRIMARY KEY (id); ALTER TABLE mood_alias ADD CONSTRAINT mood_alias_pkey PRIMARY KEY (id); diff --git a/mbslave/sql/CreateReplicationTriggers.sql b/mbslave/sql/CreateReplicationTriggers.sql index f35f62d..d989a20 100644 --- a/mbslave/sql/CreateReplicationTriggers.sql +++ b/mbslave/sql/CreateReplicationTriggers.sql @@ -815,6 +815,10 @@ CREATE TRIGGER "reptg_medium_format" AFTER INSERT OR DELETE OR UPDATE ON "medium_format" FOR EACH ROW EXECUTE PROCEDURE "recordchange" (); +CREATE TRIGGER "reptg_medium_gid_redirect" +AFTER INSERT OR DELETE OR UPDATE ON "medium_gid_redirect" +FOR EACH ROW EXECUTE PROCEDURE "recordchange" ('verbose'); + CREATE TRIGGER "reptg_medium_index" AFTER INSERT OR DELETE OR UPDATE ON "medium_index" FOR EACH ROW EXECUTE PROCEDURE "recordchange" (); diff --git a/mbslave/sql/CreateReplicationTriggers2.sql b/mbslave/sql/CreateReplicationTriggers2.sql index 1727b0a..28ff024 100644 --- a/mbslave/sql/CreateReplicationTriggers2.sql +++ b/mbslave/sql/CreateReplicationTriggers2.sql @@ -815,6 +815,10 @@ CREATE TRIGGER reptg2_medium_format AFTER INSERT OR DELETE OR UPDATE ON medium_format FOR EACH ROW EXECUTE PROCEDURE dbmirror2.recordchange(); +CREATE TRIGGER reptg2_medium_gid_redirect +AFTER INSERT OR DELETE OR UPDATE ON medium_gid_redirect +FOR EACH ROW EXECUTE PROCEDURE dbmirror2.recordchange(); + CREATE TRIGGER reptg2_medium_index AFTER INSERT OR DELETE OR UPDATE ON medium_index FOR EACH ROW EXECUTE PROCEDURE dbmirror2.recordchange(); diff --git a/mbslave/sql/CreateTables.sql b/mbslave/sql/CreateTables.sql index 7f3fabe..73cca4b 100644 --- a/mbslave/sql/CreateTables.sql +++ b/mbslave/sql/CreateTables.sql @@ -429,15 +429,7 @@ CREATE TABLE artist_release ( catalog_numbers TEXT[], country_code CHAR(2), barcode BIGINT, - -- Prior to adding these materialized tables, we'd order releases - -- by name only if all other attributes where equal. It's not too - -- common that an artist will have tons of releases with no dates, - -- catalog numbers, countries, or barcodes (though it can be seen - -- on some big composers). As a compromise between dropping the - -- name sorting and having to store the entire name here (which, - -- as a reminder, is duplicated for every artist on the release), - -- we only store the first character of the name for sorting. - sort_character CHAR(1) COLLATE musicbrainz NOT NULL, + name VARCHAR COLLATE musicbrainz NOT NULL, release INTEGER NOT NULL -- references release.id, CASCADE ) PARTITION BY LIST (is_track_artist); @@ -466,11 +458,12 @@ CREATE TABLE artist_release_group ( is_track_artist BOOLEAN NOT NULL, artist INTEGER NOT NULL, -- references artist.id, CASCADE unofficial BOOLEAN NOT NULL, + primary_type_child_order SMALLINT, primary_type SMALLINT, + secondary_type_child_orders SMALLINT[], secondary_types SMALLINT[], first_release_date INTEGER, - -- See comment for `artist_release.sort_character`. - sort_character CHAR(1) COLLATE musicbrainz NOT NULL, + name VARCHAR COLLATE musicbrainz NOT NULL, release_group INTEGER NOT NULL -- references release_group.id, CASCADE ) PARTITION BY LIST (is_track_artist); @@ -2907,7 +2900,8 @@ CREATE TABLE medium ( -- replicate (verbose) name VARCHAR NOT NULL DEFAULT '', edits_pending INTEGER NOT NULL DEFAULT 0 CHECK (edits_pending >= 0), last_updated TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - track_count INTEGER NOT NULL DEFAULT 0 + track_count INTEGER NOT NULL DEFAULT 0, + gid UUID NOT NULL ); CREATE TABLE medium_attribute_type ( -- replicate (verbose) @@ -2973,6 +2967,12 @@ CREATE TABLE medium_format ( -- replicate gid uuid NOT NULL ); +CREATE TABLE medium_gid_redirect ( -- replicate (verbose) + gid UUID NOT NULL, -- PK + new_id INTEGER NOT NULL, -- references medium.id + created TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + CREATE TABLE mood ( -- replicate (verbose) id SERIAL, -- PK gid UUID NOT NULL, diff --git a/mbslave/sql/CreateTriggers.sql b/mbslave/sql/CreateTriggers.sql index 148c7f8..7f5ee5c 100644 --- a/mbslave/sql/CreateTriggers.sql +++ b/mbslave/sql/CreateTriggers.sql @@ -454,6 +454,9 @@ CREATE TRIGGER b_upd_link_type BEFORE UPDATE ON link_type CREATE TRIGGER b_upd_link_type_attribute_type BEFORE UPDATE ON link_type_attribute_type FOR EACH ROW EXECUTE PROCEDURE b_upd_last_updated_table(); +CREATE TRIGGER a_upd_medium AFTER UPDATE ON medium + FOR EACH ROW EXECUTE PROCEDURE a_upd_medium_mirror(); + CREATE TRIGGER b_upd_medium BEFORE UPDATE ON medium FOR EACH ROW EXECUTE PROCEDURE b_upd_last_updated_table(); @@ -580,6 +583,12 @@ CREATE TRIGGER a_del_release_group AFTER DELETE ON release_group CREATE TRIGGER b_upd_release_group BEFORE UPDATE ON release_group FOR EACH ROW EXECUTE PROCEDURE b_upd_last_updated_table(); +CREATE TRIGGER a_upd_release_group_primary_type AFTER UPDATE ON release_group_primary_type + FOR EACH ROW EXECUTE PROCEDURE a_upd_release_group_primary_type_mirror(); + +CREATE TRIGGER a_upd_release_group_secondary_type AFTER UPDATE ON release_group_secondary_type + FOR EACH ROW EXECUTE PROCEDURE a_upd_release_group_secondary_type_mirror(); + CREATE TRIGGER a_ins_release_group_secondary_type_join AFTER INSERT ON release_group_secondary_type_join FOR EACH ROW EXECUTE PROCEDURE a_ins_release_group_secondary_type_join(); @@ -1299,6 +1308,18 @@ CREATE CONSTRAINT TRIGGER apply_artist_release_group_pending_updates AFTER UPDATE ON release_group_meta DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE apply_artist_release_group_pending_updates(); +CREATE CONSTRAINT TRIGGER apply_artist_release_group_pending_updates + AFTER UPDATE ON release_group_primary_type DEFERRABLE INITIALLY DEFERRED + FOR EACH ROW + WHEN (OLD.child_order IS DISTINCT FROM NEW.child_order) + EXECUTE PROCEDURE apply_artist_release_group_pending_updates(); + +CREATE CONSTRAINT TRIGGER apply_artist_release_group_pending_updates + AFTER UPDATE ON release_group_secondary_type DEFERRABLE INITIALLY DEFERRED + FOR EACH ROW + WHEN (OLD.child_order IS DISTINCT FROM NEW.child_order) + EXECUTE PROCEDURE apply_artist_release_group_pending_updates(); + CREATE CONSTRAINT TRIGGER apply_artist_release_group_pending_updates AFTER INSERT OR DELETE ON release_group_secondary_type_join DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE apply_artist_release_group_pending_updates(); diff --git a/mbslave/sql/DisableLastUpdatedTriggers.sql b/mbslave/sql/DisableLastUpdatedTriggers.sql old mode 100755 new mode 100644 diff --git a/mbslave/sql/DropFKConstraints.sql b/mbslave/sql/DropFKConstraints.sql index 0d4eadf..5c03596 100644 --- a/mbslave/sql/DropFKConstraints.sql +++ b/mbslave/sql/DropFKConstraints.sql @@ -597,6 +597,7 @@ ALTER TABLE medium_attribute_type_allowed_value_allowed_format DROP CONSTRAINT I ALTER TABLE medium_cdtoc DROP CONSTRAINT IF EXISTS medium_cdtoc_fk_medium; ALTER TABLE medium_cdtoc DROP CONSTRAINT IF EXISTS medium_cdtoc_fk_cdtoc; ALTER TABLE medium_format DROP CONSTRAINT IF EXISTS medium_format_fk_parent; +ALTER TABLE medium_gid_redirect DROP CONSTRAINT IF EXISTS medium_gid_redirect_fk_new_id; ALTER TABLE medium_index DROP CONSTRAINT IF EXISTS medium_index_fk_medium; ALTER TABLE mood_alias DROP CONSTRAINT IF EXISTS mood_alias_fk_mood; ALTER TABLE mood_alias DROP CONSTRAINT IF EXISTS mood_alias_fk_type; diff --git a/mbslave/sql/DropFunctions.sql b/mbslave/sql/DropFunctions.sql index e37ad16..0bb2472 100644 --- a/mbslave/sql/DropFunctions.sql +++ b/mbslave/sql/DropFunctions.sql @@ -39,6 +39,8 @@ DROP FUNCTION a_upd_recording(); DROP FUNCTION a_upd_release(); DROP FUNCTION a_upd_release_event(); DROP FUNCTION a_upd_release_group(); +DROP FUNCTION a_upd_release_group_primary_type_mirror(); +DROP FUNCTION a_upd_release_group_secondary_type_mirror(); DROP FUNCTION a_upd_release_label(); DROP FUNCTION a_upd_track(); DROP FUNCTION apply_artist_release_group_pending_updates(); diff --git a/mbslave/sql/DropIndexes.sql b/mbslave/sql/DropIndexes.sql index 4e83c1e..a9b8e00 100644 --- a/mbslave/sql/DropIndexes.sql +++ b/mbslave/sql/DropIndexes.sql @@ -403,6 +403,8 @@ DROP INDEX medium_cdtoc_idx_cdtoc; DROP INDEX medium_cdtoc_idx_medium; DROP INDEX medium_cdtoc_idx_uniq; DROP INDEX medium_format_idx_gid; +DROP INDEX medium_gid_redirect_idx_new_id; +DROP INDEX medium_idx_gid; DROP INDEX medium_idx_track_count; DROP INDEX medium_index_idx; DROP INDEX mood_alias_idx_mood; diff --git a/mbslave/sql/DropMirrorOnlyTriggers.sql b/mbslave/sql/DropMirrorOnlyTriggers.sql index 136d008..e7a9b17 100644 --- a/mbslave/sql/DropMirrorOnlyTriggers.sql +++ b/mbslave/sql/DropMirrorOnlyTriggers.sql @@ -17,6 +17,8 @@ DROP TRIGGER IF EXISTS a_ins_release_group_mirror ON release_group; DROP TRIGGER IF EXISTS a_upd_release_group_mirror ON release_group; DROP TRIGGER IF EXISTS a_del_release_group_mirror ON release_group; DROP TRIGGER IF EXISTS a_upd_release_group_meta_mirror ON release_group_meta; +DROP TRIGGER IF EXISTS a_upd_release_group_primary_type_mirror ON release_group_primary_type; +DROP TRIGGER IF EXISTS a_upd_release_group_secondary_type_mirror ON release_group_secondary_type; DROP TRIGGER IF EXISTS a_ins_release_group_secondary_type_join_mirror ON release_group_secondary_type_join; DROP TRIGGER IF EXISTS a_del_release_group_secondary_type_join_mirror ON release_group_secondary_type_join; DROP TRIGGER IF EXISTS a_ins_release_label_mirror ON release_label; @@ -31,6 +33,8 @@ DROP TRIGGER IF EXISTS apply_artist_release_pending_updates_mirror ON release_co DROP TRIGGER IF EXISTS apply_artist_release_pending_updates_mirror ON release_first_release_date; DROP TRIGGER IF EXISTS apply_artist_release_group_pending_updates_mirror ON release_group; DROP TRIGGER IF EXISTS apply_artist_release_group_pending_updates_mirror ON release_group_meta; +DROP TRIGGER IF EXISTS apply_artist_release_group_pending_updates_mirror ON release_group_primary_type; +DROP TRIGGER IF EXISTS apply_artist_release_group_pending_updates_mirror ON release_group_secondary_type; DROP TRIGGER IF EXISTS apply_artist_release_group_pending_updates_mirror ON release_group_secondary_type_join; DROP TRIGGER IF EXISTS apply_artist_release_pending_updates_mirror ON release_label; DROP TRIGGER IF EXISTS apply_artist_release_group_pending_updates_mirror ON track; diff --git a/mbslave/sql/DropPrimaryKeys.sql b/mbslave/sql/DropPrimaryKeys.sql index b16b198..0edea17 100644 --- a/mbslave/sql/DropPrimaryKeys.sql +++ b/mbslave/sql/DropPrimaryKeys.sql @@ -264,6 +264,7 @@ ALTER TABLE medium_attribute_type_allowed_value DROP CONSTRAINT IF EXISTS medium ALTER TABLE medium_attribute_type_allowed_value_allowed_format DROP CONSTRAINT IF EXISTS medium_attribute_type_allowed_value_allowed_format_pkey; ALTER TABLE medium_cdtoc DROP CONSTRAINT IF EXISTS medium_cdtoc_pkey; ALTER TABLE medium_format DROP CONSTRAINT IF EXISTS medium_format_pkey; +ALTER TABLE medium_gid_redirect DROP CONSTRAINT IF EXISTS medium_gid_redirect_pkey; ALTER TABLE medium_index DROP CONSTRAINT IF EXISTS medium_index_pkey; ALTER TABLE mood DROP CONSTRAINT IF EXISTS mood_pkey; ALTER TABLE mood_alias DROP CONSTRAINT IF EXISTS mood_alias_pkey; diff --git a/mbslave/sql/DropReplicationTriggers.sql b/mbslave/sql/DropReplicationTriggers.sql index 1a48ba5..4c1fa3e 100644 --- a/mbslave/sql/DropReplicationTriggers.sql +++ b/mbslave/sql/DropReplicationTriggers.sql @@ -204,6 +204,7 @@ DROP TRIGGER IF EXISTS reptg_medium_attribute_type_allowed_value ON medium_attri DROP TRIGGER IF EXISTS reptg_medium_attribute_type_allowed_value_allowed_format ON medium_attribute_type_allowed_value_allowed_format; DROP TRIGGER IF EXISTS reptg_medium_cdtoc ON medium_cdtoc; DROP TRIGGER IF EXISTS reptg_medium_format ON medium_format; +DROP TRIGGER IF EXISTS reptg_medium_gid_redirect ON medium_gid_redirect; DROP TRIGGER IF EXISTS reptg_medium_index ON medium_index; DROP TRIGGER IF EXISTS reptg_mood ON mood; DROP TRIGGER IF EXISTS reptg_mood_alias ON mood_alias; diff --git a/mbslave/sql/DropReplicationTriggers2.sql b/mbslave/sql/DropReplicationTriggers2.sql index 7474e75..ed646bf 100644 --- a/mbslave/sql/DropReplicationTriggers2.sql +++ b/mbslave/sql/DropReplicationTriggers2.sql @@ -204,6 +204,7 @@ DROP TRIGGER IF EXISTS reptg2_medium_attribute_type_allowed_value ON medium_attr DROP TRIGGER IF EXISTS reptg2_medium_attribute_type_allowed_value_allowed_format ON medium_attribute_type_allowed_value_allowed_format; DROP TRIGGER IF EXISTS reptg2_medium_cdtoc ON medium_cdtoc; DROP TRIGGER IF EXISTS reptg2_medium_format ON medium_format; +DROP TRIGGER IF EXISTS reptg2_medium_gid_redirect ON medium_gid_redirect; DROP TRIGGER IF EXISTS reptg2_medium_index ON medium_index; DROP TRIGGER IF EXISTS reptg2_mood ON mood; DROP TRIGGER IF EXISTS reptg2_mood_alias ON mood_alias; diff --git a/mbslave/sql/DropTables.sql b/mbslave/sql/DropTables.sql index 8eb5357..6d1e53e 100644 --- a/mbslave/sql/DropTables.sql +++ b/mbslave/sql/DropTables.sql @@ -268,6 +268,7 @@ DROP TABLE medium_attribute_type_allowed_value; DROP TABLE medium_attribute_type_allowed_value_allowed_format; DROP TABLE medium_cdtoc; DROP TABLE medium_format; +DROP TABLE medium_gid_redirect; DROP TABLE medium_index; DROP TABLE mood; DROP TABLE mood_alias; diff --git a/mbslave/sql/DropTriggers.sql b/mbslave/sql/DropTriggers.sql index 17e54e9..063dab1 100644 --- a/mbslave/sql/DropTriggers.sql +++ b/mbslave/sql/DropTriggers.sql @@ -194,6 +194,8 @@ DROP TRIGGER IF EXISTS a_ins_release_group ON release_group; DROP TRIGGER IF EXISTS a_upd_release_group ON release_group; DROP TRIGGER IF EXISTS a_del_release_group ON release_group; DROP TRIGGER IF EXISTS b_upd_release_group ON release_group; +DROP TRIGGER IF EXISTS a_upd_release_group_primary_type ON release_group_primary_type; +DROP TRIGGER IF EXISTS a_upd_release_group_secondary_type ON release_group_secondary_type; DROP TRIGGER IF EXISTS a_ins_release_group_secondary_type_join ON release_group_secondary_type_join; DROP TRIGGER IF EXISTS a_del_release_group_secondary_type_join ON release_group_secondary_type_join; DROP TRIGGER IF EXISTS b_upd_release_group_secondary_type_join ON release_group_secondary_type_join; @@ -386,6 +388,8 @@ DROP TRIGGER IF EXISTS apply_artist_release_pending_updates ON release_country; DROP TRIGGER IF EXISTS apply_artist_release_pending_updates ON release_first_release_date; DROP TRIGGER IF EXISTS apply_artist_release_group_pending_updates ON release_group; DROP TRIGGER IF EXISTS apply_artist_release_group_pending_updates ON release_group_meta; +DROP TRIGGER IF EXISTS apply_artist_release_group_pending_updates ON release_group_primary_type; +DROP TRIGGER IF EXISTS apply_artist_release_group_pending_updates ON release_group_secondary_type; DROP TRIGGER IF EXISTS apply_artist_release_group_pending_updates ON release_group_secondary_type_join; DROP TRIGGER IF EXISTS apply_artist_release_pending_updates ON release_label; DROP TRIGGER IF EXISTS apply_artist_release_group_pending_updates ON track; diff --git a/mbslave/sql/EnableLastUpdatedTriggers.sql b/mbslave/sql/EnableLastUpdatedTriggers.sql old mode 100755 new mode 100644 diff --git a/mbslave/sql/InsertTestData.sql b/mbslave/sql/InsertTestData.sql index 0fecd6c..8914845 100644 --- a/mbslave/sql/InsertTestData.sql +++ b/mbslave/sql/InsertTestData.sql @@ -110,8 +110,8 @@ INSERT INTO release_label (id, release, label, catalog_number) INSERT INTO url (id, gid, url) VALUES (1, '9201840b-d810-4e0f-bb75-c791205f5b24', 'http://musicbrainz.org/'); -INSERT INTO medium (id, release, position, format, name) VALUES (1, 1, 1, 1, 'The First Disc'); -INSERT INTO medium (id, release, position, format, name) VALUES (2, 1, 2, 1, 'The Second Disc'); +INSERT INTO medium (id, gid, release, position, format, name) VALUES (1, 'e67d7889-d617-478f-acc2-40012aec59f5', 1, 1, 1, 'The First Disc'); +INSERT INTO medium (id, gid, release, position, format, name) VALUES (2, 'e692e77f-8670-4c1c-b880-8c3d23d14f04', 1, 2, 1, 'The Second Disc'); INSERT INTO track (id, gid, recording, medium, position, number, name, artist_credit, length) VALUES (1, '3fd2523e-1ced-4f83-8b93-c7ecf6960b32', 1, 1, 1, 1, 1, 2, 123456); @@ -157,11 +157,11 @@ INSERT INTO release_label (id, release, label, catalog_number) INSERT INTO release_label (id, release, label, catalog_number) VALUES (4, 3, 2, '82796 97772 2'); -INSERT INTO medium (id, release, position, format, name) VALUES (3, 2, 1, 1, 'A Sea of Honey'); -INSERT INTO medium (id, release, position, format, name) VALUES (4, 2, 2, 1, 'A Sky of Honey'); +INSERT INTO medium (id, gid, release, position, format, name) VALUES (3, '6ff40540-7c91-4d0d-bbdd-a199edb45e08', 2, 1, 1, 'A Sea of Honey'); +INSERT INTO medium (id, gid, release, position, format, name) VALUES (4, '76e93539-2c4a-4e24-b3ad-79657cb8185e', 2, 2, 1, 'A Sky of Honey'); -INSERT INTO medium (id, release, position, format, name) VALUES (5, 3, 1, 1, 'A Sea of Honey'); -INSERT INTO medium (id, release, position, format, name) VALUES (6, 3, 2, 1, 'A Sky of Honey'); +INSERT INTO medium (id, gid, release, position, format, name) VALUES (5, '84130ff4-50bb-45cf-a3db-454236a59bc4', 3, 1, 1, 'A Sea of Honey'); +INSERT INTO medium (id, gid, release, position, format, name) VALUES (6, '0e8c3742-21be-48ec-8672-138cd0e6d178', 3, 2, 1, 'A Sky of Honey'); INSERT INTO recording (id, gid, name, artist_credit, length, last_updated) VALUES (2, '54b9d183-7dab-42ba-94a3-7388a66604b8', 'King of the Mountain', 3, 293720, '2020-02-20 19:00:00'); @@ -242,6 +242,7 @@ INSERT INTO isrc (isrc, recording) VALUES ('DEE250800230', 2); INSERT INTO link (id, link_type, attribute_count) VALUES (1, 148, 1); INSERT INTO link (id, link_type, attribute_count) VALUES (2, 148, 2); INSERT INTO link (id, link_type, attribute_count, begin_date_year) VALUES (3, 183, 0, 2006); +INSERT INTO link (id, link_type, attribute_count) VALUES (4, 108, 0); INSERT INTO link_attribute (link, attribute_type) VALUES (1, 229); INSERT INTO link_attribute (link, attribute_type) VALUES (2, 1); @@ -256,6 +257,7 @@ INSERT INTO artist (id, gid, name, sort_name, comment) VALUES INSERT INTO event (id, gid, name, begin_date_year, begin_date_month, begin_date_day, end_date_year, end_date_month, end_date_day, time, type, cancelled, setlist, comment, ended) VALUES (59357, 'ca1d24c1-1999-46fd-8a95-3d4108df5cb2', 'BBC Open Music Prom', 2022, 9, 1, 2022, 9, 1, '19:30:00', 1, 'f', NULL, '2022, Prom 60', 't'); +INSERT INTO l_artist_artist (id, link, entity0, entity1) VALUES (1, 4, 8, 9); INSERT INTO l_artist_recording (id, link, entity0, entity1) VALUES (1, 1, 8, 2); INSERT INTO l_artist_recording (id, link, entity0, entity1, edits_pending) VALUES (2, 1, 9, 2, 1); INSERT INTO l_artist_recording (id, link, entity0, entity1) VALUES (3, 2, 8, 3); diff --git a/mbslave/sql/TruncateTables.sql b/mbslave/sql/TruncateTables.sql index db1e112..a9daad7 100644 --- a/mbslave/sql/TruncateTables.sql +++ b/mbslave/sql/TruncateTables.sql @@ -268,6 +268,7 @@ TRUNCATE TABLE medium_attribute_type_allowed_value RESTART IDENTITY CASCADE; TRUNCATE TABLE medium_attribute_type_allowed_value_allowed_format RESTART IDENTITY CASCADE; TRUNCATE TABLE medium_cdtoc RESTART IDENTITY CASCADE; TRUNCATE TABLE medium_format RESTART IDENTITY CASCADE; +TRUNCATE TABLE medium_gid_redirect RESTART IDENTITY CASCADE; TRUNCATE TABLE medium_index RESTART IDENTITY CASCADE; TRUNCATE TABLE mood RESTART IDENTITY CASCADE; TRUNCATE TABLE mood_alias RESTART IDENTITY CASCADE; diff --git a/mbslave/sql/caa/CreateMQTriggers.sql b/mbslave/sql/caa/CreateMQTriggers.sql deleted file mode 100644 index b508800..0000000 --- a/mbslave/sql/caa/CreateMQTriggers.sql +++ /dev/null @@ -1,148 +0,0 @@ -BEGIN; - -SET search_path = 'cover_art_archive'; - -CREATE OR REPLACE FUNCTION reindex_release() RETURNS trigger AS $$ - DECLARE - release_mbid UUID; - BEGIN - SELECT gid INTO release_mbid - FROM musicbrainz.release r - JOIN cover_art_archive.cover_art caa_r ON r.id = caa_r.release - WHERE r.id = NEW.id; - - IF FOUND THEN - PERFORM amqp.publish(1, 'cover-art-archive', 'index', release_mbid::text); - END IF; - RETURN NULL; - END; -$$ LANGUAGE 'plpgsql'; - -CREATE TRIGGER caa_reindex AFTER UPDATE OR INSERT -ON musicbrainz.release FOR EACH ROW -EXECUTE PROCEDURE reindex_release(); - -CREATE OR REPLACE FUNCTION reindex_artist() RETURNS trigger AS $$ - BEGIN - -- Short circuit if the name hasn't changed - IF NEW.name = OLD.name AND NEW.sort_name = OLD.sort_name THEN - RETURN NULL; - END IF; - - PERFORM amqp.publish(1, 'cover-art-archive', 'index', r.gid::text) - FROM musicbrainz.release r - JOIN cover_art_archive.cover_art caa_r ON r.id = caa_r.release - JOIN musicbrainz.artist_credit_name acn ON r.artist_credit = acn.artist_credit - WHERE acn.artist = NEW.id; - - RETURN NULL; - END; -$$ LANGUAGE 'plpgsql'; - -CREATE TRIGGER caa_reindex AFTER UPDATE -ON musicbrainz.artist FOR EACH ROW -EXECUTE PROCEDURE reindex_artist(); - -CREATE OR REPLACE FUNCTION reindex_release_via_catno() RETURNS trigger AS $$ - DECLARE - release_mbid UUID; - BEGIN - SELECT gid INTO release_mbid - FROM musicbrainz.release - JOIN musicbrainz.release_label ON release_label.release = release.id - JOIN cover_art_archive.cover_art caa_r ON release.id = caa_r.release - WHERE release.id = NEW.release; - - IF FOUND THEN - PERFORM amqp.publish(1, 'cover-art-archive', 'index', release_mbid::text); - END IF; - RETURN NULL; - END; -$$ LANGUAGE 'plpgsql'; - -CREATE TRIGGER caa_reindex AFTER UPDATE OR INSERT -ON musicbrainz.release_label FOR EACH ROW -EXECUTE PROCEDURE reindex_release_via_catno(); - -CREATE OR REPLACE FUNCTION reindex_caa() RETURNS trigger AS $$ - BEGIN - PERFORM amqp.publish(1, 'cover-art-archive', 'index', gid::text) - FROM musicbrainz.release - WHERE id = coalesce(( - CASE TG_OP - WHEN 'DELETE' THEN OLD.release - ELSE NEW.release - END)); - RETURN NULL; - END; -$$ LANGUAGE 'plpgsql'; - -CREATE TRIGGER caa_reindex AFTER UPDATE OR INSERT OR DELETE -ON cover_art_archive.cover_art FOR EACH ROW -EXECUTE PROCEDURE reindex_caa(); - -CREATE OR REPLACE FUNCTION reindex_caa_type() RETURNS trigger AS $$ - BEGIN - PERFORM amqp.publish(1, 'cover-art-archive', 'index', r.gid::text) - FROM musicbrainz.release r - JOIN cover_art_archive.cover_art ca ON r.id = ca.release - WHERE ca.id = coalesce(( - CASE TG_OP - WHEN 'DELETE' THEN OLD.id - ELSE NEW.id - END)); - RETURN NULL; - END; -$$ LANGUAGE 'plpgsql'; - -CREATE TRIGGER caa_reindex AFTER UPDATE OR INSERT OR DELETE -ON cover_art_archive.cover_art_type FOR EACH ROW -EXECUTE PROCEDURE reindex_caa_type(); - -CREATE OR REPLACE FUNCTION caa_move() RETURNS trigger AS $$ - BEGIN - IF OLD.release != NEW.release THEN - PERFORM amqp.publish(1, 'cover-art-archive', 'move', - (SELECT ca.id || E'\n' || - old_release.gid || E'\n' || - new_release.gid || E'\n' || - it.suffix || E'\n' - FROM cover_art_archive.cover_art ca - JOIN cover_art_archive.image_type it ON it.mime_type = ca.mime_type, - musicbrainz.release old_release, - musicbrainz.release new_release - WHERE ca.id = OLD.id - AND old_release.id = OLD.release - AND new_release.id = NEW.release)); - END IF; - RETURN NEW; - END; -$$ LANGUAGE 'plpgsql'; - -CREATE TRIGGER caa_move BEFORE UPDATE -ON cover_art_archive.cover_art FOR EACH ROW -EXECUTE PROCEDURE caa_move(); - -CREATE OR REPLACE FUNCTION delete_release() RETURNS trigger AS $$ - BEGIN - PERFORM - amqp.publish(1, 'cover-art-archive', 'delete', - (cover_art.id || E'\n' || OLD.gid || E'\n' || image_type.suffix)::text) - FROM cover_art_archive.cover_art - JOIN cover_art_archive.image_type ON image_type.mime_type = cover_art.mime_type - WHERE release = OLD.id; - - PERFORM amqp.publish(1, 'cover-art-archive', 'delete', - ('index.json' || E'\n' || OLD.gid)::text) - FROM musicbrainz.release - WHERE release.id = OLD.id; - - RETURN OLD; - END; -$$ LANGUAGE 'plpgsql'; - -CREATE TRIGGER caa_delete BEFORE DELETE -ON musicbrainz.release FOR EACH ROW -EXECUTE PROCEDURE delete_release(); - -COMMIT; diff --git a/mbslave/sql/caa/CreateViews.sql b/mbslave/sql/caa/CreateViews.sql index c9cbac3..e005e2c 100644 --- a/mbslave/sql/caa/CreateViews.sql +++ b/mbslave/sql/caa/CreateViews.sql @@ -11,7 +11,6 @@ SELECT cover_art.*, JOIN cover_art_archive.cover_art ca_front USING (id) WHERE ca_front.release = cover_art.release AND type_id = 1 - AND mime_type != 'application/pdf' ORDER BY ca_front.ordering LIMIT 1), FALSE) AS is_front, coalesce(cover_art.id = (SELECT id FROM cover_art_archive.cover_art_type diff --git a/mbslave/sql/caa/DropMQTriggers.sql b/mbslave/sql/caa/DropMQTriggers.sql deleted file mode 100644 index b6449b2..0000000 --- a/mbslave/sql/caa/DropMQTriggers.sql +++ /dev/null @@ -1,19 +0,0 @@ -BEGIN; - -DROP TRIGGER caa_reindex ON musicbrainz.artist; -DROP TRIGGER caa_reindex ON musicbrainz.release; -DROP TRIGGER caa_reindex ON musicbrainz.release_label; -DROP TRIGGER caa_reindex ON cover_art_archive.cover_art; -DROP TRIGGER caa_reindex ON cover_art_archive.cover_art_type; -DROP TRIGGER caa_move ON cover_art_archive.cover_art; -DROP TRIGGER caa_delete ON musicbrainz.release; - -DROP FUNCTION cover_art_archive.reindex_release (); -DROP FUNCTION cover_art_archive.reindex_artist (); -DROP FUNCTION cover_art_archive.reindex_release_via_catno (); -DROP FUNCTION cover_art_archive.reindex_caa (); -DROP FUNCTION cover_art_archive.reindex_caa_type (); -DROP FUNCTION cover_art_archive.caa_move (); -DROP FUNCTION cover_art_archive.delete_release (); - -COMMIT; diff --git a/mbslave/sql/eaa/CreateMQTriggers.sql b/mbslave/sql/eaa/CreateMQTriggers.sql deleted file mode 100644 index 4aee8d7..0000000 --- a/mbslave/sql/eaa/CreateMQTriggers.sql +++ /dev/null @@ -1,161 +0,0 @@ -BEGIN; - -SET search_path = 'event_art_archive'; - -CREATE OR REPLACE FUNCTION reindex_event() RETURNS trigger AS $$ - DECLARE - event_mbid UUID; - BEGIN - SELECT gid INTO event_mbid - FROM musicbrainz.event e - JOIN event_art_archive.event_art ea ON e.id = ea.event - WHERE e.id = NEW.id; - - IF FOUND THEN - PERFORM amqp.publish(1, 'event-art-archive', 'index', event_mbid::text); - END IF; - RETURN NULL; - END; -$$ LANGUAGE 'plpgsql'; - -CREATE TRIGGER eaa_reindex AFTER UPDATE OR INSERT -ON musicbrainz.event FOR EACH ROW -EXECUTE PROCEDURE reindex_event(); - -CREATE OR REPLACE FUNCTION reindex_artist() RETURNS trigger AS $$ - BEGIN - -- Short circuit if the name hasn't changed - IF NEW.name = OLD.name AND NEW.sort_name = OLD.sort_name THEN - RETURN NULL; - END IF; - - PERFORM amqp.publish(1, 'event-art-archive', 'index', e.gid::text) - FROM musicbrainz.event e - JOIN event_art_archive.event_art ea ON e.id = ea.event - JOIN musicbrainz.l_artist_event lae ON e.id = lae.entity1 - WHERE lae.entity0 = NEW.id; - - RETURN NULL; - END; -$$ LANGUAGE 'plpgsql'; - -CREATE TRIGGER eaa_reindex AFTER UPDATE -ON musicbrainz.artist FOR EACH ROW -EXECUTE PROCEDURE reindex_artist(); - -CREATE OR REPLACE FUNCTION reindex_l_artist_event() RETURNS trigger AS $$ - BEGIN - PERFORM amqp.publish(1, 'event-art-archive', 'index', e.gid::text) - FROM musicbrainz.event e - JOIN event_art_archive.event_art ea ON e.id = ea.event - JOIN musicbrainz.l_artist_event lae ON e.id = lae.entity1 - WHERE lae.id = (CASE TG_OP - WHEN 'DELETE' THEN OLD.id - ELSE NEW.id - END); - RETURN NULL; - END; -$$ LANGUAGE 'plpgsql'; - -CREATE TRIGGER eaa_reindex AFTER UPDATE OR INSERT OR DELETE -ON musicbrainz.l_artist_event FOR EACH ROW -EXECUTE PROCEDURE reindex_l_artist_event(); - -CREATE OR REPLACE FUNCTION reindex_place() RETURNS trigger AS $$ - BEGIN - PERFORM amqp.publish(1, 'event-art-archive', 'index', e.gid::text) - FROM musicbrainz.event e - JOIN event_art_archive.event_art ea ON e.id = ea.event - JOIN musicbrainz.l_event_place lep ON e.id = lep.entity0 - WHERE lep.entity1 = NEW.id; - - RETURN NULL; - END; -$$ LANGUAGE 'plpgsql'; - -CREATE TRIGGER eaa_reindex AFTER UPDATE -ON musicbrainz.place FOR EACH ROW -EXECUTE PROCEDURE reindex_place(); - -CREATE OR REPLACE FUNCTION reindex_l_event_place() RETURNS trigger AS $$ - BEGIN - PERFORM amqp.publish(1, 'event-art-archive', 'index', e.gid::text) - FROM musicbrainz.event e - JOIN event_art_archive.event_art ea ON e.id = ea.event - JOIN musicbrainz.l_event_place lep ON e.id = lep.entity0 - WHERE lep.id = (CASE TG_OP - WHEN 'DELETE' THEN OLD.id - ELSE NEW.id - END); - RETURN NULL; - END; -$$ LANGUAGE 'plpgsql'; - -CREATE TRIGGER eaa_reindex AFTER UPDATE OR INSERT OR DELETE -ON musicbrainz.l_event_place FOR EACH ROW -EXECUTE PROCEDURE reindex_l_event_place(); - -CREATE OR REPLACE FUNCTION reindex_eaa() RETURNS trigger AS $$ - BEGIN - PERFORM amqp.publish(1, 'event-art-archive', 'index', gid::text) - FROM musicbrainz.event - WHERE id = coalesce(( - CASE TG_OP - WHEN 'DELETE' THEN OLD.event - ELSE NEW.event - END)); - RETURN NULL; - END; -$$ LANGUAGE 'plpgsql'; - -CREATE TRIGGER eaa_reindex AFTER UPDATE OR INSERT OR DELETE -ON event_art_archive.event_art FOR EACH ROW -EXECUTE PROCEDURE reindex_eaa(); - -CREATE OR REPLACE FUNCTION move_event() RETURNS trigger AS $$ - BEGIN - IF OLD.event != NEW.event THEN - PERFORM amqp.publish(1, 'event-art-archive', 'move', - (SELECT ea.id || E'\n' || - old_event.gid || E'\n' || - new_event.gid || E'\n' || - it.suffix || E'\n' - FROM event_art_archive.event_art ea - JOIN cover_art_archive.image_type it ON it.mime_type = ea.mime_type, - musicbrainz.event old_event, - musicbrainz.event new_event - WHERE ea.id = OLD.id - AND old_event.id = OLD.event - AND new_event.id = NEW.event)); - END IF; - RETURN NEW; - END; -$$ LANGUAGE 'plpgsql'; - -CREATE TRIGGER eaa_move BEFORE UPDATE -ON event_art_archive.event_art FOR EACH ROW -EXECUTE PROCEDURE move_event(); - -CREATE OR REPLACE FUNCTION delete_event() RETURNS trigger AS $$ - BEGIN - PERFORM - amqp.publish(1, 'event-art-archive', 'delete', - (event_art.id || E'\n' || OLD.gid || E'\n' || image_type.suffix)::text) - FROM event_art_archive.event_art - JOIN cover_art_archive.image_type ON image_type.mime_type = event_art.mime_type - WHERE event = OLD.id; - - PERFORM amqp.publish(1, 'event-art-archive', 'delete', - ('index.json' || E'\n' || OLD.gid)::text) - FROM musicbrainz.event - WHERE event.id = OLD.id; - - RETURN OLD; - END; -$$ LANGUAGE 'plpgsql'; - -CREATE TRIGGER eaa_delete BEFORE DELETE -ON musicbrainz.event FOR EACH ROW -EXECUTE PROCEDURE delete_event(); - -COMMIT; diff --git a/mbslave/sql/eaa/CreateViews.sql b/mbslave/sql/eaa/CreateViews.sql index 56f9aaa..cb87fc4 100644 --- a/mbslave/sql/eaa/CreateViews.sql +++ b/mbslave/sql/eaa/CreateViews.sql @@ -11,7 +11,6 @@ SELECT event_art.*, JOIN event_art_archive.event_art ea_front USING (id) WHERE ea_front.event = event_art.event AND type_id = 1 - AND mime_type != 'application/pdf' ORDER BY ea_front.ordering LIMIT 1), FALSE) AS is_front, array(SELECT art_type.name diff --git a/mbslave/sql/eaa/DropMQTriggers.sql b/mbslave/sql/eaa/DropMQTriggers.sql deleted file mode 100644 index 3651e68..0000000 --- a/mbslave/sql/eaa/DropMQTriggers.sql +++ /dev/null @@ -1,21 +0,0 @@ -BEGIN; - -DROP TRIGGER eaa_delete ON musicbrainz.event; -DROP TRIGGER eaa_move ON event_art_archive.event_art; -DROP TRIGGER eaa_reindex ON event_art_archive.event_art; -DROP TRIGGER eaa_reindex ON musicbrainz.artist; -DROP TRIGGER eaa_reindex ON musicbrainz.event; -DROP TRIGGER eaa_reindex ON musicbrainz.l_artist_event; -DROP TRIGGER eaa_reindex ON musicbrainz.l_event_place; -DROP TRIGGER eaa_reindex ON musicbrainz.place; - -DROP FUNCTION event_art_archive.delete_event (); -DROP FUNCTION event_art_archive.move_event (); -DROP FUNCTION event_art_archive.reindex_artist (); -DROP FUNCTION event_art_archive.reindex_eaa (); -DROP FUNCTION event_art_archive.reindex_event (); -DROP FUNCTION event_art_archive.reindex_l_artist_event (); -DROP FUNCTION event_art_archive.reindex_l_event_place (); -DROP FUNCTION event_art_archive.reindex_place (); - -COMMIT; diff --git a/mbslave/sql/updates/20240221-mbs-13492.sql b/mbslave/sql/updates/20240221-mbs-13492.sql new file mode 100644 index 0000000..6cc4950 --- /dev/null +++ b/mbslave/sql/updates/20240221-mbs-13492.sql @@ -0,0 +1,23 @@ +\set ON_ERROR_STOP 1 + +BEGIN; +SET LOCAL statement_timeout = 0; + +UPDATE editor + SET privs = privs | 8192 -- set new beginner flag + WHERE id != 4 -- avoid setting ModBot as beginner + AND NOT deleted + AND ( + member_since > NOW() - INTERVAL '2 weeks' + OR + NOT EXISTS ( + SELECT 1 + FROM edit + WHERE edit.editor = editor.id + AND edit.autoedit = 0 + AND edit.status = 2 + OFFSET 9 + ) + ); + +COMMIT; diff --git a/mbslave/sql/updates/20240726-mbs-9373.sql b/mbslave/sql/updates/20240726-mbs-9373.sql new file mode 100644 index 0000000..96c5ce1 --- /dev/null +++ b/mbslave/sql/updates/20240726-mbs-9373.sql @@ -0,0 +1,9 @@ +\set ON_ERROR_STOP 1 + +BEGIN; + +UPDATE editor + SET privs = privs | 16384 -- set new voting disabled flag + WHERE (privs & 1024) > 0; -- where editor had editing disabled flag + +COMMIT; diff --git a/mbslave/sql/updates/20241017-mbs-9253-13464.sql b/mbslave/sql/updates/20241017-mbs-9253-13464.sql new file mode 100644 index 0000000..b7af8c9 --- /dev/null +++ b/mbslave/sql/updates/20241017-mbs-9253-13464.sql @@ -0,0 +1,136 @@ +\set ON_ERROR_STOP 1 + +BEGIN; + +DROP TRIGGER IF EXISTS a_upd_release_group_primary_type ON release_group_primary_type; +DROP TRIGGER IF EXISTS a_upd_release_group_secondary_type ON release_group_secondary_type; + +DROP TRIGGER IF EXISTS apply_artist_release_group_pending_updates ON release_group_primary_type; +DROP TRIGGER IF EXISTS apply_artist_release_group_pending_updates ON release_group_secondary_type; + +DROP FUNCTION get_artist_release_group_rows(integer); + +DROP INDEX artist_release_group_nonva_idx_sort; +DROP INDEX artist_release_group_va_idx_sort; + +DROP TABLE artist_release_group_nonva; +DROP TABLE artist_release_group_va; +DROP TABLE artist_release_group; + +CREATE TABLE artist_release_group ( + -- See comment for `artist_release.is_track_artist`. + is_track_artist BOOLEAN NOT NULL, + artist INTEGER NOT NULL, -- references artist.id, CASCADE + unofficial BOOLEAN NOT NULL, + primary_type_child_order SMALLINT, + primary_type SMALLINT, + secondary_type_child_orders SMALLINT[], + secondary_types SMALLINT[], + first_release_date INTEGER, + name VARCHAR COLLATE musicbrainz NOT NULL, + release_group INTEGER NOT NULL -- references release_group.id, CASCADE +) PARTITION BY LIST (is_track_artist); + +CREATE TABLE artist_release_group_nonva + PARTITION OF artist_release_group FOR VALUES IN (FALSE); + +CREATE TABLE artist_release_group_va + PARTITION OF artist_release_group FOR VALUES IN (TRUE); + +CREATE OR REPLACE FUNCTION get_artist_release_group_rows( + release_group_id INTEGER +) RETURNS SETOF artist_release_group AS $$ +BEGIN + -- PostgreSQL 12 generates a vastly more efficient plan when only + -- one release group ID is passed. A condition like + -- `rg.id = any(...)` can be over 200x slower, even with only one + -- release group ID in the array. + RETURN QUERY EXECUTE $SQL$ + SELECT DISTINCT ON (a_rg.artist, rg.id) + a_rg.is_track_artist, + a_rg.artist, + -- Withdrawn releases were once official by definition + bool_and(r.status IS NOT NULL AND r.status != 1 AND r.status != 5), + rgpt.child_order::SMALLINT, + rg.type::SMALLINT, + array_agg( + DISTINCT rgst.child_order ORDER BY rgst.child_order) + FILTER (WHERE rgst.child_order IS NOT NULL + )::SMALLINT[], + array_agg( + DISTINCT st.secondary_type ORDER BY st.secondary_type) + FILTER (WHERE st.secondary_type IS NOT NULL + )::SMALLINT[], + integer_date( + rgm.first_release_date_year, + rgm.first_release_date_month, + rgm.first_release_date_day + ), + rg.name, + rg.id + FROM ( + SELECT FALSE AS is_track_artist, rgacn.artist, rg.id AS release_group + FROM release_group rg + JOIN artist_credit_name rgacn ON rgacn.artist_credit = rg.artist_credit + UNION ALL + SELECT TRUE AS is_track_artist, tacn.artist, r.release_group + FROM release r + JOIN medium m ON m.release = r.id + JOIN track t ON t.medium = m.id + JOIN artist_credit_name tacn ON tacn.artist_credit = t.artist_credit + ) a_rg + JOIN release_group rg ON rg.id = a_rg.release_group + LEFT JOIN release r ON r.release_group = rg.id + JOIN release_group_meta rgm ON rgm.id = rg.id + LEFT JOIN release_group_primary_type rgpt ON rgpt.id = rg.type + LEFT JOIN release_group_secondary_type_join st ON st.release_group = rg.id + LEFT JOIN release_group_secondary_type rgst ON rgst.id = st.secondary_type + $SQL$ || (CASE WHEN release_group_id IS NULL THEN '' ELSE 'WHERE rg.id = $1' END) || + $SQL$ + GROUP BY a_rg.is_track_artist, a_rg.artist, rgm.id, rg.id, rgpt.child_order + ORDER BY a_rg.artist, rg.id, a_rg.is_track_artist + $SQL$ + USING release_group_id; +END; +$$ LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION a_upd_release_group_primary_type_mirror() +RETURNS trigger AS $$ +BEGIN + -- DO NOT modify any replicated tables in this function; it's used + -- by a trigger on mirrors. + IF (NEW.child_order IS DISTINCT FROM OLD.child_order) + THEN + INSERT INTO artist_release_group_pending_update ( + SELECT id FROM release_group + WHERE release_group.type = OLD.id + ); + END IF; + RETURN NULL; +END; +$$ LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION a_upd_release_group_secondary_type_mirror() +RETURNS trigger AS $$ +BEGIN + -- DO NOT modify any replicated tables in this function; it's used + -- by a trigger on mirrors. + IF (NEW.child_order IS DISTINCT FROM OLD.child_order) + THEN + INSERT INTO artist_release_group_pending_update ( + SELECT release_group + FROM release_group_secondary_type_join + WHERE secondary_type = OLD.id + ); + END IF; + RETURN NULL; +END; +$$ LANGUAGE 'plpgsql'; + +CREATE INDEX artist_release_group_nonva_idx_sort ON artist_release_group_nonva (artist, unofficial, primary_type_child_order NULLS FIRST, primary_type NULLS FIRST, secondary_type_child_orders NULLS FIRST, secondary_types NULLS FIRST, first_release_date NULLS LAST, name, release_group); +CREATE INDEX artist_release_group_va_idx_sort ON artist_release_group_va (artist, unofficial, primary_type_child_order NULLS FIRST, primary_type NULLS FIRST, secondary_type_child_orders NULLS FIRST, secondary_types NULLS FIRST, first_release_date NULLS LAST, name, release_group); + +CREATE UNIQUE INDEX artist_release_group_nonva_idx_uniq ON artist_release_group_nonva (release_group, artist); +CREATE UNIQUE INDEX artist_release_group_va_idx_uniq ON artist_release_group_va (release_group, artist); + +COMMIT; diff --git a/mbslave/sql/updates/20241017-mbs-9253-master_and_standalone.sql b/mbslave/sql/updates/20241017-mbs-9253-master_and_standalone.sql new file mode 100644 index 0000000..dcaa5f3 --- /dev/null +++ b/mbslave/sql/updates/20241017-mbs-9253-master_and_standalone.sql @@ -0,0 +1,37 @@ +\set ON_ERROR_STOP 1 + +SET search_path = musicbrainz; + +BEGIN; + +ALTER TABLE artist_release_group + ADD CONSTRAINT artist_release_group_fk_artist + FOREIGN KEY (artist) + REFERENCES artist(id) + ON DELETE CASCADE; + +ALTER TABLE artist_release_group + ADD CONSTRAINT artist_release_group_fk_release_group + FOREIGN KEY (release_group) + REFERENCES release_group(id) + ON DELETE CASCADE; + +CREATE TRIGGER a_upd_release_group_primary_type AFTER UPDATE ON release_group_primary_type + FOR EACH ROW EXECUTE PROCEDURE a_upd_release_group_primary_type_mirror(); + +CREATE TRIGGER a_upd_release_group_secondary_type AFTER UPDATE ON release_group_secondary_type + FOR EACH ROW EXECUTE PROCEDURE a_upd_release_group_secondary_type_mirror(); + +CREATE CONSTRAINT TRIGGER apply_artist_release_group_pending_updates + AFTER UPDATE ON release_group_primary_type DEFERRABLE INITIALLY DEFERRED + FOR EACH ROW + WHEN (OLD.child_order IS DISTINCT FROM NEW.child_order) + EXECUTE PROCEDURE apply_artist_release_group_pending_updates(); + +CREATE CONSTRAINT TRIGGER apply_artist_release_group_pending_updates + AFTER UPDATE ON release_group_secondary_type DEFERRABLE INITIALLY DEFERRED + FOR EACH ROW + WHEN (OLD.child_order IS DISTINCT FROM NEW.child_order) + EXECUTE PROCEDURE apply_artist_release_group_pending_updates(); + +COMMIT; diff --git a/mbslave/sql/updates/20241017-mbs-9253-mirror_only.sql b/mbslave/sql/updates/20241017-mbs-9253-mirror_only.sql new file mode 100644 index 0000000..e42e50d --- /dev/null +++ b/mbslave/sql/updates/20241017-mbs-9253-mirror_only.sql @@ -0,0 +1,31 @@ +\set ON_ERROR_STOP 1 + +BEGIN; + +DROP TRIGGER IF EXISTS a_upd_release_group_primary_type_mirror ON release_group_primary_type; + +CREATE TRIGGER a_upd_release_group_primary_type_mirror AFTER UPDATE ON release_group_primary_type + FOR EACH ROW EXECUTE PROCEDURE a_upd_release_group_primary_type_mirror(); + +DROP TRIGGER IF EXISTS a_upd_release_group_secondary_type_mirror ON release_group_secondary_type; + +CREATE TRIGGER a_upd_release_group_secondary_type_mirror AFTER UPDATE ON release_group_secondary_type + FOR EACH ROW EXECUTE PROCEDURE a_upd_release_group_secondary_type_mirror(); + +DROP TRIGGER IF EXISTS apply_artist_release_group_pending_updates_mirror ON release_group_primary_type; + +CREATE CONSTRAINT TRIGGER apply_artist_release_group_pending_updates_mirror + AFTER UPDATE ON release_group_primary_type DEFERRABLE INITIALLY DEFERRED + FOR EACH ROW + WHEN (OLD.child_order IS DISTINCT FROM NEW.child_order) + EXECUTE PROCEDURE apply_artist_release_group_pending_updates(); + +DROP TRIGGER IF EXISTS apply_artist_release_group_pending_updates_mirror ON release_group_secondary_type; + +CREATE CONSTRAINT TRIGGER apply_artist_release_group_pending_updates_mirror + AFTER UPDATE ON release_group_secondary_type DEFERRABLE INITIALLY DEFERRED + FOR EACH ROW + WHEN (OLD.child_order IS DISTINCT FROM NEW.child_order) + EXECUTE PROCEDURE apply_artist_release_group_pending_updates(); + +COMMIT; diff --git a/mbslave/sql/updates/20241125-mbs-13832.sql b/mbslave/sql/updates/20241125-mbs-13832.sql new file mode 100644 index 0000000..5a7e1de --- /dev/null +++ b/mbslave/sql/updates/20241125-mbs-13832.sql @@ -0,0 +1,48 @@ +\set ON_ERROR_STOP 1 + +BEGIN; + +-- MBS-14014 +DROP VIEW IF EXISTS cover_art_archive.index_listing; + +-- CAA view +CREATE VIEW cover_art_archive.index_listing AS +SELECT cover_art.*, + (edit.close_time IS NOT NULL) AS approved, + coalesce(cover_art.id = (SELECT id FROM cover_art_archive.cover_art_type + JOIN cover_art_archive.cover_art ca_front USING (id) + WHERE ca_front.release = cover_art.release + AND type_id = 1 + ORDER BY ca_front.ordering + LIMIT 1), FALSE) AS is_front, + coalesce(cover_art.id = (SELECT id FROM cover_art_archive.cover_art_type + JOIN cover_art_archive.cover_art ca_front USING (id) + WHERE ca_front.release = cover_art.release + AND type_id = 2 + ORDER BY ca_front.ordering + LIMIT 1), FALSE) AS is_back, + array(SELECT art_type.name + FROM cover_art_archive.cover_art_type + JOIN cover_art_archive.art_type ON cover_art_type.type_id = art_type.id + WHERE cover_art_type.id = cover_art.id) AS types +FROM cover_art_archive.cover_art +LEFT JOIN musicbrainz.edit ON edit.id = cover_art.edit; + +-- EAA view +CREATE OR REPLACE VIEW event_art_archive.index_listing AS +SELECT event_art.*, + (edit.close_time IS NOT NULL) AS approved, + coalesce(event_art.id = (SELECT id FROM event_art_archive.event_art_type + JOIN event_art_archive.event_art ea_front USING (id) + WHERE ea_front.event = event_art.event + AND type_id = 1 + ORDER BY ea_front.ordering + LIMIT 1), FALSE) AS is_front, + array(SELECT art_type.name + FROM event_art_archive.event_art_type + JOIN event_art_archive.art_type ON event_art_type.type_id = art_type.id + WHERE event_art_type.id = event_art.id) AS types +FROM event_art_archive.event_art +LEFT JOIN musicbrainz.edit ON edit.id = event_art.edit; + +COMMIT; diff --git a/mbslave/sql/updates/20250320-mbs-13768-fks.sql b/mbslave/sql/updates/20250320-mbs-13768-fks.sql new file mode 100644 index 0000000..788b9b3 --- /dev/null +++ b/mbslave/sql/updates/20250320-mbs-13768-fks.sql @@ -0,0 +1,12 @@ +\set ON_ERROR_STOP 1 + +SET search_path = musicbrainz; + +BEGIN; + +ALTER TABLE medium_gid_redirect + ADD CONSTRAINT medium_gid_redirect_fk_new_id + FOREIGN KEY (new_id) + REFERENCES medium(id); + +COMMIT; diff --git a/mbslave/sql/updates/20250320-mbs-13768.sql b/mbslave/sql/updates/20250320-mbs-13768.sql new file mode 100644 index 0000000..366593a --- /dev/null +++ b/mbslave/sql/updates/20250320-mbs-13768.sql @@ -0,0 +1,31 @@ +\set ON_ERROR_STOP 1 + +BEGIN; + +-- Medium GID + +-- Creating column +ALTER TABLE medium ADD COLUMN gid uuid; + +-- Generating GIDs +UPDATE medium SET gid = + generate_uuid_v3('6ba7b8119dad11d180b400c04fd430c8', 'medium' || id); + +-- Adding NOT NULL constraint +ALTER TABLE medium ALTER COLUMN gid SET NOT NULL; + +-- Creating index +CREATE UNIQUE INDEX medium_idx_gid ON medium (gid); + +-- Medium GID redirect +CREATE TABLE medium_gid_redirect ( -- replicate (verbose) + gid UUID NOT NULL, -- PK + new_id INTEGER NOT NULL, -- references medium.id + created TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +ALTER TABLE medium_gid_redirect ADD CONSTRAINT medium_gid_redirect_pkey PRIMARY KEY (gid); + +CREATE INDEX medium_gid_redirect_idx_new_id ON medium_gid_redirect (new_id); + +COMMIT; diff --git a/mbslave/sql/updates/20250408-mbs-13322.sql b/mbslave/sql/updates/20250408-mbs-13322.sql new file mode 100644 index 0000000..4ed4b1c --- /dev/null +++ b/mbslave/sql/updates/20250408-mbs-13322.sql @@ -0,0 +1,15 @@ +\set ON_ERROR_STOP 1 + +BEGIN; + +CREATE OR REPLACE FUNCTION delete_unused_url(ids INTEGER[]) +RETURNS VOID AS $$ +BEGIN + DELETE FROM url_gid_redirect WHERE new_id = any(ids); + DELETE FROM url WHERE id = any(ids); +EXCEPTION + WHEN foreign_key_violation THEN RETURN; +END; +$$ LANGUAGE 'plpgsql'; + +COMMIT; diff --git a/mbslave/sql/updates/20250408-mbs-13964-all.sql b/mbslave/sql/updates/20250408-mbs-13964-all.sql new file mode 100644 index 0000000..5b2a77c --- /dev/null +++ b/mbslave/sql/updates/20250408-mbs-13964-all.sql @@ -0,0 +1,34 @@ +\set ON_ERROR_STOP 1 + +BEGIN; + +CREATE OR REPLACE FUNCTION set_mediums_recordings_first_release_dates(medium_ids INTEGER[]) +RETURNS VOID AS $$ +BEGIN + PERFORM set_recordings_first_release_dates(( + SELECT array_agg(recording) + FROM track + WHERE track.medium = any(medium_ids) + )); + RETURN; +END; +$$ LANGUAGE 'plpgsql' STRICT; + +CREATE OR REPLACE FUNCTION a_upd_medium_mirror() +RETURNS trigger AS $$ +BEGIN + -- DO NOT modify any replicated tables in this function; it's used + -- by a trigger on mirrors. + IF NEW.release IS DISTINCT FROM OLD.release THEN + PERFORM set_mediums_recordings_first_release_dates(ARRAY[OLD.id]); + END IF; + RETURN NULL; +END; +$$ LANGUAGE 'plpgsql'; + +CREATE TRIGGER a_upd_medium AFTER UPDATE ON medium + FOR EACH ROW EXECUTE PROCEDURE a_upd_medium_mirror(); + +TRUNCATE recording_first_release_date; + +COMMIT; diff --git a/mbslave/sql/updates/20250425-mbs-13464-master_and_standalone.sql b/mbslave/sql/updates/20250425-mbs-13464-master_and_standalone.sql new file mode 100644 index 0000000..42b4259 --- /dev/null +++ b/mbslave/sql/updates/20250425-mbs-13464-master_and_standalone.sql @@ -0,0 +1,19 @@ +\set ON_ERROR_STOP 1 + +SET search_path = musicbrainz; + +BEGIN; + +ALTER TABLE artist_release + ADD CONSTRAINT artist_release_fk_artist + FOREIGN KEY (artist) + REFERENCES artist(id) + ON DELETE CASCADE; + +ALTER TABLE artist_release + ADD CONSTRAINT artist_release_fk_release + FOREIGN KEY (release) + REFERENCES release(id) + ON DELETE CASCADE; + +COMMIT; diff --git a/mbslave/sql/updates/20250425-mbs-13464.sql b/mbslave/sql/updates/20250425-mbs-13464.sql new file mode 100644 index 0000000..98f596e --- /dev/null +++ b/mbslave/sql/updates/20250425-mbs-13464.sql @@ -0,0 +1,81 @@ +\set ON_ERROR_STOP 1 + +BEGIN; + +DROP FUNCTION get_artist_release_rows(integer); + +DROP TABLE artist_release_nonva; +DROP TABLE artist_release_va; +DROP TABLE artist_release; + +CREATE TABLE artist_release ( + is_track_artist BOOLEAN NOT NULL, + artist INTEGER NOT NULL, + first_release_date INTEGER, + catalog_numbers TEXT[], + country_code CHAR(2), + barcode BIGINT, + name VARCHAR COLLATE musicbrainz NOT NULL, + release INTEGER NOT NULL +) PARTITION BY LIST (is_track_artist); + +CREATE TABLE artist_release_nonva + PARTITION OF artist_release FOR VALUES IN (FALSE); + +CREATE TABLE artist_release_va + PARTITION OF artist_release FOR VALUES IN (TRUE); + +CREATE OR REPLACE FUNCTION get_artist_release_rows( + release_id INTEGER +) RETURNS SETOF artist_release AS $$ +BEGIN + -- PostgreSQL 12 generates a vastly more efficient plan when only + -- one release ID is passed. A condition like `r.id = any(...)` + -- can be over 200x slower, even with only one release ID in the + -- array. + RETURN QUERY EXECUTE $SQL$ + SELECT DISTINCT ON (ar.artist, r.id) + ar.is_track_artist, + ar.artist, + integer_date(rfrd.year, rfrd.month, rfrd.day) AS first_release_date, + array_agg( + DISTINCT rl.catalog_number ORDER BY rl.catalog_number + ) FILTER (WHERE rl.catalog_number IS NOT NULL)::TEXT[] AS catalog_numbers, + min(iso.code ORDER BY iso.code)::CHAR(2) AS country_code, + left(regexp_replace( + (CASE r.barcode WHEN '' THEN '0' ELSE r.barcode END), + '[^0-9]+', '', 'g' + ), 18)::BIGINT AS barcode, + r.name, + r.id + FROM ( + SELECT FALSE AS is_track_artist, racn.artist, r.id AS release + FROM release r + JOIN artist_credit_name racn ON racn.artist_credit = r.artist_credit + UNION ALL + SELECT TRUE AS is_track_artist, tacn.artist, m.release + FROM medium m + JOIN track t ON t.medium = m.id + JOIN artist_credit_name tacn ON tacn.artist_credit = t.artist_credit + ) ar + JOIN release r ON r.id = ar.release + LEFT JOIN release_first_release_date rfrd ON rfrd.release = r.id + LEFT JOIN release_label rl ON rl.release = r.id + LEFT JOIN release_country rc ON rc.release = r.id + LEFT JOIN iso_3166_1 iso ON iso.area = rc.country + $SQL$ || (CASE WHEN release_id IS NULL THEN '' ELSE 'WHERE r.id = $1' END) || + $SQL$ + GROUP BY ar.is_track_artist, ar.artist, rfrd.release, r.id + ORDER BY ar.artist, r.id, ar.is_track_artist + $SQL$ + USING release_id; +END; +$$ LANGUAGE plpgsql; + +CREATE INDEX artist_release_nonva_idx_sort ON artist_release_nonva (artist, first_release_date NULLS LAST, catalog_numbers NULLS LAST, country_code NULLS LAST, barcode NULLS LAST, name, release); +CREATE INDEX artist_release_va_idx_sort ON artist_release_va (artist, first_release_date NULLS LAST, catalog_numbers NULLS LAST, country_code NULLS LAST, barcode NULLS LAST, name, release); + +CREATE UNIQUE INDEX artist_release_nonva_idx_uniq ON artist_release_nonva (release, artist); +CREATE UNIQUE INDEX artist_release_va_idx_uniq ON artist_release_va (release, artist); + +COMMIT; diff --git a/mbslave/sql/updates/20250425-mbs-13966.sql b/mbslave/sql/updates/20250425-mbs-13966.sql new file mode 100644 index 0000000..3792cdb --- /dev/null +++ b/mbslave/sql/updates/20250425-mbs-13966.sql @@ -0,0 +1,50 @@ +\set ON_ERROR_STOP 1 + +BEGIN; + +CREATE OR REPLACE FUNCTION set_release_group_first_release_date(release_group_id INTEGER) +RETURNS VOID AS $$ +BEGIN + UPDATE release_group_meta SET first_release_date_year = first.year, + first_release_date_month = first.month, + first_release_date_day = first.day + FROM ( + SELECT rd.year, rd.month, rd.day + FROM release_group + LEFT JOIN release ON release.release_group = release_group.id + LEFT JOIN release_first_release_date rd ON (rd.release = release.id) + WHERE release_group.id = release_group_id + ORDER BY + rd.year NULLS LAST, + rd.month NULLS LAST, + rd.day NULLS LAST + LIMIT 1 + ) AS first + WHERE id = release_group_id; + INSERT INTO artist_release_group_pending_update VALUES (release_group_id); +END; +$$ LANGUAGE 'plpgsql'; + +UPDATE release_group_meta SET first_release_date_year = first.year, + first_release_date_month = first.month, + first_release_date_day = first.day + FROM ( + SELECT DISTINCT ON (release_group.id) + release_group.id AS release_group, rd.year, rd.month, rd.day + FROM release_group + LEFT JOIN release ON release.release_group = release_group.id + LEFT JOIN release_first_release_date rd ON (rd.release = release.id) + ORDER BY + release_group.id, + rd.year NULLS LAST, + rd.month NULLS LAST, + rd.day NULLS LAST + ) AS first +WHERE id = first.release_group + AND ( + first_release_date_year IS DISTINCT FROM first.year + OR first_release_date_month IS DISTINCT FROM first.month + OR first_release_date_day IS DISTINCT FROM first.day + ); + +COMMIT; diff --git a/mbslave/sql/updates/schema-change/30.all.sql b/mbslave/sql/updates/schema-change/30.all.sql new file mode 100644 index 0000000..2a2743d --- /dev/null +++ b/mbslave/sql/updates/schema-change/30.all.sql @@ -0,0 +1,401 @@ +-- Generated by CompileSchemaScripts.pl from: +-- 20241017-mbs-9253-13464.sql +-- 20250408-mbs-13322.sql +-- 20250425-mbs-13464.sql +-- 20241125-mbs-13832.sql +-- 20250320-mbs-13768.sql +-- 20250408-mbs-13964-all.sql +-- 20250425-mbs-13966.sql +\set ON_ERROR_STOP 1 +BEGIN; +SET search_path = musicbrainz, public; +SET LOCAL statement_timeout = 0; +-------------------------------------------------------------------------------- +SELECT '20241017-mbs-9253-13464.sql'; + + +DROP TRIGGER IF EXISTS a_upd_release_group_primary_type ON release_group_primary_type; +DROP TRIGGER IF EXISTS a_upd_release_group_secondary_type ON release_group_secondary_type; + +DROP TRIGGER IF EXISTS apply_artist_release_group_pending_updates ON release_group_primary_type; +DROP TRIGGER IF EXISTS apply_artist_release_group_pending_updates ON release_group_secondary_type; + +DROP FUNCTION get_artist_release_group_rows(integer); + +DROP INDEX artist_release_group_nonva_idx_sort; +DROP INDEX artist_release_group_va_idx_sort; + +DROP TABLE artist_release_group_nonva; +DROP TABLE artist_release_group_va; +DROP TABLE artist_release_group; + +CREATE TABLE artist_release_group ( + -- See comment for `artist_release.is_track_artist`. + is_track_artist BOOLEAN NOT NULL, + artist INTEGER NOT NULL, -- references artist.id, CASCADE + unofficial BOOLEAN NOT NULL, + primary_type_child_order SMALLINT, + primary_type SMALLINT, + secondary_type_child_orders SMALLINT[], + secondary_types SMALLINT[], + first_release_date INTEGER, + name VARCHAR COLLATE musicbrainz NOT NULL, + release_group INTEGER NOT NULL -- references release_group.id, CASCADE +) PARTITION BY LIST (is_track_artist); + +CREATE TABLE artist_release_group_nonva + PARTITION OF artist_release_group FOR VALUES IN (FALSE); + +CREATE TABLE artist_release_group_va + PARTITION OF artist_release_group FOR VALUES IN (TRUE); + +CREATE OR REPLACE FUNCTION get_artist_release_group_rows( + release_group_id INTEGER +) RETURNS SETOF artist_release_group AS $$ +BEGIN + -- PostgreSQL 12 generates a vastly more efficient plan when only + -- one release group ID is passed. A condition like + -- `rg.id = any(...)` can be over 200x slower, even with only one + -- release group ID in the array. + RETURN QUERY EXECUTE $SQL$ + SELECT DISTINCT ON (a_rg.artist, rg.id) + a_rg.is_track_artist, + a_rg.artist, + -- Withdrawn releases were once official by definition + bool_and(r.status IS NOT NULL AND r.status != 1 AND r.status != 5), + rgpt.child_order::SMALLINT, + rg.type::SMALLINT, + array_agg( + DISTINCT rgst.child_order ORDER BY rgst.child_order) + FILTER (WHERE rgst.child_order IS NOT NULL + )::SMALLINT[], + array_agg( + DISTINCT st.secondary_type ORDER BY st.secondary_type) + FILTER (WHERE st.secondary_type IS NOT NULL + )::SMALLINT[], + integer_date( + rgm.first_release_date_year, + rgm.first_release_date_month, + rgm.first_release_date_day + ), + rg.name, + rg.id + FROM ( + SELECT FALSE AS is_track_artist, rgacn.artist, rg.id AS release_group + FROM release_group rg + JOIN artist_credit_name rgacn ON rgacn.artist_credit = rg.artist_credit + UNION ALL + SELECT TRUE AS is_track_artist, tacn.artist, r.release_group + FROM release r + JOIN medium m ON m.release = r.id + JOIN track t ON t.medium = m.id + JOIN artist_credit_name tacn ON tacn.artist_credit = t.artist_credit + ) a_rg + JOIN release_group rg ON rg.id = a_rg.release_group + LEFT JOIN release r ON r.release_group = rg.id + JOIN release_group_meta rgm ON rgm.id = rg.id + LEFT JOIN release_group_primary_type rgpt ON rgpt.id = rg.type + LEFT JOIN release_group_secondary_type_join st ON st.release_group = rg.id + LEFT JOIN release_group_secondary_type rgst ON rgst.id = st.secondary_type + $SQL$ || (CASE WHEN release_group_id IS NULL THEN '' ELSE 'WHERE rg.id = $1' END) || + $SQL$ + GROUP BY a_rg.is_track_artist, a_rg.artist, rgm.id, rg.id, rgpt.child_order + ORDER BY a_rg.artist, rg.id, a_rg.is_track_artist + $SQL$ + USING release_group_id; +END; +$$ LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION a_upd_release_group_primary_type_mirror() +RETURNS trigger AS $$ +BEGIN + -- DO NOT modify any replicated tables in this function; it's used + -- by a trigger on mirrors. + IF (NEW.child_order IS DISTINCT FROM OLD.child_order) + THEN + INSERT INTO artist_release_group_pending_update ( + SELECT id FROM release_group + WHERE release_group.type = OLD.id + ); + END IF; + RETURN NULL; +END; +$$ LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION a_upd_release_group_secondary_type_mirror() +RETURNS trigger AS $$ +BEGIN + -- DO NOT modify any replicated tables in this function; it's used + -- by a trigger on mirrors. + IF (NEW.child_order IS DISTINCT FROM OLD.child_order) + THEN + INSERT INTO artist_release_group_pending_update ( + SELECT release_group + FROM release_group_secondary_type_join + WHERE secondary_type = OLD.id + ); + END IF; + RETURN NULL; +END; +$$ LANGUAGE 'plpgsql'; + +CREATE INDEX artist_release_group_nonva_idx_sort ON artist_release_group_nonva (artist, unofficial, primary_type_child_order NULLS FIRST, primary_type NULLS FIRST, secondary_type_child_orders NULLS FIRST, secondary_types NULLS FIRST, first_release_date NULLS LAST, name, release_group); +CREATE INDEX artist_release_group_va_idx_sort ON artist_release_group_va (artist, unofficial, primary_type_child_order NULLS FIRST, primary_type NULLS FIRST, secondary_type_child_orders NULLS FIRST, secondary_types NULLS FIRST, first_release_date NULLS LAST, name, release_group); + +CREATE UNIQUE INDEX artist_release_group_nonva_idx_uniq ON artist_release_group_nonva (release_group, artist); +CREATE UNIQUE INDEX artist_release_group_va_idx_uniq ON artist_release_group_va (release_group, artist); + +-------------------------------------------------------------------------------- +SELECT '20250408-mbs-13322.sql'; + + +CREATE OR REPLACE FUNCTION delete_unused_url(ids INTEGER[]) +RETURNS VOID AS $$ +BEGIN + DELETE FROM url_gid_redirect WHERE new_id = any(ids); + DELETE FROM url WHERE id = any(ids); +EXCEPTION + WHEN foreign_key_violation THEN RETURN; +END; +$$ LANGUAGE 'plpgsql'; + +-------------------------------------------------------------------------------- +SELECT '20250425-mbs-13464.sql'; + + +DROP FUNCTION get_artist_release_rows(integer); + +DROP TABLE artist_release_nonva; +DROP TABLE artist_release_va; +DROP TABLE artist_release; + +CREATE TABLE artist_release ( + is_track_artist BOOLEAN NOT NULL, + artist INTEGER NOT NULL, + first_release_date INTEGER, + catalog_numbers TEXT[], + country_code CHAR(2), + barcode BIGINT, + name VARCHAR COLLATE musicbrainz NOT NULL, + release INTEGER NOT NULL +) PARTITION BY LIST (is_track_artist); + +CREATE TABLE artist_release_nonva + PARTITION OF artist_release FOR VALUES IN (FALSE); + +CREATE TABLE artist_release_va + PARTITION OF artist_release FOR VALUES IN (TRUE); + +CREATE OR REPLACE FUNCTION get_artist_release_rows( + release_id INTEGER +) RETURNS SETOF artist_release AS $$ +BEGIN + -- PostgreSQL 12 generates a vastly more efficient plan when only + -- one release ID is passed. A condition like `r.id = any(...)` + -- can be over 200x slower, even with only one release ID in the + -- array. + RETURN QUERY EXECUTE $SQL$ + SELECT DISTINCT ON (ar.artist, r.id) + ar.is_track_artist, + ar.artist, + integer_date(rfrd.year, rfrd.month, rfrd.day) AS first_release_date, + array_agg( + DISTINCT rl.catalog_number ORDER BY rl.catalog_number + ) FILTER (WHERE rl.catalog_number IS NOT NULL)::TEXT[] AS catalog_numbers, + min(iso.code ORDER BY iso.code)::CHAR(2) AS country_code, + left(regexp_replace( + (CASE r.barcode WHEN '' THEN '0' ELSE r.barcode END), + '[^0-9]+', '', 'g' + ), 18)::BIGINT AS barcode, + r.name, + r.id + FROM ( + SELECT FALSE AS is_track_artist, racn.artist, r.id AS release + FROM release r + JOIN artist_credit_name racn ON racn.artist_credit = r.artist_credit + UNION ALL + SELECT TRUE AS is_track_artist, tacn.artist, m.release + FROM medium m + JOIN track t ON t.medium = m.id + JOIN artist_credit_name tacn ON tacn.artist_credit = t.artist_credit + ) ar + JOIN release r ON r.id = ar.release + LEFT JOIN release_first_release_date rfrd ON rfrd.release = r.id + LEFT JOIN release_label rl ON rl.release = r.id + LEFT JOIN release_country rc ON rc.release = r.id + LEFT JOIN iso_3166_1 iso ON iso.area = rc.country + $SQL$ || (CASE WHEN release_id IS NULL THEN '' ELSE 'WHERE r.id = $1' END) || + $SQL$ + GROUP BY ar.is_track_artist, ar.artist, rfrd.release, r.id + ORDER BY ar.artist, r.id, ar.is_track_artist + $SQL$ + USING release_id; +END; +$$ LANGUAGE plpgsql; + +CREATE INDEX artist_release_nonva_idx_sort ON artist_release_nonva (artist, first_release_date NULLS LAST, catalog_numbers NULLS LAST, country_code NULLS LAST, barcode NULLS LAST, name, release); +CREATE INDEX artist_release_va_idx_sort ON artist_release_va (artist, first_release_date NULLS LAST, catalog_numbers NULLS LAST, country_code NULLS LAST, barcode NULLS LAST, name, release); + +CREATE UNIQUE INDEX artist_release_nonva_idx_uniq ON artist_release_nonva (release, artist); +CREATE UNIQUE INDEX artist_release_va_idx_uniq ON artist_release_va (release, artist); + +-------------------------------------------------------------------------------- +SELECT '20241125-mbs-13832.sql'; + + +-- MBS-14014 +DROP VIEW IF EXISTS cover_art_archive.index_listing; + +-- CAA view +CREATE VIEW cover_art_archive.index_listing AS +SELECT cover_art.*, + (edit.close_time IS NOT NULL) AS approved, + coalesce(cover_art.id = (SELECT id FROM cover_art_archive.cover_art_type + JOIN cover_art_archive.cover_art ca_front USING (id) + WHERE ca_front.release = cover_art.release + AND type_id = 1 + ORDER BY ca_front.ordering + LIMIT 1), FALSE) AS is_front, + coalesce(cover_art.id = (SELECT id FROM cover_art_archive.cover_art_type + JOIN cover_art_archive.cover_art ca_front USING (id) + WHERE ca_front.release = cover_art.release + AND type_id = 2 + ORDER BY ca_front.ordering + LIMIT 1), FALSE) AS is_back, + array(SELECT art_type.name + FROM cover_art_archive.cover_art_type + JOIN cover_art_archive.art_type ON cover_art_type.type_id = art_type.id + WHERE cover_art_type.id = cover_art.id) AS types +FROM cover_art_archive.cover_art +LEFT JOIN musicbrainz.edit ON edit.id = cover_art.edit; + +-- EAA view +CREATE OR REPLACE VIEW event_art_archive.index_listing AS +SELECT event_art.*, + (edit.close_time IS NOT NULL) AS approved, + coalesce(event_art.id = (SELECT id FROM event_art_archive.event_art_type + JOIN event_art_archive.event_art ea_front USING (id) + WHERE ea_front.event = event_art.event + AND type_id = 1 + ORDER BY ea_front.ordering + LIMIT 1), FALSE) AS is_front, + array(SELECT art_type.name + FROM event_art_archive.event_art_type + JOIN event_art_archive.art_type ON event_art_type.type_id = art_type.id + WHERE event_art_type.id = event_art.id) AS types +FROM event_art_archive.event_art +LEFT JOIN musicbrainz.edit ON edit.id = event_art.edit; + +-------------------------------------------------------------------------------- +SELECT '20250320-mbs-13768.sql'; + + +-- Medium GID + +-- Creating column +ALTER TABLE medium ADD COLUMN gid uuid; + +-- Generating GIDs +UPDATE medium SET gid = + generate_uuid_v3('6ba7b8119dad11d180b400c04fd430c8', 'medium' || id); + +-- Adding NOT NULL constraint +ALTER TABLE medium ALTER COLUMN gid SET NOT NULL; + +-- Creating index +CREATE UNIQUE INDEX medium_idx_gid ON medium (gid); + +-- Medium GID redirect +CREATE TABLE medium_gid_redirect ( -- replicate (verbose) + gid UUID NOT NULL, -- PK + new_id INTEGER NOT NULL, -- references medium.id + created TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +ALTER TABLE medium_gid_redirect ADD CONSTRAINT medium_gid_redirect_pkey PRIMARY KEY (gid); + +CREATE INDEX medium_gid_redirect_idx_new_id ON medium_gid_redirect (new_id); + +-------------------------------------------------------------------------------- +SELECT '20250408-mbs-13964-all.sql'; + + +CREATE OR REPLACE FUNCTION set_mediums_recordings_first_release_dates(medium_ids INTEGER[]) +RETURNS VOID AS $$ +BEGIN + PERFORM set_recordings_first_release_dates(( + SELECT array_agg(recording) + FROM track + WHERE track.medium = any(medium_ids) + )); + RETURN; +END; +$$ LANGUAGE 'plpgsql' STRICT; + +CREATE OR REPLACE FUNCTION a_upd_medium_mirror() +RETURNS trigger AS $$ +BEGIN + -- DO NOT modify any replicated tables in this function; it's used + -- by a trigger on mirrors. + IF NEW.release IS DISTINCT FROM OLD.release THEN + PERFORM set_mediums_recordings_first_release_dates(ARRAY[OLD.id]); + END IF; + RETURN NULL; +END; +$$ LANGUAGE 'plpgsql'; + +CREATE TRIGGER a_upd_medium AFTER UPDATE ON medium + FOR EACH ROW EXECUTE PROCEDURE a_upd_medium_mirror(); + +TRUNCATE recording_first_release_date; + +-------------------------------------------------------------------------------- +SELECT '20250425-mbs-13966.sql'; + + +CREATE OR REPLACE FUNCTION set_release_group_first_release_date(release_group_id INTEGER) +RETURNS VOID AS $$ +BEGIN + UPDATE release_group_meta SET first_release_date_year = first.year, + first_release_date_month = first.month, + first_release_date_day = first.day + FROM ( + SELECT rd.year, rd.month, rd.day + FROM release_group + LEFT JOIN release ON release.release_group = release_group.id + LEFT JOIN release_first_release_date rd ON (rd.release = release.id) + WHERE release_group.id = release_group_id + ORDER BY + rd.year NULLS LAST, + rd.month NULLS LAST, + rd.day NULLS LAST + LIMIT 1 + ) AS first + WHERE id = release_group_id; + INSERT INTO artist_release_group_pending_update VALUES (release_group_id); +END; +$$ LANGUAGE 'plpgsql'; + +UPDATE release_group_meta SET first_release_date_year = first.year, + first_release_date_month = first.month, + first_release_date_day = first.day + FROM ( + SELECT DISTINCT ON (release_group.id) + release_group.id AS release_group, rd.year, rd.month, rd.day + FROM release_group + LEFT JOIN release ON release.release_group = release_group.id + LEFT JOIN release_first_release_date rd ON (rd.release = release.id) + ORDER BY + release_group.id, + rd.year NULLS LAST, + rd.month NULLS LAST, + rd.day NULLS LAST + ) AS first +WHERE id = first.release_group + AND ( + first_release_date_year IS DISTINCT FROM first.year + OR first_release_date_month IS DISTINCT FROM first.month + OR first_release_date_day IS DISTINCT FROM first.day + ); + +COMMIT; diff --git a/mbslave/sql/updates/schema-change/30.master_and_standalone.sql b/mbslave/sql/updates/schema-change/30.master_and_standalone.sql new file mode 100644 index 0000000..d92cedb --- /dev/null +++ b/mbslave/sql/updates/schema-change/30.master_and_standalone.sql @@ -0,0 +1,74 @@ +-- Generated by CompileSchemaScripts.pl from: +-- 20241017-mbs-9253-master_and_standalone.sql +-- 20250425-mbs-13464-master_and_standalone.sql +-- 20250320-mbs-13768-fks.sql +\set ON_ERROR_STOP 1 +BEGIN; +SET search_path = musicbrainz, public; +SET LOCAL statement_timeout = 0; +-------------------------------------------------------------------------------- +SELECT '20241017-mbs-9253-master_and_standalone.sql'; + +SET search_path = musicbrainz; + + +ALTER TABLE artist_release_group + ADD CONSTRAINT artist_release_group_fk_artist + FOREIGN KEY (artist) + REFERENCES artist(id) + ON DELETE CASCADE; + +ALTER TABLE artist_release_group + ADD CONSTRAINT artist_release_group_fk_release_group + FOREIGN KEY (release_group) + REFERENCES release_group(id) + ON DELETE CASCADE; + +CREATE TRIGGER a_upd_release_group_primary_type AFTER UPDATE ON release_group_primary_type + FOR EACH ROW EXECUTE PROCEDURE a_upd_release_group_primary_type_mirror(); + +CREATE TRIGGER a_upd_release_group_secondary_type AFTER UPDATE ON release_group_secondary_type + FOR EACH ROW EXECUTE PROCEDURE a_upd_release_group_secondary_type_mirror(); + +CREATE CONSTRAINT TRIGGER apply_artist_release_group_pending_updates + AFTER UPDATE ON release_group_primary_type DEFERRABLE INITIALLY DEFERRED + FOR EACH ROW + WHEN (OLD.child_order IS DISTINCT FROM NEW.child_order) + EXECUTE PROCEDURE apply_artist_release_group_pending_updates(); + +CREATE CONSTRAINT TRIGGER apply_artist_release_group_pending_updates + AFTER UPDATE ON release_group_secondary_type DEFERRABLE INITIALLY DEFERRED + FOR EACH ROW + WHEN (OLD.child_order IS DISTINCT FROM NEW.child_order) + EXECUTE PROCEDURE apply_artist_release_group_pending_updates(); + +-------------------------------------------------------------------------------- +SELECT '20250425-mbs-13464-master_and_standalone.sql'; + +SET search_path = musicbrainz; + + +ALTER TABLE artist_release + ADD CONSTRAINT artist_release_fk_artist + FOREIGN KEY (artist) + REFERENCES artist(id) + ON DELETE CASCADE; + +ALTER TABLE artist_release + ADD CONSTRAINT artist_release_fk_release + FOREIGN KEY (release) + REFERENCES release(id) + ON DELETE CASCADE; + +-------------------------------------------------------------------------------- +SELECT '20250320-mbs-13768-fks.sql'; + +SET search_path = musicbrainz; + + +ALTER TABLE medium_gid_redirect + ADD CONSTRAINT medium_gid_redirect_fk_new_id + FOREIGN KEY (new_id) + REFERENCES medium(id); + +COMMIT; diff --git a/mbslave/sql/updates/schema-change/30.mirror_only.sql b/mbslave/sql/updates/schema-change/30.mirror_only.sql new file mode 100644 index 0000000..3cf3038 --- /dev/null +++ b/mbslave/sql/updates/schema-change/30.mirror_only.sql @@ -0,0 +1,37 @@ +-- Generated by CompileSchemaScripts.pl from: +-- 20241017-mbs-9253-mirror_only.sql +\set ON_ERROR_STOP 1 +BEGIN; +SET search_path = musicbrainz, public; +SET LOCAL statement_timeout = 0; +-------------------------------------------------------------------------------- +SELECT '20241017-mbs-9253-mirror_only.sql'; + + +DROP TRIGGER IF EXISTS a_upd_release_group_primary_type_mirror ON release_group_primary_type; + +CREATE TRIGGER a_upd_release_group_primary_type_mirror AFTER UPDATE ON release_group_primary_type + FOR EACH ROW EXECUTE PROCEDURE a_upd_release_group_primary_type_mirror(); + +DROP TRIGGER IF EXISTS a_upd_release_group_secondary_type_mirror ON release_group_secondary_type; + +CREATE TRIGGER a_upd_release_group_secondary_type_mirror AFTER UPDATE ON release_group_secondary_type + FOR EACH ROW EXECUTE PROCEDURE a_upd_release_group_secondary_type_mirror(); + +DROP TRIGGER IF EXISTS apply_artist_release_group_pending_updates_mirror ON release_group_primary_type; + +CREATE CONSTRAINT TRIGGER apply_artist_release_group_pending_updates_mirror + AFTER UPDATE ON release_group_primary_type DEFERRABLE INITIALLY DEFERRED + FOR EACH ROW + WHEN (OLD.child_order IS DISTINCT FROM NEW.child_order) + EXECUTE PROCEDURE apply_artist_release_group_pending_updates(); + +DROP TRIGGER IF EXISTS apply_artist_release_group_pending_updates_mirror ON release_group_secondary_type; + +CREATE CONSTRAINT TRIGGER apply_artist_release_group_pending_updates_mirror + AFTER UPDATE ON release_group_secondary_type DEFERRABLE INITIALLY DEFERRED + FOR EACH ROW + WHEN (OLD.child_order IS DISTINCT FROM NEW.child_order) + EXECUTE PROCEDURE apply_artist_release_group_pending_updates(); + +COMMIT; From 4237f63966c19ea1c9effbd2ed0e4ad797423e50 Mon Sep 17 00:00:00 2001 From: Harald Friessnegger Date: Mon, 26 May 2025 10:35:47 +0200 Subject: [PATCH 3/6] update upgrade scripts musicbrainz has fixed upgrade issues see https://github.com/metabrainz/musicbrainz-server/commit/c7b8c2925f210a754875a3b7b35315ed2d715c9a --- mbslave/sql/updates/20250425-mbs-13966.sql | 21 +++++++++++++++++++- mbslave/sql/updates/schema-change/30.all.sql | 21 +++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/mbslave/sql/updates/20250425-mbs-13966.sql b/mbslave/sql/updates/20250425-mbs-13966.sql index 3792cdb..3a56b05 100644 --- a/mbslave/sql/updates/20250425-mbs-13966.sql +++ b/mbslave/sql/updates/20250425-mbs-13966.sql @@ -25,6 +25,16 @@ BEGIN END; $$ LANGUAGE 'plpgsql'; +CREATE TEMPORARY TABLE tmp_release_first_release_date_2025_q2 ( + release INTEGER NOT NULL PRIMARY KEY, + year SMALLINT, + month SMALLINT, + day SMALLINT +) ON COMMIT DROP; + +INSERT INTO tmp_release_first_release_date_2025_q2 + SELECT * FROM get_release_first_release_date_rows('TRUE'); + UPDATE release_group_meta SET first_release_date_year = first.year, first_release_date_month = first.month, first_release_date_day = first.day @@ -33,7 +43,7 @@ UPDATE release_group_meta SET first_release_date_year = first.year, release_group.id AS release_group, rd.year, rd.month, rd.day FROM release_group LEFT JOIN release ON release.release_group = release_group.id - LEFT JOIN release_first_release_date rd ON (rd.release = release.id) + LEFT JOIN tmp_release_first_release_date_2025_q2 rd ON (rd.release = release.id) ORDER BY release_group.id, rd.year NULLS LAST, @@ -47,4 +57,13 @@ WHERE id = first.release_group OR first_release_date_day IS DISTINCT FROM first.day ); +-- Mirrors have a `a_upd_release_group_meta_mirror` trigger which inserts +-- updated `release_group_meta` IDs into `artist_release_group_pending_update`, +-- which in turn causes the associated entries in `artist_release_group` +-- to be updated. That should be a no-op here, because the schema 30 upgrade +-- already truncates `artist_release_group` (via dropping and recreating it) +-- before this runs; but clear it anyway to avoid a pointless calculation in +-- the `apply_artist_release_group_pending_updates` function. +TRUNCATE artist_release_group_pending_update; + COMMIT; diff --git a/mbslave/sql/updates/schema-change/30.all.sql b/mbslave/sql/updates/schema-change/30.all.sql index 2a2743d..c9d8553 100644 --- a/mbslave/sql/updates/schema-change/30.all.sql +++ b/mbslave/sql/updates/schema-change/30.all.sql @@ -376,6 +376,16 @@ BEGIN END; $$ LANGUAGE 'plpgsql'; +CREATE TEMPORARY TABLE tmp_release_first_release_date_2025_q2 ( + release INTEGER NOT NULL PRIMARY KEY, + year SMALLINT, + month SMALLINT, + day SMALLINT +) ON COMMIT DROP; + +INSERT INTO tmp_release_first_release_date_2025_q2 + SELECT * FROM get_release_first_release_date_rows('TRUE'); + UPDATE release_group_meta SET first_release_date_year = first.year, first_release_date_month = first.month, first_release_date_day = first.day @@ -384,7 +394,7 @@ UPDATE release_group_meta SET first_release_date_year = first.year, release_group.id AS release_group, rd.year, rd.month, rd.day FROM release_group LEFT JOIN release ON release.release_group = release_group.id - LEFT JOIN release_first_release_date rd ON (rd.release = release.id) + LEFT JOIN tmp_release_first_release_date_2025_q2 rd ON (rd.release = release.id) ORDER BY release_group.id, rd.year NULLS LAST, @@ -398,4 +408,13 @@ WHERE id = first.release_group OR first_release_date_day IS DISTINCT FROM first.day ); +-- Mirrors have a `a_upd_release_group_meta_mirror` trigger which inserts +-- updated `release_group_meta` IDs into `artist_release_group_pending_update`, +-- which in turn causes the associated entries in `artist_release_group` +-- to be updated. That should be a no-op here, because the schema 30 upgrade +-- already truncates `artist_release_group` (via dropping and recreating it) +-- before this runs; but clear it anyway to avoid a pointless calculation in +-- the `apply_artist_release_group_pending_updates` function. +TRUNCATE artist_release_group_pending_update; + COMMIT; From 4c601fcc596d287133372bf3de04057d791fb72c Mon Sep 17 00:00:00 2001 From: Harald Friessnegger Date: Tue, 27 May 2025 21:54:44 +0200 Subject: [PATCH 4/6] mention search_path problem and possible fix --- README.rst | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 211fd8a..25282b8 100644 --- a/README.rst +++ b/README.rst @@ -36,7 +36,7 @@ There are two ways to configure the application. export MBSLAVE_CONFIG=/usr/local/etc/mbslave.conf -2. Alternativelly, you can use using environment variables:: +2. Alternatively, you can use environment variables:: export MBSLAVE_DB_HOST=127.0.0.1 export MBSLAVE_DB_PORT=5432 @@ -46,6 +46,14 @@ There are two ways to configure the application. export MBSLAVE_DB_ADMIN_USER=postgres export MBSLAVE_DB_ADMIN_PASSWORD=XXX +Be aware, that configuring a different value for `db.user` or `schemas.musicbrainz` might require to configure a +custom `search_path` for the user (or database): By default, postgres sets the search_path to `"$user", public`. +If both is `musicbrainz` everything works, if they differ, you might run into errors when running `mbslave sync` as +triggers can't lookup functions properly. One way to solve this is to set the search path for your custom user to the +schema configured for musicbrainz:: + + ALTER USER myuser SET search_path TO musicbrainz, public; + Database Setup ============== From e2c2bdef48aec749fabe4f671c5e229576cb3ddf Mon Sep 17 00:00:00 2001 From: Harald Friessnegger Date: Wed, 20 May 2026 14:46:20 +0200 Subject: [PATCH 5/6] update to schema v31 see https://blog.metabrainz.org/2026/05/12/musicbrainz-database-schema-change-release-2026-05-11-with-upgrade-instructions/ for change notes --- CHANGELOG.rst | 5 + README.rst | 10 +- mbslave/__init__.py | 2 +- mbslave/sql/CreateAllReplicationTriggers2.sql | 7 + mbslave/sql/CreateConstraints.sql | 10 ++ .../sql/CreateCustomReplicationTriggers2.sql | 10 ++ mbslave/sql/CreateFunctions.sql | 43 +++++ mbslave/sql/CreateReplicationTriggers2.sql | 8 + mbslave/sql/CreateTables.sql | 7 +- mbslave/sql/CreateViews.sql | 18 ++ mbslave/sql/DropAllReplicationTriggers2.sql | 7 + .../sql/DropCustomReplicationTriggers2.sql | 4 + mbslave/sql/DropFunctions.sql | 5 + mbslave/sql/DropMirrorOnlyTriggers.sql | 1 + mbslave/sql/DropReplicationTriggers2.sql | 2 + mbslave/sql/DropTriggers.sql | 1 + mbslave/sql/DropViews.sql | 2 + mbslave/sql/dbmirror2/README | 6 +- mbslave/sql/dbmirror2/RefreshColumnInfo.sql | 16 -- mbslave/sql/dbmirror2/ReplicationSetup.sql | 54 ------ .../{MasterSetup.sql => dbmirror2.sql} | 107 +++++------ .../sql/updates/20260121-mbs-14092-fks.sql | 21 +++ mbslave/sql/updates/20260121-mbs-14092.sql | 86 +++++++++ mbslave/sql/updates/20260212-mbs-14252.sql | 8 + mbslave/sql/updates/20260422-mbs-6551-all.sql | 19 ++ ...0260422-mbs-6551-master_and_standalone.sql | 14 ++ .../sql/updates/20260502-search-756-all.sql | 48 +++++ .../20260502-search-756-master_only.sql | 118 +++++++++++++ .../20260507-search-756-mirror_only.sql | 113 ++++++++++++ .../20260507-search-756-standalone_only.sql | 155 ++++++++++++++++ mbslave/sql/updates/schema-change/31.all.sql | 167 ++++++++++++++++++ .../31.master_and_standalone.sql | 41 +++++ .../updates/schema-change/31.master_only.sql | 124 +++++++++++++ .../updates/schema-change/31.mirror_only.sql | 119 +++++++++++++ .../schema-change/31.standalone_only.sql | 161 +++++++++++++++++ 35 files changed, 1381 insertions(+), 138 deletions(-) create mode 100644 mbslave/sql/CreateAllReplicationTriggers2.sql create mode 100644 mbslave/sql/CreateCustomReplicationTriggers2.sql create mode 100644 mbslave/sql/DropAllReplicationTriggers2.sql create mode 100644 mbslave/sql/DropCustomReplicationTriggers2.sql delete mode 100644 mbslave/sql/dbmirror2/RefreshColumnInfo.sql delete mode 100644 mbslave/sql/dbmirror2/ReplicationSetup.sql rename mbslave/sql/dbmirror2/{MasterSetup.sql => dbmirror2.sql} (68%) create mode 100644 mbslave/sql/updates/20260121-mbs-14092-fks.sql create mode 100644 mbslave/sql/updates/20260121-mbs-14092.sql create mode 100644 mbslave/sql/updates/20260212-mbs-14252.sql create mode 100644 mbslave/sql/updates/20260422-mbs-6551-all.sql create mode 100644 mbslave/sql/updates/20260422-mbs-6551-master_and_standalone.sql create mode 100644 mbslave/sql/updates/20260502-search-756-all.sql create mode 100644 mbslave/sql/updates/20260502-search-756-master_only.sql create mode 100644 mbslave/sql/updates/20260507-search-756-mirror_only.sql create mode 100644 mbslave/sql/updates/20260507-search-756-standalone_only.sql create mode 100644 mbslave/sql/updates/schema-change/31.all.sql create mode 100644 mbslave/sql/updates/schema-change/31.master_and_standalone.sql create mode 100644 mbslave/sql/updates/schema-change/31.master_only.sql create mode 100644 mbslave/sql/updates/schema-change/31.mirror_only.sql create mode 100644 mbslave/sql/updates/schema-change/31.standalone_only.sql diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a036bf9..eefef2c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,8 @@ +Version 31.0.0 +============== + +- Schema change 31. + Version 30.0.0 ============== diff --git a/README.rst b/README.rst index 25282b8..6ee3279 100644 --- a/README.rst +++ b/README.rst @@ -74,7 +74,7 @@ you are doing. Database Replication ==================== -You can also keep the database up-to-date by applying incrementa changes. +You can also keep the database up-to-date by applying incremental changes. You need get an API token from the `MetaBrainz website `__ and you need to either add it to `mbslave.conf` or set the ``MBSLAVE_MUSICBRAINZ_TOKEN`` environment variable. @@ -90,6 +90,14 @@ When the MusicBrainz database schema changes, the replication will stop working. This is usually announced on the `MusicBrainz blog `__. When it happens, you need to upgrade the database. +Release 2026-05-11 (31) +~~~~~~~~~~~~~~~~~~~~~~~ + +Run the upgrade scripts:: + + mbslave psql -f updates/schema-change/31.all.sql + echo 'UPDATE replication_control SET current_schema_sequence = 31;' | mbslave psql + Release 2025-05-19 (30) ~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/mbslave/__init__.py b/mbslave/__init__.py index 8cb7180..e1d0aae 100644 --- a/mbslave/__init__.py +++ b/mbslave/__init__.py @@ -1,4 +1,4 @@ # Copyright (C) 2013 Lukas Lalinsky # Distributed under the MIT license, see the LICENSE file for details. -__version__ = "30.0.0" +__version__ = "31.0.0" diff --git a/mbslave/sql/CreateAllReplicationTriggers2.sql b/mbslave/sql/CreateAllReplicationTriggers2.sql new file mode 100644 index 0000000..e904cf1 --- /dev/null +++ b/mbslave/sql/CreateAllReplicationTriggers2.sql @@ -0,0 +1,7 @@ +-- Helper script to create all dbmirror2 replication triggers. +\ir CreateReplicationTriggers2.sql +\ir caa/CreateReplicationTriggers2.sql +\ir documentation/CreateReplicationTriggers2.sql +\ir statistics/CreateReplicationTriggers2.sql +\ir eaa/CreateReplicationTriggers2.sql +\ir wikidocs/CreateReplicationTriggers2.sql diff --git a/mbslave/sql/CreateConstraints.sql b/mbslave/sql/CreateConstraints.sql index 5df4283..a9a3bd7 100644 --- a/mbslave/sql/CreateConstraints.sql +++ b/mbslave/sql/CreateConstraints.sql @@ -145,6 +145,7 @@ ALTER TABLE series_type ADD CONSTRAINT allowed_series_entity_type 'recording', 'release', 'release_group', + 'series', 'work' ) ); @@ -173,6 +174,15 @@ ADD CONSTRAINT group_type_implies_null_gender CHECK ( ALTER TABLE release_label ADD CHECK (catalog_number IS NOT NULL OR label IS NOT NULL); +ALTER TABLE release_label + ADD CONSTRAINT no_empty_string_catalog_number + CHECK (catalog_number != ''); + +ALTER TABLE release_label + ADD CONSTRAINT release_label_uniq + UNIQUE NULLS NOT DISTINCT (release, label, catalog_number) + DEFERRABLE INITIALLY DEFERRED; + ALTER TABLE artist ADD CONSTRAINT artist_va_check CHECK (id <> 1 OR (type = 3 AND diff --git a/mbslave/sql/CreateCustomReplicationTriggers2.sql b/mbslave/sql/CreateCustomReplicationTriggers2.sql new file mode 100644 index 0000000..8acf756 --- /dev/null +++ b/mbslave/sql/CreateCustomReplicationTriggers2.sql @@ -0,0 +1,10 @@ +\set ON_ERROR_STOP 1 + +BEGIN; + +CREATE TRIGGER sanitize_dbmirror2_editor_data + BEFORE INSERT ON dbmirror2.pending_data + FOR EACH ROW WHEN (NEW.tablename = 'musicbrainz.editor') + EXECUTE PROCEDURE sanitize_dbmirror2_editor_data(); + +COMMIT; diff --git a/mbslave/sql/CreateFunctions.sql b/mbslave/sql/CreateFunctions.sql index 047ccfa..51f9823 100644 --- a/mbslave/sql/CreateFunctions.sql +++ b/mbslave/sql/CreateFunctions.sql @@ -2247,4 +2247,47 @@ BEGIN END; $$ LANGUAGE plpgsql; +----------------------------------------------------------------------- +-- Editor data sanitization +----------------------------------------------------------------------- + +CREATE OR REPLACE FUNCTION sanitize_editor(e editor) RETURNS editor AS $$ + SELECT ROW( + e.id, + e.name, + 0::INTEGER, + ''::VARCHAR(64), + NULL::VARCHAR(255), + NULL::TEXT, + e.member_since, + e.email_confirm_date, + now()::TIMESTAMP WITH TIME ZONE, + e.last_updated, + NULL::DATE, + NULL::INTEGER, + NULL::INTEGER, + '{CLEARTEXT}mb'::VARCHAR(128), + md5(e.name || ':musicbrainz.org:mb')::CHAR(32), + e.deleted + )::editor +$$ LANGUAGE sql STABLE PARALLEL SAFE; + +-- Adding `STRICT` on `sanitize_editor` would prevent inlining/optimization +-- when called via the `editor_sanitized` view. +CREATE OR REPLACE FUNCTION sanitize_editor_strict(e editor) RETURNS editor AS $$ + SELECT sanitize_editor(e) +$$ LANGUAGE sql STABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sanitize_dbmirror2_editor_data() RETURNS trigger AS $$ +BEGIN + NEW.olddata = row_to_json(sanitize_editor_strict(json_populate_record(NULL::editor, NEW.olddata))); + NEW.newdata = row_to_json(sanitize_editor_strict(json_populate_record(NULL::editor, NEW.newdata))); + IF NEW.op = 'u' AND NEW.olddata::JSONB = NEW.newdata::JSONB THEN + -- Only sanitized columns have changed. No need to log the update. + RETURN NULL; + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + -- vi: set ts=4 sw=4 et : diff --git a/mbslave/sql/CreateReplicationTriggers2.sql b/mbslave/sql/CreateReplicationTriggers2.sql index 28ff024..fcc7e1b 100644 --- a/mbslave/sql/CreateReplicationTriggers2.sql +++ b/mbslave/sql/CreateReplicationTriggers2.sql @@ -143,6 +143,10 @@ CREATE TRIGGER reptg2_country_area AFTER INSERT OR DELETE OR UPDATE ON country_area FOR EACH ROW EXECUTE PROCEDURE dbmirror2.recordchange(); +CREATE TRIGGER reptg2_editor +AFTER INSERT OR DELETE OR UPDATE ON editor +FOR EACH ROW EXECUTE PROCEDURE dbmirror2.recordchange(); + CREATE TRIGGER reptg2_editor_collection_type AFTER INSERT OR DELETE OR UPDATE ON editor_collection_type FOR EACH ROW EXECUTE PROCEDURE dbmirror2.recordchange(); @@ -915,6 +919,10 @@ CREATE TRIGGER reptg2_recording_attribute_type_allowed_value AFTER INSERT OR DELETE OR UPDATE ON recording_attribute_type_allowed_value FOR EACH ROW EXECUTE PROCEDURE dbmirror2.recordchange(); +CREATE TRIGGER reptg2_recording_first_release_date +AFTER INSERT OR DELETE OR UPDATE ON recording_first_release_date +FOR EACH ROW EXECUTE PROCEDURE dbmirror2.recordchange(); + CREATE TRIGGER reptg2_recording_gid_redirect AFTER INSERT OR DELETE OR UPDATE ON recording_gid_redirect FOR EACH ROW EXECUTE PROCEDURE dbmirror2.recordchange(); diff --git a/mbslave/sql/CreateTables.sql b/mbslave/sql/CreateTables.sql index 73cca4b..2776bab 100644 --- a/mbslave/sql/CreateTables.sql +++ b/mbslave/sql/CreateTables.sql @@ -671,8 +671,7 @@ CREATE TABLE edit_url url INTEGER NOT NULL -- PK, references url.id CASCADE ); -CREATE TABLE editor -( +CREATE TABLE editor ( -- replicate id SERIAL, name VARCHAR(64) NOT NULL, privs INTEGER DEFAULT 0, @@ -946,7 +945,7 @@ CREATE TABLE release_first_release_date ( day SMALLINT ); -CREATE TABLE recording_first_release_date ( +CREATE TABLE recording_first_release_date ( -- replicate recording INTEGER NOT NULL, -- PK, references recording.id CASCADE year SMALLINT, month SMALLINT, @@ -1176,7 +1175,6 @@ CREATE TABLE isrc ( -- replicate (verbose) id SERIAL, recording INTEGER NOT NULL, -- references recording.id isrc CHAR(12) NOT NULL CHECK (isrc ~ E'^[A-Z]{2}[A-Z0-9]{3}[0-9]{7}$'), - source SMALLINT, edits_pending INTEGER NOT NULL DEFAULT 0 CHECK (edits_pending >= 0), created TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); @@ -1185,7 +1183,6 @@ CREATE TABLE iswc ( -- replicate (verbose) id SERIAL NOT NULL, work INTEGER NOT NULL, -- references work.id iswc CHARACTER(15) CHECK (iswc ~ E'^T-?\\d{3}.?\\d{3}.?\\d{3}[-.]?\\d$'), - source SMALLINT, edits_pending INTEGER NOT NULL DEFAULT 0, created TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now() ); diff --git a/mbslave/sql/CreateViews.sql b/mbslave/sql/CreateViews.sql index 62639f9..185e0a3 100644 --- a/mbslave/sql/CreateViews.sql +++ b/mbslave/sql/CreateViews.sql @@ -83,6 +83,20 @@ CREATE OR REPLACE VIEW release_group_series AS LEFT OUTER JOIN link_attribute_text_value latv ON (latv.attribute_type = 788 AND latv.link = l.id) ORDER BY series, link_order; +CREATE OR REPLACE VIEW series_series AS + SELECT entity0 AS series_part, + entity1 AS series, + lss.id AS relationship, + link_order, + lss.link, + COALESCE(text_value, '') AS text_value + FROM l_series_series lss + JOIN series s ON s.id = lss.entity1 + JOIN link l ON l.id = lss.link + JOIN link_type lt ON (lt.id = l.link_type AND lt.gid = '8da75c99-46ff-373c-9d31-276ca8fa8cc3') + LEFT OUTER JOIN link_attribute_text_value latv ON (latv.attribute_type = 788 AND latv.link = l.id) + ORDER BY series, link_order; + CREATE OR REPLACE VIEW work_series AS SELECT entity1 AS work, entity0 AS series, @@ -107,6 +121,10 @@ CREATE OR REPLACE VIEW medium_track_durations AS JOIN track ON track.medium = medium.id GROUP BY medium.id; +CREATE OR REPLACE VIEW editor_sanitized AS + SELECT (sanitize_editor(editor)).* + FROM editor; + COMMIT; -- vi: set ts=4 sw=4 et : diff --git a/mbslave/sql/DropAllReplicationTriggers2.sql b/mbslave/sql/DropAllReplicationTriggers2.sql new file mode 100644 index 0000000..22e4dc5 --- /dev/null +++ b/mbslave/sql/DropAllReplicationTriggers2.sql @@ -0,0 +1,7 @@ +-- Helper script to drop all dbmirror2 replication triggers. +\ir DropReplicationTriggers2.sql +\ir caa/DropReplicationTriggers2.sql +\ir documentation/DropReplicationTriggers2.sql +\ir statistics/DropReplicationTriggers2.sql +\ir eaa/DropReplicationTriggers2.sql +\ir wikidocs/DropReplicationTriggers2.sql diff --git a/mbslave/sql/DropCustomReplicationTriggers2.sql b/mbslave/sql/DropCustomReplicationTriggers2.sql new file mode 100644 index 0000000..9f7e319 --- /dev/null +++ b/mbslave/sql/DropCustomReplicationTriggers2.sql @@ -0,0 +1,4 @@ +-- Automatically generated, do not edit. +\unset ON_ERROR_STOP + +DROP TRIGGER IF EXISTS sanitize_dbmirror2_editor_data ON dbmirror2.pending_data; diff --git a/mbslave/sql/DropFunctions.sql b/mbslave/sql/DropFunctions.sql index 0bb2472..8f108bc 100644 --- a/mbslave/sql/DropFunctions.sql +++ b/mbslave/sql/DropFunctions.sql @@ -35,6 +35,7 @@ DROP FUNCTION a_upd_alternative_release_or_track(); DROP FUNCTION a_upd_edit(); DROP FUNCTION a_upd_instrument(); DROP FUNCTION a_upd_l_area_area_mirror(); +DROP FUNCTION a_upd_medium_mirror(); DROP FUNCTION a_upd_recording(); DROP FUNCTION a_upd_release(); DROP FUNCTION a_upd_release_event(); @@ -104,6 +105,10 @@ DROP FUNCTION remove_unused_links(); DROP FUNCTION remove_unused_url(); DROP FUNCTION replace_old_sub_on_add(); DROP FUNCTION restore_collection_sub_on_public(); +DROP FUNCTION sanitize_dbmirror2_editor_data(); +DROP FUNCTION sanitize_editor(e editor); +DROP FUNCTION sanitize_editor_strict(e editor); +DROP FUNCTION set_mediums_recordings_first_release_dates(medium_ids INTEGER[]); DROP FUNCTION set_recordings_first_release_dates(recording_ids INTEGER[]); DROP FUNCTION set_release_first_release_date(release_id INTEGER); DROP FUNCTION set_release_group_first_release_date(release_group_id INTEGER); diff --git a/mbslave/sql/DropMirrorOnlyTriggers.sql b/mbslave/sql/DropMirrorOnlyTriggers.sql index e7a9b17..2ffd455 100644 --- a/mbslave/sql/DropMirrorOnlyTriggers.sql +++ b/mbslave/sql/DropMirrorOnlyTriggers.sql @@ -4,6 +4,7 @@ DROP TRIGGER IF EXISTS a_ins_l_area_area_mirror ON l_area_area; DROP TRIGGER IF EXISTS a_upd_l_area_area_mirror ON l_area_area; DROP TRIGGER IF EXISTS a_del_l_area_area_mirror ON l_area_area; +DROP TRIGGER IF EXISTS a_upd_medium ON medium; DROP TRIGGER IF EXISTS a_ins_release_mirror ON release; DROP TRIGGER IF EXISTS a_upd_release_mirror ON release; DROP TRIGGER IF EXISTS a_del_release_mirror ON release; diff --git a/mbslave/sql/DropReplicationTriggers2.sql b/mbslave/sql/DropReplicationTriggers2.sql index ed646bf..48b568c 100644 --- a/mbslave/sql/DropReplicationTriggers2.sql +++ b/mbslave/sql/DropReplicationTriggers2.sql @@ -36,6 +36,7 @@ DROP TRIGGER IF EXISTS reptg2_artist_type ON artist_type; DROP TRIGGER IF EXISTS reptg2_cdtoc ON cdtoc; DROP TRIGGER IF EXISTS reptg2_cdtoc_raw ON cdtoc_raw; DROP TRIGGER IF EXISTS reptg2_country_area ON country_area; +DROP TRIGGER IF EXISTS reptg2_editor ON editor; DROP TRIGGER IF EXISTS reptg2_editor_collection_type ON editor_collection_type; DROP TRIGGER IF EXISTS reptg2_event ON event; DROP TRIGGER IF EXISTS reptg2_event_alias ON event_alias; @@ -229,6 +230,7 @@ DROP TRIGGER IF EXISTS reptg2_recording_annotation ON recording_annotation; DROP TRIGGER IF EXISTS reptg2_recording_attribute ON recording_attribute; DROP TRIGGER IF EXISTS reptg2_recording_attribute_type ON recording_attribute_type; DROP TRIGGER IF EXISTS reptg2_recording_attribute_type_allowed_value ON recording_attribute_type_allowed_value; +DROP TRIGGER IF EXISTS reptg2_recording_first_release_date ON recording_first_release_date; DROP TRIGGER IF EXISTS reptg2_recording_gid_redirect ON recording_gid_redirect; DROP TRIGGER IF EXISTS reptg2_recording_meta ON recording_meta; DROP TRIGGER IF EXISTS reptg2_recording_tag ON recording_tag; diff --git a/mbslave/sql/DropTriggers.sql b/mbslave/sql/DropTriggers.sql index 063dab1..9920e57 100644 --- a/mbslave/sql/DropTriggers.sql +++ b/mbslave/sql/DropTriggers.sql @@ -152,6 +152,7 @@ DROP TRIGGER IF EXISTS b_upd_link_attribute_text_value ON link_attribute_text_va DROP TRIGGER IF EXISTS b_upd_link_attribute_type ON link_attribute_type; DROP TRIGGER IF EXISTS b_upd_link_type ON link_type; DROP TRIGGER IF EXISTS b_upd_link_type_attribute_type ON link_type_attribute_type; +DROP TRIGGER IF EXISTS a_upd_medium ON medium; DROP TRIGGER IF EXISTS b_upd_medium ON medium; DROP TRIGGER IF EXISTS b_upd_medium_cdtoc ON medium_cdtoc; DROP TRIGGER IF EXISTS b_upd_mood ON mood; diff --git a/mbslave/sql/DropViews.sql b/mbslave/sql/DropViews.sql index 3b532a0..3fb3c77 100644 --- a/mbslave/sql/DropViews.sql +++ b/mbslave/sql/DropViews.sql @@ -2,10 +2,12 @@ \unset ON_ERROR_STOP DROP VIEW artist_series; +DROP VIEW editor_sanitized; DROP VIEW event_series; DROP VIEW medium_track_durations; DROP VIEW recording_series; DROP VIEW release_event; DROP VIEW release_group_series; DROP VIEW release_series; +DROP VIEW series_series; DROP VIEW work_series; diff --git a/mbslave/sql/dbmirror2/README b/mbslave/sql/dbmirror2/README index 1598c4a..c8ab121 100644 --- a/mbslave/sql/dbmirror2/README +++ b/mbslave/sql/dbmirror2/README @@ -1,6 +1,6 @@ -ReplicationSetup.sql and MasterSetup.sql are copied from -https://github.com/metabrainz/dbmirror2, commit eda9923. (These are the only -two files we need, and submodules are "difficult.") +dbmirror2.sql is copied from https://github.com/metabrainz/dbmirror2, +commit 5f26872. (This is the only file we need, and submodules are +"difficult.") This is not a fork; please do not make any changes to the files here without first submitting them upstream to dbmirror2. They should exactly mirror the diff --git a/mbslave/sql/dbmirror2/RefreshColumnInfo.sql b/mbslave/sql/dbmirror2/RefreshColumnInfo.sql deleted file mode 100644 index ded9dde..0000000 --- a/mbslave/sql/dbmirror2/RefreshColumnInfo.sql +++ /dev/null @@ -1,16 +0,0 @@ -\set ON_ERROR_STOP 1 - -BEGIN; - -DO $$ -BEGIN - PERFORM 1 FROM pg_matviews - WHERE schemaname = 'dbmirror2' - AND matviewname = 'column_info'; - - IF FOUND THEN - REFRESH MATERIALIZED VIEW dbmirror2.column_info; - END IF; -END $$; - -COMMIT; diff --git a/mbslave/sql/dbmirror2/ReplicationSetup.sql b/mbslave/sql/dbmirror2/ReplicationSetup.sql deleted file mode 100644 index 659a7bf..0000000 --- a/mbslave/sql/dbmirror2/ReplicationSetup.sql +++ /dev/null @@ -1,54 +0,0 @@ --- Copyright (C) 2021 MetaBrainz Foundation --- Licensed under the GPL version 2, or (at your option) any later version: --- http://www.gnu.org/licenses/gpl-2.0.txt - -BEGIN; - --- The pending_keys tables serves two purposes: --- 1. Stores the primary keys associated with each table. --- 2. Allows quickly checking if a particular table has changed --- in the packet. -CREATE TABLE dbmirror2.pending_keys ( - tablename TEXT, - keys TEXT[] NOT NULL -); - -ALTER TABLE dbmirror2.pending_keys - ADD CONSTRAINT pending_keys_pkey - PRIMARY KEY (tablename); - -CREATE TABLE dbmirror2.pending_ts ( - xid BIGINT, - ts TIMESTAMP WITH TIME ZONE NOT NULL -); - -ALTER TABLE dbmirror2.pending_ts - ADD CONSTRAINT pending_ts_pkey - PRIMARY KEY (xid); - -CREATE TABLE dbmirror2.pending_data ( - seqid BIGSERIAL, - tablename TEXT NOT NULL CONSTRAINT tablename_exists CHECK (to_regclass(tablename) IS NOT NULL), - op "char" NOT NULL CONSTRAINT op_in_diu CHECK (op IN ('d', 'i', 'u')), - xid BIGINT NOT NULL, - -- We use JSON over JSONB because there is no need to perform - -- operations on the data; this additionally lets us store the - -- keys in column-order, which makes the packets much easier - -- to read while debugging. - olddata JSON CONSTRAINT olddata_is_null_for_inserts CHECK ((olddata IS NULL) = (op = 'i')), - newdata JSON CONSTRAINT newdata_is_null_for_deletes CHECK ((newdata IS NULL) = (op = 'd')), - oldctid TID, - trgdepth INTEGER -); - -ALTER TABLE dbmirror2.pending_data - ADD CONSTRAINT pending_data_pkey - PRIMARY KEY (seqid); - -CREATE INDEX pending_data_idx_xid_seqid - ON dbmirror2.pending_data (xid, seqid); - -CREATE INDEX pending_data_idx_oldctid_xid - ON dbmirror2.pending_data (oldctid, xid); - -COMMIT; diff --git a/mbslave/sql/dbmirror2/MasterSetup.sql b/mbslave/sql/dbmirror2/dbmirror2.sql similarity index 68% rename from mbslave/sql/dbmirror2/MasterSetup.sql rename to mbslave/sql/dbmirror2/dbmirror2.sql index d4c6481..71d28ac 100644 --- a/mbslave/sql/dbmirror2/MasterSetup.sql +++ b/mbslave/sql/dbmirror2/dbmirror2.sql @@ -4,41 +4,6 @@ BEGIN; --- The column_info view allows us to determine whether a column in a given --- table is part of its primary key, and gives us its position too. --- --- This view must be refreshed after every schema change; an event trigger --- in MasterEventTriggerSetup.sql can handle this automatically. -CREATE MATERIALIZED VIEW dbmirror2.column_info ( - table_schema, - table_name, - column_name, - position, - is_primary -) AS - SELECT - c.table_schema, - c.table_name, - c.column_name, - c.ordinal_position, - coalesce(( - SELECT TRUE - FROM information_schema.key_column_usage kcu - NATURAL JOIN information_schema.table_constraints tc - WHERE kcu.table_schema = c.table_schema - AND kcu.table_name = c.table_name - AND kcu.column_name = c.column_name - AND tc.constraint_type = 'PRIMARY KEY' - ), FALSE) AS is_primary - FROM information_schema.columns c - NATURAL JOIN information_schema.tables t - WHERE t.table_type = 'BASE TABLE' - AND t.table_schema NOT IN ('dbmirror2', 'information_schema', 'pg_catalog') -WITH DATA; - -CREATE INDEX column_info_idx - ON dbmirror2.column_info (table_schema, table_name, is_primary); - CREATE FUNCTION dbmirror2.recordchange() RETURNS trigger AS $$ DECLARE @@ -46,9 +11,6 @@ DECLARE -- pending_data.tablename and pending_keys.tablename _tablename TEXT; keys TEXT[]; - jsonquery TEXT; - olddata JSON; - newdata JSON; -- prefixed with 'x' to avoid conflict with column name in queries xoldctid TID; nextseqid BIGINT; @@ -75,29 +37,11 @@ BEGIN VALUES (txid_current(), transaction_timestamp()) ON CONFLICT DO NOTHING; - jsonquery := ( - SELECT format( - 'SELECT json_build_object(%1$s)', - array_to_string( - array_agg( - format('%1$L, ($1).%1$I', column_name) ORDER BY position - ), - ', ' - ) - ) - FROM dbmirror2.column_info - WHERE table_schema = TG_TABLE_SCHEMA AND table_name = TG_TABLE_NAME - ); - IF TG_OP != 'INSERT' THEN - EXECUTE jsonquery INTO olddata USING OLD; - xoldctid := OLD.ctid; END IF; IF TG_OP != 'DELETE' THEN - EXECUTE jsonquery INTO newdata USING NEW; - -- Detect out-of-order operations caused by cascading triggers. -- -- When row-level AFTER triggers are cascaded, the innermost trigger @@ -154,8 +98,8 @@ BEGIN _tablename, lower(left(TG_OP, 1)), txid_current(), - olddata, - newdata, + row_to_json(OLD), + row_to_json(NEW), xoldctid, pg_trigger_depth() ); @@ -164,4 +108,51 @@ BEGIN END; $$ LANGUAGE plpgsql; +-- The pending_keys tables serves two purposes: +-- 1. Stores the primary keys associated with each table. +-- 2. Allows quickly checking if a particular table has changed +-- in the packet. +CREATE TABLE dbmirror2.pending_keys ( + tablename TEXT, + keys TEXT[] NOT NULL +); + +ALTER TABLE dbmirror2.pending_keys + ADD CONSTRAINT pending_keys_pkey + PRIMARY KEY (tablename); + +CREATE TABLE dbmirror2.pending_ts ( + xid BIGINT, + ts TIMESTAMP WITH TIME ZONE NOT NULL +); + +ALTER TABLE dbmirror2.pending_ts + ADD CONSTRAINT pending_ts_pkey + PRIMARY KEY (xid); + +CREATE TABLE dbmirror2.pending_data ( + seqid BIGSERIAL, + tablename TEXT NOT NULL CONSTRAINT tablename_exists CHECK (to_regclass(tablename) IS NOT NULL), + op "char" NOT NULL CONSTRAINT op_in_diu CHECK (op IN ('d', 'i', 'u')), + xid BIGINT NOT NULL, + -- We use JSON over JSONB because there is no need to perform + -- operations on the data; this additionally lets us store the + -- keys in column-order, which makes the packets much easier + -- to read while debugging. + olddata JSON CONSTRAINT olddata_is_null_for_inserts CHECK ((olddata IS NULL) = (op = 'i')), + newdata JSON CONSTRAINT newdata_is_null_for_deletes CHECK ((newdata IS NULL) = (op = 'd')), + oldctid TID, + trgdepth INTEGER +); + +ALTER TABLE dbmirror2.pending_data + ADD CONSTRAINT pending_data_pkey + PRIMARY KEY (seqid); + +CREATE INDEX pending_data_idx_xid_seqid + ON dbmirror2.pending_data (xid, seqid); + +CREATE INDEX pending_data_idx_oldctid_xid + ON dbmirror2.pending_data (oldctid, xid); + COMMIT; diff --git a/mbslave/sql/updates/20260121-mbs-14092-fks.sql b/mbslave/sql/updates/20260121-mbs-14092-fks.sql new file mode 100644 index 0000000..709e947 --- /dev/null +++ b/mbslave/sql/updates/20260121-mbs-14092-fks.sql @@ -0,0 +1,21 @@ +\set ON_ERROR_STOP 1 +BEGIN; + +------------------ +-- constraints -- +------------------ + +ALTER TABLE series_type ADD CONSTRAINT allowed_series_entity_type + CHECK ( + entity_type IN ( + 'artist', + 'event', + 'recording', + 'release', + 'release_group', + 'series', + 'work' + ) + ); + +COMMIT; diff --git a/mbslave/sql/updates/20260121-mbs-14092.sql b/mbslave/sql/updates/20260121-mbs-14092.sql new file mode 100644 index 0000000..d0889d5 --- /dev/null +++ b/mbslave/sql/updates/20260121-mbs-14092.sql @@ -0,0 +1,86 @@ +\set ON_ERROR_STOP 1 +BEGIN; + +\set SERIES_PART_OF_SERIES_ID '1307' + +\set SERIES_PART_OF_SERIES_GID '''8fe04b66-fe39-40ce-a28f-76b816d3f55a''' + +-- id from link_attribute_type where name = 'number' +\set LINK_ATTRIBUTE_TYPE_NUMBER_ID '788' + +----------------------- +-- CREATE NEW VIEWS -- +----------------------- + +CREATE OR REPLACE VIEW series_series AS + SELECT entity0 AS series_part, + entity1 AS series, + lss.id AS relationship, + link_order, + lss.link, + COALESCE(text_value, '') AS text_value + FROM l_series_series lss + JOIN series s ON s.id = lss.entity1 + JOIN link l ON l.id = lss.link + JOIN link_type lt ON (lt.id = l.link_type AND lt.gid = '8da75c99-46ff-373c-9d31-276ca8fa8cc3') + LEFT OUTER JOIN link_attribute_text_value latv ON (latv.attribute_type = 788 AND latv.link = l.id) + ORDER BY series, link_order; + +------------------------- +-- INSERT INITIAL DATA -- +------------------------- + +-- Part-of-series rel +-- Already exists in production, but disabled (no description) + +-- We insert the rel where it does not exist (outside prod) +INSERT INTO link_type (id, gid, entity_type0, entity_type1, entity0_cardinality, + entity1_cardinality, name, description, link_phrase, + reverse_link_phrase, long_link_phrase) VALUES + ( + :SERIES_PART_OF_SERIES_ID, + :SERIES_PART_OF_SERIES_GID, + 'series', 'series', 0, 0, 'part of', + '', + 'part of', 'has parts', 'is a part of' + ) ON CONFLICT DO NOTHING; + +-- We add the description to enable the rel +UPDATE link_type + SET description = 'Indicates that the series is part of a series.' + WHERE gid = :SERIES_PART_OF_SERIES_GID; + +-- We insert the attribute and orderable type where they do not exist (outside prod) +INSERT INTO link_attribute_type (id, parent, root, child_order, gid, name, description, last_updated) VALUES + ( + :LINK_ATTRIBUTE_TYPE_NUMBER_ID, + NULL, + :LINK_ATTRIBUTE_TYPE_NUMBER_ID, + 0, + 'a59c5830-5ec7-38fe-9a21-c7ea54f6650a', + 'number', + 'This attribute indicates the number of an entity in a series.', + '2021-05-10 11:27:11.858659+00' + ) ON CONFLICT DO NOTHING; + +INSERT INTO link_type_attribute_type (link_type, attribute_type, min, max) VALUES + ( + :SERIES_PART_OF_SERIES_ID, + :LINK_ATTRIBUTE_TYPE_NUMBER_ID, + 0, + 1 + ) ON CONFLICT DO NOTHING; + +INSERT INTO orderable_link_type (link_type, direction) VALUES + (:SERIES_PART_OF_SERIES_ID, 2) ON CONFLICT DO NOTHING; + +ALTER TABLE series_type DROP CONSTRAINT IF EXISTS allowed_series_entity_type; + +INSERT INTO series_type (id, name, entity_type, parent, child_order, description, gid) VALUES + (16, 'Series series', 'series', NULL, 6, 'A series of series.', generate_uuid_v3('6ba7b8119dad11d180b400c04fd430c8', 'series_type16')), + (17, 'Series award', 'series', 16, 0, 'A series of series (such as podcasts or festivals) honoured by the same award.', generate_uuid_v3('6ba7b8119dad11d180b400c04fd430c8', 'series_type17')); + +\unset SERIES_PART_OF_SERIES_GID +\unset LINK_ATTRIBUTE_TYPE_NUMBER_ID + +COMMIT; diff --git a/mbslave/sql/updates/20260212-mbs-14252.sql b/mbslave/sql/updates/20260212-mbs-14252.sql new file mode 100644 index 0000000..fe993d5 --- /dev/null +++ b/mbslave/sql/updates/20260212-mbs-14252.sql @@ -0,0 +1,8 @@ +\set ON_ERROR_STOP 1 + +BEGIN; + +ALTER TABLE isrc DROP COLUMN source CASCADE; +ALTER TABLE iswc DROP COLUMN source CASCADE; + +COMMIT; diff --git a/mbslave/sql/updates/20260422-mbs-6551-all.sql b/mbslave/sql/updates/20260422-mbs-6551-all.sql new file mode 100644 index 0000000..7f6c640 --- /dev/null +++ b/mbslave/sql/updates/20260422-mbs-6551-all.sql @@ -0,0 +1,19 @@ +\set ON_ERROR_STOP 1 + +BEGIN; + +-- Prepare data for the `no_empty_string_catalog_number` constraint +-- in admin/sql/updates/20260422-mbs-6551-master_and_standalone.sql. +UPDATE release_label SET catalog_number = NULL WHERE catalog_number = ''; + +-- This is mainly present for standalone databases. "Remove release label" +-- edits were already manually entered for the few duplicates that existed +-- in production. +DELETE FROM release_label dupe +USING release_label orig +WHERE dupe.release = orig.release + AND dupe.label IS NOT DISTINCT FROM orig.label + AND dupe.catalog_number IS NOT DISTINCT FROM orig.catalog_number + AND dupe.id > orig.id; + +COMMIT; diff --git a/mbslave/sql/updates/20260422-mbs-6551-master_and_standalone.sql b/mbslave/sql/updates/20260422-mbs-6551-master_and_standalone.sql new file mode 100644 index 0000000..b42f1bd --- /dev/null +++ b/mbslave/sql/updates/20260422-mbs-6551-master_and_standalone.sql @@ -0,0 +1,14 @@ +\set ON_ERROR_STOP 1 + +BEGIN; + +ALTER TABLE release_label + ADD CONSTRAINT no_empty_string_catalog_number + CHECK (catalog_number != ''); + +ALTER TABLE release_label + ADD CONSTRAINT release_label_uniq + UNIQUE NULLS NOT DISTINCT (release, label, catalog_number) + DEFERRABLE INITIALLY DEFERRED; + +COMMIT; diff --git a/mbslave/sql/updates/20260502-search-756-all.sql b/mbslave/sql/updates/20260502-search-756-all.sql new file mode 100644 index 0000000..bef12d2 --- /dev/null +++ b/mbslave/sql/updates/20260502-search-756-all.sql @@ -0,0 +1,48 @@ +\set ON_ERROR_STOP 1 + +BEGIN; + +CREATE OR REPLACE FUNCTION sanitize_editor(e editor) RETURNS editor AS $$ + SELECT ROW( + e.id, + e.name, + 0::INTEGER, + ''::VARCHAR(64), + NULL::VARCHAR(255), + NULL::TEXT, + e.member_since, + e.email_confirm_date, + now()::TIMESTAMP WITH TIME ZONE, + e.last_updated, + NULL::DATE, + NULL::INTEGER, + NULL::INTEGER, + '{CLEARTEXT}mb'::VARCHAR(128), + md5(e.name || ':musicbrainz.org:mb')::CHAR(32), + e.deleted + )::editor +$$ LANGUAGE sql STABLE PARALLEL SAFE; + +-- Adding `STRICT` on `sanitize_editor` would prevent inlining/optimization +-- when called via the `editor_sanitized` view. +CREATE OR REPLACE FUNCTION sanitize_editor_strict(e editor) RETURNS editor AS $$ + SELECT sanitize_editor(e) +$$ LANGUAGE sql STABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sanitize_dbmirror2_editor_data() RETURNS trigger AS $$ +BEGIN + NEW.olddata = row_to_json(sanitize_editor_strict(json_populate_record(NULL::editor, NEW.olddata))); + NEW.newdata = row_to_json(sanitize_editor_strict(json_populate_record(NULL::editor, NEW.newdata))); + IF NEW.op = 'u' AND NEW.olddata::JSONB = NEW.newdata::JSONB THEN + -- Only sanitized columns have changed. No need to log the update. + RETURN NULL; + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE VIEW editor_sanitized AS + SELECT (sanitize_editor(editor)).* + FROM editor; + +COMMIT; diff --git a/mbslave/sql/updates/20260502-search-756-master_only.sql b/mbslave/sql/updates/20260502-search-756-master_only.sql new file mode 100644 index 0000000..7fdf35b --- /dev/null +++ b/mbslave/sql/updates/20260502-search-756-master_only.sql @@ -0,0 +1,118 @@ +\set ON_ERROR_STOP 1 + +BEGIN; + +DROP MATERIALIZED VIEW IF EXISTS dbmirror2.column_info CASCADE; +DROP EVENT TRIGGER IF EXISTS refresh_column_info; +DROP FUNCTION IF EXISTS dbmirror2.refresh_column_info(); + +CREATE OR REPLACE FUNCTION dbmirror2.recordchange() +RETURNS trigger AS $$ +DECLARE + -- prefixed with an underscore to disambiguate it from the column names + -- pending_data.tablename and pending_keys.tablename + _tablename TEXT; + keys TEXT[]; + -- prefixed with 'x' to avoid conflict with column name in queries + xoldctid TID; + nextseqid BIGINT; + -- out-of-order seqid + oooseqid BIGINT; + oootrgdepth INTEGER; + pdcursor NO SCROLL CURSOR (oooseqid INTEGER) FOR + SELECT seqid + FROM dbmirror2.pending_data + WHERE xid = txid_current() + AND seqid >= oooseqid + ORDER BY seqid DESC + FOR UPDATE; +BEGIN + _tablename := ( + quote_ident(TG_TABLE_SCHEMA) || '.' || quote_ident(TG_TABLE_NAME) + ); + + nextseqid := nextval( + pg_get_serial_sequence('dbmirror2.pending_data', 'seqid') + ); + + INSERT INTO dbmirror2.pending_ts (xid, ts) + VALUES (txid_current(), transaction_timestamp()) + ON CONFLICT DO NOTHING; + + IF TG_OP != 'INSERT' THEN + xoldctid := OLD.ctid; + END IF; + + IF TG_OP != 'DELETE' THEN + -- Detect out-of-order operations caused by cascading triggers. + -- + -- When row-level AFTER triggers are cascaded, the innermost trigger + -- runs first. This means we may potentially see an UPDATE or DELETE + -- of a row version that hasn't been added yet. + -- + -- We detect this by storing OLD.ctid for every operation. (The ctid + -- is a tuple describing the physical location of the row version. We + -- only need this to be stable for the lifetime of the current + -- transaction.) We then check if there's a previous operation whose + -- OLD ctid equals our NEW ctid; these are then known to be out-of- + -- order. This previous operation's seqid is assigned to `oooseqid` + -- ("out-of-order seqid"). + -- + -- The order is fixed by shifting the sequence IDs from the current + -- transaction until they're corrected. The current-last operation + -- assumes `nextseqid`, the second-to-last assumes the seqid of the + -- last, and so on until `oooseqid` is unused. We then insert our new + -- operation with `oooseqid`. + -- + -- Since we're never modifying `pending_data` rows inserted by other + -- transactions, this shifting should be safe. + SELECT seqid, trgdepth INTO oooseqid, oootrgdepth + FROM dbmirror2.pending_data + WHERE xid = txid_current() + AND tablename = _tablename + AND oldctid = NEW.ctid; + + IF FOUND THEN + IF oootrgdepth IS NOT NULL AND oootrgdepth <= pg_trigger_depth() THEN + -- This should never happen! Cascading triggers are the only + -- known way for operations to arrive out of order. This + -- warning must be investigated if it's ever logged. + RAISE WARNING 'oootrgdepth (%) <= pg_trigger_depth() (%) (% ON %, OLD: %, NEW: %)', + oootrgdepth, pg_trigger_depth(), TG_OP, _tablename, OLD, NEW; + END IF; + + FOR pdrecord IN pdcursor (oooseqid := oooseqid) LOOP + UPDATE dbmirror2.pending_data + SET seqid = nextseqid + WHERE CURRENT OF pdcursor; + + nextseqid := pdrecord.seqid; + END LOOP; + + ASSERT (nextseqid = oooseqid); + END IF; + END IF; + + INSERT INTO dbmirror2.pending_data + (seqid, tablename, op, xid, olddata, newdata, oldctid, trgdepth) + VALUES ( + nextseqid, + _tablename, + lower(left(TG_OP, 1)), + txid_current(), + row_to_json(OLD), + row_to_json(NEW), + xoldctid, + pg_trigger_depth() + ); + + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER sanitize_dbmirror2_editor_data + BEFORE INSERT ON dbmirror2.pending_data + FOR EACH ROW WHEN (NEW.tablename = 'musicbrainz.editor') + EXECUTE PROCEDURE sanitize_dbmirror2_editor_data(); + +COMMIT; diff --git a/mbslave/sql/updates/20260507-search-756-mirror_only.sql b/mbslave/sql/updates/20260507-search-756-mirror_only.sql new file mode 100644 index 0000000..2bfb837 --- /dev/null +++ b/mbslave/sql/updates/20260507-search-756-mirror_only.sql @@ -0,0 +1,113 @@ +\set ON_ERROR_STOP 1 + +BEGIN; + +DROP MATERIALIZED VIEW IF EXISTS dbmirror2.column_info CASCADE; +DROP EVENT TRIGGER IF EXISTS refresh_column_info; +DROP FUNCTION IF EXISTS dbmirror2.refresh_column_info(); + +CREATE OR REPLACE FUNCTION dbmirror2.recordchange() +RETURNS trigger AS $$ +DECLARE + -- prefixed with an underscore to disambiguate it from the column names + -- pending_data.tablename and pending_keys.tablename + _tablename TEXT; + keys TEXT[]; + -- prefixed with 'x' to avoid conflict with column name in queries + xoldctid TID; + nextseqid BIGINT; + -- out-of-order seqid + oooseqid BIGINT; + oootrgdepth INTEGER; + pdcursor NO SCROLL CURSOR (oooseqid INTEGER) FOR + SELECT seqid + FROM dbmirror2.pending_data + WHERE xid = txid_current() + AND seqid >= oooseqid + ORDER BY seqid DESC + FOR UPDATE; +BEGIN + _tablename := ( + quote_ident(TG_TABLE_SCHEMA) || '.' || quote_ident(TG_TABLE_NAME) + ); + + nextseqid := nextval( + pg_get_serial_sequence('dbmirror2.pending_data', 'seqid') + ); + + INSERT INTO dbmirror2.pending_ts (xid, ts) + VALUES (txid_current(), transaction_timestamp()) + ON CONFLICT DO NOTHING; + + IF TG_OP != 'INSERT' THEN + xoldctid := OLD.ctid; + END IF; + + IF TG_OP != 'DELETE' THEN + -- Detect out-of-order operations caused by cascading triggers. + -- + -- When row-level AFTER triggers are cascaded, the innermost trigger + -- runs first. This means we may potentially see an UPDATE or DELETE + -- of a row version that hasn't been added yet. + -- + -- We detect this by storing OLD.ctid for every operation. (The ctid + -- is a tuple describing the physical location of the row version. We + -- only need this to be stable for the lifetime of the current + -- transaction.) We then check if there's a previous operation whose + -- OLD ctid equals our NEW ctid; these are then known to be out-of- + -- order. This previous operation's seqid is assigned to `oooseqid` + -- ("out-of-order seqid"). + -- + -- The order is fixed by shifting the sequence IDs from the current + -- transaction until they're corrected. The current-last operation + -- assumes `nextseqid`, the second-to-last assumes the seqid of the + -- last, and so on until `oooseqid` is unused. We then insert our new + -- operation with `oooseqid`. + -- + -- Since we're never modifying `pending_data` rows inserted by other + -- transactions, this shifting should be safe. + SELECT seqid, trgdepth INTO oooseqid, oootrgdepth + FROM dbmirror2.pending_data + WHERE xid = txid_current() + AND tablename = _tablename + AND oldctid = NEW.ctid; + + IF FOUND THEN + IF oootrgdepth IS NOT NULL AND oootrgdepth <= pg_trigger_depth() THEN + -- This should never happen! Cascading triggers are the only + -- known way for operations to arrive out of order. This + -- warning must be investigated if it's ever logged. + RAISE WARNING 'oootrgdepth (%) <= pg_trigger_depth() (%) (% ON %, OLD: %, NEW: %)', + oootrgdepth, pg_trigger_depth(), TG_OP, _tablename, OLD, NEW; + END IF; + + FOR pdrecord IN pdcursor (oooseqid := oooseqid) LOOP + UPDATE dbmirror2.pending_data + SET seqid = nextseqid + WHERE CURRENT OF pdcursor; + + nextseqid := pdrecord.seqid; + END LOOP; + + ASSERT (nextseqid = oooseqid); + END IF; + END IF; + + INSERT INTO dbmirror2.pending_data + (seqid, tablename, op, xid, olddata, newdata, oldctid, trgdepth) + VALUES ( + nextseqid, + _tablename, + lower(left(TG_OP, 1)), + txid_current(), + row_to_json(OLD), + row_to_json(NEW), + xoldctid, + pg_trigger_depth() + ); + + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +COMMIT; diff --git a/mbslave/sql/updates/20260507-search-756-standalone_only.sql b/mbslave/sql/updates/20260507-search-756-standalone_only.sql new file mode 100644 index 0000000..db5ea48 --- /dev/null +++ b/mbslave/sql/updates/20260507-search-756-standalone_only.sql @@ -0,0 +1,155 @@ +\set ON_ERROR_STOP 1 + +BEGIN; + +DROP MATERIALIZED VIEW IF EXISTS dbmirror2.column_info CASCADE; +DROP TABLE IF EXISTS dbmirror2.pending_keys CASCADE; +DROP TABLE IF EXISTS dbmirror2.pending_ts CASCADE; +DROP TABLE IF EXISTS dbmirror2.pending_data CASCADE; +DROP EVENT TRIGGER IF EXISTS refresh_column_info; +DROP FUNCTION IF EXISTS dbmirror2.refresh_column_info(); + +CREATE OR REPLACE FUNCTION dbmirror2.recordchange() +RETURNS trigger AS $$ +DECLARE + -- prefixed with an underscore to disambiguate it from the column names + -- pending_data.tablename and pending_keys.tablename + _tablename TEXT; + keys TEXT[]; + -- prefixed with 'x' to avoid conflict with column name in queries + xoldctid TID; + nextseqid BIGINT; + -- out-of-order seqid + oooseqid BIGINT; + oootrgdepth INTEGER; + pdcursor NO SCROLL CURSOR (oooseqid INTEGER) FOR + SELECT seqid + FROM dbmirror2.pending_data + WHERE xid = txid_current() + AND seqid >= oooseqid + ORDER BY seqid DESC + FOR UPDATE; +BEGIN + _tablename := ( + quote_ident(TG_TABLE_SCHEMA) || '.' || quote_ident(TG_TABLE_NAME) + ); + + nextseqid := nextval( + pg_get_serial_sequence('dbmirror2.pending_data', 'seqid') + ); + + INSERT INTO dbmirror2.pending_ts (xid, ts) + VALUES (txid_current(), transaction_timestamp()) + ON CONFLICT DO NOTHING; + + IF TG_OP != 'INSERT' THEN + xoldctid := OLD.ctid; + END IF; + + IF TG_OP != 'DELETE' THEN + -- Detect out-of-order operations caused by cascading triggers. + -- + -- When row-level AFTER triggers are cascaded, the innermost trigger + -- runs first. This means we may potentially see an UPDATE or DELETE + -- of a row version that hasn't been added yet. + -- + -- We detect this by storing OLD.ctid for every operation. (The ctid + -- is a tuple describing the physical location of the row version. We + -- only need this to be stable for the lifetime of the current + -- transaction.) We then check if there's a previous operation whose + -- OLD ctid equals our NEW ctid; these are then known to be out-of- + -- order. This previous operation's seqid is assigned to `oooseqid` + -- ("out-of-order seqid"). + -- + -- The order is fixed by shifting the sequence IDs from the current + -- transaction until they're corrected. The current-last operation + -- assumes `nextseqid`, the second-to-last assumes the seqid of the + -- last, and so on until `oooseqid` is unused. We then insert our new + -- operation with `oooseqid`. + -- + -- Since we're never modifying `pending_data` rows inserted by other + -- transactions, this shifting should be safe. + SELECT seqid, trgdepth INTO oooseqid, oootrgdepth + FROM dbmirror2.pending_data + WHERE xid = txid_current() + AND tablename = _tablename + AND oldctid = NEW.ctid; + + IF FOUND THEN + IF oootrgdepth IS NOT NULL AND oootrgdepth <= pg_trigger_depth() THEN + -- This should never happen! Cascading triggers are the only + -- known way for operations to arrive out of order. This + -- warning must be investigated if it's ever logged. + RAISE WARNING 'oootrgdepth (%) <= pg_trigger_depth() (%) (% ON %, OLD: %, NEW: %)', + oootrgdepth, pg_trigger_depth(), TG_OP, _tablename, OLD, NEW; + END IF; + + FOR pdrecord IN pdcursor (oooseqid := oooseqid) LOOP + UPDATE dbmirror2.pending_data + SET seqid = nextseqid + WHERE CURRENT OF pdcursor; + + nextseqid := pdrecord.seqid; + END LOOP; + + ASSERT (nextseqid = oooseqid); + END IF; + END IF; + + INSERT INTO dbmirror2.pending_data + (seqid, tablename, op, xid, olddata, newdata, oldctid, trgdepth) + VALUES ( + nextseqid, + _tablename, + lower(left(TG_OP, 1)), + txid_current(), + row_to_json(OLD), + row_to_json(NEW), + xoldctid, + pg_trigger_depth() + ); + + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE dbmirror2.pending_keys ( + tablename TEXT, + keys TEXT[] NOT NULL +); + +ALTER TABLE dbmirror2.pending_keys + ADD CONSTRAINT pending_keys_pkey + PRIMARY KEY (tablename); + +CREATE TABLE dbmirror2.pending_ts ( + xid BIGINT, + ts TIMESTAMP WITH TIME ZONE NOT NULL +); + +ALTER TABLE dbmirror2.pending_ts + ADD CONSTRAINT pending_ts_pkey + PRIMARY KEY (xid); + +CREATE TABLE dbmirror2.pending_data ( + seqid BIGSERIAL, + tablename TEXT NOT NULL CONSTRAINT tablename_exists CHECK (to_regclass(tablename) IS NOT NULL), + op "char" NOT NULL CONSTRAINT op_in_diu CHECK (op IN ('d', 'i', 'u')), + xid BIGINT NOT NULL, + olddata JSON CONSTRAINT olddata_is_null_for_inserts CHECK ((olddata IS NULL) = (op = 'i')), + newdata JSON CONSTRAINT newdata_is_null_for_deletes CHECK ((newdata IS NULL) = (op = 'd')), + oldctid TID, + trgdepth INTEGER +); + +ALTER TABLE dbmirror2.pending_data + ADD CONSTRAINT pending_data_pkey + PRIMARY KEY (seqid); + +CREATE INDEX pending_data_idx_xid_seqid + ON dbmirror2.pending_data (xid, seqid); + +CREATE INDEX pending_data_idx_oldctid_xid + ON dbmirror2.pending_data (oldctid, xid); + +COMMIT; diff --git a/mbslave/sql/updates/schema-change/31.all.sql b/mbslave/sql/updates/schema-change/31.all.sql new file mode 100644 index 0000000..2de13ee --- /dev/null +++ b/mbslave/sql/updates/schema-change/31.all.sql @@ -0,0 +1,167 @@ +-- Generated by CompileSchemaScripts.pl from: +-- 20260121-mbs-14092.sql +-- 20260212-mbs-14252.sql +-- 20260422-mbs-6551-all.sql +-- 20260502-search-756-all.sql +\set ON_ERROR_STOP 1 +BEGIN; +SET search_path = musicbrainz, public; +SET LOCAL statement_timeout = 0; +-------------------------------------------------------------------------------- +SELECT '20260121-mbs-14092.sql'; + +\set SERIES_PART_OF_SERIES_ID '1307' + +\set SERIES_PART_OF_SERIES_GID '''8fe04b66-fe39-40ce-a28f-76b816d3f55a''' + +-- id from link_attribute_type where name = 'number' +\set LINK_ATTRIBUTE_TYPE_NUMBER_ID '788' + +----------------------- +-- CREATE NEW VIEWS -- +----------------------- + +CREATE OR REPLACE VIEW series_series AS + SELECT entity0 AS series_part, + entity1 AS series, + lss.id AS relationship, + link_order, + lss.link, + COALESCE(text_value, '') AS text_value + FROM l_series_series lss + JOIN series s ON s.id = lss.entity1 + JOIN link l ON l.id = lss.link + JOIN link_type lt ON (lt.id = l.link_type AND lt.gid = '8da75c99-46ff-373c-9d31-276ca8fa8cc3') + LEFT OUTER JOIN link_attribute_text_value latv ON (latv.attribute_type = 788 AND latv.link = l.id) + ORDER BY series, link_order; + +------------------------- +-- INSERT INITIAL DATA -- +------------------------- + +-- Part-of-series rel +-- Already exists in production, but disabled (no description) + +-- We insert the rel where it does not exist (outside prod) +INSERT INTO link_type (id, gid, entity_type0, entity_type1, entity0_cardinality, + entity1_cardinality, name, description, link_phrase, + reverse_link_phrase, long_link_phrase) VALUES + ( + :SERIES_PART_OF_SERIES_ID, + :SERIES_PART_OF_SERIES_GID, + 'series', 'series', 0, 0, 'part of', + '', + 'part of', 'has parts', 'is a part of' + ) ON CONFLICT DO NOTHING; + +-- We add the description to enable the rel +UPDATE link_type + SET description = 'Indicates that the series is part of a series.' + WHERE gid = :SERIES_PART_OF_SERIES_GID; + +-- We insert the attribute and orderable type where they do not exist (outside prod) +INSERT INTO link_attribute_type (id, parent, root, child_order, gid, name, description, last_updated) VALUES + ( + :LINK_ATTRIBUTE_TYPE_NUMBER_ID, + NULL, + :LINK_ATTRIBUTE_TYPE_NUMBER_ID, + 0, + 'a59c5830-5ec7-38fe-9a21-c7ea54f6650a', + 'number', + 'This attribute indicates the number of an entity in a series.', + '2021-05-10 11:27:11.858659+00' + ) ON CONFLICT DO NOTHING; + +INSERT INTO link_type_attribute_type (link_type, attribute_type, min, max) VALUES + ( + :SERIES_PART_OF_SERIES_ID, + :LINK_ATTRIBUTE_TYPE_NUMBER_ID, + 0, + 1 + ) ON CONFLICT DO NOTHING; + +INSERT INTO orderable_link_type (link_type, direction) VALUES + (:SERIES_PART_OF_SERIES_ID, 2) ON CONFLICT DO NOTHING; + +ALTER TABLE series_type DROP CONSTRAINT IF EXISTS allowed_series_entity_type; + +INSERT INTO series_type (id, name, entity_type, parent, child_order, description, gid) VALUES + (16, 'Series series', 'series', NULL, 6, 'A series of series.', generate_uuid_v3('6ba7b8119dad11d180b400c04fd430c8', 'series_type16')), + (17, 'Series award', 'series', 16, 0, 'A series of series (such as podcasts or festivals) honoured by the same award.', generate_uuid_v3('6ba7b8119dad11d180b400c04fd430c8', 'series_type17')); + +\unset SERIES_PART_OF_SERIES_GID +\unset LINK_ATTRIBUTE_TYPE_NUMBER_ID + +-------------------------------------------------------------------------------- +SELECT '20260212-mbs-14252.sql'; + + +ALTER TABLE isrc DROP COLUMN source CASCADE; +ALTER TABLE iswc DROP COLUMN source CASCADE; + +-------------------------------------------------------------------------------- +SELECT '20260422-mbs-6551-all.sql'; + + +-- Prepare data for the `no_empty_string_catalog_number` constraint +-- in admin/sql/updates/20260422-mbs-6551-master_and_standalone.sql. +UPDATE release_label SET catalog_number = NULL WHERE catalog_number = ''; + +-- This is mainly present for standalone databases. "Remove release label" +-- edits were already manually entered for the few duplicates that existed +-- in production. +DELETE FROM release_label dupe +USING release_label orig +WHERE dupe.release = orig.release + AND dupe.label IS NOT DISTINCT FROM orig.label + AND dupe.catalog_number IS NOT DISTINCT FROM orig.catalog_number + AND dupe.id > orig.id; + +-------------------------------------------------------------------------------- +SELECT '20260502-search-756-all.sql'; + + +CREATE OR REPLACE FUNCTION sanitize_editor(e editor) RETURNS editor AS $$ + SELECT ROW( + e.id, + e.name, + 0::INTEGER, + ''::VARCHAR(64), + NULL::VARCHAR(255), + NULL::TEXT, + e.member_since, + e.email_confirm_date, + now()::TIMESTAMP WITH TIME ZONE, + e.last_updated, + NULL::DATE, + NULL::INTEGER, + NULL::INTEGER, + '{CLEARTEXT}mb'::VARCHAR(128), + md5(e.name || ':musicbrainz.org:mb')::CHAR(32), + e.deleted + )::editor +$$ LANGUAGE sql STABLE PARALLEL SAFE; + +-- Adding `STRICT` on `sanitize_editor` would prevent inlining/optimization +-- when called via the `editor_sanitized` view. +CREATE OR REPLACE FUNCTION sanitize_editor_strict(e editor) RETURNS editor AS $$ + SELECT sanitize_editor(e) +$$ LANGUAGE sql STABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sanitize_dbmirror2_editor_data() RETURNS trigger AS $$ +BEGIN + NEW.olddata = row_to_json(sanitize_editor_strict(json_populate_record(NULL::editor, NEW.olddata))); + NEW.newdata = row_to_json(sanitize_editor_strict(json_populate_record(NULL::editor, NEW.newdata))); + IF NEW.op = 'u' AND NEW.olddata::JSONB = NEW.newdata::JSONB THEN + -- Only sanitized columns have changed. No need to log the update. + RETURN NULL; + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE VIEW editor_sanitized AS + SELECT (sanitize_editor(editor)).* + FROM editor; + +COMMIT; diff --git a/mbslave/sql/updates/schema-change/31.master_and_standalone.sql b/mbslave/sql/updates/schema-change/31.master_and_standalone.sql new file mode 100644 index 0000000..c3cd5ff --- /dev/null +++ b/mbslave/sql/updates/schema-change/31.master_and_standalone.sql @@ -0,0 +1,41 @@ +-- Generated by CompileSchemaScripts.pl from: +-- 20260121-mbs-14092-fks.sql +-- 20260422-mbs-6551-master_and_standalone.sql +\set ON_ERROR_STOP 1 +BEGIN; +SET search_path = musicbrainz, public; +SET LOCAL statement_timeout = 0; +-------------------------------------------------------------------------------- +SELECT '20260121-mbs-14092-fks.sql'; + +------------------ +-- constraints -- +------------------ + +ALTER TABLE series_type ADD CONSTRAINT allowed_series_entity_type + CHECK ( + entity_type IN ( + 'artist', + 'event', + 'recording', + 'release', + 'release_group', + 'series', + 'work' + ) + ); + +-------------------------------------------------------------------------------- +SELECT '20260422-mbs-6551-master_and_standalone.sql'; + + +ALTER TABLE release_label + ADD CONSTRAINT no_empty_string_catalog_number + CHECK (catalog_number != ''); + +ALTER TABLE release_label + ADD CONSTRAINT release_label_uniq + UNIQUE NULLS NOT DISTINCT (release, label, catalog_number) + DEFERRABLE INITIALLY DEFERRED; + +COMMIT; diff --git a/mbslave/sql/updates/schema-change/31.master_only.sql b/mbslave/sql/updates/schema-change/31.master_only.sql new file mode 100644 index 0000000..d4b7202 --- /dev/null +++ b/mbslave/sql/updates/schema-change/31.master_only.sql @@ -0,0 +1,124 @@ +-- Generated by CompileSchemaScripts.pl from: +-- 20260502-search-756-master_only.sql +\set ON_ERROR_STOP 1 +BEGIN; +SET search_path = musicbrainz, public; +SET LOCAL statement_timeout = 0; +-------------------------------------------------------------------------------- +SELECT '20260502-search-756-master_only.sql'; + + +DROP MATERIALIZED VIEW IF EXISTS dbmirror2.column_info CASCADE; +DROP EVENT TRIGGER IF EXISTS refresh_column_info; +DROP FUNCTION IF EXISTS dbmirror2.refresh_column_info(); + +CREATE OR REPLACE FUNCTION dbmirror2.recordchange() +RETURNS trigger AS $$ +DECLARE + -- prefixed with an underscore to disambiguate it from the column names + -- pending_data.tablename and pending_keys.tablename + _tablename TEXT; + keys TEXT[]; + -- prefixed with 'x' to avoid conflict with column name in queries + xoldctid TID; + nextseqid BIGINT; + -- out-of-order seqid + oooseqid BIGINT; + oootrgdepth INTEGER; + pdcursor NO SCROLL CURSOR (oooseqid INTEGER) FOR + SELECT seqid + FROM dbmirror2.pending_data + WHERE xid = txid_current() + AND seqid >= oooseqid + ORDER BY seqid DESC + FOR UPDATE; +BEGIN + _tablename := ( + quote_ident(TG_TABLE_SCHEMA) || '.' || quote_ident(TG_TABLE_NAME) + ); + + nextseqid := nextval( + pg_get_serial_sequence('dbmirror2.pending_data', 'seqid') + ); + + INSERT INTO dbmirror2.pending_ts (xid, ts) + VALUES (txid_current(), transaction_timestamp()) + ON CONFLICT DO NOTHING; + + IF TG_OP != 'INSERT' THEN + xoldctid := OLD.ctid; + END IF; + + IF TG_OP != 'DELETE' THEN + -- Detect out-of-order operations caused by cascading triggers. + -- + -- When row-level AFTER triggers are cascaded, the innermost trigger + -- runs first. This means we may potentially see an UPDATE or DELETE + -- of a row version that hasn't been added yet. + -- + -- We detect this by storing OLD.ctid for every operation. (The ctid + -- is a tuple describing the physical location of the row version. We + -- only need this to be stable for the lifetime of the current + -- transaction.) We then check if there's a previous operation whose + -- OLD ctid equals our NEW ctid; these are then known to be out-of- + -- order. This previous operation's seqid is assigned to `oooseqid` + -- ("out-of-order seqid"). + -- + -- The order is fixed by shifting the sequence IDs from the current + -- transaction until they're corrected. The current-last operation + -- assumes `nextseqid`, the second-to-last assumes the seqid of the + -- last, and so on until `oooseqid` is unused. We then insert our new + -- operation with `oooseqid`. + -- + -- Since we're never modifying `pending_data` rows inserted by other + -- transactions, this shifting should be safe. + SELECT seqid, trgdepth INTO oooseqid, oootrgdepth + FROM dbmirror2.pending_data + WHERE xid = txid_current() + AND tablename = _tablename + AND oldctid = NEW.ctid; + + IF FOUND THEN + IF oootrgdepth IS NOT NULL AND oootrgdepth <= pg_trigger_depth() THEN + -- This should never happen! Cascading triggers are the only + -- known way for operations to arrive out of order. This + -- warning must be investigated if it's ever logged. + RAISE WARNING 'oootrgdepth (%) <= pg_trigger_depth() (%) (% ON %, OLD: %, NEW: %)', + oootrgdepth, pg_trigger_depth(), TG_OP, _tablename, OLD, NEW; + END IF; + + FOR pdrecord IN pdcursor (oooseqid := oooseqid) LOOP + UPDATE dbmirror2.pending_data + SET seqid = nextseqid + WHERE CURRENT OF pdcursor; + + nextseqid := pdrecord.seqid; + END LOOP; + + ASSERT (nextseqid = oooseqid); + END IF; + END IF; + + INSERT INTO dbmirror2.pending_data + (seqid, tablename, op, xid, olddata, newdata, oldctid, trgdepth) + VALUES ( + nextseqid, + _tablename, + lower(left(TG_OP, 1)), + txid_current(), + row_to_json(OLD), + row_to_json(NEW), + xoldctid, + pg_trigger_depth() + ); + + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER sanitize_dbmirror2_editor_data + BEFORE INSERT ON dbmirror2.pending_data + FOR EACH ROW WHEN (NEW.tablename = 'musicbrainz.editor') + EXECUTE PROCEDURE sanitize_dbmirror2_editor_data(); + +COMMIT; diff --git a/mbslave/sql/updates/schema-change/31.mirror_only.sql b/mbslave/sql/updates/schema-change/31.mirror_only.sql new file mode 100644 index 0000000..d250a98 --- /dev/null +++ b/mbslave/sql/updates/schema-change/31.mirror_only.sql @@ -0,0 +1,119 @@ +-- Generated by CompileSchemaScripts.pl from: +-- 20260507-search-756-mirror_only.sql +\set ON_ERROR_STOP 1 +BEGIN; +SET search_path = musicbrainz, public; +SET LOCAL statement_timeout = 0; +-------------------------------------------------------------------------------- +SELECT '20260507-search-756-mirror_only.sql'; + + +DROP MATERIALIZED VIEW IF EXISTS dbmirror2.column_info CASCADE; +DROP EVENT TRIGGER IF EXISTS refresh_column_info; +DROP FUNCTION IF EXISTS dbmirror2.refresh_column_info(); + +CREATE OR REPLACE FUNCTION dbmirror2.recordchange() +RETURNS trigger AS $$ +DECLARE + -- prefixed with an underscore to disambiguate it from the column names + -- pending_data.tablename and pending_keys.tablename + _tablename TEXT; + keys TEXT[]; + -- prefixed with 'x' to avoid conflict with column name in queries + xoldctid TID; + nextseqid BIGINT; + -- out-of-order seqid + oooseqid BIGINT; + oootrgdepth INTEGER; + pdcursor NO SCROLL CURSOR (oooseqid INTEGER) FOR + SELECT seqid + FROM dbmirror2.pending_data + WHERE xid = txid_current() + AND seqid >= oooseqid + ORDER BY seqid DESC + FOR UPDATE; +BEGIN + _tablename := ( + quote_ident(TG_TABLE_SCHEMA) || '.' || quote_ident(TG_TABLE_NAME) + ); + + nextseqid := nextval( + pg_get_serial_sequence('dbmirror2.pending_data', 'seqid') + ); + + INSERT INTO dbmirror2.pending_ts (xid, ts) + VALUES (txid_current(), transaction_timestamp()) + ON CONFLICT DO NOTHING; + + IF TG_OP != 'INSERT' THEN + xoldctid := OLD.ctid; + END IF; + + IF TG_OP != 'DELETE' THEN + -- Detect out-of-order operations caused by cascading triggers. + -- + -- When row-level AFTER triggers are cascaded, the innermost trigger + -- runs first. This means we may potentially see an UPDATE or DELETE + -- of a row version that hasn't been added yet. + -- + -- We detect this by storing OLD.ctid for every operation. (The ctid + -- is a tuple describing the physical location of the row version. We + -- only need this to be stable for the lifetime of the current + -- transaction.) We then check if there's a previous operation whose + -- OLD ctid equals our NEW ctid; these are then known to be out-of- + -- order. This previous operation's seqid is assigned to `oooseqid` + -- ("out-of-order seqid"). + -- + -- The order is fixed by shifting the sequence IDs from the current + -- transaction until they're corrected. The current-last operation + -- assumes `nextseqid`, the second-to-last assumes the seqid of the + -- last, and so on until `oooseqid` is unused. We then insert our new + -- operation with `oooseqid`. + -- + -- Since we're never modifying `pending_data` rows inserted by other + -- transactions, this shifting should be safe. + SELECT seqid, trgdepth INTO oooseqid, oootrgdepth + FROM dbmirror2.pending_data + WHERE xid = txid_current() + AND tablename = _tablename + AND oldctid = NEW.ctid; + + IF FOUND THEN + IF oootrgdepth IS NOT NULL AND oootrgdepth <= pg_trigger_depth() THEN + -- This should never happen! Cascading triggers are the only + -- known way for operations to arrive out of order. This + -- warning must be investigated if it's ever logged. + RAISE WARNING 'oootrgdepth (%) <= pg_trigger_depth() (%) (% ON %, OLD: %, NEW: %)', + oootrgdepth, pg_trigger_depth(), TG_OP, _tablename, OLD, NEW; + END IF; + + FOR pdrecord IN pdcursor (oooseqid := oooseqid) LOOP + UPDATE dbmirror2.pending_data + SET seqid = nextseqid + WHERE CURRENT OF pdcursor; + + nextseqid := pdrecord.seqid; + END LOOP; + + ASSERT (nextseqid = oooseqid); + END IF; + END IF; + + INSERT INTO dbmirror2.pending_data + (seqid, tablename, op, xid, olddata, newdata, oldctid, trgdepth) + VALUES ( + nextseqid, + _tablename, + lower(left(TG_OP, 1)), + txid_current(), + row_to_json(OLD), + row_to_json(NEW), + xoldctid, + pg_trigger_depth() + ); + + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +COMMIT; diff --git a/mbslave/sql/updates/schema-change/31.standalone_only.sql b/mbslave/sql/updates/schema-change/31.standalone_only.sql new file mode 100644 index 0000000..f54b62b --- /dev/null +++ b/mbslave/sql/updates/schema-change/31.standalone_only.sql @@ -0,0 +1,161 @@ +-- Generated by CompileSchemaScripts.pl from: +-- 20260507-search-756-standalone_only.sql +\set ON_ERROR_STOP 1 +BEGIN; +SET search_path = musicbrainz, public; +SET LOCAL statement_timeout = 0; +-------------------------------------------------------------------------------- +SELECT '20260507-search-756-standalone_only.sql'; + + +DROP MATERIALIZED VIEW IF EXISTS dbmirror2.column_info CASCADE; +DROP TABLE IF EXISTS dbmirror2.pending_keys CASCADE; +DROP TABLE IF EXISTS dbmirror2.pending_ts CASCADE; +DROP TABLE IF EXISTS dbmirror2.pending_data CASCADE; +DROP EVENT TRIGGER IF EXISTS refresh_column_info; +DROP FUNCTION IF EXISTS dbmirror2.refresh_column_info(); + +CREATE OR REPLACE FUNCTION dbmirror2.recordchange() +RETURNS trigger AS $$ +DECLARE + -- prefixed with an underscore to disambiguate it from the column names + -- pending_data.tablename and pending_keys.tablename + _tablename TEXT; + keys TEXT[]; + -- prefixed with 'x' to avoid conflict with column name in queries + xoldctid TID; + nextseqid BIGINT; + -- out-of-order seqid + oooseqid BIGINT; + oootrgdepth INTEGER; + pdcursor NO SCROLL CURSOR (oooseqid INTEGER) FOR + SELECT seqid + FROM dbmirror2.pending_data + WHERE xid = txid_current() + AND seqid >= oooseqid + ORDER BY seqid DESC + FOR UPDATE; +BEGIN + _tablename := ( + quote_ident(TG_TABLE_SCHEMA) || '.' || quote_ident(TG_TABLE_NAME) + ); + + nextseqid := nextval( + pg_get_serial_sequence('dbmirror2.pending_data', 'seqid') + ); + + INSERT INTO dbmirror2.pending_ts (xid, ts) + VALUES (txid_current(), transaction_timestamp()) + ON CONFLICT DO NOTHING; + + IF TG_OP != 'INSERT' THEN + xoldctid := OLD.ctid; + END IF; + + IF TG_OP != 'DELETE' THEN + -- Detect out-of-order operations caused by cascading triggers. + -- + -- When row-level AFTER triggers are cascaded, the innermost trigger + -- runs first. This means we may potentially see an UPDATE or DELETE + -- of a row version that hasn't been added yet. + -- + -- We detect this by storing OLD.ctid for every operation. (The ctid + -- is a tuple describing the physical location of the row version. We + -- only need this to be stable for the lifetime of the current + -- transaction.) We then check if there's a previous operation whose + -- OLD ctid equals our NEW ctid; these are then known to be out-of- + -- order. This previous operation's seqid is assigned to `oooseqid` + -- ("out-of-order seqid"). + -- + -- The order is fixed by shifting the sequence IDs from the current + -- transaction until they're corrected. The current-last operation + -- assumes `nextseqid`, the second-to-last assumes the seqid of the + -- last, and so on until `oooseqid` is unused. We then insert our new + -- operation with `oooseqid`. + -- + -- Since we're never modifying `pending_data` rows inserted by other + -- transactions, this shifting should be safe. + SELECT seqid, trgdepth INTO oooseqid, oootrgdepth + FROM dbmirror2.pending_data + WHERE xid = txid_current() + AND tablename = _tablename + AND oldctid = NEW.ctid; + + IF FOUND THEN + IF oootrgdepth IS NOT NULL AND oootrgdepth <= pg_trigger_depth() THEN + -- This should never happen! Cascading triggers are the only + -- known way for operations to arrive out of order. This + -- warning must be investigated if it's ever logged. + RAISE WARNING 'oootrgdepth (%) <= pg_trigger_depth() (%) (% ON %, OLD: %, NEW: %)', + oootrgdepth, pg_trigger_depth(), TG_OP, _tablename, OLD, NEW; + END IF; + + FOR pdrecord IN pdcursor (oooseqid := oooseqid) LOOP + UPDATE dbmirror2.pending_data + SET seqid = nextseqid + WHERE CURRENT OF pdcursor; + + nextseqid := pdrecord.seqid; + END LOOP; + + ASSERT (nextseqid = oooseqid); + END IF; + END IF; + + INSERT INTO dbmirror2.pending_data + (seqid, tablename, op, xid, olddata, newdata, oldctid, trgdepth) + VALUES ( + nextseqid, + _tablename, + lower(left(TG_OP, 1)), + txid_current(), + row_to_json(OLD), + row_to_json(NEW), + xoldctid, + pg_trigger_depth() + ); + + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE dbmirror2.pending_keys ( + tablename TEXT, + keys TEXT[] NOT NULL +); + +ALTER TABLE dbmirror2.pending_keys + ADD CONSTRAINT pending_keys_pkey + PRIMARY KEY (tablename); + +CREATE TABLE dbmirror2.pending_ts ( + xid BIGINT, + ts TIMESTAMP WITH TIME ZONE NOT NULL +); + +ALTER TABLE dbmirror2.pending_ts + ADD CONSTRAINT pending_ts_pkey + PRIMARY KEY (xid); + +CREATE TABLE dbmirror2.pending_data ( + seqid BIGSERIAL, + tablename TEXT NOT NULL CONSTRAINT tablename_exists CHECK (to_regclass(tablename) IS NOT NULL), + op "char" NOT NULL CONSTRAINT op_in_diu CHECK (op IN ('d', 'i', 'u')), + xid BIGINT NOT NULL, + olddata JSON CONSTRAINT olddata_is_null_for_inserts CHECK ((olddata IS NULL) = (op = 'i')), + newdata JSON CONSTRAINT newdata_is_null_for_deletes CHECK ((newdata IS NULL) = (op = 'd')), + oldctid TID, + trgdepth INTEGER +); + +ALTER TABLE dbmirror2.pending_data + ADD CONSTRAINT pending_data_pkey + PRIMARY KEY (seqid); + +CREATE INDEX pending_data_idx_xid_seqid + ON dbmirror2.pending_data (xid, seqid); + +CREATE INDEX pending_data_idx_oldctid_xid + ON dbmirror2.pending_data (oldctid, xid); + +COMMIT; From f652ea4e8c7ec474559c7f79a2031a59c69401e2 Mon Sep 17 00:00:00 2001 From: Harald Friessnegger Date: Wed, 20 May 2026 15:43:46 +0200 Subject: [PATCH 6/6] fix renamed dbmirror2 script --- mbslave/replication.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mbslave/replication.py b/mbslave/replication.py index e33d68e..bbbe29e 100644 --- a/mbslave/replication.py +++ b/mbslave/replication.py @@ -686,7 +686,7 @@ def mbslave_init_main(config: Config, args: argparse.Namespace) -> None: # replication ('musicbrainz', 'ReplicationSetup.sql'), - ('dbmirror2', 'dbmirror2/ReplicationSetup.sql'), + ('dbmirror2', 'dbmirror2/dbmirror2.sql'), ]